@eddyskywalker/dsh-chatgpt-subscription 0.2.21 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/CHANGELOG.md +74 -0
  2. package/README.md +115 -1
  3. package/lib/client.js +2825 -512
  4. package/lib/client.js.map +1 -1
  5. package/lib/index.js +8630 -389
  6. package/lib/types/client/ProviderHubSection.d.ts +15 -0
  7. package/lib/types/client/ProviderHubSection.d.ts.map +1 -0
  8. package/lib/types/client/command-code/CommandCodeComposerQuota.d.ts +24 -0
  9. package/lib/types/client/command-code/CommandCodeComposerQuota.d.ts.map +1 -0
  10. package/lib/types/client/command-code/CommandCodeSection.d.ts +11 -0
  11. package/lib/types/client/command-code/CommandCodeSection.d.ts.map +1 -0
  12. package/lib/types/client/command-code/locales.d.ts +401 -0
  13. package/lib/types/client/command-code/locales.d.ts.map +1 -0
  14. package/lib/types/client/index.d.ts +2 -0
  15. package/lib/types/client/index.d.ts.map +1 -1
  16. package/lib/types/client/kimi-code/KimiCodeComposerQuota.d.ts +25 -0
  17. package/lib/types/client/kimi-code/KimiCodeComposerQuota.d.ts.map +1 -0
  18. package/lib/types/client/kimi-code/KimiCodeSection.d.ts +11 -0
  19. package/lib/types/client/kimi-code/KimiCodeSection.d.ts.map +1 -0
  20. package/lib/types/client/kimi-code/KimiModelCapabilities.d.ts +16 -0
  21. package/lib/types/client/kimi-code/KimiModelCapabilities.d.ts.map +1 -0
  22. package/lib/types/client/kimi-code/locales.d.ts +665 -0
  23. package/lib/types/client/kimi-code/locales.d.ts.map +1 -0
  24. package/lib/types/client/kimi-code/styles.d.ts +4 -0
  25. package/lib/types/client/kimi-code/styles.d.ts.map +1 -0
  26. package/lib/types/client/locales.d.ts +3 -1
  27. package/lib/types/client/locales.d.ts.map +1 -1
  28. package/lib/types/client/styles.d.ts.map +1 -1
  29. package/lib/types/host/command-code/adapter.d.ts +34 -0
  30. package/lib/types/host/command-code/adapter.d.ts.map +1 -0
  31. package/lib/types/host/command-code/client.d.ts +99 -0
  32. package/lib/types/host/command-code/client.d.ts.map +1 -0
  33. package/lib/types/host/command-code/mapper.d.ts +102 -0
  34. package/lib/types/host/command-code/mapper.d.ts.map +1 -0
  35. package/lib/types/host/command-code/model-catalog.d.ts +31 -0
  36. package/lib/types/host/command-code/model-catalog.d.ts.map +1 -0
  37. package/lib/types/host/command-code/oauth.d.ts +89 -0
  38. package/lib/types/host/command-code/oauth.d.ts.map +1 -0
  39. package/lib/types/host/command-code/plans.d.ts +40 -0
  40. package/lib/types/host/command-code/plans.d.ts.map +1 -0
  41. package/lib/types/host/command-code/routes.d.ts +27 -0
  42. package/lib/types/host/command-code/routes.d.ts.map +1 -0
  43. package/lib/types/host/command-code/token-store.d.ts +88 -0
  44. package/lib/types/host/command-code/token-store.d.ts.map +1 -0
  45. package/lib/types/host/command-code/types.d.ts +138 -0
  46. package/lib/types/host/command-code/types.d.ts.map +1 -0
  47. package/lib/types/host/kimi-code/adapter.d.ts +112 -0
  48. package/lib/types/host/kimi-code/adapter.d.ts.map +1 -0
  49. package/lib/types/host/kimi-code/client.d.ts +161 -0
  50. package/lib/types/host/kimi-code/client.d.ts.map +1 -0
  51. package/lib/types/host/kimi-code/mapper.d.ts +334 -0
  52. package/lib/types/host/kimi-code/mapper.d.ts.map +1 -0
  53. package/lib/types/host/kimi-code/modalities.d.ts +94 -0
  54. package/lib/types/host/kimi-code/modalities.d.ts.map +1 -0
  55. package/lib/types/host/kimi-code/model-catalog.d.ts +77 -0
  56. package/lib/types/host/kimi-code/model-catalog.d.ts.map +1 -0
  57. package/lib/types/host/kimi-code/oauth.d.ts +133 -0
  58. package/lib/types/host/kimi-code/oauth.d.ts.map +1 -0
  59. package/lib/types/host/kimi-code/routes.d.ts +26 -0
  60. package/lib/types/host/kimi-code/routes.d.ts.map +1 -0
  61. package/lib/types/host/kimi-code/token-store.d.ts +135 -0
  62. package/lib/types/host/kimi-code/token-store.d.ts.map +1 -0
  63. package/lib/types/host/kimi-code/types.d.ts +174 -0
  64. package/lib/types/host/kimi-code/types.d.ts.map +1 -0
  65. package/lib/types/host/relay-probe.d.ts +161 -0
  66. package/lib/types/host/relay-probe.d.ts.map +1 -0
  67. package/lib/types/host/responses-client.d.ts.map +1 -1
  68. package/lib/types/index.d.ts +13 -0
  69. package/lib/types/index.d.ts.map +1 -1
  70. package/lib/types/shared/command-code-contracts.d.ts +119 -0
  71. package/lib/types/shared/command-code-contracts.d.ts.map +1 -0
  72. package/lib/types/shared/kimi-code-contracts.d.ts +203 -0
  73. package/lib/types/shared/kimi-code-contracts.d.ts.map +1 -0
  74. package/package.json +1 -1
package/lib/client.js CHANGED
@@ -56,7 +56,7 @@ window.__ModuleLoader__.load({
56
56
  //#endregion
57
57
  //#region src/client/CodexComposerQuota.tsx
58
58
  function CodexComposerQuota({ api, directory, loadModelDirectory, t }) {
59
- const modelState = useStore$1(directory);
59
+ const modelState = useStore$3(directory);
60
60
  const [status, setStatus] = (0, react.useState)(null);
61
61
  const [loading, setLoading] = (0, react.useState)(false);
62
62
  const mountedRef = (0, react.useRef)(false);
@@ -103,7 +103,7 @@ window.__ModuleLoader__.load({
103
103
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("quickQuotaLabel") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: quota === null ? loading ? t("quickQuotaLoading") : "—" : formatPercent$1(quota.remainingPercent) })]
104
104
  });
105
105
  }
106
- function useStore$1(store) {
106
+ function useStore$3(store) {
107
107
  return (0, react.useSyncExternalStore)((listener) => store.subscribe(listener), () => store.getSnapshot(), () => store.getSnapshot());
108
108
  }
109
109
  function formatPercent$1(value) {
@@ -594,7 +594,7 @@ window.__ModuleLoader__.load({
594
594
  }),
595
595
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoRow, {
596
596
  label: t("expires"),
597
- value: formatDate$1(account?.tokenExpiresAt)
597
+ value: formatDate$3(account?.tokenExpiresAt)
598
598
  })
599
599
  ] }) : null,
600
600
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoRow, {
@@ -657,7 +657,7 @@ window.__ModuleLoader__.load({
657
657
  }),
658
658
  connection !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoRow, {
659
659
  label: t("latency"),
660
- value: `${connection.latencyMs} ms · ${formatDate$1(connection.checkedAt)}`
660
+ value: `${connection.latencyMs} ms · ${formatDate$3(connection.checkedAt)}`
661
661
  }) : null,
662
662
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
663
663
  className: "dsh-codex-muted dsh-codex-models-hint",
@@ -914,7 +914,7 @@ window.__ModuleLoader__.load({
914
914
  id: inputId,
915
915
  type: "text",
916
916
  inputMode: "numeric",
917
- value: draft ?? formatCapacity$1(fallback),
917
+ value: draft ?? formatCapacity$3(fallback),
918
918
  disabled: busy !== null,
919
919
  "aria-label": entry.name + " " + t("contextWindow"),
920
920
  onChange: (event) => {
@@ -1028,7 +1028,7 @@ window.__ModuleLoader__.load({
1028
1028
  children: [
1029
1029
  t("updated"),
1030
1030
  ": ",
1031
- formatDate$1(quota.fetchedAt)
1031
+ formatDate$3(quota.fetchedAt)
1032
1032
  ]
1033
1033
  }) : null
1034
1034
  ]
@@ -1120,7 +1120,7 @@ window.__ModuleLoader__.load({
1120
1120
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("small", { children: [
1121
1121
  t("resetCreditExpires"),
1122
1122
  ": ",
1123
- resetCredits.expiresAt == null ? t("unknown") : formatReset(resetCredits.expiresAt)
1123
+ resetCredits.expiresAt == null ? t("unknown") : formatReset$2(resetCredits.expiresAt)
1124
1124
  ] })
1125
1125
  ]
1126
1126
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
@@ -1153,7 +1153,7 @@ window.__ModuleLoader__.load({
1153
1153
  }),
1154
1154
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1155
1155
  className: "dsh-codex-meter-meta",
1156
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: percent >= 100 ? `${t("exhausted")} · ${formatPercent(remaining)} ${t("remaining")}` : `${formatPercent(percent)} ${t("used")} · ${formatPercent(remaining)} ${t("remaining")}` }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: window?.resetsAt == null ? "—" : `${t("resets")}: ${formatReset(window.resetsAt)}` })]
1156
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: percent >= 100 ? `${t("exhausted")} · ${formatPercent(remaining)} ${t("remaining")}` : `${formatPercent(percent)} ${t("used")} · ${formatPercent(remaining)} ${t("remaining")}` }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: window?.resetsAt == null ? "—" : `${t("resets")}: ${formatReset$2(window.resetsAt)}` })]
1157
1157
  })
1158
1158
  ]
1159
1159
  });
@@ -1179,7 +1179,7 @@ window.__ModuleLoader__.load({
1179
1179
  return `${value} ${t("limitWindow")}`;
1180
1180
  }
1181
1181
  }
1182
- function parsePositiveCapacity$1(value) {
1182
+ function parsePositiveCapacity$3(value) {
1183
1183
  const matched = value.trim().toLowerCase().replace(/[,_\s]/g, "").match(/^(\d+(?:\.\d+)?)(k|m)?$/);
1184
1184
  if (matched === null) return null;
1185
1185
  const multiplier = matched[2] === "m" ? 1e6 : matched[2] === "k" ? 1e3 : 1;
@@ -1187,10 +1187,10 @@ window.__ModuleLoader__.load({
1187
1187
  return Number.isSafeInteger(parsed) && parsed >= 1 ? parsed : null;
1188
1188
  }
1189
1189
  function parseCapacity(value, maxContextWindow = GPT_56_MAX_CONTEXT_WINDOW) {
1190
- const parsed = parsePositiveCapacity$1(value);
1190
+ const parsed = parsePositiveCapacity$3(value);
1191
1191
  return parsed !== null && parsed <= maxContextWindow ? parsed : null;
1192
1192
  }
1193
- function formatCapacity$1(value) {
1193
+ function formatCapacity$3(value) {
1194
1194
  if (value % 1e3 === 0) return `${value / 1e3}K`;
1195
1195
  return String(value);
1196
1196
  }
@@ -1207,11 +1207,11 @@ window.__ModuleLoader__.load({
1207
1207
  limit.remainingPercent !== null && limit.remainingPercent !== void 0 ? `${formatPercent(limit.remainingPercent)} ${t("remaining")}` : null,
1208
1208
  limit.limit !== null && limit.limit !== void 0 ? `${t("limit")}: ${limit.limit}` : null,
1209
1209
  limit.used !== null && limit.used !== void 0 ? `${t("used")}: ${limit.used}` : null,
1210
- limit.resetsAt !== null && limit.resetsAt !== void 0 ? `${t("resets")}: ${formatReset(limit.resetsAt)}` : null
1210
+ limit.resetsAt !== null && limit.resetsAt !== void 0 ? `${t("resets")}: ${formatReset$2(limit.resetsAt)}` : null
1211
1211
  ].filter((part) => part !== null);
1212
1212
  return parts.length > 0 ? parts.join(" · ") : t("unknown");
1213
1213
  }
1214
- function formatDate$1(seconds) {
1214
+ function formatDate$3(seconds) {
1215
1215
  if (seconds === void 0 || seconds === null || typeof seconds !== "number" || !Number.isFinite(seconds) || seconds <= 0) return "—";
1216
1216
  const ms = seconds > 1e10 ? seconds : seconds * 1e3;
1217
1217
  try {
@@ -1223,10 +1223,10 @@ window.__ModuleLoader__.load({
1223
1223
  return "—";
1224
1224
  }
1225
1225
  }
1226
- function formatReset(seconds) {
1226
+ function formatReset$2(seconds) {
1227
1227
  if (seconds === void 0 || seconds === null || typeof seconds !== "number" || !Number.isFinite(seconds) || seconds <= 0) return "—";
1228
1228
  const ms = seconds > 1e10 ? seconds : seconds * 1e3;
1229
- const absolute = formatDate$1(seconds);
1229
+ const absolute = formatDate$3(seconds);
1230
1230
  const diff = ms - Date.now();
1231
1231
  const abs = Math.abs(diff);
1232
1232
  const [amount, unit] = abs >= 864e5 ? [Math.round(diff / 864e5), "day"] : abs >= 36e5 ? [Math.round(diff / 36e5), "hour"] : [Math.round(diff / 6e4), "minute"];
@@ -1240,380 +1240,9 @@ window.__ModuleLoader__.load({
1240
1240
  return error instanceof Error ? error.message : String(error);
1241
1241
  }
1242
1242
  //#endregion
1243
- //#region src/client/locales.ts
1244
- const NS = "dsh-chatgpt-subscription";
1245
- const dictionaries$1 = {
1246
- zh: {
1247
- title: "ChatGPT",
1248
- intro: "使用 ChatGPT 账号登录,在 DSH 中使用订阅可用的模型。",
1249
- account: "账号",
1250
- signedOut: "尚未登录",
1251
- signedIn: "已登录",
1252
- plan: "套餐",
1253
- accountId: "账号 ID",
1254
- expires: "令牌到期",
1255
- storage: "凭据存储",
1256
- storageWindows: "Windows DPAPI(当前用户加密)",
1257
- storageMacKeychain: "macOS 钥匙串(Keychain 加密)",
1258
- storageLinuxFile: "Linux 用户私有文件(权限 0600)",
1259
- storageMemory: "仅 Host 内存(不持久化)",
1260
- storageUnavailable: "凭据存储不可用",
1261
- securityWindows: "令牌由 Host 使用 Windows CurrentUser DPAPI 加密,不会进入浏览器、settings.yaml 或日志。",
1262
- securityMacKeychain: "令牌由 Host 存入 macOS 登录钥匙串(Keychain),在本机加密保存,不会进入浏览器、settings.yaml 或日志。",
1263
- securityLinuxFile: "令牌仅写入 Host 上当前 Linux 用户可读的 0600 文件,但不会额外加密;同 UID 进程、root、备份和磁盘快照仍可能读取。",
1264
- securityMemory: "令牌仅保留在 Host 内存中,Host 退出后丢失。",
1265
- securityUnavailable: "Host 无法安全访问凭据存储;请修复文件所有者或权限后重试。",
1266
- signIn: "使用 ChatGPT 登录",
1267
- signInAgain: "重新登录",
1268
- cancel: "取消登录",
1269
- signOut: "注销",
1270
- refreshToken: "刷新凭据",
1271
- pending: "请在浏览器中完成登录。",
1272
- popupBlocked: "浏览器拦截了登录窗口,请使用下面的链接继续。",
1273
- continueLogin: "打开 ChatGPT 登录页",
1274
- loading: "正在读取状态…",
1275
- connection: "连接",
1276
- provider: "Provider",
1277
- connectionState: "连接状态",
1278
- connected: "可用",
1279
- untested: "尚未测试",
1280
- testConnection: "测试连接",
1281
- testing: "测试中…",
1282
- latency: "最近延迟",
1283
- models: "可用模型",
1284
- modelsHint: "勾选后模型才会显示在对话页的模型选择列表中;至少保留一个。",
1285
- proxySettings: "网络代理",
1286
- proxyMode: "代理模式",
1287
- proxyModeHint: "GPT 与 Antigravity(Gemini)共用此代理设置;支持自动读取系统代理,修改后对后续请求生效。",
1288
- proxyModeAuto: "系统代理(自动检测)",
1289
- proxyModeCustom: "自定义代理",
1290
- proxyModeDirect: "直连(禁用代理)",
1291
- customProxyUrl: "自定义代理地址",
1292
- customProxyUrlHint: "支持 HTTP/HTTPS/SOCKS5 代理地址,例如 http://127.0.0.1:7890",
1293
- customProxyUrlPlaceholder: "例如: http://127.0.0.1:7890",
1294
- saveProxyUrl: "保存代理地址",
1295
- proxyDetected: "检测到系统代理",
1296
- proxyDetectedEffective: "(已自动生效)",
1297
- proxyNoneDetected: "未检测到系统代理,将使用直连",
1298
- proxyDirectHint: "所有网络请求将直接连接,不经过任何代理服务器。",
1299
- enhancements: "增强功能",
1300
- fastMode: "快速模式(1.5x 速度)",
1301
- fastModeHint: "提升 Token 生成速度至约 1.5 倍;将以约 2x–2.5x 速度消耗 Codex 订阅额度。",
1302
- outputVerbosity: "输出详细程度",
1303
- outputVerbosityHint: "控制 Codex 最终回答的详细程度;不影响思考深度。",
1304
- verbosityLow: "低(保持回答简洁)",
1305
- verbosityMedium: "中(兼顾细节与简洁)",
1306
- verbosityHigh: "高(在回答中包含更多细节)",
1307
- reasoningSummary: "推理摘要",
1308
- reasoningSummaryHint: "选择 ChatGPT 总结其推理的方式;不影响思考深度。",
1309
- summaryAuto: "自动(让模型选择摘要详细程度)",
1310
- summaryConcise: "简洁(显示简要推理摘要)",
1311
- summaryDetailed: "详细(显示更详细的推理摘要)",
1312
- summaryNone: "无(不显示推理摘要)",
1313
- searchProvider: "搜索与抓取来源",
1314
- searchProviderHint: "选择 DSH 联网工具使用默认来源,或改用当前 ChatGPT 订阅的 Codex 来源(自动走代理隧道)。",
1315
- searchProviderDsh: "DSH 默认",
1316
- searchProviderCodex: "ChatGPT(代理隧道)",
1317
- providerDefault: "使用 Provider 默认值",
1318
- contextWindows: "模型上下文窗口",
1319
- contextWindowsHint: "默认 272K;6 Astra 最高 872K,GPT-5.6 最高 1M。该值用于 DSH 的压缩与溢出判断,可输入 872K、512K 等容量。",
1320
- contextWindow: "上下文窗口",
1321
- contextWindowInvalid: "上下文窗口必须是正整数,且不能超过模型上限(6 Astra 为 872K,GPT-5.6 为 1M);可使用 K 或 M 后缀。",
1322
- saveContextWindow: "保存上下文窗口",
1323
- save: "保存",
1324
- tokens: "tokens",
1325
- quickQuota: "在输入框旁显示快捷用量",
1326
- quickQuotaHint: "仅在当前会话选中 codex-chatgpt 模型时显示。",
1327
- quota: "用量与限额",
1328
- quotaIntro: "数据来自 ChatGPT Codex 用量服务。页面可见时最多每 60 秒刷新一次。",
1329
- refreshQuota: "刷新用量",
1330
- refreshing: "刷新中…",
1331
- noQuota: "当前套餐未返回可显示的限额窗口。",
1332
- quotaSignedOut: "登录后可查看订阅限额。",
1333
- stale: "显示的是上次成功获取的数据。",
1334
- updated: "更新时间",
1335
- primary: "主要窗口",
1336
- secondary: "次要窗口",
1337
- limitWindow: "额度",
1338
- used: "已使用",
1339
- remaining: "剩余",
1340
- available: "可用",
1341
- unavailable: "不可用",
1342
- unlimited: "无限",
1343
- credits: "Credits",
1344
- monthlySpend: "月度消费控制",
1345
- resetCredits: "重置次数",
1346
- resetCreditExpires: "到期时间",
1347
- useResetCredit: "使用重置卡",
1348
- usingResetCredit: "使用中…",
1349
- useResetCreditConfirm: "使用后会立即消耗 1 次重置机会,并重置符合条件的 Codex 额度窗口。确定继续吗?",
1350
- resetCreditUsed: "重置卡已使用,用量数据已刷新。",
1351
- spendControlReached: "已触发月度消费控制,新的订阅调用可能被限制。",
1352
- limit: "上限",
1353
- exhausted: "额度已用尽",
1354
- resets: "重置",
1355
- quickQuotaLabel: "Codex 剩余",
1356
- quickQuotaLoading: "用量…",
1357
- imageToolRunning: "正在生成图片",
1358
- imageToolDone: "已生成图片",
1359
- imageToolFailed: "图片生成失败",
1360
- image: "图片",
1361
- openImage: "打开图片",
1362
- openNamedImage: "打开 {name}",
1363
- imageLoading: "加载图片…",
1364
- imageLoadFailed: "图片加载失败,点击重试",
1365
- imagePreviewClose: "关闭",
1366
- imagePreviewOpenOriginal: "打开原图",
1367
- retry: "重试",
1368
- unknown: "未知"
1369
- },
1370
- en: {
1371
- title: "ChatGPT",
1372
- intro: "Sign in with ChatGPT to use models available to your subscription in DSH.",
1373
- account: "Account",
1374
- signedOut: "Not signed in",
1375
- signedIn: "Signed in",
1376
- plan: "Plan",
1377
- accountId: "Account ID",
1378
- expires: "Token expires",
1379
- storage: "Credential storage",
1380
- storageWindows: "Windows DPAPI (current-user encrypted)",
1381
- storageMacKeychain: "macOS Keychain (encrypted)",
1382
- storageLinuxFile: "Linux user-private file (mode 0600)",
1383
- storageMemory: "Host memory only (not persistent)",
1384
- storageUnavailable: "Credential storage unavailable",
1385
- securityWindows: "The Host encrypts tokens with Windows CurrentUser DPAPI; they never enter the browser, settings.yaml, or logs.",
1386
- securityMacKeychain: "The Host stores tokens in the macOS login Keychain, encrypted on this machine; they never enter the browser, settings.yaml, or logs.",
1387
- securityLinuxFile: "Tokens are stored in a mode-0600 file readable only by the current Linux user, but are not additionally encrypted; same-UID processes, root, backups, and disk snapshots can still read them.",
1388
- securityMemory: "Tokens remain only in Host memory and are lost when the Host exits.",
1389
- securityUnavailable: "The Host cannot safely access credential storage. Fix the file owner or permissions and retry.",
1390
- signIn: "Sign in with ChatGPT",
1391
- signInAgain: "Sign in again",
1392
- cancel: "Cancel sign-in",
1393
- signOut: "Sign out",
1394
- refreshToken: "Refresh credentials",
1395
- pending: "Complete sign-in in your browser.",
1396
- popupBlocked: "The sign-in window was blocked. Use the link below to continue.",
1397
- continueLogin: "Open ChatGPT sign-in",
1398
- loading: "Reading status…",
1399
- connection: "Connection",
1400
- provider: "Provider",
1401
- connectionState: "Connection status",
1402
- connected: "Available",
1403
- untested: "Not tested",
1404
- testConnection: "Test connection",
1405
- testing: "Testing…",
1406
- latency: "Last latency",
1407
- models: "Available models",
1408
- modelsHint: "Checked models appear in the conversation model picker; at least one must remain enabled.",
1409
- proxySettings: "Network Proxy",
1410
- proxyMode: "Proxy Mode",
1411
- proxyModeHint: "GPT and Antigravity (Gemini) share these proxy settings. Supports system proxy detection; changes apply to subsequent requests.",
1412
- proxyModeAuto: "System Proxy (Auto-detect)",
1413
- proxyModeCustom: "Custom Proxy",
1414
- proxyModeDirect: "Direct (No Proxy)",
1415
- customProxyUrl: "Custom Proxy URL",
1416
- customProxyUrlHint: "Supports HTTP/HTTPS/SOCKS5 proxy URLs, e.g. http://127.0.0.1:7890",
1417
- customProxyUrlPlaceholder: "e.g. http://127.0.0.1:7890",
1418
- saveProxyUrl: "Save Proxy URL",
1419
- proxyDetected: "Detected system proxy",
1420
- proxyDetectedEffective: " (active)",
1421
- proxyNoneDetected: "No system proxy detected; connecting directly",
1422
- proxyDirectHint: "All requests connect directly without using any proxy server.",
1423
- enhancements: "Enhancements",
1424
- fastMode: "Fast mode (1.5x speed)",
1425
- fastModeHint: "Generates tokens ~1.5x faster; consumes Codex subscription quota at ~2x–2.5x the normal rate.",
1426
- outputVerbosity: "Output verbosity",
1427
- outputVerbosityHint: "Controls the detail level of final Codex answers without changing reasoning effort.",
1428
- verbosityLow: "Low (keep answers concise)",
1429
- verbosityMedium: "Medium (balance detail and brevity)",
1430
- verbosityHigh: "High (include more detail)",
1431
- reasoningSummary: "Reasoning summary",
1432
- reasoningSummaryHint: "Controls how ChatGPT summarizes its chain of thought; does not change reasoning depth.",
1433
- summaryAuto: "Auto (let model determine detail)",
1434
- summaryConcise: "Concise (brief summary)",
1435
- summaryDetailed: "Detailed (in-depth summary)",
1436
- summaryNone: "None (hide reasoning summary)",
1437
- searchProvider: "Search & Fetch provider",
1438
- searchProviderHint: "Choose whether DSH web tools use the default provider or the Codex proxy-backed source from this ChatGPT subscription.",
1439
- searchProviderDsh: "DSH default",
1440
- searchProviderCodex: "ChatGPT (Proxy tunnel)",
1441
- providerDefault: "Use provider default",
1442
- contextWindows: "Model context windows",
1443
- contextWindowsHint: "Defaults to 272K, with up to 872K for 6 Astra and 1M for GPT-5.6. DSH uses it for compaction and overflow detection; values such as 872K and 512K are accepted.",
1444
- contextWindow: "Context window",
1445
- contextWindowInvalid: "The context window must be a positive integer within the model limit (872K for 6 Astra, 1M for GPT-5.6). K and M suffixes are accepted.",
1446
- saveContextWindow: "Save context window",
1447
- save: "Save",
1448
- tokens: "tokens",
1449
- quickQuota: "Show quick usage beside the composer",
1450
- quickQuotaHint: "Shown only when the current session uses a codex-chatgpt model.",
1451
- quota: "Usage and limits",
1452
- quotaIntro: "Data comes from the ChatGPT Codex usage service and refreshes at most once per minute while visible.",
1453
- refreshQuota: "Refresh usage",
1454
- refreshing: "Refreshing…",
1455
- noQuota: "Your plan did not return any displayable limit windows.",
1456
- quotaSignedOut: "Sign in to view subscription limits.",
1457
- stale: "Showing the last successfully fetched data.",
1458
- updated: "Updated",
1459
- primary: "Primary window",
1460
- secondary: "Secondary window",
1461
- limitWindow: "limit",
1462
- used: "used",
1463
- remaining: "remaining",
1464
- available: "Available",
1465
- unavailable: "Unavailable",
1466
- unlimited: "Unlimited",
1467
- credits: "Credits",
1468
- monthlySpend: "Monthly spend control",
1469
- resetCredits: "Reset credits",
1470
- resetCreditExpires: "Expires",
1471
- useResetCredit: "Use reset credit",
1472
- usingResetCredit: "Using…",
1473
- useResetCreditConfirm: "This immediately spends one reset credit and resets eligible Codex limit windows. Continue?",
1474
- resetCreditUsed: "The reset credit was used and usage data was refreshed.",
1475
- spendControlReached: "Monthly spend control has been reached; new subscription calls may be limited.",
1476
- limit: "Limit",
1477
- exhausted: "Quota exhausted",
1478
- resets: "Resets",
1479
- quickQuotaLabel: "Codex left",
1480
- quickQuotaLoading: "Usage…",
1481
- imageToolRunning: "Generating image",
1482
- imageToolDone: "Generated image",
1483
- imageToolFailed: "Image generation failed",
1484
- image: "Image",
1485
- openImage: "Open image",
1486
- openNamedImage: "Open {name}",
1487
- imageLoading: "Loading image…",
1488
- imageLoadFailed: "Image failed to load. Click to retry",
1489
- imagePreviewClose: "Close",
1490
- imagePreviewOpenOriginal: "Open original",
1491
- retry: "Retry",
1492
- unknown: "Unknown"
1493
- }
1494
- };
1495
- //#endregion
1496
- //#region src/client/styles.ts
1497
- const STYLE_ID$1 = "@eddyskywalker/dsh-chatgpt-subscription/main";
1498
- const CSS = `
1499
- .dsh-codex-page{box-sizing:border-box;color:var(--dsw-alias-label-primary);display:flex;flex-direction:column;gap:20px;max-width:780px;min-width:0;padding:2px 0 30px}
1500
- .dsh-codex-page *{box-sizing:border-box}
1501
- .dsh-codex-title{font-size:15px;font-weight:650;line-height:1.4;margin:0 0 5px}
1502
- .dsh-codex-intro,.dsh-codex-muted{color:var(--dsw-alias-label-secondary);font-size:13px;line-height:1.55;margin:0}
1503
- .dsh-codex-group{border-top:0.5px solid var(--dsw-alias-border-l2);min-width:0}
1504
- .dsh-codex-grouphead{align-items:center;display:flex;gap:12px;justify-content:space-between;min-height:48px}
1505
- .dsh-codex-grouphead h3{font-size:14px;font-weight:650;margin:0}
1506
- .dsh-codex-row{align-items:center;border-bottom:0.5px solid var(--dsw-alias-border-l2);display:flex;gap:20px;justify-content:space-between;min-height:44px;padding:8px 0}
1507
- .dsh-codex-label{color:var(--dsw-alias-label-secondary);font-size:13px;flex:0 0 auto}
1508
- .dsh-codex-value{font-size:13px;min-width:0;overflow-wrap:anywhere;text-align:right}
1509
- .dsh-codex-actions{display:flex;flex-wrap:wrap;gap:8px;justify-content:flex-end;padding-top:12px}
1510
- .dsh-codex-button{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:999px;corner-shape:round;color:var(--dsw-alias-label-primary);cursor:pointer;font:inherit;font-size:13px;line-height:1;padding:8px 13px;white-space:nowrap}
1511
- .dsh-codex-button:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}
1512
- .dsh-codex-button:focus-visible,.dsh-codex-link:focus-visible{outline:2px solid var(--dsw-alias-button-info-fill,#397ee8);outline-offset:2px}
1513
- .dsh-codex-button:disabled{cursor:default;opacity:.5}
1514
- .dsh-codex-button-primary{background:var(--dsw-alias-button-info-fill,#397ee8);border-color:transparent;color:var(--dsw-alias-button-info-label,#fff)}
1515
- .dsh-codex-notice{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:7px;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.55;margin:12px 0 0;padding:10px 12px}
1516
- .dsh-codex-error,.dsh-codex-warning,.dsh-codex-success{font-size:12px;line-height:1.5;margin:8px 0 0}
1517
- .dsh-codex-error{color:var(--dsw-alias-label-danger,#d94b4b)}
1518
- .dsh-codex-warning{color:var(--dsw-alias-label-warning,#c77a18)}
1519
- .dsh-codex-success{color:var(--dsw-alias-label-success,#2e9b62)}
1520
- .dsh-codex-errorbar{align-items:center;background:color-mix(in srgb,var(--dsw-alias-label-danger,#d94b4b) 9%,transparent);border:0.5px solid color-mix(in srgb,var(--dsw-alias-label-danger,#d94b4b) 28%,transparent);border-radius:7px;color:var(--dsw-alias-label-danger,#d94b4b);display:flex;font-size:13px;gap:12px;justify-content:space-between;padding:10px 12px}
1521
- .dsh-codex-link{color:var(--dsw-alias-label-link,#3278d4);display:inline-block;font-size:13px;margin-top:8px}
1522
- .dsh-codex-models-hint{padding-top:10px}
1523
- .dsh-codex-models{display:flex;flex-wrap:wrap;gap:7px;padding-top:8px}
1524
- .dsh-codex-models label{cursor:pointer;display:block;position:relative}
1525
- .dsh-codex-models input{position:absolute;opacity:0;pointer-events:none}
1526
- .dsh-codex-models span{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:6px;color:var(--dsw-alias-label-secondary);display:block;font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:11px;padding:5px 8px}
1527
- .dsh-codex-models input:checked+span{background:color-mix(in srgb,var(--dsw-alias-button-info-fill,#397ee8) 14%,var(--dsw-alias-bg-layer-2));border-color:color-mix(in srgb,var(--dsw-alias-button-info-fill,#397ee8) 55%,var(--dsw-alias-border-l2));color:var(--dsw-alias-label-primary)}
1528
- .dsh-codex-models input:focus-visible+span{outline:2px solid var(--dsw-alias-button-info-fill,#397ee8);outline-offset:2px}
1529
- .dsh-codex-models input:disabled+span{cursor:default;opacity:.5}
1530
- .dsh-codex-pref-row{align-items:center;border-bottom:0.5px solid var(--dsw-alias-border-l2);display:flex;gap:16px;justify-content:space-between;min-height:58px;padding:10px 0}
1531
- .dsh-codex-pref-row strong,.dsh-codex-check strong{display:block;font-size:13px;font-weight:600;line-height:1.35}
1532
- .dsh-codex-segments{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:8px;display:inline-flex;flex-wrap:nowrap;gap:3px;padding:3px}
1533
- .dsh-codex-segments label{cursor:pointer;display:block;margin:0}
1534
- .dsh-codex-segments input{position:absolute;opacity:0;pointer-events:none}
1535
- .dsh-codex-segments span,.dsh-codex-segments button{border:0.5px solid transparent;border-radius:6px;color:var(--dsw-alias-label-secondary);display:block;font:inherit;font-size:12px;font-weight:500;line-height:1.2;padding:6px 12px;transition:all .15s ease;user-select:none;white-space:nowrap}
1536
- .dsh-codex-segments button{background:transparent;cursor:pointer}
1537
- .dsh-codex-segments label:hover span,.dsh-codex-segments button:hover:not(:disabled){color:var(--dsw-alias-label-primary)}
1538
- .dsh-codex-segments input:checked+span,.dsh-codex-segments button.active,.dsh-codex-segments button[aria-pressed=true]{background:color-mix(in srgb,var(--dsw-alias-button-info-fill,#397ee8) 22%,var(--dsw-alias-bg-layer-2));border-color:color-mix(in srgb,var(--dsw-alias-button-info-fill,#397ee8) 60%,var(--dsw-alias-border-l2));color:var(--dsw-alias-label-primary);font-weight:600;box-shadow:0 1px 3px rgba(0,0,0,.15)}
1539
- .dsh-codex-segments input:focus-visible+span,.dsh-codex-segments button:focus-visible{outline:2px solid var(--dsw-alias-button-info-fill,#397ee8);outline-offset:2px}
1540
- .dsh-codex-segments input:disabled+span,.dsh-codex-segments button:disabled{cursor:default;opacity:.5}
1541
- .dsh-codex-select,.dsh-codex-capacity-control input{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:7px;color:var(--dsw-alias-label-primary);font:inherit;font-size:12px;padding:7px 9px}
1542
- .dsh-codex-select{max-width:210px;min-width:150px}
1543
- .dsh-codex-context-settings{border-bottom:0.5px solid var(--dsw-alias-border-l2);display:grid;gap:10px;padding:12px 0}
1544
- .dsh-codex-context-settings>div>strong{font-size:13px;font-weight:600}
1545
- .dsh-codex-context-row{align-items:center;display:flex;font-size:12px;gap:14px;justify-content:space-between}
1546
- .dsh-codex-proxy-control{align-items:center;display:flex;gap:8px;width:100%}
1547
- .dsh-codex-proxy-control input{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:7px;color:var(--dsw-alias-label-primary);flex:1;font:inherit;font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:12px;min-width:220px;padding:7px 10px;text-align:left}
1548
- .dsh-codex-proxy-status{align-items:center;color:var(--dsw-alias-label-secondary);display:flex;flex-wrap:wrap;font-size:12px;gap:6px;margin:6px 0 0}
1549
- .dsh-codex-proxy-tag{background:color-mix(in srgb,var(--dsw-alias-button-info-fill,#397ee8) 18%,transparent);border:0.5px solid color-mix(in srgb,var(--dsw-alias-button-info-fill,#397ee8) 45%,transparent);border-radius:4px;color:var(--dsw-alias-label-primary);font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:11px;padding:2px 6px}
1550
- .dsh-codex-capacity-control{align-items:center;display:flex;gap:7px}
1551
- .dsh-codex-capacity-control input{font-family:ui-monospace,SFMono-Regular,Consolas,monospace;text-align:right;width:90px}
1552
- .dsh-codex-capacity-control input[type=number]{width:110px}
1553
- .dsh-codex-capacity-control small{color:var(--dsw-alias-label-tertiary);font-size:11px;width:42px}
1554
- .dsh-codex-context-save{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:7px;color:var(--dsw-alias-label-primary);cursor:pointer;font:inherit;font-size:12px;line-height:1;padding:8px 10px;white-space:nowrap}
1555
- .dsh-codex-context-save:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}
1556
- .dsh-codex-context-save:focus-visible{outline:2px solid var(--dsw-alias-button-info-fill,#397ee8);outline-offset:2px}
1557
- .dsh-codex-context-save:disabled{cursor:default;opacity:.45}
1558
- .dsh-codex-check{align-items:flex-start;border-bottom:0.5px solid var(--dsw-alias-border-l2);cursor:pointer;display:flex;gap:10px;padding:12px 0}
1559
- .dsh-codex-check input{flex:none;margin-top:2px}
1560
- .dsh-codex-check small{color:var(--dsw-alias-label-secondary);display:block;font-size:12px;line-height:1.45;margin-top:2px}
1561
- .dsh-codex-quota-card{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:8px;margin-top:12px;padding:12px}
1562
- .dsh-codex-quota-title{align-items:center;display:flex;font-size:13px;gap:8px;justify-content:space-between}
1563
- .dsh-codex-quota-title span{color:var(--dsw-alias-label-tertiary);font-size:11px;text-transform:uppercase}
1564
- .dsh-codex-quota-fact{align-items:flex-start;border-bottom:0.5px solid var(--dsw-alias-border-l2);display:flex;font-size:12px;gap:12px;justify-content:space-between;line-height:1.45;padding:10px 0}
1565
- .dsh-codex-quota-fact span{color:var(--dsw-alias-label-secondary);flex:none}
1566
- .dsh-codex-quota-fact strong{font-weight:600;min-width:0;overflow-wrap:anywhere;text-align:right}
1567
- .dsh-codex-reset-credits{align-items:center;border-bottom:0.5px solid var(--dsw-alias-border-l2);display:flex;gap:16px;justify-content:space-between;padding:10px 0}
1568
- .dsh-codex-reset-credits-info{display:grid;font-size:12px;gap:2px;grid-template-columns:auto auto}
1569
- .dsh-codex-reset-credits-info>span{color:var(--dsw-alias-label-secondary)}
1570
- .dsh-codex-reset-credits-info>strong{font-weight:650;text-align:right}
1571
- .dsh-codex-reset-credits-info>small{color:var(--dsw-alias-label-tertiary);font-size:11px;grid-column:1/-1}
1572
- .dsh-codex-meter-wrap{margin-top:13px}
1573
- .dsh-codex-meter-label,.dsh-codex-meter-meta{display:flex;gap:10px;justify-content:space-between}
1574
- .dsh-codex-meter-label{font-size:12px;margin-bottom:6px}
1575
- .dsh-codex-meter-meta{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:1.45;margin-top:6px}
1576
- .dsh-codex-meter{background:var(--dsw-alias-bg-layer-1,rgba(127,127,127,.15));border-radius:999px;corner-shape:round;height:7px;overflow:hidden;width:100%}
1577
- .dsh-codex-meter>span{background:var(--dsw-alias-button-info-fill,#397ee8);border-radius:inherit;display:block;height:100%;max-width:100%;min-width:0;transition:width .25s ease}
1578
- .dsh-codex-meter-warning>span{background:var(--dsw-alias-label-warning,#d58a24)}
1579
- .dsh-codex-meter-danger>span{background:var(--dsw-alias-label-danger,#d94b4b)}
1580
- .dsh-codex-empty{border:0.5px dashed var(--dsw-alias-border-l2);border-radius:7px;color:var(--dsw-alias-label-tertiary);font-size:12px;margin:12px 0 0;padding:16px;text-align:center}
1581
- .dsh-codex-timestamp{color:var(--dsw-alias-label-tertiary);font-size:11px;margin:10px 0 0;text-align:right}
1582
- .dsh-codex-skeleton{display:grid;gap:9px;padding-top:10px}
1583
- .dsh-codex-skeleton span{animation:dsh-codex-pulse 1.4s ease-in-out infinite;background:var(--dsw-alias-bg-layer-2);border-radius:5px;height:42px}
1584
- .dsh-codex-skeleton span:nth-child(2){animation-delay:.12s}.dsh-codex-skeleton span:nth-child(3){animation-delay:.24s}
1585
- .dsh-codex-sr{height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;clip:rect(0 0 0 0);white-space:nowrap}
1586
- .dsh-codex-composer-quota{align-items:center;background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:999px;corner-shape:round;color:var(--dsw-alias-label-secondary);display:inline-flex;font-size:11px;gap:6px;height:28px;line-height:1;max-width:160px;padding:0 9px;white-space:nowrap}
1587
- .dsh-codex-composer-quota strong{color:var(--dsw-alias-label-primary);font-size:11px;font-weight:650}
1588
- .dsh-codex-composer-quota[data-level=warning] strong{color:var(--dsw-alias-label-warning,#d58a24)}
1589
- .dsh-codex-composer-quota[data-level=danger] strong{color:var(--dsw-alias-label-danger,#d94b4b)}
1590
- .dsh-codex-image-tool{border:0.5px solid var(--dsw-alias-border-l2);border-radius:8px;display:flex;flex-direction:column;gap:8px;margin:4px 0 4px 4px;max-width:520px;padding:10px 12px}
1591
- .dsh-codex-image-tool-head{align-items:center;display:flex;font-size:13px;gap:8px;min-width:0}
1592
- .dsh-codex-image-dot{background:var(--dsw-alias-button-info-fill,#397ee8);border-radius:50%;corner-shape:round;display:inline-block;flex:none;height:8px;width:8px}
1593
- .dsh-codex-image-tool[data-state=error] .dsh-codex-image-dot{background:var(--dsw-alias-label-danger,#d94b4b)}
1594
- .dsh-codex-image-title{font-weight:600}
1595
- .dsh-codex-image-summary{color:var(--dsw-alias-label-tertiary);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
1596
- .dsh-codex-image-grid{display:flex;flex-wrap:wrap;gap:8px}
1597
- .dsh-codex-image-preview{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:8px;display:block;max-height:260px;max-width:min(100%,360px);object-fit:contain}
1598
- .dsh-codex-image-loading,.dsh-codex-image-failed{border:0.5px dashed var(--dsw-alias-border-l2);border-radius:8px;color:var(--dsw-alias-label-tertiary);display:inline-flex;font-size:12px;line-height:1.4;padding:18px 20px}
1599
- .dsh-codex-image-failed{color:var(--dsw-alias-label-danger,#d94b4b)}
1600
- @keyframes dsh-codex-pulse{0%,100%{opacity:.45}50%{opacity:.85}}
1601
- @media(max-width:560px){.dsh-codex-row,.dsh-codex-pref-row,.dsh-codex-quota-fact,.dsh-codex-reset-credits{align-items:flex-start;flex-direction:column;gap:3px}.dsh-codex-value,.dsh-codex-quota-fact strong{text-align:left}.dsh-codex-actions{justify-content:flex-start}.dsh-codex-grouphead{align-items:flex-start;flex-direction:column;gap:0;padding:12px 0}.dsh-codex-meter-meta{align-items:flex-start;flex-direction:column;gap:2px}.dsh-codex-errorbar{align-items:flex-start;flex-direction:column}.dsh-codex-segments{width:100%}.dsh-codex-select{max-width:none;width:100%}.dsh-codex-context-row{align-items:flex-start;flex-direction:column;gap:5px}.dsh-codex-segments label{flex:1}.dsh-codex-segments span{text-align:center}.dsh-codex-image-tool{max-width:100%;margin-left:0}}
1602
- @media(prefers-reduced-motion:reduce){.dsh-codex-meter>span{transition:none}.dsh-codex-skeleton span{animation:none}}
1603
- `;
1604
- function installStyles() {
1605
- if (document.querySelector(`style[data-plugin-css="${STYLE_ID$1}"]`) !== null) return () => void 0;
1606
- const element = document.createElement("style");
1607
- element.dataset.plugin = "@eddyskywalker/dsh-chatgpt-subscription";
1608
- element.dataset.pluginCss = STYLE_ID$1;
1609
- element.textContent = CSS;
1610
- document.head.appendChild(element);
1611
- return () => element.remove();
1612
- }
1613
- //#endregion
1614
1243
  //#region src/client/antigravity/locales.ts
1615
1244
  const NS_ANTIGRAVITY = "dsh-antigravity";
1616
- const zh = {
1245
+ const zh$3 = {
1617
1246
  title: "Antigravity",
1618
1247
  pageDesc: "登录 Google Antigravity / Cloud Code Assist,并查看当前账号的共享额度。",
1619
1248
  account: "已登录",
@@ -1664,7 +1293,7 @@ window.__ModuleLoader__.load({
1664
1293
  loginFailed: "登录失败",
1665
1294
  tokens: "tokens"
1666
1295
  };
1667
- const en = {
1296
+ const en$3 = {
1668
1297
  title: "Antigravity",
1669
1298
  pageDesc: "Sign in to Google Antigravity / Cloud Code Assist and view shared quotas.",
1670
1299
  account: "Signed in",
@@ -1715,17 +1344,17 @@ window.__ModuleLoader__.load({
1715
1344
  loginFailed: "Login failed",
1716
1345
  tokens: "tokens"
1717
1346
  };
1718
- const dictionaries = {
1719
- zh,
1720
- "zh-CN": zh,
1721
- en,
1722
- "en-US": en
1347
+ const dictionaries$3 = {
1348
+ zh: zh$3,
1349
+ "zh-CN": zh$3,
1350
+ en: en$3,
1351
+ "en-US": en$3
1723
1352
  };
1724
1353
  //#endregion
1725
1354
  //#region src/client/antigravity/AntigravitySection.tsx
1726
- const API$1 = "/antigravity/api";
1727
- async function fetchApi$1(path, options) {
1728
- const res = await fetch(`${API$1}${path}`, {
1355
+ const API$5 = "/antigravity/api";
1356
+ async function fetchApi$5(path, options) {
1357
+ const res = await fetch(`${API$5}${path}`, {
1729
1358
  ...options,
1730
1359
  headers: {
1731
1360
  "Content-Type": "application/json",
@@ -1736,14 +1365,14 @@ window.__ModuleLoader__.load({
1736
1365
  if (!res.ok || !json.ok) throw new Error(json.error || `HTTP ${res.status}`);
1737
1366
  return json.value;
1738
1367
  }
1739
- function parsePositiveCapacity(value) {
1368
+ function parsePositiveCapacity$2(value) {
1740
1369
  const matched = value.trim().toLowerCase().replace(/[,_\s]/g, "").match(/^(\d+(?:\.\d+)?)(k|m)?$/);
1741
1370
  if (matched === null) return null;
1742
1371
  const multiplier = matched[2] === "m" ? 1e6 : matched[2] === "k" ? 1e3 : 1;
1743
1372
  const parsed = Number(matched[1]) * multiplier;
1744
1373
  return Number.isSafeInteger(parsed) && parsed >= 1 ? parsed : null;
1745
1374
  }
1746
- function formatCapacity(value) {
1375
+ function formatCapacity$2(value) {
1747
1376
  if (value >= 1e6 && value % 1e5 === 0) return `${value / 1e6}M`;
1748
1377
  if (value % 1e3 === 0) return `${value / 1e3}K`;
1749
1378
  return String(value);
@@ -1759,7 +1388,7 @@ window.__ModuleLoader__.load({
1759
1388
  if (hours > 0) return `${hours}h ${mins % 60}m`;
1760
1389
  return `${mins}m`;
1761
1390
  }
1762
- function formatDate(ms) {
1391
+ function formatDate$2(ms) {
1763
1392
  if (!ms || ms <= 0) return "—";
1764
1393
  try {
1765
1394
  return new Intl.DateTimeFormat(void 0, {
@@ -1779,7 +1408,7 @@ window.__ModuleLoader__.load({
1779
1408
  const [loginProgress, setLoginProgress] = (0, react.useState)(null);
1780
1409
  const [contextDrafts, setContextDrafts] = (0, react.useState)({});
1781
1410
  const [savingModel, setSavingModel] = (0, react.useState)(null);
1782
- const t = zh;
1411
+ const t = zh$3;
1783
1412
  const notifyChange = (0, react.useCallback)(() => {
1784
1413
  onModelChange?.();
1785
1414
  loadModelDirectory?.();
@@ -1790,12 +1419,12 @@ window.__ModuleLoader__.load({
1790
1419
  setValidationUrl(null);
1791
1420
  }
1792
1421
  try {
1793
- const data = await fetchApi$1("/status");
1422
+ const data = await fetchApi$5("/status");
1794
1423
  setStatus(data);
1795
1424
  const drafts = {};
1796
1425
  for (const m of data.models) {
1797
1426
  const val = data.contextWindowOverrides[m.id] || m.defaultContextWindow;
1798
- drafts[m.id] = formatCapacity(val);
1427
+ drafts[m.id] = formatCapacity$2(val);
1799
1428
  }
1800
1429
  setContextDrafts(drafts);
1801
1430
  } catch (err) {
@@ -1822,11 +1451,11 @@ window.__ModuleLoader__.load({
1822
1451
  setError(null);
1823
1452
  setValidationUrl(null);
1824
1453
  setLoginProgress(null);
1825
- const flow = await fetchApi$1("/login", { method: "POST" });
1454
+ const flow = await fetchApi$5("/login", { method: "POST" });
1826
1455
  if (flow.authUrl) window.open(flow.authUrl, "_blank");
1827
1456
  const pollTimer = setInterval(async () => {
1828
1457
  try {
1829
- const pollStatus = await fetchApi$1("/login/status");
1458
+ const pollStatus = await fetchApi$5("/login/status");
1830
1459
  if (pollStatus.progress) setLoginProgress(pollStatus.progress);
1831
1460
  if (pollStatus.status === "complete") {
1832
1461
  clearInterval(pollTimer);
@@ -1858,7 +1487,7 @@ window.__ModuleLoader__.load({
1858
1487
  try {
1859
1488
  setBusy("quota");
1860
1489
  setError(null);
1861
- const updated = await fetchApi$1("/quota", { method: "POST" });
1490
+ const updated = await fetchApi$5("/quota", { method: "POST" });
1862
1491
  setStatus(updated);
1863
1492
  } catch (err) {
1864
1493
  setError(err instanceof Error ? err.message : String(err));
@@ -1870,7 +1499,7 @@ window.__ModuleLoader__.load({
1870
1499
  try {
1871
1500
  setBusy("logout");
1872
1501
  setError(null);
1873
- const updated = await fetchApi$1("/logout", { method: "POST" });
1502
+ const updated = await fetchApi$5("/logout", { method: "POST" });
1874
1503
  setStatus(updated);
1875
1504
  notifyChange();
1876
1505
  } catch (err) {
@@ -1885,7 +1514,7 @@ window.__ModuleLoader__.load({
1885
1514
  const nextEnabled = checked ? [.../* @__PURE__ */ new Set([...currentEnabled, modelId])] : currentEnabled.filter((id) => id !== modelId);
1886
1515
  if (nextEnabled.length === 0) return;
1887
1516
  try {
1888
- const updated = await fetchApi$1("/models", {
1517
+ const updated = await fetchApi$5("/models", {
1889
1518
  method: "POST",
1890
1519
  body: JSON.stringify({ enabledModelIds: nextEnabled })
1891
1520
  });
@@ -1899,7 +1528,7 @@ window.__ModuleLoader__.load({
1899
1528
  if (!status) return;
1900
1529
  const nextEnabled = selectAll ? status.models.map((m) => m.id) : [status.models[0].id];
1901
1530
  try {
1902
- const updated = await fetchApi$1("/models", {
1531
+ const updated = await fetchApi$5("/models", {
1903
1532
  method: "POST",
1904
1533
  body: JSON.stringify({ enabledModelIds: nextEnabled })
1905
1534
  });
@@ -1911,7 +1540,7 @@ window.__ModuleLoader__.load({
1911
1540
  };
1912
1541
  const handleUpdateEffort = async (effort) => {
1913
1542
  try {
1914
- const updated = await fetchApi$1("/settings", {
1543
+ const updated = await fetchApi$5("/settings", {
1915
1544
  method: "POST",
1916
1545
  body: JSON.stringify({ defaultReasoningEffort: effort })
1917
1546
  });
@@ -1923,7 +1552,7 @@ window.__ModuleLoader__.load({
1923
1552
  };
1924
1553
  const handleSaveContextWindow = async (modelId) => {
1925
1554
  const raw = contextDrafts[modelId] || "";
1926
- const parsed = parsePositiveCapacity(raw);
1555
+ const parsed = parsePositiveCapacity$2(raw);
1927
1556
  if (parsed === null) {
1928
1557
  setError(`无效的上下文容量值: ${raw}`);
1929
1558
  return;
@@ -1931,14 +1560,14 @@ window.__ModuleLoader__.load({
1931
1560
  try {
1932
1561
  setSavingModel(modelId);
1933
1562
  setError(null);
1934
- const updated = await fetchApi$1("/settings", {
1563
+ const updated = await fetchApi$5("/settings", {
1935
1564
  method: "POST",
1936
1565
  body: JSON.stringify({ contextWindowOverrides: { [modelId]: parsed } })
1937
1566
  });
1938
1567
  setStatus(updated);
1939
1568
  setContextDrafts((prev) => ({
1940
1569
  ...prev,
1941
- [modelId]: formatCapacity(parsed)
1570
+ [modelId]: formatCapacity$2(parsed)
1942
1571
  }));
1943
1572
  notifyChange();
1944
1573
  } catch (err) {
@@ -2005,7 +1634,7 @@ window.__ModuleLoader__.load({
2005
1634
  children: t.expires
2006
1635
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2007
1636
  className: "dsha-value",
2008
- children: formatDate(Date.now() + 864e5 * 30)
1637
+ children: formatDate$2(Date.now() + 864e5 * 30)
2009
1638
  })]
2010
1639
  })
2011
1640
  ] }),
@@ -2279,78 +1908,174 @@ window.__ModuleLoader__.load({
2279
1908
  });
2280
1909
  }
2281
1910
  //#endregion
2282
- //#region src/client/antigravity/styles.ts
2283
- const STYLE_ID = "dsh-antigravity-settings-style";
2284
- function installAntigravityStyles() {
2285
- if (typeof document === "undefined" || document.getElementById("dsh-antigravity-settings-style")) return;
2286
- const style = document.createElement("style");
2287
- style.id = STYLE_ID;
2288
- style.textContent = `
2289
- .dsha-page{box-sizing:border-box;color:var(--dsw-alias-label-primary);display:flex;flex-direction:column;gap:20px;max-width:780px;min-width:0;padding:2px 0 30px}
2290
- .dsha-page *{box-sizing:border-box}
2291
- .dsha-group{border-top:0.5px solid var(--dsw-alias-border-l2);min-width:0}
2292
- .dsha-group:first-of-type{border-top:0}
2293
- .dsha-grouphead{align-items:center;display:flex;gap:12px;justify-content:space-between;min-height:48px}
2294
- .dsha-grouphead h3{font-size:14px;font-weight:650;margin:0;color:var(--dsw-alias-label-primary)}
2295
- .dsha-row{align-items:center;border-bottom:0.5px solid var(--dsw-alias-border-l2);display:flex;gap:20px;justify-content:space-between;min-height:44px;padding:8px 0}
2296
- .dsha-label{color:var(--dsw-alias-label-secondary);font-size:13px;flex:0 0 auto}
2297
- .dsha-value{font-size:13px;min-width:0;overflow-wrap:anywhere;text-align:right;color:var(--dsw-alias-label-primary)}
2298
- .dsha-actions{display:flex;flex-wrap:wrap;gap:8px;justify-content:flex-end;padding-top:12px}
2299
- .dsha-btn{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:999px;corner-shape:round;color:var(--dsw-alias-label-primary);cursor:pointer;font:inherit;font-size:13px;line-height:1;padding:8px 13px;white-space:nowrap}
2300
- .dsha-btn:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}
2301
- .dsha-btn:disabled{cursor:default;opacity:.5}
2302
- .dsha-btn-primary{background:var(--dsw-alias-button-info-fill,#397ee8);border-color:transparent;color:var(--dsw-alias-button-info-label,#fff)}
2303
- .dsha-btn-primary:hover:not(:disabled){opacity:.9}
2304
- .dsha-notice{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:7px;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.55;margin:12px 0 0;padding:10px 12px}
2305
- .dsha-muted{color:var(--dsw-alias-label-secondary);font-size:13px;line-height:1.55;margin:0}
2306
- .dsha-models-hint{padding-top:10px}
2307
- .dsha-models{display:flex;flex-wrap:wrap;gap:7px;padding-top:8px}
2308
- .dsha-models label{cursor:pointer;display:block;position:relative}
2309
- .dsha-models input{position:absolute;opacity:0;pointer-events:none}
2310
- .dsha-models span{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:6px;color:var(--dsw-alias-label-secondary);display:block;font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:11px;padding:5px 8px;transition:all .15s ease}
2311
- .dsha-models input:checked+span{background:color-mix(in srgb,var(--dsw-alias-button-info-fill,#397ee8) 14%,var(--dsw-alias-bg-layer-2));border-color:color-mix(in srgb,var(--dsw-alias-button-info-fill,#397ee8) 55%,var(--dsw-alias-border-l2));color:var(--dsw-alias-label-primary);font-weight:600}
2312
- .dsha-models input:disabled+span{cursor:default;opacity:.5}
2313
- .dsha-pref-row{align-items:center;border-bottom:0.5px solid var(--dsw-alias-border-l2);display:flex;gap:16px;justify-content:space-between;min-height:58px;padding:10px 0}
2314
- .dsha-pref-row strong{display:block;font-size:13px;font-weight:600;line-height:1.35;color:var(--dsw-alias-label-primary)}
2315
- .dsha-select{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:7px;color:var(--dsw-alias-label-primary);font:inherit;font-size:12px;padding:7px 9px;max-width:220px;min-width:160px}
2316
- .dsha-context-settings{border-bottom:0.5px solid var(--dsw-alias-border-l2);display:grid;gap:10px;padding:12px 0}
2317
- .dsha-context-settings>div>strong{font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary)}
2318
- .dsha-context-row{align-items:center;display:flex;font-size:12px;gap:14px;justify-content:space-between}
2319
- .dsha-capacity-control{align-items:center;display:flex;gap:7px}
2320
- .dsha-capacity-control input{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:7px;color:var(--dsw-alias-label-primary);font:inherit;font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:12px;padding:7px 9px;text-align:right;width:90px}
2321
- .dsha-capacity-control small{color:var(--dsw-alias-label-tertiary);font-size:11px;width:42px}
2322
- .dsha-context-save{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:7px;color:var(--dsw-alias-label-primary);cursor:pointer;font:inherit;font-size:12px;line-height:1;padding:8px 10px;white-space:nowrap}
2323
- .dsha-context-save:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}
2324
- .dsha-context-save:disabled{cursor:default;opacity:.45}
2325
- .dsha-quota-card{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:8px;margin-top:12px;padding:12px}
2326
- .dsha-quota-title{align-items:center;display:flex;font-size:13px;gap:8px;justify-content:space-between;color:var(--dsw-alias-label-primary);font-weight:650}
2327
- .dsha-quota-title span{color:var(--dsw-alias-label-tertiary);font-size:11px;text-transform:uppercase}
2328
- .dsha-meter-wrap{margin-top:13px}
2329
- .dsha-meter-label,.dsha-meter-meta{display:flex;gap:10px;justify-content:space-between}
2330
- .dsha-meter-label{font-size:12px;margin-bottom:6px;color:var(--dsw-alias-label-primary)}
2331
- .dsha-meter-meta{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:1.45;margin-top:6px}
2332
- .dsha-meter{background:var(--dsw-alias-bg-layer-1,rgba(127,127,127,.15));border-radius:999px;corner-shape:round;height:7px;overflow:hidden;width:100%}
2333
- .dsha-meter>span{background:var(--dsw-alias-button-info-fill,#397ee8);border-radius:inherit;display:block;height:100%;max-width:100%;min-width:0;transition:width .25s ease}
2334
- .dsha-meter-green>span{background:var(--dsw-alias-label-success,#10b981)}
2335
- .dsha-meter-cyan>span{background:#06b6d4}
2336
- .dsha-timestamp{color:var(--dsw-alias-label-tertiary);font-size:11px;margin:10px 0 0;text-align:right}
2337
- .dsha-mini-btn{border:0;background:transparent;color:var(--dsw-alias-label-link,#397ee8);font-size:12px;line-height:18px;cursor:pointer;padding:0;margin-left:8px}
2338
- .dsha-mini-btn:hover{text-decoration:underline}
2339
- .dsha-empty{border:0.5px dashed var(--dsw-alias-border-l2);border-radius:7px;color:var(--dsw-alias-label-tertiary);font-size:12px;margin:12px 0 0;padding:16px;text-align:center}
2340
- .dsha-error{color:var(--dsw-alias-label-danger,#d94b4b);font-size:12px;margin:8px 0 0}
2341
- .dsha-composer-quota{align-items:center;background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:999px;corner-shape:round;color:var(--dsw-alias-label-secondary);cursor:pointer;display:inline-flex;font-size:11px;gap:6px;height:28px;line-height:1;max-width:160px;padding:0 9px;white-space:nowrap;user-select:none;transition:all .15s ease}
2342
- .dsha-composer-quota:hover{background:var(--dsw-alias-interactive-bg-hover);border-color:var(--dsw-alias-border-l1,var(--dsw-alias-border-l2))}
2343
- .dsha-composer-quota strong{color:var(--dsw-alias-label-primary);font-size:11px;font-weight:650}
2344
- .dsha-composer-quota[data-level=warning] strong{color:var(--dsw-alias-label-warning,#d58a24)}
2345
- .dsha-composer-quota[data-level=danger] strong{color:var(--dsw-alias-label-danger,#d94b4b)}
2346
- `;
2347
- document.head.append(style);
2348
- }
1911
+ //#region src/shared/command-code-contracts.ts
1912
+ /** Every level, in escalating order; the settings card renders exactly these. */
1913
+ const COMMAND_CODE_REASONING_EFFORTS = [
1914
+ "minimal",
1915
+ "low",
1916
+ "medium",
1917
+ "high",
1918
+ "xhigh",
1919
+ "max"
1920
+ ];
2349
1921
  //#endregion
2350
- //#region src/client/antigravity/AntigravityComposerQuota.tsx
2351
- const API = "/antigravity/api";
2352
- async function fetchApi(path, options) {
2353
- const res = await fetch(`${API}${path}`, {
1922
+ //#region src/client/command-code/locales.ts
1923
+ const NS_COMMAND_CODE = "dsh-command-code";
1924
+ const zh$2 = {
1925
+ title: "Command Code",
1926
+ pageDesc: "登录 Command Code,管理可用模型、上下文窗口与额度。",
1927
+ account: "账号",
1928
+ signedIn: "已登录",
1929
+ signedOut: "未登录",
1930
+ keyName: "密钥名称",
1931
+ organization: "组织",
1932
+ plan: "套餐",
1933
+ planUnknown: "—",
1934
+ subscription: "订阅状态",
1935
+ renews: "续费于",
1936
+ authenticatedAt: "登录时间",
1937
+ storage: "凭据存储",
1938
+ storageNotice: "API Key 由 Host 保存于本地安全存储 ($DSH_HOME/storages/command-code-credentials.json),不会进入浏览器。",
1939
+ signIn: "浏览器登录",
1940
+ signInAgain: "重新登录",
1941
+ signingIn: "登录中...",
1942
+ signOut: "注销",
1943
+ cancelSignIn: "取消登录",
1944
+ openSignInPage: "打开登录页面",
1945
+ apiKeySection: "手动填写 API Key",
1946
+ apiKeyHint: "在 Command Code Studio 创建 API Key 后粘贴到此处;浏览器登录不可用时使用。",
1947
+ apiKeyPlaceholder: "粘贴 Command Code API Key",
1948
+ apiKeySave: "保存并验证",
1949
+ apiKeySaving: "验证中...",
1950
+ apiKeySaved: "已保存",
1951
+ connection: "连接",
1952
+ provider: "Provider",
1953
+ providerValue: "Command Code · command-code",
1954
+ connectionState: "连接状态",
1955
+ connected: "已连接",
1956
+ untested: "未测试",
1957
+ testConnection: "测试连接",
1958
+ testingConnection: "测试中...",
1959
+ routeOwned: "模型路由由本插件提供",
1960
+ routeConflict: "模型路由已被其他 Provider 占用:{detail}。请在 DSH 设置中移除重复的 Provider 配置后重试。",
1961
+ modelsSection: "模型",
1962
+ modelsHint: "勾选后模型才会出现在对话页的模型选择列表中;至少保留一个。",
1963
+ refreshCatalog: "刷新模型目录",
1964
+ refreshingCatalog: "刷新中...",
1965
+ selectAll: "全选",
1966
+ unselectAll: "全不选",
1967
+ wireAnthropic: "Anthropic",
1968
+ wireOpenai: "OpenAI",
1969
+ enhanced: "增强功能",
1970
+ defaultReasoningEffort: "默认思考深度",
1971
+ defaultReasoningEffortHint: "控制支持推理的模型在未单独指定时的思考强度。Anthropic 模型会映射为 thinking 预算。",
1972
+ defaultEffortAuto: "使用 Provider 默认值",
1973
+ contextWindowSection: "模型上下文窗口",
1974
+ contextWindowHint: "默认取 Command Code 模型目录的 context length;可覆盖为自定义容量,该值用于 DSH 的压缩与溢出判断,可输入 1M、512K、200K 等容量。",
1975
+ save: "保存",
1976
+ saved: "已保存",
1977
+ saving: "保存中...",
1978
+ quotaSection: "用量与配额",
1979
+ quotaDesc: "数据来自 Command Code 账户接口,页面可见时最多每 60 秒刷新一次。",
1980
+ refreshQuota: "刷新用量",
1981
+ refreshingQuota: "刷新中...",
1982
+ quotaEmpty: "暂无配额数据,点击「刷新用量」重试。",
1983
+ quotaCredits: "剩余额度",
1984
+ quotaUnlimited: "不限量",
1985
+ updatedAt: "更新时间:{time}",
1986
+ loading: "加载中...",
1987
+ loginFailed: "登录失败",
1988
+ tokens: "tokens"
1989
+ };
1990
+ const en$2 = {
1991
+ title: "Command Code",
1992
+ pageDesc: "Sign in to Command Code and manage models, context windows, and quota.",
1993
+ account: "Account",
1994
+ signedIn: "Signed in",
1995
+ signedOut: "Not signed in",
1996
+ keyName: "Key name",
1997
+ organization: "Organization",
1998
+ plan: "Plan",
1999
+ planUnknown: "—",
2000
+ subscription: "Subscription",
2001
+ renews: "renews",
2002
+ authenticatedAt: "Signed in at",
2003
+ storage: "Credential storage",
2004
+ storageNotice: "The API key is stored locally by the Host ($DSH_HOME/storages/command-code-credentials.json) and never reaches the browser.",
2005
+ signIn: "Sign in with browser",
2006
+ signInAgain: "Sign in again",
2007
+ signingIn: "Signing in...",
2008
+ signOut: "Sign out",
2009
+ cancelSignIn: "Cancel",
2010
+ openSignInPage: "Open the sign-in page",
2011
+ apiKeySection: "Paste an API key",
2012
+ apiKeyHint: "Create a key in Command Code Studio and paste it here; use this when the browser flow is unavailable.",
2013
+ apiKeyPlaceholder: "Command Code API key",
2014
+ apiKeySave: "Save and verify",
2015
+ apiKeySaving: "Verifying...",
2016
+ apiKeySaved: "Saved",
2017
+ connection: "Connection",
2018
+ provider: "Provider",
2019
+ providerValue: "Command Code · command-code",
2020
+ connectionState: "Connection state",
2021
+ connected: "Connected",
2022
+ untested: "Untested",
2023
+ testConnection: "Test connection",
2024
+ testingConnection: "Testing...",
2025
+ routeOwned: "This plugin serves the model route",
2026
+ routeConflict: "Another provider already owns the model route: {detail}. Remove the duplicate provider configuration in DSH settings and reload.",
2027
+ modelsSection: "Models",
2028
+ modelsHint: "Checked models appear in the DSH conversation model list; keep at least one.",
2029
+ refreshCatalog: "Refresh catalog",
2030
+ refreshingCatalog: "Refreshing...",
2031
+ selectAll: "Select all",
2032
+ unselectAll: "Deselect all",
2033
+ wireAnthropic: "Anthropic",
2034
+ wireOpenai: "OpenAI",
2035
+ enhanced: "Enhanced features",
2036
+ defaultReasoningEffort: "Default reasoning effort",
2037
+ defaultReasoningEffortHint: "Reasoning effort for models that support it when the conversation does not specify one. Anthropic models map it to a thinking budget.",
2038
+ defaultEffortAuto: "Use provider default",
2039
+ contextWindowSection: "Model context window",
2040
+ contextWindowHint: "Defaults to the context length in the Command Code catalog; override it with the capacity DSH should use for compaction and overflow decisions (e.g., 1M, 512K, 200K).",
2041
+ save: "Save",
2042
+ saved: "Saved",
2043
+ saving: "Saving...",
2044
+ quotaSection: "Usage & quota",
2045
+ quotaDesc: "Read from the Command Code account endpoints; refreshes at most once every 60 seconds while visible.",
2046
+ refreshQuota: "Refresh quota",
2047
+ refreshingQuota: "Refreshing...",
2048
+ quotaEmpty: "No quota data yet — use “Refresh quota” to retry.",
2049
+ quotaCredits: "Remaining credits",
2050
+ quotaUnlimited: "Unlimited",
2051
+ updatedAt: "Updated at: {time}",
2052
+ loading: "Loading...",
2053
+ loginFailed: "Sign-in failed",
2054
+ tokens: "tokens"
2055
+ };
2056
+ const dictionaries$2 = {
2057
+ zh: zh$2,
2058
+ "zh-CN": zh$2,
2059
+ en: en$2,
2060
+ "en-US": en$2
2061
+ };
2062
+ //#endregion
2063
+ //#region src/client/command-code/CommandCodeSection.tsx
2064
+ /**
2065
+ * Display label per reasoning level. The locale dictionary only accepts flat
2066
+ * string values, so this nested table lives with the control that renders it.
2067
+ */
2068
+ const EFFORT_LABELS = {
2069
+ minimal: "Minimal",
2070
+ low: "Low",
2071
+ medium: "Medium",
2072
+ high: "High",
2073
+ xhigh: "X-High",
2074
+ max: "Max"
2075
+ };
2076
+ const API$4 = "/command-code/api";
2077
+ async function fetchApi$4(path, options) {
2078
+ const res = await fetch(`${API$4}${path}`, {
2354
2079
  ...options,
2355
2080
  headers: {
2356
2081
  "Content-Type": "application/json",
@@ -2361,10 +2086,2246 @@ window.__ModuleLoader__.load({
2361
2086
  if (!res.ok || !json.ok) throw new Error(json.error || `HTTP ${res.status}`);
2362
2087
  return json.value;
2363
2088
  }
2364
- function selectQuotaBucketForModel(groups, modelId) {
2365
- if (!groups || groups.length === 0) return null;
2366
- const isClaudeOrGpt = modelId ? /claude|gpt/i.test(modelId) : false;
2367
- const targetGroup = groups.find((g) => {
2089
+ /** Parse "1M", "512K", "200000" into a positive integer token count. */
2090
+ function parsePositiveCapacity$1(value) {
2091
+ const matched = value.trim().toLowerCase().replace(/[,_\s]/g, "").match(/^(\d+(?:\.\d+)?)(k|m)?$/);
2092
+ if (matched === null) return null;
2093
+ const multiplier = matched[2] === "m" ? 1e6 : matched[2] === "k" ? 1e3 : 1;
2094
+ const parsed = Number(matched[1]) * multiplier;
2095
+ return Number.isSafeInteger(parsed) && parsed >= 1 ? parsed : null;
2096
+ }
2097
+ function formatCapacity$1(value) {
2098
+ if (value >= 1e6 && value % 1e5 === 0) return `${value / 1e6}M`;
2099
+ if (value >= 1e3 && value % 1e3 === 0) return `${value / 1e3}K`;
2100
+ return String(value);
2101
+ }
2102
+ function formatDate$1(ms) {
2103
+ if (ms === void 0 || ms === null || ms <= 0) return "—";
2104
+ try {
2105
+ return new Intl.DateTimeFormat(void 0, {
2106
+ dateStyle: "medium",
2107
+ timeStyle: "short"
2108
+ }).format(ms);
2109
+ } catch {
2110
+ return "—";
2111
+ }
2112
+ }
2113
+ function formatReset$1(resetsAt) {
2114
+ if (resetsAt === void 0 || resetsAt === null || resetsAt <= 0) return "";
2115
+ const diff = resetsAt - Date.now();
2116
+ if (diff <= 0) return "now";
2117
+ const mins = Math.floor(diff / 6e4);
2118
+ const hours = Math.floor(mins / 60);
2119
+ const days = Math.floor(hours / 24);
2120
+ if (days > 0) return `${days}d ${hours % 24}h`;
2121
+ if (hours > 0) return `${hours}h ${mins % 60}m`;
2122
+ return `${mins}m`;
2123
+ }
2124
+ function CommandCodeSection({ onModelChange, loadModelDirectory }) {
2125
+ const [status, setStatus] = (0, react.useState)(null);
2126
+ const [loading, setLoading] = (0, react.useState)(true);
2127
+ const [busy, setBusy] = (0, react.useState)(null);
2128
+ const [error, setError] = (0, react.useState)(null);
2129
+ const [loginProgress, setLoginProgress] = (0, react.useState)(null);
2130
+ const [contextDrafts, setContextDrafts] = (0, react.useState)({});
2131
+ const [savingModel, setSavingModel] = (0, react.useState)(null);
2132
+ const [apiKey, setApiKey] = (0, react.useState)("");
2133
+ const t = zh$2;
2134
+ const notifyChange = (0, react.useCallback)(() => {
2135
+ onModelChange?.();
2136
+ loadModelDirectory?.();
2137
+ }, [onModelChange, loadModelDirectory]);
2138
+ const loadStatus = (0, react.useCallback)(async (quiet = false) => {
2139
+ if (!quiet) setError(null);
2140
+ try {
2141
+ const data = await fetchApi$4("/status");
2142
+ setStatus(data);
2143
+ const drafts = {};
2144
+ for (const model of data.models) drafts[model.id] = formatCapacity$1(data.contextWindowOverrides[model.id] || model.defaultContextWindow);
2145
+ setContextDrafts(drafts);
2146
+ } catch (err) {
2147
+ if (!quiet) setError(err instanceof Error ? err.message : String(err));
2148
+ } finally {
2149
+ setLoading(false);
2150
+ }
2151
+ }, []);
2152
+ (0, react.useEffect)(() => {
2153
+ loadStatus();
2154
+ const refreshWhenVisible = () => {
2155
+ if (document.visibilityState === "visible") loadStatus(true);
2156
+ };
2157
+ document.addEventListener("visibilitychange", refreshWhenVisible);
2158
+ const timer = window.setInterval(refreshWhenVisible, 6e4);
2159
+ return () => {
2160
+ window.clearInterval(timer);
2161
+ document.removeEventListener("visibilitychange", refreshWhenVisible);
2162
+ };
2163
+ }, [loadStatus]);
2164
+ const handleLogin = async () => {
2165
+ try {
2166
+ setBusy("login");
2167
+ setError(null);
2168
+ setLoginProgress(null);
2169
+ const flow = await fetchApi$4("/login", { method: "POST" });
2170
+ if (flow.authUrl) window.open(flow.authUrl, "_blank");
2171
+ const pollTimer = window.setInterval(() => {
2172
+ (async () => {
2173
+ try {
2174
+ const poll = await fetchApi$4("/login/status");
2175
+ if (poll.progress) setLoginProgress(poll.progress);
2176
+ if (poll.status === "complete") {
2177
+ window.clearInterval(pollTimer);
2178
+ setBusy(null);
2179
+ setLoginProgress(null);
2180
+ await loadStatus();
2181
+ notifyChange();
2182
+ } else if (poll.status === "error") {
2183
+ window.clearInterval(pollTimer);
2184
+ setBusy(null);
2185
+ setLoginProgress(null);
2186
+ setError(poll.error || t.loginFailed);
2187
+ }
2188
+ } catch {}
2189
+ })();
2190
+ }, 1500);
2191
+ window.setTimeout(() => {
2192
+ window.clearInterval(pollTimer);
2193
+ setBusy(null);
2194
+ setLoginProgress(null);
2195
+ }, 300 * 1e3);
2196
+ } catch (err) {
2197
+ setBusy(null);
2198
+ setLoginProgress(null);
2199
+ setError(err instanceof Error ? err.message : String(err));
2200
+ }
2201
+ };
2202
+ const handleApiKey = async () => {
2203
+ try {
2204
+ setBusy("apikey");
2205
+ setError(null);
2206
+ const updated = await fetchApi$4("/login/apikey", {
2207
+ method: "POST",
2208
+ body: JSON.stringify({ apiKey })
2209
+ });
2210
+ setStatus(updated);
2211
+ setApiKey("");
2212
+ notifyChange();
2213
+ } catch (err) {
2214
+ setError(err instanceof Error ? err.message : String(err));
2215
+ } finally {
2216
+ setBusy(null);
2217
+ }
2218
+ };
2219
+ const handleLogout = async () => {
2220
+ try {
2221
+ setBusy("logout");
2222
+ setError(null);
2223
+ const updated = await fetchApi$4("/logout", { method: "POST" });
2224
+ setStatus(updated);
2225
+ notifyChange();
2226
+ } catch (err) {
2227
+ setError(err instanceof Error ? err.message : String(err));
2228
+ } finally {
2229
+ setBusy(null);
2230
+ }
2231
+ };
2232
+ const handleRefreshQuota = async () => {
2233
+ try {
2234
+ setBusy("quota");
2235
+ setError(null);
2236
+ const updated = await fetchApi$4("/quota", { method: "POST" });
2237
+ setStatus(updated);
2238
+ } catch (err) {
2239
+ setError(err instanceof Error ? err.message : String(err));
2240
+ } finally {
2241
+ setBusy(null);
2242
+ }
2243
+ };
2244
+ const handleRefreshCatalog = async () => {
2245
+ try {
2246
+ setBusy("catalog");
2247
+ setError(null);
2248
+ const updated = await fetchApi$4("/catalog/refresh", { method: "POST" });
2249
+ setStatus(updated);
2250
+ const drafts = {};
2251
+ for (const model of updated.models) drafts[model.id] = formatCapacity$1(updated.contextWindowOverrides[model.id] || model.defaultContextWindow);
2252
+ setContextDrafts(drafts);
2253
+ notifyChange();
2254
+ } catch (err) {
2255
+ setError(err instanceof Error ? err.message : String(err));
2256
+ } finally {
2257
+ setBusy(null);
2258
+ }
2259
+ };
2260
+ const handleTestConnection = async () => {
2261
+ try {
2262
+ setBusy("connection");
2263
+ setError(null);
2264
+ await fetchApi$4("/connection/test", { method: "POST" });
2265
+ await loadStatus(true);
2266
+ } catch (err) {
2267
+ setError(err instanceof Error ? err.message : String(err));
2268
+ } finally {
2269
+ setBusy(null);
2270
+ }
2271
+ };
2272
+ const applyEnabled = async (enabledModelIds) => {
2273
+ try {
2274
+ const updated = await fetchApi$4("/models", {
2275
+ method: "POST",
2276
+ body: JSON.stringify({ enabledModelIds })
2277
+ });
2278
+ setStatus(updated);
2279
+ notifyChange();
2280
+ } catch (err) {
2281
+ setError(err instanceof Error ? err.message : String(err));
2282
+ }
2283
+ };
2284
+ const toggleModel = (modelId, checked) => {
2285
+ if (!status) return;
2286
+ const current = status.models.filter((model) => model.enabled).map((model) => model.id);
2287
+ const next = checked ? [.../* @__PURE__ */ new Set([...current, modelId])] : current.filter((id) => id !== modelId);
2288
+ if (next.length === 0) return;
2289
+ applyEnabled(next);
2290
+ };
2291
+ const setAllModels = (selectAll) => {
2292
+ if (!status || status.models.length === 0) return;
2293
+ applyEnabled(selectAll ? status.models.map((model) => model.id) : [status.models[0].id]);
2294
+ };
2295
+ const handleUpdateEffort = async (effort) => {
2296
+ try {
2297
+ const updated = await fetchApi$4("/settings", {
2298
+ method: "POST",
2299
+ body: JSON.stringify({ defaultReasoningEffort: effort })
2300
+ });
2301
+ setStatus(updated);
2302
+ notifyChange();
2303
+ } catch (err) {
2304
+ setError(err instanceof Error ? err.message : String(err));
2305
+ }
2306
+ };
2307
+ const handleSaveContextWindow = async (modelId) => {
2308
+ const raw = contextDrafts[modelId] || "";
2309
+ const parsed = parsePositiveCapacity$1(raw);
2310
+ if (parsed === null) {
2311
+ setError(`Invalid context capacity: ${raw}`);
2312
+ return;
2313
+ }
2314
+ try {
2315
+ setSavingModel(modelId);
2316
+ setError(null);
2317
+ const updated = await fetchApi$4("/settings", {
2318
+ method: "POST",
2319
+ body: JSON.stringify({ contextWindowOverrides: { [modelId]: parsed } })
2320
+ });
2321
+ setStatus(updated);
2322
+ setContextDrafts((prev) => ({
2323
+ ...prev,
2324
+ [modelId]: formatCapacity$1(parsed)
2325
+ }));
2326
+ notifyChange();
2327
+ } catch (err) {
2328
+ setError(err instanceof Error ? err.message : String(err));
2329
+ } finally {
2330
+ setSavingModel(null);
2331
+ }
2332
+ };
2333
+ if (loading) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2334
+ className: "dsha-page",
2335
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2336
+ className: "dsha-empty",
2337
+ children: t.loading
2338
+ })
2339
+ });
2340
+ const quota = status?.quota;
2341
+ const account = status?.account;
2342
+ const visibleCount = status?.models.filter((model) => model.enabled).length || 0;
2343
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2344
+ className: "dsha-page",
2345
+ children: [
2346
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2347
+ className: "dsha-group",
2348
+ children: [
2349
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2350
+ className: "dsha-grouphead",
2351
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: status?.authenticated ? t.account : t.signedOut })
2352
+ }),
2353
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2354
+ className: "dsha-row",
2355
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2356
+ className: "dsha-label",
2357
+ children: status?.authenticated ? t.signedIn : t.signedOut
2358
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2359
+ className: "dsha-value",
2360
+ children: account?.email || account?.userName || "—"
2361
+ })]
2362
+ }),
2363
+ status?.authenticated && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
2364
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2365
+ className: "dsha-row",
2366
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2367
+ className: "dsha-label",
2368
+ children: t.plan
2369
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2370
+ className: "dsha-value",
2371
+ children: quota?.planName || account?.planLabel || t.planUnknown
2372
+ })]
2373
+ }),
2374
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2375
+ className: "dsha-row",
2376
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2377
+ className: "dsha-label",
2378
+ children: t.keyName
2379
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2380
+ className: "dsha-value",
2381
+ children: account?.keyName || "—"
2382
+ })]
2383
+ }),
2384
+ quota?.subscriptionStatus && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2385
+ className: "dsha-row",
2386
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2387
+ className: "dsha-label",
2388
+ children: t.subscription
2389
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2390
+ className: "dsha-value",
2391
+ children: [quota.subscriptionStatus, quota.periodEndsAt !== null ? ` · ${t.renews} ${formatDate$1(quota.periodEndsAt)}` : ""]
2392
+ })]
2393
+ }),
2394
+ account?.organizationName && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2395
+ className: "dsha-row",
2396
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2397
+ className: "dsha-label",
2398
+ children: t.organization
2399
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2400
+ className: "dsha-value",
2401
+ children: account.organizationName
2402
+ })]
2403
+ }),
2404
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2405
+ className: "dsha-row",
2406
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2407
+ className: "dsha-label",
2408
+ children: t.authenticatedAt
2409
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2410
+ className: "dsha-value",
2411
+ children: formatDate$1(account?.authenticatedAt)
2412
+ })]
2413
+ })
2414
+ ] }),
2415
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2416
+ className: "dsha-row",
2417
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2418
+ className: "dsha-label",
2419
+ children: t.storage
2420
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2421
+ className: "dsha-value",
2422
+ children: status?.storagePath || "—"
2423
+ })]
2424
+ }),
2425
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2426
+ className: "dsha-notice",
2427
+ children: t.storageNotice
2428
+ }),
2429
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2430
+ className: "dsha-actions",
2431
+ children: !status?.authenticated ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2432
+ className: "dsha-btn dsha-btn-primary",
2433
+ disabled: busy !== null,
2434
+ onClick: () => void handleLogin(),
2435
+ children: busy === "login" ? loginProgress || t.signingIn : t.signIn
2436
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
2437
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2438
+ className: "dsha-btn dsha-btn-primary",
2439
+ disabled: busy !== null,
2440
+ onClick: () => void handleLogin(),
2441
+ children: busy === "login" ? loginProgress || t.signingIn : t.signInAgain
2442
+ }),
2443
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2444
+ className: "dsha-btn",
2445
+ disabled: busy !== null,
2446
+ onClick: () => void handleRefreshQuota(),
2447
+ children: busy === "quota" ? t.refreshingQuota : t.refreshQuota
2448
+ }),
2449
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2450
+ className: "dsha-btn",
2451
+ disabled: busy !== null,
2452
+ onClick: () => void handleLogout(),
2453
+ children: t.signOut
2454
+ })
2455
+ ] })
2456
+ })
2457
+ ]
2458
+ }),
2459
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2460
+ className: "dsha-group",
2461
+ children: [
2462
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2463
+ className: "dsha-grouphead",
2464
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t.apiKeySection })
2465
+ }),
2466
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2467
+ className: "dsha-muted",
2468
+ children: t.apiKeyHint
2469
+ }),
2470
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2471
+ className: "dsha-capacity-control",
2472
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2473
+ type: "password",
2474
+ "aria-label": t.apiKeySection,
2475
+ placeholder: t.apiKeyPlaceholder,
2476
+ value: apiKey,
2477
+ onChange: (event) => setApiKey(event.currentTarget.value),
2478
+ onKeyDown: (event) => {
2479
+ if (event.key === "Enter" && apiKey.trim() !== "") handleApiKey();
2480
+ }
2481
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2482
+ type: "button",
2483
+ className: "dsha-context-save",
2484
+ disabled: busy !== null || apiKey.trim() === "",
2485
+ onClick: () => void handleApiKey(),
2486
+ children: busy === "apikey" ? t.apiKeySaving : t.apiKeySave
2487
+ })]
2488
+ })
2489
+ ]
2490
+ }),
2491
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2492
+ className: "dsha-group",
2493
+ children: [
2494
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2495
+ className: "dsha-grouphead",
2496
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t.connection })
2497
+ }),
2498
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2499
+ className: "dsha-row",
2500
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2501
+ className: "dsha-label",
2502
+ children: t.provider
2503
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2504
+ className: "dsha-value",
2505
+ children: t.providerValue
2506
+ })]
2507
+ }),
2508
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2509
+ className: "dsha-row",
2510
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2511
+ className: "dsha-label",
2512
+ children: t.connectionState
2513
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2514
+ className: "dsha-value",
2515
+ children: status?.authenticated ? t.connected : t.untested
2516
+ })]
2517
+ }),
2518
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2519
+ className: "dsha-notice",
2520
+ children: status?.serving === false && status.conflict ? t.routeConflict.replace("{detail}", status.conflict) : t.routeOwned
2521
+ }),
2522
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2523
+ className: "dsha-actions",
2524
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2525
+ className: "dsha-btn",
2526
+ disabled: busy !== null || !status?.authenticated,
2527
+ onClick: () => void handleTestConnection(),
2528
+ children: busy === "connection" ? t.testingConnection : t.testConnection
2529
+ })
2530
+ })
2531
+ ]
2532
+ }),
2533
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2534
+ className: "dsha-group",
2535
+ children: [
2536
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2537
+ className: "dsha-grouphead",
2538
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t.modelsSection }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2539
+ className: "dsha-btn",
2540
+ disabled: busy !== null,
2541
+ onClick: () => void handleRefreshCatalog(),
2542
+ children: busy === "catalog" ? t.refreshingCatalog : t.refreshCatalog
2543
+ })]
2544
+ }),
2545
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2546
+ className: "dsha-muted dsha-models-hint",
2547
+ children: t.modelsHint
2548
+ }),
2549
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2550
+ className: "dsha-models",
2551
+ "aria-label": "Command Code Models",
2552
+ children: status?.models.map((model) => {
2553
+ const lastVisible = model.enabled && visibleCount === 1;
2554
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
2555
+ title: `${model.id} · ${model.wire === "anthropic" ? t.wireAnthropic : t.wireOpenai}`,
2556
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2557
+ type: "checkbox",
2558
+ checked: model.enabled,
2559
+ disabled: busy !== null || lastVisible,
2560
+ onChange: (event) => toggleModel(model.id, event.currentTarget.checked)
2561
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: model.name })]
2562
+ }, model.id);
2563
+ })
2564
+ }),
2565
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2566
+ className: "dsha-actions",
2567
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2568
+ className: "dsha-btn",
2569
+ disabled: busy !== null,
2570
+ onClick: () => setAllModels(true),
2571
+ children: t.selectAll
2572
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2573
+ className: "dsha-btn",
2574
+ disabled: busy !== null,
2575
+ onClick: () => setAllModels(false),
2576
+ children: t.unselectAll
2577
+ })]
2578
+ })
2579
+ ]
2580
+ }),
2581
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2582
+ className: "dsha-group",
2583
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2584
+ className: "dsha-grouphead",
2585
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t.enhanced })
2586
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2587
+ className: "dsha-pref-row",
2588
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t.defaultReasoningEffort }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2589
+ className: "dsha-muted",
2590
+ children: t.defaultReasoningEffortHint
2591
+ })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
2592
+ className: "dsha-select",
2593
+ "aria-label": t.defaultReasoningEffort,
2594
+ value: status?.defaultReasoningEffort ?? "",
2595
+ disabled: busy !== null,
2596
+ onChange: (event) => {
2597
+ const value = event.currentTarget.value;
2598
+ handleUpdateEffort(value === "" ? null : value);
2599
+ },
2600
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2601
+ value: "",
2602
+ children: t.defaultEffortAuto
2603
+ }), COMMAND_CODE_REASONING_EFFORTS.map((effort) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2604
+ value: effort,
2605
+ children: EFFORT_LABELS[effort]
2606
+ }, effort))]
2607
+ })]
2608
+ })]
2609
+ }),
2610
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2611
+ className: "dsha-group",
2612
+ children: [
2613
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2614
+ className: "dsha-grouphead",
2615
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t.contextWindowSection })
2616
+ }),
2617
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2618
+ className: "dsha-muted",
2619
+ children: t.contextWindowHint
2620
+ }),
2621
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2622
+ className: "dsha-context-settings",
2623
+ children: status?.models.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2624
+ className: "dsha-context-row",
2625
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2626
+ title: model.id,
2627
+ children: model.name
2628
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2629
+ className: "dsha-capacity-control",
2630
+ children: [
2631
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2632
+ type: "text",
2633
+ "aria-label": `${model.name} context window`,
2634
+ value: contextDrafts[model.id] ?? "",
2635
+ onChange: (event) => setContextDrafts({
2636
+ ...contextDrafts,
2637
+ [model.id]: event.currentTarget.value
2638
+ }),
2639
+ onKeyDown: (event) => {
2640
+ if (event.key === "Enter") handleSaveContextWindow(model.id);
2641
+ }
2642
+ }),
2643
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: t.tokens }),
2644
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2645
+ type: "button",
2646
+ className: "dsha-context-save",
2647
+ disabled: savingModel === model.id,
2648
+ onClick: () => void handleSaveContextWindow(model.id),
2649
+ children: savingModel === model.id ? t.saving : t.save
2650
+ })
2651
+ ]
2652
+ })]
2653
+ }, model.id))
2654
+ })
2655
+ ]
2656
+ }),
2657
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2658
+ className: "dsha-group",
2659
+ children: [
2660
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2661
+ className: "dsha-grouphead",
2662
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t.quotaSection }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2663
+ className: "dsha-btn",
2664
+ disabled: busy !== null || !status?.authenticated,
2665
+ onClick: () => void handleRefreshQuota(),
2666
+ children: busy === "quota" ? t.refreshingQuota : t.refreshQuota
2667
+ })]
2668
+ }),
2669
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2670
+ className: "dsha-muted",
2671
+ children: t.quotaDesc
2672
+ }),
2673
+ !status?.authenticated ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2674
+ className: "dsha-empty",
2675
+ children: t.signedOut
2676
+ }) : quota === null || quota === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2677
+ className: "dsha-empty",
2678
+ children: busy === "quota" ? t.refreshingQuota : t.quotaEmpty
2679
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2680
+ className: "dsha-quota-card",
2681
+ children: [
2682
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2683
+ className: "dsha-quota-title",
2684
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t.quotaCredits }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: quota.unlimited ? t.quotaUnlimited : quota.creditBalance !== null ? quota.creditBalance : "—" })]
2685
+ }),
2686
+ quota.windows.map((window) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2687
+ className: "dsha-meter-wrap",
2688
+ children: [
2689
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2690
+ className: "dsha-meter-label",
2691
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: window.label }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("strong", { children: [Math.max(0, 100 - window.usedPercent), "% left"] })]
2692
+ }),
2693
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2694
+ className: "dsha-meter dsha-meter-green",
2695
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { style: { width: `${Math.max(0, 100 - window.usedPercent)}%` } })
2696
+ }),
2697
+ window.resetsAt !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2698
+ className: "dsha-meter-meta",
2699
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: ["Reset: ", formatReset$1(window.resetsAt)] })
2700
+ })
2701
+ ]
2702
+ }, window.id)),
2703
+ quota.meters.map((meter) => {
2704
+ const remaining = meter.remainingFraction;
2705
+ const percent = remaining === null ? null : Math.round(remaining * 100);
2706
+ const isWindow = percent !== null;
2707
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2708
+ className: "dsha-meter-wrap",
2709
+ children: [
2710
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2711
+ className: "dsha-meter-label",
2712
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: meter.label }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: isWindow ? `${percent}% left` : meter.limit ?? "—" })]
2713
+ }),
2714
+ isWindow && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2715
+ className: "dsha-meter dsha-meter-cyan",
2716
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { style: { width: `${percent}%` } })
2717
+ }),
2718
+ isWindow ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2719
+ className: "dsha-meter-meta",
2720
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
2721
+ meter.used ?? "—",
2722
+ " / ",
2723
+ meter.limit ?? "—"
2724
+ ] }), meter.resetsAt !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: ["Reset: ", formatReset$1(meter.resetsAt)] })]
2725
+ }) : meter.description !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2726
+ className: "dsha-meter-meta",
2727
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: meter.description })
2728
+ }) : null
2729
+ ]
2730
+ }, meter.id);
2731
+ }),
2732
+ quota.meters.length === 0 && quota.windows.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2733
+ className: "dsha-empty",
2734
+ children: t.quotaEmpty
2735
+ }),
2736
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2737
+ className: "dsha-timestamp",
2738
+ children: t.updatedAt.replace("{time}", new Date(quota.fetchedAt).toLocaleString())
2739
+ })
2740
+ ]
2741
+ })
2742
+ ]
2743
+ }),
2744
+ error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2745
+ className: "dsha-error",
2746
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: error })
2747
+ })
2748
+ ]
2749
+ });
2750
+ }
2751
+ //#endregion
2752
+ //#region src/shared/kimi-code-contracts.ts
2753
+ /** Every level, in escalating order; the settings card renders exactly these. */
2754
+ const KIMI_CODE_REASONING_EFFORTS = [
2755
+ "low",
2756
+ "high",
2757
+ "max",
2758
+ "none"
2759
+ ];
2760
+ //#endregion
2761
+ //#region src/client/kimi-code/locales.ts
2762
+ const NS_KIMI_CODE = "dsh-kimi-code";
2763
+ const zh$1 = {
2764
+ title: "Kimi Code",
2765
+ pageDesc: "登录 Kimi Code 订阅,管理模型、思考档位、上下文窗口与额度。",
2766
+ account: "账号",
2767
+ signedIn: "已登录",
2768
+ signedOut: "未登录",
2769
+ nickname: "昵称",
2770
+ email: "邮箱",
2771
+ plan: "套餐",
2772
+ planUnknown: "—",
2773
+ region: "区域",
2774
+ regionMainland: "中国大陆 (kimi.com)",
2775
+ regionGlobal: "国际 (kimi.ai)",
2776
+ authenticatedAt: "登录时间",
2777
+ storage: "凭据存储",
2778
+ storageNotice: "OAuth 凭据由 Host 保存在本地安全存储 ($DSH_HOME/storages/kimi-code-credentials.json),不会进入浏览器。访问令牌会自动刷新。",
2779
+ refreshRejected: "刷新令牌已被拒绝,请重新登录。",
2780
+ signIn: "设备码登录",
2781
+ signInAgain: "重新登录",
2782
+ signingIn: "登录中...",
2783
+ signOut: "注销",
2784
+ cancelSignIn: "取消登录",
2785
+ deviceCodeSection: "设备码授权",
2786
+ deviceCodeHint: "在浏览器中打开下方链接(已自动填充用户码),确认后用同一账号授权。",
2787
+ openSignInPage: "打开授权页面",
2788
+ copyCode: "复制用户码",
2789
+ copied: "已复制",
2790
+ userCode: "用户码",
2791
+ codeExpires: "有效期至",
2792
+ waitingAuthorization: "等待浏览器授权...",
2793
+ requestingCode: "正在申请设备码...",
2794
+ connection: "连接",
2795
+ provider: "Provider",
2796
+ providerValue: "Kimi Code · kimi-code",
2797
+ oauthHost: "OAuth 主机",
2798
+ codingEndpoint: "模型接口",
2799
+ connectionState: "连接状态",
2800
+ connected: "已连接",
2801
+ untested: "未测试",
2802
+ testConnection: "测试连接",
2803
+ testingConnection: "测试中...",
2804
+ testSuccess: "连接正常",
2805
+ testFailed: "连接失败:Kimi Code 未接受当前凭据",
2806
+ routeOwned: "模型路由由本插件提供",
2807
+ routeConflict: "模型路由已被其他 Provider 占用:{detail}。请在 DSH 设置中移除重复的 Provider 配置后重试。",
2808
+ modelsSection: "模型",
2809
+ modelsHint: "勾选后模型才会出现在对话页的模型选择列表中;至少保留一个。",
2810
+ refreshCatalog: "刷新模型目录",
2811
+ refreshingCatalog: "刷新中...",
2812
+ selectAll: "全选",
2813
+ unselectAll: "全不选",
2814
+ wireAnthropic: "Anthropic",
2815
+ wireOpenai: "OpenAI",
2816
+ capVideo: "视频",
2817
+ capDynamicTools: "动态工具",
2818
+ capImageOnly: "仅图片",
2819
+ capNone: "—",
2820
+ capabilitiesTitle: "模型能力",
2821
+ capColMedia: "多模态",
2822
+ capColTools: "动态工具",
2823
+ capColNotes: "说明",
2824
+ capPlan: "需要套餐:{plan}",
2825
+ speedRegular: "标准",
2826
+ speedHighspeed: "高速 (约 6×)",
2827
+ quotaCost: "额度倍率",
2828
+ minimumPlan: "所需套餐",
2829
+ enhanced: "思考设置",
2830
+ defaultReasoningEffort: "默认思考档位",
2831
+ defaultReasoningEffortHint: "Kimi Code 只接受 low / high / max 三档;none 表示关闭思考。切换档位会使上下文缓存失效,建议在同一会话中保持一致。",
2832
+ defaultEffortAuto: "使用模型默认值",
2833
+ effortLow: "低 (low)",
2834
+ effortHigh: "高 (high)",
2835
+ effortMax: "最高 (max)",
2836
+ effortNone: "关闭思考",
2837
+ contextWindowSection: "模型上下文窗口",
2838
+ contextWindowHint: "默认取模型目录的上下文长度。k3 的 1M 上下文需要 Allegretto 及以上套餐,因此默认按 256K 计算;如已升级可在此覆盖为 1M。",
2839
+ contextWindowPlan: "完整上下文需要 {plan} 套餐",
2840
+ save: "保存",
2841
+ saved: "已保存",
2842
+ saving: "保存中...",
2843
+ cacheSection: "缓存与 K3 调优",
2844
+ cacheDesc: "Kimi 的前缀缓存按请求内容自动命中,无需也无法手动声明,因此这里用读缓存比例反映实际效果。",
2845
+ cacheEmpty: "本进程还没有带用量回报的请求。",
2846
+ cacheHitRatio: "缓存命中率",
2847
+ cacheCached: "命中 tokens",
2848
+ cacheFresh: "新处理 tokens",
2849
+ cacheOutput: "输出 tokens",
2850
+ cacheRequests: "请求数",
2851
+ cacheHint: "同一会话内:系统提示与工具列表一旦变化会使缓存整体失效,因此保持工具集合稳定、把新增内容追加在末尾,命中率最高。",
2852
+ preserveThinking: "保留思考 (Preserved Thinking)",
2853
+ preserveOn: "已开启(官方默认):跨轮保留 reasoning_content,多轮工具调用更连贯,但思考 token 也计入输出。",
2854
+ preserveOff: "已关闭:不要求服务回传推理内容。可设置 DSH_KIMI_CODE_PRESERVE_THINKING=0 关闭。",
2855
+ outputCap: "输出上限跟随上下文窗口,长思考不会被 32K 截断",
2856
+ quotaSection: "用量与额度",
2857
+ quotaDesc: "数据来自 Kimi Code 账户接口,页面可见时最多每 60 秒刷新一次。",
2858
+ refreshQuota: "刷新用量",
2859
+ refreshingQuota: "刷新中...",
2860
+ quotaEmpty: "暂无配额数据,点击「刷新用量」重试。",
2861
+ quotaPlan: "当前套餐",
2862
+ quotaWallet: "加油包余额",
2863
+ quotaWalletLimit: "月度上限",
2864
+ quotaWalletUsed: "本月已用",
2865
+ quotaUnlimited: "不限量",
2866
+ updatedAt: "更新时间:{time}",
2867
+ resetAt: "重置:{time}",
2868
+ loading: "加载中...",
2869
+ loginFailed: "登录失败",
2870
+ requests: "次请求",
2871
+ tokens: "tokens"
2872
+ };
2873
+ const en$1 = {
2874
+ title: "Kimi Code",
2875
+ pageDesc: "Sign in to the Kimi Code subscription and manage models, thinking level, context windows, and quota.",
2876
+ account: "Account",
2877
+ signedIn: "Signed in",
2878
+ signedOut: "Not signed in",
2879
+ nickname: "Nickname",
2880
+ email: "Email",
2881
+ plan: "Plan",
2882
+ planUnknown: "—",
2883
+ region: "Region",
2884
+ regionMainland: "Mainland China (kimi.com)",
2885
+ regionGlobal: "Global (kimi.ai)",
2886
+ authenticatedAt: "Signed in at",
2887
+ storage: "Credential storage",
2888
+ storageNotice: "The OAuth credential is stored locally by the Host ($DSH_HOME/storages/kimi-code-credentials.json) and never reaches the browser. Access tokens refresh automatically.",
2889
+ refreshRejected: "The refresh token was rejected — sign in again.",
2890
+ signIn: "Sign in with device code",
2891
+ signInAgain: "Sign in again",
2892
+ signingIn: "Signing in...",
2893
+ signOut: "Sign out",
2894
+ cancelSignIn: "Cancel",
2895
+ deviceCodeSection: "Device authorization",
2896
+ deviceCodeHint: "Open the link below in a browser (the user code is pre-filled) and approve it with the same account.",
2897
+ openSignInPage: "Open the sign-in page",
2898
+ copyCode: "Copy user code",
2899
+ copied: "Copied",
2900
+ userCode: "User code",
2901
+ codeExpires: "Expires at",
2902
+ waitingAuthorization: "Waiting for browser authorization...",
2903
+ requestingCode: "Requesting a device code...",
2904
+ connection: "Connection",
2905
+ provider: "Provider",
2906
+ providerValue: "Kimi Code · kimi-code",
2907
+ oauthHost: "OAuth host",
2908
+ codingEndpoint: "Model endpoint",
2909
+ connectionState: "Connection state",
2910
+ connected: "Connected",
2911
+ untested: "Untested",
2912
+ testConnection: "Test connection",
2913
+ testingConnection: "Testing...",
2914
+ testSuccess: "Connection OK",
2915
+ testFailed: "Connection failed: Kimi Code did not accept the stored credential",
2916
+ routeOwned: "This plugin serves the model route",
2917
+ routeConflict: "Another provider already owns the model route: {detail}. Remove the duplicate provider configuration in DSH settings and reload.",
2918
+ modelsSection: "Models",
2919
+ modelsHint: "Checked models appear in the DSH conversation model list; keep at least one.",
2920
+ refreshCatalog: "Refresh catalog",
2921
+ refreshingCatalog: "Refreshing...",
2922
+ selectAll: "Select all",
2923
+ unselectAll: "Deselect all",
2924
+ wireAnthropic: "Anthropic",
2925
+ wireOpenai: "OpenAI",
2926
+ capVideo: "Video",
2927
+ capDynamicTools: "Dynamic tools",
2928
+ capImageOnly: "Images",
2929
+ capNone: "—",
2930
+ capabilitiesTitle: "Capabilities",
2931
+ capColMedia: "Multimodal",
2932
+ capColTools: "Dynamic tools",
2933
+ capColNotes: "Notes",
2934
+ capPlan: "Requires plan: {plan}",
2935
+ speedRegular: "Regular",
2936
+ speedHighspeed: "HighSpeed (~6x)",
2937
+ quotaCost: "Quota cost",
2938
+ minimumPlan: "Required plan",
2939
+ enhanced: "Thinking",
2940
+ defaultReasoningEffort: "Default thinking level",
2941
+ defaultReasoningEffortHint: "Kimi Code accepts only low / high / max; none disables thinking. Switching the level invalidates the context cache, so keep it steady within a session.",
2942
+ defaultEffortAuto: "Use the model default",
2943
+ effortLow: "Low",
2944
+ effortHigh: "High",
2945
+ effortMax: "Max",
2946
+ effortNone: "Thinking off",
2947
+ contextWindowSection: "Model context window",
2948
+ contextWindowHint: "Defaults to the catalog context length. k3 reaches 1M only on Allegretto and above, so it defaults to 256K; raise the override here after upgrading.",
2949
+ contextWindowPlan: "Full context requires the {plan} plan",
2950
+ save: "Save",
2951
+ saved: "Saved",
2952
+ saving: "Saving...",
2953
+ cacheSection: "Cache & K3 tuning",
2954
+ cacheDesc: "Kimi's prefix cache is automatic and content-hash based — there is no marker to set — so the cache-read ratio is the only evidence it is working.",
2955
+ cacheEmpty: "No request in this process has reported usage yet.",
2956
+ cacheHitRatio: "Cache hit ratio",
2957
+ cacheCached: "Cached tokens",
2958
+ cacheFresh: "Fresh tokens",
2959
+ cacheOutput: "Output tokens",
2960
+ cacheRequests: "Requests",
2961
+ cacheHint: "Within one session: any change to the system prompt or the tool list invalidates the whole prefix cache, so keep the tool set stable and append new material at the tail for the best hit rate.",
2962
+ preserveThinking: "Preserved Thinking",
2963
+ preserveOn: "On (the official default): reasoning_content is retained across turns, which keeps multi-step tool use coherent — its tokens are billed as output.",
2964
+ preserveOff: "Off: the service is not asked to return reasoning. Set DSH_KIMI_CODE_PRESERVE_THINKING=0 to disable.",
2965
+ outputCap: "Output cap follows the context window, so long reasoning is not cut off at 32K",
2966
+ quotaSection: "Usage & quota",
2967
+ quotaDesc: "Read from the Kimi Code account endpoints; refreshes at most once every 60 seconds while visible.",
2968
+ refreshQuota: "Refresh quota",
2969
+ refreshingQuota: "Refreshing...",
2970
+ quotaEmpty: "No quota data yet — use \"Refresh quota\" to retry.",
2971
+ quotaPlan: "Current plan",
2972
+ quotaWallet: "Booster wallet",
2973
+ quotaWalletLimit: "Monthly cap",
2974
+ quotaWalletUsed: "Used this month",
2975
+ quotaUnlimited: "Unlimited",
2976
+ updatedAt: "Updated at: {time}",
2977
+ resetAt: "Reset: {time}",
2978
+ loading: "Loading...",
2979
+ loginFailed: "Sign-in failed",
2980
+ requests: "requests",
2981
+ tokens: "tokens"
2982
+ };
2983
+ const dictionaries$1 = {
2984
+ zh: zh$1,
2985
+ "zh-CN": zh$1,
2986
+ en: en$1,
2987
+ "en-US": en$1
2988
+ };
2989
+ //#endregion
2990
+ //#region src/client/kimi-code/KimiModelCapabilities.tsx
2991
+ /**
2992
+ * Capability table for the Kimi model list.
2993
+ *
2994
+ * Extracted from the settings card so the layout is testable: an earlier
2995
+ * revision inlined each capability as prose beside the model name, which
2996
+ * stretched the name column and pushed the descriptions out of alignment.
2997
+ * Capabilities are now short tags in their own columns, with the per-model
2998
+ * detail kept in a hover title.
2999
+ */
3000
+ const t = zh$1;
3001
+ function KimiModelCapabilities({ models }) {
3002
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3003
+ className: "dsha-cap-table",
3004
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("table", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", { children: [
3005
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: t.capabilitiesTitle }),
3006
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: t.capColMedia }),
3007
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: t.capColTools }),
3008
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: t.capColNotes })
3009
+ ] }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tbody", { children: models.map((model) => {
3010
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", { children: [
3011
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
3012
+ title: [
3013
+ model.id,
3014
+ model.wire === "anthropic" ? t.wireAnthropic : t.wireOpenai,
3015
+ ...model.defaultReasoningEffort ? [t.defaultReasoningEffort + ": " + model.defaultReasoningEffort] : [],
3016
+ ...model.minimumPlan ? [t.capPlan.replace("{plan}", model.minimumPlan)] : []
3017
+ ].join(" · "),
3018
+ children: model.name
3019
+ }),
3020
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3021
+ className: model.supportsVideo ? "dsha-cap-on" : "dsha-cap-off",
3022
+ children: model.supportsVideo ? t.capVideo : t.capImageOnly
3023
+ }) }),
3024
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: model.supportsDynamicTools ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3025
+ className: "dsha-cap-on",
3026
+ children: t.capDynamicTools
3027
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3028
+ className: "dsha-cap-off",
3029
+ children: t.capNone
3030
+ }) }),
3031
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
3032
+ className: "dsha-cap-notes",
3033
+ children: model.description ?? ""
3034
+ })
3035
+ ] }, model.id);
3036
+ }) })] })
3037
+ });
3038
+ }
3039
+ //#endregion
3040
+ //#region src/client/kimi-code/KimiCodeSection.tsx
3041
+ const API$3 = "/kimi-code/api";
3042
+ async function fetchApi$3(path, options) {
3043
+ const res = await fetch(`${API$3}${path}`, {
3044
+ ...options,
3045
+ headers: {
3046
+ "Content-Type": "application/json",
3047
+ ...options?.headers
3048
+ }
3049
+ });
3050
+ const json = await res.json();
3051
+ if (!res.ok || !json.ok) throw new Error(json.error || `HTTP ${res.status}`);
3052
+ return json.value;
3053
+ }
3054
+ /** Parse "1M", "512K", "200000" into a positive integer token count. */
3055
+ function parsePositiveCapacity(value) {
3056
+ const matched = value.trim().toLowerCase().replace(/[,_\s]/g, "").match(/^(\d+(?:\.\d+)?)(k|m)?$/);
3057
+ if (matched === null) return null;
3058
+ const multiplier = matched[2] === "m" ? 1e6 : matched[2] === "k" ? 1e3 : 1;
3059
+ const parsed = Number(matched[1]) * multiplier;
3060
+ return Number.isSafeInteger(parsed) && parsed >= 1 ? parsed : null;
3061
+ }
3062
+ function formatCapacity(value) {
3063
+ if (value >= 1e6 && value % 1e5 === 0) return `${value / 1e6}M`;
3064
+ if (value >= 1e3 && value % 1e3 === 0) return `${value / 1e3}K`;
3065
+ return String(value);
3066
+ }
3067
+ function formatDate(ms) {
3068
+ if (ms === void 0 || ms === null || ms <= 0) return "—";
3069
+ try {
3070
+ return new Intl.DateTimeFormat(void 0, {
3071
+ dateStyle: "medium",
3072
+ timeStyle: "short"
3073
+ }).format(ms);
3074
+ } catch {
3075
+ return "—";
3076
+ }
3077
+ }
3078
+ function formatReset(resetsAt) {
3079
+ if (resetsAt === void 0 || resetsAt === null || resetsAt <= 0) return "";
3080
+ const diff = resetsAt - Date.now();
3081
+ if (diff <= 0) return "now";
3082
+ const mins = Math.floor(diff / 6e4);
3083
+ const hours = Math.floor(mins / 60);
3084
+ const days = Math.floor(hours / 24);
3085
+ if (days > 0) return `${days}d ${hours % 24}h`;
3086
+ if (hours > 0) return `${hours}h ${mins % 60}m`;
3087
+ return `${mins}m`;
3088
+ }
3089
+ /** Money the service reports in minor units, rendered in its own currency. */
3090
+ function formatMoney(cents, currency) {
3091
+ if (cents === null) return "—";
3092
+ const code = currency ?? "USD";
3093
+ try {
3094
+ return new Intl.NumberFormat(void 0, {
3095
+ style: "currency",
3096
+ currency: code
3097
+ }).format(cents / 100);
3098
+ } catch {
3099
+ return `${(cents / 100).toFixed(2)} ${code}`;
3100
+ }
3101
+ }
3102
+ const EFFORT_LABEL = {
3103
+ low: zh$1.effortLow,
3104
+ high: zh$1.effortHigh,
3105
+ max: zh$1.effortMax,
3106
+ none: zh$1.effortNone
3107
+ };
3108
+ function KimiCodeSection({ onModelChange, loadModelDirectory }) {
3109
+ const [status, setStatus] = (0, react.useState)(null);
3110
+ const [loading, setLoading] = (0, react.useState)(true);
3111
+ const [busy, setBusy] = (0, react.useState)(null);
3112
+ const [error, setError] = (0, react.useState)(null);
3113
+ const [flow, setFlow] = (0, react.useState)(null);
3114
+ const [contextDrafts, setContextDrafts] = (0, react.useState)({});
3115
+ const [savingModel, setSavingModel] = (0, react.useState)(null);
3116
+ const [copied, setCopied] = (0, react.useState)(false);
3117
+ const [connectionNotice, setConnectionNotice] = (0, react.useState)(null);
3118
+ const t = zh$1;
3119
+ const notifyChange = (0, react.useCallback)(() => {
3120
+ onModelChange?.();
3121
+ loadModelDirectory?.();
3122
+ }, [onModelChange, loadModelDirectory]);
3123
+ const loadStatus = (0, react.useCallback)(async (quiet = false) => {
3124
+ if (!quiet) setError(null);
3125
+ try {
3126
+ const data = await fetchApi$3("/status");
3127
+ setStatus(data);
3128
+ const drafts = {};
3129
+ for (const model of data.models) drafts[model.id] = formatCapacity(data.contextWindowOverrides[model.id] || model.defaultContextWindow);
3130
+ setContextDrafts(drafts);
3131
+ } catch (err) {
3132
+ if (!quiet) setError(err instanceof Error ? err.message : String(err));
3133
+ } finally {
3134
+ setLoading(false);
3135
+ }
3136
+ }, []);
3137
+ (0, react.useEffect)(() => {
3138
+ loadStatus();
3139
+ const refreshWhenVisible = () => {
3140
+ if (document.visibilityState === "visible") loadStatus(true);
3141
+ };
3142
+ document.addEventListener("visibilitychange", refreshWhenVisible);
3143
+ const timer = window.setInterval(refreshWhenVisible, 6e4);
3144
+ return () => {
3145
+ window.clearInterval(timer);
3146
+ document.removeEventListener("visibilitychange", refreshWhenVisible);
3147
+ };
3148
+ }, [loadStatus]);
3149
+ (0, react.useEffect)(() => {
3150
+ if (flow?.status !== "pending") return;
3151
+ const timer = window.setInterval(() => {
3152
+ (async () => {
3153
+ try {
3154
+ const poll = await fetchApi$3("/login/status");
3155
+ setFlow(poll);
3156
+ if (poll.status === "complete") {
3157
+ await loadStatus();
3158
+ notifyChange();
3159
+ } else if (poll.status === "error") setError(poll.error || t.loginFailed);
3160
+ } catch {}
3161
+ })();
3162
+ }, 2e3);
3163
+ return () => window.clearInterval(timer);
3164
+ }, [
3165
+ flow?.status,
3166
+ loadStatus,
3167
+ notifyChange,
3168
+ t.loginFailed
3169
+ ]);
3170
+ const handleLogin = async () => {
3171
+ try {
3172
+ setBusy("login");
3173
+ setError(null);
3174
+ setCopied(false);
3175
+ const next = await fetchApi$3("/login", {
3176
+ method: "POST",
3177
+ body: JSON.stringify({})
3178
+ });
3179
+ setFlow(next);
3180
+ } catch (err) {
3181
+ setError(err instanceof Error ? err.message : String(err));
3182
+ } finally {
3183
+ setBusy(null);
3184
+ }
3185
+ };
3186
+ const handleCancelLogin = async () => {
3187
+ try {
3188
+ await fetchApi$3("/login/cancel", { method: "POST" });
3189
+ setFlow({ status: "idle" });
3190
+ } catch (err) {
3191
+ setError(err instanceof Error ? err.message : String(err));
3192
+ }
3193
+ };
3194
+ const handleCopyCode = async () => {
3195
+ const code = flow?.userCode;
3196
+ if (!code) return;
3197
+ try {
3198
+ await navigator.clipboard.writeText(code);
3199
+ setCopied(true);
3200
+ window.setTimeout(() => setCopied(false), 2e3);
3201
+ } catch {}
3202
+ };
3203
+ const handleLogout = async () => {
3204
+ try {
3205
+ setBusy("logout");
3206
+ setError(null);
3207
+ const updated = await fetchApi$3("/logout", { method: "POST" });
3208
+ setStatus(updated);
3209
+ setFlow({ status: "idle" });
3210
+ notifyChange();
3211
+ } catch (err) {
3212
+ setError(err instanceof Error ? err.message : String(err));
3213
+ } finally {
3214
+ setBusy(null);
3215
+ }
3216
+ };
3217
+ const handleRefreshQuota = async () => {
3218
+ try {
3219
+ setBusy("quota");
3220
+ setError(null);
3221
+ const updated = await fetchApi$3("/quota", { method: "POST" });
3222
+ setStatus(updated);
3223
+ } catch (err) {
3224
+ setError(err instanceof Error ? err.message : String(err));
3225
+ await loadStatus(true);
3226
+ } finally {
3227
+ setBusy(null);
3228
+ }
3229
+ };
3230
+ const handleRefreshCatalog = async () => {
3231
+ try {
3232
+ setBusy("catalog");
3233
+ setError(null);
3234
+ const updated = await fetchApi$3("/catalog/refresh", { method: "POST" });
3235
+ setStatus(updated);
3236
+ const drafts = {};
3237
+ for (const model of updated.models) drafts[model.id] = formatCapacity(updated.contextWindowOverrides[model.id] || model.defaultContextWindow);
3238
+ setContextDrafts(drafts);
3239
+ notifyChange();
3240
+ } catch (err) {
3241
+ setError(err instanceof Error ? err.message : String(err));
3242
+ } finally {
3243
+ setBusy(null);
3244
+ }
3245
+ };
3246
+ const handleTestConnection = async () => {
3247
+ try {
3248
+ setBusy("connection");
3249
+ setError(null);
3250
+ setConnectionNotice(null);
3251
+ const result = await fetchApi$3("/connection/test", { method: "POST" });
3252
+ setConnectionNotice(result.connected ? `${t.testSuccess} · ${result.latencyMs} ms` : t.testFailed);
3253
+ await loadStatus(true);
3254
+ } catch (err) {
3255
+ setError(err instanceof Error ? err.message : String(err));
3256
+ } finally {
3257
+ setBusy(null);
3258
+ }
3259
+ };
3260
+ const applyEnabled = async (enabledModelIds) => {
3261
+ try {
3262
+ const updated = await fetchApi$3("/models", {
3263
+ method: "POST",
3264
+ body: JSON.stringify({ enabledModelIds })
3265
+ });
3266
+ setStatus(updated);
3267
+ notifyChange();
3268
+ } catch (err) {
3269
+ setError(err instanceof Error ? err.message : String(err));
3270
+ }
3271
+ };
3272
+ const toggleModel = (modelId, checked) => {
3273
+ if (!status) return;
3274
+ const current = status.models.filter((model) => model.enabled).map((model) => model.id);
3275
+ const next = checked ? [.../* @__PURE__ */ new Set([...current, modelId])] : current.filter((id) => id !== modelId);
3276
+ if (next.length === 0) return;
3277
+ applyEnabled(next);
3278
+ };
3279
+ const setAllModels = (selectAll) => {
3280
+ if (!status || status.models.length === 0) return;
3281
+ applyEnabled(selectAll ? status.models.map((model) => model.id) : [status.models[0].id]);
3282
+ };
3283
+ const handleUpdateEffort = async (effort) => {
3284
+ try {
3285
+ const updated = await fetchApi$3("/settings", {
3286
+ method: "POST",
3287
+ body: JSON.stringify({ defaultReasoningEffort: effort })
3288
+ });
3289
+ setStatus(updated);
3290
+ notifyChange();
3291
+ } catch (err) {
3292
+ setError(err instanceof Error ? err.message : String(err));
3293
+ }
3294
+ };
3295
+ const handleSaveContextWindow = async (modelId) => {
3296
+ const raw = contextDrafts[modelId] || "";
3297
+ const parsed = parsePositiveCapacity(raw);
3298
+ if (parsed === null) {
3299
+ setError(`Invalid context capacity: ${raw}`);
3300
+ return;
3301
+ }
3302
+ try {
3303
+ setSavingModel(modelId);
3304
+ setError(null);
3305
+ const updated = await fetchApi$3("/settings", {
3306
+ method: "POST",
3307
+ body: JSON.stringify({ contextWindowOverrides: { [modelId]: parsed } })
3308
+ });
3309
+ setStatus(updated);
3310
+ setContextDrafts((prev) => ({
3311
+ ...prev,
3312
+ [modelId]: formatCapacity(parsed)
3313
+ }));
3314
+ notifyChange();
3315
+ } catch (err) {
3316
+ setError(err instanceof Error ? err.message : String(err));
3317
+ } finally {
3318
+ setSavingModel(null);
3319
+ }
3320
+ };
3321
+ if (loading) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3322
+ className: "dsha-page",
3323
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3324
+ className: "dsha-empty",
3325
+ children: t.loading
3326
+ })
3327
+ });
3328
+ const quota = status?.quota;
3329
+ const account = status?.account;
3330
+ const visibleCount = status?.models.filter((model) => model.enabled).length || 0;
3331
+ const loginPending = flow?.status === "pending";
3332
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3333
+ className: "dsha-page",
3334
+ children: [
3335
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
3336
+ className: "dsha-group",
3337
+ children: [
3338
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3339
+ className: "dsha-grouphead",
3340
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: status?.authenticated ? t.account : t.signedOut })
3341
+ }),
3342
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3343
+ className: "dsha-row",
3344
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3345
+ className: "dsha-label",
3346
+ children: status?.authenticated ? t.signedIn : t.signedOut
3347
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3348
+ className: "dsha-value",
3349
+ children: account?.nickname || account?.email || account?.userId || "—"
3350
+ })]
3351
+ }),
3352
+ status?.authenticated && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
3353
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3354
+ className: "dsha-row",
3355
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3356
+ className: "dsha-label",
3357
+ children: t.plan
3358
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3359
+ className: "dsha-value",
3360
+ children: quota?.planName || account?.planName || t.planUnknown
3361
+ })]
3362
+ }),
3363
+ account?.email && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3364
+ className: "dsha-row",
3365
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3366
+ className: "dsha-label",
3367
+ children: t.email
3368
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3369
+ className: "dsha-value",
3370
+ children: account.email
3371
+ })]
3372
+ }),
3373
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3374
+ className: "dsha-row",
3375
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3376
+ className: "dsha-label",
3377
+ children: t.region
3378
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3379
+ className: "dsha-value",
3380
+ children: status.region === "global" ? t.regionGlobal : t.regionMainland
3381
+ })]
3382
+ }),
3383
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3384
+ className: "dsha-row",
3385
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3386
+ className: "dsha-label",
3387
+ children: t.authenticatedAt
3388
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3389
+ className: "dsha-value",
3390
+ children: formatDate(account?.authenticatedAt)
3391
+ })]
3392
+ })
3393
+ ] }),
3394
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3395
+ className: "dsha-row",
3396
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3397
+ className: "dsha-label",
3398
+ children: t.storage
3399
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3400
+ className: "dsha-value",
3401
+ children: status?.storagePath || "—"
3402
+ })]
3403
+ }),
3404
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3405
+ className: "dsha-notice",
3406
+ children: t.storageNotice
3407
+ }),
3408
+ status?.credentialsRejected && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3409
+ className: "dsha-notice",
3410
+ children: t.refreshRejected
3411
+ }),
3412
+ loginPending && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3413
+ className: "dsha-device-box",
3414
+ children: [
3415
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t.deviceCodeSection }),
3416
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3417
+ className: "dsha-muted",
3418
+ children: t.deviceCodeHint
3419
+ }),
3420
+ flow?.userCode && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3421
+ className: "dsha-code-row",
3422
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3423
+ className: "dsha-code",
3424
+ children: flow.userCode
3425
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3426
+ className: "dsha-btn",
3427
+ onClick: () => void handleCopyCode(),
3428
+ children: copied ? t.copied : t.copyCode
3429
+ })]
3430
+ }),
3431
+ flow?.verificationUriComplete && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
3432
+ className: "dsha-link",
3433
+ href: flow.verificationUriComplete,
3434
+ target: "_blank",
3435
+ rel: "noreferrer noopener",
3436
+ children: flow.verificationUriComplete
3437
+ }),
3438
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3439
+ className: "dsha-row",
3440
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3441
+ className: "dsha-label",
3442
+ children: t.codeExpires
3443
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3444
+ className: "dsha-value",
3445
+ children: formatDate(flow?.expiresAt)
3446
+ })]
3447
+ }),
3448
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3449
+ className: "dsha-actions",
3450
+ children: [flow?.verificationUriComplete && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
3451
+ className: "dsha-btn",
3452
+ href: flow.verificationUriComplete,
3453
+ target: "_blank",
3454
+ rel: "noreferrer noopener",
3455
+ children: t.openSignInPage
3456
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3457
+ className: "dsha-btn",
3458
+ disabled: busy !== null,
3459
+ onClick: () => void handleCancelLogin(),
3460
+ children: t.cancelSignIn
3461
+ })]
3462
+ })
3463
+ ]
3464
+ }),
3465
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3466
+ className: "dsha-actions",
3467
+ children: !status?.authenticated ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3468
+ className: "dsha-btn dsha-btn-primary",
3469
+ disabled: busy !== null || loginPending,
3470
+ onClick: () => void handleLogin(),
3471
+ children: loginPending ? t.waitingAuthorization : busy === "login" ? t.requestingCode : t.signIn
3472
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
3473
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3474
+ className: "dsha-btn dsha-btn-primary",
3475
+ disabled: busy !== null || loginPending,
3476
+ onClick: () => void handleLogin(),
3477
+ children: loginPending ? t.waitingAuthorization : t.signInAgain
3478
+ }),
3479
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3480
+ className: "dsha-btn",
3481
+ disabled: busy !== null,
3482
+ onClick: () => void handleRefreshQuota(),
3483
+ children: busy === "quota" ? t.refreshingQuota : t.refreshQuota
3484
+ }),
3485
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3486
+ className: "dsha-btn",
3487
+ disabled: busy !== null,
3488
+ onClick: () => void handleLogout(),
3489
+ children: t.signOut
3490
+ })
3491
+ ] })
3492
+ })
3493
+ ]
3494
+ }),
3495
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
3496
+ className: "dsha-group",
3497
+ children: [
3498
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3499
+ className: "dsha-grouphead",
3500
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t.connection })
3501
+ }),
3502
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3503
+ className: "dsha-row",
3504
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3505
+ className: "dsha-label",
3506
+ children: t.provider
3507
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3508
+ className: "dsha-value",
3509
+ children: t.providerValue
3510
+ })]
3511
+ }),
3512
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3513
+ className: "dsha-row",
3514
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3515
+ className: "dsha-label",
3516
+ children: t.oauthHost
3517
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3518
+ className: "dsha-value",
3519
+ children: status?.oauthHost || "—"
3520
+ })]
3521
+ }),
3522
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3523
+ className: "dsha-row",
3524
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3525
+ className: "dsha-label",
3526
+ children: t.codingEndpoint
3527
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3528
+ className: "dsha-value",
3529
+ children: status?.codingBaseUrl || "—"
3530
+ })]
3531
+ }),
3532
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3533
+ className: "dsha-row",
3534
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3535
+ className: "dsha-label",
3536
+ children: t.connectionState
3537
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3538
+ className: "dsha-value",
3539
+ children: status?.authenticated ? t.connected : t.untested
3540
+ })]
3541
+ }),
3542
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3543
+ className: "dsha-notice",
3544
+ children: status?.serving === false && status.conflict ? t.routeConflict.replace("{detail}", status.conflict) : t.routeOwned
3545
+ }),
3546
+ connectionNotice && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3547
+ className: "dsha-muted",
3548
+ children: connectionNotice
3549
+ }),
3550
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3551
+ className: "dsha-actions",
3552
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3553
+ className: "dsha-btn",
3554
+ disabled: busy !== null || !status?.authenticated,
3555
+ onClick: () => void handleTestConnection(),
3556
+ children: busy === "connection" ? t.testingConnection : t.testConnection
3557
+ })
3558
+ })
3559
+ ]
3560
+ }),
3561
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
3562
+ className: "dsha-group",
3563
+ children: [
3564
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3565
+ className: "dsha-grouphead",
3566
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t.modelsSection }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3567
+ className: "dsha-btn",
3568
+ disabled: busy !== null,
3569
+ onClick: () => void handleRefreshCatalog(),
3570
+ children: busy === "catalog" ? t.refreshingCatalog : t.refreshCatalog
3571
+ })]
3572
+ }),
3573
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3574
+ className: "dsha-muted dsha-models-hint",
3575
+ children: t.modelsHint
3576
+ }),
3577
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3578
+ className: "dsha-models",
3579
+ "aria-label": "Kimi Code Models",
3580
+ children: status?.models.map((model) => {
3581
+ const lastVisible = model.enabled && visibleCount === 1;
3582
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
3583
+ title: [
3584
+ model.id,
3585
+ model.wire === "anthropic" ? t.wireAnthropic : t.wireOpenai,
3586
+ ...model.supportsVideo ? [t.capVideo] : [],
3587
+ ...model.supportsDynamicTools ? [t.capDynamicTools] : [],
3588
+ ...model.minimumPlan ? [t.capPlan.replace("{plan}", model.minimumPlan)] : []
3589
+ ].join(" · "),
3590
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
3591
+ type: "checkbox",
3592
+ checked: model.enabled,
3593
+ disabled: busy !== null || lastVisible,
3594
+ onChange: (event) => toggleModel(model.id, event.currentTarget.checked)
3595
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: model.name })]
3596
+ }, model.id);
3597
+ })
3598
+ }),
3599
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3600
+ className: "dsha-actions",
3601
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3602
+ className: "dsha-btn",
3603
+ disabled: busy !== null,
3604
+ onClick: () => setAllModels(true),
3605
+ children: t.selectAll
3606
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3607
+ className: "dsha-btn",
3608
+ disabled: busy !== null,
3609
+ onClick: () => setAllModels(false),
3610
+ children: t.unselectAll
3611
+ })]
3612
+ }),
3613
+ status !== null && status.models.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(KimiModelCapabilities, { models: status.models })
3614
+ ]
3615
+ }),
3616
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
3617
+ className: "dsha-group",
3618
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3619
+ className: "dsha-grouphead",
3620
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t.enhanced })
3621
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3622
+ className: "dsha-pref-row",
3623
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t.defaultReasoningEffort }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3624
+ className: "dsha-muted",
3625
+ children: t.defaultReasoningEffortHint
3626
+ })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
3627
+ className: "dsha-select",
3628
+ "aria-label": t.defaultReasoningEffort,
3629
+ value: status?.defaultReasoningEffort ?? "",
3630
+ disabled: busy !== null,
3631
+ onChange: (event) => {
3632
+ const value = event.currentTarget.value;
3633
+ handleUpdateEffort(value === "" ? null : value);
3634
+ },
3635
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3636
+ value: "",
3637
+ children: t.defaultEffortAuto
3638
+ }), KIMI_CODE_REASONING_EFFORTS.map((effort) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3639
+ value: effort,
3640
+ children: EFFORT_LABEL[effort]
3641
+ }, effort))]
3642
+ })]
3643
+ })]
3644
+ }),
3645
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
3646
+ className: "dsha-group",
3647
+ children: [
3648
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3649
+ className: "dsha-grouphead",
3650
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t.contextWindowSection })
3651
+ }),
3652
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3653
+ className: "dsha-muted",
3654
+ children: t.contextWindowHint
3655
+ }),
3656
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3657
+ className: "dsha-context-settings",
3658
+ children: status?.models.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3659
+ className: "dsha-context-row",
3660
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3661
+ title: model.id,
3662
+ children: [model.name, model.defaultContextWindow < model.contextWindow && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3663
+ className: "dsha-model-meta",
3664
+ children: formatCapacity(model.contextWindow)
3665
+ })]
3666
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3667
+ className: "dsha-capacity-control",
3668
+ children: [
3669
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
3670
+ type: "text",
3671
+ "aria-label": `${model.name} context window`,
3672
+ value: contextDrafts[model.id] ?? "",
3673
+ onChange: (event) => setContextDrafts({
3674
+ ...contextDrafts,
3675
+ [model.id]: event.currentTarget.value
3676
+ }),
3677
+ onKeyDown: (event) => {
3678
+ if (event.key === "Enter") handleSaveContextWindow(model.id);
3679
+ }
3680
+ }),
3681
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: t.tokens }),
3682
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3683
+ type: "button",
3684
+ className: "dsha-context-save",
3685
+ disabled: savingModel === model.id,
3686
+ onClick: () => void handleSaveContextWindow(model.id),
3687
+ children: savingModel === model.id ? t.saving : t.save
3688
+ })
3689
+ ]
3690
+ })]
3691
+ }, model.id))
3692
+ })
3693
+ ]
3694
+ }),
3695
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
3696
+ className: "dsha-group",
3697
+ children: [
3698
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3699
+ className: "dsha-grouphead",
3700
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t.quotaSection }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3701
+ className: "dsha-btn",
3702
+ disabled: busy !== null || !status?.authenticated,
3703
+ onClick: () => void handleRefreshQuota(),
3704
+ children: busy === "quota" ? t.refreshingQuota : t.refreshQuota
3705
+ })]
3706
+ }),
3707
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3708
+ className: "dsha-muted",
3709
+ children: t.quotaDesc
3710
+ }),
3711
+ status?.quotaError && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3712
+ className: "dsha-notice",
3713
+ children: status.quotaError
3714
+ }),
3715
+ !status?.authenticated ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3716
+ className: "dsha-empty",
3717
+ children: t.signedOut
3718
+ }) : quota === null || quota === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3719
+ className: "dsha-empty",
3720
+ children: busy === "quota" ? t.refreshingQuota : t.quotaEmpty
3721
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3722
+ className: "dsha-quota-card",
3723
+ children: [
3724
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3725
+ className: "dsha-quota-title",
3726
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t.quotaPlan }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: quota.planName || t.planUnknown })]
3727
+ }),
3728
+ quota.windows.map((window) => {
3729
+ const remaining = Math.max(0, 100 - window.usedPercent);
3730
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3731
+ className: "dsha-meter-wrap",
3732
+ children: [
3733
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3734
+ className: "dsha-meter-label",
3735
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: window.label }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("strong", { children: [remaining, "% left"] })]
3736
+ }),
3737
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3738
+ className: `dsha-meter ${remaining <= 10 ? "dsha-meter-cyan" : "dsha-meter-green"}`,
3739
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { style: { width: `${remaining}%` } })
3740
+ }),
3741
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3742
+ className: "dsha-meter-meta",
3743
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
3744
+ window.used ?? "—",
3745
+ " / ",
3746
+ window.limit ?? "—",
3747
+ " ",
3748
+ t.requests
3749
+ ] }), window.resetsAt !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t.resetAt.replace("{time}", formatReset(window.resetsAt)) })]
3750
+ })
3751
+ ]
3752
+ }, window.id);
3753
+ }),
3754
+ quota.extraUsage !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3755
+ className: "dsha-meter-wrap",
3756
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3757
+ className: "dsha-meter-label",
3758
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t.quotaWallet }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: formatMoney(quota.extraUsage.balanceCents, quota.extraUsage.currency) })]
3759
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3760
+ className: "dsha-meter-meta",
3761
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: formatMoney(quota.extraUsage.totalCents, quota.extraUsage.currency) }), quota.extraUsage.monthlyChargeLimitEnabled && quota.extraUsage.monthlyChargeLimitCents !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
3762
+ t.quotaWalletLimit,
3763
+ ": ",
3764
+ formatMoney(quota.extraUsage.monthlyChargeLimitCents, quota.extraUsage.currency)
3765
+ ] })]
3766
+ })]
3767
+ }),
3768
+ quota.windows.length === 0 && quota.extraUsage === null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3769
+ className: "dsha-empty",
3770
+ children: t.quotaEmpty
3771
+ }),
3772
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3773
+ className: "dsha-timestamp",
3774
+ children: t.updatedAt.replace("{time}", new Date(quota.fetchedAt).toLocaleString())
3775
+ })
3776
+ ]
3777
+ })
3778
+ ]
3779
+ }),
3780
+ error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3781
+ className: "dsha-error",
3782
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: error })
3783
+ })
3784
+ ]
3785
+ });
3786
+ }
3787
+ //#endregion
3788
+ //#region src/client/ProviderHubSection.tsx
3789
+ const HUB_TABS = [
3790
+ {
3791
+ id: "chatgpt",
3792
+ label: "ChatGPT"
3793
+ },
3794
+ {
3795
+ id: "antigravity",
3796
+ label: "Antigravity"
3797
+ },
3798
+ {
3799
+ id: "command-code",
3800
+ label: "Command Code"
3801
+ },
3802
+ {
3803
+ id: "kimi-code",
3804
+ label: "Kimi Code"
3805
+ }
3806
+ ];
3807
+ /**
3808
+ * Single settings page that hosts every subscription provider behind tabs,
3809
+ * so the settings sidebar shows one entry instead of one per provider. Only
3810
+ * the active tab is mounted, mirroring the shell's previous behavior of
3811
+ * mounting just the selected settings page (each provider section refetches
3812
+ * its status on mount).
3813
+ */
3814
+ function ProviderHubSection({ t, onModelChange, ...runtime }) {
3815
+ const [active, setActive] = (0, react.useState)("chatgpt");
3816
+ const onTabKeyDown = (0, react.useCallback)((event) => {
3817
+ if (event.key !== "ArrowLeft" && event.key !== "ArrowRight") return;
3818
+ event.preventDefault();
3819
+ const index = HUB_TABS.findIndex((tab) => tab.id === active);
3820
+ const delta = event.key === "ArrowRight" ? 1 : -1;
3821
+ const next = HUB_TABS[(index + delta + HUB_TABS.length) % HUB_TABS.length];
3822
+ setActive(next.id);
3823
+ document.getElementById(`dsh-hub-tab-${next.id}`)?.focus();
3824
+ }, [active]);
3825
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
3826
+ className: "dsh-codex-page",
3827
+ "aria-labelledby": "dsh-hub-title",
3828
+ children: [
3829
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("header", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
3830
+ id: "dsh-hub-title",
3831
+ className: "dsh-codex-title",
3832
+ children: t("hubTitle")
3833
+ }) }),
3834
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3835
+ className: "dsh-codex-segments dsh-hub-tabs",
3836
+ role: "tablist",
3837
+ "aria-label": t("hubTitle"),
3838
+ children: HUB_TABS.map((tab) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3839
+ type: "button",
3840
+ role: "tab",
3841
+ id: `dsh-hub-tab-${tab.id}`,
3842
+ "aria-selected": active === tab.id,
3843
+ "aria-controls": `dsh-hub-panel-${tab.id}`,
3844
+ className: active === tab.id ? "active" : void 0,
3845
+ tabIndex: active === tab.id ? 0 : -1,
3846
+ onClick: () => setActive(tab.id),
3847
+ onKeyDown: onTabKeyDown,
3848
+ children: tab.label
3849
+ }, tab.id))
3850
+ }),
3851
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3852
+ role: "tabpanel",
3853
+ id: `dsh-hub-panel-${active}`,
3854
+ "aria-labelledby": `dsh-hub-tab-${active}`,
3855
+ children: [
3856
+ active === "chatgpt" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CodexSubscriptionSection, {
3857
+ t,
3858
+ ...runtime
3859
+ }) : null,
3860
+ active === "antigravity" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AntigravitySection, { onModelChange }) : null,
3861
+ active === "command-code" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CommandCodeSection, { onModelChange }) : null,
3862
+ active === "kimi-code" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(KimiCodeSection, { onModelChange }) : null
3863
+ ]
3864
+ })
3865
+ ]
3866
+ });
3867
+ }
3868
+ //#endregion
3869
+ //#region src/client/locales.ts
3870
+ const NS = "dsh-chatgpt-subscription";
3871
+ const dictionaries = {
3872
+ zh: {
3873
+ title: "ChatGPT",
3874
+ hubTitle: "订阅服务",
3875
+ intro: "使用 ChatGPT 账号登录,在 DSH 中使用订阅可用的模型。",
3876
+ account: "账号",
3877
+ signedOut: "尚未登录",
3878
+ signedIn: "已登录",
3879
+ plan: "套餐",
3880
+ accountId: "账号 ID",
3881
+ expires: "令牌到期",
3882
+ storage: "凭据存储",
3883
+ storageWindows: "Windows DPAPI(当前用户加密)",
3884
+ storageMacKeychain: "macOS 钥匙串(Keychain 加密)",
3885
+ storageLinuxFile: "Linux 用户私有文件(权限 0600)",
3886
+ storageMemory: "仅 Host 内存(不持久化)",
3887
+ storageUnavailable: "凭据存储不可用",
3888
+ securityWindows: "令牌由 Host 使用 Windows CurrentUser DPAPI 加密,不会进入浏览器、settings.yaml 或日志。",
3889
+ securityMacKeychain: "令牌由 Host 存入 macOS 登录钥匙串(Keychain),在本机加密保存,不会进入浏览器、settings.yaml 或日志。",
3890
+ securityLinuxFile: "令牌仅写入 Host 上当前 Linux 用户可读的 0600 文件,但不会额外加密;同 UID 进程、root、备份和磁盘快照仍可能读取。",
3891
+ securityMemory: "令牌仅保留在 Host 内存中,Host 退出后丢失。",
3892
+ securityUnavailable: "Host 无法安全访问凭据存储;请修复文件所有者或权限后重试。",
3893
+ signIn: "使用 ChatGPT 登录",
3894
+ signInAgain: "重新登录",
3895
+ cancel: "取消登录",
3896
+ signOut: "注销",
3897
+ refreshToken: "刷新凭据",
3898
+ pending: "请在浏览器中完成登录。",
3899
+ popupBlocked: "浏览器拦截了登录窗口,请使用下面的链接继续。",
3900
+ continueLogin: "打开 ChatGPT 登录页",
3901
+ loading: "正在读取状态…",
3902
+ connection: "连接",
3903
+ provider: "Provider",
3904
+ connectionState: "连接状态",
3905
+ connected: "可用",
3906
+ untested: "尚未测试",
3907
+ testConnection: "测试连接",
3908
+ testing: "测试中…",
3909
+ latency: "最近延迟",
3910
+ models: "可用模型",
3911
+ modelsHint: "勾选后模型才会显示在对话页的模型选择列表中;至少保留一个。",
3912
+ proxySettings: "网络代理",
3913
+ proxyMode: "代理模式",
3914
+ proxyModeHint: "GPT 与 Antigravity(Gemini)共用此代理设置;支持自动读取系统代理,修改后对后续请求生效。",
3915
+ proxyModeAuto: "系统代理(自动检测)",
3916
+ proxyModeCustom: "自定义代理",
3917
+ proxyModeDirect: "直连(禁用代理)",
3918
+ customProxyUrl: "自定义代理地址",
3919
+ customProxyUrlHint: "支持 HTTP/HTTPS/SOCKS5 代理地址,例如 http://127.0.0.1:7890",
3920
+ customProxyUrlPlaceholder: "例如: http://127.0.0.1:7890",
3921
+ saveProxyUrl: "保存代理地址",
3922
+ proxyDetected: "检测到系统代理",
3923
+ proxyDetectedEffective: "(已自动生效)",
3924
+ proxyNoneDetected: "未检测到系统代理,将使用直连",
3925
+ proxyDirectHint: "所有网络请求将直接连接,不经过任何代理服务器。",
3926
+ enhancements: "增强功能",
3927
+ fastMode: "快速模式(1.5x 速度)",
3928
+ fastModeHint: "提升 Token 生成速度至约 1.5 倍;将以约 2x–2.5x 速度消耗 Codex 订阅额度。",
3929
+ outputVerbosity: "输出详细程度",
3930
+ outputVerbosityHint: "控制 Codex 最终回答的详细程度;不影响思考深度。",
3931
+ verbosityLow: "低(保持回答简洁)",
3932
+ verbosityMedium: "中(兼顾细节与简洁)",
3933
+ verbosityHigh: "高(在回答中包含更多细节)",
3934
+ reasoningSummary: "推理摘要",
3935
+ reasoningSummaryHint: "选择 ChatGPT 总结其推理的方式;不影响思考深度。",
3936
+ summaryAuto: "自动(让模型选择摘要详细程度)",
3937
+ summaryConcise: "简洁(显示简要推理摘要)",
3938
+ summaryDetailed: "详细(显示更详细的推理摘要)",
3939
+ summaryNone: "无(不显示推理摘要)",
3940
+ searchProvider: "搜索与抓取来源",
3941
+ searchProviderHint: "选择 DSH 联网工具使用默认来源,或改用当前 ChatGPT 订阅的 Codex 来源(自动走代理隧道)。",
3942
+ searchProviderDsh: "DSH 默认",
3943
+ searchProviderCodex: "ChatGPT(代理隧道)",
3944
+ providerDefault: "使用 Provider 默认值",
3945
+ contextWindows: "模型上下文窗口",
3946
+ contextWindowsHint: "默认 272K;6 Astra 最高 872K,GPT-5.6 最高 1M。该值用于 DSH 的压缩与溢出判断,可输入 872K、512K 等容量。",
3947
+ contextWindow: "上下文窗口",
3948
+ contextWindowInvalid: "上下文窗口必须是正整数,且不能超过模型上限(6 Astra 为 872K,GPT-5.6 为 1M);可使用 K 或 M 后缀。",
3949
+ saveContextWindow: "保存上下文窗口",
3950
+ save: "保存",
3951
+ tokens: "tokens",
3952
+ quickQuota: "在输入框旁显示快捷用量",
3953
+ quickQuotaHint: "仅在当前会话选中 codex-chatgpt 模型时显示。",
3954
+ quota: "用量与限额",
3955
+ quotaIntro: "数据来自 ChatGPT Codex 用量服务。页面可见时最多每 60 秒刷新一次。",
3956
+ refreshQuota: "刷新用量",
3957
+ refreshing: "刷新中…",
3958
+ noQuota: "当前套餐未返回可显示的限额窗口。",
3959
+ quotaSignedOut: "登录后可查看订阅限额。",
3960
+ stale: "显示的是上次成功获取的数据。",
3961
+ updated: "更新时间",
3962
+ primary: "主要窗口",
3963
+ secondary: "次要窗口",
3964
+ limitWindow: "额度",
3965
+ used: "已使用",
3966
+ remaining: "剩余",
3967
+ available: "可用",
3968
+ unavailable: "不可用",
3969
+ unlimited: "无限",
3970
+ credits: "Credits",
3971
+ monthlySpend: "月度消费控制",
3972
+ resetCredits: "重置次数",
3973
+ resetCreditExpires: "到期时间",
3974
+ useResetCredit: "使用重置卡",
3975
+ usingResetCredit: "使用中…",
3976
+ useResetCreditConfirm: "使用后会立即消耗 1 次重置机会,并重置符合条件的 Codex 额度窗口。确定继续吗?",
3977
+ resetCreditUsed: "重置卡已使用,用量数据已刷新。",
3978
+ spendControlReached: "已触发月度消费控制,新的订阅调用可能被限制。",
3979
+ limit: "上限",
3980
+ exhausted: "额度已用尽",
3981
+ resets: "重置",
3982
+ quickQuotaLabel: "Codex 剩余",
3983
+ quickQuotaLoading: "用量…",
3984
+ imageToolRunning: "正在生成图片",
3985
+ imageToolDone: "已生成图片",
3986
+ imageToolFailed: "图片生成失败",
3987
+ image: "图片",
3988
+ openImage: "打开图片",
3989
+ openNamedImage: "打开 {name}",
3990
+ imageLoading: "加载图片…",
3991
+ imageLoadFailed: "图片加载失败,点击重试",
3992
+ imagePreviewClose: "关闭",
3993
+ imagePreviewOpenOriginal: "打开原图",
3994
+ retry: "重试",
3995
+ unknown: "未知"
3996
+ },
3997
+ en: {
3998
+ title: "ChatGPT",
3999
+ hubTitle: "Subscriptions",
4000
+ intro: "Sign in with ChatGPT to use models available to your subscription in DSH.",
4001
+ account: "Account",
4002
+ signedOut: "Not signed in",
4003
+ signedIn: "Signed in",
4004
+ plan: "Plan",
4005
+ accountId: "Account ID",
4006
+ expires: "Token expires",
4007
+ storage: "Credential storage",
4008
+ storageWindows: "Windows DPAPI (current-user encrypted)",
4009
+ storageMacKeychain: "macOS Keychain (encrypted)",
4010
+ storageLinuxFile: "Linux user-private file (mode 0600)",
4011
+ storageMemory: "Host memory only (not persistent)",
4012
+ storageUnavailable: "Credential storage unavailable",
4013
+ securityWindows: "The Host encrypts tokens with Windows CurrentUser DPAPI; they never enter the browser, settings.yaml, or logs.",
4014
+ securityMacKeychain: "The Host stores tokens in the macOS login Keychain, encrypted on this machine; they never enter the browser, settings.yaml, or logs.",
4015
+ securityLinuxFile: "Tokens are stored in a mode-0600 file readable only by the current Linux user, but are not additionally encrypted; same-UID processes, root, backups, and disk snapshots can still read them.",
4016
+ securityMemory: "Tokens remain only in Host memory and are lost when the Host exits.",
4017
+ securityUnavailable: "The Host cannot safely access credential storage. Fix the file owner or permissions and retry.",
4018
+ signIn: "Sign in with ChatGPT",
4019
+ signInAgain: "Sign in again",
4020
+ cancel: "Cancel sign-in",
4021
+ signOut: "Sign out",
4022
+ refreshToken: "Refresh credentials",
4023
+ pending: "Complete sign-in in your browser.",
4024
+ popupBlocked: "The sign-in window was blocked. Use the link below to continue.",
4025
+ continueLogin: "Open ChatGPT sign-in",
4026
+ loading: "Reading status…",
4027
+ connection: "Connection",
4028
+ provider: "Provider",
4029
+ connectionState: "Connection status",
4030
+ connected: "Available",
4031
+ untested: "Not tested",
4032
+ testConnection: "Test connection",
4033
+ testing: "Testing…",
4034
+ latency: "Last latency",
4035
+ models: "Available models",
4036
+ modelsHint: "Checked models appear in the conversation model picker; at least one must remain enabled.",
4037
+ proxySettings: "Network Proxy",
4038
+ proxyMode: "Proxy Mode",
4039
+ proxyModeHint: "GPT and Antigravity (Gemini) share these proxy settings. Supports system proxy detection; changes apply to subsequent requests.",
4040
+ proxyModeAuto: "System Proxy (Auto-detect)",
4041
+ proxyModeCustom: "Custom Proxy",
4042
+ proxyModeDirect: "Direct (No Proxy)",
4043
+ customProxyUrl: "Custom Proxy URL",
4044
+ customProxyUrlHint: "Supports HTTP/HTTPS/SOCKS5 proxy URLs, e.g. http://127.0.0.1:7890",
4045
+ customProxyUrlPlaceholder: "e.g. http://127.0.0.1:7890",
4046
+ saveProxyUrl: "Save Proxy URL",
4047
+ proxyDetected: "Detected system proxy",
4048
+ proxyDetectedEffective: " (active)",
4049
+ proxyNoneDetected: "No system proxy detected; connecting directly",
4050
+ proxyDirectHint: "All requests connect directly without using any proxy server.",
4051
+ enhancements: "Enhancements",
4052
+ fastMode: "Fast mode (1.5x speed)",
4053
+ fastModeHint: "Generates tokens ~1.5x faster; consumes Codex subscription quota at ~2x–2.5x the normal rate.",
4054
+ outputVerbosity: "Output verbosity",
4055
+ outputVerbosityHint: "Controls the detail level of final Codex answers without changing reasoning effort.",
4056
+ verbosityLow: "Low (keep answers concise)",
4057
+ verbosityMedium: "Medium (balance detail and brevity)",
4058
+ verbosityHigh: "High (include more detail)",
4059
+ reasoningSummary: "Reasoning summary",
4060
+ reasoningSummaryHint: "Controls how ChatGPT summarizes its chain of thought; does not change reasoning depth.",
4061
+ summaryAuto: "Auto (let model determine detail)",
4062
+ summaryConcise: "Concise (brief summary)",
4063
+ summaryDetailed: "Detailed (in-depth summary)",
4064
+ summaryNone: "None (hide reasoning summary)",
4065
+ searchProvider: "Search & Fetch provider",
4066
+ searchProviderHint: "Choose whether DSH web tools use the default provider or the Codex proxy-backed source from this ChatGPT subscription.",
4067
+ searchProviderDsh: "DSH default",
4068
+ searchProviderCodex: "ChatGPT (Proxy tunnel)",
4069
+ providerDefault: "Use provider default",
4070
+ contextWindows: "Model context windows",
4071
+ contextWindowsHint: "Defaults to 272K, with up to 872K for 6 Astra and 1M for GPT-5.6. DSH uses it for compaction and overflow detection; values such as 872K and 512K are accepted.",
4072
+ contextWindow: "Context window",
4073
+ contextWindowInvalid: "The context window must be a positive integer within the model limit (872K for 6 Astra, 1M for GPT-5.6). K and M suffixes are accepted.",
4074
+ saveContextWindow: "Save context window",
4075
+ save: "Save",
4076
+ tokens: "tokens",
4077
+ quickQuota: "Show quick usage beside the composer",
4078
+ quickQuotaHint: "Shown only when the current session uses a codex-chatgpt model.",
4079
+ quota: "Usage and limits",
4080
+ quotaIntro: "Data comes from the ChatGPT Codex usage service and refreshes at most once per minute while visible.",
4081
+ refreshQuota: "Refresh usage",
4082
+ refreshing: "Refreshing…",
4083
+ noQuota: "Your plan did not return any displayable limit windows.",
4084
+ quotaSignedOut: "Sign in to view subscription limits.",
4085
+ stale: "Showing the last successfully fetched data.",
4086
+ updated: "Updated",
4087
+ primary: "Primary window",
4088
+ secondary: "Secondary window",
4089
+ limitWindow: "limit",
4090
+ used: "used",
4091
+ remaining: "remaining",
4092
+ available: "Available",
4093
+ unavailable: "Unavailable",
4094
+ unlimited: "Unlimited",
4095
+ credits: "Credits",
4096
+ monthlySpend: "Monthly spend control",
4097
+ resetCredits: "Reset credits",
4098
+ resetCreditExpires: "Expires",
4099
+ useResetCredit: "Use reset credit",
4100
+ usingResetCredit: "Using…",
4101
+ useResetCreditConfirm: "This immediately spends one reset credit and resets eligible Codex limit windows. Continue?",
4102
+ resetCreditUsed: "The reset credit was used and usage data was refreshed.",
4103
+ spendControlReached: "Monthly spend control has been reached; new subscription calls may be limited.",
4104
+ limit: "Limit",
4105
+ exhausted: "Quota exhausted",
4106
+ resets: "Resets",
4107
+ quickQuotaLabel: "Codex left",
4108
+ quickQuotaLoading: "Usage…",
4109
+ imageToolRunning: "Generating image",
4110
+ imageToolDone: "Generated image",
4111
+ imageToolFailed: "Image generation failed",
4112
+ image: "Image",
4113
+ openImage: "Open image",
4114
+ openNamedImage: "Open {name}",
4115
+ imageLoading: "Loading image…",
4116
+ imageLoadFailed: "Image failed to load. Click to retry",
4117
+ imagePreviewClose: "Close",
4118
+ imagePreviewOpenOriginal: "Open original",
4119
+ retry: "Retry",
4120
+ unknown: "Unknown"
4121
+ }
4122
+ };
4123
+ //#endregion
4124
+ //#region src/client/styles.ts
4125
+ const STYLE_ID$2 = "@eddyskywalker/dsh-chatgpt-subscription/main";
4126
+ const CSS = `
4127
+ .dsh-codex-page{box-sizing:border-box;color:var(--dsw-alias-label-primary);display:flex;flex-direction:column;gap:20px;max-width:780px;min-width:0;padding:2px 0 30px}
4128
+ .dsh-codex-page *{box-sizing:border-box}
4129
+ .dsh-codex-title{font-size:15px;font-weight:650;line-height:1.4;margin:0 0 5px}
4130
+ .dsh-codex-intro,.dsh-codex-muted{color:var(--dsw-alias-label-secondary);font-size:13px;line-height:1.55;margin:0}
4131
+ .dsh-codex-group{border-top:0.5px solid var(--dsw-alias-border-l2);min-width:0}
4132
+ .dsh-codex-grouphead{align-items:center;display:flex;gap:12px;justify-content:space-between;min-height:48px}
4133
+ .dsh-codex-grouphead h3{font-size:14px;font-weight:650;margin:0}
4134
+ .dsh-codex-row{align-items:center;border-bottom:0.5px solid var(--dsw-alias-border-l2);display:flex;gap:20px;justify-content:space-between;min-height:44px;padding:8px 0}
4135
+ .dsh-codex-label{color:var(--dsw-alias-label-secondary);font-size:13px;flex:0 0 auto}
4136
+ .dsh-codex-value{font-size:13px;min-width:0;overflow-wrap:anywhere;text-align:right}
4137
+ .dsh-codex-actions{display:flex;flex-wrap:wrap;gap:8px;justify-content:flex-end;padding-top:12px}
4138
+ .dsh-codex-button{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:999px;corner-shape:round;color:var(--dsw-alias-label-primary);cursor:pointer;font:inherit;font-size:13px;line-height:1;padding:8px 13px;white-space:nowrap}
4139
+ .dsh-codex-button:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}
4140
+ .dsh-codex-button:focus-visible,.dsh-codex-link:focus-visible{outline:2px solid var(--dsw-alias-button-info-fill,#397ee8);outline-offset:2px}
4141
+ .dsh-codex-button:disabled{cursor:default;opacity:.5}
4142
+ .dsh-codex-button-primary{background:var(--dsw-alias-button-info-fill,#397ee8);border-color:transparent;color:var(--dsw-alias-button-info-label,#fff)}
4143
+ .dsh-codex-notice{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:7px;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.55;margin:12px 0 0;padding:10px 12px}
4144
+ .dsh-codex-error,.dsh-codex-warning,.dsh-codex-success{font-size:12px;line-height:1.5;margin:8px 0 0}
4145
+ .dsh-codex-error{color:var(--dsw-alias-label-danger,#d94b4b)}
4146
+ .dsh-codex-warning{color:var(--dsw-alias-label-warning,#c77a18)}
4147
+ .dsh-codex-success{color:var(--dsw-alias-label-success,#2e9b62)}
4148
+ .dsh-codex-errorbar{align-items:center;background:color-mix(in srgb,var(--dsw-alias-label-danger,#d94b4b) 9%,transparent);border:0.5px solid color-mix(in srgb,var(--dsw-alias-label-danger,#d94b4b) 28%,transparent);border-radius:7px;color:var(--dsw-alias-label-danger,#d94b4b);display:flex;font-size:13px;gap:12px;justify-content:space-between;padding:10px 12px}
4149
+ .dsh-codex-link{color:var(--dsw-alias-label-link,#3278d4);display:inline-block;font-size:13px;margin-top:8px}
4150
+ .dsh-codex-models-hint{padding-top:10px}
4151
+ .dsh-codex-models{display:flex;flex-wrap:wrap;gap:7px;padding-top:8px}
4152
+ .dsh-codex-models label{cursor:pointer;display:block;position:relative}
4153
+ .dsh-codex-models input{position:absolute;opacity:0;pointer-events:none}
4154
+ .dsh-codex-models span{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:6px;color:var(--dsw-alias-label-secondary);display:block;font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:11px;padding:5px 8px}
4155
+ .dsh-codex-models input:checked+span{background:color-mix(in srgb,var(--dsw-alias-button-info-fill,#397ee8) 14%,var(--dsw-alias-bg-layer-2));border-color:color-mix(in srgb,var(--dsw-alias-button-info-fill,#397ee8) 55%,var(--dsw-alias-border-l2));color:var(--dsw-alias-label-primary)}
4156
+ .dsh-codex-models input:focus-visible+span{outline:2px solid var(--dsw-alias-button-info-fill,#397ee8);outline-offset:2px}
4157
+ .dsh-codex-models input:disabled+span{cursor:default;opacity:.5}
4158
+ .dsh-codex-pref-row{align-items:center;border-bottom:0.5px solid var(--dsw-alias-border-l2);display:flex;gap:16px;justify-content:space-between;min-height:58px;padding:10px 0}
4159
+ .dsh-codex-pref-row strong,.dsh-codex-check strong{display:block;font-size:13px;font-weight:600;line-height:1.35}
4160
+ .dsh-codex-segments{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:8px;display:inline-flex;flex-wrap:nowrap;gap:3px;padding:3px}
4161
+ .dsh-codex-segments label{cursor:pointer;display:block;margin:0}
4162
+ .dsh-codex-segments input{position:absolute;opacity:0;pointer-events:none}
4163
+ .dsh-codex-segments span,.dsh-codex-segments button{border:0.5px solid transparent;border-radius:6px;color:var(--dsw-alias-label-secondary);display:block;font:inherit;font-size:12px;font-weight:500;line-height:1.2;padding:6px 12px;transition:all .15s ease;user-select:none;white-space:nowrap}
4164
+ .dsh-codex-segments button{background:transparent;cursor:pointer}
4165
+ .dsh-codex-segments label:hover span,.dsh-codex-segments button:hover:not(:disabled){color:var(--dsw-alias-label-primary)}
4166
+ .dsh-codex-segments input:checked+span,.dsh-codex-segments button.active,.dsh-codex-segments button[aria-pressed=true]{background:color-mix(in srgb,var(--dsw-alias-button-info-fill,#397ee8) 22%,var(--dsw-alias-bg-layer-2));border-color:color-mix(in srgb,var(--dsw-alias-button-info-fill,#397ee8) 60%,var(--dsw-alias-border-l2));color:var(--dsw-alias-label-primary);font-weight:600;box-shadow:0 1px 3px rgba(0,0,0,.15)}
4167
+ .dsh-codex-segments input:focus-visible+span,.dsh-codex-segments button:focus-visible{outline:2px solid var(--dsw-alias-button-info-fill,#397ee8);outline-offset:2px}
4168
+ .dsh-codex-segments input:disabled+span,.dsh-codex-segments button:disabled{cursor:default;opacity:.5}
4169
+ .dsh-codex-select,.dsh-codex-capacity-control input{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:7px;color:var(--dsw-alias-label-primary);font:inherit;font-size:12px;padding:7px 9px}
4170
+ .dsh-codex-select{max-width:210px;min-width:150px}
4171
+ .dsh-codex-context-settings{border-bottom:0.5px solid var(--dsw-alias-border-l2);display:grid;gap:10px;padding:12px 0}
4172
+ .dsh-codex-context-settings>div>strong{font-size:13px;font-weight:600}
4173
+ .dsh-codex-context-row{align-items:center;display:flex;font-size:12px;gap:14px;justify-content:space-between}
4174
+ .dsh-codex-proxy-control{align-items:center;display:flex;gap:8px;width:100%}
4175
+ .dsh-codex-proxy-control input{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:7px;color:var(--dsw-alias-label-primary);flex:1;font:inherit;font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:12px;min-width:220px;padding:7px 10px;text-align:left}
4176
+ .dsh-codex-proxy-status{align-items:center;color:var(--dsw-alias-label-secondary);display:flex;flex-wrap:wrap;font-size:12px;gap:6px;margin:6px 0 0}
4177
+ .dsh-codex-proxy-tag{background:color-mix(in srgb,var(--dsw-alias-button-info-fill,#397ee8) 18%,transparent);border:0.5px solid color-mix(in srgb,var(--dsw-alias-button-info-fill,#397ee8) 45%,transparent);border-radius:4px;color:var(--dsw-alias-label-primary);font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:11px;padding:2px 6px}
4178
+ .dsh-codex-capacity-control{align-items:center;display:flex;gap:7px}
4179
+ .dsh-codex-capacity-control input{font-family:ui-monospace,SFMono-Regular,Consolas,monospace;text-align:right;width:90px}
4180
+ .dsh-codex-capacity-control input[type=number]{width:110px}
4181
+ .dsh-codex-capacity-control small{color:var(--dsw-alias-label-tertiary);font-size:11px;width:42px}
4182
+ .dsh-codex-context-save{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:7px;color:var(--dsw-alias-label-primary);cursor:pointer;font:inherit;font-size:12px;line-height:1;padding:8px 10px;white-space:nowrap}
4183
+ .dsh-codex-context-save:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}
4184
+ .dsh-codex-context-save:focus-visible{outline:2px solid var(--dsw-alias-button-info-fill,#397ee8);outline-offset:2px}
4185
+ .dsh-codex-context-save:disabled{cursor:default;opacity:.45}
4186
+ .dsh-codex-check{align-items:flex-start;border-bottom:0.5px solid var(--dsw-alias-border-l2);cursor:pointer;display:flex;gap:10px;padding:12px 0}
4187
+ .dsh-codex-check input{flex:none;margin-top:2px}
4188
+ .dsh-codex-check small{color:var(--dsw-alias-label-secondary);display:block;font-size:12px;line-height:1.45;margin-top:2px}
4189
+ .dsh-codex-quota-card{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:8px;margin-top:12px;padding:12px}
4190
+ .dsh-codex-quota-title{align-items:center;display:flex;font-size:13px;gap:8px;justify-content:space-between}
4191
+ .dsh-codex-quota-title span{color:var(--dsw-alias-label-tertiary);font-size:11px;text-transform:uppercase}
4192
+ .dsh-codex-quota-fact{align-items:flex-start;border-bottom:0.5px solid var(--dsw-alias-border-l2);display:flex;font-size:12px;gap:12px;justify-content:space-between;line-height:1.45;padding:10px 0}
4193
+ .dsh-codex-quota-fact span{color:var(--dsw-alias-label-secondary);flex:none}
4194
+ .dsh-codex-quota-fact strong{font-weight:600;min-width:0;overflow-wrap:anywhere;text-align:right}
4195
+ .dsh-codex-reset-credits{align-items:center;border-bottom:0.5px solid var(--dsw-alias-border-l2);display:flex;gap:16px;justify-content:space-between;padding:10px 0}
4196
+ .dsh-codex-reset-credits-info{display:grid;font-size:12px;gap:2px;grid-template-columns:auto auto}
4197
+ .dsh-codex-reset-credits-info>span{color:var(--dsw-alias-label-secondary)}
4198
+ .dsh-codex-reset-credits-info>strong{font-weight:650;text-align:right}
4199
+ .dsh-codex-reset-credits-info>small{color:var(--dsw-alias-label-tertiary);font-size:11px;grid-column:1/-1}
4200
+ .dsh-codex-meter-wrap{margin-top:13px}
4201
+ .dsh-codex-meter-label,.dsh-codex-meter-meta{display:flex;gap:10px;justify-content:space-between}
4202
+ .dsh-codex-meter-label{font-size:12px;margin-bottom:6px}
4203
+ .dsh-codex-meter-meta{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:1.45;margin-top:6px}
4204
+ .dsh-codex-meter{background:var(--dsw-alias-bg-layer-1,rgba(127,127,127,.15));border-radius:999px;corner-shape:round;height:7px;overflow:hidden;width:100%}
4205
+ .dsh-codex-meter>span{background:var(--dsw-alias-button-info-fill,#397ee8);border-radius:inherit;display:block;height:100%;max-width:100%;min-width:0;transition:width .25s ease}
4206
+ .dsh-codex-meter-warning>span{background:var(--dsw-alias-label-warning,#d58a24)}
4207
+ .dsh-codex-meter-danger>span{background:var(--dsw-alias-label-danger,#d94b4b)}
4208
+ .dsh-codex-empty{border:0.5px dashed var(--dsw-alias-border-l2);border-radius:7px;color:var(--dsw-alias-label-tertiary);font-size:12px;margin:12px 0 0;padding:16px;text-align:center}
4209
+ .dsh-codex-timestamp{color:var(--dsw-alias-label-tertiary);font-size:11px;margin:10px 0 0;text-align:right}
4210
+ .dsh-codex-skeleton{display:grid;gap:9px;padding-top:10px}
4211
+ .dsh-codex-skeleton span{animation:dsh-codex-pulse 1.4s ease-in-out infinite;background:var(--dsw-alias-bg-layer-2);border-radius:5px;height:42px}
4212
+ .dsh-codex-skeleton span:nth-child(2){animation-delay:.12s}.dsh-codex-skeleton span:nth-child(3){animation-delay:.24s}
4213
+ .dsh-codex-sr{height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;clip:rect(0 0 0 0);white-space:nowrap}
4214
+ .dsh-codex-composer-quota{align-items:center;background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:999px;corner-shape:round;color:var(--dsw-alias-label-secondary);display:inline-flex;font-size:11px;gap:6px;height:28px;line-height:1;max-width:160px;padding:0 9px;white-space:nowrap}
4215
+ .dsh-codex-composer-quota strong{color:var(--dsw-alias-label-primary);font-size:11px;font-weight:650}
4216
+ .dsh-codex-composer-quota[data-level=warning] strong{color:var(--dsw-alias-label-warning,#d58a24)}
4217
+ .dsh-codex-composer-quota[data-level=danger] strong{color:var(--dsw-alias-label-danger,#d94b4b)}
4218
+ .dsh-codex-image-tool{border:0.5px solid var(--dsw-alias-border-l2);border-radius:8px;display:flex;flex-direction:column;gap:8px;margin:4px 0 4px 4px;max-width:520px;padding:10px 12px}
4219
+ .dsh-codex-image-tool-head{align-items:center;display:flex;font-size:13px;gap:8px;min-width:0}
4220
+ .dsh-codex-image-dot{background:var(--dsw-alias-button-info-fill,#397ee8);border-radius:50%;corner-shape:round;display:inline-block;flex:none;height:8px;width:8px}
4221
+ .dsh-codex-image-tool[data-state=error] .dsh-codex-image-dot{background:var(--dsw-alias-label-danger,#d94b4b)}
4222
+ .dsh-codex-image-title{font-weight:600}
4223
+ .dsh-codex-image-summary{color:var(--dsw-alias-label-tertiary);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
4224
+ .dsh-codex-image-grid{display:flex;flex-wrap:wrap;gap:8px}
4225
+ .dsh-codex-image-preview{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:8px;display:block;max-height:260px;max-width:min(100%,360px);object-fit:contain}
4226
+ .dsh-codex-image-loading,.dsh-codex-image-failed{border:0.5px dashed var(--dsw-alias-border-l2);border-radius:8px;color:var(--dsw-alias-label-tertiary);display:inline-flex;font-size:12px;line-height:1.4;padding:18px 20px}
4227
+ .dsh-codex-image-failed{color:var(--dsw-alias-label-danger,#d94b4b)}
4228
+ .dsh-hub-tabs{align-self:flex-start;max-width:100%}
4229
+ @keyframes dsh-codex-pulse{0%,100%{opacity:.45}50%{opacity:.85}}
4230
+ @media(max-width:560px){.dsh-codex-row,.dsh-codex-pref-row,.dsh-codex-quota-fact,.dsh-codex-reset-credits{align-items:flex-start;flex-direction:column;gap:3px}.dsh-codex-value,.dsh-codex-quota-fact strong{text-align:left}.dsh-codex-actions{justify-content:flex-start}.dsh-codex-grouphead{align-items:flex-start;flex-direction:column;gap:0;padding:12px 0}.dsh-codex-meter-meta{align-items:flex-start;flex-direction:column;gap:2px}.dsh-codex-errorbar{align-items:flex-start;flex-direction:column}.dsh-codex-segments{width:100%}.dsh-codex-select{max-width:none;width:100%}.dsh-codex-context-row{align-items:flex-start;flex-direction:column;gap:5px}.dsh-codex-segments label{flex:1}.dsh-codex-segments span{text-align:center}.dsh-codex-image-tool{max-width:100%;margin-left:0}}
4231
+ @media(prefers-reduced-motion:reduce){.dsh-codex-meter>span{transition:none}.dsh-codex-skeleton span{animation:none}}
4232
+ `;
4233
+ function installStyles() {
4234
+ if (document.querySelector(`style[data-plugin-css="${STYLE_ID$2}"]`) !== null) return () => void 0;
4235
+ const element = document.createElement("style");
4236
+ element.dataset.plugin = "@eddyskywalker/dsh-chatgpt-subscription";
4237
+ element.dataset.pluginCss = STYLE_ID$2;
4238
+ element.textContent = CSS;
4239
+ document.head.appendChild(element);
4240
+ return () => element.remove();
4241
+ }
4242
+ //#endregion
4243
+ //#region src/client/antigravity/styles.ts
4244
+ const STYLE_ID$1 = "dsh-antigravity-settings-style";
4245
+ function installAntigravityStyles() {
4246
+ if (typeof document === "undefined" || document.getElementById("dsh-antigravity-settings-style")) return;
4247
+ const style = document.createElement("style");
4248
+ style.id = STYLE_ID$1;
4249
+ style.textContent = `
4250
+ .dsha-page{box-sizing:border-box;color:var(--dsw-alias-label-primary);display:flex;flex-direction:column;gap:20px;max-width:780px;min-width:0;padding:2px 0 30px}
4251
+ .dsha-page *{box-sizing:border-box}
4252
+ .dsha-group{border-top:0.5px solid var(--dsw-alias-border-l2);min-width:0}
4253
+ .dsha-group:first-of-type{border-top:0}
4254
+ .dsha-grouphead{align-items:center;display:flex;gap:12px;justify-content:space-between;min-height:48px}
4255
+ .dsha-grouphead h3{font-size:14px;font-weight:650;margin:0;color:var(--dsw-alias-label-primary)}
4256
+ .dsha-row{align-items:center;border-bottom:0.5px solid var(--dsw-alias-border-l2);display:flex;gap:20px;justify-content:space-between;min-height:44px;padding:8px 0}
4257
+ .dsha-label{color:var(--dsw-alias-label-secondary);font-size:13px;flex:0 0 auto}
4258
+ .dsha-value{font-size:13px;min-width:0;overflow-wrap:anywhere;text-align:right;color:var(--dsw-alias-label-primary)}
4259
+ .dsha-actions{display:flex;flex-wrap:wrap;gap:8px;justify-content:flex-end;padding-top:12px}
4260
+ .dsha-btn{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:999px;corner-shape:round;color:var(--dsw-alias-label-primary);cursor:pointer;font:inherit;font-size:13px;line-height:1;padding:8px 13px;white-space:nowrap}
4261
+ .dsha-btn:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}
4262
+ .dsha-btn:disabled{cursor:default;opacity:.5}
4263
+ .dsha-btn-primary{background:var(--dsw-alias-button-info-fill,#397ee8);border-color:transparent;color:var(--dsw-alias-button-info-label,#fff)}
4264
+ .dsha-btn-primary:hover:not(:disabled){opacity:.9}
4265
+ .dsha-notice{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:7px;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.55;margin:12px 0 0;padding:10px 12px}
4266
+ .dsha-muted{color:var(--dsw-alias-label-secondary);font-size:13px;line-height:1.55;margin:0}
4267
+ .dsha-models-hint{padding-top:10px}
4268
+ .dsha-models{display:flex;flex-wrap:wrap;gap:7px;padding-top:8px}
4269
+ .dsha-models label{cursor:pointer;display:block;position:relative}
4270
+ .dsha-models input{position:absolute;opacity:0;pointer-events:none}
4271
+ .dsha-models span{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:6px;color:var(--dsw-alias-label-secondary);display:block;font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:11px;padding:5px 8px;transition:all .15s ease}
4272
+ .dsha-models input:checked+span{background:color-mix(in srgb,var(--dsw-alias-button-info-fill,#397ee8) 14%,var(--dsw-alias-bg-layer-2));border-color:color-mix(in srgb,var(--dsw-alias-button-info-fill,#397ee8) 55%,var(--dsw-alias-border-l2));color:var(--dsw-alias-label-primary);font-weight:600}
4273
+ .dsha-models input:disabled+span{cursor:default;opacity:.5}
4274
+ .dsha-pref-row{align-items:center;border-bottom:0.5px solid var(--dsw-alias-border-l2);display:flex;gap:16px;justify-content:space-between;min-height:58px;padding:10px 0}
4275
+ .dsha-pref-row strong{display:block;font-size:13px;font-weight:600;line-height:1.35;color:var(--dsw-alias-label-primary)}
4276
+ .dsha-select{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:7px;color:var(--dsw-alias-label-primary);font:inherit;font-size:12px;padding:7px 9px;max-width:220px;min-width:160px}
4277
+ .dsha-context-settings{border-bottom:0.5px solid var(--dsw-alias-border-l2);display:grid;gap:10px;padding:12px 0}
4278
+ .dsha-context-settings>div>strong{font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary)}
4279
+ .dsha-context-row{align-items:center;display:flex;font-size:12px;gap:14px;justify-content:space-between}
4280
+ .dsha-capacity-control{align-items:center;display:flex;gap:7px}
4281
+ .dsha-capacity-control input{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:7px;color:var(--dsw-alias-label-primary);font:inherit;font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:12px;padding:7px 9px;text-align:right;width:90px}
4282
+ .dsha-capacity-control small{color:var(--dsw-alias-label-tertiary);font-size:11px;width:42px}
4283
+ .dsha-context-save{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:7px;color:var(--dsw-alias-label-primary);cursor:pointer;font:inherit;font-size:12px;line-height:1;padding:8px 10px;white-space:nowrap}
4284
+ .dsha-context-save:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}
4285
+ .dsha-context-save:disabled{cursor:default;opacity:.45}
4286
+ .dsha-quota-card{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:8px;margin-top:12px;padding:12px}
4287
+ .dsha-quota-title{align-items:center;display:flex;font-size:13px;gap:8px;justify-content:space-between;color:var(--dsw-alias-label-primary);font-weight:650}
4288
+ .dsha-quota-title span{color:var(--dsw-alias-label-tertiary);font-size:11px;text-transform:uppercase}
4289
+ .dsha-meter-wrap{margin-top:13px}
4290
+ .dsha-meter-label,.dsha-meter-meta{display:flex;gap:10px;justify-content:space-between}
4291
+ .dsha-meter-label{font-size:12px;margin-bottom:6px;color:var(--dsw-alias-label-primary)}
4292
+ .dsha-meter-meta{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:1.45;margin-top:6px}
4293
+ .dsha-meter{background:var(--dsw-alias-bg-layer-1,rgba(127,127,127,.15));border-radius:999px;corner-shape:round;height:7px;overflow:hidden;width:100%}
4294
+ .dsha-meter>span{background:var(--dsw-alias-button-info-fill,#397ee8);border-radius:inherit;display:block;height:100%;max-width:100%;min-width:0;transition:width .25s ease}
4295
+ .dsha-meter-green>span{background:var(--dsw-alias-label-success,#10b981)}
4296
+ .dsha-meter-cyan>span{background:#06b6d4}
4297
+ .dsha-timestamp{color:var(--dsw-alias-label-tertiary);font-size:11px;margin:10px 0 0;text-align:right}
4298
+ .dsha-mini-btn{border:0;background:transparent;color:var(--dsw-alias-label-link,#397ee8);font-size:12px;line-height:18px;cursor:pointer;padding:0;margin-left:8px}
4299
+ .dsha-mini-btn:hover{text-decoration:underline}
4300
+ .dsha-empty{border:0.5px dashed var(--dsw-alias-border-l2);border-radius:7px;color:var(--dsw-alias-label-tertiary);font-size:12px;margin:12px 0 0;padding:16px;text-align:center}
4301
+ .dsha-error{color:var(--dsw-alias-label-danger,#d94b4b);font-size:12px;margin:8px 0 0}
4302
+ .dsha-composer-quota{align-items:center;background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:999px;corner-shape:round;color:var(--dsw-alias-label-secondary);cursor:pointer;display:inline-flex;font-size:11px;gap:6px;height:28px;line-height:1;max-width:160px;padding:0 9px;white-space:nowrap;user-select:none;transition:all .15s ease}
4303
+ .dsha-composer-quota:hover{background:var(--dsw-alias-interactive-bg-hover);border-color:var(--dsw-alias-border-l1,var(--dsw-alias-border-l2))}
4304
+ .dsha-composer-quota strong{color:var(--dsw-alias-label-primary);font-size:11px;font-weight:650}
4305
+ .dsha-composer-quota[data-level=warning] strong{color:var(--dsw-alias-label-warning,#d58a24)}
4306
+ .dsha-composer-quota[data-level=danger] strong{color:var(--dsw-alias-label-danger,#d94b4b)}
4307
+ `;
4308
+ document.head.append(style);
4309
+ }
4310
+ //#endregion
4311
+ //#region src/client/antigravity/AntigravityComposerQuota.tsx
4312
+ const API$2 = "/antigravity/api";
4313
+ async function fetchApi$2(path, options) {
4314
+ const res = await fetch(`${API$2}${path}`, {
4315
+ ...options,
4316
+ headers: {
4317
+ "Content-Type": "application/json",
4318
+ ...options?.headers
4319
+ }
4320
+ });
4321
+ const json = await res.json();
4322
+ if (!res.ok || !json.ok) throw new Error(json.error || `HTTP ${res.status}`);
4323
+ return json.value;
4324
+ }
4325
+ function selectQuotaBucketForModel(groups, modelId) {
4326
+ if (!groups || groups.length === 0) return null;
4327
+ const isClaudeOrGpt = modelId ? /claude|gpt/i.test(modelId) : false;
4328
+ const targetGroup = groups.find((g) => {
2368
4329
  const match = /claude|gpt|3p/i.test(g.displayName);
2369
4330
  return isClaudeOrGpt ? match : !match;
2370
4331
  }) || groups[0];
@@ -2391,7 +4352,7 @@ window.__ModuleLoader__.load({
2391
4352
  }
2392
4353
  }
2393
4354
  function AntigravityComposerQuota({ directory, loadModelDirectory }) {
2394
- const modelState = useStore(directory);
4355
+ const modelState = useStore$2(directory);
2395
4356
  const [status, setStatus] = (0, react.useState)(null);
2396
4357
  const [loading, setLoading] = (0, react.useState)(false);
2397
4358
  const mountedRef = (0, react.useRef)(false);
@@ -2410,7 +4371,7 @@ window.__ModuleLoader__.load({
2410
4371
  if (!mountedRef.current) return;
2411
4372
  setLoading(true);
2412
4373
  try {
2413
- const data = refresh ? await fetchApi("/quota", { method: "POST" }) : await fetchApi("/status");
4374
+ const data = refresh ? await fetchApi$2("/quota", { method: "POST" }) : await fetchApi$2("/status");
2414
4375
  if (mountedRef.current) setStatus(data);
2415
4376
  } catch {} finally {
2416
4377
  if (mountedRef.current) setLoading(false);
@@ -2448,10 +4409,330 @@ window.__ModuleLoader__.load({
2448
4409
  })]
2449
4410
  });
2450
4411
  }
4412
+ function useStore$2(store) {
4413
+ return (0, react.useSyncExternalStore)((listener) => store.subscribe(listener), () => store.getSnapshot(), () => store.getSnapshot());
4414
+ }
4415
+ //#endregion
4416
+ //#region src/client/command-code/CommandCodeComposerQuota.tsx
4417
+ const API$1 = "/command-code/api";
4418
+ async function fetchApi$1(path, options) {
4419
+ const res = await fetch(`${API$1}${path}`, {
4420
+ ...options,
4421
+ headers: {
4422
+ "Content-Type": "application/json",
4423
+ ...options?.headers
4424
+ }
4425
+ });
4426
+ const json = await res.json();
4427
+ if (!res.ok || !json.ok) throw new Error(json.error || `HTTP ${res.status}`);
4428
+ return json.value;
4429
+ }
4430
+ /**
4431
+ * Pick the most meaningful number for the badge.
4432
+ *
4433
+ * A bounded usage window is the number a user actually spends down, so it wins
4434
+ * over a credit balance; the balance shows only when no window is reported.
4435
+ */
4436
+ function selectBadgeFacts$1(status) {
4437
+ const quota = status?.quota;
4438
+ if (quota === null || quota === void 0) return {
4439
+ text: "—",
4440
+ tooltip: "Command Code quota unavailable — click to refresh",
4441
+ level: "normal"
4442
+ };
4443
+ const window = [...quota.windows ?? []].sort((a, b) => (a.windowDurationMins ?? Infinity) - (b.windowDurationMins ?? Infinity))[0];
4444
+ if (window !== void 0) {
4445
+ const remaining = Math.max(0, 100 - window.usedPercent);
4446
+ return {
4447
+ text: `${remaining}%`,
4448
+ tooltip: `[Command Code] ${window.label}: ${remaining}% left`,
4449
+ level: remaining <= 5 ? "danger" : remaining <= 20 ? "warning" : "normal"
4450
+ };
4451
+ }
4452
+ const meter = (quota.meters ?? []).find((candidate) => candidate.remainingFraction !== null);
4453
+ if (meter !== void 0 && meter.remainingFraction !== null) {
4454
+ const remaining = Math.round(meter.remainingFraction * 100);
4455
+ return {
4456
+ text: `${remaining}%`,
4457
+ tooltip: `[Command Code] ${meter.label}: ${remaining}% left`,
4458
+ level: remaining <= 5 ? "danger" : remaining <= 20 ? "warning" : "normal"
4459
+ };
4460
+ }
4461
+ if (quota.unlimited) return {
4462
+ text: "∞",
4463
+ tooltip: "[Command Code] Unlimited plan allowance",
4464
+ level: "normal"
4465
+ };
4466
+ if (quota.creditBalance !== null) return {
4467
+ text: quota.creditBalance,
4468
+ tooltip: `[Command Code] Remaining credits: ${quota.creditBalance}`,
4469
+ level: "normal"
4470
+ };
4471
+ return {
4472
+ text: "—",
4473
+ tooltip: "Command Code quota unavailable — click to refresh",
4474
+ level: "normal"
4475
+ };
4476
+ }
4477
+ function CommandCodeComposerQuota({ directory, loadModelDirectory }) {
4478
+ const modelState = useStore$1(directory);
4479
+ const [status, setStatus] = (0, react.useState)(null);
4480
+ const [loading, setLoading] = (0, react.useState)(false);
4481
+ const mountedRef = (0, react.useRef)(false);
4482
+ const isCommandCode = modelState.current?.provider === "command-code";
4483
+ (0, react.useEffect)(() => {
4484
+ loadModelDirectory();
4485
+ }, [loadModelDirectory]);
4486
+ (0, react.useEffect)(() => {
4487
+ mountedRef.current = true;
4488
+ return () => {
4489
+ mountedRef.current = false;
4490
+ };
4491
+ }, []);
4492
+ const fetchStatus = (0, react.useCallback)(async (refresh = false) => {
4493
+ if (!mountedRef.current) return;
4494
+ setLoading(true);
4495
+ try {
4496
+ const data = refresh ? await fetchApi$1("/quota", { method: "POST" }) : await fetchApi$1("/status");
4497
+ if (mountedRef.current) setStatus(data);
4498
+ } catch {} finally {
4499
+ if (mountedRef.current) setLoading(false);
4500
+ }
4501
+ }, []);
4502
+ (0, react.useEffect)(() => {
4503
+ if (!isCommandCode) return;
4504
+ fetchStatus(false);
4505
+ const timer = window.setInterval(() => {
4506
+ if (document.visibilityState === "visible") fetchStatus(false);
4507
+ }, 6e4);
4508
+ return () => {
4509
+ window.clearInterval(timer);
4510
+ };
4511
+ }, [fetchStatus, isCommandCode]);
4512
+ const facts = (0, react.useMemo)(() => selectBadgeFacts$1(status), [status]);
4513
+ if (!isCommandCode || !status?.authenticated || facts === null) return null;
4514
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4515
+ className: "dsha-composer-quota",
4516
+ "data-level": facts.level,
4517
+ title: facts.tooltip,
4518
+ "aria-label": facts.tooltip,
4519
+ onClick: () => void fetchStatus(true),
4520
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4521
+ className: "dsha-composer-quota-label",
4522
+ children: "额度"
4523
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", {
4524
+ className: "dsha-composer-quota-val",
4525
+ children: loading ? "…" : facts.text
4526
+ })]
4527
+ });
4528
+ }
4529
+ function useStore$1(store) {
4530
+ return (0, react.useSyncExternalStore)((listener) => store.subscribe(listener), () => store.getSnapshot(), () => store.getSnapshot());
4531
+ }
4532
+ //#endregion
4533
+ //#region src/client/kimi-code/KimiCodeComposerQuota.tsx
4534
+ const API = "/kimi-code/api";
4535
+ async function fetchApi(path, options) {
4536
+ const res = await fetch(`${API}${path}`, {
4537
+ ...options,
4538
+ headers: {
4539
+ "Content-Type": "application/json",
4540
+ ...options?.headers
4541
+ }
4542
+ });
4543
+ const json = await res.json();
4544
+ if (!res.ok || !json.ok) throw new Error(json.error || `HTTP ${res.status}`);
4545
+ return json.value;
4546
+ }
4547
+ /**
4548
+ * Pick the most meaningful number for the badge.
4549
+ *
4550
+ * The shortest window is the one a user spends down first and the one that
4551
+ * blocks work soonest, so it wins over the longer pools; the 7-day allowance
4552
+ * shows only when no 5-hour window is reported.
4553
+ */
4554
+ function selectBadgeFacts(status) {
4555
+ const quota = status?.quota;
4556
+ if (quota === null || quota === void 0) return {
4557
+ text: "—",
4558
+ tooltip: "[Kimi Code] quota unavailable — click to refresh",
4559
+ level: "normal"
4560
+ };
4561
+ const window = [...quota.windows ?? []].filter((candidate) => candidate.windowDurationMins !== null).sort((a, b) => (a.windowDurationMins ?? Infinity) - (b.windowDurationMins ?? Infinity))[0] ?? quota.windows[0];
4562
+ if (window !== void 0) {
4563
+ const remaining = Math.max(0, 100 - window.usedPercent);
4564
+ return {
4565
+ text: `${remaining}%`,
4566
+ tooltip: `[Kimi Code] ${window.label}: ${remaining}% left`,
4567
+ level: remaining <= 5 ? "danger" : remaining <= 20 ? "warning" : "normal"
4568
+ };
4569
+ }
4570
+ const wallet = quota.extraUsage;
4571
+ if (wallet !== null && wallet !== void 0 && wallet.balanceCents !== null) {
4572
+ const amount = (wallet.balanceCents / 100).toFixed(2);
4573
+ return {
4574
+ text: amount,
4575
+ tooltip: `[Kimi Code] booster wallet: ${amount} ${wallet.currency ?? ""}`.trim(),
4576
+ level: "normal"
4577
+ };
4578
+ }
4579
+ return {
4580
+ text: "—",
4581
+ tooltip: "[Kimi Code] quota unavailable — click to refresh",
4582
+ level: "normal"
4583
+ };
4584
+ }
4585
+ function KimiCodeComposerQuota({ directory, loadModelDirectory }) {
4586
+ const modelState = useStore(directory);
4587
+ const [status, setStatus] = (0, react.useState)(null);
4588
+ const [loading, setLoading] = (0, react.useState)(false);
4589
+ const mountedRef = (0, react.useRef)(false);
4590
+ const isKimiCode = modelState.current?.provider === "kimi-code";
4591
+ (0, react.useEffect)(() => {
4592
+ loadModelDirectory();
4593
+ }, [loadModelDirectory]);
4594
+ (0, react.useEffect)(() => {
4595
+ mountedRef.current = true;
4596
+ return () => {
4597
+ mountedRef.current = false;
4598
+ };
4599
+ }, []);
4600
+ const fetchStatus = (0, react.useCallback)(async (refresh = false) => {
4601
+ if (!mountedRef.current) return;
4602
+ setLoading(true);
4603
+ try {
4604
+ const data = refresh ? await fetchApi("/quota", { method: "POST" }) : await fetchApi("/status");
4605
+ if (mountedRef.current) setStatus(data);
4606
+ } catch {} finally {
4607
+ if (mountedRef.current) setLoading(false);
4608
+ }
4609
+ }, []);
4610
+ (0, react.useEffect)(() => {
4611
+ if (!isKimiCode) return;
4612
+ fetchStatus(false);
4613
+ const timer = window.setInterval(() => {
4614
+ if (document.visibilityState === "visible") fetchStatus(false);
4615
+ }, 6e4);
4616
+ return () => {
4617
+ window.clearInterval(timer);
4618
+ };
4619
+ }, [fetchStatus, isKimiCode]);
4620
+ const facts = (0, react.useMemo)(() => selectBadgeFacts(status), [status]);
4621
+ if (!isKimiCode || !status?.authenticated || facts === null) return null;
4622
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4623
+ className: "dsha-composer-quota",
4624
+ "data-level": facts.level,
4625
+ title: facts.tooltip,
4626
+ "aria-label": facts.tooltip,
4627
+ onClick: () => void fetchStatus(true),
4628
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4629
+ className: "dsha-composer-quota-label",
4630
+ children: "额度"
4631
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", {
4632
+ className: "dsha-composer-quota-val",
4633
+ children: loading ? "…" : facts.text
4634
+ })]
4635
+ });
4636
+ }
2451
4637
  function useStore(store) {
2452
4638
  return (0, react.useSyncExternalStore)((listener) => store.subscribe(listener), () => store.getSnapshot(), () => store.getSnapshot());
2453
4639
  }
2454
4640
  //#endregion
4641
+ //#region src/client/kimi-code/styles.ts
4642
+ const STYLE_ID = "dsh-kimi-code-settings-style";
4643
+ /**
4644
+ * Node the base control styles come from.
4645
+ *
4646
+ * The Kimi Code card renders the same controls as the sibling routes, so it
4647
+ * reuses one extracted stylesheet verbatim rather than re-declaring a parallel
4648
+ * design system that would drift from it.
4649
+ */
4650
+ const BASE = `
4651
+ .dsha-page{box-sizing:border-box;color:var(--dsw-alias-label-primary);display:flex;flex-direction:column;gap:20px;max-width:780px;min-width:0;padding:2px 0 30px}
4652
+ .dsha-page *{box-sizing:border-box}
4653
+ .dsha-group{border-top:0.5px solid var(--dsw-alias-border-l2);min-width:0}
4654
+ .dsha-group:first-of-type{border-top:0}
4655
+ .dsha-grouphead{align-items:center;display:flex;gap:12px;justify-content:space-between;min-height:48px}
4656
+ .dsha-grouphead h3{font-size:14px;font-weight:650;margin:0;color:var(--dsw-alias-label-primary)}
4657
+ .dsha-row{align-items:center;border-bottom:0.5px solid var(--dsw-alias-border-l2);display:flex;gap:20px;justify-content:space-between;min-height:44px;padding:8px 0}
4658
+ .dsha-label{color:var(--dsw-alias-label-secondary);font-size:13px;flex:0 0 auto}
4659
+ .dsha-value{font-size:13px;min-width:0;overflow-wrap:anywhere;text-align:right;color:var(--dsw-alias-label-primary)}
4660
+ .dsha-actions{display:flex;flex-wrap:wrap;gap:8px;justify-content:flex-end;padding-top:12px}
4661
+ .dsha-btn{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:999px;corner-shape:round;color:var(--dsw-alias-label-primary);cursor:pointer;font:inherit;font-size:13px;line-height:1;padding:8px 13px;white-space:nowrap}
4662
+ .dsha-btn:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}
4663
+ .dsha-btn:disabled{cursor:default;opacity:.5}
4664
+ .dsha-btn-primary{background:var(--dsw-alias-button-info-fill,#397ee8);border-color:transparent;color:var(--dsw-alias-button-info-label,#fff)}
4665
+ .dsha-btn-primary:hover:not(:disabled){opacity:.9}
4666
+ .dsha-notice{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:7px;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.55;margin:12px 0 0;padding:10px 12px}
4667
+ .dsha-muted{color:var(--dsw-alias-label-secondary);font-size:13px;line-height:1.55;margin:0}
4668
+ .dsha-models-hint{padding-top:10px}
4669
+ .dsha-models{display:flex;flex-wrap:wrap;gap:7px;padding-top:8px}
4670
+ .dsha-models label{cursor:pointer;display:block;position:relative}
4671
+ .dsha-models input{position:absolute;opacity:0;pointer-events:none}
4672
+ .dsha-models span{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:6px;color:var(--dsw-alias-label-secondary);display:block;font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:11px;padding:5px 8px;transition:all .15s ease}
4673
+ .dsha-models input:checked+span{background:color-mix(in srgb,var(--dsw-alias-button-info-fill,#397ee8) 14%,var(--dsw-alias-bg-layer-2));border-color:color-mix(in srgb,var(--dsw-alias-button-info-fill,#397ee8) 55%,var(--dsw-alias-border-l2));color:var(--dsw-alias-label-primary);font-weight:600}
4674
+ .dsha-models input:disabled+span{cursor:default;opacity:.5}
4675
+ .dsha-pref-row{align-items:center;border-bottom:0.5px solid var(--dsw-alias-border-l2);display:flex;gap:16px;justify-content:space-between;min-height:58px;padding:10px 0}
4676
+ .dsha-pref-row strong{display:block;font-size:13px;font-weight:600;line-height:1.35;color:var(--dsw-alias-label-primary)}
4677
+ .dsha-select{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:7px;color:var(--dsw-alias-label-primary);font:inherit;font-size:12px;padding:7px 9px;max-width:220px;min-width:160px}
4678
+ .dsha-context-settings{border-bottom:0.5px solid var(--dsw-alias-border-l2);display:grid;gap:10px;padding:12px 0}
4679
+ .dsha-context-settings>div>strong{font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary)}
4680
+ .dsha-context-row{align-items:center;display:flex;font-size:12px;gap:14px;justify-content:space-between}
4681
+ .dsha-capacity-control{align-items:center;display:flex;gap:7px}
4682
+ .dsha-capacity-control input{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:7px;color:var(--dsw-alias-label-primary);font:inherit;font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:12px;padding:7px 9px;text-align:right;width:90px}
4683
+ .dsha-capacity-control small{color:var(--dsw-alias-label-tertiary);font-size:11px;width:42px}
4684
+ .dsha-context-save{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:7px;color:var(--dsw-alias-label-primary);cursor:pointer;font:inherit;font-size:12px;line-height:1;padding:8px 10px;white-space:nowrap}
4685
+ .dsha-context-save:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}
4686
+ .dsha-context-save:disabled{cursor:default;opacity:.45}
4687
+ .dsha-quota-card{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:8px;margin-top:12px;padding:12px}
4688
+ .dsha-quota-title{align-items:center;display:flex;font-size:13px;gap:8px;justify-content:space-between;color:var(--dsw-alias-label-primary);font-weight:650}
4689
+ .dsha-quota-title span{color:var(--dsw-alias-label-tertiary);font-size:11px;text-transform:uppercase}
4690
+ .dsha-meter-wrap{margin-top:13px}
4691
+ .dsha-meter-label,.dsha-meter-meta{display:flex;gap:10px;justify-content:space-between}
4692
+ .dsha-meter-label{font-size:12px;margin-bottom:6px;color:var(--dsw-alias-label-primary)}
4693
+ .dsha-meter-meta{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:1.45;margin-top:6px}
4694
+ .dsha-meter{background:var(--dsw-alias-bg-layer-1,rgba(127,127,127,.15));border-radius:999px;corner-shape:round;height:7px;overflow:hidden;width:100%}
4695
+ .dsha-meter>span{background:var(--dsw-alias-button-info-fill,#397ee8);border-radius:inherit;display:block;height:100%;max-width:100%;min-width:0;transition:width .25s ease}
4696
+ .dsha-meter-green>span{background:var(--dsw-alias-label-success,#10b981)}
4697
+ .dsha-meter-cyan>span{background:#06b6d4}
4698
+ .dsha-timestamp{color:var(--dsw-alias-label-tertiary);font-size:11px;margin:10px 0 0;text-align:right}
4699
+ .dsha-mini-btn{border:0;background:transparent;color:var(--dsw-alias-label-link,#397ee8);font-size:12px;line-height:18px;cursor:pointer;padding:0;margin-left:8px}
4700
+ .dsha-mini-btn:hover{text-decoration:underline}
4701
+ .dsha-empty{border:0.5px dashed var(--dsw-alias-border-l2);border-radius:7px;color:var(--dsw-alias-label-tertiary);font-size:12px;margin:12px 0 0;padding:16px;text-align:center}
4702
+ .dsha-error{color:var(--dsw-alias-label-danger,#d94b4b);font-size:12px;margin:8px 0 0}
4703
+ .dsha-composer-quota{align-items:center;background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:999px;corner-shape:round;color:var(--dsw-alias-label-secondary);cursor:pointer;display:inline-flex;font-size:11px;gap:6px;height:28px;line-height:1;max-width:160px;padding:0 9px;white-space:nowrap;user-select:none;transition:all .15s ease}
4704
+ .dsha-composer-quota:hover{background:var(--dsw-alias-interactive-bg-hover);border-color:var(--dsw-alias-border-l1,var(--dsw-alias-border-l2))}
4705
+ .dsha-composer-quota strong{color:var(--dsw-alias-label-primary);font-size:11px;font-weight:650}
4706
+ .dsha-composer-quota[data-level=warning] strong{color:var(--dsw-alias-label-warning,#d58a24)}
4707
+ .dsha-composer-quota[data-level=danger] strong{color:var(--dsw-alias-label-danger,#d94b4b)}
4708
+
4709
+ .dsha-device-box{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:8px;display:flex;flex-direction:column;gap:10px;margin-top:12px;padding:12px}
4710
+ .dsha-code-row{align-items:center;display:flex;flex-wrap:wrap;gap:10px;justify-content:space-between}
4711
+ .dsha-code{background:var(--dsw-alias-bg-layer-1,rgba(127,127,127,.14));border:0.5px solid var(--dsw-alias-border-l2);border-radius:7px;color:var(--dsw-alias-label-primary);font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:16px;font-weight:650;letter-spacing:.14em;padding:8px 12px}
4712
+ .dsha-link{color:var(--dsw-alias-label-link,#397ee8);font-size:12px;overflow-wrap:anywhere;text-decoration:none}
4713
+ .dsha-link:hover{text-decoration:underline}
4714
+ .dsha-model-meta{color:var(--dsw-alias-label-tertiary);font-size:11px;margin-left:5px}
4715
+ .dsha-badge{background:var(--dsw-alias-bg-layer-1,rgba(127,127,127,.14));border:0.5px solid var(--dsw-alias-border-l2);border-radius:999px;color:var(--dsw-alias-label-tertiary);font-size:10px;padding:2px 6px}
4716
+ .dsha-cap-table{border-bottom:0.5px solid var(--dsw-alias-border-l2);padding:12px 0}
4717
+ .dsha-cap-table table{border-collapse:collapse;font-size:11px;width:100%}
4718
+ .dsha-cap-table th{color:var(--dsw-alias-label-tertiary);font-size:11px;font-weight:500;padding:0 10px 6px 0;text-align:left;white-space:nowrap}
4719
+ .dsha-cap-table th:last-child,.dsha-cap-table td:last-child{padding-right:0}
4720
+ .dsha-cap-table td{border-top:0.5px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);padding:8px 10px 8px 0;vertical-align:top}
4721
+ .dsha-cap-table td:first-child{color:var(--dsw-alias-label-primary);font-family:ui-monospace,SFMono-Regular,Consolas,monospace;white-space:nowrap}
4722
+ .dsha-cap-table td:nth-child(2),.dsha-cap-table td:nth-child(3){white-space:nowrap}
4723
+ .dsha-cap-on{color:var(--dsw-alias-label-primary);font-weight:500}
4724
+ .dsha-cap-off{color:var(--dsw-alias-label-tertiary)}
4725
+ .dsha-cap-notes{line-height:1.5;min-width:200px}
4726
+ `;
4727
+ /** Kimi-specific rules layered on top of the shared control styles. */
4728
+ function installKimiCodeStyles() {
4729
+ if (typeof document === "undefined" || document.getElementById("dsh-kimi-code-settings-style")) return;
4730
+ const style = document.createElement("style");
4731
+ style.id = STYLE_ID;
4732
+ style.textContent = BASE;
4733
+ document.head.appendChild(style);
4734
+ }
4735
+ //#endregion
2455
4736
  //#region src/client/mermaid/sanitize.ts
2456
4737
  const STRIP_ELEMENTS = /* @__PURE__ */ new Set([
2457
4738
  "foreignobject",
@@ -2789,39 +5070,41 @@ window.__ModuleLoader__.load({
2789
5070
  "conversation"
2790
5071
  ];
2791
5072
  function apply(ctx) {
2792
- ctx.effect(() => ctx.locale.register(NS, dictionaries$1), "dsh-chatgpt-subscription: dictionaries");
5073
+ ctx.effect(() => ctx.locale.register(NS, dictionaries), "dsh-chatgpt-subscription: dictionaries");
2793
5074
  ctx.effect(() => installStyles(), "dsh-chatgpt-subscription: styles");
2794
- ctx.effect(() => ctx.locale.register(NS_ANTIGRAVITY, dictionaries), "dsh-antigravity: dictionaries");
5075
+ ctx.effect(() => ctx.locale.register(NS_ANTIGRAVITY, dictionaries$3), "dsh-antigravity: dictionaries");
2795
5076
  ctx.effect(() => {
2796
5077
  installAntigravityStyles();
2797
5078
  return () => {};
2798
5079
  }, "dsh-antigravity: styles");
5080
+ ctx.effect(() => ctx.locale.register(NS_COMMAND_CODE, dictionaries$2), "dsh-command-code: dictionaries");
5081
+ ctx.effect(() => ctx.locale.register(NS_KIMI_CODE, dictionaries$1), "dsh-kimi-code: dictionaries");
5082
+ ctx.effect(() => {
5083
+ installKimiCodeStyles();
5084
+ return () => {};
5085
+ }, "dsh-kimi-code: styles");
2799
5086
  ctx.effect(() => setupMermaidObserver(), "dsh-mermaid: observer");
2800
5087
  const t = ctx.locale.bind(NS);
5088
+ const handleModelDirectoryReload = () => {
5089
+ try {
5090
+ const conv = ctx.conversation;
5091
+ const activeSessionId = conv?.activeSessionId || conv?.currentSessionId || conv?.activeId;
5092
+ if (activeSessionId && ctx.modelDirectories) ctx.modelDirectories.directoryFor(activeSessionId)?.load?.().catch?.(() => void 0);
5093
+ } catch {}
5094
+ };
5095
+ const ProviderHubSectionWrapper = (props) => {
5096
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProviderHubSection, {
5097
+ ...props,
5098
+ onModelChange: handleModelDirectoryReload
5099
+ });
5100
+ };
2801
5101
  ctx.slots.inject("settings.section", () => ctx.slots.register({
2802
5102
  name: "settings.section",
2803
- id: "codex-subscription",
5103
+ id: "subscription-hub",
2804
5104
  order: 45,
2805
- label: () => t("title"),
5105
+ label: () => t("hubTitle"),
2806
5106
  locale: NS
2807
- }, CodexSubscriptionSection));
2808
- const AntigravitySectionWrapper = () => {
2809
- const handleModelChange = () => {
2810
- try {
2811
- const conv = ctx.conversation;
2812
- const activeSessionId = conv?.activeSessionId || conv?.currentSessionId || conv?.activeId;
2813
- if (activeSessionId && ctx.modelDirectories) ctx.modelDirectories.directoryFor(activeSessionId)?.load?.().catch?.(() => void 0);
2814
- } catch {}
2815
- };
2816
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AntigravitySection, { onModelChange: handleModelChange });
2817
- };
2818
- ctx.slots.inject("settings.section", () => ctx.slots.register({
2819
- name: "settings.section",
2820
- id: "antigravity",
2821
- order: 46,
2822
- label: () => "Antigravity",
2823
- locale: NS_ANTIGRAVITY
2824
- }, AntigravitySectionWrapper));
5107
+ }, ProviderHubSectionWrapper));
2825
5108
  ctx.slots.inject("conversation.input.right", () => ctx.slots.register({
2826
5109
  name: "conversation.input.right",
2827
5110
  id: "codex-subscription-quota",
@@ -2853,6 +5136,36 @@ window.__ModuleLoader__.load({
2853
5136
  };
2854
5137
  }
2855
5138
  }, AntigravityComposerQuota));
5139
+ ctx.slots.inject("conversation.input.right", () => ctx.slots.register({
5140
+ name: "conversation.input.right",
5141
+ id: "command-code-quota",
5142
+ order: 37,
5143
+ locale: NS_COMMAND_CODE,
5144
+ inject: (sessionId) => {
5145
+ const directory = ctx.modelDirectories.directoryFor(sessionId);
5146
+ return {
5147
+ directory: directory.store,
5148
+ loadModelDirectory: () => {
5149
+ directory.load().catch(() => void 0);
5150
+ }
5151
+ };
5152
+ }
5153
+ }, CommandCodeComposerQuota));
5154
+ ctx.slots.inject("conversation.input.right", () => ctx.slots.register({
5155
+ name: "conversation.input.right",
5156
+ id: "kimi-code-quota",
5157
+ order: 38,
5158
+ locale: NS_KIMI_CODE,
5159
+ inject: (sessionId) => {
5160
+ const directory = ctx.modelDirectories.directoryFor(sessionId);
5161
+ return {
5162
+ directory: directory.store,
5163
+ loadModelDirectory: () => {
5164
+ directory.load().catch(() => void 0);
5165
+ }
5166
+ };
5167
+ }
5168
+ }, KimiCodeComposerQuota));
2856
5169
  ctx.slots.inject("tool.call.toolview", () => ctx.slots.register({
2857
5170
  name: "tool.call.toolview",
2858
5171
  key: CODEX_IMAGE_TOOL_NAME,