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.
- package/README.md +92 -308
- package/README.zh.md +94 -318
- package/cli/local-bridge.js +227 -0
- package/cli/update.js +162 -0
- package/cli.js +357 -112
- package/lib/cli-sessions.js +16 -6
- package/lib/win-tray.js +119 -0
- package/package.json +2 -2
- package/web-ui/app.js +4 -0
- package/web-ui/logic.sessions.mjs +17 -1
- package/web-ui/modules/app.computed.session.mjs +51 -315
- package/web-ui/modules/app.methods.agents.mjs +19 -0
- package/web-ui/modules/app.methods.claude-config.mjs +71 -2
- package/web-ui/modules/app.methods.codex-config.mjs +20 -0
- package/web-ui/modules/app.methods.providers.mjs +53 -7
- package/web-ui/modules/app.methods.session-actions.mjs +1 -1
- package/web-ui/modules/app.methods.session-browser.mjs +29 -1
- package/web-ui/modules/app.methods.startup-claude.mjs +4 -0
- package/web-ui/modules/i18n.dict.mjs +21 -3
- package/web-ui/partials/index/layout-header.html +1 -2
- package/web-ui/partials/index/modal-config-template-agents.html +12 -1
- package/web-ui/partials/index/modals-basic.html +14 -3
- package/web-ui/partials/index/panel-config-claude.html +57 -85
- package/web-ui/partials/index/panel-config-codex.html +60 -226
- package/web-ui/partials/index/panel-dashboard.html +0 -33
- package/web-ui/partials/index/panel-docs.html +21 -53
- package/web-ui/partials/index/panel-sessions.html +37 -20
- package/web-ui/partials/index/panel-trash.html +33 -38
- package/web-ui/partials/index/panel-usage.html +71 -304
- package/web-ui/styles/controls-forms.css +11 -0
- package/web-ui/styles/docs-panel.css +57 -83
- package/web-ui/styles/layout-shell.css +26 -24
- package/web-ui/styles/modals-core.css +33 -0
- package/web-ui/styles/responsive.css +5 -67
- package/web-ui/styles/sessions-list.css +274 -8
- package/web-ui/styles/sessions-toolbar-trash.css +185 -15
- 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
11152
|
-
|
|
11153
|
-
|
|
11154
|
-
|
|
11155
|
-
|
|
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
|
-
|
|
11215
|
-
|
|
11216
|
-
|
|
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
|
-
|
|
13886
|
-
|
|
13887
|
-
|
|
13888
|
-
|
|
13889
|
-
|
|
13890
|
-
|
|
13891
|
-
|
|
13892
|
-
|
|
13893
|
-
|
|
13894
|
-
|
|
13895
|
-
|
|
13896
|
-
|
|
13897
|
-
|
|
13898
|
-
|
|
13899
|
-
|
|
13900
|
-
|
|
13901
|
-
|
|
13902
|
-
|
|
13903
|
-
|
|
13904
|
-
|
|
13905
|
-
|
|
13906
|
-
|
|
13907
|
-
|
|
13908
|
-
|
|
13909
|
-
|
|
13910
|
-
|
|
13911
|
-
|
|
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
|
-
|
|
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;
|