@united-workforce/cli 0.2.1-rc.9 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -8
- package/dist/__tests__/adapter-json-roundtrip.test.js +1 -1
- package/dist/__tests__/adapter-json-roundtrip.test.js.map +1 -1
- package/dist/__tests__/agent-resolution-llm-free.test.d.ts +2 -0
- package/dist/__tests__/agent-resolution-llm-free.test.d.ts.map +1 -0
- package/dist/__tests__/agent-resolution-llm-free.test.js +30 -0
- package/dist/__tests__/agent-resolution-llm-free.test.js.map +1 -0
- package/dist/__tests__/build-step-entry.test.d.ts +2 -0
- package/dist/__tests__/build-step-entry.test.d.ts.map +1 -0
- package/dist/__tests__/build-step-entry.test.js +173 -0
- package/dist/__tests__/build-step-entry.test.js.map +1 -0
- package/dist/__tests__/clear-thread-failed-attempts.test.d.ts +2 -0
- package/dist/__tests__/clear-thread-failed-attempts.test.d.ts.map +1 -0
- package/dist/__tests__/clear-thread-failed-attempts.test.js +93 -0
- package/dist/__tests__/clear-thread-failed-attempts.test.js.map +1 -0
- package/dist/__tests__/config.test.js +26 -302
- package/dist/__tests__/config.test.js.map +1 -1
- package/dist/__tests__/current-role.test.js +7 -6
- package/dist/__tests__/current-role.test.js.map +1 -1
- package/dist/__tests__/e2e-mock-agent.test.js +20 -23
- package/dist/__tests__/e2e-mock-agent.test.js.map +1 -1
- package/dist/__tests__/issue-180-workflow-ref-removed.test.d.ts +2 -0
- package/dist/__tests__/issue-180-workflow-ref-removed.test.d.ts.map +1 -0
- package/dist/__tests__/issue-180-workflow-ref-removed.test.js +40 -0
- package/dist/__tests__/issue-180-workflow-ref-removed.test.js.map +1 -0
- package/dist/__tests__/moderator-evaluate.test.js +9 -50
- package/dist/__tests__/moderator-evaluate.test.js.map +1 -1
- package/dist/__tests__/pid-recycling.test.d.ts +2 -0
- package/dist/__tests__/pid-recycling.test.d.ts.map +1 -0
- package/dist/__tests__/pid-recycling.test.js +271 -0
- package/dist/__tests__/pid-recycling.test.js.map +1 -0
- package/dist/__tests__/prompt.test.js +321 -0
- package/dist/__tests__/prompt.test.js.map +1 -1
- package/dist/__tests__/resolve-head-hash.test.js +4 -4
- package/dist/__tests__/resolve-head-hash.test.js.map +1 -1
- package/dist/__tests__/setup-agent-discovery.test.js +21 -30
- package/dist/__tests__/setup-agent-discovery.test.js.map +1 -1
- package/dist/__tests__/setup-complexity.test.js +2 -168
- package/dist/__tests__/setup-complexity.test.js.map +1 -1
- package/dist/__tests__/setup-no-llm.test.d.ts +2 -0
- package/dist/__tests__/setup-no-llm.test.d.ts.map +1 -0
- package/dist/__tests__/setup-no-llm.test.js +52 -0
- package/dist/__tests__/setup-no-llm.test.js.map +1 -0
- package/dist/__tests__/solve-issue-tea-worktree.test.js +24 -27
- package/dist/__tests__/solve-issue-tea-worktree.test.js.map +1 -1
- package/dist/__tests__/step-ask.test.d.ts +2 -0
- package/dist/__tests__/step-ask.test.d.ts.map +1 -0
- package/dist/__tests__/step-ask.test.js +499 -0
- package/dist/__tests__/step-ask.test.js.map +1 -0
- package/dist/__tests__/step-show-json.test.js +1 -0
- package/dist/__tests__/step-show-json.test.js.map +1 -1
- package/dist/__tests__/step-timing.test.js +2 -0
- package/dist/__tests__/step-timing.test.js.map +1 -1
- package/dist/__tests__/store-global-cas.test.js +2 -2
- package/dist/__tests__/store-global-cas.test.js.map +1 -1
- package/dist/__tests__/store-unified-threads.test.js +9 -9
- package/dist/__tests__/store-unified-threads.test.js.map +1 -1
- package/dist/__tests__/thread-cancel-status.test.js +6 -6
- package/dist/__tests__/thread-cancel-status.test.js.map +1 -1
- package/dist/__tests__/thread-list-filters.test.js +344 -9
- package/dist/__tests__/thread-list-filters.test.js.map +1 -1
- package/dist/__tests__/thread-poke.test.d.ts +2 -0
- package/dist/__tests__/thread-poke.test.d.ts.map +1 -0
- package/dist/__tests__/thread-poke.test.js +412 -0
- package/dist/__tests__/thread-poke.test.js.map +1 -0
- package/dist/__tests__/thread-resume.test.js +10 -14
- package/dist/__tests__/thread-resume.test.js.map +1 -1
- package/dist/__tests__/thread-show-status.test.js +17 -28
- package/dist/__tests__/thread-show-status.test.js.map +1 -1
- package/dist/__tests__/thread-suspend-step.test.js +8 -14
- package/dist/__tests__/thread-suspend-step.test.js.map +1 -1
- package/dist/__tests__/thread-suspended-display.test.js +10 -22
- package/dist/__tests__/thread-suspended-display.test.js.map +1 -1
- package/dist/__tests__/thread.test.js +4 -4
- package/dist/__tests__/thread.test.js.map +1 -1
- package/dist/__tests__/validate-semantic.test.js +49 -21
- package/dist/__tests__/validate-semantic.test.js.map +1 -1
- package/dist/__tests__/workflow-list-recursive.test.d.ts +2 -0
- package/dist/__tests__/workflow-list-recursive.test.d.ts.map +1 -0
- package/dist/__tests__/workflow-list-recursive.test.js +283 -0
- package/dist/__tests__/workflow-list-recursive.test.js.map +1 -0
- package/dist/__tests__/workflow-resolution.test.js +36 -21
- package/dist/__tests__/workflow-resolution.test.js.map +1 -1
- package/dist/__tests__/workflow-show-resolution.test.d.ts +2 -0
- package/dist/__tests__/workflow-show-resolution.test.d.ts.map +1 -0
- package/dist/__tests__/workflow-show-resolution.test.js +210 -0
- package/dist/__tests__/workflow-show-resolution.test.js.map +1 -0
- package/dist/__tests__/workflow-validate.test.d.ts +2 -0
- package/dist/__tests__/workflow-validate.test.d.ts.map +1 -0
- package/dist/__tests__/workflow-validate.test.js +687 -0
- package/dist/__tests__/workflow-validate.test.js.map +1 -0
- package/dist/background/background.d.ts +22 -1
- package/dist/background/background.d.ts.map +1 -1
- package/dist/background/background.js +83 -6
- package/dist/background/background.js.map +1 -1
- package/dist/background/index.d.ts +1 -1
- package/dist/background/index.d.ts.map +1 -1
- package/dist/background/index.js +1 -1
- package/dist/background/index.js.map +1 -1
- package/dist/background/types.d.ts +1 -0
- package/dist/background/types.d.ts.map +1 -1
- package/dist/cli.js +66 -31
- package/dist/cli.js.map +1 -1
- package/dist/commands/config.d.ts +3 -1
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +7 -33
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/prompt.d.ts.map +1 -1
- package/dist/commands/prompt.js +15 -2
- package/dist/commands/prompt.js.map +1 -1
- package/dist/commands/setup.d.ts +7 -39
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +27 -302
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/step.d.ts +44 -1
- package/dist/commands/step.d.ts.map +1 -1
- package/dist/commands/step.js +255 -11
- package/dist/commands/step.js.map +1 -1
- package/dist/commands/thread.d.ts +16 -3
- package/dist/commands/thread.d.ts.map +1 -1
- package/dist/commands/thread.js +379 -140
- package/dist/commands/thread.js.map +1 -1
- package/dist/commands/workflow.d.ts +9 -1
- package/dist/commands/workflow.d.ts.map +1 -1
- package/dist/commands/workflow.js +130 -6
- package/dist/commands/workflow.js.map +1 -1
- package/dist/moderator/__tests__/evaluate.test.js +31 -17
- package/dist/moderator/__tests__/evaluate.test.js.map +1 -1
- package/dist/moderator/evaluate.d.ts.map +1 -1
- package/dist/moderator/evaluate.js +4 -16
- package/dist/moderator/evaluate.js.map +1 -1
- package/dist/moderator/index.d.ts +1 -2
- package/dist/moderator/index.d.ts.map +1 -1
- package/dist/moderator/index.js +0 -1
- package/dist/moderator/index.js.map +1 -1
- package/dist/moderator/types.d.ts +6 -10
- package/dist/moderator/types.d.ts.map +1 -1
- package/dist/moderator/types.js +1 -3
- package/dist/moderator/types.js.map +1 -1
- package/dist/schemas.d.ts +2 -0
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +5 -3
- package/dist/schemas.js.map +1 -1
- package/dist/store.d.ts +28 -9
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +75 -16
- package/dist/store.js.map +1 -1
- package/dist/validate-semantic.d.ts.map +1 -1
- package/dist/validate-semantic.js +83 -66
- package/dist/validate-semantic.js.map +1 -1
- package/dist/validate.d.ts +6 -0
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +24 -0
- package/dist/validate.js.map +1 -1
- package/package.json +8 -10
- package/src/__tests__/adapter-json-roundtrip.test.ts +1 -1
- package/src/__tests__/agent-resolution-llm-free.test.ts +39 -0
- package/src/__tests__/build-step-entry.test.ts +203 -0
- package/src/__tests__/clear-thread-failed-attempts.test.ts +122 -0
- package/src/__tests__/config.test.ts +33 -321
- package/src/__tests__/current-role.test.ts +7 -6
- package/src/__tests__/e2e-mock-agent.test.ts +20 -23
- package/src/__tests__/fixtures/e2e-count.workflow.yaml +1 -0
- package/src/__tests__/fixtures/e2e-linear.workflow.yaml +1 -0
- package/src/__tests__/fixtures/{e2e-mustache.workflow.yaml → e2e-liquid.workflow.yaml} +3 -2
- package/src/__tests__/fixtures/e2e-loop.workflow.yaml +1 -0
- package/src/__tests__/fixtures/e2e-suspend.mock.yaml +2 -2
- package/src/__tests__/fixtures/e2e-suspend.workflow.yaml +6 -10
- package/src/__tests__/issue-180-workflow-ref-removed.test.ts +43 -0
- package/src/__tests__/moderator-evaluate.test.ts +9 -52
- package/src/__tests__/pid-recycling.test.ts +328 -0
- package/src/__tests__/prompt.test.ts +397 -0
- package/src/__tests__/resolve-head-hash.test.ts +4 -4
- package/src/__tests__/setup-agent-discovery.test.ts +26 -51
- package/src/__tests__/setup-complexity.test.ts +1 -203
- package/src/__tests__/setup-no-llm.test.ts +68 -0
- package/src/__tests__/solve-issue-tea-worktree.test.ts +24 -30
- package/src/__tests__/step-ask.test.ts +670 -0
- package/src/__tests__/step-show-json.test.ts +1 -0
- package/src/__tests__/step-timing.test.ts +2 -0
- package/src/__tests__/store-global-cas.test.ts +2 -2
- package/src/__tests__/store-unified-threads.test.ts +9 -9
- package/src/__tests__/thread-cancel-status.test.ts +6 -6
- package/src/__tests__/thread-list-filters.test.ts +434 -8
- package/src/__tests__/thread-poke.test.ts +545 -0
- package/src/__tests__/thread-resume.test.ts +10 -14
- package/src/__tests__/thread-show-status.test.ts +17 -29
- package/src/__tests__/thread-suspend-step.test.ts +8 -14
- package/src/__tests__/thread-suspended-display.test.ts +10 -22
- package/src/__tests__/thread.test.ts +4 -4
- package/src/__tests__/validate-semantic.test.ts +59 -31
- package/src/__tests__/workflow-list-recursive.test.ts +370 -0
- package/src/__tests__/workflow-resolution.test.ts +39 -21
- package/src/__tests__/workflow-show-resolution.test.ts +285 -0
- package/src/__tests__/workflow-validate.test.ts +806 -0
- package/src/background/background.ts +88 -6
- package/src/background/index.ts +2 -0
- package/src/background/types.ts +1 -0
- package/src/cli.ts +97 -47
- package/src/commands/config.ts +7 -35
- package/src/commands/prompt.ts +15 -2
- package/src/commands/setup.ts +29 -357
- package/src/commands/step.ts +339 -12
- package/src/commands/thread.ts +463 -169
- package/src/commands/workflow.ts +159 -4
- package/src/moderator/__tests__/evaluate.test.ts +34 -17
- package/src/moderator/evaluate.ts +5 -17
- package/src/moderator/index.ts +1 -6
- package/src/moderator/types.ts +6 -14
- package/src/schemas.ts +13 -3
- package/src/store.ts +86 -20
- package/src/validate-semantic.ts +109 -78
- package/src/validate.ts +27 -0
- package/dist/__tests__/setup-validate.test.d.ts +0 -2
- package/dist/__tests__/setup-validate.test.d.ts.map +0 -1
- package/dist/__tests__/setup-validate.test.js +0 -108
- package/dist/__tests__/setup-validate.test.js.map +0 -1
- package/src/__tests__/setup-validate.test.ts +0 -148
- /package/src/__tests__/fixtures/{e2e-mustache.mock.yaml → e2e-liquid.mock.yaml} +0 -0
package/src/commands/setup.ts
CHANGED
|
@@ -3,88 +3,10 @@ import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSy
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { stdin as input, stdout as output } from "node:process";
|
|
5
5
|
import { createInterface } from "node:readline/promises";
|
|
6
|
-
import type { Result } from "@united-workforce/util";
|
|
7
6
|
import { parse, stringify } from "yaml";
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
* Returns ok on 2xx, error with reason string otherwise.
|
|
12
|
-
*/
|
|
13
|
-
export async function validateModel(
|
|
14
|
-
baseUrl: string,
|
|
15
|
-
apiKey: string,
|
|
16
|
-
model: string,
|
|
17
|
-
): Promise<Result<void, string>> {
|
|
18
|
-
try {
|
|
19
|
-
const url = `${baseUrl.replace(/\/+$/, "")}/chat/completions`;
|
|
20
|
-
const res = await fetch(url, {
|
|
21
|
-
method: "POST",
|
|
22
|
-
headers: {
|
|
23
|
-
Authorization: `Bearer ${apiKey}`,
|
|
24
|
-
"Content-Type": "application/json",
|
|
25
|
-
},
|
|
26
|
-
body: JSON.stringify({
|
|
27
|
-
model,
|
|
28
|
-
messages: [{ role: "user", content: "hi" }],
|
|
29
|
-
max_tokens: 1,
|
|
30
|
-
}),
|
|
31
|
-
signal: AbortSignal.timeout(15_000),
|
|
32
|
-
});
|
|
33
|
-
if (!res.ok) {
|
|
34
|
-
return { ok: false, error: `HTTP ${res.status} ${res.statusText}` };
|
|
35
|
-
}
|
|
36
|
-
return { ok: true, value: undefined };
|
|
37
|
-
} catch (err: unknown) {
|
|
38
|
-
if (err instanceof DOMException && err.name === "AbortError") {
|
|
39
|
-
return { ok: false, error: "Request timed out — model endpoint unreachable" };
|
|
40
|
-
}
|
|
41
|
-
return { ok: false, error: `Network error — could not reach endpoint (${String(err)})` };
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Preset provider list — embedded to avoid runtime YAML loading dependency.
|
|
47
|
-
* Keep in sync with providers.yaml in cli.
|
|
48
|
-
*/
|
|
49
|
-
const PRESET_PROVIDERS = [
|
|
50
|
-
// International
|
|
51
|
-
{ name: "openai", label: "OpenAI", baseUrl: "https://api.openai.com/v1" },
|
|
52
|
-
{ name: "xai", label: "xAI", baseUrl: "https://api.x.ai/v1" },
|
|
53
|
-
{ name: "openrouter", label: "OpenRouter", baseUrl: "https://openrouter.ai/api/v1" },
|
|
54
|
-
{ name: "venice", label: "Venice", baseUrl: "https://api.venice.ai/api/v1" },
|
|
55
|
-
// China
|
|
56
|
-
{
|
|
57
|
-
name: "dashscope",
|
|
58
|
-
label: "DashScope (Alibaba)",
|
|
59
|
-
baseUrl: "https://dashscope.aliyuncs.com/compatible-mode/v1",
|
|
60
|
-
},
|
|
61
|
-
{ name: "deepseek", label: "DeepSeek", baseUrl: "https://api.deepseek.com/v1" },
|
|
62
|
-
{ name: "siliconflow", label: "SiliconFlow", baseUrl: "https://api.siliconflow.cn/v1" },
|
|
63
|
-
{
|
|
64
|
-
name: "volcengine",
|
|
65
|
-
label: "Volcengine (ByteDance)",
|
|
66
|
-
baseUrl: "https://ark.cn-beijing.volces.com/api/v3",
|
|
67
|
-
},
|
|
68
|
-
{ name: "kimi", label: "Kimi (Moonshot)", baseUrl: "https://api.moonshot.cn/v1" },
|
|
69
|
-
{ name: "glm", label: "GLM (Zhipu AI)", baseUrl: "https://open.bigmodel.cn/api/paas/v4" },
|
|
70
|
-
{ name: "stepfun", label: "StepFun", baseUrl: "https://api.stepfun.com/v1" },
|
|
71
|
-
{ name: "minimax", label: "MiniMax", baseUrl: "https://api.minimax.io/v1" },
|
|
72
|
-
// Local
|
|
73
|
-
{ name: "ollama", label: "Ollama (local)", baseUrl: "http://localhost:11434/v1" },
|
|
74
|
-
] as const;
|
|
75
|
-
|
|
76
|
-
/** Look up the base URL for a preset provider name. Returns null if not a preset. */
|
|
77
|
-
export function resolvePresetBaseUrl(providerName: string): string | null {
|
|
78
|
-
const preset = PRESET_PROVIDERS.find((p) => p.name === providerName);
|
|
79
|
-
return preset !== undefined ? preset.baseUrl : null;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
type SetupArgs = {
|
|
83
|
-
provider: string;
|
|
84
|
-
baseUrl: string;
|
|
85
|
-
apiKey: string;
|
|
86
|
-
model: string;
|
|
87
|
-
agent?: string | undefined;
|
|
8
|
+
export type SetupArgs = {
|
|
9
|
+
agent: string;
|
|
88
10
|
storageRoot: string;
|
|
89
11
|
};
|
|
90
12
|
|
|
@@ -193,77 +115,17 @@ async function _tryWhichDiscovery(): Promise<string[] | null> {
|
|
|
193
115
|
}
|
|
194
116
|
|
|
195
117
|
// ──────────────────────────────────────────────────────────────────────────────
|
|
196
|
-
//
|
|
118
|
+
// Terminator/backspace helpers (kept for reuse + test coverage)
|
|
197
119
|
// ──────────────────────────────────────────────────────────────────────────────
|
|
198
120
|
|
|
199
121
|
/** Returns true for newline, carriage return, or EOF (EOT). */
|
|
200
122
|
export function _isTerminator(c: string): boolean {
|
|
201
|
-
return c === "\n" || c === "\r" || c === "
|
|
123
|
+
return c === "\n" || c === "\r" || c === "\u0004";
|
|
202
124
|
}
|
|
203
125
|
|
|
204
126
|
/** Returns true for DEL or backspace. */
|
|
205
127
|
export function _isBackspace(c: string): boolean {
|
|
206
|
-
return c === "
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
// ──────────────────────────────────────────────────────────────────────────────
|
|
210
|
-
// Extracted helpers — cmdSetupInteractive
|
|
211
|
-
// ──────────────────────────────────────────────────────────────────────────────
|
|
212
|
-
|
|
213
|
-
type ProviderEntry = { name: string; label: string; baseUrl: string };
|
|
214
|
-
|
|
215
|
-
/** Prints the numbered provider list and custom option to stdout. */
|
|
216
|
-
export function _printProviderMenu(providers: readonly ProviderEntry[]): void {
|
|
217
|
-
const numWidth = String(providers.length + 1).length;
|
|
218
|
-
for (let i = 0; i < providers.length; i++) {
|
|
219
|
-
const p = providers[i];
|
|
220
|
-
if (!p) continue;
|
|
221
|
-
const num = String(i + 1).padStart(numWidth);
|
|
222
|
-
console.log(` ${num}) ${p.label.padEnd(28)} ${p.baseUrl}`);
|
|
223
|
-
}
|
|
224
|
-
const customNum = String(providers.length + 1).padStart(numWidth);
|
|
225
|
-
console.log(` ${customNum}) Custom (enter name and URL manually)\n`);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
/** Resolves a numeric choice string to a preset provider, or null for custom/invalid. */
|
|
229
|
-
export function _resolveProviderChoice(
|
|
230
|
-
choice: string,
|
|
231
|
-
providers: readonly ProviderEntry[],
|
|
232
|
-
): { providerName: string; baseUrl: string } | null {
|
|
233
|
-
const n = Number.parseInt(choice, 10);
|
|
234
|
-
if (Number.isNaN(n) || n < 1 || n > providers.length) return null;
|
|
235
|
-
const p = providers[n - 1];
|
|
236
|
-
if (!p) return null;
|
|
237
|
-
return { providerName: p.name, baseUrl: p.baseUrl };
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
/** Resolves numeric index or literal model name to a model string. */
|
|
241
|
-
export function _resolveModelChoice(input: string, models: string[]): string {
|
|
242
|
-
const n = Number.parseInt(input, 10);
|
|
243
|
-
if (!Number.isNaN(n) && n >= 1 && n <= models.length) {
|
|
244
|
-
return models[n - 1] ?? input;
|
|
245
|
-
}
|
|
246
|
-
return input;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
/** Prints the multi-column model list to stdout. */
|
|
250
|
-
export function _printModelMenu(models: string[], termCols: number): void {
|
|
251
|
-
const nw = String(models.length).length;
|
|
252
|
-
const maxLen = models.reduce((m, s) => Math.max(m, s.length), 0);
|
|
253
|
-
const colWidth = nw + 2 + maxLen + 4;
|
|
254
|
-
const cols = Math.max(1, Math.floor(termCols / colWidth));
|
|
255
|
-
const rows = Math.ceil(models.length / cols);
|
|
256
|
-
for (let r = 0; r < rows; r++) {
|
|
257
|
-
let line = "";
|
|
258
|
-
for (let c = 0; c < cols; c++) {
|
|
259
|
-
const idx = c * rows + r;
|
|
260
|
-
if (idx >= models.length) break;
|
|
261
|
-
const num = String(idx + 1).padStart(nw);
|
|
262
|
-
const name = (models[idx] ?? "").padEnd(maxLen);
|
|
263
|
-
line += ` ${num}) ${name} `;
|
|
264
|
-
}
|
|
265
|
-
console.log(line.trimEnd());
|
|
266
|
-
}
|
|
128
|
+
return c === "\u007f" || c === "\b";
|
|
267
129
|
}
|
|
268
130
|
|
|
269
131
|
// ──────────────────────────────────────────────────────────────────────────────
|
|
@@ -323,7 +185,6 @@ export async function _promptAgentSelection(
|
|
|
323
185
|
console.log(` Found 1 agent: ${label} — auto-selected.\n`);
|
|
324
186
|
return name;
|
|
325
187
|
}
|
|
326
|
-
|
|
327
188
|
console.log(` Found ${agents.length} agents:\n`);
|
|
328
189
|
_printAgentMenu(agents);
|
|
329
190
|
const choice = (await rl.question(`Choose default agent [1-${agents.length}]: `)).trim();
|
|
@@ -340,61 +201,33 @@ export async function _promptAgentSelection(
|
|
|
340
201
|
return name;
|
|
341
202
|
}
|
|
342
203
|
|
|
343
|
-
type ValidationResult = { ok: boolean; error: string | null };
|
|
344
|
-
|
|
345
|
-
/** Prints the model validation result to stdout. */
|
|
346
|
-
export function _printValidationResult(validation: ValidationResult): void {
|
|
347
|
-
if (validation.ok) {
|
|
348
|
-
console.log("✓ Model verified — connection successful.\n");
|
|
349
|
-
} else {
|
|
350
|
-
console.log(`\n⚠ Warning: Could not reach model — ${validation.error}`);
|
|
351
|
-
console.log(
|
|
352
|
-
" Config saved, but you may want to try a different model or check your API key.\n",
|
|
353
|
-
);
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
|
|
357
204
|
// ──────────────────────────────────────────────────────────────────────────────
|
|
358
205
|
|
|
359
206
|
/**
|
|
360
|
-
* Merge setup args into config.yaml structure. Non-destructive — preserves
|
|
207
|
+
* Merge setup args into config.yaml structure. Non-destructive — preserves
|
|
208
|
+
* existing entries (including agentOverrides). Engine config is LLM-free, so
|
|
209
|
+
* legacy provider/model fields are dropped on rewrite.
|
|
361
210
|
*/
|
|
362
211
|
function mergeConfig(existing: Record<string, unknown>, args: SetupArgs): Record<string, unknown> {
|
|
363
|
-
const providers = (
|
|
364
|
-
typeof existing.providers === "object" && existing.providers !== null
|
|
365
|
-
? { ...(existing.providers as Record<string, unknown>) }
|
|
366
|
-
: {}
|
|
367
|
-
) as Record<string, unknown>;
|
|
368
|
-
|
|
369
|
-
providers[args.provider] = { baseUrl: args.baseUrl, apiKey: args.apiKey };
|
|
370
|
-
|
|
371
|
-
const models = (
|
|
372
|
-
typeof existing.models === "object" && existing.models !== null
|
|
373
|
-
? { ...(existing.models as Record<string, unknown>) }
|
|
374
|
-
: {}
|
|
375
|
-
) as Record<string, unknown>;
|
|
376
|
-
models.default = { provider: args.provider, name: args.model };
|
|
377
|
-
|
|
378
212
|
const agents = (
|
|
379
213
|
typeof existing.agents === "object" && existing.agents !== null
|
|
380
214
|
? { ...(existing.agents as Record<string, unknown>) }
|
|
381
215
|
: {}
|
|
382
216
|
) as Record<string, unknown>;
|
|
383
217
|
|
|
384
|
-
const agentName = _agentNameFromBinary(args.agent
|
|
385
|
-
// Ensure the selected agent has an entry
|
|
218
|
+
const agentName = _agentNameFromBinary(args.agent);
|
|
386
219
|
if (!agents[agentName]) {
|
|
387
220
|
agents[agentName] = { command: `uwf-${agentName}`, args: [] };
|
|
388
221
|
}
|
|
389
222
|
|
|
390
|
-
|
|
391
|
-
...existing,
|
|
392
|
-
providers,
|
|
393
|
-
models,
|
|
223
|
+
const merged: Record<string, unknown> = {
|
|
394
224
|
agents,
|
|
395
225
|
defaultAgent: agentName,
|
|
396
|
-
defaultModel: existing.defaultModel ?? "default",
|
|
397
226
|
};
|
|
227
|
+
if (existing.agentOverrides !== undefined) {
|
|
228
|
+
merged.agentOverrides = existing.agentOverrides;
|
|
229
|
+
}
|
|
230
|
+
return merged;
|
|
398
231
|
}
|
|
399
232
|
|
|
400
233
|
/**
|
|
@@ -429,7 +262,9 @@ export function _checkAdapterAvailability(agentName: string): string[] {
|
|
|
429
262
|
}
|
|
430
263
|
|
|
431
264
|
/**
|
|
432
|
-
* Non-interactive setup.
|
|
265
|
+
* Non-interactive setup. Engine config is LLM-free — only writes
|
|
266
|
+
* agents + defaultAgent. LLM provider/model configuration lives in
|
|
267
|
+
* config.yaml under `providers` and `models`.
|
|
433
268
|
*/
|
|
434
269
|
export async function cmdSetup(args: SetupArgs): Promise<Record<string, unknown>> {
|
|
435
270
|
const { storageRoot } = args;
|
|
@@ -445,11 +280,8 @@ export async function cmdSetup(args: SetupArgs): Promise<Record<string, unknown>
|
|
|
445
280
|
// Print config path to stderr (stdout is reserved for JSON output)
|
|
446
281
|
console.error(`Config saved to ${configPath} ✓`);
|
|
447
282
|
|
|
448
|
-
// Validate model connectivity
|
|
449
|
-
const validation = await validateModel(args.baseUrl, args.apiKey, args.model);
|
|
450
|
-
|
|
451
283
|
// Check adapter availability
|
|
452
|
-
const agentName = _agentNameFromBinary(args.agent
|
|
284
|
+
const agentName = _agentNameFromBinary(args.agent);
|
|
453
285
|
const adapterWarnings = _checkAdapterAvailability(agentName);
|
|
454
286
|
for (const w of adapterWarnings) {
|
|
455
287
|
console.error(`⚠ ${w}`);
|
|
@@ -457,191 +289,31 @@ export async function cmdSetup(args: SetupArgs): Promise<Record<string, unknown>
|
|
|
457
289
|
|
|
458
290
|
return {
|
|
459
291
|
configPath,
|
|
460
|
-
provider: args.provider,
|
|
461
|
-
model: args.model,
|
|
462
292
|
defaultAgent: merged.defaultAgent,
|
|
463
|
-
validation,
|
|
464
293
|
adapterWarnings,
|
|
465
294
|
};
|
|
466
295
|
}
|
|
467
296
|
|
|
468
|
-
type SecretState = {
|
|
469
|
-
buf: string;
|
|
470
|
-
rawWasSet: boolean;
|
|
471
|
-
resolve: (value: string) => void;
|
|
472
|
-
onData: (chunk: string) => void;
|
|
473
|
-
};
|
|
474
|
-
|
|
475
|
-
function _handleSecretTerminator(state: SecretState): void {
|
|
476
|
-
if (process.stdin.isTTY) process.stdin.setRawMode(state.rawWasSet);
|
|
477
|
-
process.stdin.pause();
|
|
478
|
-
process.stdin.removeListener("data", state.onData);
|
|
479
|
-
process.stdout.write("\n");
|
|
480
|
-
state.resolve(state.buf.trim());
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
function _handleSecretBackspace(state: SecretState): void {
|
|
484
|
-
if (state.buf.length > 0) {
|
|
485
|
-
state.buf = state.buf.slice(0, -1);
|
|
486
|
-
process.stdout.write("\b \b");
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
function _handleSecretChar(c: string, state: SecretState): boolean {
|
|
491
|
-
if (_isTerminator(c)) {
|
|
492
|
-
_handleSecretTerminator(state);
|
|
493
|
-
return true;
|
|
494
|
-
}
|
|
495
|
-
if (_isBackspace(c)) {
|
|
496
|
-
_handleSecretBackspace(state);
|
|
497
|
-
return false;
|
|
498
|
-
}
|
|
499
|
-
if (c === "") {
|
|
500
|
-
if (process.stdin.isTTY) process.stdin.setRawMode(state.rawWasSet);
|
|
501
|
-
process.exit(130);
|
|
502
|
-
}
|
|
503
|
-
state.buf += c;
|
|
504
|
-
process.stdout.write("*");
|
|
505
|
-
return false;
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
/** Read a line with terminal echo disabled (for secrets). */
|
|
509
|
-
async function promptSecret(label: string): Promise<string> {
|
|
510
|
-
process.stdout.write(label);
|
|
511
|
-
return new Promise((resolve) => {
|
|
512
|
-
const rawWasSet = process.stdin.isRaw;
|
|
513
|
-
if (process.stdin.isTTY) {
|
|
514
|
-
process.stdin.setRawMode(true);
|
|
515
|
-
}
|
|
516
|
-
process.stdin.resume();
|
|
517
|
-
process.stdin.setEncoding("utf8");
|
|
518
|
-
|
|
519
|
-
const state: SecretState = { buf: "", rawWasSet, resolve, onData: () => {} };
|
|
520
|
-
state.onData = (chunk: string) => {
|
|
521
|
-
for (const c of chunk.toString()) {
|
|
522
|
-
if (_handleSecretChar(c, state)) return;
|
|
523
|
-
}
|
|
524
|
-
};
|
|
525
|
-
process.stdin.on("data", state.onData);
|
|
526
|
-
});
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
/** Fetch available models from an OpenAI-compatible /models endpoint. */
|
|
530
|
-
async function fetchModels(baseUrl: string, apiKey: string): Promise<string[]> {
|
|
531
|
-
try {
|
|
532
|
-
const url = `${baseUrl.replace(/\/+$/, "")}/models`;
|
|
533
|
-
const res = await fetch(url, {
|
|
534
|
-
headers: { Authorization: `Bearer ${apiKey}` },
|
|
535
|
-
signal: AbortSignal.timeout(10_000),
|
|
536
|
-
});
|
|
537
|
-
if (!res.ok) return [];
|
|
538
|
-
const body = (await res.json()) as { data?: { id: string }[] };
|
|
539
|
-
if (!Array.isArray(body.data)) return [];
|
|
540
|
-
const NON_CHAT =
|
|
541
|
-
/speech|embed|image|video|audio|ocr|rerank|tts|asr|paraformer|sambert|cosyvoice|wordart|wanx|wan2|flux|stable-diffusion|gui-/i;
|
|
542
|
-
return body.data
|
|
543
|
-
.map((m) => m.id)
|
|
544
|
-
.filter((id) => !NON_CHAT.test(id))
|
|
545
|
-
.sort();
|
|
546
|
-
} catch {
|
|
547
|
-
return [];
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
async function _promptProviderSelection(
|
|
552
|
-
rl: ReturnType<typeof createInterface>,
|
|
553
|
-
): Promise<{ providerName: string; baseUrl: string }> {
|
|
554
|
-
console.log("Select a provider:\n");
|
|
555
|
-
_printProviderMenu(PRESET_PROVIDERS);
|
|
556
|
-
|
|
557
|
-
const choice = (await rl.question(`Choose [1-${PRESET_PROVIDERS.length + 1}]: `)).trim();
|
|
558
|
-
const choiceNum = Number.parseInt(choice, 10);
|
|
559
|
-
if (Number.isNaN(choiceNum) || choiceNum < 1 || choiceNum > PRESET_PROVIDERS.length + 1) {
|
|
560
|
-
throw new Error(`Invalid choice: ${choice}`);
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
const preset = _resolveProviderChoice(choice, PRESET_PROVIDERS);
|
|
564
|
-
if (preset) {
|
|
565
|
-
const selected = PRESET_PROVIDERS[choiceNum - 1];
|
|
566
|
-
if (selected) {
|
|
567
|
-
console.log(`\n → ${selected.label} (${selected.baseUrl})\n`);
|
|
568
|
-
}
|
|
569
|
-
return preset;
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
const providerName = (await rl.question("Provider name (e.g. my-proxy): ")).trim();
|
|
573
|
-
if (!providerName) throw new Error("Provider name required");
|
|
574
|
-
const baseUrl = (await rl.question("OpenAI-compatible API base URL: ")).trim();
|
|
575
|
-
if (!baseUrl) throw new Error("Base URL required");
|
|
576
|
-
return { providerName, baseUrl };
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
async function _promptModelSelection(
|
|
580
|
-
rl: ReturnType<typeof createInterface>,
|
|
581
|
-
baseUrl: string,
|
|
582
|
-
apiKey: string,
|
|
583
|
-
): Promise<string> {
|
|
584
|
-
console.log("\nFetching available models...");
|
|
585
|
-
const models = await fetchModels(baseUrl, apiKey);
|
|
586
|
-
|
|
587
|
-
if (models.length === 0) {
|
|
588
|
-
console.log("Could not fetch models. Enter model name manually.");
|
|
589
|
-
const model = (await rl.question("Default model (e.g. qwen-plus, gpt-4o): ")).trim();
|
|
590
|
-
if (!model) throw new Error("Model required");
|
|
591
|
-
return model;
|
|
592
|
-
}
|
|
593
|
-
console.log(`\nAvailable models (${models.length}):\n`);
|
|
594
|
-
_printModelMenu(models, process.stdout.columns || 100);
|
|
595
|
-
console.log(`\nChoose a number, or type a model name directly.`);
|
|
596
|
-
const modelInput = (await rl.question(`Default model [1-${models.length}]: `)).trim();
|
|
597
|
-
if (!modelInput) throw new Error("Model required");
|
|
598
|
-
return _resolveModelChoice(modelInput, models);
|
|
599
|
-
}
|
|
600
|
-
|
|
601
297
|
/**
|
|
602
|
-
* Interactive setup — prompts user for
|
|
298
|
+
* Interactive setup — prompts the user only for the default agent. LLM
|
|
299
|
+
* configuration lives in config.yaml under `providers` and `models`.
|
|
603
300
|
*/
|
|
604
301
|
export async function cmdSetupInteractive(storageRoot: string): Promise<Record<string, unknown>> {
|
|
605
302
|
const rl = createInterface({ input, output });
|
|
606
|
-
|
|
607
303
|
try {
|
|
608
|
-
console.log("Configure
|
|
609
|
-
|
|
610
|
-
const { providerName, baseUrl } = await _promptProviderSelection(rl);
|
|
304
|
+
console.log("Configure default agent for uwf workflow.\n");
|
|
611
305
|
|
|
612
|
-
|
|
306
|
+
const agentName = await _promptAgentSelection(rl);
|
|
613
307
|
rl.close();
|
|
614
|
-
const apiKey = await promptSecret("API key: ");
|
|
615
|
-
if (!apiKey) throw new Error("API key required");
|
|
616
|
-
|
|
617
|
-
// 3. Model selection
|
|
618
|
-
const rl2 = createInterface({ input, output });
|
|
619
|
-
const model = await _promptModelSelection(rl2, baseUrl, apiKey);
|
|
620
|
-
rl2.close();
|
|
621
|
-
console.log(` → ${providerName}/${model}\n`);
|
|
622
|
-
|
|
623
|
-
// 4. Agent discovery & selection
|
|
624
|
-
const rl3 = createInterface({ input, output });
|
|
625
|
-
const agentName = await _promptAgentSelection(rl3);
|
|
626
|
-
rl3.close();
|
|
627
|
-
|
|
628
|
-
const setupResult = await cmdSetup({
|
|
629
|
-
provider: providerName,
|
|
630
|
-
baseUrl,
|
|
631
|
-
apiKey,
|
|
632
|
-
model,
|
|
633
|
-
agent: agentName,
|
|
634
|
-
storageRoot,
|
|
635
|
-
});
|
|
636
308
|
|
|
637
|
-
|
|
638
|
-
if (setupResult.validation && typeof setupResult.validation === "object") {
|
|
639
|
-
_printValidationResult(setupResult.validation as ValidationResult);
|
|
640
|
-
}
|
|
309
|
+
await cmdSetup({ agent: agentName, storageRoot });
|
|
641
310
|
console.log("Setup complete! Get started:\n");
|
|
642
|
-
console.log(" uwf workflow
|
|
643
|
-
console.log(
|
|
644
|
-
console.log(
|
|
311
|
+
console.log(" uwf workflow list List available workflows");
|
|
312
|
+
console.log(" uwf workflow add <workflow.yaml> Register a workflow");
|
|
313
|
+
console.log(' uwf thread start <name> -p "..." Start a thread');
|
|
314
|
+
console.log(" uwf thread exec <thread-id> Execute next step");
|
|
315
|
+
console.log("");
|
|
316
|
+
console.log("LLM config: edit ~/.uwf/config.yaml (providers + models sections).");
|
|
645
317
|
console.log("");
|
|
646
318
|
|
|
647
319
|
return null as unknown as Record<string, unknown>;
|