chatccc 0.2.201 → 0.2.203

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 (95) hide show
  1. package/README.md +23 -21
  2. package/agent-prompts/claude_specific.md +45 -45
  3. package/agent-prompts/codex_specific.md +2 -2
  4. package/agent-prompts/cursor_specific.md +13 -13
  5. package/bin/cccagent.mjs +17 -17
  6. package/config.sample.json +27 -27
  7. package/im-skills/feishu-skill/receive-send-file.md +63 -63
  8. package/im-skills/feishu-skill/receive-send-image.md +24 -24
  9. package/im-skills/feishu-skill/skill.md +3 -3
  10. package/im-skills/wechat-file-skill/receive-send-file.md +38 -38
  11. package/im-skills/wechat-file-skill/send-file.mjs +83 -83
  12. package/im-skills/wechat-file-skill/skill.md +10 -10
  13. package/im-skills/wechat-image-skill/skill.md +10 -10
  14. package/im-skills/wechat-video-skill/receive-send-video.md +38 -38
  15. package/im-skills/wechat-video-skill/send-video.mjs +79 -79
  16. package/im-skills/wechat-video-skill/skill.md +10 -10
  17. package/package.json +1 -1
  18. package/scripts/postinstall-sharp-check.mjs +58 -58
  19. package/src/__tests__/agent-delegate-task-rpc.test.ts +165 -165
  20. package/src/__tests__/agent-reload-config-rpc.test.ts +99 -99
  21. package/src/__tests__/builtin-chat-session.test.ts +277 -277
  22. package/src/__tests__/builtin-cli-json.test.ts +39 -39
  23. package/src/__tests__/builtin-config.test.ts +33 -33
  24. package/src/__tests__/builtin-context.test.ts +163 -163
  25. package/src/__tests__/builtin-file-tools.test.ts +224 -224
  26. package/src/__tests__/builtin-session-select.test.ts +116 -116
  27. package/src/__tests__/builtin-sigint.test.ts +56 -56
  28. package/src/__tests__/card-plain-text.test.ts +5 -5
  29. package/src/__tests__/cardkit.test.ts +60 -60
  30. package/src/__tests__/cards.test.ts +133 -122
  31. package/src/__tests__/ccc-adapter.test.ts +114 -114
  32. package/src/__tests__/chatgpt-subscription-rpc.test.ts +89 -89
  33. package/src/__tests__/chatgpt-subscription.test.ts +135 -135
  34. package/src/__tests__/chrome-devtools-guard.test.ts +165 -165
  35. package/src/__tests__/claude-adapter.test.ts +592 -592
  36. package/src/__tests__/claude-raw-stream-log.test.ts +87 -87
  37. package/src/__tests__/codex-raw-stream-log.test.ts +163 -163
  38. package/src/__tests__/codex-reset-actions.test.ts +146 -146
  39. package/src/__tests__/config-reload.test.ts +10 -10
  40. package/src/__tests__/config-sample.test.ts +18 -18
  41. package/src/__tests__/cursor-adapter.test.ts +226 -226
  42. package/src/__tests__/feishu-api.test.ts +60 -60
  43. package/src/__tests__/feishu-avatar.test.ts +90 -90
  44. package/src/__tests__/feishu-platform.test.ts +22 -22
  45. package/src/__tests__/format-message.test.ts +47 -47
  46. package/src/__tests__/jsonl-stream.test.ts +79 -79
  47. package/src/__tests__/orchestrator.test.ts +464 -301
  48. package/src/__tests__/privacy.test.ts +198 -198
  49. package/src/__tests__/raw-stream-log.test.ts +106 -106
  50. package/src/__tests__/session.test.ts +46 -44
  51. package/src/__tests__/shared-prefix.test.ts +36 -36
  52. package/src/__tests__/sim-platform.test.ts +12 -12
  53. package/src/__tests__/stream-state.test.ts +42 -42
  54. package/src/__tests__/update-command-guard.test.ts +144 -0
  55. package/src/__tests__/web-ui.test.ts +209 -209
  56. package/src/adapters/ccc-adapter.ts +121 -121
  57. package/src/adapters/claude-adapter.ts +603 -603
  58. package/src/adapters/claude-session-meta-store.ts +120 -120
  59. package/src/adapters/codex-adapter.ts +380 -380
  60. package/src/adapters/cursor-adapter.ts +673 -673
  61. package/src/adapters/jsonl-stream.ts +157 -157
  62. package/src/adapters/raw-stream-log.ts +124 -124
  63. package/src/adapters/resource-monitor.ts +140 -140
  64. package/src/agent-delegate-task-rpc.ts +153 -153
  65. package/src/agent-delegate-task.ts +81 -81
  66. package/src/agent-reload-config-rpc.ts +34 -34
  67. package/src/agent-stop-stuck.ts +129 -129
  68. package/src/builtin/cli.ts +473 -473
  69. package/src/builtin/context.ts +323 -323
  70. package/src/builtin/file-tools.ts +1072 -1072
  71. package/src/builtin/index.ts +404 -404
  72. package/src/builtin/session-select.ts +48 -48
  73. package/src/builtin/sigint.ts +50 -50
  74. package/src/cards.ts +222 -210
  75. package/src/chatgpt-subscription-rpc.ts +27 -27
  76. package/src/chatgpt-subscription.ts +299 -299
  77. package/src/chrome-devtools-guard.ts +318 -318
  78. package/src/codex-reset-actions.ts +184 -184
  79. package/src/config.ts +125 -125
  80. package/src/feishu-api.ts +118 -118
  81. package/src/feishu-platform.ts +20 -20
  82. package/src/format-message.ts +293 -293
  83. package/src/index.ts +48 -9
  84. package/src/litellm-proxy.ts +374 -374
  85. package/src/orchestrator.ts +391 -346
  86. package/src/privacy.ts +118 -118
  87. package/src/runtime-reload.ts +34 -34
  88. package/src/session-chat-binding.ts +6 -6
  89. package/src/session-name.ts +8 -8
  90. package/src/session.ts +177 -169
  91. package/src/shared-prefix.ts +29 -29
  92. package/src/sim-platform.ts +20 -20
  93. package/src/turn-cards.ts +117 -117
  94. package/src/update-command-guard.ts +165 -0
  95. package/src/web-ui.ts +205 -205
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  ChatCCC 把本地 AI 编程工具接入即时通讯软件。你可以在手机上发消息,让 Claude Code、Cursor Agent 或 Codex 继续写代码、查问题、跑命令;不用一直守在电脑前。
6
6
 
7
- 飞书是推荐入口:群聊就是会话,卡片能流式更新,体验完整。微信 iLink 更适合快速试用或临时使用:扫码即可接入,但只能走私聊文本模式。
7
+ 飞书是推荐入口:直接私聊机器人即可持续使用同一个专属会话,需要并行任务时再用 `/new` 创建独立会话群;卡片能流式更新,体验完整。微信 iLink 更适合快速试用或临时使用:扫码即可接入,但只能走私聊文本模式。
8
8
 
9
9
  <p align="center">
10
10
  <img src="images/img_readme_messages.jpg" alt="飞书会话列表" width="220" align="top" />
@@ -19,7 +19,7 @@ ChatCCC 把本地 AI 编程工具接入即时通讯软件。你可以在手机
19
19
  ## 为什么用 ChatCCC
20
20
 
21
21
  - **手机上也能用 AI 编程工具**:在飞书或微信发消息,就像在终端给 Agent 下指令。
22
- - **飞书体验更完整**:一群一会话、CardKit 卡片流式更新、支持群管理和多会话并行。
22
+ - **飞书体验更完整**:私聊可持续对话,`/new` 创建的一群一会话支持多任务并行,CardKit 卡片可流式更新。
23
23
  - **微信接入更轻**:不用创建飞书应用,启动后扫码即可在微信私聊里使用。
24
24
  - **多 Agent 切换**:`/new` 使用默认 Agent,也可以用 `/new claude`、`/new cursor`、`/new codex` 指定工具。
25
25
  - **群里能跑 git**:`/git status`、`/git pull`、`/git log` 会在当前会话工作目录执行,并把输出发回聊天窗口。
@@ -29,7 +29,7 @@ ChatCCC 把本地 AI 编程工具接入即时通讯软件。你可以在手机
29
29
  | 项目 | 飞书(推荐) | 微信 iLink |
30
30
  | --- | --- | --- |
31
31
  | 使用场景 | 长期主力使用 | 快速试用、临时远程控制 |
32
- | 会话形态 | 群聊,一群一会话 | 私聊,一对一 |
32
+ | 会话形态 | 私聊固定专属会话;`/new` 创建一群一会话 | 私聊,一对一 |
33
33
  | 消息展示 | CardKit 卡片,流式更新 | 纯文本,增量推送 |
34
34
  | `/new` | 自动创建新群并绑定新会话 | 在当前私聊里创建新会话 |
35
35
  | 多会话并行 | 直接切换不同群 | 支持并行,使用切换指令后未完成的任务会继续在后台进行,但不如飞书直观方便 |
@@ -164,11 +164,11 @@ npm run dev
164
164
 
165
165
  ### 2. 即时通讯软件配置
166
166
 
167
- #### 飞书(推荐)
168
-
169
- 如果 Chrome 已登录飞书开放平台,也可以让 Codex、Claude 或 Cursor 使用项目内的 [`create-chatccc-feishu-app`](.agents/skills/create-chatccc-feishu-app/SKILL.md) Skill,通过 Chrome DevTools/CDP 自动创建并配置机器人(默认 CDP 端口为 `15166`)。创建应用、开通权限和正式发布属于外部变更,执行前仍需明确确认;不要把 App Secret 写入对话或仓库。
170
-
171
- 1. 打开 [飞书开放平台](https://open.feishu.cn),创建一个**企业自建应用**。
167
+ #### 飞书(推荐)
168
+
169
+ 如果 Chrome 已登录飞书开放平台,也可以让 Codex、Claude 或 Cursor 使用项目内的 [`create-chatccc-feishu-app`](.agents/skills/create-chatccc-feishu-app/SKILL.md) Skill,通过 Chrome DevTools/CDP 自动创建并配置机器人(默认 CDP 端口为 `15166`)。创建应用、开通权限和正式发布属于外部变更,执行前仍需明确确认;不要把 App Secret 写入对话或仓库。
170
+
171
+ 1. 打开 [飞书开放平台](https://open.feishu.cn),创建一个**企业自建应用**。
172
172
  2. 在「应用功能」里开启**机器人**能力。
173
173
  3. 在「权限管理」里开通 `im:` 和 `cardkit:` 前缀下的相关权限:
174
174
 
@@ -326,7 +326,7 @@ Codex 的默认模型和推理强度可继续由 `~/.codex/config.toml` 管理
326
326
 
327
327
  ### 5. 开始使用
328
328
 
329
- **飞书:** 找到你的机器人,发送 `/new`、`/new claude`、`/new cursor` 或 `/new codex`。机器人会创建一个新群并绑定 AI 会话,之后直接在群里聊天即可。
329
+ **飞书:** 找到机器人后直接发送普通消息,即可在当前私聊中创建并持续使用专属 AI 会话;私聊工作目录固定为运行 ChatCCC 的系统账号用户目录。需要独立任务时,发送 `/new`、`/new claude`、`/new cursor` 或 `/new codex`,机器人会另外创建会话群,原私聊会话不受影响。
330
330
 
331
331
  **微信:** 扫码登录后,在机器人私聊里发送 `/new` 或指定 Agent 的 `/new ...` 命令即可开始。功能与飞书基本一致,但展示为纯文本。
332
332
 
@@ -334,28 +334,30 @@ Codex 的默认模型和推理强度可继续由 `~/.codex/config.toml` 管理
334
334
 
335
335
  | 指令 | 作用 |
336
336
  | --- | --- |
337
- | `/new` | 使用默认 Agent 创建新会话 |
338
- | `/new claude` | 创建 Claude Code 会话 |
339
- | `/new cursor` | 创建 Cursor 会话 |
340
- | `/new codex` | 创建 Codex 会话 |
341
- | `/newh` | 重置当前会话,保留工作目录 |
337
+ | `/new` | 使用默认 Agent 创建新会话;飞书中会创建新群 |
338
+ | `/new claude` | 创建 Claude Code 会话;飞书中会创建新群 |
339
+ | `/new cursor` | 创建 Cursor 会话;飞书中会创建新群 |
340
+ | `/new codex` | 创建 Codex 会话;飞书中会创建新群 |
341
+ | `/newh` | 在当前聊天原地重置会话;群聊保留工作目录,飞书私聊固定使用系统用户目录 |
342
342
  | `/model` | 查看或切换当前会话的模型 |
343
343
  | `/stop` | 停止当前回复 |
344
344
  | `/cancel` | 取消当前会话里排队等待处理的消息 |
345
345
  | `/state` | 查看当前会话状态 |
346
- | `/cd` | 查看或设置当前会话工作目录 |
346
+ | `/cd` | 查看或设置后续新建会话的默认工作目录,不改变当前会话;飞书私聊自身始终使用系统用户目录 |
347
347
  | `/sessions` | 查看所有会话状态 |
348
- | `/session <数字>` | 将当前群聊切换到 `/sessions` 列表中的指定会话 |
349
- | `/usage` | 查看当前会话对应 Agent 的用量;Codex 按接口实际返回显示 5h/7天窗口(缺失窗口不显示),若 Chrome CDP 可用且 ChatGPT 已登录,会额外显示订阅到期时间;Cursor 显示当前周期用量 |
348
+ | `/session <数字>` | 将当前群聊切换到 `/sessions` 列表中的指定会话;飞书私聊不支持切换 |
349
+ | `/usage` | 查看当前会话对应 Agent 的用量;Codex 按接口实际返回显示 5h/7天窗口(缺失窗口不显示),若 Chrome CDP 可用且 ChatGPT 已登录,会额外显示订阅到期时间;Cursor 显示当前周期用量 |
350
350
  | `/git <子命令>` | 在当前会话工作目录执行 `git ...` 并回传输出 |
351
351
  | `/abd<内容>` | 去掉 `/abd` 前缀后把内容发给 Agent,并在消息末尾追加第一性原理需求澄清提示 |
352
352
  | `/plan <内容>` | 只读计划模式:仅允许读文件和 stop-stuck-loop 请求,不执行任何写操作 |
353
353
  | `/ask <内容>` | 只读问答模式:与 /plan 相同,仅允许读文件和 stop-stuck-loop 请求 |
354
354
  | `/restart` | 重启机器人进程 |
355
- | `/update` | 更新 npm 全局包并重启(仅限 `npm install -g chatccc` 安装的全局进程) |
356
- | `/deleteg` | 解散当前飞书会话群;Agent 会话记录保留 |
357
-
358
- > **模型切换**:`/model` 查看当前会话 Agent 的可选模型清单,`/model <名称>` 模糊匹配切换,`/model clear` 恢复默认。可选模型来自当前 Agent 的配置:Claude 使用 `claude.model` / `claude.subagentModel`,Cursor 使用 `cursor.model`,Codex 使用 `codex.model`。
355
+ | `/update` | 更新 npm 全局包并重启(仅限 `npm install -g chatccc` 安装的全局进程;同一飞书事件跨重启去重) |
356
+ | `/deleteg` | 解散当前飞书会话群;Agent 会话记录保留 |
357
+
358
+ `/update` 会在执行 npm 更新前把飞书消息或按钮事件 ID 原子写入 `~/.chatccc/state/update-command-guard.json`。同一 ID 跨重启重投时会静默忽略;用户主动发送的新 `/update` 因事件 ID 不同,仍可立即执行。该保护仅作用于 `/update`,普通消息与 `/restart` 的处理不变。
359
+
360
+ > **模型切换**:`/model` 查看当前会话 Agent 的可选模型清单,`/model <名称>` 模糊匹配切换,`/model clear` 恢复默认。可选模型来自当前 Agent 的配置:Claude 使用 `claude.model` / `claude.subagentModel`,Cursor 使用 `cursor.model`,Codex 使用 `codex.model`。
359
361
 
360
362
  ---
361
363
 
@@ -1,46 +1,46 @@
1
- ## 重复成功命令保护
2
-
3
- 通过 Claude Agent SDK 在本项目中工作时,重复成功的 shell 命令是完成信号,不应继续使用工具。
4
-
5
- 以下条件**全部**满足时,视为相同命令:
6
- - 命令文本基本相同
7
- - 工作目录相同
8
- - 任务目标未变
9
- - 没有新的用户输入改变任务
10
- - 没有新的错误输出需要重新处理
11
-
12
- 同一命令**连续成功两次**后,停止调用工具,直接给出最终回复。不要"再验证一次"。
13
-
14
- 此规则适用于所有工具调用。
15
-
16
- ## 硬停止规则
17
-
18
- 同一命令在本项目中**不得成功执行三次**。若连续两次成功,第三次必须是最终回复。
19
-
20
- ## 卡住自检
21
-
22
- 如果你感觉任务已经完成,但发现自己陷入了循环(反复调用相同工具、输出 "stuck in a loop" 等),**立即**调用以下接口。
23
-
24
- **重要:必须用 heredoc 写临时文件再 curl,禁止在命令行参数中直接写中文 JSON。** Windows 下命令行参数传中文会变成 `?` 乱码。
25
-
26
- ```bash
27
- cat > /tmp/stop_ccc.json << 'STOPJSON'
28
- {"session_id": "{{session_id}}", "final_reply": "<你给用户的最终回复,总结已完成的工作和结果>"}
29
- STOPJSON
30
- curl -s -X POST {{stop_stuck_url}} -H "Content-Type: application/json" -d @/tmp/stop_ccc.json
31
- ```
32
-
33
- final_reply 注意事项:
34
- - 把 `<...>` 占位符替换为你的实际回复,不要包含双引号 `"` 和反斜杠 `\`
35
- - 不要包含换行,用空格代替
36
- - 保持简短(1-2 句话)
37
-
38
- 调用后停止所有工具调用,直接输出最终回复即可。
39
-
40
- ## 禁止 PushNotification
41
-
42
- **不要调用 PushNotification 工具。** 如果你认为当前会话已经完成任务需要结束,直接调用上述 stop-stuck-loop 接口,不要使用 PushNotification。
43
-
44
- ## 计划/问答模式权限规则
45
-
1
+ ## 重复成功命令保护
2
+
3
+ 通过 Claude Agent SDK 在本项目中工作时,重复成功的 shell 命令是完成信号,不应继续使用工具。
4
+
5
+ 以下条件**全部**满足时,视为相同命令:
6
+ - 命令文本基本相同
7
+ - 工作目录相同
8
+ - 任务目标未变
9
+ - 没有新的用户输入改变任务
10
+ - 没有新的错误输出需要重新处理
11
+
12
+ 同一命令**连续成功两次**后,停止调用工具,直接给出最终回复。不要"再验证一次"。
13
+
14
+ 此规则适用于所有工具调用。
15
+
16
+ ## 硬停止规则
17
+
18
+ 同一命令在本项目中**不得成功执行三次**。若连续两次成功,第三次必须是最终回复。
19
+
20
+ ## 卡住自检
21
+
22
+ 如果你感觉任务已经完成,但发现自己陷入了循环(反复调用相同工具、输出 "stuck in a loop" 等),**立即**调用以下接口。
23
+
24
+ **重要:必须用 heredoc 写临时文件再 curl,禁止在命令行参数中直接写中文 JSON。** Windows 下命令行参数传中文会变成 `?` 乱码。
25
+
26
+ ```bash
27
+ cat > /tmp/stop_ccc.json << 'STOPJSON'
28
+ {"session_id": "{{session_id}}", "final_reply": "<你给用户的最终回复,总结已完成的工作和结果>"}
29
+ STOPJSON
30
+ curl -s -X POST {{stop_stuck_url}} -H "Content-Type: application/json" -d @/tmp/stop_ccc.json
31
+ ```
32
+
33
+ final_reply 注意事项:
34
+ - 把 `<...>` 占位符替换为你的实际回复,不要包含双引号 `"` 和反斜杠 `\`
35
+ - 不要包含换行,用空格代替
36
+ - 保持简短(1-2 句话)
37
+
38
+ 调用后停止所有工具调用,直接输出最终回复即可。
39
+
40
+ ## 禁止 PushNotification
41
+
42
+ **不要调用 PushNotification 工具。** 如果你认为当前会话已经完成任务需要结束,直接调用上述 stop-stuck-loop 接口,不要使用 PushNotification。
43
+
44
+ ## 计划/问答模式权限规则
45
+
46
46
  如果用户消息以 `/plan` 或 `/ask` 开头,说明当前处于只读模式。**遇到任何需要用户同意的权限请求时,不要向用户申请权限,立即调用 stop-stuck-loop 接口提前结束本轮会话。**
@@ -1,3 +1,3 @@
1
- ## 计划/问答模式权限规则
2
-
1
+ ## 计划/问答模式权限规则
2
+
3
3
  如果用户消息以 `/plan` 或 `/ask` 开头,说明当前处于只读模式。**遇到任何需要用户同意的权限请求时,不要向用户申请权限,立即调用 stop-stuck-loop 接口提前结束本轮会话。**
@@ -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);
@@ -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
+ }
@@ -1,64 +1,64 @@
1
- # Sending & Downloading Files/Videos
2
-
3
- ## Send Files or Videos
4
-
5
- Videos are sent as regular files (not media), which looks cleaner in Feishu.
6
-
7
- **Use the node script below. Do NOT use curl or raw HTTP — the script correctly handles errors and exits non-zero on failure.**
8
-
9
- ### Script
10
-
11
- ```bash
12
- node "{{send_file_script}}" --url "{{send_file_url}}" --session-id "{{session_id}}" --path "<absolute file path>" --caption "<optional caption>"
13
- ```
14
-
15
- ### Rules
16
-
17
- - Use the node script above — never curl or raw HTTP.
18
- - Save or choose a local file first.
19
- - Use an absolute local path.
20
- - Max file size: 30MB.
21
- - Supported formats: .mp4 .mov .avi .mkv .webm .flv .mp3 .wav .ogg .aac .m4a .pdf .doc .docx .xls .xlsx .csv .ppt .pptx .txt .zip .tar .gz.
22
- - Only send a file/video when the user asked for one or when it materially helps the answer.
23
- - **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.**
24
-
25
- ### Video Compression (when file > 30MB)
26
-
27
- If the video exceeds 30MB, compress it with ffmpeg before sending.
28
-
29
- **Ensure ffmpeg is available** (install if missing):
30
-
31
- | OS | Install command |
32
- |----|----------------|
33
- | macOS | `brew install ffmpeg` |
34
- | Linux (Debian/Ubuntu) | `sudo apt install ffmpeg` |
35
- | Linux (RHEL/Fedora) | `sudo dnf install ffmpeg` |
36
- | Windows | `winget install Gyan.FFmpeg` |
37
-
38
- **Two-pass compression** (target ~28MB for 30s video, adjust `b:v` for other durations):
39
-
40
- ```bash
41
- ffmpeg -y -i "<input>" -c:v libx264 -b:v <bitrate>k -pass 1 -f mp4 NUL
42
- ffmpeg -y -i "<input>" -c:v libx264 -b:v <bitrate>k -pass 2 -c:a aac -b:a 128k "<output>"
43
- ```
44
-
45
- Bitrate formula: `bitrate = 28 × 8 × 1000 ÷ duration_seconds - 128` (target ~28MB, safe under 30MB).
46
- On Windows replace `NUL` with `NUL` (same); on Linux/macOS use `/dev/null`.
47
-
48
- If the compressed file still exceeds 30MB, explain to the user that automatic compression wasn't enough and suggest they manually trim or re-encode the source.
49
-
50
- ## Download Files or Videos
51
-
52
- When the user sends a file or video to the bot, the message contains `message_id` and `file_key`. Download it with:
53
-
54
- ```bash
55
- node "{{download_video_script}}" --message-id <message_id> --file-key <file_key> --name <file_name>
56
- ```
57
-
58
- If only `chat_id` and `file_key` are available:
59
-
60
- ```bash
61
- node "{{download_video_script}}" --chat-id <chat_id> --file-key <file_key> --name <file_name>
62
- ```
63
-
1
+ # Sending & Downloading Files/Videos
2
+
3
+ ## Send Files or Videos
4
+
5
+ Videos are sent as regular files (not media), which looks cleaner in Feishu.
6
+
7
+ **Use the node script below. Do NOT use curl or raw HTTP — the script correctly handles errors and exits non-zero on failure.**
8
+
9
+ ### Script
10
+
11
+ ```bash
12
+ node "{{send_file_script}}" --url "{{send_file_url}}" --session-id "{{session_id}}" --path "<absolute file path>" --caption "<optional caption>"
13
+ ```
14
+
15
+ ### Rules
16
+
17
+ - Use the node script above — never curl or raw HTTP.
18
+ - Save or choose a local file first.
19
+ - Use an absolute local path.
20
+ - Max file size: 30MB.
21
+ - Supported formats: .mp4 .mov .avi .mkv .webm .flv .mp3 .wav .ogg .aac .m4a .pdf .doc .docx .xls .xlsx .csv .ppt .pptx .txt .zip .tar .gz.
22
+ - Only send a file/video when the user asked for one or when it materially helps the answer.
23
+ - **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.**
24
+
25
+ ### Video Compression (when file > 30MB)
26
+
27
+ If the video exceeds 30MB, compress it with ffmpeg before sending.
28
+
29
+ **Ensure ffmpeg is available** (install if missing):
30
+
31
+ | OS | Install command |
32
+ |----|----------------|
33
+ | macOS | `brew install ffmpeg` |
34
+ | Linux (Debian/Ubuntu) | `sudo apt install ffmpeg` |
35
+ | Linux (RHEL/Fedora) | `sudo dnf install ffmpeg` |
36
+ | Windows | `winget install Gyan.FFmpeg` |
37
+
38
+ **Two-pass compression** (target ~28MB for 30s video, adjust `b:v` for other durations):
39
+
40
+ ```bash
41
+ ffmpeg -y -i "<input>" -c:v libx264 -b:v <bitrate>k -pass 1 -f mp4 NUL
42
+ ffmpeg -y -i "<input>" -c:v libx264 -b:v <bitrate>k -pass 2 -c:a aac -b:a 128k "<output>"
43
+ ```
44
+
45
+ Bitrate formula: `bitrate = 28 × 8 × 1000 ÷ duration_seconds - 128` (target ~28MB, safe under 30MB).
46
+ On Windows replace `NUL` with `NUL` (same); on Linux/macOS use `/dev/null`.
47
+
48
+ If the compressed file still exceeds 30MB, explain to the user that automatic compression wasn't enough and suggest they manually trim or re-encode the source.
49
+
50
+ ## Download Files or Videos
51
+
52
+ When the user sends a file or video to the bot, the message contains `message_id` and `file_key`. Download it with:
53
+
54
+ ```bash
55
+ node "{{download_video_script}}" --message-id <message_id> --file-key <file_key> --name <file_name>
56
+ ```
57
+
58
+ If only `chat_id` and `file_key` are available:
59
+
60
+ ```bash
61
+ node "{{download_video_script}}" --chat-id <chat_id> --file-key <file_key> --name <file_name>
62
+ ```
63
+
64
64
  Downloads are saved under `~/.chatccc/videos/downloads/`.
@@ -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 条未回复会截断。