chatccc 0.2.197 → 0.2.199
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/agent-prompts/cursor_specific.md +13 -13
- package/bin/cccagent.mjs +17 -17
- package/config.sample.json +27 -27
- package/package.json +2 -1
- package/src/__tests__/agent-reload-config-rpc.test.ts +99 -99
- package/src/__tests__/builtin-chat-session.test.ts +277 -277
- package/src/__tests__/builtin-cli-json.test.ts +39 -39
- package/src/__tests__/builtin-config.test.ts +33 -33
- package/src/__tests__/builtin-context.test.ts +163 -163
- package/src/__tests__/builtin-file-tools.test.ts +224 -224
- package/src/__tests__/builtin-session-select.test.ts +116 -116
- package/src/__tests__/builtin-sigint.test.ts +56 -56
- package/src/__tests__/cards.test.ts +109 -109
- package/src/__tests__/ccc-adapter.test.ts +114 -114
- 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-raw-stream-log.test.ts +87 -87
- package/src/__tests__/codex-raw-stream-log.test.ts +163 -120
- package/src/__tests__/config-reload.test.ts +10 -10
- package/src/__tests__/config-sample.test.ts +18 -18
- package/src/__tests__/cursor-adapter.test.ts +114 -1
- package/src/__tests__/feishu-avatar.test.ts +40 -40
- package/src/__tests__/jsonl-stream.test.ts +79 -0
- package/src/__tests__/orchestrator.test.ts +200 -200
- package/src/__tests__/raw-stream-log.test.ts +106 -106
- package/src/__tests__/session.test.ts +40 -40
- package/src/__tests__/sim-platform.test.ts +12 -12
- package/src/__tests__/web-ui.test.ts +209 -209
- package/src/adapters/ccc-adapter.ts +121 -121
- package/src/adapters/claude-adapter.ts +603 -603
- package/src/adapters/codex-adapter.ts +380 -392
- package/src/adapters/cursor-adapter.ts +56 -30
- package/src/adapters/jsonl-stream.ts +157 -0
- package/src/adapters/raw-stream-log.ts +124 -124
- package/src/agent-reload-config-rpc.ts +34 -34
- package/src/builtin/cli.ts +473 -473
- package/src/builtin/context.ts +323 -323
- package/src/builtin/file-tools.ts +1072 -1072
- package/src/builtin/index.ts +404 -404
- package/src/builtin/session-select.ts +48 -48
- package/src/builtin/sigint.ts +50 -50
- package/src/cards.ts +195 -195
- 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/config.ts +125 -125
- package/src/feishu-api.ts +49 -49
- package/src/orchestrator.ts +179 -179
- package/src/runtime-reload.ts +34 -34
- package/src/session.ts +141 -141
- package/src/web-ui.ts +205 -205
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
## 计划/问答模式权限规则
|
|
2
|
-
|
|
3
|
-
如果用户消息以 `/plan` 或 `/ask` 开头,说明当前处于只读模式。**遇到任何需要用户同意的权限请求时,不要向用户申请权限,立即调用 stop-stuck-loop 接口提前结束本轮会话。**
|
|
4
|
-
|
|
5
|
-
## 用户问题澄清规则
|
|
6
|
-
|
|
7
|
-
当需要在开始实现前向用户确认问题、让用户做选择,或用户要求“从第一性原理出发挖掘真实需求 / 判断需求是否合理 / 开始实现前有什么问题要问我”时,**不要调用 `AskQuestion`、多选表单或任何结构化选择工具**。
|
|
8
|
-
|
|
9
|
-
必须直接用普通文本回复用户:
|
|
10
|
-
|
|
11
|
-
- 先简要说明你理解到的真实需求和你对合理性的判断。
|
|
12
|
-
- 再列出需要用户确认的问题;如果有推荐选项,就用文本说明“我建议 X,原因是 Y”。
|
|
13
|
-
- 问题数量尽量少,只问会影响实现边界、风险或不可逆决策的问题。
|
|
1
|
+
## 计划/问答模式权限规则
|
|
2
|
+
|
|
3
|
+
如果用户消息以 `/plan` 或 `/ask` 开头,说明当前处于只读模式。**遇到任何需要用户同意的权限请求时,不要向用户申请权限,立即调用 stop-stuck-loop 接口提前结束本轮会话。**
|
|
4
|
+
|
|
5
|
+
## 用户问题澄清规则
|
|
6
|
+
|
|
7
|
+
当需要在开始实现前向用户确认问题、让用户做选择,或用户要求“从第一性原理出发挖掘真实需求 / 判断需求是否合理 / 开始实现前有什么问题要问我”时,**不要调用 `AskQuestion`、多选表单或任何结构化选择工具**。
|
|
8
|
+
|
|
9
|
+
必须直接用普通文本回复用户:
|
|
10
|
+
|
|
11
|
+
- 先简要说明你理解到的真实需求和你对合理性的判断。
|
|
12
|
+
- 再列出需要用户确认的问题;如果有推荐选项,就用文本说明“我建议 X,原因是 Y”。
|
|
13
|
+
- 问题数量尽量少,只问会影响实现边界、风险或不可逆决策的问题。
|
|
14
14
|
- 不要把这些问题放进工具调用参数里,也不要只在卡片/工具结果中呈现。
|
package/bin/cccagent.mjs
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { spawnSync } from "node:child_process";
|
|
3
|
-
import { createRequire } from "node:module";
|
|
4
|
-
import { dirname, join } from "node:path";
|
|
5
|
-
|
|
6
|
-
const require = createRequire(import.meta.url);
|
|
7
|
-
const pkgRoot = dirname(require.resolve("../package.json"));
|
|
8
|
-
const cliTs = join(pkgRoot, "src", "builtin", "cli.ts");
|
|
9
|
-
const tsxCli = require.resolve("tsx/cli");
|
|
10
|
-
|
|
11
|
-
const result = spawnSync(process.execPath, [tsxCli, cliTs, ...process.argv.slice(2)], {
|
|
12
|
-
stdio: "inherit",
|
|
13
|
-
cwd: process.cwd(),
|
|
14
|
-
env: process.env,
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
process.exit(result.status === null ? 1 : result.status);
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
|
|
6
|
+
const require = createRequire(import.meta.url);
|
|
7
|
+
const pkgRoot = dirname(require.resolve("../package.json"));
|
|
8
|
+
const cliTs = join(pkgRoot, "src", "builtin", "cli.ts");
|
|
9
|
+
const tsxCli = require.resolve("tsx/cli");
|
|
10
|
+
|
|
11
|
+
const result = spawnSync(process.execPath, [tsxCli, cliTs, ...process.argv.slice(2)], {
|
|
12
|
+
stdio: "inherit",
|
|
13
|
+
cwd: process.cwd(),
|
|
14
|
+
env: process.env,
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
process.exit(result.status === null ? 1 : result.status);
|
package/config.sample.json
CHANGED
|
@@ -28,19 +28,19 @@
|
|
|
28
28
|
"retentionDays": 7,
|
|
29
29
|
"keepCompleted": false
|
|
30
30
|
},
|
|
31
|
-
"codex": {
|
|
32
|
-
"enabled": false,
|
|
33
|
-
"maxBytesPerTurn": 52428800,
|
|
34
|
-
"retentionDays": 7,
|
|
35
|
-
"keepCompleted": false
|
|
36
|
-
},
|
|
37
|
-
"ccc": {
|
|
38
|
-
"enabled": false,
|
|
39
|
-
"maxBytesPerTurn": 52428800,
|
|
40
|
-
"retentionDays": 7,
|
|
41
|
-
"keepCompleted": false
|
|
42
|
-
}
|
|
43
|
-
},
|
|
31
|
+
"codex": {
|
|
32
|
+
"enabled": false,
|
|
33
|
+
"maxBytesPerTurn": 52428800,
|
|
34
|
+
"retentionDays": 7,
|
|
35
|
+
"keepCompleted": false
|
|
36
|
+
},
|
|
37
|
+
"ccc": {
|
|
38
|
+
"enabled": false,
|
|
39
|
+
"maxBytesPerTurn": 52428800,
|
|
40
|
+
"retentionDays": 7,
|
|
41
|
+
"keepCompleted": false
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
44
|
"claude": {
|
|
45
45
|
"enabled": false,
|
|
46
46
|
"defaultAgent": true,
|
|
@@ -60,17 +60,17 @@
|
|
|
60
60
|
"avatarBatteryMode": "apiPercent",
|
|
61
61
|
"onDemandMonthlyBudget": 1000
|
|
62
62
|
},
|
|
63
|
-
"codex": {
|
|
64
|
-
"enabled": false,
|
|
65
|
-
"defaultAgent": false,
|
|
66
|
-
"path": "",
|
|
67
|
-
"model": "",
|
|
68
|
-
"alternativeModel": "",
|
|
69
|
-
"effort": ""
|
|
70
|
-
},
|
|
71
|
-
"ccc": {
|
|
72
|
-
"DEEPSEEK_API_KEY": "",
|
|
73
|
-
"DEEPSEEK_BASE_URL": "https://api.deepseek.com/v1",
|
|
74
|
-
"model": "deepseek-v4-pro"
|
|
75
|
-
}
|
|
76
|
-
}
|
|
63
|
+
"codex": {
|
|
64
|
+
"enabled": false,
|
|
65
|
+
"defaultAgent": false,
|
|
66
|
+
"path": "",
|
|
67
|
+
"model": "",
|
|
68
|
+
"alternativeModel": "",
|
|
69
|
+
"effort": ""
|
|
70
|
+
},
|
|
71
|
+
"ccc": {
|
|
72
|
+
"DEEPSEEK_API_KEY": "",
|
|
73
|
+
"DEEPSEEK_BASE_URL": "https://api.deepseek.com/v1",
|
|
74
|
+
"model": "deepseek-v4-pro"
|
|
75
|
+
}
|
|
76
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chatccc",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.199",
|
|
4
4
|
"description": "Feishu bot bridge for Claude Code",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"demo:permission-check": "tsx demo/permission_check.ts",
|
|
37
37
|
"demo:claude-hi": "tsx demo/claude_say_hi.ts",
|
|
38
38
|
"demo:codex-hi": "tsx demo/codex_say_hi.ts",
|
|
39
|
+
"demo:codex-app-server-approval": "tsx demo/codex-app-server-approval/approval_demo.ts",
|
|
39
40
|
"demo:ilink-echo": "tsx demo/ilink_echo_probe.ts",
|
|
40
41
|
"claude-proxy": "tsx src/litellm-proxy.ts",
|
|
41
42
|
"test": "vitest run",
|
|
@@ -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
|
+
});
|