chatccc 0.2.219 → 0.2.221
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 +36 -16
- package/config.sample.json +4 -1
- package/package.json +1 -1
- package/src/__tests__/builtin-config.test.ts +3 -0
- package/src/__tests__/builtin-file-tools.test.ts +31 -0
- package/src/__tests__/cards.test.ts +26 -6
- package/src/__tests__/config-reload.test.ts +19 -8
- package/src/__tests__/config-sample.test.ts +6 -2
- package/src/__tests__/orchestrator.test.ts +84 -5
- package/src/__tests__/sim-platform.test.ts +10 -0
- package/src/__tests__/web-ui.test.ts +40 -0
- package/src/builtin/file-tools.ts +14 -4
- package/src/cards.ts +27 -6
- package/src/config.ts +41 -10
- package/src/index.ts +13 -0
- package/src/orchestrator.ts +1553 -1488
- package/src/web-ui.ts +164 -46
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
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1757
|
-
state.
|
|
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');
|