chatccc 0.2.259 → 0.2.260
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 +20 -11
- package/config.sample.json +23 -8
- package/dist/src/adapters/claude-adapter.js +3 -2
- package/dist/src/adapters/dsh-adapter.js +230 -0
- package/dist/src/agent-tool.js +2 -1
- package/dist/src/card-action-parser.js +1 -0
- package/dist/src/cards.js +11 -3
- package/dist/src/config.js +34 -2
- package/dist/src/engines/engine-manager.js +408 -0
- package/dist/src/engines/engine-specs.js +157 -0
- package/dist/src/feishu-api.js +2 -1
- package/dist/src/orchestrator.js +21 -10
- package/dist/src/session.js +23 -0
- package/dist/src/web-ui.js +286 -200
- package/package.json +1 -1
- package/dist/src/claude-sdk-installer.js +0 -249
package/dist/src/web-ui.js
CHANGED
|
@@ -16,7 +16,7 @@ import { handleAgentTeamRequest } from "./agent-team/http/board-routes.js";
|
|
|
16
16
|
import { AGENT_TEAM_PAGE_HTML } from "./agent-team/web/agent-team-page.js";
|
|
17
17
|
export { AGENT_TEAM_PAGE_HTML } from "./agent-team/web/agent-team-page.js";
|
|
18
18
|
import { buildWebUiUrl, createInternalRestartEnv, openWebUiInDefaultBrowser, } from "./startup-lifecycle.js";
|
|
19
|
-
import {
|
|
19
|
+
import { engineManager } from "./engines/engine-specs.js";
|
|
20
20
|
const PROJECT_ROOT = CHATCCC_PACKAGE_ROOT;
|
|
21
21
|
const USER_DATA_DIR = join(homedir(), ".chatccc");
|
|
22
22
|
const CONFIG_FILE = join(USER_DATA_DIR, "config.json");
|
|
@@ -511,6 +511,34 @@ export function unflattenConfig(flat) {
|
|
|
511
511
|
result.ccc = result.ccc || {};
|
|
512
512
|
result.ccc.defaultAgent = val === true || val === "true";
|
|
513
513
|
}
|
|
514
|
+
else if (key === "CHATCCC_DSH_API_KEY") {
|
|
515
|
+
result.dsh = result.dsh || {};
|
|
516
|
+
result.dsh.apiKey = val;
|
|
517
|
+
}
|
|
518
|
+
else if (key === "CHATCCC_DSH_BASE_URL") {
|
|
519
|
+
result.dsh = result.dsh || {};
|
|
520
|
+
result.dsh.baseUrl = val;
|
|
521
|
+
}
|
|
522
|
+
else if (key === "CHATCCC_DSH_MODEL") {
|
|
523
|
+
result.dsh = result.dsh || {};
|
|
524
|
+
result.dsh.model = val;
|
|
525
|
+
}
|
|
526
|
+
else if (key === "CHATCCC_DSH_PROVIDER") {
|
|
527
|
+
result.dsh = result.dsh || {};
|
|
528
|
+
result.dsh.provider = val;
|
|
529
|
+
}
|
|
530
|
+
else if (key === "CHATCCC_DSH_MAX_TOKENS") {
|
|
531
|
+
result.dsh = result.dsh || {};
|
|
532
|
+
result.dsh.maxTokens = parseInt(String(val), 10) || 49152;
|
|
533
|
+
}
|
|
534
|
+
else if (key === "CHATCCC_DSH_ENABLED") {
|
|
535
|
+
result.dsh = result.dsh || {};
|
|
536
|
+
result.dsh.enabled = val === true || val === "true";
|
|
537
|
+
}
|
|
538
|
+
else if (key === "CHATCCC_DSH_DEFAULT_AGENT") {
|
|
539
|
+
result.dsh = result.dsh || {};
|
|
540
|
+
result.dsh.defaultAgent = val === true || val === "true";
|
|
541
|
+
}
|
|
514
542
|
}
|
|
515
543
|
return result;
|
|
516
544
|
}
|
|
@@ -607,33 +635,20 @@ async function handleForgetIlink(_req, res) {
|
|
|
607
635
|
jsonReply(res, 500, { ok: false, error: err.message });
|
|
608
636
|
}
|
|
609
637
|
}
|
|
610
|
-
async function
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
// 展开到顶层:前端轮询 JS 读顶层 phase/message/percent/error(历史契约),
|
|
617
|
-
// 若只放在 progress 嵌套里,前端 s.phase 恒为 undefined → 进度条永不渲染。
|
|
618
|
-
...progress,
|
|
619
|
-
// 保留嵌套结构,兼容其它调用方。
|
|
620
|
-
progress,
|
|
621
|
-
});
|
|
622
|
-
}
|
|
623
|
-
async function handleClaudeSdkInstall(_req, res) {
|
|
624
|
-
if (isInstallRunning()) {
|
|
625
|
-
jsonReply(res, 200, { ok: true, alreadyRunning: true });
|
|
626
|
-
return;
|
|
638
|
+
async function handleEngineStatus(engineId, res) {
|
|
639
|
+
try {
|
|
640
|
+
jsonReply(res, 200, { ok: true, ...(await engineManager.getStatus(engineId)) });
|
|
641
|
+
}
|
|
642
|
+
catch (err) {
|
|
643
|
+
jsonReply(res, err.message.startsWith("Unknown engine:") ? 404 : 500, { ok: false, error: err.message });
|
|
627
644
|
}
|
|
645
|
+
}
|
|
646
|
+
async function handleEngineInstall(engineId, res) {
|
|
628
647
|
try {
|
|
629
|
-
|
|
630
|
-
installClaudeSdk().catch((err) => {
|
|
631
|
-
console.error(`[WEB-UI] Claude SDK 安装失败: ${err.message}`);
|
|
632
|
-
});
|
|
633
|
-
jsonReply(res, 200, { ok: true });
|
|
648
|
+
jsonReply(res, 202, { ok: true, job: await engineManager.startInstall(engineId) });
|
|
634
649
|
}
|
|
635
650
|
catch (err) {
|
|
636
|
-
jsonReply(res, 500, { ok: false, error: err.message });
|
|
651
|
+
jsonReply(res, err.message.startsWith("Unknown engine:") ? 404 : 500, { ok: false, error: err.message });
|
|
637
652
|
}
|
|
638
653
|
}
|
|
639
654
|
// ---------------------------------------------------------------------------
|
|
@@ -682,7 +697,12 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
|
|
|
682
697
|
.btn-danger:hover{background:#dc2626}
|
|
683
698
|
.btn-success{background:#16a34a;color:#fff}
|
|
684
699
|
.btn-success:hover{background:#15803d}
|
|
685
|
-
.btn:disabled{opacity:.5;cursor:not-allowed}
|
|
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}
|
|
686
706
|
.btn-group{display:flex;gap:8px;flex-wrap:wrap}
|
|
687
707
|
.agent-cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:12px;margin-bottom:16px}
|
|
688
708
|
.agent-card{min-width:0}
|
|
@@ -913,12 +933,41 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
|
|
|
913
933
|
<div class="hint">压缩阈值自动 = 窗口 × 80%(超出即把较早消息压缩为摘要)。⚠️ 超过模型/服务端实际上限时请求会被 API 直接拒绝(context length exceeded);实际窗口以模型与所用服务端为准(如 litellm 代理的 max_input_tokens)。</div>
|
|
914
934
|
</div>
|
|
915
935
|
</fieldset>
|
|
916
|
-
</div>
|
|
917
|
-
|
|
918
|
-
<!--
|
|
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>Provider 路由</label><input type="text" id="field-CHATCCC_DSH_PROVIDER" placeholder="deepseek-official"></div>
|
|
956
|
+
<div class="form-group"><label>单次最大输出 Tokens</label><input type="number" id="field-CHATCCC_DSH_MAX_TOKENS" min="1" placeholder="49152"></div>
|
|
957
|
+
<div class="form-group" style="border-top:1px solid #e2e8f0;padding-top:12px;margin-top:4px">
|
|
958
|
+
<label>DeepSeek Harness 引擎</label>
|
|
959
|
+
<div id="dsh-engine-status" class="engine-status">检测中...</div>
|
|
960
|
+
<div id="dsh-engine-steps" class="engine-steps"></div>
|
|
961
|
+
<button type="button" class="btn btn-outline" id="dsh-engine-install-btn" onclick="installEngine('dsh')">安装并启用</button>
|
|
962
|
+
<div class="hint" style="margin-top:6px">一次点击自动完成环境检查、下载、校验、Runtime 握手和原子切换;页面刷新后仍可恢复进度。</div>
|
|
963
|
+
</div>
|
|
964
|
+
</fieldset>
|
|
965
|
+
</div>
|
|
966
|
+
|
|
967
|
+
<!-- Claude 卡片 -->
|
|
919
968
|
<div class="agent-card" id="agent-card-claude">
|
|
920
969
|
<div class="agent-card-header">
|
|
921
|
-
<input type="checkbox" class="agent-toggle" id="agent-enable-claude" onchange="
|
|
970
|
+
<input type="checkbox" class="agent-toggle" id="agent-enable-claude" onchange="onInstallableAgentToggle('claude', this)">
|
|
922
971
|
<div class="meta">
|
|
923
972
|
<div class="name">Claude Code</div>
|
|
924
973
|
<div class="desc">Anthropic Claude Code CLI<br>模型、effort 均为选填</div>
|
|
@@ -951,14 +1000,9 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
|
|
|
951
1000
|
</div>
|
|
952
1001
|
<div class="form-group" style="border-top:1px solid #e2e8f0;padding-top:12px;margin-top:4px">
|
|
953
1002
|
<label>Claude Code 引擎(Agent SDK)</label>
|
|
954
|
-
<div id="claude-engine-status"
|
|
955
|
-
<div id="claude-engine-
|
|
956
|
-
|
|
957
|
-
<div id="claude-engine-progress-bar" style="width:0%;height:100%;background:#3b82f6;transition:width .3s"></div>
|
|
958
|
-
</div>
|
|
959
|
-
<div id="claude-engine-progress-text" style="font-size:12px;color:#64748b;margin-top:4px"></div>
|
|
960
|
-
</div>
|
|
961
|
-
<button class="btn btn-outline" id="claude-engine-install-btn" onclick="installClaudeEngine()">安装 Claude Code SDK</button>
|
|
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>
|
|
962
1006
|
<div class="hint" style="margin-top:6px">ChatCCC 通过 Claude Agent SDK 调用 Claude Code;SDK 引擎按需下载到本机(仅启用 Claude Code 时需要),安装期间请保持网络畅通。</div>
|
|
963
1007
|
</div>
|
|
964
1008
|
</fieldset>
|
|
@@ -1136,7 +1180,7 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
|
|
|
1136
1180
|
</div>
|
|
1137
1181
|
</details>
|
|
1138
1182
|
|
|
1139
|
-
<details class="card config-section" id="dash-claude">
|
|
1183
|
+
<details class="card config-section" id="dash-claude">
|
|
1140
1184
|
<summary>Claude Agent</summary>
|
|
1141
1185
|
<div class="section-detail">
|
|
1142
1186
|
<div class="config-row"><span class="key">模型</span><span class="val" id="cfg-ANTHROPIC_MODEL">-</span></div>
|
|
@@ -1149,7 +1193,24 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
|
|
|
1149
1193
|
<div class="hint" style="margin-top:6px;line-height:1.6">生效范围:保存后下一条消息或下个新会话生效,当前生成不中断。</div>
|
|
1150
1194
|
<button class="btn btn-outline" style="margin-top:8px" onclick="editSection('claude')">编辑</button>
|
|
1151
1195
|
</div>
|
|
1152
|
-
</details>
|
|
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">Provider</span><span class="val" id="cfg-DSH_PROVIDER">-</span></div>
|
|
1205
|
+
<div class="config-row"><span class="key">最大输出 Tokens</span><span class="val" id="cfg-DSH_MAX_TOKENS">-</span></div>
|
|
1206
|
+
<div id="dsh-dashboard-engine-status" class="engine-status">检测中...</div>
|
|
1207
|
+
<div id="dsh-dashboard-engine-steps" class="engine-steps"></div>
|
|
1208
|
+
<button type="button" class="btn btn-outline" id="dsh-dashboard-engine-install-btn" onclick="installEngine('dsh')">安装或升级引擎</button>
|
|
1209
|
+
<label class="agent-default-row" style="margin-top:10px"><input type="checkbox" id="dash-default-dsh" onchange="setDashboardDefaultAgent('dsh', this.checked)"> 设为默认 Agent</label>
|
|
1210
|
+
<div class="hint" style="margin-top:6px;line-height:1.6">安装过程不调用付费模型;保存后下一条消息或下个新会话生效。</div>
|
|
1211
|
+
<button class="btn btn-outline" style="margin-top:8px" onclick="editSection('dsh')">编辑</button>
|
|
1212
|
+
</div>
|
|
1213
|
+
</details>
|
|
1153
1214
|
|
|
1154
1215
|
<details class="card config-section" id="dash-cursor">
|
|
1155
1216
|
<summary>Cursor Agent</summary>
|
|
@@ -1220,8 +1281,8 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
|
|
|
1220
1281
|
<script>
|
|
1221
1282
|
let state = {
|
|
1222
1283
|
view: 'loading',
|
|
1223
|
-
//
|
|
1224
|
-
agentsEnabled: { claude: false, cursor: false, codex: false, ccc: false },
|
|
1284
|
+
// 五个 Agent 各自的启用开关;初始全 false,renderStep2() 会按已存在 config 自动打开
|
|
1285
|
+
agentsEnabled: { claude: false, cursor: false, codex: false, ccc: false, dsh: false },
|
|
1225
1286
|
defaultAgent: 'claude',
|
|
1226
1287
|
wizardStep: 1,
|
|
1227
1288
|
config: {},
|
|
@@ -1238,7 +1299,8 @@ const AGENT_FIELDS = {
|
|
|
1238
1299
|
claude: ['CHATCCC_ANTHROPIC_MODEL','CHATCCC_ANTHROPIC_SUBAGENT_MODEL','CHATCCC_ANTHROPIC_EFFORT','CHATCCC_ANTHROPIC_API_KEY','CHATCCC_ANTHROPIC_BASE_URL','CHATCCC_ANTHROPIC_MAX_TURN'],
|
|
1239
1300
|
cursor: ['CHATCCC_CURSOR_PATH','CHATCCC_CURSOR_MODEL','CHATCCC_CURSOR_ALTERNATIVE_MODEL','CHATCCC_CURSOR_AVATAR_BATTERY_MODE','CHATCCC_CURSOR_ON_DEMAND_MONTHLY_BUDGET'],
|
|
1240
1301
|
codex: ['CHATCCC_CODEX_PATH','CHATCCC_CODEX_MODEL','CHATCCC_CODEX_ALTERNATIVE_MODEL','CHATCCC_CODEX_EFFORT','CHATCCC_CODEX_FAST_MODE'],
|
|
1241
|
-
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']
|
|
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']
|
|
1242
1304
|
};
|
|
1243
1305
|
const FEISHU_FIELDS = ['CHATCCC_APP_ID','CHATCCC_APP_SECRET'];
|
|
1244
1306
|
const WEB_UI_FIELDS = ['CHATCCC_WEB_UI_OPEN_ON_START'];
|
|
@@ -1392,28 +1454,31 @@ function onAgentToggle(agent, enabled) {
|
|
|
1392
1454
|
updateStep2NextBtn();
|
|
1393
1455
|
}
|
|
1394
1456
|
|
|
1395
|
-
function firstEnabledAgent() {
|
|
1457
|
+
function firstEnabledAgent() {
|
|
1396
1458
|
if (state.agentsEnabled.claude) return 'claude';
|
|
1397
1459
|
if (state.agentsEnabled.cursor) return 'cursor';
|
|
1398
1460
|
if (state.agentsEnabled.codex) return 'codex';
|
|
1399
|
-
if (state.agentsEnabled.ccc) return 'ccc';
|
|
1461
|
+
if (state.agentsEnabled.ccc) return 'ccc';
|
|
1462
|
+
if (state.agentsEnabled.dsh) return 'dsh';
|
|
1400
1463
|
return null;
|
|
1401
1464
|
}
|
|
1402
1465
|
|
|
1403
|
-
function resolveDefaultAgentFromConfig(c, claudeOn, cursorOn, codexOn, cccOn) {
|
|
1466
|
+
function resolveDefaultAgentFromConfig(c, claudeOn, cursorOn, codexOn, cccOn, dshOn) {
|
|
1404
1467
|
if (claudeOn && c.claude && c.claude.defaultAgent === true) return 'claude';
|
|
1405
1468
|
if (cursorOn && c.cursor && c.cursor.defaultAgent === true) return 'cursor';
|
|
1406
1469
|
if (codexOn && c.codex && c.codex.defaultAgent === true) return 'codex';
|
|
1407
|
-
if (cccOn && c.ccc && c.ccc.defaultAgent === true) return 'ccc';
|
|
1470
|
+
if (cccOn && c.ccc && c.ccc.defaultAgent === true) return 'ccc';
|
|
1471
|
+
if (dshOn && c.dsh && c.dsh.defaultAgent === true) return 'dsh';
|
|
1408
1472
|
if (claudeOn) return 'claude';
|
|
1409
1473
|
if (cursorOn) return 'cursor';
|
|
1410
1474
|
if (codexOn) return 'codex';
|
|
1411
|
-
if (cccOn) return 'ccc';
|
|
1475
|
+
if (cccOn) return 'ccc';
|
|
1476
|
+
if (dshOn) return 'dsh';
|
|
1412
1477
|
return 'claude';
|
|
1413
1478
|
}
|
|
1414
1479
|
|
|
1415
1480
|
function updateDefaultAgentToggles() {
|
|
1416
|
-
['claude','cursor','codex','ccc'].forEach(function(agent){
|
|
1481
|
+
['claude','cursor','codex','ccc','dsh'].forEach(function(agent){
|
|
1417
1482
|
var el = document.getElementById('agent-default-' + agent);
|
|
1418
1483
|
if (el) {
|
|
1419
1484
|
el.checked = state.defaultAgent === agent;
|
|
@@ -1445,7 +1510,8 @@ function updateStep2NextBtn() {
|
|
|
1445
1510
|
if (state.agentsEnabled.claude) validCount++;
|
|
1446
1511
|
if (state.agentsEnabled.cursor) validCount++;
|
|
1447
1512
|
if (state.agentsEnabled.codex) validCount++;
|
|
1448
|
-
if (state.agentsEnabled.ccc) validCount++;
|
|
1513
|
+
if (state.agentsEnabled.ccc) validCount++;
|
|
1514
|
+
if (state.agentsEnabled.dsh) validCount++;
|
|
1449
1515
|
if (validCount > 0 && !state.defaultAgent) {
|
|
1450
1516
|
state.defaultAgent = firstEnabledAgent();
|
|
1451
1517
|
updateDefaultAgentToggles();
|
|
@@ -1587,7 +1653,8 @@ var CLAUDE_FALLBACK_KEYS = ['model','subagentModel','effort','maxTurn'];
|
|
|
1587
1653
|
var CURSOR_FALLBACK_KEYS = ['path','command','model','alternativeModel'];
|
|
1588
1654
|
var CODEX_FALLBACK_KEYS = ['path','command','model','alternativeModel','effort','fastMode'];
|
|
1589
1655
|
// 旧版 CCC 配置没有 enabled;只有 API Key 才表示用户实际完成了配置。
|
|
1590
|
-
var CCC_FALLBACK_KEYS = ['DEEPSEEK_API_KEY'];
|
|
1656
|
+
var CCC_FALLBACK_KEYS = ['DEEPSEEK_API_KEY'];
|
|
1657
|
+
var DSH_FALLBACK_KEYS = ['apiKey'];
|
|
1591
1658
|
|
|
1592
1659
|
function renderStep2() {
|
|
1593
1660
|
var c = state.config || {};
|
|
@@ -1620,7 +1687,7 @@ function renderStep2() {
|
|
|
1620
1687
|
}
|
|
1621
1688
|
var codexFastModeEl = document.getElementById('field-CHATCCC_CODEX_FAST_MODE');
|
|
1622
1689
|
if (codexFastModeEl) codexFastModeEl.checked = !!(c.codex && c.codex.fastMode === true);
|
|
1623
|
-
if (c.ccc) {
|
|
1690
|
+
if (c.ccc) {
|
|
1624
1691
|
prefillNested('field-CHATCCC_CCC_API_KEY', c.ccc.DEEPSEEK_API_KEY);
|
|
1625
1692
|
prefillNested('field-CHATCCC_CCC_BASE_URL', c.ccc.DEEPSEEK_BASE_URL);
|
|
1626
1693
|
prefillNested('field-CHATCCC_CCC_PROVIDER', c.ccc.provider);
|
|
@@ -1628,27 +1695,37 @@ function renderStep2() {
|
|
|
1628
1695
|
prefillNested('field-CHATCCC_CCC_SUB_MODEL', c.ccc.subModel);
|
|
1629
1696
|
prefillNested('field-CHATCCC_CCC_ALTERNATIVE_MODEL', c.ccc.alternativeModel);
|
|
1630
1697
|
prefillNested('field-CHATCCC_CCC_EFFORT', c.ccc.effort);
|
|
1631
|
-
prefillContextWindow('field-', c.ccc.contextWindow);
|
|
1632
|
-
}
|
|
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-CHATCCC_DSH_PROVIDER', c.dsh.provider);
|
|
1705
|
+
prefillNested('field-CHATCCC_DSH_MAX_TOKENS', c.dsh.maxTokens || 49152);
|
|
1706
|
+
}
|
|
1633
1707
|
|
|
1634
1708
|
// 按已有 config 决定每个 Agent 默认是否开启:优先 enabled 字段,缺省时按"任一字段非空"
|
|
1635
1709
|
var claudeOn = isAgentEnabled(c.claude, CLAUDE_FALLBACK_KEYS);
|
|
1636
1710
|
var cursorOn = isAgentEnabled(c.cursor, CURSOR_FALLBACK_KEYS);
|
|
1637
1711
|
var codexOn = isAgentEnabled(c.codex, CODEX_FALLBACK_KEYS);
|
|
1638
|
-
var cccOn = isAgentEnabled(c.ccc, CCC_FALLBACK_KEYS);
|
|
1639
|
-
|
|
1640
|
-
|
|
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) {
|
|
1641
1716
|
cccOn = true;
|
|
1642
1717
|
}
|
|
1643
|
-
state.defaultAgent = resolveDefaultAgentFromConfig(c, claudeOn, cursorOn, codexOn, cccOn);
|
|
1718
|
+
state.defaultAgent = resolveDefaultAgentFromConfig(c, claudeOn, cursorOn, codexOn, cccOn, dshOn);
|
|
1644
1719
|
document.getElementById('agent-enable-claude').checked = claudeOn;
|
|
1645
1720
|
document.getElementById('agent-enable-cursor').checked = cursorOn;
|
|
1646
1721
|
document.getElementById('agent-enable-codex').checked = codexOn;
|
|
1647
|
-
document.getElementById('agent-enable-ccc').checked = cccOn;
|
|
1722
|
+
document.getElementById('agent-enable-ccc').checked = cccOn;
|
|
1723
|
+
document.getElementById('agent-enable-dsh').checked = dshOn;
|
|
1648
1724
|
onAgentToggle('claude', claudeOn);
|
|
1649
1725
|
onAgentToggle('cursor', cursorOn);
|
|
1650
1726
|
onAgentToggle('codex', codexOn);
|
|
1651
|
-
onAgentToggle('ccc', cccOn);
|
|
1727
|
+
onAgentToggle('ccc', cccOn);
|
|
1728
|
+
onAgentToggle('dsh', dshOn);
|
|
1652
1729
|
updateDefaultAgentToggles();
|
|
1653
1730
|
|
|
1654
1731
|
// Cursor path placeholder/hint:把已探测到的路径显示为占位
|
|
@@ -1672,7 +1749,7 @@ function renderStep2() {
|
|
|
1672
1749
|
* 收集"待落地到 config.json 的扁平 vars"。
|
|
1673
1750
|
*
|
|
1674
1751
|
* - 飞书字段始终收集
|
|
1675
|
-
* -
|
|
1752
|
+
* - 五个 Agent 的 enabled 状态都显式下发,让 config.json 持久化用户的最新开关偏好
|
|
1676
1753
|
* - Agent 字段仅在该 Agent 开关启用时收集;未启用的 Agent 不下发其它字段,
|
|
1677
1754
|
* 服务端 deepMerge 会保留 config.json 中已有值(避免关闭开关时误清空旧配置)
|
|
1678
1755
|
*/
|
|
@@ -1698,14 +1775,16 @@ function collectAllFields() {
|
|
|
1698
1775
|
vars.CHATCCC_CLAUDE_ENABLED = !!state.agentsEnabled.claude;
|
|
1699
1776
|
vars.CHATCCC_CURSOR_ENABLED = !!state.agentsEnabled.cursor;
|
|
1700
1777
|
vars.CHATCCC_CODEX_ENABLED = !!state.agentsEnabled.codex;
|
|
1701
|
-
vars.CHATCCC_CCC_ENABLED = !!state.agentsEnabled.ccc;
|
|
1778
|
+
vars.CHATCCC_CCC_ENABLED = !!state.agentsEnabled.ccc;
|
|
1779
|
+
vars.CHATCCC_DSH_ENABLED = !!state.agentsEnabled.dsh;
|
|
1702
1780
|
if (!state.defaultAgent || !state.agentsEnabled[state.defaultAgent]) {
|
|
1703
1781
|
state.defaultAgent = firstEnabledAgent();
|
|
1704
1782
|
}
|
|
1705
1783
|
vars.CHATCCC_CLAUDE_DEFAULT_AGENT = state.defaultAgent === 'claude';
|
|
1706
1784
|
vars.CHATCCC_CURSOR_DEFAULT_AGENT = state.defaultAgent === 'cursor';
|
|
1707
1785
|
vars.CHATCCC_CODEX_DEFAULT_AGENT = state.defaultAgent === 'codex';
|
|
1708
|
-
vars.CHATCCC_CCC_DEFAULT_AGENT = state.defaultAgent === 'ccc';
|
|
1786
|
+
vars.CHATCCC_CCC_DEFAULT_AGENT = state.defaultAgent === 'ccc';
|
|
1787
|
+
vars.CHATCCC_DSH_DEFAULT_AGENT = state.defaultAgent === 'dsh';
|
|
1709
1788
|
if (state.agentsEnabled.claude) {
|
|
1710
1789
|
AGENT_FIELDS.claude.forEach(function(key){
|
|
1711
1790
|
var el = document.getElementById('field-' + key);
|
|
@@ -1726,7 +1805,7 @@ function collectAllFields() {
|
|
|
1726
1805
|
else if (el.value.trim()) vars[key] = el.value.trim();
|
|
1727
1806
|
});
|
|
1728
1807
|
}
|
|
1729
|
-
if (state.agentsEnabled.ccc) {
|
|
1808
|
+
if (state.agentsEnabled.ccc) {
|
|
1730
1809
|
AGENT_FIELDS.ccc.forEach(function(key){
|
|
1731
1810
|
if (key === 'CHATCCC_CCC_CONTEXT_WINDOW') {
|
|
1732
1811
|
var cw = contextWindowToTokensValue('field-');
|
|
@@ -1736,7 +1815,13 @@ function collectAllFields() {
|
|
|
1736
1815
|
var el = document.getElementById('field-' + key);
|
|
1737
1816
|
if (el && el.value.trim()) vars[key] = el.value.trim();
|
|
1738
1817
|
});
|
|
1739
|
-
}
|
|
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
|
+
}
|
|
1740
1825
|
return vars;
|
|
1741
1826
|
}
|
|
1742
1827
|
|
|
@@ -1776,7 +1861,8 @@ function renderStep3() {
|
|
|
1776
1861
|
if (state.agentsEnabled.claude) enabledList.push('claude');
|
|
1777
1862
|
if (state.agentsEnabled.cursor) enabledList.push('cursor');
|
|
1778
1863
|
if (state.agentsEnabled.codex) enabledList.push('codex');
|
|
1779
|
-
if (state.agentsEnabled.ccc) enabledList.push('ccc');
|
|
1864
|
+
if (state.agentsEnabled.ccc) enabledList.push('ccc');
|
|
1865
|
+
if (state.agentsEnabled.dsh) enabledList.push('dsh');
|
|
1780
1866
|
if (enabledList.length === 0) {
|
|
1781
1867
|
lines.push('<div style="color:#ef4444">未启用任何 AI Agent</div>');
|
|
1782
1868
|
} else {
|
|
@@ -1807,7 +1893,7 @@ function renderStep3() {
|
|
|
1807
1893
|
lines.push('<div class="config-row"><span class="key">备选模型</span><span class="val">' + (vars.CHATCCC_CODEX_ALTERNATIVE_MODEL || '(留空)') + '</span></div>');
|
|
1808
1894
|
lines.push('<div class="config-row"><span class="key">Effort</span><span class="val">' + (vars.CHATCCC_CODEX_EFFORT || '(留空)') + '</span></div>');
|
|
1809
1895
|
lines.push('<div class="config-row"><span class="key">Fast 模式</span><span class="val">' + (vars.CHATCCC_CODEX_FAST_MODE ? '已启用' : '已禁用') + '</span></div>');
|
|
1810
|
-
} else if (t === 'ccc') {
|
|
1896
|
+
} else if (t === 'ccc') {
|
|
1811
1897
|
lines.push('<h4 style="margin:10px 0 4px;color:#334155">CCC Agent</h4>');
|
|
1812
1898
|
lines.push('<div class="config-row"><span class="key">API Key</span><span class="val">' + (vars.CHATCCC_CCC_API_KEY ? '***已设置***' : '(留空)') + '</span></div>');
|
|
1813
1899
|
lines.push('<div class="config-row"><span class="key">Base URL</span><span class="val">' + (vars.CHATCCC_CCC_BASE_URL || '(留空)') + '</span></div>');
|
|
@@ -1815,7 +1901,12 @@ function renderStep3() {
|
|
|
1815
1901
|
lines.push('<div class="config-row"><span class="key">子模型</span><span class="val">' + (vars.CHATCCC_CCC_SUB_MODEL || '(留空,跟随主模型)') + '</span></div>');
|
|
1816
1902
|
lines.push('<div class="config-row"><span class="key">备选模型</span><span class="val">' + (vars.CHATCCC_CCC_ALTERNATIVE_MODEL || '(留空)') + '</span></div>');
|
|
1817
1903
|
lines.push('<div class="config-row"><span class="key">Effort</span><span class="val">' + (vars.CHATCCC_CCC_EFFORT || '(留空)') + '</span></div>');
|
|
1818
|
-
lines.push('<div class="config-row"><span class="key">上下文窗口</span><span class="val">' + contextWindowTokensLabel(vars.CHATCCC_CCC_CONTEXT_WINDOW || 1048576) + '</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>');
|
|
1819
1910
|
}
|
|
1820
1911
|
});
|
|
1821
1912
|
document.getElementById('review-content').innerHTML = lines.join('');
|
|
@@ -1843,18 +1934,21 @@ async function setDashboardDefaultAgent(agent, enabled) {
|
|
|
1843
1934
|
CHATCCC_CLAUDE_DEFAULT_AGENT: agent === 'claude',
|
|
1844
1935
|
CHATCCC_CURSOR_DEFAULT_AGENT: agent === 'cursor',
|
|
1845
1936
|
CHATCCC_CODEX_DEFAULT_AGENT: agent === 'codex',
|
|
1846
|
-
CHATCCC_CCC_DEFAULT_AGENT: agent === 'ccc'
|
|
1937
|
+
CHATCCC_CCC_DEFAULT_AGENT: agent === 'ccc',
|
|
1938
|
+
CHATCCC_DSH_DEFAULT_AGENT: agent === 'dsh'
|
|
1847
1939
|
};
|
|
1848
1940
|
var result = await api('/api/config', 'POST', { vars: vars });
|
|
1849
1941
|
if (result.ok) {
|
|
1850
1942
|
state.config.claude = state.config.claude || {};
|
|
1851
1943
|
state.config.cursor = state.config.cursor || {};
|
|
1852
1944
|
state.config.codex = state.config.codex || {};
|
|
1853
|
-
state.config.ccc = state.config.ccc || {};
|
|
1945
|
+
state.config.ccc = state.config.ccc || {};
|
|
1946
|
+
state.config.dsh = state.config.dsh || {};
|
|
1854
1947
|
state.config.claude.defaultAgent = agent === 'claude';
|
|
1855
1948
|
state.config.cursor.defaultAgent = agent === 'cursor';
|
|
1856
1949
|
state.config.codex.defaultAgent = agent === 'codex';
|
|
1857
|
-
state.config.ccc.defaultAgent = agent === 'ccc';
|
|
1950
|
+
state.config.ccc.defaultAgent = agent === 'ccc';
|
|
1951
|
+
state.config.dsh.defaultAgent = agent === 'dsh';
|
|
1858
1952
|
updateDefaultAgentToggles();
|
|
1859
1953
|
toastConfigApplyResult(result, '默认 Agent 已更新');
|
|
1860
1954
|
} else {
|
|
@@ -1971,9 +2065,10 @@ function updateDashboardUI() {
|
|
|
1971
2065
|
var claudeOn = isAgentEnabled(c.claude, CLAUDE_FALLBACK_KEYS);
|
|
1972
2066
|
var cursorOn = isAgentEnabled(c.cursor, CURSOR_FALLBACK_KEYS);
|
|
1973
2067
|
var codexOn = isAgentEnabled(c.codex, CODEX_FALLBACK_KEYS);
|
|
1974
|
-
var cccOn = isAgentEnabled(c.ccc, CCC_FALLBACK_KEYS);
|
|
1975
|
-
|
|
1976
|
-
state.
|
|
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);
|
|
1977
2072
|
|
|
1978
2073
|
// 微信 iLink 平台开关:同步复选框和标签
|
|
1979
2074
|
var ilinkEnabled = c.platforms && c.platforms.ilink ? c.platforms.ilink.enabled !== false : true;
|
|
@@ -2005,11 +2100,12 @@ function updateDashboardUI() {
|
|
|
2005
2100
|
document.getElementById('dash-claude').style.display = claudeOn ? '' : 'none';
|
|
2006
2101
|
document.getElementById('dash-cursor').style.display = cursorOn ? '' : 'none';
|
|
2007
2102
|
document.getElementById('dash-codex').style.display = codexOn ? '' : 'none';
|
|
2008
|
-
document.getElementById('dash-ccc').style.display = cccOn ? '' : 'none';
|
|
2103
|
+
document.getElementById('dash-ccc').style.display = cccOn ? '' : 'none';
|
|
2104
|
+
document.getElementById('dash-dsh').style.display = dshOn ? '' : 'none';
|
|
2009
2105
|
updateDefaultAgentToggles();
|
|
2010
|
-
//
|
|
2106
|
+
// 五个都未启用时给一个空态提示,引导用户去配置向导启用
|
|
2011
2107
|
var emptyHint = document.getElementById('dash-no-agent-hint');
|
|
2012
|
-
if (emptyHint) emptyHint.style.display = (!claudeOn && !cursorOn && !codexOn && !cccOn) ? '' : 'none';
|
|
2108
|
+
if (emptyHint) emptyHint.style.display = (!claudeOn && !cursorOn && !codexOn && !cccOn && !dshOn) ? '' : 'none';
|
|
2013
2109
|
|
|
2014
2110
|
document.getElementById('cfg-ANTHROPIC_MODEL').textContent = (c.claude && c.claude.model) || '(留空)';
|
|
2015
2111
|
document.getElementById('cfg-ANTHROPIC_SUBAGENT_MODEL').textContent = (c.claude && c.claude.subagentModel) || '(留空)';
|
|
@@ -2035,7 +2131,13 @@ function updateDashboardUI() {
|
|
|
2035
2131
|
document.getElementById('cfg-CCC_PROVIDER').textContent = (c.ccc && c.ccc.provider) ? c.ccc.provider : '(跟随 DeepCCC 内核配置)';
|
|
2036
2132
|
document.getElementById('cfg-CCC_MODEL').textContent = (c.ccc && c.ccc.model) || '(留空)';
|
|
2037
2133
|
document.getElementById('cfg-CCC_SUB_MODEL').textContent = (c.ccc && c.ccc.subModel) || '(留空,跟随主模型)';
|
|
2038
|
-
document.getElementById('cfg-CCC_ALTERNATIVE_MODEL').textContent = (c.ccc && c.ccc.alternativeModel) || '(留空)';
|
|
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-DSH_PROVIDER').textContent = (c.dsh && c.dsh.provider) || 'deepseek-official';
|
|
2139
|
+
document.getElementById('cfg-DSH_MAX_TOKENS').textContent = String((c.dsh && c.dsh.maxTokens) || 49152);
|
|
2140
|
+
engineRefreshStatus('dsh');
|
|
2039
2141
|
}
|
|
2040
2142
|
|
|
2041
2143
|
function pollStatus() {
|
|
@@ -2091,7 +2193,7 @@ function editSection(section) {
|
|
|
2091
2193
|
else if (section === 'chromeDevtools') fields = CHROME_DEVTOOLS_FIELDS;
|
|
2092
2194
|
else fields = AGENT_FIELDS[section] || [];
|
|
2093
2195
|
|
|
2094
|
-
var titleMap = { feishu: '飞书', webUi: 'Web UI', chromeDevtools: 'Chrome CDP', claude: 'Claude Agent', cursor: 'Cursor Agent', codex: 'Codex Agent', ccc: 'CCC Agent' };
|
|
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' };
|
|
2095
2197
|
document.getElementById('edit-modal-title').textContent = '编辑 ' + (titleMap[section] || section);
|
|
2096
2198
|
|
|
2097
2199
|
document.getElementById('edit-modal-effect').textContent = configEffectHint(section);
|
|
@@ -2112,7 +2214,8 @@ function editSection(section) {
|
|
|
2112
2214
|
'CHATCCC_CODEX_FAST_MODE': 'Fast 模式',
|
|
2113
2215
|
'CHATCCC_CCC_API_KEY': 'API Key', 'CHATCCC_CCC_BASE_URL': 'Base URL',
|
|
2114
2216
|
'CHATCCC_CCC_PROVIDER': 'API 协议(选填)',
|
|
2115
|
-
'CHATCCC_CCC_MODEL': '模型', 'CHATCCC_CCC_SUB_MODEL': '子模型', 'CHATCCC_CCC_ALTERNATIVE_MODEL': '备选模型', 'CHATCCC_CCC_EFFORT': 'Effort', 'CHATCCC_CCC_CONTEXT_WINDOW': '上下文窗口'
|
|
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'
|
|
2116
2219
|
};
|
|
2117
2220
|
var hintMap = {
|
|
2118
2221
|
'CHATCCC_WEB_UI_OPEN_ON_START': '关闭后可继续手动访问 http://localhost:<端口>/;/restart、/update 和 Web UI 重启无论此项为何值都不会自动打开。',
|
|
@@ -2160,7 +2263,7 @@ function editSection(section) {
|
|
|
2160
2263
|
else if (key === 'CHATCCC_CODEX_ALTERNATIVE_MODEL') val = state.config.codex.alternativeModel || '';
|
|
2161
2264
|
else if (key === 'CHATCCC_CODEX_EFFORT') val = state.config.codex.effort || '';
|
|
2162
2265
|
else if (key === 'CHATCCC_CODEX_FAST_MODE') val = state.config.codex.fastMode === true ? 'true' : 'false';
|
|
2163
|
-
} else if (section === 'ccc' && state.config.ccc) {
|
|
2266
|
+
} else if (section === 'ccc' && state.config.ccc) {
|
|
2164
2267
|
if (key === 'CHATCCC_CCC_API_KEY') val = state.config.ccc.DEEPSEEK_API_KEY || '';
|
|
2165
2268
|
else if (key === 'CHATCCC_CCC_BASE_URL') val = state.config.ccc.DEEPSEEK_BASE_URL || '';
|
|
2166
2269
|
else if (key === 'CHATCCC_CCC_PROVIDER') val = state.config.ccc.provider || '';
|
|
@@ -2168,8 +2271,14 @@ function editSection(section) {
|
|
|
2168
2271
|
else if (key === 'CHATCCC_CCC_SUB_MODEL') val = state.config.ccc.subModel || '';
|
|
2169
2272
|
else if (key === 'CHATCCC_CCC_ALTERNATIVE_MODEL') val = state.config.ccc.alternativeModel || '';
|
|
2170
2273
|
else if (key === 'CHATCCC_CCC_EFFORT') val = state.config.ccc.effort || '';
|
|
2171
|
-
else if (key === 'CHATCCC_CCC_CONTEXT_WINDOW') val = state.config.ccc.contextWindow || '1048576';
|
|
2172
|
-
}
|
|
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 === 'CHATCCC_DSH_PROVIDER') val = state.config.dsh.provider || '';
|
|
2280
|
+
else if (key === 'CHATCCC_DSH_MAX_TOKENS') val = state.config.dsh.maxTokens || '49152';
|
|
2281
|
+
}
|
|
2173
2282
|
}
|
|
2174
2283
|
var isSecret = key.includes('SECRET') || key.includes('API_KEY');
|
|
2175
2284
|
if (key === 'CHATCCC_WEB_UI_OPEN_ON_START' || key === 'CHATCCC_CHROME_DEVTOOLS_ENABLED' || key === 'CHATCCC_CODEX_FAST_MODE') {
|
|
@@ -2319,124 +2428,95 @@ function validateCli(tool) {
|
|
|
2319
2428
|
});
|
|
2320
2429
|
}
|
|
2321
2430
|
|
|
2322
|
-
// ----
|
|
2323
|
-
var
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
el
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
}
|
|
2375
|
-
|
|
2376
|
-
function
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
}
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
}
|
|
2406
|
-
}
|
|
2407
|
-
if (s.phase === 'downloading' || s.phase === 'installing' || s.phase === 'detecting') {
|
|
2408
|
-
claudeEnginePollTimer = setTimeout(claudeEngineRefreshStatus, 600);
|
|
2409
|
-
} else if (claudeEnginePollTimer) {
|
|
2410
|
-
clearTimeout(claudeEnginePollTimer);
|
|
2411
|
-
claudeEnginePollTimer = null;
|
|
2412
|
-
}
|
|
2413
|
-
}).catch(function(){
|
|
2414
|
-
// 网络/服务异常时停止轮询,避免无限重试
|
|
2415
|
-
if (claudeEnginePollTimer) { clearTimeout(claudeEnginePollTimer); claudeEnginePollTimer = null; }
|
|
2416
|
-
});
|
|
2417
|
-
}
|
|
2418
|
-
|
|
2419
|
-
function installClaudeEngine() {
|
|
2420
|
-
var btn = claudeEngineEl('claude-engine-install-btn');
|
|
2421
|
-
if (btn) btn.disabled = true;
|
|
2422
|
-
api('/api/claude-sdk/install', 'POST').then(function(r){
|
|
2423
|
-
if (r.ok) {
|
|
2424
|
-
claudeEngineRefreshStatus();
|
|
2425
|
-
} else {
|
|
2426
|
-
claudeEngineRenderStatus({ phase: 'error', message: '启动安装失败', error: r.error || '' });
|
|
2427
|
-
if (btn) btn.disabled = false;
|
|
2428
|
-
}
|
|
2429
|
-
}).catch(function(e){
|
|
2430
|
-
claudeEngineRenderStatus({ phase: 'error', message: '请求失败', error: String(e) });
|
|
2431
|
-
if (btn) btn.disabled = false;
|
|
2432
|
-
});
|
|
2433
|
-
}
|
|
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
|
+
});
|
|
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
|
+
}
|
|
2434
2514
|
|
|
2435
2515
|
// ---- Start ----
|
|
2436
2516
|
init();
|
|
2437
2517
|
|
|
2438
|
-
//
|
|
2439
|
-
setTimeout(
|
|
2518
|
+
// 页面刷新后从持久化任务文件恢复每一步的安装进度。
|
|
2519
|
+
setTimeout(function(){ engineRefreshStatus('claude'); engineRefreshStatus('dsh'); }, 300);
|
|
2440
2520
|
</script>
|
|
2441
2521
|
</body>
|
|
2442
2522
|
</html>`;
|
|
@@ -2470,15 +2550,21 @@ async function handleRequest(req, res) {
|
|
|
2470
2550
|
return handleValidate(req, res);
|
|
2471
2551
|
if (url === "/api/ilink/forget" && method === "POST")
|
|
2472
2552
|
return handleForgetIlink(req, res);
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2553
|
+
const engineStatusMatch = pathname.match(/^\/api\/engines\/([^/]+)\/status$/);
|
|
2554
|
+
if (engineStatusMatch && method === "GET")
|
|
2555
|
+
return handleEngineStatus(decodeURIComponent(engineStatusMatch[1]), res);
|
|
2556
|
+
const engineInstallMatch = pathname.match(/^\/api\/engines\/([^/]+)\/install$/);
|
|
2557
|
+
if (engineInstallMatch && method === "POST")
|
|
2558
|
+
return handleEngineInstall(decodeURIComponent(engineInstallMatch[1]), res);
|
|
2477
2559
|
if (method === "GET" && (pathname === "/agent-team" || pathname === "/agent-team/")) {
|
|
2478
2560
|
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
2479
2561
|
res.end(AGENT_TEAM_PAGE_HTML);
|
|
2480
2562
|
return;
|
|
2481
2563
|
}
|
|
2564
|
+
if (pathname.startsWith("/api/")) {
|
|
2565
|
+
jsonReply(res, 404, { error: "Not found" });
|
|
2566
|
+
return;
|
|
2567
|
+
}
|
|
2482
2568
|
// Serve HTML page for all other GET requests
|
|
2483
2569
|
if (method === "GET") {
|
|
2484
2570
|
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|