codexmate 0.0.31 → 0.0.33

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.
Files changed (37) hide show
  1. package/README.md +92 -308
  2. package/README.zh.md +94 -318
  3. package/cli/local-bridge.js +227 -0
  4. package/cli/update.js +162 -0
  5. package/cli.js +357 -112
  6. package/lib/cli-sessions.js +16 -6
  7. package/lib/win-tray.js +119 -0
  8. package/package.json +2 -2
  9. package/web-ui/app.js +4 -0
  10. package/web-ui/logic.sessions.mjs +17 -1
  11. package/web-ui/modules/app.computed.session.mjs +51 -315
  12. package/web-ui/modules/app.methods.agents.mjs +19 -0
  13. package/web-ui/modules/app.methods.claude-config.mjs +71 -2
  14. package/web-ui/modules/app.methods.codex-config.mjs +20 -0
  15. package/web-ui/modules/app.methods.providers.mjs +53 -7
  16. package/web-ui/modules/app.methods.session-actions.mjs +1 -1
  17. package/web-ui/modules/app.methods.session-browser.mjs +29 -1
  18. package/web-ui/modules/app.methods.startup-claude.mjs +4 -0
  19. package/web-ui/modules/i18n.dict.mjs +21 -3
  20. package/web-ui/partials/index/layout-header.html +1 -2
  21. package/web-ui/partials/index/modal-config-template-agents.html +12 -1
  22. package/web-ui/partials/index/modals-basic.html +14 -3
  23. package/web-ui/partials/index/panel-config-claude.html +57 -85
  24. package/web-ui/partials/index/panel-config-codex.html +60 -226
  25. package/web-ui/partials/index/panel-dashboard.html +0 -33
  26. package/web-ui/partials/index/panel-docs.html +21 -53
  27. package/web-ui/partials/index/panel-sessions.html +37 -20
  28. package/web-ui/partials/index/panel-trash.html +33 -38
  29. package/web-ui/partials/index/panel-usage.html +71 -304
  30. package/web-ui/styles/controls-forms.css +11 -0
  31. package/web-ui/styles/docs-panel.css +57 -83
  32. package/web-ui/styles/layout-shell.css +26 -24
  33. package/web-ui/styles/modals-core.css +33 -0
  34. package/web-ui/styles/responsive.css +5 -67
  35. package/web-ui/styles/sessions-list.css +274 -8
  36. package/web-ui/styles/sessions-toolbar-trash.css +185 -15
  37. package/web-ui/styles/sessions-usage.css +336 -788
package/cli.js CHANGED
@@ -148,6 +148,7 @@ const {
148
148
  deleteCodexSkills
149
149
  } = require('./cli/skills');
150
150
  const { cmdImportSkills: cmdImportSkillsFromUrl } = require('./cli/import-skills-url');
151
+ const { cmdToolUpdate } = require('./cli/update');
151
152
  const {
152
153
  getFileStatSafe,
153
154
  isBootstrapLikeText,
@@ -192,6 +193,8 @@ const BUILTIN_PROXY_SETTINGS_FILE = path.join(CONFIG_DIR, 'codexmate-proxy.json'
192
193
  const BUILTIN_CLAUDE_PROXY_SETTINGS_FILE = path.join(CONFIG_DIR, 'codexmate-claude-proxy.json');
193
194
  const OPENAI_BRIDGE_SETTINGS_FILE = path.join(CONFIG_DIR, 'codexmate-openai-bridge.json');
194
195
  const LOCAL_BRIDGE_SETTINGS_FILE = path.join(CONFIG_DIR, 'codexmate-local-bridge.json');
196
+ const CLAUDE_LOCAL_BRIDGE_SETTINGS_FILE = path.join(CONFIG_DIR, 'codexmate-claude-local-bridge.json');
197
+ const CLAUDE_LOCAL_PROVIDERS_FILE = path.join(CONFIG_DIR, 'codexmate-claude-bridge.json');
195
198
  const CODEX_SESSIONS_DIR = path.join(CONFIG_DIR, 'sessions');
196
199
  const SESSION_TRASH_DIR = path.join(CONFIG_DIR, 'codexmate-session-trash');
197
200
  const SESSION_TRASH_FILES_DIR = path.join(SESSION_TRASH_DIR, 'files');
@@ -338,6 +341,7 @@ const openaiBridgeHandler = createOpenaiBridgeHttpHandler({
338
341
  const localBridgeHandler = createLocalBridgeHttpHandler({
339
342
  readConfigFn: readConfig,
340
343
  openaiBridgeFile: OPENAI_BRIDGE_SETTINGS_FILE,
344
+ claudeProvidersFile: CLAUDE_LOCAL_PROVIDERS_FILE,
341
345
  localBridgeSettingsFile: LOCAL_BRIDGE_SETTINGS_FILE,
342
346
  expectedToken: typeof process.env.CODEXMATE_HTTP_TOKEN === 'string' ? process.env.CODEXMATE_HTTP_TOKEN.trim() : '',
343
347
  maxBodySize: MAX_API_BODY_SIZE,
@@ -568,7 +572,7 @@ function releaseRunPortIfNeeded(port, host, deps = {}) {
568
572
  try {
569
573
  killProcess(pid, 'SIGKILL');
570
574
  released = true;
571
- } catch (_) {}
575
+ } catch (_) { }
572
576
  }
573
577
  }
574
578
 
@@ -723,7 +727,7 @@ function readModels() {
723
727
  if (fs.existsSync(MODELS_FILE)) {
724
728
  try {
725
729
  return JSON.parse(fs.readFileSync(MODELS_FILE, 'utf-8'));
726
- } catch (e) {}
730
+ } catch (e) { }
727
731
  }
728
732
  return [...DEFAULT_MODELS];
729
733
  }
@@ -736,7 +740,7 @@ function readCurrentModels() {
736
740
  if (fs.existsSync(CURRENT_MODELS_FILE)) {
737
741
  try {
738
742
  return JSON.parse(fs.readFileSync(CURRENT_MODELS_FILE, 'utf-8'));
739
- } catch (e) {}
743
+ } catch (e) { }
740
744
  }
741
745
  return {};
742
746
  }
@@ -751,7 +755,7 @@ function updateAuthJson(apiKey) {
751
755
  try {
752
756
  const content = fs.readFileSync(AUTH_FILE, 'utf-8');
753
757
  if (content.trim()) authData = JSON.parse(content);
754
- } catch (e) {}
758
+ } catch (e) { }
755
759
  }
756
760
  authData['OPENAI_API_KEY'] = apiKey;
757
761
  fs.writeFileSync(AUTH_FILE, JSON.stringify(authData, null, 2), 'utf-8');
@@ -856,7 +860,7 @@ function appendLegacySegmentsVariant(provider, segments) {
856
860
  configurable: true,
857
861
  writable: true
858
862
  });
859
- } catch (e) {}
863
+ } catch (e) { }
860
864
  }
861
865
 
862
866
  function setLegacySegmentsMetadata(provider, segments) {
@@ -1870,7 +1874,7 @@ function getConfigTemplate(params = {}) {
1870
1874
  if (raw && raw.trim()) {
1871
1875
  content = raw;
1872
1876
  }
1873
- } catch (e) {}
1877
+ } catch (e) { }
1874
1878
  }
1875
1879
  if (
1876
1880
  params.modelAutoCompactTokenLimit !== undefined
@@ -2202,6 +2206,30 @@ function updateProviderInConfig(params = {}) {
2202
2206
  }
2203
2207
  }
2204
2208
 
2209
+ function getProviderKey(params = {}) {
2210
+ const name = typeof params.name === 'string' ? params.name.trim() : '';
2211
+ if (!name) return { error: '名称不能为空' };
2212
+ try {
2213
+ const config = readConfig();
2214
+ const provider = config.model_providers && config.model_providers[name];
2215
+ if (!provider) return { error: '提供商不存在' };
2216
+
2217
+ const bridge = typeof provider.codexmate_bridge === 'string' ? provider.codexmate_bridge.trim() : '';
2218
+ const isTransform = bridge === 'openai' || String(provider.base_url || '').includes('/bridge/openai/');
2219
+ if (isTransform) {
2220
+ const settings = readOpenaiBridgeSettings(OPENAI_BRIDGE_SETTINGS_FILE);
2221
+ const entry = settings.providers ? settings.providers[name] : null;
2222
+ const key = entry && typeof entry.apiKey === 'string' ? entry.apiKey : '';
2223
+ return { key };
2224
+ }
2225
+
2226
+ const key = typeof provider.preferred_auth_method === 'string' ? provider.preferred_auth_method : '';
2227
+ return { key };
2228
+ } catch (e) {
2229
+ return { error: e.message || '读取失败' };
2230
+ }
2231
+ }
2232
+
2205
2233
  function deleteProviderFromConfig(params = {}) {
2206
2234
  const name = typeof params.name === 'string' ? params.name.trim() : '';
2207
2235
  if (!name) return { error: '名称不能为空' };
@@ -2468,7 +2496,7 @@ function readJsonlRecords(filePath) {
2468
2496
  if (!trimmed) continue;
2469
2497
  try {
2470
2498
  records.push(JSON.parse(trimmed));
2471
- } catch (e) {}
2499
+ } catch (e) { }
2472
2500
  }
2473
2501
  return records;
2474
2502
  }
@@ -2490,7 +2518,7 @@ function getFileHeadText(filePath, maxBytes = SESSION_SUMMARY_READ_BYTES) {
2490
2518
  return '';
2491
2519
  } finally {
2492
2520
  if (fd !== undefined) {
2493
- try { fs.closeSync(fd); } catch (e) {}
2521
+ try { fs.closeSync(fd); } catch (e) { }
2494
2522
  }
2495
2523
  }
2496
2524
  }
@@ -2518,7 +2546,7 @@ function getFileTailText(filePath, maxBytes = SESSION_USAGE_TAIL_READ_BYTES) {
2518
2546
  return '';
2519
2547
  } finally {
2520
2548
  if (fd !== undefined) {
2521
- try { fs.closeSync(fd); } catch (e) {}
2549
+ try { fs.closeSync(fd); } catch (e) { }
2522
2550
  }
2523
2551
  }
2524
2552
  }
@@ -2535,7 +2563,7 @@ function parseJsonlContent(content) {
2535
2563
  if (!trimmed) continue;
2536
2564
  try {
2537
2565
  records.push(JSON.parse(trimmed));
2538
- } catch (e) {}
2566
+ } catch (e) { }
2539
2567
  }
2540
2568
  return records;
2541
2569
  }
@@ -2783,10 +2811,10 @@ async function countConversationMessagesInFile(filePath, source) {
2783
2811
  return safeCount;
2784
2812
  } finally {
2785
2813
  if (rl) {
2786
- try { rl.close(); } catch (e) {}
2814
+ try { rl.close(); } catch (e) { }
2787
2815
  }
2788
2816
  if (stream && !stream.destroyed && stream.destroy) {
2789
- try { stream.destroy(); } catch (e) {}
2817
+ try { stream.destroy(); } catch (e) { }
2790
2818
  }
2791
2819
  }
2792
2820
  }
@@ -2866,10 +2894,10 @@ async function extractSessionDetailPreviewFromFile(filePath, source, messageLimi
2866
2894
  return extractSessionDetailPreviewFromRecords(readJsonlRecords(filePath), source, safeMessageLimit);
2867
2895
  } finally {
2868
2896
  if (rl) {
2869
- try { rl.close(); } catch (e) {}
2897
+ try { rl.close(); } catch (e) { }
2870
2898
  }
2871
2899
  if (stream && !stream.destroyed && stream.destroy) {
2872
- try { stream.destroy(); } catch (e) {}
2900
+ try { stream.destroy(); } catch (e) { }
2873
2901
  }
2874
2902
  }
2875
2903
  }
@@ -3302,10 +3330,10 @@ async function scanSessionContentForQuery(session, tokens, options = {}) {
3302
3330
  return scanSessionContentForQueryInRecords(readJsonlRecords(filePath), session.source, state);
3303
3331
  } finally {
3304
3332
  if (rl) {
3305
- try { rl.close(); } catch (e) {}
3333
+ try { rl.close(); } catch (e) { }
3306
3334
  }
3307
3335
  if (stream && !stream.destroyed && stream.destroy) {
3308
- try { stream.destroy(); } catch (e) {}
3336
+ try { stream.destroy(); } catch (e) { }
3309
3337
  }
3310
3338
  }
3311
3339
  }
@@ -3413,7 +3441,7 @@ function collectRecentJsonlFiles(rootDir, options = {}) {
3413
3441
  try {
3414
3442
  const stat = fs.statSync(fullPath);
3415
3443
  filesMeta.push({ filePath: fullPath, mtimeMs: stat.mtimeMs || 0 });
3416
- } catch (e) {}
3444
+ } catch (e) { }
3417
3445
 
3418
3446
  if (scanned >= maxFilesScanned) {
3419
3447
  break;
@@ -3465,7 +3493,7 @@ function collectRecentJsonlFilesFromRoots(rootDirs, options = {}) {
3465
3493
  try {
3466
3494
  const stat = fs.statSync(fullPath);
3467
3495
  filesMeta.push({ filePath: fullPath, mtimeMs: stat.mtimeMs || 0 });
3468
- } catch (_) {}
3496
+ } catch (_) { }
3469
3497
  if (scanned >= maxFilesScanned) {
3470
3498
  break;
3471
3499
  }
@@ -3688,7 +3716,7 @@ function readTotalTokensFromUsage(usage) {
3688
3716
  const inputTokens = readNonNegativeInteger(usage.input_tokens ?? usage.inputTokens);
3689
3717
  const cachedInputTokens = readNonNegativeInteger(
3690
3718
  usage.cached_input_tokens ?? usage.cachedInputTokens
3691
- ?? usage.cache_read_input_tokens ?? usage.cacheReadInputTokens
3719
+ ?? usage.cache_read_input_tokens ?? usage.cacheReadInputTokens
3692
3720
  );
3693
3721
  const cacheCreationInputTokens = readNonNegativeInteger(
3694
3722
  usage.cache_creation_input_tokens ?? usage.cacheCreationInputTokens
@@ -3708,7 +3736,7 @@ function readUsageTotalsFromUsage(usage) {
3708
3736
  const inputTokens = readNonNegativeInteger(usage.input_tokens ?? usage.inputTokens);
3709
3737
  const cachedInputTokens = readNonNegativeInteger(
3710
3738
  usage.cached_input_tokens ?? usage.cachedInputTokens
3711
- ?? usage.cache_read_input_tokens ?? usage.cacheReadInputTokens
3739
+ ?? usage.cache_read_input_tokens ?? usage.cacheReadInputTokens
3712
3740
  );
3713
3741
  const cacheCreationInputTokens = readNonNegativeInteger(
3714
3742
  usage.cache_creation_input_tokens ?? usage.cacheCreationInputTokens
@@ -4943,7 +4971,7 @@ function listGeminiSessions(limit, options = {}) {
4943
4971
  try {
4944
4972
  const stat = fs.statSync(fullPath);
4945
4973
  filesMeta.push({ filePath: fullPath, mtimeMs: stat.mtimeMs || 0 });
4946
- } catch (_) {}
4974
+ } catch (_) { }
4947
4975
  scanned += 1;
4948
4976
  if (scanned >= maxFilesScanned) {
4949
4977
  break;
@@ -5549,6 +5577,156 @@ function getLocalBridgeExcludedProviders() {
5549
5577
  return { excludedProviders: settings.excludedProviders };
5550
5578
  }
5551
5579
 
5580
+ // ============================================================================
5581
+ // Claude Local Bridge
5582
+ // ============================================================================
5583
+
5584
+ function readClaudeLocalBridgeSettings() {
5585
+ const defaults = { enabled: false, lastActiveBaseUrl: '', lastModel: '', excludedProviders: [] };
5586
+ try {
5587
+ if (!fs.existsSync(CLAUDE_LOCAL_BRIDGE_SETTINGS_FILE)) return defaults;
5588
+ const raw = JSON.parse(fs.readFileSync(CLAUDE_LOCAL_BRIDGE_SETTINGS_FILE, 'utf-8'));
5589
+ return {
5590
+ enabled: !!raw.enabled,
5591
+ lastActiveBaseUrl: typeof raw.lastActiveBaseUrl === 'string' ? raw.lastActiveBaseUrl.trim() : '',
5592
+ lastModel: typeof raw.lastModel === 'string' ? raw.lastModel.trim() : '',
5593
+ excludedProviders: Array.isArray(raw.excludedProviders) ? raw.excludedProviders.filter(p => typeof p === 'string') : []
5594
+ };
5595
+ } catch (e) {
5596
+ return defaults;
5597
+ }
5598
+ }
5599
+
5600
+ function writeClaudeLocalBridgeSettings(settings) {
5601
+ fs.writeFileSync(CLAUDE_LOCAL_BRIDGE_SETTINGS_FILE, JSON.stringify(settings, null, 2), 'utf-8');
5602
+ }
5603
+
5604
+ function readClaudeLocalProvidersFile() {
5605
+ try {
5606
+ if (!fs.existsSync(CLAUDE_LOCAL_PROVIDERS_FILE)) return { providers: {} };
5607
+ const raw = JSON.parse(fs.readFileSync(CLAUDE_LOCAL_PROVIDERS_FILE, 'utf-8'));
5608
+ return { providers: (raw && typeof raw.providers === 'object') ? raw.providers : {} };
5609
+ } catch (e) {
5610
+ return { providers: {} };
5611
+ }
5612
+ }
5613
+
5614
+ function writeClaudeLocalProvidersFile(data) {
5615
+ ensureDir(CONFIG_DIR);
5616
+ fs.writeFileSync(CLAUDE_LOCAL_PROVIDERS_FILE, JSON.stringify(data, null, 2), 'utf-8');
5617
+ }
5618
+
5619
+ function syncClaudeProvidersToBridgeFile() {
5620
+ // Sync Claude configs from localStorage-equivalent (browser) to bridge file
5621
+ // Called when providers are added/updated/deleted via web UI
5622
+ const existing = readClaudeLocalProvidersFile();
5623
+ const providers = existing.providers || {};
5624
+ // Preserve existing entries, update from params
5625
+ return { providers };
5626
+ }
5627
+
5628
+ function toggleClaudeLocalBridge(params = {}) {
5629
+ const enable = !!params.enable;
5630
+ const settings = readClaudeLocalBridgeSettings();
5631
+
5632
+ try {
5633
+ const readResult = readJsonObjectFromFile(CLAUDE_SETTINGS_FILE, {});
5634
+ if (!readResult.ok) {
5635
+ return { success: false, error: readResult.error || '读取 Claude settings.json 失败' };
5636
+ }
5637
+ const currentSettings = readResult.data || {};
5638
+ const currentEnv = (currentSettings.env && typeof currentSettings.env === 'object' && !Array.isArray(currentSettings.env))
5639
+ ? currentSettings.env : {};
5640
+ const currentBaseUrl = typeof currentEnv.ANTHROPIC_BASE_URL === 'string' ? currentEnv.ANTHROPIC_BASE_URL.trim() : '';
5641
+
5642
+ if (enable) {
5643
+ if (currentBaseUrl.includes('/bridge/claude-local/')) {
5644
+ return { success: true, enabled: true, notice: '已启用 Claude 本地负载均衡' };
5645
+ }
5646
+ settings.lastActiveBaseUrl = currentBaseUrl;
5647
+ settings.lastModel = typeof currentEnv.ANTHROPIC_MODEL === 'string' ? currentEnv.ANTHROPIC_MODEL.trim() : '';
5648
+ settings.enabled = true;
5649
+ writeClaudeLocalBridgeSettings(settings);
5650
+
5651
+ const localPort = resolveWebPort();
5652
+ const localBaseUrl = `http://127.0.0.1:${localPort}/bridge/claude-local/v1`;
5653
+ const nextEnv = { ...currentEnv, ANTHROPIC_BASE_URL: localBaseUrl };
5654
+ const nextSettings = { ...currentSettings, env: nextEnv };
5655
+ ensureDir(CLAUDE_DIR);
5656
+ backupFileIfNeededOnce(CLAUDE_SETTINGS_FILE);
5657
+ writeJsonAtomic(CLAUDE_SETTINGS_FILE, nextSettings);
5658
+ return { success: true, enabled: true, previousBaseUrl: currentBaseUrl };
5659
+ } else {
5660
+ if (!currentBaseUrl.includes('/bridge/claude-local/')) {
5661
+ settings.enabled = false;
5662
+ writeClaudeLocalBridgeSettings(settings);
5663
+ return { success: true, enabled: false, notice: 'Claude 本地负载均衡未启用' };
5664
+ }
5665
+ const restoreBaseUrl = settings.lastActiveBaseUrl || '';
5666
+ if (!restoreBaseUrl) {
5667
+ settings.enabled = false;
5668
+ writeClaudeLocalBridgeSettings(settings);
5669
+ return { success: true, enabled: false, notice: '已关闭 Claude 本地负载均衡(无历史配置可恢复)' };
5670
+ }
5671
+ const nextEnv = { ...currentEnv, ANTHROPIC_BASE_URL: restoreBaseUrl };
5672
+ if (settings.lastModel) {
5673
+ nextEnv.ANTHROPIC_MODEL = settings.lastModel;
5674
+ }
5675
+ const nextSettings = { ...currentSettings, env: nextEnv };
5676
+ ensureDir(CLAUDE_DIR);
5677
+ backupFileIfNeededOnce(CLAUDE_SETTINGS_FILE);
5678
+ writeJsonAtomic(CLAUDE_SETTINGS_FILE, nextSettings);
5679
+ settings.enabled = false;
5680
+ writeClaudeLocalBridgeSettings(settings);
5681
+ return { success: true, enabled: false, restoredBaseUrl: restoreBaseUrl, restoredModel: settings.lastModel };
5682
+ }
5683
+ } catch (e) {
5684
+ return { error: e && e.message ? e.message : '操作失败' };
5685
+ }
5686
+ }
5687
+
5688
+ function getClaudeLocalBridgeStatus() {
5689
+ const settings = readClaudeLocalBridgeSettings();
5690
+ let currentBaseUrl = '';
5691
+ try {
5692
+ const readResult = readJsonObjectFromFile(CLAUDE_SETTINGS_FILE, {});
5693
+ if (readResult.ok && readResult.data && readResult.data.env) {
5694
+ currentBaseUrl = typeof readResult.data.env.ANTHROPIC_BASE_URL === 'string' ? readResult.data.env.ANTHROPIC_BASE_URL.trim() : '';
5695
+ }
5696
+ } catch (e) { /* ignore */ }
5697
+ const providersData = readClaudeLocalProvidersFile();
5698
+ const providerNames = Object.keys(providersData.providers || {});
5699
+ return {
5700
+ enabled: settings.enabled,
5701
+ active: currentBaseUrl.includes('/bridge/claude-local/'),
5702
+ excludedProviders: settings.excludedProviders,
5703
+ lastActiveBaseUrl: settings.lastActiveBaseUrl,
5704
+ lastModel: settings.lastModel,
5705
+ providers: providerNames
5706
+ };
5707
+ }
5708
+
5709
+ function setClaudeLocalBridgeExcludedProviders(params = {}) {
5710
+ const names = Array.isArray(params.names) ? params.names.filter(n => typeof n === 'string' && n.trim()) : [];
5711
+ const settings = readClaudeLocalBridgeSettings();
5712
+ settings.excludedProviders = names;
5713
+ writeClaudeLocalBridgeSettings(settings);
5714
+ return { success: true, excludedProviders: names };
5715
+ }
5716
+
5717
+ function getClaudeLocalBridgeExcludedProviders() {
5718
+ const settings = readClaudeLocalBridgeSettings();
5719
+ return { excludedProviders: settings.excludedProviders };
5720
+ }
5721
+
5722
+ function syncClaudeBridgeProviders(params = {}) {
5723
+ const providers = (params.providers && typeof params.providers === 'object') ? params.providers : {};
5724
+ const existing = readClaudeLocalProvidersFile();
5725
+ const excluded = existing.excludedProviders || [];
5726
+ writeClaudeLocalProvidersFile({ providers, excludedProviders: excluded });
5727
+ return { success: true, count: Object.keys(providers).length };
5728
+ }
5729
+
5552
5730
  function removeClaudeSessionIndexEntry(indexPath, sessionFilePath, sessionId) {
5553
5731
  if (!indexPath || !fs.existsSync(indexPath)) {
5554
5732
  return { removed: false, entry: null };
@@ -5617,7 +5795,7 @@ function moveFileSync(sourcePath, targetPath) {
5617
5795
  } catch (error) {
5618
5796
  try {
5619
5797
  fs.unlinkSync(targetPath);
5620
- } catch (_) {}
5798
+ } catch (_) { }
5621
5799
  throw error;
5622
5800
  }
5623
5801
  }
@@ -5791,7 +5969,7 @@ function purgeExpiredSessionTrashEntries(retentionDays) {
5791
5969
  if (deletedAtMs > 0 && deletedAtMs < cutoffMs) {
5792
5970
  const trashFilePath = resolveSessionTrashFilePath(entry);
5793
5971
  if (trashFilePath) {
5794
- try { fs.unlinkSync(trashFilePath); } catch (_) {}
5972
+ try { fs.unlinkSync(trashFilePath); } catch (_) { }
5795
5973
  }
5796
5974
  purgedCount += 1;
5797
5975
  } else {
@@ -6112,12 +6290,12 @@ async function restoreSessionTrashItem(params = {}) {
6112
6290
  try {
6113
6291
  moveFileSync(targetFilePath, trashFilePath);
6114
6292
  rollbackSucceeded = true;
6115
- } catch (_) {}
6293
+ } catch (_) { }
6116
6294
  }
6117
6295
  if (rollbackSucceeded && entry.source === 'claude' && claudeIndexPath && fs.existsSync(claudeIndexPath)) {
6118
6296
  try {
6119
6297
  removeClaudeSessionIndexEntry(claudeIndexPath, targetFilePath, entry.sessionId);
6120
- } catch (_) {}
6298
+ } catch (_) { }
6121
6299
  }
6122
6300
  return { error: `恢复会话失败: ${e.message}` };
6123
6301
  }
@@ -6264,7 +6442,7 @@ async function trashSessionData(params = {}) {
6264
6442
  try {
6265
6443
  moveFileSync(trashFilePath, filePath);
6266
6444
  rollbackSucceeded = true;
6267
- } catch (_) {}
6445
+ } catch (_) { }
6268
6446
  }
6269
6447
  if (rollbackSucceeded && source === 'claude' && claudeIndexPath && removedClaudeIndexEntry) {
6270
6448
  try {
@@ -6282,10 +6460,10 @@ async function trashSessionData(params = {}) {
6282
6460
  trashId,
6283
6461
  trashFileName
6284
6462
  });
6285
- } catch (_) {}
6463
+ } catch (_) { }
6286
6464
  }
6287
6465
  if (!rollbackSucceeded && fs.existsSync(trashFilePath)) {
6288
- try { fs.unlinkSync(trashFilePath); } catch (_) {}
6466
+ try { fs.unlinkSync(trashFilePath); } catch (_) { }
6289
6467
  }
6290
6468
  return { error: `移入回收站失败: ${e.message}` };
6291
6469
  }
@@ -6455,7 +6633,7 @@ async function cloneCodexSession(params = {}) {
6455
6633
  maxTimestampMs = ts;
6456
6634
  }
6457
6635
  }
6458
- } catch (e) {}
6636
+ } catch (e) { }
6459
6637
  }
6460
6638
 
6461
6639
  const sessionsDir = getCodexSessionsDir();
@@ -6514,7 +6692,7 @@ async function cloneCodexSession(params = {}) {
6514
6692
  }
6515
6693
  try {
6516
6694
  fs.utimesSync(newFilePath, cloneTime, cloneTime);
6517
- } catch (e) {}
6695
+ } catch (e) { }
6518
6696
 
6519
6697
  invalidateSessionListCache();
6520
6698
 
@@ -6926,10 +7104,10 @@ async function extractMessagesFromFile(filePath, source, options = {}) {
6926
7104
  return extractMessagesFromRecords(fallbackRecords, source, { maxMessages });
6927
7105
  } finally {
6928
7106
  if (rl) {
6929
- try { rl.close(); } catch (e) {}
7107
+ try { rl.close(); } catch (e) { }
6930
7108
  }
6931
7109
  if (stream && !stream.destroyed && stream.destroy) {
6932
- try { stream.destroy(); } catch (e) {}
7110
+ try { stream.destroy(); } catch (e) { }
6933
7111
  }
6934
7112
  }
6935
7113
 
@@ -7557,7 +7735,7 @@ async function importDerivedSessionToNative(params = {}) {
7557
7735
  } catch (e) {
7558
7736
  try {
7559
7737
  if (fs.existsSync(tmpNativePath)) fs.unlinkSync(tmpNativePath);
7560
- } catch (_) {}
7738
+ } catch (_) { }
7561
7739
  try {
7562
7740
  if (previousNative) {
7563
7741
  ensureDir(path.dirname(resolvedNativePath));
@@ -7565,7 +7743,7 @@ async function importDerivedSessionToNative(params = {}) {
7565
7743
  } else if (!hadNativeBefore && fs.existsSync(resolvedNativePath)) {
7566
7744
  fs.unlinkSync(resolvedNativePath);
7567
7745
  }
7568
- } catch (_) {}
7746
+ } catch (_) { }
7569
7747
  try {
7570
7748
  if (previousMeta) {
7571
7749
  ensureDir(path.dirname(targetMetaPath));
@@ -7573,7 +7751,7 @@ async function importDerivedSessionToNative(params = {}) {
7573
7751
  } else if (targetMetaPath && fs.existsSync(targetMetaPath)) {
7574
7752
  fs.unlinkSync(targetMetaPath);
7575
7753
  }
7576
- } catch (_) {}
7754
+ } catch (_) { }
7577
7755
  try {
7578
7756
  if (indexPath) {
7579
7757
  if (previousIndex) {
@@ -7583,7 +7761,7 @@ async function importDerivedSessionToNative(params = {}) {
7583
7761
  fs.unlinkSync(indexPath);
7584
7762
  }
7585
7763
  }
7586
- } catch (_) {}
7764
+ } catch (_) { }
7587
7765
  return { error: `Import to native failed: ${e.message}`, errorCode: 'IMPORT_DERIVED_SESSION_FAILED', reason: e.message };
7588
7766
  }
7589
7767
 
@@ -9042,6 +9220,42 @@ function readClaudeSettingsInfo() {
9042
9220
  };
9043
9221
  }
9044
9222
 
9223
+ function readClaudeSettingsRaw() {
9224
+ if (!fs.existsSync(CLAUDE_SETTINGS_FILE)) {
9225
+ return { content: '{}', exists: false, targetPath: CLAUDE_SETTINGS_FILE };
9226
+ }
9227
+ try {
9228
+ const raw = fs.readFileSync(CLAUDE_SETTINGS_FILE, 'utf-8');
9229
+ return { content: raw || '{}', exists: true, targetPath: CLAUDE_SETTINGS_FILE };
9230
+ } catch (e) {
9231
+ return { error: e.message || '读取 settings.json 失败' };
9232
+ }
9233
+ }
9234
+
9235
+ function applyClaudeSettingsRaw(params = {}) {
9236
+ const content = typeof params.content === 'string' ? params.content : '';
9237
+ if (!content.trim()) {
9238
+ return { error: '内容不能为空' };
9239
+ }
9240
+ let parsed;
9241
+ try {
9242
+ parsed = JSON.parse(content);
9243
+ } catch (e) {
9244
+ return { error: `JSON 解析失败: ${e.message}` };
9245
+ }
9246
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
9247
+ return { error: 'JSON 内容必须是一个对象' };
9248
+ }
9249
+ try {
9250
+ ensureDir(CLAUDE_DIR);
9251
+ backupFileIfNeededOnce(CLAUDE_SETTINGS_FILE);
9252
+ writeJsonAtomic(CLAUDE_SETTINGS_FILE, parsed);
9253
+ return { success: true, targetPath: CLAUDE_SETTINGS_FILE };
9254
+ } catch (e) {
9255
+ return { error: e.message || '写入 settings.json 失败' };
9256
+ }
9257
+ }
9258
+
9045
9259
  // API: 打包 Claude 配置目录(系统 zip 可用则使用,否则回退 zip-lib)
9046
9260
  async function prepareClaudeDirDownload() {
9047
9261
  return await prepareDirectoryDownload(CLAUDE_DIR, {
@@ -9410,7 +9624,7 @@ function resolveExportOutputPath(outputPath, defaultFileName) {
9410
9624
  if (stat.isDirectory()) {
9411
9625
  return path.join(resolved, defaultFileName);
9412
9626
  }
9413
- } catch (e) {}
9627
+ } catch (e) { }
9414
9628
  }
9415
9629
 
9416
9630
  return resolved;
@@ -9631,7 +9845,7 @@ function watchPathsForRestart(targets, onChange) {
9631
9845
  watcherEntries.delete(watchKey);
9632
9846
  try {
9633
9847
  entry.watcher.close();
9634
- } catch (_) {}
9848
+ } catch (_) { }
9635
9849
  };
9636
9850
 
9637
9851
  const listDirectoryTree = (rootDir) => {
@@ -10095,12 +10309,12 @@ function streamZipDownloadResponse(res, filePath, options = {}) {
10095
10309
  if (deleteAfterDownload && fs.existsSync(filePath)) {
10096
10310
  try {
10097
10311
  fs.unlinkSync(filePath);
10098
- } catch (_) {}
10312
+ } catch (_) { }
10099
10313
  }
10100
10314
  if (onAfterComplete) {
10101
10315
  try {
10102
10316
  onAfterComplete();
10103
- } catch (_) {}
10317
+ } catch (_) { }
10104
10318
  }
10105
10319
  };
10106
10320
  stream.on('error', () => {
@@ -10110,7 +10324,7 @@ function streamZipDownloadResponse(res, filePath, options = {}) {
10110
10324
  } else {
10111
10325
  try {
10112
10326
  res.destroy();
10113
- } catch (_) {}
10327
+ } catch (_) { }
10114
10328
  }
10115
10329
  finalize();
10116
10330
  });
@@ -10252,7 +10466,7 @@ function createWebServer({ htmlPath, assetsDir, webDir, host, port, openBrowser
10252
10466
  });
10253
10467
  req.on('error', () => finish(false));
10254
10468
  req.setTimeout(1000, () => {
10255
- try { req.destroy(); } catch (_) {}
10469
+ try { req.destroy(); } catch (_) { }
10256
10470
  finish(false);
10257
10471
  });
10258
10472
  req.end(payload, 'utf-8');
@@ -10280,7 +10494,7 @@ function createWebServer({ htmlPath, assetsDir, webDir, host, port, openBrowser
10280
10494
  });
10281
10495
  req.on('error', () => finish(false));
10282
10496
  req.setTimeout(1000, () => {
10283
- try { req.destroy(); } catch (_) {}
10497
+ try { req.destroy(); } catch (_) { }
10284
10498
  finish(false);
10285
10499
  });
10286
10500
  req.end();
@@ -10355,7 +10569,7 @@ function createWebServer({ htmlPath, assetsDir, webDir, host, port, openBrowser
10355
10569
  if (res.headersSent) {
10356
10570
  try {
10357
10571
  res.destroy(error);
10358
- } catch (_) {}
10572
+ } catch (_) { }
10359
10573
  return;
10360
10574
  }
10361
10575
  res.writeHead(500, { 'Content-Type': 'text/plain; charset=utf-8' });
@@ -10475,7 +10689,16 @@ function createWebServer({ htmlPath, assetsDir, webDir, host, port, openBrowser
10475
10689
  'model_auto_compact_token_limit',
10476
10690
  budgetReadOptions
10477
10691
  );
10692
+ const pkgVersion = (() => {
10693
+ try {
10694
+ const pkg = require('./package.json');
10695
+ return pkg && pkg.version ? pkg.version : '';
10696
+ } catch (_) {
10697
+ return '';
10698
+ }
10699
+ })();
10478
10700
  result = {
10701
+ version: pkgVersion,
10479
10702
  provider: config.model_provider || '未设置',
10480
10703
  model: config.model || '未设置',
10481
10704
  currentModels: readCurrentModels(),
@@ -10532,7 +10755,7 @@ function createWebServer({ htmlPath, assetsDir, webDir, host, port, openBrowser
10532
10755
  result = { error: 'Refusing to access private network baseUrl from non-loopback request' };
10533
10756
  break;
10534
10757
  }
10535
- } catch (_) {}
10758
+ } catch (_) { }
10536
10759
  }
10537
10760
  const res = await fetchModelsFromBaseUrl(baseUrl, apiKey);
10538
10761
  if (res.error) {
@@ -10560,6 +10783,9 @@ function createWebServer({ htmlPath, assetsDir, webDir, host, port, openBrowser
10560
10783
  case 'update-provider':
10561
10784
  result = updateProviderInConfig(params || {});
10562
10785
  break;
10786
+ case 'get-provider-key':
10787
+ result = getProviderKey(params || {});
10788
+ break;
10563
10789
  case 'delete-provider':
10564
10790
  result = deleteProviderFromConfig(params || {});
10565
10791
  break;
@@ -10600,7 +10826,7 @@ function createWebServer({ htmlPath, assetsDir, webDir, host, port, openBrowser
10600
10826
  result = applyClaudeMdFile(params || {});
10601
10827
  if (result && !result.error) {
10602
10828
  const mdTarget = (params && params.targetPath) ? String(params.targetPath) : 'CLAUDE.md';
10603
- notifyWebhook('claude-md-edit', 'CLAUDE.md modified: ' + mdTarget, { targetPath: mdTarget }).catch(function () {});
10829
+ notifyWebhook('claude-md-edit', 'CLAUDE.md modified: ' + mdTarget, { targetPath: mdTarget }).catch(function () { });
10604
10830
  }
10605
10831
  break;
10606
10832
  case 'preview-agents-diff':
@@ -10675,6 +10901,12 @@ function createWebServer({ htmlPath, assetsDir, webDir, host, port, openBrowser
10675
10901
  case 'get-claude-settings':
10676
10902
  result = readClaudeSettingsInfo();
10677
10903
  break;
10904
+ case 'get-claude-settings-raw':
10905
+ result = readClaudeSettingsRaw();
10906
+ break;
10907
+ case 'apply-claude-settings-raw':
10908
+ result = applyClaudeSettingsRaw(params || {});
10909
+ break;
10678
10910
  case 'apply-claude-config':
10679
10911
  result = applyToClaudeSettings(params.config);
10680
10912
  if (result && !result.error) {
@@ -10683,7 +10915,7 @@ function createWebServer({ htmlPath, assetsDir, webDir, host, port, openBrowser
10683
10915
  const summary = cfgFrom
10684
10916
  ? ('Provider switched: ' + cfgFrom + ' -> ' + cfgName)
10685
10917
  : ('Provider applied: ' + cfgName);
10686
- notifyWebhook('provider-switch', summary, { name: cfgName, previousName: cfgFrom }).catch(function () {});
10918
+ notifyWebhook('provider-switch', summary, { name: cfgName, previousName: cfgFrom }).catch(function () { });
10687
10919
  }
10688
10920
  break;
10689
10921
  case 'get-webhook-config':
@@ -10929,6 +11161,21 @@ function createWebServer({ htmlPath, assetsDir, webDir, host, port, openBrowser
10929
11161
  case 'local-bridge-get-excluded':
10930
11162
  result = getLocalBridgeExcludedProviders();
10931
11163
  break;
11164
+ case 'claude-local-bridge-toggle':
11165
+ result = toggleClaudeLocalBridge(params || {});
11166
+ break;
11167
+ case 'claude-local-bridge-status':
11168
+ result = getClaudeLocalBridgeStatus();
11169
+ break;
11170
+ case 'claude-local-bridge-set-excluded':
11171
+ result = setClaudeLocalBridgeExcludedProviders(params || {});
11172
+ break;
11173
+ case 'claude-local-bridge-get-excluded':
11174
+ result = getClaudeLocalBridgeExcludedProviders();
11175
+ break;
11176
+ case 'claude-local-bridge-sync-providers':
11177
+ result = syncClaudeBridgeProviders(params || {});
11178
+ break;
10932
11179
  case 'workflow-list':
10933
11180
  result = listWorkflowDefinitions();
10934
11181
  break;
@@ -11016,7 +11263,7 @@ function createWebServer({ htmlPath, assetsDir, webDir, host, port, openBrowser
11016
11263
  }
11017
11264
  const taskId = typeof params.taskId === 'string' && params.taskId.trim() ? params.taskId.trim() : createTaskId();
11018
11265
  const runId = createTaskRunId();
11019
- runTaskPlanInternal(plan, { taskId, runId }).catch(() => {});
11266
+ runTaskPlanInternal(plan, { taskId, runId }).catch(() => { });
11020
11267
  result = {
11021
11268
  ok: true,
11022
11269
  started: true,
@@ -11148,11 +11395,11 @@ function createWebServer({ htmlPath, assetsDir, webDir, host, port, openBrowser
11148
11395
  ? 'application/javascript; charset=utf-8'
11149
11396
  : ext === '.html'
11150
11397
  ? 'text/html; charset=utf-8'
11151
- : ext === '.css'
11152
- ? 'text/css; charset=utf-8'
11153
- : ext === '.json'
11154
- ? 'application/json; charset=utf-8'
11155
- : 'application/octet-stream';
11398
+ : ext === '.css'
11399
+ ? 'text/css; charset=utf-8'
11400
+ : ext === '.json'
11401
+ ? 'application/json; charset=utf-8'
11402
+ : 'application/octet-stream';
11156
11403
  res.writeHead(200, { 'Content-Type': mime });
11157
11404
  fs.createReadStream(filePath).pipe(res);
11158
11405
  } else if (requestPath.startsWith('/download/')) {
@@ -11211,9 +11458,9 @@ function createWebServer({ htmlPath, assetsDir, webDir, host, port, openBrowser
11211
11458
  ? 'application/javascript; charset=utf-8'
11212
11459
  : ext === '.html'
11213
11460
  ? 'text/html; charset=utf-8'
11214
- : ext === '.json'
11215
- ? 'application/json; charset=utf-8'
11216
- : 'application/octet-stream';
11461
+ : ext === '.json'
11462
+ ? 'application/json; charset=utf-8'
11463
+ : 'application/octet-stream';
11217
11464
  res.writeHead(200, { 'Content-Type': mime });
11218
11465
  fs.createReadStream(filePath).pipe(res);
11219
11466
  } else {
@@ -11274,7 +11521,7 @@ function createWebServer({ htmlPath, assetsDir, webDir, host, port, openBrowser
11274
11521
  if (done) return;
11275
11522
  done = true;
11276
11523
  for (const socket of connections) {
11277
- try { socket.destroy(); } catch (_) {}
11524
+ try { socket.destroy(); } catch (_) { }
11278
11525
  }
11279
11526
  connections.clear();
11280
11527
  resolve();
@@ -11390,7 +11637,7 @@ function cmdStart(options = {}) {
11390
11637
 
11391
11638
  // 禁止前端变更侦测与自动重启:避免终端输出噪音与访问时短暂 Connection Refused。
11392
11639
  // 如需热重启,请由开发者自行使用外部 watcher / nodemon 等工具。
11393
- const stopWatch = () => {};
11640
+ const stopWatch = () => { };
11394
11641
 
11395
11642
  const handleExit = () => {
11396
11643
  stopWatch();
@@ -13374,7 +13621,7 @@ function listWorkflowRunRecords(limit = 20) {
13374
13621
  if (parsed.length >= max) {
13375
13622
  break;
13376
13623
  }
13377
- } catch (_) {}
13624
+ } catch (_) { }
13378
13625
  }
13379
13626
  return parsed;
13380
13627
  }
@@ -13473,7 +13720,7 @@ async function runWorkflowById(workflowId, input = {}, options = {}) {
13473
13720
  };
13474
13721
  try {
13475
13722
  appendWorkflowRunRecord(record);
13476
- } catch (_) {}
13723
+ } catch (_) { }
13477
13724
 
13478
13725
  return {
13479
13726
  success: execution.success === true,
@@ -13671,10 +13918,10 @@ function withTaskQueueLock(fn) {
13671
13918
  if (ageMs > 5000) {
13672
13919
  try {
13673
13920
  fs.unlinkSync(lockPath);
13674
- } catch (_) {}
13921
+ } catch (_) { }
13675
13922
  lockFd = fs.openSync(lockPath, 'wx', 0o600);
13676
13923
  }
13677
- } catch (_) {}
13924
+ } catch (_) { }
13678
13925
  }
13679
13926
  }
13680
13927
  if (!lockFd) {
@@ -13685,10 +13932,10 @@ function withTaskQueueLock(fn) {
13685
13932
  } finally {
13686
13933
  try {
13687
13934
  fs.closeSync(lockFd);
13688
- } catch (_) {}
13935
+ } catch (_) { }
13689
13936
  try {
13690
13937
  fs.unlinkSync(lockPath);
13691
- } catch (_) {}
13938
+ } catch (_) { }
13692
13939
  }
13693
13940
  }
13694
13941
 
@@ -13857,7 +14104,7 @@ function writeTaskRunArtifacts(detail = {}) {
13857
14104
  const combined = `${runLogText}${nodeLogText ? `\n\n${nodeLogText}` : ''}`.trim();
13858
14105
  try {
13859
14106
  fs.writeFileSync(path.join(dir, 'logs.txt'), combined, { encoding: 'utf-8', mode: 0o600 });
13860
- } catch (_) {}
14107
+ } catch (_) { }
13861
14108
  }
13862
14109
 
13863
14110
  async function notifyAutomationOnTaskRun(detail = {}) {
@@ -13882,34 +14129,34 @@ function startAutomationScheduler() {
13882
14129
  }
13883
14130
  tickInFlight = true;
13884
14131
  try {
13885
- const cfg = readAutomationConfig(AUTOMATION_CONFIG_FILE, { env: process.env });
13886
- if (!cfg.ok || !cfg.config) {
13887
- return;
13888
- }
13889
- const schedules = Array.isArray(cfg.config.schedules) ? cfg.config.schedules : [];
13890
- if (schedules.length === 0) {
13891
- return;
13892
- }
13893
- const now = new Date();
13894
- const tickKey = now.toISOString().slice(0, 16);
13895
- for (const schedule of schedules) {
13896
- if (!schedule || schedule.enabled === false) continue;
13897
- if (!schedule.id || !schedule.cron) continue;
13898
- if (!isCronMatch(schedule.cron, now)) continue;
13899
- if (lastTicks.get(schedule.id) === tickKey) continue;
13900
- lastTicks.set(schedule.id, tickKey);
13901
- const action = schedule.action && typeof schedule.action === 'object' ? schedule.action : {};
13902
- const actionType = typeof action.type === 'string' ? action.type.trim().toLowerCase() : '';
13903
- if (actionType !== 'task.queue.add') continue;
13904
- const taskPayload = action.task && typeof action.task === 'object' ? action.task : {};
13905
- try {
13906
- const enqueue = addTaskToQueue(taskPayload);
13907
- if (enqueue && enqueue.error) continue;
13908
- if (action.startQueue === true) {
13909
- await startTaskQueueProcessing({ taskId: '', detach: true });
13910
- }
13911
- } catch (_) {}
13912
- }
14132
+ const cfg = readAutomationConfig(AUTOMATION_CONFIG_FILE, { env: process.env });
14133
+ if (!cfg.ok || !cfg.config) {
14134
+ return;
14135
+ }
14136
+ const schedules = Array.isArray(cfg.config.schedules) ? cfg.config.schedules : [];
14137
+ if (schedules.length === 0) {
14138
+ return;
14139
+ }
14140
+ const now = new Date();
14141
+ const tickKey = now.toISOString().slice(0, 16);
14142
+ for (const schedule of schedules) {
14143
+ if (!schedule || schedule.enabled === false) continue;
14144
+ if (!schedule.id || !schedule.cron) continue;
14145
+ if (!isCronMatch(schedule.cron, now)) continue;
14146
+ if (lastTicks.get(schedule.id) === tickKey) continue;
14147
+ lastTicks.set(schedule.id, tickKey);
14148
+ const action = schedule.action && typeof schedule.action === 'object' ? schedule.action : {};
14149
+ const actionType = typeof action.type === 'string' ? action.type.trim().toLowerCase() : '';
14150
+ if (actionType !== 'task.queue.add') continue;
14151
+ const taskPayload = action.task && typeof action.task === 'object' ? action.task : {};
14152
+ try {
14153
+ const enqueue = addTaskToQueue(taskPayload);
14154
+ if (enqueue && enqueue.error) continue;
14155
+ if (action.startQueue === true) {
14156
+ await startTaskQueueProcessing({ taskId: '', detach: true });
14157
+ }
14158
+ } catch (_) { }
14159
+ }
13913
14160
  } finally {
13914
14161
  tickInFlight = false;
13915
14162
  }
@@ -14071,7 +14318,7 @@ async function runCodexExecTaskNode(node, context = {}) {
14071
14318
  if (!sessionId) {
14072
14319
  sessionId = findCodexSessionId(payload);
14073
14320
  }
14074
- } catch (_) {}
14321
+ } catch (_) { }
14075
14322
  };
14076
14323
  const captureLines = (bucket, text, stream) => {
14077
14324
  const currentPartial = stream === 'stderr' ? stderrPartial : stdoutPartial;
@@ -14106,7 +14353,7 @@ async function runCodexExecTaskNode(node, context = {}) {
14106
14353
  context.registerAbort(() => {
14107
14354
  try {
14108
14355
  child.kill('SIGTERM');
14109
- } catch (_) {}
14356
+ } catch (_) { }
14110
14357
  });
14111
14358
  }
14112
14359
  child.stdout.on('data', (chunk) => {
@@ -14131,7 +14378,7 @@ async function runCodexExecTaskNode(node, context = {}) {
14131
14378
  } else {
14132
14379
  fs.rmdirSync(tempDir, { recursive: true });
14133
14380
  }
14134
- } catch (_) {}
14381
+ } catch (_) { }
14135
14382
  const success = exit.code === 0;
14136
14383
  const errorMessage = success
14137
14384
  ? ''
@@ -14239,7 +14486,7 @@ async function runTaskPlanInternal(plan, options = {}) {
14239
14486
  abort() {
14240
14487
  try {
14241
14488
  controller.abort();
14242
- } catch (_) {}
14489
+ } catch (_) { }
14243
14490
  }
14244
14491
  });
14245
14492
  if (options.queueItem) {
@@ -14253,7 +14500,7 @@ async function runTaskPlanInternal(plan, options = {}) {
14253
14500
  updatedAt: toIsoTime(Date.now()),
14254
14501
  plan
14255
14502
  });
14256
- if (queued && queued.error) {}
14503
+ if (queued && queued.error) { }
14257
14504
  }
14258
14505
  try {
14259
14506
  const run = await executeTaskPlan(plan, {
@@ -14289,7 +14536,7 @@ async function runTaskPlanInternal(plan, options = {}) {
14289
14536
  updatedAt: toIsoTime(Date.now()),
14290
14537
  plan
14291
14538
  });
14292
- if (queued && queued.error) {}
14539
+ if (queued && queued.error) { }
14293
14540
  }
14294
14541
  }
14295
14542
  });
@@ -14304,7 +14551,7 @@ async function runTaskPlanInternal(plan, options = {}) {
14304
14551
  writeTaskRunArtifacts(detail);
14305
14552
  try {
14306
14553
  await notifyAutomationOnTaskRun(detail);
14307
- } catch (_) {}
14554
+ } catch (_) { }
14308
14555
  if (options.queueItem) {
14309
14556
  const queued = upsertTaskQueueItem({
14310
14557
  ...options.queueItem,
@@ -14318,7 +14565,7 @@ async function runTaskPlanInternal(plan, options = {}) {
14318
14565
  updatedAt: toIsoTime(Date.now()),
14319
14566
  plan
14320
14567
  });
14321
- if (queued && queued.error) {}
14568
+ if (queued && queued.error) { }
14322
14569
  }
14323
14570
  return detail;
14324
14571
  } finally {
@@ -14630,7 +14877,7 @@ function readDetachedTaskWorkerPayload(payloadPath = '') {
14630
14877
  const parsed = readJsonObjectFromFile(filePath, {});
14631
14878
  try {
14632
14879
  fs.unlinkSync(filePath);
14633
- } catch (_) {}
14880
+ } catch (_) { }
14634
14881
  if (!parsed.ok || !parsed.exists) {
14635
14882
  return { error: parsed.error || 'task worker payload not found' };
14636
14883
  }
@@ -14644,7 +14891,7 @@ function spawnDetachedTaskWorker(payload = {}) {
14644
14891
  detached: true,
14645
14892
  windowsHide: true
14646
14893
  });
14647
- child.on('error', () => {});
14894
+ child.on('error', () => { });
14648
14895
  if (typeof child.unref === 'function') {
14649
14896
  child.unref();
14650
14897
  }
@@ -14701,7 +14948,7 @@ function readTaskQueueWorkerState() {
14701
14948
  if (!isTaskWorkerProcessId(state.pid)) {
14702
14949
  try {
14703
14950
  fs.unlinkSync(TASK_QUEUE_WORKER_FILE);
14704
- } catch (_) {}
14951
+ } catch (_) { }
14705
14952
  return null;
14706
14953
  }
14707
14954
  return state;
@@ -14719,7 +14966,7 @@ function writeTaskQueueWorkerState(state = {}) {
14719
14966
  function clearTaskQueueWorkerState() {
14720
14967
  try {
14721
14968
  fs.unlinkSync(TASK_QUEUE_WORKER_FILE);
14722
- } catch (_) {}
14969
+ } catch (_) { }
14723
14970
  }
14724
14971
 
14725
14972
  function findRunningTaskRunDetailByTaskId(taskId = '') {
@@ -15395,7 +15642,7 @@ function createMcpResources() {
15395
15642
  pathFilter = parsed.searchParams.get('pathFilter') || '';
15396
15643
  roleFilter = parsed.searchParams.get('roleFilter') || '';
15397
15644
  timeRangePreset = parsed.searchParams.get('timeRangePreset') || '';
15398
- } catch (_) {}
15645
+ } catch (_) { }
15399
15646
  const normalizedSource = normalizeMcpSource(source);
15400
15647
  if (normalizedSource === null) {
15401
15648
  return {
@@ -15455,7 +15702,7 @@ function createMcpResources() {
15455
15702
  limit = parsedLimit;
15456
15703
  }
15457
15704
  }
15458
- } catch (_) {}
15705
+ } catch (_) { }
15459
15706
  const payload = {
15460
15707
  runs: listWorkflowRunRecords(limit),
15461
15708
  limit: Number.isFinite(limit) ? Math.max(1, Math.floor(limit)) : 20
@@ -15638,13 +15885,14 @@ function printMainHelp() {
15638
15885
  console.log(' codexmate add <名称> <URL> [密钥] [--bridge <openai>]');
15639
15886
  console.log(' codexmate delete <名称> 删除提供商');
15640
15887
  console.log(' codexmate claude 等同于 claude --dangerously-skip-permissions');
15641
- console.log(' codexmate claude <BaseURL> <API密钥> [模型] 写入 Claude Code 配置');
15888
+ console.log(' codexmate claude <BaseURL> <API密钥> [模型] 写入 Claude Code 配置');
15642
15889
  console.log(' codexmate auth <list|import|switch|delete|status> 认证管理');
15643
15890
  console.log(' codexmate add-model <模型> 添加模型');
15644
15891
  console.log(' codexmate delete-model <模型> 删除模型');
15645
15892
  console.log(' codexmate workflow <list|get|validate|run|runs> MCP 工作流中心');
15646
15893
  console.log(' codexmate task <plan|run|runs|queue|retry|cancel|logs> 本地任务编排');
15647
15894
  console.log(' codexmate run [--host <HOST>] [--no-browser] 启动 Web 界面');
15895
+ console.log(' codexmate update [--check] 检查并快速更新工具');
15648
15896
  console.log(' codexmate codex [参数...] [--follow-up <文本>|--queued-follow-up <文本> 可重复] 等同于 codex --yolo');
15649
15897
  console.log(' 注: follow-up 自动排队仅支持 linux/android/netbsd/openbsd/darwin/freebsd 且 stdin 必须是 TTY,其他平台会报错');
15650
15898
  console.log(' codexmate qwen [参数...] 等同于 qwen --yolo');
@@ -15728,7 +15976,6 @@ async function main() {
15728
15976
  case 'claude': {
15729
15977
  const exitCode = await cmdClaude(args.slice(1));
15730
15978
  process.exit(exitCode);
15731
- break;
15732
15979
  }
15733
15980
  case 'add-model': cmdAddModel(args[1]); break;
15734
15981
  case 'delete-model': cmdDeleteModel(args[1]); break;
@@ -15737,19 +15984,17 @@ async function main() {
15737
15984
  case 'workflow': await cmdWorkflow(args.slice(1)); break;
15738
15985
  case 'task': await cmdTask(args.slice(1)); break;
15739
15986
  case 'run': cmdStart(parseStartOptions(args.slice(1))); break;
15987
+ case 'update': await cmdToolUpdate(args.slice(1)); break;
15740
15988
  case 'start':
15741
15989
  console.error('错误: 命令已更名为 "run",请使用: codexmate run');
15742
15990
  process.exit(1);
15743
- break;
15744
15991
  case 'codex': {
15745
15992
  const exitCode = await cmdCodex(args.slice(1));
15746
15993
  process.exit(exitCode);
15747
- break;
15748
15994
  }
15749
15995
  case 'qwen': {
15750
15996
  const exitCode = await cmdQwen(args.slice(1));
15751
15997
  process.exit(exitCode);
15752
- break;
15753
15998
  }
15754
15999
  case 'mcp': await cmdMcp(args.slice(1)); break;
15755
16000
  case 'export-session': await cmdExportSession(args.slice(1)); break;