chatccc 0.2.261 → 0.2.263
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -22
- package/config.sample.json +25 -23
- package/dist/src/adapters/dsh-adapter.js +4 -0
- package/dist/src/config.js +5 -0
- package/dist/src/engines/engine-specs.js +4 -0
- package/dist/src/feishu-api.js +1 -0
- package/dist/src/session.js +1 -0
- package/dist/src/web-ui.js +255 -237
- package/images/avatars/badges/badge_dsh.png +0 -0
- package/images/avatars/combinations/avatar_dsh_busy.png +0 -0
- package/images/avatars/combinations/avatar_dsh_idle.png +0 -0
- package/images/avatars/combinations/avatar_dsh_new.png +0 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# ChatCCC
|
|
2
2
|
|
|
3
|
-
**用飞书或微信聊天控制 Claude Code / Cursor / Codex / CCC Agent / DeepSeek Harness。**
|
|
3
|
+
**用飞书或微信聊天控制 Claude Code / Cursor / Codex / CCC Agent / DeepSeek Harness。**
|
|
4
4
|
|
|
5
|
-
ChatCCC 把本地 AI 编程工具接入即时通讯软件。你可以在手机上发消息,让 Claude Code、Cursor Agent、Codex、内置 CCC Agent 或 DeepSeek Harness 继续写代码、查问题、跑命令;不用一直守在电脑前。
|
|
5
|
+
ChatCCC 把本地 AI 编程工具接入即时通讯软件。你可以在手机上发消息,让 Claude Code、Cursor Agent、Codex、内置 CCC Agent 或 DeepSeek Harness 继续写代码、查问题、跑命令;不用一直守在电脑前。
|
|
6
6
|
|
|
7
7
|
飞书是推荐入口:直接私聊机器人即可持续使用同一个专属会话,需要并行任务时再用 `/new` 创建独立会话群;卡片能流式更新,体验完整。微信 iLink 更适合快速试用或临时使用:扫码即可接入,但只能走私聊文本模式。
|
|
8
8
|
|
|
@@ -21,7 +21,7 @@ ChatCCC 把本地 AI 编程工具接入即时通讯软件。你可以在手机
|
|
|
21
21
|
- **手机上也能用 AI 编程工具**:在飞书或微信发消息,就像在终端给 Agent 下指令。
|
|
22
22
|
- **飞书体验更完整**:私聊可持续对话,`/new` 创建的一群一会话支持多任务并行,CardKit 卡片可流式更新。
|
|
23
23
|
- **微信接入更轻**:不用创建飞书应用,启动后扫码即可在微信私聊里使用。
|
|
24
|
-
- **多 Agent 切换**:`/new` 使用默认 Agent,也可以用 `/new claude`、`/new cursor`、`/new codex`、`/new ccc`、`/new dsh` 指定工具。
|
|
24
|
+
- **多 Agent 切换**:`/new` 使用默认 Agent,也可以用 `/new claude`、`/new cursor`、`/new codex`、`/new ccc`、`/new dsh` 指定工具。
|
|
25
25
|
- **群里能跑 git**:`/git status`、`/git pull`、`/git log` 会在当前会话工作目录执行,并把输出发回聊天窗口。
|
|
26
26
|
|
|
27
27
|
## 飞书和微信的差异
|
|
@@ -216,7 +216,7 @@ chatccc
|
|
|
216
216
|
|
|
217
217
|
### 3. AI 工具配置
|
|
218
218
|
|
|
219
|
-
Claude Code、Cursor 和 Codex 需要对应的本地工具;CCC Agent 内置于 ChatCCC,开箱即用,**模型接入不限于 DeepSeek**——它支持 OpenAI-compatible 和 Anthropic Messages 两种协议(详见下文 CCC Agent)。DeepSeek Harness(DSH)采用按需安装,不计入 ChatCCC 主包生产依赖。
|
|
219
|
+
Claude Code、Cursor 和 Codex 需要对应的本地工具;CCC Agent 内置于 ChatCCC,开箱即用,**模型接入不限于 DeepSeek**——它支持 OpenAI-compatible 和 Anthropic Messages 两种协议(详见下文 CCC Agent)。DeepSeek Harness(DSH)采用按需安装,不计入 ChatCCC 主包生产依赖。
|
|
220
220
|
|
|
221
221
|
#### CCC Agent
|
|
222
222
|
|
|
@@ -242,15 +242,15 @@ ChatCCC 会把 `ccc.DEEPSEEK_API_KEY`、`ccc.DEEPSEEK_BASE_URL`、模型和 effo
|
|
|
242
242
|
|
|
243
243
|
#### Claude Code
|
|
244
244
|
|
|
245
|
-
ChatCCC 通过 Anthropic Claude Agent SDK 调用 Claude Code 能力。SDK 引擎(含 Claude Code CLI 原生二进制,约 220MB)**按需下载**:在首次配置向导或 Web 管理页打开 Claude Code 开关,或点击一次安装按钮,页面会连续显示环境检查、下载、校验、Runtime 验证、原子切换和清理进度,不需要逐步确认。**只有启用 Claude Code 的用户才需要下载**,不使用 Claude Code 的安装不包含该引擎,chatccc 主包体积保持精简。
|
|
245
|
+
ChatCCC 通过 Anthropic Claude Agent SDK 调用 Claude Code 能力。SDK 引擎(含 Claude Code CLI 原生二进制,约 220MB)**按需下载**:在首次配置向导或 Web 管理页打开 Claude Code 开关,或点击一次安装按钮,页面会连续显示环境检查、下载、校验、Runtime 验证、原子切换和清理进度,不需要逐步确认。**只有启用 Claude Code 的用户才需要下载**,不使用 Claude Code 的安装不包含该引擎,chatccc 主包体积保持精简。
|
|
246
246
|
|
|
247
|
-
SDK 始终使用其内置的 Claude Code CLI(档位 b),不依赖用户自行安装的 `claude` 命令。本机完成 Claude Code 登录后即可使用(SDK 复用 `~/.claude` 登录态);同时支持官方和第三方 Anthropic 兼容 API:使用官方服务无需额外配置,使用第三方 API 则填写 `claude.apiKey` 和 `claude.baseUrl`。`claude.model`、`claude.subagentModel`、`claude.effort`、`claude.apiKey`、`claude.baseUrl` 均为选填;`claude.maxTurn` 控制每次对话的最大轮数(默认 0,即无限制)。填写后会把对应配置传给 Claude Agent SDK;留空则以 `~/.claude/settings.json` 为准。
|
|
248
|
-
|
|
249
|
-
#### DeepSeek Harness
|
|
250
|
-
|
|
251
|
-
DeepSeek Harness 是可选 Agent。在首次配置向导或 Web 管理页打开 DSH 开关,ChatCCC 会一次点击完成环境检查、临时目录准备、精确版本下载、包校验、Runtime 握手、原子切换和旧版本清理,并在页面逐步显示进度。安装失败时不会替换当前可用版本;刷新页面后仍可恢复安装进度。当前固定安装 `0.1.0-rc.6`,要求 Node.js >= 22.19.0。
|
|
252
|
-
|
|
253
|
-
DSH 引擎安装在 `~/.chatccc/engines/dsh/`,不加入 ChatCCC 的 `dependencies`。配置 `dsh.apiKey`、`dsh.baseUrl`、`dsh.model`、`dsh.provider` 和 `dsh.maxTokens` 后,可设为默认 Agent 或使用 `/new dsh` 新建会话。安装验证只做本地 JSON-RPC Runtime 握手,不会调用付费模型。
|
|
247
|
+
SDK 始终使用其内置的 Claude Code CLI(档位 b),不依赖用户自行安装的 `claude` 命令。本机完成 Claude Code 登录后即可使用(SDK 复用 `~/.claude` 登录态);同时支持官方和第三方 Anthropic 兼容 API:使用官方服务无需额外配置,使用第三方 API 则填写 `claude.apiKey` 和 `claude.baseUrl`。`claude.model`、`claude.subagentModel`、`claude.effort`、`claude.apiKey`、`claude.baseUrl` 均为选填;`claude.maxTurn` 控制每次对话的最大轮数(默认 0,即无限制)。填写后会把对应配置传给 Claude Agent SDK;留空则以 `~/.claude/settings.json` 为准。
|
|
248
|
+
|
|
249
|
+
#### DeepSeek Harness
|
|
250
|
+
|
|
251
|
+
DeepSeek Harness 是可选 Agent。在首次配置向导或 Web 管理页打开 DSH 开关,ChatCCC 会一次点击完成环境检查、临时目录准备、精确版本下载、包校验、Runtime 握手、原子切换和旧版本清理,并在页面逐步显示进度。安装失败时不会替换当前可用版本;刷新页面后仍可恢复安装进度。当前固定安装 `0.1.0-rc.6`,要求 Node.js >= 22.19.0。
|
|
252
|
+
|
|
253
|
+
DSH 引擎安装在 `~/.chatccc/engines/dsh/`,不加入 ChatCCC 的 `dependencies`。配置 `dsh.apiKey`、`dsh.baseUrl`、`dsh.model`、`dsh.provider` 和 `dsh.maxTokens` 后,可设为默认 Agent 或使用 `/new dsh` 新建会话。安装验证只做本地 JSON-RPC Runtime 握手,不会调用付费模型。
|
|
254
254
|
|
|
255
255
|
#### Cursor Agent CLI
|
|
256
256
|
|
|
@@ -375,20 +375,21 @@ Codex 的默认模型和推理强度可继续由 `~/.codex/config.toml` 管理
|
|
|
375
375
|
| `claude.model` / `claude.subagentModel` / `claude.effort` | 选填;设置后传给 Claude Agent SDK,留空以 `~/.claude/settings.json` 为准 |
|
|
376
376
|
| `claude.apiKey` / `claude.baseUrl` | 选填;设置后传给 Claude Agent SDK,留空以 `~/.claude/settings.json` 为准 |
|
|
377
377
|
| `claude.maxTurn` | 选填;Claude 最大对话轮数,默认 0(无限制),可在 Web UI 编辑 |
|
|
378
|
-
| `cursor.alternativeModel` / `codex.alternativeModel` / `ccc.alternativeModel` | 单个备选模型;加入 `/model` 人工切换列表,不会自动故障转移 |
|
|
378
|
+
| `cursor.alternativeModel` / `codex.alternativeModel` / `ccc.alternativeModel` / `dsh.alternativeModel` | 单个备选模型;加入 `/model` 人工切换列表,不会自动故障转移 |
|
|
379
379
|
| `ccc.DEEPSEEK_API_KEY` / `ccc.DEEPSEEK_BASE_URL` | CCC Agent 的 API Key 和服务地址;**不限于 DeepSeek**——可填任意 OpenAI 兼容端点(OpenAI、Kimi、通义、智谱、Ollama 本地等) |
|
|
380
380
|
| `ccc.model` | CCC Agent 默认模型 |
|
|
381
381
|
| `ccc.subModel` | CCC Agent 子模型(选填):用于内部轻量环节(压缩摘要生成、task 子代理任务);留空跟随主模型 |
|
|
382
382
|
| `ccc.compactionTimeoutMs` | CCC Agent 上下文压缩单轮超时(毫秒),默认 300000(5 分钟);压缩超时会让整轮对话失败,建议保持默认或调大 |
|
|
383
|
-
| `ccc.contextWindow` | CCC Agent 模型上下文窗口(token),默认 1048576(1M,DeepSeek V4 Pro/Flash 原生规格);压缩阈值自动 = 窗口 × 80%;超过模型/服务端实际上限会被 API 拒绝,可在 Web UI 下拉选择或自定义(单位 k) |
|
|
384
|
-
| `dsh.apiKey` / `dsh.baseUrl` | DeepSeek Harness 的 API Key 和官方 DeepSeek 服务地址 |
|
|
385
|
-
| `dsh.model` / `dsh.provider` / `dsh.maxTokens` | DSH 默认模型、Runtime provider 和最大 token 数 |
|
|
383
|
+
| `ccc.contextWindow` | CCC Agent 模型上下文窗口(token),默认 1048576(1M,DeepSeek V4 Pro/Flash 原生规格);压缩阈值自动 = 窗口 × 80%;超过模型/服务端实际上限会被 API 拒绝,可在 Web UI 下拉选择或自定义(单位 k) |
|
|
384
|
+
| `dsh.apiKey` / `dsh.baseUrl` | DeepSeek Harness 的 API Key 和官方 DeepSeek 服务地址 |
|
|
385
|
+
| `dsh.model` / `dsh.provider` / `dsh.maxTokens` | DSH 默认模型、Runtime provider 和最大 token 数 |
|
|
386
|
+
| `dsh.subModel` | DSH 子代理模型(选填):用于 `subagent` 工具派生的子代理;留空跟随主模型(与 `ccc.subModel` 语义一致) |
|
|
386
387
|
|
|
387
388
|
> **权限控制**:普通消息以 `bypassPermissions` 模式运行,跳过 Agent 操作确认。使用 `/plan` 或 `/ask` 前缀时,ChatCCC 自动切换为只读模式:Claude SDK 仅放行 Read + stop-stuck-loop 网络请求,Codex 使用 `--sandbox read-only`,Cursor 使用 `--mode plan/ask`。请只在可信环境中使用。
|
|
388
389
|
|
|
389
390
|
### 5. 开始使用
|
|
390
391
|
|
|
391
|
-
**飞书:** 找到机器人后直接发送普通消息,即可在当前私聊中创建并持续使用专属 AI 会话;私聊工作目录固定为运行 ChatCCC 的系统账号用户目录。默认 Agent 发生变化后,下一条私聊普通消息会触发切换并创建新的空会话;若旧 Agent 正在生成,该消息会先排队,待当前回复完成后再切换。命令不会触发自动切换。需要独立任务时,发送 `/new`、`/new claude`、`/new cursor`、`/new codex`、`/new ccc` 或 `/new dsh`,机器人会另外创建会话群。
|
|
392
|
+
**飞书:** 找到机器人后直接发送普通消息,即可在当前私聊中创建并持续使用专属 AI 会话;私聊工作目录固定为运行 ChatCCC 的系统账号用户目录。默认 Agent 发生变化后,下一条私聊普通消息会触发切换并创建新的空会话;若旧 Agent 正在生成,该消息会先排队,待当前回复完成后再切换。命令不会触发自动切换。需要独立任务时,发送 `/new`、`/new claude`、`/new cursor`、`/new codex`、`/new ccc` 或 `/new dsh`,机器人会另外创建会话群。
|
|
392
393
|
|
|
393
394
|
**微信:** 扫码登录后,在机器人私聊里发送 `/new` 或指定 Agent 的 `/new ...` 命令即可开始。功能与飞书基本一致,但展示为纯文本。
|
|
394
395
|
|
|
@@ -404,8 +405,8 @@ Codex 的默认模型和推理强度可继续由 `~/.codex/config.toml` 管理
|
|
|
404
405
|
| `/new claude` | 创建 Claude Code 会话;飞书中会创建新群 |
|
|
405
406
|
| `/new cursor` | 创建 Cursor 会话;飞书中会创建新群 |
|
|
406
407
|
| `/new codex` | 创建 Codex 会话;飞书中会创建新群 |
|
|
407
|
-
| `/new ccc` | 创建内置 CCC Agent 会话;飞书中会创建新群 |
|
|
408
|
-
| `/new dsh` | 创建 DeepSeek Harness 会话;飞书中会创建新群 |
|
|
408
|
+
| `/new ccc` | 创建内置 CCC Agent 会话;飞书中会创建新群 |
|
|
409
|
+
| `/new dsh` | 创建 DeepSeek Harness 会话;飞书中会创建新群 |
|
|
409
410
|
| `/forget` | 在当前聊天原地重置会话(忘掉上下文);群聊保留工作目录,飞书私聊固定使用系统用户目录 |
|
|
410
411
|
| `/model` | 查看或切换当前会话的模型 |
|
|
411
412
|
| `/fast` | 查看当前 Codex 会话的 Fast 模式;使用 `/fast on` 或 `/fast off` 切换 |
|
|
@@ -415,7 +416,7 @@ Codex 的默认模型和推理强度可继续由 `~/.codex/config.toml` 管理
|
|
|
415
416
|
| `/cd` | 查看或设置后续新建会话的默认工作目录,不改变当前会话;飞书私聊自身始终使用系统用户目录 |
|
|
416
417
|
| `/sessions` | 查看所有会话状态 |
|
|
417
418
|
| `/session <数字>` | 将当前群聊切换到 `/sessions` 列表中的指定会话;飞书私聊不支持切换 |
|
|
418
|
-
| `/usage` | 查看当前会话对应 Agent 的用量;Codex 显示 5h/7天窗口,Cursor 显示当前周期用量,CCC Agent 和 DSH 仅在官方 DeepSeek 端点时显示账户余额(其他兼容端点自动跳过) |
|
|
419
|
+
| `/usage` | 查看当前会话对应 Agent 的用量;Codex 显示 5h/7天窗口,Cursor 显示当前周期用量,CCC Agent 和 DSH 仅在官方 DeepSeek 端点时显示账户余额(其他兼容端点自动跳过) |
|
|
419
420
|
| `/git <子命令>` | 在当前会话工作目录执行 `git ...` 并回传输出 |
|
|
420
421
|
| `/abd<内容>` | 去掉 `/abd` 前缀后把内容发给 Agent,并在消息末尾追加第一性原理需求澄清提示 |
|
|
421
422
|
| `/plan <内容>` | 只读计划模式:仅允许读文件和 stop-stuck-loop 请求,不执行任何写操作 |
|
|
@@ -426,7 +427,7 @@ Codex 的默认模型和推理强度可继续由 `~/.codex/config.toml` 管理
|
|
|
426
427
|
|
|
427
428
|
`/update` 会在执行 npm 更新前把飞书消息或按钮事件 ID 原子写入 `~/.chatccc/state/update-command-guard.json`。同一 ID 跨重启重投时会静默忽略;用户主动发送的新 `/update` 因事件 ID 不同,仍可立即执行。该保护仅作用于 `/update`,普通消息与 `/restart` 的处理不变。
|
|
428
429
|
|
|
429
|
-
> **模型切换**:`/model` 查看当前会话 Agent 的可选模型清单,`/model <名称>` 模糊匹配切换,`/model clear` 恢复默认。可选模型来自当前 Agent 的配置:Claude 使用 `claude.model` / `claude.subagentModel`;Cursor、Codex 和
|
|
430
|
+
> **模型切换**:`/model` 查看当前会话 Agent 的可选模型清单,`/model <名称>` 模糊匹配切换,`/model clear` 恢复默认。可选模型来自当前 Agent 的配置:Claude 使用 `claude.model` / `claude.subagentModel`;Cursor、Codex、CCC Agent 和 DSH 使用各自的 `model` / `alternativeModel`。
|
|
430
431
|
|
|
431
432
|
> **Codex Fast 模式**:Web UI 中的“Fast 模式”设置新 Codex 会话的全局默认值,默认关闭。进入 Codex 会话后,`/fast` 查询当前状态,`/fast on` 和 `/fast off` 只覆盖当前会话并从下一条消息生效。ChatCCC 会显式向 Codex CLI 传入 `service_tier="fast"` 或 `service_tier="default"`,因此关闭时不会继承用户 `config.toml` 中可能开启的 Fast。
|
|
432
433
|
|
|
@@ -434,4 +435,4 @@ Codex 的默认模型和推理强度可继续由 `~/.codex/config.toml` 管理
|
|
|
434
435
|
|
|
435
436
|
## 技术栈
|
|
436
437
|
|
|
437
|
-
TypeScript(发布前编译为 JavaScript,tsx 仅用于开发)/ Node.js >= 20 / AI SDK / Anthropic Claude Agent SDK(按需下载)/ DeepSeek Harness SDK + JSON-RPC Runtime(按需下载,要求 Node.js >= 22.19.0)/ Cursor Agent CLI / Codex CLI / 飞书 WebSocket API / CardKit / 微信 iLink
|
|
438
|
+
TypeScript(发布前编译为 JavaScript,tsx 仅用于开发)/ Node.js >= 20 / AI SDK / Anthropic Claude Agent SDK(按需下载)/ DeepSeek Harness SDK + JSON-RPC Runtime(按需下载,要求 Node.js >= 22.19.0)/ Cursor Agent CLI / Codex CLI / 飞书 WebSocket API / CardKit / 微信 iLink
|
package/config.sample.json
CHANGED
|
@@ -37,20 +37,20 @@
|
|
|
37
37
|
"retentionDays": 7,
|
|
38
38
|
"keepCompleted": false
|
|
39
39
|
},
|
|
40
|
-
"ccc": {
|
|
41
|
-
"enabled": false,
|
|
42
|
-
"maxBytesPerTurn": 52428800,
|
|
43
|
-
"retentionDays": 7,
|
|
44
|
-
"keepCompleted": false
|
|
45
|
-
},
|
|
46
|
-
"dsh": {
|
|
47
|
-
"enabled": false,
|
|
48
|
-
"maxBytesPerTurn": 52428800,
|
|
49
|
-
"retentionDays": 7,
|
|
50
|
-
"keepCompleted": false
|
|
40
|
+
"ccc": {
|
|
41
|
+
"enabled": false,
|
|
42
|
+
"maxBytesPerTurn": 52428800,
|
|
43
|
+
"retentionDays": 7,
|
|
44
|
+
"keepCompleted": false
|
|
45
|
+
},
|
|
46
|
+
"dsh": {
|
|
47
|
+
"enabled": false,
|
|
48
|
+
"maxBytesPerTurn": 52428800,
|
|
49
|
+
"retentionDays": 7,
|
|
50
|
+
"keepCompleted": false
|
|
51
51
|
}
|
|
52
52
|
},
|
|
53
|
-
"ccc": {
|
|
53
|
+
"ccc": {
|
|
54
54
|
"enabled": false,
|
|
55
55
|
"defaultAgent": false,
|
|
56
56
|
"DEEPSEEK_API_KEY": "",
|
|
@@ -61,17 +61,19 @@
|
|
|
61
61
|
"effort": "",
|
|
62
62
|
"provider": "",
|
|
63
63
|
"compactionTimeoutMs": 300000,
|
|
64
|
-
"contextWindow": 1048576
|
|
65
|
-
},
|
|
66
|
-
"dsh": {
|
|
67
|
-
"enabled": false,
|
|
68
|
-
"defaultAgent": false,
|
|
69
|
-
"apiKey": "",
|
|
70
|
-
"baseUrl": "https://api.deepseek.com/v1",
|
|
71
|
-
"model": "deepseek-v4-flash",
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
|
|
64
|
+
"contextWindow": 1048576
|
|
65
|
+
},
|
|
66
|
+
"dsh": {
|
|
67
|
+
"enabled": false,
|
|
68
|
+
"defaultAgent": false,
|
|
69
|
+
"apiKey": "",
|
|
70
|
+
"baseUrl": "https://api.deepseek.com/v1",
|
|
71
|
+
"model": "deepseek-v4-flash",
|
|
72
|
+
"subModel": "",
|
|
73
|
+
"alternativeModel": "",
|
|
74
|
+
"provider": "deepseek-official",
|
|
75
|
+
"maxTokens": 49152
|
|
76
|
+
},
|
|
75
77
|
"claude": {
|
|
76
78
|
"enabled": false,
|
|
77
79
|
"defaultAgent": true,
|
|
@@ -50,6 +50,10 @@ export function createDshAdapter(options = {}) {
|
|
|
50
50
|
DSH_SESSION_ROOT: sessionRoot,
|
|
51
51
|
...(options.apiKey ? { DEEPSEEK_API_KEY: options.apiKey } : {}),
|
|
52
52
|
...(options.baseUrl ? { DEEPSEEK_BASE_URL: options.baseUrl } : {}),
|
|
53
|
+
// 子代理模型:subModel 留空时跟随主模型(与 CCC 的 ccc.subModel 语义一致)
|
|
54
|
+
DSH_SUBAGENT_PROVIDER: options.provider ?? "deepseek-official",
|
|
55
|
+
DSH_SUBAGENT_MODEL: options.subModel || options.model || "deepseek-v4-flash",
|
|
56
|
+
DSH_SUBAGENT_MAX_TOKENS: String(options.maxTokens ?? 49152),
|
|
53
57
|
},
|
|
54
58
|
},
|
|
55
59
|
cwd,
|
package/dist/src/config.js
CHANGED
|
@@ -60,6 +60,7 @@ export function getAllModelsForTool(tool, cfg = config) {
|
|
|
60
60
|
}
|
|
61
61
|
else if (tool === "dsh") {
|
|
62
62
|
collect(cfg.dsh.model);
|
|
63
|
+
collect(cfg.dsh.alternativeModel);
|
|
63
64
|
}
|
|
64
65
|
return Array.from(seen).slice(0, 100);
|
|
65
66
|
}
|
|
@@ -326,6 +327,8 @@ function loadConfig() {
|
|
|
326
327
|
apiKey: "",
|
|
327
328
|
baseUrl: DEFAULT_CCC_DEEPSEEK_BASE_URL,
|
|
328
329
|
model: "deepseek-v4-flash",
|
|
330
|
+
subModel: "",
|
|
331
|
+
alternativeModel: "",
|
|
329
332
|
provider: "deepseek-official",
|
|
330
333
|
maxTokens: 49152,
|
|
331
334
|
},
|
|
@@ -533,6 +536,8 @@ function loadConfig() {
|
|
|
533
536
|
apiKey: normalizeOptionalConfigField(dshRaw.apiKey, { label: "dsh.apiKey" }),
|
|
534
537
|
baseUrl: normalizeOptionalConfigField(dshRaw.baseUrl, { label: "dsh.baseUrl", fallback: DEFAULT_CCC_DEEPSEEK_BASE_URL }),
|
|
535
538
|
model: normalizeOptionalConfigField(dshRaw.model, { label: "dsh.model", fallback: "deepseek-v4-flash" }),
|
|
539
|
+
subModel: normalizeOptionalConfigField(dshRaw.subModel, { label: "dsh.subModel" }),
|
|
540
|
+
alternativeModel: normalizeOptionalConfigField(dshRaw.alternativeModel, { label: "dsh.alternativeModel" }),
|
|
536
541
|
provider: normalizeOptionalConfigField(dshRaw.provider, { label: "dsh.provider", fallback: "deepseek-official" }),
|
|
537
542
|
maxTokens: normalizePositiveInteger(dshRaw.maxTokens, 49152),
|
|
538
543
|
},
|
|
@@ -83,6 +83,10 @@ export const DSH_RUNTIME_CONFIG = `# Generated and owned by ChatCCC. Secrets are
|
|
|
83
83
|
provider: spawn
|
|
84
84
|
toolName: subagent
|
|
85
85
|
enableRunInBackground: false
|
|
86
|
+
agentOptions:
|
|
87
|
+
provider: !!js process.env.DSH_SUBAGENT_PROVIDER ?? 'deepseek-official'
|
|
88
|
+
model: !!js process.env.DSH_SUBAGENT_MODEL ?? 'deepseek-v4-flash'
|
|
89
|
+
maxTokens: !!js Number(process.env.DSH_SUBAGENT_MAX_TOKENS ?? '49152')
|
|
86
90
|
- id: tool-todo
|
|
87
91
|
name: '@deepseek-ai/dsh-tool-todo'
|
|
88
92
|
config:
|
package/dist/src/feishu-api.js
CHANGED
|
@@ -240,6 +240,7 @@ const AVATAR_BADGES = {
|
|
|
240
240
|
claude: resolvePath(AVATAR_BADGE_DIR, "badge_claude.png"),
|
|
241
241
|
cursor: resolvePath(AVATAR_BADGE_DIR, "badge_cursor.png"),
|
|
242
242
|
codex: resolvePath(AVATAR_BADGE_DIR, "badge_codex.png"),
|
|
243
|
+
dsh: resolvePath(AVATAR_BADGE_DIR, "badge_dsh.png"),
|
|
243
244
|
};
|
|
244
245
|
const AVATAR_SIZE = 256;
|
|
245
246
|
const AVATAR_BADGE_SIZE = 92;
|
package/dist/src/session.js
CHANGED
|
@@ -544,6 +544,7 @@ export function getAdapterForTool(tool, sessionId) {
|
|
|
544
544
|
apiKey: config.dsh.apiKey,
|
|
545
545
|
baseUrl: config.dsh.baseUrl,
|
|
546
546
|
model: effectiveModel || config.dsh.model,
|
|
547
|
+
...(config.dsh.subModel ? { subModel: config.dsh.subModel } : {}),
|
|
547
548
|
provider: config.dsh.provider,
|
|
548
549
|
maxTokens: config.dsh.maxTokens,
|
|
549
550
|
});
|
package/dist/src/web-ui.js
CHANGED
|
@@ -523,6 +523,14 @@ export function unflattenConfig(flat) {
|
|
|
523
523
|
result.dsh = result.dsh || {};
|
|
524
524
|
result.dsh.model = val;
|
|
525
525
|
}
|
|
526
|
+
else if (key === "CHATCCC_DSH_SUB_MODEL") {
|
|
527
|
+
result.dsh = result.dsh || {};
|
|
528
|
+
result.dsh.subModel = val;
|
|
529
|
+
}
|
|
530
|
+
else if (key === "CHATCCC_DSH_ALTERNATIVE_MODEL") {
|
|
531
|
+
result.dsh = result.dsh || {};
|
|
532
|
+
result.dsh.alternativeModel = val;
|
|
533
|
+
}
|
|
526
534
|
else if (key === "CHATCCC_DSH_PROVIDER") {
|
|
527
535
|
result.dsh = result.dsh || {};
|
|
528
536
|
result.dsh.provider = val;
|
|
@@ -697,12 +705,12 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
|
|
|
697
705
|
.btn-danger:hover{background:#dc2626}
|
|
698
706
|
.btn-success{background:#16a34a;color:#fff}
|
|
699
707
|
.btn-success:hover{background:#15803d}
|
|
700
|
-
.btn:disabled{opacity:.5;cursor:not-allowed}
|
|
701
|
-
.engine-status{font-size:13px;color:#64748b;margin-bottom:8px}
|
|
702
|
-
.engine-steps{display:grid;gap:5px;margin:8px 0 10px}
|
|
703
|
-
.engine-step{display:grid;grid-template-columns:18px 1fr auto;gap:7px;align-items:center;padding:5px 7px;border-radius:7px;background:#f8fafc;font-size:12px;color:#64748b}
|
|
704
|
-
.engine-step.running{background:#eff6ff;color:#1d4ed8}.engine-step.completed{color:#15803d}.engine-step.failed{background:#fef2f2;color:#dc2626}
|
|
705
|
-
.engine-step-icon{font-weight:700;text-align:center}.engine-step-percent{font-variant-numeric:tabular-nums;color:inherit}
|
|
708
|
+
.btn:disabled{opacity:.5;cursor:not-allowed}
|
|
709
|
+
.engine-status{font-size:13px;color:#64748b;margin-bottom:8px}
|
|
710
|
+
.engine-steps{display:grid;gap:5px;margin:8px 0 10px}
|
|
711
|
+
.engine-step{display:grid;grid-template-columns:18px 1fr auto;gap:7px;align-items:center;padding:5px 7px;border-radius:7px;background:#f8fafc;font-size:12px;color:#64748b}
|
|
712
|
+
.engine-step.running{background:#eff6ff;color:#1d4ed8}.engine-step.completed{color:#15803d}.engine-step.failed{background:#fef2f2;color:#dc2626}
|
|
713
|
+
.engine-step-icon{font-weight:700;text-align:center}.engine-step-percent{font-variant-numeric:tabular-nums;color:inherit}
|
|
706
714
|
.btn-group{display:flex;gap:8px;flex-wrap:wrap}
|
|
707
715
|
.agent-cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:12px;margin-bottom:16px}
|
|
708
716
|
.agent-card{min-width:0}
|
|
@@ -933,41 +941,43 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
|
|
|
933
941
|
<div class="hint">压缩阈值自动 = 窗口 × 80%(超出即把较早消息压缩为摘要)。⚠️ 超过模型/服务端实际上限时请求会被 API 直接拒绝(context length exceeded);实际窗口以模型与所用服务端为准(如 litellm 代理的 max_input_tokens)。</div>
|
|
934
942
|
</div>
|
|
935
943
|
</fieldset>
|
|
936
|
-
</div>
|
|
937
|
-
|
|
938
|
-
<!-- DeepSeek Harness 卡片 -->
|
|
939
|
-
<div class="agent-card" id="agent-card-dsh">
|
|
940
|
-
<div class="agent-card-header">
|
|
941
|
-
<input type="checkbox" class="agent-toggle" id="agent-enable-dsh" onchange="onInstallableAgentToggle('dsh', this)">
|
|
942
|
-
<div class="meta">
|
|
943
|
-
<div class="name">DeepSeek Harness</div>
|
|
944
|
-
<div class="desc">DeepSeek 官方 Agent Harness<br>按需安装,独立于 CCC Agent</div>
|
|
945
|
-
</div>
|
|
946
|
-
</div>
|
|
947
|
-
<label class="agent-default-row">
|
|
948
|
-
<input type="checkbox" id="agent-default-dsh" onchange="onDefaultAgentToggle('dsh', this.checked)">
|
|
949
|
-
设为默认 Agent
|
|
950
|
-
</label>
|
|
951
|
-
<fieldset class="agent-body" id="agent-body-dsh" disabled>
|
|
952
|
-
<div class="form-group"><label>API Key</label><input type="password" id="field-CHATCCC_DSH_API_KEY" placeholder="DeepSeek API Key"></div>
|
|
953
|
-
<div class="form-group"><label>Base URL</label><input type="text" id="field-CHATCCC_DSH_BASE_URL" placeholder="https://api.deepseek.com/v1"></div>
|
|
954
|
-
<div class="form-group"><label>模型</label><input type="text" id="field-CHATCCC_DSH_MODEL" placeholder="deepseek-v4-flash"></div>
|
|
955
|
-
<div class="form-group"><label
|
|
956
|
-
<div class="form-group"><label
|
|
957
|
-
<div class="form-group"
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
<
|
|
961
|
-
<
|
|
962
|
-
<div
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
944
|
+
</div>
|
|
945
|
+
|
|
946
|
+
<!-- DeepSeek Harness 卡片 -->
|
|
947
|
+
<div class="agent-card" id="agent-card-dsh">
|
|
948
|
+
<div class="agent-card-header">
|
|
949
|
+
<input type="checkbox" class="agent-toggle" id="agent-enable-dsh" onchange="onInstallableAgentToggle('dsh', this)">
|
|
950
|
+
<div class="meta">
|
|
951
|
+
<div class="name">DeepSeek Harness</div>
|
|
952
|
+
<div class="desc">DeepSeek 官方 Agent Harness<br>按需安装,独立于 CCC Agent</div>
|
|
953
|
+
</div>
|
|
954
|
+
</div>
|
|
955
|
+
<label class="agent-default-row">
|
|
956
|
+
<input type="checkbox" id="agent-default-dsh" onchange="onDefaultAgentToggle('dsh', this.checked)">
|
|
957
|
+
设为默认 Agent
|
|
958
|
+
</label>
|
|
959
|
+
<fieldset class="agent-body" id="agent-body-dsh" disabled>
|
|
960
|
+
<div class="form-group"><label>API Key</label><input type="password" id="field-CHATCCC_DSH_API_KEY" placeholder="DeepSeek API Key"></div>
|
|
961
|
+
<div class="form-group"><label>Base URL</label><input type="text" id="field-CHATCCC_DSH_BASE_URL" placeholder="https://api.deepseek.com/v1"></div>
|
|
962
|
+
<div class="form-group"><label>模型</label><input type="text" id="field-CHATCCC_DSH_MODEL" placeholder="deepseek-v4-flash"></div>
|
|
963
|
+
<div class="form-group"><label>子模型(选填)</label><input type="text" id="field-CHATCCC_DSH_SUB_MODEL" placeholder="留空跟随主模型;用于 subagent 子代理任务"></div>
|
|
964
|
+
<div class="form-group"><label>备选模型(选填)</label><input type="text" id="field-CHATCCC_DSH_ALTERNATIVE_MODEL" placeholder="加入 /model 列表,便于会话内切换"></div>
|
|
965
|
+
<div class="form-group"><label>Provider 路由</label><input type="text" id="field-CHATCCC_DSH_PROVIDER" placeholder="deepseek-official"></div>
|
|
966
|
+
<div class="form-group"><label>单次最大输出 Tokens</label><input type="number" id="field-CHATCCC_DSH_MAX_TOKENS" min="1" placeholder="49152"></div>
|
|
967
|
+
<div class="form-group" style="border-top:1px solid #e2e8f0;padding-top:12px;margin-top:4px">
|
|
968
|
+
<label>DeepSeek Harness 引擎</label>
|
|
969
|
+
<div id="dsh-engine-status" class="engine-status">检测中...</div>
|
|
970
|
+
<div id="dsh-engine-steps" class="engine-steps"></div>
|
|
971
|
+
<button type="button" class="btn btn-outline" id="dsh-engine-install-btn" onclick="installEngine('dsh')">安装并启用</button>
|
|
972
|
+
<div class="hint" style="margin-top:6px">一次点击自动完成环境检查、下载、校验、Runtime 握手和原子切换;页面刷新后仍可恢复进度。</div>
|
|
973
|
+
</div>
|
|
974
|
+
</fieldset>
|
|
975
|
+
</div>
|
|
976
|
+
|
|
977
|
+
<!-- Claude 卡片 -->
|
|
968
978
|
<div class="agent-card" id="agent-card-claude">
|
|
969
979
|
<div class="agent-card-header">
|
|
970
|
-
<input type="checkbox" class="agent-toggle" id="agent-enable-claude" onchange="onInstallableAgentToggle('claude', this)">
|
|
980
|
+
<input type="checkbox" class="agent-toggle" id="agent-enable-claude" onchange="onInstallableAgentToggle('claude', this)">
|
|
971
981
|
<div class="meta">
|
|
972
982
|
<div class="name">Claude Code</div>
|
|
973
983
|
<div class="desc">Anthropic Claude Code CLI<br>模型、effort 均为选填</div>
|
|
@@ -1000,9 +1010,9 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
|
|
|
1000
1010
|
</div>
|
|
1001
1011
|
<div class="form-group" style="border-top:1px solid #e2e8f0;padding-top:12px;margin-top:4px">
|
|
1002
1012
|
<label>Claude Code 引擎(Agent SDK)</label>
|
|
1003
|
-
<div id="claude-engine-status" class="engine-status">检测中...</div>
|
|
1004
|
-
<div id="claude-engine-steps" class="engine-steps"></div>
|
|
1005
|
-
<button type="button" class="btn btn-outline" id="claude-engine-install-btn" onclick="installEngine('claude')">安装并启用</button>
|
|
1013
|
+
<div id="claude-engine-status" class="engine-status">检测中...</div>
|
|
1014
|
+
<div id="claude-engine-steps" class="engine-steps"></div>
|
|
1015
|
+
<button type="button" class="btn btn-outline" id="claude-engine-install-btn" onclick="installEngine('claude')">安装并启用</button>
|
|
1006
1016
|
<div class="hint" style="margin-top:6px">ChatCCC 通过 Claude Agent SDK 调用 Claude Code;SDK 引擎按需下载到本机(仅启用 Claude Code 时需要),安装期间请保持网络畅通。</div>
|
|
1007
1017
|
</div>
|
|
1008
1018
|
</fieldset>
|
|
@@ -1180,7 +1190,7 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
|
|
|
1180
1190
|
</div>
|
|
1181
1191
|
</details>
|
|
1182
1192
|
|
|
1183
|
-
<details class="card config-section" id="dash-claude">
|
|
1193
|
+
<details class="card config-section" id="dash-claude">
|
|
1184
1194
|
<summary>Claude Agent</summary>
|
|
1185
1195
|
<div class="section-detail">
|
|
1186
1196
|
<div class="config-row"><span class="key">模型</span><span class="val" id="cfg-ANTHROPIC_MODEL">-</span></div>
|
|
@@ -1193,24 +1203,26 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
|
|
|
1193
1203
|
<div class="hint" style="margin-top:6px;line-height:1.6">生效范围:保存后下一条消息或下个新会话生效,当前生成不中断。</div>
|
|
1194
1204
|
<button class="btn btn-outline" style="margin-top:8px" onclick="editSection('claude')">编辑</button>
|
|
1195
1205
|
</div>
|
|
1196
|
-
</details>
|
|
1197
|
-
|
|
1198
|
-
<details class="card config-section" id="dash-dsh">
|
|
1199
|
-
<summary>DeepSeek Harness</summary>
|
|
1200
|
-
<div class="section-detail">
|
|
1201
|
-
<div class="config-row"><span class="key">API Key</span><span class="val" id="cfg-DSH_API_KEY">-</span></div>
|
|
1202
|
-
<div class="config-row"><span class="key">Base URL</span><span class="val" id="cfg-DSH_BASE_URL">-</span></div>
|
|
1203
|
-
<div class="config-row"><span class="key">模型</span><span class="val" id="cfg-DSH_MODEL">-</span></div>
|
|
1204
|
-
<div class="config-row"><span class="key"
|
|
1205
|
-
<div class="config-row"><span class="key"
|
|
1206
|
-
<div
|
|
1207
|
-
<div
|
|
1208
|
-
<
|
|
1209
|
-
<
|
|
1210
|
-
<
|
|
1211
|
-
<
|
|
1212
|
-
|
|
1213
|
-
|
|
1206
|
+
</details>
|
|
1207
|
+
|
|
1208
|
+
<details class="card config-section" id="dash-dsh">
|
|
1209
|
+
<summary>DeepSeek Harness</summary>
|
|
1210
|
+
<div class="section-detail">
|
|
1211
|
+
<div class="config-row"><span class="key">API Key</span><span class="val" id="cfg-DSH_API_KEY">-</span></div>
|
|
1212
|
+
<div class="config-row"><span class="key">Base URL</span><span class="val" id="cfg-DSH_BASE_URL">-</span></div>
|
|
1213
|
+
<div class="config-row"><span class="key">模型</span><span class="val" id="cfg-DSH_MODEL">-</span></div>
|
|
1214
|
+
<div class="config-row"><span class="key">子模型</span><span class="val" id="cfg-DSH_SUB_MODEL">-</span></div>
|
|
1215
|
+
<div class="config-row"><span class="key">备选模型</span><span class="val" id="cfg-DSH_ALTERNATIVE_MODEL">-</span></div>
|
|
1216
|
+
<div class="config-row"><span class="key">Provider</span><span class="val" id="cfg-DSH_PROVIDER">-</span></div>
|
|
1217
|
+
<div class="config-row"><span class="key">最大输出 Tokens</span><span class="val" id="cfg-DSH_MAX_TOKENS">-</span></div>
|
|
1218
|
+
<div id="dsh-dashboard-engine-status" class="engine-status">检测中...</div>
|
|
1219
|
+
<div id="dsh-dashboard-engine-steps" class="engine-steps"></div>
|
|
1220
|
+
<button type="button" class="btn btn-outline" id="dsh-dashboard-engine-install-btn" onclick="installEngine('dsh')">安装或升级引擎</button>
|
|
1221
|
+
<label class="agent-default-row" style="margin-top:10px"><input type="checkbox" id="dash-default-dsh" onchange="setDashboardDefaultAgent('dsh', this.checked)"> 设为默认 Agent</label>
|
|
1222
|
+
<div class="hint" style="margin-top:6px;line-height:1.6">安装过程不调用付费模型;保存后下一条消息或下个新会话生效。</div>
|
|
1223
|
+
<button class="btn btn-outline" style="margin-top:8px" onclick="editSection('dsh')">编辑</button>
|
|
1224
|
+
</div>
|
|
1225
|
+
</details>
|
|
1214
1226
|
|
|
1215
1227
|
<details class="card config-section" id="dash-cursor">
|
|
1216
1228
|
<summary>Cursor Agent</summary>
|
|
@@ -1281,8 +1293,8 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
|
|
|
1281
1293
|
<script>
|
|
1282
1294
|
let state = {
|
|
1283
1295
|
view: 'loading',
|
|
1284
|
-
// 五个 Agent 各自的启用开关;初始全 false,renderStep2() 会按已存在 config 自动打开
|
|
1285
|
-
agentsEnabled: { claude: false, cursor: false, codex: false, ccc: false, dsh: false },
|
|
1296
|
+
// 五个 Agent 各自的启用开关;初始全 false,renderStep2() 会按已存在 config 自动打开
|
|
1297
|
+
agentsEnabled: { claude: false, cursor: false, codex: false, ccc: false, dsh: false },
|
|
1286
1298
|
defaultAgent: 'claude',
|
|
1287
1299
|
wizardStep: 1,
|
|
1288
1300
|
config: {},
|
|
@@ -1299,8 +1311,8 @@ const AGENT_FIELDS = {
|
|
|
1299
1311
|
claude: ['CHATCCC_ANTHROPIC_MODEL','CHATCCC_ANTHROPIC_SUBAGENT_MODEL','CHATCCC_ANTHROPIC_EFFORT','CHATCCC_ANTHROPIC_API_KEY','CHATCCC_ANTHROPIC_BASE_URL','CHATCCC_ANTHROPIC_MAX_TURN'],
|
|
1300
1312
|
cursor: ['CHATCCC_CURSOR_PATH','CHATCCC_CURSOR_MODEL','CHATCCC_CURSOR_ALTERNATIVE_MODEL','CHATCCC_CURSOR_AVATAR_BATTERY_MODE','CHATCCC_CURSOR_ON_DEMAND_MONTHLY_BUDGET'],
|
|
1301
1313
|
codex: ['CHATCCC_CODEX_PATH','CHATCCC_CODEX_MODEL','CHATCCC_CODEX_ALTERNATIVE_MODEL','CHATCCC_CODEX_EFFORT','CHATCCC_CODEX_FAST_MODE'],
|
|
1302
|
-
ccc: ['CHATCCC_CCC_API_KEY','CHATCCC_CCC_BASE_URL','CHATCCC_CCC_MODEL','CHATCCC_CCC_SUB_MODEL','CHATCCC_CCC_ALTERNATIVE_MODEL','CHATCCC_CCC_EFFORT','CHATCCC_CCC_PROVIDER','CHATCCC_CCC_CONTEXT_WINDOW'],
|
|
1303
|
-
dsh: ['CHATCCC_DSH_API_KEY','CHATCCC_DSH_BASE_URL','CHATCCC_DSH_MODEL','CHATCCC_DSH_PROVIDER','CHATCCC_DSH_MAX_TOKENS']
|
|
1314
|
+
ccc: ['CHATCCC_CCC_API_KEY','CHATCCC_CCC_BASE_URL','CHATCCC_CCC_MODEL','CHATCCC_CCC_SUB_MODEL','CHATCCC_CCC_ALTERNATIVE_MODEL','CHATCCC_CCC_EFFORT','CHATCCC_CCC_PROVIDER','CHATCCC_CCC_CONTEXT_WINDOW'],
|
|
1315
|
+
dsh: ['CHATCCC_DSH_API_KEY','CHATCCC_DSH_BASE_URL','CHATCCC_DSH_MODEL','CHATCCC_DSH_SUB_MODEL','CHATCCC_DSH_ALTERNATIVE_MODEL','CHATCCC_DSH_PROVIDER','CHATCCC_DSH_MAX_TOKENS']
|
|
1304
1316
|
};
|
|
1305
1317
|
const FEISHU_FIELDS = ['CHATCCC_APP_ID','CHATCCC_APP_SECRET'];
|
|
1306
1318
|
const WEB_UI_FIELDS = ['CHATCCC_WEB_UI_OPEN_ON_START'];
|
|
@@ -1454,31 +1466,31 @@ function onAgentToggle(agent, enabled) {
|
|
|
1454
1466
|
updateStep2NextBtn();
|
|
1455
1467
|
}
|
|
1456
1468
|
|
|
1457
|
-
function firstEnabledAgent() {
|
|
1469
|
+
function firstEnabledAgent() {
|
|
1458
1470
|
if (state.agentsEnabled.claude) return 'claude';
|
|
1459
1471
|
if (state.agentsEnabled.cursor) return 'cursor';
|
|
1460
1472
|
if (state.agentsEnabled.codex) return 'codex';
|
|
1461
|
-
if (state.agentsEnabled.ccc) return 'ccc';
|
|
1462
|
-
if (state.agentsEnabled.dsh) return 'dsh';
|
|
1473
|
+
if (state.agentsEnabled.ccc) return 'ccc';
|
|
1474
|
+
if (state.agentsEnabled.dsh) return 'dsh';
|
|
1463
1475
|
return null;
|
|
1464
1476
|
}
|
|
1465
1477
|
|
|
1466
|
-
function resolveDefaultAgentFromConfig(c, claudeOn, cursorOn, codexOn, cccOn, dshOn) {
|
|
1478
|
+
function resolveDefaultAgentFromConfig(c, claudeOn, cursorOn, codexOn, cccOn, dshOn) {
|
|
1467
1479
|
if (claudeOn && c.claude && c.claude.defaultAgent === true) return 'claude';
|
|
1468
1480
|
if (cursorOn && c.cursor && c.cursor.defaultAgent === true) return 'cursor';
|
|
1469
1481
|
if (codexOn && c.codex && c.codex.defaultAgent === true) return 'codex';
|
|
1470
|
-
if (cccOn && c.ccc && c.ccc.defaultAgent === true) return 'ccc';
|
|
1471
|
-
if (dshOn && c.dsh && c.dsh.defaultAgent === true) return 'dsh';
|
|
1482
|
+
if (cccOn && c.ccc && c.ccc.defaultAgent === true) return 'ccc';
|
|
1483
|
+
if (dshOn && c.dsh && c.dsh.defaultAgent === true) return 'dsh';
|
|
1472
1484
|
if (claudeOn) return 'claude';
|
|
1473
1485
|
if (cursorOn) return 'cursor';
|
|
1474
1486
|
if (codexOn) return 'codex';
|
|
1475
|
-
if (cccOn) return 'ccc';
|
|
1476
|
-
if (dshOn) return 'dsh';
|
|
1487
|
+
if (cccOn) return 'ccc';
|
|
1488
|
+
if (dshOn) return 'dsh';
|
|
1477
1489
|
return 'claude';
|
|
1478
1490
|
}
|
|
1479
1491
|
|
|
1480
1492
|
function updateDefaultAgentToggles() {
|
|
1481
|
-
['claude','cursor','codex','ccc','dsh'].forEach(function(agent){
|
|
1493
|
+
['claude','cursor','codex','ccc','dsh'].forEach(function(agent){
|
|
1482
1494
|
var el = document.getElementById('agent-default-' + agent);
|
|
1483
1495
|
if (el) {
|
|
1484
1496
|
el.checked = state.defaultAgent === agent;
|
|
@@ -1510,8 +1522,8 @@ function updateStep2NextBtn() {
|
|
|
1510
1522
|
if (state.agentsEnabled.claude) validCount++;
|
|
1511
1523
|
if (state.agentsEnabled.cursor) validCount++;
|
|
1512
1524
|
if (state.agentsEnabled.codex) validCount++;
|
|
1513
|
-
if (state.agentsEnabled.ccc) validCount++;
|
|
1514
|
-
if (state.agentsEnabled.dsh) validCount++;
|
|
1525
|
+
if (state.agentsEnabled.ccc) validCount++;
|
|
1526
|
+
if (state.agentsEnabled.dsh) validCount++;
|
|
1515
1527
|
if (validCount > 0 && !state.defaultAgent) {
|
|
1516
1528
|
state.defaultAgent = firstEnabledAgent();
|
|
1517
1529
|
updateDefaultAgentToggles();
|
|
@@ -1653,8 +1665,8 @@ var CLAUDE_FALLBACK_KEYS = ['model','subagentModel','effort','maxTurn'];
|
|
|
1653
1665
|
var CURSOR_FALLBACK_KEYS = ['path','command','model','alternativeModel'];
|
|
1654
1666
|
var CODEX_FALLBACK_KEYS = ['path','command','model','alternativeModel','effort','fastMode'];
|
|
1655
1667
|
// 旧版 CCC 配置没有 enabled;只有 API Key 才表示用户实际完成了配置。
|
|
1656
|
-
var CCC_FALLBACK_KEYS = ['DEEPSEEK_API_KEY'];
|
|
1657
|
-
var DSH_FALLBACK_KEYS = ['apiKey'];
|
|
1668
|
+
var CCC_FALLBACK_KEYS = ['DEEPSEEK_API_KEY'];
|
|
1669
|
+
var DSH_FALLBACK_KEYS = ['apiKey'];
|
|
1658
1670
|
|
|
1659
1671
|
function renderStep2() {
|
|
1660
1672
|
var c = state.config || {};
|
|
@@ -1687,7 +1699,7 @@ function renderStep2() {
|
|
|
1687
1699
|
}
|
|
1688
1700
|
var codexFastModeEl = document.getElementById('field-CHATCCC_CODEX_FAST_MODE');
|
|
1689
1701
|
if (codexFastModeEl) codexFastModeEl.checked = !!(c.codex && c.codex.fastMode === true);
|
|
1690
|
-
if (c.ccc) {
|
|
1702
|
+
if (c.ccc) {
|
|
1691
1703
|
prefillNested('field-CHATCCC_CCC_API_KEY', c.ccc.DEEPSEEK_API_KEY);
|
|
1692
1704
|
prefillNested('field-CHATCCC_CCC_BASE_URL', c.ccc.DEEPSEEK_BASE_URL);
|
|
1693
1705
|
prefillNested('field-CHATCCC_CCC_PROVIDER', c.ccc.provider);
|
|
@@ -1695,37 +1707,39 @@ function renderStep2() {
|
|
|
1695
1707
|
prefillNested('field-CHATCCC_CCC_SUB_MODEL', c.ccc.subModel);
|
|
1696
1708
|
prefillNested('field-CHATCCC_CCC_ALTERNATIVE_MODEL', c.ccc.alternativeModel);
|
|
1697
1709
|
prefillNested('field-CHATCCC_CCC_EFFORT', c.ccc.effort);
|
|
1698
|
-
prefillContextWindow('field-', c.ccc.contextWindow);
|
|
1699
|
-
}
|
|
1700
|
-
if (c.dsh) {
|
|
1701
|
-
prefillNested('field-CHATCCC_DSH_API_KEY', c.dsh.apiKey);
|
|
1702
|
-
prefillNested('field-CHATCCC_DSH_BASE_URL', c.dsh.baseUrl);
|
|
1703
|
-
prefillNested('field-CHATCCC_DSH_MODEL', c.dsh.model);
|
|
1704
|
-
prefillNested('field-
|
|
1705
|
-
prefillNested('field-
|
|
1706
|
-
|
|
1710
|
+
prefillContextWindow('field-', c.ccc.contextWindow);
|
|
1711
|
+
}
|
|
1712
|
+
if (c.dsh) {
|
|
1713
|
+
prefillNested('field-CHATCCC_DSH_API_KEY', c.dsh.apiKey);
|
|
1714
|
+
prefillNested('field-CHATCCC_DSH_BASE_URL', c.dsh.baseUrl);
|
|
1715
|
+
prefillNested('field-CHATCCC_DSH_MODEL', c.dsh.model);
|
|
1716
|
+
prefillNested('field-CHATCCC_DSH_SUB_MODEL', c.dsh.subModel);
|
|
1717
|
+
prefillNested('field-CHATCCC_DSH_ALTERNATIVE_MODEL', c.dsh.alternativeModel);
|
|
1718
|
+
prefillNested('field-CHATCCC_DSH_PROVIDER', c.dsh.provider);
|
|
1719
|
+
prefillNested('field-CHATCCC_DSH_MAX_TOKENS', c.dsh.maxTokens || 49152);
|
|
1720
|
+
}
|
|
1707
1721
|
|
|
1708
1722
|
// 按已有 config 决定每个 Agent 默认是否开启:优先 enabled 字段,缺省时按"任一字段非空"
|
|
1709
1723
|
var claudeOn = isAgentEnabled(c.claude, CLAUDE_FALLBACK_KEYS);
|
|
1710
1724
|
var cursorOn = isAgentEnabled(c.cursor, CURSOR_FALLBACK_KEYS);
|
|
1711
1725
|
var codexOn = isAgentEnabled(c.codex, CODEX_FALLBACK_KEYS);
|
|
1712
|
-
var cccOn = isAgentEnabled(c.ccc, CCC_FALLBACK_KEYS);
|
|
1713
|
-
var dshOn = isAgentEnabled(c.dsh, DSH_FALLBACK_KEYS);
|
|
1714
|
-
// 全新用户:五个 Agent 均无启用/配置痕迹时,只默认勾选 DeepCCC(ccc),其余不勾
|
|
1715
|
-
if (!claudeOn && !cursorOn && !codexOn && !cccOn && !dshOn) {
|
|
1726
|
+
var cccOn = isAgentEnabled(c.ccc, CCC_FALLBACK_KEYS);
|
|
1727
|
+
var dshOn = isAgentEnabled(c.dsh, DSH_FALLBACK_KEYS);
|
|
1728
|
+
// 全新用户:五个 Agent 均无启用/配置痕迹时,只默认勾选 DeepCCC(ccc),其余不勾
|
|
1729
|
+
if (!claudeOn && !cursorOn && !codexOn && !cccOn && !dshOn) {
|
|
1716
1730
|
cccOn = true;
|
|
1717
1731
|
}
|
|
1718
|
-
state.defaultAgent = resolveDefaultAgentFromConfig(c, claudeOn, cursorOn, codexOn, cccOn, dshOn);
|
|
1732
|
+
state.defaultAgent = resolveDefaultAgentFromConfig(c, claudeOn, cursorOn, codexOn, cccOn, dshOn);
|
|
1719
1733
|
document.getElementById('agent-enable-claude').checked = claudeOn;
|
|
1720
1734
|
document.getElementById('agent-enable-cursor').checked = cursorOn;
|
|
1721
1735
|
document.getElementById('agent-enable-codex').checked = codexOn;
|
|
1722
|
-
document.getElementById('agent-enable-ccc').checked = cccOn;
|
|
1723
|
-
document.getElementById('agent-enable-dsh').checked = dshOn;
|
|
1736
|
+
document.getElementById('agent-enable-ccc').checked = cccOn;
|
|
1737
|
+
document.getElementById('agent-enable-dsh').checked = dshOn;
|
|
1724
1738
|
onAgentToggle('claude', claudeOn);
|
|
1725
1739
|
onAgentToggle('cursor', cursorOn);
|
|
1726
1740
|
onAgentToggle('codex', codexOn);
|
|
1727
|
-
onAgentToggle('ccc', cccOn);
|
|
1728
|
-
onAgentToggle('dsh', dshOn);
|
|
1741
|
+
onAgentToggle('ccc', cccOn);
|
|
1742
|
+
onAgentToggle('dsh', dshOn);
|
|
1729
1743
|
updateDefaultAgentToggles();
|
|
1730
1744
|
|
|
1731
1745
|
// Cursor path placeholder/hint:把已探测到的路径显示为占位
|
|
@@ -1749,7 +1763,7 @@ function renderStep2() {
|
|
|
1749
1763
|
* 收集"待落地到 config.json 的扁平 vars"。
|
|
1750
1764
|
*
|
|
1751
1765
|
* - 飞书字段始终收集
|
|
1752
|
-
* - 五个 Agent 的 enabled 状态都显式下发,让 config.json 持久化用户的最新开关偏好
|
|
1766
|
+
* - 五个 Agent 的 enabled 状态都显式下发,让 config.json 持久化用户的最新开关偏好
|
|
1753
1767
|
* - Agent 字段仅在该 Agent 开关启用时收集;未启用的 Agent 不下发其它字段,
|
|
1754
1768
|
* 服务端 deepMerge 会保留 config.json 中已有值(避免关闭开关时误清空旧配置)
|
|
1755
1769
|
*/
|
|
@@ -1775,16 +1789,16 @@ function collectAllFields() {
|
|
|
1775
1789
|
vars.CHATCCC_CLAUDE_ENABLED = !!state.agentsEnabled.claude;
|
|
1776
1790
|
vars.CHATCCC_CURSOR_ENABLED = !!state.agentsEnabled.cursor;
|
|
1777
1791
|
vars.CHATCCC_CODEX_ENABLED = !!state.agentsEnabled.codex;
|
|
1778
|
-
vars.CHATCCC_CCC_ENABLED = !!state.agentsEnabled.ccc;
|
|
1779
|
-
vars.CHATCCC_DSH_ENABLED = !!state.agentsEnabled.dsh;
|
|
1792
|
+
vars.CHATCCC_CCC_ENABLED = !!state.agentsEnabled.ccc;
|
|
1793
|
+
vars.CHATCCC_DSH_ENABLED = !!state.agentsEnabled.dsh;
|
|
1780
1794
|
if (!state.defaultAgent || !state.agentsEnabled[state.defaultAgent]) {
|
|
1781
1795
|
state.defaultAgent = firstEnabledAgent();
|
|
1782
1796
|
}
|
|
1783
1797
|
vars.CHATCCC_CLAUDE_DEFAULT_AGENT = state.defaultAgent === 'claude';
|
|
1784
1798
|
vars.CHATCCC_CURSOR_DEFAULT_AGENT = state.defaultAgent === 'cursor';
|
|
1785
1799
|
vars.CHATCCC_CODEX_DEFAULT_AGENT = state.defaultAgent === 'codex';
|
|
1786
|
-
vars.CHATCCC_CCC_DEFAULT_AGENT = state.defaultAgent === 'ccc';
|
|
1787
|
-
vars.CHATCCC_DSH_DEFAULT_AGENT = state.defaultAgent === 'dsh';
|
|
1800
|
+
vars.CHATCCC_CCC_DEFAULT_AGENT = state.defaultAgent === 'ccc';
|
|
1801
|
+
vars.CHATCCC_DSH_DEFAULT_AGENT = state.defaultAgent === 'dsh';
|
|
1788
1802
|
if (state.agentsEnabled.claude) {
|
|
1789
1803
|
AGENT_FIELDS.claude.forEach(function(key){
|
|
1790
1804
|
var el = document.getElementById('field-' + key);
|
|
@@ -1805,7 +1819,7 @@ function collectAllFields() {
|
|
|
1805
1819
|
else if (el.value.trim()) vars[key] = el.value.trim();
|
|
1806
1820
|
});
|
|
1807
1821
|
}
|
|
1808
|
-
if (state.agentsEnabled.ccc) {
|
|
1822
|
+
if (state.agentsEnabled.ccc) {
|
|
1809
1823
|
AGENT_FIELDS.ccc.forEach(function(key){
|
|
1810
1824
|
if (key === 'CHATCCC_CCC_CONTEXT_WINDOW') {
|
|
1811
1825
|
var cw = contextWindowToTokensValue('field-');
|
|
@@ -1815,13 +1829,13 @@ function collectAllFields() {
|
|
|
1815
1829
|
var el = document.getElementById('field-' + key);
|
|
1816
1830
|
if (el && el.value.trim()) vars[key] = el.value.trim();
|
|
1817
1831
|
});
|
|
1818
|
-
}
|
|
1819
|
-
if (state.agentsEnabled.dsh) {
|
|
1820
|
-
AGENT_FIELDS.dsh.forEach(function(key){
|
|
1821
|
-
var el = document.getElementById('field-' + key);
|
|
1822
|
-
if (el && el.value.trim()) vars[key] = el.value.trim();
|
|
1823
|
-
});
|
|
1824
|
-
}
|
|
1832
|
+
}
|
|
1833
|
+
if (state.agentsEnabled.dsh) {
|
|
1834
|
+
AGENT_FIELDS.dsh.forEach(function(key){
|
|
1835
|
+
var el = document.getElementById('field-' + key);
|
|
1836
|
+
if (el && el.value.trim()) vars[key] = el.value.trim();
|
|
1837
|
+
});
|
|
1838
|
+
}
|
|
1825
1839
|
return vars;
|
|
1826
1840
|
}
|
|
1827
1841
|
|
|
@@ -1861,8 +1875,8 @@ function renderStep3() {
|
|
|
1861
1875
|
if (state.agentsEnabled.claude) enabledList.push('claude');
|
|
1862
1876
|
if (state.agentsEnabled.cursor) enabledList.push('cursor');
|
|
1863
1877
|
if (state.agentsEnabled.codex) enabledList.push('codex');
|
|
1864
|
-
if (state.agentsEnabled.ccc) enabledList.push('ccc');
|
|
1865
|
-
if (state.agentsEnabled.dsh) enabledList.push('dsh');
|
|
1878
|
+
if (state.agentsEnabled.ccc) enabledList.push('ccc');
|
|
1879
|
+
if (state.agentsEnabled.dsh) enabledList.push('dsh');
|
|
1866
1880
|
if (enabledList.length === 0) {
|
|
1867
1881
|
lines.push('<div style="color:#ef4444">未启用任何 AI Agent</div>');
|
|
1868
1882
|
} else {
|
|
@@ -1893,7 +1907,7 @@ function renderStep3() {
|
|
|
1893
1907
|
lines.push('<div class="config-row"><span class="key">备选模型</span><span class="val">' + (vars.CHATCCC_CODEX_ALTERNATIVE_MODEL || '(留空)') + '</span></div>');
|
|
1894
1908
|
lines.push('<div class="config-row"><span class="key">Effort</span><span class="val">' + (vars.CHATCCC_CODEX_EFFORT || '(留空)') + '</span></div>');
|
|
1895
1909
|
lines.push('<div class="config-row"><span class="key">Fast 模式</span><span class="val">' + (vars.CHATCCC_CODEX_FAST_MODE ? '已启用' : '已禁用') + '</span></div>');
|
|
1896
|
-
} else if (t === 'ccc') {
|
|
1910
|
+
} else if (t === 'ccc') {
|
|
1897
1911
|
lines.push('<h4 style="margin:10px 0 4px;color:#334155">CCC Agent</h4>');
|
|
1898
1912
|
lines.push('<div class="config-row"><span class="key">API Key</span><span class="val">' + (vars.CHATCCC_CCC_API_KEY ? '***已设置***' : '(留空)') + '</span></div>');
|
|
1899
1913
|
lines.push('<div class="config-row"><span class="key">Base URL</span><span class="val">' + (vars.CHATCCC_CCC_BASE_URL || '(留空)') + '</span></div>');
|
|
@@ -1901,12 +1915,12 @@ function renderStep3() {
|
|
|
1901
1915
|
lines.push('<div class="config-row"><span class="key">子模型</span><span class="val">' + (vars.CHATCCC_CCC_SUB_MODEL || '(留空,跟随主模型)') + '</span></div>');
|
|
1902
1916
|
lines.push('<div class="config-row"><span class="key">备选模型</span><span class="val">' + (vars.CHATCCC_CCC_ALTERNATIVE_MODEL || '(留空)') + '</span></div>');
|
|
1903
1917
|
lines.push('<div class="config-row"><span class="key">Effort</span><span class="val">' + (vars.CHATCCC_CCC_EFFORT || '(留空)') + '</span></div>');
|
|
1904
|
-
lines.push('<div class="config-row"><span class="key">上下文窗口</span><span class="val">' + contextWindowTokensLabel(vars.CHATCCC_CCC_CONTEXT_WINDOW || 1048576) + '</span></div>');
|
|
1905
|
-
} else if (t === 'dsh') {
|
|
1906
|
-
lines.push('<h4 style="margin:10px 0 4px;color:#334155">DeepSeek Harness</h4>');
|
|
1907
|
-
lines.push('<div class="config-row"><span class="key">API Key</span><span class="val">' + (vars.CHATCCC_DSH_API_KEY ? '***已设置***' : '(留空)') + '</span></div>');
|
|
1908
|
-
lines.push('<div class="config-row"><span class="key">Base URL</span><span class="val">' + (vars.CHATCCC_DSH_BASE_URL || '(默认)') + '</span></div>');
|
|
1909
|
-
lines.push('<div class="config-row"><span class="key">模型</span><span class="val">' + (vars.CHATCCC_DSH_MODEL || 'deepseek-v4-flash') + '</span></div>');
|
|
1918
|
+
lines.push('<div class="config-row"><span class="key">上下文窗口</span><span class="val">' + contextWindowTokensLabel(vars.CHATCCC_CCC_CONTEXT_WINDOW || 1048576) + '</span></div>');
|
|
1919
|
+
} else if (t === 'dsh') {
|
|
1920
|
+
lines.push('<h4 style="margin:10px 0 4px;color:#334155">DeepSeek Harness</h4>');
|
|
1921
|
+
lines.push('<div class="config-row"><span class="key">API Key</span><span class="val">' + (vars.CHATCCC_DSH_API_KEY ? '***已设置***' : '(留空)') + '</span></div>');
|
|
1922
|
+
lines.push('<div class="config-row"><span class="key">Base URL</span><span class="val">' + (vars.CHATCCC_DSH_BASE_URL || '(默认)') + '</span></div>');
|
|
1923
|
+
lines.push('<div class="config-row"><span class="key">模型</span><span class="val">' + (vars.CHATCCC_DSH_MODEL || 'deepseek-v4-flash') + '</span></div>');
|
|
1910
1924
|
}
|
|
1911
1925
|
});
|
|
1912
1926
|
document.getElementById('review-content').innerHTML = lines.join('');
|
|
@@ -1934,21 +1948,21 @@ async function setDashboardDefaultAgent(agent, enabled) {
|
|
|
1934
1948
|
CHATCCC_CLAUDE_DEFAULT_AGENT: agent === 'claude',
|
|
1935
1949
|
CHATCCC_CURSOR_DEFAULT_AGENT: agent === 'cursor',
|
|
1936
1950
|
CHATCCC_CODEX_DEFAULT_AGENT: agent === 'codex',
|
|
1937
|
-
CHATCCC_CCC_DEFAULT_AGENT: agent === 'ccc',
|
|
1938
|
-
CHATCCC_DSH_DEFAULT_AGENT: agent === 'dsh'
|
|
1951
|
+
CHATCCC_CCC_DEFAULT_AGENT: agent === 'ccc',
|
|
1952
|
+
CHATCCC_DSH_DEFAULT_AGENT: agent === 'dsh'
|
|
1939
1953
|
};
|
|
1940
1954
|
var result = await api('/api/config', 'POST', { vars: vars });
|
|
1941
1955
|
if (result.ok) {
|
|
1942
1956
|
state.config.claude = state.config.claude || {};
|
|
1943
1957
|
state.config.cursor = state.config.cursor || {};
|
|
1944
1958
|
state.config.codex = state.config.codex || {};
|
|
1945
|
-
state.config.ccc = state.config.ccc || {};
|
|
1946
|
-
state.config.dsh = state.config.dsh || {};
|
|
1959
|
+
state.config.ccc = state.config.ccc || {};
|
|
1960
|
+
state.config.dsh = state.config.dsh || {};
|
|
1947
1961
|
state.config.claude.defaultAgent = agent === 'claude';
|
|
1948
1962
|
state.config.cursor.defaultAgent = agent === 'cursor';
|
|
1949
1963
|
state.config.codex.defaultAgent = agent === 'codex';
|
|
1950
|
-
state.config.ccc.defaultAgent = agent === 'ccc';
|
|
1951
|
-
state.config.dsh.defaultAgent = agent === 'dsh';
|
|
1964
|
+
state.config.ccc.defaultAgent = agent === 'ccc';
|
|
1965
|
+
state.config.dsh.defaultAgent = agent === 'dsh';
|
|
1952
1966
|
updateDefaultAgentToggles();
|
|
1953
1967
|
toastConfigApplyResult(result, '默认 Agent 已更新');
|
|
1954
1968
|
} else {
|
|
@@ -2065,10 +2079,10 @@ function updateDashboardUI() {
|
|
|
2065
2079
|
var claudeOn = isAgentEnabled(c.claude, CLAUDE_FALLBACK_KEYS);
|
|
2066
2080
|
var cursorOn = isAgentEnabled(c.cursor, CURSOR_FALLBACK_KEYS);
|
|
2067
2081
|
var codexOn = isAgentEnabled(c.codex, CODEX_FALLBACK_KEYS);
|
|
2068
|
-
var cccOn = isAgentEnabled(c.ccc, CCC_FALLBACK_KEYS);
|
|
2069
|
-
var dshOn = isAgentEnabled(c.dsh, DSH_FALLBACK_KEYS);
|
|
2070
|
-
state.agentsEnabled = { claude: claudeOn, cursor: cursorOn, codex: codexOn, ccc: cccOn, dsh: dshOn };
|
|
2071
|
-
state.defaultAgent = resolveDefaultAgentFromConfig(c, claudeOn, cursorOn, codexOn, cccOn, dshOn);
|
|
2082
|
+
var cccOn = isAgentEnabled(c.ccc, CCC_FALLBACK_KEYS);
|
|
2083
|
+
var dshOn = isAgentEnabled(c.dsh, DSH_FALLBACK_KEYS);
|
|
2084
|
+
state.agentsEnabled = { claude: claudeOn, cursor: cursorOn, codex: codexOn, ccc: cccOn, dsh: dshOn };
|
|
2085
|
+
state.defaultAgent = resolveDefaultAgentFromConfig(c, claudeOn, cursorOn, codexOn, cccOn, dshOn);
|
|
2072
2086
|
|
|
2073
2087
|
// 微信 iLink 平台开关:同步复选框和标签
|
|
2074
2088
|
var ilinkEnabled = c.platforms && c.platforms.ilink ? c.platforms.ilink.enabled !== false : true;
|
|
@@ -2100,12 +2114,12 @@ function updateDashboardUI() {
|
|
|
2100
2114
|
document.getElementById('dash-claude').style.display = claudeOn ? '' : 'none';
|
|
2101
2115
|
document.getElementById('dash-cursor').style.display = cursorOn ? '' : 'none';
|
|
2102
2116
|
document.getElementById('dash-codex').style.display = codexOn ? '' : 'none';
|
|
2103
|
-
document.getElementById('dash-ccc').style.display = cccOn ? '' : 'none';
|
|
2104
|
-
document.getElementById('dash-dsh').style.display = dshOn ? '' : 'none';
|
|
2117
|
+
document.getElementById('dash-ccc').style.display = cccOn ? '' : 'none';
|
|
2118
|
+
document.getElementById('dash-dsh').style.display = dshOn ? '' : 'none';
|
|
2105
2119
|
updateDefaultAgentToggles();
|
|
2106
|
-
// 五个都未启用时给一个空态提示,引导用户去配置向导启用
|
|
2120
|
+
// 五个都未启用时给一个空态提示,引导用户去配置向导启用
|
|
2107
2121
|
var emptyHint = document.getElementById('dash-no-agent-hint');
|
|
2108
|
-
if (emptyHint) emptyHint.style.display = (!claudeOn && !cursorOn && !codexOn && !cccOn && !dshOn) ? '' : 'none';
|
|
2122
|
+
if (emptyHint) emptyHint.style.display = (!claudeOn && !cursorOn && !codexOn && !cccOn && !dshOn) ? '' : 'none';
|
|
2109
2123
|
|
|
2110
2124
|
document.getElementById('cfg-ANTHROPIC_MODEL').textContent = (c.claude && c.claude.model) || '(留空)';
|
|
2111
2125
|
document.getElementById('cfg-ANTHROPIC_SUBAGENT_MODEL').textContent = (c.claude && c.claude.subagentModel) || '(留空)';
|
|
@@ -2131,13 +2145,15 @@ function updateDashboardUI() {
|
|
|
2131
2145
|
document.getElementById('cfg-CCC_PROVIDER').textContent = (c.ccc && c.ccc.provider) ? c.ccc.provider : '(跟随 DeepCCC 内核配置)';
|
|
2132
2146
|
document.getElementById('cfg-CCC_MODEL').textContent = (c.ccc && c.ccc.model) || '(留空)';
|
|
2133
2147
|
document.getElementById('cfg-CCC_SUB_MODEL').textContent = (c.ccc && c.ccc.subModel) || '(留空,跟随主模型)';
|
|
2134
|
-
document.getElementById('cfg-CCC_ALTERNATIVE_MODEL').textContent = (c.ccc && c.ccc.alternativeModel) || '(留空)';
|
|
2135
|
-
document.getElementById('cfg-DSH_API_KEY').textContent = (c.dsh && c.dsh.apiKey) ? '***已设置***' : '(留空)';
|
|
2136
|
-
document.getElementById('cfg-DSH_BASE_URL').textContent = (c.dsh && c.dsh.baseUrl) || 'https://api.deepseek.com/v1';
|
|
2137
|
-
document.getElementById('cfg-DSH_MODEL').textContent = (c.dsh && c.dsh.model) || 'deepseek-v4-flash';
|
|
2138
|
-
document.getElementById('cfg-
|
|
2139
|
-
document.getElementById('cfg-
|
|
2140
|
-
|
|
2148
|
+
document.getElementById('cfg-CCC_ALTERNATIVE_MODEL').textContent = (c.ccc && c.ccc.alternativeModel) || '(留空)';
|
|
2149
|
+
document.getElementById('cfg-DSH_API_KEY').textContent = (c.dsh && c.dsh.apiKey) ? '***已设置***' : '(留空)';
|
|
2150
|
+
document.getElementById('cfg-DSH_BASE_URL').textContent = (c.dsh && c.dsh.baseUrl) || 'https://api.deepseek.com/v1';
|
|
2151
|
+
document.getElementById('cfg-DSH_MODEL').textContent = (c.dsh && c.dsh.model) || 'deepseek-v4-flash';
|
|
2152
|
+
document.getElementById('cfg-DSH_SUB_MODEL').textContent = (c.dsh && c.dsh.subModel) || '(留空,跟随主模型)';
|
|
2153
|
+
document.getElementById('cfg-DSH_ALTERNATIVE_MODEL').textContent = (c.dsh && c.dsh.alternativeModel) || '(留空)';
|
|
2154
|
+
document.getElementById('cfg-DSH_PROVIDER').textContent = (c.dsh && c.dsh.provider) || 'deepseek-official';
|
|
2155
|
+
document.getElementById('cfg-DSH_MAX_TOKENS').textContent = String((c.dsh && c.dsh.maxTokens) || 49152);
|
|
2156
|
+
engineRefreshStatus('dsh');
|
|
2141
2157
|
}
|
|
2142
2158
|
|
|
2143
2159
|
function pollStatus() {
|
|
@@ -2193,7 +2209,7 @@ function editSection(section) {
|
|
|
2193
2209
|
else if (section === 'chromeDevtools') fields = CHROME_DEVTOOLS_FIELDS;
|
|
2194
2210
|
else fields = AGENT_FIELDS[section] || [];
|
|
2195
2211
|
|
|
2196
|
-
var titleMap = { feishu: '飞书', webUi: 'Web UI', chromeDevtools: 'Chrome CDP', claude: 'Claude Agent', cursor: 'Cursor Agent', codex: 'Codex Agent', ccc: 'CCC Agent', dsh: 'DeepSeek Harness' };
|
|
2212
|
+
var titleMap = { feishu: '飞书', webUi: 'Web UI', chromeDevtools: 'Chrome CDP', claude: 'Claude Agent', cursor: 'Cursor Agent', codex: 'Codex Agent', ccc: 'CCC Agent', dsh: 'DeepSeek Harness' };
|
|
2197
2213
|
document.getElementById('edit-modal-title').textContent = '编辑 ' + (titleMap[section] || section);
|
|
2198
2214
|
|
|
2199
2215
|
document.getElementById('edit-modal-effect').textContent = configEffectHint(section);
|
|
@@ -2214,8 +2230,8 @@ function editSection(section) {
|
|
|
2214
2230
|
'CHATCCC_CODEX_FAST_MODE': 'Fast 模式',
|
|
2215
2231
|
'CHATCCC_CCC_API_KEY': 'API Key', 'CHATCCC_CCC_BASE_URL': 'Base URL',
|
|
2216
2232
|
'CHATCCC_CCC_PROVIDER': 'API 协议(选填)',
|
|
2217
|
-
'CHATCCC_CCC_MODEL': '模型', 'CHATCCC_CCC_SUB_MODEL': '子模型', 'CHATCCC_CCC_ALTERNATIVE_MODEL': '备选模型', 'CHATCCC_CCC_EFFORT': 'Effort', 'CHATCCC_CCC_CONTEXT_WINDOW': '上下文窗口',
|
|
2218
|
-
'CHATCCC_DSH_API_KEY': 'API Key', 'CHATCCC_DSH_BASE_URL': 'Base URL', 'CHATCCC_DSH_MODEL': '模型', 'CHATCCC_DSH_PROVIDER': 'Provider 路由', 'CHATCCC_DSH_MAX_TOKENS': '单次最大输出 Tokens'
|
|
2233
|
+
'CHATCCC_CCC_MODEL': '模型', 'CHATCCC_CCC_SUB_MODEL': '子模型', 'CHATCCC_CCC_ALTERNATIVE_MODEL': '备选模型', 'CHATCCC_CCC_EFFORT': 'Effort', 'CHATCCC_CCC_CONTEXT_WINDOW': '上下文窗口',
|
|
2234
|
+
'CHATCCC_DSH_API_KEY': 'API Key', 'CHATCCC_DSH_BASE_URL': 'Base URL', 'CHATCCC_DSH_MODEL': '模型', 'CHATCCC_DSH_SUB_MODEL': '子模型', 'CHATCCC_DSH_ALTERNATIVE_MODEL': '备选模型', 'CHATCCC_DSH_PROVIDER': 'Provider 路由', 'CHATCCC_DSH_MAX_TOKENS': '单次最大输出 Tokens'
|
|
2219
2235
|
};
|
|
2220
2236
|
var hintMap = {
|
|
2221
2237
|
'CHATCCC_WEB_UI_OPEN_ON_START': '关闭后可继续手动访问 http://localhost:<端口>/;/restart、/update 和 Web UI 重启无论此项为何值都不会自动打开。',
|
|
@@ -2263,7 +2279,7 @@ function editSection(section) {
|
|
|
2263
2279
|
else if (key === 'CHATCCC_CODEX_ALTERNATIVE_MODEL') val = state.config.codex.alternativeModel || '';
|
|
2264
2280
|
else if (key === 'CHATCCC_CODEX_EFFORT') val = state.config.codex.effort || '';
|
|
2265
2281
|
else if (key === 'CHATCCC_CODEX_FAST_MODE') val = state.config.codex.fastMode === true ? 'true' : 'false';
|
|
2266
|
-
} else if (section === 'ccc' && state.config.ccc) {
|
|
2282
|
+
} else if (section === 'ccc' && state.config.ccc) {
|
|
2267
2283
|
if (key === 'CHATCCC_CCC_API_KEY') val = state.config.ccc.DEEPSEEK_API_KEY || '';
|
|
2268
2284
|
else if (key === 'CHATCCC_CCC_BASE_URL') val = state.config.ccc.DEEPSEEK_BASE_URL || '';
|
|
2269
2285
|
else if (key === 'CHATCCC_CCC_PROVIDER') val = state.config.ccc.provider || '';
|
|
@@ -2271,14 +2287,16 @@ function editSection(section) {
|
|
|
2271
2287
|
else if (key === 'CHATCCC_CCC_SUB_MODEL') val = state.config.ccc.subModel || '';
|
|
2272
2288
|
else if (key === 'CHATCCC_CCC_ALTERNATIVE_MODEL') val = state.config.ccc.alternativeModel || '';
|
|
2273
2289
|
else if (key === 'CHATCCC_CCC_EFFORT') val = state.config.ccc.effort || '';
|
|
2274
|
-
else if (key === 'CHATCCC_CCC_CONTEXT_WINDOW') val = state.config.ccc.contextWindow || '1048576';
|
|
2275
|
-
} else if (section === 'dsh' && state.config.dsh) {
|
|
2276
|
-
if (key === 'CHATCCC_DSH_API_KEY') val = state.config.dsh.apiKey || '';
|
|
2277
|
-
else if (key === 'CHATCCC_DSH_BASE_URL') val = state.config.dsh.baseUrl || '';
|
|
2278
|
-
else if (key === 'CHATCCC_DSH_MODEL') val = state.config.dsh.model || '';
|
|
2279
|
-
else if (key === '
|
|
2280
|
-
else if (key === '
|
|
2281
|
-
|
|
2290
|
+
else if (key === 'CHATCCC_CCC_CONTEXT_WINDOW') val = state.config.ccc.contextWindow || '1048576';
|
|
2291
|
+
} else if (section === 'dsh' && state.config.dsh) {
|
|
2292
|
+
if (key === 'CHATCCC_DSH_API_KEY') val = state.config.dsh.apiKey || '';
|
|
2293
|
+
else if (key === 'CHATCCC_DSH_BASE_URL') val = state.config.dsh.baseUrl || '';
|
|
2294
|
+
else if (key === 'CHATCCC_DSH_MODEL') val = state.config.dsh.model || '';
|
|
2295
|
+
else if (key === 'CHATCCC_DSH_SUB_MODEL') val = state.config.dsh.subModel || '';
|
|
2296
|
+
else if (key === 'CHATCCC_DSH_ALTERNATIVE_MODEL') val = state.config.dsh.alternativeModel || '';
|
|
2297
|
+
else if (key === 'CHATCCC_DSH_PROVIDER') val = state.config.dsh.provider || '';
|
|
2298
|
+
else if (key === 'CHATCCC_DSH_MAX_TOKENS') val = state.config.dsh.maxTokens || '49152';
|
|
2299
|
+
}
|
|
2282
2300
|
}
|
|
2283
2301
|
var isSecret = key.includes('SECRET') || key.includes('API_KEY');
|
|
2284
2302
|
if (key === 'CHATCCC_WEB_UI_OPEN_ON_START' || key === 'CHATCCC_CHROME_DEVTOOLS_ENABLED' || key === 'CHATCCC_CODEX_FAST_MODE') {
|
|
@@ -2428,95 +2446,95 @@ function validateCli(tool) {
|
|
|
2428
2446
|
});
|
|
2429
2447
|
}
|
|
2430
2448
|
|
|
2431
|
-
// ---- 通用 Agent 引擎:一次点击,后端自动执行全部原子安装步骤 ----
|
|
2432
|
-
var enginePollTimers = {};
|
|
2433
|
-
function escapeHtml(value) {
|
|
2434
|
-
return String(value == null ? '' : value).replace(/[&<>"']/g, function(char){
|
|
2435
|
-
return ({'&':'&','<':'<','>':'>','"':'"',"'":'''})[char];
|
|
2436
|
-
});
|
|
2437
|
-
}
|
|
2438
|
-
|
|
2439
|
-
function onInstallableAgentToggle(engineId, el) {
|
|
2440
|
-
if (!el.checked) { onAgentToggle(engineId, false); return; }
|
|
2441
|
-
onAgentToggle(engineId, true);
|
|
2442
|
-
api('/api/engines/' + encodeURIComponent(engineId) + '/status', 'GET').then(function(status){
|
|
2443
|
-
renderEngineStatus(engineId, status);
|
|
2444
|
-
if (!status.installed && !status.running) installEngine(engineId);
|
|
2445
|
-
else if (status.running) scheduleEnginePoll(engineId);
|
|
2446
|
-
}).catch(function(error){
|
|
2447
|
-
el.checked = false;
|
|
2448
|
-
onAgentToggle(engineId, false);
|
|
2449
|
-
toast('引擎状态查询失败: ' + String(error), 'error');
|
|
2450
|
-
});
|
|
2451
|
-
}
|
|
2452
|
-
|
|
2453
|
-
function renderEngineStatus(engineId, status) {
|
|
2454
|
-
var ids = [engineId + '-engine-status', engineId + '-dashboard-engine-status'];
|
|
2455
|
-
var job = status.job;
|
|
2456
|
-
var state = job && job.state;
|
|
2457
|
-
var text = status.installed ? ('已安装 v' + (status.version || '未知版本')) : '未安装';
|
|
2458
|
-
var color = status.installed ? '#15803d' : '#64748b';
|
|
2459
|
-
if (state === 'running') { text = '安装中 ' + (job.percent || 0) + '%'; color = '#1d4ed8'; }
|
|
2460
|
-
if (state === 'failed') { text = '安装失败'; color = '#dc2626'; }
|
|
2461
|
-
ids.forEach(function(id){
|
|
2462
|
-
var el = document.getElementById(id);
|
|
2463
|
-
if (!el) return;
|
|
2464
|
-
el.innerHTML = '<span style="color:' + color + '">' + text + '</span>' +
|
|
2465
|
-
(job && job.error ? '<br><span style="color:#dc2626;font-size:12px">' + escapeHtml(job.error) + '</span>' : '');
|
|
2466
|
-
});
|
|
2467
|
-
[engineId + '-engine-steps', engineId + '-dashboard-engine-steps'].forEach(function(id){
|
|
2468
|
-
var root = document.getElementById(id);
|
|
2469
|
-
if (!root) return;
|
|
2470
|
-
root.innerHTML = job && job.steps ? job.steps.map(function(step){
|
|
2471
|
-
var icon = step.state === 'completed' ? '✓' : step.state === 'failed' ? '!' : step.state === 'running' ? '●' : '○';
|
|
2472
|
-
return '<div class="engine-step ' + step.state + '"><span class="engine-step-icon">' + icon + '</span><span>' +
|
|
2473
|
-
escapeHtml(step.label + (step.message && step.message !== '等待中' ? ' · ' + step.message : '')) +
|
|
2474
|
-
'</span><span class="engine-step-percent">' + (step.state === 'running' ? step.percent + '%' : '') + '</span></div>';
|
|
2475
|
-
}).join('') : '';
|
|
2476
|
-
});
|
|
2477
|
-
[engineId + '-engine-install-btn', engineId + '-dashboard-engine-install-btn'].forEach(function(id){
|
|
2478
|
-
var button = document.getElementById(id);
|
|
2479
|
-
if (!button) return;
|
|
2480
|
-
button.disabled = state === 'running';
|
|
2481
|
-
button.textContent = state === 'running' ? '自动安装中…' : state === 'failed' ? '重试' : status.installed ? '原子升级/重新安装' : '安装并启用';
|
|
2482
|
-
});
|
|
2483
|
-
}
|
|
2484
|
-
|
|
2485
|
-
function scheduleEnginePoll(engineId) {
|
|
2486
|
-
if (enginePollTimers[engineId]) clearTimeout(enginePollTimers[engineId]);
|
|
2487
|
-
enginePollTimers[engineId] = setTimeout(function(){ engineRefreshStatus(engineId); }, 800);
|
|
2488
|
-
}
|
|
2489
|
-
|
|
2490
|
-
function engineRefreshStatus(engineId) {
|
|
2491
|
-
return api('/api/engines/' + encodeURIComponent(engineId) + '/status', 'GET').then(function(status){
|
|
2492
|
-
renderEngineStatus(engineId, status);
|
|
2493
|
-
if (status.running || (status.job && status.job.state === 'running')) scheduleEnginePoll(engineId);
|
|
2494
|
-
return status;
|
|
2495
|
-
}).catch(function(){ scheduleEnginePoll(engineId); });
|
|
2496
|
-
}
|
|
2497
|
-
|
|
2498
|
-
function installEngine(engineId) {
|
|
2499
|
-
var toggle = document.getElementById('agent-enable-' + engineId);
|
|
2500
|
-
if (toggle && !toggle.checked) {
|
|
2501
|
-
toggle.checked = true;
|
|
2502
|
-
onAgentToggle(engineId, true);
|
|
2503
|
-
}
|
|
2504
|
-
return api('/api/engines/' + encodeURIComponent(engineId) + '/install', 'POST').then(function(result){
|
|
2505
|
-
if (!result.ok) throw new Error(result.error || '启动安装失败');
|
|
2506
|
-
renderEngineStatus(engineId, { installed: false, running: true, job: result.job });
|
|
2507
|
-
scheduleEnginePoll(engineId);
|
|
2508
|
-
return result;
|
|
2509
|
-
}).catch(function(error){
|
|
2510
|
-
renderEngineStatus(engineId, { installed: false, running: false, job: { state: 'failed', error: String(error), steps: [] } });
|
|
2511
|
-
toast('引擎安装失败: ' + String(error), 'error');
|
|
2512
|
-
});
|
|
2513
|
-
}
|
|
2449
|
+
// ---- 通用 Agent 引擎:一次点击,后端自动执行全部原子安装步骤 ----
|
|
2450
|
+
var enginePollTimers = {};
|
|
2451
|
+
function escapeHtml(value) {
|
|
2452
|
+
return String(value == null ? '' : value).replace(/[&<>"']/g, function(char){
|
|
2453
|
+
return ({'&':'&','<':'<','>':'>','"':'"',"'":'''})[char];
|
|
2454
|
+
});
|
|
2455
|
+
}
|
|
2456
|
+
|
|
2457
|
+
function onInstallableAgentToggle(engineId, el) {
|
|
2458
|
+
if (!el.checked) { onAgentToggle(engineId, false); return; }
|
|
2459
|
+
onAgentToggle(engineId, true);
|
|
2460
|
+
api('/api/engines/' + encodeURIComponent(engineId) + '/status', 'GET').then(function(status){
|
|
2461
|
+
renderEngineStatus(engineId, status);
|
|
2462
|
+
if (!status.installed && !status.running) installEngine(engineId);
|
|
2463
|
+
else if (status.running) scheduleEnginePoll(engineId);
|
|
2464
|
+
}).catch(function(error){
|
|
2465
|
+
el.checked = false;
|
|
2466
|
+
onAgentToggle(engineId, false);
|
|
2467
|
+
toast('引擎状态查询失败: ' + String(error), 'error');
|
|
2468
|
+
});
|
|
2469
|
+
}
|
|
2470
|
+
|
|
2471
|
+
function renderEngineStatus(engineId, status) {
|
|
2472
|
+
var ids = [engineId + '-engine-status', engineId + '-dashboard-engine-status'];
|
|
2473
|
+
var job = status.job;
|
|
2474
|
+
var state = job && job.state;
|
|
2475
|
+
var text = status.installed ? ('已安装 v' + (status.version || '未知版本')) : '未安装';
|
|
2476
|
+
var color = status.installed ? '#15803d' : '#64748b';
|
|
2477
|
+
if (state === 'running') { text = '安装中 ' + (job.percent || 0) + '%'; color = '#1d4ed8'; }
|
|
2478
|
+
if (state === 'failed') { text = '安装失败'; color = '#dc2626'; }
|
|
2479
|
+
ids.forEach(function(id){
|
|
2480
|
+
var el = document.getElementById(id);
|
|
2481
|
+
if (!el) return;
|
|
2482
|
+
el.innerHTML = '<span style="color:' + color + '">' + text + '</span>' +
|
|
2483
|
+
(job && job.error ? '<br><span style="color:#dc2626;font-size:12px">' + escapeHtml(job.error) + '</span>' : '');
|
|
2484
|
+
});
|
|
2485
|
+
[engineId + '-engine-steps', engineId + '-dashboard-engine-steps'].forEach(function(id){
|
|
2486
|
+
var root = document.getElementById(id);
|
|
2487
|
+
if (!root) return;
|
|
2488
|
+
root.innerHTML = job && job.steps ? job.steps.map(function(step){
|
|
2489
|
+
var icon = step.state === 'completed' ? '✓' : step.state === 'failed' ? '!' : step.state === 'running' ? '●' : '○';
|
|
2490
|
+
return '<div class="engine-step ' + step.state + '"><span class="engine-step-icon">' + icon + '</span><span>' +
|
|
2491
|
+
escapeHtml(step.label + (step.message && step.message !== '等待中' ? ' · ' + step.message : '')) +
|
|
2492
|
+
'</span><span class="engine-step-percent">' + (step.state === 'running' ? step.percent + '%' : '') + '</span></div>';
|
|
2493
|
+
}).join('') : '';
|
|
2494
|
+
});
|
|
2495
|
+
[engineId + '-engine-install-btn', engineId + '-dashboard-engine-install-btn'].forEach(function(id){
|
|
2496
|
+
var button = document.getElementById(id);
|
|
2497
|
+
if (!button) return;
|
|
2498
|
+
button.disabled = state === 'running';
|
|
2499
|
+
button.textContent = state === 'running' ? '自动安装中…' : state === 'failed' ? '重试' : status.installed ? '原子升级/重新安装' : '安装并启用';
|
|
2500
|
+
});
|
|
2501
|
+
}
|
|
2502
|
+
|
|
2503
|
+
function scheduleEnginePoll(engineId) {
|
|
2504
|
+
if (enginePollTimers[engineId]) clearTimeout(enginePollTimers[engineId]);
|
|
2505
|
+
enginePollTimers[engineId] = setTimeout(function(){ engineRefreshStatus(engineId); }, 800);
|
|
2506
|
+
}
|
|
2507
|
+
|
|
2508
|
+
function engineRefreshStatus(engineId) {
|
|
2509
|
+
return api('/api/engines/' + encodeURIComponent(engineId) + '/status', 'GET').then(function(status){
|
|
2510
|
+
renderEngineStatus(engineId, status);
|
|
2511
|
+
if (status.running || (status.job && status.job.state === 'running')) scheduleEnginePoll(engineId);
|
|
2512
|
+
return status;
|
|
2513
|
+
}).catch(function(){ scheduleEnginePoll(engineId); });
|
|
2514
|
+
}
|
|
2515
|
+
|
|
2516
|
+
function installEngine(engineId) {
|
|
2517
|
+
var toggle = document.getElementById('agent-enable-' + engineId);
|
|
2518
|
+
if (toggle && !toggle.checked) {
|
|
2519
|
+
toggle.checked = true;
|
|
2520
|
+
onAgentToggle(engineId, true);
|
|
2521
|
+
}
|
|
2522
|
+
return api('/api/engines/' + encodeURIComponent(engineId) + '/install', 'POST').then(function(result){
|
|
2523
|
+
if (!result.ok) throw new Error(result.error || '启动安装失败');
|
|
2524
|
+
renderEngineStatus(engineId, { installed: false, running: true, job: result.job });
|
|
2525
|
+
scheduleEnginePoll(engineId);
|
|
2526
|
+
return result;
|
|
2527
|
+
}).catch(function(error){
|
|
2528
|
+
renderEngineStatus(engineId, { installed: false, running: false, job: { state: 'failed', error: String(error), steps: [] } });
|
|
2529
|
+
toast('引擎安装失败: ' + String(error), 'error');
|
|
2530
|
+
});
|
|
2531
|
+
}
|
|
2514
2532
|
|
|
2515
2533
|
// ---- Start ----
|
|
2516
2534
|
init();
|
|
2517
2535
|
|
|
2518
|
-
// 页面刷新后从持久化任务文件恢复每一步的安装进度。
|
|
2519
|
-
setTimeout(function(){ engineRefreshStatus('claude'); engineRefreshStatus('dsh'); }, 300);
|
|
2536
|
+
// 页面刷新后从持久化任务文件恢复每一步的安装进度。
|
|
2537
|
+
setTimeout(function(){ engineRefreshStatus('claude'); engineRefreshStatus('dsh'); }, 300);
|
|
2520
2538
|
</script>
|
|
2521
2539
|
</body>
|
|
2522
2540
|
</html>`;
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|