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.
Files changed (41) hide show
  1. package/dist/web/assets/{Analytics-D5kLOhUo.js → Analytics-BmPIW7oK.js} +1 -1
  2. package/dist/web/assets/{ConfigTemplates-D_-16blp.js → ConfigTemplates-CG4j4ua4.js} +1 -1
  3. package/dist/web/assets/Home-B9yAAZrD.js +1 -0
  4. package/dist/web/assets/Home-DSTUSxrT.css +1 -0
  5. package/dist/web/assets/{PluginManager-Cv_rv8Dh.js → PluginManager-CCJ7mo35.js} +1 -1
  6. package/dist/web/assets/{ProjectList-H_m4Tx4E.js → ProjectList-DVUs3o8x.js} +1 -1
  7. package/dist/web/assets/SessionList-DwssBydw.js +1 -0
  8. package/dist/web/assets/{SkillManager-CRav3m2a.js → SkillManager-DmOTHcQ7.js} +1 -1
  9. package/dist/web/assets/{WorkspaceManager-B3f8R4EF.js → WorkspaceManager-DLVj0zlu.js} +1 -1
  10. package/dist/web/assets/{icons-Dop5j7Bf.js → icons-nGdSKQn2.js} +1 -1
  11. package/dist/web/assets/index-BjdNYwSs.js +2 -0
  12. package/dist/web/assets/index-kQCC5dlu.css +1 -0
  13. package/dist/web/index.html +3 -3
  14. package/package.json +1 -1
  15. package/src/config/paths.js +2 -0
  16. package/src/server/api/channels.js +14 -1
  17. package/src/server/api/codex-channels.js +21 -1
  18. package/src/server/api/codex-projects.js +25 -8
  19. package/src/server/api/dashboard.js +167 -75
  20. package/src/server/api/gemini-channels.js +21 -1
  21. package/src/server/api/gemini-projects.js +23 -7
  22. package/src/server/api/omp-channels.js +17 -1
  23. package/src/server/api/omp-projects.js +23 -6
  24. package/src/server/api/opencode-channels.js +21 -1
  25. package/src/server/api/opencode-projects.js +23 -7
  26. package/src/server/api/projects.js +38 -21
  27. package/src/server/services/channel-balance.js +51 -5
  28. package/src/server/services/channel-sync-utils.js +368 -0
  29. package/src/server/services/channels.js +119 -0
  30. package/src/server/services/codex-channels.js +125 -0
  31. package/src/server/services/gemini-channels.js +123 -0
  32. package/src/server/services/omp-channels.js +299 -2
  33. package/src/server/services/omp-settings-manager.js +2 -0
  34. package/src/server/services/opencode-channels.js +172 -4
  35. package/src/server/services/project-snapshots.js +52 -0
  36. package/src/server/services/snapshot-cache.js +295 -0
  37. package/dist/web/assets/Home-DM_56Fyr.js +0 -1
  38. package/dist/web/assets/Home-Jom_ZcdY.css +0 -1
  39. package/dist/web/assets/SessionList-DcUQvsbR.js +0 -1
  40. package/dist/web/assets/index-Bnni1zPN.js +0 -2
  41. 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};