chatccc 0.2.223 → 0.2.225
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/config.sample.json +40 -39
- package/package.json +1 -1
- package/src/__tests__/builtin-chat-session.test.ts +33 -0
- package/src/__tests__/builtin-config.test.ts +1 -0
- package/src/__tests__/ccc-adapter.test.ts +20 -0
- package/src/__tests__/config-reload.test.ts +52 -52
- package/src/__tests__/config-sample.test.ts +41 -40
- package/src/__tests__/orchestrator.test.ts +836 -803
- package/src/__tests__/session.test.ts +1183 -1173
- package/src/__tests__/terminal-renderer.test.ts +104 -0
- package/src/__tests__/web-ui.test.ts +3 -0
- package/src/adapters/ccc-adapter.ts +1 -0
- package/src/builtin/cli.ts +63 -17
- package/src/builtin/index.ts +10 -0
- package/src/config.ts +203 -192
- package/src/progress/terminal-renderer.ts +112 -8
- package/src/session.ts +5 -2
- package/src/web-ui.ts +626 -607
package/config.sample.json
CHANGED
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
"appId": "",
|
|
4
4
|
"appSecret": ""
|
|
5
5
|
},
|
|
6
|
-
"platforms": {
|
|
7
|
-
"feishu": { "enabled": true, "platformType": "feishu" },
|
|
8
|
-
"ilink": { "enabled": true, "reuseTokenOnStart": true }
|
|
9
|
-
},
|
|
10
|
-
"webUi": {
|
|
11
|
-
"openOnStart": true
|
|
12
|
-
},
|
|
13
|
-
"chromeDevtools": {
|
|
6
|
+
"platforms": {
|
|
7
|
+
"feishu": { "enabled": true, "platformType": "feishu" },
|
|
8
|
+
"ilink": { "enabled": true, "reuseTokenOnStart": true }
|
|
9
|
+
},
|
|
10
|
+
"webUi": {
|
|
11
|
+
"openOnStart": true
|
|
12
|
+
},
|
|
13
|
+
"chromeDevtools": {
|
|
14
14
|
"enabled": false,
|
|
15
15
|
"port": 15166,
|
|
16
16
|
"chromePath": ""
|
|
@@ -31,19 +31,19 @@
|
|
|
31
31
|
"retentionDays": 7,
|
|
32
32
|
"keepCompleted": false
|
|
33
33
|
},
|
|
34
|
-
"codex": {
|
|
35
|
-
"enabled": false,
|
|
36
|
-
"maxBytesPerTurn": 52428800,
|
|
37
|
-
"retentionDays": 7,
|
|
38
|
-
"keepCompleted": false
|
|
39
|
-
},
|
|
40
|
-
"ccc": {
|
|
41
|
-
"enabled": false,
|
|
42
|
-
"maxBytesPerTurn": 52428800,
|
|
43
|
-
"retentionDays": 7,
|
|
44
|
-
"keepCompleted": false
|
|
45
|
-
}
|
|
46
|
-
},
|
|
34
|
+
"codex": {
|
|
35
|
+
"enabled": false,
|
|
36
|
+
"maxBytesPerTurn": 52428800,
|
|
37
|
+
"retentionDays": 7,
|
|
38
|
+
"keepCompleted": false
|
|
39
|
+
},
|
|
40
|
+
"ccc": {
|
|
41
|
+
"enabled": false,
|
|
42
|
+
"maxBytesPerTurn": 52428800,
|
|
43
|
+
"retentionDays": 7,
|
|
44
|
+
"keepCompleted": false
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
47
|
"claude": {
|
|
48
48
|
"enabled": false,
|
|
49
49
|
"defaultAgent": true,
|
|
@@ -63,21 +63,22 @@
|
|
|
63
63
|
"avatarBatteryMode": "apiPercent",
|
|
64
64
|
"onDemandMonthlyBudget": 1000
|
|
65
65
|
},
|
|
66
|
-
"codex": {
|
|
67
|
-
"enabled": false,
|
|
68
|
-
"defaultAgent": false,
|
|
69
|
-
"path": "",
|
|
70
|
-
"model": "",
|
|
71
|
-
"alternativeModel": "",
|
|
72
|
-
"effort": "",
|
|
73
|
-
"fastMode": false
|
|
74
|
-
},
|
|
75
|
-
"ccc": {
|
|
76
|
-
"enabled": false,
|
|
77
|
-
"defaultAgent": false,
|
|
78
|
-
"DEEPSEEK_API_KEY": "",
|
|
79
|
-
"DEEPSEEK_BASE_URL": "https://api.deepseek.com/v1",
|
|
80
|
-
"model": "deepseek-v4-pro",
|
|
81
|
-
"alternativeModel": ""
|
|
82
|
-
|
|
83
|
-
}
|
|
66
|
+
"codex": {
|
|
67
|
+
"enabled": false,
|
|
68
|
+
"defaultAgent": false,
|
|
69
|
+
"path": "",
|
|
70
|
+
"model": "",
|
|
71
|
+
"alternativeModel": "",
|
|
72
|
+
"effort": "",
|
|
73
|
+
"fastMode": false
|
|
74
|
+
},
|
|
75
|
+
"ccc": {
|
|
76
|
+
"enabled": false,
|
|
77
|
+
"defaultAgent": false,
|
|
78
|
+
"DEEPSEEK_API_KEY": "",
|
|
79
|
+
"DEEPSEEK_BASE_URL": "https://api.deepseek.com/v1",
|
|
80
|
+
"model": "deepseek-v4-pro",
|
|
81
|
+
"alternativeModel": "",
|
|
82
|
+
"effort": ""
|
|
83
|
+
}
|
|
84
|
+
}
|
package/package.json
CHANGED
|
@@ -315,3 +315,36 @@ describe("ChatSession context management", () => {
|
|
|
315
315
|
expect(rawLogCloseMock).toHaveBeenCalledWith({ keep: true });
|
|
316
316
|
});
|
|
317
317
|
});
|
|
318
|
+
|
|
319
|
+
describe("ChatSession effort passthrough", () => {
|
|
320
|
+
it("passes reasoningEffort via providerOptions when effort is configured", async () => {
|
|
321
|
+
const { ChatSession } = await import("../builtin/index.ts");
|
|
322
|
+
const dir = await mkdtemp(join(tmpdir(), "chatccc-session-effort-high-"));
|
|
323
|
+
streamTextMock.mockReturnValueOnce({ textStream: textStream("done") });
|
|
324
|
+
|
|
325
|
+
const session = new ChatSession(
|
|
326
|
+
{ apiKey: "sk-test", effort: "high" },
|
|
327
|
+
{ cwd: dir, sessionId: "effort-high" },
|
|
328
|
+
);
|
|
329
|
+
await collect(session.chat("hi"));
|
|
330
|
+
|
|
331
|
+
expect(streamTextMock).toHaveBeenLastCalledWith(expect.objectContaining({
|
|
332
|
+
providerOptions: { deepseek: { reasoningEffort: "high" } },
|
|
333
|
+
}));
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
it("omits providerOptions when effort is not set", async () => {
|
|
337
|
+
const { ChatSession } = await import("../builtin/index.ts");
|
|
338
|
+
const dir = await mkdtemp(join(tmpdir(), "chatccc-session-effort-none-"));
|
|
339
|
+
streamTextMock.mockReturnValueOnce({ textStream: textStream("done") });
|
|
340
|
+
|
|
341
|
+
const session = new ChatSession(
|
|
342
|
+
{ apiKey: "sk-test", effort: "" },
|
|
343
|
+
{ cwd: dir, sessionId: "effort-none" },
|
|
344
|
+
);
|
|
345
|
+
await collect(session.chat("hi"));
|
|
346
|
+
|
|
347
|
+
const lastCall = streamTextMock.mock.calls.at(-1)?.[0] as Record<string, unknown>;
|
|
348
|
+
expect(lastCall.providerOptions).toBeUndefined();
|
|
349
|
+
});
|
|
350
|
+
});
|
|
@@ -113,4 +113,24 @@ describe("createCccAdapter", () => {
|
|
|
113
113
|
{ type: "assistant", blocks: [], isFinalResponse: true },
|
|
114
114
|
]);
|
|
115
115
|
});
|
|
116
|
+
|
|
117
|
+
it("passes effort into ChatSession so streamText receives reasoningEffort", async () => {
|
|
118
|
+
const { createCccAdapter } = await import("../adapters/ccc-adapter.ts");
|
|
119
|
+
const contextDir = await mkdtemp(join(tmpdir(), "chatccc-ccc-adapter-effort-"));
|
|
120
|
+
const adapter = createCccAdapter({
|
|
121
|
+
apiKey: "sk-test",
|
|
122
|
+
contextDir,
|
|
123
|
+
effort: "xhigh",
|
|
124
|
+
});
|
|
125
|
+
const { sessionId } = await adapter.createSession("F:\\repo");
|
|
126
|
+
streamTextMock.mockReturnValueOnce({ textStream: textStream("ok") });
|
|
127
|
+
|
|
128
|
+
for await (const _message of adapter.prompt(sessionId, "hi", "F:\\repo")) {
|
|
129
|
+
// drain
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
expect(streamTextMock).toHaveBeenLastCalledWith(expect.objectContaining({
|
|
133
|
+
providerOptions: { deepseek: { reasoningEffort: "xhigh" } },
|
|
134
|
+
}));
|
|
135
|
+
});
|
|
116
136
|
});
|
|
@@ -38,20 +38,20 @@ import {
|
|
|
38
38
|
// 原地切换复用同一 server,重新读端口只会引入混乱)
|
|
39
39
|
// ---------------------------------------------------------------------------
|
|
40
40
|
|
|
41
|
-
const baseAppConfig: AppConfig = {
|
|
42
|
-
feishu: { appId: "INITIAL_APP", appSecret: "INITIAL_SECRET" },
|
|
43
|
-
platforms: { feishu: { enabled: true }, ilink: { enabled: true } },
|
|
44
|
-
webUi: { openOnStart: true },
|
|
45
|
-
chromeDevtools: { enabled: false, port: 15166, chromePath: "" },
|
|
41
|
+
const baseAppConfig: AppConfig = {
|
|
42
|
+
feishu: { appId: "INITIAL_APP", appSecret: "INITIAL_SECRET" },
|
|
43
|
+
platforms: { feishu: { enabled: true }, ilink: { enabled: true } },
|
|
44
|
+
webUi: { openOnStart: true },
|
|
45
|
+
chromeDevtools: { enabled: false, port: 15166, chromePath: "" },
|
|
46
46
|
port: 18080,
|
|
47
47
|
gitTimeoutSeconds: 180,
|
|
48
48
|
allowInterrupt: false,
|
|
49
|
-
rawStreamLogs: {
|
|
50
|
-
claude: { enabled: false, maxBytesPerTurn: 52_428_800, retentionDays: 7, keepCompleted: false },
|
|
51
|
-
cursor: { enabled: false, maxBytesPerTurn: 52_428_800, retentionDays: 7, keepCompleted: false },
|
|
52
|
-
codex: { enabled: false, maxBytesPerTurn: 52_428_800, retentionDays: 7, keepCompleted: false },
|
|
53
|
-
ccc: { enabled: false, maxBytesPerTurn: 52_428_800, retentionDays: 7, keepCompleted: false },
|
|
54
|
-
},
|
|
49
|
+
rawStreamLogs: {
|
|
50
|
+
claude: { enabled: false, maxBytesPerTurn: 52_428_800, retentionDays: 7, keepCompleted: false },
|
|
51
|
+
cursor: { enabled: false, maxBytesPerTurn: 52_428_800, retentionDays: 7, keepCompleted: false },
|
|
52
|
+
codex: { enabled: false, maxBytesPerTurn: 52_428_800, retentionDays: 7, keepCompleted: false },
|
|
53
|
+
ccc: { enabled: false, maxBytesPerTurn: 52_428_800, retentionDays: 7, keepCompleted: false },
|
|
54
|
+
},
|
|
55
55
|
claude: {
|
|
56
56
|
enabled: true,
|
|
57
57
|
defaultAgent: true,
|
|
@@ -70,10 +70,10 @@ const baseAppConfig: AppConfig = {
|
|
|
70
70
|
alternativeModel: "initial-cursor-alt-model",
|
|
71
71
|
avatarBatteryMode: "apiPercent",
|
|
72
72
|
onDemandMonthlyBudget: 1000,
|
|
73
|
-
},
|
|
74
|
-
codex: { enabled: true, defaultAgent: false, path: "/initial/codex", model: "initial-codex-model", alternativeModel: "initial-codex-alt-model", effort: "initial-codex-effort", fastMode: false },
|
|
75
|
-
ccc: { enabled: true, defaultAgent: false, DEEPSEEK_API_KEY: "initial-ccc-key", DEEPSEEK_BASE_URL: "https://initial.deepseek.test/v1", model: "initial-ccc-model", alternativeModel: "initial-ccc-alt-model" },
|
|
76
|
-
};
|
|
73
|
+
},
|
|
74
|
+
codex: { enabled: true, defaultAgent: false, path: "/initial/codex", model: "initial-codex-model", alternativeModel: "initial-codex-alt-model", effort: "initial-codex-effort", fastMode: false },
|
|
75
|
+
ccc: { enabled: true, defaultAgent: false, DEEPSEEK_API_KEY: "initial-ccc-key", DEEPSEEK_BASE_URL: "https://initial.deepseek.test/v1", model: "initial-ccc-model", alternativeModel: "initial-ccc-alt-model", effort: "initial-ccc-effort" },
|
|
76
|
+
};
|
|
77
77
|
|
|
78
78
|
// 把 module 状态抢救快照:每个 it 跑前重置回这个状态,避免污染相邻测试。
|
|
79
79
|
// 不直接用启动时的 config 引用做 snapshot——它可能已经被前一个 it 改写。
|
|
@@ -177,18 +177,18 @@ describe("applyLoadedConfig — 刷新 export let 常量", () => {
|
|
|
177
177
|
expect(CURSOR_AGENT_COMMAND).toBe("C:/custom/cursor.exe");
|
|
178
178
|
});
|
|
179
179
|
|
|
180
|
-
it("/model 候选列表包含 Cursor/Codex/CCC 的单个备选模型并保持主模型优先", () => {
|
|
180
|
+
it("/model 候选列表包含 Cursor/Codex/CCC 的单个备选模型并保持主模型优先", () => {
|
|
181
181
|
const cfg = structuredClone(baseAppConfig);
|
|
182
182
|
cfg.cursor.model = "cursor-main";
|
|
183
183
|
cfg.cursor.alternativeModel = "cursor-alt";
|
|
184
184
|
cfg.codex.model = "codex-main";
|
|
185
|
-
cfg.codex.alternativeModel = "codex-main";
|
|
186
|
-
cfg.ccc.model = "ccc-main";
|
|
187
|
-
cfg.ccc.alternativeModel = "ccc-alt";
|
|
185
|
+
cfg.codex.alternativeModel = "codex-main";
|
|
186
|
+
cfg.ccc.model = "ccc-main";
|
|
187
|
+
cfg.ccc.alternativeModel = "ccc-alt";
|
|
188
188
|
|
|
189
189
|
expect(getAllModelsForTool("cursor", cfg)).toEqual(["cursor-main", "cursor-alt"]);
|
|
190
|
-
expect(getAllModelsForTool("codex", cfg)).toEqual(["codex-main"]);
|
|
191
|
-
expect(getAllModelsForTool("ccc", cfg)).toEqual(["ccc-main", "ccc-alt"]);
|
|
190
|
+
expect(getAllModelsForTool("codex", cfg)).toEqual(["codex-main"]);
|
|
191
|
+
expect(getAllModelsForTool("ccc", cfg)).toEqual(["ccc-main", "ccc-alt"]);
|
|
192
192
|
});
|
|
193
193
|
|
|
194
194
|
it("不修改 CHATCCC_PORT(端口在 setup 切换时必须保持不变)", () => {
|
|
@@ -210,18 +210,18 @@ describe("applyLoadedConfig — config 对象引用契约", () => {
|
|
|
210
210
|
applyLoadedConfig({
|
|
211
211
|
...structuredClone(baseAppConfig),
|
|
212
212
|
feishu: { appId: "REF_TEST_APP", appSecret: "REF_TEST_SECRET" },
|
|
213
|
-
codex: { enabled: true, defaultAgent: false, path: "/refresh/codex", model: "fresh-model", alternativeModel: "", effort: "low", fastMode: true },
|
|
213
|
+
codex: { enabled: true, defaultAgent: false, path: "/refresh/codex", model: "fresh-model", alternativeModel: "", effort: "low", fastMode: true },
|
|
214
214
|
});
|
|
215
215
|
|
|
216
216
|
// 必须是同一个引用:codex-adapter 等下游模块"直接 import config",
|
|
217
217
|
// 替换引用会破坏它们对 config.codex.* 的访问。
|
|
218
218
|
expect(config).toBe(refBefore);
|
|
219
|
-
expect(config.feishu.appId).toBe("REF_TEST_APP");
|
|
220
|
-
expect(config.codex.path).toBe("/refresh/codex");
|
|
221
|
-
expect(config.codex.fastMode).toBe(true);
|
|
222
|
-
});
|
|
219
|
+
expect(config.feishu.appId).toBe("REF_TEST_APP");
|
|
220
|
+
expect(config.codex.path).toBe("/refresh/codex");
|
|
221
|
+
expect(config.codex.fastMode).toBe(true);
|
|
222
|
+
});
|
|
223
223
|
|
|
224
|
-
it("刷新 chromeDevtools 配置但保持 config 引用", () => {
|
|
224
|
+
it("刷新 chromeDevtools 配置但保持 config 引用", () => {
|
|
225
225
|
const refBefore = config;
|
|
226
226
|
|
|
227
227
|
applyLoadedConfig({
|
|
@@ -231,19 +231,19 @@ describe("applyLoadedConfig — config 对象引用契约", () => {
|
|
|
231
231
|
|
|
232
232
|
expect(config).toBe(refBefore);
|
|
233
233
|
expect(config.chromeDevtools).toEqual({ enabled: true, port: 15166, chromePath: "C:/Chrome/chrome.exe" });
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
it("刷新 webUi 启动开关但保持 config 引用", () => {
|
|
237
|
-
const refBefore = config;
|
|
238
|
-
|
|
239
|
-
applyLoadedConfig({
|
|
240
|
-
...structuredClone(baseAppConfig),
|
|
241
|
-
webUi: { openOnStart: false },
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
expect(config).toBe(refBefore);
|
|
245
|
-
expect(config.webUi).toEqual({ openOnStart: false });
|
|
246
|
-
});
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it("刷新 webUi 启动开关但保持 config 引用", () => {
|
|
237
|
+
const refBefore = config;
|
|
238
|
+
|
|
239
|
+
applyLoadedConfig({
|
|
240
|
+
...structuredClone(baseAppConfig),
|
|
241
|
+
webUi: { openOnStart: false },
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
expect(config).toBe(refBefore);
|
|
245
|
+
expect(config.webUi).toEqual({ openOnStart: false });
|
|
246
|
+
});
|
|
247
247
|
|
|
248
248
|
it("空 feishu 凭证也能正确刷入(向导回滚到空的反向场景)", () => {
|
|
249
249
|
applyLoadedConfig({
|
|
@@ -265,20 +265,20 @@ describe("resolveDefaultAgentTool", () => {
|
|
|
265
265
|
expect(resolveDefaultAgentTool(cfg)).toBe("cursor");
|
|
266
266
|
});
|
|
267
267
|
|
|
268
|
-
it("defaultAgent 指向未启用 Agent 时回退到第一个已启用 Agent", () => {
|
|
268
|
+
it("defaultAgent 指向未启用 Agent 时回退到第一个已启用 Agent", () => {
|
|
269
269
|
const cfg = structuredClone(baseAppConfig);
|
|
270
270
|
cfg.claude.enabled = false;
|
|
271
271
|
cfg.claude.defaultAgent = true;
|
|
272
272
|
cfg.cursor.defaultAgent = false;
|
|
273
273
|
|
|
274
|
-
expect(resolveDefaultAgentTool(cfg)).toBe("cursor");
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
it("支持把已启用的 CCC Agent 设为默认 Agent", () => {
|
|
278
|
-
const cfg = structuredClone(baseAppConfig);
|
|
279
|
-
for (const tool of ["claude", "cursor", "codex"] as const) cfg[tool].defaultAgent = false;
|
|
280
|
-
cfg.ccc.defaultAgent = true;
|
|
281
|
-
|
|
282
|
-
expect(resolveDefaultAgentTool(cfg)).toBe("ccc");
|
|
283
|
-
});
|
|
284
|
-
});
|
|
274
|
+
expect(resolveDefaultAgentTool(cfg)).toBe("cursor");
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
it("支持把已启用的 CCC Agent 设为默认 Agent", () => {
|
|
278
|
+
const cfg = structuredClone(baseAppConfig);
|
|
279
|
+
for (const tool of ["claude", "cursor", "codex"] as const) cfg[tool].defaultAgent = false;
|
|
280
|
+
cfg.ccc.defaultAgent = true;
|
|
281
|
+
|
|
282
|
+
expect(resolveDefaultAgentTool(cfg)).toBe("ccc");
|
|
283
|
+
});
|
|
284
|
+
});
|
|
@@ -28,34 +28,35 @@ describe("config.sample.json", () => {
|
|
|
28
28
|
expect(sample.claude?.subagentModel).toBe("");
|
|
29
29
|
});
|
|
30
30
|
|
|
31
|
-
it("leaves alternative models empty and Codex Fast mode off by default", () => {
|
|
32
|
-
const configSamplePath = join(process.cwd(), "config.sample.json");
|
|
33
|
-
const sample = JSON.parse(readFileSync(configSamplePath, "utf8")) as {
|
|
34
|
-
cursor?: { alternativeModel?: unknown };
|
|
35
|
-
codex?: { alternativeModel?: unknown; fastMode?: unknown };
|
|
36
|
-
ccc?: { alternativeModel?: unknown };
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
expect(sample.cursor?.alternativeModel).toBe("");
|
|
40
|
-
expect(sample.codex?.alternativeModel).toBe("");
|
|
41
|
-
expect(sample.ccc?.alternativeModel).toBe("");
|
|
42
|
-
expect(sample.codex?.fastMode).toBe(false);
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it("sets ccc agent DeepSeek defaults in the sample config", () => {
|
|
46
|
-
const configSamplePath = join(process.cwd(), "config.sample.json");
|
|
47
|
-
const sample = JSON.parse(readFileSync(configSamplePath, "utf8")) as {
|
|
48
|
-
ccc?: { enabled?: unknown; defaultAgent?: unknown; DEEPSEEK_API_KEY?: unknown; DEEPSEEK_BASE_URL?: unknown; model?: unknown };
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
expect(sample.ccc?.enabled).toBe(false);
|
|
52
|
-
expect(sample.ccc?.defaultAgent).toBe(false);
|
|
53
|
-
expect(sample.ccc?.DEEPSEEK_API_KEY).toBe("");
|
|
54
|
-
expect(sample.ccc?.DEEPSEEK_BASE_URL).toBe("https://api.deepseek.com/v1");
|
|
55
|
-
expect(sample.ccc?.model).toBe("deepseek-v4-pro");
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
31
|
+
it("leaves alternative models empty and Codex Fast mode off by default", () => {
|
|
32
|
+
const configSamplePath = join(process.cwd(), "config.sample.json");
|
|
33
|
+
const sample = JSON.parse(readFileSync(configSamplePath, "utf8")) as {
|
|
34
|
+
cursor?: { alternativeModel?: unknown };
|
|
35
|
+
codex?: { alternativeModel?: unknown; fastMode?: unknown };
|
|
36
|
+
ccc?: { alternativeModel?: unknown };
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
expect(sample.cursor?.alternativeModel).toBe("");
|
|
40
|
+
expect(sample.codex?.alternativeModel).toBe("");
|
|
41
|
+
expect(sample.ccc?.alternativeModel).toBe("");
|
|
42
|
+
expect(sample.codex?.fastMode).toBe(false);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("sets ccc agent DeepSeek defaults in the sample config", () => {
|
|
46
|
+
const configSamplePath = join(process.cwd(), "config.sample.json");
|
|
47
|
+
const sample = JSON.parse(readFileSync(configSamplePath, "utf8")) as {
|
|
48
|
+
ccc?: { enabled?: unknown; defaultAgent?: unknown; DEEPSEEK_API_KEY?: unknown; DEEPSEEK_BASE_URL?: unknown; model?: unknown; effort?: unknown };
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
expect(sample.ccc?.enabled).toBe(false);
|
|
52
|
+
expect(sample.ccc?.defaultAgent).toBe(false);
|
|
53
|
+
expect(sample.ccc?.DEEPSEEK_API_KEY).toBe("");
|
|
54
|
+
expect(sample.ccc?.DEEPSEEK_BASE_URL).toBe("https://api.deepseek.com/v1");
|
|
55
|
+
expect(sample.ccc?.model).toBe("deepseek-v4-pro");
|
|
56
|
+
expect(sample.ccc?.effort).toBe("");
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("keeps Chrome CDP guard disabled by default with port 15166", () => {
|
|
59
60
|
const configSamplePath = join(process.cwd(), "config.sample.json");
|
|
60
61
|
const sample = JSON.parse(readFileSync(configSamplePath, "utf8")) as {
|
|
61
62
|
chromeDevtools?: { enabled?: unknown; port?: unknown; chromePath?: unknown };
|
|
@@ -64,16 +65,16 @@ describe("config.sample.json", () => {
|
|
|
64
65
|
expect(sample.chromeDevtools?.enabled).toBe(false);
|
|
65
66
|
expect(sample.chromeDevtools?.port).toBe(15166);
|
|
66
67
|
expect(sample.chromeDevtools?.chromePath).toBe("");
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it("opens the Web UI on direct startup by default", () => {
|
|
70
|
-
const configSamplePath = join(process.cwd(), "config.sample.json");
|
|
71
|
-
const sample = JSON.parse(readFileSync(configSamplePath, "utf8")) as {
|
|
72
|
-
webUi?: { openOnStart?: unknown };
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
expect(sample.webUi?.openOnStart).toBe(true);
|
|
76
|
-
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("opens the Web UI on direct startup by default", () => {
|
|
71
|
+
const configSamplePath = join(process.cwd(), "config.sample.json");
|
|
72
|
+
const sample = JSON.parse(readFileSync(configSamplePath, "utf8")) as {
|
|
73
|
+
webUi?: { openOnStart?: unknown };
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
expect(sample.webUi?.openOnStart).toBe(true);
|
|
77
|
+
});
|
|
77
78
|
|
|
78
79
|
it("keeps raw stream logs disabled by default for every agent", () => {
|
|
79
80
|
const configSamplePath = join(process.cwd(), "config.sample.json");
|
|
@@ -86,11 +87,11 @@ describe("config.sample.json", () => {
|
|
|
86
87
|
}>;
|
|
87
88
|
};
|
|
88
89
|
|
|
89
|
-
for (const tool of ["claude", "cursor", "codex", "ccc"]) {
|
|
90
|
+
for (const tool of ["claude", "cursor", "codex", "ccc"]) {
|
|
90
91
|
expect(sample.rawStreamLogs?.[tool]?.enabled).toBe(false);
|
|
91
92
|
expect(sample.rawStreamLogs?.[tool]?.maxBytesPerTurn).toBe(52_428_800);
|
|
92
93
|
expect(sample.rawStreamLogs?.[tool]?.retentionDays).toBe(7);
|
|
93
94
|
expect(sample.rawStreamLogs?.[tool]?.keepCompleted).toBe(false);
|
|
94
95
|
}
|
|
95
96
|
});
|
|
96
|
-
});
|
|
97
|
+
});
|