chatccc 0.2.218 → 0.2.220

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/src/web-ui.ts CHANGED
@@ -52,9 +52,17 @@ interface AppConfig {
52
52
  alternativeModel?: string;
53
53
  avatarBatteryMode?: string;
54
54
  onDemandMonthlyBudget?: number;
55
- };
55
+ };
56
56
  codex?: { enabled?: boolean; defaultAgent?: boolean; path?: string; command?: string; model?: string; alternativeModel?: string; effort?: string; fastMode?: boolean };
57
- }
57
+ ccc?: {
58
+ enabled?: boolean;
59
+ defaultAgent?: boolean;
60
+ DEEPSEEK_API_KEY?: string;
61
+ DEEPSEEK_BASE_URL?: string;
62
+ model?: string;
63
+ alternativeModel?: string;
64
+ };
65
+ }
58
66
 
59
67
  // ---------------------------------------------------------------------------
60
68
  // /api/start 路径选择(纯函数,便于单测护栏)
@@ -497,10 +505,28 @@ export function unflattenConfig(flat: Record<string, unknown>): Record<string, u
497
505
  } else if (key === "CHATCCC_CODEX_ENABLED") {
498
506
  result.codex = result.codex || {};
499
507
  (result.codex as Record<string, unknown>).enabled = val === true || val === "true";
500
- } else if (key === "CHATCCC_CODEX_DEFAULT_AGENT") {
501
- result.codex = result.codex || {};
502
- (result.codex as Record<string, unknown>).defaultAgent = val === true || val === "true";
503
- }
508
+ } else if (key === "CHATCCC_CODEX_DEFAULT_AGENT") {
509
+ result.codex = result.codex || {};
510
+ (result.codex as Record<string, unknown>).defaultAgent = val === true || val === "true";
511
+ } else if (key === "CHATCCC_CCC_API_KEY") {
512
+ result.ccc = result.ccc || {};
513
+ (result.ccc as Record<string, unknown>).DEEPSEEK_API_KEY = val;
514
+ } else if (key === "CHATCCC_CCC_BASE_URL") {
515
+ result.ccc = result.ccc || {};
516
+ (result.ccc as Record<string, unknown>).DEEPSEEK_BASE_URL = val;
517
+ } else if (key === "CHATCCC_CCC_MODEL") {
518
+ result.ccc = result.ccc || {};
519
+ (result.ccc as Record<string, unknown>).model = val;
520
+ } else if (key === "CHATCCC_CCC_ALTERNATIVE_MODEL") {
521
+ result.ccc = result.ccc || {};
522
+ (result.ccc as Record<string, unknown>).alternativeModel = val;
523
+ } else if (key === "CHATCCC_CCC_ENABLED") {
524
+ result.ccc = result.ccc || {};
525
+ (result.ccc as Record<string, unknown>).enabled = val === true || val === "true";
526
+ } else if (key === "CHATCCC_CCC_DEFAULT_AGENT") {
527
+ result.ccc = result.ccc || {};
528
+ (result.ccc as Record<string, unknown>).defaultAgent = val === true || val === "true";
529
+ }
504
530
  }
505
531
  return result;
506
532
  }
@@ -905,7 +931,7 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
905
931
  </fieldset>
906
932
  </div>
907
933
 
908
- <!-- Codex 卡片 -->
934
+ <!-- Codex 卡片 -->
909
935
  <div class="agent-card" id="agent-card-codex">
910
936
  <div class="agent-card-header">
911
937
  <input type="checkbox" class="agent-toggle" id="agent-enable-codex" onchange="onAgentToggle('codex', this.checked)">
@@ -943,9 +969,42 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
943
969
  <button class="btn btn-outline" onclick="validateCli('codex')" style="margin-bottom:12px">检测 Codex CLI</button>
944
970
  <div id="codex-validate-result"></div>
945
971
  </fieldset>
946
- </div>
947
-
948
- </div>
972
+ </div>
973
+
974
+ <!-- CCC Agent 卡片 -->
975
+ <div class="agent-card" id="agent-card-ccc">
976
+ <div class="agent-card-header">
977
+ <input type="checkbox" class="agent-toggle" id="agent-enable-ccc" onchange="onAgentToggle('ccc', this.checked)">
978
+ <div class="meta">
979
+ <div class="name">CCC Agent</div>
980
+ <div class="desc">ChatCCC 内置 Agent<br>使用 DeepSeek 兼容 API</div>
981
+ </div>
982
+ </div>
983
+ <label class="agent-default-row">
984
+ <input type="checkbox" id="agent-default-ccc" onchange="onDefaultAgentToggle('ccc', this.checked)">
985
+ 设为默认 Agent
986
+ </label>
987
+ <fieldset class="agent-body" id="agent-body-ccc" disabled>
988
+ <div class="form-group">
989
+ <label>API Key</label>
990
+ <input type="password" id="field-CHATCCC_CCC_API_KEY" placeholder="DeepSeek 兼容 API Key">
991
+ </div>
992
+ <div class="form-group">
993
+ <label>Base URL</label>
994
+ <input type="text" id="field-CHATCCC_CCC_BASE_URL" placeholder="https://api.deepseek.com/v1">
995
+ </div>
996
+ <div class="form-group">
997
+ <label>模型</label>
998
+ <input type="text" id="field-CHATCCC_CCC_MODEL" placeholder="deepseek-v4-pro">
999
+ </div>
1000
+ <div class="form-group">
1001
+ <label>备选模型(选填)</label>
1002
+ <input type="text" id="field-CHATCCC_CCC_ALTERNATIVE_MODEL" placeholder="加入 /model 列表,便于会话内切换">
1003
+ </div>
1004
+ </fieldset>
1005
+ </div>
1006
+
1007
+ </div>
949
1008
 
950
1009
  <div class="btn-group" style="justify-content:space-between">
951
1010
  <button class="btn btn-outline" onclick="goStep(1)">返回</button>
@@ -1063,7 +1122,7 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
1063
1122
  </div>
1064
1123
  </details>
1065
1124
 
1066
- <details class="card config-section" id="dash-codex">
1125
+ <details class="card config-section" id="dash-codex">
1067
1126
  <summary>Codex Agent</summary>
1068
1127
  <div class="section-detail">
1069
1128
  <div class="config-row"><span class="key">CLI 路径</span><span class="val" id="cfg-CODEX_PATH">-</span></div>
@@ -1075,7 +1134,20 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
1075
1134
  <div class="hint" style="margin-top:6px;line-height:1.6">生效范围:保存后下一条消息或下个新会话生效,当前生成不中断。</div>
1076
1135
  <button class="btn btn-outline" style="margin-top:8px" onclick="editSection('codex')">编辑</button>
1077
1136
  </div>
1078
- </details>
1137
+ </details>
1138
+
1139
+ <details class="card config-section" id="dash-ccc">
1140
+ <summary>CCC Agent</summary>
1141
+ <div class="section-detail">
1142
+ <div class="config-row"><span class="key">API Key</span><span class="val" id="cfg-CCC_API_KEY">-</span></div>
1143
+ <div class="config-row"><span class="key">Base URL</span><span class="val" id="cfg-CCC_BASE_URL">-</span></div>
1144
+ <div class="config-row"><span class="key">模型</span><span class="val" id="cfg-CCC_MODEL">-</span></div>
1145
+ <div class="config-row"><span class="key">备选模型</span><span class="val" id="cfg-CCC_ALTERNATIVE_MODEL">-</span></div>
1146
+ <label class="agent-default-row" style="margin-top:10px"><input type="checkbox" id="dash-default-ccc" onchange="setDashboardDefaultAgent('ccc', this.checked)"> 设为默认 Agent</label>
1147
+ <div class="hint" style="margin-top:6px;line-height:1.6">备选模型仅加入 /model 人工切换列表;保存后下一条消息或下个新会话生效。</div>
1148
+ <button class="btn btn-outline" style="margin-top:8px" onclick="editSection('ccc')">编辑</button>
1149
+ </div>
1150
+ </details>
1079
1151
 
1080
1152
  <div class="card" id="dash-no-agent-hint" style="text-align:center;color:#94a3b8;display:none">
1081
1153
  未启用任何 AI Agent。点击下方按钮重新运行配置向导启用。
@@ -1103,8 +1175,8 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
1103
1175
  <script>
1104
1176
  let state = {
1105
1177
  view: 'loading',
1106
- // 三个 Agent 各自的启用开关;初始全 false,renderStep2() 会按已存在 config 自动打开
1107
- agentsEnabled: { claude: false, cursor: false, codex: false },
1178
+ // 四个 Agent 各自的启用开关;初始全 false,renderStep2() 会按已存在 config 自动打开
1179
+ agentsEnabled: { claude: false, cursor: false, codex: false, ccc: false },
1108
1180
  defaultAgent: 'claude',
1109
1181
  wizardStep: 1,
1110
1182
  config: {},
@@ -1120,7 +1192,8 @@ var step2InputBound = false;
1120
1192
  const AGENT_FIELDS = {
1121
1193
  claude: ['CHATCCC_ANTHROPIC_MODEL','CHATCCC_ANTHROPIC_SUBAGENT_MODEL','CHATCCC_ANTHROPIC_EFFORT','CHATCCC_ANTHROPIC_API_KEY','CHATCCC_ANTHROPIC_BASE_URL','CHATCCC_ANTHROPIC_MAX_TURN'],
1122
1194
  cursor: ['CHATCCC_CURSOR_PATH','CHATCCC_CURSOR_MODEL','CHATCCC_CURSOR_ALTERNATIVE_MODEL','CHATCCC_CURSOR_AVATAR_BATTERY_MODE','CHATCCC_CURSOR_ON_DEMAND_MONTHLY_BUDGET'],
1123
- codex: ['CHATCCC_CODEX_PATH','CHATCCC_CODEX_MODEL','CHATCCC_CODEX_ALTERNATIVE_MODEL','CHATCCC_CODEX_EFFORT','CHATCCC_CODEX_FAST_MODE']
1195
+ codex: ['CHATCCC_CODEX_PATH','CHATCCC_CODEX_MODEL','CHATCCC_CODEX_ALTERNATIVE_MODEL','CHATCCC_CODEX_EFFORT','CHATCCC_CODEX_FAST_MODE'],
1196
+ ccc: ['CHATCCC_CCC_API_KEY','CHATCCC_CCC_BASE_URL','CHATCCC_CCC_MODEL','CHATCCC_CCC_ALTERNATIVE_MODEL']
1124
1197
  };
1125
1198
  const FEISHU_FIELDS = ['CHATCCC_APP_ID','CHATCCC_APP_SECRET'];
1126
1199
  const WEB_UI_FIELDS = ['CHATCCC_WEB_UI_OPEN_ON_START'];
@@ -1225,22 +1298,25 @@ function onAgentToggle(agent, enabled) {
1225
1298
  function firstEnabledAgent() {
1226
1299
  if (state.agentsEnabled.claude) return 'claude';
1227
1300
  if (state.agentsEnabled.cursor) return 'cursor';
1228
- if (state.agentsEnabled.codex) return 'codex';
1301
+ if (state.agentsEnabled.codex) return 'codex';
1302
+ if (state.agentsEnabled.ccc) return 'ccc';
1229
1303
  return null;
1230
1304
  }
1231
1305
 
1232
- function resolveDefaultAgentFromConfig(c, claudeOn, cursorOn, codexOn) {
1306
+ function resolveDefaultAgentFromConfig(c, claudeOn, cursorOn, codexOn, cccOn) {
1233
1307
  if (claudeOn && c.claude && c.claude.defaultAgent === true) return 'claude';
1234
1308
  if (cursorOn && c.cursor && c.cursor.defaultAgent === true) return 'cursor';
1235
- if (codexOn && c.codex && c.codex.defaultAgent === true) return 'codex';
1309
+ if (codexOn && c.codex && c.codex.defaultAgent === true) return 'codex';
1310
+ if (cccOn && c.ccc && c.ccc.defaultAgent === true) return 'ccc';
1236
1311
  if (claudeOn) return 'claude';
1237
1312
  if (cursorOn) return 'cursor';
1238
- if (codexOn) return 'codex';
1313
+ if (codexOn) return 'codex';
1314
+ if (cccOn) return 'ccc';
1239
1315
  return 'claude';
1240
1316
  }
1241
1317
 
1242
1318
  function updateDefaultAgentToggles() {
1243
- ['claude','cursor','codex'].forEach(function(agent){
1319
+ ['claude','cursor','codex','ccc'].forEach(function(agent){
1244
1320
  var el = document.getElementById('agent-default-' + agent);
1245
1321
  if (el) {
1246
1322
  el.checked = state.defaultAgent === agent;
@@ -1271,7 +1347,8 @@ function updateStep2NextBtn() {
1271
1347
  var validCount = 0;
1272
1348
  if (state.agentsEnabled.claude) validCount++;
1273
1349
  if (state.agentsEnabled.cursor) validCount++;
1274
- if (state.agentsEnabled.codex) validCount++;
1350
+ if (state.agentsEnabled.codex) validCount++;
1351
+ if (state.agentsEnabled.ccc) validCount++;
1275
1352
  if (validCount > 0 && !state.defaultAgent) {
1276
1353
  state.defaultAgent = firstEnabledAgent();
1277
1354
  updateDefaultAgentToggles();
@@ -1412,6 +1489,8 @@ function isAgentEnabled(node, keys) {
1412
1489
  var CLAUDE_FALLBACK_KEYS = ['model','subagentModel','effort','maxTurn'];
1413
1490
  var CURSOR_FALLBACK_KEYS = ['path','command','model','alternativeModel'];
1414
1491
  var CODEX_FALLBACK_KEYS = ['path','command','model','alternativeModel','effort','fastMode'];
1492
+ // 旧版 CCC 配置没有 enabled;只有 API Key 才表示用户实际完成了配置。
1493
+ var CCC_FALLBACK_KEYS = ['DEEPSEEK_API_KEY'];
1415
1494
 
1416
1495
  function renderStep2() {
1417
1496
  var c = state.config || {};
@@ -1444,18 +1523,27 @@ function renderStep2() {
1444
1523
  }
1445
1524
  var codexFastModeEl = document.getElementById('field-CHATCCC_CODEX_FAST_MODE');
1446
1525
  if (codexFastModeEl) codexFastModeEl.checked = !!(c.codex && c.codex.fastMode === true);
1526
+ if (c.ccc) {
1527
+ prefillNested('field-CHATCCC_CCC_API_KEY', c.ccc.DEEPSEEK_API_KEY);
1528
+ prefillNested('field-CHATCCC_CCC_BASE_URL', c.ccc.DEEPSEEK_BASE_URL);
1529
+ prefillNested('field-CHATCCC_CCC_MODEL', c.ccc.model);
1530
+ prefillNested('field-CHATCCC_CCC_ALTERNATIVE_MODEL', c.ccc.alternativeModel);
1531
+ }
1447
1532
 
1448
1533
  // 按已有 config 决定每个 Agent 默认是否开启:优先 enabled 字段,缺省时按"任一字段非空"
1449
1534
  var claudeOn = isAgentEnabled(c.claude, CLAUDE_FALLBACK_KEYS);
1450
1535
  var cursorOn = isAgentEnabled(c.cursor, CURSOR_FALLBACK_KEYS);
1451
- var codexOn = isAgentEnabled(c.codex, CODEX_FALLBACK_KEYS);
1452
- state.defaultAgent = resolveDefaultAgentFromConfig(c, claudeOn, cursorOn, codexOn);
1536
+ var codexOn = isAgentEnabled(c.codex, CODEX_FALLBACK_KEYS);
1537
+ var cccOn = isAgentEnabled(c.ccc, CCC_FALLBACK_KEYS);
1538
+ state.defaultAgent = resolveDefaultAgentFromConfig(c, claudeOn, cursorOn, codexOn, cccOn);
1453
1539
  document.getElementById('agent-enable-claude').checked = claudeOn;
1454
1540
  document.getElementById('agent-enable-cursor').checked = cursorOn;
1455
- document.getElementById('agent-enable-codex').checked = codexOn;
1541
+ document.getElementById('agent-enable-codex').checked = codexOn;
1542
+ document.getElementById('agent-enable-ccc').checked = cccOn;
1456
1543
  onAgentToggle('claude', claudeOn);
1457
1544
  onAgentToggle('cursor', cursorOn);
1458
- onAgentToggle('codex', codexOn);
1545
+ onAgentToggle('codex', codexOn);
1546
+ onAgentToggle('ccc', cccOn);
1459
1547
  updateDefaultAgentToggles();
1460
1548
 
1461
1549
  // Cursor path placeholder/hint:把已探测到的路径显示为占位
@@ -1479,7 +1567,7 @@ function renderStep2() {
1479
1567
  * 收集"待落地到 config.json 的扁平 vars"。
1480
1568
  *
1481
1569
  * - 飞书字段始终收集
1482
- * - 三个 Agent 的 enabled 状态都显式下发,让 config.json 持久化用户的最新开关偏好
1570
+ * - 四个 Agent 的 enabled 状态都显式下发,让 config.json 持久化用户的最新开关偏好
1483
1571
  * - Agent 字段仅在该 Agent 开关启用时收集;未启用的 Agent 不下发其它字段,
1484
1572
  * 服务端 deepMerge 会保留 config.json 中已有值(避免关闭开关时误清空旧配置)
1485
1573
  */
@@ -1504,13 +1592,15 @@ function collectAllFields() {
1504
1592
  if (cdpPathEl && cdpPathEl.value.trim()) vars.CHATCCC_CHROME_DEVTOOLS_PATH = cdpPathEl.value.trim();
1505
1593
  vars.CHATCCC_CLAUDE_ENABLED = !!state.agentsEnabled.claude;
1506
1594
  vars.CHATCCC_CURSOR_ENABLED = !!state.agentsEnabled.cursor;
1507
- vars.CHATCCC_CODEX_ENABLED = !!state.agentsEnabled.codex;
1595
+ vars.CHATCCC_CODEX_ENABLED = !!state.agentsEnabled.codex;
1596
+ vars.CHATCCC_CCC_ENABLED = !!state.agentsEnabled.ccc;
1508
1597
  if (!state.defaultAgent || !state.agentsEnabled[state.defaultAgent]) {
1509
1598
  state.defaultAgent = firstEnabledAgent();
1510
1599
  }
1511
1600
  vars.CHATCCC_CLAUDE_DEFAULT_AGENT = state.defaultAgent === 'claude';
1512
1601
  vars.CHATCCC_CURSOR_DEFAULT_AGENT = state.defaultAgent === 'cursor';
1513
- vars.CHATCCC_CODEX_DEFAULT_AGENT = state.defaultAgent === 'codex';
1602
+ vars.CHATCCC_CODEX_DEFAULT_AGENT = state.defaultAgent === 'codex';
1603
+ vars.CHATCCC_CCC_DEFAULT_AGENT = state.defaultAgent === 'ccc';
1514
1604
  if (state.agentsEnabled.claude) {
1515
1605
  AGENT_FIELDS.claude.forEach(function(key){
1516
1606
  var el = document.getElementById('field-' + key);
@@ -1530,8 +1620,14 @@ function collectAllFields() {
1530
1620
  if (key === 'CHATCCC_CODEX_FAST_MODE') vars[key] = !!el.checked;
1531
1621
  else if (el.value.trim()) vars[key] = el.value.trim();
1532
1622
  });
1533
- }
1534
- return vars;
1623
+ }
1624
+ if (state.agentsEnabled.ccc) {
1625
+ AGENT_FIELDS.ccc.forEach(function(key){
1626
+ var el = document.getElementById('field-' + key);
1627
+ if (el && el.value.trim()) vars[key] = el.value.trim();
1628
+ });
1629
+ }
1630
+ return vars;
1535
1631
  }
1536
1632
 
1537
1633
  function renderStep3() {
@@ -1553,7 +1649,6 @@ function renderStep3() {
1553
1649
  if (!state.platformsEnabled.feishu && !state.platformsEnabled.ilink) {
1554
1650
  lines.push('<div style="color:#ef4444;margin-top:8px">未启用任何平台</div>');
1555
1651
  }
1556
-
1557
1652
  lines.push('<h3 style="margin:16px 0 8px">Web UI</h3>');
1558
1653
  lines.push('<div class="config-row"><span class="key">直接启动时自动打开</span><span class="val">' + (vars.CHATCCC_WEB_UI_OPEN_ON_START ? '已启用' : '已禁用') + '</span></div>');
1559
1654
 
@@ -1570,11 +1665,12 @@ function renderStep3() {
1570
1665
  var enabledList = [];
1571
1666
  if (state.agentsEnabled.claude) enabledList.push('claude');
1572
1667
  if (state.agentsEnabled.cursor) enabledList.push('cursor');
1573
- if (state.agentsEnabled.codex) enabledList.push('codex');
1668
+ if (state.agentsEnabled.codex) enabledList.push('codex');
1669
+ if (state.agentsEnabled.ccc) enabledList.push('ccc');
1574
1670
  if (enabledList.length === 0) {
1575
1671
  lines.push('<div style="color:#ef4444">未启用任何 AI Agent</div>');
1576
1672
  } else {
1577
- var defaultLabel = state.defaultAgent === 'cursor' ? 'Cursor' : state.defaultAgent === 'codex' ? 'Codex' : 'Claude Code';
1673
+ var defaultLabel = state.defaultAgent === 'cursor' ? 'Cursor' : state.defaultAgent === 'codex' ? 'Codex' : state.defaultAgent === 'ccc' ? 'CCC Agent' : 'Claude Code';
1578
1674
  lines.push('<div class="config-row"><span class="key">/new 默认 Agent</span><span class="val">' + defaultLabel + '</span></div>');
1579
1675
  }
1580
1676
  enabledList.forEach(function(t){
@@ -1594,13 +1690,19 @@ function renderStep3() {
1594
1690
  if (vars.CHATCCC_CURSOR_AVATAR_BATTERY_MODE === 'onDemandUse') {
1595
1691
  lines.push('<div class="config-row"><span class="key">每月On demand use预算</span><span class="val">' + (vars.CHATCCC_CURSOR_ON_DEMAND_MONTHLY_BUDGET || '1000') + '</span></div>');
1596
1692
  }
1597
- } else if (t === 'codex') {
1693
+ } else if (t === 'codex') {
1598
1694
  lines.push('<h4 style="margin:10px 0 4px;color:#334155">Codex</h4>');
1599
1695
  if (vars.CHATCCC_CODEX_PATH) lines.push('<div class="config-row"><span class="key">CLI 路径</span><span class="val">' + vars.CHATCCC_CODEX_PATH + '</span></div>');
1600
1696
  lines.push('<div class="config-row"><span class="key">模型</span><span class="val">' + (vars.CHATCCC_CODEX_MODEL || '(留空)') + '</span></div>');
1601
1697
  lines.push('<div class="config-row"><span class="key">备选模型</span><span class="val">' + (vars.CHATCCC_CODEX_ALTERNATIVE_MODEL || '(留空)') + '</span></div>');
1602
1698
  lines.push('<div class="config-row"><span class="key">Effort</span><span class="val">' + (vars.CHATCCC_CODEX_EFFORT || '(留空)') + '</span></div>');
1603
1699
  lines.push('<div class="config-row"><span class="key">Fast 模式</span><span class="val">' + (vars.CHATCCC_CODEX_FAST_MODE ? '已启用' : '已禁用') + '</span></div>');
1700
+ } else if (t === 'ccc') {
1701
+ lines.push('<h4 style="margin:10px 0 4px;color:#334155">CCC Agent</h4>');
1702
+ lines.push('<div class="config-row"><span class="key">API Key</span><span class="val">' + (vars.CHATCCC_CCC_API_KEY ? '***已设置***' : '(留空)') + '</span></div>');
1703
+ lines.push('<div class="config-row"><span class="key">Base URL</span><span class="val">' + (vars.CHATCCC_CCC_BASE_URL || '(留空)') + '</span></div>');
1704
+ lines.push('<div class="config-row"><span class="key">模型</span><span class="val">' + (vars.CHATCCC_CCC_MODEL || '(留空)') + '</span></div>');
1705
+ lines.push('<div class="config-row"><span class="key">备选模型</span><span class="val">' + (vars.CHATCCC_CCC_ALTERNATIVE_MODEL || '(留空)') + '</span></div>');
1604
1706
  }
1605
1707
  });
1606
1708
  document.getElementById('review-content').innerHTML = lines.join('');
@@ -1626,17 +1728,20 @@ async function setDashboardDefaultAgent(agent, enabled) {
1626
1728
  state.defaultAgent = agent;
1627
1729
  var vars = {
1628
1730
  CHATCCC_CLAUDE_DEFAULT_AGENT: agent === 'claude',
1629
- CHATCCC_CURSOR_DEFAULT_AGENT: agent === 'cursor',
1630
- CHATCCC_CODEX_DEFAULT_AGENT: agent === 'codex'
1731
+ CHATCCC_CURSOR_DEFAULT_AGENT: agent === 'cursor',
1732
+ CHATCCC_CODEX_DEFAULT_AGENT: agent === 'codex',
1733
+ CHATCCC_CCC_DEFAULT_AGENT: agent === 'ccc'
1631
1734
  };
1632
1735
  var result = await api('/api/config', 'POST', { vars: vars });
1633
1736
  if (result.ok) {
1634
1737
  state.config.claude = state.config.claude || {};
1635
1738
  state.config.cursor = state.config.cursor || {};
1636
- state.config.codex = state.config.codex || {};
1739
+ state.config.codex = state.config.codex || {};
1740
+ state.config.ccc = state.config.ccc || {};
1637
1741
  state.config.claude.defaultAgent = agent === 'claude';
1638
1742
  state.config.cursor.defaultAgent = agent === 'cursor';
1639
1743
  state.config.codex.defaultAgent = agent === 'codex';
1744
+ state.config.ccc.defaultAgent = agent === 'ccc';
1640
1745
  updateDefaultAgentToggles();
1641
1746
  toastConfigApplyResult(result, '默认 Agent 已更新');
1642
1747
  } else {
@@ -1752,9 +1857,10 @@ function updateDashboardUI() {
1752
1857
  // 只显示已启用的 Agent 卡片(按 enabled 字段;缺省时退回到"任一字段非空"兼容旧 config)
1753
1858
  var claudeOn = isAgentEnabled(c.claude, CLAUDE_FALLBACK_KEYS);
1754
1859
  var cursorOn = isAgentEnabled(c.cursor, CURSOR_FALLBACK_KEYS);
1755
- var codexOn = isAgentEnabled(c.codex, CODEX_FALLBACK_KEYS);
1756
- state.agentsEnabled = { claude: claudeOn, cursor: cursorOn, codex: codexOn };
1757
- state.defaultAgent = resolveDefaultAgentFromConfig(c, claudeOn, cursorOn, codexOn);
1860
+ var codexOn = isAgentEnabled(c.codex, CODEX_FALLBACK_KEYS);
1861
+ var cccOn = isAgentEnabled(c.ccc, CCC_FALLBACK_KEYS);
1862
+ state.agentsEnabled = { claude: claudeOn, cursor: cursorOn, codex: codexOn, ccc: cccOn };
1863
+ state.defaultAgent = resolveDefaultAgentFromConfig(c, claudeOn, cursorOn, codexOn, cccOn);
1758
1864
 
1759
1865
  // 微信 iLink 平台开关:同步复选框和标签
1760
1866
  var ilinkEnabled = c.platforms && c.platforms.ilink ? c.platforms.ilink.enabled !== false : true;
@@ -1785,11 +1891,12 @@ function updateDashboardUI() {
1785
1891
 
1786
1892
  document.getElementById('dash-claude').style.display = claudeOn ? '' : 'none';
1787
1893
  document.getElementById('dash-cursor').style.display = cursorOn ? '' : 'none';
1788
- document.getElementById('dash-codex').style.display = codexOn ? '' : 'none';
1894
+ document.getElementById('dash-codex').style.display = codexOn ? '' : 'none';
1895
+ document.getElementById('dash-ccc').style.display = cccOn ? '' : 'none';
1789
1896
  updateDefaultAgentToggles();
1790
- // 三个都未启用时给一个空态提示,引导用户去配置向导启用
1897
+ // 四个都未启用时给一个空态提示,引导用户去配置向导启用
1791
1898
  var emptyHint = document.getElementById('dash-no-agent-hint');
1792
- if (emptyHint) emptyHint.style.display = (!claudeOn && !cursorOn && !codexOn) ? '' : 'none';
1899
+ if (emptyHint) emptyHint.style.display = (!claudeOn && !cursorOn && !codexOn && !cccOn) ? '' : 'none';
1793
1900
 
1794
1901
  document.getElementById('cfg-ANTHROPIC_MODEL').textContent = (c.claude && c.claude.model) || '(留空)';
1795
1902
  document.getElementById('cfg-ANTHROPIC_SUBAGENT_MODEL').textContent = (c.claude && c.claude.subagentModel) || '(留空)';
@@ -1810,6 +1917,10 @@ function updateDashboardUI() {
1810
1917
  document.getElementById('cfg-CODEX_ALTERNATIVE_MODEL').textContent = (c.codex && c.codex.alternativeModel) || '(留空)';
1811
1918
  document.getElementById('cfg-CODEX_EFFORT').textContent = (c.codex && c.codex.effort) || '(留空)';
1812
1919
  document.getElementById('cfg-CODEX_FAST_MODE').textContent = c.codex && c.codex.fastMode === true ? '已启用' : '已禁用';
1920
+ document.getElementById('cfg-CCC_API_KEY').textContent = (c.ccc && c.ccc.DEEPSEEK_API_KEY) ? '***已设置***' : '(留空)';
1921
+ document.getElementById('cfg-CCC_BASE_URL').textContent = (c.ccc && c.ccc.DEEPSEEK_BASE_URL) || '(留空)';
1922
+ document.getElementById('cfg-CCC_MODEL').textContent = (c.ccc && c.ccc.model) || '(留空)';
1923
+ document.getElementById('cfg-CCC_ALTERNATIVE_MODEL').textContent = (c.ccc && c.ccc.alternativeModel) || '(留空)';
1813
1924
  }
1814
1925
 
1815
1926
  function pollStatus() {
@@ -1865,7 +1976,7 @@ function editSection(section) {
1865
1976
  else if (section === 'chromeDevtools') fields = CHROME_DEVTOOLS_FIELDS;
1866
1977
  else fields = AGENT_FIELDS[section] || [];
1867
1978
 
1868
- var titleMap = { feishu: '飞书', webUi: 'Web UI', chromeDevtools: 'Chrome CDP', claude: 'Claude Agent', cursor: 'Cursor Agent', codex: 'Codex Agent' };
1979
+ var titleMap = { feishu: '飞书', webUi: 'Web UI', chromeDevtools: 'Chrome CDP', claude: 'Claude Agent', cursor: 'Cursor Agent', codex: 'Codex Agent', ccc: 'CCC Agent' };
1869
1980
  document.getElementById('edit-modal-title').textContent = '编辑 ' + (titleMap[section] || section);
1870
1981
 
1871
1982
  document.getElementById('edit-modal-effect').textContent = configEffectHint(section);
@@ -1883,7 +1994,9 @@ function editSection(section) {
1883
1994
  'CHATCCC_CURSOR_AVATAR_BATTERY_MODE': '头像电池电量',
1884
1995
  'CHATCCC_CURSOR_ON_DEMAND_MONTHLY_BUDGET': '每月On demand use预算',
1885
1996
  'CHATCCC_CODEX_PATH': 'CLI 路径', 'CHATCCC_CODEX_MODEL': '模型', 'CHATCCC_CODEX_ALTERNATIVE_MODEL': '备选模型', 'CHATCCC_CODEX_EFFORT': 'Effort',
1886
- 'CHATCCC_CODEX_FAST_MODE': 'Fast 模式'
1997
+ 'CHATCCC_CODEX_FAST_MODE': 'Fast 模式',
1998
+ 'CHATCCC_CCC_API_KEY': 'API Key', 'CHATCCC_CCC_BASE_URL': 'Base URL',
1999
+ 'CHATCCC_CCC_MODEL': '模型', 'CHATCCC_CCC_ALTERNATIVE_MODEL': '备选模型'
1887
2000
  };
1888
2001
  var hintMap = {
1889
2002
  'CHATCCC_WEB_UI_OPEN_ON_START': '关闭后可继续手动访问 http://localhost:<端口>/;/restart、/update 和 Web UI 重启无论此项为何值都不会自动打开。',
@@ -1922,12 +2035,17 @@ function editSection(section) {
1922
2035
  else if (key === 'CHATCCC_CURSOR_ALTERNATIVE_MODEL') val = state.config.cursor.alternativeModel || '';
1923
2036
  else if (key === 'CHATCCC_CURSOR_AVATAR_BATTERY_MODE') val = state.config.cursor.avatarBatteryMode || 'apiPercent';
1924
2037
  else if (key === 'CHATCCC_CURSOR_ON_DEMAND_MONTHLY_BUDGET') val = (state.config.cursor.onDemandMonthlyBudget != null) ? String(state.config.cursor.onDemandMonthlyBudget) : '1000';
1925
- } else if (section === 'codex' && state.config.codex) {
2038
+ } else if (section === 'codex' && state.config.codex) {
1926
2039
  if (key === 'CHATCCC_CODEX_PATH') val = state.config.codex.path || state.config.codex.command || '';
1927
2040
  else if (key === 'CHATCCC_CODEX_MODEL') val = state.config.codex.model || '';
1928
2041
  else if (key === 'CHATCCC_CODEX_ALTERNATIVE_MODEL') val = state.config.codex.alternativeModel || '';
1929
2042
  else if (key === 'CHATCCC_CODEX_EFFORT') val = state.config.codex.effort || '';
1930
2043
  else if (key === 'CHATCCC_CODEX_FAST_MODE') val = state.config.codex.fastMode === true ? 'true' : 'false';
2044
+ } else if (section === 'ccc' && state.config.ccc) {
2045
+ if (key === 'CHATCCC_CCC_API_KEY') val = state.config.ccc.DEEPSEEK_API_KEY || '';
2046
+ else if (key === 'CHATCCC_CCC_BASE_URL') val = state.config.ccc.DEEPSEEK_BASE_URL || '';
2047
+ else if (key === 'CHATCCC_CCC_MODEL') val = state.config.ccc.model || '';
2048
+ else if (key === 'CHATCCC_CCC_ALTERNATIVE_MODEL') val = state.config.ccc.alternativeModel || '';
1931
2049
  }
1932
2050
  }
1933
2051
  var isSecret = key.includes('SECRET') || key.includes('API_KEY');