chatccc 0.2.231 → 0.2.233
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 -86
- 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 +449 -393
- 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 +319 -202
- package/src/__tests__/builtin-file-tools.test.ts +240 -240
- package/src/__tests__/builtin-permissions.test.ts +211 -211
- package/src/__tests__/builtin-session-search.test.ts +228 -0
- package/src/__tests__/builtin-session-select.test.ts +116 -116
- package/src/__tests__/builtin-sigint.test.ts +56 -56
- package/src/__tests__/builtin-skills.test.ts +284 -284
- 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 -170
- 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__/crash-logging.test.ts +95 -1
- 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 -121
- package/src/__tests__/raw-stream-log.test.ts +106 -106
- package/src/__tests__/response-stall.test.ts +49 -49
- package/src/__tests__/restart.test.ts +55 -10
- package/src/__tests__/session.test.ts +199 -199
- 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 -54
- 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 +33 -33
- package/src/adapters/ccc-adapter.ts +141 -141
- 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 -175
- 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 +6 -0
- package/src/builtin/config.ts +84 -84
- package/src/builtin/context.ts +465 -391
- package/src/builtin/file-log.ts +38 -38
- package/src/builtin/file-tools.ts +34 -0
- package/src/builtin/index.ts +24 -23
- 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 -113
- 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-search.ts +363 -0
- package/src/builtin/session-select.ts +48 -48
- package/src/builtin/sigint.ts +50 -50
- package/src/builtin/skills.ts +205 -205
- 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/index.ts +6 -1
- package/src/litellm-proxy.ts +374 -374
- package/src/orchestrator.ts +36 -13
- package/src/platform-adapter.ts +6 -6
- package/src/privacy.ts +118 -118
- package/src/progress/reducer.ts +113 -113
- 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 +201 -201
- package/src/shared-prefix.ts +29 -29
- package/src/shared.ts +44 -0
- package/src/sim-platform.ts +20 -20
- package/src/startup-lifecycle.ts +250 -250
- package/src/stream-state.ts +21 -21
- package/src/terminal-error.ts +129 -129
- package/src/turn-cards.ts +117 -117
- package/src/update-command-guard.ts +165 -165
|
@@ -1,144 +1,144 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
-
import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
3
|
-
import { tmpdir } from "node:os";
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
acquireUpdateCommandGuard,
|
|
8
|
-
buildUpdateCommandId,
|
|
9
|
-
extractFeishuEventId,
|
|
10
|
-
} from "../update-command-guard.ts";
|
|
11
|
-
|
|
12
|
-
describe("Feishu update command IDs", () => {
|
|
13
|
-
it("extracts and namespaces a card callback event_id", () => {
|
|
14
|
-
const eventId = extractFeishuEventId({
|
|
15
|
-
schema: "2.0",
|
|
16
|
-
header: { event_id: "evt_update_001" },
|
|
17
|
-
event: { action: { value: { action: "update" } } },
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
expect(buildUpdateCommandId("card", eventId)).toBe("card:evt_update_001");
|
|
21
|
-
expect(buildUpdateCommandId("message", "om_update_001")).toBe("message:om_update_001");
|
|
22
|
-
});
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
describe("acquireUpdateCommandGuard", () => {
|
|
26
|
-
let tempDir: string;
|
|
27
|
-
let guardFile: string;
|
|
28
|
-
|
|
29
|
-
beforeEach(async () => {
|
|
30
|
-
tempDir = await mkdtemp(join(tmpdir(), "chatccc-update-guard-"));
|
|
31
|
-
guardFile = join(tempDir, "state", "update-command-guard.json");
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
afterEach(async () => {
|
|
35
|
-
await rm(tempDir, { recursive: true, force: true });
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it("persists an accepted update ID before returning", async () => {
|
|
39
|
-
const result = acquireUpdateCommandGuard({
|
|
40
|
-
filePath: guardFile,
|
|
41
|
-
commandId: "message:om_update_001",
|
|
42
|
-
now: 1_000,
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
expect(result).toEqual({ allowed: true, reason: "accepted" });
|
|
46
|
-
const saved = JSON.parse(await readFile(guardFile, "utf8"));
|
|
47
|
-
expect(saved).toEqual({
|
|
48
|
-
version: 1,
|
|
49
|
-
processed: [{ id: "message:om_update_001", recordedAt: 1_000 }],
|
|
50
|
-
});
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it("rejects the same ID after a simulated process restart", () => {
|
|
54
|
-
expect(acquireUpdateCommandGuard({
|
|
55
|
-
filePath: guardFile,
|
|
56
|
-
commandId: "message:om_update_001",
|
|
57
|
-
now: 1_000,
|
|
58
|
-
}).allowed).toBe(true);
|
|
59
|
-
|
|
60
|
-
// 第二次调用不共享任何内存状态,只通过落盘文件模拟新进程重启后的判断。
|
|
61
|
-
expect(acquireUpdateCommandGuard({
|
|
62
|
-
filePath: guardFile,
|
|
63
|
-
commandId: "message:om_update_001",
|
|
64
|
-
now: 9_999_999,
|
|
65
|
-
})).toEqual({ allowed: false, reason: "duplicate_id" });
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it("accepts different IDs immediately without a cooldown", () => {
|
|
69
|
-
expect(acquireUpdateCommandGuard({
|
|
70
|
-
filePath: guardFile,
|
|
71
|
-
commandId: "message:om_update_001",
|
|
72
|
-
now: 1_000,
|
|
73
|
-
}).allowed).toBe(true);
|
|
74
|
-
|
|
75
|
-
expect(acquireUpdateCommandGuard({
|
|
76
|
-
filePath: guardFile,
|
|
77
|
-
commandId: "message:om_update_002",
|
|
78
|
-
now: 1_001,
|
|
79
|
-
})).toEqual({ allowed: true, reason: "accepted" });
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
it("repairs a corrupt state file and warns without creating an update loop", async () => {
|
|
83
|
-
await mkdir(join(tempDir, "state"), { recursive: true });
|
|
84
|
-
await writeFile(guardFile, "not-json", "utf8");
|
|
85
|
-
const warn = vi.fn();
|
|
86
|
-
|
|
87
|
-
expect(acquireUpdateCommandGuard({
|
|
88
|
-
filePath: guardFile,
|
|
89
|
-
commandId: "message:om_update_001",
|
|
90
|
-
now: 1_000,
|
|
91
|
-
warn,
|
|
92
|
-
})).toEqual({ allowed: true, reason: "accepted" });
|
|
93
|
-
expect(warn).toHaveBeenCalledOnce();
|
|
94
|
-
|
|
95
|
-
// 损坏文件已被有效状态覆盖,因此重启后的重复投递仍会被拦截。
|
|
96
|
-
expect(acquireUpdateCommandGuard({
|
|
97
|
-
filePath: guardFile,
|
|
98
|
-
commandId: "message:om_update_001",
|
|
99
|
-
now: 2_000,
|
|
100
|
-
warn,
|
|
101
|
-
})).toEqual({ allowed: false, reason: "duplicate_id" });
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
it("fails closed when the accepted ID cannot be persisted", async () => {
|
|
105
|
-
const blocker = join(tempDir, "not-a-directory");
|
|
106
|
-
await writeFile(blocker, "block", "utf8");
|
|
107
|
-
const warn = vi.fn();
|
|
108
|
-
|
|
109
|
-
expect(acquireUpdateCommandGuard({
|
|
110
|
-
filePath: join(blocker, "update-command-guard.json"),
|
|
111
|
-
commandId: "message:om_update_001",
|
|
112
|
-
now: 1_000,
|
|
113
|
-
warn,
|
|
114
|
-
})).toEqual({ allowed: false, reason: "state_write_failed" });
|
|
115
|
-
expect(warn).toHaveBeenCalledOnce();
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
it("allows sources without a stable ID without writing a misleading record", async () => {
|
|
119
|
-
expect(acquireUpdateCommandGuard({
|
|
120
|
-
filePath: guardFile,
|
|
121
|
-
commandId: undefined,
|
|
122
|
-
now: 1_000,
|
|
123
|
-
})).toEqual({ allowed: true, reason: "missing_id" });
|
|
124
|
-
await expect(readFile(guardFile, "utf8")).rejects.toMatchObject({ code: "ENOENT" });
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
it("keeps only the newest configured number of update IDs", async () => {
|
|
128
|
-
for (let i = 0; i < 4; i++) {
|
|
129
|
-
expect(acquireUpdateCommandGuard({
|
|
130
|
-
filePath: guardFile,
|
|
131
|
-
commandId: `message:om_update_${i}`,
|
|
132
|
-
now: i,
|
|
133
|
-
maxEntries: 3,
|
|
134
|
-
}).allowed).toBe(true);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
const saved = JSON.parse(await readFile(guardFile, "utf8"));
|
|
138
|
-
expect(saved.processed.map((entry: { id: string }) => entry.id)).toEqual([
|
|
139
|
-
"message:om_update_1",
|
|
140
|
-
"message:om_update_2",
|
|
141
|
-
"message:om_update_3",
|
|
142
|
-
]);
|
|
143
|
-
});
|
|
144
|
-
});
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
acquireUpdateCommandGuard,
|
|
8
|
+
buildUpdateCommandId,
|
|
9
|
+
extractFeishuEventId,
|
|
10
|
+
} from "../update-command-guard.ts";
|
|
11
|
+
|
|
12
|
+
describe("Feishu update command IDs", () => {
|
|
13
|
+
it("extracts and namespaces a card callback event_id", () => {
|
|
14
|
+
const eventId = extractFeishuEventId({
|
|
15
|
+
schema: "2.0",
|
|
16
|
+
header: { event_id: "evt_update_001" },
|
|
17
|
+
event: { action: { value: { action: "update" } } },
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
expect(buildUpdateCommandId("card", eventId)).toBe("card:evt_update_001");
|
|
21
|
+
expect(buildUpdateCommandId("message", "om_update_001")).toBe("message:om_update_001");
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe("acquireUpdateCommandGuard", () => {
|
|
26
|
+
let tempDir: string;
|
|
27
|
+
let guardFile: string;
|
|
28
|
+
|
|
29
|
+
beforeEach(async () => {
|
|
30
|
+
tempDir = await mkdtemp(join(tmpdir(), "chatccc-update-guard-"));
|
|
31
|
+
guardFile = join(tempDir, "state", "update-command-guard.json");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
afterEach(async () => {
|
|
35
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("persists an accepted update ID before returning", async () => {
|
|
39
|
+
const result = acquireUpdateCommandGuard({
|
|
40
|
+
filePath: guardFile,
|
|
41
|
+
commandId: "message:om_update_001",
|
|
42
|
+
now: 1_000,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
expect(result).toEqual({ allowed: true, reason: "accepted" });
|
|
46
|
+
const saved = JSON.parse(await readFile(guardFile, "utf8"));
|
|
47
|
+
expect(saved).toEqual({
|
|
48
|
+
version: 1,
|
|
49
|
+
processed: [{ id: "message:om_update_001", recordedAt: 1_000 }],
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("rejects the same ID after a simulated process restart", () => {
|
|
54
|
+
expect(acquireUpdateCommandGuard({
|
|
55
|
+
filePath: guardFile,
|
|
56
|
+
commandId: "message:om_update_001",
|
|
57
|
+
now: 1_000,
|
|
58
|
+
}).allowed).toBe(true);
|
|
59
|
+
|
|
60
|
+
// 第二次调用不共享任何内存状态,只通过落盘文件模拟新进程重启后的判断。
|
|
61
|
+
expect(acquireUpdateCommandGuard({
|
|
62
|
+
filePath: guardFile,
|
|
63
|
+
commandId: "message:om_update_001",
|
|
64
|
+
now: 9_999_999,
|
|
65
|
+
})).toEqual({ allowed: false, reason: "duplicate_id" });
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("accepts different IDs immediately without a cooldown", () => {
|
|
69
|
+
expect(acquireUpdateCommandGuard({
|
|
70
|
+
filePath: guardFile,
|
|
71
|
+
commandId: "message:om_update_001",
|
|
72
|
+
now: 1_000,
|
|
73
|
+
}).allowed).toBe(true);
|
|
74
|
+
|
|
75
|
+
expect(acquireUpdateCommandGuard({
|
|
76
|
+
filePath: guardFile,
|
|
77
|
+
commandId: "message:om_update_002",
|
|
78
|
+
now: 1_001,
|
|
79
|
+
})).toEqual({ allowed: true, reason: "accepted" });
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("repairs a corrupt state file and warns without creating an update loop", async () => {
|
|
83
|
+
await mkdir(join(tempDir, "state"), { recursive: true });
|
|
84
|
+
await writeFile(guardFile, "not-json", "utf8");
|
|
85
|
+
const warn = vi.fn();
|
|
86
|
+
|
|
87
|
+
expect(acquireUpdateCommandGuard({
|
|
88
|
+
filePath: guardFile,
|
|
89
|
+
commandId: "message:om_update_001",
|
|
90
|
+
now: 1_000,
|
|
91
|
+
warn,
|
|
92
|
+
})).toEqual({ allowed: true, reason: "accepted" });
|
|
93
|
+
expect(warn).toHaveBeenCalledOnce();
|
|
94
|
+
|
|
95
|
+
// 损坏文件已被有效状态覆盖,因此重启后的重复投递仍会被拦截。
|
|
96
|
+
expect(acquireUpdateCommandGuard({
|
|
97
|
+
filePath: guardFile,
|
|
98
|
+
commandId: "message:om_update_001",
|
|
99
|
+
now: 2_000,
|
|
100
|
+
warn,
|
|
101
|
+
})).toEqual({ allowed: false, reason: "duplicate_id" });
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("fails closed when the accepted ID cannot be persisted", async () => {
|
|
105
|
+
const blocker = join(tempDir, "not-a-directory");
|
|
106
|
+
await writeFile(blocker, "block", "utf8");
|
|
107
|
+
const warn = vi.fn();
|
|
108
|
+
|
|
109
|
+
expect(acquireUpdateCommandGuard({
|
|
110
|
+
filePath: join(blocker, "update-command-guard.json"),
|
|
111
|
+
commandId: "message:om_update_001",
|
|
112
|
+
now: 1_000,
|
|
113
|
+
warn,
|
|
114
|
+
})).toEqual({ allowed: false, reason: "state_write_failed" });
|
|
115
|
+
expect(warn).toHaveBeenCalledOnce();
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it("allows sources without a stable ID without writing a misleading record", async () => {
|
|
119
|
+
expect(acquireUpdateCommandGuard({
|
|
120
|
+
filePath: guardFile,
|
|
121
|
+
commandId: undefined,
|
|
122
|
+
now: 1_000,
|
|
123
|
+
})).toEqual({ allowed: true, reason: "missing_id" });
|
|
124
|
+
await expect(readFile(guardFile, "utf8")).rejects.toMatchObject({ code: "ENOENT" });
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("keeps only the newest configured number of update IDs", async () => {
|
|
128
|
+
for (let i = 0; i < 4; i++) {
|
|
129
|
+
expect(acquireUpdateCommandGuard({
|
|
130
|
+
filePath: guardFile,
|
|
131
|
+
commandId: `message:om_update_${i}`,
|
|
132
|
+
now: i,
|
|
133
|
+
maxEntries: 3,
|
|
134
|
+
}).allowed).toBe(true);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const saved = JSON.parse(await readFile(guardFile, "utf8"));
|
|
138
|
+
expect(saved.processed.map((entry: { id: string }) => entry.id)).toEqual([
|
|
139
|
+
"message:om_update_1",
|
|
140
|
+
"message:om_update_2",
|
|
141
|
+
"message:om_update_3",
|
|
142
|
+
]);
|
|
143
|
+
});
|
|
144
|
+
});
|