waygate-open 1.6.15 → 1.6.17

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 (27) hide show
  1. package/README.md +2 -2
  2. package/package.json +1 -1
  3. package/server/dist/api/overview.service.js +6 -1
  4. package/server/dist/api/providers.service.js +20 -16
  5. package/server/dist/config/config.service.js +25 -0
  6. package/server/dist/proxy/quota/quota-parse.js +9 -4
  7. package/server/dist/version.js +1 -1
  8. package/server/package.json +1 -1
  9. package/web/dist/assets/{ApiKeysPage-CdguoLCa.js → ApiKeysPage-CNwL6k51.js} +1 -1
  10. package/web/dist/assets/ClientMetricTip-Doe5BMQH.js +1 -0
  11. package/web/dist/assets/ClientsPage-BOO_gjVL.js +1 -0
  12. package/web/dist/assets/LandingPage-Bx7xbIg8.css +1 -0
  13. package/web/dist/assets/{LandingPage-Cwbtdh2I.js → LandingPage-C8o638Op.js} +1 -1
  14. package/web/dist/assets/OverviewPage-BCLvrGFb.js +1 -0
  15. package/web/dist/assets/{RequestsPage-ivdKipjv.js → RequestsPage-BriBXVtF.js} +1 -1
  16. package/web/dist/assets/{Select-HrLgXSri.js → Select-CE8etafp.js} +1 -1
  17. package/web/dist/assets/{SettingsPage-PX7wr7r_.js → SettingsPage-C6Pm1MaS.js} +5 -3
  18. package/web/dist/assets/UsagePage-sKDKbUkP.js +1 -0
  19. package/web/dist/assets/{index-BJtz6Ses.js → index-Bb8UvXuA.js} +2 -2
  20. package/web/dist/assets/index-BzSTojlK.css +1 -0
  21. package/web/dist/index.html +2 -2
  22. package/web/dist/assets/ClientMetricTip-DIe3I7w4.js +0 -1
  23. package/web/dist/assets/ClientsPage-D5F7-rin.js +0 -1
  24. package/web/dist/assets/LandingPage-B2hMgbmZ.css +0 -1
  25. package/web/dist/assets/OverviewPage-AeWM2Lg5.js +0 -1
  26. package/web/dist/assets/UsagePage-oe_p1Aiz.js +0 -1
  27. package/web/dist/assets/index-CEoe20BF.css +0 -1
package/README.md CHANGED
@@ -8,7 +8,7 @@
8
8
  [![NestJS](https://img.shields.io/badge/NestJS-12-E0234E?style=flat-square)](https://nestjs.com)
9
9
  [![SQLite](https://img.shields.io/badge/SQLite-better--sqlite3-003B57?style=flat-square)](https://github.com/WiseLibs/better-sqlite3)
10
10
  [![Node](https://img.shields.io/badge/Node-%E2%89%A522.9-3C873A?style=flat-square)](https://nodejs.org)
11
- [![version](https://img.shields.io/badge/version-1.6.15-4A38C0?style=flat-square)](https://github.com/zhanghaojie513/waygate-open/releases)
11
+ [![version](https://img.shields.io/badge/version-1.6.17-4A38C0?style=flat-square)](https://github.com/zhanghaojie513/waygate-open/releases)
12
12
  [![tests](https://img.shields.io/badge/tests-741%20passed-0A7247?style=flat-square)](./server)
13
13
 
14
14
  ![途关 · Waygate](docs/img/intro.png)
@@ -391,4 +391,4 @@ tools/ 仓库级工具(lint 门禁、token 合并、截图
391
391
 
392
392
  ---
393
393
 
394
- 版本约定:`1.0.0` 起 API 契约稳定(当前 `1.6.15`,见 `server/src/version.ts`;历史版本见 [Releases](https://github.com/zhanghaojie513/waygate-open/releases))。
394
+ 版本约定:`1.0.0` 起 API 契约稳定(当前 `1.6.17`,见 `server/src/version.ts`;历史版本见 [Releases](https://github.com/zhanghaojie513/waygate-open/releases))。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "waygate-open",
3
- "version": "1.6.15",
3
+ "version": "1.6.17",
4
4
  "description": "途关 Waygate · 本地启动 —— 把 WorkBuddy / MiniMax Code / Qoder CN 的客户端订阅转成 OpenAI / Anthropic 兼容 base_url(自带控制台,一条命令起服务并自动打开浏览器)",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -112,6 +112,11 @@ let OverviewService = OverviewService_1 = class OverviewService {
112
112
  MAX(ts) AS lastTs,
113
113
  MAX(key_hint) AS keyHint
114
114
  FROM requests WHERE ts >= ? AND ts < ?
115
+ /* [2026-10-17] 排除 model 为空的行:实测 36 条全是 429 并发拒绝 ——
116
+ 请求在模型解析之前就被闸挡下,model 列为空串;它们不是任何
117
+ 「模型」的用量,进 byModel 只会多出一行 0 token 的「未知模型」。
118
+ metrics.requests 仍计入(被拒也是一次请求),仅摘要剔除。 */
119
+ AND model <> ''
115
120
  GROUP BY client_id, model
116
121
  )
117
122
  SELECT g.client_id AS clientId, g.model, g.requests, g.errors, g.creditsRaw, g.knownCount,
@@ -451,7 +456,7 @@ let OverviewService = OverviewService_1 = class OverviewService {
451
456
  升格为概览横幅(与 11128 告警同机制)。按 note 计数(upstream_credential_invalid
452
457
  是 note 标记而非独立业务码列);阈值 1 —— 单次即报,停摆级不等累积。
453
458
  窗口与 channelReject 一致:固定最近 1 小时,与 window 参数无关。 */
454
- const credentialInvalidCount = this.stmt(`SELECT COUNT(*) AS n FROM requests
459
+ const credentialInvalidCount = this.stmt(`SELECT COUNT(*) AS n FROM requests
455
460
  WHERE ts >= ? AND ts < ? AND note LIKE '%' || ? || '%' AND status >= 400`)
456
461
  .get(now - exports.CHANNEL_REJECT_WINDOW_MS, now, 'upstream_credential_invalid').n;
457
462
  const channelRejectCount = this.stmt(`SELECT COUNT(*) AS n FROM requests
@@ -150,7 +150,7 @@ let ProvidersService = ProvidersService_1 = class ProvidersService {
150
150
  return;
151
151
  const baseUrl = this.config.get().upstream.baseUrl;
152
152
  this.repo.create({ id: exports.DEFAULT_PROVIDER_ID, name: exports.DEFAULT_PROVIDER_NAME, baseUrl, active: true });
153
- /* [v1.6.11] 不再把 config.upstreamKey 迁到 .key —— WorkBuddy 的凭证是 OAuth 槽
153
+ /* [v1.6.13] 不再把 config.upstreamKey 迁到 .key —— WorkBuddy 的凭证是 OAuth 槽
154
154
  (ck_ 已退役)。升级后该行显示「未配置」,用户在控制台点「重新导入授权」即可。
155
155
  旧 .key 文件如存在也不读(不再是凭证来源),保留不删以免误伤回滚。 */
156
156
  if (this.catalog) {
@@ -242,7 +242,7 @@ let ProvidersService = ProvidersService_1 = class ProvidersService {
242
242
  if (row.protocol === 'anthropic')
243
243
  this.minimaxTokens?.invalidate(id);
244
244
  }
245
- /* [v1.6.11] openai 走上面的 parseCredential 报错(ck_ 已退役)—— 这里到不了。 */
245
+ /* [v1.6.13] openai 走上面的 parseCredential 报错(ck_ 已退役)—— 这里到不了。 */
246
246
  }
247
247
  /* 活提供商的地址改了 → 立即写进设置(转发/实测/目录拉取都读它) */
248
248
  const updated = this.repo.findById(id);
@@ -267,7 +267,7 @@ let ProvidersService = ProvidersService_1 = class ProvidersService {
267
267
  其余都不放:
268
268
  · [v0.5.0] anthropic+oauth_minimax:文件里是 refresh token,出站时换 access token;
269
269
  · [v0.6.0] qoder:凭证是身份 JSON,出站由 QoderCredentialStore 直读文件;
270
- · [v1.6.11] openai(WorkBuddy):凭证是 OAuth 槽,出站由 WbOauthService 取(ck_ 已退役)。 */
270
+ · [v1.6.13] openai(WorkBuddy):凭证是 OAuth 槽,出站由 WbOauthService 取(ck_ 已退役)。 */
271
271
  if (row.protocol === 'anthropic' && row.cred_kind !== 'oauth_minimax') {
272
272
  this.config.setUpstreamKey(this.readCredentialFile(id));
273
273
  }
@@ -294,15 +294,19 @@ let ProvidersService = ProvidersService_1 = class ProvidersService {
294
294
  this.repo.delete(id);
295
295
  /* 目录分区一并清(多生效下其他家的清单不动)。 */
296
296
  this.catalog?.clearProvider(id);
297
- /* 偏好与凭证都是"这家提供商的",一起清(provider_model_prefs 无 FK,不连带清理会留孤儿行)。 */
297
+ /* 偏好与凭证都是"这家提供商的",一起清(provider_model_prefs 无 FK,不连带清理会留孤儿行)。
298
+ [2026-10-17] 凭证清理从「只删 .key」扩到「该 id 的全部凭证类文件」——
299
+ 实测删 p_mm0001 后 .access 残留(token 服务的缓存文件不在原清理范围)。 */
298
300
  this.prefs.deleteForProvider(id);
299
- try {
300
- const file = this.credentialPath(id);
301
- if ((0, node_fs_1.existsSync)(file))
302
- (0, node_fs_1.unlinkSync)(file);
303
- }
304
- catch (err) {
305
- this.logger.warn(`删除凭证文件失败(提供商已删,文件遗留无危害):${err.message}`);
301
+ for (const suffix of ['.key', '.access', '.oauth']) {
302
+ try {
303
+ const file = this.credentialPath(id).replace(/\.key$/, suffix);
304
+ if ((0, node_fs_1.existsSync)(file))
305
+ (0, node_fs_1.unlinkSync)(file);
306
+ }
307
+ catch (err) {
308
+ this.logger.warn(`删除凭证文件 ${suffix} 失败(提供商已删,文件遗留无危害):${err.message}`);
309
+ }
306
310
  }
307
311
  this.logger.log(`删除提供商 ${row.name}(${row.id})`);
308
312
  }
@@ -342,7 +346,7 @@ let ProvidersService = ProvidersService_1 = class ProvidersService {
342
346
  const row = this.mustFind(id);
343
347
  let key;
344
348
  if (row.protocol === 'openai') {
345
- /* [v1.6.11] WorkBuddy 的凭证是 OAuth 槽(不与行内文件同址)—— 从服务取 access token。
349
+ /* [v1.6.13] WorkBuddy 的凭证是 OAuth 槽(不与行内文件同址)—— 从服务取 access token。
346
350
  不接草稿 credential(ck_ 已退役,界面也不再提供输入框)。 */
347
351
  if (rawKey !== undefined && rawKey !== null && rawKey !== '') {
348
352
  throw this.bad('WorkBuddy 已不再使用 ck_ 密钥 —— 请用「重新导入授权」完成官方授权。');
@@ -397,7 +401,7 @@ let ProvidersService = ProvidersService_1 = class ProvidersService {
397
401
  // ---- 内部 ----
398
402
  toItem(row) {
399
403
  const key = this.readCredentialFile(row.id);
400
- /* [v1.6.11] openai(WorkBuddy)的「已配置」改看 **OAuth 槽** —— ck_ 已退役。
404
+ /* [v1.6.13] openai(WorkBuddy)的「已配置」改看 **OAuth 槽** —— ck_ 已退役。
401
405
  其余两家仍看凭证文件(anthropic = mmort_ refresh token / M Plan key;
402
406
  qoder = 身份 JSON)。 */
403
407
  const isOpenai = row.protocol !== 'anthropic' && row.protocol !== 'qoder';
@@ -452,7 +456,7 @@ let ProvidersService = ProvidersService_1 = class ProvidersService {
452
456
  }
453
457
  /** 写进 settings.upstream.baseUrl —— 持久化 + 热生效(resolveUpstreamBase 每请求都读它)。 */
454
458
  applyBaseUrlToSettings(baseUrl) {
455
- this.config.applyUpdates([{ key: 'upstream.baseUrl', value: baseUrl }]);
459
+ this.config.applyInternal([{ key: 'upstream.baseUrl', value: baseUrl }]);
456
460
  }
457
461
  readCredentialFile(id) {
458
462
  try {
@@ -532,11 +536,11 @@ let ProvidersService = ProvidersService_1 = class ProvidersService {
532
536
  * 这两家的凭证是客户端登录态里的东西(MiniMax = `mmort_` refresh token;
533
537
  * Qoder = 一整段身份 JSON),用户手上没有、也手敲不出来 —— 「配置」抽屉为此在
534
538
  * v1.6.4 已改为导入指引。但**新增**弹窗当时没跟着改,仍强制必填,
535
- * 于是这两家在界面上**根本建不出来**:导入脚本 `qoder-import-credential.mjs <id>`
539
+ * 于是这两家在界面上**根本建不出来**:导入又要 provider id
536
540
  * 又要 provider id,而 id 建完才有 —— 死锁。
537
541
  *
538
542
  * 放行留空后:行先建出来(`configured=false`),再用「自动导入」把凭证灌进去。
539
- * [v1.6.11] openai(WorkBuddy)也放行留空 —— **ck_ 已退役**,三家统一走
543
+ * [v1.6.13] openai(WorkBuddy)也放行留空 —— **ck_ 已退役**,三家统一走
540
544
  * 「导入客户端 → 官方授权」;凭证分别落 .oauth(WorkBuddy)/ .key(另两家)。
541
545
  */
542
546
  parseCredential(raw, protocol = 'openai') {
@@ -217,6 +217,31 @@ let RelayConfigService = class RelayConfigService {
217
217
  (0, node_fs_1.writeFileSync)(this.settingsPath(), JSON.stringify({ version: this.version, settings: this.settings }, null, 2), { mode: 0o600 });
218
218
  return { applied: entries.length, requiresRestart, version: this.version };
219
219
  }
220
+ /**
221
+ * 内部系统写入:**不走 `EDITABLE_KEYS` 闸**(那是用户设置 PUT 的防线)。
222
+ *
223
+ * 用途:提供商激活 / 改址时同步 `upstream.baseUrl` —— 该键 v0.6.5 起对用户只读
224
+ * (出站都读 `provider.base_url`),但仍是「空库种子」与「无 active 提供商时的
225
+ * 额度回落」的数据源,系统侧需要保持它指向当前活提供商。
226
+ *
227
+ * ⚠️ [2026-10-17 事故] 此前激活路径误走 `applyUpdates` —— 撞只读闸抛
228
+ * 「upstream.baseUrl 不是可修改的设置项」,而 `repo.activate()` 已先落库:
229
+ * 用户看到报错、刷新后却已启用(启用按钮自 v0.6.5 起一直带着这个静默故障,
230
+ * 首次 UI 触发激活是本次删除重导才暴露)。
231
+ */
232
+ applyInternal(entries) {
233
+ for (const entry of entries) {
234
+ const check = (0, settings_validate_1.validateSetting)(entry.key, entry.value);
235
+ if (!check.ok)
236
+ throw new errors_1.RelayHttpError({
237
+ httpStatus: 400, code: 'invalid_request_body', apiCode: errors_1.ApiCode.BAD_PARAM,
238
+ message: check.message ?? `${entry.key} 校验失败`,
239
+ });
240
+ this.assign(entry.key, entry.value);
241
+ }
242
+ this.version += 1;
243
+ (0, node_fs_1.writeFileSync)(this.settingsPath(), JSON.stringify({ version: this.version, settings: this.settings }, null, 2), { mode: 0o600 });
244
+ }
220
245
  assign(key, value) {
221
246
  const [group, field] = key.split('.');
222
247
  const target = this.settings;
@@ -71,19 +71,24 @@ function aggregateAccounts(accounts, nowMs) {
71
71
  continue;
72
72
  const acc = item;
73
73
  const expireMs = parseCnTime(acc['ExpiredTime']);
74
- if (expireMs !== null && expireMs <= nowMs)
75
- continue; // 已过期:作废不计入
74
+ /* [2026-10-17 口径修正] 过期包**不再整包跳过**,只按字段分流:
75
+ · remain(剩余)只累计**未过期**包 —— 过期包的余额确实不可用;
76
+ · total(累计发放)/ used(已消耗)**含过期包** —— 发放和消耗是历史事实,
77
+ 包过期不该让它们从账上消失(实测:WB 的 6300 已消耗藏在 7 个过期包里,
78
+ 整包跳过导致「已消耗 96.95」小得反常)。 */
79
+ const expired = expireMs !== null && expireMs <= nowMs;
76
80
  const r = pickAccountNumber(acc, 'CapacityRemainPrecise', 'CapacityRemain');
77
81
  const s = pickAccountNumber(acc, 'CapacitySizePrecise', 'CapacitySize');
78
82
  if (r === null && s === null)
79
83
  continue; // 两数都没有:跳过该包,不猜
80
- remain += r ?? 0;
84
+ if (!expired)
85
+ remain += r ?? 0;
81
86
  total += s ?? 0;
82
87
  const used = pickAccountNumber(acc, 'CapacityUsedPrecise', 'CapacityUsed');
83
88
  usedSum = usedSum === null ? (used !== null ? used : null) : used !== null ? usedSum + used : usedSum || null;
84
89
  packages.push({
85
90
  name: typeof acc['PackageName'] === 'string' ? acc['PackageName'] : '',
86
- remain: r ?? 0,
91
+ remain: expired ? 0 : r ?? 0,
87
92
  size: s ?? 0,
88
93
  used,
89
94
  expireAt: expireMs,
@@ -12,4 +12,4 @@ exports.VERSION = void 0;
12
12
  * `version` 一致,不一致直接 fail build —— 两处版本永远不会悄悄分叉。
13
13
  * 升版本 = 同时改 package.json 与这里(构建会逼你对齐)。
14
14
  */
15
- exports.VERSION = '1.6.15';
15
+ exports.VERSION = '1.6.17';
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "waygate-server",
3
- "version": "1.6.15",
3
+ "version": "1.6.17",
4
4
  "private": true,
5
5
  "description": "驿站 · Relay — OpenAI 兼容反向代理与控制台(后端)",
6
6
  "engines": {
@@ -1,4 +1,4 @@
1
- import{D as e,J as t,K as n,S as r,_ as i,a,g as o,h as s,it as c,n as l,t as u,tt as d,z as f}from"./vendor-befEYGHM.js";import{A as p,D as m,M as h,a as g,c as _,g as v,j as y,m as b,r as x,v as S,y as C}from"./index-BJtz6Ses.js";import{a as w,c as T,l as E,n as D,o as O,r as k,s as A,t as j}from"./Select-HrLgXSri.js";import{t as M}from"./Modal-B-rIrvXS.js";import{n as N}from"./PageSkeleton-DG-Dj4_9.js";var P=c(),F=d(),I={live:`live`,off:`off`,exp:`exp`};function L({rows:e,clientByKey:t,pendingId:n,onToggle:r,onEdit:i,onDelete:c}){return(0,F.jsx)(`div`,{className:`tbl-scroll`,children:(0,F.jsxs)(`table`,{className:`tbl`,children:[(0,F.jsxs)(`caption`,{className:`sr-only`,children:[`API Key 列表,共 `,e.length,` 把密钥`]}),(0,F.jsx)(`thead`,{children:(0,F.jsxs)(`tr`,{children:[(0,F.jsx)(`th`,{scope:`col`,children:`名称 / 密钥`}),(0,F.jsx)(`th`,{scope:`col`,children:`绑定提供商`}),(0,F.jsx)(`th`,{scope:`col`,children:`权限范围`}),(0,F.jsx)(`th`,{scope:`col`,className:`r`,children:`近24h请求`}),(0,F.jsx)(`th`,{scope:`col`,className:`r`,children:`最后使用`}),(0,F.jsx)(`th`,{scope:`col`,children:`状态 / 到期`}),(0,F.jsx)(`th`,{scope:`col`,className:`r`,style:{width:184},children:`操作`})]})}),(0,F.jsx)(`tbody`,{children:e.map(e=>{let u=p(e.enabled,e.expiresAt),d=n===e.id,f=t?.get(e.id),h=e.clients??[];return(0,F.jsxs)(`tr`,{children:[(0,F.jsxs)(`td`,{children:[(0,F.jsx)(`div`,{className:`td-strong`,children:e.name}),(0,F.jsx)(`div`,{className:`td-sub td-mono`,children:e.hint})]}),(0,F.jsx)(`td`,{children:h.length>0?(0,F.jsx)(g,{content:`绑定提供商(路由优先级从高到低):\n${h.join(`
1
+ import{D as e,J as t,K as n,S as r,_ as i,a,g as o,h as s,it as c,n as l,t as u,tt as d,z as f}from"./vendor-befEYGHM.js";import{A as p,D as m,M as h,a as g,c as _,g as v,j as y,m as b,r as x,v as S,y as C}from"./index-Bb8UvXuA.js";import{a as w,c as T,l as E,n as D,o as O,r as k,s as A,t as j}from"./Select-CE8etafp.js";import{t as M}from"./Modal-B-rIrvXS.js";import{n as N}from"./PageSkeleton-DG-Dj4_9.js";var P=c(),F=d(),I={live:`live`,off:`off`,exp:`exp`};function L({rows:e,clientByKey:t,pendingId:n,onToggle:r,onEdit:i,onDelete:c}){return(0,F.jsx)(`div`,{className:`tbl-scroll`,children:(0,F.jsxs)(`table`,{className:`tbl`,children:[(0,F.jsxs)(`caption`,{className:`sr-only`,children:[`API Key 列表,共 `,e.length,` 把密钥`]}),(0,F.jsx)(`thead`,{children:(0,F.jsxs)(`tr`,{children:[(0,F.jsx)(`th`,{scope:`col`,children:`名称 / 密钥`}),(0,F.jsx)(`th`,{scope:`col`,children:`绑定提供商`}),(0,F.jsx)(`th`,{scope:`col`,children:`权限范围`}),(0,F.jsx)(`th`,{scope:`col`,className:`r`,children:`近24h请求`}),(0,F.jsx)(`th`,{scope:`col`,className:`r`,children:`最后使用`}),(0,F.jsx)(`th`,{scope:`col`,children:`状态 / 到期`}),(0,F.jsx)(`th`,{scope:`col`,className:`r`,style:{width:184},children:`操作`})]})}),(0,F.jsx)(`tbody`,{children:e.map(e=>{let u=p(e.enabled,e.expiresAt),d=n===e.id,f=t?.get(e.id),h=e.clients??[];return(0,F.jsxs)(`tr`,{children:[(0,F.jsxs)(`td`,{children:[(0,F.jsx)(`div`,{className:`td-strong`,children:e.name}),(0,F.jsx)(`div`,{className:`td-sub td-mono`,children:e.hint})]}),(0,F.jsx)(`td`,{children:h.length>0?(0,F.jsx)(g,{content:`绑定提供商(路由优先级从高到低):\n${h.join(`
2
2
  `)}`,children:(0,F.jsx)(`span`,{className:`key-bindings`,children:(()=>{let e=h[0],t=f?.name===e?f:void 0;return(0,F.jsxs)(F.Fragment,{children:[t?(0,F.jsx)(`a`,{href:`#/clients?clientId=${encodeURIComponent(t.clientId)}`,children:e}):e,h.length>1?(0,F.jsx)(`i`,{className:`kb-rank`,children:`优先`}):null]})})()})}):f?(0,F.jsx)(`a`,{href:`#/clients?clientId=${encodeURIComponent(f.clientId)}`,children:f.name}):(0,F.jsx)(`span`,{className:`td-sub`,children:`未绑定`})}),(0,F.jsx)(`td`,{children:C(e.scope)}),(0,F.jsx)(`td`,{className:`r td-mono`,children:v(e.requests24h)}),(0,F.jsx)(`td`,{className:`r td-mono`,children:S(e.lastSeenAt)}),(0,F.jsxs)(`td`,{children:[(0,F.jsxs)(`span`,{style:{display:`inline-flex`,alignItems:`center`,gap:8,whiteSpace:`nowrap`},children:[(0,F.jsx)(E,{tone:I[u],children:m[u]}),(0,F.jsx)(`span`,{className:`td-sub td-mono`,children:e.expiresAt?`至 ${b(e.expiresAt)}`:`永不过期`})]}),e.maxIps!==null||e.tokenQuota!==null?(0,F.jsxs)(`div`,{className:`td-sub td-mono`,style:{marginTop:2},children:[e.maxIps===null?null:`IP ${e.usedIps??0}/${e.maxIps}`,e.maxIps!==null&&e.tokenQuota!==null?` · `:null,e.tokenQuota===null?null:`${v(e.usedTokens??0)}/${v(e.tokenQuota)} tok`,e.ipWhitelist&&e.ipWhitelist.length>0?` · 白名单 ${e.ipWhitelist.length} 条`:null]}):null]}),(0,F.jsx)(`td`,{className:`r`,children:(0,F.jsxs)(`span`,{style:{display:`inline-flex`,gap:8,justifyContent:`flex-start`},children:[(0,F.jsx)(g,{content:`改名 / 改绑定 / 访问控制(不动权限范围,密钥与哈希不受影响)`,children:d?(0,F.jsx)(`span`,{tabIndex:0,children:(0,F.jsxs)(`button`,{type:`button`,className:`mini-pill`,disabled:d,onClick:()=>i(e),children:[(0,F.jsx)(l,{size:12}),`修改`]})}):(0,F.jsxs)(`button`,{type:`button`,className:`mini-pill`,onClick:()=>i(e),children:[(0,F.jsx)(l,{size:12}),`修改`]})}),(0,F.jsx)(g,{content:u===`exp`?`已过期,需重新创建`:e.enabled?`停用后立即失效`:`重新启用`,children:d||u===`exp`?(0,F.jsx)(`span`,{tabIndex:0,children:(0,F.jsxs)(`button`,{type:`button`,className:`mini-pill`,disabled:!0,onClick:()=>r(e),children:[e.enabled?(0,F.jsx)(o,{size:12}):(0,F.jsx)(s,{size:12}),e.enabled?`停用`:`启用`]})}):(0,F.jsxs)(`button`,{type:`button`,className:`mini-pill`,onClick:()=>r(e),children:[e.enabled?(0,F.jsx)(o,{size:12}):(0,F.jsx)(s,{size:12}),e.enabled?`停用`:`启用`]})}),(0,F.jsxs)(`button`,{type:`button`,className:`mini-pill danger`,disabled:d,onClick:()=>c(e),children:[(0,F.jsx)(a,{size:12}),`删除`]})]})})]},e.id)})})]})})}var R={ipText:``,maxIps:``,tokenQuota:``};function z(e){return{ipText:e.ipWhitelist.join(`
3
3
  `),maxIps:e.maxIps===null?``:String(e.maxIps),tokenQuota:e.tokenQuota===null?``:String(e.tokenQuota)}}function B(e){return e.ipText.trim()!==``||e.maxIps.trim()!==``||e.tokenQuota.trim()!==``}function V(e){let t=e.trim();if(!t)return null;let n=t.indexOf(`/`);if(n>0){let e=t.slice(0,n),r=t.slice(n+1);return!/^\d{1,2}$/.test(r)||Number(r)>32?`「${t}」的掩码位数只能是 0-32`:H(e)?null:`「${t}」CIDR 只支持 IPv4 网段`}return H(t)||t.includes(`:`)&&/^[0-9a-fA-F:]+$/.test(t)?null:`「${t}」不是合法的 IP 地址(支持 IPv4、IPv6、IPv4 CIDR)`}function H(e){let t=e.split(`.`);return t.length===4&&t.every(e=>/^\d{1,3}$/.test(e)&&Number(e)<=255)}function U(e){let t=e.ipText.split(/[\n,]+/).map(e=>e.trim()).filter(e=>e!==``);for(let e of t){let t=V(e);if(t)return{error:`IP 白名单:${t}`}}let n=null;if(e.maxIps.trim()!==``&&(n=Number(e.maxIps),!Number.isInteger(n)||n<1))return{error:`最大 IP 数必须是正整数(留空 = 不限)。`};let r=null;return e.tokenQuota.trim()!==``&&(r=Number(e.tokenQuota),!Number.isInteger(r)||r<1)?{error:`Token 配额必须是正整数(留空 = 不限)。`}:{ipWhitelist:t,maxIps:n,tokenQuota:r}}function W(e){let t=[],n=e.ipText.split(/[\n,]+/).map(e=>e.trim()).filter(e=>e!==``).length;return n>0&&t.push(`白名单 ${n} 条`),e.maxIps.trim()!==``&&t.push(`最大 ${e.maxIps.trim()} IP`),e.tokenQuota.trim()!==``&&t.push(`配额 ${Number(e.tokenQuota).toLocaleString()} tok`),t.length>0?t.join(` · `):`未限制`}function G({draft:e,onChange:t,defaultOpen:r=!1}){let[i,a]=(0,P.useState)(r||B(e));return(0,F.jsxs)(`div`,{className:`ov-field`,children:[(0,F.jsxs)(`button`,{type:`button`,className:`policy-toggle`,"aria-expanded":i,onClick:()=>a(e=>!e),children:[(0,F.jsx)(n,{size:14,"aria-hidden":!0}),(0,F.jsxs)(`span`,{children:[`访问控制`,i?``:`(可选,留空 = 不限)`]}),i?null:(0,F.jsx)(`span`,{className:`policy-sum`,children:W(e)})]}),i?(0,F.jsxs)(F.Fragment,{children:[(0,F.jsx)(`textarea`,{className:`field-input text`,style:{width:`100%`,minHeight:48,resize:`vertical`,fontFamily:`var(--font-mono)`,marginTop:8},value:e.ipText,placeholder:`IP 白名单,每行一个:
4
4
  203.0.113.7
@@ -0,0 +1 @@
1
+ import{tt as e}from"./vendor-befEYGHM.js";import{_ as t,d as n,g as r,p as i,x as a}from"./index-Bb8UvXuA.js";var o=e();function s({metrics:e}){return(0,o.jsxs)(`div`,{className:`credit-banner`,children:[(0,o.jsxs)(`div`,{className:`cb-left`,children:[(0,o.jsx)(`div`,{className:`cb-label`,children:`本窗口总 tokens`}),(0,o.jsx)(`div`,{children:(0,o.jsx)(`span`,{className:`cb-num`,children:a(e.totalTokens)})}),(0,o.jsx)(`div`,{className:`cb-why`,children:(0,o.jsxs)(`span`,{children:[`输入 `,a(e.promptTokens),` · 输出 `,a(e.completionTokens)]})})]}),(0,o.jsxs)(`div`,{className:`cb-mid`,children:[(0,o.jsx)(`div`,{className:`row`,children:(0,o.jsxs)(`span`,{className:`t`,children:[`经本服务转发已用 `,i(e.creditsUsed),` 积分`]})}),(0,o.jsxs)(`div`,{className:`cb-note`,children:[`仅统计经本服务转发的请求(取上游实扣 usage.credit;上游未回报的行积分未知,不计入合计`,e.creditsUnknownCount>0?`,本窗口 ${r(e.creditsUnknownCount)} 条`:``,`)。 官方客户端直连的消耗不计入 —— 账户总消耗与余额以官方客户端为准。`]})]}),(0,o.jsxs)(`div`,{className:`cb-right`,children:[(0,o.jsx)(`div`,{className:`cb-label`,children:`缓存命中率`}),(0,o.jsx)(`div`,{className:`cb-num`,style:{fontSize:22},children:n(e.cacheHitRate)}),(0,o.jsxs)(`div`,{className:`cb-note`,children:[a(e.cachedTokens),` / `,a(e.promptTokens),` 输入 tokens`]})]})]})}var c={credits:`消耗积分`,tokens:`Token 消耗`,requests:`请求次数`};function l(e,t){return e[t]??0}function u(e,t){return t===`credits`?i(e):t===`tokens`?a(e):r(e)}function d(e){return e===`credits`?`积分`:e===`tokens`?`tokens`:`次`}function f({rows:e,metric:n,windowLabel:r,total:i,totalCount:a}){let s=e.slice(0,6),f=Math.max(0,a-s.length),p=Math.max(0,i);return(0,o.jsxs)(`div`,{className:`client-metric-tip`,children:[(0,o.jsxs)(`div`,{className:`client-metric-tip-head`,children:[(0,o.jsxs)(`div`,{children:[(0,o.jsx)(`strong`,{children:`客户端拆分`}),(0,o.jsxs)(`span`,{children:[r,` · `,c[n]]})]}),(0,o.jsxs)(`span`,{className:`client-metric-tip-tag`,children:[`TOP `,s.length]})]}),(0,o.jsxs)(`div`,{className:`client-metric-tip-total`,children:[(0,o.jsx)(`span`,{children:`窗口合计`}),(0,o.jsxs)(`b`,{children:[u(p,n),` `,(0,o.jsx)(`em`,{children:d(n)})]})]}),(0,o.jsx)(`div`,{className:`client-metric-tip-list`,children:s.map((e,r)=>{let i=l(e,n),a=p>0?i/p:0;return(0,o.jsxs)(`div`,{className:`client-metric-tip-row`,children:[(0,o.jsxs)(`div`,{className:`client-metric-tip-row-top`,children:[(0,o.jsx)(`span`,{className:`client-metric-tip-rank`,children:String(r+1).padStart(2,`0`)}),(0,o.jsx)(`span`,{className:`client-metric-tip-name`,children:e.client??`未归属`}),(0,o.jsxs)(`b`,{children:[u(i,n),` `,(0,o.jsx)(`em`,{children:d(n)})]})]}),(0,o.jsxs)(`div`,{className:`client-metric-tip-row-bottom`,children:[(0,o.jsx)(`span`,{className:`client-metric-tip-track`,children:(0,o.jsx)(`i`,{style:{width:`${Math.max(0,Math.min(100,a*100))}%`}})}),(0,o.jsx)(`span`,{children:t(a)})]})]},e.clientId??e.client??r)})}),f>0?(0,o.jsxs)(`div`,{className:`client-metric-tip-note`,children:[`另有 `,f,` 个客户端未展开,完整明细见按客户端列表。`]}):null]})}export{s as n,f as t};
@@ -0,0 +1 @@
1
+ import{H as e,I as t,J as n,L as r,Q as i,S as a,U as o,a as s,it as c,m as l,n as u,tt as d,z as f}from"./vendor-befEYGHM.js";import{M as p,a as m,c as h,g,j as _,k as v,p as y,r as b,t as x,v as S}from"./index-Bb8UvXuA.js";import{a as C,c as w,n as T,r as E,s as D,t as O}from"./Select-CE8etafp.js";import{t as k}from"./Modal-B-rIrvXS.js";import{t as A}from"./PageSkeleton-DG-Dj4_9.js";import{n as j,t as M}from"./sharedWindow-FDfq75PI.js";import{t as N}from"./Drawer-CleQGhRU.js";import{t as P}from"./Switch-0KA1AbiV.js";var F=c(),I=d();function L({rows:e,byProvider:t,busyId:n,onEdit:r,onModels:a,onSignin:o,onGrowthRun:c,onOauthImport:d,onActivate:f,onDeactivate:p,onDelete:h}){let _=()=>new Intl.DateTimeFormat(`en-CA`,{timeZone:`Asia/Shanghai`,year:`numeric`,month:`2-digit`,day:`2-digit`}).format(new Date),v=new Map((t??[]).map(e=>[e.providerId??``,e]));return(0,I.jsx)(`div`,{className:`tbl-scroll`,children:(0,I.jsxs)(`table`,{className:`tbl`,children:[(0,I.jsxs)(`caption`,{className:`sr-only`,children:[`提供商列表,共 `,e.length,` 家提供商`]}),(0,I.jsx)(`thead`,{children:(0,I.jsxs)(`tr`,{children:[(0,I.jsx)(`th`,{scope:`col`,children:`提供商`}),(0,I.jsx)(`th`,{scope:`col`,children:`密钥`}),(0,I.jsx)(`th`,{scope:`col`,className:`r`,children:`请求次数`}),(0,I.jsx)(`th`,{scope:`col`,className:`r`,children:`消耗积分`}),(0,I.jsx)(`th`,{scope:`col`,className:`r`,children:`最后活跃`}),(0,I.jsx)(`th`,{scope:`col`,className:`r`,children:`操作`})]})}),(0,I.jsx)(`tbody`,{children:e.map(t=>{let b=n===t.id;return(0,I.jsxs)(`tr`,{className:`prov-row`,children:[(0,I.jsxs)(`td`,{className:`td-strong`,children:[t.name,t.active?(0,I.jsx)(w,{tone:`live`,children:`已启用`}):(0,I.jsx)(w,{tone:`ghost`,children:`未启用`}),t.protocol===`anthropic`?(0,I.jsx)(m,{content:`Anthropic Messages 方言(mavis 网关):模型目录用内置清单,凭证为 refresh token 自动换新`,children:(0,I.jsx)(`span`,{className:`pill pill-ghost`,children:`Anthropic`})}):t.protocol===`qoder`?(0,I.jsx)(m,{content:`Qoder CN 网关(COSY 协议):免费档 billable:false 实扣 0;目录从网关 /model/list 拉取;凭证为客户端身份 JSON(不自动刷新)`,children:(0,I.jsx)(`span`,{className:`pill pill-ghost`,children:`Qoder`})}):null,(0,I.jsx)(`div`,{className:`td-sub td-mono`,children:(0,I.jsx)(m,{content:(0,I.jsx)(I.Fragment,{children:(0,I.jsxs)(`div`,{className:`client-model-tip`,children:[(0,I.jsxs)(`div`,{className:`client-model-tip-head`,children:[(0,I.jsxs)(`div`,{className:`client-model-tip-title`,children:[(0,I.jsx)(`strong`,{children:t.name}),(0,I.jsx)(`span`,{children:t.baseUrl})]}),(0,I.jsx)(`span`,{className:`client-model-tip-tag`,children:t.active?`已启用`:`未启用`})]}),(0,I.jsxs)(`div`,{className:`client-model-tip-summary`,children:[(0,I.jsxs)(`div`,{children:[(0,I.jsx)(`span`,{children:`模型总数`}),(0,I.jsx)(`b`,{children:g(t.modelCount??0)})]}),(0,I.jsxs)(`div`,{children:[(0,I.jsx)(`span`,{children:`已启用`}),(0,I.jsx)(`b`,{children:g(t.modelEnabled??0)})]})]}),(()=>{let e=t.credits?.available?{remaining:t.credits.remaining,consumed:t.credits.consumed}:t.wbCredits?{remaining:t.wbCredits.remaining,consumed:t.wbCredits.consumed}:t.qoderCredits?{remaining:t.qoderCredits.remaining,consumed:t.qoderCredits.consumed}:null;return e&&(e.remaining!==null||e.consumed!==null)?(0,I.jsxs)(`div`,{className:`client-metric-tip-total`,children:[(0,I.jsx)(`span`,{children:`剩余积分`}),(0,I.jsxs)(`b`,{children:[y(e.remaining??0),` `,(0,I.jsxs)(`em`,{children:[`已用 `,y(e.consumed??0)]})]})]}):null})(),t.credits?.stale||t.qoderCredits?.stale?(0,I.jsx)(`div`,{className:`client-model-tip-note`,children:`⚠ 积分数据偏旧(账本快照超时)`}):null]})}),children:(0,I.jsxs)(`span`,{className:`prov-model-count`,children:[g(t.modelCount??0),` 个模型`]})})})]}),(0,I.jsxs)(`td`,{className:`td-cred`,children:[(0,I.jsx)(m,{content:`供应商访问密钥(掩码显示;全量只存在服务器上)`,children:(0,I.jsx)(`span`,{className:`cred-mask`,children:t.credentialMask})}),(0,I.jsx)(`span`,{className:`up-badge${t.configured?``:` warn`}`,children:t.configured?`有效`:`未配置`})]}),(0,I.jsx)(`td`,{className:`r td-mono`,children:t.active?g(v.get(t.id)?.requests??0):`—`}),(0,I.jsx)(`td`,{className:`r td-mono`,children:t.active?y(v.get(t.id)?.credits??0):`—`}),(0,I.jsx)(`td`,{className:`r td-mono`,children:t.active?S(v.get(t.id)?.lastSeenAt??null):`—`}),(0,I.jsx)(`td`,{className:`r`,children:(0,I.jsxs)(`span`,{className:`prov-row-actions`,children:[(0,I.jsx)(m,{content:`改名称与上游地址;凭证可在抽屉内「重新导入授权」`,children:(0,I.jsxs)(`button`,{type:`button`,className:`mini-pill`,disabled:b,onClick:()=>r(t),children:[(0,I.jsx)(u,{size:12}),`配置`]})}),(0,I.jsx)(m,{content:`查看这家自己的模型清单(启停只作用于该提供商分区)`,children:(0,I.jsxs)(`button`,{type:`button`,className:`mini-pill`,"aria-disabled":b||!t.active,onClick:()=>{!b&&t.active&&a(t)},children:[(0,I.jsx)(l,{size:12}),`模型列表`]})}),t.credKind===`oauth_minimax`||t.protocol===`qoder`?(0,I.jsx)(m,{content:(()=>{let e=t.protocol===`qoder`?`10:05`:`08:05`,n=t.signin?.checkedOn?`最近签到 ${t.signin.checkedOn}${t.signin.lastPoints?` · +${t.signin.lastPoints} 积分`:``}(relay 每天 ${e} 自动签)`:`今天还没签到 —— 点一下立即签(relay 每天 ${e} 也会自动签)`;return t.protocol===`qoder`?(0,I.jsxs)(I.Fragment,{children:[n,(0,I.jsx)(`br`,{}),`Qoder 官方标注:每日 10:00(UTC+8)刷新,领取后 30 天有效。`]}):n})(),children:(0,I.jsxs)(`button`,{type:`button`,className:`mini-pill`,"aria-disabled":b||t.signin?.checkedOn===_(),onClick:()=>{b||t.signin?.checkedOn===_()||o(t)},children:[(0,I.jsx)(i,{size:12}),t.signin?.checkedOn===_()?`已签到`:`签到`]})}):null,t.protocol===`openai`?t.wbOAuth?.configured?(0,I.jsx)(m,{content:t.wbSignin?.checkedOn===_()?`今天已签到过(明天 08:05 自动跑;任务面板在设置 → 提供商健康)`:`立即签到 + 顺带跑一轮成长任务与连登兑换(每天 08:05 也会自动跑)。转发不受影响。`,children:(0,I.jsxs)(`button`,{type:`button`,className:`mini-pill`,"aria-disabled":b||t.wbSignin?.checkedOn===_(),onClick:()=>{!b&&t.wbSignin?.checkedOn!==_()&&c(t)},children:[(0,I.jsx)(i,{size:12}),t.wbSignin?.checkedOn===_()?`已签到`:`签到`]})}):(0,I.jsx)(m,{content:`导入桌面端 OAuth 凭证后可代领签到/任务(上游不允许 API 密钥代领)。粘贴 tools/wb-oauth-export.mjs 的输出;转发仍走 API 密钥。`,children:(0,I.jsxs)(`button`,{type:`button`,className:`mini-pill`,"aria-disabled":b,onClick:()=>{b||d(t)},children:[(0,I.jsx)(i,{size:12}),`导入OAuth`]})}):null,t.active?(0,I.jsx)(m,{content:`退出出站路由(目录与凭证保留,可再启用)`,children:(0,I.jsx)(`button`,{type:`button`,className:`mini-pill`,"aria-disabled":b,onClick:()=>{b||p(t)},children:`停用`})}):(0,I.jsx)(`button`,{type:`button`,className:`mini-pill`,disabled:b,onClick:()=>f(t),children:`启用`}),(0,I.jsx)(m,{content:t.active?`不能删除已启用的提供商 —— 先停用它`:e.length<=1?`至少保留一个提供商`:`删除这个提供商(凭证文件一并删除)`,children:(0,I.jsxs)(`button`,{type:`button`,className:`mini-pill danger`,"aria-disabled":b||t.active||e.length<=1,onClick:()=>{!b&&!t.active&&e.length>1&&h(t)},children:[(0,I.jsx)(s,{size:12}),`删除`]})})]})})]},t.id)})})]})})}function R({command:e}){let[t,r]=(0,F.useState)(!1);if(!e)return null;let i=async()=>{try{await navigator.clipboard.writeText(e),r(!0),setTimeout(()=>r(!1),2e3)}catch{r(!1)}};return(0,I.jsxs)(`div`,{className:`cred-guide-cmd`,children:[(0,I.jsx)(`code`,{children:e}),(0,I.jsxs)(`button`,{type:`button`,className:`copy`,"aria-label":t?`已复制命令`:`复制命令`,onClick:()=>void i(),children:[t?(0,I.jsx)(n,{size:13}):(0,I.jsx)(f,{size:13}),t?`已复制`:`复制`]})]})}var z=[{protocol:`openai`,label:`WorkBuddy`,desc:`OpenAI 方言`,needsAuth:!0},{protocol:`anthropic`,label:`MiniMax Code`,desc:`Anthropic 方言`,needsAuth:!0},{protocol:`qoder`,label:`Qoder CN`,desc:`COSY 协议`,needsAuth:!0}],B={openai:`https://copilot.tencent.com`,anthropic:`https://agent.minimax.cn`,qoder:`https://gateway.qoder.com.cn`},V=2500,H=3e5;function U({provider:e,onClose:n,onImported:r}){let i=x(),[o,s]=(0,F.useState)(e?`confirm`:`pick`),[c,l]=(0,F.useState)(e?.protocol??`anthropic`),[u,d]=(0,F.useState)(e?.name??``),[f,m]=(0,F.useState)(``),[g,_]=(0,F.useState)(``),[v,y]=(0,F.useState)(!1),[S,C]=(0,F.useState)(null),w=(0,F.useRef)(e?.id??null),T=(0,F.useRef)(!1),D=(0,F.useRef)(!1),O=z.find(e=>e.protocol===c);(0,F.useEffect)(()=>()=>{D.current=!0;let e=w.current;e&&h.providerImportDeviceCancel(e).catch(()=>void 0)},[]);let A=(0,F.useCallback)(async()=>{y(!0),C(null);try{let e=w.current;e||(e=(await h.createProvider({name:u.trim()||O.label,baseUrl:B[c]??B.openai,credential:``,protocol:c})).item.id,w.current=e,T.current=!0);let t=await h.providerImportDeviceStart(e);if(!t.ok||!t.authUrl){C(new p(t.message??`申请授权失败`,0,0)),s(`confirm`),y(!1);return}m(t.authUrl),_(t.userCode??``),window.open(t.authUrl,`_blank`,`noopener`),s(`waiting`),y(!1);let n=Date.now()+H;for(;!D.current&&Date.now()<n;){if(await new Promise(e=>setTimeout(e,V)),D.current)return;let t=await h.providerImportDevicePoll(e);if(D.current)return;if(t.status===`done`){s(`done`),i.push({tone:`success`,message:`${u.trim()||O.label} 授权已导入。`}),r();return}if(t.status===`error`){C(new p(t.message??`授权失败`,0,0)),s(`confirm`);return}}D.current||(C(new p(`授权超时(5 分钟)—— 请重新发起导入。`,0,0)),s(`confirm`))}catch(e){C(e instanceof p?e:new p(String(e),0,0)),s(`confirm`),y(!1)}},[O.label,u,r,c,i]);if(o===`done`)return(0,I.jsxs)(k,{title:`授权已导入`,onClose:n,footer:(0,I.jsx)(`button`,{type:`button`,className:`btn btn-primary btn-sm`,onClick:n,children:`完成`}),children:[(0,I.jsx)(b,{tone:`success`,children:(0,I.jsxs)(`span`,{children:[u.trim()||O.label,` 的授权已通过官方通道导入。`]})}),e?null:(0,I.jsxs)(`p`,{className:`ov-sub`,style:{marginTop:12},children:[`回到列表点`,(0,I.jsx)(`b`,{children:`试通`}),`确认可用,再按需启用(启用后它才参与出站路由)。`]})]});if(o===`waiting`)return(0,I.jsx)(k,{title:`等待你完成授权`,onClose:n,children:(0,I.jsxs)(`div`,{className:`cred-guide`,children:[(0,I.jsxs)(`div`,{className:`cred-guide-note`,children:[`已为你打开 `,(0,I.jsx)(`b`,{children:O.label}),` 的授权页 —— `,(0,I.jsx)(`b`,{children:`请在浏览器里完成授权`}),`, 本窗口会自动检测结果,无需回到终端。`]}),g?(0,I.jsxs)(`div`,{className:`cred-guide-note`,children:[`若页面要求输入短码:`,(0,I.jsx)(`span`,{className:`code-chip`,children:g})]}):null,(0,I.jsx)(R,{command:f}),(0,I.jsxs)(`div`,{className:`cred-guide-note`,style:{display:`flex`,alignItems:`center`,gap:8},children:[(0,I.jsx)(a,{size:14}),`正在等待授权完成…(最多 5 分钟;没弹出浏览器就点上面的链接)`]}),S?(0,I.jsx)(b,{tone:`error`,children:E(S)}):null]})});let j=o===`confirm`;return(0,I.jsx)(k,{title:e?`重新导入授权 · ${e.name}`:`导入客户端`,onClose:n,footer:(0,I.jsxs)(I.Fragment,{children:[(0,I.jsx)(`button`,{type:`button`,className:`btn btn-quiet btn-sm`,onClick:n,children:`取消`}),(0,I.jsxs)(`button`,{type:`button`,className:`btn btn-primary btn-sm`,disabled:v,onClick:()=>{if(!j){s(`confirm`);return}A()},children:[v?(0,I.jsx)(a,{size:14}):(0,I.jsx)(t,{size:14}),v?`正在申请授权…`:j?`确定,前往授权页`:`导入`]})]}),children:j?(0,I.jsxs)(I.Fragment,{children:[(0,I.jsx)(b,{tone:`warn`,children:(0,I.jsxs)(`span`,{children:[`即将跳转至 `,(0,I.jsx)(`b`,{children:O.label}),` 的授权页面(`,(0,I.jsx)(`b`,{children:`新标签页`}),`)。 请在页面里登录并完成授权,授权完成后本窗口会自动检测。`]})}),(0,I.jsxs)(`div`,{className:`cred-guide-note`,style:{marginTop:12},children:[`授权只用于取本工具的凭证;令牌保存在`,(0,I.jsx)(`b`,{children:`本机服务端`}),`(0600),不会回传到浏览器。`]}),c===`anthropic`?(0,I.jsxs)(`div`,{className:`cred-guide-note`,style:{marginTop:8},children:[`⚠️ MiniMax 的凭证是`,(0,I.jsx)(`b`,{children:`同账号单会话`}),`:请用`,(0,I.jsx)(`b`,{children:`专用账号`}),`, 否则会与日常客户端互相顶下线。`]}):null,S?(0,I.jsx)(b,{tone:`error`,children:E(S)}):null]}):(0,I.jsxs)(I.Fragment,{children:[(0,I.jsxs)(`p`,{className:`ov-sub`,children:[`选一个客户端,用它的`,(0,I.jsx)(`b`,{children:`官方授权`}),`拿到凭证(凭证在客户端登录态里,用户拿不到、无需手填)。`]}),(0,I.jsxs)(`div`,{className:`ov-field`,children:[(0,I.jsx)(`label`,{children:`客户端类型`}),(0,I.jsx)(`div`,{style:{display:`flex`,flexDirection:`column`,gap:6},children:z.map(e=>(0,I.jsxs)(`label`,{style:{display:`flex`,alignItems:`center`,gap:8,cursor:`pointer`},children:[(0,I.jsx)(`input`,{type:`radio`,name:`import-client`,checked:c===e.protocol,onChange:()=>l(e.protocol)}),(0,I.jsxs)(`span`,{children:[(0,I.jsx)(`b`,{children:e.label}),(0,I.jsx)(`span`,{className:`ov-hint`,style:{marginLeft:8},children:e.desc})]})]},e.protocol))})]}),(0,I.jsxs)(`div`,{className:`ov-field`,children:[(0,I.jsx)(`label`,{htmlFor:`imp-name`,children:`名称(可留空)`}),(0,I.jsx)(`input`,{id:`imp-name`,className:`field-input text`,style:{width:`100%`},value:u,placeholder:O.label,onChange:e=>d(e.target.value)})]})]})})}var W={upstream:`上游`,seed:`种子`,manual:`手工`},G={upstream:`从上游模型目录拉取到的`,seed:`来自本地种子清单(上游拉取失败时的回落)`,manual:`人工添加的`};function K({catalog:t,catalogLoading:n,catalogMsg:r,catalogError:i,onCatalogRefresh:s,onCatalogReload:c,onModelToggled:u}){let[d,f]=(0,F.useState)(null),p=async e=>{f(e.id);try{await u(e.id,!e.enabled)}finally{f(null)}};return(0,I.jsxs)(`div`,{className:`up-col`,children:[(0,I.jsxs)(`div`,{className:`up-models-head`,children:[(0,I.jsxs)(`div`,{children:[(0,I.jsx)(`b`,{children:`可用模型`}),(0,I.jsx)(`span`,{children:t?`${t.models.length} 个 · ${t.models.filter(e=>e.enabled).length} 个已启用`:`尚未获取`})]}),(0,I.jsx)(`div`,{className:`up-models-actions`,children:n?(0,I.jsxs)(`button`,{type:`button`,className:`btn btn-quiet btn-sm`,disabled:!0,children:[(0,I.jsx)(a,{size:14,className:`spin`,style:{animation:`spin 1s linear infinite`}}),`正在实测模型…`]}):(0,I.jsxs)(`button`,{type:`button`,className:`btn btn-quiet btn-sm`,onClick:s,children:[(0,I.jsx)(l,{size:14}),`获取模型`]})})]}),n?(0,I.jsxs)(`div`,{className:`up-progress`,role:`status`,children:[(0,I.jsx)(a,{size:14,className:`spin`,style:{animation:`spin 1s linear infinite`},"aria-hidden":!0}),(0,I.jsxs)(`span`,{children:[`正在逐个实测模型(每个 1 token)…这一步会真打上游,`,(0,I.jsx)(`b`,{children:`可能要几十秒`}),`,请不要关掉页面; 完成后结果会出现在这里,模型清单也会自动刷新。`]})]}):null,r?(0,I.jsx)(b,{tone:r.kind===`error`?`error`:r.kind===`partial`?`warn`:`success`,children:r.text}):null,i?(0,I.jsx)(T,{kind:`error`,title:`模型清单没拉到`,detail:E(i),onRetry:c}):n&&!t?(0,I.jsx)(T,{kind:`loading`,title:`正在向上游获取模型…`}):!t||t.models.length===0?(0,I.jsx)(T,{kind:`empty`,title:`还没有模型清单`,detail:`启用提供商后会自动同步一次它自己的模型清单;这里也可以手动点「获取模型」逐项实测是否真的能调通。`}):(0,I.jsx)(`div`,{className:`up-models`,children:t.models.map(t=>(0,I.jsxs)(`div`,{className:`up-model${t.enabled?``:` off`}`,children:[(0,I.jsxs)(`div`,{className:`um-info`,children:[(0,I.jsxs)(`b`,{children:[(0,I.jsx)(`span`,{className:`um-k`,children:`模型名:`}),t.displayName,(0,I.jsx)(m,{content:G[t.source],children:(0,I.jsx)(`span`,{className:`um-src src-${t.source}`,children:W[t.source]})}),t.tag?(0,I.jsx)(m,{content:`上游计价标签,随目录同步更新`,children:(0,I.jsx)(`span`,{className:`mu-tag`,children:t.tag})}):null]}),(0,I.jsxs)(`i`,{className:`code-chip`,children:[(0,I.jsx)(`span`,{className:`um-k`,children:`模型ID:`}),t.id]})]}),(0,I.jsxs)(`div`,{className:`um-meta`,children:[t.verified?(0,I.jsx)(m,{content:`实测可调通`,children:(0,I.jsxs)(`span`,{className:`um-ok`,children:[(0,I.jsx)(o,{size:12}),`可用`]})}):(0,I.jsx)(m,{content:`实测未通过(可能未订阅)`,children:(0,I.jsxs)(`span`,{className:`um-bad`,children:[(0,I.jsx)(e,{size:12}),`未通过`]})}),t.multiplier==null?(0,I.jsx)(m,{content:`接口与本地快照都没有该模型的倍率 —— 不编造`,children:(0,I.jsx)(`span`,{className:`um-mult unknown`,children:`倍率未知`})}):(0,I.jsx)(m,{content:t.source===`upstream`?`倍率取自上游 /v3/config 接口实时值,随目录刷新与每日同步更新。`:`接口未覆盖该模型,倍率为内置快照,可能随官方调价过期。`,children:(0,I.jsxs)(`span`,{className:`um-mult`,children:[`×`,t.multiplier]})}),(0,I.jsx)(P,{checked:t.enabled,disabled:d===t.id,label:`${t.enabled?`停用`:`启用`} ${t.displayName}`,onChange:()=>void p(t)})]})]},t.id))}),(0,I.jsxs)(`div`,{className:`up-foot`,children:[`实测会真打上游、消耗少量积分(每模型 1 token)。上游不接受的模型会标「未通过」, 但`,(0,I.jsx)(`b`,{children:`不删除`}),` —— 订阅以后可能又开,随时可以再拉一次。 倍率以 /v3/config 接口实时值为准(目录刷新与每日自动同步都会更新), 接口未覆盖的模型回落内置快照。`,(0,I.jsx)(`br`,{}),`启用提供商时会同步这家自己的清单;已有提供商的目录互不覆盖,启用/停用选择也分别保留。`]})]})}function q({window:e}){let t=_(()=>h.providers(),[],{cacheKey:`providers`}),[n,i]=(0,F.useState)(null),o=_(()=>h.catalog(n?.id),[n?.id],{cacheKey:n?.id?`catalog:${n.id}`:`catalog`}),s=_(()=>h.clients(e),[e],{cacheKey:`clients:${e}`}),[c,l]=(0,F.useState)(!1),[u,d]=(0,F.useState)(null),[f,g]=(0,F.useState)(null),[v,y]=(0,F.useState)(null),[S,D]=(0,F.useState)(null),[O,j]=(0,F.useState)(null),[M,P]=(0,F.useState)(null),[R,z]=(0,F.useState)(!1),B=x(),V=t.data?.items??[],H=s.data?.byProvider??[],W=()=>{t.reload(),o.reload(),s.reload()},G=async e=>{g(e.id),j(null);try{await h.activateProvider(e.id),P(null),W()}catch(e){j(e instanceof p?e:new p(String(e),0,0))}finally{g(null)}},q=async e=>{g(e.id),j(null);try{await h.deactivateProvider(e.id),W()}catch(e){j(e instanceof p?e:new p(String(e),0,0))}finally{g(null)}},Y=async e=>{g(e.id),j(null);try{let n=await h.providerSigninNow(e.id);P({kind:n.ok?`ok`:`error`,text:n.message}),n.ok?B.push(n.outcome===`none`?{tone:`info`,message:`${e.name}:${n.message}`}:{tone:`success`,message:`${e.name} 签到成功${n.points?` · +${n.points} 积分`:``}。`}):B.push({tone:`error`,message:`${e.name} 签到失败:${n.message}`}),t.reload()}catch(t){let n=t instanceof p?t:new p(String(t),0,0);j(n),B.push({tone:`error`,message:`${e.name} 签到失败:${n.message||`请求未完成`}`})}finally{g(null)}},X=async e=>{g(e.id),j(null);try{let n=await h.providerGrowthRun(e.id),r=n.tasks?.filter(e=>e.outcome===`claimed`).length??0,i=n.tasks?.filter(e=>e.outcome===`already`).length??0,a=n.tasks?.filter(e=>e.outcome===`skipped`).length??0,o=n.ok?`领取完成:签到 ${n.signin?.points??0} 积分;任务新领 ${r}、已领过 ${i}、待完成 ${a};本轮合计 +${n.creditsGained??0}。`:n.message??`领取失败。`;P({kind:n.ok?`ok`:`error`,text:o}),B.push(n.ok?{tone:`success`,message:`${e.name}:${o}`}:{tone:`error`,message:`${e.name} 领取失败:${n.message??`未知原因`}`}),t.reload()}catch(t){let n=t instanceof p?t:new p(String(t),0,0);j(n),B.push({tone:`error`,message:`${e.name} 领取失败:${n.message||`请求未完成`}`})}finally{g(null)}},Z=async e=>{let n=globalThis.prompt?.(`粘贴 WorkBuddy OAuth 凭证 JSON(tools/wb-oauth-export.mjs 的输出):`,``)??null;if(!n)return;let r;try{r=JSON.parse(n)}catch{P({kind:`error`,text:`不是合法 JSON —— 请完整复制导出文件内容。`});return}g(e.id),j(null);try{let n=await h.providerOauthImport(e.id,r);P({kind:n.ok?`ok`:`error`,text:n.message}),t.reload()}catch(e){j(e instanceof p?e:new p(String(e),0,0))}finally{g(null)}},Q=async()=>{if(!S||f)return;let e=S;g(e.id),j(null);try{await h.deleteProvider(e.id),D(null),W()}catch(e){j(e instanceof p?e:new p(String(e),0,0)),D(null)}finally{g(null)}},ee=async()=>{if(!R){z(!0),P(null),j(null);try{let e=await h.refreshCatalog(),r=e.configPulled===!1?` 倍率未从接口取到,沿用本地快照。`:e.multipliersUpdated?` 倍率已按接口同步(${e.multipliersUpdated} 项有更新)。`:``,i=e.perProvider?.find(e=>e.id===n?.id),a=i?.checked??e.checked,s=i?.ok??e.ok,c=i?.failed.length??e.failed.length;P(c>0?{kind:`partial`,text:`已实测 ${a} 个模型:${s} 个可用、${c} 个未通过(可能未订阅)。未通过的不会删除,订阅后可再拉一次。${r}`}:{kind:`ok`,text:`已实测 ${a} 个模型,全部可用。${r}`}),o.reload(),t.reload()}catch(e){let t=e instanceof p?e:new p(String(e),0,0);P({kind:`error`,text:`获取模型失败:${E(t)}`})}finally{z(!1)}}},te=async(e,t)=>{j(null);try{await h.setModelEnabled(e,t,n?.id),o.reload()}catch(e){j(e instanceof p?e:new p(String(e),0,0))}},$=V.filter(e=>e.active).length;return(0,I.jsxs)(I.Fragment,{children:[O?(0,I.jsx)(b,{tone:`error`,children:E(O)}):null,s.error?(0,I.jsxs)(b,{tone:`error`,actions:(0,I.jsx)(`button`,{type:`button`,className:`btn btn-quiet btn-sm`,onClick:s.reload,children:`重试`}),children:[`提供商用量没取到:`,E(s.error)]}):null,(0,I.jsxs)(C,{title:`提供商`,aside:(0,I.jsxs)(`span`,{className:`panel-aside-row`,children:[$>0?(0,I.jsxs)(w,{tone:`live`,dot:!0,children:[`已启用 `,$,` 家`]}):null,(0,I.jsx)(m,{content:`选一个客户端(WorkBuddy / MiniMax Code / Qoder CN),用它的官方授权导入凭证`,children:(0,I.jsxs)(`button`,{type:`button`,className:`btn btn-primary btn-sm`,onClick:()=>l(!0),children:[(0,I.jsx)(r,{size:14}),`导入客户端`]})})]}),padded:!1,children:[t.initial&&t.loading?(0,I.jsx)(`div`,{style:{padding:`0 20px 18px`},children:(0,I.jsx)(A,{})}):t.error?(0,I.jsx)(`div`,{style:{padding:`0 20px 18px`},children:(0,I.jsx)(T,{kind:`error`,title:`提供商列表没取到`,detail:E(t.error),onRetry:t.reload})}):V.length===0?(0,I.jsx)(`div`,{style:{padding:`0 20px 18px`},children:(0,I.jsx)(T,{kind:`empty`,title:`还没有任何提供商`,detail:`先登记一个上游,再按需启用它的出站路由。`})}):(0,I.jsx)(L,{rows:V,byProvider:H,busyId:f,onEdit:y,onSignin:e=>void Y(e),onGrowthRun:e=>void X(e),onOauthImport:e=>void Z(e),onModels:e=>{P(null),i(e)},onActivate:e=>void G(e),onDeactivate:e=>void q(e),onDelete:D}),(0,I.jsxs)(`div`,{className:`footer-note`,style:{padding:`0 20px 18px`},children:[`启用提供商会让它加入出站路由,并同步它自己的模型清单;已启用的提供商可以并行生效,模型按各自目录路由。 三家凭证都来自各客户端的`,(0,I.jsx)(`b`,{children:`官方授权`}),`(用户无需也无法手填)——点「重新导入授权」即可换账号或刷新令牌; 令牌只存在服务端,任何接口都读不回完整值。`,(0,I.jsx)(`br`,{}),`行内三列用量按`,(0,I.jsx)(`b`,{children:`提供商`}),`归属:由「模型 id → 生效目录的提供商」解析(请求记录本身没有 provider 维度)。模型不属于任何生效目录的行归到「未归属」,不冒充某一家。`]})]}),c?(0,I.jsx)(U,{onClose:()=>{l(!1),W()},onImported:()=>W()}):null,u?(0,I.jsx)(U,{provider:{id:u.id,name:u.name,protocol:u.protocol},onClose:()=>{d(null),W()},onImported:()=>W()}):null,v?(0,I.jsx)(J,{provider:v,onClose:()=>y(null),onSaved:()=>{y(null),W()},onReimport:()=>{let e=v;y(null),e&&d(e)}}):null,n?(0,I.jsx)(N,{title:`模型列表 · ${n.name}`,subtitle:n.baseUrl,onClose:()=>i(null),children:(0,I.jsx)(`div`,{className:`drawer-body`,children:(0,I.jsx)(K,{catalog:o.data,catalogLoading:R,catalogMsg:M,catalogError:o.error,onCatalogRefresh:()=>void ee(),onCatalogReload:o.reload,onModelToggled:te})})}):null,S?(0,I.jsx)(k,{title:`删除这个提供商?`,onClose:()=>{f||D(null)},footer:(0,I.jsxs)(I.Fragment,{children:[(0,I.jsx)(`button`,{type:`button`,className:`btn btn-primary btn-sm`,disabled:!!f,onClick:()=>D(null),children:`取消(保留)`}),(0,I.jsxs)(`button`,{type:`button`,className:`btn btn-danger btn-sm`,disabled:!!f,onClick:()=>void Q(),children:[f?(0,I.jsx)(a,{size:14,className:`spin`}):null,f?`删除中…`:`确认删除`]})]}),children:(0,I.jsxs)(`div`,{className:`ov-lose`,children:[(0,I.jsx)(`b`,{children:`会失去什么:`}),`提供商「`,S.name,`」(`,S.baseUrl,`)的登记与它的凭证文件一并删除, 需要重新登记并填一次 ck_ 密钥。此操作不可撤销。`]})}):null]})}function J({provider:e,onClose:t,onSaved:i,onReimport:o}){let[s,c]=(0,F.useState)(e.name),[l,u]=(0,F.useState)(e.baseUrl),[d,f]=(0,F.useState)(!1),[g,_]=(0,F.useState)(!1),[v,y]=(0,F.useState)(null),[x,S]=(0,F.useState)(null),[C,w]=(0,F.useState)(!1),[T,D]=(0,F.useState)(null),O=async()=>{if(!C){w(!0),D(null),y(null);try{let t=await h.providerImportLocal(e.id);if(t.ok)D({kind:`ok`,text:t.message+(t.detail?`\n${t.detail}`:``)}),i();else if(t.outcome===`unavailable`){let t=e.protocol===`qoder`?`qoder`:`minimax`;D({kind:`unavailable`,text:`本服务所在机器上读不到客户端登录态(远程部署的正常情形)。`,command:`node tools/import-credential-push.mjs ${location.origin} ${t} --provider ${e.id}`})}else D({kind:`error`,text:t.message+(t.detail?`\n${t.detail}`:``)})}catch(e){y(e instanceof p?e:new p(String(e),0,0))}finally{w(!1)}}},k=async t=>{if(t.preventDefault(),!d){f(!0),y(null);try{let t={};s.trim()!==e.name&&(t.name=s.trim()),l.trim()!==e.baseUrl&&(t.baseUrl=l.trim()),Object.keys(t).length>0&&await h.patchProvider(e.id,t),i()}catch(e){y(e instanceof p?e:new p(String(e),0,0))}finally{f(!1)}}},A=async()=>{if(!g){_(!0),y(null),S(null);try{S(await h.testProvider(e.id,void 0))}catch(e){y(e instanceof p?e:new p(String(e),0,0))}finally{_(!1)}}};return(0,I.jsx)(N,{title:`配置提供商 · ${e.name}`,subtitle:e.baseUrl,onClose:t,children:(0,I.jsxs)(`div`,{className:`drawer-body`,children:[v?(0,I.jsx)(b,{tone:`error`,children:E(v)}):null,(0,I.jsxs)(`form`,{onSubmit:e=>void k(e),className:`prov-edit`,style:{marginTop:6},children:[(0,I.jsxs)(`div`,{className:`up-row`,children:[(0,I.jsx)(`div`,{className:`up-lab`,children:`名称`}),(0,I.jsx)(`input`,{className:`field-input text`,style:{width:`100%`},value:s,onChange:e=>c(e.target.value),"aria-label":`提供商名称`})]}),(0,I.jsxs)(`div`,{className:`up-row`,children:[(0,I.jsx)(`div`,{className:`up-lab`,children:`地址`}),(0,I.jsx)(`input`,{className:`field-input text`,style:{width:`100%`},value:l,placeholder:`https://api.example.com`,onChange:e=>u(e.target.value),"aria-label":`提供商地址`})]}),(0,I.jsxs)(`div`,{className:`up-row`,children:[(0,I.jsx)(`div`,{className:`up-lab`,children:`凭证`}),(0,I.jsxs)(`div`,{className:`cred-guide`,children:[(0,I.jsx)(`div`,{className:`cred-guide-note`,children:e.protocol===`openai`?(0,I.jsxs)(I.Fragment,{children:[`凭证来自 WorkBuddy `,(0,I.jsx)(`b`,{children:`官方 OAuth 授权`}),`(桌面端 / 插件同一条通道),无需手填。`]}):e.protocol===`anthropic`?(0,I.jsxs)(I.Fragment,{children:[`凭证是客户端登录态里的 `,(0,I.jsx)(`span`,{className:`code-chip`,children:`mmort_…`}),` refresh token。 请用`,(0,I.jsx)(`b`,{children:`专用账号`}),`(与桌面端共用会互相顶下线)。`]}):(0,I.jsxs)(I.Fragment,{children:[`凭证来自 Qoder 客户端登录态(一段`,(0,I.jsx)(`b`,{children:`身份 JSON`}),`)。桌面客户端与它`,(0,I.jsx)(`b`,{children:`互不影响`}),`。`]})}),(0,I.jsxs)(`div`,{className:`cred-guide-actions`,children:[(0,I.jsxs)(`button`,{type:`button`,className:`btn btn-primary btn-sm`,disabled:d,onClick:()=>o(),children:[(0,I.jsx)(r,{size:14}),`重新导入授权`]}),(0,I.jsxs)(`button`,{type:`button`,className:`btn btn-quiet btn-sm`,disabled:C,onClick:()=>void O(),children:[C?(0,I.jsx)(a,{size:14}):(0,I.jsx)(r,{size:14}),`从本机客户端读取`]})]}),T?(0,I.jsx)(b,{tone:T.kind===`ok`?`success`:T.kind===`unavailable`?`warn`:`error`,children:(0,I.jsxs)(`span`,{children:[T.text,T.command?(0,I.jsxs)(I.Fragment,{children:[(0,I.jsx)(`br`,{}),`在`,(0,I.jsx)(`b`,{children:`装了客户端的那台机器`}),`的仓库目录下跑:`,(0,I.jsx)(R,{command:T.command})]}):null]})}):null,e.protocol===`qoder`?(0,I.jsxs)(`div`,{className:`cred-guide-note`,children:[`本服务`,(0,I.jsx)(`b`,{children:`不自动刷新`}),` Qoder 令牌;过期后重跑一次导入即可。`]}):null]})]}),(0,I.jsxs)(`div`,{className:`up-actions`,children:[(0,I.jsxs)(`button`,{type:`submit`,className:`btn btn-primary btn-sm`,disabled:d,children:[d?(0,I.jsx)(a,{size:14}):(0,I.jsx)(n,{size:14}),`保存`]}),(0,I.jsx)(m,{content:`用已导入的授权测这家上游是否还通`,children:(0,I.jsxs)(`button`,{type:`button`,className:`btn btn-quiet btn-sm`,disabled:g,onClick:()=>void A(),children:[g?(0,I.jsx)(a,{size:14}):null,`测试`]})})]})]}),x?(0,I.jsx)(b,{tone:x.ok?`success`:`warn`,children:(0,I.jsx)(`span`,{children:x.ok?(0,I.jsxs)(I.Fragment,{children:[`通了:`,e.name,` 接受了这次 1 token 探测(`,x.elapsedMs,`ms)。`]}):x.httpStatus===null?(0,I.jsxs)(I.Fragment,{children:[`没连上(网络层):`,x.reason??`未知原因`,`。先查地址与出网,再看凭证。`]}):(0,I.jsxs)(I.Fragment,{children:[`连上了但被拒(HTTP `,x.httpStatus,x.upstreamCode===null?``:` · 业务码 ${x.upstreamCode}`,`)。`,x.upstreamCode===11102?`业务码 11102 = 账号没订阅这个模型。key 可能是好的,换个模型再测或直接切换。`:`通常是凭证问题;换一把再测。`]})})}):null,(0,I.jsx)(`div`,{className:`up-note`,style:{marginTop:18},children:(0,I.jsxs)(`span`,{children:[`凭证只存在`,(0,I.jsx)(`b`,{children:`服务端`}),`:界面只显示账号摘要或掩码,任何接口都读不回完整值。`]})})]})})}function Y(){let[e,t]=(0,F.useState)(()=>M()??`today`);return(0,I.jsxs)(`div`,{className:`view on`,children:[(0,I.jsx)(D,{title:`提供商管理`,actions:(0,I.jsx)(O,{value:e,options:v,onChange:e=>{j(e),t(e)},width:148})}),(0,I.jsx)(`div`,{className:`view-body stack`,children:(0,I.jsx)(q,{window:e})})]})}export{Y as ClientsPage};
@@ -0,0 +1 @@
1
+ .landing,.landing *,.landing :before,.landing :after{box-sizing:border-box}html.relay-landing{scroll-behavior:smooth;-webkit-text-size-adjust:100%}@media (prefers-reduced-motion:reduce){html.relay-landing{scroll-behavior:auto}}.landing{background:var(--bg-page);color:var(--text-secondary);font-family:var(--font-body);font-size:var(--fs-body);line-height:var(--lh-body);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;margin:0;overflow-x:hidden}.landing h1,.landing h2,.landing h3,.landing h4{font-family:var(--font-display);color:var(--text-primary);margin:0;font-weight:700}.landing p{margin:0}.landing a{color:inherit;text-decoration:none}.landing button{font:inherit;color:inherit}.landing code,.landing kbd{font-family:var(--font-mono);font-size:.92em}html.relay-landing ::-webkit-scrollbar{width:10px;height:10px}html.relay-landing ::-webkit-scrollbar-track{background:0 0}html.relay-landing ::-webkit-scrollbar-thumb{background:color-mix(in srgb, var(--iris) 22%, transparent);background-clip:content-box;border:2px solid #0000;border-radius:6px}.landing ::-webkit-scrollbar-thumb:hover{background:color-mix(in srgb, var(--iris) 38%, transparent);background-clip:content-box}.landing .shell{z-index:1;position:relative}.landing .wrap{width:100%;max-width:var(--maxw);padding-inline:var(--gutter);margin:0 auto}.landing .nav{z-index:40;align-items:center;gap:var(--space-6);padding:18px var(--gutter);transition:background var(--d-panel) var(--ease-out), border-color var(--d-panel) var(--ease-out), backdrop-filter var(--d-panel) var(--ease-out), padding var(--d-panel) var(--ease-out);border-bottom:1px solid #0000;display:flex;position:fixed;top:0;left:0;right:0}.landing .nav.stuck{background:color-mix(in srgb, var(--bg-page) 72%, transparent);border-bottom-color:var(--border-subtle);-webkit-backdrop-filter:blur(16px)saturate(140%);padding-block:12px}.landing .nav .brand{align-items:center;gap:11px;display:flex}.landing .nav .brand b{font-family:var(--font-display);letter-spacing:var(--track-tight);color:var(--text-primary);font-size:15px;font-weight:700}.landing .nav .brand span{font-family:var(--font-mono);letter-spacing:var(--track-eyebrow);color:var(--text-ghost);text-transform:uppercase;font-size:9.5px}.landing .nav-links{align-items:center;gap:26px;margin-left:auto;display:flex}.landing .nav-links a{color:var(--text-muted);transition:color var(--d-hover) var(--ease-out);font-size:13px;position:relative}.landing .nav-links a:hover{color:var(--text-primary)}.landing .nav-links a:after{content:"";background:var(--gradient-brand);transform-origin:0;height:1px;transition:transform var(--d-panel) var(--ease-out);position:absolute;bottom:-5px;left:0;right:0;transform:scaleX(0)}.landing .nav-links a:hover:after{transform:scaleX(1)}.landing .nav-menu-toggle,.landing .mobile-nav{display:none}@media (max-width:860px){.landing .nav-links{display:none}.landing .nav-menu-toggle{border:1px solid var(--border-subtle);border-radius:var(--radius-sm);background:color-mix(in srgb, var(--bg-surface) 66%, transparent);width:36px;height:36px;color:var(--text-secondary);cursor:pointer;justify-content:center;align-items:center;padding:0;display:inline-flex}.landing .nav-menu-toggle:hover{color:var(--text-primary);border-color:var(--border-strong)}.landing .nav-menu-toggle:focus-visible{color:var(--brand-400);box-shadow:0 0 0 3px var(--brand-tint);outline:none}.landing .mobile-nav{top:calc(100% + 8px);left:var(--gutter);right:var(--gutter);border:1px solid var(--border-default);border-radius:var(--radius-md);background:color-mix(in srgb, var(--bg-elevated) 94%, transparent);box-shadow:var(--shadow-float);-webkit-backdrop-filter:blur(16px)saturate(140%);backdrop-filter:blur(16px)saturate(140%);gap:4px;padding:8px;display:grid;position:absolute}.landing .mobile-nav[aria-hidden=true]{display:none}.landing .mobile-nav a{border-radius:var(--radius-sm);color:var(--text-secondary);padding:11px 12px;font-size:13px}.landing .mobile-nav a:hover,.landing .mobile-nav a:focus-visible{color:var(--text-primary);background:var(--brand-tint);outline:none}}.landing .display-1{font-size:var(--fs-display-1);line-height:var(--lh-display);letter-spacing:var(--track-display);font-weight:700}.landing .display-2{font-size:var(--fs-display-2);letter-spacing:var(--track-display);font-weight:700;line-height:1.14}.landing .display-3{font-size:var(--fs-display-3);line-height:var(--lh-title);letter-spacing:var(--track-tight);font-weight:600}.landing .lead{font-size:var(--fs-lead);color:var(--text-muted);max-width:62ch;line-height:1.7}.landing .eyebrow{font-family:var(--font-mono);font-size:var(--fs-micro);letter-spacing:var(--track-eyebrow);text-transform:uppercase;color:var(--c,var(--accent-cyan));align-items:center;gap:12px;display:inline-flex}.landing .eyebrow:before{content:"";opacity:.5;background:currentColor;width:26px;height:1px}.landing .eyebrow .idx{color:var(--text-ghost)}.landing .mono{font-family:var(--font-mono)}.landing .dim{color:var(--text-ghost)}.landing .btn{border-radius:var(--radius-md);cursor:pointer;white-space:nowrap;height:44px;transition:transform var(--d-tap) var(--ease-out), box-shadow var(--d-hover) var(--ease-out), border-color var(--d-hover) var(--ease-out), background var(--d-hover) var(--ease-out);border:1px solid #0000;align-items:center;gap:9px;padding:0 22px;font-size:14px;font-weight:500;display:inline-flex}.landing .btn:active{transform:translateY(1px)}.landing .btn-primary{background:var(--brand-500);color:#fff;box-shadow:0 8px 26px -8px color-mix(in srgb, var(--brand-500) 70%, transparent)}.landing .btn-primary:hover{background:var(--brand-hover);box-shadow:0 12px 34px -8px color-mix(in srgb, var(--brand-500) 90%, transparent)}.landing .btn-ghost{background:var(--wash-3);color:var(--text-secondary);border-color:var(--border-default)}.landing .btn-ghost:hover{border-color:var(--border-strong);color:var(--text-primary);background:var(--wash-6)}.landing .btn-sm{height:34px;padding:0 15px;font-size:12.5px}.landing .glass{background:var(--glass-bg);border:1px solid var(--glass-border);border-radius:var(--radius-xl);-webkit-backdrop-filter:blur(var(--glass-blur)) saturate(150%);box-shadow:var(--shadow-float);position:relative;overflow:hidden}.landing .glass:before{content:"";pointer-events:none;background:var(--edge-light);position:absolute;inset:0}.landing .glass-soft{background:var(--glass-bg-soft);-webkit-backdrop-filter:blur(calc(var(--glass-blur) * .6));box-shadow:none}.landing .section{padding-block:clamp(62px,8.4vh,122px);position:relative}.landing .section:before,.landing .strip:before{content:"";z-index:-1;pointer-events:none;background:linear-gradient(180deg, transparent 0%, color-mix(in srgb, var(--bg-band) 84%, transparent) 6%, color-mix(in srgb, var(--bg-band) 88%, transparent) 50%, color-mix(in srgb, var(--bg-band) 84%, transparent) 94%, transparent 100%);position:absolute;inset:0}.landing .sec-head{gap:var(--space-6) var(--space-8);margin-bottom:var(--space-8);grid-template-columns:minmax(0,7fr) minmax(0,5fr);align-items:end;display:grid}.landing .sec-head .lead{padding-bottom:6px}@media (max-width:900px){.landing .sec-head{grid-template-columns:1fr;align-items:start}}.landing .rule{background:var(--border-subtle);border:0;height:1px;margin:0}.landing .reveal{opacity:0;transition:opacity .9s var(--ease-out), transform .9s var(--ease-out);transition-delay:calc(var(--i,0) * 80ms);transform:translateY(22px)}.landing .reveal.in{opacity:1;transform:none}@media (prefers-reduced-motion:reduce){.landing .reveal{opacity:1!important;transform:none!important}}.landing .rail{z-index:30;flex-direction:column;align-items:center;gap:9px;display:flex;position:fixed;top:50%;right:22px;transform:translateY(-50%)}.landing .rail i{background:var(--border-default);transform-origin:50% 0;width:1px;height:26px;display:block}.landing .rail i.on{background:var(--gradient-brand);box-shadow:0 0 12px 1px color-mix(in srgb, var(--brand-500) 60%, transparent)}@media (max-width:1180px){.landing .rail{display:none}}.landing .code{background:var(--bg-code);border:1px solid var(--border-subtle);border-radius:var(--radius-lg);font-family:var(--font-mono);color:var(--text-tertiary);white-space:pre;padding:18px 20px;font-size:12.5px;line-height:1.9;position:relative;overflow-x:auto;-webkit-mask-image:none}.landing .code .c{color:var(--text-faint)}.landing .code .k{color:var(--accent-cyan)}.landing .code .s{color:var(--signal-live)}.landing .code .v{color:var(--brand-400)}.landing .code .fn{color:var(--text-primary);font-weight:500}.landing .footer{z-index:1;border-top:1px solid var(--border-subtle);background:var(--bg-band);color:var(--text-ghost);padding-block:40px;font-size:12.5px;position:relative}.landing .footer .row{flex-wrap:wrap;align-items:center;gap:14px 28px;display:flex}.landing .footer .row .sp{margin-left:auto}.landing .grid-2{gap:var(--space-5);grid-template-columns:repeat(2,minmax(0,1fr));display:grid}.landing .grid-3{gap:var(--space-5);grid-template-columns:repeat(3,minmax(0,1fr));display:grid}.landing .grid-4{gap:var(--space-5);grid-template-columns:repeat(4,minmax(0,1fr));display:grid}@media (max-width:1100px){.landing .grid-3,.landing .grid-4{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (max-width:720px){.landing .grid-2,.landing .grid-3,.landing .grid-4{grid-template-columns:1fr}}.landing .card{background:var(--bg-surface-grad);border:1px solid var(--border-subtle);border-radius:var(--radius-lg);position:relative}.landing .card-2{background:var(--bg-surface-2-grad)}.landing .dot{background:currentColor;border-radius:50%;flex:0 0 7px;width:7px;height:7px;display:inline-block;box-shadow:0 0 10px 1px}.landing .dot-live{animation:dot-pulse 2.6s var(--ease-in-out) infinite}@keyframes dot-pulse{0%,to{box-shadow:0 0 10px 1px}50%{box-shadow:0 0 16px 3px}}.landing .pill{height:22px;font-family:var(--font-mono);letter-spacing:.02em;color:var(--text-tertiary);background:var(--wash-3);border:1px solid var(--border-default);white-space:nowrap;border-radius:999px;align-items:center;gap:6px;padding:0 9px;font-size:11.5px;display:inline-flex}.landing .pill-live{color:var(--signal-live);border-color:var(--signal-live-track);background:color-mix(in srgb, var(--signal-live) 7%, transparent)}.landing .pill-amber{color:var(--warn-amber);border-color:var(--warn-amber-line);background:color-mix(in srgb, var(--warn-amber) 7%, transparent)}.landing .pill-brand{color:var(--brand-400);border-color:color-mix(in srgb, var(--iris) 28%, transparent);background:var(--brand-tint)}.landing .num{font-family:var(--font-display);font-variant-numeric:tabular-nums;font-feature-settings:"tnum" 1;letter-spacing:var(--track-display);font-weight:700}.landing .unit{color:var(--text-faint);letter-spacing:0;margin-left:3px;font-size:.42em;font-weight:600}@media (prefers-reduced-motion:reduce){.landing .dot-live,.landing .grad-text{animation:none!important}}.landing [id]{scroll-margin-top:104px}.landing .progress{z-index:60;pointer-events:none;height:2px;position:fixed;top:0;left:0;right:0}.landing .progress i{height:100%;transform:scaleX(var(--scroll-p,0));transform-origin:0;background:var(--gradient-brand);box-shadow:0 0 14px 1px color-mix(in srgb, var(--brand-500) 55%, transparent);will-change:transform;display:block}.landing .hero{padding-block:clamp(140px,16vh,196px) clamp(56px,8vh,96px);position:relative}.landing .hero-grid{grid-template-columns:minmax(0,1.55fr) minmax(0,.85fr);align-items:start;gap:clamp(28px,3.6vw,64px);display:grid}.landing .hero-inner{max-width:940px}.landing .hero-side{padding-top:clamp(34px,4.4vh,58px)}@media (max-width:1080px){.landing .hero-grid{grid-template-columns:1fr}.landing .hero-side{display:none}}.landing .hero-badge{background:color-mix(in srgb, var(--signal-live) 7%, transparent);border:1px solid var(--signal-live-track);color:var(--signal-live);font-size:12.5px;font-family:var(--font-mono);letter-spacing:.01em;border-radius:999px;align-items:center;gap:9px;margin-bottom:32px;padding:7px 15px;display:inline-flex}.landing .hero-badge .n{font-variant-numeric:tabular-nums}.landing .hero-title{position:relative}.landing .hero-h1{margin:0}.landing .hero-h1 .mk{font-family:var(--font-mono);letter-spacing:-.03em;font-weight:700}.landing .hero-flip{pointer-events:none;-webkit-user-select:none;user-select:none;opacity:.26;filter:blur(.8px);width:100%;height:clamp(44px,5.6vh,72px);position:absolute;top:100%;left:0;overflow:hidden;-webkit-mask-image:linear-gradient(#0000006b 0%,#0000001a 40%,#0000 82%);mask-image:linear-gradient(#0000006b 0%,#0000001a 40%,#0000 82%)}.landing .hero-flip>.hero-h1{transform-origin:50% 100%;width:100%;position:absolute;bottom:100%;left:0;transform:scaleY(-1)}.landing .hero-flip .grad-text{background-position:30% 0;animation:none}.landing .hero-sub{max-width:600px;font-size:var(--fs-lead);color:var(--text-muted);margin-top:clamp(62px,7.6vh,92px);line-height:1.78}.landing .hero-acts{flex-wrap:wrap;gap:14px;margin-top:38px;display:flex}.landing .trust{color:var(--text-faint);font-size:12.5px;font-family:var(--font-mono);letter-spacing:.01em;flex-wrap:wrap;gap:12px 26px;margin-top:28px;display:flex}.landing .trust span{align-items:center;gap:8px;display:inline-flex}.landing .trust span:before{content:"";background:var(--brand-400);opacity:.8;flex:0 0 5px;width:5px;height:5px;transform:rotate(45deg)}.landing .rv-line{display:block;overflow:hidden}.landing .rv-line>span{transition:transform 1.15s var(--ease-out);transition-delay:calc(var(--i,0) * .11s);display:block;transform:translateY(106%)}.landing .reveal.in .rv-line>span{transform:none}@media (prefers-reduced-motion:reduce){.landing .rv-line>span{transform:none!important}}.landing .scroll-cue{left:var(--gutter);font-family:var(--font-mono);letter-spacing:var(--track-eyebrow);text-transform:uppercase;color:var(--text-ghost);opacity:max(0, calc(1 - var(--scroll-p) * 22));align-items:center;gap:12px;font-size:9.5px;transition:opacity .2s linear;display:flex;position:absolute;bottom:34px}.landing .scroll-cue .tick{background:linear-gradient(180deg, var(--border-strong), transparent);width:1px;height:40px;position:relative;overflow:hidden}.landing .scroll-cue .tick:after{content:"";background:var(--accent-cyan);width:1px;height:14px;box-shadow:0 0 8px 1px var(--glow-cyan-mid);animation:cue-run 2.4s var(--ease-in-out) infinite;position:absolute;left:0}@keyframes cue-run{0%{opacity:0;top:-14px}25%{opacity:1}75%{opacity:1}to{opacity:0;top:40px}}@media (max-width:900px){.landing .scroll-cue{display:none}}.landing .access{border-radius:var(--radius-xl);background:linear-gradient(180deg, color-mix(in srgb, var(--bg-section-b) 96%, transparent) 0%, color-mix(in srgb, var(--bg-section-a) 94%, transparent) 100%);padding:20px 22px 22px}.landing .access-h{justify-content:space-between;align-items:center;gap:12px;display:flex}.landing .access-url{background:color-mix(in srgb, var(--bg-band) 62%, transparent);border:1px solid var(--border-default);border-radius:var(--radius-md);align-items:center;gap:12px;margin-top:14px;padding:11px 12px 11px 14px;display:flex}.landing .access-url code{min-width:0;font-family:var(--font-mono);color:var(--accent-cyan);text-overflow:ellipsis;white-space:nowrap;flex:1;font-size:12.5px;overflow:hidden}.landing .access-url .copy{height:26px;font-family:var(--font-mono);color:var(--text-muted);cursor:pointer;background:var(--wash-4);border:1px solid var(--border-default);border-radius:var(--radius-xs);transition:color var(--d-hover) var(--ease-out), border-color var(--d-hover) var(--ease-out);flex:none;padding:0 10px;font-size:11px}.landing .access-url .copy:hover{color:var(--text-primary);border-color:var(--border-strong)}.landing .access-note{color:var(--text-ghost);margin-top:12px;font-size:12px;line-height:1.7}.landing .kv{margin:18px 0 0;padding:0}.landing .kv>div{border-top:1px solid var(--border-subtle);justify-content:space-between;align-items:baseline;gap:12px;padding:9px 0;display:flex}.landing .kv>div:last-child{border-bottom:1px solid var(--border-subtle)}.landing .kv dt{color:var(--text-muted);font-size:12.5px}.landing .kv dd{font-family:var(--font-mono);color:var(--text-secondary);font-variant-numeric:tabular-nums;margin:0;font-size:12.5px}.landing .panel-wrap{border-radius:var(--radius-2xl);grid-template-columns:minmax(0,1.3fr) minmax(0,.9fr);margin-top:clamp(52px,7vh,88px);display:grid}.landing .panel-left{min-width:0}.landing .winbar{border-bottom:1px solid var(--border-subtle);background:color-mix(in srgb, var(--bg-page) 40%, transparent);align-items:center;gap:8px;padding:12px 14px 12px 18px;display:flex}.landing .winbar>i{border-radius:50%;width:10px;height:10px;display:block}.landing .winbar .fname{font-family:var(--font-mono);color:var(--text-faint);margin-left:10px;font-size:11.5px}.landing .winbar .wtag{border-radius:var(--radius-xs);font-family:var(--font-mono);letter-spacing:.1em;color:var(--text-ghost);background:var(--wash-4);border:1px solid var(--border-subtle);padding:2px 6px;font-size:9.5px}.landing .winbar .wsp{flex:1}.landing .winbar .wbtn{cursor:pointer;height:24px;font-family:var(--font-mono);color:var(--text-muted);background:var(--wash-4);border:1px solid var(--border-subtle);border-radius:var(--radius-xs);transition:color var(--d-hover) var(--ease-out), border-color var(--d-hover) var(--ease-out);padding:0 10px;font-size:10.5px}.landing .winbar .wbtn:hover{color:var(--text-primary);border-color:var(--border-strong)}.landing .panel-code{font-family:var(--font-mono);padding:12px 0 16px;font-size:12.5px;line-height:1.9}.landing .cl{grid-template-columns:46px minmax(0,1fr);padding-right:20px;display:grid}.landing .cl>i{text-align:right;color:var(--text-faint);-webkit-user-select:none;user-select:none;padding-right:16px;font-style:normal}.landing .cl>span{white-space:pre;color:var(--text-tertiary);overflow-x:auto}.landing .cl.hl{background:color-mix(in srgb, var(--brand-500) 8.5%, transparent);box-shadow:inset 2px 0 0 var(--brand-500)}.landing .cl.hl>i{opacity:.72;color:var(--brand-400)}.landing .panel-code .k{color:var(--accent-cyan)}.landing .panel-code .s{color:var(--signal-live)}.landing .panel-code .c{color:var(--text-faint)}.landing .panel-code .fn{color:var(--text-primary);font-weight:500}.landing .panel-right{border-left:1px solid var(--border-subtle);background:color-mix(in srgb, var(--bg-page) 40%, transparent);padding:16px 20px 20px}.landing .pr-head{justify-content:space-between;align-items:baseline;gap:12px;padding-bottom:12px;display:flex}.landing .panel-right h4{font-family:var(--font-mono);letter-spacing:var(--track-eyebrow);text-transform:uppercase;color:var(--text-ghost);font-size:10.5px;font-weight:500}.landing .pr-count{font-family:var(--font-mono);color:var(--text-ghost);font-size:10.5px}.landing .conn{border-top:1px solid var(--border-subtle);align-items:center;gap:10px;padding:12px 0;font-size:12.5px;display:flex}.landing .conn .cname{min-width:0;color:var(--text-secondary);text-overflow:ellipsis;white-space:nowrap;flex:1;overflow:hidden}.landing .conn .dev{color:var(--text-ghost);font-size:10.5px;font-family:var(--font-mono)}.landing .conn .lat{text-align:right;font-family:var(--font-mono);color:var(--text-faint);font-variant-numeric:tabular-nums;flex:0 0 40px;font-size:10.5px}.landing .panel-metrics{border-top:1px solid var(--border-subtle);grid-template-columns:repeat(3,1fr);gap:14px;margin-top:16px;padding-top:16px;display:grid}.landing .panel-metrics .k{color:var(--text-ghost);margin-bottom:7px;font-size:10.5px}.landing .panel-metrics .v{color:var(--text-primary);font-size:19px}@media (max-width:900px){.landing .panel-wrap{grid-template-columns:1fr}.landing .panel-right{border-left:none;border-top:1px solid var(--border-subtle)}.landing .winbar .wtag,.landing .winbar .wbtn{display:none}}.landing .strip{padding-block:clamp(34px,5vh,52px);position:relative}.landing .strip .cap{color:var(--text-muted);margin-bottom:20px;font-size:13.5px}.landing .strip .cap b{color:var(--text-secondary);font-weight:600}.landing .logos{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:18px 30px;display:flex}.landing .logos span{font-family:var(--font-display);color:var(--text-faint);letter-spacing:.01em;transition:color var(--d-panel) var(--ease-out);font-size:16px;font-weight:600}.landing .logos span:hover{color:var(--text-tertiary)}.landing .logos span.cn{font-family:var(--font-body);color:var(--text-muted);font-size:15px;font-weight:500}.landing .split{grid-template-columns:440px minmax(0,1fr);align-items:start;gap:clamp(40px,5vw,76px);display:grid}.landing .split-l{position:sticky;top:124px}.landing .split-l .display-2{margin:20px 0 18px;font-size:clamp(26px,2.6vw,40px)}.landing .split-l p{color:var(--text-muted);font-size:14px;line-height:1.8}@media (max-width:980px){.landing .split{grid-template-columns:1fr;gap:32px}.landing .split-l{position:static}}.landing .linelist{margin:0;padding:0;list-style:none}.landing .linelist li{border-top:1px solid var(--border-subtle);gap:20px;padding:28px 0;display:flex}.landing .linelist li:last-child{border-bottom:1px solid var(--border-subtle)}.landing .linelist li:first-child{border-top-color:var(--border-default)}.landing .ll-ico{border-radius:var(--radius-md);background:var(--bg-surface-2-grad);border:1px solid var(--border-default);flex:0 0 40px;justify-content:center;align-items:center;width:40px;height:40px;display:flex}.landing .ll-tx b{color:var(--text-primary);margin-bottom:8px;font-size:15.5px;font-weight:600;display:block}.landing .ll-tx i{color:var(--text-tertiary);font-size:13.5px;font-style:normal;line-height:1.75}.landing .steps{grid-template-columns:repeat(3,1fr);margin:0;padding:0;list-style:none;display:grid}.landing .steps li{padding-right:44px;position:relative}.landing .steps li:before{content:"";background:linear-gradient(90deg, color-mix(in srgb, var(--iris) 34%, transparent) 0%, color-mix(in srgb, var(--iris) 34%, transparent) 36%, var(--glow-cyan-core) 50%, color-mix(in srgb, var(--accent-cyan) 30%, transparent) 64%, color-mix(in srgb, var(--accent-cyan) 30%, transparent) 100%);height:1px;animation:4.6s linear infinite step-flow;animation-delay:calc(var(--i,0) * .55s);background-size:280% 100%;position:absolute;top:23px;left:56px;right:12px}.landing .steps li:after{content:"";background:color-mix(in srgb, var(--iris) 70%, transparent);border-radius:50%;width:5px;height:5px;position:absolute;top:20.5px;left:56px}.landing .steps li:last-child:before,.landing .steps li:last-child:after{display:none}@keyframes step-flow{0%{background-position:140% 0}to{background-position:-140% 0}}.landing .st-n{z-index:1;border-radius:var(--radius-lg);width:46px;height:46px;font-family:var(--font-mono);color:var(--brand-400);background:var(--bg-surface-2-grad);border:1px solid var(--border-default);font-variant-numeric:tabular-nums;justify-content:center;align-items:center;font-size:14px;display:flex;position:relative}.landing .steps h3{color:var(--text-primary);margin:22px 0 10px;font-size:16px;font-weight:600}.landing .steps p{color:var(--text-muted);font-size:13.5px;line-height:1.75}.landing .steps .cmd{font-family:var(--font-mono);color:var(--accent-cyan);background:var(--bg-code);border:1px solid var(--border-default);border-radius:var(--radius-sm);white-space:nowrap;margin-top:16px;padding:10px 12px;font-size:11.5px;overflow-x:auto}@media (max-width:900px){.landing .steps{grid-template-columns:1fr;gap:34px}.landing .steps li{padding-right:0}.landing .steps li:before,.landing .steps li:after{display:none}}.landing .term{border:1px solid var(--border-subtle);border-radius:var(--radius-lg);background:color-mix(in srgb, var(--bg-band) 66%, transparent);margin-top:clamp(38px,5vh,62px);overflow:hidden}.landing .term-bar{border-bottom:1px solid var(--border-subtle);background:color-mix(in srgb, var(--bg-page) 52%, transparent);font-family:var(--font-mono);letter-spacing:.02em;color:var(--text-faint);align-items:center;gap:9px;padding:12px 18px;font-size:11px;display:flex}.landing .term-tag{color:var(--text-ghost);margin-left:auto}.landing .term-body{font-family:var(--font-mono);padding:15px 20px 17px;font-size:12.5px;line-height:2.05}.landing .tl{color:var(--text-tertiary)}.landing .tl .pr{color:var(--brand-400);margin-right:9px}.landing .tl.out{color:var(--text-muted)}.landing .tl.ok,.landing .tl .hl{color:var(--signal-live)}@media (max-width:720px){.landing .term-body{font-size:11.5px;line-height:1.95}.landing .tl{word-break:break-all}}.landing .cfg{flex-direction:column;padding:22px;display:flex}.landing .cfg h3{margin-bottom:7px;font-size:16px;font-weight:600}.landing .cfg .sub{color:var(--text-faint);font-size:11.5px;font-family:var(--font-mono);white-space:nowrap;text-overflow:ellipsis;margin-bottom:18px;overflow:hidden}.landing .cfg .code{white-space:pre-wrap;overflow-wrap:anywhere;flex:1;font-size:12px;line-height:1.8}.landing .specbar{background:var(--bg-surface);border:1px solid var(--border-subtle);border-radius:var(--radius-lg);grid-template-columns:repeat(4,1fr);margin-top:22px;display:grid;overflow:hidden}.landing .specbar>div{border-right:1px solid var(--border-subtle);padding:17px 20px}.landing .specbar>div:last-child{border-right:none}.landing .specbar .k{color:var(--text-muted);margin-bottom:9px;font-size:11.5px}.landing .specbar .v{font-family:var(--font-mono);color:var(--text-primary);font-size:14px}@media (max-width:720px){.landing .specbar{grid-template-columns:repeat(2,1fr)}.landing .specbar>div:nth-child(2){border-right:none}.landing .specbar>div:nth-child(-n+2){border-bottom:1px solid var(--border-subtle)}}.landing .cfg-note{background:var(--bg-surface);border:1px solid var(--border-subtle);border-radius:var(--radius-lg);margin-top:18px;padding:16px 20px}.landing .cfg-note-row{border-bottom:1px solid var(--border-subtle);grid-template-columns:132px 1fr;align-items:baseline;gap:6px 14px;padding:9px 0;display:grid}.landing .cfg-note-row .k{color:var(--text-muted);font-size:12.5px}.landing .cfg-note-row code{font-family:var(--font-mono);color:var(--text-primary);font-size:12.5px}.landing .cfg-note-row .v{color:var(--text-tertiary);grid-column:2;font-size:12px}.landing .cfg-note-row .v b{color:var(--text-primary);font-weight:600}.landing .cfg-note-foot{color:var(--text-tertiary);margin-top:12px;font-size:12px;line-height:1.7}.landing .cfg-note-foot:first-of-type{margin-top:14px}.landing .cfg-note-foot code{font-family:var(--font-mono);color:var(--text-primary);overflow-wrap:anywhere;font-size:11.5px}.landing .cfg-note-foot b{color:var(--text-secondary);font-weight:600}@media (max-width:720px){.landing .cfg-note-row{grid-template-columns:1fr}.landing .cfg-note-row .v{grid-column:1}}.landing .band{background:linear-gradient(180deg, transparent 0%, color-mix(in srgb, var(--bg-page) 62%, transparent) 18%, color-mix(in srgb, var(--bg-page) 62%, transparent) 82%, transparent 100%);position:relative}.landing .status-strip{text-align:center;grid-template-columns:repeat(4,1fr);display:grid}.landing .status-strip>div{padding:6px 12px;position:relative}.landing .status-strip>div+div:before{content:"";background:var(--border-subtle);width:1px;position:absolute;top:12%;bottom:12%;left:0}.landing .status-strip .v{color:var(--text-primary);font-size:clamp(34px,4vw,54px);line-height:1}.landing .status-strip .v.live{color:var(--signal-live)}.landing .status-strip .k{color:var(--text-muted);margin-top:14px;font-size:12.5px}@media (max-width:720px){.landing .status-strip{grid-template-columns:repeat(2,1fr);gap:30px 0}.landing .status-strip>div:nth-child(odd):before{display:none}}.landing .cta{text-align:center;padding-block:clamp(96px,15vh,172px);position:relative}.landing .cta:before{content:"";background:radial-gradient(58% 66% at 50% 50%, color-mix(in srgb, var(--bg-band) 88%, transparent) 0%, color-mix(in srgb, var(--bg-band) 55%, transparent) 46%, transparent 76%);pointer-events:none;position:absolute;inset:0}.landing .cta>.wrap{position:relative}.landing .cta h2{max-width:720px;margin:0 auto}.landing .cta .cmdbox{background:color-mix(in srgb, var(--bg-band) 70%, transparent);border:1px solid var(--border-strong);border-radius:var(--radius-lg);font-family:var(--font-mono);color:var(--signal-live);box-shadow:0 0 0 1px color-mix(in srgb, var(--signal-live) 6%, transparent), 0 24px 60px -34px color-mix(in srgb, var(--signal-live) 50%, transparent);align-items:center;gap:12px;margin:32px auto 26px;padding:15px 28px;font-size:15px;display:inline-flex}.landing .cta .cmdbox .pr{color:var(--text-ghost)}.landing .cmdbox .typing:after{content:"";vertical-align:-2px;background:var(--accent-cyan);width:7px;height:1em;margin-left:3px;animation:1s step-end infinite caret;display:inline-block}@keyframes caret{50%{opacity:0}}@media (prefers-reduced-motion:reduce){.landing .cmdbox .typing:after{animation:none}}.landing .cta p.note{color:var(--text-muted);max-width:620px;margin:0 auto;font-size:14px;line-height:1.8}.landing .cta .acts{flex-wrap:wrap;justify-content:center;gap:14px;margin-top:32px;display:flex}.landing .cta .trust{justify-content:center}.landing .footer{padding-block:72px 40px}.landing .foot-top{grid-template-columns:minmax(0,1.6fr) minmax(0,1fr) minmax(0,1fr);gap:44px;display:grid}.landing .foot-brand p{color:var(--text-muted);max-width:340px;margin:20px 0 22px;font-size:13.5px;line-height:1.85}.landing .foot-col h5{color:var(--text-muted);font-size:11.5px;font-weight:500;font-family:var(--font-mono);letter-spacing:var(--track-eyebrow);text-transform:uppercase;margin-bottom:18px}.landing .foot-col a{color:var(--text-faint);transition:color var(--d-hover) var(--ease-out);font-size:13px;line-height:2.2;display:block}.landing .foot-col a:hover{color:var(--text-secondary)}.landing .foot-bar{border-top:1px solid var(--border-subtle);color:var(--text-ghost);flex-wrap:wrap;justify-content:space-between;gap:10px 28px;margin-top:56px;padding-top:24px;font-size:12px;display:flex}@media (max-width:900px){.landing .foot-top{grid-template-columns:1fr 1fr}.landing .foot-brand{grid-column:1/-1}}.landing .rail{align-items:flex-end}.landing .rail a{flex-direction:row-reverse;align-items:center;gap:9px;display:flex}.landing .rail a b{font-family:var(--font-mono);letter-spacing:var(--track-eyebrow);text-transform:uppercase;color:#0000;white-space:nowrap;transition:color var(--d-panel) var(--ease-out);font-size:9.5px}.landing .rail a:hover b,.landing .rail a.on b{color:var(--text-faint)}.landing .rail a i{transition:background var(--d-panel) var(--ease-out), box-shadow var(--d-panel) var(--ease-out)}html.relay-landing{overflow-x:hidden}.landing{overflow-x:visible}.landing .nav{height:auto;box-shadow:none;-webkit-backdrop-filter:none;backdrop-filter:none;background:0 0}.landing .nav:before,.landing .nav:after{content:none}.landing .section{padding-inline:0}.landing .section:after{content:none}.landing .eyebrow{margin-bottom:0;font-weight:400}.landing .pill{font-weight:400}.landing .note{background:0 0;border:0;padding:0;display:block}.landing .btn:hover{transform:none}.btn-primary:hover{box-shadow:0 6px 20px -4px color-mix(in srgb, var(--brand-500) 55%, transparent), inset 0 1px 0 #ffffff2e;border-color:color-mix(in srgb, var(--brand-400) 60%, transparent)}.btn-quiet:hover{border-color:var(--border-strong);box-shadow:0 3px 12px -4px #0000004d}.landing h5{margin:revert}.landing .glass{-webkit-backdrop-filter:blur(var(--glass-blur)) saturate(150%);backdrop-filter:blur(var(--glass-blur)) saturate(150%)}.landing .glass-soft{-webkit-backdrop-filter:blur(calc(var(--glass-blur) * .6));backdrop-filter:blur(calc(var(--glass-blur) * .6))}.landing .nav.stuck{-webkit-backdrop-filter:blur(16px)saturate(140%);backdrop-filter:blur(16px)saturate(140%)}.landing .nav{justify-content:normal}.landing .nav-links{color:var(--text-secondary)}.landing .hero>.wrap{z-index:auto;position:static}.landing .note{border-radius:0;gap:normal}.landing .btn{justify-content:normal}
@@ -1,4 +1,4 @@
1
- import{b as e,it as t,t as n,tt as r}from"./vendor-befEYGHM.js";import{c as i,i as a,n as o,o as s,s as c,x as l}from"./index-BJtz6Ses.js";import{t as u}from"./accessUrl-DbL5DLDz.js";var d=t(),f=`1.6.15`,p={totalTokens:193e4,cacheHitRate:62.4,requestsToday:1284,successRate:99.7};function m(e,t=0){let n=e.toFixed(t).split(`.`);return n[0]=n[0].replace(/\B(?=(\d{3})+(?!\d))/g,`,`),n.join(`.`)}function h(e,t=1){return e==null?`—`:`${e.toFixed(t)}%`}function g(e){if(e&&typeof e==`object`){let t=e;return{ok:t.ok!==!1,version:typeof t.version==`string`?t.version:void 0}}return{ok:!0}}function _(){let e=c(),[t,n]=(0,d.useState)(!0),[r,a]=(0,d.useState)(f),[o,s]=(0,d.useState)(p),[l,u]=(0,d.useState)(!1);return(0,d.useEffect)(()=>{let e=!0,t=async()=>{try{let t=await fetch(`/healthz`,{headers:{Accept:`application/json`}});if(!t.ok)throw Error(`healthz ${t.status}`);let r=g(await t.json());if(!e)return;n(r.ok),r.version&&a(r.version)}catch{e&&n(!1)}};t();let r=window.setInterval(()=>void t(),3e4);return()=>{e=!1,window.clearInterval(r)}},[]),(0,d.useEffect)(()=>{if(e.status!==`authed`){u(!1),s(p);return}let t=!0;return i.overview(`24h`).then(e=>{t&&(s({totalTokens:e.metrics.totalTokens,cacheHitRate:e.metrics.cacheHitRate*100,requestsToday:e.metrics.requests,successRate:e.metrics.successRate*100}),u(!0))}).catch(()=>{t&&(u(!1),s(p))}),()=>{t=!1}},[e.status]),{online:t,version:r,real:l,numbers:o}}function v(){var e=window.matchMedia(`(prefers-reduced-motion: reduce)`).matches,t=!0,n=[],r=[],i={totalTokens:`本窗口 tokens`,cacheHitRate:`缓存命中率`,requestsToday:`今日请求`,successRate:`今日成功率`};function a(){var e=window.RelayDemo;if(e&&e.summary){var t=[];[].forEach.call(document.querySelectorAll(`[data-demo]`),function(n){var r=n.getAttribute(`data-demo`),a=e.summary[r];if(typeof a==`number`){var o=parseInt(n.getAttribute(`data-dec`)||`0`,10),s=n.textContent.trim().replace(/,/g,``);s!==``&&Math.abs(parseFloat(s)-a)>10**-o/2&&t.push((i[r]||r)+`:页面上写的 `+s+`,数据层是 `+a),n.setAttribute(`data-count`,a),n.textContent=p(a,o)}}),t.length&&window.console&&console.warn(`[page] 首页演示数字与 demo-data 不一致(已按数据层显示):
1
+ import{b as e,it as t,t as n,tt as r}from"./vendor-befEYGHM.js";import{c as i,i as a,n as o,o as s,s as c,x as l}from"./index-Bb8UvXuA.js";import{t as u}from"./accessUrl-DbL5DLDz.js";var d=t(),f=`1.6.17`,p={totalTokens:193e4,cacheHitRate:62.4,requestsToday:1284,successRate:99.7};function m(e,t=0){let n=e.toFixed(t).split(`.`);return n[0]=n[0].replace(/\B(?=(\d{3})+(?!\d))/g,`,`),n.join(`.`)}function h(e,t=1){return e==null?`—`:`${e.toFixed(t)}%`}function g(e){if(e&&typeof e==`object`){let t=e;return{ok:t.ok!==!1,version:typeof t.version==`string`?t.version:void 0}}return{ok:!0}}function _(){let e=c(),[t,n]=(0,d.useState)(!0),[r,a]=(0,d.useState)(f),[o,s]=(0,d.useState)(p),[l,u]=(0,d.useState)(!1);return(0,d.useEffect)(()=>{let e=!0,t=async()=>{try{let t=await fetch(`/healthz`,{headers:{Accept:`application/json`}});if(!t.ok)throw Error(`healthz ${t.status}`);let r=g(await t.json());if(!e)return;n(r.ok),r.version&&a(r.version)}catch{e&&n(!1)}};t();let r=window.setInterval(()=>void t(),3e4);return()=>{e=!1,window.clearInterval(r)}},[]),(0,d.useEffect)(()=>{if(e.status!==`authed`){u(!1),s(p);return}let t=!0;return i.overview(`24h`).then(e=>{t&&(s({totalTokens:e.metrics.totalTokens,cacheHitRate:e.metrics.cacheHitRate*100,requestsToday:e.metrics.requests,successRate:e.metrics.successRate*100}),u(!0))}).catch(()=>{t&&(u(!1),s(p))}),()=>{t=!1}},[e.status]),{online:t,version:r,real:l,numbers:o}}function v(){var e=window.matchMedia(`(prefers-reduced-motion: reduce)`).matches,t=!0,n=[],r=[],i={totalTokens:`本窗口 tokens`,cacheHitRate:`缓存命中率`,requestsToday:`今日请求`,successRate:`今日成功率`};function a(){var e=window.RelayDemo;if(e&&e.summary){var t=[];[].forEach.call(document.querySelectorAll(`[data-demo]`),function(n){var r=n.getAttribute(`data-demo`),a=e.summary[r];if(typeof a==`number`){var o=parseInt(n.getAttribute(`data-dec`)||`0`,10),s=n.textContent.trim().replace(/,/g,``);s!==``&&Math.abs(parseFloat(s)-a)>10**-o/2&&t.push((i[r]||r)+`:页面上写的 `+s+`,数据层是 `+a),n.setAttribute(`data-count`,a),n.textContent=p(a,o)}}),t.length&&window.console&&console.warn(`[page] 首页演示数字与 demo-data 不一致(已按数据层显示):
2
2
  · `+t.join(`
3
3
  · `))}}function o(e){var t=!1;return function(){t||(t=!0,requestAnimationFrame(function(){t=!1,e()}))}}function s(){var e=[].slice.call(document.querySelectorAll(`.reveal`));if(e.length){if(!(`IntersectionObserver`in window)){e.forEach(function(e){e.classList.add(`in`)});return}var t=new IntersectionObserver(function(e){e.forEach(function(e){e.isIntersecting&&(e.target.classList.add(`in`),t.unobserve(e.target))})},{rootMargin:`0px 0px -10% 0px`,threshold:.1});r.push(t),e.forEach(function(e){t.observe(e)})}}var c=document.querySelector(`.nav`);function l(){c&&c.classList.toggle(`stuck`,window.pageYOffset>28)}var u=[].slice.call(document.querySelectorAll(`.rail a`)),d=u.map(function(e){return document.querySelector(e.getAttribute(`href`))});function f(){if(u.length){for(var e=window.pageYOffset+window.innerHeight*.42,t=0,n=0;n<d.length;n+=1){var r=d[n];r&&r.offsetTop<=e&&(t=n)}for(var i=0;i<u.length;i+=1)u[i].classList.toggle(`on`,i===t)}}function p(e,t){var n=e.toFixed(t).split(`.`);return n[0]=n[0].replace(/\B(?=(\d{3})+(?!\d))/g,`,`),n.join(`.`)}function m(e){var n=parseFloat(e.getAttribute(`data-count`));if(isNaN(n))return;var r=parseInt(e.getAttribute(`data-dec`)||`0`,10),i=1500,a=0;function o(s){if(t){a||=s;var c=Math.min(1,(s-a)/i);e.textContent=p(n*(c===1?1:1-2**(-10*c)),r),c<1?requestAnimationFrame(o):e.textContent=p(n,r)}}requestAnimationFrame(o)}function h(){var t=[].slice.call(document.querySelectorAll(`[data-count]`));if(t.length){if(e||!(`IntersectionObserver`in window)){t.forEach(function(e){var t=parseFloat(e.getAttribute(`data-count`)),n=parseInt(e.getAttribute(`data-dec`)||`0`,10);isNaN(t)||(e.textContent=p(t,n))});return}var n=new IntersectionObserver(function(e){e.forEach(function(e){e.isIntersecting&&(m(e.target),n.unobserve(e.target))})},{threshold:.6});r.push(n),t.forEach(function(e){n.observe(e)})}}function g(){var i=[].slice.call(document.querySelectorAll(`[data-type]`));if(i.length&&!e&&`IntersectionObserver`in window){var a=new IntersectionObserver(function(e){e.forEach(function(e){if(e.isIntersecting){var r=e.target;a.unobserve(r);var i=r.getAttribute(`data-type`),o=0;r.textContent=``,r.classList.add(`typing`),(function e(){if(t){if(o>i.length){r.classList.remove(`typing`);return}r.textContent=i.slice(0,o),o+=1,n.push(window.setTimeout(e,34))}})()}})},{threshold:.8});r.push(a),i.forEach(function(e){a.observe(e)})}}var _=o(function(){l(),f()}),v=o(function(){f()}),y=function(){l(),f()};return a(),s(),h(),g(),l(),f(),window.addEventListener(`scroll`,_,{passive:!0}),window.addEventListener(`resize`,v),window.addEventListener(`load`,y),function(){t=!1,window.removeEventListener(`scroll`,_),window.removeEventListener(`resize`,v),window.removeEventListener(`load`,y),r.forEach(function(e){e.disconnect()}),n.forEach(function(e){clearTimeout(e)}),r=[],n=[]}}var y=r(),b=[{href:`#top`,label:`概览`},{href:`#ways`,label:`运行方式`},{href:`#quickstart`,label:`快速开始`},{href:`#clients`,label:`客户端配置`},{href:`#status`,label:`运行状态`},{href:`#cta`,label:`开始使用`}];function x(){return(0,y.jsx)(`nav`,{className:`rail`,"aria-label":`章节`,children:b.map(e=>(0,y.jsxs)(`a`,{href:e.href,children:[(0,y.jsx)(`i`,{}),(0,y.jsx)(`b`,{children:e.label})]},e.href))})}function S({version:t}){let[r,i]=(0,d.useState)(!1),o=()=>i(!1);return(0,y.jsxs)(`nav`,{className:`nav${r?` menu-open`:``}`,children:[(0,y.jsxs)(`a`,{className:`brand`,href:`#top`,onClick:o,children:[(0,y.jsx)(s,{size:28,id:`relay-mark-nav`}),(0,y.jsx)(`b`,{children:`途关`}),(0,y.jsx)(`span`,{children:`Waygate`})]}),(0,y.jsxs)(`div`,{className:`nav-links`,children:[(0,y.jsx)(`a`,{href:`#ways`,onClick:o,children:`运行方式`}),(0,y.jsx)(`a`,{href:`#quickstart`,onClick:o,children:`快速开始`}),(0,y.jsx)(`a`,{href:`#clients`,onClick:o,children:`客户端配置`}),(0,y.jsx)(`a`,{href:`#status`,onClick:o,children:`运行状态`})]}),(0,y.jsxs)(`div`,{className:`nav-right`,style:{display:`flex`,alignItems:`center`,gap:14,marginLeft:`auto`},children:[(0,y.jsxs)(`span`,{className:`pill pill-brand`,children:[`v`,t]}),(0,y.jsx)(`a`,{className:`btn btn-primary btn-sm`,href:a(),children:`打开控制台`}),(0,y.jsx)(`button`,{type:`button`,className:`nav-menu-toggle`,"aria-label":r?`关闭导航菜单`:`打开导航菜单`,"aria-controls":`landing-mobile-nav`,"aria-expanded":r,onClick:()=>i(e=>!e),children:r?(0,y.jsx)(n,{size:18,"aria-hidden":`true`}):(0,y.jsx)(e,{size:18,"aria-hidden":`true`})})]}),(0,y.jsxs)(`div`,{id:`landing-mobile-nav`,className:`mobile-nav`,"aria-hidden":!r,children:[(0,y.jsx)(`a`,{href:`#ways`,onClick:o,children:`运行方式`}),(0,y.jsx)(`a`,{href:`#quickstart`,onClick:o,children:`快速开始`}),(0,y.jsx)(`a`,{href:`#clients`,onClick:o,children:`客户端配置`}),(0,y.jsx)(`a`,{href:`#status`,onClick:o,children:`运行状态`})]})]})}function C(e,t=0,n){if(e==null)return{children:`—`};let r={"data-count":e,children:m(e,t)};return t&&(r[`data-dec`]=t),n&&(r[`data-demo`]=n),r}var w={live:`var(--signal-live)`,offline:`var(--warn-amber)`};function T(e=window.location.hostname){return/^\d{1,3}(\.\d{1,3}){3}$/.test(e)||e.includes(`:`)}function E(e){return T(e)?`自签证书`:`公共 CA`}var D=e=>e;function O({text:e,className:t,label:n,idle:r=`复制`,done:i=`已复制`}){let[a,o]=(0,d.useState)(!1),s=(0,d.useRef)(null);(0,d.useEffect)(()=>()=>{s.current&&window.clearTimeout(s.current)},[]);let c=(0,d.useCallback)(()=>{let t=navigator.clipboard;t&&typeof t.writeText==`function`&&t.writeText(e).then(()=>{o(!0),s.current&&window.clearTimeout(s.current),s.current=window.setTimeout(()=>o(!1),1200)},()=>void 0)},[e]);return(0,y.jsx)(`button`,{className:t,type:`button`,"aria-label":n,onClick:c,children:a?i:r})}var k=`{"x":0,"y":0,"z":0,"rx":0,"ry":0,"rz":0,"fog":0.16,"dim":0.90}`,A=[`# 克隆并构建`,`git clone <仓库地址> && cd relay-station`,`npm ci --prefix server && npm run build --prefix server`,`npm ci --prefix web && npm run build --prefix web`,``,`# 启动 systemd 服务(TLS 由 nginx 终止)`,`sudo systemctl start relay`].join(`
4
4
  `);function j({status:e}){let{online:t,real:n,numbers:r}=e,i=u(),a=r.totalTokens==null?`—`:l(r.totalTokens),o=t?void 0:D({color:`var(--warn-amber)`,background:`color-mix(in srgb, var(--warn-amber) 7%, transparent)`,borderColor:`var(--warn-amber-line)`});return(0,y.jsxs)(`header`,{className:`hero`,id:`top`,"data-cam":k,children:[(0,y.jsxs)(`div`,{className:`wrap`,children:[(0,y.jsxs)(`div`,{className:`hero-grid`,children:[(0,y.jsxs)(`div`,{className:`hero-inner`,children:[(0,y.jsxs)(`div`,{className:`hero-badge reveal`,style:o,children:[(0,y.jsx)(`i`,{className:t?`dot dot-live`:`dot`,style:{color:t?w.live:w.offline}}),t?`服务运行中`:`服务离线`,` · 本窗口 tokens`,` `,(0,y.jsx)(`span`,{className:`n`,children:a}),n?null:`(示例)`]}),(0,y.jsxs)(`div`,{className:`hero-title reveal`,children:[(0,y.jsxs)(`h1`,{className:`hero-h1 display-1`,children:[(0,y.jsx)(`span`,{className:`rv-line`,children:(0,y.jsx)(`span`,{style:D({"--i":0}),children:`把自己的订阅积分`})}),(0,y.jsx)(`span`,{className:`rv-line`,children:(0,y.jsxs)(`span`,{style:D({"--i":1}),children:[`变成一个 `,(0,y.jsx)(`span`,{className:`mk grad-text`,children:`base_url`})]})})]}),(0,y.jsx)(`div`,{className:`hero-flip`,"aria-hidden":`true`,children:(0,y.jsxs)(`h1`,{className:`hero-h1 display-1`,children:[`把自己的订阅积分`,(0,y.jsx)(`br`,{}),`变成一个 `,(0,y.jsx)(`span`,{className:`mk grad-text`,children:`base_url`})]})})]}),(0,y.jsx)(`p`,{className:`hero-sub reveal`,style:D({"--i":3}),children:`部署在你自己机器上,把你的 AI 订阅积分(WorkBuddy / MiniMax / Qoder)暴露成 OpenAI 兼容接口。 IDE、聊天客户端、手机、脚本共用同一个 Key,积分消耗实时可见。`}),(0,y.jsxs)(`div`,{className:`hero-acts reveal`,style:D({"--i":4}),children:[(0,y.jsx)(O,{className:`btn btn-primary`,text:A,label:`复制部署命令`,idle:`复制部署命令`}),(0,y.jsx)(`a`,{className:`btn btn-ghost`,href:`#clients`,children:`查看客户端配置`})]}),(0,y.jsxs)(`div`,{className:`trust reveal`,style:D({"--i":5}),children:[(0,y.jsx)(`span`,{children:`只监听本机回环`}),(0,y.jsx)(`span`,{children:`外发直连上游,无中转`}),(0,y.jsx)(`span`,{children:`请求日志保存在本地`})]})]}),(0,y.jsx)(`aside`,{className:`hero-side reveal`,style:D({"--i":2}),children:(0,y.jsxs)(`div`,{className:`glass access`,children:[(0,y.jsxs)(`div`,{className:`access-h`,children:[(0,y.jsx)(`span`,{className:`eyebrow`,style:D({"--c":`var(--text-ghost)`}),children:`接入地址`}),(0,y.jsxs)(`span`,{className:t?`pill pill-live`:`pill pill-amber`,children:[(0,y.jsx)(`i`,{className:t?`dot dot-live`:`dot`}),t?`可用`:`离线`]})]}),(0,y.jsxs)(`div`,{className:`access-url`,children:[(0,y.jsx)(`code`,{children:i}),(0,y.jsx)(O,{className:`copy`,text:i,label:`复制接入地址`})]}),(0,y.jsx)(`div`,{className:`access-note`,children:`把它填进任何支持 OpenAI 兼容协议的工具`}),(0,y.jsxs)(`dl`,{className:`kv`,children:[(0,y.jsxs)(`div`,{children:[(0,y.jsx)(`dt`,{children:`协议`}),(0,y.jsx)(`dd`,{children:`OpenAI v1 · Anthropic`})]}),(0,y.jsxs)(`div`,{children:[(0,y.jsx)(`dt`,{children:`日志保留`}),(0,y.jsx)(`dd`,{children:`默认 30 天`})]}),(0,y.jsxs)(`div`,{children:[(0,y.jsx)(`dt`,{children:`TLS`}),(0,y.jsx)(`dd`,{children:E()})]}),(0,y.jsxs)(`div`,{children:[(0,y.jsx)(`dt`,{children:`部署形态`}),(0,y.jsx)(`dd`,{children:`systemd + nginx`})]})]})]})})]}),(0,y.jsxs)(`div`,{className:`panel-wrap glass reveal`,style:D({"--i":6}),children:[(0,y.jsxs)(`div`,{className:`panel-left`,children:[(0,y.jsxs)(`div`,{className:`winbar`,children:[(0,y.jsx)(`i`,{style:{background:`var(--danger-red)`}}),(0,y.jsx)(`i`,{style:{background:`var(--warn-amber)`}}),(0,y.jsx)(`i`,{style:{background:`var(--signal-live)`}}),(0,y.jsx)(`span`,{className:`fname`,children:`deploy.sh`}),(0,y.jsx)(`span`,{className:`wtag`,children:`SHELL`}),(0,y.jsx)(`span`,{className:`wsp`}),(0,y.jsx)(O,{className:`wbtn`,text:A})]}),(0,y.jsxs)(`div`,{className:`panel-code`,children:[(0,y.jsxs)(`div`,{className:`cl`,children:[(0,y.jsx)(`i`,{children:`1`}),(0,y.jsx)(`span`,{children:(0,y.jsx)(`span`,{className:`c`,children:`# Ubuntu + systemd(TLS 由 nginx 终止)`})})]}),(0,y.jsxs)(`div`,{className:`cl`,children:[(0,y.jsx)(`i`,{children:`2`}),(0,y.jsx)(`span`,{children:(0,y.jsx)(`span`,{className:`fn`,children:`$ sudo systemctl start relay`})})]}),(0,y.jsxs)(`div`,{className:`cl`,children:[(0,y.jsx)(`i`,{children:`3`}),(0,y.jsx)(`span`,{children:(0,y.jsx)(`span`,{className:`fn`,children:`$ sudo systemctl status relay`})})]}),(0,y.jsxs)(`div`,{className:`cl`,children:[(0,y.jsx)(`i`,{children:`4`}),(0,y.jsx)(`span`,{children:(0,y.jsx)(`span`,{className:`fn`,children:`$ sudo systemctl restart relay`})})]}),(0,y.jsxs)(`div`,{className:`cl hl`,children:[(0,y.jsx)(`i`,{children:`5`}),(0,y.jsx)(`span`,{children:(0,y.jsx)(`span`,{className:`c`,children:`# 首次启动打印管理口令(仅此一次)`})})]}),(0,y.jsxs)(`div`,{className:`cl`,children:[(0,y.jsx)(`i`,{children:`6`}),(0,y.jsx)(`span`,{children:(0,y.jsx)(`span`,{className:`c`,children:`# API Key 在控制台「客户端与密钥」页新建`})})]})]})]}),(0,y.jsxs)(`div`,{className:`panel-right`,children:[(0,y.jsxs)(`div`,{className:`pr-head`,children:[(0,y.jsx)(`h4`,{children:`连接状态`}),(0,y.jsx)(`span`,{className:`pr-count`,children:t?`示例 · 3 个客户端`:`服务离线`})]}),(0,y.jsxs)(`div`,{className:`conn`,children:[(0,y.jsx)(`i`,{className:`dot dot-live`,style:{color:w.live}}),(0,y.jsx)(`span`,{className:`cname`,children:`Claude Code`}),(0,y.jsx)(`span`,{className:`dev`,children:`本机`}),(0,y.jsx)(`span`,{className:`lat`,children:`12ms`}),(0,y.jsx)(`span`,{className:`pill pill-live`,children:`在线`})]}),(0,y.jsxs)(`div`,{className:`conn`,children:[(0,y.jsx)(`i`,{className:`dot dot-live`,style:{color:w.live}}),(0,y.jsx)(`span`,{className:`cname`,children:`Cherry Studio`}),(0,y.jsx)(`span`,{className:`dev`,children:`桌面`}),(0,y.jsx)(`span`,{className:`lat`,children:`48ms`}),(0,y.jsx)(`span`,{className:`pill pill-live`,children:`在线`})]}),(0,y.jsxs)(`div`,{className:`conn`,children:[(0,y.jsx)(`i`,{className:`dot`,style:{color:w.offline}}),(0,y.jsx)(`span`,{className:`cname`,children:`Cursor`}),(0,y.jsx)(`span`,{className:`dev`,children:`笔记本`}),(0,y.jsx)(`span`,{className:`lat`,children:`—`}),(0,y.jsx)(`span`,{className:`pill pill-amber`,children:`待机`})]}),(0,y.jsxs)(`div`,{className:`panel-metrics`,children:[(0,y.jsxs)(`div`,{children:[(0,y.jsx)(`div`,{className:`k`,children:`本窗口 tokens`}),(0,y.jsx)(`div`,{className:`v num live`,style:{color:w.live},children:a})]}),(0,y.jsxs)(`div`,{children:[(0,y.jsx)(`div`,{className:`k`,children:`缓存命中率`}),(0,y.jsx)(`div`,{className:`v num`,children:h(r.cacheHitRate)})]}),(0,y.jsxs)(`div`,{children:[(0,y.jsx)(`div`,{className:`k`,children:`今日请求`}),(0,y.jsx)(`div`,{className:`v num`,children:r.requestsToday==null?`—`:m(r.requestsToday)})]})]})]})]})]}),(0,y.jsxs)(`div`,{className:`scroll-cue`,children:[(0,y.jsx)(`span`,{className:`tick`}),`SCROLL`]})]})}var M=`{"x":-26,"y":-16,"z":-150,"rx":2.5,"ry":3,"rz":0,"fog":0.30,"dim":0.44}`;function N(){return(0,y.jsx)(`section`,{className:`strip`,"data-cam":M,children:(0,y.jsxs)(`div`,{className:`wrap reveal`,children:[(0,y.jsxs)(`div`,{className:`cap`,children:[`任何支持 `,(0,y.jsx)(`b`,{children:`OpenAI 兼容协议`}),` 的工具,填上地址就能用`]}),(0,y.jsxs)(`div`,{className:`logos`,children:[(0,y.jsx)(`span`,{children:`Claude Code`}),(0,y.jsx)(`span`,{children:`Cursor`}),(0,y.jsx)(`span`,{children:`Cherry Studio`}),(0,y.jsx)(`span`,{children:`ChatBox`}),(0,y.jsx)(`span`,{children:`LobeChat`}),(0,y.jsx)(`span`,{children:`Continue`}),(0,y.jsx)(`span`,{className:`cn`,children:`你的脚本`})]})]})})}var P=`{"x":-118,"y":-38,"z":-310,"rx":5,"ry":7,"rz":-1.5,"fog":0.44,"dim":0.40}`,F={color:`var(--accent-cyan)`},I={fill:`none`,stroke:`currentColor`,strokeWidth:1.6};function L(){return(0,y.jsx)(`section`,{className:`section`,id:`ways`,"data-cam":P,children:(0,y.jsx)(`div`,{className:`wrap`,children:(0,y.jsxs)(`div`,{className:`split`,children:[(0,y.jsxs)(`div`,{className:`split-l reveal`,children:[(0,y.jsxs)(`div`,{className:`eyebrow`,children:[(0,y.jsx)(`span`,{className:`idx`,children:`02`}),`运行方式`]}),(0,y.jsxs)(`h2`,{className:`display-2`,children:[`一个进程,`,(0,y.jsx)(`br`,{}),`管住所有客户端的积分`]}),(0,y.jsx)(`p`,{children:`不用为每个客户端单独登录、单独算积分。服务跑在你自己的机器上,所有请求走同一个出口。`})]}),(0,y.jsxs)(`ul`,{className:`linelist`,children:[(0,y.jsxs)(`li`,{className:`reveal`,children:[(0,y.jsx)(`span`,{className:`ll-ico`,children:(0,y.jsxs)(`svg`,{width:`20`,height:`20`,viewBox:`0 0 20 20`,"aria-hidden":`true`,style:F,...I,children:[(0,y.jsx)(`circle`,{cx:`6.5`,cy:`10`,r:`3.5`}),(0,y.jsx)(`path`,{d:`M10 10h8M15 10v3M17.5 10v3`,strokeLinecap:`round`})]})}),(0,y.jsxs)(`span`,{className:`ll-tx`,children:[(0,y.jsx)(`b`,{children:`一个 Key,多端共用`}),(0,y.jsx)(`i`,{children:`桌面客户端、IDE、手机、脚本共用同一个密钥,控制台按客户端拆开看用量。`})]})]}),(0,y.jsxs)(`li`,{className:`reveal`,style:D({"--i":1}),children:[(0,y.jsx)(`span`,{className:`ll-ico`,children:(0,y.jsxs)(`svg`,{width:`20`,height:`20`,viewBox:`0 0 20 20`,"aria-hidden":`true`,style:F,...I,children:[(0,y.jsx)(`path`,{d:`M4 10h4M12 10h4`,strokeLinecap:`round`}),(0,y.jsx)(`circle`,{cx:`10`,cy:`10`,r:`2`}),(0,y.jsx)(`path`,{d:`M12 10V5.5M12 10v4.5`,strokeLinecap:`round`})]})}),(0,y.jsxs)(`span`,{className:`ll-tx`,children:[(0,y.jsx)(`b`,{children:`请求只在你自己的机器上流转`}),(0,y.jsx)(`i`,{children:`应用默认只监听本机回环,对外由你自己的 nginx 暴露,没有第三方中转。`})]})]}),(0,y.jsxs)(`li`,{className:`reveal`,style:D({"--i":2}),children:[(0,y.jsx)(`span`,{className:`ll-ico`,children:(0,y.jsxs)(`svg`,{width:`20`,height:`20`,viewBox:`0 0 20 20`,"aria-hidden":`true`,style:F,...I,children:[(0,y.jsx)(`rect`,{x:`2`,y:`4.5`,width:`16`,height:`11`,rx:`2.5`}),(0,y.jsx)(`path`,{d:`M2 8.5h16`}),(0,y.jsx)(`path`,{d:`M4.5 13h4`,strokeLinecap:`round`})]})}),(0,y.jsxs)(`span`,{className:`ll-tx`,children:[(0,y.jsx)(`b`,{children:`用量与消耗实时同步`}),(0,y.jsx)(`i`,{children:`本窗口总 tokens、缓存命中率、每条请求消耗多少积分,控制台里一眼看清。`})]})]}),(0,y.jsxs)(`li`,{className:`reveal`,style:D({"--i":3}),children:[(0,y.jsx)(`span`,{className:`ll-ico`,children:(0,y.jsx)(`svg`,{width:`20`,height:`20`,viewBox:`0 0 20 20`,"aria-hidden":`true`,style:F,...I,children:(0,y.jsx)(`path`,{d:`M10 2.5 16.5 5v5c0 4-2.8 6.4-6.5 7.5C6.3 16.4 3.5 14 3.5 10V5L10 2.5Z`,strokeLinejoin:`round`})})}),(0,y.jsxs)(`span`,{className:`ll-tx`,children:[(0,y.jsx)(`b`,{children:`请求日志与超限保护`}),(0,y.jsx)(`i`,{children:`每条请求都落库、可回溯;超过并发上限时直接回 429,失败不会被静默吞掉。`})]})]})]})]})})})}var R=`{"x":58,"y":-70,"z":-480,"rx":10,"ry":-5,"rz":1.5,"fog":0.52,"dim":0.36}`;function z({version:e}){return(0,y.jsx)(`section`,{className:`section`,id:`quickstart`,"data-cam":R,children:(0,y.jsxs)(`div`,{className:`wrap`,children:[(0,y.jsxs)(`div`,{className:`sec-head reveal`,children:[(0,y.jsxs)(`div`,{children:[(0,y.jsxs)(`div`,{className:`eyebrow`,children:[(0,y.jsx)(`span`,{className:`idx`,children:`03`}),`快速开始`]}),(0,y.jsx)(`h2`,{className:`display-2`,style:{marginTop:20},children:`三步,十分钟内跑起来`})]}),(0,y.jsx)(`p`,{className:`lead`,children:`自己的一台机器就够。首次启动打印管理口令(只显示一次),客户端的 Key 在控制台新建。`})]}),(0,y.jsxs)(`ol`,{className:`steps`,children:[(0,y.jsxs)(`li`,{className:`reveal`,children:[(0,y.jsx)(`div`,{className:`st-n`,children:`01`}),(0,y.jsx)(`h3`,{children:`部署服务`}),(0,y.jsx)(`p`,{children:`克隆仓库,构建前后端,装成 systemd 服务(nginx 在外层终止 TLS)。首次启动即打印管理口令。`}),(0,y.jsx)(`div`,{className:`cmd`,style:{marginTop:8},children:`git clone … → npm ci → npm run build → systemctl start`})]}),(0,y.jsxs)(`li`,{className:`reveal`,style:D({"--i":1}),children:[(0,y.jsx)(`div`,{className:`st-n`,children:`02`}),(0,y.jsx)(`h3`,{children:`生成密钥`}),(0,y.jsx)(`p`,{children:`在控制台「客户端与密钥」页新建密钥,按客户端命名,可以随时停用或删除。`}),(0,y.jsx)(`div`,{className:`cmd`,children:`控制台 → 客户端与密钥 → 新建密钥`})]}),(0,y.jsxs)(`li`,{className:`reveal`,style:D({"--i":2}),children:[(0,y.jsx)(`div`,{className:`st-n`,children:`03`}),(0,y.jsx)(`h3`,{children:`填进客户端`}),(0,y.jsx)(`p`,{children:`把 base_url 填成控制台的同源地址,模型名照抄,其余代码不用改。`})]})]}),(0,y.jsxs)(`div`,{className:`term reveal`,children:[(0,y.jsxs)(`div`,{className:`term-bar`,children:[(0,y.jsx)(`i`,{className:`dot dot-live`,style:{color:`var(--signal-live)`}}),(0,y.jsx)(`span`,{children:`首次启动输出`}),(0,y.jsxs)(`span`,{className:`term-tag`,children:[`relay · v`,e]})]}),(0,y.jsxs)(`div`,{className:`term-body`,children:[(0,y.jsxs)(`div`,{className:`tl`,children:[(0,y.jsx)(`span`,{className:`pr`,children:`$`}),` sudo systemctl start relay`]}),(0,y.jsx)(`div`,{className:`tl out`,children:`Waygate · 首次启动,已生成管理口令(此口令只显示这一次)`}),(0,y.jsxs)(`div`,{className:`tl out`,children:[`口令:`,(0,y.jsx)(`span`,{className:`hl`,children:`••••••••••••••••`})]}),(0,y.jsx)(`div`,{className:`tl out`,children:`请立即复制保存。忘记口令时:控制台「设置 → 管理口令」重置。`}),(0,y.jsx)(`div`,{className:`tl ok`,children:`Waygate 已在 http://127.0.0.1:8787 就绪(默认仅回环,不对公网直接暴露)`})]})]})]})})}var B=`{"x":148,"y":-94,"z":-630,"rx":6,"ry":-11,"rz":-2.5,"fog":0.60,"dim":0.34}`,V=`sk-waygate-4f9c2a7e1b83d04f391c7a2e`,H=u(),U=u().replace(/\/v1$/,``),W=[{k:`兼容协议`,v:`OpenAI v1 · Anthropic`},{k:`接入方式`,v:`HTTPS · ${E()}`},{k:`统计口径`,v:`本机日志聚合`},{k:`日志存储`,v:`本地`}];function G(){return(0,y.jsx)(`section`,{className:`section`,id:`clients`,"data-cam":B,children:(0,y.jsxs)(`div`,{className:`wrap`,children:[(0,y.jsxs)(`div`,{className:`sec-head reveal`,children:[(0,y.jsxs)(`div`,{children:[(0,y.jsxs)(`div`,{className:`eyebrow`,children:[(0,y.jsx)(`span`,{className:`idx`,children:`04`}),`客户端配置`]}),(0,y.jsx)(`h2`,{className:`display-2`,style:{marginTop:20},children:`四处填好,立刻能用`})]}),(0,y.jsx)(`p`,{className:`lead`,children:`所有客户端都只需要一个地址加一个密钥。下面是最常见的四种接法。`})]}),(0,y.jsxs)(`div`,{className:`grid-2`,children:[(0,y.jsxs)(`div`,{className:`glass glass-soft cfg reveal`,children:[(0,y.jsx)(`h3`,{children:`编程工具 / IDE`}),(0,y.jsx)(`div`,{className:`sub`,children:`Continue · Cline · 任意 OpenAI 兼容插件`}),(0,y.jsxs)(`div`,{className:`code`,children:[`OPENAI_BASE_URL=`,(0,y.jsx)(`span`,{className:`s`,children:H}),`
@@ -0,0 +1 @@
1
+ import{B as e,N as t,_ as n,i as r,it as i,r as a,tt as o}from"./vendor-befEYGHM.js";import{O as s,_ as c,c as l,g as u,j as d,k as f,p,r as m,v as h,x as g}from"./index-Bb8UvXuA.js";import{a as _,c as v,i as y,n as b,r as x,s as S,t as C}from"./Select-CE8etafp.js";import{r as w}from"./PageSkeleton-DG-Dj4_9.js";import{n as T,t as E}from"./sharedWindow-FDfq75PI.js";import{t as D}from"./useFx-xcrp_LHB.js";import{n as O,t as k}from"./ClientMetricTip-Doe5BMQH.js";var A=i(),j=o();function M({navigate:i}){let[o,M]=(0,A.useState)(()=>E()??`today`),N=d(()=>l.overview(o),[o],{cacheKey:`overview:${o}`});D(`overview-${o}-${N.data?`ready`:`pending`}`);let P=N.data,F={today:(()=>{let e=new Date;return e.getHours()*60+e.getMinutes()||1})(),"1h":60,"24h":1440,"7d":10080,"30d":43200},I=P?F[P.window]:null,L=P&&I?P.metrics.requests/I:null,R=P&&I?P.metrics.totalTokens/I:null,z=e=>{if(!P||P.byClient.length===0)return;let t=[...P.byClient].sort((t,n)=>(n[e]??0)-(t[e]??0));return(0,j.jsx)(k,{rows:t,metric:e,windowLabel:s[P.window],total:e===`credits`?P.metrics.creditsUsed:P.metrics.requests,totalCount:t.length})};return(0,j.jsxs)(`div`,{className:`view on`,children:[(0,j.jsx)(S,{title:`概览`,status:P?(0,j.jsxs)(v,{tone:`live`,dot:!0,children:[`本机日志聚合 · `,h(P.generatedAt)]}):null,actions:(0,j.jsxs)(j.Fragment,{children:[(0,j.jsx)(C,{value:o,options:f,onChange:e=>{T(e),M(e)},width:148}),(0,j.jsxs)(`button`,{type:`button`,className:`btn btn-primary btn-sm`,onClick:()=>i(`keys`),children:[(0,j.jsx)(n,{size:14}),`新建密钥`]})]})}),(0,j.jsx)(`div`,{className:`view-body stack`,children:N.initial&&N.loading?(0,j.jsx)(w,{}):N.error?(0,j.jsx)(_,{children:(0,j.jsx)(b,{kind:`error`,title:`概览数据没取到`,detail:x(N.error),onRetry:N.reload})}):P?(0,j.jsxs)(j.Fragment,{children:[P.alerts.channelReject.count>=P.alerts.channelReject.threshold?(0,j.jsxs)(m,{tone:`warn`,children:[(0,j.jsx)(a,{size:14,"aria-hidden":!0}),`最近 1 小时出现 `,(0,j.jsxs)(`b`,{children:[P.alerts.channelReject.count,` 次`]}),`上游渠道校验拒绝(11128): 请求的 system 提示命中了上游的第三方客户端指纹。本服务会自动改写模板行、被拒时降级重试, 连续失败说明出现了新的指纹变体。请到「请求日志」筛 note 含 upstream_11128 的行排查。`]}):null,P.alerts.credentialInvalid?.count!=null&&P.alerts.credentialInvalid.count>=P.alerts.credentialInvalid.threshold?(0,j.jsxs)(m,{tone:`error`,actions:(0,j.jsx)(`a`,{href:`#/clients`,className:`btn btn-quiet btn-sm`,children:`去提供商管理`}),children:[(0,j.jsx)(a,{size:14,"aria-hidden":!0}),`最近 1 小时出现 `,(0,j.jsxs)(`b`,{children:[P.alerts.credentialInvalid.count,` 次`]}),`上游访问密钥失效: 所有请求将以 502 失败,直到换新为止。`]}):null,(0,j.jsx)(O,{metrics:P.metrics}),(0,j.jsxs)(`div`,{className:`metric-row`,children:[(0,j.jsx)(y,{label:`已消耗积分`,value:p(P.metrics.creditsUsed),detail:P.metrics.creditsUnknownCount>0?`上游实扣 · 另 ${u(P.metrics.creditsUnknownCount)} 条上游未回报,未计入`:`上游实扣 · 全部请求均已回报`,tone:`amber`,icon:(0,j.jsx)(e,{size:14}),tip:z(`credits`),tipPos:`down`}),(0,j.jsx)(y,{label:`平均 RPM`,value:L===null?`—`:u(Math.round(L)),detail:`每分钟请求 · ${s[P.window]}`,tone:`violet`,icon:(0,j.jsx)(t,{size:14}),tip:z(`requests`),tipPos:`down`}),(0,j.jsx)(y,{label:`平均 TPM`,value:R===null?`—`:g(Math.round(R)),detail:`每分钟 tokens · ${s[P.window]}`,tone:`green`,icon:(0,j.jsx)(r,{size:14}),tip:z(`tokens`),tipPos:`down`})]}),(0,j.jsx)(_,{title:`提供商用量摘要`,aside:(0,j.jsxs)(v,{children:[P.byProvider.length,` 个提供商 · 按 Token 排序`]}),children:P.byProvider.length>0?(0,j.jsx)(`div`,{className:`tbl-scroll`,children:(0,j.jsxs)(`table`,{className:`tbl`,children:[(0,j.jsxs)(`caption`,{className:`sr-only`,children:[`提供商用量摘要,共 `,P.byProvider.length,` 个提供商`]}),(0,j.jsx)(`thead`,{children:(0,j.jsxs)(`tr`,{children:[(0,j.jsx)(`th`,{scope:`col`,children:`提供商`}),(0,j.jsx)(`th`,{scope:`col`,className:`r`,children:`请求次数`}),(0,j.jsx)(`th`,{scope:`col`,className:`r`,children:`Token 消耗`}),(0,j.jsx)(`th`,{scope:`col`,className:`r`,children:`消耗积分`}),(0,j.jsx)(`th`,{scope:`col`,className:`r`,children:`Token 占比`}),(0,j.jsx)(`th`,{scope:`col`,children:`分布`})]})}),(0,j.jsx)(`tbody`,{children:[...P.byProvider].sort((e,t)=>(t.tokens??0)-(e.tokens??0)).slice(0,6).map(e=>{let t=P&&P.metrics.totalTokens>0?(e.tokens??0)/P.metrics.totalTokens:null;return(0,j.jsxs)(`tr`,{children:[(0,j.jsx)(`td`,{className:`td-strong`,children:(0,j.jsx)(`span`,{children:e.provider??`未归属`})}),(0,j.jsx)(`td`,{className:`r td-mono`,children:u(e.requests)}),(0,j.jsx)(`td`,{className:`r td-mono`,children:g(e.tokens??0)}),(0,j.jsx)(`td`,{className:`r td-mono`,children:p(e.credits)}),(0,j.jsx)(`td`,{className:`r td-mono`,children:c(t)}),(0,j.jsx)(`td`,{children:(0,j.jsx)(`div`,{className:`ov-bar-track`,children:(0,j.jsx)(`div`,{className:`ov-bar-fill`,style:{width:`${Math.max(2,Math.round((t??0)*100))}%`}})})})]},e.providerId??`__unattributed__`)})})]})}):(0,j.jsxs)(`div`,{className:`empty-note`,style:{padding:`24px 0`},children:[s[P.window],`没有提供商消耗记录。`]})}),(0,j.jsxs)(_,{title:`模型用量摘要`,aside:(0,j.jsxs)(v,{children:[P.byModel.length,` 个模型 · 按 Token 排序`]}),children:[P.byModel.length>0?(0,j.jsx)(`div`,{className:`tbl-scroll`,children:(0,j.jsxs)(`table`,{className:`tbl`,children:[(0,j.jsxs)(`caption`,{className:`sr-only`,children:[`模型用量摘要,共 `,P.byModel.length,` 个模型`]}),(0,j.jsx)(`thead`,{children:(0,j.jsxs)(`tr`,{children:[(0,j.jsx)(`th`,{scope:`col`,children:`模型`}),(0,j.jsx)(`th`,{scope:`col`,className:`r`,children:`请求次数`}),(0,j.jsx)(`th`,{scope:`col`,className:`r`,children:`Token 消耗`}),(0,j.jsx)(`th`,{scope:`col`,className:`r`,children:`消耗积分`}),(0,j.jsx)(`th`,{scope:`col`,className:`r`,children:`Token 占比`}),(0,j.jsx)(`th`,{scope:`col`,children:`分布`})]})}),(0,j.jsx)(`tbody`,{children:[...P.byModel].sort((e,t)=>(t.tokens??0)-(e.tokens??0)).slice(0,8).map(e=>{let t=e.displayName??e.model??`未知`;return(0,j.jsxs)(`tr`,{children:[(0,j.jsx)(`td`,{className:`td-strong`,children:t}),(0,j.jsx)(`td`,{className:`r td-mono`,children:u(e.requests)}),(0,j.jsx)(`td`,{className:`r td-mono`,children:g(e.tokens??0)}),(0,j.jsx)(`td`,{className:`r td-mono`,children:p(e.credits)}),(0,j.jsx)(`td`,{className:`r td-mono`,children:c(e.share)}),(0,j.jsx)(`td`,{children:(0,j.jsx)(`div`,{className:`ov-bar-track`,children:(0,j.jsx)(`div`,{className:`ov-bar-fill`,style:{width:`${Math.max(2,Math.round((e.share??0)*100))}%`}})})})]},e.model??t)})})]})}):(0,j.jsxs)(`div`,{className:`empty-note`,style:{padding:`24px 0`},children:[s[P.window],`没有模型消耗记录。`]}),(0,j.jsx)(`div`,{className:`footer-note`,children:`这一页只留结论。详细构成去「积分与用量」,逐条请求去「请求日志」。`})]}),(0,j.jsx)(`div`,{className:`footer-note`,style:{marginTop:0},children:P.provenance.note})]}):null})]})}export{M as OverviewPage};
@@ -1,4 +1,4 @@
1
- import{B as e,C as t,D as n,G as r,K as i,M as a,N as o,R as s,V as c,W as l,X as u,Z as d,et as f,it as p,j as m,o as h,q as g,tt as _,u as ee}from"./vendor-befEYGHM.js";import{C as v,E as y,M as te,S as b,T as x,_ as ne,a as S,b as C,c as w,f as T,g as E,h as D,j as O,l as k,m as A,p as j,r as re,w as M}from"./index-BJtz6Ses.js";import{a as N,c as P,n as F,o as ie,r as I,s as ae,t as L}from"./Select-HrLgXSri.js";import{i as oe}from"./PageSkeleton-DG-Dj4_9.js";import{t as se}from"./Drawer-CleQGhRU.js";import{t as ce}from"./useFx-xcrp_LHB.js";var R=p();function le(e,t=320){let[n,r]=(0,R.useState)(e);return(0,R.useEffect)(()=>{let n=window.setTimeout(()=>r(e),t);return()=>window.clearTimeout(n)},[e,t]),n}var z=_(),B=[20,50,100];function ue({page:e,limit:t,total:n,onPage:a,onLimit:o}){let s=Math.max(1,Math.ceil(n/t)),c=[],l=e=>{c[c.length-1]!==e&&c.push(e)},u=[1,e-1,e,e+1,s].filter(e=>e>=1&&e<=s).sort((e,t)=>e-t),d=0;return u.forEach(e=>{e-d>1&&l(`…`),l(e),d=e}),(0,z.jsxs)(`div`,{className:`pager`,children:[(0,z.jsxs)(`span`,{className:`txt`,children:[`第 `,e,` / `,s,` 页 · 每页 `,t,` 条,共 `,E(n),` 条`]}),(0,z.jsxs)(`span`,{className:`btns`,children:[(0,z.jsx)(L,{value:String(t),options:B.map(e=>({value:String(e),label:`${e} / 页`})),onChange:e=>o(Number(e)),size:`sm`,width:92,ariaLabel:`每页条数`}),(0,z.jsx)(`button`,{type:`button`,className:`pg`,disabled:e<=1,onClick:()=>a(e-1),"aria-label":`上一页`,children:(0,z.jsx)(r,{size:13})}),c.map((t,n)=>t===`…`?(0,z.jsx)(`span`,{className:`pg`,style:{cursor:`default`,background:`transparent`,border:`none`},children:`…`},`gap-${n}`):(0,z.jsx)(`button`,{type:`button`,className:`pg${t===e?` on`:``}`,onClick:()=>a(t),children:t},t)),(0,z.jsx)(`button`,{type:`button`,className:`pg`,disabled:e>=s,onClick:()=>a(e+1),"aria-label":`下一页`,children:(0,z.jsx)(i,{size:13})})]})]})}function V(e){let t=e.toLowerCase();return t.includes(`python`)||t.includes(`httpx`)||t.includes(`aiohttp`)?`python`:t.includes(`node`)||t.includes(`deno`)||t.includes(`bun`)||t.includes(`axios`)||t.includes(`undici`)?`node`:t.includes(`curl`)||t.includes(`wget`)||t.includes(`http`)?`terminal`:t.includes(`sdk`)?`sdk`:`cli`}var H=`M11.9 0C8.7 0 8.4 1.7 8.4 1.7v2.4h3.6v.7H5.3S2.4 4.5 2.4 8.4c0 3.8 2.5 3.7 2.5 3.7h1.5v-2.2s-.1-2.5 2.6-2.5h3.6s2.6 0 2.6-2.5V2.1S15.4 0 11.9 0zM9.3 1.5a.9.9 0 1 1 0 1.7.9.9 0 0 1 0-1.7zm6.3 3.4v2.3s.1 2.6-2.6 2.6H9.4s-2.5 0-2.5 2.5v4.4s-.2 2.7 4.8 2.7 4.7-2.3 4.7-2.3v-2.1h-3.6v-.7h6.7s3.2.3 3.2-3.7c0-4-2.8-3.8-2.8-3.8h-1.6v2.3s.1 2.6-2.6 2.6h-3.5s-2.4 0-2.4 2.4v4.1s-.4 2.4 4.4 2.4 4.5-2.1 4.5-2.1v-2h-3.4v-.7h6.6s3.4.4 3.4-3.9c0-4.3-2.9-4.1-2.9-4.1zM14.6 21.9a.9.9 0 1 1 0 1.8.9.9 0 0 1 0-1.8z`,U=`M12 1.5c-.3 0-.6.1-.9.3L3.4 6.4c-.5.3-.9.9-.9 1.5v8.2c0 .6.4 1.2.9 1.5l2 1.2c1 .5 1.8.1 1.8-1v-9c0-.7.5-1.2 1.2-1.2h7c.7 0 1.2.5 1.2 1.2v9c0 1.1.8 1.5 1.8 1l2-1.2c.5-.3.9-.9.9-1.5V7.9c0-.6-.4-1.2-.9-1.5L12.9 1.8c-.3-.2-.6-.3-.9-.3zm5.1 6c-.6 0-1.1.5-1.1 1.1v6.8c0 .6.5 1.1 1.1 1.1h5.2l-1.9 3.3 2.7-1.6c.5-.3.9-.9.9-1.5V7.9c0-.6-.4-1.2-.9-1.5l-7.7-4.6`,W=`M5 7l5 5-5 5m7 0h7`,G=`M14.7 6.3a5 5 0 0 0-6.9 6l-6.1 6.1a2 2 0 1 0 2.8 2.8l6.1-6.1a5 5 0 0 0 6-6.9l-3.2 3.2-2.8-2.8 3.1-3.1z`,K=`M8 6l-6 6 6 6m8-12l6 6-6 6`;function q({tool:e,size:t=14,style:n}){let r=e?V(e):`none`;if(r===`none`)return(0,z.jsxs)(`svg`,{width:t,height:t,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`1.8`,strokeLinecap:`round`,strokeLinejoin:`round`,className:`ua-ico ${r}`,style:n,"aria-hidden":`true`,children:[(0,z.jsx)(`circle`,{cx:`12`,cy:`12`,r:`10`}),(0,z.jsx)(`path`,{d:`M2 12h20`}),(0,z.jsx)(`path`,{d:`M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z`})]});if(r===`python`||r===`node`){let e=r===`python`?H:U;return(0,z.jsx)(`svg`,{width:t,height:t,viewBox:`0 0 24 24`,fill:`currentColor`,className:`ua-ico ${r}`,style:n,"aria-hidden":`true`,children:(0,z.jsx)(`path`,{d:e})})}let i=r===`terminal`?W:r===`sdk`?G:K;return(0,z.jsx)(`svg`,{width:t,height:t,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`1.8`,strokeLinecap:`round`,strokeLinejoin:`round`,className:`ua-ico ${r}`,style:n,"aria-hidden":`true`,children:(0,z.jsx)(`path`,{d:i})})}var J={live:`var(--text-tertiary)`,warn:`var(--warn-amber)`,fatal:`var(--danger-red)`,muted:`var(--text-faint)`},Y={live:`var(--signal-live)`,warn:`var(--warn-amber)`,fatal:`var(--danger-red)`,muted:`var(--text-faint)`};function X(e,t){if(!e)return!1;let n=e=>e.toLowerCase().replace(/[^a-z0-9]/g,``),r=n(e.trim().split(/[\s/]/)[0]??``);return r?t.split(/[\s.\-_/]+/).map(n).filter(e=>e.length>=3).some(e=>r.includes(e)):!1}function Z(e){let t=new Date(e);return Number.isNaN(t.getTime())?`--`:t.toLocaleDateString(`zh-CN`)}function de({rows:t,activeId:r,onOpen:l,resolveClientName:u,modelTagOf:d,providerOfModel:p}){return(0,z.jsx)(`div`,{className:`tbl-scroll`,children:(0,z.jsxs)(`table`,{className:`tbl`,children:[(0,z.jsxs)(`caption`,{className:`sr-only`,children:[`请求日志,共 `,t.length,` 条当前筛选结果`]}),(0,z.jsx)(`thead`,{children:(0,z.jsxs)(`tr`,{children:[(0,z.jsx)(`th`,{"aria-hidden":`true`}),(0,z.jsxs)(`th`,{scope:`col`,children:[(0,z.jsx)(c,{size:12}),`请求时间`]}),(0,z.jsxs)(`th`,{scope:`col`,children:[(0,z.jsx)(n,{size:12}),`令牌`]}),(0,z.jsxs)(`th`,{scope:`col`,children:[(0,z.jsx)(s,{size:12}),`模型 / 提供商`]}),(0,z.jsxs)(`th`,{scope:`col`,children:[(0,z.jsx)(m,{size:12}),`User Agent`]}),(0,z.jsxs)(`th`,{scope:`col`,className:`r`,children:[(0,z.jsx)(f,{size:12}),`状态`]}),(0,z.jsxs)(`th`,{scope:`col`,className:`r`,children:[(0,z.jsx)(h,{size:12}),`耗时`]}),(0,z.jsxs)(`th`,{scope:`col`,className:`r`,children:[(0,z.jsx)(o,{size:12}),`输出速度`]}),(0,z.jsxs)(`th`,{scope:`col`,className:`r`,children:[(0,z.jsx)(e,{size:12}),`Tokens`]}),(0,z.jsxs)(`th`,{scope:`col`,className:`r`,children:[(0,z.jsx)(a,{size:12}),`积分`]})]})}),(0,z.jsx)(`tbody`,{children:t.map(e=>{let t=x(e.status,e.note),n=v(e.note),a=e.modelDisplay||e.model;return(0,z.jsxs)(`tr`,{onClick:()=>l(e),className:r===e.id?`row-on`:void 0,style:{cursor:`pointer`,background:r===e.id?`var(--brand-tint)`:t===`fatal`?`color-mix(in srgb, var(--danger-red) 5%, transparent)`:void 0},children:[(0,z.jsx)(`td`,{className:`row-x-td`,children:(0,z.jsx)(`button`,{type:`button`,className:`row-open`,"aria-label":`打开 ${Z(e.ts)} ${T(e.ts)} 的请求详情`,onClick:t=>{t.stopPropagation(),l(e)},children:(0,z.jsx)(i,{size:14,className:`row-x`,"aria-hidden":`true`})})}),(0,z.jsxs)(`td`,{children:[(0,z.jsx)(`div`,{className:`cell-date`,children:Z(e.ts)}),(0,z.jsx)(`div`,{className:`td-mono`,style:{color:`var(--text-ghost)`},children:T(e.ts)})]}),(0,z.jsx)(`td`,{className:`td-mono`,style:{color:`var(--text-tertiary)`},children:(0,z.jsx)(S,{content:`密钥 ${e.keyHint}`,placement:`below`,children:(0,z.jsx)(`span`,{children:e.keyHint})})}),(0,z.jsxs)(`td`,{children:[(0,z.jsx)(`div`,{className:`td-strong`,children:a}),(0,z.jsxs)(`div`,{className:`model-meta`,children:[(0,z.jsx)(S,{content:`提供商:${p(e.model)??u(e)}(按模型目录归属解析)`,children:(0,z.jsx)(`span`,{className:`prov-pill`,children:p(e.model)??u(e)})}),d(e.model)?(0,z.jsx)(`span`,{className:`mu-tag`,children:d(e.model)}):null]})]}),(0,z.jsx)(`td`,{className:`td-ua`,children:e.ua?(0,z.jsx)(S,{content:e.ua,children:(0,z.jsxs)(`span`,{style:{display:`block`},children:[(0,z.jsxs)(`span`,{className:`ua-raw`,children:[(0,z.jsx)(q,{tool:e.tool,size:14,style:{marginRight:5,verticalAlign:-2}}),e.ua]}),e.tool&&!X(e.ua,e.tool)?(0,z.jsx)(`div`,{className:`ua-tool`,children:e.tool}):e.tool?null:(0,z.jsx)(`div`,{className:`ua-tool`,children:(0,z.jsx)(`span`,{style:{color:`var(--text-ghost)`},children:`未识别`})})]})}):(0,z.jsxs)(z.Fragment,{children:[(0,z.jsx)(`span`,{className:`ua-raw`,style:{color:`var(--text-ghost)`},children:`未采集`}),e.tool?(0,z.jsx)(`div`,{className:`ua-tool`,children:e.tool}):(0,z.jsx)(`div`,{className:`ua-tool`,children:(0,z.jsx)(`span`,{style:{color:`var(--text-ghost)`},children:`未识别`})})]})}),(0,z.jsx)(`td`,{className:`r td-mono`,style:{color:J[t]},children:n.length>0?(0,z.jsx)(S,{content:n.join(` · `),children:(0,z.jsxs)(`span`,{children:[(0,z.jsx)(`span`,{className:`dotc`,style:{background:Y[t]},"aria-hidden":`true`}),e.status]})}):(0,z.jsxs)(z.Fragment,{children:[(0,z.jsx)(`span`,{className:`dotc`,style:{background:Y[t]},"aria-hidden":`true`}),e.status]})}),(0,z.jsxs)(`td`,{className:`r td-mono`,children:[(0,z.jsx)(`div`,{children:(0,z.jsx)(S,{content:`首字耗时 —— 从发出请求到收到第一个内容块的等待
1
+ import{B as e,C as t,D as n,G as r,K as i,M as a,N as o,R as s,V as c,W as l,X as u,Z as d,et as f,it as p,j as m,o as h,q as g,tt as _,u as ee}from"./vendor-befEYGHM.js";import{C as v,E as y,M as te,S as b,T as x,_ as ne,a as S,b as C,c as w,f as T,g as E,h as D,j as O,l as k,m as A,p as j,r as re,w as M}from"./index-Bb8UvXuA.js";import{a as N,c as P,n as F,o as ie,r as I,s as ae,t as L}from"./Select-CE8etafp.js";import{i as oe}from"./PageSkeleton-DG-Dj4_9.js";import{t as se}from"./Drawer-CleQGhRU.js";import{t as ce}from"./useFx-xcrp_LHB.js";var R=p();function le(e,t=320){let[n,r]=(0,R.useState)(e);return(0,R.useEffect)(()=>{let n=window.setTimeout(()=>r(e),t);return()=>window.clearTimeout(n)},[e,t]),n}var z=_(),B=[20,50,100];function ue({page:e,limit:t,total:n,onPage:a,onLimit:o}){let s=Math.max(1,Math.ceil(n/t)),c=[],l=e=>{c[c.length-1]!==e&&c.push(e)},u=[1,e-1,e,e+1,s].filter(e=>e>=1&&e<=s).sort((e,t)=>e-t),d=0;return u.forEach(e=>{e-d>1&&l(`…`),l(e),d=e}),(0,z.jsxs)(`div`,{className:`pager`,children:[(0,z.jsxs)(`span`,{className:`txt`,children:[`第 `,e,` / `,s,` 页 · 每页 `,t,` 条,共 `,E(n),` 条`]}),(0,z.jsxs)(`span`,{className:`btns`,children:[(0,z.jsx)(L,{value:String(t),options:B.map(e=>({value:String(e),label:`${e} / 页`})),onChange:e=>o(Number(e)),size:`sm`,width:92,ariaLabel:`每页条数`}),(0,z.jsx)(`button`,{type:`button`,className:`pg`,disabled:e<=1,onClick:()=>a(e-1),"aria-label":`上一页`,children:(0,z.jsx)(r,{size:13})}),c.map((t,n)=>t===`…`?(0,z.jsx)(`span`,{className:`pg`,style:{cursor:`default`,background:`transparent`,border:`none`},children:`…`},`gap-${n}`):(0,z.jsx)(`button`,{type:`button`,className:`pg${t===e?` on`:``}`,onClick:()=>a(t),children:t},t)),(0,z.jsx)(`button`,{type:`button`,className:`pg`,disabled:e>=s,onClick:()=>a(e+1),"aria-label":`下一页`,children:(0,z.jsx)(i,{size:13})})]})]})}function V(e){let t=e.toLowerCase();return t.includes(`python`)||t.includes(`httpx`)||t.includes(`aiohttp`)?`python`:t.includes(`node`)||t.includes(`deno`)||t.includes(`bun`)||t.includes(`axios`)||t.includes(`undici`)?`node`:t.includes(`curl`)||t.includes(`wget`)||t.includes(`http`)?`terminal`:t.includes(`sdk`)?`sdk`:`cli`}var H=`M11.9 0C8.7 0 8.4 1.7 8.4 1.7v2.4h3.6v.7H5.3S2.4 4.5 2.4 8.4c0 3.8 2.5 3.7 2.5 3.7h1.5v-2.2s-.1-2.5 2.6-2.5h3.6s2.6 0 2.6-2.5V2.1S15.4 0 11.9 0zM9.3 1.5a.9.9 0 1 1 0 1.7.9.9 0 0 1 0-1.7zm6.3 3.4v2.3s.1 2.6-2.6 2.6H9.4s-2.5 0-2.5 2.5v4.4s-.2 2.7 4.8 2.7 4.7-2.3 4.7-2.3v-2.1h-3.6v-.7h6.7s3.2.3 3.2-3.7c0-4-2.8-3.8-2.8-3.8h-1.6v2.3s.1 2.6-2.6 2.6h-3.5s-2.4 0-2.4 2.4v4.1s-.4 2.4 4.4 2.4 4.5-2.1 4.5-2.1v-2h-3.4v-.7h6.6s3.4.4 3.4-3.9c0-4.3-2.9-4.1-2.9-4.1zM14.6 21.9a.9.9 0 1 1 0 1.8.9.9 0 0 1 0-1.8z`,U=`M12 1.5c-.3 0-.6.1-.9.3L3.4 6.4c-.5.3-.9.9-.9 1.5v8.2c0 .6.4 1.2.9 1.5l2 1.2c1 .5 1.8.1 1.8-1v-9c0-.7.5-1.2 1.2-1.2h7c.7 0 1.2.5 1.2 1.2v9c0 1.1.8 1.5 1.8 1l2-1.2c.5-.3.9-.9.9-1.5V7.9c0-.6-.4-1.2-.9-1.5L12.9 1.8c-.3-.2-.6-.3-.9-.3zm5.1 6c-.6 0-1.1.5-1.1 1.1v6.8c0 .6.5 1.1 1.1 1.1h5.2l-1.9 3.3 2.7-1.6c.5-.3.9-.9.9-1.5V7.9c0-.6-.4-1.2-.9-1.5l-7.7-4.6`,W=`M5 7l5 5-5 5m7 0h7`,G=`M14.7 6.3a5 5 0 0 0-6.9 6l-6.1 6.1a2 2 0 1 0 2.8 2.8l6.1-6.1a5 5 0 0 0 6-6.9l-3.2 3.2-2.8-2.8 3.1-3.1z`,K=`M8 6l-6 6 6 6m8-12l6 6-6 6`;function q({tool:e,size:t=14,style:n}){let r=e?V(e):`none`;if(r===`none`)return(0,z.jsxs)(`svg`,{width:t,height:t,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`1.8`,strokeLinecap:`round`,strokeLinejoin:`round`,className:`ua-ico ${r}`,style:n,"aria-hidden":`true`,children:[(0,z.jsx)(`circle`,{cx:`12`,cy:`12`,r:`10`}),(0,z.jsx)(`path`,{d:`M2 12h20`}),(0,z.jsx)(`path`,{d:`M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z`})]});if(r===`python`||r===`node`){let e=r===`python`?H:U;return(0,z.jsx)(`svg`,{width:t,height:t,viewBox:`0 0 24 24`,fill:`currentColor`,className:`ua-ico ${r}`,style:n,"aria-hidden":`true`,children:(0,z.jsx)(`path`,{d:e})})}let i=r===`terminal`?W:r===`sdk`?G:K;return(0,z.jsx)(`svg`,{width:t,height:t,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`1.8`,strokeLinecap:`round`,strokeLinejoin:`round`,className:`ua-ico ${r}`,style:n,"aria-hidden":`true`,children:(0,z.jsx)(`path`,{d:i})})}var J={live:`var(--text-tertiary)`,warn:`var(--warn-amber)`,fatal:`var(--danger-red)`,muted:`var(--text-faint)`},Y={live:`var(--signal-live)`,warn:`var(--warn-amber)`,fatal:`var(--danger-red)`,muted:`var(--text-faint)`};function X(e,t){if(!e)return!1;let n=e=>e.toLowerCase().replace(/[^a-z0-9]/g,``),r=n(e.trim().split(/[\s/]/)[0]??``);return r?t.split(/[\s.\-_/]+/).map(n).filter(e=>e.length>=3).some(e=>r.includes(e)):!1}function Z(e){let t=new Date(e);return Number.isNaN(t.getTime())?`--`:t.toLocaleDateString(`zh-CN`)}function de({rows:t,activeId:r,onOpen:l,resolveClientName:u,modelTagOf:d,providerOfModel:p}){return(0,z.jsx)(`div`,{className:`tbl-scroll`,children:(0,z.jsxs)(`table`,{className:`tbl`,children:[(0,z.jsxs)(`caption`,{className:`sr-only`,children:[`请求日志,共 `,t.length,` 条当前筛选结果`]}),(0,z.jsx)(`thead`,{children:(0,z.jsxs)(`tr`,{children:[(0,z.jsx)(`th`,{"aria-hidden":`true`}),(0,z.jsxs)(`th`,{scope:`col`,children:[(0,z.jsx)(c,{size:12}),`请求时间`]}),(0,z.jsxs)(`th`,{scope:`col`,children:[(0,z.jsx)(n,{size:12}),`令牌`]}),(0,z.jsxs)(`th`,{scope:`col`,children:[(0,z.jsx)(s,{size:12}),`模型 / 提供商`]}),(0,z.jsxs)(`th`,{scope:`col`,children:[(0,z.jsx)(m,{size:12}),`User Agent`]}),(0,z.jsxs)(`th`,{scope:`col`,className:`r`,children:[(0,z.jsx)(f,{size:12}),`状态`]}),(0,z.jsxs)(`th`,{scope:`col`,className:`r`,children:[(0,z.jsx)(h,{size:12}),`耗时`]}),(0,z.jsxs)(`th`,{scope:`col`,className:`r`,children:[(0,z.jsx)(o,{size:12}),`输出速度`]}),(0,z.jsxs)(`th`,{scope:`col`,className:`r`,children:[(0,z.jsx)(e,{size:12}),`Tokens`]}),(0,z.jsxs)(`th`,{scope:`col`,className:`r`,children:[(0,z.jsx)(a,{size:12}),`积分`]})]})}),(0,z.jsx)(`tbody`,{children:t.map(e=>{let t=x(e.status,e.note),n=v(e.note),a=e.modelDisplay||e.model;return(0,z.jsxs)(`tr`,{onClick:()=>l(e),className:r===e.id?`row-on`:void 0,style:{cursor:`pointer`,background:r===e.id?`var(--brand-tint)`:t===`fatal`?`color-mix(in srgb, var(--danger-red) 5%, transparent)`:void 0},children:[(0,z.jsx)(`td`,{className:`row-x-td`,children:(0,z.jsx)(`button`,{type:`button`,className:`row-open`,"aria-label":`打开 ${Z(e.ts)} ${T(e.ts)} 的请求详情`,onClick:t=>{t.stopPropagation(),l(e)},children:(0,z.jsx)(i,{size:14,className:`row-x`,"aria-hidden":`true`})})}),(0,z.jsxs)(`td`,{children:[(0,z.jsx)(`div`,{className:`cell-date`,children:Z(e.ts)}),(0,z.jsx)(`div`,{className:`td-mono`,style:{color:`var(--text-ghost)`},children:T(e.ts)})]}),(0,z.jsx)(`td`,{className:`td-mono`,style:{color:`var(--text-tertiary)`},children:(0,z.jsx)(S,{content:`密钥 ${e.keyHint}`,placement:`below`,children:(0,z.jsx)(`span`,{children:e.keyHint})})}),(0,z.jsxs)(`td`,{children:[(0,z.jsx)(`div`,{className:`td-strong`,children:a}),(0,z.jsxs)(`div`,{className:`model-meta`,children:[(0,z.jsx)(S,{content:`提供商:${p(e.model)??u(e)}(按模型目录归属解析)`,children:(0,z.jsx)(`span`,{className:`prov-pill`,children:p(e.model)??u(e)})}),d(e.model)?(0,z.jsx)(`span`,{className:`mu-tag`,children:d(e.model)}):null]})]}),(0,z.jsx)(`td`,{className:`td-ua`,children:e.ua?(0,z.jsx)(S,{content:e.ua,children:(0,z.jsxs)(`span`,{style:{display:`block`},children:[(0,z.jsxs)(`span`,{className:`ua-raw`,children:[(0,z.jsx)(q,{tool:e.tool,size:14,style:{marginRight:5,verticalAlign:-2}}),e.ua]}),e.tool&&!X(e.ua,e.tool)?(0,z.jsx)(`div`,{className:`ua-tool`,children:e.tool}):e.tool?null:(0,z.jsx)(`div`,{className:`ua-tool`,children:(0,z.jsx)(`span`,{style:{color:`var(--text-ghost)`},children:`未识别`})})]})}):(0,z.jsxs)(z.Fragment,{children:[(0,z.jsx)(`span`,{className:`ua-raw`,style:{color:`var(--text-ghost)`},children:`未采集`}),e.tool?(0,z.jsx)(`div`,{className:`ua-tool`,children:e.tool}):(0,z.jsx)(`div`,{className:`ua-tool`,children:(0,z.jsx)(`span`,{style:{color:`var(--text-ghost)`},children:`未识别`})})]})}),(0,z.jsx)(`td`,{className:`r td-mono`,style:{color:J[t]},children:n.length>0?(0,z.jsx)(S,{content:n.join(` · `),children:(0,z.jsxs)(`span`,{children:[(0,z.jsx)(`span`,{className:`dotc`,style:{background:Y[t]},"aria-hidden":`true`}),e.status]})}):(0,z.jsxs)(z.Fragment,{children:[(0,z.jsx)(`span`,{className:`dotc`,style:{background:Y[t]},"aria-hidden":`true`}),e.status]})}),(0,z.jsxs)(`td`,{className:`r td-mono`,children:[(0,z.jsx)(`div`,{children:(0,z.jsx)(S,{content:`首字耗时 —— 从发出请求到收到第一个内容块的等待
2
2
  (含上游排队与思考时间;失败行没有首字)`,children:(0,z.jsx)(`span`,{className:`cell-sub`,children:e.firstByteMs===null?`无首字`:`首字 ${D(e.firstByteMs)}`})})}),(0,z.jsx)(`div`,{children:(0,z.jsx)(S,{content:`总耗时 —— 从发出请求到流结束`,children:(0,z.jsxs)(`span`,{children:[`耗时 `,D(e.ms)]})})})]}),(0,z.jsx)(`td`,{className:`r td-mono`,children:(0,z.jsx)(S,{content:`输出速度 = 输出 tokens ÷ 生成时长(总耗时 − 首字)
3
3
  上游不回报生成时长,此值由本服务按两端观测推算;
4
4
  失败行或首字即结束的请求没有生成过程,显示 --`,children:(0,z.jsx)(`span`,{children:C(M(e.completionTokens,e.ms,e.firstByteMs))})})}),(0,z.jsxs)(`td`,{className:`r td-mono`,children:[(0,z.jsx)(`div`,{children:(0,z.jsx)(S,{content:`输入 ${E(e.promptTokens)} tokens / 输出 ${E(e.completionTokens)} tokens`,children:(0,z.jsxs)(`span`,{children:[E(e.promptTokens),`/`,E(e.completionTokens)]})})}),e.cachedTokens>0?(0,z.jsx)(`div`,{children:(0,z.jsx)(S,{content:`输入中命中缓存 ${E(e.cachedTokens)} tokens\n上游按缓存价计费(已体现在积分里)`,children:(0,z.jsxs)(`span`,{className:`cell-cached`,children:[`+`,E(e.cachedTokens)]})})}):null]}),(0,z.jsx)(`td`,{className:`r td-mono`,children:e.credits===null?(0,z.jsx)(S,{content:`上游未回报该次费用(未知)
@@ -1 +1 @@
1
- import{J as e,S as t,it as n,k as r,m as i,q as a,r as o,rt as s,tt as c}from"./vendor-befEYGHM.js";import{a as l}from"./index-BJtz6Ses.js";var u=c();function d({tone:e=`ghost`,dot:t=!1,children:n}){return(0,u.jsxs)(`span`,{className:`pill pill-${e}`,children:[t?(0,u.jsx)(`span`,{className:`dot dot-live`}):null,n]})}function f({tone:e=`off`,children:t}){return(0,u.jsx)(`span`,{className:`tag tag-${e}`,children:t})}function p({title:e,status:t,actions:n}){return(0,u.jsxs)(`div`,{className:`topbar`,children:[(0,u.jsxs)(`div`,{className:`topbar-left`,children:[(0,u.jsx)(`h1`,{children:e}),t]}),n?(0,u.jsx)(`div`,{className:`topbar-right`,children:n}):null]})}function m({title:e,aside:t,children:n,className:r=``,padded:i=!0}){return(0,u.jsxs)(`div`,{className:`card${i?` panel`:``} ${r}`.trim(),children:[e?(0,u.jsxs)(`div`,{className:`panel-head`,style:i?void 0:{padding:`18px 20px 0`,marginBottom:12},children:[(0,u.jsx)(`h3`,{children:e}),t]}):null,n]})}function h({items:e}){return(0,u.jsx)(`div`,{className:`statbar`,"data-count":e.length,children:e.map(e=>(0,u.jsxs)(`div`,{children:[(0,u.jsxs)(`div`,{className:`sb-top`,children:[(0,u.jsx)(`span`,{className:`lb`,children:e.label}),e.detail?(0,u.jsx)(`span`,{className:`sdl`,children:e.detail}):null]}),e.tip?(0,u.jsx)(l,{content:e.tip,placement:e.tipPos===`down`?`below`:`auto`,className:`tooltip-value-anchor`,children:(0,u.jsx)(`div`,{className:`vl`,style:e.tone===`live`?{color:`var(--signal-live)`}:e.tone===`amber`?{color:`var(--warn-amber)`}:void 0,children:e.value})}):(0,u.jsx)(`div`,{className:`vl`,style:e.tone===`live`?{color:`var(--signal-live)`}:e.tone===`amber`?{color:`var(--warn-amber)`}:void 0,children:e.value})]},e.label))})}function g({label:e,value:t,detail:n,tone:r,icon:i,tip:a,tipPos:o}){let s=r===`cyan`||r===`violet`||r===`green`||r===`amber`||r===`blue`||r===`rose`?` m-${r}`:``;return(0,u.jsxs)(`div`,{className:`card metric`,children:[(0,u.jsxs)(`div`,{className:`m-top`,children:[i?(0,u.jsx)(`span`,{className:`m-ico${s}`,"aria-hidden":`true`,children:i}):null,(0,u.jsx)(`span`,{className:`lb`,children:e})]}),a?(0,u.jsx)(l,{content:a,placement:o===`down`?`below`:`auto`,className:`tooltip-value-anchor`,children:(0,u.jsx)(`div`,{className:`vl`,children:t})}):(0,u.jsx)(`div`,{className:`vl`,children:t}),n?(0,u.jsx)(`div`,{className:`dl${r===`up`?` up`:r===`warn`?` warn`:``}`,children:n}):null]})}function _({kind:e,title:n,detail:a,onRetry:s,children:c}){return e===`loading`?(0,u.jsxs)(`div`,{className:`state-block`,role:`status`,"aria-live":`polite`,"aria-busy":`true`,children:[(0,u.jsx)(t,{size:22,className:`spin`,style:{animation:`spin 1s linear infinite`}}),(0,u.jsx)(`div`,{className:`t`,children:n??`加载中…`})]}):e===`empty`?(0,u.jsxs)(`div`,{className:`state-block`,role:`status`,"aria-live":`polite`,children:[(0,u.jsx)(r,{size:26}),(0,u.jsx)(`div`,{className:`t`,children:n??`暂无数据`}),a?(0,u.jsx)(`div`,{className:`d`,children:a}):null,c]}):(0,u.jsxs)(`div`,{className:`state-block`,role:`alert`,"aria-live":`assertive`,children:[(0,u.jsx)(o,{size:26}),(0,u.jsx)(`div`,{className:`t`,children:n??`加载失败`}),a?(0,u.jsx)(`div`,{className:`d`,children:a}):null,s?(0,u.jsxs)(`button`,{type:`button`,className:`btn btn-quiet btn-sm`,onClick:s,children:[(0,u.jsx)(i,{size:14}),`重试`]}):null]})}function v(e){if(!e)return``;let t={40001:`有字段没通过校验,回退一步检查刚填的内容。`,40002:`确认文字不匹配 —— 这是刻意设计的门槛。`,40101:`管理口令不正确,注意区分大小写。`,40102:`会话已过期,登录后继续。`,40290:`登录失败次数过多,请 ${e.retryAfterSeconds?Math.ceil(e.retryAfterSeconds/60):10} 分钟后重试。`,40301:`请求来源校验失败(CSRF 防护),刷新页面重试。`,40401:`这条记录已不存在,可能刚被删掉了。`,40901:`同名密钥已存在,换一个名字。`,50001:`服务端未分类异常,可稍后重试或查看服务日志。`,50301:`数据库暂时不可用,确认后端进程是否在运行。`};return(e.code===40001||e.code===50001)&&/[\u4e00-\u9fa5]/.test(e.message)?e.message:t[e.code]?t[e.code]:e.status===0?e.message:e.message||`请求失败(HTTP ${e.status})`}var y=n(),b=s();function x({value:t,options:n,onChange:r,width:i,disabled:o,size:s=`md`,ariaLabel:c}){let[l,d]=(0,y.useState)(!1),[f,p]=(0,y.useState)(0),[m,h]=(0,y.useState)(null),[g,_]=(0,y.useState)(!1),v=(0,y.useRef)(null),x=(0,y.useRef)(null),S=(0,y.useRef)({buf:``,at:0}),C=(0,y.useId)(),w=`${C}list`,T=e=>`${C}opt${e}`,E=Math.max(0,n.findIndex(e=>e.value===t)),D=(0,y.useCallback)(()=>{let e=v.current;if(!e)return;let t=e.getBoundingClientRect();h({left:t.left,top:t.top,bottom:t.bottom,width:t.width})},[]),O=(0,y.useCallback)(()=>{o||n.length===0||(S.current={buf:``,at:0},p(E),D(),d(!0))},[o,D,n.length,E]),k=(0,y.useCallback)(e=>{let t=n[e];t&&r(t.value),d(!1),v.current?.focus()},[r,n]);(0,y.useLayoutEffect)(()=>{l&&x.current?.focus()},[l]),(0,y.useLayoutEffect)(()=>{if(!l||!m)return;let e=x.current;if(!e)return;let t=window.innerHeight-m.bottom;_(t<e.offsetHeight+12&&m.top>t)},[l,m,n.length]),(0,y.useEffect)(()=>{l&&x.current?.querySelector(`[data-opt="${f}"]`)?.scrollIntoView({block:`nearest`})},[f,l]),(0,y.useEffect)(()=>{if(!l)return;let e=()=>D(),t=e=>{let t=e.target;t&&(v.current?.contains(t)||x.current?.contains(t))||d(!1)};return window.addEventListener(`scroll`,e,!0),window.addEventListener(`resize`,e),document.addEventListener(`pointerdown`,t,!0),()=>{window.removeEventListener(`scroll`,e,!0),window.removeEventListener(`resize`,e),document.removeEventListener(`pointerdown`,t,!0)}},[l,D]);let A=(0,y.useCallback)((e,t)=>{let r=Date.now(),i=(r-S.current.at>700?``:S.current.buf)+e;S.current={buf:i,at:r};let a=i.toLowerCase(),o=n.length,s=i.length===1?(t+1)%o:0;for(let e=0;e<o;e+=1){let t=(s+e)%o;if(n[t].label.toLowerCase().startsWith(a)){p(t);return}}},[n]),j=e=>e.key.length===1&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&/\S/.test(e.key),M=e=>{if(!(o||l)){if(e.key===`ArrowDown`||e.key===`ArrowUp`||e.key===`Enter`||e.key===` `){e.preventDefault(),O();return}j(e)&&(e.preventDefault(),O(),A(e.key,E))}},N=e=>{let t=n.length;switch(e.key){case`ArrowDown`:e.preventDefault(),p(e=>(e+1)%t);break;case`ArrowUp`:e.preventDefault(),p(e=>(e-1+t)%t);break;case`Home`:e.preventDefault(),p(0);break;case`End`:e.preventDefault(),p(t-1);break;case`Enter`:case` `:e.preventDefault(),k(f);break;case`Escape`:e.preventDefault(),e.stopPropagation(),d(!1),v.current?.focus();break;case`Tab`:d(!1),v.current?.focus();break;default:j(e)&&A(e.key,f)}},P=n[E]?.label??``;return(0,u.jsxs)(`div`,{className:`sel-field`,style:i?{width:i}:void 0,children:[(0,u.jsxs)(`button`,{type:`button`,ref:v,className:s===`sm`?`sel sm`:`sel`,"aria-haspopup":`listbox`,"aria-expanded":l,"aria-controls":l?w:void 0,"aria-label":c,"data-value":t,disabled:o,onClick:()=>l?d(!1):O(),onKeyDown:M,children:[(0,u.jsx)(`span`,{className:`sel-val`,children:P}),(0,u.jsx)(a,{size:s===`sm`?12:13,className:`ic caret-glyph`,"aria-hidden":!0})]}),l&&m?(0,b.createPortal)((0,u.jsx)(`div`,{ref:x,id:w,role:`listbox`,tabIndex:-1,"aria-activedescendant":n.length?T(f):void 0,className:s===`sm`?`sel-pop sm`:`sel-pop`,onKeyDown:N,style:{left:Math.max(8,Math.min(m.left,window.innerWidth-Math.max(m.width,168)-8)),top:g?void 0:m.bottom+6,bottom:g?window.innerHeight-m.top+6:void 0,minWidth:m.width},children:n.map((n,r)=>(0,u.jsxs)(`div`,{id:T(r),"data-opt":r,"data-value":n.value,role:`option`,"aria-selected":n.value===t,className:`sel-opt${r===f?` on`:``}${n.value===t?` picked`:``}`,onMouseEnter:()=>p(r),onPointerDown:e=>e.preventDefault(),onClick:()=>k(r),children:[(0,u.jsx)(`span`,{className:`sel-opt-lb`,children:n.label}),n.value===t?(0,u.jsx)(e,{size:13,className:`ic`,"aria-hidden":!0}):null]},n.value))}),document.body):null]})}export{m as a,d as c,g as i,f as l,_ as n,h as o,v as r,p as s,x as t};
1
+ import{J as e,S as t,it as n,k as r,m as i,q as a,r as o,rt as s,tt as c}from"./vendor-befEYGHM.js";import{a as l}from"./index-Bb8UvXuA.js";var u=c();function d({tone:e=`ghost`,dot:t=!1,children:n}){return(0,u.jsxs)(`span`,{className:`pill pill-${e}`,children:[t?(0,u.jsx)(`span`,{className:`dot dot-live`}):null,n]})}function f({tone:e=`off`,children:t}){return(0,u.jsx)(`span`,{className:`tag tag-${e}`,children:t})}function p({title:e,status:t,actions:n}){return(0,u.jsxs)(`div`,{className:`topbar`,children:[(0,u.jsxs)(`div`,{className:`topbar-left`,children:[(0,u.jsx)(`h1`,{children:e}),t]}),n?(0,u.jsx)(`div`,{className:`topbar-right`,children:n}):null]})}function m({title:e,aside:t,children:n,className:r=``,padded:i=!0}){return(0,u.jsxs)(`div`,{className:`card${i?` panel`:``} ${r}`.trim(),children:[e?(0,u.jsxs)(`div`,{className:`panel-head`,style:i?void 0:{padding:`18px 20px 0`,marginBottom:12},children:[(0,u.jsx)(`h3`,{children:e}),t]}):null,n]})}function h({items:e}){return(0,u.jsx)(`div`,{className:`statbar`,"data-count":e.length,children:e.map(e=>(0,u.jsxs)(`div`,{children:[(0,u.jsxs)(`div`,{className:`sb-top`,children:[(0,u.jsx)(`span`,{className:`lb`,children:e.label}),e.detail?(0,u.jsx)(`span`,{className:`sdl`,children:e.detail}):null]}),e.tip?(0,u.jsx)(l,{content:e.tip,placement:e.tipPos===`down`?`below`:`auto`,className:`tooltip-value-anchor`,children:(0,u.jsx)(`div`,{className:`vl`,style:e.tone===`live`?{color:`var(--signal-live)`}:e.tone===`amber`?{color:`var(--warn-amber)`}:void 0,children:e.value})}):(0,u.jsx)(`div`,{className:`vl`,style:e.tone===`live`?{color:`var(--signal-live)`}:e.tone===`amber`?{color:`var(--warn-amber)`}:void 0,children:e.value})]},e.label))})}function g({label:e,value:t,detail:n,tone:r,icon:i,tip:a,tipPos:o}){let s=r===`cyan`||r===`violet`||r===`green`||r===`amber`||r===`blue`||r===`rose`?` m-${r}`:``;return(0,u.jsxs)(`div`,{className:`card metric`,children:[(0,u.jsxs)(`div`,{className:`m-top`,children:[i?(0,u.jsx)(`span`,{className:`m-ico${s}`,"aria-hidden":`true`,children:i}):null,(0,u.jsx)(`span`,{className:`lb`,children:e})]}),a?(0,u.jsx)(l,{content:a,placement:o===`down`?`below`:`auto`,className:`tooltip-value-anchor`,children:(0,u.jsx)(`div`,{className:`vl`,children:t})}):(0,u.jsx)(`div`,{className:`vl`,children:t}),n?(0,u.jsx)(`div`,{className:`dl${r===`up`?` up`:r===`warn`?` warn`:``}`,children:n}):null]})}function _({kind:e,title:n,detail:a,onRetry:s,children:c}){return e===`loading`?(0,u.jsxs)(`div`,{className:`state-block`,role:`status`,"aria-live":`polite`,"aria-busy":`true`,children:[(0,u.jsx)(t,{size:22,className:`spin`,style:{animation:`spin 1s linear infinite`}}),(0,u.jsx)(`div`,{className:`t`,children:n??`加载中…`})]}):e===`empty`?(0,u.jsxs)(`div`,{className:`state-block`,role:`status`,"aria-live":`polite`,children:[(0,u.jsx)(r,{size:26}),(0,u.jsx)(`div`,{className:`t`,children:n??`暂无数据`}),a?(0,u.jsx)(`div`,{className:`d`,children:a}):null,c]}):(0,u.jsxs)(`div`,{className:`state-block`,role:`alert`,"aria-live":`assertive`,children:[(0,u.jsx)(o,{size:26}),(0,u.jsx)(`div`,{className:`t`,children:n??`加载失败`}),a?(0,u.jsx)(`div`,{className:`d`,children:a}):null,s?(0,u.jsxs)(`button`,{type:`button`,className:`btn btn-quiet btn-sm`,onClick:s,children:[(0,u.jsx)(i,{size:14}),`重试`]}):null]})}function v(e){if(!e)return``;let t={40001:`有字段没通过校验,回退一步检查刚填的内容。`,40002:`确认文字不匹配 —— 这是刻意设计的门槛。`,40101:`管理口令不正确,注意区分大小写。`,40102:`会话已过期,登录后继续。`,40290:`登录失败次数过多,请 ${e.retryAfterSeconds?Math.ceil(e.retryAfterSeconds/60):10} 分钟后重试。`,40301:`请求来源校验失败(CSRF 防护),刷新页面重试。`,40401:`这条记录已不存在,可能刚被删掉了。`,40901:`同名密钥已存在,换一个名字。`,50001:`服务端未分类异常,可稍后重试或查看服务日志。`,50301:`数据库暂时不可用,确认后端进程是否在运行。`};return(e.code===40001||e.code===50001)&&/[\u4e00-\u9fa5]/.test(e.message)?e.message:t[e.code]?t[e.code]:e.status===0?e.message:e.message||`请求失败(HTTP ${e.status})`}var y=n(),b=s();function x({value:t,options:n,onChange:r,width:i,disabled:o,size:s=`md`,ariaLabel:c}){let[l,d]=(0,y.useState)(!1),[f,p]=(0,y.useState)(0),[m,h]=(0,y.useState)(null),[g,_]=(0,y.useState)(!1),v=(0,y.useRef)(null),x=(0,y.useRef)(null),S=(0,y.useRef)({buf:``,at:0}),C=(0,y.useId)(),w=`${C}list`,T=e=>`${C}opt${e}`,E=Math.max(0,n.findIndex(e=>e.value===t)),D=(0,y.useCallback)(()=>{let e=v.current;if(!e)return;let t=e.getBoundingClientRect();h({left:t.left,top:t.top,bottom:t.bottom,width:t.width})},[]),O=(0,y.useCallback)(()=>{o||n.length===0||(S.current={buf:``,at:0},p(E),D(),d(!0))},[o,D,n.length,E]),k=(0,y.useCallback)(e=>{let t=n[e];t&&r(t.value),d(!1),v.current?.focus()},[r,n]);(0,y.useLayoutEffect)(()=>{l&&x.current?.focus()},[l]),(0,y.useLayoutEffect)(()=>{if(!l||!m)return;let e=x.current;if(!e)return;let t=window.innerHeight-m.bottom;_(t<e.offsetHeight+12&&m.top>t)},[l,m,n.length]),(0,y.useEffect)(()=>{l&&x.current?.querySelector(`[data-opt="${f}"]`)?.scrollIntoView({block:`nearest`})},[f,l]),(0,y.useEffect)(()=>{if(!l)return;let e=()=>D(),t=e=>{let t=e.target;t&&(v.current?.contains(t)||x.current?.contains(t))||d(!1)};return window.addEventListener(`scroll`,e,!0),window.addEventListener(`resize`,e),document.addEventListener(`pointerdown`,t,!0),()=>{window.removeEventListener(`scroll`,e,!0),window.removeEventListener(`resize`,e),document.removeEventListener(`pointerdown`,t,!0)}},[l,D]);let A=(0,y.useCallback)((e,t)=>{let r=Date.now(),i=(r-S.current.at>700?``:S.current.buf)+e;S.current={buf:i,at:r};let a=i.toLowerCase(),o=n.length,s=i.length===1?(t+1)%o:0;for(let e=0;e<o;e+=1){let t=(s+e)%o;if(n[t].label.toLowerCase().startsWith(a)){p(t);return}}},[n]),j=e=>e.key.length===1&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&/\S/.test(e.key),M=e=>{if(!(o||l)){if(e.key===`ArrowDown`||e.key===`ArrowUp`||e.key===`Enter`||e.key===` `){e.preventDefault(),O();return}j(e)&&(e.preventDefault(),O(),A(e.key,E))}},N=e=>{let t=n.length;switch(e.key){case`ArrowDown`:e.preventDefault(),p(e=>(e+1)%t);break;case`ArrowUp`:e.preventDefault(),p(e=>(e-1+t)%t);break;case`Home`:e.preventDefault(),p(0);break;case`End`:e.preventDefault(),p(t-1);break;case`Enter`:case` `:e.preventDefault(),k(f);break;case`Escape`:e.preventDefault(),e.stopPropagation(),d(!1),v.current?.focus();break;case`Tab`:d(!1),v.current?.focus();break;default:j(e)&&A(e.key,f)}},P=n[E]?.label??``;return(0,u.jsxs)(`div`,{className:`sel-field`,style:i?{width:i}:void 0,children:[(0,u.jsxs)(`button`,{type:`button`,ref:v,className:s===`sm`?`sel sm`:`sel`,"aria-haspopup":`listbox`,"aria-expanded":l,"aria-controls":l?w:void 0,"aria-label":c,"data-value":t,disabled:o,onClick:()=>l?d(!1):O(),onKeyDown:M,children:[(0,u.jsx)(`span`,{className:`sel-val`,children:P}),(0,u.jsx)(a,{size:s===`sm`?12:13,className:`ic caret-glyph`,"aria-hidden":!0})]}),l&&m?(0,b.createPortal)((0,u.jsx)(`div`,{ref:x,id:w,role:`listbox`,tabIndex:-1,"aria-activedescendant":n.length?T(f):void 0,className:s===`sm`?`sel-pop sm`:`sel-pop`,onKeyDown:N,style:{left:Math.max(8,Math.min(m.left,window.innerWidth-Math.max(m.width,168)-8)),top:g?void 0:m.bottom+6,bottom:g?window.innerHeight-m.top+6:void 0,minWidth:m.width},children:n.map((n,r)=>(0,u.jsxs)(`div`,{id:T(r),"data-opt":r,"data-value":n.value,role:`option`,"aria-selected":n.value===t,className:`sel-opt${r===f?` on`:``}${n.value===t?` picked`:``}`,onMouseEnter:()=>p(r),onPointerDown:e=>e.preventDefault(),onClick:()=>k(r),children:[(0,u.jsx)(`span`,{className:`sel-opt-lb`,children:n.label}),n.value===t?(0,u.jsx)(e,{size:13,className:`ic`,"aria-hidden":!0}):null]},n.value))}),document.body):null]})}export{m as a,d as c,g as i,f as l,_ as n,h as o,v as r,p as s,x as t};