chatccc 0.2.229 → 0.2.231
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/.agents/skills/create-chatccc-feishu-app/SKILL.md +85 -85
- package/.claude/skills/create-chatccc-feishu-app/SKILL.md +85 -85
- package/.cursor/skills/create-chatccc-feishu-app/SKILL.md +85 -85
- package/README.md +90 -90
- package/agent-prompts/claude_specific.md +45 -45
- package/agent-prompts/codex_specific.md +2 -2
- package/agent-prompts/cursor_specific.md +13 -13
- package/bin/cccagent.mjs +17 -17
- package/im-skills/feishu-skill/receive-send-file.md +63 -63
- package/im-skills/feishu-skill/receive-send-image.md +24 -24
- package/im-skills/feishu-skill/skill.md +3 -3
- package/im-skills/wechat-file-skill/receive-send-file.md +38 -38
- package/im-skills/wechat-file-skill/send-file.mjs +83 -83
- package/im-skills/wechat-file-skill/skill.md +10 -10
- package/im-skills/wechat-image-skill/skill.md +10 -10
- package/im-skills/wechat-video-skill/receive-send-video.md +38 -38
- package/im-skills/wechat-video-skill/send-video.mjs +79 -79
- package/im-skills/wechat-video-skill/skill.md +10 -10
- package/package.json +1 -1
- package/scripts/postinstall-sharp-check.mjs +58 -58
- package/src/__tests__/agent-activity.test.ts +86 -76
- package/src/__tests__/agent-delegate-task-rpc.test.ts +165 -165
- package/src/__tests__/agent-reload-config-rpc.test.ts +99 -99
- package/src/__tests__/builtin-chat-session.test.ts +65 -1
- package/src/__tests__/builtin-cli-json.test.ts +39 -39
- package/src/__tests__/builtin-config.test.ts +26 -26
- package/src/__tests__/builtin-context.test.ts +202 -163
- package/src/__tests__/builtin-permissions.test.ts +211 -211
- package/src/__tests__/builtin-session-select.test.ts +116 -116
- package/src/__tests__/builtin-sigint.test.ts +56 -56
- package/src/__tests__/builtin-web-tools.test.ts +220 -220
- package/src/__tests__/card-action-routing.test.ts +18 -18
- package/src/__tests__/cardkit.test.ts +60 -60
- package/src/__tests__/ccc-adapter.test.ts +170 -136
- package/src/__tests__/chatgpt-subscription-rpc.test.ts +89 -89
- package/src/__tests__/chatgpt-subscription.test.ts +135 -135
- package/src/__tests__/chrome-devtools-guard.test.ts +165 -165
- package/src/__tests__/claude-adapter.test.ts +614 -614
- package/src/__tests__/claude-raw-stream-log.test.ts +87 -87
- package/src/__tests__/codex-adapter.test.ts +58 -58
- package/src/__tests__/codex-raw-stream-log.test.ts +170 -170
- package/src/__tests__/codex-reset-actions.test.ts +146 -146
- package/src/__tests__/cursor-adapter.test.ts +268 -268
- package/src/__tests__/feishu-api.test.ts +60 -60
- package/src/__tests__/feishu-avatar.test.ts +164 -164
- package/src/__tests__/feishu-message-ingress.test.ts +138 -138
- package/src/__tests__/feishu-platform.test.ts +22 -22
- package/src/__tests__/format-message.test.ts +47 -47
- package/src/__tests__/jsonl-stream.test.ts +79 -79
- package/src/__tests__/package-files.test.ts +24 -24
- package/src/__tests__/privacy.test.ts +198 -198
- package/src/__tests__/progress-reducer.test.ts +121 -110
- package/src/__tests__/raw-stream-log.test.ts +106 -106
- package/src/__tests__/response-stall.test.ts +49 -49
- package/src/__tests__/session.test.ts +174 -16
- package/src/__tests__/shared-prefix.test.ts +36 -36
- package/src/__tests__/sim-platform.test.ts +16 -16
- package/src/__tests__/startup-lifecycle.test.ts +231 -231
- package/src/__tests__/stop-session.test.ts +34 -34
- package/src/__tests__/stream-state.test.ts +42 -42
- package/src/__tests__/terminal-error.test.ts +54 -0
- package/src/__tests__/terminal-renderer.test.ts +247 -247
- package/src/__tests__/update-command-guard.test.ts +144 -144
- package/src/__tests__/web-ui.test.ts +326 -326
- package/src/adapters/adapter-interface.ts +24 -18
- package/src/adapters/ccc-adapter.ts +141 -131
- package/src/adapters/claude-adapter.ts +620 -620
- package/src/adapters/claude-session-meta-store.ts +120 -120
- package/src/adapters/codex-adapter.ts +426 -426
- package/src/adapters/cursor-adapter.ts +681 -681
- package/src/adapters/jsonl-stream.ts +157 -157
- package/src/adapters/raw-stream-log.ts +124 -124
- package/src/adapters/resource-monitor.ts +140 -140
- package/src/agent-activity.ts +175 -170
- package/src/agent-delegate-task-rpc.ts +153 -153
- package/src/agent-delegate-task.ts +91 -91
- package/src/agent-reload-config-rpc.ts +34 -34
- package/src/agent-stop-stuck.ts +129 -129
- package/src/builtin/cli.ts +9 -2
- package/src/builtin/config.ts +84 -84
- package/src/builtin/context.ts +62 -4
- package/src/builtin/file-log.ts +38 -38
- package/src/builtin/index.ts +83 -19
- package/src/builtin/proc-tree-kill.ts +61 -61
- package/src/builtin/progress/cards-helpers.ts +76 -76
- package/src/builtin/progress/reducer.ts +113 -108
- package/src/builtin/progress/terminal-renderer.ts +294 -294
- package/src/builtin/progress/view.ts +77 -77
- package/src/builtin/raw-stream-log.ts +124 -124
- package/src/builtin/session-select.ts +48 -48
- package/src/builtin/sigint.ts +50 -50
- package/src/builtin/web-tools.ts +313 -313
- package/src/card-action-routing.ts +14 -14
- package/src/chatgpt-subscription-rpc.ts +27 -27
- package/src/chatgpt-subscription.ts +299 -299
- package/src/chrome-devtools-guard.ts +318 -318
- package/src/codex-reset-actions.ts +184 -184
- package/src/feishu-api.ts +193 -193
- package/src/feishu-message-ingress.ts +195 -195
- package/src/feishu-platform.ts +20 -20
- package/src/format-message.ts +293 -293
- package/src/litellm-proxy.ts +374 -374
- package/src/orchestrator.ts +8 -3
- package/src/platform-adapter.ts +6 -6
- package/src/privacy.ts +118 -118
- package/src/progress/reducer.ts +113 -108
- package/src/progress/terminal-renderer.ts +294 -294
- package/src/progress/view.ts +77 -77
- package/src/response-stall.ts +28 -28
- package/src/runtime-reload.ts +34 -34
- package/src/session-chat-binding.ts +82 -82
- package/src/session-name.ts +8 -8
- package/src/session.ts +149 -26
- package/src/shared-prefix.ts +29 -29
- package/src/sim-platform.ts +20 -20
- package/src/startup-lifecycle.ts +250 -250
- package/src/stream-state.ts +21 -18
- package/src/terminal-error.ts +129 -0
- package/src/turn-cards.ts +117 -117
- package/src/update-command-guard.ts +165 -165
|
@@ -1,99 +1,99 @@
|
|
|
1
|
-
import { Readable } from "node:stream";
|
|
2
|
-
import { describe, expect, it, vi } from "vitest";
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
AGENT_RELOAD_CONFIG_PATH,
|
|
6
|
-
handleAgentReloadConfigRequest,
|
|
7
|
-
} from "../agent-reload-config-rpc.ts";
|
|
8
|
-
|
|
9
|
-
function request(path = AGENT_RELOAD_CONFIG_PATH, method = "POST"): Readable & {
|
|
10
|
-
url?: string;
|
|
11
|
-
method?: string;
|
|
12
|
-
headers: Record<string, string>;
|
|
13
|
-
} {
|
|
14
|
-
const req = Readable.from([]) as Readable & {
|
|
15
|
-
url?: string;
|
|
16
|
-
method?: string;
|
|
17
|
-
headers: Record<string, string>;
|
|
18
|
-
};
|
|
19
|
-
req.url = path;
|
|
20
|
-
req.method = method;
|
|
21
|
-
req.headers = {};
|
|
22
|
-
return req;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function response() {
|
|
26
|
-
const res = {
|
|
27
|
-
statusCode: 0,
|
|
28
|
-
headers: {} as Record<string, string>,
|
|
29
|
-
body: "",
|
|
30
|
-
writeHead(status: number, headers: Record<string, string>) {
|
|
31
|
-
this.statusCode = status;
|
|
32
|
-
this.headers = headers;
|
|
33
|
-
return this;
|
|
34
|
-
},
|
|
35
|
-
end(chunk?: string) {
|
|
36
|
-
this.body += chunk ?? "";
|
|
37
|
-
return this;
|
|
38
|
-
},
|
|
39
|
-
};
|
|
40
|
-
return res;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
describe("agent reload config RPC", () => {
|
|
44
|
-
it("reloads runtime config for POST requests", async () => {
|
|
45
|
-
const reload = vi.fn(async () => ({
|
|
46
|
-
configPath: "C:\\Users\\me\\.chatccc\\config.json",
|
|
47
|
-
defaultAgent: "codex" as const,
|
|
48
|
-
reloadedAt: "2026-07-02T05:00:00.000Z",
|
|
49
|
-
}));
|
|
50
|
-
const res = response();
|
|
51
|
-
|
|
52
|
-
await expect(handleAgentReloadConfigRequest(request() as never, res as never, reload)).resolves.toBe(true);
|
|
53
|
-
|
|
54
|
-
expect(reload).toHaveBeenCalledWith("agent-reload-api");
|
|
55
|
-
expect(res.statusCode).toBe(200);
|
|
56
|
-
expect(JSON.parse(res.body)).toEqual({
|
|
57
|
-
ok: true,
|
|
58
|
-
configPath: "C:\\Users\\me\\.chatccc\\config.json",
|
|
59
|
-
defaultAgent: "codex",
|
|
60
|
-
reloadedAt: "2026-07-02T05:00:00.000Z",
|
|
61
|
-
});
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it("rejects non-POST requests without reloading", async () => {
|
|
65
|
-
const reload = vi.fn();
|
|
66
|
-
const res = response();
|
|
67
|
-
|
|
68
|
-
await expect(handleAgentReloadConfigRequest(request(AGENT_RELOAD_CONFIG_PATH, "GET") as never, res as never, reload)).resolves.toBe(true);
|
|
69
|
-
|
|
70
|
-
expect(reload).not.toHaveBeenCalled();
|
|
71
|
-
expect(res.statusCode).toBe(405);
|
|
72
|
-
expect(JSON.parse(res.body)).toMatchObject({ ok: false, error: "Method not allowed" });
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it("does not handle other paths", async () => {
|
|
76
|
-
const res = response();
|
|
77
|
-
|
|
78
|
-
await expect(handleAgentReloadConfigRequest(request("/api/other") as never, res as never, vi.fn())).resolves.toBe(false);
|
|
79
|
-
|
|
80
|
-
expect(res.body).toBe("");
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it("returns a visible error when reload fails", async () => {
|
|
84
|
-
const res = response();
|
|
85
|
-
|
|
86
|
-
await expect(
|
|
87
|
-
handleAgentReloadConfigRequest(
|
|
88
|
-
request() as never,
|
|
89
|
-
res as never,
|
|
90
|
-
vi.fn(async () => {
|
|
91
|
-
throw new Error("bad config");
|
|
92
|
-
}),
|
|
93
|
-
),
|
|
94
|
-
).resolves.toBe(true);
|
|
95
|
-
|
|
96
|
-
expect(res.statusCode).toBe(500);
|
|
97
|
-
expect(JSON.parse(res.body)).toEqual({ ok: false, error: "bad config" });
|
|
98
|
-
});
|
|
99
|
-
});
|
|
1
|
+
import { Readable } from "node:stream";
|
|
2
|
+
import { describe, expect, it, vi } from "vitest";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
AGENT_RELOAD_CONFIG_PATH,
|
|
6
|
+
handleAgentReloadConfigRequest,
|
|
7
|
+
} from "../agent-reload-config-rpc.ts";
|
|
8
|
+
|
|
9
|
+
function request(path = AGENT_RELOAD_CONFIG_PATH, method = "POST"): Readable & {
|
|
10
|
+
url?: string;
|
|
11
|
+
method?: string;
|
|
12
|
+
headers: Record<string, string>;
|
|
13
|
+
} {
|
|
14
|
+
const req = Readable.from([]) as Readable & {
|
|
15
|
+
url?: string;
|
|
16
|
+
method?: string;
|
|
17
|
+
headers: Record<string, string>;
|
|
18
|
+
};
|
|
19
|
+
req.url = path;
|
|
20
|
+
req.method = method;
|
|
21
|
+
req.headers = {};
|
|
22
|
+
return req;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function response() {
|
|
26
|
+
const res = {
|
|
27
|
+
statusCode: 0,
|
|
28
|
+
headers: {} as Record<string, string>,
|
|
29
|
+
body: "",
|
|
30
|
+
writeHead(status: number, headers: Record<string, string>) {
|
|
31
|
+
this.statusCode = status;
|
|
32
|
+
this.headers = headers;
|
|
33
|
+
return this;
|
|
34
|
+
},
|
|
35
|
+
end(chunk?: string) {
|
|
36
|
+
this.body += chunk ?? "";
|
|
37
|
+
return this;
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
return res;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
describe("agent reload config RPC", () => {
|
|
44
|
+
it("reloads runtime config for POST requests", async () => {
|
|
45
|
+
const reload = vi.fn(async () => ({
|
|
46
|
+
configPath: "C:\\Users\\me\\.chatccc\\config.json",
|
|
47
|
+
defaultAgent: "codex" as const,
|
|
48
|
+
reloadedAt: "2026-07-02T05:00:00.000Z",
|
|
49
|
+
}));
|
|
50
|
+
const res = response();
|
|
51
|
+
|
|
52
|
+
await expect(handleAgentReloadConfigRequest(request() as never, res as never, reload)).resolves.toBe(true);
|
|
53
|
+
|
|
54
|
+
expect(reload).toHaveBeenCalledWith("agent-reload-api");
|
|
55
|
+
expect(res.statusCode).toBe(200);
|
|
56
|
+
expect(JSON.parse(res.body)).toEqual({
|
|
57
|
+
ok: true,
|
|
58
|
+
configPath: "C:\\Users\\me\\.chatccc\\config.json",
|
|
59
|
+
defaultAgent: "codex",
|
|
60
|
+
reloadedAt: "2026-07-02T05:00:00.000Z",
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("rejects non-POST requests without reloading", async () => {
|
|
65
|
+
const reload = vi.fn();
|
|
66
|
+
const res = response();
|
|
67
|
+
|
|
68
|
+
await expect(handleAgentReloadConfigRequest(request(AGENT_RELOAD_CONFIG_PATH, "GET") as never, res as never, reload)).resolves.toBe(true);
|
|
69
|
+
|
|
70
|
+
expect(reload).not.toHaveBeenCalled();
|
|
71
|
+
expect(res.statusCode).toBe(405);
|
|
72
|
+
expect(JSON.parse(res.body)).toMatchObject({ ok: false, error: "Method not allowed" });
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("does not handle other paths", async () => {
|
|
76
|
+
const res = response();
|
|
77
|
+
|
|
78
|
+
await expect(handleAgentReloadConfigRequest(request("/api/other") as never, res as never, vi.fn())).resolves.toBe(false);
|
|
79
|
+
|
|
80
|
+
expect(res.body).toBe("");
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("returns a visible error when reload fails", async () => {
|
|
84
|
+
const res = response();
|
|
85
|
+
|
|
86
|
+
await expect(
|
|
87
|
+
handleAgentReloadConfigRequest(
|
|
88
|
+
request() as never,
|
|
89
|
+
res as never,
|
|
90
|
+
vi.fn(async () => {
|
|
91
|
+
throw new Error("bad config");
|
|
92
|
+
}),
|
|
93
|
+
),
|
|
94
|
+
).resolves.toBe(true);
|
|
95
|
+
|
|
96
|
+
expect(res.statusCode).toBe(500);
|
|
97
|
+
expect(JSON.parse(res.body)).toEqual({ ok: false, error: "bad config" });
|
|
98
|
+
});
|
|
99
|
+
});
|
|
@@ -52,6 +52,7 @@ afterEach(() => {
|
|
|
52
52
|
rawLogWriteLineMock.mockReset();
|
|
53
53
|
rawLogCloseMock.mockReset();
|
|
54
54
|
config.rawStreamLogs = structuredClone(originalRawStreamLogs);
|
|
55
|
+
vi.useRealTimers();
|
|
55
56
|
});
|
|
56
57
|
|
|
57
58
|
describe("ChatSession context management", () => {
|
|
@@ -223,10 +224,49 @@ describe("ChatSession context management", () => {
|
|
|
223
224
|
expect.objectContaining({ role: "user", content: "new question" }),
|
|
224
225
|
]),
|
|
225
226
|
}));
|
|
226
|
-
expect(events
|
|
227
|
+
expect(events.slice(0, 3)).toEqual([
|
|
228
|
+
{ type: "status", phase: "compacting" },
|
|
229
|
+
{ type: "compact", compactedMessages: 2 },
|
|
230
|
+
{ type: "status", phase: "generating" },
|
|
231
|
+
]);
|
|
227
232
|
expect(restored.history.map((m) => m.content).join("\n")).toContain("new answer");
|
|
228
233
|
});
|
|
229
234
|
|
|
235
|
+
it("times out context compaction independently before reply generation", async () => {
|
|
236
|
+
vi.useFakeTimers();
|
|
237
|
+
const { ChatSession } = await import("../builtin/index.ts");
|
|
238
|
+
const dir = await mkdtemp(join(tmpdir(), "deepccc-session-compaction-timeout-"));
|
|
239
|
+
const seed = new ChatSession(
|
|
240
|
+
{ apiKey: "sk-test" },
|
|
241
|
+
{ persist: true, contextDir: dir, sessionId: "timeout", compactAtTokens: 10_000 },
|
|
242
|
+
);
|
|
243
|
+
streamTextMock.mockReturnValueOnce({ textStream: textStream("old answer") });
|
|
244
|
+
await collect(seed.chat("old question"));
|
|
245
|
+
|
|
246
|
+
generateTextMock.mockImplementationOnce(({ abortSignal }: { abortSignal: AbortSignal }) =>
|
|
247
|
+
new Promise((_resolve, reject) => {
|
|
248
|
+
abortSignal.addEventListener("abort", () => reject(abortSignal.reason), { once: true });
|
|
249
|
+
}));
|
|
250
|
+
const restored = new ChatSession(
|
|
251
|
+
{ apiKey: "sk-test" },
|
|
252
|
+
{
|
|
253
|
+
persist: true,
|
|
254
|
+
contextDir: dir,
|
|
255
|
+
sessionId: "timeout",
|
|
256
|
+
compactAtTokens: 1,
|
|
257
|
+
keepRecentMessages: 1,
|
|
258
|
+
compactionTimeoutMs: 100,
|
|
259
|
+
},
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
const result = collect(restored.chat("new question"));
|
|
263
|
+
const timeoutAssertion = expect(result).rejects.toThrow("Context compaction timed out");
|
|
264
|
+
await vi.waitFor(() => expect(generateTextMock).toHaveBeenCalledOnce());
|
|
265
|
+
await vi.advanceTimersByTimeAsync(101);
|
|
266
|
+
await timeoutAssertion;
|
|
267
|
+
expect(streamTextMock).toHaveBeenCalledTimes(1);
|
|
268
|
+
});
|
|
269
|
+
|
|
230
270
|
it("streams tool calls and tool results from fullStream", async () => {
|
|
231
271
|
const { ChatSession } = await import("../builtin/index.ts");
|
|
232
272
|
const dir = await mkdtemp(join(tmpdir(), "deepccc-session-tools-"));
|
|
@@ -265,6 +305,30 @@ describe("ChatSession context management", () => {
|
|
|
265
305
|
expect(events).toContainEqual({ type: "text", text: "done", accumulated: "done" });
|
|
266
306
|
});
|
|
267
307
|
|
|
308
|
+
it("caps the total persisted tool transcript for a turn", async () => {
|
|
309
|
+
const { ChatSession } = await import("../builtin/index.ts");
|
|
310
|
+
const dir = await mkdtemp(join(tmpdir(), "deepccc-session-tool-cap-"));
|
|
311
|
+
const session = new ChatSession(
|
|
312
|
+
{ apiKey: "sk-test" },
|
|
313
|
+
{ persist: true, contextDir: dir, sessionId: "tool-cap" },
|
|
314
|
+
);
|
|
315
|
+
const parts = Array.from({ length: 12 }, (_, index) => ({
|
|
316
|
+
type: "tool-result",
|
|
317
|
+
toolCallId: `call-${index}`,
|
|
318
|
+
toolName: "read_file",
|
|
319
|
+
output: { content: "x".repeat(8_000) },
|
|
320
|
+
}));
|
|
321
|
+
streamTextMock.mockReturnValueOnce({
|
|
322
|
+
fullStream: fullStream(...parts, { type: "text-delta", text: "done" }),
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
await collect(session.chat("read many files"));
|
|
326
|
+
|
|
327
|
+
const persisted = session.history.at(-1)?.content ?? "";
|
|
328
|
+
expect(persisted.length).toBeLessThan(40_000);
|
|
329
|
+
expect(persisted).toContain("tool transcript truncated");
|
|
330
|
+
});
|
|
331
|
+
|
|
268
332
|
it("writes raw DeepCCC fullStream parts when raw stream logs are enabled", async () => {
|
|
269
333
|
const { ChatSession } = await import("../builtin/index.ts");
|
|
270
334
|
config.rawStreamLogs = {
|
|
@@ -1,39 +1,39 @@
|
|
|
1
|
-
import { execFile } from "node:child_process";
|
|
2
|
-
import { promisify } from "node:util";
|
|
3
|
-
|
|
4
|
-
import { describe, expect, it } from "vitest";
|
|
5
|
-
|
|
6
|
-
const execFileAsync = promisify(execFile);
|
|
7
|
-
|
|
8
|
-
describe("builtin cli --stream-json", () => {
|
|
9
|
-
it("writes only JSON lines to stdout when startup fails", async () => {
|
|
10
|
-
let caught: unknown;
|
|
11
|
-
try {
|
|
12
|
-
await execFileAsync(process.execPath, [
|
|
13
|
-
"bin/cccagent.mjs",
|
|
14
|
-
"--stream-json",
|
|
15
|
-
"--prompt",
|
|
16
|
-
"hello",
|
|
17
|
-
"--api-key",
|
|
18
|
-
"",
|
|
19
|
-
], {
|
|
20
|
-
cwd: process.cwd(),
|
|
21
|
-
timeout: 10_000,
|
|
22
|
-
windowsHide: true,
|
|
23
|
-
});
|
|
24
|
-
} catch (err) {
|
|
25
|
-
caught = err;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
expect(caught).toMatchObject({ code: 1 });
|
|
29
|
-
const stdout = (caught as { stdout?: string }).stdout ?? "";
|
|
30
|
-
const lines = stdout.trim().split(/\r?\n/).filter(Boolean);
|
|
31
|
-
expect(lines.length).toBeGreaterThan(0);
|
|
32
|
-
for (const line of lines) {
|
|
33
|
-
expect(() => JSON.parse(line)).not.toThrow();
|
|
34
|
-
}
|
|
35
|
-
expect(JSON.parse(lines.at(-1)!)).toEqual(expect.objectContaining({
|
|
36
|
-
type: "error",
|
|
37
|
-
}));
|
|
38
|
-
});
|
|
39
|
-
});
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
|
|
4
|
+
import { describe, expect, it } from "vitest";
|
|
5
|
+
|
|
6
|
+
const execFileAsync = promisify(execFile);
|
|
7
|
+
|
|
8
|
+
describe("builtin cli --stream-json", () => {
|
|
9
|
+
it("writes only JSON lines to stdout when startup fails", async () => {
|
|
10
|
+
let caught: unknown;
|
|
11
|
+
try {
|
|
12
|
+
await execFileAsync(process.execPath, [
|
|
13
|
+
"bin/cccagent.mjs",
|
|
14
|
+
"--stream-json",
|
|
15
|
+
"--prompt",
|
|
16
|
+
"hello",
|
|
17
|
+
"--api-key",
|
|
18
|
+
"",
|
|
19
|
+
], {
|
|
20
|
+
cwd: process.cwd(),
|
|
21
|
+
timeout: 10_000,
|
|
22
|
+
windowsHide: true,
|
|
23
|
+
});
|
|
24
|
+
} catch (err) {
|
|
25
|
+
caught = err;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
expect(caught).toMatchObject({ code: 1 });
|
|
29
|
+
const stdout = (caught as { stdout?: string }).stdout ?? "";
|
|
30
|
+
const lines = stdout.trim().split(/\r?\n/).filter(Boolean);
|
|
31
|
+
expect(lines.length).toBeGreaterThan(0);
|
|
32
|
+
for (const line of lines) {
|
|
33
|
+
expect(() => JSON.parse(line)).not.toThrow();
|
|
34
|
+
}
|
|
35
|
+
expect(JSON.parse(lines.at(-1)!)).toEqual(expect.objectContaining({
|
|
36
|
+
type: "error",
|
|
37
|
+
}));
|
|
38
|
+
});
|
|
39
|
+
});
|
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
import { afterEach, describe, expect, it } from "vitest";
|
|
2
|
-
|
|
3
|
-
import { ChatSession } from "../builtin/index.ts";
|
|
4
|
-
import { config } from "../builtin/config.ts";
|
|
5
|
-
|
|
6
|
-
const originalDeepSeekApiKey = process.env.DEEPSEEK_API_KEY;
|
|
7
|
-
const originalDeepCccApiKey = config.apiKey;
|
|
8
|
-
|
|
9
|
-
afterEach(() => {
|
|
10
|
-
if (originalDeepSeekApiKey === undefined) {
|
|
11
|
-
delete process.env.DEEPSEEK_API_KEY;
|
|
12
|
-
} else {
|
|
13
|
-
process.env.DEEPSEEK_API_KEY = originalDeepSeekApiKey;
|
|
14
|
-
}
|
|
15
|
-
config.apiKey = originalDeepCccApiKey;
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
describe("builtin ChatSession config", () => {
|
|
19
|
-
it("uses the builtin ~/.deepccc config when no apiKey is passed", () => {
|
|
20
|
-
expect(() => new ChatSession()).not.toThrow();
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it("allows constructor parameters to override config defaults", () => {
|
|
24
|
-
expect(() => new ChatSession({ apiKey: "sk-test" })).not.toThrow();
|
|
25
|
-
});
|
|
26
|
-
});
|
|
1
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
2
|
+
|
|
3
|
+
import { ChatSession } from "../builtin/index.ts";
|
|
4
|
+
import { config } from "../builtin/config.ts";
|
|
5
|
+
|
|
6
|
+
const originalDeepSeekApiKey = process.env.DEEPSEEK_API_KEY;
|
|
7
|
+
const originalDeepCccApiKey = config.apiKey;
|
|
8
|
+
|
|
9
|
+
afterEach(() => {
|
|
10
|
+
if (originalDeepSeekApiKey === undefined) {
|
|
11
|
+
delete process.env.DEEPSEEK_API_KEY;
|
|
12
|
+
} else {
|
|
13
|
+
process.env.DEEPSEEK_API_KEY = originalDeepSeekApiKey;
|
|
14
|
+
}
|
|
15
|
+
config.apiKey = originalDeepCccApiKey;
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
describe("builtin ChatSession config", () => {
|
|
19
|
+
it("uses the builtin ~/.deepccc config when no apiKey is passed", () => {
|
|
20
|
+
expect(() => new ChatSession()).not.toThrow();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("allows constructor parameters to override config defaults", () => {
|
|
24
|
+
expect(() => new ChatSession({ apiKey: "sk-test" })).not.toThrow();
|
|
25
|
+
});
|
|
26
|
+
});
|