chatccc 0.2.229 → 0.2.231

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 (120) 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 -76
  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 +65 -1
  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 +202 -163
  28. package/src/__tests__/builtin-permissions.test.ts +211 -211
  29. package/src/__tests__/builtin-session-select.test.ts +116 -116
  30. package/src/__tests__/builtin-sigint.test.ts +56 -56
  31. package/src/__tests__/builtin-web-tools.test.ts +220 -220
  32. package/src/__tests__/card-action-routing.test.ts +18 -18
  33. package/src/__tests__/cardkit.test.ts +60 -60
  34. package/src/__tests__/ccc-adapter.test.ts +170 -136
  35. package/src/__tests__/chatgpt-subscription-rpc.test.ts +89 -89
  36. package/src/__tests__/chatgpt-subscription.test.ts +135 -135
  37. package/src/__tests__/chrome-devtools-guard.test.ts +165 -165
  38. package/src/__tests__/claude-adapter.test.ts +614 -614
  39. package/src/__tests__/claude-raw-stream-log.test.ts +87 -87
  40. package/src/__tests__/codex-adapter.test.ts +58 -58
  41. package/src/__tests__/codex-raw-stream-log.test.ts +170 -170
  42. package/src/__tests__/codex-reset-actions.test.ts +146 -146
  43. package/src/__tests__/cursor-adapter.test.ts +268 -268
  44. package/src/__tests__/feishu-api.test.ts +60 -60
  45. package/src/__tests__/feishu-avatar.test.ts +164 -164
  46. package/src/__tests__/feishu-message-ingress.test.ts +138 -138
  47. package/src/__tests__/feishu-platform.test.ts +22 -22
  48. package/src/__tests__/format-message.test.ts +47 -47
  49. package/src/__tests__/jsonl-stream.test.ts +79 -79
  50. package/src/__tests__/package-files.test.ts +24 -24
  51. package/src/__tests__/privacy.test.ts +198 -198
  52. package/src/__tests__/progress-reducer.test.ts +121 -110
  53. package/src/__tests__/raw-stream-log.test.ts +106 -106
  54. package/src/__tests__/response-stall.test.ts +49 -49
  55. package/src/__tests__/session.test.ts +174 -16
  56. package/src/__tests__/shared-prefix.test.ts +36 -36
  57. package/src/__tests__/sim-platform.test.ts +16 -16
  58. package/src/__tests__/startup-lifecycle.test.ts +231 -231
  59. package/src/__tests__/stop-session.test.ts +34 -34
  60. package/src/__tests__/stream-state.test.ts +42 -42
  61. package/src/__tests__/terminal-error.test.ts +54 -0
  62. package/src/__tests__/terminal-renderer.test.ts +247 -247
  63. package/src/__tests__/update-command-guard.test.ts +144 -144
  64. package/src/__tests__/web-ui.test.ts +326 -326
  65. package/src/adapters/adapter-interface.ts +24 -18
  66. package/src/adapters/ccc-adapter.ts +141 -131
  67. package/src/adapters/claude-adapter.ts +620 -620
  68. package/src/adapters/claude-session-meta-store.ts +120 -120
  69. package/src/adapters/codex-adapter.ts +426 -426
  70. package/src/adapters/cursor-adapter.ts +681 -681
  71. package/src/adapters/jsonl-stream.ts +157 -157
  72. package/src/adapters/raw-stream-log.ts +124 -124
  73. package/src/adapters/resource-monitor.ts +140 -140
  74. package/src/agent-activity.ts +175 -170
  75. package/src/agent-delegate-task-rpc.ts +153 -153
  76. package/src/agent-delegate-task.ts +91 -91
  77. package/src/agent-reload-config-rpc.ts +34 -34
  78. package/src/agent-stop-stuck.ts +129 -129
  79. package/src/builtin/cli.ts +9 -2
  80. package/src/builtin/config.ts +84 -84
  81. package/src/builtin/context.ts +62 -4
  82. package/src/builtin/file-log.ts +38 -38
  83. package/src/builtin/index.ts +83 -19
  84. package/src/builtin/proc-tree-kill.ts +61 -61
  85. package/src/builtin/progress/cards-helpers.ts +76 -76
  86. package/src/builtin/progress/reducer.ts +113 -108
  87. package/src/builtin/progress/terminal-renderer.ts +294 -294
  88. package/src/builtin/progress/view.ts +77 -77
  89. package/src/builtin/raw-stream-log.ts +124 -124
  90. package/src/builtin/session-select.ts +48 -48
  91. package/src/builtin/sigint.ts +50 -50
  92. package/src/builtin/web-tools.ts +313 -313
  93. package/src/card-action-routing.ts +14 -14
  94. package/src/chatgpt-subscription-rpc.ts +27 -27
  95. package/src/chatgpt-subscription.ts +299 -299
  96. package/src/chrome-devtools-guard.ts +318 -318
  97. package/src/codex-reset-actions.ts +184 -184
  98. package/src/feishu-api.ts +193 -193
  99. package/src/feishu-message-ingress.ts +195 -195
  100. package/src/feishu-platform.ts +20 -20
  101. package/src/format-message.ts +293 -293
  102. package/src/litellm-proxy.ts +374 -374
  103. package/src/orchestrator.ts +8 -3
  104. package/src/platform-adapter.ts +6 -6
  105. package/src/privacy.ts +118 -118
  106. package/src/progress/reducer.ts +113 -108
  107. package/src/progress/terminal-renderer.ts +294 -294
  108. package/src/progress/view.ts +77 -77
  109. package/src/response-stall.ts +28 -28
  110. package/src/runtime-reload.ts +34 -34
  111. package/src/session-chat-binding.ts +82 -82
  112. package/src/session-name.ts +8 -8
  113. package/src/session.ts +149 -26
  114. package/src/shared-prefix.ts +29 -29
  115. package/src/sim-platform.ts +20 -20
  116. package/src/startup-lifecycle.ts +250 -250
  117. package/src/stream-state.ts +21 -18
  118. package/src/terminal-error.ts +129 -0
  119. package/src/turn-cards.ts +117 -117
  120. package/src/update-command-guard.ts +165 -165
package/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # ChatCCC
2
2
 
3
- **用飞书或微信聊天控制 Claude Code / Cursor / Codex / CCC Agent。**
3
+ **用飞书或微信聊天控制 Claude Code / Cursor / Codex / CCC Agent。**
4
4
 
5
- ChatCCC 把本地 AI 编程工具接入即时通讯软件。你可以在手机上发消息,让 Claude Code、Cursor Agent、Codex 或内置 CCC Agent 继续写代码、查问题、跑命令;不用一直守在电脑前。
5
+ ChatCCC 把本地 AI 编程工具接入即时通讯软件。你可以在手机上发消息,让 Claude Code、Cursor Agent、Codex 或内置 CCC Agent 继续写代码、查问题、跑命令;不用一直守在电脑前。
6
6
 
7
- 飞书是推荐入口:直接私聊机器人即可持续使用同一个专属会话,需要并行任务时再用 `/new` 创建独立会话群;卡片能流式更新,体验完整。微信 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,9 +19,9 @@ ChatCCC 把本地 AI 编程工具接入即时通讯软件。你可以在手机
19
19
  ## 为什么用 ChatCCC
20
20
 
21
21
  - **手机上也能用 AI 编程工具**:在飞书或微信发消息,就像在终端给 Agent 下指令。
22
- - **飞书体验更完整**:私聊可持续对话,`/new` 创建的一群一会话支持多任务并行,CardKit 卡片可流式更新。
22
+ - **飞书体验更完整**:私聊可持续对话,`/new` 创建的一群一会话支持多任务并行,CardKit 卡片可流式更新。
23
23
  - **微信接入更轻**:不用创建飞书应用,启动后扫码即可在微信私聊里使用。
24
- - **多 Agent 切换**:`/new` 使用默认 Agent,也可以用 `/new claude`、`/new cursor`、`/new codex`、`/new ccc` 指定工具。
24
+ - **多 Agent 切换**:`/new` 使用默认 Agent,也可以用 `/new claude`、`/new cursor`、`/new codex`、`/new ccc` 指定工具。
25
25
  - **群里能跑 git**:`/git status`、`/git pull`、`/git log` 会在当前会话工作目录执行,并把输出发回聊天窗口。
26
26
 
27
27
  ## 飞书和微信的差异
@@ -29,7 +29,7 @@ ChatCCC 把本地 AI 编程工具接入即时通讯软件。你可以在手机
29
29
  | 项目 | 飞书(推荐) | 微信 iLink |
30
30
  | --- | --- | --- |
31
31
  | 使用场景 | 长期主力使用 | 快速试用、临时远程控制 |
32
- | 会话形态 | 私聊固定专属会话;`/new` 创建一群一会话 | 私聊,一对一 |
32
+ | 会话形态 | 私聊固定专属会话;`/new` 创建一群一会话 | 私聊,一对一 |
33
33
  | 消息展示 | CardKit 卡片,流式更新 | 纯文本,增量推送 |
34
34
  | `/new` | 自动创建新群并绑定新会话 | 在当前私聊里创建新会话 |
35
35
  | 多会话并行 | 直接切换不同群 | 支持并行,使用切换指令后未完成的任务会继续在后台进行,但不如飞书直观方便 |
@@ -133,7 +133,7 @@ Write-Host ''
133
133
  chatccc
134
134
  ```
135
135
 
136
- 如果一切顺利,系统默认浏览器会自动打开 `http://localhost:18080/` 的 Web 配置页面。按页面提示填入飞书 App ID / App Secret,点击"保存并启动"即可。
136
+ 如果一切顺利,系统默认浏览器会自动打开 `http://localhost:18080/` 的 Web 配置页面。按页面提示填入飞书 App ID / App Secret,点击"保存并启动"即可。
137
137
 
138
138
  > **只想装 ChatCCC 本体?** 如果你已经有 Node.js,直接 `npm install -g chatccc && chatccc` 即可,不需要跑上面的完整脚本。
139
139
 
@@ -151,7 +151,7 @@ chatccc
151
151
 
152
152
  旧版本留在仓库或包目录下的 `config.json`、`logs/`、`state/` 会在首次启动时自动迁移到用户目录。
153
153
 
154
- 每次直接运行 `chatccc` 时,无论是否已经完成配置,ChatCCC 默认都会用系统默认浏览器打开本地 Web UI(默认 `http://localhost:18080/`,修改 `config.port` 后跟随实际端口)。可在首次配置向导或管理页的 **Web UI** 设置中关闭;关闭后从下一次直接启动起生效。由 `/restart`、`/update` 或 Web UI 发起的内部重启始终不会重复打开浏览器。Linux 服务器没有 `DISPLAY`/`WAYLAND_DISPLAY` 时会跳过打开,并在终端输出 SSH 隧道访问提示。主页顶部的 **Agent Team** 入口会跳转到独立的 `/agent-team` 页面。
154
+ 每次直接运行 `chatccc` 时,无论是否已经完成配置,ChatCCC 默认都会用系统默认浏览器打开本地 Web UI(默认 `http://localhost:18080/`,修改 `config.port` 后跟随实际端口)。可在首次配置向导或管理页的 **Web UI** 设置中关闭;关闭后从下一次直接启动起生效。由 `/restart`、`/update` 或 Web UI 发起的内部重启始终不会重复打开浏览器。Linux 服务器没有 `DISPLAY`/`WAYLAND_DISPLAY` 时会跳过打开,并在终端输出 SSH 隧道访问提示。主页顶部的 **Agent Team** 入口会跳转到独立的 `/agent-team` 页面。
155
155
 
156
156
  #### 从源码运行
157
157
 
@@ -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
 
@@ -208,7 +208,7 @@ chatccc
208
208
 
209
209
  ### 3. AI 工具配置
210
210
 
211
- Claude Code、Cursor 和 Codex 需要对应的本地工具;CCC Agent 内置于 ChatCCC,只需配置 DeepSeek 兼容 API。
211
+ Claude Code、Cursor 和 Codex 需要对应的本地工具;CCC Agent 内置于 ChatCCC,只需配置 DeepSeek 兼容 API。
212
212
 
213
213
  #### Claude Code
214
214
 
@@ -237,13 +237,13 @@ codex login
237
237
  codex --version
238
238
  ```
239
239
 
240
- Codex 的默认模型和推理强度可继续由 `~/.codex/config.toml` 管理,也可以在 `config.json` 中覆盖。
241
-
242
- #### CCC Agent
243
-
244
- CCC Agent 是 ChatCCC 内置的编程 Agent,不需要额外安装 CLI。在首次配置向导或 Web 管理页中启用后,填写 DeepSeek 兼容 API Key、Base URL 和模型即可使用;它可以设为 `/new` 的默认 Agent,也可以通过 `/new ccc` 显式创建会话。
245
-
246
- `ccc.alternativeModel` 是单个备选模型,只会加入 `/model` 的人工切换列表,不会在请求失败时自动重试或切换,避免重复执行带副作用的工具调用。
240
+ Codex 的默认模型和推理强度可继续由 `~/.codex/config.toml` 管理,也可以在 `config.json` 中覆盖。
241
+
242
+ #### CCC Agent
243
+
244
+ CCC Agent 是 ChatCCC 内置的编程 Agent,不需要额外安装 CLI。在首次配置向导或 Web 管理页中启用后,填写 DeepSeek 兼容 API Key、Base URL 和模型即可使用;它可以设为 `/new` 的默认 Agent,也可以通过 `/new ccc` 显式创建会话。
245
+
246
+ `ccc.alternativeModel` 是单个备选模型,只会加入 `/model` 的人工切换列表,不会在请求失败时自动重试或切换,避免重复执行带副作用的工具调用。
247
247
 
248
248
  #### 可选:Chrome CDP
249
249
 
@@ -266,14 +266,14 @@ CCC Agent 是 ChatCCC 内置的编程 Agent,不需要额外安装 CLI。在首
266
266
  "appId": "cli_xxxxxxxxxxxx",
267
267
  "appSecret": "xxxxxxxxxxxxxxxxxxxxxxxxxx"
268
268
  },
269
- "platforms": {
270
- "feishu": { "enabled": true, "platformType": "feishu" },
271
- "ilink": { "enabled": true, "reuseTokenOnStart": true }
272
- },
273
- "webUi": {
274
- "openOnStart": true
275
- },
276
- "chromeDevtools": {
269
+ "platforms": {
270
+ "feishu": { "enabled": true, "platformType": "feishu" },
271
+ "ilink": { "enabled": true, "reuseTokenOnStart": true }
272
+ },
273
+ "webUi": {
274
+ "openOnStart": true
275
+ },
276
+ "chromeDevtools": {
277
277
  "enabled": false,
278
278
  "port": 15166,
279
279
  "chromePath": ""
@@ -291,32 +291,32 @@ CCC Agent 是 ChatCCC 内置的编程 Agent,不需要额外安装 CLI。在首
291
291
  "baseUrl": "",
292
292
  "maxTurn": 0
293
293
  },
294
- "cursor": {
294
+ "cursor": {
295
295
  "enabled": false,
296
296
  "defaultAgent": false,
297
- "path": "",
298
- "model": "",
299
- "alternativeModel": "",
300
- "avatarBatteryMode": "apiPercent",
297
+ "path": "",
298
+ "model": "",
299
+ "alternativeModel": "",
300
+ "avatarBatteryMode": "apiPercent",
301
301
  "onDemandMonthlyBudget": 1000
302
302
  },
303
- "codex": {
303
+ "codex": {
304
+ "enabled": false,
305
+ "defaultAgent": false,
306
+ "path": "",
307
+ "model": "",
308
+ "alternativeModel": "",
309
+ "effort": "",
310
+ "fastMode": false
311
+ },
312
+ "ccc": {
304
313
  "enabled": false,
305
- "defaultAgent": false,
306
- "path": "",
307
- "model": "",
308
- "alternativeModel": "",
309
- "effort": "",
310
- "fastMode": false
311
- },
312
- "ccc": {
313
- "enabled": false,
314
- "defaultAgent": false,
315
- "DEEPSEEK_API_KEY": "",
316
- "DEEPSEEK_BASE_URL": "https://api.deepseek.com/v1",
317
- "model": "deepseek-v4-pro",
318
- "alternativeModel": ""
319
- }
314
+ "defaultAgent": false,
315
+ "DEEPSEEK_API_KEY": "",
316
+ "DEEPSEEK_BASE_URL": "https://api.deepseek.com/v1",
317
+ "model": "deepseek-v4-pro",
318
+ "alternativeModel": ""
319
+ }
320
320
  }
321
321
  ```
322
322
 
@@ -325,75 +325,75 @@ CCC Agent 是 ChatCCC 内置的编程 Agent,不需要额外安装 CLI。在首
325
325
  | `feishu.appId` / `feishu.appSecret` | 飞书应用凭证 |
326
326
  | `platforms.feishu.enabled` | 是否启用飞书 |
327
327
  | `platforms.feishu.platformType` | 飞书平台类型,默认 `feishu` |
328
- | `platforms.ilink.enabled` | 是否启用微信 iLink |
329
- | `platforms.ilink.reuseTokenOnStart` | 启动时是否复用已有微信登录 token |
330
- | `webUi.openOnStart` | 直接启动时是否打开系统默认浏览器;默认 true,内部重启始终跳过 |
331
- | `chromeDevtools.enabled` | 是否启用常驻 Chrome CDP;默认 false |
328
+ | `platforms.ilink.enabled` | 是否启用微信 iLink |
329
+ | `platforms.ilink.reuseTokenOnStart` | 启动时是否复用已有微信登录 token |
330
+ | `webUi.openOnStart` | 直接启动时是否打开系统默认浏览器;默认 true,内部重启始终跳过 |
331
+ | `chromeDevtools.enabled` | 是否启用常驻 Chrome CDP;默认 false |
332
332
  | `chromeDevtools.port` | Chrome CDP 端口;默认 15166 |
333
333
  | `chromeDevtools.chromePath` | Chrome 可执行文件路径;留空时自动探测 |
334
334
  | `port` | 本地 Web 配置面板和中继服务端口 |
335
335
  | `gitTimeoutSeconds` | `/git` 命令超时时间,默认 180 秒 |
336
336
  | `allowInterrupt` | 是否允许新消息中断正在运行的任务;默认 false |
337
337
  | `*.enabled` | 是否启用对应 AI Agent |
338
- | `*.defaultAgent` | `/new` 未指定 Agent 时使用哪个工具;飞书私聊会在下一条普通消息到达时跟随变化并创建新的空会话 |
339
- | `cursor.path` / `codex.path` | CLI 可执行文件路径;留空时自动探测或使用 PATH |
340
- | `codex.fastMode` | Codex Fast 模式的全局默认值;默认 `false`,每次调用都会显式覆盖 Codex CLI 的 service tier |
338
+ | `*.defaultAgent` | `/new` 未指定 Agent 时使用哪个工具;飞书私聊会在下一条普通消息到达时跟随变化并创建新的空会话 |
339
+ | `cursor.path` / `codex.path` | CLI 可执行文件路径;留空时自动探测或使用 PATH |
340
+ | `codex.fastMode` | Codex Fast 模式的全局默认值;默认 `false`,每次调用都会显式覆盖 Codex CLI 的 service tier |
341
341
  | `cursor.avatarBatteryMode` | Cursor 头像电量显示来源:`apiPercent` 或 `onDemandUse` |
342
342
  | `cursor.onDemandMonthlyBudget` | `avatarBatteryMode=onDemandUse` 时用于计算电量的月预算 |
343
343
  | `claude.model` / `claude.subagentModel` / `claude.effort` | 选填;设置后传给 Claude Agent SDK,留空以 `~/.claude/settings.json` 为准 |
344
344
  | `claude.apiKey` / `claude.baseUrl` | 选填;设置后传给 Claude Agent SDK,留空以 `~/.claude/settings.json` 为准 |
345
- | `claude.maxTurn` | 选填;Claude 最大对话轮数,默认 0(无限制),可在 Web UI 编辑 |
346
- | `cursor.alternativeModel` / `codex.alternativeModel` / `ccc.alternativeModel` | 单个备选模型;加入 `/model` 人工切换列表,不会自动故障转移 |
347
- | `ccc.DEEPSEEK_API_KEY` / `ccc.DEEPSEEK_BASE_URL` | CCC Agent 使用的 DeepSeek 兼容 API 凭证和服务地址 |
348
- | `ccc.model` | CCC Agent 默认模型 |
345
+ | `claude.maxTurn` | 选填;Claude 最大对话轮数,默认 0(无限制),可在 Web UI 编辑 |
346
+ | `cursor.alternativeModel` / `codex.alternativeModel` / `ccc.alternativeModel` | 单个备选模型;加入 `/model` 人工切换列表,不会自动故障转移 |
347
+ | `ccc.DEEPSEEK_API_KEY` / `ccc.DEEPSEEK_BASE_URL` | CCC Agent 使用的 DeepSeek 兼容 API 凭证和服务地址 |
348
+ | `ccc.model` | CCC Agent 默认模型 |
349
349
 
350
350
  > **权限控制**:普通消息以 `bypassPermissions` 模式运行,跳过 Agent 操作确认。使用 `/plan` 或 `/ask` 前缀时,ChatCCC 自动切换为只读模式:Claude SDK 仅放行 Read + stop-stuck-loop 网络请求,Codex 使用 `--sandbox read-only`,Cursor 使用 `--mode plan/ask`。请只在可信环境中使用。
351
351
 
352
352
  ### 5. 开始使用
353
353
 
354
- **飞书:** 找到机器人后直接发送普通消息,即可在当前私聊中创建并持续使用专属 AI 会话;私聊工作目录固定为运行 ChatCCC 的系统账号用户目录。默认 Agent 发生变化后,下一条私聊普通消息会触发切换并创建新的空会话;若旧 Agent 正在生成,该消息会先排队,待当前回复完成后再切换。命令不会触发自动切换。需要独立任务时,发送 `/new`、`/new claude`、`/new cursor`、`/new codex` 或 `/new ccc`,机器人会另外创建会话群。
354
+ **飞书:** 找到机器人后直接发送普通消息,即可在当前私聊中创建并持续使用专属 AI 会话;私聊工作目录固定为运行 ChatCCC 的系统账号用户目录。默认 Agent 发生变化后,下一条私聊普通消息会触发切换并创建新的空会话;若旧 Agent 正在生成,该消息会先排队,待当前回复完成后再切换。命令不会触发自动切换。需要独立任务时,发送 `/new`、`/new claude`、`/new cursor`、`/new codex` 或 `/new ccc`,机器人会另外创建会话群。
355
+
356
+ **微信:** 扫码登录后,在机器人私聊里发送 `/new` 或指定 Agent 的 `/new ...` 命令即可开始。功能与飞书基本一致,但展示为纯文本。
357
+
358
+ **会话停滞保护:** 只有 Agent 明确进入“生成回复中”后,连续 3 分钟没有新增回复字符且尚未报告权威终态,ChatCCC 才判定停滞、结束旧 CLI,并优先补发一次“完成了吗?如果没完成继续”;恢复轮再次发生相同停滞时不再递归续跑。启动、上下文压缩、思考、搜索和工具调用阶段不会触发这项回复停滞计时;其中 CCC Agent 会单独显示“压缩上下文中”,压缩最多等待 5 分钟,失败时直接报告具体原因且不自动重放。`/new claude` 和 `/new cursor` 等创建会话操作仍有独立的 init 超时,进程资源监控也继续负责识别真正僵死。Codex 只有 `turn.completed` 才算权威终态,阶段性的 `agent_message` 不算;任一 Agent 报告权威终态后若输出流仍超过 10 秒未关闭,ChatCCC 会强制清理该 CLI 并按正常完成收尾,不会重复询问 Agent。
355
359
 
356
- **微信:** 扫码登录后,在机器人私聊里发送 `/new` 或指定 Agent `/new ...` 命令即可开始。功能与飞书基本一致,但展示为纯文本。
357
-
358
- **会话停滞保护:** 当 Agent 连续 3 分钟停在“正在启动 Agent”且没有任何事件,或停在“正在生成回复”且回复字符数没有变化,同时尚未报告权威终态时,ChatCCC 会结束旧 CLI,并优先补发一次“完成了吗?如果没完成继续”;恢复轮再次发生相同停滞时不再递归续跑。`/new claude` 和 `/new cursor` 在等待底层 init 事件时也使用 3 分钟超时并主动清理 SDK/CLI。思考、搜索和工具调用阶段不按回复字符数误判,由进程资源监控负责识别真正僵死。Codex 只有 `turn.completed` 才算权威终态,阶段性的 `agent_message` 不算;任一 Agent 报告权威终态后若输出流仍超过 10 秒未关闭,ChatCCC 会强制清理该 CLI 并按正常完成收尾,不会重复询问 Agent。
359
-
360
- **CCC Agent 代码搜索:** `search_code` 使用项目自带的跨平台 ripgrep,不要求系统另行安装 `rg`。如果当前平台没有可用的 bundled/system ripgrep,会自动降级为内置 Node 搜索,并继续支持常用正则、glob、结果上限、中止和超时控制。
361
-
362
- ## 可用指令
360
+ **CCC Agent 代码搜索:** `search_code` 使用项目自带的跨平台 ripgrep,不要求系统另行安装 `rg`。如果当前平台没有可用的 bundled/system ripgrep,会自动降级为内置 Node 搜索,并继续支持常用正则、glob、结果上限、中止和超时控制。
361
+
362
+ ## 可用指令
363
363
 
364
364
  | 指令 | 作用 |
365
365
  | --- | --- |
366
- | `/new` | 使用默认 Agent 创建新会话;飞书中会创建新群 |
367
- | `/new claude` | 创建 Claude Code 会话;飞书中会创建新群 |
368
- | `/new cursor` | 创建 Cursor 会话;飞书中会创建新群 |
369
- | `/new codex` | 创建 Codex 会话;飞书中会创建新群 |
370
- | `/new ccc` | 创建内置 CCC Agent 会话;飞书中会创建新群 |
371
- | `/newh` | 在当前聊天原地重置会话;群聊保留工作目录,飞书私聊固定使用系统用户目录 |
372
- | `/model` | 查看或切换当前会话的模型 |
373
- | `/fast` | 查看当前 Codex 会话的 Fast 模式;使用 `/fast on` 或 `/fast off` 切换 |
374
- | `/stop` | 停止当前回复 |
366
+ | `/new` | 使用默认 Agent 创建新会话;飞书中会创建新群 |
367
+ | `/new claude` | 创建 Claude Code 会话;飞书中会创建新群 |
368
+ | `/new cursor` | 创建 Cursor 会话;飞书中会创建新群 |
369
+ | `/new codex` | 创建 Codex 会话;飞书中会创建新群 |
370
+ | `/new ccc` | 创建内置 CCC Agent 会话;飞书中会创建新群 |
371
+ | `/newh` | 在当前聊天原地重置会话;群聊保留工作目录,飞书私聊固定使用系统用户目录 |
372
+ | `/model` | 查看或切换当前会话的模型 |
373
+ | `/fast` | 查看当前 Codex 会话的 Fast 模式;使用 `/fast on` 或 `/fast off` 切换 |
374
+ | `/stop` | 停止当前回复 |
375
375
  | `/cancel` | 取消当前会话里排队等待处理的消息 |
376
376
  | `/state` | 查看当前会话状态 |
377
- | `/cd` | 查看或设置后续新建会话的默认工作目录,不改变当前会话;飞书私聊自身始终使用系统用户目录 |
377
+ | `/cd` | 查看或设置后续新建会话的默认工作目录,不改变当前会话;飞书私聊自身始终使用系统用户目录 |
378
378
  | `/sessions` | 查看所有会话状态 |
379
- | `/session <数字>` | 将当前群聊切换到 `/sessions` 列表中的指定会话;飞书私聊不支持切换 |
380
- | `/usage` | 查看当前会话对应 Agent 的用量;Codex 显示 5h/7天窗口,Cursor 显示当前周期用量,使用官方 DeepSeek API 的 CCC Agent 显示账户余额 |
379
+ | `/session <数字>` | 将当前群聊切换到 `/sessions` 列表中的指定会话;飞书私聊不支持切换 |
380
+ | `/usage` | 查看当前会话对应 Agent 的用量;Codex 显示 5h/7天窗口,Cursor 显示当前周期用量,使用官方 DeepSeek API 的 CCC Agent 显示账户余额 |
381
381
  | `/git <子命令>` | 在当前会话工作目录执行 `git ...` 并回传输出 |
382
382
  | `/abd<内容>` | 去掉 `/abd` 前缀后把内容发给 Agent,并在消息末尾追加第一性原理需求澄清提示 |
383
383
  | `/plan <内容>` | 只读计划模式:仅允许读文件和 stop-stuck-loop 请求,不执行任何写操作 |
384
384
  | `/ask <内容>` | 只读问答模式:与 /plan 相同,仅允许读文件和 stop-stuck-loop 请求 |
385
385
  | `/restart` | 重启机器人进程 |
386
- | `/update` | 更新 npm 全局包并重启(仅限 `npm install -g chatccc` 安装的全局进程;同一飞书事件跨重启去重) |
387
- | `/deleteg` | 解散当前飞书会话群;Agent 会话记录保留 |
388
-
389
- `/update` 会在执行 npm 更新前把飞书消息或按钮事件 ID 原子写入 `~/.chatccc/state/update-command-guard.json`。同一 ID 跨重启重投时会静默忽略;用户主动发送的新 `/update` 因事件 ID 不同,仍可立即执行。该保护仅作用于 `/update`,普通消息与 `/restart` 的处理不变。
390
-
391
- > **模型切换**:`/model` 查看当前会话 Agent 的可选模型清单,`/model <名称>` 模糊匹配切换,`/model clear` 恢复默认。可选模型来自当前 Agent 的配置:Claude 使用 `claude.model` / `claude.subagentModel`;Cursor、Codex 和 CCC Agent 使用各自的 `model` / `alternativeModel`。
392
-
393
- > **Codex Fast 模式**:Web UI 中的“Fast 模式”设置新 Codex 会话的全局默认值,默认关闭。进入 Codex 会话后,`/fast` 查询当前状态,`/fast on` 和 `/fast off` 只覆盖当前会话并从下一条消息生效。ChatCCC 会显式向 Codex CLI 传入 `service_tier="fast"` 或 `service_tier="default"`,因此关闭时不会继承用户 `config.toml` 中可能开启的 Fast。
386
+ | `/update` | 更新 npm 全局包并重启(仅限 `npm install -g chatccc` 安装的全局进程;同一飞书事件跨重启去重) |
387
+ | `/deleteg` | 解散当前飞书会话群;Agent 会话记录保留 |
388
+
389
+ `/update` 会在执行 npm 更新前把飞书消息或按钮事件 ID 原子写入 `~/.chatccc/state/update-command-guard.json`。同一 ID 跨重启重投时会静默忽略;用户主动发送的新 `/update` 因事件 ID 不同,仍可立即执行。该保护仅作用于 `/update`,普通消息与 `/restart` 的处理不变。
390
+
391
+ > **模型切换**:`/model` 查看当前会话 Agent 的可选模型清单,`/model <名称>` 模糊匹配切换,`/model clear` 恢复默认。可选模型来自当前 Agent 的配置:Claude 使用 `claude.model` / `claude.subagentModel`;Cursor、Codex 和 CCC Agent 使用各自的 `model` / `alternativeModel`。
392
+
393
+ > **Codex Fast 模式**:Web UI 中的“Fast 模式”设置新 Codex 会话的全局默认值,默认关闭。进入 Codex 会话后,`/fast` 查询当前状态,`/fast on` 和 `/fast off` 只覆盖当前会话并从下一条消息生效。ChatCCC 会显式向 Codex CLI 传入 `service_tier="fast"` 或 `service_tier="default"`,因此关闭时不会继承用户 `config.toml` 中可能开启的 Fast。
394
394
 
395
395
  ---
396
396
 
397
397
  ## 技术栈
398
398
 
399
- TypeScript / Node.js >= 20 / tsx / AI SDK / Anthropic Claude Agent SDK / Cursor Agent CLI / Codex CLI / 飞书 WebSocket API / CardKit / 微信 iLink
399
+ TypeScript / Node.js >= 20 / tsx / AI SDK / Anthropic Claude Agent SDK / Cursor Agent CLI / Codex CLI / 飞书 WebSocket API / CardKit / 微信 iLink
@@ -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);
@@ -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/`.