fix: preset base-url auto-fill, bootstrap ACP docs, friendlier errors #109

Merged
xiaomo merged 1 commits from fix/106-107-108-bootstrap-ux into main 2026-06-05 11:16:31 +00:00
5 changed files with 60 additions and 12 deletions
+10
View File
@@ -0,0 +1,10 @@
---
"@united-workforce/cli": patch
---
fix: preset provider base-url auto-fill, bootstrap ACP docs, friendlier name mismatch error
- `uwf setup --provider dashscope` now auto-fills `--base-url` from preset list (#106)
- Bootstrap guide documents uwf-hermes ACP dependency (`pip install hermes-agent[acp]`) (#107)
- Bootstrap verify step uses inline workflow instead of missing `examples/eval-simple.yaml` (#107)
- Workflow filename mismatch error now suggests how to fix it (#108)
+8 -4
View File
@@ -11,7 +11,7 @@ import {
cmdPromptUsage, cmdPromptUsage,
cmdPromptWorkflowAuthoring, cmdPromptWorkflowAuthoring,
} from "./commands/prompt.js"; } from "./commands/prompt.js";
import { cmdSetup, cmdSetupInteractive } from "./commands/setup.js"; import { cmdSetup, cmdSetupInteractive, resolvePresetBaseUrl } from "./commands/setup.js";
import { cmdStepFork, cmdStepList, cmdStepRead, cmdStepShow } from "./commands/step.js"; import { cmdStepFork, cmdStepList, cmdStepRead, cmdStepShow } from "./commands/step.js";
import { import {
cmdThreadCancel, cmdThreadCancel,
@@ -558,10 +558,14 @@ program
}) => { }) => {
const storageRoot = resolveStorageRoot(); const storageRoot = resolveStorageRoot();
runAction(async () => { runAction(async () => {
if (opts.provider && opts.baseUrl && opts.apiKey && opts.model) { // Resolve preset base-url when provider is known but --base-url is omitted
const resolvedBaseUrl =
opts.baseUrl ??
(opts.provider !== undefined ? resolvePresetBaseUrl(opts.provider) : null);
if (opts.provider && resolvedBaseUrl && opts.apiKey && opts.model) {
const result = await cmdSetup({ const result = await cmdSetup({
provider: opts.provider, provider: opts.provider,
baseUrl: opts.baseUrl, baseUrl: resolvedBaseUrl,
apiKey: opts.apiKey, apiKey: opts.apiKey,
model: opts.model, model: opts.model,
agent: opts.agent ?? undefined, agent: opts.agent ?? undefined,
@@ -572,7 +576,7 @@ program
await cmdSetupInteractive(storageRoot); await cmdSetupInteractive(storageRoot);
} else { } else {
throw new Error( throw new Error(
"Non-interactive setup requires all of: --provider, --base-url, --api-key, --model", "Non-interactive setup requires: --provider, --api-key, --model (--base-url is optional for preset providers)",
); );
} }
}); });
+35 -7
View File
@@ -43,6 +43,11 @@ Install an agent adapter (at least one is required):
| uwf-claude-code | \`npm install -g @united-workforce/agent-claude-code\` | When using Claude Code CLI directly | | uwf-claude-code | \`npm install -g @united-workforce/agent-claude-code\` | When using Claude Code CLI directly |
| uwf-builtin | \`npm install -g @united-workforce/agent-builtin\` | Lightweight built-in agent (no external dependency) | | uwf-builtin | \`npm install -g @united-workforce/agent-builtin\` | Lightweight built-in agent (no external dependency) |
**uwf-hermes** also requires the Hermes ACP plugin. After installing \`hermes-agent\`, run:
\`\`\`bash
pip install hermes-agent[acp] # or: pip install -e .[acp] if installed from source
\`\`\`
Verify the adapter is installed: \`uwf-hermes --version\` (or whichever you chose). Verify the adapter is installed: \`uwf-hermes --version\` (or whichever you chose).
### Step 2 — Configure provider and model ### Step 2 — Configure provider and model
@@ -81,20 +86,43 @@ Verify skills are installed by listing them (e.g. \`skills_list()\`) and confirm
### Step 4 — Verify end-to-end ### Step 4 — Verify end-to-end
Run a quick smoke test with the built-in eval workflow: Create a minimal workflow file to test your setup:
\`\`\`bash \`\`\`bash
# Start a thread with the example workflow cat > /tmp/hello.yaml << 'YAML'
uwf thread start examples/eval-simple.yaml -p "Hello, test run" name: hello
description: Minimal smoke test
roles:
greeter:
description: "Greet the user"
goal: "Respond with a friendly greeting"
capabilities: []
procedure: "Write a short greeting based on the prompt."
output: "A greeting message."
frontmatter:
type: object
properties:
$status: { enum: [done] }
message: { type: string }
required: [$status, message]
graph:
$START:
new: { role: greeter, prompt: "Say hello to the user." }
resume: { role: greeter, prompt: "Greet the user again." }
greeter:
done: { role: "$END", prompt: "Done." }
YAML
\`\`\`
# Execute one step Then run:
\`\`\`bash
uwf thread start /tmp/hello.yaml -p "Hello, world!"
uwf thread exec <thread-id> uwf thread exec <thread-id>
# Check result
uwf thread show <thread-id> uwf thread show <thread-id>
\`\`\` \`\`\`
If the thread reaches \`$END\` or produces output, the setup is working. If the thread reaches \`$END\` with status \`completed\`, the setup is working.
## Scenario B: Upgrade from Previous Version ## Scenario B: Upgrade from Previous Version
+6
View File
@@ -72,6 +72,12 @@ const PRESET_PROVIDERS = [
{ name: "ollama", label: "Ollama (local)", baseUrl: "http://localhost:11434/v1" }, { name: "ollama", label: "Ollama (local)", baseUrl: "http://localhost:11434/v1" },
] as const; ] as const;
/** Look up the base URL for a preset provider name. Returns null if not a preset. */
export function resolvePresetBaseUrl(providerName: string): string | null {
const preset = PRESET_PROVIDERS.find((p) => p.name === providerName);
return preset !== undefined ? preset.baseUrl : null;
}
type SetupArgs = { type SetupArgs = {
provider: string; provider: string;
baseUrl: string; baseUrl: string;
+1 -1
View File
@@ -99,7 +99,7 @@ export function checkWorkflowFilenameConsistency(
): string | null { ): string | null {
const expected = workflowNameFromPath(filePath); const expected = workflowNameFromPath(filePath);
if (payload.name !== expected) { if (payload.name !== expected) {
return `workflow name mismatch: file "${basename(filePath)}" implies name "${expected}" but YAML declares name "${payload.name}"`; return `workflow name mismatch: file "${basename(filePath)}" implies name "${expected}" but YAML declares name "${payload.name}". Either rename the file to "${payload.name}.yaml" or change the YAML \`name\` field to "${expected}"`;
} }
return null; return null;
} }