chatccc 0.2.231 → 0.2.232

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 (127) 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__/cursor-adapter.test.ts +268 -268
  47. package/src/__tests__/feishu-api.test.ts +60 -60
  48. package/src/__tests__/feishu-avatar.test.ts +164 -164
  49. package/src/__tests__/feishu-message-ingress.test.ts +138 -138
  50. package/src/__tests__/feishu-platform.test.ts +22 -22
  51. package/src/__tests__/format-message.test.ts +47 -47
  52. package/src/__tests__/jsonl-stream.test.ts +79 -79
  53. package/src/__tests__/package-files.test.ts +24 -24
  54. package/src/__tests__/privacy.test.ts +198 -198
  55. package/src/__tests__/progress-reducer.test.ts +121 -121
  56. package/src/__tests__/raw-stream-log.test.ts +106 -106
  57. package/src/__tests__/response-stall.test.ts +49 -49
  58. package/src/__tests__/restart.test.ts +132 -132
  59. package/src/__tests__/session.test.ts +199 -199
  60. package/src/__tests__/shared-prefix.test.ts +36 -36
  61. package/src/__tests__/sim-platform.test.ts +16 -16
  62. package/src/__tests__/startup-lifecycle.test.ts +231 -231
  63. package/src/__tests__/stop-session.test.ts +34 -34
  64. package/src/__tests__/stream-state.test.ts +42 -42
  65. package/src/__tests__/terminal-error.test.ts +54 -54
  66. package/src/__tests__/terminal-renderer.test.ts +247 -247
  67. package/src/__tests__/update-command-guard.test.ts +144 -144
  68. package/src/__tests__/web-ui.test.ts +326 -326
  69. package/src/adapters/adapter-interface.ts +33 -33
  70. package/src/adapters/ccc-adapter.ts +141 -141
  71. package/src/adapters/claude-adapter.ts +620 -620
  72. package/src/adapters/claude-session-meta-store.ts +120 -120
  73. package/src/adapters/codex-adapter.ts +426 -426
  74. package/src/adapters/cursor-adapter.ts +681 -681
  75. package/src/adapters/jsonl-stream.ts +157 -157
  76. package/src/adapters/raw-stream-log.ts +124 -124
  77. package/src/adapters/resource-monitor.ts +140 -140
  78. package/src/agent-activity.ts +175 -175
  79. package/src/agent-delegate-task-rpc.ts +153 -153
  80. package/src/agent-delegate-task.ts +91 -91
  81. package/src/agent-reload-config-rpc.ts +34 -34
  82. package/src/agent-stop-stuck.ts +129 -129
  83. package/src/builtin/cli.ts +6 -0
  84. package/src/builtin/config.ts +84 -84
  85. package/src/builtin/context.ts +465 -391
  86. package/src/builtin/file-log.ts +38 -38
  87. package/src/builtin/file-tools.ts +34 -0
  88. package/src/builtin/index.ts +24 -23
  89. package/src/builtin/proc-tree-kill.ts +61 -61
  90. package/src/builtin/progress/cards-helpers.ts +76 -76
  91. package/src/builtin/progress/reducer.ts +113 -113
  92. package/src/builtin/progress/terminal-renderer.ts +294 -294
  93. package/src/builtin/progress/view.ts +77 -77
  94. package/src/builtin/raw-stream-log.ts +124 -124
  95. package/src/builtin/session-search.ts +363 -0
  96. package/src/builtin/session-select.ts +48 -48
  97. package/src/builtin/sigint.ts +50 -50
  98. package/src/builtin/skills.ts +205 -205
  99. package/src/builtin/web-tools.ts +313 -313
  100. package/src/card-action-routing.ts +14 -14
  101. package/src/chatgpt-subscription-rpc.ts +27 -27
  102. package/src/chatgpt-subscription.ts +299 -299
  103. package/src/chrome-devtools-guard.ts +318 -318
  104. package/src/codex-reset-actions.ts +184 -184
  105. package/src/feishu-api.ts +193 -193
  106. package/src/feishu-message-ingress.ts +195 -195
  107. package/src/feishu-platform.ts +20 -20
  108. package/src/format-message.ts +293 -293
  109. package/src/litellm-proxy.ts +374 -374
  110. package/src/orchestrator.ts +2494 -2494
  111. package/src/platform-adapter.ts +6 -6
  112. package/src/privacy.ts +118 -118
  113. package/src/progress/reducer.ts +113 -113
  114. package/src/progress/terminal-renderer.ts +294 -294
  115. package/src/progress/view.ts +77 -77
  116. package/src/response-stall.ts +28 -28
  117. package/src/runtime-reload.ts +34 -34
  118. package/src/session-chat-binding.ts +82 -82
  119. package/src/session-name.ts +8 -8
  120. package/src/session.ts +201 -201
  121. package/src/shared-prefix.ts +29 -29
  122. package/src/sim-platform.ts +20 -20
  123. package/src/startup-lifecycle.ts +250 -250
  124. package/src/stream-state.ts +21 -21
  125. package/src/terminal-error.ts +129 -129
  126. package/src/turn-cards.ts +117 -117
  127. 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.232",
4
4
  "description": "Feishu bot bridge for Claude Code",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",