coding-tool-x 3.6.12 → 3.7.0
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 +46 -33
- package/dist/web/assets/{Analytics-BuBlsbcI.js → Analytics-Dma_j2Rq.js} +1 -1
- package/dist/web/assets/{ConfigTemplates-DQczxuOX.js → ConfigTemplates-C49Y1PLH.js} +1 -1
- package/dist/web/assets/Home-DGGC8H4g.js +1 -0
- package/dist/web/assets/Home-ewgeIhF7.css +1 -0
- package/dist/web/assets/{PluginManager-C8gAlaps.js → PluginManager-B1ECTHyY.js} +1 -1
- package/dist/web/assets/{ProjectList-DL4JK6ci.css → ProjectList-B_L55-l9.css} +1 -1
- package/dist/web/assets/ProjectList-DyiGyT-H.js +1 -0
- package/dist/web/assets/SessionList-CH5GwwiJ.js +1 -0
- package/dist/web/assets/{SkillManager-BDbNbNkK.js → SkillManager-CRyVhjYQ.js} +1 -1
- package/dist/web/assets/{WorkspaceManager-DGGaGW2c.js → WorkspaceManager-CO9tclVt.js} +1 -1
- package/dist/web/assets/{icons-QFXzEgqM.js → icons-Dop5j7Bf.js} +1 -1
- package/dist/web/assets/index-CM26UrnJ.css +1 -0
- package/dist/web/assets/index-EQq56pRa.js +2 -0
- package/dist/web/assets/{naive-ui-kcWDeJwl.js → naive-ui-CVyvdmg1.js} +1 -1
- package/dist/web/index.html +4 -4
- package/package.json +2 -1
- package/src/commands/channels.js +85 -24
- package/src/commands/daemon.js +7 -4
- package/src/commands/doctor.js +17 -8
- package/src/commands/logs.js +48 -6
- package/src/commands/port-config.js +81 -75
- package/src/commands/proxy-control.js +52 -19
- package/src/commands/stats.js +37 -19
- package/src/commands/toggle-proxy.js +131 -53
- package/src/commands/ui.js +3 -4
- package/src/config/default.js +14 -3
- package/src/config/paths.js +99 -11
- package/src/index.js +11 -16
- package/src/server/api/commands.js +1 -1
- package/src/server/api/config-export.js +4 -2
- package/src/server/api/config-registry.js +40 -34
- package/src/server/api/config.js +2 -1
- package/src/server/api/dashboard.js +22 -6
- package/src/server/api/mcp.js +5 -3
- package/src/server/api/pi-channels.js +349 -0
- package/src/server/api/pi-config.js +164 -0
- package/src/server/api/pi-projects.js +76 -0
- package/src/server/api/pi-proxy.js +154 -0
- package/src/server/api/pi-sessions.js +334 -0
- package/src/server/api/pi-statistics.js +40 -0
- package/src/server/api/plugins.js +1 -1
- package/src/server/api/prompts.js +3 -2
- package/src/server/api/skills.js +1 -1
- package/src/server/api/workspaces.js +2 -2
- package/src/server/index.js +41 -0
- package/src/server/pi-proxy-server.js +66 -0
- package/src/server/services/agents-service.js +2 -1
- package/src/server/services/base/proxy-utils.js +1 -0
- package/src/server/services/channel-balance.js +151 -1
- package/src/server/services/channel-scheduler.js +7 -1
- package/src/server/services/commands-service.js +11 -2
- package/src/server/services/config-export-service.js +275 -18
- package/src/server/services/config-registry-service.js +20 -18
- package/src/server/services/config-sync-manager.js +115 -6
- package/src/server/services/config-sync-service.js +4 -2
- package/src/server/services/config-templates-service.js +183 -45
- package/src/server/services/mcp-service.js +217 -33
- package/src/server/services/pi-auth-providers.js +294 -0
- package/src/server/services/pi-channels.js +94 -0
- package/src/server/services/pi-config.js +287 -0
- package/src/server/services/pi-sessions.js +490 -0
- package/src/server/services/pi-settings-manager.js +571 -0
- package/src/server/services/pi-statistics-service.js +317 -0
- package/src/server/services/plugins-service.js +673 -11
- package/src/server/services/prompts-service.js +112 -7
- package/src/server/services/proxy-log-helper.js +1 -0
- package/src/server/services/proxy-runtime.js +5 -1
- package/src/server/services/skill-service.js +13 -3
- package/src/server/services/speed-test.js +9 -3
- package/src/server/services/ui-config.js +50 -12
- package/src/server/services/workspace-service.js +19 -5
- package/src/server/websocket-server.js +7 -1
- package/dist/web/assets/Home-BvAzzWnB.js +0 -1
- package/dist/web/assets/Home-CIua-b-o.css +0 -1
- package/dist/web/assets/ProjectList-nqgi6_cR.js +0 -1
- package/dist/web/assets/SessionList-D8AxMYy6.js +0 -1
- package/dist/web/assets/index-Dsh4Q5Cg.css +0 -1
- package/dist/web/assets/index-y-eEayxR.js +0 -2
- package/src/utils/cli-flags.js +0 -7
|
@@ -16,17 +16,22 @@ const HOME_DIR = resolvePreferredHomeDir(process.platform, process.env, os.homed
|
|
|
16
16
|
const PROMPTS_FILE = PATHS.prompts;
|
|
17
17
|
|
|
18
18
|
// 各平台提示词文件路径
|
|
19
|
-
const
|
|
19
|
+
const CLAUDE_CONFIG_DIR = NATIVE_PATHS.claude.dir || path.dirname(NATIVE_PATHS.claude.settings);
|
|
20
|
+
const CLAUDE_PROMPT_PATH = NATIVE_PATHS.claude.prompt || path.join(CLAUDE_CONFIG_DIR, 'CLAUDE.md');
|
|
20
21
|
const CODEX_PROMPT_PATH = path.join(HOME_DIR, '.codex', 'AGENTS.md');
|
|
21
22
|
const GEMINI_PROMPT_PATH = path.join(HOME_DIR, '.gemini', 'GEMINI.md');
|
|
22
23
|
const OPENCODE_PROMPT_PATH = path.join(NATIVE_PATHS.opencode.config, 'AGENTS.md');
|
|
24
|
+
const PI_PROMPTS_DIR = NATIVE_PATHS.pi?.prompts
|
|
25
|
+
|| path.join(NATIVE_PATHS.pi?.dir || path.join(HOME_DIR, '.omp', 'agent'), 'prompts');
|
|
26
|
+
const MANAGED_OMP_PROMPTS_DIR = path.join(PI_PROMPTS_DIR, 'coding-tool-x');
|
|
23
27
|
|
|
24
|
-
function normalizeApps(apps = {}, defaults = { claude: true, codex: true, gemini: true, opencode: false }) {
|
|
28
|
+
function normalizeApps(apps = {}, defaults = { claude: true, codex: true, gemini: true, opencode: false, pi: false }) {
|
|
25
29
|
return {
|
|
26
30
|
claude: apps.claude !== undefined ? !!apps.claude : defaults.claude,
|
|
27
31
|
codex: apps.codex !== undefined ? !!apps.codex : defaults.codex,
|
|
28
32
|
gemini: apps.gemini !== undefined ? !!apps.gemini : defaults.gemini,
|
|
29
|
-
opencode: apps.opencode !== undefined ? !!apps.opencode : defaults.opencode
|
|
33
|
+
opencode: apps.opencode !== undefined ? !!apps.opencode : defaults.opencode,
|
|
34
|
+
pi: apps.pi !== undefined ? !!apps.pi : defaults.pi
|
|
30
35
|
};
|
|
31
36
|
}
|
|
32
37
|
|
|
@@ -211,7 +216,7 @@ function initPromptsData() {
|
|
|
211
216
|
name: '当前使用',
|
|
212
217
|
description: '从现有配置导入',
|
|
213
218
|
content: existingContent,
|
|
214
|
-
apps: normalizeApps({ claude: true, codex: false, gemini: false, opencode: false }),
|
|
219
|
+
apps: normalizeApps({ claude: true, codex: false, gemini: false, opencode: false, pi: false }),
|
|
215
220
|
isBuiltin: false,
|
|
216
221
|
isImported: true,
|
|
217
222
|
createdAt: Date.now(),
|
|
@@ -234,6 +239,7 @@ function initPromptsData() {
|
|
|
234
239
|
preset.apps.codex !== normalizedApps.codex ||
|
|
235
240
|
preset.apps.gemini !== normalizedApps.gemini ||
|
|
236
241
|
preset.apps.opencode !== normalizedApps.opencode
|
|
242
|
+
|| preset.apps.pi !== normalizedApps.pi
|
|
237
243
|
) {
|
|
238
244
|
preset.apps = normalizedApps;
|
|
239
245
|
updated = true;
|
|
@@ -307,6 +313,7 @@ function savePreset(preset) {
|
|
|
307
313
|
|
|
308
314
|
const data = initPromptsData();
|
|
309
315
|
const existing = data.presets[preset.id];
|
|
316
|
+
const wasActive = data.activePresetId === preset.id;
|
|
310
317
|
|
|
311
318
|
// 内置模板不允许修改
|
|
312
319
|
if (existing && existing.isBuiltin) {
|
|
@@ -325,6 +332,13 @@ function savePreset(preset) {
|
|
|
325
332
|
data.presets[preset.id] = preset;
|
|
326
333
|
writeJsonFile(PROMPTS_FILE, data);
|
|
327
334
|
|
|
335
|
+
if (wasActive) {
|
|
336
|
+
if (existing && normalizeApps(existing.apps).pi && !preset.apps.pi) {
|
|
337
|
+
deleteOmpPromptTemplate(existing);
|
|
338
|
+
}
|
|
339
|
+
syncPresetToAllPlatforms(preset);
|
|
340
|
+
}
|
|
341
|
+
|
|
328
342
|
return preset;
|
|
329
343
|
}
|
|
330
344
|
|
|
@@ -348,6 +362,10 @@ function deletePreset(id) {
|
|
|
348
362
|
data.activePresetId = null;
|
|
349
363
|
}
|
|
350
364
|
|
|
365
|
+
if (normalizeApps(preset.apps).pi) {
|
|
366
|
+
deleteOmpPromptTemplate(preset);
|
|
367
|
+
}
|
|
368
|
+
|
|
351
369
|
delete data.presets[id];
|
|
352
370
|
writeJsonFile(PROMPTS_FILE, data);
|
|
353
371
|
|
|
@@ -360,6 +378,7 @@ function deletePreset(id) {
|
|
|
360
378
|
async function activatePreset(id) {
|
|
361
379
|
const data = initPromptsData();
|
|
362
380
|
const preset = data.presets[id];
|
|
381
|
+
const previousPreset = data.activePresetId ? data.presets[data.activePresetId] : null;
|
|
363
382
|
|
|
364
383
|
if (!preset) {
|
|
365
384
|
throw new Error(`预设 "${id}" 不存在`);
|
|
@@ -369,6 +388,10 @@ async function activatePreset(id) {
|
|
|
369
388
|
data.activePresetId = id;
|
|
370
389
|
writeJsonFile(PROMPTS_FILE, data);
|
|
371
390
|
|
|
391
|
+
if (previousPreset && previousPreset.id !== id && normalizeApps(previousPreset.apps).pi) {
|
|
392
|
+
deleteOmpPromptTemplate(previousPreset);
|
|
393
|
+
}
|
|
394
|
+
|
|
372
395
|
// 同步到各平台
|
|
373
396
|
await syncPresetToAllPlatforms(preset);
|
|
374
397
|
|
|
@@ -380,6 +403,7 @@ async function activatePreset(id) {
|
|
|
380
403
|
*/
|
|
381
404
|
async function deactivatePrompt() {
|
|
382
405
|
const data = initPromptsData();
|
|
406
|
+
const activePreset = data.activePresetId ? data.presets[data.activePresetId] : null;
|
|
383
407
|
|
|
384
408
|
// 清空激活状态
|
|
385
409
|
data.activePresetId = null;
|
|
@@ -390,7 +414,10 @@ async function deactivatePrompt() {
|
|
|
390
414
|
claude: deleteFile(CLAUDE_PROMPT_PATH),
|
|
391
415
|
codex: deleteFile(CODEX_PROMPT_PATH),
|
|
392
416
|
gemini: deleteFile(GEMINI_PROMPT_PATH),
|
|
393
|
-
opencode: deleteFile(OPENCODE_PROMPT_PATH)
|
|
417
|
+
opencode: deleteFile(OPENCODE_PROMPT_PATH),
|
|
418
|
+
pi: activePreset && normalizeApps(activePreset.apps).pi
|
|
419
|
+
? deleteOmpPromptTemplate(activePreset)
|
|
420
|
+
: false
|
|
394
421
|
};
|
|
395
422
|
|
|
396
423
|
console.log('[Prompts] Deactivated and removed prompt files:', results);
|
|
@@ -405,7 +432,7 @@ async function deactivatePrompt() {
|
|
|
405
432
|
/**
|
|
406
433
|
* 同步预设到所有已启用的平台
|
|
407
434
|
*/
|
|
408
|
-
|
|
435
|
+
function syncPresetToAllPlatforms(preset) {
|
|
409
436
|
const apps = normalizeApps(preset.apps);
|
|
410
437
|
const { content } = preset;
|
|
411
438
|
|
|
@@ -428,6 +455,43 @@ async function syncPresetToAllPlatforms(preset) {
|
|
|
428
455
|
writeTextFile(OPENCODE_PROMPT_PATH, content);
|
|
429
456
|
console.log('[Prompts] Synced to OpenCode:', OPENCODE_PROMPT_PATH);
|
|
430
457
|
}
|
|
458
|
+
|
|
459
|
+
if (apps.pi) {
|
|
460
|
+
const targetPath = writeOmpPromptTemplate(preset);
|
|
461
|
+
console.log('[Prompts] Synced to OMP:', targetPath);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
function buildOmpPromptFileName(preset) {
|
|
466
|
+
const rawId = (preset?.id || 'preset').toString().trim();
|
|
467
|
+
const safeId = rawId.replace(/[^a-zA-Z0-9_-]/g, '-').replace(/-+/g, '-').slice(0, 100) || 'preset';
|
|
468
|
+
return `${safeId}.md`;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
function getOmpPromptTemplatePath(preset) {
|
|
472
|
+
return path.join(MANAGED_OMP_PROMPTS_DIR, buildOmpPromptFileName(preset));
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
function escapeFrontmatterValue(value) {
|
|
476
|
+
return String(value || '')
|
|
477
|
+
.replace(/\\/g, '\\\\')
|
|
478
|
+
.replace(/"/g, '\\"')
|
|
479
|
+
.replace(/\r?\n/g, ' ');
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
function buildOmpPromptTemplateContent(preset) {
|
|
483
|
+
const description = escapeFrontmatterValue(preset.description || preset.name || preset.id);
|
|
484
|
+
return `---\ndescription: "${description}"\n---\n\n${preset.content || ''}`;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
function writeOmpPromptTemplate(preset) {
|
|
488
|
+
const filePath = getOmpPromptTemplatePath(preset);
|
|
489
|
+
writeTextFile(filePath, buildOmpPromptTemplateContent(preset));
|
|
490
|
+
return filePath;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
function deleteOmpPromptTemplate(preset) {
|
|
494
|
+
return deleteFile(getOmpPromptTemplatePath(preset));
|
|
431
495
|
}
|
|
432
496
|
|
|
433
497
|
/**
|
|
@@ -443,11 +507,46 @@ function readPlatformPrompt(platform) {
|
|
|
443
507
|
return readTextFile(GEMINI_PROMPT_PATH, '');
|
|
444
508
|
case 'opencode':
|
|
445
509
|
return readTextFile(OPENCODE_PROMPT_PATH, '');
|
|
510
|
+
case 'pi':
|
|
511
|
+
throw new Error('OMP 使用 prompts/ 目录中的模板文件,请通过平台状态查看模板列表');
|
|
446
512
|
default:
|
|
447
513
|
throw new Error(`无效的平台: ${platform}`);
|
|
448
514
|
}
|
|
449
515
|
}
|
|
450
516
|
|
|
517
|
+
function listOmpPromptTemplates() {
|
|
518
|
+
if (!fs.existsSync(PI_PROMPTS_DIR)) {
|
|
519
|
+
return [];
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
const results = [];
|
|
523
|
+
const walk = (dir) => {
|
|
524
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
525
|
+
if (entry.name.startsWith('.')) continue;
|
|
526
|
+
const fullPath = path.join(dir, entry.name);
|
|
527
|
+
if (entry.isDirectory()) {
|
|
528
|
+
walk(fullPath);
|
|
529
|
+
} else if (entry.isFile() && path.extname(entry.name).toLowerCase() === '.md') {
|
|
530
|
+
const relativePath = path.relative(PI_PROMPTS_DIR, fullPath);
|
|
531
|
+
results.push({
|
|
532
|
+
name: path.basename(entry.name, '.md'),
|
|
533
|
+
path: fullPath,
|
|
534
|
+
relativePath,
|
|
535
|
+
managed: relativePath.split(path.sep)[0] === 'coding-tool-x'
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
};
|
|
540
|
+
|
|
541
|
+
try {
|
|
542
|
+
walk(PI_PROMPTS_DIR);
|
|
543
|
+
} catch (err) {
|
|
544
|
+
console.error(`[Prompts] Failed to list OMP prompt templates:`, err.message);
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
return results.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
|
|
548
|
+
}
|
|
549
|
+
|
|
451
550
|
/**
|
|
452
551
|
* 获取各平台当前的提示词状态
|
|
453
552
|
*/
|
|
@@ -472,6 +571,12 @@ function getPlatformStatus() {
|
|
|
472
571
|
path: OPENCODE_PROMPT_PATH,
|
|
473
572
|
exists: fs.existsSync(OPENCODE_PROMPT_PATH),
|
|
474
573
|
content: readTextFile(OPENCODE_PROMPT_PATH, '')
|
|
574
|
+
},
|
|
575
|
+
pi: {
|
|
576
|
+
path: PI_PROMPTS_DIR,
|
|
577
|
+
managedPath: MANAGED_OMP_PROMPTS_DIR,
|
|
578
|
+
exists: fs.existsSync(PI_PROMPTS_DIR),
|
|
579
|
+
templates: listOmpPromptTemplates()
|
|
475
580
|
}
|
|
476
581
|
};
|
|
477
582
|
}
|
|
@@ -494,7 +599,7 @@ function importFromPlatform(platform, presetName) {
|
|
|
494
599
|
name: presetName || `从 ${platform} 导入`,
|
|
495
600
|
description: `从 ${platform} 导入的提示词`,
|
|
496
601
|
content,
|
|
497
|
-
apps: normalizeApps({ claude: false, codex: false, gemini: false, opencode: false }),
|
|
602
|
+
apps: normalizeApps({ claude: false, codex: false, gemini: false, opencode: false, pi: false }),
|
|
498
603
|
isBuiltin: false,
|
|
499
604
|
createdAt: Date.now(),
|
|
500
605
|
updatedAt: Date.now()
|
|
@@ -9,6 +9,7 @@ function normalizeToolSource(source = '') {
|
|
|
9
9
|
if (normalized === 'codex') return 'codex';
|
|
10
10
|
if (normalized === 'gemini') return 'gemini';
|
|
11
11
|
if (normalized === 'opencode') return 'opencode';
|
|
12
|
+
if (normalized === 'pi' || normalized === 'pi-agent') return 'pi';
|
|
12
13
|
return 'claude';
|
|
13
14
|
}
|
|
14
15
|
|
|
@@ -11,7 +11,11 @@ const PROXY_START_LOG_PATTERNS = {
|
|
|
11
11
|
],
|
|
12
12
|
codex: [/Codex proxy server started on http:\/\/127\.0\.0\.1:\d+/],
|
|
13
13
|
gemini: [/Gemini proxy server started on http:\/\/127\.0\.0\.1:\d+/],
|
|
14
|
-
opencode: [/OpenCode proxy server started on http:\/\/127\.0\.0\.1:\d+/]
|
|
14
|
+
opencode: [/OpenCode proxy server started on http:\/\/127\.0\.0\.1:\d+/],
|
|
15
|
+
pi: [
|
|
16
|
+
/OMP models\.yml provider config enabled, port: \d+/,
|
|
17
|
+
/OMP models\.yml 受管 provider 已自动启用,端口: \d+/
|
|
18
|
+
]
|
|
15
19
|
};
|
|
16
20
|
|
|
17
21
|
function getRuntimeFilePath(proxyType) {
|
|
@@ -25,7 +25,7 @@ const {
|
|
|
25
25
|
resolveInsideRoot
|
|
26
26
|
} = require('./config-artifact-paths');
|
|
27
27
|
|
|
28
|
-
const SUPPORTED_PLATFORMS = ['claude', 'codex', 'gemini', 'opencode'];
|
|
28
|
+
const SUPPORTED_PLATFORMS = ['claude', 'codex', 'gemini', 'opencode', 'pi'];
|
|
29
29
|
const SUPPORTED_REPO_PROVIDERS = ['github', 'gitlab', 'local'];
|
|
30
30
|
const DEFAULT_GITHUB_HOST = 'https://github.com';
|
|
31
31
|
const DEFAULT_GITLAB_HOST = 'https://gitlab.com';
|
|
@@ -173,12 +173,16 @@ const DEFAULT_REPOS_BY_PLATFORM = {
|
|
|
173
173
|
claude: [],
|
|
174
174
|
codex: [],
|
|
175
175
|
gemini: [],
|
|
176
|
-
opencode: []
|
|
176
|
+
opencode: [],
|
|
177
|
+
pi: []
|
|
177
178
|
};
|
|
178
179
|
|
|
180
|
+
const CLAUDE_SKILLS_DIR = NATIVE_PATHS.claude.skills
|
|
181
|
+
|| path.join(NATIVE_PATHS.claude.dir || path.dirname(NATIVE_PATHS.claude.settings), 'skills');
|
|
182
|
+
|
|
179
183
|
const PLATFORM_CONFIG = {
|
|
180
184
|
claude: {
|
|
181
|
-
installDir:
|
|
185
|
+
installDir: CLAUDE_SKILLS_DIR,
|
|
182
186
|
storageDir: PATHS.localSkills.claude,
|
|
183
187
|
reposFile: PATHS.skillRepos.claude,
|
|
184
188
|
cacheFile: PATHS.skillCaches.claude
|
|
@@ -200,6 +204,12 @@ const PLATFORM_CONFIG = {
|
|
|
200
204
|
storageDir: PATHS.localSkills.opencode,
|
|
201
205
|
reposFile: PATHS.skillRepos.opencode,
|
|
202
206
|
cacheFile: PATHS.skillCaches.opencode
|
|
207
|
+
},
|
|
208
|
+
pi: {
|
|
209
|
+
installDir: NATIVE_PATHS.pi.skills,
|
|
210
|
+
storageDir: PATHS.localSkills.pi,
|
|
211
|
+
reposFile: PATHS.skillRepos.pi,
|
|
212
|
+
cacheFile: PATHS.skillCaches.pi
|
|
203
213
|
}
|
|
204
214
|
};
|
|
205
215
|
|
|
@@ -13,6 +13,7 @@ const { getEffectiveApiKey: getClaudeEffectiveApiKey } = require('./channels');
|
|
|
13
13
|
const { getEffectiveApiKey: getCodexEffectiveApiKey } = require('./codex-channels');
|
|
14
14
|
const { getEffectiveApiKey: getGeminiEffectiveApiKey } = require('./gemini-channels');
|
|
15
15
|
const { getEffectiveApiKey: getOpenCodeEffectiveApiKey } = require('./opencode-channels');
|
|
16
|
+
const { getEffectiveApiKey: getPiEffectiveApiKey } = require('./pi-channels');
|
|
16
17
|
const { loadClaudeRequestTemplate } = require('./request-logger');
|
|
17
18
|
|
|
18
19
|
// 测试结果缓存
|
|
@@ -102,6 +103,8 @@ function resolveExplicitModel(channel, model) {
|
|
|
102
103
|
|
|
103
104
|
function resolveEffectiveApiKey(channel, channelType) {
|
|
104
105
|
switch (channelType) {
|
|
106
|
+
case 'pi':
|
|
107
|
+
return getPiEffectiveApiKey(channel);
|
|
105
108
|
case 'codex':
|
|
106
109
|
return getCodexEffectiveApiKey(channel);
|
|
107
110
|
case 'gemini':
|
|
@@ -376,11 +379,14 @@ function extractJsonPayloads(responseData) {
|
|
|
376
379
|
* 测试单个渠道的连接速度和 API 功能
|
|
377
380
|
* @param {Object} channel - 渠道配置
|
|
378
381
|
* @param {number} timeout - 超时时间(毫秒)
|
|
379
|
-
* @param {string} channelType -
|
|
382
|
+
* @param {string} channelType - 请求格式类型:'claude' | 'codex' | 'gemini' | 'opencode'
|
|
383
|
+
* @param {Object} options - 可选行为
|
|
384
|
+
* @param {string} options.authSourceType - API key 来源平台;OMP 使用 'pi',请求格式仍由 channelType 决定
|
|
380
385
|
* @returns {Promise<Object>} 测试结果
|
|
381
386
|
*/
|
|
382
|
-
async function testChannelSpeed(channel, timeout = DEFAULT_TIMEOUT, channelType = 'claude') {
|
|
387
|
+
async function testChannelSpeed(channel, timeout = DEFAULT_TIMEOUT, channelType = 'claude', options = {}) {
|
|
383
388
|
const sanitizedTimeout = sanitizeTimeout(timeout);
|
|
389
|
+
const authSourceType = options?.authSourceType || channelType;
|
|
384
390
|
|
|
385
391
|
try {
|
|
386
392
|
if (!channel.baseUrl) {
|
|
@@ -416,7 +422,7 @@ async function testChannelSpeed(channel, timeout = DEFAULT_TIMEOUT, channelType
|
|
|
416
422
|
};
|
|
417
423
|
}
|
|
418
424
|
|
|
419
|
-
const effectiveApiKey = resolveEffectiveApiKey(channel,
|
|
425
|
+
const effectiveApiKey = resolveEffectiveApiKey(channel, authSourceType);
|
|
420
426
|
if (!effectiveApiKey) {
|
|
421
427
|
return {
|
|
422
428
|
channelId: channel.id,
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const { PATHS } = require('../../config/paths');
|
|
4
|
+
const {
|
|
5
|
+
DEFAULT_HOME_CLI_COLUMNS,
|
|
6
|
+
normalizeCustomCliPlatforms,
|
|
7
|
+
normalizeHomeCliColumns
|
|
8
|
+
} = require('../../shared/platforms');
|
|
4
9
|
|
|
5
10
|
const UI_CONFIG_FILE = PATHS.uiConfig;
|
|
6
11
|
|
|
@@ -17,18 +22,27 @@ const DEFAULT_UI_CONFIG = {
|
|
|
17
22
|
channelLocks: {
|
|
18
23
|
claude: false,
|
|
19
24
|
codex: false,
|
|
20
|
-
gemini: false
|
|
25
|
+
gemini: false,
|
|
26
|
+
opencode: false,
|
|
27
|
+
pi: false
|
|
21
28
|
},
|
|
22
29
|
channelCollapse: {
|
|
23
30
|
claude: [],
|
|
24
31
|
codex: [],
|
|
25
|
-
gemini: []
|
|
32
|
+
gemini: [],
|
|
33
|
+
opencode: [],
|
|
34
|
+
pi: []
|
|
26
35
|
},
|
|
27
36
|
channelOrder: {
|
|
28
37
|
claude: [],
|
|
29
38
|
codex: [],
|
|
30
|
-
gemini: []
|
|
31
|
-
|
|
39
|
+
gemini: [],
|
|
40
|
+
opencode: [],
|
|
41
|
+
pi: []
|
|
42
|
+
},
|
|
43
|
+
dashboardChannelOrder: DEFAULT_HOME_CLI_COLUMNS,
|
|
44
|
+
homeCliColumns: DEFAULT_HOME_CLI_COLUMNS,
|
|
45
|
+
customCliPlatforms: []
|
|
32
46
|
};
|
|
33
47
|
|
|
34
48
|
// 内存缓存
|
|
@@ -55,7 +69,7 @@ function readUIConfigFromFile() {
|
|
|
55
69
|
const content = fs.readFileSync(UI_CONFIG_FILE, 'utf8');
|
|
56
70
|
const data = JSON.parse(content);
|
|
57
71
|
// Merge with defaults to ensure all keys exist
|
|
58
|
-
return {
|
|
72
|
+
return normalizeUIConfig({
|
|
59
73
|
...DEFAULT_UI_CONFIG,
|
|
60
74
|
...data,
|
|
61
75
|
theme: data.theme || DEFAULT_UI_CONFIG.theme,
|
|
@@ -64,13 +78,34 @@ function readUIConfigFromFile() {
|
|
|
64
78
|
channelLocks: { ...DEFAULT_UI_CONFIG.channelLocks, ...data.channelLocks },
|
|
65
79
|
channelCollapse: { ...DEFAULT_UI_CONFIG.channelCollapse, ...data.channelCollapse },
|
|
66
80
|
channelOrder: { ...DEFAULT_UI_CONFIG.channelOrder, ...data.channelOrder }
|
|
67
|
-
};
|
|
81
|
+
});
|
|
68
82
|
} catch (error) {
|
|
69
83
|
console.error('Error loading UI config:', error);
|
|
70
|
-
return { ...DEFAULT_UI_CONFIG };
|
|
84
|
+
return normalizeUIConfig({ ...DEFAULT_UI_CONFIG });
|
|
71
85
|
}
|
|
72
86
|
}
|
|
73
87
|
|
|
88
|
+
function normalizeUIConfig(config = {}) {
|
|
89
|
+
const customCliPlatforms = normalizeCustomCliPlatforms(config.customCliPlatforms);
|
|
90
|
+
const legacyOrder = Array.isArray(config.homeCliColumns)
|
|
91
|
+
? config.homeCliColumns
|
|
92
|
+
: config.dashboardChannelOrder;
|
|
93
|
+
const homeCliColumns = normalizeHomeCliColumns(legacyOrder, customCliPlatforms);
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
...DEFAULT_UI_CONFIG,
|
|
97
|
+
...config,
|
|
98
|
+
panelVisibility: { ...DEFAULT_UI_CONFIG.panelVisibility, ...(config.panelVisibility || {}) },
|
|
99
|
+
channelBalance: { ...DEFAULT_UI_CONFIG.channelBalance, ...(config.channelBalance || {}) },
|
|
100
|
+
channelLocks: { ...DEFAULT_UI_CONFIG.channelLocks, ...(config.channelLocks || {}) },
|
|
101
|
+
channelCollapse: { ...DEFAULT_UI_CONFIG.channelCollapse, ...(config.channelCollapse || {}) },
|
|
102
|
+
channelOrder: { ...DEFAULT_UI_CONFIG.channelOrder, ...(config.channelOrder || {}) },
|
|
103
|
+
dashboardChannelOrder: homeCliColumns,
|
|
104
|
+
homeCliColumns,
|
|
105
|
+
customCliPlatforms
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
74
109
|
// 初始化缓存(延迟初始化)
|
|
75
110
|
function initializeCache() {
|
|
76
111
|
if (cacheInitialized) return;
|
|
@@ -100,9 +135,10 @@ function saveUIConfig(config) {
|
|
|
100
135
|
ensureConfigDir();
|
|
101
136
|
|
|
102
137
|
try {
|
|
103
|
-
|
|
138
|
+
const normalizedConfig = normalizeUIConfig(config);
|
|
139
|
+
fs.writeFileSync(UI_CONFIG_FILE, JSON.stringify(normalizedConfig, null, 2), 'utf8');
|
|
104
140
|
// 同时更新缓存
|
|
105
|
-
uiConfigCache = JSON.parse(JSON.stringify(
|
|
141
|
+
uiConfigCache = JSON.parse(JSON.stringify(normalizedConfig));
|
|
106
142
|
} catch (error) {
|
|
107
143
|
console.error('Error saving UI config:', error);
|
|
108
144
|
throw error;
|
|
@@ -114,7 +150,7 @@ function updateUIConfig(key, value) {
|
|
|
114
150
|
const config = loadUIConfig();
|
|
115
151
|
config[key] = value;
|
|
116
152
|
saveUIConfig(config);
|
|
117
|
-
return
|
|
153
|
+
return loadUIConfig();
|
|
118
154
|
}
|
|
119
155
|
|
|
120
156
|
// Update nested config
|
|
@@ -125,12 +161,14 @@ function updateNestedUIConfig(parentKey, childKey, value) {
|
|
|
125
161
|
}
|
|
126
162
|
config[parentKey][childKey] = value;
|
|
127
163
|
saveUIConfig(config);
|
|
128
|
-
return
|
|
164
|
+
return loadUIConfig();
|
|
129
165
|
}
|
|
130
166
|
|
|
131
167
|
module.exports = {
|
|
132
168
|
loadUIConfig,
|
|
133
169
|
saveUIConfig,
|
|
134
170
|
updateUIConfig,
|
|
135
|
-
updateNestedUIConfig
|
|
171
|
+
updateNestedUIConfig,
|
|
172
|
+
normalizeUIConfig,
|
|
173
|
+
DEFAULT_UI_CONFIG
|
|
136
174
|
};
|
|
@@ -621,7 +621,7 @@ function removeProjectFromWorkspace(workspaceId, projectName) {
|
|
|
621
621
|
|
|
622
622
|
|
|
623
623
|
/**
|
|
624
|
-
* 获取所有渠道(Claude/Codex/Gemini)的项目并集
|
|
624
|
+
* 获取所有渠道(Claude/Codex/Gemini/OpenCode/OMP)的项目并集
|
|
625
625
|
* @returns {Promise<Array>} 去重后的项目列表
|
|
626
626
|
*/
|
|
627
627
|
async function getAllAvailableProjects() {
|
|
@@ -630,6 +630,7 @@ async function getAllAvailableProjects() {
|
|
|
630
630
|
const codexSessionsService = require('./codex-sessions');
|
|
631
631
|
const geminiSessionsService = require('./gemini-sessions');
|
|
632
632
|
const opencodeSessionsService = require('./opencode-sessions');
|
|
633
|
+
const piSessionsService = require('./pi-sessions');
|
|
633
634
|
const { isCodexInstalled } = require('./codex-config');
|
|
634
635
|
const { isGeminiInstalled } = require('./gemini-config');
|
|
635
636
|
const { isOpenCodeInstalled } = require('./opencode-sessions');
|
|
@@ -647,7 +648,7 @@ async function getAllAvailableProjects() {
|
|
|
647
648
|
seenKeys.add(projectKey);
|
|
648
649
|
|
|
649
650
|
const displayName = project.displayName || (projectPath ? path.basename(projectPath) : project.name);
|
|
650
|
-
const lastUsedValue = project.lastUsed || project.lastUpdated || null;
|
|
651
|
+
const lastUsedValue = project.lastUsed || project.lastUpdated || project.mtimeMs || null;
|
|
651
652
|
const lastUsed = typeof lastUsedValue === 'string'
|
|
652
653
|
? new Date(lastUsedValue).getTime()
|
|
653
654
|
: (lastUsedValue || 0);
|
|
@@ -702,6 +703,16 @@ async function getAllAvailableProjects() {
|
|
|
702
703
|
console.error('获取 opencode 项目失败:', error.message);
|
|
703
704
|
}
|
|
704
705
|
|
|
706
|
+
try {
|
|
707
|
+
const piProjects = piSessionsService.getProjects();
|
|
708
|
+
const list = Array.isArray(piProjects) ? piProjects : [];
|
|
709
|
+
if (list.length > 0 || piSessionsService.isPiInstalled()) {
|
|
710
|
+
list.forEach(project => addProject('pi', project));
|
|
711
|
+
}
|
|
712
|
+
} catch (error) {
|
|
713
|
+
console.error('获取 OMP 项目失败:', error.message);
|
|
714
|
+
}
|
|
715
|
+
|
|
705
716
|
// 按最后使用时间排序
|
|
706
717
|
allProjects.sort((a, b) => (b.lastUsed || 0) - (a.lastUsed || 0));
|
|
707
718
|
|
|
@@ -711,7 +722,7 @@ async function getAllAvailableProjects() {
|
|
|
711
722
|
/**
|
|
712
723
|
* 在工作区中启动 CLI 工具
|
|
713
724
|
* @param {string} workspaceId 工作区 ID
|
|
714
|
-
* @param {string} tool 工具名称 (claude/codex/gemini/opencode)
|
|
725
|
+
* @param {string} tool 工具名称 (claude/codex/gemini/opencode/pi/omp)
|
|
715
726
|
* @param {string} projectName 可选,工作区内的项目名
|
|
716
727
|
* @returns {object} 启动信息
|
|
717
728
|
*/
|
|
@@ -738,15 +749,18 @@ function getLaunchCommand(workspaceId, tool, projectName = null) {
|
|
|
738
749
|
}
|
|
739
750
|
}
|
|
740
751
|
|
|
752
|
+
const normalizedTool = tool === 'omp' ? 'pi' : tool;
|
|
753
|
+
|
|
741
754
|
// 根据工具类型生成启动命令
|
|
742
755
|
const commands = {
|
|
743
756
|
claude: 'claude',
|
|
744
757
|
codex: 'codex',
|
|
745
758
|
gemini: 'gemini',
|
|
746
|
-
opencode: 'opencode'
|
|
759
|
+
opencode: 'opencode',
|
|
760
|
+
pi: 'omp'
|
|
747
761
|
};
|
|
748
762
|
|
|
749
|
-
const cmd = commands[
|
|
763
|
+
const cmd = commands[normalizedTool];
|
|
750
764
|
if (!cmd) {
|
|
751
765
|
throw new Error(`不支持的工具: ${tool}`);
|
|
752
766
|
}
|
|
@@ -182,18 +182,24 @@ function inferSource(log) {
|
|
|
182
182
|
if (log.toolType) {
|
|
183
183
|
if (log.toolType.includes('codex')) return 'codex';
|
|
184
184
|
if (log.toolType.includes('gemini')) return 'gemini';
|
|
185
|
+
if (log.toolType.includes('opencode')) return 'opencode';
|
|
186
|
+
if (log.toolType.includes('pi')) return 'pi';
|
|
185
187
|
}
|
|
186
188
|
if (typeof log.model === 'string') {
|
|
187
189
|
const model = log.model.toLowerCase();
|
|
188
190
|
if (model.includes('gemini')) return 'gemini';
|
|
191
|
+
if (model.includes('opencode')) return 'opencode';
|
|
192
|
+
if (model.includes('pi')) return 'pi';
|
|
189
193
|
if (model.includes('gpt') || model.includes('o1') || model.includes('o3')) return 'codex';
|
|
190
194
|
if (model.includes('claude')) return 'claude';
|
|
191
195
|
}
|
|
192
196
|
if (typeof log.action === 'string') {
|
|
193
197
|
if (log.action.includes('codex')) return 'codex';
|
|
194
198
|
if (log.action.includes('gemini')) return 'gemini';
|
|
199
|
+
if (log.action.includes('opencode')) return 'opencode';
|
|
200
|
+
if (log.action.includes('pi')) return 'pi';
|
|
195
201
|
}
|
|
196
|
-
if (log.channelType === 'codex' || log.channelType === 'gemini') {
|
|
202
|
+
if (log.channelType === 'codex' || log.channelType === 'gemini' || log.channelType === 'opencode' || log.channelType === 'pi') {
|
|
197
203
|
return log.channelType;
|
|
198
204
|
}
|
|
199
205
|
return 'claude';
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{d as e}from"./vendors-DQA4Hw9v.js";import{a8 as a,f as n,r as l,w as s,h as c,K as t,a7 as o,c as i,o as u,a as d,W as r,X as v,Y as p,N as h,a0 as y,a3 as g,_ as m,p as f,L as k,F as T,$ as b,Z as x,v as w}from"./vue-vendor-B5RqjEjQ.js";import{_ as C,u as $,a as _,R as z,g as q,b as S,c as L,d as I,e as j,f as M,h as O,i as A,j as N,k as E,l as F,s as P}from"./index-y-eEayxR.js";import{E as D,o as U,n as V,m as J,aq as K,h as R,P as B,ar as G,as as Z,at as H,au as W,a9 as X,i as Y,A as Q,av as ee,aw as ae,T as ne,a as le,b as se}from"./icons-QFXzEgqM.js";import{L as ce,N as te,B as oe,s as ie,b as ue,c as de,a as re,Z as ve}from"./naive-ui-kcWDeJwl.js";import"./markdown-BNJfEgCA.js";const pe={class:"channel-column"},he={class:"drag-handle",title:"拖拽排序"},ye={class:"header-icon"},ge={class:"channel-title"},me={key:0,class:"claude-extra-area"},fe={key:0,class:"channel-content"},ke={class:"card"},Te={class:"card-header"},be={key:0,class:"runtime-badge"},xe={class:"card-body",style:{padding:"6px 10px"}},we={class:"proxy-info-row"},Ce={class:"proxy-status"},$e={class:"proxy-port"},_e={class:"channel-name"},ze={class:"channel-quick-panel"},qe={class:"panel-title"},Se={key:0,class:"no-channels"},Le={key:1,class:"channel-quick-list"},Ie={class:"channel-quick-info"},je={class:"channel-quick-name"},Me={key:0,class:"channel-switching-tip"},Oe={class:"channel-metrics"},Ae={class:"metric-item"},Ne={class:"metric-value"},Ee={class:"metric-item"},Fe={class:"metric-value"},Pe={class:"metric-item"},De={class:"metric-value"},Ue={class:"metric-item"},Ve={class:"card"},Je={class:"card-header compact"},Ke={class:"card-body",style:{padding:"8px 10px"}},Re={class:"quick-access-list"},Be={class:"access-icon"},Ge={class:"access-content"},Ze={class:"access-value"},He={class:"access-icon"},We={class:"access-content"},Xe={class:"access-value"},Ye={class:"access-icon"},Qe={class:"access-content"},ea={class:"access-goto"},aa={class:"card-header compact"},na={class:"card-body",style:{padding:"8px 10px"}},la={class:"stats-inline stats-3col"},sa={class:"stat-inline-item stat-requests"},ca={class:"stat-info"},ta={class:"stat-inline-item stat-input"},oa={class:"stat-info"},ia={class:"stat-inline-item stat-output"},ua={class:"stat-info"},da={key:1,class:"card logs-card"},ra={class:"card-header compact"},va={class:"card-body logs-card-body"},pa={class:"logs-table-wrapper"},ha={key:0,class:"empty-logs"},ya=["title"],ga={key:0,class:"action-content"},ma={class:"action-msg"},fa={class:"action-time"},ka={key:1,class:"action-content"},Ta={class:"action-msg error-msg"},ba={class:"action-time error-time"},xa={class:"locked-content"},wa={class:"lock-icon"},Ca="channelLocks",$a=C({__name:"ChannelColumn",props:{channelType:{type:String,required:!0,validator:e=>["claude","codex","gemini","opencode"].includes(e)}},setup(e){const C=e,F=a(),P=ce(),{claudeProxy:$a,codexProxy:_a,geminiProxy:za,opencodeProxy:qa,claudeChannels:Sa,codexChannels:La,geminiChannels:Ia,opencodeChannels:ja,schedulerState:Ma,getProxyState:Oa,startProxy:Aa,stopProxy:Na,getLogs:Ea,clearLogsForSource:Fa,loadChannels:Pa,logLimit:Da,statsInterval:Ua}=$(),{dashboardData:Va,loadDashboard:Ja}=_(),Ka={claude:{title:"ClaudeCode",subtitle:"智能编程助手",icon:J},codex:{title:"Codex-CLI",subtitle:"高效代码生成",icon:V},gemini:{title:"Gemini-CLI",subtitle:"多模态AI助手",icon:U},opencode:{title:"OpenCode",subtitle:"AI 代码助手",icon:D}},Ra=n(()=>Ka[C.channelType].title);n(()=>Ka[C.channelType].subtitle);const Ba=n(()=>Ka[C.channelType].icon),Ga=n(()=>"claude"===C.channelType?$a.value:"codex"===C.channelType?_a.value:"gemini"===C.channelType?za.value:"opencode"===C.channelType?qa.value:{}),Za=n(()=>"claude"===C.channelType?"Claude":"codex"===C.channelType?"Codex":"gemini"===C.channelType?"Gemini":"opencode"===C.channelType?"OpenCode":""),Ha=l({projects:0,sessions:0}),Wa=l({requests:0,tokens:0,cost:0}),Xa=l({requests:0,tokens:0,cost:0}),Ya={requests:0,tokens:0,cost:3};let Qa={requests:null,tokens:null,cost:null};const en=l({requests:!1,tokens:!1,cost:!1}),an=n(()=>{var e,a,n;const l="claude"===C.channelType?"claude":"codex"===C.channelType?"codex":"opencode"===C.channelType?"opencode":"gemini";return(null==(n=null==(a=null==(e=Va.value)?void 0:e.todayStats)?void 0:a[l])?void 0:n.byModel)||{}});function nn(e,a,n,l=600){Qa[e]&&cancelAnimationFrame(Qa[e]),en.value[e]=!0;const s=Date.now(),c=()=>{const t=Date.now()-s,o=Math.min(t/l,1),i=1-Math.pow(1-o,2),u=a+(n-a)*i,d=Ya[e]??0,r=Math.pow(10,d),v=d>0?Math.round(u*r)/r:Math.round(u);Xa.value[e]=v,o<1?Qa[e]=requestAnimationFrame(c):en.value[e]=!1};Qa[e]=requestAnimationFrame(c)}const ln=l(!1);function sn(e){try{const a=localStorage.getItem(Ca),n=a?JSON.parse(a):{};n[C.channelType]=e,localStorage.setItem(Ca,JSON.stringify(n))}catch(a){}}const cn=l(function(){try{const e=localStorage.getItem(Ca);if(e){return JSON.parse(e)[C.channelType]||!1}}catch(e){}return!1}());async function tn(){cn.value=!cn.value,sn(cn.value);try{if(await E("channelLocks",C.channelType,cn.value),Va.value){const e=Va.value.uiConfig||{},a={...e.channelLocks||{}};a[C.channelType]=cn.value,Va.value={...Va.value,uiConfig:{...e,channelLocks:a}}}}catch(e){}}const on=l(!0);function un(e){e.detail&&void 0!==e.detail.showLogs&&(on.value=e.detail.showLogs)}const dn=l(Date.now()),rn=n(()=>{if(!Ga.value.running)return"";const e=Ga.value.startTime;if(!e)return"";const a=dn.value-e;if(a<=0)return"";const n=Math.floor(a/1e3),l=Math.floor(n/3600),s=Math.floor(n%3600/60),c=n%60;let t="已运行 ";return l>0&&(t+=`${l}小时`),s>0&&(t+=`${s}分`),(c>0||0===l&&0===s)&&(t+=`${c}秒`),t}),vn=l(null),pn=n(()=>Da.value),hn={claude:Ea("claude"),codex:Ea("codex"),gemini:Ea("gemini"),opencode:Ea("opencode")},yn=n(()=>((hn[C.channelType]||hn.claude).value||[]).slice(0,pn.value));let gn=null,mn=null,fn=null,kn=!1,Tn=null;const bn=n(()=>{let e=[];"claude"===C.channelType?e=Sa.value||[]:"codex"===C.channelType?e=La.value||[]:"gemini"===C.channelType?e=Ia.value||[]:"opencode"===C.channelType&&(e=ja.value||[]);return[...e.filter(e=>!1!==e.enabled),...e.filter(e=>!1===e.enabled)]});function xn(e){const a=Ma[C.channelType];if(!a||!a.channels)return 0;const n=a.channels.find(a=>a.id===e);return n?n.inflight:0}const wn=n(()=>{var e;const a=bn.value.filter(e=>!1!==e.enabled).length;return(null==(e=Ga.value.proxy)?void 0:e.running)?`${a}个渠道调度中`:bn.value.length>0?`${a}个渠道已启用`:"无渠道"}),Cn=l({});function $n(e){return Cn.value[e]||{requests:0,tokens:0,cost:0}}async function _n(){var e;try{let a;if("claude"===C.channelType?a=await q():"codex"===C.channelType?a=await S():"gemini"===C.channelType?a=await L():"opencode"===C.channelType&&(a=await I()),a&&a.byChannel){const n={};for(const[l,s]of Object.entries(a.byChannel))n[l]={requests:s.requests||0,tokens:(null==(e=s.tokens)?void 0:e.total)||0,cost:s.cost||0};Cn.value=n}}catch(a){}}function zn(e,a){var n;return(null==e?void 0:e.usageMissing)?"--":(null==(n=null==e?void 0:e.tokens)?void 0:n[a])||0}function qn(e){if(!e||"action"===e.type||"error"===e.status)return"";const a=[];return e.model&&a.push(`模型: ${e.model}`),e.originalModel&&e.originalModel!==e.model&&a.push(`重定向: ${e.originalModel} -> ${e.model}`),e.usageMissing&&a.push("上游响应未返回 usage,当前无法计算 token。"),a.join("\n")}async function Sn(e){var a,n;const l=Oa(C.channelType);l.value.loading=!0;try{let a;a=e?await Aa(C.channelType):await Na(C.channelType,{refreshChannelsDrawer:!0}),!1!==a.success?P.success(e?`${Ra.value} 代理已启动`:`${Ra.value} 代理已停止`):(P.error(a.error||"操作失败"),l.value.running=!e)}catch(s){P.error((null==(n=null==(a=s.response)?void 0:a.data)?void 0:n.error)||s.message||"操作失败"),l.value.running=!e}finally{l.value.loading=!1}}function Ln(){F.push({name:`${C.channelType}-projects`})}function In(){F.push({name:`${C.channelType}-projects`})}s(yn,e=>{var a;const n=(null==(a=e[0])?void 0:a.id)||null;if(!n||n===gn)return void(gn=n);gn=n,Tn&&clearTimeout(Tn),Tn=setTimeout(()=>{_n()},5e3);(!vn.value||vn.value.scrollTop<20)&&w(()=>{vn.value&&(vn.value.scrollTop=0)})}),s(()=>Wa.value.requests,e=>{nn("requests",Xa.value.requests,e,600)}),s(()=>Wa.value.tokens,e=>{nn("tokens",Xa.value.tokens,e,600)}),s(()=>Wa.value.cost,e=>{nn("cost",Xa.value.cost,e,600)}),s(()=>C.channelType,()=>{var e;gn=(null==(e=yn.value[0])?void 0:e.id)||null}),s(()=>{var e,a;return null==(a=null==(e=Va.value)?void 0:e.counts)?void 0:a[C.channelType]},()=>{An()}),s(Ua,()=>{kn&&function(){mn&&(clearInterval(mn),mn=null);const e=Ua.value||30,a=Math.max(1e3*e,1e4);mn=setInterval(()=>{Nn(),_n()},a)}()});const jn=l({});function Mn(e){return!!jn.value[e]}function On(e,a){e&&(jn.value={...jn.value,[e]:!!a})}function An(){var e,a;const n=null==(a=null==(e=Va.value)?void 0:e.counts)?void 0:a[C.channelType];Ha.value.projects=(null==n?void 0:n.projectCount)||0,Ha.value.sessions=(null==n?void 0:n.sessionCount)||0}async function Nn(){if(Va.value&&Va.value.todayStats){const e=Va.value.todayStats[C.channelType];e&&(Wa.value.requests=e.requests||0,Wa.value.tokens=e.tokens||0,Wa.value.cost=e.cost||0)}An()}function En(){Fa(C.channelType)}function Fn(e){var a,n;null==(n=null==(a=null==e?void 0:e.currentTarget)?void 0:a.blur)||n.call(a)}function Pn(e){window.dispatchEvent(new CustomEvent("open-skills-drawer",{detail:{platform:C.channelType}})),Fn(e)}function Dn(e){window.dispatchEvent(new CustomEvent("open-commands-drawer",{detail:{platform:C.channelType}})),Fn(e)}function Un(e){window.dispatchEvent(new CustomEvent("open-agents-drawer",{detail:{platform:C.channelType}})),Fn(e)}return c(async()=>{await Promise.all([Ja().then(()=>Nn()),_n()]),async function(){var e,a;try{if(Va.value&&Va.value.uiConfig)on.value=!1!==(null==(e=Va.value.uiConfig.panelVisibility)?void 0:e.showLogs);else{const e=await j();e.success&&e.config&&(on.value=!1!==(null==(a=e.config.panelVisibility)?void 0:a.showLogs))}}catch(n){}}(),async function(){var e,a;try{let n=!1;if(Va.value&&Va.value.uiConfig)n=(null==(e=Va.value.uiConfig.channelLocks)?void 0:e[C.channelType])||!1;else{const e=await j();e.success&&e.config&&(n=(null==(a=e.config.channelLocks)?void 0:a[C.channelType])||!1)}cn.value=n,sn(n)}catch(n){}}(),window.addEventListener("panel-visibility-change",un),Xa.value={...Wa.value},kn=!0,fn=setInterval(()=>{dn.value=Date.now()},1e3)}),t(()=>{kn=!1,mn&&clearInterval(mn),fn&&clearInterval(fn),Tn&&clearTimeout(Tn),window.removeEventListener("panel-visibility-change",un),Object.values(Qa).forEach(e=>{e&&cancelAnimationFrame(e)})}),(a,n)=>{const l=o("n-collapse");return u(),i("div",pe,[d("div",{class:k(["channel-header",e.channelType])},[d("div",he,[v(h(te),{size:16},{default:p(()=>[v(h(K))]),_:1})]),d("div",ye,[v(h(te),{size:20},{default:p(()=>[(u(),y(g(Ba.value)))]),_:1})]),d("h2",ge,m(Ra.value),1),["claude","codex","gemini","opencode"].includes(e.channelType)?(u(),i("div",me,[v(h(ie),{trigger:"hover"},{trigger:p(()=>[v(h(oe),{text:"",class:"config-shortcut-button",onClick:Pn,title:"Skills 技能管理"},{icon:p(()=>[v(h(te),{size:18},{default:p(()=>[v(h(D))]),_:1})]),_:1})]),default:p(()=>[n[3]||(n[3]=f(" Skills 技能管理 ",-1))]),_:1}),v(h(ie),{trigger:"hover"},{trigger:p(()=>[v(h(oe),{text:"",class:"config-shortcut-button",onClick:Dn,title:"Commands 命令管理"},{icon:p(()=>[v(h(te),{size:18},{default:p(()=>[v(h(R))]),_:1})]),_:1})]),default:p(()=>[n[4]||(n[4]=f(" Commands 命令管理 ",-1))]),_:1}),v(h(ie),{trigger:"hover"},{trigger:p(()=>[v(h(oe),{text:"",class:"config-shortcut-button",onClick:Un,title:"Agents 代理管理"},{icon:p(()=>[v(h(te),{size:18},{default:p(()=>[v(h(B))]),_:1})]),_:1})]),default:p(()=>[n[5]||(n[5]=f(" Agents 代理管理 ",-1))]),_:1})])):r("",!0),v(h(oe),{text:"",class:"lock-button",onClick:tn,title:cn.value?"解锁此列":"锁定此列"},{icon:p(()=>[v(h(te),{size:18},{default:p(()=>[cn.value?(u(),y(h(G),{key:0})):(u(),y(h(Z),{key:1}))]),_:1})]),_:1},8,["title"])],2),cn.value?r("",!0):(u(),i("div",fe,[d("div",ke,[d("div",Te,[v(h(te),{size:16},{default:p(()=>[v(h(H))]),_:1}),n[6]||(n[6]=d("h3",{class:"card-title"},"代理控制",-1)),Ga.value.running&&rn.value?(u(),i("span",be,m(rn.value),1)):r("",!0),v(h(ue),{value:Ga.value.running,"onUpdate:value":[n[0]||(n[0]=e=>Ga.value.running=e),Sn],loading:Ga.value.loading,size:"small",style:{"margin-left":"auto"}},null,8,["value","loading"])]),d("div",xe,[d("div",we,[d("div",Ce,[d("div",{class:k(["status-dot",{active:Ga.value.running}])},null,2),v(h(de),{type:Ga.value.running?"success":"default",style:{"font-size":"12px"}},{default:p(()=>[f(m(Ga.value.running?"运行中":"已停止"),1)]),_:1},8,["type"]),d("span",$e,"端口: "+m(Ga.value.port),1)]),v(h(ve),{trigger:"click",placement:"bottom",width:320,class:"channel-popover"},{trigger:p(()=>[v(h(oe),{text:"",size:"tiny",class:"channel-status"},{default:p(()=>[d("span",_e,m(wn.value),1)]),_:1})]),default:p(()=>[d("div",ze,[d("div",qe,[n[8]||(n[8]=d("span",null,"渠道快捷管理",-1)),v(h(de),{depth:"3",style:{"font-size":"11px"}},{default:p(()=>[...n[7]||(n[7]=[f("点击开关切换状态",-1)])]),_:1})]),0===bn.value.length?(u(),i("div",Se,[v(h(de),{depth:"3"},{default:p(()=>[...n[9]||(n[9]=[f("暂无配置渠道",-1)])]),_:1})])):(u(),i("div",Le,[(u(!0),i(T,null,b(bn.value,e=>{var a,l;return u(),i("div",{key:e.id,class:k(["channel-quick-item",{disabled:!1===e.enabled}])},[d("div",Ie,[d("span",je,m(e.name),1),"frozen"===(null==(a=e.health)?void 0:a.status)?(u(),y(h(re),{key:0,size:"tiny",type:"error",bordered:!1},{default:p(()=>[...n[10]||(n[10]=[f(" 冻结 ",-1)])]),_:1})):r("",!0),v(h(ue),{size:"small",value:!1!==e.enabled,"onUpdate:value":a=>async function(e,a){if(e&&!Mn(e.id)){On(e.id,!0);try{let n;"claude"===C.channelType?n=M:"codex"===C.channelType?n=O:"gemini"===C.channelType?n=A:"opencode"===C.channelType&&(n=N),n&&(await n(e.id,{enabled:a}),P.success(a?`渠道「${e.name}」已启用`:`渠道「${e.name}」已停用`),await Pa())}catch(n){P.error("操作失败: "+n.message)}finally{On(e.id,!1)}}}(e,a),loading:Mn(e.id),disabled:Mn(e.id),style:{"margin-left":"auto"}},null,8,["value","onUpdate:value","loading","disabled"])]),Mn(e.id)?(u(),i("div",Me," 正在切换渠道状态... ")):r("",!0),d("div",Oe,[d("span",Ae,[n[11]||(n[11]=d("span",{class:"metric-label"},"请求",-1)),d("span",Ne,m($n(e.id).requests),1)]),d("span",Ee,[n[12]||(n[12]=d("span",{class:"metric-label"},"Tokens",-1)),d("span",Fe,m((l=$n(e.id).tokens,l>=1e6?(l/1e6).toFixed(1)+"M":l>=1e3?(l/1e3).toFixed(1)+"K":l.toString())),1)]),d("span",Pe,[n[13]||(n[13]=d("span",{class:"metric-label"},"权重",-1)),d("span",De,m(e.weight||1),1)]),d("span",Ue,[n[14]||(n[14]=d("span",{class:"metric-label"},"并发",-1)),d("span",{class:k(["metric-value",{active:xn(e.id)>0}])},m(xn(e.id))+m(e.maxConcurrency?`/${e.maxConcurrency}`:""),3)])])],2)}),128))]))])]),_:1})])])]),d("div",Ve,[d("div",Je,[v(h(te),{size:14},{default:p(()=>[v(h(W))]),_:1}),n[15]||(n[15]=d("h3",{class:"card-title"},"快速访问",-1))]),d("div",Ke,[d("div",Re,[d("div",{class:"access-card access-card-projects clickable",onClick:Ln},[d("div",Be,[v(h(te),{size:16},{default:p(()=>[v(h(X))]),_:1})]),d("div",Ge,[n[16]||(n[16]=d("span",{class:"access-label"},"项目",-1)),d("span",Ze,m(Ha.value.projects),1)])]),d("div",{class:"access-card access-card-sessions clickable",onClick:n[1]||(n[1]=e=>ln.value=!0)},[d("div",He,[v(h(te),{size:16},{default:p(()=>[v(h(Y))]),_:1})]),d("div",We,[n[17]||(n[17]=d("span",{class:"access-label"},"最新对话",-1)),d("span",Xe,m(Ha.value.sessions),1)])]),d("div",{class:"access-card access-card-goto clickable",onClick:In},[d("div",Ye,[v(h(te),{size:16},{default:p(()=>[v(h(Q))]),_:1})]),d("div",Qe,[n[18]||(n[18]=d("span",{class:"access-label"},"前往",-1)),d("span",ea,m(Za.value),1)])])])])]),d("div",{class:k(["card stats-card",`stats-card-${e.channelType}`])},[d("div",aa,[v(h(te),{size:14},{default:p(()=>[v(h(ee))]),_:1}),n[19]||(n[19]=d("h3",{class:"card-title"},"今日数据",-1))]),d("div",na,[d("div",la,[d("div",sa,[n[21]||(n[21]=d("div",{class:"stat-icon-dot requests"},null,-1)),d("div",ca,[n[20]||(n[20]=d("span",{class:"stat-label"},"请求",-1)),d("span",{class:k(["stat-value",{animating:en.value.requests}])},m(Xa.value.requests),3)])]),d("div",ta,[n[23]||(n[23]=d("div",{class:"stat-icon-dot tokens"},null,-1)),d("div",oa,[n[22]||(n[22]=d("span",{class:"stat-label"},"总 Tokens",-1)),d("span",{class:k(["stat-value",{animating:en.value.tokens}])},m((c=Xa.value.tokens,c>=1e6?(c/1e6).toFixed(1)+"M":c>=1e3?(c/1e3).toFixed(1)+"K":c.toString())),3)])]),d("div",ia,[n[25]||(n[25]=d("div",{class:"stat-icon-dot cost"},null,-1)),d("div",ua,[n[24]||(n[24]=d("span",{class:"stat-label"},"成本 / USD",-1)),d("span",{class:k(["stat-value",{animating:en.value.cost}])},m((s=Xa.value.cost,!s||Number.isNaN(s)?"$0":"$"+Number(s).toFixed(3))),3)])])])])],2),Object.keys(an.value).length>0?(u(),i("div",{key:0,class:k(["card chart-card",`chart-card-${e.channelType}`])},[v(l,{"default-expanded-names":[],accordion:""})],2)):r("",!0),on.value?(u(),i("div",da,[d("div",ra,[v(h(te),{size:14},{default:p(()=>[v(h(ae))]),_:1}),n[26]||(n[26]=d("h3",{class:"card-title"},"实时日志",-1)),v(h(oe),{text:"",size:"tiny",onClick:x(En,["stop"]),style:{"margin-left":"auto"},title:"清空日志"},{icon:p(()=>[v(h(te),{size:14},{default:p(()=>[v(h(ne))]),_:1})]),_:1})]),d("div",va,[d("div",pa,[d("div",{class:k(["logs-table-header",`logs-header-${e.channelType}`])},[d("div",{class:k(["log-col col-channel",`col-channel-${e.channelType}`])},"渠道",2),d("div",{class:k(["log-col col-token",`col-token-${e.channelType}`])},"请求",2),d("div",{class:k(["log-col col-token",`col-token-${e.channelType}`])},"回复",2),"claude"===e.channelType?(u(),i(T,{key:0},[d("div",{class:k(["log-col col-token",`col-token-${e.channelType}`])},"写入",2),d("div",{class:k(["log-col col-token",`col-token-${e.channelType}`])},"命中",2)],64)):"codex"===e.channelType?(u(),i(T,{key:1},[d("div",{class:k(["log-col col-token",`col-token-${e.channelType}`])},"推理",2),d("div",{class:k(["log-col col-token",`col-token-${e.channelType}`])},"缓存",2),d("div",{class:k(["log-col col-token",`col-token-${e.channelType}`])},"总计",2)],64)):"gemini"===e.channelType?(u(),i(T,{key:2},[d("div",{class:k(["log-col col-token",`col-token-${e.channelType}`])},"缓存",2),d("div",{class:k(["log-col col-token",`col-token-${e.channelType}`])},"总计",2)],64)):"opencode"===e.channelType?(u(),i(T,{key:3},[d("div",{class:k(["log-col col-token",`col-token-${e.channelType}`])},"推理",2),d("div",{class:k(["log-col col-token",`col-token-${e.channelType}`])},"缓存",2),d("div",{class:k(["log-col col-token",`col-token-${e.channelType}`])},"总计",2)],64)):r("",!0),d("div",{class:k(["log-col col-time",`col-time-${e.channelType}`])},"时间",2)],2),d("div",{class:"logs-container",ref_key:"logsContainer",ref:vn},[0===yn.value.length?(u(),i("div",ha,[v(h(te),{size:32,depth:"3",style:{"margin-bottom":"8px"}},{default:p(()=>[v(h(ae))]),_:1}),v(h(de),{depth:"3",style:{"font-size":"12px","font-weight":"500"}},{default:p(()=>[...n[27]||(n[27]=[f("暂无实时日志",-1)])]),_:1}),v(h(de),{depth:"3",style:{"font-size":"11px","margin-top":"4px"}},{default:p(()=>[...n[28]||(n[28]=[f("开启代理后将显示请求记录",-1)])]),_:1})])):r("",!0),(u(!0),i(T,null,b(yn.value,a=>(u(),i("div",{key:a.id,class:k(["log-row",{"action-row":"action"===a.type,"error-row":"error"===a.status,"new-log":a.isNew}]),title:qn(a)},["action"===a.type?(u(),i("div",ga,[v(h(te),{size:12,color:"#18a058"},{default:p(()=>[v(h(le))]),_:1}),d("span",ma,m(a.message),1),d("span",fa,m(a.time),1)])):"error"===a.status?(u(),i("div",ka,[v(h(te),{size:12,color:"#d03050"},{default:p(()=>[v(h(se))]),_:1}),d("span",Ta,m(a.channel)+": "+m(a.error||a.message||"请求失败"),1),d("span",ba,m(a.time),1)])):(u(),i(T,{key:2},[d("div",{class:k(["log-col col-channel",`col-channel-${e.channelType}`])},[v(h(re),{size:"tiny",type:"success"},{default:p(()=>[f(m(a.channel),1)]),_:2},1024)],2),d("div",{class:k(["log-col col-token",`col-token-${e.channelType}`])},m(zn(a,"input")),3),d("div",{class:k(["log-col col-token",`col-token-${e.channelType}`])},m(zn(a,"output")),3),"claude"===e.channelType?(u(),i(T,{key:0},[d("div",{class:k(["log-col col-token",`col-token-${e.channelType}`])},m(zn(a,"cacheCreation")),3),d("div",{class:k(["log-col col-token",`col-token-${e.channelType}`])},m(zn(a,"cacheRead")),3)],64)):"codex"===e.channelType?(u(),i(T,{key:1},[d("div",{class:k(["log-col col-token",`col-token-${e.channelType}`])},m(zn(a,"reasoning")),3),d("div",{class:k(["log-col col-token",`col-token-${e.channelType}`])},m(zn(a,"cached")),3),d("div",{class:k(["log-col col-token",`col-token-${e.channelType}`])},m(zn(a,"total")),3)],64)):"gemini"===e.channelType?(u(),i(T,{key:2},[d("div",{class:k(["log-col col-token",`col-token-${e.channelType}`])},m(zn(a,"cached")),3),d("div",{class:k(["log-col col-token",`col-token-${e.channelType}`])},m(zn(a,"total")),3)],64)):"opencode"===e.channelType?(u(),i(T,{key:3},[d("div",{class:k(["log-col col-token",`col-token-${e.channelType}`])},m(zn(a,"reasoning")),3),d("div",{class:k(["log-col col-token",`col-token-${e.channelType}`])},m(zn(a,"cached")),3),d("div",{class:k(["log-col col-token",`col-token-${e.channelType}`])},m(zn(a,"total")),3)],64)):r("",!0),d("div",{class:k(["log-col col-time",`col-time-${e.channelType}`])},m(a.time),3)],64))],10,ya))),128))],512)])])])):r("",!0)])),cn.value?(u(),i("div",{key:1,class:k(["locked-overlay",`locked-${e.channelType}`])},[d("div",xa,[d("div",wa,[v(h(te),{size:48},{default:p(()=>[v(h(G))]),_:1})]),n[30]||(n[30]=d("h3",{class:"locked-title"},"该渠道已锁定",-1)),v(h(de),{depth:"3",class:"locked-hint"},{default:p(()=>[...n[29]||(n[29]=[f(" 点击上方按钮解锁以查看内容 ",-1)])]),_:1})])],2)):r("",!0),v(z,{visible:ln.value,"onUpdate:visible":n[2]||(n[2]=e=>ln.value=e),channel:e.channelType},null,8,["visible","channel"])]);var s,c}}},[["__scopeId","data-v-3a91c627"]]),_a=l({theme:"light",panelVisibility:{showChannels:!0,showLogs:!0},channelBalance:{showRemaining:!1},channelLocks:{claude:!1,codex:!1,gemini:!1},channelCollapse:{claude:[],codex:[],gemini:[]},channelOrder:{claude:[],codex:[],gemini:[]}});let za=!1,qa=null;async function Sa(){return za?_a.value:qa||(qa=(async()=>{try{const e=await j();e.success&&e.config&&(_a.value=e.config,za=!0)}catch(e){}finally{qa=null}return _a.value})(),qa)}const La={class:"dashboard-container"},Ia="dashboardChannelOrder",ja=C({__name:"Home",setup(a){const n=["claude","codex","gemini","opencode"],{uiConfig:s,updateConfig:t,loadUIConfig:o}=(za||Sa(),{uiConfig:_a,loadUIConfig:Sa,saveConfig:async function(e){try{const a=await P(e);return!!a.success&&(_a.value=a.config,!0)}catch(a){return!1}},updateConfig:async function(e,a){try{const n=await F(e,a);return!!n.success&&(_a.value=n.config,!0)}catch(n){return!1}},updateNestedConfig:async function(e,a,n){try{const l=await E(e,a,n);return!!l.success&&(_a.value=l.config,!0)}catch(l){return!1}}});function d(e){try{localStorage.setItem(Ia,JSON.stringify(e))}catch(a){}}const r=l(function(){try{const e=localStorage.getItem(Ia);if(e){const a=JSON.parse(e);if(Array.isArray(a)&&4===a.length)return a}}catch(e){}return n}().map(e=>({type:e})));async function g(){const e=r.value.map(e=>e.type);d(e),await t("dashboardChannelOrder",e)}return c(async()=>{if(await o(),s.value.dashboardChannelOrder&&Array.isArray(s.value.dashboardChannelOrder)&&4===s.value.dashboardChannelOrder.length){const e=s.value.dashboardChannelOrder;r.value=e.map(e=>({type:e})),d(e)}}),(a,n)=>(u(),i("div",La,[v(h(e),{modelValue:r.value,"onUpdate:modelValue":n[0]||(n[0]=e=>r.value=e),class:"dashboard-grid","item-key":"type",animation:200,handle:".drag-handle",onEnd:g},{item:p(({element:e})=>[(u(),y($a,{"channel-type":e.type,key:e.type},null,8,["channel-type"]))]),_:1},8,["modelValue"])]))}},[["__scopeId","data-v-a6410dd2"]]);export{ja as default};
|