coding-tool-x 3.7.2 → 3.7.4
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/dist/web/assets/{Analytics-D5kLOhUo.js → Analytics-BmPIW7oK.js} +1 -1
- package/dist/web/assets/{ConfigTemplates-D_-16blp.js → ConfigTemplates-CG4j4ua4.js} +1 -1
- package/dist/web/assets/Home-B9yAAZrD.js +1 -0
- package/dist/web/assets/Home-DSTUSxrT.css +1 -0
- package/dist/web/assets/{PluginManager-Cv_rv8Dh.js → PluginManager-CCJ7mo35.js} +1 -1
- package/dist/web/assets/{ProjectList-H_m4Tx4E.js → ProjectList-DVUs3o8x.js} +1 -1
- package/dist/web/assets/SessionList-DwssBydw.js +1 -0
- package/dist/web/assets/{SkillManager-CRav3m2a.js → SkillManager-DmOTHcQ7.js} +1 -1
- package/dist/web/assets/{WorkspaceManager-B3f8R4EF.js → WorkspaceManager-DLVj0zlu.js} +1 -1
- package/dist/web/assets/{icons-Dop5j7Bf.js → icons-nGdSKQn2.js} +1 -1
- package/dist/web/assets/index-BjdNYwSs.js +2 -0
- package/dist/web/assets/index-kQCC5dlu.css +1 -0
- package/dist/web/index.html +3 -3
- package/package.json +1 -1
- package/src/config/paths.js +2 -0
- package/src/server/api/channels.js +14 -1
- package/src/server/api/codex-channels.js +21 -1
- package/src/server/api/codex-projects.js +25 -8
- package/src/server/api/dashboard.js +167 -75
- package/src/server/api/gemini-channels.js +21 -1
- package/src/server/api/gemini-projects.js +23 -7
- package/src/server/api/omp-channels.js +17 -1
- package/src/server/api/omp-projects.js +23 -6
- package/src/server/api/opencode-channels.js +21 -1
- package/src/server/api/opencode-projects.js +23 -7
- package/src/server/api/projects.js +38 -21
- package/src/server/services/channel-balance.js +51 -5
- package/src/server/services/channel-sync-utils.js +368 -0
- package/src/server/services/channels.js +119 -0
- package/src/server/services/codex-channels.js +125 -0
- package/src/server/services/gemini-channels.js +123 -0
- package/src/server/services/omp-channels.js +299 -2
- package/src/server/services/omp-settings-manager.js +2 -0
- package/src/server/services/opencode-channels.js +172 -4
- package/src/server/services/project-snapshots.js +52 -0
- package/src/server/services/snapshot-cache.js +295 -0
- package/dist/web/assets/Home-DM_56Fyr.js +0 -1
- package/dist/web/assets/Home-Jom_ZcdY.css +0 -1
- package/dist/web/assets/SessionList-DcUQvsbR.js +0 -1
- package/dist/web/assets/index-Bnni1zPN.js +0 -2
- package/dist/web/assets/index-CfMuHG8u.css +0 -1
|
@@ -5,9 +5,18 @@ const { PATHS } = require('../../config/paths');
|
|
|
5
5
|
const { resolveChannelWebsiteUrl } = require('../../config/channel-preset-websites');
|
|
6
6
|
const {
|
|
7
7
|
setChannelConfig,
|
|
8
|
-
clearManagedChannelConfig
|
|
8
|
+
clearManagedChannelConfig,
|
|
9
|
+
readConfig,
|
|
10
|
+
selectConfigPath
|
|
9
11
|
} = require('./opencode-settings-manager');
|
|
10
12
|
const { normalizeGatewaySourceType } = require('./base/proxy-utils');
|
|
13
|
+
const {
|
|
14
|
+
createSkippedResult,
|
|
15
|
+
isLocalProxyBaseUrl,
|
|
16
|
+
resolveApiKeyValue,
|
|
17
|
+
resolveExistingActiveChannel,
|
|
18
|
+
upsertSyncedChannels
|
|
19
|
+
} = require('./channel-sync-utils');
|
|
11
20
|
|
|
12
21
|
function clearChannelBalanceCache(channel) {
|
|
13
22
|
try {
|
|
@@ -93,7 +102,7 @@ function loadChannels() {
|
|
|
93
102
|
gatewaySourceType: normalizeGatewaySourceType(ch.gatewaySourceType, 'codex'),
|
|
94
103
|
allowedModels: ch.allowedModels || []
|
|
95
104
|
};
|
|
96
|
-
normalized.providerKey = deriveProviderKey(normalized);
|
|
105
|
+
normalized.providerKey = ch.providerKey || deriveProviderKey(normalized);
|
|
97
106
|
normalized.websiteUrl = resolveChannelWebsiteUrl('opencode', normalized);
|
|
98
107
|
return normalized;
|
|
99
108
|
});
|
|
@@ -113,6 +122,15 @@ function deriveProviderKey(channel) {
|
|
|
113
122
|
return `opencode_${base}`;
|
|
114
123
|
}
|
|
115
124
|
|
|
125
|
+
function sanitizeOpenCodeProviderId(value) {
|
|
126
|
+
return String(value || '')
|
|
127
|
+
.toLowerCase()
|
|
128
|
+
.replace(/[^a-z0-9-]/g, '-')
|
|
129
|
+
.replace(/-+/g, '-')
|
|
130
|
+
.replace(/^-|-$/g, '')
|
|
131
|
+
|| 'channel';
|
|
132
|
+
}
|
|
133
|
+
|
|
116
134
|
function getOpenCodeProxyRunning() {
|
|
117
135
|
const { getOpenCodeProxyStatus } = require('../opencode-proxy-server');
|
|
118
136
|
return Boolean(getOpenCodeProxyStatus()?.running);
|
|
@@ -229,7 +247,7 @@ function updateChannel(channelId, updates) {
|
|
|
229
247
|
),
|
|
230
248
|
updatedAt: Date.now()
|
|
231
249
|
};
|
|
232
|
-
merged.providerKey = updates.providerKey || deriveProviderKey(merged);
|
|
250
|
+
merged.providerKey = updates.providerKey || oldChannel.providerKey || deriveProviderKey(merged);
|
|
233
251
|
merged.websiteUrl = resolveChannelWebsiteUrl('opencode', merged);
|
|
234
252
|
data.channels[index] = merged;
|
|
235
253
|
|
|
@@ -463,6 +481,155 @@ function disableAllChannels() {
|
|
|
463
481
|
saveChannels(data);
|
|
464
482
|
}
|
|
465
483
|
|
|
484
|
+
function getCurrentOpenCodeProviderId(config = {}) {
|
|
485
|
+
const modelRef = String(config?.model || '').trim();
|
|
486
|
+
if (modelRef.includes('/')) {
|
|
487
|
+
return modelRef.split('/')[0].trim();
|
|
488
|
+
}
|
|
489
|
+
const providers = config?.provider && typeof config.provider === 'object'
|
|
490
|
+
? Object.keys(config.provider).filter(Boolean)
|
|
491
|
+
: [];
|
|
492
|
+
return providers.length === 1 ? providers[0] : '';
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
function findOpenCodeExistingByProvider(channels = [], providerId = '', baseUrl = '') {
|
|
496
|
+
const normalizedProviderId = sanitizeOpenCodeProviderId(providerId);
|
|
497
|
+
const url = String(baseUrl || '').trim();
|
|
498
|
+
return channels.find(channel => {
|
|
499
|
+
const providerKeys = [
|
|
500
|
+
channel.providerKey,
|
|
501
|
+
sanitizeOpenCodeProviderId(channel.providerKey),
|
|
502
|
+
sanitizeOpenCodeProviderId(channel.name)
|
|
503
|
+
].filter(Boolean);
|
|
504
|
+
return providerKeys.includes(providerId) || providerKeys.includes(normalizedProviderId);
|
|
505
|
+
}) || channels.find(channel => url && channel.baseUrl === url) || null;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
function normalizeOpenCodeChannel(channel = {}) {
|
|
509
|
+
const normalized = {
|
|
510
|
+
...channel,
|
|
511
|
+
enabled: channel.enabled !== false,
|
|
512
|
+
weight: channel.weight || 1,
|
|
513
|
+
maxConcurrency: channel.maxConcurrency || null,
|
|
514
|
+
balanceToken: channel.balanceToken || '',
|
|
515
|
+
balanceUserId: channel.balanceUserId || null,
|
|
516
|
+
modelRedirects: channel.modelRedirects || [],
|
|
517
|
+
speedTestModel: channel.speedTestModel || null,
|
|
518
|
+
wireApi: channel.wireApi || 'openai',
|
|
519
|
+
gatewaySourceType: normalizeGatewaySourceType(channel.gatewaySourceType, 'codex'),
|
|
520
|
+
allowedModels: Array.isArray(channel.allowedModels) ? channel.allowedModels : []
|
|
521
|
+
};
|
|
522
|
+
normalized.providerKey = normalized.providerKey || deriveProviderKey(normalized);
|
|
523
|
+
normalized.websiteUrl = resolveChannelWebsiteUrl('opencode', normalized);
|
|
524
|
+
return normalized;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
function collectOpenCodeProviderModels(provider = {}) {
|
|
528
|
+
const models = provider?.models && typeof provider.models === 'object' && !Array.isArray(provider.models)
|
|
529
|
+
? Object.keys(provider.models)
|
|
530
|
+
: [];
|
|
531
|
+
return models.map(model => String(model || '').trim()).filter(Boolean);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
function buildOpenCodeSyncCandidate(config, channels) {
|
|
535
|
+
const providerId = getCurrentOpenCodeProviderId(config);
|
|
536
|
+
if (!providerId) {
|
|
537
|
+
return {
|
|
538
|
+
skip: true,
|
|
539
|
+
warning: 'OpenCode 原生配置未明确当前 provider,无法同步当前渠道。'
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
const provider = config?.provider?.[providerId] || null;
|
|
544
|
+
if (!provider || typeof provider !== 'object') {
|
|
545
|
+
return {
|
|
546
|
+
skip: true,
|
|
547
|
+
warning: `OpenCode 当前 provider "${providerId}" 未在配置中定义。`
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
const baseUrl = String(provider?.options?.baseURL || provider?.options?.baseUrl || provider?.baseUrl || provider?.baseURL || '').trim();
|
|
552
|
+
const rawApiKey = provider?.options?.apiKey || provider?.apiKey || provider?.key || '';
|
|
553
|
+
const existing = findOpenCodeExistingByProvider(channels, providerId, baseUrl);
|
|
554
|
+
const providerIsProxy = providerId === 'ctx-proxy'
|
|
555
|
+
|| rawApiKey === 'PROXY_KEY'
|
|
556
|
+
|| isLocalProxyBaseUrl(baseUrl);
|
|
557
|
+
|
|
558
|
+
if (providerIsProxy) {
|
|
559
|
+
const activeExisting = resolveExistingActiveChannel('opencode', channels)
|
|
560
|
+
|| existing;
|
|
561
|
+
if (activeExisting) {
|
|
562
|
+
return {
|
|
563
|
+
skip: true,
|
|
564
|
+
channel: activeExisting,
|
|
565
|
+
warning: 'OpenCode 当前配置指向 ctx 代理;对应渠道已在列表中,未重复导入。'
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
return {
|
|
569
|
+
skip: true,
|
|
570
|
+
warning: 'OpenCode 当前配置指向 ctx 代理,但没有找到可匹配的现有渠道。'
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
const credential = resolveApiKeyValue(rawApiKey);
|
|
575
|
+
const apiKey = credential.value || getEffectiveApiKeyCandidates(existing || {})[0] || '';
|
|
576
|
+
if (!apiKey) {
|
|
577
|
+
return {
|
|
578
|
+
skip: true,
|
|
579
|
+
channel: existing || null,
|
|
580
|
+
warning: `OpenCode 当前 provider "${providerId}" 缺少可解析 API Key,无法同步导入。`
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
const modelRef = String(config?.model || '').trim();
|
|
585
|
+
const model = modelRef.startsWith(`${providerId}/`) ? modelRef.slice(providerId.length + 1) : '';
|
|
586
|
+
const allowedModels = collectOpenCodeProviderModels(provider);
|
|
587
|
+
return {
|
|
588
|
+
name: existing?.name || provider.name || providerId,
|
|
589
|
+
providerKey: providerId,
|
|
590
|
+
baseUrl,
|
|
591
|
+
apiKey,
|
|
592
|
+
wireApi: existing?.wireApi || 'openai',
|
|
593
|
+
model: model || existing?.model || allowedModels[0] || null,
|
|
594
|
+
allowedModels,
|
|
595
|
+
gatewaySourceType: existing?.gatewaySourceType || 'codex',
|
|
596
|
+
credentialSource: credential.value ? credential.source : 'existing-channel'
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
function syncCurrentOpenCodeChannel() {
|
|
601
|
+
if (typeof readConfig !== 'function' || typeof selectConfigPath !== 'function') {
|
|
602
|
+
return createSkippedResult('opencode', 'OpenCode 配置读取能力不可用,无法同步当前渠道。');
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
const data = loadChannels();
|
|
606
|
+
const channels = Array.isArray(data.channels) ? data.channels : [];
|
|
607
|
+
let config = {};
|
|
608
|
+
try {
|
|
609
|
+
const configPath = selectConfigPath();
|
|
610
|
+
config = readConfig(configPath);
|
|
611
|
+
} catch (error) {
|
|
612
|
+
return createSkippedResult('opencode', `OpenCode 配置读取失败:${error.message}`);
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
const candidate = buildOpenCodeSyncCandidate(config, channels);
|
|
616
|
+
if (candidate?.skip) {
|
|
617
|
+
return createSkippedResult('opencode', candidate.warning, candidate.channel);
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
return upsertSyncedChannels({
|
|
621
|
+
toolType: 'opencode',
|
|
622
|
+
loadChannels,
|
|
623
|
+
saveChannels,
|
|
624
|
+
applyDefaults: normalizeOpenCodeChannel,
|
|
625
|
+
candidates: [candidate],
|
|
626
|
+
matchers: [
|
|
627
|
+
(channel, current) => channel.providerKey && channel.providerKey === current.providerKey,
|
|
628
|
+
(channel, current) => channel.baseUrl === current.baseUrl && channel.apiKey === current.apiKey
|
|
629
|
+
]
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
|
|
466
633
|
module.exports = {
|
|
467
634
|
getChannels,
|
|
468
635
|
createChannel,
|
|
@@ -474,5 +641,6 @@ module.exports = {
|
|
|
474
641
|
applyChannelToSettings,
|
|
475
642
|
getEffectiveApiKey,
|
|
476
643
|
getEffectiveApiKeyCandidates,
|
|
477
|
-
disableAllChannels
|
|
644
|
+
disableAllChannels,
|
|
645
|
+
syncCurrentOpenCodeChannel
|
|
478
646
|
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
getSnapshot,
|
|
5
|
+
invalidateSnapshot
|
|
6
|
+
} = require('./snapshot-cache');
|
|
7
|
+
|
|
8
|
+
const PROJECT_SNAPSHOT_TTL_MS = 60 * 1000;
|
|
9
|
+
|
|
10
|
+
function projectListKey(source) {
|
|
11
|
+
return `projects:list:${source}`;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function projectCountKey(source) {
|
|
15
|
+
return `dashboard:counts:${source}`;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function emptyProjectList(currentProject = null, extra = {}) {
|
|
19
|
+
return {
|
|
20
|
+
projects: [],
|
|
21
|
+
currentProject,
|
|
22
|
+
...extra
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async function getProjectListSnapshot(source, {
|
|
27
|
+
fallbackValue,
|
|
28
|
+
refresh,
|
|
29
|
+
force = false
|
|
30
|
+
}) {
|
|
31
|
+
return getSnapshot(projectListKey(source), {
|
|
32
|
+
ttlMs: PROJECT_SNAPSHOT_TTL_MS,
|
|
33
|
+
fallbackValue,
|
|
34
|
+
refresh,
|
|
35
|
+
force,
|
|
36
|
+
backgroundOnMiss: !force
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function invalidateProjectSnapshots(source) {
|
|
41
|
+
invalidateSnapshot(projectListKey(source));
|
|
42
|
+
invalidateSnapshot(projectCountKey(source));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
module.exports = {
|
|
46
|
+
PROJECT_SNAPSHOT_TTL_MS,
|
|
47
|
+
projectListKey,
|
|
48
|
+
projectCountKey,
|
|
49
|
+
emptyProjectList,
|
|
50
|
+
getProjectListSnapshot,
|
|
51
|
+
invalidateProjectSnapshots
|
|
52
|
+
};
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const os = require('os');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const { PATHS } = require('../../config/paths');
|
|
7
|
+
|
|
8
|
+
const DEFAULT_TTL_MS = 60 * 1000;
|
|
9
|
+
const SNAPSHOT_VERSION = 1;
|
|
10
|
+
const DEFAULT_BACKGROUND_DELAY_MS = process.env.NODE_ENV === 'test' ? 0 : 750;
|
|
11
|
+
const DEFAULT_BACKGROUND_GAP_MS = process.env.NODE_ENV === 'test' ? 0 : 100;
|
|
12
|
+
|
|
13
|
+
const snapshots = new Map();
|
|
14
|
+
const inflight = new Map();
|
|
15
|
+
const deferredQueue = [];
|
|
16
|
+
let deferredRunning = false;
|
|
17
|
+
|
|
18
|
+
function getSnapshotDir() {
|
|
19
|
+
return PATHS.snapshotCache
|
|
20
|
+
|| path.join(PATHS.cache || path.join(PATHS.storage || os.tmpdir(), 'cache'), 'snapshots');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function cloneJson(value) {
|
|
24
|
+
if (value === undefined) return value;
|
|
25
|
+
try {
|
|
26
|
+
return JSON.parse(JSON.stringify(value));
|
|
27
|
+
} catch {
|
|
28
|
+
return value;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function encodeKey(key) {
|
|
33
|
+
return Buffer.from(String(key)).toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, '');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function getSnapshotPath(key) {
|
|
37
|
+
return path.join(getSnapshotDir(), `${encodeKey(key)}.json`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function normalizeEntry(key, payload) {
|
|
41
|
+
if (!payload || typeof payload !== 'object') return null;
|
|
42
|
+
if (!Object.prototype.hasOwnProperty.call(payload, 'value')) return null;
|
|
43
|
+
const updatedAtMs = Number(payload.updatedAtMs || Date.parse(payload.generatedAt || ''));
|
|
44
|
+
return {
|
|
45
|
+
key,
|
|
46
|
+
value: cloneJson(payload.value),
|
|
47
|
+
generatedAt: payload.generatedAt || (Number.isFinite(updatedAtMs) ? new Date(updatedAtMs).toISOString() : null),
|
|
48
|
+
updatedAtMs: Number.isFinite(updatedAtMs) ? updatedAtMs : 0,
|
|
49
|
+
lastError: typeof payload.lastError === 'string' ? payload.lastError : null
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function readPersistedSnapshot(key) {
|
|
54
|
+
try {
|
|
55
|
+
const filePath = getSnapshotPath(key);
|
|
56
|
+
if (!fs.existsSync(filePath)) return null;
|
|
57
|
+
const payload = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
58
|
+
return normalizeEntry(key, payload);
|
|
59
|
+
} catch {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function getEntry(key) {
|
|
65
|
+
if (snapshots.has(key)) {
|
|
66
|
+
return snapshots.get(key);
|
|
67
|
+
}
|
|
68
|
+
const persisted = readPersistedSnapshot(key);
|
|
69
|
+
if (persisted) {
|
|
70
|
+
snapshots.set(key, persisted);
|
|
71
|
+
}
|
|
72
|
+
return persisted;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function persistEntry(key, entry) {
|
|
76
|
+
try {
|
|
77
|
+
const filePath = getSnapshotPath(key);
|
|
78
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
79
|
+
const tempPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
80
|
+
fs.writeFileSync(tempPath, JSON.stringify({
|
|
81
|
+
version: SNAPSHOT_VERSION,
|
|
82
|
+
key,
|
|
83
|
+
value: entry.value,
|
|
84
|
+
generatedAt: entry.generatedAt,
|
|
85
|
+
updatedAtMs: entry.updatedAtMs,
|
|
86
|
+
lastError: entry.lastError || null
|
|
87
|
+
}, null, 2), 'utf8');
|
|
88
|
+
fs.renameSync(tempPath, filePath);
|
|
89
|
+
} catch {
|
|
90
|
+
// Persistence is an optimization; in-memory snapshots still protect the request path.
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function makeEntry(key, value, nowMs = Date.now()) {
|
|
95
|
+
return {
|
|
96
|
+
key,
|
|
97
|
+
value: cloneJson(value),
|
|
98
|
+
generatedAt: new Date(nowMs).toISOString(),
|
|
99
|
+
updatedAtMs: nowMs,
|
|
100
|
+
lastError: null
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function pumpDeferredQueue() {
|
|
105
|
+
if (deferredRunning) return;
|
|
106
|
+
const task = deferredQueue.shift();
|
|
107
|
+
if (!task) return;
|
|
108
|
+
|
|
109
|
+
deferredRunning = true;
|
|
110
|
+
setTimeout(() => {
|
|
111
|
+
Promise.resolve()
|
|
112
|
+
.then(() => task.refresh())
|
|
113
|
+
.then(task.resolve, task.reject)
|
|
114
|
+
.finally(() => {
|
|
115
|
+
deferredRunning = false;
|
|
116
|
+
setTimeout(pumpDeferredQueue, task.gapMs);
|
|
117
|
+
});
|
|
118
|
+
}, task.delayMs);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function runDeferredRefresh(refresh, delayMs = DEFAULT_BACKGROUND_DELAY_MS, gapMs = DEFAULT_BACKGROUND_GAP_MS) {
|
|
122
|
+
return new Promise((resolve, reject) => {
|
|
123
|
+
deferredQueue.push({ refresh, resolve, reject, delayMs, gapMs });
|
|
124
|
+
pumpDeferredQueue();
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function runRefresh(refresh, defer, deferMs) {
|
|
129
|
+
if (!defer) {
|
|
130
|
+
return Promise.resolve().then(() => refresh());
|
|
131
|
+
}
|
|
132
|
+
return runDeferredRefresh(refresh, deferMs);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function refreshSnapshot(key, refresh, { defer = false, deferMs = DEFAULT_BACKGROUND_DELAY_MS } = {}) {
|
|
136
|
+
if (inflight.has(key)) {
|
|
137
|
+
return inflight.get(key);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const promise = runRefresh(refresh, defer, deferMs)
|
|
141
|
+
.then((value) => {
|
|
142
|
+
const entry = makeEntry(key, value);
|
|
143
|
+
snapshots.set(key, entry);
|
|
144
|
+
persistEntry(key, entry);
|
|
145
|
+
return entry;
|
|
146
|
+
})
|
|
147
|
+
.catch((error) => {
|
|
148
|
+
const existing = getEntry(key);
|
|
149
|
+
if (existing) {
|
|
150
|
+
const retained = {
|
|
151
|
+
...existing,
|
|
152
|
+
lastError: error?.message || String(error)
|
|
153
|
+
};
|
|
154
|
+
snapshots.set(key, retained);
|
|
155
|
+
persistEntry(key, retained);
|
|
156
|
+
}
|
|
157
|
+
throw error;
|
|
158
|
+
})
|
|
159
|
+
.finally(() => {
|
|
160
|
+
inflight.delete(key);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
inflight.set(key, promise);
|
|
164
|
+
promise.catch(() => {});
|
|
165
|
+
return promise;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function isExpired(entry, ttlMs, nowMs) {
|
|
169
|
+
if (!entry) return true;
|
|
170
|
+
return nowMs - Number(entry.updatedAtMs || 0) >= ttlMs;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function makeMeta(entry, {
|
|
174
|
+
stale,
|
|
175
|
+
refreshing,
|
|
176
|
+
fallback,
|
|
177
|
+
error
|
|
178
|
+
}) {
|
|
179
|
+
return {
|
|
180
|
+
generatedAt: entry?.generatedAt || null,
|
|
181
|
+
stale: Boolean(stale),
|
|
182
|
+
refreshing: Boolean(refreshing),
|
|
183
|
+
fallback: Boolean(fallback),
|
|
184
|
+
error: error || entry?.lastError || null
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async function getSnapshot(key, {
|
|
189
|
+
ttlMs = DEFAULT_TTL_MS,
|
|
190
|
+
fallbackValue = null,
|
|
191
|
+
refresh,
|
|
192
|
+
backgroundOnMiss = true,
|
|
193
|
+
force = false,
|
|
194
|
+
nowMs = Date.now(),
|
|
195
|
+
deferMs = DEFAULT_BACKGROUND_DELAY_MS
|
|
196
|
+
} = {}) {
|
|
197
|
+
if (typeof refresh !== 'function') {
|
|
198
|
+
const entry = getEntry(key);
|
|
199
|
+
return {
|
|
200
|
+
value: cloneJson(entry ? entry.value : fallbackValue),
|
|
201
|
+
meta: makeMeta(entry, {
|
|
202
|
+
stale: !entry,
|
|
203
|
+
refreshing: false,
|
|
204
|
+
fallback: !entry
|
|
205
|
+
})
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
let entry = force ? null : getEntry(key);
|
|
210
|
+
let expired = force || isExpired(entry, ttlMs, nowMs);
|
|
211
|
+
let refreshPromise = null;
|
|
212
|
+
let refreshError = null;
|
|
213
|
+
|
|
214
|
+
if (expired) {
|
|
215
|
+
if (!entry && backgroundOnMiss === false) {
|
|
216
|
+
try {
|
|
217
|
+
entry = await refreshSnapshot(key, refresh);
|
|
218
|
+
expired = false;
|
|
219
|
+
} catch (error) {
|
|
220
|
+
refreshError = error?.message || String(error);
|
|
221
|
+
entry = getEntry(key);
|
|
222
|
+
if (!entry) throw error;
|
|
223
|
+
}
|
|
224
|
+
} else {
|
|
225
|
+
refreshPromise = refreshSnapshot(key, refresh, { defer: true, deferMs });
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const hasEntry = Boolean(entry);
|
|
230
|
+
return {
|
|
231
|
+
value: cloneJson(hasEntry ? entry.value : fallbackValue),
|
|
232
|
+
meta: makeMeta(entry, {
|
|
233
|
+
stale: !hasEntry || expired,
|
|
234
|
+
refreshing: Boolean(refreshPromise || inflight.has(key)),
|
|
235
|
+
fallback: !hasEntry,
|
|
236
|
+
error: refreshError
|
|
237
|
+
})
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function invalidateSnapshot(keyOrPrefix) {
|
|
242
|
+
if (!keyOrPrefix) {
|
|
243
|
+
snapshots.clear();
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const key = String(keyOrPrefix);
|
|
248
|
+
snapshots.delete(key);
|
|
249
|
+
for (const existingKey of Array.from(snapshots.keys())) {
|
|
250
|
+
if (existingKey.startsWith(key)) {
|
|
251
|
+
snapshots.delete(existingKey);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
try {
|
|
256
|
+
const dir = getSnapshotDir();
|
|
257
|
+
if (!fs.existsSync(dir)) return;
|
|
258
|
+
const exactFile = getSnapshotPath(key);
|
|
259
|
+
if (fs.existsSync(exactFile)) {
|
|
260
|
+
fs.unlinkSync(exactFile);
|
|
261
|
+
}
|
|
262
|
+
} catch {
|
|
263
|
+
// Best-effort invalidation; the in-memory state has already been cleared.
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function clearSnapshotCache({ deleteFiles = false } = {}) {
|
|
268
|
+
snapshots.clear();
|
|
269
|
+
inflight.clear();
|
|
270
|
+
deferredQueue.splice(0, deferredQueue.length);
|
|
271
|
+
deferredRunning = false;
|
|
272
|
+
if (!deleteFiles) return;
|
|
273
|
+
try {
|
|
274
|
+
fs.rmSync(getSnapshotDir(), { recursive: true, force: true });
|
|
275
|
+
} catch {
|
|
276
|
+
// ignore cleanup failures in tests
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
module.exports = {
|
|
281
|
+
getSnapshot,
|
|
282
|
+
refreshSnapshot,
|
|
283
|
+
invalidateSnapshot,
|
|
284
|
+
clearSnapshotCache,
|
|
285
|
+
_test: {
|
|
286
|
+
getSnapshotDir,
|
|
287
|
+
getSnapshotPath,
|
|
288
|
+
encodeKey,
|
|
289
|
+
readPersistedSnapshot,
|
|
290
|
+
getEntry,
|
|
291
|
+
inflight,
|
|
292
|
+
snapshots,
|
|
293
|
+
deferredQueue
|
|
294
|
+
}
|
|
295
|
+
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{d as e}from"./vendors-DQA4Hw9v.js";import{r as a,a8 as l,f as n,w as s,h as c,K as t,a7 as o,c as i,o as u,a as r,W as d,a0 as v,X as p,Y as h,N as y,a3 as m,_ as g,p as f,R as k,L as T,F as C,$ as w,Z as b,v as x}from"./vue-vendor-B5RqjEjQ.js";import{g as $,u as _,a as S,s as z,D as q,_ as P,b as j,c as L,d as M,R as I,e as O,f as E,h as N,i as F,j as A,k as D,l as U,m as J,n as V,o as R,p as K}from"./index-Bnni1zPN.js";import{h as B,ar as Z,E as G,P as H,as as W,at as X,au as Y,av as Q,aa as ee,i as ae,A as le,aw as ne,ax as se,T as ce,a as te,b as oe}from"./icons-Dop5j7Bf.js";import{L as ie,N as ue,B as re,s as de,b as ve,c as pe,a as he,Z as ye}from"./naive-ui-CVyvdmg1.js";import"./markdown-BNJfEgCA.js";const me=a({theme:"light",panelVisibility:{showChannels:!0,showLogs:!0},channelBalance:{showRemaining:!1},channelLocks:{claude:!1,codex:!1,gemini:!1,opencode:!1,omp:!1},channelCollapse:{claude:[],codex:[],gemini:[],opencode:[],omp:[]},channelOrder:{claude:[],codex:[],gemini:[],opencode:[],omp:[]},dashboardChannelOrder:q,homeCliColumns:q,customCliPlatforms:[]});let ge=!1,fe=null;async function ke(){return ge?me.value:fe||(fe=(async()=>{try{const e=await $();e.success&&e.config&&(me.value=e.config,ge=!0)}catch(e){}finally{fe=null}return me.value})(),fe)}function Te(){return ge||ke(),{uiConfig:me,loadUIConfig:ke,saveConfig:async function(e){try{const a=await z(e);return!!a.success&&(me.value=a.config,!0)}catch(a){return!1}},updateConfig:async function(e,a){try{const l=await S(e,a);return!!l.success&&(me.value=l.config,!0)}catch(l){return!1}},updateNestedConfig:async function(e,a,l){try{const n=await _(e,a,l);return!!n.success&&(me.value=n.config,!0)}catch(n){return!1}}}}const Ce={class:"channel-column"},we={class:"drag-handle",title:"拖拽排序"},be={class:"header-icon"},xe={class:"channel-title"},$e={key:0,class:"claude-extra-area"},_e={key:0,class:"channel-content"},Se={key:0,class:"card"},ze={class:"card-header"},qe={key:0,class:"runtime-badge"},Pe={class:"card-body",style:{padding:"6px 10px"}},je={class:"proxy-info-row"},Le={class:"proxy-status"},Me={class:"proxy-port"},Ie={class:"channel-name"},Oe={class:"channel-quick-panel"},Ee={class:"panel-title"},Ne={key:0,class:"no-channels"},Fe={key:1,class:"channel-quick-list"},Ae={class:"channel-quick-info"},De={class:"channel-quick-name"},Ue={key:0,class:"channel-switching-tip"},Je={class:"channel-metrics"},Ve={class:"metric-item"},Re={class:"metric-value"},Ke={class:"metric-item"},Be={class:"metric-value"},Ze={class:"metric-item"},Ge={class:"metric-value"},He={class:"metric-item"},We={class:"card"},Xe={class:"card-header compact"},Ye={class:"card-body",style:{padding:"8px 10px"}},Qe={class:"quick-access-list"},ea={class:"access-icon"},aa={class:"access-content"},la={class:"access-value"},na={class:"access-icon"},sa={class:"access-content"},ca={class:"access-value"},ta={class:"access-icon"},oa={class:"access-content"},ia={class:"access-goto"},ua={class:"card-header compact"},ra={class:"card-body",style:{padding:"8px 10px"}},da={class:"stats-inline stats-3col"},va={class:"stat-inline-item stat-requests"},pa={class:"stat-info"},ha={class:"stat-inline-item stat-input"},ya={class:"stat-info"},ma={class:"stat-inline-item stat-output"},ga={class:"stat-info"},fa={key:2,class:"card logs-card"},ka={class:"card-header compact"},Ta={class:"card-body logs-card-body"},Ca={class:"logs-table-wrapper"},wa={key:0,class:"empty-logs"},ba=["title"],xa={key:0,class:"action-content"},$a={class:"action-msg"},_a={class:"action-time"},Sa={key:1,class:"action-content"},za={class:"action-msg error-msg"},qa={class:"action-time error-time"},Pa={class:"locked-content"},ja={class:"lock-icon"},La="channelLocks",Ma=P({__name:"ChannelColumn",props:{channelType:{type:String,required:!0}},setup(e){const S=e,z=l(),q=ie(),{uiConfig:P}=Te(),{claudeProxy:K,codexProxy:me,geminiProxy:ge,opencodeProxy:fe,ompProxy:ke,claudeChannels:Ma,codexChannels:Ia,geminiChannels:Oa,opencodeChannels:Ea,ompChannels:Na,schedulerState:Fa,getProxyState:Aa,startProxy:Da,stopProxy:Ua,getLogs:Ja,clearLogsForSource:Va,loadChannels:Ra,logLimit:Ka,statsInterval:Ba}=j(),{dashboardData:Za,loadDashboard:Ga}=L(),Ha=n(()=>M(S.channelType,P.value.customCliPlatforms)),Wa=n(()=>Ha.value.title||Ha.value.label||S.channelType),Xa=n(()=>Ha.value.icon||B),Ya=n(()=>Ha.value.color||"#64748b"),Qa=n(()=>"claude"===S.channelType?K.value:"codex"===S.channelType?me.value:"gemini"===S.channelType?ge.value:"opencode"===S.channelType?fe.value:"omp"===S.channelType?ke.value:Ha.value.custom?{running:!1,loading:!1,activeChannel:null,port:null,runtime:null,startTime:null}:{}),el=n(()=>"claude"===S.channelType?"Claude":"codex"===S.channelType?"Codex":"gemini"===S.channelType?"Gemini":"opencode"===S.channelType?"OpenCode":"omp"===S.channelType?"OMP":Ha.value.label||S.channelType),al=a({projects:0,sessions:0}),ll=a({requests:0,tokens:0,cost:0}),nl=a({requests:0,tokens:0,cost:0}),sl={requests:0,tokens:0,cost:3};let cl={requests:null,tokens:null,cost:null};const tl=a({requests:!1,tokens:!1,cost:!1}),ol=n(()=>{var e,a,l;return(null==(l=null==(a=null==(e=Za.value)?void 0:e.todayStats)?void 0:a[S.channelType])?void 0:l.byModel)||{}});function il(e,a,l,n=600){cl[e]&&cancelAnimationFrame(cl[e]),tl.value[e]=!0;const s=Date.now(),c=()=>{const t=Date.now()-s,o=Math.min(t/n,1),i=1-Math.pow(1-o,2),u=a+(l-a)*i,r=sl[e]??0,d=Math.pow(10,r),v=r>0?Math.round(u*d)/d:Math.round(u);nl.value[e]=v,o<1?cl[e]=requestAnimationFrame(c):tl.value[e]=!1};cl[e]=requestAnimationFrame(c)}const ul=a(!1),rl={claude:"claudeChannelCollapse",codex:"codexChannelCollapse",gemini:"geminiChannelCollapse",opencode:"opencodeChannelCollapse",omp:"ompChannelCollapse"};function dl(e){try{const a=localStorage.getItem(La),l=a?JSON.parse(a):{};l[S.channelType]=e,localStorage.setItem(La,JSON.stringify(l))}catch(a){}}const vl=a(function(){try{const e=localStorage.getItem(La);if(e){return JSON.parse(e)[S.channelType]||!1}}catch(e){}return!1}());async function pl(){vl.value=!vl.value,dl(vl.value);try{if(await _("channelLocks",S.channelType,vl.value),Za.value){const e=Za.value.uiConfig||{},a={...e.channelLocks||{}};a[S.channelType]=vl.value,Za.value={...Za.value,uiConfig:{...e,channelLocks:a}}}}catch(e){}}const hl=a(!0);function yl(e){e.detail&&void 0!==e.detail.showLogs&&(hl.value=e.detail.showLogs)}const ml=a(Date.now()),gl=n(()=>{if(!Qa.value.running)return"";const e=Qa.value.startTime;if(!e)return"";const a=ml.value-e;if(a<=0)return"";const l=Math.floor(a/1e3),n=Math.floor(l/3600),s=Math.floor(l%3600/60),c=l%60;let t="已运行 ";return n>0&&(t+=`${n}小时`),s>0&&(t+=`${s}分`),(c>0||0===n&&0===s)&&(t+=`${c}秒`),t}),fl=a(null),kl=n(()=>Ka.value),Tl={claude:Ja("claude"),codex:Ja("codex"),gemini:Ja("gemini"),opencode:Ja("opencode"),omp:Ja("omp")},Cl=n(()=>{if(!Il())return[];return((Tl[S.channelType]||Tl.claude).value||[]).slice(0,kl.value)});let wl=null,bl=null,xl=null,$l=!1,_l=null;const Sl=n(()=>{let e=[];"claude"===S.channelType?e=Ma.value||[]:"codex"===S.channelType?e=Ia.value||[]:"gemini"===S.channelType?e=Oa.value||[]:"opencode"===S.channelType?e=Ea.value||[]:"omp"===S.channelType&&(e=Na.value||[]);return[...e.filter(e=>!1!==e.enabled),...e.filter(e=>!1===e.enabled)]});function zl(e){const a=Fa[S.channelType];if(!a||!a.channels)return 0;const l=a.channels.find(a=>a.id===e);return l?l.inflight:0}const ql=n(()=>{const e=Sl.value.filter(e=>!1!==e.enabled).length;return Qa.value.running?`${e}个渠道调度中`:Sl.value.length>0?`${e}个渠道已启用`:"无渠道"}),Pl=a({});function jl(e){return Pl.value[e]||{requests:0,tokens:0,cost:0}}async function Ll(){var e;try{let a;if("claude"===S.channelType?a=await O():"codex"===S.channelType?a=await E():"gemini"===S.channelType?a=await N():"opencode"===S.channelType?a=await F():"omp"===S.channelType&&(a=await A()),a&&a.byChannel){const l={};for(const[n,s]of Object.entries(a.byChannel))l[n]={requests:s.requests||0,tokens:(null==(e=s.tokens)?void 0:e.total)||0,cost:s.cost||0};Pl.value=l}}catch(a){}}function Ml(e,a){var l;return(null==e?void 0:e.usageMissing)?"--":(null==(l=null==e?void 0:e.tokens)?void 0:l[a])||0}function Il(){return["claude","codex","gemini","opencode","omp"].includes(S.channelType)}function Ol(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 El(e){var a,l;if(!1===Ha.value.supportsProxy||!Il())return void q.warning(`${Wa.value} 暂未接入动态代理`);const n=Aa(S.channelType);n.value.loading=!0;try{let a;a=e?await Da(S.channelType):await Ua(S.channelType,{refreshChannelsDrawer:!0}),!1!==a.success?q.success(e?`${Wa.value} 代理已启动`:`${Wa.value} 代理已停止`):(q.error(a.error||"操作失败"),n.value.running=!e)}catch(s){q.error((null==(l=null==(a=s.response)?void 0:a.data)?void 0:l.error)||s.message||"操作失败"),n.value.running=!e}finally{n.value.loading=!1}}function Nl(){!1!==Ha.value.supportsProjects&&Il()?z.push({name:`${S.channelType}-projects`}):q.info(`${Wa.value} 当前仅作为首页显示项`)}function Fl(){!1!==Ha.value.supportsProjects&&Il()?z.push({name:`${S.channelType}-projects`}):q.info(`${Wa.value} 当前仅作为首页显示项`)}function Al(){!1!==Ha.value.supportsSessions&&Il()?ul.value=!0:q.info(`${Wa.value} 当前没有托管会话列表`)}s(Cl,e=>{var a;const l=(null==(a=e[0])?void 0:a.id)||null;if(!l||l===wl)return void(wl=l);wl=l,_l&&clearTimeout(_l),_l=setTimeout(()=>{Ll()},5e3);(!fl.value||fl.value.scrollTop<20)&&x(()=>{fl.value&&(fl.value.scrollTop=0)})}),s(()=>ll.value.requests,e=>{il("requests",nl.value.requests,e,600)}),s(()=>ll.value.tokens,e=>{il("tokens",nl.value.tokens,e,600)}),s(()=>ll.value.cost,e=>{il("cost",nl.value.cost,e,600)}),s(()=>S.channelType,()=>{var e;wl=(null==(e=Cl.value[0])?void 0:e.id)||null}),s(()=>{var e,a;return null==(a=null==(e=Za.value)?void 0:e.counts)?void 0:a[S.channelType]},()=>{Rl()}),s(Ba,()=>{$l&&function(){bl&&(clearInterval(bl),bl=null);const e=Ba.value||30,a=Math.max(1e3*e,1e4);bl=setInterval(()=>{Kl(),Ll()},a)}()});const Dl=a({});function Ul(e){return!!Dl.value[e]}function Jl(e,a){e&&(Dl.value={...Dl.value,[e]:!!a})}async function Vl(e,a){if(e&&!Ul(e.id)){Jl(e.id,!0);try{let l;"claude"===S.channelType?l=D:"codex"===S.channelType?l=U:"gemini"===S.channelType?l=J:"opencode"===S.channelType?l=V:"omp"===S.channelType&&(l=R),l&&(await l(e.id,{enabled:a}),await async function(e,a){if(!e)return;const l=rl[S.channelType];let n={};try{const e=l?localStorage.getItem(l):null;n=e?JSON.parse(e):{}}catch(s){n={}}a?delete n[e]:n[e]=!0;try{l&&localStorage.setItem(l,JSON.stringify(n))}catch(s){}try{await _("channelCollapse",S.channelType,n)}catch(s){}}(e.id,a),q.success(a?`渠道「${e.name}」已启用`:`渠道「${e.name}」已停用`),await Ra(),window.dispatchEvent(new CustomEvent("channel-management-refresh",{detail:{channel:S.channelType}})))}catch(l){q.error("操作失败: "+l.message)}finally{Jl(e.id,!1)}}}function Rl(){var e,a;const l=null==(a=null==(e=Za.value)?void 0:e.counts)?void 0:a[S.channelType];al.value.projects=(null==l?void 0:l.projectCount)||0,al.value.sessions=(null==l?void 0:l.sessionCount)||0}async function Kl(){if(Za.value&&Za.value.todayStats){const e=Za.value.todayStats[S.channelType];e&&(ll.value.requests=e.requests||0,ll.value.tokens=e.tokens||0,ll.value.cost=e.cost||0)}Rl()}function Bl(){Va(S.channelType)}function Zl(e){var a,l;null==(l=null==(a=null==e?void 0:e.currentTarget)?void 0:a.blur)||l.call(a)}function Gl(e){window.dispatchEvent(new CustomEvent("open-skills-drawer",{detail:{platform:S.channelType}})),Zl(e)}function Hl(e){window.dispatchEvent(new CustomEvent("open-commands-drawer",{detail:{platform:S.channelType}})),Zl(e)}function Wl(e){if(!1===Ha.value.supportsAgents)return q.info(`${Wa.value} 的 Agent 能力通过插件/扩展提供`),void Zl(e);window.dispatchEvent(new CustomEvent("open-agents-drawer",{detail:{platform:S.channelType}})),Zl(e)}return c(async()=>{await Promise.all([Ga().then(()=>Kl()),Ll()]),async function(){var e,a;try{if(Za.value&&Za.value.uiConfig)hl.value=!1!==(null==(e=Za.value.uiConfig.panelVisibility)?void 0:e.showLogs);else{const e=await $();e.success&&e.config&&(hl.value=!1!==(null==(a=e.config.panelVisibility)?void 0:a.showLogs))}}catch(l){}}(),async function(){var e,a;try{let l=!1;if(Za.value&&Za.value.uiConfig)l=(null==(e=Za.value.uiConfig.channelLocks)?void 0:e[S.channelType])||!1;else{const e=await $();e.success&&e.config&&(l=(null==(a=e.config.channelLocks)?void 0:a[S.channelType])||!1)}vl.value=l,dl(l)}catch(l){}}(),window.addEventListener("panel-visibility-change",yl),nl.value={...ll.value},$l=!0,xl=setInterval(()=>{ml.value=Date.now()},1e3)}),t(()=>{$l=!1,bl&&clearInterval(bl),xl&&clearInterval(xl),_l&&clearTimeout(_l),window.removeEventListener("panel-visibility-change",yl),Object.values(cl).forEach(e=>{e&&cancelAnimationFrame(e)})}),(a,l)=>{const n=o("n-collapse");return u(),i("div",Ce,[r("div",{class:T(["channel-header",[e.channelType,{"custom-cli":Ha.value.custom}]]),style:k(Ha.value.custom?{"--channel-accent":Ya.value}:null)},[r("div",we,[p(y(ue),{size:16},{default:h(()=>[p(y(Z))]),_:1})]),r("div",be,[p(y(ue),{size:20},{default:h(()=>[(u(),v(m(Xa.value)))]),_:1})]),r("h2",xe,g(Wa.value),1),!1!==Ha.value.supportsManagedConfig?(u(),i("div",$e,[p(y(de),{trigger:"hover"},{trigger:h(()=>[p(y(re),{text:"",class:"config-shortcut-button",onClick:Gl,title:"Skills 技能管理"},{icon:h(()=>[p(y(ue),{size:18},{default:h(()=>[p(y(G))]),_:1})]),_:1})]),default:h(()=>[l[2]||(l[2]=f(" Skills 技能管理 ",-1))]),_:1}),p(y(de),{trigger:"hover"},{trigger:h(()=>[p(y(re),{text:"",class:"config-shortcut-button",onClick:Hl,title:"Commands 命令管理"},{icon:h(()=>[p(y(ue),{size:18},{default:h(()=>[p(y(B))]),_:1})]),_:1})]),default:h(()=>[l[3]||(l[3]=f(" Commands 命令管理 ",-1))]),_:1}),!1!==Ha.value.supportsAgents?(u(),v(y(de),{key:0,trigger:"hover"},{trigger:h(()=>[p(y(re),{text:"",class:"config-shortcut-button",onClick:Wl,title:"Agents 代理管理"},{icon:h(()=>[p(y(ue),{size:18},{default:h(()=>[p(y(H))]),_:1})]),_:1})]),default:h(()=>[l[4]||(l[4]=f(" Agents 代理管理 ",-1))]),_:1})):d("",!0)])):d("",!0),p(y(re),{text:"",class:"lock-button",onClick:pl,title:vl.value?"解锁此列":"锁定此列"},{icon:h(()=>[p(y(ue),{size:18},{default:h(()=>[vl.value?(u(),v(y(W),{key:0})):(u(),v(y(X),{key:1}))]),_:1})]),_:1},8,["title"])],6),vl.value?d("",!0):(u(),i("div",_e,[!1!==Ha.value.supportsProxy?(u(),i("div",Se,[r("div",ze,[p(y(ue),{size:16},{default:h(()=>[p(y(Y))]),_:1}),l[5]||(l[5]=r("h3",{class:"card-title"},"代理控制",-1)),Qa.value.running&&gl.value?(u(),i("span",qe,g(gl.value),1)):d("",!0),p(y(ve),{value:Qa.value.running,"onUpdate:value":[l[0]||(l[0]=e=>Qa.value.running=e),El],loading:Qa.value.loading,size:"small",style:{"margin-left":"auto"}},null,8,["value","loading"])]),r("div",Pe,[r("div",je,[r("div",Le,[r("div",{class:T(["status-dot",{active:Qa.value.running}])},null,2),p(y(pe),{type:Qa.value.running?"success":"default",style:{"font-size":"12px"}},{default:h(()=>[f(g(Qa.value.running?"运行中":"已停止"),1)]),_:1},8,["type"]),r("span",Me,"端口: "+g(Qa.value.port),1)]),p(y(ye),{trigger:"click",placement:"bottom",width:320,class:"channel-popover"},{trigger:h(()=>[p(y(re),{text:"",size:"tiny",class:"channel-status"},{default:h(()=>[r("span",Ie,g(ql.value),1)]),_:1})]),default:h(()=>[r("div",Oe,[r("div",Ee,[l[7]||(l[7]=r("span",null,"渠道快捷管理",-1)),p(y(pe),{depth:"3",style:{"font-size":"11px"}},{default:h(()=>[...l[6]||(l[6]=[f("点击开关切换状态",-1)])]),_:1})]),0===Sl.value.length?(u(),i("div",Ne,[p(y(pe),{depth:"3"},{default:h(()=>[...l[8]||(l[8]=[f("暂无配置渠道",-1)])]),_:1})])):(u(),i("div",Fe,[(u(!0),i(C,null,w(Sl.value,e=>{var a,n;return u(),i("div",{key:e.id,class:T(["channel-quick-item",{disabled:!1===e.enabled}])},[r("div",Ae,[r("span",De,g(e.name),1),"frozen"===(null==(a=e.health)?void 0:a.status)?(u(),v(y(he),{key:0,size:"tiny",type:"error",bordered:!1},{default:h(()=>[...l[9]||(l[9]=[f(" 冻结 ",-1)])]),_:1})):d("",!0),p(y(ve),{size:"small",value:!1!==e.enabled,"onUpdate:value":a=>Vl(e,a),loading:Ul(e.id),disabled:Ul(e.id),style:{"margin-left":"auto"}},null,8,["value","onUpdate:value","loading","disabled"])]),Ul(e.id)?(u(),i("div",Ue," 正在切换渠道状态... ")):d("",!0),r("div",Je,[r("span",Ve,[l[10]||(l[10]=r("span",{class:"metric-label"},"请求",-1)),r("span",Re,g(jl(e.id).requests),1)]),r("span",Ke,[l[11]||(l[11]=r("span",{class:"metric-label"},"Tokens",-1)),r("span",Be,g((n=jl(e.id).tokens,n>=1e6?(n/1e6).toFixed(1)+"M":n>=1e3?(n/1e3).toFixed(1)+"K":n.toString())),1)]),r("span",Ze,[l[12]||(l[12]=r("span",{class:"metric-label"},"权重",-1)),r("span",Ge,g(e.weight||1),1)]),r("span",He,[l[13]||(l[13]=r("span",{class:"metric-label"},"并发",-1)),r("span",{class:T(["metric-value",{active:zl(e.id)>0}])},g(zl(e.id))+g(e.maxConcurrency?`/${e.maxConcurrency}`:""),3)])])],2)}),128))]))])]),_:1})])])])):d("",!0),r("div",We,[r("div",Xe,[p(y(ue),{size:14},{default:h(()=>[p(y(Q))]),_:1}),l[14]||(l[14]=r("h3",{class:"card-title"},"快速访问",-1))]),r("div",Ye,[r("div",Qe,[r("div",{class:T(["access-card access-card-projects",{clickable:!1!==Ha.value.supportsProjects,disabled:!1===Ha.value.supportsProjects}]),onClick:Nl},[r("div",ea,[p(y(ue),{size:16},{default:h(()=>[p(y(ee))]),_:1})]),r("div",aa,[l[15]||(l[15]=r("span",{class:"access-label"},"项目",-1)),r("span",la,g(al.value.projects),1)])],2),r("div",{class:T(["access-card access-card-sessions",{clickable:!1!==Ha.value.supportsSessions,disabled:!1===Ha.value.supportsSessions}]),onClick:Al},[r("div",na,[p(y(ue),{size:16},{default:h(()=>[p(y(ae))]),_:1})]),r("div",sa,[l[16]||(l[16]=r("span",{class:"access-label"},"最新对话",-1)),r("span",ca,g(al.value.sessions),1)])],2),r("div",{class:T(["access-card access-card-goto",{clickable:!1!==Ha.value.supportsProjects,disabled:!1===Ha.value.supportsProjects}]),onClick:Fl},[r("div",ta,[p(y(ue),{size:16},{default:h(()=>[p(y(le))]),_:1})]),r("div",oa,[l[17]||(l[17]=r("span",{class:"access-label"},"前往",-1)),r("span",ia,g(el.value),1)])],2)])])]),r("div",{class:T(["card stats-card",`stats-card-${e.channelType}`])},[r("div",ua,[p(y(ue),{size:14},{default:h(()=>[p(y(ne))]),_:1}),l[18]||(l[18]=r("h3",{class:"card-title"},"今日数据",-1))]),r("div",ra,[r("div",da,[r("div",va,[l[20]||(l[20]=r("div",{class:"stat-icon-dot requests"},null,-1)),r("div",pa,[l[19]||(l[19]=r("span",{class:"stat-label"},"请求",-1)),r("span",{class:T(["stat-value",{animating:tl.value.requests}])},g(nl.value.requests),3)])]),r("div",ha,[l[22]||(l[22]=r("div",{class:"stat-icon-dot tokens"},null,-1)),r("div",ya,[l[21]||(l[21]=r("span",{class:"stat-label"},"总 Tokens",-1)),r("span",{class:T(["stat-value",{animating:tl.value.tokens}])},g((c=nl.value.tokens,c>=1e6?(c/1e6).toFixed(1)+"M":c>=1e3?(c/1e3).toFixed(1)+"K":c.toString())),3)])]),r("div",ma,[l[24]||(l[24]=r("div",{class:"stat-icon-dot cost"},null,-1)),r("div",ga,[l[23]||(l[23]=r("span",{class:"stat-label"},"成本 / USD",-1)),r("span",{class:T(["stat-value",{animating:tl.value.cost}])},g((s=nl.value.cost,!s||Number.isNaN(s)?"$0":"$"+Number(s).toFixed(3))),3)])])])])],2),Object.keys(ol.value).length>0?(u(),i("div",{key:1,class:T(["card chart-card",`chart-card-${e.channelType}`])},[p(n,{"default-expanded-names":[],accordion:""})],2)):d("",!0),hl.value?(u(),i("div",fa,[r("div",ka,[p(y(ue),{size:14},{default:h(()=>[p(y(se))]),_:1}),l[25]||(l[25]=r("h3",{class:"card-title"},"实时日志",-1)),p(y(re),{text:"",size:"tiny",onClick:b(Bl,["stop"]),style:{"margin-left":"auto"},title:"清空日志"},{icon:h(()=>[p(y(ue),{size:14},{default:h(()=>[p(y(ce))]),_:1})]),_:1})]),r("div",Ta,[r("div",Ca,[r("div",{class:T(["logs-table-header",`logs-header-${e.channelType}`])},[r("div",{class:T(["log-col col-channel",`col-channel-${e.channelType}`])},"渠道",2),r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},"请求",2),r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},"回复",2),"claude"===e.channelType?(u(),i(C,{key:0},[r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},"写入",2),r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},"命中",2)],64)):"codex"===e.channelType?(u(),i(C,{key:1},[r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},"推理",2),r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},"缓存",2),r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},"总计",2)],64)):"gemini"===e.channelType?(u(),i(C,{key:2},[r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},"缓存",2),r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},"总计",2)],64)):"opencode"===e.channelType||"omp"===e.channelType?(u(),i(C,{key:3},[r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},"推理",2),r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},"缓存",2),r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},"总计",2)],64)):d("",!0),r("div",{class:T(["log-col col-time",`col-time-${e.channelType}`])},"时间",2)],2),r("div",{class:"logs-container",ref_key:"logsContainer",ref:fl},[0===Cl.value.length?(u(),i("div",wa,[p(y(ue),{size:32,depth:"3",style:{"margin-bottom":"8px"}},{default:h(()=>[p(y(se))]),_:1}),p(y(pe),{depth:"3",style:{"font-size":"12px","font-weight":"500"}},{default:h(()=>[...l[26]||(l[26]=[f("暂无实时日志",-1)])]),_:1}),p(y(pe),{depth:"3",style:{"font-size":"11px","margin-top":"4px"}},{default:h(()=>[...l[27]||(l[27]=[f("开启代理后将显示请求记录",-1)])]),_:1})])):d("",!0),(u(!0),i(C,null,w(Cl.value,a=>(u(),i("div",{key:a.id,class:T(["log-row",{"action-row":"action"===a.type,"error-row":"error"===a.status,"new-log":a.isNew}]),title:Ol(a)},["action"===a.type?(u(),i("div",xa,[p(y(ue),{size:12,color:"#18a058"},{default:h(()=>[p(y(te))]),_:1}),r("span",$a,g(a.message),1),r("span",_a,g(a.time),1)])):"error"===a.status?(u(),i("div",Sa,[p(y(ue),{size:12,color:"#d03050"},{default:h(()=>[p(y(oe))]),_:1}),r("span",za,g(a.channel)+": "+g(a.error||a.message||"请求失败"),1),r("span",qa,g(a.time),1)])):(u(),i(C,{key:2},[r("div",{class:T(["log-col col-channel",`col-channel-${e.channelType}`])},[p(y(he),{size:"tiny",type:"success"},{default:h(()=>[f(g(a.channel),1)]),_:2},1024)],2),r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},g(Ml(a,"input")),3),r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},g(Ml(a,"output")),3),"claude"===e.channelType?(u(),i(C,{key:0},[r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},g(Ml(a,"cacheCreation")),3),r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},g(Ml(a,"cacheRead")),3)],64)):"codex"===e.channelType?(u(),i(C,{key:1},[r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},g(Ml(a,"reasoning")),3),r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},g(Ml(a,"cached")),3),r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},g(Ml(a,"total")),3)],64)):"gemini"===e.channelType?(u(),i(C,{key:2},[r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},g(Ml(a,"cached")),3),r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},g(Ml(a,"total")),3)],64)):"opencode"===e.channelType||"omp"===e.channelType?(u(),i(C,{key:3},[r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},g(Ml(a,"reasoning")),3),r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},g(Ml(a,"cached")),3),r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},g(Ml(a,"total")),3)],64)):d("",!0),r("div",{class:T(["log-col col-time",`col-time-${e.channelType}`])},g(a.time),3)],64))],10,ba))),128))],512)])])])):d("",!0)])),vl.value?(u(),i("div",{key:1,class:T(["locked-overlay",`locked-${e.channelType}`])},[r("div",Pa,[r("div",ja,[p(y(ue),{size:48},{default:h(()=>[p(y(W))]),_:1})]),l[29]||(l[29]=r("h3",{class:"locked-title"},"该渠道已锁定",-1)),p(y(pe),{depth:"3",class:"locked-hint"},{default:h(()=>[...l[28]||(l[28]=[f(" 点击上方按钮解锁以查看内容 ",-1)])]),_:1})])],2)):d("",!0),!1!==Ha.value.supportsSessions?(u(),v(I,{key:2,visible:ul.value,"onUpdate:visible":l[1]||(l[1]=e=>ul.value=e),channel:e.channelType},null,8,["visible","channel"])):d("",!0)]);var s,c}}},[["__scopeId","data-v-6009b6d9"]]),Ia={class:"dashboard-container"},Oa="dashboardChannelOrder",Ea=P({__name:"Home",setup(l){const{uiConfig:n,updateConfig:s,loadUIConfig:o}=Te();function r(e){try{localStorage.setItem(Oa,JSON.stringify(e))}catch(a){}}function d(e,a=n.value.customCliPlatforms){const l=K(e,a);return m.value=l.map(e=>({type:e})),r(l),l}const m=a(function(){try{const e=localStorage.getItem(Oa);if(e){const a=JSON.parse(e);return K(a,n.value.customCliPlatforms)}}catch(e){}return q}().map(e=>({type:e})));async function g(){const e=K(m.value.map(e=>e.type),n.value.customCliPlatforms);m.value=e.map(e=>({type:e})),r(e),await s("homeCliColumns",e),await s("dashboardChannelOrder",e)}function f(e){const a=(null==e?void 0:e.detail)||{};d(a.homeCliColumns,a.customCliPlatforms)}return c(async()=>{window.addEventListener("home-cli-columns-change",f),await o(),d(n.value.homeCliColumns||n.value.dashboardChannelOrder,n.value.customCliPlatforms)}),t(()=>{window.removeEventListener("home-cli-columns-change",f)}),(a,l)=>(u(),i("div",Ia,[p(y(e),{modelValue:m.value,"onUpdate:modelValue":l[0]||(l[0]=e=>m.value=e),class:"dashboard-grid","item-key":"type",animation:200,handle:".drag-handle",onEnd:g},{item:h(({element:e})=>[(u(),v(Ma,{"channel-type":e.type,key:e.type},null,8,["channel-type"]))]),_:1},8,["modelValue"])]))}},[["__scopeId","data-v-276ca51b"]]);export{Ea as default};
|