chatccc 0.2.52 → 0.2.53
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 +95 -256
- package/bin/chatccc.mjs +23 -23
- package/demo/ilink_echo_probe.ts +222 -222
- package/im-skills/feishu-skill/download-video.mjs +162 -162
- package/im-skills/feishu-skill/receive-send-file.md +66 -66
- package/im-skills/feishu-skill/receive-send-image.md +27 -27
- package/im-skills/feishu-skill/send-file.mjs +50 -50
- package/im-skills/feishu-skill/send-image.mjs +50 -50
- package/im-skills/feishu-skill/skill.md +10 -10
- package/package.json +59 -59
- package/src/__tests__/agent-image-rpc.test.ts +33 -33
- package/src/__tests__/agent-rpc-body.test.ts +41 -41
- package/src/__tests__/card-plain-text.test.ts +42 -42
- package/src/__tests__/codex-adapter.test.ts +304 -304
- package/src/__tests__/config-reload.test.ts +26 -26
- package/src/__tests__/config-sample.test.ts +19 -19
- package/src/__tests__/crash-logging.test.ts +62 -62
- package/src/__tests__/fixtures/codex_simple_text.jsonl +4 -4
- package/src/__tests__/fixtures/codex_with_tool.jsonl +6 -6
- package/src/__tests__/im-skills.test.ts +46 -46
- package/src/__tests__/session.test.ts +57 -2
- package/src/__tests__/sim-agent.test.ts +173 -173
- package/src/__tests__/sim-platform.test.ts +76 -76
- package/src/__tests__/sim-store.test.ts +213 -213
- package/src/__tests__/stream-state.test.ts +134 -134
- package/src/__tests__/wechat-platform.test.ts +57 -32
- package/src/adapters/codex-adapter.ts +294 -294
- package/src/adapters/codex-session-meta-store.ts +130 -130
- package/src/agent-file-rpc.ts +156 -156
- package/src/agent-image-rpc.ts +152 -152
- package/src/agent-rpc-body.ts +48 -48
- package/src/card-plain-text.ts +108 -108
- package/src/im-skills.ts +109 -109
- package/src/platform-adapter.ts +60 -60
- package/src/session-chat-binding.ts +6 -1
- package/src/session.ts +40 -35
- package/src/sim-agent.ts +167 -167
- package/src/trace.ts +50 -50
- package/src/wechat-platform.ts +1 -1
|
@@ -1,51 +1,51 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { basename } from "node:path";
|
|
3
|
-
|
|
4
|
-
function parseArgs(argv) {
|
|
5
|
-
const result = {};
|
|
6
|
-
for (let i = 0; i < argv.length; i++) {
|
|
7
|
-
const key = argv[i];
|
|
8
|
-
if (!key.startsWith("--")) continue;
|
|
9
|
-
const value = argv[i + 1] && !argv[i + 1].startsWith("--") ? argv[++i] : "true";
|
|
10
|
-
result[key.slice(2)] = value;
|
|
11
|
-
}
|
|
12
|
-
return result;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function usage() {
|
|
16
|
-
console.error(`Usage:
|
|
17
|
-
node ${basename(process.argv[1])} --url <url> --session-id <session_id> --path <absolute image path> [--caption <text>]`);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
async function main() {
|
|
21
|
-
const args = parseArgs(process.argv.slice(2));
|
|
22
|
-
const url = args.url || process.env.CHATCCC_SEND_IMAGE_URL;
|
|
23
|
-
const sessionId = args["session-id"] || args.session_id || process.env.CHATCCC_SESSION_ID;
|
|
24
|
-
const path = args.path;
|
|
25
|
-
const caption = args.caption || "";
|
|
26
|
-
|
|
27
|
-
if (!url || !sessionId || !path) {
|
|
28
|
-
usage();
|
|
29
|
-
process.exit(1);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const body = Buffer.from(JSON.stringify({ session_id: sessionId, path, caption }), "utf8");
|
|
33
|
-
const response = await fetch(url, {
|
|
34
|
-
method: "POST",
|
|
35
|
-
headers: {
|
|
36
|
-
"Content-Type": "application/json; charset=utf-8",
|
|
37
|
-
},
|
|
38
|
-
body,
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
const text = await response.text();
|
|
42
|
-
if (!response.ok) {
|
|
43
|
-
throw new Error(`HTTP ${response.status}: ${text}`);
|
|
44
|
-
}
|
|
45
|
-
console.log(text);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
main().catch((err) => {
|
|
49
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
50
|
-
process.exit(1);
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { basename } from "node:path";
|
|
3
|
+
|
|
4
|
+
function parseArgs(argv) {
|
|
5
|
+
const result = {};
|
|
6
|
+
for (let i = 0; i < argv.length; i++) {
|
|
7
|
+
const key = argv[i];
|
|
8
|
+
if (!key.startsWith("--")) continue;
|
|
9
|
+
const value = argv[i + 1] && !argv[i + 1].startsWith("--") ? argv[++i] : "true";
|
|
10
|
+
result[key.slice(2)] = value;
|
|
11
|
+
}
|
|
12
|
+
return result;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function usage() {
|
|
16
|
+
console.error(`Usage:
|
|
17
|
+
node ${basename(process.argv[1])} --url <url> --session-id <session_id> --path <absolute image path> [--caption <text>]`);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async function main() {
|
|
21
|
+
const args = parseArgs(process.argv.slice(2));
|
|
22
|
+
const url = args.url || process.env.CHATCCC_SEND_IMAGE_URL;
|
|
23
|
+
const sessionId = args["session-id"] || args.session_id || process.env.CHATCCC_SESSION_ID;
|
|
24
|
+
const path = args.path;
|
|
25
|
+
const caption = args.caption || "";
|
|
26
|
+
|
|
27
|
+
if (!url || !sessionId || !path) {
|
|
28
|
+
usage();
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const body = Buffer.from(JSON.stringify({ session_id: sessionId, path, caption }), "utf8");
|
|
33
|
+
const response = await fetch(url, {
|
|
34
|
+
method: "POST",
|
|
35
|
+
headers: {
|
|
36
|
+
"Content-Type": "application/json; charset=utf-8",
|
|
37
|
+
},
|
|
38
|
+
body,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const text = await response.text();
|
|
42
|
+
if (!response.ok) {
|
|
43
|
+
throw new Error(`HTTP ${response.status}: ${text}`);
|
|
44
|
+
}
|
|
45
|
+
console.log(text);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
main().catch((err) => {
|
|
49
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
50
|
+
process.exit(1);
|
|
51
51
|
});
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: feishu-skill
|
|
3
|
-
description: Feishu IM local skills for sending and receiving images, files, and videos.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
Current working directory: {{cwd}}
|
|
7
|
-
|
|
8
|
-
Use local endpoints instead of calling Feishu Open Platform directly.
|
|
9
|
-
|
|
10
|
-
- **Send images**: POST `{{send_image_url}}` with `{"session_id":"{{session_id}}","path":"<absolute path>","caption":"<optional>"}` — read `{{im_skills_cache_dir}}/feishu-skill/receive-send-image.md`
|
|
1
|
+
---
|
|
2
|
+
name: feishu-skill
|
|
3
|
+
description: Feishu IM local skills for sending and receiving images, files, and videos.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Current working directory: {{cwd}}
|
|
7
|
+
|
|
8
|
+
Use local endpoints instead of calling Feishu Open Platform directly.
|
|
9
|
+
|
|
10
|
+
- **Send images**: POST `{{send_image_url}}` with `{"session_id":"{{session_id}}","path":"<absolute path>","caption":"<optional>"}` — read `{{im_skills_cache_dir}}/feishu-skill/receive-send-image.md`
|
|
11
11
|
- **Send files/videos**: POST `{{send_file_url}}` with `{"session_id":"{{session_id}}","path":"<absolute path>","caption":"<optional>"}` — read `{{im_skills_cache_dir}}/feishu-skill/receive-send-file.md`
|
package/package.json
CHANGED
|
@@ -1,59 +1,59 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "chatccc",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "Feishu bot bridge for Claude Code",
|
|
5
|
-
"license": "Apache-2.0",
|
|
6
|
-
"type": "module",
|
|
7
|
-
"main": "./src/index.ts",
|
|
8
|
-
"bin": {
|
|
9
|
-
"chatccc": "bin/chatccc.mjs"
|
|
10
|
-
},
|
|
11
|
-
"files": [
|
|
12
|
-
"src/",
|
|
13
|
-
"bin/",
|
|
14
|
-
"demo/ilink_echo_probe.ts",
|
|
15
|
-
"im-skills/",
|
|
16
|
-
"images/img_readme_*.jpg",
|
|
17
|
-
"images/img_readme_*.png",
|
|
18
|
-
"images/avatars/status_*.png",
|
|
19
|
-
"images/avatars/badges/",
|
|
20
|
-
"package.json",
|
|
21
|
-
"README.md",
|
|
22
|
-
"config.sample.json"
|
|
23
|
-
],
|
|
24
|
-
"scripts": {
|
|
25
|
-
"dev": "tsx src/index.ts",
|
|
26
|
-
"chatccc": "tsx src/index.ts",
|
|
27
|
-
"start": "tsx src/index.ts",
|
|
28
|
-
"demo:bot-test": "tsx demo/bot_test.ts",
|
|
29
|
-
"demo:bot-test:local": "tsx demo/bot_test.ts --local",
|
|
30
|
-
"demo:create-group": "tsx src/index.ts",
|
|
31
|
-
"demo:create-group:local": "tsx src/index.ts --local",
|
|
32
|
-
"demo:permission-check": "tsx demo/permission_check.ts",
|
|
33
|
-
"demo:claude-hi": "tsx demo/claude_say_hi.ts",
|
|
34
|
-
"demo:codex-hi": "tsx demo/codex_say_hi.ts",
|
|
35
|
-
"demo:ilink-echo": "tsx demo/ilink_echo_probe.ts",
|
|
36
|
-
"test": "vitest run",
|
|
37
|
-
"test:watch": "vitest"
|
|
38
|
-
},
|
|
39
|
-
"dependencies": {
|
|
40
|
-
"@anthropic-ai/claude-agent-sdk": "0.2.133",
|
|
41
|
-
"@larksuiteoapi/node-sdk": "^1.59.0",
|
|
42
|
-
"@openilink/openilink-sdk-node": "^0.6.0",
|
|
43
|
-
"nodemailer": "^8.0.7",
|
|
44
|
-
"qrcode-terminal": "^0.12.0",
|
|
45
|
-
"sharp": "^0.34.5",
|
|
46
|
-
"tsx": "^4.0.0",
|
|
47
|
-
"ws": "^8.18.0"
|
|
48
|
-
},
|
|
49
|
-
"devDependencies": {
|
|
50
|
-
"@types/node": "^20.0.0",
|
|
51
|
-
"@types/qrcode-terminal": "^0.12.2",
|
|
52
|
-
"@types/ws": "^8.18.1",
|
|
53
|
-
"typescript": "^5.0.0",
|
|
54
|
-
"vitest": "^4.1.5"
|
|
55
|
-
},
|
|
56
|
-
"engines": {
|
|
57
|
-
"node": ">=20"
|
|
58
|
-
}
|
|
59
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "chatccc",
|
|
3
|
+
"version": "0.2.53",
|
|
4
|
+
"description": "Feishu bot bridge for Claude Code",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./src/index.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"chatccc": "bin/chatccc.mjs"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"src/",
|
|
13
|
+
"bin/",
|
|
14
|
+
"demo/ilink_echo_probe.ts",
|
|
15
|
+
"im-skills/",
|
|
16
|
+
"images/img_readme_*.jpg",
|
|
17
|
+
"images/img_readme_*.png",
|
|
18
|
+
"images/avatars/status_*.png",
|
|
19
|
+
"images/avatars/badges/",
|
|
20
|
+
"package.json",
|
|
21
|
+
"README.md",
|
|
22
|
+
"config.sample.json"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"dev": "tsx src/index.ts",
|
|
26
|
+
"chatccc": "tsx src/index.ts",
|
|
27
|
+
"start": "tsx src/index.ts",
|
|
28
|
+
"demo:bot-test": "tsx demo/bot_test.ts",
|
|
29
|
+
"demo:bot-test:local": "tsx demo/bot_test.ts --local",
|
|
30
|
+
"demo:create-group": "tsx src/index.ts",
|
|
31
|
+
"demo:create-group:local": "tsx src/index.ts --local",
|
|
32
|
+
"demo:permission-check": "tsx demo/permission_check.ts",
|
|
33
|
+
"demo:claude-hi": "tsx demo/claude_say_hi.ts",
|
|
34
|
+
"demo:codex-hi": "tsx demo/codex_say_hi.ts",
|
|
35
|
+
"demo:ilink-echo": "tsx demo/ilink_echo_probe.ts",
|
|
36
|
+
"test": "vitest run",
|
|
37
|
+
"test:watch": "vitest"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@anthropic-ai/claude-agent-sdk": "0.2.133",
|
|
41
|
+
"@larksuiteoapi/node-sdk": "^1.59.0",
|
|
42
|
+
"@openilink/openilink-sdk-node": "^0.6.0",
|
|
43
|
+
"nodemailer": "^8.0.7",
|
|
44
|
+
"qrcode-terminal": "^0.12.0",
|
|
45
|
+
"sharp": "^0.34.5",
|
|
46
|
+
"tsx": "^4.0.0",
|
|
47
|
+
"ws": "^8.18.0"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@types/node": "^20.0.0",
|
|
51
|
+
"@types/qrcode-terminal": "^0.12.2",
|
|
52
|
+
"@types/ws": "^8.18.1",
|
|
53
|
+
"typescript": "^5.0.0",
|
|
54
|
+
"vitest": "^4.1.5"
|
|
55
|
+
},
|
|
56
|
+
"engines": {
|
|
57
|
+
"node": ">=20"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -1,34 +1,34 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
AGENT_SEND_IMAGE_PATH,
|
|
5
|
-
buildAgentImageCapabilityPrompt,
|
|
6
|
-
} from "../agent-image-rpc.ts";
|
|
7
|
-
|
|
8
|
-
describe("agent image RPC", () => {
|
|
9
|
-
it("builds prompt instructions with session_id", () => {
|
|
10
|
-
const prompt = buildAgentImageCapabilityPrompt({
|
|
11
|
-
url: `http://127.0.0.1:18080${AGENT_SEND_IMAGE_PATH}`,
|
|
12
|
-
sessionId: "sid-test",
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
expect(prompt).toContain("POST http://127.0.0.1:18080/api/agent/send-image");
|
|
16
|
-
expect(prompt).toContain('"session_id":"sid-test"');
|
|
17
|
-
expect(prompt).toContain("Content-Type: application/json; charset=utf-8");
|
|
18
|
-
expect(prompt).toContain("UTF-8 encoded JSON bytes");
|
|
19
|
-
expect(prompt).toContain('"path"');
|
|
20
|
-
expect(prompt).not.toContain("Authorization: Bearer");
|
|
21
|
-
expect(prompt).not.toContain("CHATCCC_SEND_IMAGE_URL");
|
|
22
|
-
expect(prompt).not.toContain("CHATCCC_SEND_IMAGE_TOKEN");
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it("builds prompt with cwd hint", () => {
|
|
26
|
-
const prompt = buildAgentImageCapabilityPrompt({
|
|
27
|
-
url: `http://127.0.0.1:18080${AGENT_SEND_IMAGE_PATH}`,
|
|
28
|
-
sessionId: "sid-1",
|
|
29
|
-
cwd: "F:/repo",
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
expect(prompt).toContain("Current working directory: F:/repo");
|
|
33
|
-
});
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
AGENT_SEND_IMAGE_PATH,
|
|
5
|
+
buildAgentImageCapabilityPrompt,
|
|
6
|
+
} from "../agent-image-rpc.ts";
|
|
7
|
+
|
|
8
|
+
describe("agent image RPC", () => {
|
|
9
|
+
it("builds prompt instructions with session_id", () => {
|
|
10
|
+
const prompt = buildAgentImageCapabilityPrompt({
|
|
11
|
+
url: `http://127.0.0.1:18080${AGENT_SEND_IMAGE_PATH}`,
|
|
12
|
+
sessionId: "sid-test",
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
expect(prompt).toContain("POST http://127.0.0.1:18080/api/agent/send-image");
|
|
16
|
+
expect(prompt).toContain('"session_id":"sid-test"');
|
|
17
|
+
expect(prompt).toContain("Content-Type: application/json; charset=utf-8");
|
|
18
|
+
expect(prompt).toContain("UTF-8 encoded JSON bytes");
|
|
19
|
+
expect(prompt).toContain('"path"');
|
|
20
|
+
expect(prompt).not.toContain("Authorization: Bearer");
|
|
21
|
+
expect(prompt).not.toContain("CHATCCC_SEND_IMAGE_URL");
|
|
22
|
+
expect(prompt).not.toContain("CHATCCC_SEND_IMAGE_TOKEN");
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("builds prompt with cwd hint", () => {
|
|
26
|
+
const prompt = buildAgentImageCapabilityPrompt({
|
|
27
|
+
url: `http://127.0.0.1:18080${AGENT_SEND_IMAGE_PATH}`,
|
|
28
|
+
sessionId: "sid-1",
|
|
29
|
+
cwd: "F:/repo",
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
expect(prompt).toContain("Current working directory: F:/repo");
|
|
33
|
+
});
|
|
34
34
|
});
|
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
import type { IncomingMessage } from "node:http";
|
|
2
|
-
import { Readable } from "node:stream";
|
|
3
|
-
|
|
4
|
-
import { describe, expect, it } from "vitest";
|
|
5
|
-
|
|
6
|
-
import { getRequestCharset, readUtf8JsonBody } from "../agent-rpc-body.ts";
|
|
7
|
-
|
|
8
|
-
function requestFrom(body: Buffer, contentType?: string): IncomingMessage {
|
|
9
|
-
const req = Readable.from([body]) as IncomingMessage;
|
|
10
|
-
req.headers = {};
|
|
11
|
-
if (contentType) req.headers["content-type"] = contentType;
|
|
12
|
-
return req;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
describe("agent RPC body parsing", () => {
|
|
16
|
-
it("defaults JSON requests to UTF-8 and preserves Unicode captions", async () => {
|
|
17
|
-
const caption = "\u4e2d\u6587 caption";
|
|
18
|
-
const body = Buffer.from(JSON.stringify({ caption }), "utf8");
|
|
19
|
-
const req = requestFrom(body, "application/json");
|
|
20
|
-
|
|
21
|
-
await expect(readUtf8JsonBody(req, 1024)).resolves.toEqual({ caption });
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it("reads charset from content-type", () => {
|
|
25
|
-
const req = requestFrom(Buffer.from("{}"), 'application/json; charset="utf-8"');
|
|
26
|
-
|
|
27
|
-
expect(getRequestCharset(req)).toBe("utf-8");
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it("rejects non UTF-8 charsets", async () => {
|
|
31
|
-
const req = requestFrom(Buffer.from("{}"), "application/json; charset=gbk");
|
|
32
|
-
|
|
33
|
-
await expect(readUtf8JsonBody(req, 1024)).rejects.toThrow("Unsupported charset");
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it("rejects invalid UTF-8 bytes", async () => {
|
|
37
|
-
const req = requestFrom(Buffer.from([0xff, 0xfe]), "application/json; charset=utf-8");
|
|
38
|
-
|
|
39
|
-
await expect(readUtf8JsonBody(req, 1024)).rejects.toThrow("valid UTF-8");
|
|
40
|
-
});
|
|
41
|
-
});
|
|
1
|
+
import type { IncomingMessage } from "node:http";
|
|
2
|
+
import { Readable } from "node:stream";
|
|
3
|
+
|
|
4
|
+
import { describe, expect, it } from "vitest";
|
|
5
|
+
|
|
6
|
+
import { getRequestCharset, readUtf8JsonBody } from "../agent-rpc-body.ts";
|
|
7
|
+
|
|
8
|
+
function requestFrom(body: Buffer, contentType?: string): IncomingMessage {
|
|
9
|
+
const req = Readable.from([body]) as IncomingMessage;
|
|
10
|
+
req.headers = {};
|
|
11
|
+
if (contentType) req.headers["content-type"] = contentType;
|
|
12
|
+
return req;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
describe("agent RPC body parsing", () => {
|
|
16
|
+
it("defaults JSON requests to UTF-8 and preserves Unicode captions", async () => {
|
|
17
|
+
const caption = "\u4e2d\u6587 caption";
|
|
18
|
+
const body = Buffer.from(JSON.stringify({ caption }), "utf8");
|
|
19
|
+
const req = requestFrom(body, "application/json");
|
|
20
|
+
|
|
21
|
+
await expect(readUtf8JsonBody(req, 1024)).resolves.toEqual({ caption });
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("reads charset from content-type", () => {
|
|
25
|
+
const req = requestFrom(Buffer.from("{}"), 'application/json; charset="utf-8"');
|
|
26
|
+
|
|
27
|
+
expect(getRequestCharset(req)).toBe("utf-8");
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("rejects non UTF-8 charsets", async () => {
|
|
31
|
+
const req = requestFrom(Buffer.from("{}"), "application/json; charset=gbk");
|
|
32
|
+
|
|
33
|
+
await expect(readUtf8JsonBody(req, 1024)).rejects.toThrow("Unsupported charset");
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("rejects invalid UTF-8 bytes", async () => {
|
|
37
|
+
const req = requestFrom(Buffer.from([0xff, 0xfe]), "application/json; charset=utf-8");
|
|
38
|
+
|
|
39
|
+
await expect(readUtf8JsonBody(req, 1024)).rejects.toThrow("valid UTF-8");
|
|
40
|
+
});
|
|
41
|
+
});
|
|
@@ -1,42 +1,42 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
|
|
3
|
-
import { cardJsonToPlainText } from "../card-plain-text.ts";
|
|
4
|
-
import {
|
|
5
|
-
buildHelpCard,
|
|
6
|
-
buildProgressCard,
|
|
7
|
-
buildStatusCard,
|
|
8
|
-
} from "../cards.ts";
|
|
9
|
-
|
|
10
|
-
describe("cardJsonToPlainText", () => {
|
|
11
|
-
it("converts help cards to readable text with commands", () => {
|
|
12
|
-
const text = cardJsonToPlainText(buildHelpCard("hello"));
|
|
13
|
-
|
|
14
|
-
expect(text).toContain("# ChatCCC");
|
|
15
|
-
expect(text).toContain("hello");
|
|
16
|
-
expect(text).toContain("/new");
|
|
17
|
-
expect(text).toContain("/new cursor");
|
|
18
|
-
expect(text).toContain("/new codex");
|
|
19
|
-
expect(text).toContain("/restart");
|
|
20
|
-
expect(text).toContain("/cd");
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it("converts status cards from v1 card format", () => {
|
|
24
|
-
const text = cardJsonToPlainText(buildStatusCard("status body", "green"));
|
|
25
|
-
|
|
26
|
-
expect(text).toContain("# 会话状态");
|
|
27
|
-
expect(text).toContain("status body");
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it("converts schema 2.0 progress cards", () => {
|
|
31
|
-
const text = cardJsonToPlainText(buildProgressCard("stream body"));
|
|
32
|
-
|
|
33
|
-
expect(text).toContain("# 生成中...");
|
|
34
|
-
expect(text).toContain("stream body");
|
|
35
|
-
expect(text).toContain("/status");
|
|
36
|
-
expect(text).toContain("/stop");
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it("returns null for invalid card json", () => {
|
|
40
|
-
expect(cardJsonToPlainText("{bad json")).toBeNull();
|
|
41
|
-
});
|
|
42
|
-
});
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
|
|
3
|
+
import { cardJsonToPlainText } from "../card-plain-text.ts";
|
|
4
|
+
import {
|
|
5
|
+
buildHelpCard,
|
|
6
|
+
buildProgressCard,
|
|
7
|
+
buildStatusCard,
|
|
8
|
+
} from "../cards.ts";
|
|
9
|
+
|
|
10
|
+
describe("cardJsonToPlainText", () => {
|
|
11
|
+
it("converts help cards to readable text with commands", () => {
|
|
12
|
+
const text = cardJsonToPlainText(buildHelpCard("hello"));
|
|
13
|
+
|
|
14
|
+
expect(text).toContain("# ChatCCC");
|
|
15
|
+
expect(text).toContain("hello");
|
|
16
|
+
expect(text).toContain("/new");
|
|
17
|
+
expect(text).toContain("/new cursor");
|
|
18
|
+
expect(text).toContain("/new codex");
|
|
19
|
+
expect(text).toContain("/restart");
|
|
20
|
+
expect(text).toContain("/cd");
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("converts status cards from v1 card format", () => {
|
|
24
|
+
const text = cardJsonToPlainText(buildStatusCard("status body", "green"));
|
|
25
|
+
|
|
26
|
+
expect(text).toContain("# 会话状态");
|
|
27
|
+
expect(text).toContain("status body");
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("converts schema 2.0 progress cards", () => {
|
|
31
|
+
const text = cardJsonToPlainText(buildProgressCard("stream body"));
|
|
32
|
+
|
|
33
|
+
expect(text).toContain("# 生成中...");
|
|
34
|
+
expect(text).toContain("stream body");
|
|
35
|
+
expect(text).toContain("/status");
|
|
36
|
+
expect(text).toContain("/stop");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("returns null for invalid card json", () => {
|
|
40
|
+
expect(cardJsonToPlainText("{bad json")).toBeNull();
|
|
41
|
+
});
|
|
42
|
+
});
|