chatccc 0.2.233 → 0.2.235

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
@@ -18,6 +18,13 @@ import {
18
18
  createInternalRestartEnv,
19
19
  openWebUiInDefaultBrowser,
20
20
  } from "./startup-lifecycle.ts";
21
+ import {
22
+ getClaudeSdkInstalledVersion,
23
+ getLastInstallProgress,
24
+ installClaudeSdk,
25
+ isClaudeSdkInstalled,
26
+ isInstallRunning,
27
+ } from "./claude-sdk-installer.ts";
21
28
 
22
29
  const __dirname = dirname(fileURLToPath(import.meta.url));
23
30
  const PROJECT_ROOT = join(__dirname, "..");
@@ -629,6 +636,31 @@ async function handleForgetIlink(_req: IncomingMessage, res: ServerResponse): Pr
629
636
  }
630
637
  }
631
638
 
639
+ async function handleClaudeSdkStatus(_req: IncomingMessage, res: ServerResponse): Promise<void> {
640
+ jsonReply(res, 200, {
641
+ installed: isClaudeSdkInstalled(),
642
+ version: getClaudeSdkInstalledVersion(),
643
+ running: isInstallRunning(),
644
+ progress: getLastInstallProgress(),
645
+ });
646
+ }
647
+
648
+ async function handleClaudeSdkInstall(_req: IncomingMessage, res: ServerResponse): Promise<void> {
649
+ if (isInstallRunning()) {
650
+ jsonReply(res, 200, { ok: true, alreadyRunning: true });
651
+ return;
652
+ }
653
+ try {
654
+ // 后台执行,不阻塞响应;前端通过 /api/claude-sdk/status 轮询进度
655
+ installClaudeSdk().catch((err: unknown) => {
656
+ console.error(`[WEB-UI] Claude SDK 安装失败: ${(err as Error).message}`);
657
+ });
658
+ jsonReply(res, 200, { ok: true });
659
+ } catch (err) {
660
+ jsonReply(res, 500, { ok: false, error: (err as Error).message });
661
+ }
662
+ }
663
+
632
664
  // ---------------------------------------------------------------------------
633
665
  // HTML page (embedded template)
634
666
  // ---------------------------------------------------------------------------
@@ -854,6 +886,52 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
854
886
 
855
887
  <div class="agent-cards">
856
888
 
889
+ <!-- CCC Agent 卡片(置顶:ChatCCC 内置 Agent,开箱即用) -->
890
+ <div class="agent-card" id="agent-card-ccc">
891
+ <div class="agent-card-header">
892
+ <input type="checkbox" class="agent-toggle" id="agent-enable-ccc" onchange="onAgentToggle('ccc', this.checked)">
893
+ <div class="meta">
894
+ <div class="name">CCC Agent</div>
895
+ <div class="desc">ChatCCC 内置 Agent<br>OpenAI 兼容 API(不限于 DeepSeek)</div>
896
+ </div>
897
+ </div>
898
+ <label class="agent-default-row">
899
+ <input type="checkbox" id="agent-default-ccc" onchange="onDefaultAgentToggle('ccc', this.checked)">
900
+ 设为默认 Agent
901
+ </label>
902
+ <fieldset class="agent-body" id="agent-body-ccc" disabled>
903
+ <div class="form-group">
904
+ <label>API Key</label>
905
+ <input type="password" id="field-CHATCCC_CCC_API_KEY" placeholder="OpenAI 兼容 API Key(如 DeepSeek)">
906
+ </div>
907
+ <div class="form-group">
908
+ <label>Base URL</label>
909
+ <input type="text" id="field-CHATCCC_CCC_BASE_URL" placeholder="https://api.deepseek.com/v1(可填任意 OpenAI 兼容端点)">
910
+ </div>
911
+ <div class="form-group">
912
+ <label>模型</label>
913
+ <input type="text" id="field-CHATCCC_CCC_MODEL" placeholder="deepseek-v4-pro">
914
+ </div>
915
+ <div class="form-group">
916
+ <label>备选模型(选填)</label>
917
+ <input type="text" id="field-CHATCCC_CCC_ALTERNATIVE_MODEL" placeholder="加入 /model 列表,便于会话内切换">
918
+ </div>
919
+ <div class="form-group">
920
+ <label>Effort(推理强度,选填)</label>
921
+ <select id="field-CHATCCC_CCC_EFFORT">
922
+ <option value="">(留空/默认,服务端 medium)</option>
923
+ <option value="none">none - 直接作答,最省 token</option>
924
+ <option value="minimal">minimal</option>
925
+ <option value="low">low</option>
926
+ <option value="medium">medium</option>
927
+ <option value="high">high</option>
928
+ <option value="xhigh">xhigh</option>
929
+ <option value="max">max - 最强推理</option>
930
+ </select>
931
+ </div>
932
+ </fieldset>
933
+ </div>
934
+
857
935
  <!-- Claude 卡片 -->
858
936
  <div class="agent-card" id="agent-card-claude">
859
937
  <div class="agent-card-header">
@@ -888,6 +966,18 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
888
966
  <label>Base URL(选填)</label>
889
967
  <input type="text" id="field-CHATCCC_ANTHROPIC_BASE_URL" placeholder="留空使用默认端点">
890
968
  </div>
969
+ <div class="form-group" style="border-top:1px solid #e2e8f0;padding-top:12px;margin-top:4px">
970
+ <label>Claude Code 引擎(Agent SDK)</label>
971
+ <div id="claude-engine-status" style="font-size:13px;color:#64748b;margin-bottom:8px">检测中...</div>
972
+ <div id="claude-engine-progress-wrap" style="display:none;margin-bottom:8px">
973
+ <div style="background:#e2e8f0;border-radius:6px;height:8px;overflow:hidden">
974
+ <div id="claude-engine-progress-bar" style="width:0%;height:100%;background:#3b82f6;transition:width .3s"></div>
975
+ </div>
976
+ <div id="claude-engine-progress-text" style="font-size:12px;color:#64748b;margin-top:4px"></div>
977
+ </div>
978
+ <button class="btn btn-outline" id="claude-engine-install-btn" onclick="installClaudeEngine()">安装引擎(约 220MB)</button>
979
+ <div class="hint" style="margin-top:6px">ChatCCC 通过 Claude Agent SDK 调用 Claude Code;SDK 引擎按需下载到本机(仅启用 Claude Code 时需要),安装期间请保持网络畅通。</div>
980
+ </div>
891
981
  </fieldset>
892
982
  </div>
893
983
 
@@ -974,52 +1064,6 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
974
1064
  </fieldset>
975
1065
  </div>
976
1066
 
977
- <!-- CCC Agent 卡片 -->
978
- <div class="agent-card" id="agent-card-ccc">
979
- <div class="agent-card-header">
980
- <input type="checkbox" class="agent-toggle" id="agent-enable-ccc" onchange="onAgentToggle('ccc', this.checked)">
981
- <div class="meta">
982
- <div class="name">CCC Agent</div>
983
- <div class="desc">ChatCCC 内置 Agent<br>使用 DeepSeek 兼容 API</div>
984
- </div>
985
- </div>
986
- <label class="agent-default-row">
987
- <input type="checkbox" id="agent-default-ccc" onchange="onDefaultAgentToggle('ccc', this.checked)">
988
- 设为默认 Agent
989
- </label>
990
- <fieldset class="agent-body" id="agent-body-ccc" disabled>
991
- <div class="form-group">
992
- <label>API Key</label>
993
- <input type="password" id="field-CHATCCC_CCC_API_KEY" placeholder="DeepSeek 兼容 API Key">
994
- </div>
995
- <div class="form-group">
996
- <label>Base URL</label>
997
- <input type="text" id="field-CHATCCC_CCC_BASE_URL" placeholder="https://api.deepseek.com/v1">
998
- </div>
999
- <div class="form-group">
1000
- <label>模型</label>
1001
- <input type="text" id="field-CHATCCC_CCC_MODEL" placeholder="deepseek-v4-pro">
1002
- </div>
1003
- <div class="form-group">
1004
- <label>备选模型(选填)</label>
1005
- <input type="text" id="field-CHATCCC_CCC_ALTERNATIVE_MODEL" placeholder="加入 /model 列表,便于会话内切换">
1006
- </div>
1007
- <div class="form-group">
1008
- <label>Effort(推理强度,选填)</label>
1009
- <select id="field-CHATCCC_CCC_EFFORT">
1010
- <option value="">(留空/默认,服务端 medium)</option>
1011
- <option value="none">none - 直接作答,最省 token</option>
1012
- <option value="minimal">minimal</option>
1013
- <option value="low">low</option>
1014
- <option value="medium">medium</option>
1015
- <option value="high">high</option>
1016
- <option value="xhigh">xhigh</option>
1017
- <option value="max">max - 最强推理</option>
1018
- </select>
1019
- </div>
1020
- </fieldset>
1021
- </div>
1022
-
1023
1067
  </div>
1024
1068
 
1025
1069
  <div class="btn-group" style="justify-content:space-between">
@@ -2182,8 +2226,87 @@ function validateCli(tool) {
2182
2226
  });
2183
2227
  }
2184
2228
 
2229
+ // ---- Claude Code 引擎(Agent SDK)按需安装 ----
2230
+ var claudeEnginePollTimer = null;
2231
+
2232
+ function claudeEngineEl(id) { return document.getElementById(id); }
2233
+
2234
+ function claudeEngineRenderStatus(s) {
2235
+ var el = claudeEngineEl('claude-engine-status');
2236
+ if (!el) return;
2237
+ var phase = s.phase;
2238
+ var text = s.message || '';
2239
+ var color = '#64748b';
2240
+ if (phase === 'done') color = '#16a34a';
2241
+ else if (phase === 'error') color = '#ef4444';
2242
+ else if (phase === 'downloading' || phase === 'installing') color = '#3b82f6';
2243
+ el.innerHTML = '<span style="color:' + color + '">' + (text ? text : '未知状态') + '</span>';
2244
+ if (s.error) el.innerHTML += '<br><span style="color:#ef4444;font-size:12px">' + s.error + '</span>';
2245
+ }
2246
+
2247
+ function claudeEngineRenderProgress(p) {
2248
+ var wrap = claudeEngineEl('claude-engine-progress-wrap');
2249
+ if (!wrap) return;
2250
+ if (p && (p.phase === 'downloading' || p.phase === 'installing' || p.phase === 'detecting')) {
2251
+ wrap.style.display = 'block';
2252
+ claudeEngineEl('claude-engine-progress-bar').style.width = (p.percent || 0) + '%';
2253
+ claudeEngineEl('claude-engine-progress-text').textContent = p.message || '';
2254
+ } else {
2255
+ wrap.style.display = 'none';
2256
+ }
2257
+ }
2258
+
2259
+ function claudeEngineRefreshStatus() {
2260
+ api('/api/claude-sdk/status', 'GET').then(function(s){
2261
+ if (!s || !s.phase) return;
2262
+ claudeEngineRenderStatus(s);
2263
+ claudeEngineRenderProgress(s);
2264
+ var btn = claudeEngineEl('claude-engine-install-btn');
2265
+ if (btn) {
2266
+ if (s.phase === 'done' || s.installed) {
2267
+ btn.textContent = '重新安装引擎';
2268
+ btn.disabled = false;
2269
+ } else if (s.phase === 'downloading' || s.phase === 'installing' || s.phase === 'detecting') {
2270
+ btn.textContent = '安装中…';
2271
+ btn.disabled = true;
2272
+ } else {
2273
+ btn.textContent = '安装引擎(约 220MB)';
2274
+ btn.disabled = false;
2275
+ }
2276
+ }
2277
+ if (s.phase === 'downloading' || s.phase === 'installing' || s.phase === 'detecting') {
2278
+ claudeEnginePollTimer = setTimeout(claudeEngineRefreshStatus, 600);
2279
+ } else if (claudeEnginePollTimer) {
2280
+ clearTimeout(claudeEnginePollTimer);
2281
+ claudeEnginePollTimer = null;
2282
+ }
2283
+ }).catch(function(){
2284
+ // 网络/服务异常时停止轮询,避免无限重试
2285
+ if (claudeEnginePollTimer) { clearTimeout(claudeEnginePollTimer); claudeEnginePollTimer = null; }
2286
+ });
2287
+ }
2288
+
2289
+ function installClaudeEngine() {
2290
+ var btn = claudeEngineEl('claude-engine-install-btn');
2291
+ if (btn) btn.disabled = true;
2292
+ api('/api/claude-sdk/install', 'POST').then(function(r){
2293
+ if (r.ok) {
2294
+ claudeEngineRefreshStatus();
2295
+ } else {
2296
+ claudeEngineRenderStatus({ phase: 'error', message: '启动安装失败', error: r.error || '' });
2297
+ if (btn) btn.disabled = false;
2298
+ }
2299
+ }).catch(function(e){
2300
+ claudeEngineRenderStatus({ phase: 'error', message: '请求失败', error: String(e) });
2301
+ if (btn) btn.disabled = false;
2302
+ });
2303
+ }
2304
+
2185
2305
  // ---- Start ----
2186
2306
  init();
2307
+
2308
+ // 进入 dashboard/向导后初始化 Claude 引擎状态(页面元素此时已存在)
2309
+ setTimeout(claudeEngineRefreshStatus, 300);
2187
2310
  </script>
2188
2311
  </body>
2189
2312
  </html>`;
@@ -2209,6 +2332,8 @@ async function handleRequest(req: IncomingMessage, res: ServerResponse): Promise
2209
2332
  if (url === "/api/restart" && method === "POST") return handleRestartService(req, res);
2210
2333
  if (url === "/api/validate" && method === "POST") return handleValidate(req, res);
2211
2334
  if (url === "/api/ilink/forget" && method === "POST") return handleForgetIlink(req, res);
2335
+ if (url === "/api/claude-sdk/status" && method === "GET") return handleClaudeSdkStatus(req, res);
2336
+ if (url === "/api/claude-sdk/install" && method === "POST") return handleClaudeSdkInstall(req, res);
2212
2337
 
2213
2338
  if (method === "GET" && (pathname === "/agent-team" || pathname === "/agent-team/")) {
2214
2339
  res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });