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.
Files changed (130) hide show
  1. package/.agents/skills/create-chatccc-feishu-app/SKILL.md +85 -85
  2. package/.claude/skills/create-chatccc-feishu-app/SKILL.md +85 -85
  3. package/.cursor/skills/create-chatccc-feishu-app/SKILL.md +85 -85
  4. package/README.md +90 -90
  5. package/agent-prompts/claude_specific.md +45 -45
  6. package/agent-prompts/codex_specific.md +2 -2
  7. package/agent-prompts/cursor_specific.md +13 -13
  8. package/bin/cccagent.mjs +17 -17
  9. package/im-skills/feishu-skill/receive-send-file.md +63 -63
  10. package/im-skills/feishu-skill/receive-send-image.md +24 -24
  11. package/im-skills/feishu-skill/skill.md +3 -3
  12. package/im-skills/wechat-file-skill/receive-send-file.md +38 -38
  13. package/im-skills/wechat-file-skill/send-file.mjs +83 -83
  14. package/im-skills/wechat-file-skill/skill.md +10 -10
  15. package/im-skills/wechat-image-skill/skill.md +10 -10
  16. package/im-skills/wechat-video-skill/receive-send-video.md +38 -38
  17. package/im-skills/wechat-video-skill/send-video.mjs +79 -79
  18. package/im-skills/wechat-video-skill/skill.md +10 -10
  19. package/package.json +1 -1
  20. package/scripts/postinstall-sharp-check.mjs +58 -58
  21. package/src/__tests__/agent-activity.test.ts +86 -86
  22. package/src/__tests__/agent-delegate-task-rpc.test.ts +165 -165
  23. package/src/__tests__/agent-reload-config-rpc.test.ts +99 -99
  24. package/src/__tests__/builtin-chat-session.test.ts +449 -393
  25. package/src/__tests__/builtin-cli-json.test.ts +39 -39
  26. package/src/__tests__/builtin-config.test.ts +26 -26
  27. package/src/__tests__/builtin-context.test.ts +319 -202
  28. package/src/__tests__/builtin-file-tools.test.ts +240 -240
  29. package/src/__tests__/builtin-permissions.test.ts +211 -211
  30. package/src/__tests__/builtin-session-search.test.ts +228 -0
  31. package/src/__tests__/builtin-session-select.test.ts +116 -116
  32. package/src/__tests__/builtin-sigint.test.ts +56 -56
  33. package/src/__tests__/builtin-skills.test.ts +284 -284
  34. package/src/__tests__/builtin-web-tools.test.ts +220 -220
  35. package/src/__tests__/card-action-routing.test.ts +18 -18
  36. package/src/__tests__/cardkit.test.ts +60 -60
  37. package/src/__tests__/ccc-adapter.test.ts +170 -170
  38. package/src/__tests__/chatgpt-subscription-rpc.test.ts +89 -89
  39. package/src/__tests__/chatgpt-subscription.test.ts +135 -135
  40. package/src/__tests__/chrome-devtools-guard.test.ts +165 -165
  41. package/src/__tests__/claude-adapter.test.ts +614 -614
  42. package/src/__tests__/claude-raw-stream-log.test.ts +87 -87
  43. package/src/__tests__/codex-adapter.test.ts +58 -58
  44. package/src/__tests__/codex-raw-stream-log.test.ts +170 -170
  45. package/src/__tests__/codex-reset-actions.test.ts +146 -146
  46. package/src/__tests__/crash-logging.test.ts +95 -1
  47. package/src/__tests__/cursor-adapter.test.ts +268 -268
  48. package/src/__tests__/feishu-api.test.ts +60 -60
  49. package/src/__tests__/feishu-avatar.test.ts +164 -164
  50. package/src/__tests__/feishu-message-ingress.test.ts +138 -138
  51. package/src/__tests__/feishu-platform.test.ts +22 -22
  52. package/src/__tests__/format-message.test.ts +47 -47
  53. package/src/__tests__/jsonl-stream.test.ts +79 -79
  54. package/src/__tests__/package-files.test.ts +24 -24
  55. package/src/__tests__/privacy.test.ts +198 -198
  56. package/src/__tests__/progress-reducer.test.ts +121 -121
  57. package/src/__tests__/raw-stream-log.test.ts +106 -106
  58. package/src/__tests__/response-stall.test.ts +49 -49
  59. package/src/__tests__/restart.test.ts +55 -10
  60. package/src/__tests__/session.test.ts +199 -199
  61. package/src/__tests__/shared-prefix.test.ts +36 -36
  62. package/src/__tests__/sim-platform.test.ts +16 -16
  63. package/src/__tests__/startup-lifecycle.test.ts +231 -231
  64. package/src/__tests__/stop-session.test.ts +34 -34
  65. package/src/__tests__/stream-state.test.ts +42 -42
  66. package/src/__tests__/terminal-error.test.ts +54 -54
  67. package/src/__tests__/terminal-renderer.test.ts +247 -247
  68. package/src/__tests__/update-command-guard.test.ts +144 -144
  69. package/src/__tests__/web-ui.test.ts +326 -326
  70. package/src/adapters/adapter-interface.ts +33 -33
  71. package/src/adapters/ccc-adapter.ts +141 -141
  72. package/src/adapters/claude-adapter.ts +620 -620
  73. package/src/adapters/claude-session-meta-store.ts +120 -120
  74. package/src/adapters/codex-adapter.ts +426 -426
  75. package/src/adapters/cursor-adapter.ts +681 -681
  76. package/src/adapters/jsonl-stream.ts +157 -157
  77. package/src/adapters/raw-stream-log.ts +124 -124
  78. package/src/adapters/resource-monitor.ts +140 -140
  79. package/src/agent-activity.ts +175 -175
  80. package/src/agent-delegate-task-rpc.ts +153 -153
  81. package/src/agent-delegate-task.ts +91 -91
  82. package/src/agent-reload-config-rpc.ts +34 -34
  83. package/src/agent-stop-stuck.ts +129 -129
  84. package/src/builtin/cli.ts +6 -0
  85. package/src/builtin/config.ts +84 -84
  86. package/src/builtin/context.ts +465 -391
  87. package/src/builtin/file-log.ts +38 -38
  88. package/src/builtin/file-tools.ts +34 -0
  89. package/src/builtin/index.ts +24 -23
  90. package/src/builtin/proc-tree-kill.ts +61 -61
  91. package/src/builtin/progress/cards-helpers.ts +76 -76
  92. package/src/builtin/progress/reducer.ts +113 -113
  93. package/src/builtin/progress/terminal-renderer.ts +294 -294
  94. package/src/builtin/progress/view.ts +77 -77
  95. package/src/builtin/raw-stream-log.ts +124 -124
  96. package/src/builtin/session-search.ts +363 -0
  97. package/src/builtin/session-select.ts +48 -48
  98. package/src/builtin/sigint.ts +50 -50
  99. package/src/builtin/skills.ts +205 -205
  100. package/src/builtin/web-tools.ts +313 -313
  101. package/src/card-action-routing.ts +14 -14
  102. package/src/chatgpt-subscription-rpc.ts +27 -27
  103. package/src/chatgpt-subscription.ts +299 -299
  104. package/src/chrome-devtools-guard.ts +318 -318
  105. package/src/codex-reset-actions.ts +184 -184
  106. package/src/feishu-api.ts +193 -193
  107. package/src/feishu-message-ingress.ts +195 -195
  108. package/src/feishu-platform.ts +20 -20
  109. package/src/format-message.ts +293 -293
  110. package/src/index.ts +6 -1
  111. package/src/litellm-proxy.ts +374 -374
  112. package/src/orchestrator.ts +36 -13
  113. package/src/platform-adapter.ts +6 -6
  114. package/src/privacy.ts +118 -118
  115. package/src/progress/reducer.ts +113 -113
  116. package/src/progress/terminal-renderer.ts +294 -294
  117. package/src/progress/view.ts +77 -77
  118. package/src/response-stall.ts +28 -28
  119. package/src/runtime-reload.ts +34 -34
  120. package/src/session-chat-binding.ts +82 -82
  121. package/src/session-name.ts +8 -8
  122. package/src/session.ts +201 -201
  123. package/src/shared-prefix.ts +29 -29
  124. package/src/shared.ts +44 -0
  125. package/src/sim-platform.ts +20 -20
  126. package/src/startup-lifecycle.ts +250 -250
  127. package/src/stream-state.ts +21 -21
  128. package/src/terminal-error.ts +129 -129
  129. package/src/turn-cards.ts +117 -117
  130. package/src/update-command-guard.ts +165 -165
@@ -1,25 +1,25 @@
1
- # Sending & Receiving Images
2
-
3
- ## Send Images
4
-
5
- **Use the node script below. Do NOT use curl or raw HTTP — the script correctly handles errors and exits non-zero on failure.**
6
-
7
- ### Script
8
-
9
- ```bash
10
- node "{{send_image_script}}" --url "{{send_image_url}}" --session-id "{{session_id}}" --path "<absolute image path>" --caption "<optional caption>"
11
- ```
12
-
13
- ### Rules
14
-
15
- - Use the node script above — never curl or raw HTTP.
16
- - Save or choose a local image file first.
17
- - Use an absolute local path.
18
- - Supported formats: .png, .jpg, .jpeg, .webp, .gif, .bmp.
19
- - Max image size: 10MB.
20
- - Only send an image when the user asked for one or when it materially helps the answer.
21
- - **If the script fails (non-zero exit), read stderr for the error. Do NOT retry with the same path. Either fix the problem (wrong extension, missing file, etc.) or tell the user what the error was. Never retry more than once.**
22
-
23
- ## Receive Images
24
-
1
+ # Sending & Receiving Images
2
+
3
+ ## Send Images
4
+
5
+ **Use the node script below. Do NOT use curl or raw HTTP — the script correctly handles errors and exits non-zero on failure.**
6
+
7
+ ### Script
8
+
9
+ ```bash
10
+ node "{{send_image_script}}" --url "{{send_image_url}}" --session-id "{{session_id}}" --path "<absolute image path>" --caption "<optional caption>"
11
+ ```
12
+
13
+ ### Rules
14
+
15
+ - Use the node script above — never curl or raw HTTP.
16
+ - Save or choose a local image file first.
17
+ - Use an absolute local path.
18
+ - Supported formats: .png, .jpg, .jpeg, .webp, .gif, .bmp.
19
+ - Max image size: 10MB.
20
+ - Only send an image when the user asked for one or when it materially helps the answer.
21
+ - **If the script fails (non-zero exit), read stderr for the error. Do NOT retry with the same path. Either fix the problem (wrong extension, missing file, etc.) or tell the user what the error was. Never retry more than once.**
22
+
23
+ ## Receive Images
24
+
25
25
  Images sent to the bot are automatically downloaded to `~/.chatccc/images/downloads/`. The message contains an `image_key` that maps to the cached file.
@@ -7,6 +7,6 @@ Current working directory: {{cwd}}
7
7
 
8
8
  Use local endpoints instead of calling Feishu Open Platform directly.
9
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
- - **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`
12
- - **Delegate a task to a new agent conversation**: POST `{{delegate_task_url}}` with `{"tool":"codex|claude|cursor","cwd":"<absolute working directory>","open_id":"<Feishu user open_id>","prompt":"<first task>"}`. Use `open_ids` for multiple users. This uses the normal ChatCCC prompt flow, so project prompt injection and IM skills still apply.
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
+ - **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`
12
+ - **Delegate a task to a new agent conversation**: POST `{{delegate_task_url}}` with `{"tool":"codex|claude|cursor","cwd":"<absolute working directory>","open_id":"<Feishu user open_id>","prompt":"<first task>"}`. Use `open_ids` for multiple users. This uses the normal ChatCCC prompt flow, so project prompt injection and IM skills still apply.
@@ -1,39 +1,39 @@
1
- # Receiving & Sending Files (WeChat)
2
-
3
- ## Receive Files
4
-
5
- Files sent to the bot are **automatically downloaded** to `~/.chatccc/images/downloads/` with the `wx_` filename prefix.
6
-
7
- The message text you receive will include the downloaded path in the format:
8
- ```
9
- [文件] C:\Users\<用户名>\.chatccc\images\downloads\wx_<key>_<filename>
10
- ```
11
-
12
- You can read the file at that path to understand what the user sent.
13
-
14
- ## Send Files
15
-
16
- ### Script (recommended)
17
- ```bash
18
- node "{{wechat_send_file_script}}" --path "<absolute file path>" --caption "<optional caption>"
19
- ```
20
-
21
- The script reads the current WeChat session's auth token, chat ID, and context token from `~/.chatccc/state/ilink-auth.json`, then sends the file to the most recent chat.
22
-
23
- ### Direct usage in test scripts
24
-
25
- The underlying SDK call is:
26
- ```ts
27
- import { Client as OpenIlinkWire } from "@openilink/openilink-sdk-node";
28
- const wire = new OpenIlinkWire(token, { base_url: baseUrl });
29
- await wire.sendMediaFile(chatId, contextToken, fileBuffer, "filename.pdf", "caption");
30
- ```
31
-
32
- ### Rules
33
-
34
- - Save or choose a local file first.
35
- - Use an absolute local path.
36
- - Supported formats: .pdf, .doc, .docx, .xls, .xlsx, .csv, .ppt, .pptx, .txt, .zip, .tar, .gz, .rar, .7z, .mp3, .wav, .ogg, .aac, .m4a.
37
- - Max file size: 100MB.
38
- - Only send a file when the user asked for one or when it materially helps the answer.
1
+ # Receiving & Sending Files (WeChat)
2
+
3
+ ## Receive Files
4
+
5
+ Files sent to the bot are **automatically downloaded** to `~/.chatccc/images/downloads/` with the `wx_` filename prefix.
6
+
7
+ The message text you receive will include the downloaded path in the format:
8
+ ```
9
+ [文件] C:\Users\<用户名>\.chatccc\images\downloads\wx_<key>_<filename>
10
+ ```
11
+
12
+ You can read the file at that path to understand what the user sent.
13
+
14
+ ## Send Files
15
+
16
+ ### Script (recommended)
17
+ ```bash
18
+ node "{{wechat_send_file_script}}" --path "<absolute file path>" --caption "<optional caption>"
19
+ ```
20
+
21
+ The script reads the current WeChat session's auth token, chat ID, and context token from `~/.chatccc/state/ilink-auth.json`, then sends the file to the most recent chat.
22
+
23
+ ### Direct usage in test scripts
24
+
25
+ The underlying SDK call is:
26
+ ```ts
27
+ import { Client as OpenIlinkWire } from "@openilink/openilink-sdk-node";
28
+ const wire = new OpenIlinkWire(token, { base_url: baseUrl });
29
+ await wire.sendMediaFile(chatId, contextToken, fileBuffer, "filename.pdf", "caption");
30
+ ```
31
+
32
+ ### Rules
33
+
34
+ - Save or choose a local file first.
35
+ - Use an absolute local path.
36
+ - Supported formats: .pdf, .doc, .docx, .xls, .xlsx, .csv, .ppt, .pptx, .txt, .zip, .tar, .gz, .rar, .7z, .mp3, .wav, .ogg, .aac, .m4a.
37
+ - Max file size: 100MB.
38
+ - Only send a file when the user asked for one or when it materially helps the answer.
39
39
  - **Claw 限制**: 文件发送也计入微信 claw 连发计数,连续 10 条未回复会截断。
@@ -1,84 +1,84 @@
1
- #!/usr/bin/env node
2
- import { existsSync, readFileSync, statSync } from "node:fs";
3
- import { basename, extname, join } from "node:path";
4
- import { homedir } from "node:os";
5
-
6
- import { Client as OpenIlinkWire } from "@openilink/openilink-sdk-node";
7
-
8
- const ILINK_AUTH_PATH = join(homedir(), ".chatccc", "state", "ilink-auth.json");
9
- const MAX_FILE_BYTES = 100 * 1024 * 1024;
10
- const ALLOWED_EXTS = new Set([
11
- ".pdf", ".doc", ".docx", ".xls", ".xlsx", ".csv", ".ppt", ".pptx",
12
- ".txt", ".zip", ".tar", ".gz", ".rar", ".7z",
13
- ".mp3", ".wav", ".ogg", ".aac", ".m4a",
14
- ]);
15
-
16
- function parseArgs(argv) {
17
- const result = {};
18
- for (let i = 0; i < argv.length; i++) {
19
- const key = argv[i];
20
- if (!key.startsWith("--")) continue;
21
- const value = argv[i + 1] && !argv[i + 1].startsWith("--") ? argv[++i] : "true";
22
- result[key.slice(2)] = value;
23
- }
24
- return result;
25
- }
26
-
27
- function usage() {
28
- console.error(`Usage:
29
- node ${basename(process.argv[1])} --path <absolute file path> [--caption <text>]`);
30
- }
31
-
32
- async function main() {
33
- const args = parseArgs(process.argv.slice(2));
34
- const filePath = args.path;
35
- const caption = args.caption || "";
36
-
37
- if (!filePath) {
38
- usage();
39
- process.exit(1);
40
- }
41
-
42
- const ext = extname(filePath).toLowerCase();
43
- if (!ALLOWED_EXTS.has(ext)) {
44
- console.error(`Unsupported file extension: ${ext || "(none)"}`);
45
- process.exit(1);
46
- }
47
-
48
- const st = statSync(filePath);
49
- if (!st.isFile()) {
50
- console.error("File path is not a file");
51
- process.exit(1);
52
- }
53
- if (st.size > MAX_FILE_BYTES) {
54
- console.error("File exceeds 100MB limit");
55
- process.exit(1);
56
- }
57
-
58
- if (!existsSync(ILINK_AUTH_PATH)) {
59
- console.error(`Auth snapshot not found: ${ILINK_AUTH_PATH}`);
60
- console.error("Make sure the WeChat iLink platform is logged in.");
61
- process.exit(1);
62
- }
63
-
64
- const snap = JSON.parse(readFileSync(ILINK_AUTH_PATH, "utf8"));
65
- if (!snap.token || !snap.lastChatId || !snap.contextToken) {
66
- console.error("Auth snapshot missing token, lastChatId, or contextToken.");
67
- process.exit(1);
68
- }
69
-
70
- const fileData = readFileSync(filePath);
71
- const fileName = basename(filePath);
72
-
73
- const wire = new OpenIlinkWire(snap.token, {
74
- base_url: snap.baseUrl,
75
- });
76
-
77
- await wire.sendMediaFile(snap.lastChatId, snap.contextToken, fileData, fileName, caption);
78
- console.log(JSON.stringify({ ok: true, sentTo: 1 }));
79
- }
80
-
81
- main().catch((err) => {
82
- console.error(err instanceof Error ? err.message : String(err));
83
- process.exit(1);
1
+ #!/usr/bin/env node
2
+ import { existsSync, readFileSync, statSync } from "node:fs";
3
+ import { basename, extname, join } from "node:path";
4
+ import { homedir } from "node:os";
5
+
6
+ import { Client as OpenIlinkWire } from "@openilink/openilink-sdk-node";
7
+
8
+ const ILINK_AUTH_PATH = join(homedir(), ".chatccc", "state", "ilink-auth.json");
9
+ const MAX_FILE_BYTES = 100 * 1024 * 1024;
10
+ const ALLOWED_EXTS = new Set([
11
+ ".pdf", ".doc", ".docx", ".xls", ".xlsx", ".csv", ".ppt", ".pptx",
12
+ ".txt", ".zip", ".tar", ".gz", ".rar", ".7z",
13
+ ".mp3", ".wav", ".ogg", ".aac", ".m4a",
14
+ ]);
15
+
16
+ function parseArgs(argv) {
17
+ const result = {};
18
+ for (let i = 0; i < argv.length; i++) {
19
+ const key = argv[i];
20
+ if (!key.startsWith("--")) continue;
21
+ const value = argv[i + 1] && !argv[i + 1].startsWith("--") ? argv[++i] : "true";
22
+ result[key.slice(2)] = value;
23
+ }
24
+ return result;
25
+ }
26
+
27
+ function usage() {
28
+ console.error(`Usage:
29
+ node ${basename(process.argv[1])} --path <absolute file path> [--caption <text>]`);
30
+ }
31
+
32
+ async function main() {
33
+ const args = parseArgs(process.argv.slice(2));
34
+ const filePath = args.path;
35
+ const caption = args.caption || "";
36
+
37
+ if (!filePath) {
38
+ usage();
39
+ process.exit(1);
40
+ }
41
+
42
+ const ext = extname(filePath).toLowerCase();
43
+ if (!ALLOWED_EXTS.has(ext)) {
44
+ console.error(`Unsupported file extension: ${ext || "(none)"}`);
45
+ process.exit(1);
46
+ }
47
+
48
+ const st = statSync(filePath);
49
+ if (!st.isFile()) {
50
+ console.error("File path is not a file");
51
+ process.exit(1);
52
+ }
53
+ if (st.size > MAX_FILE_BYTES) {
54
+ console.error("File exceeds 100MB limit");
55
+ process.exit(1);
56
+ }
57
+
58
+ if (!existsSync(ILINK_AUTH_PATH)) {
59
+ console.error(`Auth snapshot not found: ${ILINK_AUTH_PATH}`);
60
+ console.error("Make sure the WeChat iLink platform is logged in.");
61
+ process.exit(1);
62
+ }
63
+
64
+ const snap = JSON.parse(readFileSync(ILINK_AUTH_PATH, "utf8"));
65
+ if (!snap.token || !snap.lastChatId || !snap.contextToken) {
66
+ console.error("Auth snapshot missing token, lastChatId, or contextToken.");
67
+ process.exit(1);
68
+ }
69
+
70
+ const fileData = readFileSync(filePath);
71
+ const fileName = basename(filePath);
72
+
73
+ const wire = new OpenIlinkWire(snap.token, {
74
+ base_url: snap.baseUrl,
75
+ });
76
+
77
+ await wire.sendMediaFile(snap.lastChatId, snap.contextToken, fileData, fileName, caption);
78
+ console.log(JSON.stringify({ ok: true, sentTo: 1 }));
79
+ }
80
+
81
+ main().catch((err) => {
82
+ console.error(err instanceof Error ? err.message : String(err));
83
+ process.exit(1);
84
84
  });
@@ -1,11 +1,11 @@
1
- ---
2
- name: wechat-file-skill
3
- description: WeChat iLink local skills for sending and receiving files.
4
- ---
5
-
6
- Current working directory: {{cwd}}
7
-
8
- WeChat files are handled through the iLink SDK. The local server does NOT have HTTP RPC endpoints for WeChat; use the helper scripts below instead.
9
-
10
- - **Receive files**: Files sent to the bot are automatically downloaded to `~/.chatccc/images/downloads/` with the filename prefix `wx_`. The message text will include the local path as `[文件] <absolute path>` — read `{{im_skills_cache_dir}}/wechat-file-skill/receive-send-file.md`
1
+ ---
2
+ name: wechat-file-skill
3
+ description: WeChat iLink local skills for sending and receiving files.
4
+ ---
5
+
6
+ Current working directory: {{cwd}}
7
+
8
+ WeChat files are handled through the iLink SDK. The local server does NOT have HTTP RPC endpoints for WeChat; use the helper scripts below instead.
9
+
10
+ - **Receive files**: Files sent to the bot are automatically downloaded to `~/.chatccc/images/downloads/` with the filename prefix `wx_`. The message text will include the local path as `[文件] <absolute path>` — read `{{im_skills_cache_dir}}/wechat-file-skill/receive-send-file.md`
11
11
  - **Send files**: Use the send-file helper script — read `{{im_skills_cache_dir}}/wechat-file-skill/receive-send-file.md`
@@ -1,11 +1,11 @@
1
- ---
2
- name: wechat-image-skill
3
- description: WeChat iLink local skills for sending and receiving images.
4
- ---
5
-
6
- Current working directory: {{cwd}}
7
-
8
- WeChat images are handled through the iLink SDK. The local server does NOT have HTTP RPC endpoints for WeChat; use the helper scripts below instead.
9
-
10
- - **Receive images**: Images sent to the bot are automatically downloaded to `~/.chatccc/images/downloads/` with the filename prefix `wx_`. The message text will include the local path as `[图片] <absolute path>` — read `{{im_skills_cache_dir}}/wechat-image-skill/receive-send-image.md`
1
+ ---
2
+ name: wechat-image-skill
3
+ description: WeChat iLink local skills for sending and receiving images.
4
+ ---
5
+
6
+ Current working directory: {{cwd}}
7
+
8
+ WeChat images are handled through the iLink SDK. The local server does NOT have HTTP RPC endpoints for WeChat; use the helper scripts below instead.
9
+
10
+ - **Receive images**: Images sent to the bot are automatically downloaded to `~/.chatccc/images/downloads/` with the filename prefix `wx_`. The message text will include the local path as `[图片] <absolute path>` — read `{{im_skills_cache_dir}}/wechat-image-skill/receive-send-image.md`
11
11
  - **Send images**: Use the send-image helper script — read `{{im_skills_cache_dir}}/wechat-image-skill/receive-send-image.md`
@@ -1,39 +1,39 @@
1
- # Receiving & Sending Videos (WeChat)
2
-
3
- ## Receive Videos
4
-
5
- Videos sent to the bot are **automatically downloaded** to `~/.chatccc/images/downloads/` with the `wx_` filename prefix.
6
-
7
- The message text you receive will include the downloaded path in the format:
8
- ```
9
- [视频] C:\Users\<用户名>\.chatccc\images\downloads\wx_<key>.mp4
10
- ```
11
-
12
- You can read the video file at that path to understand what the user sent.
13
-
14
- ## Send Videos
15
-
16
- ### Script (recommended)
17
- ```bash
18
- node "{{wechat_send_video_script}}" --path "<absolute video path>" --caption "<optional caption>"
19
- ```
20
-
21
- The script reads the current WeChat session's auth token, chat ID, and context token from `~/.chatccc/state/ilink-auth.json`, then sends the video to the most recent chat.
22
-
23
- ### Direct usage in test scripts
24
-
25
- The underlying SDK call is:
26
- ```ts
27
- import { Client as OpenIlinkWire } from "@openilink/openilink-sdk-node";
28
- const wire = new OpenIlinkWire(token, { base_url: baseUrl });
29
- await wire.sendMediaFile(chatId, contextToken, videoBuffer, "video.mp4", "caption");
30
- ```
31
-
32
- ### Rules
33
-
34
- - Save or choose a local video file first.
35
- - Use an absolute local path.
36
- - Supported formats: .mp4, .mov, .avi, .mkv, .webm, .flv.
37
- - Max video size: 100MB.
38
- - Only send a video when the user asked for one or when it materially helps the answer.
1
+ # Receiving & Sending Videos (WeChat)
2
+
3
+ ## Receive Videos
4
+
5
+ Videos sent to the bot are **automatically downloaded** to `~/.chatccc/images/downloads/` with the `wx_` filename prefix.
6
+
7
+ The message text you receive will include the downloaded path in the format:
8
+ ```
9
+ [视频] C:\Users\<用户名>\.chatccc\images\downloads\wx_<key>.mp4
10
+ ```
11
+
12
+ You can read the video file at that path to understand what the user sent.
13
+
14
+ ## Send Videos
15
+
16
+ ### Script (recommended)
17
+ ```bash
18
+ node "{{wechat_send_video_script}}" --path "<absolute video path>" --caption "<optional caption>"
19
+ ```
20
+
21
+ The script reads the current WeChat session's auth token, chat ID, and context token from `~/.chatccc/state/ilink-auth.json`, then sends the video to the most recent chat.
22
+
23
+ ### Direct usage in test scripts
24
+
25
+ The underlying SDK call is:
26
+ ```ts
27
+ import { Client as OpenIlinkWire } from "@openilink/openilink-sdk-node";
28
+ const wire = new OpenIlinkWire(token, { base_url: baseUrl });
29
+ await wire.sendMediaFile(chatId, contextToken, videoBuffer, "video.mp4", "caption");
30
+ ```
31
+
32
+ ### Rules
33
+
34
+ - Save or choose a local video file first.
35
+ - Use an absolute local path.
36
+ - Supported formats: .mp4, .mov, .avi, .mkv, .webm, .flv.
37
+ - Max video size: 100MB.
38
+ - Only send a video when the user asked for one or when it materially helps the answer.
39
39
  - **Claw 限制**: 视频发送也计入微信 claw 连发计数,连续 10 条未回复会截断。
@@ -1,80 +1,80 @@
1
- #!/usr/bin/env node
2
- import { existsSync, readFileSync, statSync } from "node:fs";
3
- import { basename, extname, join } from "node:path";
4
- import { homedir } from "node:os";
5
-
6
- import { Client as OpenIlinkWire } from "@openilink/openilink-sdk-node";
7
-
8
- const ILINK_AUTH_PATH = join(homedir(), ".chatccc", "state", "ilink-auth.json");
9
- const MAX_VIDEO_BYTES = 100 * 1024 * 1024;
10
- const ALLOWED_EXTS = new Set([".mp4", ".mov", ".avi", ".mkv", ".webm", ".flv"]);
11
-
12
- function parseArgs(argv) {
13
- const result = {};
14
- for (let i = 0; i < argv.length; i++) {
15
- const key = argv[i];
16
- if (!key.startsWith("--")) continue;
17
- const value = argv[i + 1] && !argv[i + 1].startsWith("--") ? argv[++i] : "true";
18
- result[key.slice(2)] = value;
19
- }
20
- return result;
21
- }
22
-
23
- function usage() {
24
- console.error(`Usage:
25
- node ${basename(process.argv[1])} --path <absolute video path> [--caption <text>]`);
26
- }
27
-
28
- async function main() {
29
- const args = parseArgs(process.argv.slice(2));
30
- const videoPath = args.path;
31
- const caption = args.caption || "";
32
-
33
- if (!videoPath) {
34
- usage();
35
- process.exit(1);
36
- }
37
-
38
- const ext = extname(videoPath).toLowerCase();
39
- if (!ALLOWED_EXTS.has(ext)) {
40
- console.error(`Unsupported video extension: ${ext || "(none)"}`);
41
- process.exit(1);
42
- }
43
-
44
- const st = statSync(videoPath);
45
- if (!st.isFile()) {
46
- console.error("Video path is not a file");
47
- process.exit(1);
48
- }
49
- if (st.size > MAX_VIDEO_BYTES) {
50
- console.error("Video file exceeds 100MB limit");
51
- process.exit(1);
52
- }
53
-
54
- if (!existsSync(ILINK_AUTH_PATH)) {
55
- console.error(`Auth snapshot not found: ${ILINK_AUTH_PATH}`);
56
- console.error("Make sure the WeChat iLink platform is logged in.");
57
- process.exit(1);
58
- }
59
-
60
- const snap = JSON.parse(readFileSync(ILINK_AUTH_PATH, "utf8"));
61
- if (!snap.token || !snap.lastChatId || !snap.contextToken) {
62
- console.error("Auth snapshot missing token, lastChatId, or contextToken.");
63
- process.exit(1);
64
- }
65
-
66
- const videoData = readFileSync(videoPath);
67
- const fileName = basename(videoPath);
68
-
69
- const wire = new OpenIlinkWire(snap.token, {
70
- base_url: snap.baseUrl,
71
- });
72
-
73
- await wire.sendMediaFile(snap.lastChatId, snap.contextToken, videoData, fileName, caption);
74
- console.log(JSON.stringify({ ok: true, sentTo: 1 }));
75
- }
76
-
77
- main().catch((err) => {
78
- console.error(err instanceof Error ? err.message : String(err));
79
- process.exit(1);
1
+ #!/usr/bin/env node
2
+ import { existsSync, readFileSync, statSync } from "node:fs";
3
+ import { basename, extname, join } from "node:path";
4
+ import { homedir } from "node:os";
5
+
6
+ import { Client as OpenIlinkWire } from "@openilink/openilink-sdk-node";
7
+
8
+ const ILINK_AUTH_PATH = join(homedir(), ".chatccc", "state", "ilink-auth.json");
9
+ const MAX_VIDEO_BYTES = 100 * 1024 * 1024;
10
+ const ALLOWED_EXTS = new Set([".mp4", ".mov", ".avi", ".mkv", ".webm", ".flv"]);
11
+
12
+ function parseArgs(argv) {
13
+ const result = {};
14
+ for (let i = 0; i < argv.length; i++) {
15
+ const key = argv[i];
16
+ if (!key.startsWith("--")) continue;
17
+ const value = argv[i + 1] && !argv[i + 1].startsWith("--") ? argv[++i] : "true";
18
+ result[key.slice(2)] = value;
19
+ }
20
+ return result;
21
+ }
22
+
23
+ function usage() {
24
+ console.error(`Usage:
25
+ node ${basename(process.argv[1])} --path <absolute video path> [--caption <text>]`);
26
+ }
27
+
28
+ async function main() {
29
+ const args = parseArgs(process.argv.slice(2));
30
+ const videoPath = args.path;
31
+ const caption = args.caption || "";
32
+
33
+ if (!videoPath) {
34
+ usage();
35
+ process.exit(1);
36
+ }
37
+
38
+ const ext = extname(videoPath).toLowerCase();
39
+ if (!ALLOWED_EXTS.has(ext)) {
40
+ console.error(`Unsupported video extension: ${ext || "(none)"}`);
41
+ process.exit(1);
42
+ }
43
+
44
+ const st = statSync(videoPath);
45
+ if (!st.isFile()) {
46
+ console.error("Video path is not a file");
47
+ process.exit(1);
48
+ }
49
+ if (st.size > MAX_VIDEO_BYTES) {
50
+ console.error("Video file exceeds 100MB limit");
51
+ process.exit(1);
52
+ }
53
+
54
+ if (!existsSync(ILINK_AUTH_PATH)) {
55
+ console.error(`Auth snapshot not found: ${ILINK_AUTH_PATH}`);
56
+ console.error("Make sure the WeChat iLink platform is logged in.");
57
+ process.exit(1);
58
+ }
59
+
60
+ const snap = JSON.parse(readFileSync(ILINK_AUTH_PATH, "utf8"));
61
+ if (!snap.token || !snap.lastChatId || !snap.contextToken) {
62
+ console.error("Auth snapshot missing token, lastChatId, or contextToken.");
63
+ process.exit(1);
64
+ }
65
+
66
+ const videoData = readFileSync(videoPath);
67
+ const fileName = basename(videoPath);
68
+
69
+ const wire = new OpenIlinkWire(snap.token, {
70
+ base_url: snap.baseUrl,
71
+ });
72
+
73
+ await wire.sendMediaFile(snap.lastChatId, snap.contextToken, videoData, fileName, caption);
74
+ console.log(JSON.stringify({ ok: true, sentTo: 1 }));
75
+ }
76
+
77
+ main().catch((err) => {
78
+ console.error(err instanceof Error ? err.message : String(err));
79
+ process.exit(1);
80
80
  });
@@ -1,11 +1,11 @@
1
- ---
2
- name: wechat-video-skill
3
- description: WeChat iLink local skills for sending and receiving videos.
4
- ---
5
-
6
- Current working directory: {{cwd}}
7
-
8
- WeChat videos are handled through the iLink SDK. The local server does NOT have HTTP RPC endpoints for WeChat; use the helper scripts below instead.
9
-
10
- - **Receive videos**: Videos sent to the bot are automatically downloaded to `~/.chatccc/images/downloads/` with the filename prefix `wx_`. The message text will include the local path as `[视频] <absolute path>` — read `{{im_skills_cache_dir}}/wechat-video-skill/receive-send-video.md`
1
+ ---
2
+ name: wechat-video-skill
3
+ description: WeChat iLink local skills for sending and receiving videos.
4
+ ---
5
+
6
+ Current working directory: {{cwd}}
7
+
8
+ WeChat videos are handled through the iLink SDK. The local server does NOT have HTTP RPC endpoints for WeChat; use the helper scripts below instead.
9
+
10
+ - **Receive videos**: Videos sent to the bot are automatically downloaded to `~/.chatccc/images/downloads/` with the filename prefix `wx_`. The message text will include the local path as `[视频] <absolute path>` — read `{{im_skills_cache_dir}}/wechat-video-skill/receive-send-video.md`
11
11
  - **Send videos**: Use the send-video helper script — read `{{im_skills_cache_dir}}/wechat-video-skill/receive-send-video.md`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chatccc",
3
- "version": "0.2.231",
3
+ "version": "0.2.233",
4
4
  "description": "Feishu bot bridge for Claude Code",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",