coding-tool-x 3.9.5-261014 → 3.9.6-261014

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 (43) hide show
  1. package/dist/web/assets/{AgentsDrawer-K97azdxi.js → AgentsDrawer-D-1A92Ig.js} +1 -1
  2. package/dist/web/assets/{Analytics-CNPDGLNe.js → Analytics-B2O5txNK.js} +1 -1
  3. package/dist/web/assets/{AssetPathField-BzwSZryS.js → AssetPathField-DuohRI-K.js} +1 -1
  4. package/dist/web/assets/{ChatHistoryDrawer-BVbfqK-u.js → ChatHistoryDrawer-CjyehFJ5.js} +1 -1
  5. package/dist/web/assets/{CollapseItem-DgBQWlsJ.js → CollapseItem--khwfagQ.js} +1 -1
  6. package/dist/web/assets/{CommandsDrawer-885BtWjs.js → CommandsDrawer-gZsBcvyW.js} +1 -1
  7. package/dist/web/assets/{ConfigExportDrawer-jn00iZWm.js → ConfigExportDrawer-CJnljMGE.js} +1 -1
  8. package/dist/web/assets/{ConfigTemplates-DoUG3mOo.js → ConfigTemplates-wkwjB2C5.js} +1 -1
  9. package/dist/web/assets/{ConfigTemplatesDrawer-BQ8h43E6.js → ConfigTemplatesDrawer-hAGvUnSt.js} +1 -1
  10. package/dist/web/assets/{ConfigTemplatesPanel-VCghYqMx.js → ConfigTemplatesPanel-BfF8qmL_.js} +1 -1
  11. package/dist/web/assets/{FavoritesDrawer-CKjlAe4O.js → FavoritesDrawer-YeAnYTuA.js} +1 -1
  12. package/dist/web/assets/{GatewayConvertDrawer-Cin2yotJ.js → GatewayConvertDrawer-D1znBVev.js} +1 -1
  13. package/dist/web/assets/{Home-ui0qANow.js → Home-CSsVyr4n.js} +1 -1
  14. package/dist/web/assets/{MarkdownEditor-LiZaVnJu.js → MarkdownEditor-CeLdWA-c.js} +1 -1
  15. package/dist/web/assets/{McpDrawer-ClaSJOLK.js → McpDrawer-z-veNi2j.js} +1 -1
  16. package/dist/web/assets/{McpFormDrawer-w1EJUIsa.js → McpFormDrawer-DgDBnMOk.js} +1 -1
  17. package/dist/web/assets/{PluginManager-C_Yr9wyj.js → PluginManager-DFYt_Xmd.js} +1 -1
  18. package/dist/web/assets/{PluginsDrawer-CUnoai6T.js → PluginsDrawer-Dqpb2UsR.js} +1 -1
  19. package/dist/web/assets/{PluginsPanel-CNShDbDF.js → PluginsPanel-CvcT0Up3.js} +1 -1
  20. package/dist/web/assets/{ProjectList-CZ2ZwKRn.js → ProjectList-DjwDDvVo.js} +1 -1
  21. package/dist/web/assets/{PromptsDrawer-BhB_q-KZ.js → PromptsDrawer-B2vI4UfM.js} +1 -1
  22. package/dist/web/assets/{RecentSessionsDrawer-BZ8YKqwq.js → RecentSessionsDrawer-DTbikshr.js} +1 -1
  23. package/dist/web/assets/{SessionCard-CEFt-CNZ.js → SessionCard-AKJF0KLk.js} +1 -1
  24. package/dist/web/assets/{SessionList-CTO4NiMY.js → SessionList-C2kxSHyw.js} +1 -1
  25. package/dist/web/assets/{SettingsDrawer-BiOXu9lZ.js → SettingsDrawer-CYNU3Scm.js} +1 -1
  26. package/dist/web/assets/{SkillManager-Bf87paMe.js → SkillManager-MMDMnZuA.js} +1 -1
  27. package/dist/web/assets/{SkillsDrawer-BwkyaeRk.js → SkillsDrawer-Bg6QLvgm.js} +1 -1
  28. package/dist/web/assets/{SkillsPanel-obdDEThm.js → SkillsPanel-CDUSYnfc.js} +1 -1
  29. package/dist/web/assets/{Upload-DdjJgjTW.js → Upload-DMr2S_D5.js} +1 -1
  30. package/dist/web/assets/{WorkspaceDrawer-UWKpEUXI.js → WorkspaceDrawer-VrUPX-Pa.js} +1 -1
  31. package/dist/web/assets/{WorkspaceManager-rKzSKzKC.js → WorkspaceManager-q76RE4Ss.js} +1 -1
  32. package/dist/web/assets/{WorkspacePanel-BCMcITVD.js → WorkspacePanel-CSDGZMrU.js} +1 -1
  33. package/dist/web/assets/{index-aW6itxZg.js → index-BXFl1mzC.js} +2 -2
  34. package/dist/web/assets/index-CcIxENSq.css +1 -0
  35. package/dist/web/assets/{useResponsiveDrawer-Cn6gH3VG.js → useResponsiveDrawer-DXmsFFe1.js} +1 -1
  36. package/dist/web/index.html +2 -2
  37. package/package.json +1 -1
  38. package/src/platforms/drivers/omp/channels.js +27 -0
  39. package/src/platforms/drivers/omp/gateway-routing.js +123 -33
  40. package/src/platforms/drivers/omp/gateway.js +102 -40
  41. package/src/server/services/memory-pressure.js +101 -0
  42. package/src/shared/channel-driver.js +12 -0
  43. package/dist/web/assets/index-DvoMGWQv.css +0 -1
@@ -1,15 +1,26 @@
1
1
  'use strict';
2
2
 
3
+ const { memoryPressureMiddleware, rejectUpgradeUnderPressure } = require('../../../server/services/memory-pressure');
4
+
3
5
  const crypto = require('crypto');
4
6
  const { downstreamHeaders } = require('../../../shared/proxy-transport');
5
7
  const http = require('http');
6
8
  const https = require('https');
7
9
  const httpProxy = require('http-proxy');
8
10
  const { URL } = require('url');
9
- const { allocateChannel, releaseChannel } = require('../../../server/services/channel-scheduler');
11
+ const {
12
+ allocateChannel,
13
+ releaseChannel,
14
+ getSchedulerState
15
+ } = require('../../../server/services/channel-scheduler');
10
16
  const { recordSuccess, recordFailure } = require('../../../server/services/channel-health');
11
- const { attachServerShutdownHandling, expediteServerShutdown } = require('../../../server/services/server-shutdown');
12
- const { resolveOmpGatewayRoute } = require('./gateway-routing');
17
+ const { broadcastSchedulerState } = require('../../../server/websocket-server');
18
+ const { attachServerShutdownHandling, expediteServerShutdown, PROXY_IDLE_TIMEOUT_MS } = require('../../../server/services/server-shutdown');
19
+ const {
20
+ resolveOmpGatewayRoute,
21
+ channelSupportsOmpModel,
22
+ normalizeOmpModelId
23
+ } = require('./gateway-routing');
13
24
  const { persistProxyRequestSnapshot } = require('../../../server/services/request-logger');
14
25
 
15
26
  const RETRYABLE_STATUS = new Set([401, 403, 429, 502, 503, 504]);
@@ -115,6 +126,7 @@ function rewriteCredential(headers, requestUrl, route, channel, options = {}) {
115
126
  }
116
127
 
117
128
  function readRequestBody(req) {
129
+ if (req.aborted || req.destroyed) return Promise.reject(createAbortError());
118
130
  return new Promise((resolve, reject) => {
119
131
  const chunks = [];
120
132
  let size = 0;
@@ -157,35 +169,40 @@ function readRequestBody(req) {
157
169
  });
158
170
  }
159
171
 
160
- function extractModelId(body, contentType = '') {
161
- if (!body?.length || !String(contentType).includes('json')) return '';
172
+ function extractModelId(body, contentType = '', requestUrl = '') {
173
+ if (body?.length && String(contentType).includes('json')) {
174
+ try {
175
+ const parsed = JSON.parse(body.toString('utf8'));
176
+ const bodyModel = typeof parsed.model === 'string'
177
+ ? parsed.model.trim()
178
+ : String(parsed.model?.id || parsed.model?.name || parsed.modelId || parsed.model_id || '').trim();
179
+ if (bodyModel) return bodyModel;
180
+ } catch {
181
+ // Some provider endpoints carry the model in the URL instead of JSON.
182
+ }
183
+ }
184
+
162
185
  try {
163
- const parsed = JSON.parse(body.toString('utf8'));
164
- if (typeof parsed.model === 'string') return parsed.model.trim();
165
- if (parsed.model && typeof parsed.model === 'object') {
166
- return String(parsed.model.id || parsed.model.name || '').trim();
186
+ const url = new URL(String(requestUrl || '/'), 'http://127.0.0.1');
187
+ const queryModel = url.searchParams.get('model')
188
+ || url.searchParams.get('model_id')
189
+ || url.searchParams.get('modelId');
190
+ if (queryModel?.trim()) return queryModel.trim();
191
+
192
+ // Gemini and Vertex encode the model in paths such as
193
+ // /v1beta/models/gemini-2.5-pro:generateContent.
194
+ const match = url.pathname.match(/(?:^|\/)models\/([^/:]+):[^/]+$/i);
195
+ if (match?.[1]) {
196
+ try {
197
+ return decodeURIComponent(match[1]).trim();
198
+ } catch {
199
+ return match[1].trim();
200
+ }
167
201
  }
168
- return String(parsed.modelId || parsed.model_id || '').trim();
169
202
  } catch {
170
203
  return '';
171
204
  }
172
- }
173
-
174
- function normalizeModelId(value) {
175
- return String(value || '').trim().replace(/:(minimal|low|medium|high|xhigh|max|off)$/i, '');
176
- }
177
-
178
- function channelSupportsModel(channel, modelId) {
179
- const requested = normalizeModelId(modelId).toLowerCase();
180
- if (!requested) return true;
181
- const declared = [];
182
- (Array.isArray(channel.allowedModels) ? channel.allowedModels : []).forEach(value => declared.push(value));
183
- (Array.isArray(channel.models) ? channel.models : []).forEach((model) => {
184
- declared.push(typeof model === 'string' ? model : (model?.id || model?.name));
185
- });
186
- if (channel.model) declared.push(channel.model);
187
- const normalized = [...new Set(declared.map(normalizeModelId).filter(Boolean).map(value => value.toLowerCase()))];
188
- return normalized.length === 0 || normalized.includes(requested);
205
+ return '';
189
206
  }
190
207
 
191
208
  function writeJson(res, statusCode, payload) {
@@ -253,6 +270,8 @@ function createOmpGateway(options = {}) {
253
270
  const getChannels = options.getChannels;
254
271
  const allocate = options.allocateChannel || allocateChannel;
255
272
  const release = options.releaseChannel || releaseChannel;
273
+ const readSchedulerState = options.getSchedulerState || getSchedulerState;
274
+ const publishSchedulerState = options.broadcastSchedulerState || broadcastSchedulerState;
256
275
  const onSuccess = options.recordSuccess || recordSuccess;
257
276
  const onFailure = options.recordFailure || recordFailure;
258
277
  const websocketProxy = httpProxy.createProxyServer({
@@ -269,6 +288,22 @@ function createOmpGateway(options = {}) {
269
288
  let inflightRequests = 0;
270
289
  const activeUpstreams = new Set();
271
290
 
291
+ function broadcastCurrentSchedulerState() {
292
+ try {
293
+ publishSchedulerState('omp', readSchedulerState('omp'));
294
+ } catch {
295
+ // Scheduler broadcasts are observational and must not interrupt a request.
296
+ }
297
+ }
298
+
299
+ function releaseAndBroadcast(channelId) {
300
+ try {
301
+ release(channelId, 'omp');
302
+ } finally {
303
+ broadcastCurrentSchedulerState();
304
+ }
305
+ }
306
+
272
307
  function gatewayDescriptor() {
273
308
  return { host, port, secret };
274
309
  }
@@ -282,7 +317,7 @@ function createOmpGateway(options = {}) {
282
317
  const channels = currentChannels();
283
318
  const candidateIds = route.channelIds.filter((channelId) => {
284
319
  const channel = channels.find(item => item.id === channelId);
285
- return channel && channelSupportsModel(channel, modelId);
320
+ return channel && channelSupportsOmpModel(channel, modelId);
286
321
  });
287
322
  if (candidateIds.length === 0) {
288
323
  throw Object.assign(
@@ -290,7 +325,7 @@ function createOmpGateway(options = {}) {
290
325
  { statusCode: 422 }
291
326
  );
292
327
  }
293
- return allocate({
328
+ const channel = await allocate({
294
329
  source: 'omp',
295
330
  enableSessionBinding: false,
296
331
  candidateIds,
@@ -298,9 +333,11 @@ function createOmpGateway(options = {}) {
298
333
  routingGroup: route.routingGroup,
299
334
  providerKey: route.providerKey,
300
335
  providerApi: route.providerApi,
301
- modelId: normalizeModelId(modelId),
336
+ modelId: normalizeOmpModelId(modelId),
302
337
  signal
303
338
  });
339
+ if (channel) broadcastCurrentSchedulerState();
340
+ return channel;
304
341
  }
305
342
 
306
343
  function forwardAttempt(req, route, channel, body, signal) {
@@ -309,7 +346,7 @@ function createOmpGateway(options = {}) {
309
346
  const releaseOnce = () => {
310
347
  if (released) return;
311
348
  released = true;
312
- release(channel.id, 'omp');
349
+ releaseAndBroadcast(channel.id);
313
350
  };
314
351
  if (signal?.aborted) {
315
352
  releaseOnce();
@@ -363,6 +400,9 @@ function createOmpGateway(options = {}) {
363
400
  });
364
401
 
365
402
  activeUpstreams.add(upstreamRequest);
403
+ upstreamRequest.setTimeout(PROXY_IDLE_TIMEOUT_MS, () => {
404
+ upstreamRequest.destroy(new Error('OMP upstream request timeout'));
405
+ });
366
406
  const abortUpstream = () => {
367
407
  const error = createAbortError();
368
408
  if (upstreamResponse) {
@@ -385,7 +425,7 @@ function createOmpGateway(options = {}) {
385
425
  async function handleProxyRequest(req, res, route, signal) {
386
426
  const body = await readRequestBody(req);
387
427
  if (signal?.aborted) throw createAbortError();
388
- const modelId = extractModelId(body, req.headers['content-type']);
428
+ const modelId = extractModelId(body, req.headers['content-type'], route.upstreamPath);
389
429
  const attemptedIds = [];
390
430
  let lastError = null;
391
431
 
@@ -398,7 +438,7 @@ function createOmpGateway(options = {}) {
398
438
  break;
399
439
  }
400
440
  if (signal?.aborted) {
401
- release(channel.id, 'omp');
441
+ releaseAndBroadcast(channel.id);
402
442
  lastError = createAbortError();
403
443
  break;
404
444
  }
@@ -409,7 +449,7 @@ function createOmpGateway(options = {}) {
409
449
  if (attempt === 0 && RETRYABLE_STATUS.has(result.statusCode)) {
410
450
  result.response.destroy();
411
451
  result.release();
412
- onFailure(channel.id, 'omp');
452
+ if (!signal?.aborted) onFailure(channel.id, 'omp');
413
453
  lastError = Object.assign(new Error(`OMP upstream returned HTTP ${result.statusCode}`), {
414
454
  statusCode: result.statusCode
415
455
  });
@@ -427,7 +467,7 @@ function createOmpGateway(options = {}) {
427
467
  if (result.statusCode < 400 && !error) {
428
468
  onSuccess(channel.id, 'omp');
429
469
  } else {
430
- onFailure(channel.id, 'omp');
470
+ if (!signal?.aborted) onFailure(channel.id, 'omp');
431
471
  }
432
472
  resolve();
433
473
  };
@@ -455,7 +495,7 @@ function createOmpGateway(options = {}) {
455
495
  if (error?.name === 'AbortError') {
456
496
  break;
457
497
  }
458
- onFailure(channel.id, 'omp');
498
+ if (!signal?.aborted) onFailure(channel.id, 'omp');
459
499
  }
460
500
  }
461
501
 
@@ -514,6 +554,11 @@ function createOmpGateway(options = {}) {
514
554
  }
515
555
  });
516
556
 
557
+ req.proxyPlatform = 'omp';
558
+ let admitted = false;
559
+ memoryPressureMiddleware(req, res, () => { admitted = true; });
560
+ if (!admitted) return;
561
+
517
562
  inflightRequests++;
518
563
  const abortController = new AbortController();
519
564
  const abortDownstream = () => abortController.abort();
@@ -541,6 +586,7 @@ function createOmpGateway(options = {}) {
541
586
  const statusText = {
542
587
  401: 'Unauthorized',
543
588
  404: 'Not Found',
589
+ 422: 'Unprocessable Entity',
544
590
  503: 'Service Unavailable'
545
591
  }[statusCode] || 'Bad Gateway';
546
592
  socket.end([
@@ -554,6 +600,8 @@ function createOmpGateway(options = {}) {
554
600
  }
555
601
 
556
602
  async function handleUpgrade(req, socket, head) {
603
+ req.proxyPlatform = 'omp';
604
+ if (rejectUpgradeUnderPressure(req, socket)) return;
557
605
  if (draining) {
558
606
  rejectUpgrade(socket, 503, 'OMP gateway is draining');
559
607
  return;
@@ -583,10 +631,22 @@ function createOmpGateway(options = {}) {
583
631
  route.upstreamPath = `${route.upstreamPath}${parsed.search}`;
584
632
 
585
633
  let channel;
634
+ const controller = new AbortController();
635
+ const abortWaiting = () => controller.abort();
636
+ socket.once('close', abortWaiting);
637
+ socket.once('error', abortWaiting);
586
638
  try {
587
- channel = await allocateForRoute(route);
639
+ const modelId = extractModelId(null, '', route.upstreamPath);
640
+ channel = await allocateForRoute(route, [], modelId, controller.signal);
588
641
  } catch (error) {
589
- rejectUpgrade(socket, 503, error.message || 'No compatible OMP channel is available');
642
+ rejectUpgrade(socket, error.statusCode || 503, error.message || 'No compatible OMP channel is available');
643
+ return;
644
+ } finally {
645
+ socket.removeListener('close', abortWaiting);
646
+ socket.removeListener('error', abortWaiting);
647
+ }
648
+ if (controller.signal.aborted || socket.destroyed) {
649
+ releaseAndBroadcast(channel.id);
590
650
  return;
591
651
  }
592
652
 
@@ -607,7 +667,7 @@ function createOmpGateway(options = {}) {
607
667
  if (!failed) {
608
668
  onSuccess(channel.id, 'omp');
609
669
  }
610
- release(channel.id, 'omp');
670
+ releaseAndBroadcast(channel.id);
611
671
  };
612
672
  socket.once('close', releaseOnce);
613
673
  socket.once('error', (error) => {
@@ -631,7 +691,9 @@ function createOmpGateway(options = {}) {
631
691
  websocketProxy.ws(req, socket, head, {
632
692
  target: upstream.origin,
633
693
  changeOrigin: true,
634
- secure: upstream.protocol === 'https:'
694
+ secure: upstream.protocol === 'https:',
695
+ proxyTimeout: PROXY_IDLE_TIMEOUT_MS,
696
+ timeout: 0
635
697
  });
636
698
  } catch (error) {
637
699
  markFailedOnce(error);
@@ -0,0 +1,101 @@
1
+ 'use strict';
2
+
3
+ const MIB = 1024 * 1024;
4
+ const PAUSE_RSS_BYTES = 380 * MIB;
5
+ const RESUME_RSS_BYTES = 330 * MIB;
6
+ const CHECK_INTERVAL_MS = 5_000;
7
+ let timer = null;
8
+ let paused = false;
9
+ let lowChecks = 0;
10
+ let rejected = 0;
11
+ let latestRss = 0;
12
+ let persistenceBlocked = false;
13
+
14
+ function readPersistencePressure() {
15
+ // Admission must not initialize storage or enter the statistics/driver cycle.
16
+ const statistics = require.cache[require.resolve('./statistics-service')]?.exports;
17
+ const nativeLogs = require.cache[require.resolve('./native-log-observer')]?.exports;
18
+ return statistics?.getStatisticsQueueDiagnostics?.().blocked === true
19
+ || nativeLogs?.getNativeLogDiagnostics?.().persistenceBlocked === true;
20
+ }
21
+
22
+ function checkMemoryPressure() {
23
+ latestRss = process.memoryUsage().rss;
24
+ persistenceBlocked = readPersistencePressure();
25
+ if (latestRss >= PAUSE_RSS_BYTES || persistenceBlocked) {
26
+ paused = true;
27
+ lowChecks = 0;
28
+ } else if (paused) {
29
+ lowChecks = latestRss < RESUME_RSS_BYTES ? lowChecks + 1 : 0;
30
+ if (lowChecks >= 2) {
31
+ paused = false;
32
+ lowChecks = 0;
33
+ }
34
+ }
35
+ return getMemoryPressureDiagnostics();
36
+ }
37
+
38
+ function startMemoryPressureMonitor() {
39
+ if (timer) return false;
40
+ checkMemoryPressure();
41
+ timer = setInterval(checkMemoryPressure, CHECK_INTERVAL_MS);
42
+ timer.unref?.();
43
+ return true;
44
+ }
45
+
46
+ function stopMemoryPressureMonitor() {
47
+ if (timer) clearInterval(timer);
48
+ timer = null;
49
+ paused = false;
50
+ lowChecks = 0;
51
+ rejected = 0;
52
+ persistenceBlocked = false;
53
+ latestRss = 0;
54
+ }
55
+
56
+ function isUnderMemoryPressure() {
57
+ startMemoryPressureMonitor();
58
+ // Admission can trip the guard immediately, but only periodic checks recover it.
59
+ latestRss = process.memoryUsage().rss;
60
+ persistenceBlocked = readPersistencePressure();
61
+ if (latestRss >= PAUSE_RSS_BYTES || persistenceBlocked) {
62
+ paused = true;
63
+ lowChecks = 0;
64
+ }
65
+ return paused;
66
+ }
67
+
68
+ function errorBody(platform) {
69
+ const message = 'Server memory pressure; retry in 5 seconds.';
70
+ if (platform === 'claude') return { type: 'error', error: { type: 'overloaded_error', message } };
71
+ if (platform === 'gemini') return { error: { code: 503, status: 'UNAVAILABLE', message } };
72
+ return { error: { type: 'server_error', code: 'memory_pressure', message } };
73
+ }
74
+
75
+ function memoryPressureMiddleware(req, res, next) {
76
+ if (!isUnderMemoryPressure()) return next();
77
+ rejected += 1;
78
+ res.statusCode = 503;
79
+ res.setHeader('Retry-After', '5');
80
+ res.setHeader('Content-Type', 'application/json; charset=utf-8');
81
+ res.end(JSON.stringify(errorBody(req.proxyPlatform)));
82
+ }
83
+
84
+ function rejectUpgradeUnderPressure(req, socket) {
85
+ if (!isUnderMemoryPressure()) return false;
86
+ rejected += 1;
87
+ const body = JSON.stringify(errorBody(req.proxyPlatform));
88
+ socket.end(`HTTP/1.1 503 Service Unavailable\r\nRetry-After: 5\r\nContent-Type: application/json; charset=utf-8\r\nContent-Length: ${Buffer.byteLength(body)}\r\nConnection: close\r\n\r\n${body}`);
89
+ return true;
90
+ }
91
+
92
+ function getMemoryPressureDiagnostics() {
93
+ return { paused, rss: latestRss, lowChecks, rejected, persistenceBlocked };
94
+ }
95
+
96
+ module.exports = {
97
+ memoryPressureMiddleware, rejectUpgradeUnderPressure,
98
+ startMemoryPressureMonitor, stopMemoryPressureMonitor,
99
+ getMemoryPressureDiagnostics,
100
+ _test: { checkMemoryPressure }
101
+ };
@@ -92,6 +92,7 @@ function createChannelDriver({
92
92
  formatCliChannelDetails,
93
93
  dashboardChannelShape = 'object',
94
94
  modelListType,
95
+ schedulerStateProvider,
95
96
  ...context
96
97
  } = {}) {
97
98
  let service;
@@ -245,6 +246,17 @@ function createChannelDriver({
245
246
  ? call('markRecentlyUsed', 'markChannelAsRecentlyUsed', [id], sanitizeChannel)
246
247
  : invalid(platform, capability, 'markRecentlyUsed', new Error('Channel id is required'));
247
248
  driver.saveOrder = order => call('saveOrder', 'saveChannelOrder', [order]);
249
+ driver.poolStatus = () => {
250
+ try {
251
+ const readSchedulerState = schedulerStateProvider
252
+ || require('../server/services/channel-scheduler').getSchedulerState;
253
+ return ok(platform, capability, 'poolStatus', {
254
+ scheduler: readSchedulerState(platform)
255
+ });
256
+ } catch (error) {
257
+ return failed(platform, capability, 'poolStatus', error);
258
+ }
259
+ };
248
260
 
249
261
  return driver;
250
262
  }
@@ -1 +0,0 @@
1
- .header-button[data-v-b515354d]{border:1px solid var(--border-primary);background:var(--bg-elevated);cursor:pointer;border-radius:999px;outline:none;justify-content:center;align-items:center;width:40px;height:40px;padding:0;transition:all .2s;display:inline-flex;box-shadow:0 10px 24px #0f171d14}.header-button[data-v-b515354d] .n-icon{color:var(--text-tertiary);transition:all .2s}.header-button[data-v-b515354d]:hover:not(.disabled){background:color-mix(in srgb, var(--bg-elevated) 90%, var(--primary-color) 10%);border-color:#18a0583d;box-shadow:0 14px 30px #0f171d1f}.header-button[data-v-b515354d]:hover:not(.disabled) .n-icon{color:var(--primary-color,#18a058)}.header-button[data-v-b515354d]:active:not(.disabled){transform:scale(.95)}.header-button.active[data-v-b515354d]{background:linear-gradient(135deg,#18a05824,#18a0580f);border-color:#18a05852}.header-button.active[data-v-b515354d] .n-icon{color:#18a058!important}.header-button.disabled[data-v-b515354d]{cursor:not-allowed;opacity:.5}[data-theme=dark] .header-button[data-v-b515354d]{box-shadow:none;background:#0d171eeb;border-color:#364c56e6}[data-theme=dark] .header-button[data-v-b515354d]:hover:not(.disabled){background:#14222afa;border-color:#18a05847;box-shadow:0 0 0 1px #18a05814}.modal-header[data-v-7ea71ef7]{align-items:center;gap:10px;display:flex}.warning-icon[data-v-7ea71ef7]{color:#f59e0b}.header-title[data-v-7ea71ef7]{color:var(--text-primary);font-size:16px;font-weight:600}.modal-content[data-v-7ea71ef7]{padding:4px 0}.desc-text[data-v-7ea71ef7]{color:var(--text-secondary);margin:0 0 16px;font-size:13px;line-height:1.6}.conflict-list[data-v-7ea71ef7]{background:var(--bg-tertiary);border-radius:6px;padding:12px}.list-header[data-v-7ea71ef7]{border-bottom:1px solid var(--border-primary);align-items:center;gap:12px;margin-bottom:10px;padding-bottom:10px;display:flex}.selected-hint[data-v-7ea71ef7]{color:var(--text-tertiary);font-size:12px}.conflict-items[data-v-7ea71ef7]{flex-direction:column;gap:8px;display:flex}.conflict-item[data-v-7ea71ef7]{background:var(--bg-secondary);cursor:pointer;border:1px solid #0000;border-radius:6px;align-items:flex-start;gap:10px;padding:10px 12px;transition:all .15s;display:flex}.conflict-item[data-v-7ea71ef7]:hover{background:#18a05805;border-color:#18a05866}.conflict-item.selected[data-v-7ea71ef7]{background:#18a0580f;border-color:#18a058}.item-content[data-v-7ea71ef7]{flex:1;min-width:0}.item-header[data-v-7ea71ef7]{align-items:center;gap:8px;margin-bottom:4px;display:flex}.var-name[data-v-7ea71ef7]{color:var(--text-primary);background:var(--bg-tertiary);border-radius:4px;padding:2px 6px;font-size:13px;font-weight:600}.var-value[data-v-7ea71ef7]{color:var(--text-secondary);word-break:break-all;margin-bottom:4px;font-family:SF Mono,Monaco,monospace;font-size:12px}.var-source[data-v-7ea71ef7]{color:var(--text-tertiary);font-size:11px}.source-label[data-v-7ea71ef7]{color:var(--text-quaternary)}.modal-footer[data-v-7ea71ef7]{justify-content:flex-end;gap:8px;display:flex}.process-env-alert[data-v-7ea71ef7]{margin-bottom:16px}.alert-content[data-v-7ea71ef7]{font-size:12px;line-height:1.6}.alert-title[data-v-7ea71ef7]{margin:0 0 8px;font-weight:500}.alert-list[data-v-7ea71ef7]{margin:0;padding-left:20px}.alert-list li[data-v-7ea71ef7]{margin:4px 0}.alert-list code[data-v-7ea71ef7]{background:var(--bg-tertiary);color:#18a058;border-radius:4px;padding:2px 6px;font-family:SF Mono,Monaco,monospace;font-size:11px}.alert-hint[data-v-7ea71ef7]{color:var(--text-tertiary);margin:8px 0 0;font-size:11px}.channel-card[data-v-beb1848e]{border:1px solid var(--border-primary);background:var(--bg-primary);border-radius:8px;transition:all .2s}.channel-card[data-v-beb1848e]:hover{border-color:var(--border-secondary)}.channel-header[data-v-beb1848e]{border-bottom:1px solid var(--border-primary);justify-content:space-between;align-items:center;gap:8px;padding:12px 16px;display:flex}.channel-title[data-v-beb1848e]{flex:auto;align-items:center;gap:6px;min-width:0;margin-right:0;display:flex}.channel-name[data-v-beb1848e]{color:var(--text-primary);text-overflow:ellipsis;white-space:nowrap;flex:auto;min-width:0;font-size:13px;font-weight:600;overflow:hidden}.channel-title[data-v-beb1848e] .n-tag{flex-shrink:0;padding:0 5px;font-size:11px}.collapse-btn[data-v-beb1848e]{flex-shrink:0}.collapse-btn .n-icon.collapsed[data-v-beb1848e]{transform:rotate(-90deg)}.channel-actions[data-v-beb1848e]{flex-wrap:nowrap;flex:none;justify-content:flex-end;align-items:center;gap:4px;max-width:100%;margin-left:auto;display:flex}.channel-actions[data-v-beb1848e] .n-button{min-width:0;height:22px;padding:0 5px;font-size:11px}.channel-actions[data-v-beb1848e] .n-button__content{gap:2px}.channel-actions[data-v-beb1848e] .n-button__icon{margin:0}.apply-btn[data-v-beb1848e]{display:none}.channel-card:hover .apply-btn[data-v-beb1848e]{display:inline-flex}.test-result[data-v-beb1848e]{border-bottom:1px solid var(--border-primary);background:var(--bg-secondary);padding:10px 16px}.test-result.success[data-v-beb1848e]{background:#18a0580f;border-left:3px solid #18a058}.test-result.failed[data-v-beb1848e]{background:#f56c6c0f;border-left:3px solid #f56c6c}.test-result.warning[data-v-beb1848e]{background:#d990000f;border-left:3px solid #d99000}.test-result-header[data-v-beb1848e]{align-items:center;gap:12px;display:flex}.test-result-status[data-v-beb1848e]{align-items:center;gap:6px;font-size:13px;font-weight:500;display:flex}.test-result.success .test-result-status[data-v-beb1848e]{color:#18a058}.test-result.failed .test-result-status[data-v-beb1848e]{color:#f56c6c}.test-result.warning .test-result-status[data-v-beb1848e]{color:#b87900}.test-result-info[data-v-beb1848e]{flex:1;align-items:center;gap:10px;display:flex}.test-result-info .latency[data-v-beb1848e]{color:var(--text-primary);background:var(--bg-tertiary);border-radius:4px;padding:2px 8px;font-family:SF Mono,Monaco,monospace;font-size:13px;font-weight:600}.test-result-info .status-code[data-v-beb1848e]{color:var(--text-tertiary);font-family:SF Mono,Monaco,monospace;font-size:11px}.test-result-error[data-v-beb1848e]{color:#f56c6c;word-break:break-word;background:#f56c6c14;border-radius:4px;margin-top:8px;padding:8px 10px;font-size:12px;line-height:1.4}.test-result-warning[data-v-beb1848e]{color:#9b6800;word-break:break-word;background:#d9900014;border-radius:4px;margin-top:8px;padding:8px 10px;font-size:12px;line-height:1.4}.channel-info[data-v-beb1848e]{padding:12px 16px}.info-main[data-v-beb1848e]{flex-direction:column;gap:8px;display:flex}.info-row[data-v-beb1848e]{align-items:center;gap:12px;display:flex}.info-row .label[data-v-beb1848e]{min-width:80px;font-size:13px}.info-row .value[data-v-beb1848e]{flex:1;font-size:13px}.info-row .value.mono[data-v-beb1848e]{font-family:SF Mono,Monaco,monospace}.info-footer[data-v-beb1848e]{border-top:1px solid var(--border-primary);justify-content:space-between;align-items:center;margin-top:12px;padding-top:8px;display:flex}.footer-spacer[data-v-beb1848e]{flex:1}.channel-meta[data-v-beb1848e]{flex-wrap:wrap;justify-content:flex-end;align-items:center;gap:12px;font-size:12px;display:flex}.balance-meta-tag[data-v-beb1848e]{cursor:pointer;flex-shrink:0;max-width:110px}.balance-meta-tag[data-v-beb1848e] .n-tag__content{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.meta-item[data-v-beb1848e]{color:var(--text-tertiary);white-space:nowrap}.meta-value[data-v-beb1848e]{color:var(--text-secondary);font-weight:500}.meta-active[data-v-beb1848e]{color:#18a058}@media (width<=1024px){.channel-header[data-v-beb1848e],.channel-info[data-v-beb1848e]{padding:10px 12px}.channel-actions[data-v-beb1848e]{gap:6px}.info-row[data-v-beb1848e]{gap:8px}.info-row .label[data-v-beb1848e]{min-width:70px;font-size:12px}.info-row .value[data-v-beb1848e]{font-size:12px}.channel-meta[data-v-beb1848e]{gap:12px}}@media (width<=768px){.channel-header[data-v-beb1848e]{flex-direction:column;align-items:stretch;gap:8px;padding:8px 10px}.channel-title[data-v-beb1848e]{margin-right:0}.channel-actions[data-v-beb1848e]{justify-content:space-between}.channel-actions[data-v-beb1848e] .n-button{padding:0 8px;font-size:12px}.channel-info[data-v-beb1848e]{padding:8px 10px}.info-row[data-v-beb1848e]{flex-direction:column;align-items:flex-start;gap:4px}.info-row .label[data-v-beb1848e]{min-width:auto}.info-footer[data-v-beb1848e]{flex-direction:column;align-items:flex-start;gap:8px;margin-top:8px}.channel-meta[data-v-beb1848e]{justify-content:flex-start;align-self:stretch;gap:8px 12px}}@media (width<=640px){.channel-card[data-v-beb1848e]{border-radius:6px}.channel-header[data-v-beb1848e]{padding:6px 8px}.channel-name[data-v-beb1848e]{min-width:0;font-size:12px}.channel-title[data-v-beb1848e]{min-width:0}.channel-title[data-v-beb1848e] .n-tag{font-size:10px}.channel-info[data-v-beb1848e]{padding:6px 8px}.info-row .label[data-v-beb1848e],.info-row .value[data-v-beb1848e]{font-size:11px}.channel-actions[data-v-beb1848e] .n-button{height:24px;padding:0 6px;font-size:11px}.channel-meta[data-v-beb1848e]{font-size:11px}}@media (width<=480px){.channel-header[data-v-beb1848e],.channel-info[data-v-beb1848e]{padding:4px 6px}.channel-actions[data-v-beb1848e]{gap:4px}.channel-actions[data-v-beb1848e] .n-button{min-width:0;height:22px;padding:0 4px;font-size:10px}}.model-redirect-editor[data-v-8ebdcad0]{width:100%}.redirect-row[data-v-8ebdcad0]{align-items:center;gap:8px;margin-bottom:8px;display:flex}.redirect-input[data-v-8ebdcad0]{flex:1;min-width:0}.redirect-arrow[data-v-8ebdcad0]{flex-shrink:0;justify-content:center;align-items:center;width:24px;display:flex}.add-btn[data-v-8ebdcad0]{width:100%;margin-top:4px}.channel-auth-section[data-v-8fcf77d4]{gap:10px;width:100%;display:grid}.oauth-content[data-v-8fcf77d4],.oauth-quota[data-v-8fcf77d4]{gap:8px;display:grid}.oauth-meta[data-v-8fcf77d4]{color:var(--text-color-3)}.oauth-quota[data-v-8fcf77d4]{grid-template-columns:repeat(3,auto);align-items:center}.loading-container{justify-content:center;align-items:center;padding:80px 0;display:flex}.empty-state{justify-content:center;align-items:center;min-height:200px;display:flex}.model-catalog-control{flex-wrap:wrap;align-items:center;gap:6px 8px;margin:-3px 0 10px;font-size:12px;line-height:1.45;display:flex}.model-catalog-status{color:var(--n-text-color-3)}.model-catalog-status.is-loading{color:var(--n-info-color,var(--n-text-color-3))}.model-catalog-status.is-error{color:var(--n-error-color,#d03050)}.channels-list{flex-direction:column;gap:10px;display:flex;position:relative;overflow:hidden}.channel-card{border:1px solid var(--n-divider-color);background:var(--bg-secondary);border-left:3px solid #18a058;border-radius:6px;padding:10px 12px;transition:all .2s;box-shadow:0 1px 2px #0000000a}.channel-card.active{border-color:var(--n-primary-color);background:var(--n-primary-color-suppl,#18a05814);box-shadow:0 2px 8px #18a0581a}.channel-card:hover{border-color:var(--n-border-color);box-shadow:0 2px 8px #00000014}.channel-card.disabled{opacity:.6;border-left-color:#909399}.channel-card.status-frozen{background:linear-gradient(90deg,#d0305014 0%,#0000 100%);border-left-color:#d03050}.channel-card.status-checking{background:linear-gradient(90deg,#f0a02014 0%,#0000 100%);border-left-color:#f0a020}.channel-card.collapsed .channel-info{display:none}.channel-header{justify-content:space-between;align-items:center;gap:12px;display:flex}.channel-title{flex:1;align-items:center;gap:6px;min-width:0;display:flex}.channel-name{color:var(--n-text-color);white-space:nowrap;text-overflow:ellipsis;font-size:13px;font-weight:600;overflow:hidden}.channel-actions{flex-shrink:0;align-items:center;gap:6px;display:flex}.channel-actions .apply-btn{opacity:0;transition:opacity .2s}.channel-card:hover .channel-actions .apply-btn{opacity:1}.channel-actions .n-switch{margin-left:2px}.channel-info{border-top:1px dashed var(--n-border-color);margin-top:8px;padding-top:8px}.info-main{flex-direction:column;gap:4px;display:flex}.info-row{align-items:center;gap:8px;display:flex}.label{color:var(--n-text-color-3);flex-shrink:0;width:56px;font-size:11px}.value{color:var(--n-text-color-2);word-break:break-word;flex:1;font-size:12px}.value.mono{font-family:IBM Plex Mono,SFMono-Regular,Consolas,monospace}.info-footer{border-top:1px dashed var(--n-border-color);align-items:center;gap:8px;margin-top:6px;padding-top:6px;display:flex}.footer-spacer{flex:1}.channel-meta{flex-shrink:0;gap:12px;display:flex}.meta-item{color:var(--n-text-color-3);white-space:nowrap;font-size:11px}.meta-value{color:#18a058;font-weight:600}.meta-value.meta-active{color:#f0a020;animation:1.5s ease-in-out infinite pulse}@keyframes pulse{0%,to{opacity:1}50%{opacity:.6}}.collapse-btn{align-items:center;padding:0;display:flex}.collapse-btn .n-icon{transition:transform .2s}.collapse-btn .n-icon.collapsed{transform:rotate(-90deg)}.dialog-footer{justify-content:flex-end;gap:8px;display:flex}.channel-form{padding:0}.form-section{background:var(--n-color);border:1px solid var(--n-border-color);border-radius:6px;margin-bottom:10px;padding:12px}.form-section:last-child{margin-bottom:0}.section-title{color:var(--n-text-color);border-bottom:1px solid var(--n-border-color);align-items:center;gap:6px;margin-bottom:10px;padding-bottom:6px;font-size:12px;font-weight:600;display:flex}.section-desc{color:var(--n-text-color-3);font-size:11px;font-weight:400}.form-section.collapsible{background:var(--n-color-hover)}.channel-form .n-radio-group{width:100%}.channel-form .n-radio-group .n-space{gap:16px;width:100%;display:flex}.channel-form .n-radio{border:1px solid var(--n-border-color);border-radius:6px;flex:1;justify-content:center;padding:8px 16px;transition:all .2s;display:flex}.channel-form .n-radio:hover,.channel-form .n-radio.n-radio--checked{border-color:var(--n-primary-color);background:var(--n-primary-color-suppl,#18a05814)}.channel-form .n-form-item{margin-bottom:10px}.channel-form .n-form-item:last-child{margin-bottom:0}.channel-form .n-form-item-label{padding-bottom:0;font-size:12px}.auth-provider-status{flex-direction:column;justify-content:center;gap:6px;width:100%;min-height:28px;display:flex}.auth-provider-tags{min-height:24px}.auth-provider-message{color:var(--n-text-color-3);font-size:12px;line-height:1.45}.auth-provider-message.warning{color:#c97a00}.model-definitions-editor[data-v-60c67edc]{width:100%}.model-definitions-toolbar[data-v-60c67edc]{align-items:center;gap:10px;margin-bottom:8px;display:flex}.proxy-logs[data-v-1d31e3d2]{background:#fff;border-top:1px solid #e5e7eb;flex-direction:column;height:100%;display:flex}[data-theme=dark] .proxy-logs[data-v-1d31e3d2]{background:var(--bg-secondary);border-top:1px solid var(--border-primary)}.logs-header[data-v-1d31e3d2]{border-bottom:1px solid #e5e7eb;flex-shrink:0;justify-content:space-between;align-items:center;padding:12px 16px;display:flex}[data-theme=dark] .logs-header[data-v-1d31e3d2]{border-bottom:1px solid var(--border-primary)}.header-left[data-v-1d31e3d2]{align-items:center;gap:12px;display:flex}.title[data-v-1d31e3d2]{color:#18181b;margin:0;font-size:14px;font-weight:600}[data-theme=dark] .title[data-v-1d31e3d2]{color:var(--text-primary)}.today-stats[data-v-1d31e3d2]{align-items:center;gap:6px;display:flex}.today-stats[data-v-1d31e3d2] .n-tag{padding:2px 8px;font-size:11px;font-weight:600}.logs-table[data-v-1d31e3d2]{flex-direction:column;flex:1;display:flex;overflow:hidden}.table-header[data-v-1d31e3d2]{color:#6b7280;white-space:nowrap;background:#f9fafb;border-bottom:1px solid #e5e7eb;flex-shrink:0;padding:8px 12px;font-size:11px;font-weight:600;display:flex}[data-theme=dark] .table-header[data-v-1d31e3d2]{border-bottom:1px solid var(--border-primary);color:var(--text-tertiary);background:#ffffff05}.table-body[data-v-1d31e3d2]{flex:1;min-height:0;padding-bottom:40px;scroll-padding-bottom:40px;overflow:hidden auto}.table-row[data-v-1d31e3d2]{cursor:pointer;border-bottom:1px solid #f3f4f6;padding:8px 12px;font-size:12px;transition:background-color .2s;display:flex;position:relative}[data-theme=dark] .table-row[data-v-1d31e3d2]{border-bottom:1px solid #ffffff0a}.table-row[data-v-1d31e3d2]:hover{background:#f9fafb}[data-theme=dark] .table-row[data-v-1d31e3d2]:hover{background:#ffffff0a}.table-row.new-log[data-v-1d31e3d2],.action-row.new-log[data-v-1d31e3d2]{border-left:3px solid #18a058;padding-left:9px;animation:4.5s cubic-bezier(.25,.46,.45,.94) forwards newLogPulse-1d31e3d2}@keyframes newLogPulse-1d31e3d2{0%{background:linear-gradient(90deg,#18a05826 0%,#18a0580f 100%);box-shadow:0 0 8px #18a05833}5%{background:linear-gradient(90deg,#18a0582e 0%,#18a05814 100%);box-shadow:0 0 12px 1px #18a05840}15%{background:linear-gradient(90deg,#18a05829 0%,#18a05812 100%);box-shadow:0 0 10px 1px #18a05833}30%{background:linear-gradient(90deg,#18a0581f 0%,#18a0580d 100%);box-shadow:0 0 6px #18a05826}50%{background:linear-gradient(90deg,#18a05814 0%,#18a05808 100%);box-shadow:0 0 4px #18a0581a}70%{background:linear-gradient(90deg,#18a0580a 0%,#18a05804 100%);box-shadow:0 0 2px #18a0580f}85%{background:linear-gradient(90deg,#18a05805 0%,#18a05802 100%);box-shadow:0 0 1px #18a05808}to{background:0 0;border-left-color:#0000;box-shadow:0 0 #18a05800}}[data-theme=dark] .table-row.new-log[data-v-1d31e3d2],[data-theme=dark] .action-row.new-log[data-v-1d31e3d2]{animation:4.5s cubic-bezier(.25,.46,.45,.94) forwards newLogPulseDark-1d31e3d2}@keyframes newLogPulseDark-1d31e3d2{0%{background:linear-gradient(90deg,#18a05833 0%,#18a05814 100%);box-shadow:0 0 10px #18a05840}5%{background:linear-gradient(90deg,#18a0583d 0%,#18a0581a 100%);box-shadow:0 0 14px 2px #18a0584d}15%{background:linear-gradient(90deg,#18a05836 0%,#18a05817 100%);box-shadow:0 0 12px 1px #18a05840}30%{background:linear-gradient(90deg,#18a05829 0%,#18a05812 100%);box-shadow:0 0 8px 1px #18a0582e}50%{background:linear-gradient(90deg,#18a0581c 0%,#18a0580b 100%);box-shadow:0 0 5px #18a0581f}70%{background:linear-gradient(90deg,#18a0580f 0%,#18a05806 100%);box-shadow:0 0 3px #18a05814}85%{background:linear-gradient(90deg,#18a05808 0%,#18a05803 100%);box-shadow:0 0 1px #18a0580a}to{background:0 0;border-left-color:#0000;box-shadow:0 0 #18a05800}}.table-row-content[data-v-1d31e3d2]{width:100%;display:flex}.action-row[data-v-1d31e3d2]{color:#166534;background:#f0fdf4;border-bottom:1px solid #f3f4f6;border-left:3px solid #18a058;align-items:center;padding:10px 12px;font-size:13px;display:flex}[data-theme=dark] .action-row[data-v-1d31e3d2]{color:#4ade80;background:#18a0581f;border-bottom:1px solid #ffffff0a}.error-row[data-v-1d31e3d2]{color:#b42318;background:#fef2f2;border-bottom:1px solid #f3f4f6;border-left:3px solid #d03050;align-items:center;padding:10px 12px;font-size:13px;display:flex}[data-theme=dark] .error-row[data-v-1d31e3d2]{color:#fda4af;background:#d030501f;border-bottom:1px solid #ffffff0a}.action-message[data-v-1d31e3d2]{flex:1;font-weight:500}.action-time[data-v-1d31e3d2]{color:#6b7280;margin-left:12px;margin-right:22px;font-family:monospace;font-size:11px}[data-theme=dark] .action-time[data-v-1d31e3d2]{color:var(--text-tertiary)}.col[data-v-1d31e3d2]{white-space:nowrap;text-overflow:ellipsis;align-items:center;display:flex;overflow:hidden}.col-channel-claude[data-v-1d31e3d2]{flex:80px;min-width:0;overflow:hidden}.col-token-claude[data-v-1d31e3d2]{flex:0 0 68px;justify-content:center;min-width:0}.col-time-claude[data-v-1d31e3d2]{flex:0 0 85px;justify-content:flex-end;min-width:85px;padding-left:10px;padding-right:6px;font-family:monospace;font-size:11px}.col-channel-codex[data-v-1d31e3d2]{flex:65px;min-width:0;overflow:hidden}.col-token-codex[data-v-1d31e3d2]{flex:0 0 58px;justify-content:center;min-width:0}.col-time-codex[data-v-1d31e3d2]{flex:0 0 80px;justify-content:flex-end;min-width:80px;padding-left:10px;padding-right:6px;font-family:monospace;font-size:11px}.col-channel-gemini[data-v-1d31e3d2]{flex:80px;min-width:0;overflow:hidden}.col-token-gemini[data-v-1d31e3d2]{flex:0 0 68px;justify-content:center;min-width:0}.col-time-gemini[data-v-1d31e3d2]{flex:0 0 85px;justify-content:flex-end;min-width:85px;padding-left:10px;padding-right:6px;font-family:monospace;font-size:11px}.col-channel-opencode[data-v-1d31e3d2]{flex:65px;min-width:0;overflow:hidden}.col-token-opencode[data-v-1d31e3d2]{flex:0 0 58px;justify-content:center;min-width:0}.col-time-opencode[data-v-1d31e3d2]{flex:0 0 80px;justify-content:flex-end;min-width:80px;padding-left:10px;padding-right:6px;font-family:monospace;font-size:11px}.col-channel-omp[data-v-1d31e3d2]{flex:65px;min-width:0;overflow:hidden}.col-token-omp[data-v-1d31e3d2]{flex:0 0 58px;justify-content:center;min-width:0}.col-time-omp[data-v-1d31e3d2]{flex:0 0 80px;justify-content:flex-end;min-width:80px;padding-left:10px;padding-right:6px;font-family:monospace;font-size:11px}.col-channel-basic[data-v-1d31e3d2]{flex:80px;min-width:0;overflow:hidden}.col-token-basic[data-v-1d31e3d2]{flex:0 0 68px;justify-content:center;min-width:0}.col-time-basic[data-v-1d31e3d2]{flex:0 0 85px;justify-content:flex-end;min-width:85px;padding-left:10px;padding-right:6px;font-family:monospace;font-size:11px}.col-channel[data-v-1d31e3d2]{min-width:0;overflow:hidden}.col-channel .n-tag[data-v-1d31e3d2]{text-overflow:ellipsis;max-width:100%;overflow:hidden}.col-token[data-v-1d31e3d2]{justify-content:center;min-width:0}.col-time[data-v-1d31e3d2]{justify-content:flex-end;font-family:monospace;font-size:11px}.empty-state[data-v-1d31e3d2]{text-align:center;color:#9ca3af;padding:40px 16px;font-size:13px}[data-theme=dark] .empty-state[data-v-1d31e3d2]{color:var(--text-tertiary)}.logs-footer[data-v-1d31e3d2]{color:#6b7280;border-top:1px solid #e5e7eb;justify-content:space-between;align-items:center;padding:8px 16px;font-size:12px;display:flex}[data-theme=dark] .logs-footer[data-v-1d31e3d2]{border-top:1px solid var(--border-primary);color:var(--text-tertiary)}.status[data-v-1d31e3d2]{color:#ef4444;align-items:center;gap:4px;display:flex}.status.connected[data-v-1d31e3d2]{color:#10b981}[data-theme=dark] .status.connected[data-v-1d31e3d2]{color:#4ade80}.count[data-v-1d31e3d2]{color:#6b7280}[data-theme=dark] .count[data-v-1d31e3d2]{color:var(--text-tertiary)}@media (width<=1024px){.logs-header[data-v-1d31e3d2]{padding:10px 12px}.header-left[data-v-1d31e3d2]{gap:8px}.title[data-v-1d31e3d2]{font-size:13px}.today-stats[data-v-1d31e3d2] .n-tag{padding:1px 6px;font-size:10px}.table-header[data-v-1d31e3d2]{padding:6px 10px;font-size:10px}.table-row[data-v-1d31e3d2]{padding:6px 10px}.logs-footer[data-v-1d31e3d2]{padding:6px 12px;font-size:11px}.col-channel-claude[data-v-1d31e3d2],.col-channel-gemini[data-v-1d31e3d2],.col-channel-basic[data-v-1d31e3d2]{flex:0 0 75px}.col-token-claude[data-v-1d31e3d2],.col-token-gemini[data-v-1d31e3d2],.col-token-basic[data-v-1d31e3d2]{flex:0 0 55px}.col-time-claude[data-v-1d31e3d2],.col-time-gemini[data-v-1d31e3d2],.col-time-basic[data-v-1d31e3d2]{flex:0 0 80px;min-width:80px}.col-channel-codex[data-v-1d31e3d2],.col-channel-opencode[data-v-1d31e3d2],.col-channel-omp[data-v-1d31e3d2]{flex:0 0 65px}.col-token-codex[data-v-1d31e3d2],.col-token-opencode[data-v-1d31e3d2],.col-token-omp[data-v-1d31e3d2]{flex:0 0 48px}.col-time-codex[data-v-1d31e3d2],.col-time-opencode[data-v-1d31e3d2],.col-time-omp[data-v-1d31e3d2]{flex:0 0 75px;min-width:75px}}@media (width<=768px){.logs-header[data-v-1d31e3d2]{flex-direction:column;align-items:stretch;gap:8px;padding:8px 10px}.header-left[data-v-1d31e3d2]{justify-content:space-between}.title[data-v-1d31e3d2]{font-size:12px}.today-stats[data-v-1d31e3d2] .n-tag{padding:1px 4px;font-size:9px}.table-header[data-v-1d31e3d2],.table-row[data-v-1d31e3d2]{padding:4px 8px;font-size:10px}.table-row-content[data-v-1d31e3d2]{font-size:10px}.action-row[data-v-1d31e3d2]{padding:8px 10px;font-size:11px}.action-message[data-v-1d31e3d2]{font-size:11px}.action-time[data-v-1d31e3d2]{margin-right:12px;font-size:9px}.col-channel-claude[data-v-1d31e3d2],.col-channel-gemini[data-v-1d31e3d2],.col-channel-codex[data-v-1d31e3d2],.col-channel-opencode[data-v-1d31e3d2],.col-channel-omp[data-v-1d31e3d2],.col-channel-basic[data-v-1d31e3d2]{flex:0 0 60px}.col-channel[data-v-1d31e3d2] .n-tag{height:18px;padding:0 4px;font-size:9px}.col-token-claude[data-v-1d31e3d2],.col-token-gemini[data-v-1d31e3d2],.col-token-codex[data-v-1d31e3d2],.col-token-opencode[data-v-1d31e3d2],.col-token-omp[data-v-1d31e3d2],.col-token-basic[data-v-1d31e3d2]{flex:0 0 40px;font-size:9px}.col-time-claude[data-v-1d31e3d2],.col-time-gemini[data-v-1d31e3d2],.col-time-codex[data-v-1d31e3d2],.col-time-opencode[data-v-1d31e3d2],.col-time-omp[data-v-1d31e3d2],.col-time-basic[data-v-1d31e3d2]{flex:0 0 65px;min-width:65px;font-size:9px}.logs-footer[data-v-1d31e3d2]{padding:4px 10px;font-size:10px}}@media (width<=640px){.logs-header[data-v-1d31e3d2]{gap:6px;padding:6px 8px}.header-left[data-v-1d31e3d2]{flex-wrap:wrap;gap:4px}.title[data-v-1d31e3d2]{font-size:11px}.today-stats[data-v-1d31e3d2]{gap:4px}.today-stats[data-v-1d31e3d2] .n-tag{height:16px;padding:0 3px;font-size:8px}.table-header[data-v-1d31e3d2],.table-row[data-v-1d31e3d2]{padding:3px 6px;font-size:9px}.table-row-content[data-v-1d31e3d2]{font-size:9px}.action-row[data-v-1d31e3d2]{padding:6px 8px;font-size:10px}.action-message[data-v-1d31e3d2]{font-size:10px}.action-time[data-v-1d31e3d2]{margin-left:8px;margin-right:8px;font-size:8px}.col-channel-claude[data-v-1d31e3d2],.col-channel-gemini[data-v-1d31e3d2],.col-channel-codex[data-v-1d31e3d2],.col-channel-opencode[data-v-1d31e3d2],.col-channel-omp[data-v-1d31e3d2],.col-channel-basic[data-v-1d31e3d2]{flex:0 0 50px}.col-channel[data-v-1d31e3d2] .n-tag{height:16px;padding:0 3px;font-size:8px}.col-token-claude[data-v-1d31e3d2],.col-token-gemini[data-v-1d31e3d2],.col-token-codex[data-v-1d31e3d2],.col-token-opencode[data-v-1d31e3d2],.col-token-omp[data-v-1d31e3d2],.col-token-basic[data-v-1d31e3d2]{flex:0 0 32px;font-size:8px}.col-time-claude[data-v-1d31e3d2],.col-time-gemini[data-v-1d31e3d2],.col-time-codex[data-v-1d31e3d2],.col-time-opencode[data-v-1d31e3d2],.col-time-omp[data-v-1d31e3d2],.col-time-basic[data-v-1d31e3d2]{flex:0 0 55px;min-width:55px;font-size:8px}.logs-footer[data-v-1d31e3d2]{padding:3px 8px;font-size:9px}.status[data-v-1d31e3d2],.count[data-v-1d31e3d2]{font-size:9px}}@media (width<=480px){.logs-header[data-v-1d31e3d2]{padding:4px 6px}.title[data-v-1d31e3d2]{font-size:10px}.today-stats[data-v-1d31e3d2] .n-tag{height:14px;font-size:7px}.table-header[data-v-1d31e3d2],.table-row[data-v-1d31e3d2]{padding:2px 4px;font-size:8px}.action-row[data-v-1d31e3d2]{padding:4px 6px;font-size:9px}.action-row[data-v-1d31e3d2] .n-icon{margin-right:4px}.col-channel-claude[data-v-1d31e3d2],.col-channel-gemini[data-v-1d31e3d2],.col-channel-codex[data-v-1d31e3d2],.col-channel-opencode[data-v-1d31e3d2],.col-channel-omp[data-v-1d31e3d2],.col-channel-basic[data-v-1d31e3d2]{flex:0 0 45px}.col-token-claude[data-v-1d31e3d2],.col-token-gemini[data-v-1d31e3d2],.col-token-codex[data-v-1d31e3d2],.col-token-opencode[data-v-1d31e3d2],.col-token-omp[data-v-1d31e3d2],.col-token-basic[data-v-1d31e3d2]{flex:0 0 28px;font-size:7px}.col-time-claude[data-v-1d31e3d2],.col-time-gemini[data-v-1d31e3d2],.col-time-codex[data-v-1d31e3d2],.col-time-opencode[data-v-1d31e3d2],.col-time-omp[data-v-1d31e3d2],.col-time-basic[data-v-1d31e3d2]{flex:0 0 48px;min-width:48px;font-size:7px}.logs-footer[data-v-1d31e3d2]{padding:2px 6px;font-size:8px}}.right-panel[data-v-71b20d97]{background:var(--bg-primary);border-left:1px solid var(--border-primary);flex-direction:column;width:480px;min-width:480px;height:100%;display:flex}.toolbar-section[data-v-71b20d97]{background:var(--bg-secondary);border-bottom:1px solid var(--border-primary);flex-shrink:0;justify-content:space-between;align-items:center;padding:10px 14px;display:flex}.toolbar-left[data-v-71b20d97]{align-items:center;gap:8px;display:flex}.toolbar-label[data-v-71b20d97]{margin-right:2px;font-size:12px}.toolbar-right[data-v-71b20d97]{align-items:center;gap:2px;display:flex}.toolbar-btn[data-v-71b20d97]{--toolbar-button-text:var(--text-secondary);border-radius:4px;transition:transform .18s cubic-bezier(.4,0,.2,1);--n-text-color:var(--toolbar-button-text)!important;--n-text-color-hover:var(--toolbar-button-text)!important;--n-text-color-pressed:var(--toolbar-button-text)!important;--n-text-color-focus:var(--toolbar-button-text)!important;--n-color-hover:transparent!important;--n-color-pressed:transparent!important;--n-color-focus:transparent!important;color:var(--toolbar-button-text)!important;padding:5px!important}.toolbar-btn[data-v-71b20d97] .n-button__content,.toolbar-btn[data-v-71b20d97] .n-button__icon{color:var(--toolbar-button-text)!important}.toolbar-btn[data-v-71b20d97]:hover,.toolbar-btn[data-v-71b20d97]:focus,.toolbar-btn[data-v-71b20d97]:focus-visible,.toolbar-btn[data-v-71b20d97]:active{background:0 0;transform:translateY(-1px);color:var(--toolbar-button-text)!important}.toolbar-btn[data-v-71b20d97]:active{transform:translateY(0)}.toolbar-divider[data-v-71b20d97]{background:var(--border-primary);width:1px;height:16px;margin:0 4px}.oauth-banner[data-v-71b20d97]{color:#2080f0;background:#2080f014;border-bottom:1px solid #2080f033;flex-shrink:0;align-items:center;gap:6px;padding:6px 14px;font-size:12px;display:flex}.oauth-banner span[data-v-71b20d97]{flex:1}.channels-section[data-v-71b20d97]{flex-direction:column;flex:1;min-height:0;display:flex;overflow:hidden}.channels-section.full-height[data-v-71b20d97]{flex:1}.panel-header[data-v-71b20d97]{background:var(--bg-secondary);border-bottom:1px solid var(--border-primary);flex-shrink:0;justify-content:space-between;align-items:center;padding:12px 14px;display:flex}.header-title[data-v-71b20d97]{align-items:baseline;gap:8px;min-width:0;display:flex}.header-title h3[data-v-71b20d97]{color:var(--text-primary);margin:0;font-size:13px;font-weight:600}.header-hint[data-v-71b20d97]{font-size:11px}.header-actions[data-v-71b20d97]{flex-shrink:0;align-items:center;gap:8px;display:flex}.channels-scroll-area[data-v-71b20d97]{flex:1;min-height:0;padding:12px;overflow-y:auto}.logs-section[data-v-71b20d97]{flex:0 0 340px;min-height:340px;max-height:340px;overflow:hidden}.logs-section.full-height[data-v-71b20d97]{flex:1;min-height:0;max-height:none}@media (width<=1024px){.right-panel[data-v-71b20d97]{width:420px;min-width:420px}.logs-section[data-v-71b20d97]{flex:0 0 300px;min-height:300px;max-height:300px}}@media (width<=768px){.right-panel[data-v-71b20d97]{width:100%;min-width:100%}.toolbar-section[data-v-71b20d97]{padding:8px 12px}.toolbar-label[data-v-71b20d97]{font-size:11px}.logs-section[data-v-71b20d97]{flex:0 0 260px;min-height:260px;max-height:260px}}@media (height<=600px){.right-panel[data-v-71b20d97]{overflow-y:auto}.channels-section[data-v-71b20d97]{flex:0 0 360px;min-height:360px}.logs-section[data-v-71b20d97]{flex:0 0 260px}}.layout[data-v-182ef64e]{background:var(--bg-primary);flex-direction:column;height:100vh;display:flex}.header[data-v-182ef64e]{background:var(--gradient-header);border-bottom:1px solid var(--border-primary);flex-shrink:0;justify-content:space-between;align-items:center;height:74px;padding:10px 22px;display:flex;box-shadow:inset 0 1px #ffffff57}.header-actions[data-v-182ef64e]{background:color-mix(in srgb, var(--bg-primary) 82%, transparent);border:1px solid var(--border-primary);border-radius:999px;align-items:center;gap:8px;padding:6px 10px;display:flex;box-shadow:0 14px 34px #0f171d14}.more-menu-trigger[data-v-182ef64e]{display:flex}.header-divider[data-v-182ef64e]{background:#95a7af59;width:1px;height:24px;margin:0 2px}.env-warning-btn[data-v-182ef64e]{cursor:pointer;background:#f59e0b1a;border-radius:4px;align-items:center;gap:4px;padding:4px 8px;transition:background .2s;display:flex}.env-warning-btn[data-v-182ef64e]:hover{background:#f59e0b2e}.env-warning-icon[data-v-182ef64e]{color:#f59e0b}.env-warning-count[data-v-182ef64e]{color:#f59e0b;font-size:11px;font-weight:600}.logo-section[data-v-182ef64e]{cursor:pointer;color:inherit;border-radius:18px;align-items:center;gap:14px;margin-left:-8px;padding:8px 14px;text-decoration:none;transition:background .2s;display:flex}.logo-section[data-v-182ef64e]:hover{background:var(--hover-bg)}.logo-wrapper[data-v-182ef64e]{background:linear-gradient(135deg,#18a0582e,#18a0580d);border-radius:14px;justify-content:center;align-items:center;width:40px;height:40px;display:flex;box-shadow:0 10px 24px #0f171d14}.logo-image[data-v-182ef64e]{object-fit:contain;width:22px;height:22px}.title-group[data-v-182ef64e]{flex-direction:column;gap:1px;display:flex}.title-main[data-v-182ef64e]{color:var(--brand-title-color);margin:0;font-size:18px;font-weight:700;line-height:1.2}.title-sub[data-v-182ef64e]{color:var(--text-tertiary);font-size:11px}.nav-tabs[data-v-182ef64e]{background:color-mix(in srgb, var(--bg-primary) 86%, transparent);border:1px solid var(--border-primary);border-radius:999px;align-items:center;gap:6px;margin-left:28px;padding:6px 8px;display:flex;box-shadow:0 14px 36px #0f171d14}.nav-tab[data-v-182ef64e]{cursor:pointer;border-radius:999px;align-items:center;gap:7px;padding:8px 14px;transition:background .2s;display:flex;position:relative}.nav-tab[data-v-182ef64e]:hover{background:var(--hover-bg)}.nav-tab.active[data-v-182ef64e]{background:var(--primary-color-soft)}.nav-tab.active[data-v-182ef64e]:after{content:"";border-radius:inherit;background:0 0;border:1px solid #18a05838;position:absolute;inset:0}.nav-icon[data-v-182ef64e]{color:var(--text-tertiary)}.nav-tab:hover .nav-icon[data-v-182ef64e],.nav-tab.active .nav-icon[data-v-182ef64e]{color:var(--primary-color,#18a058)}.nav-label[data-v-182ef64e]{color:var(--text-secondary);font-size:13px;font-weight:500}.nav-tab:hover .nav-label[data-v-182ef64e],.nav-tab.active .nav-label[data-v-182ef64e]{color:var(--primary-color,#18a058)}.nav-tab.active .nav-label[data-v-182ef64e]{font-weight:600}.main-container[data-v-182ef64e]{flex:1;height:calc(100vh - 74px);min-height:0;display:flex;overflow:hidden}.global-loading-overlay[data-v-182ef64e]{background:var(--bg-overlay);z-index:1000;justify-content:center;align-items:center;display:flex;position:absolute;inset:0}.left-content[data-v-182ef64e]{flex:1;min-width:0;height:100%;overflow:hidden}.favorites-button-wrapper[data-v-182ef64e]{position:relative}.favorites-badge[data-v-182ef64e]{color:#fff;pointer-events:none;background:#ff4d4f;border-radius:7px;justify-content:center;align-items:center;min-width:14px;height:14px;padding:0 3px;font-size:9px;font-weight:600;display:flex;position:absolute;top:-3px;right:-3px}.help-content[data-v-182ef64e]{max-height:70vh;overflow-y:auto}.help-section[data-v-182ef64e]{background:var(--bg-secondary);border:1px solid var(--border-primary);border-radius:8px;margin-bottom:20px;padding:16px}.help-section[data-v-182ef64e]:last-child{margin-bottom:0}.help-section h4[data-v-182ef64e]{color:var(--text-primary);border-bottom:1px solid var(--border-primary);margin:0 0 12px;padding-bottom:10px;font-size:15px;font-weight:600}.help-section p[data-v-182ef64e]{color:var(--text-secondary);margin:0 0 10px;font-size:13px;line-height:1.7}.help-section ul[data-v-182ef64e]{margin:8px 0 0;padding-left:20px}.help-section li[data-v-182ef64e]{color:var(--text-secondary);margin-bottom:4px;font-size:13px;line-height:1.8}.help-section li strong[data-v-182ef64e]{color:var(--primary-color,#18a058)}.command-list[data-v-182ef64e]{flex-direction:column;gap:8px;display:flex}.command-item[data-v-182ef64e]{background:var(--bg-primary);border:1px solid var(--border-primary);border-radius:6px;align-items:center;gap:12px;padding:10px 12px;display:flex}.command-item code[data-v-182ef64e]{min-width:140px;color:var(--primary-color,#18a058);background:#18a0581a;border-radius:4px;padding:4px 10px;font-family:monospace;font-size:12px;font-weight:600}.command-item span[data-v-182ef64e]{color:var(--text-secondary);font-size:13px}.help-section kbd[data-v-182ef64e]{color:var(--text-primary);background:var(--bg-primary);border:1px solid var(--border-primary);border-radius:3px;margin:0 2px;padding:2px 6px;font-family:monospace;font-size:11px;font-weight:600;display:inline-block}.link-list[data-v-182ef64e]{flex-wrap:wrap;gap:12px;display:flex}.link-list a[data-v-182ef64e]{color:var(--primary-color,#18a058);background:#18a05814;border-radius:4px;padding:6px 10px;font-size:13px;text-decoration:none}.link-list a[data-v-182ef64e]:hover{background:#18a05826}.panel-security-auth[data-v-182ef64e]{flex-direction:column;gap:12px;display:flex}.panel-security-error[data-v-182ef64e]{color:#d03050;font-size:12px}.panel-security-hint[data-v-182ef64e]{font-size:12px;line-height:1.6}.header-actions[data-v-182ef64e],.nav-tabs[data-v-182ef64e]{-webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px)}[data-theme=dark] .header[data-v-182ef64e]{border-bottom-color:#2b424aeb;box-shadow:inset 0 -1px #ffffff08}[data-theme=dark] .header-actions[data-v-182ef64e],[data-theme=dark] .nav-tabs[data-v-182ef64e]{background:#0c151bdb;border-color:#364c56e6;box-shadow:0 14px 32px #00000042}[data-theme=dark] .logo-section[data-v-182ef64e]:hover{background:#ffffff0d}@media (width<=1024px){.header[data-v-182ef64e]{height:68px;padding:10px 16px}.main-container[data-v-182ef64e]{height:calc(100vh - 68px)}.nav-tabs[data-v-182ef64e]{margin-left:16px;padding:5px 6px}.title-sub[data-v-182ef64e]{display:none}}@media (width<=768px){.header[data-v-182ef64e]{height:60px;padding:8px 12px}.main-container[data-v-182ef64e]{height:calc(100vh - 60px)}.logo-wrapper[data-v-182ef64e]{width:28px;height:28px}.logo-image[data-v-182ef64e]{width:18px;height:18px}.title-main[data-v-182ef64e]{font-size:14px}.title-sub[data-v-182ef64e]{display:none}.nav-tabs[data-v-182ef64e]{gap:2px;margin-left:10px;padding:4px 5px}.nav-tab[data-v-182ef64e]{gap:4px;padding:6px 10px}.nav-label[data-v-182ef64e]{font-size:12px}.header-actions[data-v-182ef64e]{gap:2px}}@media (width<=640px){.header[data-v-182ef64e]{height:54px;padding:6px 8px}.main-container[data-v-182ef64e]{height:calc(100vh - 54px)}.title-group[data-v-182ef64e]{display:none}.nav-tabs[data-v-182ef64e]{flex:1;justify-content:center;margin-left:8px}.nav-tab[data-v-182ef64e]{flex-direction:column;gap:2px;padding:4px 6px}.nav-label[data-v-182ef64e]{font-size:10px}}.asset-panel{background:var(--bg-primary);flex-direction:column;flex:1;height:100%;min-height:0;display:flex}.asset-panel-header,.asset-drawer-toolbar{border-bottom:1px solid var(--border-primary);background:var(--bg-primary);justify-content:space-between;align-items:center;gap:16px;padding:14px 18px;display:flex}.asset-drawer-toolbar{background:var(--bg-secondary);justify-content:flex-end;padding:10px 12px}.asset-title-group,.asset-title-row,.asset-action-row,.asset-filter-bar,.asset-summary,.asset-meta,.asset-tags,.asset-card-actions{align-items:center;display:flex}.asset-title-group{gap:10px;min-width:0}.asset-heading{min-width:0}.asset-title-row{gap:8px;min-width:0}.asset-title{color:var(--text-primary);text-overflow:ellipsis;white-space:nowrap;font-size:15px;font-weight:650;line-height:1.25;overflow:hidden}.asset-subtitle{color:var(--text-tertiary);margin-top:3px;font-size:12px;line-height:1.35}.asset-platform-pill{border:1px solid var(--border-primary);background:var(--bg-secondary);color:var(--text-tertiary);border-radius:999px;flex-shrink:0;padding:2px 7px;font-size:11px;font-weight:500;line-height:1.4}.asset-action-row{flex-shrink:0;gap:6px}.asset-action-row .n-button{font-size:12px}.asset-summary{border-bottom:1px solid var(--border-primary);background:var(--bg-secondary);gap:8px;padding:10px 18px}.asset-panel.in-drawer .asset-summary{padding:10px 12px}.asset-summary-item{border:1px solid var(--border-primary);background:var(--bg-primary);border-radius:999px;align-items:baseline;gap:6px;min-width:0;padding:4px 8px;display:inline-flex}.asset-summary-label{color:var(--text-tertiary);font-size:11px}.asset-summary-value{color:var(--text-primary);font-variant-numeric:tabular-nums;font-size:12px;font-weight:650}.asset-filter-bar{border-bottom:1px solid var(--border-primary);background:var(--bg-primary);gap:10px;padding:12px 18px}.asset-panel.in-drawer .asset-filter-bar{padding:10px 12px}.asset-search{flex:1;min-width:160px}.asset-filter{flex-shrink:0;width:116px}.asset-panel-content{flex:1;min-height:0;padding:16px 18px;overflow-y:auto}.asset-panel.in-drawer .asset-panel-content{padding:12px}.asset-panel-content .n-spin-container{min-height:220px}.asset-empty{justify-content:center;align-items:center;min-height:220px;display:flex}.asset-list{flex-direction:column;gap:10px;display:flex}.asset-footer{border-top:1px solid var(--border-primary);background:var(--bg-secondary);color:var(--text-tertiary);align-items:center;gap:6px;padding:9px 18px;font-size:11px;display:flex}.asset-panel.in-drawer .asset-footer{padding:8px 12px}.asset-info-icon{color:var(--text-quaternary)}.asset-card{border:1px solid var(--border-primary);background:var(--bg-primary);cursor:pointer;border-radius:8px;justify-content:space-between;align-items:flex-start;gap:14px;min-height:96px;padding:14px;transition:border-color .16s ease-out,background-color .16s ease-out,box-shadow .16s ease-out;display:flex}.asset-card:hover{border-color:var(--border-secondary);background:var(--bg-secondary);box-shadow:var(--shadow-sm)}.asset-card.is-installed,.asset-card.is-managed,.asset-card.is-project{background:linear-gradient(0deg, oklch(62% .15 151/.045), oklch(62% .15 151/.045)), var(--bg-primary)}.asset-card.is-disabled,.asset-card.is-protected{background:var(--bg-secondary)}.asset-card-main{flex:1;min-width:0}.asset-card-top{justify-content:space-between;align-items:flex-start;gap:12px;display:flex}.asset-card-title-stack{min-width:0}.asset-card-title-row{align-items:center;gap:7px;min-width:0;display:flex}.asset-status-dot{background:var(--text-quaternary);border-radius:999px;flex:none;width:7px;height:7px}.asset-status-dot.active{background:var(--primary-color);box-shadow:0 0 0 3px oklch(62% .15 151/.1)}.asset-status-dot.warning{background:oklch(68% .14 78);box-shadow:0 0 0 3px oklch(68% .14 78/.1)}.asset-name{min-width:0;color:var(--text-primary);text-overflow:ellipsis;white-space:nowrap;font-size:13px;font-weight:650;line-height:1.35;overflow:hidden}.asset-name.mono{font-family:var(--font-mono,"SF Mono", "JetBrains Mono", "Cascadia Code", monospace)}.asset-tags{flex-wrap:wrap;gap:5px;margin-top:6px}.asset-description{color:var(--text-secondary);text-overflow:ellipsis;-webkit-line-clamp:2;-webkit-box-orient:vertical;margin-top:9px;font-size:12px;line-height:1.5;display:-webkit-box;overflow:hidden}.asset-meta{color:var(--text-tertiary);flex-wrap:wrap;gap:8px;margin-top:9px;font-size:11px}.asset-meta-item{text-overflow:ellipsis;white-space:nowrap;align-items:center;gap:4px;min-width:0;max-width:100%;display:inline-flex;overflow:hidden}.asset-meta-item.mono{font-family:var(--font-mono,"SF Mono", "JetBrains Mono", "Cascadia Code", monospace)}.asset-link{color:var(--primary-color);flex-shrink:0;text-decoration:none}.asset-link:hover{text-decoration:underline}.asset-card-actions{flex-shrink:0;gap:5px}.asset-platform-strip{align-items:center;gap:5px;margin-left:6px;display:flex}.asset-platform-icon{border:1px solid var(--border-primary);background:var(--bg-secondary);width:24px;height:24px;color:var(--text-tertiary);cursor:pointer;border-radius:6px;justify-content:center;align-items:center;transition:border-color .16s ease-out,color .16s ease-out,background-color .16s ease-out;display:inline-flex}.asset-platform-icon:hover{border-color:var(--border-secondary);color:var(--text-secondary)}.asset-platform-icon.active{color:var(--primary-color);background:oklch(62% .15 151/.1);border-color:oklch(62% .15 151/.34)}.asset-detail-drawer-header{width:100%}.asset-detail-identity{align-items:center;gap:12px;min-width:0;display:flex}.asset-detail-icon{color:var(--primary-color);flex-shrink:0}.asset-detail-heading{flex-direction:column;gap:4px;min-width:0;display:flex}.asset-detail-name{color:var(--text-primary);text-overflow:ellipsis;white-space:nowrap;font-size:16px;font-weight:650;line-height:1.25;overflow:hidden}.asset-detail-name.mono{font-family:var(--font-mono,"SF Mono", "JetBrains Mono", "Cascadia Code", monospace)}.asset-detail-meta{flex-wrap:wrap;align-items:center;gap:8px;min-width:0;display:flex}.asset-detail-subtle{min-width:0;color:var(--text-tertiary);align-items:center;gap:4px;font-size:12px;display:inline-flex}.asset-detail-body{flex-direction:column;height:100%;min-height:0;display:flex;overflow:hidden}.asset-detail-container{flex-direction:column;gap:22px;min-height:0;display:flex}.asset-detail-info-section{flex-direction:column;gap:10px;display:flex}.asset-detail-section-title{color:var(--text-primary);margin:0;font-size:14px;font-weight:650}.asset-detail-description{color:var(--text-secondary);margin:0;font-size:13px;line-height:1.65}.asset-detail-list{border:1px solid var(--border-primary);background:var(--bg-secondary);border-radius:8px;overflow:hidden}.asset-detail-row{border-bottom:1px solid var(--border-primary);grid-template-columns:104px minmax(0,1fr);gap:12px;padding:10px 12px;font-size:13px;display:grid}.asset-detail-row:last-child{border-bottom:0}.asset-detail-label{color:var(--text-tertiary)}.asset-detail-value{overflow-wrap:anywhere;min-width:0;color:var(--text-secondary)}.asset-detail-value.mono{font-family:var(--font-mono,"SF Mono", "JetBrains Mono", "Cascadia Code", monospace);font-size:12px}.asset-detail-box{border:1px solid var(--border-primary);background:var(--bg-secondary);min-width:0;color:var(--text-secondary);border-radius:8px;align-items:center;gap:8px;padding:12px;font-size:13px;display:flex}.asset-detail-box code,.asset-detail-code{font-family:var(--font-mono,"SF Mono", "JetBrains Mono", "Cascadia Code", monospace)}.asset-detail-box code{overflow-wrap:anywhere;color:var(--text-secondary);font-size:12px}.asset-detail-path-box{align-items:flex-start}.asset-detail-path-icon{flex:none;margin-top:1px}.asset-detail-path-text{flex:1;min-width:0;line-height:1.5}.asset-detail-path-copy{color:var(--text-tertiary);flex:none;margin:-4px -4px -4px 4px}.asset-detail-path-copy:hover{color:var(--text-primary)}.asset-detail-link{overflow-wrap:anywhere;min-width:0;color:var(--primary-color);text-decoration:none}.asset-detail-link:hover{text-decoration:underline}.asset-detail-actions{flex-wrap:wrap;align-items:center;gap:8px;display:flex}.asset-detail-content{border:1px solid var(--border-primary);background:var(--bg-secondary);min-height:0;color:var(--text-secondary);overflow-wrap:anywhere;border-radius:8px;flex:1;padding:18px;font-size:13px;line-height:1.7;overflow:auto}.asset-detail-content h1,.asset-detail-content h2,.asset-detail-content h3,.asset-detail-content h4,.asset-detail-content h5,.asset-detail-content h6{color:var(--text-primary);margin:22px 0 10px;font-weight:650;line-height:1.35}.asset-detail-content h1:first-child,.asset-detail-content h2:first-child,.asset-detail-content h3:first-child{margin-top:0}.asset-detail-content h1{border-bottom:1px solid var(--border-primary);padding-bottom:10px;font-size:20px}.asset-detail-content h2{border-bottom:1px solid var(--border-primary);padding-bottom:7px;font-size:17px}.asset-detail-content h3{font-size:15px}.asset-detail-content h4{font-size:14px}.asset-detail-content h5,.asset-detail-content h6{color:var(--text-secondary);font-size:13px}.asset-detail-content p{margin:10px 0}.asset-detail-content strong{color:var(--text-primary);font-weight:650}.asset-detail-content code{border:1px solid var(--border-primary);background:var(--bg-tertiary);font-family:var(--font-mono,"SF Mono", "JetBrains Mono", "Cascadia Code", monospace);border-radius:5px;padding:2px 6px;font-size:12px}.asset-detail-content pre{border:1px solid var(--border-primary);background:var(--bg-primary);border-radius:8px;margin:14px 0;padding:14px;line-height:1.65;overflow-x:auto}.asset-detail-content pre code{color:var(--text-secondary);background:0 0;border:0;padding:0;font-size:12px}.asset-detail-content ul,.asset-detail-content ol{margin:10px 0;padding-left:24px}.asset-detail-content li{margin:5px 0}.asset-detail-content li>p{margin:6px 0}.asset-detail-content a{color:var(--primary-color);text-underline-offset:3px;text-decoration:underline;text-decoration-thickness:1px}.asset-detail-content a:hover{color:var(--primary-color-hover,var(--primary-color))}.asset-detail-content blockquote{border-left:1px solid var(--border-secondary);background:var(--bg-primary);color:var(--text-tertiary);border-radius:0 8px 8px 0;margin:14px 0;padding:10px 12px}.asset-detail-content blockquote>:first-child{margin-top:0}.asset-detail-content blockquote>:last-child{margin-bottom:0}.asset-detail-content table{border-collapse:collapse;width:max-content;max-width:100%;margin:14px 0;font-size:12px;display:block;overflow-x:auto}.asset-detail-content th,.asset-detail-content td{border:1px solid var(--border-primary);text-align:left;vertical-align:top;padding:8px 10px}.asset-detail-content th{background:var(--bg-tertiary);color:var(--text-primary);font-weight:650}.asset-detail-content img{border-radius:8px;max-width:100%;height:auto;margin:14px auto;display:block}.asset-detail-content hr{background:var(--border-primary);border:0;height:1px;margin:20px 0}.asset-detail-code{border:1px solid var(--border-primary);background:var(--bg-secondary);min-height:0;color:var(--text-secondary);white-space:pre-wrap;word-break:break-word;border-radius:8px;flex:1;margin:0;padding:16px;font-size:12px;line-height:1.65;overflow:auto}.asset-detail-loading,.asset-detail-empty,.asset-detail-error{color:var(--text-tertiary);text-align:center;flex-direction:column;justify-content:center;align-items:center;gap:16px;padding:64px 24px;display:flex}.asset-detail-empty-icon,.asset-detail-error-icon{color:var(--text-quaternary)}.asset-detail-error-icon{color:oklch(56% .18 26)}.asset-detail-tabs{flex:1;height:100%;min-height:0}.asset-detail-tabs .n-tabs-pane-wrapper{flex:1;min-height:0;padding-top:16px;overflow:hidden}.asset-detail-tabs .n-tab-pane{flex-direction:column;height:100%;min-height:0;display:flex;overflow:auto}@media (width<=720px){.asset-panel-header{flex-direction:column;align-items:flex-start}.asset-action-row,.asset-filter-bar,.asset-card-top{width:100%}.asset-action-row,.asset-summary{flex-wrap:wrap}.asset-filter-bar{flex-direction:column;align-items:stretch}.asset-filter{width:100%}.asset-card{flex-direction:column}.asset-card-actions{flex-wrap:wrap;align-self:flex-start}}:root{--bg-primary:#fff;--bg-secondary:#f4f7f8;--bg-tertiary:#edf3f1;--bg-elevated:#fff;--bg-overlay:#ffffffeb;--text-primary:#122025;--text-secondary:#58717d;--text-tertiary:#738995;--text-quaternary:#95a7af;--border-primary:#d8e1de;--border-secondary:#c3d0cb;--hover-bg:#18a05814;--active-bg:#18a0581f;--shadow-sm:0 8px 20px #0f171d0f;--shadow-md:0 18px 42px #0f171d1a;--shadow-lg:0 30px 70px #0f171d24;--font-mono:"SF Mono", "JetBrains Mono", "Cascadia Code", "Fira Code", monospace;--gradient-header:linear-gradient(135deg, #f8fbfc 0%, #eef6f3 52%, #e9f7f1 100%);--gradient-bg:linear-gradient(180deg, #f4f7f8 0%, #eef4f2 100%);--gradient-card:linear-gradient(145deg, #fffffffa 0%, #f7faf8fa 100%);--primary-color:#18a058;--primary-color-soft:#18a0581f;--brand-title-color:#0d8f55}[data-theme=dark]{--bg-primary:#0a1116;--bg-secondary:#101920;--bg-tertiary:#16222a;--bg-elevated:#1a2831;--bg-overlay:#0a1116f0;--text-primary:#e7eef0;--text-secondary:#a8bbc0;--text-tertiary:#82969d;--text-quaternary:#667b83;--border-primary:#263740;--border-secondary:#334852;--hover-bg:#ffffff14;--active-bg:#ffffff1f;--shadow-sm:0 10px 24px #0000003d;--shadow-md:0 22px 52px #0000004d;--shadow-lg:0 36px 76px #0000005c;--font-mono:"SF Mono", "JetBrains Mono", "Cascadia Code", "Fira Code", monospace;--gradient-header:linear-gradient(135deg, #0b1419 0%, #101b22 58%, #0d171d 100%);--gradient-bg:linear-gradient(180deg, #0a1116 0%, #0d151b 56%, #091016 100%);--gradient-card:linear-gradient(145deg, #121b22fa 0%, #0c151bfa 100%);--primary-color:#18a058;--primary-color-soft:#18a05829;--brand-title-color:#37d08c}body{background:var(--bg-primary);color:var(--text-primary);margin:0;padding:0;font-family:SF Pro Display,SF Pro Text,Segoe UI Variable Display,PingFang SC,Hiragino Sans GB,Microsoft YaHei,sans-serif;transition:background-color .3s,color .3s}#app{background:var(--bg-primary);min-height:100vh}*{scrollbar-width:thin;scrollbar-color:#18a05847 transparent}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-track{background:0 0}::-webkit-scrollbar-thumb{background-color:#18a05847;border-radius:3px;transition:background-color .2s}::-webkit-scrollbar-thumb:hover{background-color:#18a05880}[data-theme=dark] ::-webkit-scrollbar-thumb{background-color:#18a05866}[data-theme=dark] ::-webkit-scrollbar-thumb:hover{background-color:#18a05899}