@united-workforce/cli 0.3.0 → 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
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { mkdir, mkdtemp, rm } from "node:fs/promises";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import type { CasRef, ThreadId } from "@united-workforce/protocol";
|
|
5
|
+
import { afterEach, beforeEach, describe, expect, test } from "vitest";
|
|
6
|
+
import {
|
|
7
|
+
clearThreadFailedAttempts,
|
|
8
|
+
completeThread,
|
|
9
|
+
createUwfStore,
|
|
10
|
+
setThread,
|
|
11
|
+
type UwfStore,
|
|
12
|
+
} from "../store.js";
|
|
13
|
+
|
|
14
|
+
let tmpDir: string;
|
|
15
|
+
let originalEnv: string | undefined;
|
|
16
|
+
|
|
17
|
+
beforeEach(async () => {
|
|
18
|
+
tmpDir = await mkdtemp(join(tmpdir(), "cli-clear-failed-"));
|
|
19
|
+
originalEnv = process.env.OCAS_HOME;
|
|
20
|
+
process.env.OCAS_HOME = join(tmpDir, "cas");
|
|
21
|
+
await mkdir(process.env.OCAS_HOME, { recursive: true });
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
afterEach(async () => {
|
|
25
|
+
await rm(tmpDir, { recursive: true, force: true });
|
|
26
|
+
if (originalEnv === undefined) {
|
|
27
|
+
delete process.env.OCAS_HOME;
|
|
28
|
+
} else {
|
|
29
|
+
process.env.OCAS_HOME = originalEnv;
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
function failedVarName(threadId: ThreadId, role: string): string {
|
|
34
|
+
return `@uwf/thread-failed/${threadId}/${role}`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function seedFailedAttempt(uwf: UwfStore, threadId: ThreadId, role: string): Promise<void> {
|
|
38
|
+
const listHash = (await uwf.store.cas.put(
|
|
39
|
+
uwf.schemas.text,
|
|
40
|
+
JSON.stringify(["STEP00000000A"]),
|
|
41
|
+
)) as CasRef;
|
|
42
|
+
uwf.varStore.set(failedVarName(threadId, role), listHash);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function seedHead(uwf: UwfStore, label: string): Promise<CasRef> {
|
|
46
|
+
return (await uwf.store.cas.put(uwf.schemas.text, label)) as CasRef;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function countFailedVars(uwf: UwfStore, threadId: ThreadId): number {
|
|
50
|
+
return uwf.varStore.list({ namePrefix: `@uwf/thread-failed/${threadId}/` }).length;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
describe("clearThreadFailedAttempts", () => {
|
|
54
|
+
test("removes all failed-attempts vars for the given thread only", async () => {
|
|
55
|
+
const uwf = await createUwfStore(tmpDir);
|
|
56
|
+
const threadId = "01JTESTCLEAR0000000000001A" as ThreadId;
|
|
57
|
+
const otherThread = "01JTESTCLEAR0000000000002B" as ThreadId;
|
|
58
|
+
|
|
59
|
+
await seedFailedAttempt(uwf, threadId, "planner");
|
|
60
|
+
await seedFailedAttempt(uwf, threadId, "reviewer");
|
|
61
|
+
await seedFailedAttempt(uwf, otherThread, "planner");
|
|
62
|
+
|
|
63
|
+
expect(countFailedVars(uwf, threadId)).toBe(2);
|
|
64
|
+
expect(countFailedVars(uwf, otherThread)).toBe(1);
|
|
65
|
+
|
|
66
|
+
clearThreadFailedAttempts(uwf.varStore, threadId);
|
|
67
|
+
|
|
68
|
+
expect(countFailedVars(uwf, threadId)).toBe(0);
|
|
69
|
+
// The other thread's lineage is untouched.
|
|
70
|
+
expect(countFailedVars(uwf, otherThread)).toBe(1);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("is a no-op when no failed-attempts vars exist", async () => {
|
|
74
|
+
const uwf = await createUwfStore(tmpDir);
|
|
75
|
+
const threadId = "01JTESTCLEAR0000000000003C" as ThreadId;
|
|
76
|
+
expect(() => clearThreadFailedAttempts(uwf.varStore, threadId)).not.toThrow();
|
|
77
|
+
expect(countFailedVars(uwf, threadId)).toBe(0);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe("completeThread clears failed-attempts lineage", () => {
|
|
82
|
+
test("completion clears the thread's failed-attempts vars", async () => {
|
|
83
|
+
const uwf = await createUwfStore(tmpDir);
|
|
84
|
+
const threadId = "01JTESTCLEAR0000000000004D" as ThreadId;
|
|
85
|
+
const head = await seedHead(uwf, "head");
|
|
86
|
+
|
|
87
|
+
setThread(uwf.varStore, threadId, {
|
|
88
|
+
head,
|
|
89
|
+
status: "idle",
|
|
90
|
+
suspendedRole: null,
|
|
91
|
+
suspendMessage: null,
|
|
92
|
+
completedAt: null,
|
|
93
|
+
});
|
|
94
|
+
await seedFailedAttempt(uwf, threadId, "planner");
|
|
95
|
+
expect(countFailedVars(uwf, threadId)).toBe(1);
|
|
96
|
+
|
|
97
|
+
completeThread(uwf.varStore, threadId, "end");
|
|
98
|
+
|
|
99
|
+
expect(countFailedVars(uwf, threadId)).toBe(0);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test("cancellation clears the thread's failed-attempts vars", async () => {
|
|
103
|
+
const uwf = await createUwfStore(tmpDir);
|
|
104
|
+
const threadId = "01JTESTCLEAR0000000000005E" as ThreadId;
|
|
105
|
+
const head = await seedHead(uwf, "head");
|
|
106
|
+
|
|
107
|
+
setThread(uwf.varStore, threadId, {
|
|
108
|
+
head,
|
|
109
|
+
status: "running",
|
|
110
|
+
suspendedRole: null,
|
|
111
|
+
suspendMessage: null,
|
|
112
|
+
completedAt: null,
|
|
113
|
+
});
|
|
114
|
+
await seedFailedAttempt(uwf, threadId, "planner");
|
|
115
|
+
await seedFailedAttempt(uwf, threadId, "reviewer");
|
|
116
|
+
expect(countFailedVars(uwf, threadId)).toBe(2);
|
|
117
|
+
|
|
118
|
+
completeThread(uwf.varStore, threadId, "cancelled");
|
|
119
|
+
|
|
120
|
+
expect(countFailedVars(uwf, threadId)).toBe(0);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
@@ -21,22 +21,8 @@ describe("config command", () => {
|
|
|
21
21
|
return configPath;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
// Sample test config
|
|
25
|
-
const sampleConfig = `
|
|
26
|
-
dashscope:
|
|
27
|
-
baseUrl: https://dashscope.aliyuncs.com/compatible-mode/v1
|
|
28
|
-
apiKey: sk-test-dashscope-key
|
|
29
|
-
openai:
|
|
30
|
-
baseUrl: https://api.openai.com/v1
|
|
31
|
-
apiKey: sk-test-openai-key
|
|
32
|
-
models:
|
|
33
|
-
default:
|
|
34
|
-
provider: dashscope
|
|
35
|
-
name: qwen-max
|
|
36
|
-
gpt4:
|
|
37
|
-
provider: openai
|
|
38
|
-
name: gpt-4
|
|
39
|
-
agents:
|
|
24
|
+
// Sample test config — engine-only (no providers/models/defaultModel/modelOverrides)
|
|
25
|
+
const sampleConfig = `agents:
|
|
40
26
|
hermes:
|
|
41
27
|
command: uwf-hermes
|
|
42
28
|
args:
|
|
@@ -48,7 +34,6 @@ agents:
|
|
|
48
34
|
- --profile
|
|
49
35
|
- work
|
|
50
36
|
defaultAgent: hermes
|
|
51
|
-
defaultModel: default
|
|
52
37
|
`;
|
|
53
38
|
|
|
54
39
|
describe("helper functions", () => {
|
|
@@ -56,11 +41,7 @@ defaultModel: default
|
|
|
56
41
|
test("splits dot notation correctly", () => {
|
|
57
42
|
expect(parseDotPath("a.b.c")).toEqual(["a", "b", "c"]);
|
|
58
43
|
expect(parseDotPath("defaultAgent")).toEqual(["defaultAgent"]);
|
|
59
|
-
expect(parseDotPath("
|
|
60
|
-
"providers",
|
|
61
|
-
"dashscope",
|
|
62
|
-
"baseUrl",
|
|
63
|
-
]);
|
|
44
|
+
expect(parseDotPath("agents.hermes.command")).toEqual(["agents", "hermes", "command"]);
|
|
64
45
|
});
|
|
65
46
|
});
|
|
66
47
|
|
|
@@ -102,84 +83,14 @@ defaultModel: default
|
|
|
102
83
|
});
|
|
103
84
|
|
|
104
85
|
describe("maskApiKeys", () => {
|
|
105
|
-
test("deep
|
|
86
|
+
test("returns deep clone (no mutation) — engine config has no apiKey to mask", () => {
|
|
106
87
|
const config = {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
baseUrl: "https://example.com",
|
|
110
|
-
apiKey: "sk-test-key-12345",
|
|
111
|
-
},
|
|
112
|
-
openai: {
|
|
113
|
-
baseUrl: "https://api.openai.com",
|
|
114
|
-
apiKey: "sk-another-secret",
|
|
115
|
-
},
|
|
116
|
-
},
|
|
117
|
-
models: {
|
|
118
|
-
default: { provider: "dashscope" },
|
|
119
|
-
},
|
|
88
|
+
agents: { hermes: { command: "uwf-hermes", args: [] } },
|
|
89
|
+
defaultAgent: "hermes",
|
|
120
90
|
};
|
|
121
91
|
const masked = maskApiKeys(config);
|
|
122
|
-
expect(masked).toEqual({
|
|
123
|
-
providers: {
|
|
124
|
-
dashscope: {
|
|
125
|
-
baseUrl: "https://example.com",
|
|
126
|
-
apiKey: "***MASKED***",
|
|
127
|
-
},
|
|
128
|
-
openai: {
|
|
129
|
-
baseUrl: "https://api.openai.com",
|
|
130
|
-
apiKey: "***MASKED***",
|
|
131
|
-
},
|
|
132
|
-
},
|
|
133
|
-
models: {
|
|
134
|
-
default: { provider: "dashscope" },
|
|
135
|
-
},
|
|
136
|
-
});
|
|
137
|
-
// Ensure it's a deep clone
|
|
138
|
-
expect(masked).not.toBe(config);
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
test("handles config without providers", () => {
|
|
142
|
-
const config = { models: { default: { provider: "test" } } };
|
|
143
|
-
const masked = maskApiKeys(config);
|
|
144
92
|
expect(masked).toEqual(config);
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
test("does not mask non-provider apiKey fields", () => {
|
|
148
|
-
const config = {
|
|
149
|
-
apiKey: "root-level-key",
|
|
150
|
-
providers: {
|
|
151
|
-
dashscope: { apiKey: "sk-secret" },
|
|
152
|
-
},
|
|
153
|
-
models: {
|
|
154
|
-
default: { provider: "dashscope" },
|
|
155
|
-
},
|
|
156
|
-
};
|
|
157
|
-
const masked = maskApiKeys(config);
|
|
158
|
-
// Root-level apiKey should NOT be masked
|
|
159
|
-
expect(masked.apiKey).toBe("root-level-key");
|
|
160
|
-
// Provider apiKey SHOULD be masked
|
|
161
|
-
const providers = masked.providers as Record<string, Record<string, unknown>>;
|
|
162
|
-
expect(providers.dashscope.apiKey).toBe("***MASKED***");
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
test("handles empty provider object", () => {
|
|
166
|
-
const config = {
|
|
167
|
-
providers: { dashscope: {} },
|
|
168
|
-
};
|
|
169
|
-
const masked = maskApiKeys(config);
|
|
170
|
-
expect(masked).toEqual({ providers: { dashscope: {} } });
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
test("handles provider with null apiKey", () => {
|
|
174
|
-
const config = {
|
|
175
|
-
providers: {
|
|
176
|
-
dashscope: { apiKey: null, baseUrl: "https://example.com" },
|
|
177
|
-
},
|
|
178
|
-
};
|
|
179
|
-
const masked = maskApiKeys(config);
|
|
180
|
-
const providers = masked.providers as Record<string, Record<string, unknown>>;
|
|
181
|
-
expect(providers.dashscope.apiKey).toBe("***MASKED***");
|
|
182
|
-
expect(providers.dashscope.baseUrl).toBe("https://example.com");
|
|
93
|
+
expect(masked).not.toBe(config);
|
|
183
94
|
});
|
|
184
95
|
});
|
|
185
96
|
});
|
|
@@ -192,26 +103,8 @@ defaultModel: default
|
|
|
192
103
|
const result = await cmdConfigList(tempDir);
|
|
193
104
|
expect(result).toBeDefined();
|
|
194
105
|
expect(typeof result).toBe("object");
|
|
195
|
-
expect(result).toHaveProperty("providers");
|
|
196
|
-
expect(result).toHaveProperty("models");
|
|
197
106
|
expect(result).toHaveProperty("agents");
|
|
198
107
|
expect(result).toHaveProperty("defaultAgent");
|
|
199
|
-
expect(result).toHaveProperty("defaultModel");
|
|
200
|
-
} finally {
|
|
201
|
-
rmSync(tempDir, { recursive: true, force: true });
|
|
202
|
-
}
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
test("masks all apiKey values in providers section", async () => {
|
|
206
|
-
const tempDir = mkdtempSync(join(tmpdir(), "test-config-"));
|
|
207
|
-
try {
|
|
208
|
-
createTestConfig(tempDir, sampleConfig);
|
|
209
|
-
const result = (await cmdConfigList(tempDir)) as Record<string, unknown>;
|
|
210
|
-
const providers = result.providers as Record<string, unknown>;
|
|
211
|
-
const dashscope = providers.dashscope as Record<string, unknown>;
|
|
212
|
-
const openai = providers.openai as Record<string, unknown>;
|
|
213
|
-
expect(dashscope.apiKey).toBe("***MASKED***");
|
|
214
|
-
expect(openai.apiKey).toBe("***MASKED***");
|
|
215
108
|
} finally {
|
|
216
109
|
rmSync(tempDir, { recursive: true, force: true });
|
|
217
110
|
}
|
|
@@ -260,53 +153,6 @@ defaultModel: default
|
|
|
260
153
|
}
|
|
261
154
|
});
|
|
262
155
|
|
|
263
|
-
test("retrieves top-level string value (defaultModel)", async () => {
|
|
264
|
-
const tempDir = mkdtempSync(join(tmpdir(), "test-config-"));
|
|
265
|
-
try {
|
|
266
|
-
createTestConfig(tempDir, sampleConfig);
|
|
267
|
-
const result = await cmdConfigGet(tempDir, "defaultModel");
|
|
268
|
-
expect(result).toBe("default");
|
|
269
|
-
} finally {
|
|
270
|
-
rmSync(tempDir, { recursive: true, force: true });
|
|
271
|
-
}
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
test("retrieves nested object (providers.dashscope)", async () => {
|
|
275
|
-
const tempDir = mkdtempSync(join(tmpdir(), "test-config-"));
|
|
276
|
-
try {
|
|
277
|
-
createTestConfig(tempDir, sampleConfig);
|
|
278
|
-
const result = await cmdConfigGet(tempDir, "providers.dashscope");
|
|
279
|
-
expect(result).toEqual({
|
|
280
|
-
baseUrl: "https://dashscope.aliyuncs.com/compatible-mode/v1",
|
|
281
|
-
apiKey: "sk-test-dashscope-key",
|
|
282
|
-
});
|
|
283
|
-
} finally {
|
|
284
|
-
rmSync(tempDir, { recursive: true, force: true });
|
|
285
|
-
}
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
test("retrieves deeply nested string (providers.dashscope.baseUrl)", async () => {
|
|
289
|
-
const tempDir = mkdtempSync(join(tmpdir(), "test-config-"));
|
|
290
|
-
try {
|
|
291
|
-
createTestConfig(tempDir, sampleConfig);
|
|
292
|
-
const result = await cmdConfigGet(tempDir, "providers.dashscope.baseUrl");
|
|
293
|
-
expect(result).toBe("https://dashscope.aliyuncs.com/compatible-mode/v1");
|
|
294
|
-
} finally {
|
|
295
|
-
rmSync(tempDir, { recursive: true, force: true });
|
|
296
|
-
}
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
test("retrieves nested string in models (models.default.provider)", async () => {
|
|
300
|
-
const tempDir = mkdtempSync(join(tmpdir(), "test-config-"));
|
|
301
|
-
try {
|
|
302
|
-
createTestConfig(tempDir, sampleConfig);
|
|
303
|
-
const result = await cmdConfigGet(tempDir, "models.default.provider");
|
|
304
|
-
expect(result).toBe("dashscope");
|
|
305
|
-
} finally {
|
|
306
|
-
rmSync(tempDir, { recursive: true, force: true });
|
|
307
|
-
}
|
|
308
|
-
});
|
|
309
|
-
|
|
310
156
|
test("retrieves array value (agents.hermes.args)", async () => {
|
|
311
157
|
const tempDir = mkdtempSync(join(tmpdir(), "test-config-"));
|
|
312
158
|
try {
|
|
@@ -355,7 +201,6 @@ defaultModel: default
|
|
|
355
201
|
createTestConfig(tempDir, sampleConfig);
|
|
356
202
|
const result = await cmdConfigSet(tempDir, "defaultAgent", "claude-code");
|
|
357
203
|
expect(result).toEqual({ key: "defaultAgent", value: "claude-code" });
|
|
358
|
-
// Verify it was written
|
|
359
204
|
const updated = await cmdConfigGet(tempDir, "defaultAgent");
|
|
360
205
|
expect(updated).toBe("claude-code");
|
|
361
206
|
} finally {
|
|
@@ -363,42 +208,6 @@ defaultModel: default
|
|
|
363
208
|
}
|
|
364
209
|
});
|
|
365
210
|
|
|
366
|
-
test("sets nested string value (providers.dashscope.baseUrl)", async () => {
|
|
367
|
-
const tempDir = mkdtempSync(join(tmpdir(), "test-config-"));
|
|
368
|
-
try {
|
|
369
|
-
createTestConfig(tempDir, sampleConfig);
|
|
370
|
-
const newUrl = "https://new-api.example.com/v1";
|
|
371
|
-
const result = await cmdConfigSet(tempDir, "providers.dashscope.baseUrl", newUrl);
|
|
372
|
-
expect(result).toEqual({
|
|
373
|
-
key: "providers.dashscope.baseUrl",
|
|
374
|
-
value: newUrl,
|
|
375
|
-
});
|
|
376
|
-
// Verify it was written
|
|
377
|
-
const updated = await cmdConfigGet(tempDir, "providers.dashscope.baseUrl");
|
|
378
|
-
expect(updated).toBe(newUrl);
|
|
379
|
-
} finally {
|
|
380
|
-
rmSync(tempDir, { recursive: true, force: true });
|
|
381
|
-
}
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
test("creates new nested path (providers.newprovider.baseUrl)", async () => {
|
|
385
|
-
const tempDir = mkdtempSync(join(tmpdir(), "test-config-"));
|
|
386
|
-
try {
|
|
387
|
-
createTestConfig(tempDir, sampleConfig);
|
|
388
|
-
const newUrl = "https://new-provider.com/v1";
|
|
389
|
-
const result = await cmdConfigSet(tempDir, "providers.newprovider.baseUrl", newUrl);
|
|
390
|
-
expect(result).toEqual({
|
|
391
|
-
key: "providers.newprovider.baseUrl",
|
|
392
|
-
value: newUrl,
|
|
393
|
-
});
|
|
394
|
-
// Verify it was created
|
|
395
|
-
const updated = await cmdConfigGet(tempDir, "providers.newprovider.baseUrl");
|
|
396
|
-
expect(updated).toBe(newUrl);
|
|
397
|
-
} finally {
|
|
398
|
-
rmSync(tempDir, { recursive: true, force: true });
|
|
399
|
-
}
|
|
400
|
-
});
|
|
401
|
-
|
|
402
211
|
test("sets array value for args key with valid JSON array", async () => {
|
|
403
212
|
const tempDir = mkdtempSync(join(tmpdir(), "test-config-"));
|
|
404
213
|
try {
|
|
@@ -409,7 +218,6 @@ defaultModel: default
|
|
|
409
218
|
key: "agents.hermes.args",
|
|
410
219
|
value: ["--new", "--flags"],
|
|
411
220
|
});
|
|
412
|
-
// Verify it was written
|
|
413
221
|
const updated = await cmdConfigGet(tempDir, "agents.hermes.args");
|
|
414
222
|
expect(updated).toEqual(["--new", "--flags"]);
|
|
415
223
|
} finally {
|
|
@@ -422,11 +230,8 @@ defaultModel: default
|
|
|
422
230
|
try {
|
|
423
231
|
createTestConfig(tempDir, sampleConfig);
|
|
424
232
|
await cmdConfigSet(tempDir, "defaultAgent", "claude-code");
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
expect(defaultModel).toBe("default");
|
|
428
|
-
const dashscopeUrl = await cmdConfigGet(tempDir, "providers.dashscope.baseUrl");
|
|
429
|
-
expect(dashscopeUrl).toBe("https://dashscope.aliyuncs.com/compatible-mode/v1");
|
|
233
|
+
const cmd = await cmdConfigGet(tempDir, "agents.hermes.command");
|
|
234
|
+
expect(cmd).toBe("uwf-hermes");
|
|
430
235
|
} finally {
|
|
431
236
|
rmSync(tempDir, { recursive: true, force: true });
|
|
432
237
|
}
|
|
@@ -437,7 +242,6 @@ defaultModel: default
|
|
|
437
242
|
try {
|
|
438
243
|
const result = await cmdConfigSet(tempDir, "defaultAgent", "hermes");
|
|
439
244
|
expect(result).toEqual({ key: "defaultAgent", value: "hermes" });
|
|
440
|
-
// Verify file was created
|
|
441
245
|
const configPath = getConfigPath(tempDir);
|
|
442
246
|
const content = readFileSync(configPath, "utf8");
|
|
443
247
|
expect(content).toContain("defaultAgent: hermes");
|
|
@@ -468,23 +272,6 @@ defaultModel: default
|
|
|
468
272
|
}
|
|
469
273
|
});
|
|
470
274
|
|
|
471
|
-
test("sets deeply nested model config (models.gpt4.provider)", async () => {
|
|
472
|
-
const tempDir = mkdtempSync(join(tmpdir(), "test-config-"));
|
|
473
|
-
try {
|
|
474
|
-
createTestConfig(tempDir, sampleConfig);
|
|
475
|
-
const result = await cmdConfigSet(tempDir, "models.gpt4.provider", "new-provider");
|
|
476
|
-
expect(result).toEqual({
|
|
477
|
-
key: "models.gpt4.provider",
|
|
478
|
-
value: "new-provider",
|
|
479
|
-
});
|
|
480
|
-
// Verify it was written
|
|
481
|
-
const updated = await cmdConfigGet(tempDir, "models.gpt4.provider");
|
|
482
|
-
expect(updated).toBe("new-provider");
|
|
483
|
-
} finally {
|
|
484
|
-
rmSync(tempDir, { recursive: true, force: true });
|
|
485
|
-
}
|
|
486
|
-
});
|
|
487
|
-
|
|
488
275
|
test("sets agent command (agents.claude-code.command)", async () => {
|
|
489
276
|
const tempDir = mkdtempSync(join(tmpdir(), "test-config-"));
|
|
490
277
|
try {
|
|
@@ -494,7 +281,6 @@ defaultModel: default
|
|
|
494
281
|
key: "agents.claude-code.command",
|
|
495
282
|
value: "new-command",
|
|
496
283
|
});
|
|
497
|
-
// Verify it was written
|
|
498
284
|
const updated = await cmdConfigGet(tempDir, "agents.claude-code.command");
|
|
499
285
|
expect(updated).toBe("new-command");
|
|
500
286
|
} finally {
|
|
@@ -503,97 +289,87 @@ defaultModel: default
|
|
|
503
289
|
});
|
|
504
290
|
});
|
|
505
291
|
|
|
506
|
-
describe("cmdConfigSet
|
|
507
|
-
test("rejects unknown top-level key", async () => {
|
|
508
|
-
const tempDir = mkdtempSync(join(tmpdir(), "test-config-"));
|
|
509
|
-
try {
|
|
510
|
-
createTestConfig(tempDir, sampleConfig);
|
|
511
|
-
await expect(cmdConfigSet(tempDir, "unknownKey", "value")).rejects.toThrow(
|
|
512
|
-
/Unknown config key.*unknownKey/,
|
|
513
|
-
);
|
|
514
|
-
} finally {
|
|
515
|
-
rmSync(tempDir, { recursive: true, force: true });
|
|
516
|
-
}
|
|
517
|
-
});
|
|
518
|
-
|
|
519
|
-
test("rejects unknown nested key in providers", async () => {
|
|
292
|
+
describe("cmdConfigSet — LLM keys removed from engine config (issue #143)", () => {
|
|
293
|
+
test("rejects providers as unknown top-level key", async () => {
|
|
520
294
|
const tempDir = mkdtempSync(join(tmpdir(), "test-config-"));
|
|
521
295
|
try {
|
|
522
296
|
createTestConfig(tempDir, sampleConfig);
|
|
523
297
|
await expect(
|
|
524
|
-
cmdConfigSet(tempDir, "providers.
|
|
525
|
-
).rejects.toThrow(/Unknown
|
|
298
|
+
cmdConfigSet(tempDir, "providers.openai.baseUrl", "https://api.openai.com/v1"),
|
|
299
|
+
).rejects.toThrow(/Unknown config key/);
|
|
526
300
|
} finally {
|
|
527
301
|
rmSync(tempDir, { recursive: true, force: true });
|
|
528
302
|
}
|
|
529
303
|
});
|
|
530
304
|
|
|
531
|
-
test("rejects unknown
|
|
305
|
+
test("rejects models as unknown top-level key", async () => {
|
|
532
306
|
const tempDir = mkdtempSync(join(tmpdir(), "test-config-"));
|
|
533
307
|
try {
|
|
534
308
|
createTestConfig(tempDir, sampleConfig);
|
|
535
|
-
await expect(cmdConfigSet(tempDir, "models.default.
|
|
536
|
-
/Unknown
|
|
309
|
+
await expect(cmdConfigSet(tempDir, "models.default.provider", "openai")).rejects.toThrow(
|
|
310
|
+
/Unknown config key/,
|
|
537
311
|
);
|
|
538
312
|
} finally {
|
|
539
313
|
rmSync(tempDir, { recursive: true, force: true });
|
|
540
314
|
}
|
|
541
315
|
});
|
|
542
316
|
|
|
543
|
-
test("rejects unknown
|
|
317
|
+
test("rejects defaultModel as unknown top-level key", async () => {
|
|
544
318
|
const tempDir = mkdtempSync(join(tmpdir(), "test-config-"));
|
|
545
319
|
try {
|
|
546
320
|
createTestConfig(tempDir, sampleConfig);
|
|
547
|
-
await expect(cmdConfigSet(tempDir, "
|
|
548
|
-
/Unknown
|
|
321
|
+
await expect(cmdConfigSet(tempDir, "defaultModel", "default")).rejects.toThrow(
|
|
322
|
+
/Unknown config key/,
|
|
549
323
|
);
|
|
550
324
|
} finally {
|
|
551
325
|
rmSync(tempDir, { recursive: true, force: true });
|
|
552
326
|
}
|
|
553
327
|
});
|
|
554
328
|
|
|
555
|
-
test("rejects
|
|
329
|
+
test("rejects modelOverrides as unknown top-level key", async () => {
|
|
556
330
|
const tempDir = mkdtempSync(join(tmpdir(), "test-config-"));
|
|
557
331
|
try {
|
|
558
332
|
createTestConfig(tempDir, sampleConfig);
|
|
559
|
-
await expect(cmdConfigSet(tempDir, "
|
|
560
|
-
/
|
|
333
|
+
await expect(cmdConfigSet(tempDir, "modelOverrides.extract", "fast")).rejects.toThrow(
|
|
334
|
+
/Unknown config key/,
|
|
561
335
|
);
|
|
562
336
|
} finally {
|
|
563
337
|
rmSync(tempDir, { recursive: true, force: true });
|
|
564
338
|
}
|
|
565
339
|
});
|
|
340
|
+
});
|
|
566
341
|
|
|
567
|
-
|
|
342
|
+
describe("cmdConfigSet validation", () => {
|
|
343
|
+
test("rejects unknown top-level key", async () => {
|
|
568
344
|
const tempDir = mkdtempSync(join(tmpdir(), "test-config-"));
|
|
569
345
|
try {
|
|
570
346
|
createTestConfig(tempDir, sampleConfig);
|
|
571
|
-
await expect(cmdConfigSet(tempDir, "
|
|
572
|
-
/
|
|
347
|
+
await expect(cmdConfigSet(tempDir, "unknownKey", "value")).rejects.toThrow(
|
|
348
|
+
/Unknown config key.*unknownKey/,
|
|
573
349
|
);
|
|
574
350
|
} finally {
|
|
575
351
|
rmSync(tempDir, { recursive: true, force: true });
|
|
576
352
|
}
|
|
577
353
|
});
|
|
578
354
|
|
|
579
|
-
test("rejects
|
|
355
|
+
test("rejects unknown nested key in agents", async () => {
|
|
580
356
|
const tempDir = mkdtempSync(join(tmpdir(), "test-config-"));
|
|
581
357
|
try {
|
|
582
358
|
createTestConfig(tempDir, sampleConfig);
|
|
583
|
-
await expect(cmdConfigSet(tempDir, "
|
|
584
|
-
/
|
|
359
|
+
await expect(cmdConfigSet(tempDir, "agents.hermes.badField", "value")).rejects.toThrow(
|
|
360
|
+
/Unknown field.*badField.*agents/,
|
|
585
361
|
);
|
|
586
362
|
} finally {
|
|
587
363
|
rmSync(tempDir, { recursive: true, force: true });
|
|
588
364
|
}
|
|
589
365
|
});
|
|
590
366
|
|
|
591
|
-
test("rejects
|
|
367
|
+
test("rejects nested path on scalar key (defaultAgent)", async () => {
|
|
592
368
|
const tempDir = mkdtempSync(join(tmpdir(), "test-config-"));
|
|
593
369
|
try {
|
|
594
370
|
createTestConfig(tempDir, sampleConfig);
|
|
595
|
-
await expect(cmdConfigSet(tempDir, "
|
|
596
|
-
/
|
|
371
|
+
await expect(cmdConfigSet(tempDir, "defaultAgent.foo", "value")).rejects.toThrow(
|
|
372
|
+
/defaultAgent.*scalar|Cannot set property/i,
|
|
597
373
|
);
|
|
598
374
|
} finally {
|
|
599
375
|
rmSync(tempDir, { recursive: true, force: true });
|
|
@@ -612,36 +388,6 @@ defaultModel: default
|
|
|
612
388
|
}
|
|
613
389
|
});
|
|
614
390
|
|
|
615
|
-
test("allows valid nested keys in providers", async () => {
|
|
616
|
-
const tempDir = mkdtempSync(join(tmpdir(), "test-config-"));
|
|
617
|
-
try {
|
|
618
|
-
createTestConfig(tempDir, sampleConfig);
|
|
619
|
-
await cmdConfigSet(tempDir, "providers.newprovider.baseUrl", "https://example.com");
|
|
620
|
-
await cmdConfigSet(tempDir, "providers.newprovider.apiKey", "sk-test");
|
|
621
|
-
const baseUrl = await cmdConfigGet(tempDir, "providers.newprovider.baseUrl");
|
|
622
|
-
const apiKey = await cmdConfigGet(tempDir, "providers.newprovider.apiKey");
|
|
623
|
-
expect(baseUrl).toBe("https://example.com");
|
|
624
|
-
expect(apiKey).toBe("sk-test");
|
|
625
|
-
} finally {
|
|
626
|
-
rmSync(tempDir, { recursive: true, force: true });
|
|
627
|
-
}
|
|
628
|
-
});
|
|
629
|
-
|
|
630
|
-
test("allows valid nested keys in models", async () => {
|
|
631
|
-
const tempDir = mkdtempSync(join(tmpdir(), "test-config-"));
|
|
632
|
-
try {
|
|
633
|
-
createTestConfig(tempDir, sampleConfig);
|
|
634
|
-
await cmdConfigSet(tempDir, "models.gpt4.provider", "openai");
|
|
635
|
-
await cmdConfigSet(tempDir, "models.gpt4.name", "gpt-4o");
|
|
636
|
-
const provider = await cmdConfigGet(tempDir, "models.gpt4.provider");
|
|
637
|
-
const name = await cmdConfigGet(tempDir, "models.gpt4.name");
|
|
638
|
-
expect(provider).toBe("openai");
|
|
639
|
-
expect(name).toBe("gpt-4o");
|
|
640
|
-
} finally {
|
|
641
|
-
rmSync(tempDir, { recursive: true, force: true });
|
|
642
|
-
}
|
|
643
|
-
});
|
|
644
|
-
|
|
645
391
|
test("allows valid nested keys in agents", async () => {
|
|
646
392
|
const tempDir = mkdtempSync(join(tmpdir(), "test-config-"));
|
|
647
393
|
try {
|
|
@@ -681,30 +427,6 @@ defaultModel: default
|
|
|
681
427
|
}
|
|
682
428
|
});
|
|
683
429
|
|
|
684
|
-
test("modelOverrides — accepts valid 2-segment path", async () => {
|
|
685
|
-
const tempDir = mkdtempSync(join(tmpdir(), "test-config-"));
|
|
686
|
-
try {
|
|
687
|
-
createTestConfig(tempDir, sampleConfig);
|
|
688
|
-
await cmdConfigSet(tempDir, "modelOverrides.extract", "gpt4");
|
|
689
|
-
const value = await cmdConfigGet(tempDir, "modelOverrides.extract");
|
|
690
|
-
expect(value).toBe("gpt4");
|
|
691
|
-
} finally {
|
|
692
|
-
rmSync(tempDir, { recursive: true, force: true });
|
|
693
|
-
}
|
|
694
|
-
});
|
|
695
|
-
|
|
696
|
-
test("modelOverrides — rejects incomplete path (1 segment only)", async () => {
|
|
697
|
-
const tempDir = mkdtempSync(join(tmpdir(), "test-config-"));
|
|
698
|
-
try {
|
|
699
|
-
createTestConfig(tempDir, sampleConfig);
|
|
700
|
-
await expect(cmdConfigSet(tempDir, "modelOverrides", "gpt4")).rejects.toThrow(
|
|
701
|
-
/incomplete path|must specify a field/i,
|
|
702
|
-
);
|
|
703
|
-
} finally {
|
|
704
|
-
rmSync(tempDir, { recursive: true, force: true });
|
|
705
|
-
}
|
|
706
|
-
});
|
|
707
|
-
|
|
708
430
|
test("rejects unknown top-level key (regression)", async () => {
|
|
709
431
|
const tempDir = mkdtempSync(join(tmpdir(), "test-config-"));
|
|
710
432
|
try {
|
|
@@ -726,15 +448,5 @@ defaultModel: default
|
|
|
726
448
|
);
|
|
727
449
|
expect(configSource).not.toContain("apiKeyEnv");
|
|
728
450
|
});
|
|
729
|
-
|
|
730
|
-
test("config.test.ts has no references to apiKeyEnv (except this test)", () => {
|
|
731
|
-
const testSource = readFileSync(__filename, "utf8");
|
|
732
|
-
// Remove this test block's own mentions before checking
|
|
733
|
-
const withoutThisTest = testSource.replace(
|
|
734
|
-
/describe\("no legacy apiKeyEnv references"[\s\S]*$/,
|
|
735
|
-
"",
|
|
736
|
-
);
|
|
737
|
-
expect(withoutThisTest).not.toContain("apiKeyEnv");
|
|
738
|
-
});
|
|
739
451
|
});
|
|
740
452
|
});
|