@eddyskywalker/dsh-chatgpt-subscription 0.4.0 → 0.6.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 (109) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/README.md +67 -1
  3. package/lib/client.js +2255 -742
  4. package/lib/client.js.map +1 -1
  5. package/lib/index.js +17139 -12058
  6. package/lib/types/client/CodexSubscriptionSection.d.ts +1 -1
  7. package/lib/types/client/CodexSubscriptionSection.d.ts.map +1 -1
  8. package/lib/types/client/ProviderHubSection.d.ts.map +1 -1
  9. package/lib/types/client/antigravity/AntigravitySection.d.ts.map +1 -1
  10. package/lib/types/client/antigravity/locales.d.ts +24 -192
  11. package/lib/types/client/antigravity/locales.d.ts.map +1 -1
  12. package/lib/types/client/api.d.ts +13 -1
  13. package/lib/types/client/api.d.ts.map +1 -1
  14. package/lib/types/client/command-code/CommandCodeSection.d.ts.map +1 -1
  15. package/lib/types/client/command-code/locales.d.ts +66 -201
  16. package/lib/types/client/command-code/locales.d.ts.map +1 -1
  17. package/lib/types/client/common/AccountPoolSection.d.ts +55 -0
  18. package/lib/types/client/common/AccountPoolSection.d.ts.map +1 -0
  19. package/lib/types/client/common/account-pool-labels.d.ts +43 -0
  20. package/lib/types/client/common/account-pool-labels.d.ts.map +1 -0
  21. package/lib/types/client/common/styles.d.ts +11 -0
  22. package/lib/types/client/common/styles.d.ts.map +1 -0
  23. package/lib/types/client/index.d.ts +1 -0
  24. package/lib/types/client/index.d.ts.map +1 -1
  25. package/lib/types/client/kimi-code/KimiCodeSection.d.ts.map +1 -1
  26. package/lib/types/client/kimi-code/locales.d.ts +63 -336
  27. package/lib/types/client/kimi-code/locales.d.ts.map +1 -1
  28. package/lib/types/client/locales.d.ts +41 -1
  29. package/lib/types/client/locales.d.ts.map +1 -1
  30. package/lib/types/client/workbuddy/WorkBuddyComposerQuota.d.ts +26 -0
  31. package/lib/types/client/workbuddy/WorkBuddyComposerQuota.d.ts.map +1 -0
  32. package/lib/types/client/workbuddy/WorkBuddySection.d.ts +41 -0
  33. package/lib/types/client/workbuddy/WorkBuddySection.d.ts.map +1 -0
  34. package/lib/types/client/workbuddy/locales.d.ts +563 -0
  35. package/lib/types/client/workbuddy/locales.d.ts.map +1 -0
  36. package/lib/types/client/workbuddy/styles.d.ts +11 -0
  37. package/lib/types/client/workbuddy/styles.d.ts.map +1 -0
  38. package/lib/types/host/antigravity/account-pool.d.ts +15 -5
  39. package/lib/types/host/antigravity/account-pool.d.ts.map +1 -1
  40. package/lib/types/host/antigravity/routes.d.ts.map +1 -1
  41. package/lib/types/host/codex-account-pool.d.ts +64 -0
  42. package/lib/types/host/codex-account-pool.d.ts.map +1 -0
  43. package/lib/types/host/command-code/account-pool.d.ts +60 -0
  44. package/lib/types/host/command-code/account-pool.d.ts.map +1 -0
  45. package/lib/types/host/command-code/adapter.d.ts +11 -1
  46. package/lib/types/host/command-code/adapter.d.ts.map +1 -1
  47. package/lib/types/host/command-code/client.d.ts +17 -1
  48. package/lib/types/host/command-code/client.d.ts.map +1 -1
  49. package/lib/types/host/command-code/oauth.d.ts +4 -1
  50. package/lib/types/host/command-code/oauth.d.ts.map +1 -1
  51. package/lib/types/host/command-code/routes.d.ts +11 -2
  52. package/lib/types/host/command-code/routes.d.ts.map +1 -1
  53. package/lib/types/host/common/account-pool.d.ts +197 -0
  54. package/lib/types/host/common/account-pool.d.ts.map +1 -0
  55. package/lib/types/host/common/home.d.ts +8 -0
  56. package/lib/types/host/common/home.d.ts.map +1 -0
  57. package/lib/types/host/kimi-code/account-pool.d.ts +48 -0
  58. package/lib/types/host/kimi-code/account-pool.d.ts.map +1 -0
  59. package/lib/types/host/kimi-code/adapter.d.ts +10 -1
  60. package/lib/types/host/kimi-code/adapter.d.ts.map +1 -1
  61. package/lib/types/host/kimi-code/client.d.ts +16 -0
  62. package/lib/types/host/kimi-code/client.d.ts.map +1 -1
  63. package/lib/types/host/kimi-code/oauth.d.ts +2 -0
  64. package/lib/types/host/kimi-code/oauth.d.ts.map +1 -1
  65. package/lib/types/host/kimi-code/routes.d.ts +10 -2
  66. package/lib/types/host/kimi-code/routes.d.ts.map +1 -1
  67. package/lib/types/host/oauth-service.d.ts +41 -2
  68. package/lib/types/host/oauth-service.d.ts.map +1 -1
  69. package/lib/types/host/preset-sync.d.ts +7 -1
  70. package/lib/types/host/preset-sync.d.ts.map +1 -1
  71. package/lib/types/host/responses-client.d.ts +15 -0
  72. package/lib/types/host/responses-client.d.ts.map +1 -1
  73. package/lib/types/host/routes.d.ts +2 -1
  74. package/lib/types/host/routes.d.ts.map +1 -1
  75. package/lib/types/host/usage-service.d.ts +15 -0
  76. package/lib/types/host/usage-service.d.ts.map +1 -1
  77. package/lib/types/host/workbuddy/account-pool.d.ts +106 -0
  78. package/lib/types/host/workbuddy/account-pool.d.ts.map +1 -0
  79. package/lib/types/host/workbuddy/adapter.d.ts +97 -0
  80. package/lib/types/host/workbuddy/adapter.d.ts.map +1 -0
  81. package/lib/types/host/workbuddy/client.d.ts +90 -0
  82. package/lib/types/host/workbuddy/client.d.ts.map +1 -0
  83. package/lib/types/host/workbuddy/mapper.d.ts +114 -0
  84. package/lib/types/host/workbuddy/mapper.d.ts.map +1 -0
  85. package/lib/types/host/workbuddy/model-catalog.d.ts +108 -0
  86. package/lib/types/host/workbuddy/model-catalog.d.ts.map +1 -0
  87. package/lib/types/host/workbuddy/oauth.d.ts +38 -0
  88. package/lib/types/host/workbuddy/oauth.d.ts.map +1 -0
  89. package/lib/types/host/workbuddy/routes.d.ts +33 -0
  90. package/lib/types/host/workbuddy/routes.d.ts.map +1 -0
  91. package/lib/types/host/workbuddy/token-store.d.ts +216 -0
  92. package/lib/types/host/workbuddy/token-store.d.ts.map +1 -0
  93. package/lib/types/host/workbuddy/types.d.ts +127 -0
  94. package/lib/types/host/workbuddy/types.d.ts.map +1 -0
  95. package/lib/types/index.d.ts +10 -0
  96. package/lib/types/index.d.ts.map +1 -1
  97. package/lib/types/shared/account-pool-contracts.d.ts +55 -0
  98. package/lib/types/shared/account-pool-contracts.d.ts.map +1 -0
  99. package/lib/types/shared/antigravity-contracts.d.ts +4 -10
  100. package/lib/types/shared/antigravity-contracts.d.ts.map +1 -1
  101. package/lib/types/shared/command-code-contracts.d.ts +14 -0
  102. package/lib/types/shared/command-code-contracts.d.ts.map +1 -1
  103. package/lib/types/shared/contracts.d.ts +6 -0
  104. package/lib/types/shared/contracts.d.ts.map +1 -1
  105. package/lib/types/shared/kimi-code-contracts.d.ts +14 -0
  106. package/lib/types/shared/kimi-code-contracts.d.ts.map +1 -1
  107. package/lib/types/shared/workbuddy-contracts.d.ts +204 -0
  108. package/lib/types/shared/workbuddy-contracts.d.ts.map +1 -0
  109. 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$3(directory);
59
+ const modelState = useStore$4(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$3(store) {
106
+ function useStore$4(store) {
107
107
  return (0, react.useSyncExternalStore)((listener) => store.subscribe(listener), () => store.getSnapshot(), () => store.getSnapshot());
108
108
  }
109
109
  function formatPercent$1(value) {
@@ -313,8 +313,21 @@ window.__ModuleLoader__.load({
313
313
  cancelLogin(loginId) {
314
314
  return post(`${ROUTE_PREFIX}/login/cancel`, { loginId });
315
315
  }
316
- logout() {
317
- return post(`${ROUTE_PREFIX}/logout`, {});
316
+ /** Sign out one pooled account; without an id, the active one. */
317
+ logout(accountId) {
318
+ return post(`${ROUTE_PREFIX}/logout`, accountId === void 0 ? {} : { accountId });
319
+ }
320
+ /**
321
+ * One account-pool action, returning the refreshed status.
322
+ *
323
+ * The host answers with the same envelope the status route uses, so the card
324
+ * never has to guess what the pool looks like after a change.
325
+ */
326
+ accountAction(action, body = {}) {
327
+ return post(`${ROUTE_PREFIX}/accounts`, {
328
+ action,
329
+ ...body
330
+ });
318
331
  }
319
332
  refresh() {
320
333
  return post(`${ROUTE_PREFIX}/token/refresh`, {});
@@ -360,6 +373,275 @@ window.__ModuleLoader__.load({
360
373
  }
361
374
  }
362
375
  //#endregion
376
+ //#region src/client/common/account-pool-labels.ts
377
+ /** Chinese labels; the wording the Antigravity card shipped with. */
378
+ const accountPoolZh = {
379
+ accountPool: "账号管理",
380
+ addAccount: "添加账号",
381
+ accountCount: "已登录 {count} 个账号",
382
+ primaryAccount: "主账号",
383
+ activeAccount: "当前使用",
384
+ setPrimary: "设为主账号",
385
+ deleteAccount: "删除",
386
+ cooling: "冷却中",
387
+ cooldownLeft: "剩 {minutes} 分",
388
+ clearCooldown: "重置冷却",
389
+ needsRelogin: "需重新登录",
390
+ relogin: "重新登录",
391
+ rotationStrategy: "调度策略",
392
+ strategySequential: "顺序耗尽(当前优先,遇限流自动切号)",
393
+ strategyRoundRobin: "轮询调度(按账号循环均匀分摊)",
394
+ strategySticky: "粘性会话(保持当前账号,遇限流才切换)",
395
+ noAccounts: "暂无账号,点击「添加账号」完成登录授权。",
396
+ storage: "凭据存储",
397
+ storageNotice: "令牌由 Host 保存于本地安全存储,不会进入浏览器。",
398
+ email: "邮箱",
399
+ expires: "令牌到期",
400
+ lastUsed: "上次调用",
401
+ accountId: "账号 ID"
402
+ };
403
+ /** English labels for the ChatGPT tab, which is the bilingual one. */
404
+ const accountPoolEn = {
405
+ accountPool: "Account Management",
406
+ addAccount: "Add Account",
407
+ accountCount: "{count} account(s) signed in",
408
+ primaryAccount: "Primary",
409
+ activeAccount: "In Use",
410
+ setPrimary: "Set as Primary",
411
+ deleteAccount: "Delete",
412
+ cooling: "In Cooldown",
413
+ cooldownLeft: "{minutes} min left",
414
+ clearCooldown: "Clear Cooldown",
415
+ needsRelogin: "Sign-in required",
416
+ relogin: "Sign in again",
417
+ rotationStrategy: "Scheduling Strategy",
418
+ strategySequential: "Sequential Drain (primary first, fail over on 429)",
419
+ strategyRoundRobin: "Round-Robin (evenly rotate across accounts)",
420
+ strategySticky: "Sticky Session (keep the current account until it is limited)",
421
+ noAccounts: "No accounts yet. Click \"Add Account\" to authorize.",
422
+ storage: "Credential storage",
423
+ storageNotice: "Credentials are stored locally by the Host and never sent to the browser.",
424
+ email: "Email",
425
+ expires: "Token expires",
426
+ lastUsed: "Last used",
427
+ accountId: "Account ID"
428
+ };
429
+ /** Replace `{name}` placeholders in one label. */
430
+ function formatPoolLabel(template, values) {
431
+ return template.replace(/\{(\w+)\}/g, (match, name) => name in values ? String(values[name]) : match);
432
+ }
433
+ //#endregion
434
+ //#region src/client/common/AccountPoolSection.tsx
435
+ /** Format one account timestamp the way every tab renders it. */
436
+ function formatAccountDate(ms) {
437
+ if (!ms || ms <= 0) return "—";
438
+ try {
439
+ return new Intl.DateTimeFormat(void 0, {
440
+ dateStyle: "medium",
441
+ timeStyle: "short"
442
+ }).format(ms);
443
+ } catch {
444
+ return "—";
445
+ }
446
+ }
447
+ /** Whole minutes left in a cooldown, never below one. */
448
+ function cooldownMinutesLeft(cooldownUntil, now = Date.now()) {
449
+ return Math.max(1, Math.ceil((cooldownUntil - now) / 6e4));
450
+ }
451
+ /** Whether one account currently carries a live 429 cooldown. */
452
+ function isCoolingDown(account, now = Date.now()) {
453
+ return typeof account.cooldownUntil === "number" && account.cooldownUntil > now;
454
+ }
455
+ /**
456
+ * The account-management group every provider tab renders.
457
+ *
458
+ * One component rather than four near-identical blocks: the account card, its
459
+ * badges, the rotation-strategy picker and the storage notice are identical
460
+ * across providers, and only the identity details differ.
461
+ */
462
+ function AccountPoolSection(props) {
463
+ const t = props.labels;
464
+ const accounts = props.accounts;
465
+ const now = Date.now();
466
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
467
+ className: "dsha-group",
468
+ children: [
469
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
470
+ className: "dsha-grouphead",
471
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("h3", { children: [t.accountPool, accounts.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
472
+ className: "dsha-muted",
473
+ style: {
474
+ fontWeight: 400,
475
+ marginLeft: 8
476
+ },
477
+ children: [
478
+ "(",
479
+ formatPoolLabel(t.accountCount, { count: accounts.length }),
480
+ ")"
481
+ ]
482
+ })] }), props.renderLoginActions !== void 0 ? props.renderLoginActions() : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
483
+ className: "dsha-btn dsha-btn-primary",
484
+ disabled: props.busy !== null,
485
+ onClick: props.onLogin,
486
+ children: props.loginBusyLabel || t.addAccount
487
+ })]
488
+ }),
489
+ accounts.length > 1 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
490
+ className: "dsha-pref-row",
491
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t.rotationStrategy }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
492
+ className: "dsha-muted",
493
+ style: {
494
+ fontSize: 12,
495
+ marginTop: 2
496
+ },
497
+ children: props.rotationStrategy === "round-robin" ? t.strategyRoundRobin : props.rotationStrategy === "sticky" ? t.strategySticky : t.strategySequential
498
+ })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
499
+ className: "dsha-select",
500
+ "aria-label": t.rotationStrategy,
501
+ value: props.rotationStrategy,
502
+ disabled: props.busy !== null,
503
+ onChange: (event) => props.onSetStrategy(event.target.value),
504
+ children: [
505
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
506
+ value: "sequential",
507
+ children: "顺序耗尽"
508
+ }),
509
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
510
+ value: "round-robin",
511
+ children: "轮询调度"
512
+ }),
513
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
514
+ value: "sticky",
515
+ children: "粘性会话"
516
+ })
517
+ ]
518
+ })]
519
+ }),
520
+ accounts.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
521
+ className: "dsha-empty",
522
+ children: t.noAccounts
523
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
524
+ className: "dsha-accounts-list",
525
+ children: accounts.map((account) => {
526
+ const isActive = account.id === props.activeAccountId;
527
+ const cooling = isCoolingDown(account, now);
528
+ const needsRelogin = account.authStatus !== void 0 && account.authStatus !== "ok";
529
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
530
+ className: `dsha-account-card ${isActive ? "active" : ""}`,
531
+ children: [
532
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
533
+ className: "dsha-account-header",
534
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
535
+ className: "dsha-account-identity",
536
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
537
+ className: "dsha-account-title",
538
+ children: account.alias || account.email || account.id
539
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
540
+ className: "dsha-badges",
541
+ children: [
542
+ account.isPrimary && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
543
+ className: "dsha-badge primary",
544
+ children: t.primaryAccount
545
+ }),
546
+ isActive && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
547
+ className: "dsha-badge active",
548
+ children: t.activeAccount
549
+ }),
550
+ cooling && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
551
+ className: "dsha-badge cooldown",
552
+ children: [
553
+ t.cooling,
554
+ "(",
555
+ formatPoolLabel(t.cooldownLeft, { minutes: cooldownMinutesLeft(account.cooldownUntil, now) }),
556
+ ")"
557
+ ]
558
+ }),
559
+ needsRelogin && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
560
+ className: "dsha-badge danger",
561
+ children: t.needsRelogin
562
+ })
563
+ ]
564
+ })]
565
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
566
+ className: "dsha-account-actions",
567
+ children: [
568
+ props.renderAccountActions?.(account),
569
+ !account.isPrimary && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
570
+ className: "dsha-btn",
571
+ disabled: props.busy !== null,
572
+ onClick: () => props.onSetPrimary(account.id),
573
+ children: t.setPrimary
574
+ }),
575
+ cooling && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
576
+ className: "dsha-btn",
577
+ disabled: props.busy !== null,
578
+ onClick: () => props.onClearCooldown(account.id),
579
+ children: t.clearCooldown
580
+ }),
581
+ needsRelogin && props.onRelogin && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
582
+ className: "dsha-btn",
583
+ disabled: props.busy !== null,
584
+ onClick: () => props.onRelogin(account.id),
585
+ children: t.relogin
586
+ }),
587
+ account.removable !== false && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
588
+ className: "dsha-btn",
589
+ disabled: props.busy !== null,
590
+ onClick: () => props.onDelete(account.id),
591
+ children: t.deleteAccount
592
+ })
593
+ ]
594
+ })]
595
+ }),
596
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
597
+ className: "dsha-account-details",
598
+ children: [
599
+ props.renderDetails?.(account),
600
+ account.email && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
601
+ t.email,
602
+ ": ",
603
+ account.email
604
+ ] }),
605
+ account.expiresAt !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
606
+ t.expires,
607
+ ": ",
608
+ formatAccountDate(account.expiresAt)
609
+ ] }),
610
+ account.lastUsedAt !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
611
+ t.lastUsed,
612
+ ": ",
613
+ formatAccountDate(account.lastUsedAt)
614
+ ] })
615
+ ]
616
+ }),
617
+ needsRelogin && account.authFailedReason && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
618
+ className: "dsha-account-details",
619
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: account.authFailedReason })
620
+ })
621
+ ]
622
+ }, account.id);
623
+ })
624
+ }),
625
+ props.children,
626
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
627
+ className: "dsha-row",
628
+ style: { marginTop: 12 },
629
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
630
+ className: "dsha-label",
631
+ children: t.storage
632
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
633
+ className: "dsha-value",
634
+ children: props.storageValue ?? "本地安全存储 (JSON/DPAPI)"
635
+ })]
636
+ }),
637
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
638
+ className: "dsha-notice",
639
+ children: t.storageNotice
640
+ })
641
+ ]
642
+ });
643
+ }
644
+ //#endregion
363
645
  //#region src/client/CodexSubscriptionSection.tsx
364
646
  function CodexSubscriptionSection({ t }) {
365
647
  const apiRef = (0, react.useRef)(new SubscriptionApi());
@@ -531,14 +813,71 @@ window.__ModuleLoader__.load({
531
813
  await updatePreferences({ customProxyUrl: draft ? draft : null });
532
814
  setCustomProxyDraft(null);
533
815
  };
534
- const logout = async () => run("logout", async () => {
535
- await apiRef.current.logout();
536
- eventSourceRef.current?.close();
537
- eventSourceRef.current = null;
538
- setAuthUrl(null);
539
- setConnection(null);
540
- await load();
541
- });
816
+ /**
817
+ * The shared card takes a label object while this tab translates by key, so
818
+ * every pool label is resolved through the tab's own translator and follows
819
+ * whichever locale is active.
820
+ */
821
+ const poolLabels = (() => {
822
+ const keys = [
823
+ "accountPool",
824
+ "addAccount",
825
+ "accountCount",
826
+ "primaryAccount",
827
+ "activeAccount",
828
+ "setPrimary",
829
+ "deleteAccount",
830
+ "cooling",
831
+ "cooldownLeft",
832
+ "clearCooldown",
833
+ "needsRelogin",
834
+ "relogin",
835
+ "rotationStrategy",
836
+ "strategySequential",
837
+ "strategyRoundRobin",
838
+ "strategySticky",
839
+ "noAccounts",
840
+ "storage",
841
+ "storageNotice",
842
+ "email",
843
+ "expires",
844
+ "lastUsed",
845
+ "accountId"
846
+ ];
847
+ const labels = {};
848
+ for (const key of keys) labels[key] = t(key);
849
+ return labels;
850
+ })();
851
+ const accountAction = async (action, accountId) => {
852
+ await run(action, async () => {
853
+ const next = await apiRef.current.accountAction(action, { accountId });
854
+ setStatus(next);
855
+ });
856
+ };
857
+ const setRotationStrategy = async (strategy) => {
858
+ await run("strategy", async () => {
859
+ const next = await apiRef.current.accountAction("strategy", { strategy });
860
+ setStatus(next);
861
+ });
862
+ };
863
+ /**
864
+ * Re-authorize one account.
865
+ *
866
+ * The failure marker is cleared first and nothing is deleted: the account
867
+ * keeps its alias and place in the rotation.
868
+ */
869
+ const relogin = (accountId) => {
870
+ (async () => {
871
+ try {
872
+ const next = await apiRef.current.accountAction("relogin", { accountId });
873
+ setStatus(next);
874
+ } catch (cause) {
875
+ setError(messageOf(cause));
876
+ return;
877
+ }
878
+ await startLogin();
879
+ })();
880
+ };
542
881
  const run = async (action, task) => {
543
882
  setBusy(action);
544
883
  setError(null);
@@ -551,14 +890,14 @@ window.__ModuleLoader__.load({
551
890
  setBusy(null);
552
891
  }
553
892
  };
554
- const account = status?.account;
893
+ status?.account;
555
894
  const preferences = status?.preferences;
556
895
  const quota = status?.quota;
557
896
  const storage = status?.storage;
558
897
  const login = status?.login;
559
898
  const visibleModelIds = preferences?.visibleModelIds ?? DEFAULT_VISIBLE_CODEX_MODEL_IDS;
560
899
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
561
- className: "dsh-codex-page",
900
+ className: "dsha-page",
562
901
  "aria-labelledby": "dsh-codex-title",
563
902
  children: [
564
903
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
@@ -579,37 +918,36 @@ window.__ModuleLoader__.load({
579
918
  }) : null]
580
919
  }) : null,
581
920
  status === null && error === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Skeleton, { label: t("loading") }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
582
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Section, {
583
- title: t("account"),
921
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(AccountPoolSection, {
922
+ accounts: status?.accounts ?? [],
923
+ activeAccountId: status?.activeAccountId,
924
+ rotationStrategy: status?.rotationStrategy ?? "sequential",
925
+ busy,
926
+ labels: poolLabels,
927
+ loginBusyLabel: login?.active ? t("pending") : void 0,
928
+ onLogin: () => void startLogin(),
929
+ onSetPrimary: (accountId) => void accountAction("set-primary", accountId),
930
+ onDelete: (accountId) => void accountAction("delete", accountId),
931
+ onClearCooldown: (accountId) => void accountAction("clear-cooldown", accountId),
932
+ onRelogin: (accountId) => relogin(accountId),
933
+ onSetStrategy: (strategy) => void setRotationStrategy(strategy),
934
+ storageValue: storageLabel(storage, t),
935
+ renderDetails: (entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [entry.planLabel && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
936
+ t("plan"),
937
+ ": ",
938
+ entry.planLabel
939
+ ] }), entry.email && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
940
+ t("email"),
941
+ ": ",
942
+ entry.email
943
+ ] })] }),
584
944
  children: [
585
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoRow, {
586
- label: status?.authenticated ? t("signedIn") : t("signedOut"),
587
- value: account?.email ?? "—"
588
- }),
589
- status?.authenticated ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
590
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoRow, {
591
- label: t("plan"),
592
- value: account?.planType ?? t("unknown")
593
- }),
594
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoRow, {
595
- label: t("accountId"),
596
- value: account?.accountIdSuffix ?? "—"
597
- }),
598
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoRow, {
599
- label: t("expires"),
600
- value: formatDate$3(account?.tokenExpiresAt)
601
- })
602
- ] }) : null,
603
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoRow, {
604
- label: t("storage"),
605
- value: storageLabel(storage, t)
606
- }),
607
945
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
608
- className: "dsh-codex-notice",
946
+ className: "dsha-notice",
609
947
  children: storageNotice(storage, t)
610
948
  }),
611
949
  login?.active ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
612
- className: "dsh-codex-muted",
950
+ className: "dsha-muted",
613
951
  role: "status",
614
952
  children: t("pending")
615
953
  }) : null,
@@ -625,25 +963,16 @@ window.__ModuleLoader__.load({
625
963
  children: t("continueLogin")
626
964
  }) : null,
627
965
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
628
- className: "dsh-codex-actions",
966
+ className: "dsha-actions",
629
967
  children: [login?.active ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
630
968
  disabled: busy !== null,
631
969
  onClick: cancelLogin,
632
970
  children: t("cancel")
633
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
634
- primary: true,
635
- disabled: busy !== null || storage?.available === false,
636
- onClick: startLogin,
637
- children: status?.authenticated ? t("signInAgain") : t("signIn")
638
- }), status?.authenticated ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
971
+ }) : null, status?.authenticated ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
639
972
  disabled: busy !== null,
640
973
  onClick: refreshToken,
641
974
  children: t("refreshToken")
642
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
643
- disabled: busy !== null,
644
- onClick: logout,
645
- children: t("signOut")
646
- })] }) : null]
975
+ }) : null]
647
976
  })
648
977
  ]
649
978
  }),
@@ -651,7 +980,7 @@ window.__ModuleLoader__.load({
651
980
  title: t("connection"),
652
981
  children: [
653
982
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
654
- className: "dsh-codex-pref-row",
983
+ className: "dsha-pref-row",
655
984
  style: { marginBottom: 12 },
656
985
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("enableProvider") }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
657
986
  type: "checkbox",
@@ -683,11 +1012,11 @@ window.__ModuleLoader__.load({
683
1012
  value: `${connection.latencyMs} ms · ${formatDate$3(connection.checkedAt)}`
684
1013
  }) : null,
685
1014
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
686
- className: "dsh-codex-muted dsh-codex-models-hint",
1015
+ className: "dsha-muted dsha-models-hint",
687
1016
  children: t("modelsHint")
688
1017
  }),
689
1018
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
690
- className: "dsh-codex-models",
1019
+ className: "dsha-models",
691
1020
  "aria-label": t("models"),
692
1021
  children: CODEX_MODEL_CATALOG.map((model) => {
693
1022
  const checked = visibleModelIds.some((id) => id === model.id);
@@ -720,7 +1049,7 @@ window.__ModuleLoader__.load({
720
1049
  })]
721
1050
  }),
722
1051
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
723
- className: "dsh-codex-actions",
1052
+ className: "dsha-actions",
724
1053
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
725
1054
  disabled: !status?.authenticated || busy !== null,
726
1055
  onClick: testConnection,
@@ -733,12 +1062,12 @@ window.__ModuleLoader__.load({
733
1062
  title: t("proxySettings"),
734
1063
  children: [
735
1064
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
736
- className: "dsh-codex-pref-row",
1065
+ className: "dsha-pref-row",
737
1066
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("proxyMode") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
738
- className: "dsh-codex-muted",
1067
+ className: "dsha-muted",
739
1068
  children: t("proxyModeHint")
740
1069
  })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
741
- className: "dsh-codex-select",
1070
+ className: "dsha-select",
742
1071
  "aria-label": t("proxyMode"),
743
1072
  value: preferences?.proxyMode ?? "auto",
744
1073
  disabled: busy !== null,
@@ -782,22 +1111,22 @@ window.__ModuleLoader__.load({
782
1111
  children: t("proxyDetectedEffective")
783
1112
  })
784
1113
  ] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
785
- className: "dsh-codex-muted",
1114
+ className: "dsha-muted",
786
1115
  children: t("proxyNoneDetected")
787
1116
  })
788
1117
  }) : null,
789
1118
  preferences?.proxyMode === "direct" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
790
- className: "dsh-codex-muted",
1119
+ className: "dsha-muted",
791
1120
  style: { margin: "8px 0 0" },
792
1121
  children: t("proxyDirectHint")
793
1122
  }) : null,
794
1123
  preferences?.proxyMode === "custom" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
795
- className: "dsh-codex-context-settings",
1124
+ className: "dsha-context-settings",
796
1125
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("customProxyUrl") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
797
- className: "dsh-codex-muted",
1126
+ className: "dsha-muted",
798
1127
  children: t("customProxyUrlHint")
799
1128
  })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
800
- className: "dsh-codex-context-row",
1129
+ className: "dsha-context-row",
801
1130
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
802
1131
  className: "dsh-codex-proxy-control",
803
1132
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
@@ -816,7 +1145,7 @@ window.__ModuleLoader__.load({
816
1145
  updateCustomProxyUrl();
817
1146
  }
818
1147
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
819
- className: "dsh-codex-context-save",
1148
+ className: "dsha-context-save",
820
1149
  type: "button",
821
1150
  "aria-label": t("saveProxyUrl"),
822
1151
  disabled: busy !== null || customProxyDraft === null,
@@ -832,9 +1161,9 @@ window.__ModuleLoader__.load({
832
1161
  title: t("enhancements"),
833
1162
  children: [
834
1163
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
835
- className: "dsh-codex-pref-row",
1164
+ className: "dsha-pref-row",
836
1165
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("searchProvider") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
837
- className: "dsh-codex-muted",
1166
+ className: "dsha-muted",
838
1167
  children: t("searchProviderHint")
839
1168
  })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
840
1169
  className: "dsh-codex-segments",
@@ -858,12 +1187,12 @@ window.__ModuleLoader__.load({
858
1187
  })]
859
1188
  }),
860
1189
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
861
- className: "dsh-codex-pref-row",
1190
+ className: "dsha-pref-row",
862
1191
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("outputVerbosity") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
863
- className: "dsh-codex-muted",
1192
+ className: "dsha-muted",
864
1193
  children: t("outputVerbosityHint")
865
1194
  })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
866
- className: "dsh-codex-select",
1195
+ className: "dsha-select",
867
1196
  "aria-label": t("outputVerbosity"),
868
1197
  value: preferences?.outputVerbosity ?? "",
869
1198
  disabled: busy !== null,
@@ -892,12 +1221,12 @@ window.__ModuleLoader__.load({
892
1221
  })]
893
1222
  }),
894
1223
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
895
- className: "dsh-codex-pref-row",
1224
+ className: "dsha-pref-row",
896
1225
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("reasoningSummary") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
897
- className: "dsh-codex-muted",
1226
+ className: "dsha-muted",
898
1227
  children: t("reasoningSummaryHint")
899
1228
  })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
900
- className: "dsh-codex-select",
1229
+ className: "dsha-select",
901
1230
  "aria-label": t("reasoningSummary"),
902
1231
  value: preferences?.reasoningSummary ?? "",
903
1232
  disabled: busy !== null,
@@ -930,9 +1259,9 @@ window.__ModuleLoader__.load({
930
1259
  })]
931
1260
  }),
932
1261
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
933
- className: "dsh-codex-context-settings",
1262
+ className: "dsha-context-settings",
934
1263
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("contextWindows") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
935
- className: "dsh-codex-muted",
1264
+ className: "dsha-muted",
936
1265
  children: t("contextWindowsHint")
937
1266
  })] }), CONFIGURABLE_CONTEXT_MODEL_IDS.map((model) => {
938
1267
  const entry = resolveCodexCatalogEntry(model);
@@ -942,18 +1271,18 @@ window.__ModuleLoader__.load({
942
1271
  const dirty = draft !== void 0 && parsedDraft !== fallback;
943
1272
  const inputId = `dsh-codex-context-${model}`;
944
1273
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
945
- className: "dsh-codex-context-row",
1274
+ className: "dsha-context-row",
946
1275
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
947
1276
  htmlFor: inputId,
948
1277
  children: entry.name
949
1278
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
950
- className: "dsh-codex-capacity-control",
1279
+ className: "dsha-capacity-control",
951
1280
  children: [
952
1281
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
953
1282
  id: inputId,
954
1283
  type: "text",
955
1284
  inputMode: "numeric",
956
- value: draft ?? formatCapacity$3(fallback),
1285
+ value: draft ?? formatCapacity$4(fallback),
957
1286
  disabled: busy !== null,
958
1287
  "aria-label": entry.name + " " + t("contextWindow"),
959
1288
  onChange: (event) => {
@@ -971,7 +1300,7 @@ window.__ModuleLoader__.load({
971
1300
  }),
972
1301
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: t("tokens") }),
973
1302
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
974
- className: "dsh-codex-context-save",
1303
+ className: "dsha-context-save",
975
1304
  type: "button",
976
1305
  "data-model": model,
977
1306
  "aria-label": entry.name + " " + t("saveContextWindow"),
@@ -1013,7 +1342,7 @@ window.__ModuleLoader__.load({
1013
1342
  }),
1014
1343
  children: [
1015
1344
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1016
- className: "dsh-codex-muted",
1345
+ className: "dsha-muted",
1017
1346
  children: t("quotaIntro")
1018
1347
  }),
1019
1348
  quota?.state === "signed-out" || !status?.authenticated ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
@@ -1083,16 +1412,16 @@ window.__ModuleLoader__.load({
1083
1412
  }
1084
1413
  function Section({ title, aside, children }) {
1085
1414
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1086
- className: "dsh-codex-group",
1415
+ className: "dsha-group",
1087
1416
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1088
- className: "dsh-codex-grouphead",
1417
+ className: "dsha-grouphead",
1089
1418
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: title }), aside]
1090
1419
  }), children]
1091
1420
  });
1092
1421
  }
1093
1422
  function Button({ primary = false, disabled, onClick, children }) {
1094
1423
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1095
- className: `dsh-codex-button${primary ? " dsh-codex-button-primary" : ""}`,
1424
+ className: `dsha-btn${primary ? " dsha-btn-primary" : ""}`,
1096
1425
  type: "button",
1097
1426
  disabled,
1098
1427
  onClick: () => void onClick(),
@@ -1101,12 +1430,12 @@ window.__ModuleLoader__.load({
1101
1430
  }
1102
1431
  function InfoRow({ label, value }) {
1103
1432
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1104
- className: "dsh-codex-row",
1433
+ className: "dsha-row",
1105
1434
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1106
- className: "dsh-codex-label",
1435
+ className: "dsha-label",
1107
1436
  children: label
1108
1437
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1109
- className: "dsh-codex-value",
1438
+ className: "dsha-value",
1110
1439
  children: value
1111
1440
  })]
1112
1441
  });
@@ -1159,7 +1488,7 @@ window.__ModuleLoader__.load({
1159
1488
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("small", { children: [
1160
1489
  t("resetCreditExpires"),
1161
1490
  ": ",
1162
- resetCredits.expiresAt == null ? t("unknown") : formatReset$2(resetCredits.expiresAt)
1491
+ resetCredits.expiresAt == null ? t("unknown") : formatReset$3(resetCredits.expiresAt)
1163
1492
  ] })
1164
1493
  ]
1165
1494
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
@@ -1192,7 +1521,7 @@ window.__ModuleLoader__.load({
1192
1521
  }),
1193
1522
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1194
1523
  className: "dsh-codex-meter-meta",
1195
- 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)}` })]
1524
+ 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$3(window.resetsAt)}` })]
1196
1525
  })
1197
1526
  ]
1198
1527
  });
@@ -1218,7 +1547,7 @@ window.__ModuleLoader__.load({
1218
1547
  return `${value} ${t("limitWindow")}`;
1219
1548
  }
1220
1549
  }
1221
- function parsePositiveCapacity$3(value) {
1550
+ function parsePositiveCapacity$4(value) {
1222
1551
  const matched = value.trim().toLowerCase().replace(/[,_\s]/g, "").match(/^(\d+(?:\.\d+)?)(k|m)?$/);
1223
1552
  if (matched === null) return null;
1224
1553
  const multiplier = matched[2] === "m" ? 1e6 : matched[2] === "k" ? 1e3 : 1;
@@ -1226,10 +1555,10 @@ window.__ModuleLoader__.load({
1226
1555
  return Number.isSafeInteger(parsed) && parsed >= 1 ? parsed : null;
1227
1556
  }
1228
1557
  function parseCapacity(value, maxContextWindow = GPT_56_MAX_CONTEXT_WINDOW) {
1229
- const parsed = parsePositiveCapacity$3(value);
1558
+ const parsed = parsePositiveCapacity$4(value);
1230
1559
  return parsed !== null && parsed <= maxContextWindow ? parsed : null;
1231
1560
  }
1232
- function formatCapacity$3(value) {
1561
+ function formatCapacity$4(value) {
1233
1562
  if (value % 1e3 === 0) return `${value / 1e3}K`;
1234
1563
  return String(value);
1235
1564
  }
@@ -1246,7 +1575,7 @@ window.__ModuleLoader__.load({
1246
1575
  limit.remainingPercent !== null && limit.remainingPercent !== void 0 ? `${formatPercent(limit.remainingPercent)} ${t("remaining")}` : null,
1247
1576
  limit.limit !== null && limit.limit !== void 0 ? `${t("limit")}: ${limit.limit}` : null,
1248
1577
  limit.used !== null && limit.used !== void 0 ? `${t("used")}: ${limit.used}` : null,
1249
- limit.resetsAt !== null && limit.resetsAt !== void 0 ? `${t("resets")}: ${formatReset$2(limit.resetsAt)}` : null
1578
+ limit.resetsAt !== null && limit.resetsAt !== void 0 ? `${t("resets")}: ${formatReset$3(limit.resetsAt)}` : null
1250
1579
  ].filter((part) => part !== null);
1251
1580
  return parts.length > 0 ? parts.join(" · ") : t("unknown");
1252
1581
  }
@@ -1262,7 +1591,7 @@ window.__ModuleLoader__.load({
1262
1591
  return "—";
1263
1592
  }
1264
1593
  }
1265
- function formatReset$2(seconds) {
1594
+ function formatReset$3(seconds) {
1266
1595
  if (seconds === void 0 || seconds === null || typeof seconds !== "number" || !Number.isFinite(seconds) || seconds <= 0) return "—";
1267
1596
  const ms = seconds > 1e10 ? seconds : seconds * 1e3;
1268
1597
  const absolute = formatDate$3(seconds);
@@ -1281,7 +1610,8 @@ window.__ModuleLoader__.load({
1281
1610
  //#endregion
1282
1611
  //#region src/client/antigravity/locales.ts
1283
1612
  const NS_ANTIGRAVITY = "dsh-antigravity";
1284
- const zh$3 = {
1613
+ const zh$4 = {
1614
+ ...accountPoolZh,
1285
1615
  title: "Antigravity",
1286
1616
  pageDesc: "登录 Google Antigravity / Cloud Code Assist,并查看当前账号的共享额度。",
1287
1617
  account: "已登录",
@@ -1298,6 +1628,7 @@ window.__ModuleLoader__.load({
1298
1628
  rotationStrategy: "调度策略",
1299
1629
  strategySequential: "顺序耗尽(当前优先,遇限流自动切号)",
1300
1630
  strategyRoundRobin: "轮询调度(按账号循环均匀分摊)",
1631
+ strategySticky: "会话粘性(保持当前账号直到被限流)",
1301
1632
  noAccounts: "暂无账号,点击「添加账号」完成登录授权。",
1302
1633
  plan: "套餐",
1303
1634
  accountId: "账号 ID",
@@ -1345,7 +1676,8 @@ window.__ModuleLoader__.load({
1345
1676
  loginFailed: "登录失败",
1346
1677
  tokens: "tokens"
1347
1678
  };
1348
- const en$3 = {
1679
+ const en$4 = {
1680
+ ...accountPoolEn,
1349
1681
  title: "Antigravity",
1350
1682
  pageDesc: "Sign in to Google Antigravity / Cloud Code Assist and view shared quotas.",
1351
1683
  account: "Signed in",
@@ -1362,6 +1694,7 @@ window.__ModuleLoader__.load({
1362
1694
  rotationStrategy: "Scheduling Strategy",
1363
1695
  strategySequential: "Sequential Drain (Primary first, failover on 429)",
1364
1696
  strategyRoundRobin: "Round-Robin (Evenly rotate across accounts)",
1697
+ strategySticky: "Sticky session (hold the current account until it is limited)",
1365
1698
  noAccounts: "No accounts yet. Click \"Add Account\" to authorize.",
1366
1699
  plan: "Plan",
1367
1700
  accountId: "Account ID",
@@ -1409,17 +1742,17 @@ window.__ModuleLoader__.load({
1409
1742
  loginFailed: "Login failed",
1410
1743
  tokens: "tokens"
1411
1744
  };
1412
- const dictionaries$3 = {
1413
- zh: zh$3,
1414
- "zh-CN": zh$3,
1415
- en: en$3,
1416
- "en-US": en$3
1745
+ const dictionaries$4 = {
1746
+ zh: zh$4,
1747
+ "zh-CN": zh$4,
1748
+ en: en$4,
1749
+ "en-US": en$4
1417
1750
  };
1418
1751
  //#endregion
1419
1752
  //#region src/client/antigravity/AntigravitySection.tsx
1420
- const API$5 = "/antigravity/api";
1421
- async function fetchApi$5(path, options) {
1422
- const res = await fetch(`${API$5}${path}`, {
1753
+ const API$7 = "/antigravity/api";
1754
+ async function fetchApi$7(path, options) {
1755
+ const res = await fetch(`${API$7}${path}`, {
1423
1756
  ...options,
1424
1757
  headers: {
1425
1758
  "Content-Type": "application/json",
@@ -1430,14 +1763,14 @@ window.__ModuleLoader__.load({
1430
1763
  if (!res.ok || !json.ok) throw new Error(json.error || `HTTP ${res.status}`);
1431
1764
  return json.value;
1432
1765
  }
1433
- function parsePositiveCapacity$2(value) {
1766
+ function parsePositiveCapacity$3(value) {
1434
1767
  const matched = value.trim().toLowerCase().replace(/[,_\s]/g, "").match(/^(\d+(?:\.\d+)?)(k|m)?$/);
1435
1768
  if (matched === null) return null;
1436
1769
  const multiplier = matched[2] === "m" ? 1e6 : matched[2] === "k" ? 1e3 : 1;
1437
1770
  const parsed = Number(matched[1]) * multiplier;
1438
1771
  return Number.isSafeInteger(parsed) && parsed >= 1 ? parsed : null;
1439
1772
  }
1440
- function formatCapacity$2(value) {
1773
+ function formatCapacity$3(value) {
1441
1774
  if (value >= 1e6 && value % 1e5 === 0) return `${value / 1e6}M`;
1442
1775
  if (value % 1e3 === 0) return `${value / 1e3}K`;
1443
1776
  return String(value);
@@ -1473,7 +1806,7 @@ window.__ModuleLoader__.load({
1473
1806
  const [loginProgress, setLoginProgress] = (0, react.useState)(null);
1474
1807
  const [contextDrafts, setContextDrafts] = (0, react.useState)({});
1475
1808
  const [savingModel, setSavingModel] = (0, react.useState)(null);
1476
- const t = zh$3;
1809
+ const t = zh$4;
1477
1810
  const notifyChange = (0, react.useCallback)(() => {
1478
1811
  onModelChange?.();
1479
1812
  loadModelDirectory?.();
@@ -1484,12 +1817,12 @@ window.__ModuleLoader__.load({
1484
1817
  setValidationUrl(null);
1485
1818
  }
1486
1819
  try {
1487
- const data = await fetchApi$5("/status");
1820
+ const data = await fetchApi$7("/status");
1488
1821
  setStatus(data);
1489
1822
  const drafts = {};
1490
1823
  for (const m of data.models) {
1491
1824
  const val = data.contextWindowOverrides[m.id] || m.defaultContextWindow;
1492
- drafts[m.id] = formatCapacity$2(val);
1825
+ drafts[m.id] = formatCapacity$3(val);
1493
1826
  }
1494
1827
  setContextDrafts(drafts);
1495
1828
  } catch (err) {
@@ -1516,11 +1849,11 @@ window.__ModuleLoader__.load({
1516
1849
  setError(null);
1517
1850
  setValidationUrl(null);
1518
1851
  setLoginProgress(null);
1519
- const flow = await fetchApi$5("/login", { method: "POST" });
1852
+ const flow = await fetchApi$7("/login", { method: "POST" });
1520
1853
  if (flow.authUrl) window.open(flow.authUrl, "_blank");
1521
1854
  const pollTimer = setInterval(async () => {
1522
1855
  try {
1523
- const pollStatus = await fetchApi$5("/login/status");
1856
+ const pollStatus = await fetchApi$7("/login/status");
1524
1857
  if (pollStatus.progress) setLoginProgress(pollStatus.progress);
1525
1858
  if (pollStatus.status === "complete") {
1526
1859
  clearInterval(pollTimer);
@@ -1552,7 +1885,7 @@ window.__ModuleLoader__.load({
1552
1885
  try {
1553
1886
  setBusy("quota");
1554
1887
  setError(null);
1555
- const updated = await fetchApi$5("/quota", { method: "POST" });
1888
+ const updated = await fetchApi$7("/quota", { method: "POST" });
1556
1889
  setStatus(updated);
1557
1890
  } catch (err) {
1558
1891
  setError(err instanceof Error ? err.message : String(err));
@@ -1564,7 +1897,7 @@ window.__ModuleLoader__.load({
1564
1897
  try {
1565
1898
  setBusy(`primary-${accountId}`);
1566
1899
  setError(null);
1567
- const updated = await fetchApi$5("/accounts", {
1900
+ const updated = await fetchApi$7("/accounts", {
1568
1901
  method: "POST",
1569
1902
  body: JSON.stringify({
1570
1903
  action: "set-primary",
@@ -1583,7 +1916,7 @@ window.__ModuleLoader__.load({
1583
1916
  try {
1584
1917
  setBusy(`delete-${accountId}`);
1585
1918
  setError(null);
1586
- const updated = await fetchApi$5("/accounts", {
1919
+ const updated = await fetchApi$7("/accounts", {
1587
1920
  method: "POST",
1588
1921
  body: JSON.stringify({
1589
1922
  action: "delete",
@@ -1602,7 +1935,7 @@ window.__ModuleLoader__.load({
1602
1935
  try {
1603
1936
  setBusy(`cooldown-${accountId}`);
1604
1937
  setError(null);
1605
- const updated = await fetchApi$5("/accounts", {
1938
+ const updated = await fetchApi$7("/accounts", {
1606
1939
  method: "POST",
1607
1940
  body: JSON.stringify({
1608
1941
  action: "clear-cooldown",
@@ -1620,7 +1953,7 @@ window.__ModuleLoader__.load({
1620
1953
  try {
1621
1954
  setBusy("strategy");
1622
1955
  setError(null);
1623
- const updated = await fetchApi$5("/accounts", {
1956
+ const updated = await fetchApi$7("/accounts", {
1624
1957
  method: "POST",
1625
1958
  body: JSON.stringify({
1626
1959
  action: "strategy",
@@ -1640,7 +1973,7 @@ window.__ModuleLoader__.load({
1640
1973
  enabled
1641
1974
  } : prev);
1642
1975
  try {
1643
- const updated = await fetchApi$5("/settings", {
1976
+ const updated = await fetchApi$7("/settings", {
1644
1977
  method: "POST",
1645
1978
  body: JSON.stringify({ enabled })
1646
1979
  });
@@ -1656,7 +1989,7 @@ window.__ModuleLoader__.load({
1656
1989
  const currentEnabled = status.models.filter((m) => m.enabled).map((m) => m.id);
1657
1990
  const nextEnabled = checked ? [.../* @__PURE__ */ new Set([...currentEnabled, modelId])] : currentEnabled.filter((id) => id !== modelId);
1658
1991
  try {
1659
- const updated = await fetchApi$5("/models", {
1992
+ const updated = await fetchApi$7("/models", {
1660
1993
  method: "POST",
1661
1994
  body: JSON.stringify({ enabledModelIds: nextEnabled })
1662
1995
  });
@@ -1670,7 +2003,7 @@ window.__ModuleLoader__.load({
1670
2003
  if (!status) return;
1671
2004
  const nextEnabled = selectAll ? status.models.map((m) => m.id) : [];
1672
2005
  try {
1673
- const updated = await fetchApi$5("/models", {
2006
+ const updated = await fetchApi$7("/models", {
1674
2007
  method: "POST",
1675
2008
  body: JSON.stringify({ enabledModelIds: nextEnabled })
1676
2009
  });
@@ -1682,7 +2015,7 @@ window.__ModuleLoader__.load({
1682
2015
  };
1683
2016
  const handleUpdateEffort = async (effort) => {
1684
2017
  try {
1685
- const updated = await fetchApi$5("/settings", {
2018
+ const updated = await fetchApi$7("/settings", {
1686
2019
  method: "POST",
1687
2020
  body: JSON.stringify({ defaultReasoningEffort: effort })
1688
2021
  });
@@ -1694,7 +2027,7 @@ window.__ModuleLoader__.load({
1694
2027
  };
1695
2028
  const handleSaveContextWindow = async (modelId) => {
1696
2029
  const raw = contextDrafts[modelId] || "";
1697
- const parsed = parsePositiveCapacity$2(raw);
2030
+ const parsed = parsePositiveCapacity$3(raw);
1698
2031
  if (parsed === null) {
1699
2032
  setError(`无效的上下文容量值: ${raw}`);
1700
2033
  return;
@@ -1702,14 +2035,14 @@ window.__ModuleLoader__.load({
1702
2035
  try {
1703
2036
  setSavingModel(modelId);
1704
2037
  setError(null);
1705
- const updated = await fetchApi$5("/settings", {
2038
+ const updated = await fetchApi$7("/settings", {
1706
2039
  method: "POST",
1707
2040
  body: JSON.stringify({ contextWindowOverrides: { [modelId]: parsed } })
1708
2041
  });
1709
2042
  setStatus(updated);
1710
2043
  setContextDrafts((prev) => ({
1711
2044
  ...prev,
1712
- [modelId]: formatCapacity$2(parsed)
2045
+ [modelId]: formatCapacity$3(parsed)
1713
2046
  }));
1714
2047
  notifyChange();
1715
2048
  } catch (err) {
@@ -1731,148 +2064,40 @@ window.__ModuleLoader__.load({
1731
2064
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1732
2065
  className: "dsha-page",
1733
2066
  children: [
1734
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1735
- className: "dsha-group",
1736
- children: [
1737
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1738
- className: "dsha-grouphead",
1739
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("h3", { children: [t.accountPool, status?.accounts && status.accounts.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1740
- className: "dsha-muted",
1741
- style: {
1742
- fontWeight: 400,
1743
- marginLeft: 8
1744
- },
1745
- children: [
1746
- "(已登录 ",
1747
- status.accounts.length,
1748
- " 个账号)"
1749
- ]
1750
- })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1751
- className: "dsha-btn dsha-btn-primary",
1752
- disabled: busy !== null,
1753
- onClick: handleLogin,
1754
- children: busy === "login" ? loginProgress || t.signingIn : t.addAccount
1755
- })]
1756
- }),
1757
- status?.accounts && status.accounts.length > 1 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1758
- className: "dsha-pref-row",
1759
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t.rotationStrategy }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1760
- className: "dsha-muted",
1761
- style: {
1762
- fontSize: 12,
1763
- marginTop: 2
1764
- },
1765
- children: status.rotationStrategy === "round-robin" ? t.strategyRoundRobin : t.strategySequential
1766
- })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
1767
- className: "dsha-select",
1768
- value: status.rotationStrategy || "sequential",
1769
- disabled: busy !== null,
1770
- onChange: (e) => void handleSetStrategy(e.target.value),
1771
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1772
- value: "sequential",
1773
- children: "顺序耗尽"
1774
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1775
- value: "round-robin",
1776
- children: "轮询调度"
1777
- })]
1778
- })]
1779
- }),
1780
- !status?.accounts || status.accounts.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1781
- className: "dsha-empty",
1782
- children: t.noAccounts
1783
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1784
- className: "dsha-accounts-list",
1785
- children: status.accounts.map((acc) => {
1786
- const isCurrentActive = acc.id === status.activeAccountId;
1787
- const isCooling = typeof acc.cooldownUntil === "number" && acc.cooldownUntil > Date.now();
1788
- const cooldownLeftSec = isCooling ? Math.ceil((acc.cooldownUntil - Date.now()) / 1e3) : 0;
1789
- const cooldownLeftMin = Math.ceil(cooldownLeftSec / 60);
1790
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1791
- className: `dsha-account-card ${isCurrentActive ? "active" : ""}`,
1792
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1793
- className: "dsha-account-header",
1794
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1795
- className: "dsha-account-identity",
1796
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1797
- className: "dsha-account-title",
1798
- children: acc.alias || acc.email || acc.id
1799
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1800
- className: "dsha-badges",
1801
- children: [
1802
- acc.isPrimary && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1803
- className: "dsha-badge primary",
1804
- children: t.primaryAccount
1805
- }),
1806
- isCurrentActive && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1807
- className: "dsha-badge active",
1808
- children: t.activeAccount
1809
- }),
1810
- isCooling && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1811
- className: "dsha-badge cooldown",
1812
- children: [
1813
- t.cooling,
1814
- "(剩 ",
1815
- cooldownLeftMin,
1816
- " 分)"
1817
- ]
1818
- })
1819
- ]
1820
- })]
1821
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1822
- className: "dsha-account-actions",
1823
- children: [
1824
- !acc.isPrimary && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1825
- className: "dsha-btn",
1826
- disabled: busy !== null,
1827
- onClick: () => void handleSetPrimary(acc.id),
1828
- children: t.setPrimary
1829
- }),
1830
- isCooling && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1831
- className: "dsha-btn",
1832
- disabled: busy !== null,
1833
- onClick: () => void handleClearCooldown(acc.id),
1834
- children: t.clearCooldown
1835
- }),
1836
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1837
- className: "dsha-btn",
1838
- disabled: busy !== null,
1839
- onClick: () => void handleDeleteAccount(acc.id),
1840
- children: t.deleteAccount
1841
- })
1842
- ]
1843
- })]
1844
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1845
- className: "dsha-account-details",
1846
- children: [
1847
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
1848
- t.accountId,
1849
- ": ",
1850
- acc.projectId || "antigravity-default"
1851
- ] }),
1852
- acc.email && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: ["邮箱: ", acc.email] }),
1853
- acc.expiresAt && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: ["令牌到期: ", formatDate$2(acc.expiresAt)] }),
1854
- acc.lastUsedAt && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: ["上次调用: ", formatDate$2(acc.lastUsedAt)] })
1855
- ]
1856
- })]
1857
- }, acc.id);
1858
- })
1859
- }),
1860
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1861
- className: "dsha-row",
1862
- style: { marginTop: 12 },
1863
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1864
- className: "dsha-label",
1865
- children: t.storage
1866
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1867
- className: "dsha-value",
1868
- children: "本地安全存储 (JSON/DPAPI)"
1869
- })]
1870
- }),
1871
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1872
- className: "dsha-notice",
1873
- children: t.storageNotice
1874
- })
1875
- ]
2067
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountPoolSection, {
2068
+ accounts: status?.accounts ?? [],
2069
+ activeAccountId: status?.activeAccountId,
2070
+ rotationStrategy: status?.rotationStrategy ?? "sequential",
2071
+ busy,
2072
+ labels: t,
2073
+ loginBusyLabel: busy === "login" ? loginProgress || t.signingIn : void 0,
2074
+ onLogin: () => void handleLogin(),
2075
+ onSetPrimary: (accountId) => void handleSetPrimary(accountId),
2076
+ onDelete: (accountId) => void handleDeleteAccount(accountId),
2077
+ onClearCooldown: (accountId) => void handleClearCooldown(accountId),
2078
+ onSetStrategy: (strategy) => void handleSetStrategy(strategy),
2079
+ renderDetails: (account) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
2080
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
2081
+ t.accountId,
2082
+ ": ",
2083
+ account.projectId || "antigravity-default"
2084
+ ] }),
2085
+ account.email && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
2086
+ t.email,
2087
+ ": ",
2088
+ account.email
2089
+ ] }),
2090
+ account.expiresAt && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
2091
+ t.expires,
2092
+ ": ",
2093
+ formatDate$2(account.expiresAt)
2094
+ ] }),
2095
+ account.lastUsedAt && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
2096
+ t.lastUsed,
2097
+ ": ",
2098
+ formatDate$2(account.lastUsedAt)
2099
+ ] })
2100
+ ] })
1876
2101
  }),
1877
2102
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1878
2103
  className: "dsha-group",
@@ -2126,7 +2351,8 @@ window.__ModuleLoader__.load({
2126
2351
  //#endregion
2127
2352
  //#region src/client/command-code/locales.ts
2128
2353
  const NS_COMMAND_CODE = "dsh-command-code";
2129
- const zh$2 = {
2354
+ const zh$3 = {
2355
+ ...accountPoolZh,
2130
2356
  title: "Command Code",
2131
2357
  pageDesc: "登录 Command Code,管理可用模型、上下文窗口与额度。",
2132
2358
  account: "账号",
@@ -2193,7 +2419,8 @@ window.__ModuleLoader__.load({
2193
2419
  loginFailed: "登录失败",
2194
2420
  tokens: "tokens"
2195
2421
  };
2196
- const en$2 = {
2422
+ const en$3 = {
2423
+ ...accountPoolEn,
2197
2424
  title: "Command Code",
2198
2425
  pageDesc: "Sign in to Command Code and manage models, context windows, and quota.",
2199
2426
  account: "Account",
@@ -2260,11 +2487,11 @@ window.__ModuleLoader__.load({
2260
2487
  loginFailed: "Sign-in failed",
2261
2488
  tokens: "tokens"
2262
2489
  };
2263
- const dictionaries$2 = {
2264
- zh: zh$2,
2265
- "zh-CN": zh$2,
2266
- en: en$2,
2267
- "en-US": en$2
2490
+ const dictionaries$3 = {
2491
+ zh: zh$3,
2492
+ "zh-CN": zh$3,
2493
+ en: en$3,
2494
+ "en-US": en$3
2268
2495
  };
2269
2496
  //#endregion
2270
2497
  //#region src/client/command-code/CommandCodeSection.tsx
@@ -2272,7 +2499,7 @@ window.__ModuleLoader__.load({
2272
2499
  * Display label per reasoning level. The locale dictionary only accepts flat
2273
2500
  * string values, so this nested table lives with the control that renders it.
2274
2501
  */
2275
- const EFFORT_LABELS = {
2502
+ const EFFORT_LABELS$1 = {
2276
2503
  minimal: "Minimal",
2277
2504
  low: "Low",
2278
2505
  medium: "Medium",
@@ -2280,9 +2507,9 @@ window.__ModuleLoader__.load({
2280
2507
  xhigh: "X-High",
2281
2508
  max: "Max"
2282
2509
  };
2283
- const API$4 = "/command-code/api";
2284
- async function fetchApi$4(path, options) {
2285
- const res = await fetch(`${API$4}${path}`, {
2510
+ const API$6 = "/command-code/api";
2511
+ async function fetchApi$6(path, options) {
2512
+ const res = await fetch(`${API$6}${path}`, {
2286
2513
  ...options,
2287
2514
  headers: {
2288
2515
  "Content-Type": "application/json",
@@ -2294,30 +2521,19 @@ window.__ModuleLoader__.load({
2294
2521
  return json.value;
2295
2522
  }
2296
2523
  /** Parse "1M", "512K", "200000" into a positive integer token count. */
2297
- function parsePositiveCapacity$1(value) {
2524
+ function parsePositiveCapacity$2(value) {
2298
2525
  const matched = value.trim().toLowerCase().replace(/[,_\s]/g, "").match(/^(\d+(?:\.\d+)?)(k|m)?$/);
2299
2526
  if (matched === null) return null;
2300
2527
  const multiplier = matched[2] === "m" ? 1e6 : matched[2] === "k" ? 1e3 : 1;
2301
2528
  const parsed = Number(matched[1]) * multiplier;
2302
2529
  return Number.isSafeInteger(parsed) && parsed >= 1 ? parsed : null;
2303
2530
  }
2304
- function formatCapacity$1(value) {
2531
+ function formatCapacity$2(value) {
2305
2532
  if (value >= 1e6 && value % 1e5 === 0) return `${value / 1e6}M`;
2306
2533
  if (value >= 1e3 && value % 1e3 === 0) return `${value / 1e3}K`;
2307
2534
  return String(value);
2308
2535
  }
2309
- function formatDate$1(ms) {
2310
- if (ms === void 0 || ms === null || ms <= 0) return "—";
2311
- try {
2312
- return new Intl.DateTimeFormat(void 0, {
2313
- dateStyle: "medium",
2314
- timeStyle: "short"
2315
- }).format(ms);
2316
- } catch {
2317
- return "—";
2318
- }
2319
- }
2320
- function formatReset$1(resetsAt) {
2536
+ function formatReset$2(resetsAt) {
2321
2537
  if (resetsAt === void 0 || resetsAt === null || resetsAt <= 0) return "";
2322
2538
  const diff = resetsAt - Date.now();
2323
2539
  if (diff <= 0) return "now";
@@ -2337,7 +2553,7 @@ window.__ModuleLoader__.load({
2337
2553
  const [contextDrafts, setContextDrafts] = (0, react.useState)({});
2338
2554
  const [savingModel, setSavingModel] = (0, react.useState)(null);
2339
2555
  const [apiKey, setApiKey] = (0, react.useState)("");
2340
- const t = zh$2;
2556
+ const t = zh$3;
2341
2557
  const notifyChange = (0, react.useCallback)(() => {
2342
2558
  onModelChange?.();
2343
2559
  loadModelDirectory?.();
@@ -2345,10 +2561,10 @@ window.__ModuleLoader__.load({
2345
2561
  const loadStatus = (0, react.useCallback)(async (quiet = false) => {
2346
2562
  if (!quiet) setError(null);
2347
2563
  try {
2348
- const data = await fetchApi$4("/status");
2564
+ const data = await fetchApi$6("/status");
2349
2565
  setStatus(data);
2350
2566
  const drafts = {};
2351
- for (const model of data.models) drafts[model.id] = formatCapacity$1(data.contextWindowOverrides[model.id] || model.defaultContextWindow);
2567
+ for (const model of data.models) drafts[model.id] = formatCapacity$2(data.contextWindowOverrides[model.id] || model.defaultContextWindow);
2352
2568
  setContextDrafts(drafts);
2353
2569
  } catch (err) {
2354
2570
  if (!quiet) setError(err instanceof Error ? err.message : String(err));
@@ -2373,12 +2589,12 @@ window.__ModuleLoader__.load({
2373
2589
  setBusy("login");
2374
2590
  setError(null);
2375
2591
  setLoginProgress(null);
2376
- const flow = await fetchApi$4("/login", { method: "POST" });
2592
+ const flow = await fetchApi$6("/login", { method: "POST" });
2377
2593
  if (flow.authUrl) window.open(flow.authUrl, "_blank");
2378
2594
  const pollTimer = window.setInterval(() => {
2379
2595
  (async () => {
2380
2596
  try {
2381
- const poll = await fetchApi$4("/login/status");
2597
+ const poll = await fetchApi$6("/login/status");
2382
2598
  if (poll.progress) setLoginProgress(poll.progress);
2383
2599
  if (poll.status === "complete") {
2384
2600
  window.clearInterval(pollTimer);
@@ -2410,7 +2626,7 @@ window.__ModuleLoader__.load({
2410
2626
  try {
2411
2627
  setBusy("apikey");
2412
2628
  setError(null);
2413
- const updated = await fetchApi$4("/login/apikey", {
2629
+ const updated = await fetchApi$6("/login/apikey", {
2414
2630
  method: "POST",
2415
2631
  body: JSON.stringify({ apiKey })
2416
2632
  });
@@ -2423,11 +2639,18 @@ window.__ModuleLoader__.load({
2423
2639
  setBusy(null);
2424
2640
  }
2425
2641
  };
2426
- const handleLogout = async () => {
2642
+ /** One account-level action on the shared pool card. */
2643
+ const handleAccountAction = async (action, accountId) => {
2427
2644
  try {
2428
- setBusy("logout");
2645
+ setBusy(`${action}-${accountId}`);
2429
2646
  setError(null);
2430
- const updated = await fetchApi$4("/logout", { method: "POST" });
2647
+ const updated = await fetchApi$6("/accounts", {
2648
+ method: "POST",
2649
+ body: JSON.stringify({
2650
+ action,
2651
+ accountId
2652
+ })
2653
+ });
2431
2654
  setStatus(updated);
2432
2655
  notifyChange();
2433
2656
  } catch (err) {
@@ -2436,11 +2659,53 @@ window.__ModuleLoader__.load({
2436
2659
  setBusy(null);
2437
2660
  }
2438
2661
  };
2662
+ const handleSetStrategy = async (strategy) => {
2663
+ try {
2664
+ setBusy("strategy");
2665
+ setError(null);
2666
+ const updated = await fetchApi$6("/accounts", {
2667
+ method: "POST",
2668
+ body: JSON.stringify({
2669
+ action: "strategy",
2670
+ strategy
2671
+ })
2672
+ });
2673
+ setStatus(updated);
2674
+ } catch (err) {
2675
+ setError(err instanceof Error ? err.message : String(err));
2676
+ } finally {
2677
+ setBusy(null);
2678
+ }
2679
+ };
2680
+ /**
2681
+ * Re-authorize one key.
2682
+ *
2683
+ * Nothing is deleted: the failure marker is cleared and the ordinary sign-in
2684
+ * flow runs again, so the account keeps its alias and place in the rotation.
2685
+ */
2686
+ const handleRelogin = (accountId) => {
2687
+ (async () => {
2688
+ try {
2689
+ const updated = await fetchApi$6("/accounts", {
2690
+ method: "POST",
2691
+ body: JSON.stringify({
2692
+ action: "relogin",
2693
+ accountId
2694
+ })
2695
+ });
2696
+ setStatus(updated);
2697
+ } catch (err) {
2698
+ setError(err instanceof Error ? err.message : String(err));
2699
+ return;
2700
+ }
2701
+ await handleLogin();
2702
+ })();
2703
+ };
2439
2704
  const handleRefreshQuota = async () => {
2440
2705
  try {
2441
2706
  setBusy("quota");
2442
2707
  setError(null);
2443
- const updated = await fetchApi$4("/quota", { method: "POST" });
2708
+ const updated = await fetchApi$6("/quota", { method: "POST" });
2444
2709
  setStatus(updated);
2445
2710
  } catch (err) {
2446
2711
  setError(err instanceof Error ? err.message : String(err));
@@ -2452,10 +2717,10 @@ window.__ModuleLoader__.load({
2452
2717
  try {
2453
2718
  setBusy("catalog");
2454
2719
  setError(null);
2455
- const updated = await fetchApi$4("/catalog/refresh", { method: "POST" });
2720
+ const updated = await fetchApi$6("/catalog/refresh", { method: "POST" });
2456
2721
  setStatus(updated);
2457
2722
  const drafts = {};
2458
- for (const model of updated.models) drafts[model.id] = formatCapacity$1(updated.contextWindowOverrides[model.id] || model.defaultContextWindow);
2723
+ for (const model of updated.models) drafts[model.id] = formatCapacity$2(updated.contextWindowOverrides[model.id] || model.defaultContextWindow);
2459
2724
  setContextDrafts(drafts);
2460
2725
  notifyChange();
2461
2726
  } catch (err) {
@@ -2468,7 +2733,7 @@ window.__ModuleLoader__.load({
2468
2733
  try {
2469
2734
  setBusy("connection");
2470
2735
  setError(null);
2471
- await fetchApi$4("/connection/test", { method: "POST" });
2736
+ await fetchApi$6("/connection/test", { method: "POST" });
2472
2737
  await loadStatus(true);
2473
2738
  } catch (err) {
2474
2739
  setError(err instanceof Error ? err.message : String(err));
@@ -2478,7 +2743,7 @@ window.__ModuleLoader__.load({
2478
2743
  };
2479
2744
  const applyEnabled = async (enabledModelIds) => {
2480
2745
  try {
2481
- const updated = await fetchApi$4("/models", {
2746
+ const updated = await fetchApi$6("/models", {
2482
2747
  method: "POST",
2483
2748
  body: JSON.stringify({ enabledModelIds })
2484
2749
  });
@@ -2494,7 +2759,7 @@ window.__ModuleLoader__.load({
2494
2759
  enabled
2495
2760
  } : prev);
2496
2761
  try {
2497
- const updated = await fetchApi$4("/settings", {
2762
+ const updated = await fetchApi$6("/settings", {
2498
2763
  method: "POST",
2499
2764
  body: JSON.stringify({ enabled })
2500
2765
  });
@@ -2517,7 +2782,7 @@ window.__ModuleLoader__.load({
2517
2782
  };
2518
2783
  const handleUpdateEffort = async (effort) => {
2519
2784
  try {
2520
- const updated = await fetchApi$4("/settings", {
2785
+ const updated = await fetchApi$6("/settings", {
2521
2786
  method: "POST",
2522
2787
  body: JSON.stringify({ defaultReasoningEffort: effort })
2523
2788
  });
@@ -2529,7 +2794,7 @@ window.__ModuleLoader__.load({
2529
2794
  };
2530
2795
  const handleSaveContextWindow = async (modelId) => {
2531
2796
  const raw = contextDrafts[modelId] || "";
2532
- const parsed = parsePositiveCapacity$1(raw);
2797
+ const parsed = parsePositiveCapacity$2(raw);
2533
2798
  if (parsed === null) {
2534
2799
  setError(`Invalid context capacity: ${raw}`);
2535
2800
  return;
@@ -2537,14 +2802,14 @@ window.__ModuleLoader__.load({
2537
2802
  try {
2538
2803
  setSavingModel(modelId);
2539
2804
  setError(null);
2540
- const updated = await fetchApi$4("/settings", {
2805
+ const updated = await fetchApi$6("/settings", {
2541
2806
  method: "POST",
2542
2807
  body: JSON.stringify({ contextWindowOverrides: { [modelId]: parsed } })
2543
2808
  });
2544
2809
  setStatus(updated);
2545
2810
  setContextDrafts((prev) => ({
2546
2811
  ...prev,
2547
- [modelId]: formatCapacity$1(parsed)
2812
+ [modelId]: formatCapacity$2(parsed)
2548
2813
  }));
2549
2814
  notifyChange();
2550
2815
  } catch (err) {
@@ -2561,155 +2826,57 @@ window.__ModuleLoader__.load({
2561
2826
  })
2562
2827
  });
2563
2828
  const quota = status?.quota;
2564
- const account = status?.account;
2829
+ status?.account;
2565
2830
  status?.models.filter((model) => model.enabled).length;
2566
2831
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2567
2832
  className: "dsha-page",
2568
2833
  children: [
2569
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2570
- className: "dsha-group",
2571
- children: [
2572
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2573
- className: "dsha-grouphead",
2574
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: status?.authenticated ? t.account : t.signedOut })
2575
- }),
2576
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2577
- className: "dsha-row",
2578
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2579
- className: "dsha-label",
2580
- children: status?.authenticated ? t.signedIn : t.signedOut
2581
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2582
- className: "dsha-value",
2583
- children: account?.email || account?.userName || "—"
2584
- })]
2585
- }),
2586
- status?.authenticated && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
2587
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2588
- className: "dsha-row",
2589
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2590
- className: "dsha-label",
2591
- children: t.plan
2592
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2593
- className: "dsha-value",
2594
- children: quota?.planName || account?.planLabel || t.planUnknown
2595
- })]
2596
- }),
2597
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2598
- className: "dsha-row",
2599
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2600
- className: "dsha-label",
2601
- children: t.keyName
2602
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2603
- className: "dsha-value",
2604
- children: account?.keyName || "—"
2605
- })]
2606
- }),
2607
- quota?.subscriptionStatus && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2608
- className: "dsha-row",
2609
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2610
- className: "dsha-label",
2611
- children: t.subscription
2612
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2613
- className: "dsha-value",
2614
- children: [quota.subscriptionStatus, quota.periodEndsAt !== null ? ` · ${t.renews} ${formatDate$1(quota.periodEndsAt)}` : ""]
2615
- })]
2616
- }),
2617
- account?.organizationName && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2618
- className: "dsha-row",
2619
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2620
- className: "dsha-label",
2621
- children: t.organization
2622
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2623
- className: "dsha-value",
2624
- children: account.organizationName
2625
- })]
2626
- }),
2627
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2628
- className: "dsha-row",
2629
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2630
- className: "dsha-label",
2631
- children: t.authenticatedAt
2632
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2633
- className: "dsha-value",
2634
- children: formatDate$1(account?.authenticatedAt)
2635
- })]
2636
- })
2637
- ] }),
2638
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2639
- className: "dsha-row",
2640
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2641
- className: "dsha-label",
2642
- children: t.storage
2643
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2644
- className: "dsha-value",
2645
- children: status?.storagePath || "—"
2646
- })]
2647
- }),
2648
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2649
- className: "dsha-notice",
2650
- children: t.storageNotice
2651
- }),
2652
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2653
- className: "dsha-actions",
2654
- children: !status?.authenticated ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2655
- className: "dsha-btn dsha-btn-primary",
2656
- disabled: busy !== null,
2657
- onClick: () => void handleLogin(),
2658
- children: busy === "login" ? loginProgress || t.signingIn : t.signIn
2659
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
2660
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2661
- className: "dsha-btn dsha-btn-primary",
2662
- disabled: busy !== null,
2663
- onClick: () => void handleLogin(),
2664
- children: busy === "login" ? loginProgress || t.signingIn : t.signInAgain
2665
- }),
2666
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2667
- className: "dsha-btn",
2668
- disabled: busy !== null,
2669
- onClick: () => void handleRefreshQuota(),
2670
- children: busy === "quota" ? t.refreshingQuota : t.refreshQuota
2671
- }),
2672
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2673
- className: "dsha-btn",
2674
- disabled: busy !== null,
2675
- onClick: () => void handleLogout(),
2676
- children: t.signOut
2677
- })
2678
- ] })
2679
- })
2680
- ]
2681
- }),
2682
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2683
- className: "dsha-group",
2684
- children: [
2685
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2686
- className: "dsha-grouphead",
2687
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t.apiKeySection })
2688
- }),
2689
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2690
- className: "dsha-muted",
2691
- children: t.apiKeyHint
2692
- }),
2693
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2694
- className: "dsha-capacity-control",
2695
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2696
- type: "password",
2697
- "aria-label": t.apiKeySection,
2698
- placeholder: t.apiKeyPlaceholder,
2699
- value: apiKey,
2700
- onChange: (event) => setApiKey(event.currentTarget.value),
2701
- onKeyDown: (event) => {
2702
- if (event.key === "Enter" && apiKey.trim() !== "") handleApiKey();
2703
- }
2704
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2705
- type: "button",
2706
- className: "dsha-context-save",
2707
- disabled: busy !== null || apiKey.trim() === "",
2708
- onClick: () => void handleApiKey(),
2709
- children: busy === "apikey" ? t.apiKeySaving : t.apiKeySave
2710
- })]
2711
- })
2712
- ]
2834
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(AccountPoolSection, {
2835
+ accounts: status?.accounts ?? [],
2836
+ activeAccountId: status?.activeAccountId,
2837
+ rotationStrategy: status?.rotationStrategy ?? "sequential",
2838
+ busy,
2839
+ labels: t,
2840
+ loginBusyLabel: busy === "login" ? loginProgress || t.signingIn : void 0,
2841
+ onLogin: () => void handleLogin(),
2842
+ onSetPrimary: (accountId) => void handleAccountAction("set-primary", accountId),
2843
+ onDelete: (accountId) => void handleAccountAction("delete", accountId),
2844
+ onClearCooldown: (accountId) => void handleAccountAction("clear-cooldown", accountId),
2845
+ onRelogin: (accountId) => handleRelogin(accountId),
2846
+ onSetStrategy: (strategy) => void handleSetStrategy(strategy),
2847
+ storageValue: status?.storagePath || "—",
2848
+ renderDetails: (entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [entry.keyName && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
2849
+ t.keyName,
2850
+ ": ",
2851
+ entry.keyName
2852
+ ] }), entry.planLabel && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
2853
+ t.plan,
2854
+ ": ",
2855
+ entry.planLabel
2856
+ ] })] }),
2857
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2858
+ className: "dsha-muted",
2859
+ style: { paddingTop: 12 },
2860
+ children: t.apiKeyHint
2861
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2862
+ className: "dsha-capacity-control",
2863
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2864
+ type: "password",
2865
+ "aria-label": t.apiKeySection,
2866
+ placeholder: t.apiKeyPlaceholder,
2867
+ value: apiKey,
2868
+ onChange: (event) => setApiKey(event.currentTarget.value),
2869
+ onKeyDown: (event) => {
2870
+ if (event.key === "Enter" && apiKey.trim() !== "") handleApiKey();
2871
+ }
2872
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2873
+ type: "button",
2874
+ className: "dsha-context-save",
2875
+ disabled: busy !== null || apiKey.trim() === "",
2876
+ onClick: () => void handleApiKey(),
2877
+ children: busy === "apikey" ? t.apiKeySaving : t.apiKeySave
2878
+ })]
2879
+ })]
2713
2880
  }),
2714
2881
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2715
2882
  className: "dsha-group",
@@ -2838,7 +3005,7 @@ window.__ModuleLoader__.load({
2838
3005
  children: t.defaultEffortAuto
2839
3006
  }), COMMAND_CODE_REASONING_EFFORTS.map((effort) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2840
3007
  value: effort,
2841
- children: EFFORT_LABELS[effort]
3008
+ children: EFFORT_LABELS$1[effort]
2842
3009
  }, effort))]
2843
3010
  })]
2844
3011
  })]
@@ -2932,7 +3099,7 @@ window.__ModuleLoader__.load({
2932
3099
  }),
2933
3100
  window.resetsAt !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2934
3101
  className: "dsha-meter-meta",
2935
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: ["Reset: ", formatReset$1(window.resetsAt)] })
3102
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: ["Reset: ", formatReset$2(window.resetsAt)] })
2936
3103
  })
2937
3104
  ]
2938
3105
  }, window.id)),
@@ -2957,7 +3124,7 @@ window.__ModuleLoader__.load({
2957
3124
  meter.used ?? "—",
2958
3125
  " / ",
2959
3126
  meter.limit ?? "—"
2960
- ] }), meter.resetsAt !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: ["Reset: ", formatReset$1(meter.resetsAt)] })]
3127
+ ] }), meter.resetsAt !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: ["Reset: ", formatReset$2(meter.resetsAt)] })]
2961
3128
  }) : meter.description !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2962
3129
  className: "dsha-meter-meta",
2963
3130
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: meter.description })
@@ -2996,7 +3163,8 @@ window.__ModuleLoader__.load({
2996
3163
  //#endregion
2997
3164
  //#region src/client/kimi-code/locales.ts
2998
3165
  const NS_KIMI_CODE = "dsh-kimi-code";
2999
- const zh$1 = {
3166
+ const zh$2 = {
3167
+ ...accountPoolZh,
3000
3168
  title: "Kimi Code",
3001
3169
  pageDesc: "登录 Kimi Code 订阅,管理模型、思考档位、上下文窗口与额度。",
3002
3170
  account: "账号",
@@ -3108,7 +3276,8 @@ window.__ModuleLoader__.load({
3108
3276
  requests: "次请求",
3109
3277
  tokens: "tokens"
3110
3278
  };
3111
- const en$1 = {
3279
+ const en$2 = {
3280
+ ...accountPoolEn,
3112
3281
  title: "Kimi Code",
3113
3282
  pageDesc: "Sign in to the Kimi Code subscription and manage models, thinking level, context windows, and quota.",
3114
3283
  account: "Account",
@@ -3220,11 +3389,11 @@ window.__ModuleLoader__.load({
3220
3389
  requests: "requests",
3221
3390
  tokens: "tokens"
3222
3391
  };
3223
- const dictionaries$1 = {
3224
- zh: zh$1,
3225
- "zh-CN": zh$1,
3226
- en: en$1,
3227
- "en-US": en$1
3392
+ const dictionaries$2 = {
3393
+ zh: zh$2,
3394
+ "zh-CN": zh$2,
3395
+ en: en$2,
3396
+ "en-US": en$2
3228
3397
  };
3229
3398
  //#endregion
3230
3399
  //#region src/client/kimi-code/KimiModelCapabilities.tsx
@@ -3237,7 +3406,7 @@ window.__ModuleLoader__.load({
3237
3406
  * Capabilities are now short tags in their own columns, with the per-model
3238
3407
  * detail kept in a hover title.
3239
3408
  */
3240
- const t = zh$1;
3409
+ const t = zh$2;
3241
3410
  function KimiModelCapabilities({ models }) {
3242
3411
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3243
3412
  className: "dsha-cap-table",
@@ -3281,9 +3450,9 @@ window.__ModuleLoader__.load({
3281
3450
  }
3282
3451
  //#endregion
3283
3452
  //#region src/client/kimi-code/KimiCodeSection.tsx
3284
- const API$3 = "/kimi-code/api";
3285
- async function fetchApi$3(path, options) {
3286
- const res = await fetch(`${API$3}${path}`, {
3453
+ const API$5 = "/kimi-code/api";
3454
+ async function fetchApi$5(path, options) {
3455
+ const res = await fetch(`${API$5}${path}`, {
3287
3456
  ...options,
3288
3457
  headers: {
3289
3458
  "Content-Type": "application/json",
@@ -3295,19 +3464,19 @@ window.__ModuleLoader__.load({
3295
3464
  return json.value;
3296
3465
  }
3297
3466
  /** Parse "1M", "512K", "200000" into a positive integer token count. */
3298
- function parsePositiveCapacity(value) {
3467
+ function parsePositiveCapacity$1(value) {
3299
3468
  const matched = value.trim().toLowerCase().replace(/[,_\s]/g, "").match(/^(\d+(?:\.\d+)?)(k|m)?$/);
3300
3469
  if (matched === null) return null;
3301
3470
  const multiplier = matched[2] === "m" ? 1e6 : matched[2] === "k" ? 1e3 : 1;
3302
3471
  const parsed = Number(matched[1]) * multiplier;
3303
3472
  return Number.isSafeInteger(parsed) && parsed >= 1 ? parsed : null;
3304
3473
  }
3305
- function formatCapacity(value) {
3474
+ function formatCapacity$1(value) {
3306
3475
  if (value >= 1e6 && value % 1e5 === 0) return `${value / 1e6}M`;
3307
3476
  if (value >= 1e3 && value % 1e3 === 0) return `${value / 1e3}K`;
3308
3477
  return String(value);
3309
3478
  }
3310
- function formatDate(ms) {
3479
+ function formatDate$1(ms) {
3311
3480
  if (ms === void 0 || ms === null || ms <= 0) return "—";
3312
3481
  try {
3313
3482
  return new Intl.DateTimeFormat(void 0, {
@@ -3318,7 +3487,7 @@ window.__ModuleLoader__.load({
3318
3487
  return "—";
3319
3488
  }
3320
3489
  }
3321
- function formatReset(resetsAt) {
3490
+ function formatReset$1(resetsAt) {
3322
3491
  if (resetsAt === void 0 || resetsAt === null || resetsAt <= 0) return "";
3323
3492
  const diff = resetsAt - Date.now();
3324
3493
  if (diff <= 0) return "now";
@@ -3343,10 +3512,10 @@ window.__ModuleLoader__.load({
3343
3512
  }
3344
3513
  }
3345
3514
  const EFFORT_LABEL = {
3346
- low: zh$1.effortLow,
3347
- high: zh$1.effortHigh,
3348
- max: zh$1.effortMax,
3349
- none: zh$1.effortNone
3515
+ low: zh$2.effortLow,
3516
+ high: zh$2.effortHigh,
3517
+ max: zh$2.effortMax,
3518
+ none: zh$2.effortNone
3350
3519
  };
3351
3520
  function KimiCodeSection({ onModelChange, loadModelDirectory }) {
3352
3521
  const [status, setStatus] = (0, react.useState)(null);
@@ -3358,7 +3527,7 @@ window.__ModuleLoader__.load({
3358
3527
  const [savingModel, setSavingModel] = (0, react.useState)(null);
3359
3528
  const [copied, setCopied] = (0, react.useState)(false);
3360
3529
  const [connectionNotice, setConnectionNotice] = (0, react.useState)(null);
3361
- const t = zh$1;
3530
+ const t = zh$2;
3362
3531
  const notifyChange = (0, react.useCallback)(() => {
3363
3532
  onModelChange?.();
3364
3533
  loadModelDirectory?.();
@@ -3366,10 +3535,10 @@ window.__ModuleLoader__.load({
3366
3535
  const loadStatus = (0, react.useCallback)(async (quiet = false) => {
3367
3536
  if (!quiet) setError(null);
3368
3537
  try {
3369
- const data = await fetchApi$3("/status");
3538
+ const data = await fetchApi$5("/status");
3370
3539
  setStatus(data);
3371
3540
  const drafts = {};
3372
- for (const model of data.models) drafts[model.id] = formatCapacity(data.contextWindowOverrides[model.id] || model.defaultContextWindow);
3541
+ for (const model of data.models) drafts[model.id] = formatCapacity$1(data.contextWindowOverrides[model.id] || model.defaultContextWindow);
3373
3542
  setContextDrafts(drafts);
3374
3543
  } catch (err) {
3375
3544
  if (!quiet) setError(err instanceof Error ? err.message : String(err));
@@ -3394,7 +3563,7 @@ window.__ModuleLoader__.load({
3394
3563
  const timer = window.setInterval(() => {
3395
3564
  (async () => {
3396
3565
  try {
3397
- const poll = await fetchApi$3("/login/status");
3566
+ const poll = await fetchApi$5("/login/status");
3398
3567
  setFlow(poll);
3399
3568
  if (poll.status === "complete") {
3400
3569
  await loadStatus();
@@ -3415,7 +3584,7 @@ window.__ModuleLoader__.load({
3415
3584
  setBusy("login");
3416
3585
  setError(null);
3417
3586
  setCopied(false);
3418
- const next = await fetchApi$3("/login", {
3587
+ const next = await fetchApi$5("/login", {
3419
3588
  method: "POST",
3420
3589
  body: JSON.stringify({})
3421
3590
  });
@@ -3428,7 +3597,7 @@ window.__ModuleLoader__.load({
3428
3597
  };
3429
3598
  const handleCancelLogin = async () => {
3430
3599
  try {
3431
- await fetchApi$3("/login/cancel", { method: "POST" });
3600
+ await fetchApi$5("/login/cancel", { method: "POST" });
3432
3601
  setFlow({ status: "idle" });
3433
3602
  } catch (err) {
3434
3603
  setError(err instanceof Error ? err.message : String(err));
@@ -3443,11 +3612,18 @@ window.__ModuleLoader__.load({
3443
3612
  window.setTimeout(() => setCopied(false), 2e3);
3444
3613
  } catch {}
3445
3614
  };
3446
- const handleLogout = async () => {
3615
+ /** One account-level action on the shared pool card. */
3616
+ const handleAccountAction = async (action, accountId) => {
3447
3617
  try {
3448
- setBusy("logout");
3618
+ setBusy(`${action}-${accountId}`);
3449
3619
  setError(null);
3450
- const updated = await fetchApi$3("/logout", { method: "POST" });
3620
+ const updated = await fetchApi$5("/accounts", {
3621
+ method: "POST",
3622
+ body: JSON.stringify({
3623
+ action,
3624
+ accountId
3625
+ })
3626
+ });
3451
3627
  setStatus(updated);
3452
3628
  setFlow({ status: "idle" });
3453
3629
  notifyChange();
@@ -3457,11 +3633,53 @@ window.__ModuleLoader__.load({
3457
3633
  setBusy(null);
3458
3634
  }
3459
3635
  };
3636
+ const handleSetStrategy = async (strategy) => {
3637
+ try {
3638
+ setBusy("strategy");
3639
+ setError(null);
3640
+ const updated = await fetchApi$5("/accounts", {
3641
+ method: "POST",
3642
+ body: JSON.stringify({
3643
+ action: "strategy",
3644
+ strategy
3645
+ })
3646
+ });
3647
+ setStatus(updated);
3648
+ } catch (err) {
3649
+ setError(err instanceof Error ? err.message : String(err));
3650
+ } finally {
3651
+ setBusy(null);
3652
+ }
3653
+ };
3654
+ /**
3655
+ * Re-authorize one account.
3656
+ *
3657
+ * Nothing is deleted: the failure marker is cleared and the device-code flow
3658
+ * runs again, so the account keeps its alias and place in the rotation.
3659
+ */
3660
+ const handleRelogin = (accountId) => {
3661
+ (async () => {
3662
+ try {
3663
+ const updated = await fetchApi$5("/accounts", {
3664
+ method: "POST",
3665
+ body: JSON.stringify({
3666
+ action: "relogin",
3667
+ accountId
3668
+ })
3669
+ });
3670
+ setStatus(updated);
3671
+ } catch (err) {
3672
+ setError(err instanceof Error ? err.message : String(err));
3673
+ return;
3674
+ }
3675
+ await handleLogin();
3676
+ })();
3677
+ };
3460
3678
  const handleRefreshQuota = async () => {
3461
3679
  try {
3462
3680
  setBusy("quota");
3463
3681
  setError(null);
3464
- const updated = await fetchApi$3("/quota", { method: "POST" });
3682
+ const updated = await fetchApi$5("/quota", { method: "POST" });
3465
3683
  setStatus(updated);
3466
3684
  } catch (err) {
3467
3685
  setError(err instanceof Error ? err.message : String(err));
@@ -3474,10 +3692,10 @@ window.__ModuleLoader__.load({
3474
3692
  try {
3475
3693
  setBusy("catalog");
3476
3694
  setError(null);
3477
- const updated = await fetchApi$3("/catalog/refresh", { method: "POST" });
3695
+ const updated = await fetchApi$5("/catalog/refresh", { method: "POST" });
3478
3696
  setStatus(updated);
3479
3697
  const drafts = {};
3480
- for (const model of updated.models) drafts[model.id] = formatCapacity(updated.contextWindowOverrides[model.id] || model.defaultContextWindow);
3698
+ for (const model of updated.models) drafts[model.id] = formatCapacity$1(updated.contextWindowOverrides[model.id] || model.defaultContextWindow);
3481
3699
  setContextDrafts(drafts);
3482
3700
  notifyChange();
3483
3701
  } catch (err) {
@@ -3491,7 +3709,7 @@ window.__ModuleLoader__.load({
3491
3709
  setBusy("connection");
3492
3710
  setError(null);
3493
3711
  setConnectionNotice(null);
3494
- const result = await fetchApi$3("/connection/test", { method: "POST" });
3712
+ const result = await fetchApi$5("/connection/test", { method: "POST" });
3495
3713
  setConnectionNotice(result.connected ? `${t.testSuccess} · ${result.latencyMs} ms` : t.testFailed);
3496
3714
  await loadStatus(true);
3497
3715
  } catch (err) {
@@ -3502,7 +3720,7 @@ window.__ModuleLoader__.load({
3502
3720
  };
3503
3721
  const applyEnabled = async (enabledModelIds) => {
3504
3722
  try {
3505
- const updated = await fetchApi$3("/models", {
3723
+ const updated = await fetchApi$5("/models", {
3506
3724
  method: "POST",
3507
3725
  body: JSON.stringify({ enabledModelIds })
3508
3726
  });
@@ -3518,7 +3736,7 @@ window.__ModuleLoader__.load({
3518
3736
  enabled
3519
3737
  } : prev);
3520
3738
  try {
3521
- const updated = await fetchApi$3("/settings", {
3739
+ const updated = await fetchApi$5("/settings", {
3522
3740
  method: "POST",
3523
3741
  body: JSON.stringify({ enabled })
3524
3742
  });
@@ -3541,7 +3759,7 @@ window.__ModuleLoader__.load({
3541
3759
  };
3542
3760
  const handleUpdateEffort = async (effort) => {
3543
3761
  try {
3544
- const updated = await fetchApi$3("/settings", {
3762
+ const updated = await fetchApi$5("/settings", {
3545
3763
  method: "POST",
3546
3764
  body: JSON.stringify({ defaultReasoningEffort: effort })
3547
3765
  });
@@ -3553,7 +3771,7 @@ window.__ModuleLoader__.load({
3553
3771
  };
3554
3772
  const handleSaveContextWindow = async (modelId) => {
3555
3773
  const raw = contextDrafts[modelId] || "";
3556
- const parsed = parsePositiveCapacity(raw);
3774
+ const parsed = parsePositiveCapacity$1(raw);
3557
3775
  if (parsed === null) {
3558
3776
  setError(`Invalid context capacity: ${raw}`);
3559
3777
  return;
@@ -3561,14 +3779,14 @@ window.__ModuleLoader__.load({
3561
3779
  try {
3562
3780
  setSavingModel(modelId);
3563
3781
  setError(null);
3564
- const updated = await fetchApi$3("/settings", {
3782
+ const updated = await fetchApi$5("/settings", {
3565
3783
  method: "POST",
3566
3784
  body: JSON.stringify({ contextWindowOverrides: { [modelId]: parsed } })
3567
3785
  });
3568
3786
  setStatus(updated);
3569
3787
  setContextDrafts((prev) => ({
3570
3788
  ...prev,
3571
- [modelId]: formatCapacity(parsed)
3789
+ [modelId]: formatCapacity$1(parsed)
3572
3790
  }));
3573
3791
  notifyChange();
3574
3792
  } catch (err) {
@@ -3585,171 +3803,96 @@ window.__ModuleLoader__.load({
3585
3803
  })
3586
3804
  });
3587
3805
  const quota = status?.quota;
3588
- const account = status?.account;
3806
+ status?.account;
3589
3807
  status?.models.filter((model) => model.enabled).length;
3590
3808
  const loginPending = flow?.status === "pending";
3591
3809
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3592
3810
  className: "dsha-page",
3593
3811
  children: [
3594
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
3595
- className: "dsha-group",
3596
- children: [
3597
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3598
- className: "dsha-grouphead",
3599
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: status?.authenticated ? t.account : t.signedOut })
3600
- }),
3601
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3602
- className: "dsha-row",
3603
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3604
- className: "dsha-label",
3605
- children: status?.authenticated ? t.signedIn : t.signedOut
3606
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3607
- className: "dsha-value",
3608
- children: account?.nickname || account?.email || account?.userId || "—"
3609
- })]
3610
- }),
3611
- status?.authenticated && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
3612
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3613
- className: "dsha-row",
3614
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3615
- className: "dsha-label",
3616
- children: t.plan
3617
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3618
- className: "dsha-value",
3619
- children: quota?.planName || account?.planName || t.planUnknown
3620
- })]
3812
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountPoolSection, {
3813
+ accounts: status?.accounts ?? [],
3814
+ activeAccountId: status?.activeAccountId,
3815
+ rotationStrategy: status?.rotationStrategy ?? "sequential",
3816
+ busy,
3817
+ labels: t,
3818
+ loginBusyLabel: loginPending ? t.waitingAuthorization : busy === "login" ? t.requestingCode : void 0,
3819
+ onLogin: () => void handleLogin(),
3820
+ onSetPrimary: (accountId) => void handleAccountAction("set-primary", accountId),
3821
+ onDelete: (accountId) => void handleAccountAction("delete", accountId),
3822
+ onClearCooldown: (accountId) => void handleAccountAction("clear-cooldown", accountId),
3823
+ onRelogin: (accountId) => handleRelogin(accountId),
3824
+ onSetStrategy: (strategy) => void handleSetStrategy(strategy),
3825
+ storageValue: status?.storagePath || "—",
3826
+ renderDetails: (entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
3827
+ entry.planName && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
3828
+ t.plan,
3829
+ ": ",
3830
+ entry.planName
3831
+ ] }),
3832
+ entry.region && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
3833
+ t.region,
3834
+ ": ",
3835
+ entry.region === "global" ? t.regionGlobal : t.regionMainland
3836
+ ] }),
3837
+ entry.email && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
3838
+ t.email,
3839
+ ": ",
3840
+ entry.email
3841
+ ] })
3842
+ ] }),
3843
+ children: loginPending && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3844
+ className: "dsha-device-box",
3845
+ children: [
3846
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t.deviceCodeSection }),
3847
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3848
+ className: "dsha-muted",
3849
+ children: t.deviceCodeHint
3621
3850
  }),
3622
- account?.email && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3623
- className: "dsha-row",
3851
+ flow?.userCode && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3852
+ className: "dsha-code-row",
3624
3853
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3625
- className: "dsha-label",
3626
- children: t.email
3627
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3628
- className: "dsha-value",
3629
- children: account.email
3854
+ className: "dsha-code",
3855
+ children: flow.userCode
3856
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3857
+ className: "dsha-btn",
3858
+ onClick: () => void handleCopyCode(),
3859
+ children: copied ? t.copied : t.copyCode
3630
3860
  })]
3631
3861
  }),
3862
+ flow?.verificationUriComplete && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
3863
+ className: "dsha-link",
3864
+ href: flow.verificationUriComplete,
3865
+ target: "_blank",
3866
+ rel: "noreferrer noopener",
3867
+ children: flow.verificationUriComplete
3868
+ }),
3632
3869
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3633
3870
  className: "dsha-row",
3634
3871
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3635
3872
  className: "dsha-label",
3636
- children: t.region
3873
+ children: t.codeExpires
3637
3874
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3638
3875
  className: "dsha-value",
3639
- children: status.region === "global" ? t.regionGlobal : t.regionMainland
3876
+ children: formatDate$1(flow?.expiresAt)
3640
3877
  })]
3641
3878
  }),
3642
3879
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3643
- className: "dsha-row",
3644
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3645
- className: "dsha-label",
3646
- children: t.authenticatedAt
3647
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3648
- className: "dsha-value",
3649
- children: formatDate(account?.authenticatedAt)
3650
- })]
3651
- })
3652
- ] }),
3653
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3654
- className: "dsha-row",
3655
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3656
- className: "dsha-label",
3657
- children: t.storage
3658
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3659
- className: "dsha-value",
3660
- children: status?.storagePath || "—"
3661
- })]
3662
- }),
3663
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3664
- className: "dsha-notice",
3665
- children: t.storageNotice
3666
- }),
3667
- status?.credentialsRejected && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3668
- className: "dsha-notice",
3669
- children: t.refreshRejected
3670
- }),
3671
- loginPending && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3672
- className: "dsha-device-box",
3673
- children: [
3674
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t.deviceCodeSection }),
3675
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3676
- className: "dsha-muted",
3677
- children: t.deviceCodeHint
3678
- }),
3679
- flow?.userCode && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3680
- className: "dsha-code-row",
3681
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3682
- className: "dsha-code",
3683
- children: flow.userCode
3684
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3685
- className: "dsha-btn",
3686
- onClick: () => void handleCopyCode(),
3687
- children: copied ? t.copied : t.copyCode
3688
- })]
3689
- }),
3690
- flow?.verificationUriComplete && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
3691
- className: "dsha-link",
3880
+ className: "dsha-actions",
3881
+ children: [flow?.verificationUriComplete && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
3882
+ className: "dsha-btn",
3692
3883
  href: flow.verificationUriComplete,
3693
3884
  target: "_blank",
3694
3885
  rel: "noreferrer noopener",
3695
- children: flow.verificationUriComplete
3696
- }),
3697
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3698
- className: "dsha-row",
3699
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3700
- className: "dsha-label",
3701
- children: t.codeExpires
3702
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3703
- className: "dsha-value",
3704
- children: formatDate(flow?.expiresAt)
3705
- })]
3706
- }),
3707
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3708
- className: "dsha-actions",
3709
- children: [flow?.verificationUriComplete && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
3710
- className: "dsha-btn",
3711
- href: flow.verificationUriComplete,
3712
- target: "_blank",
3713
- rel: "noreferrer noopener",
3714
- children: t.openSignInPage
3715
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3716
- className: "dsha-btn",
3717
- disabled: busy !== null,
3718
- onClick: () => void handleCancelLogin(),
3719
- children: t.cancelSignIn
3720
- })]
3721
- })
3722
- ]
3723
- }),
3724
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3725
- className: "dsha-actions",
3726
- children: !status?.authenticated ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3727
- className: "dsha-btn dsha-btn-primary",
3728
- disabled: busy !== null || loginPending,
3729
- onClick: () => void handleLogin(),
3730
- children: loginPending ? t.waitingAuthorization : busy === "login" ? t.requestingCode : t.signIn
3731
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
3732
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3733
- className: "dsha-btn dsha-btn-primary",
3734
- disabled: busy !== null || loginPending,
3735
- onClick: () => void handleLogin(),
3736
- children: loginPending ? t.waitingAuthorization : t.signInAgain
3737
- }),
3738
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3739
- className: "dsha-btn",
3740
- disabled: busy !== null,
3741
- onClick: () => void handleRefreshQuota(),
3742
- children: busy === "quota" ? t.refreshingQuota : t.refreshQuota
3743
- }),
3744
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3886
+ children: t.openSignInPage
3887
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3745
3888
  className: "dsha-btn",
3746
3889
  disabled: busy !== null,
3747
- onClick: () => void handleLogout(),
3748
- children: t.signOut
3749
- })
3750
- ] })
3751
- })
3752
- ]
3890
+ onClick: () => void handleCancelLogin(),
3891
+ children: t.cancelSignIn
3892
+ })]
3893
+ })
3894
+ ]
3895
+ })
3753
3896
  }),
3754
3897
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
3755
3898
  className: "dsha-group",
@@ -3882,37 +4025,1212 @@ window.__ModuleLoader__.load({
3882
4025
  children: t.unselectAll
3883
4026
  })]
3884
4027
  }),
3885
- status !== null && status.models.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(KimiModelCapabilities, { models: status.models })
3886
- ]
3887
- }),
3888
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
3889
- className: "dsha-group",
3890
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3891
- className: "dsha-grouphead",
3892
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t.enhanced })
3893
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3894
- className: "dsha-pref-row",
3895
- 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", {
4028
+ status !== null && status.models.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(KimiModelCapabilities, { models: status.models })
4029
+ ]
4030
+ }),
4031
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
4032
+ className: "dsha-group",
4033
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4034
+ className: "dsha-grouphead",
4035
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t.enhanced })
4036
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4037
+ className: "dsha-pref-row",
4038
+ 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", {
4039
+ className: "dsha-muted",
4040
+ children: t.defaultReasoningEffortHint
4041
+ })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
4042
+ className: "dsha-select",
4043
+ "aria-label": t.defaultReasoningEffort,
4044
+ value: status?.defaultReasoningEffort ?? "",
4045
+ disabled: busy !== null,
4046
+ onChange: (event) => {
4047
+ const value = event.currentTarget.value;
4048
+ handleUpdateEffort(value === "" ? null : value);
4049
+ },
4050
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
4051
+ value: "",
4052
+ children: t.defaultEffortAuto
4053
+ }), KIMI_CODE_REASONING_EFFORTS.map((effort) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
4054
+ value: effort,
4055
+ children: EFFORT_LABEL[effort]
4056
+ }, effort))]
4057
+ })]
4058
+ })]
4059
+ }),
4060
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
4061
+ className: "dsha-group",
4062
+ children: [
4063
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4064
+ className: "dsha-grouphead",
4065
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t.contextWindowSection })
4066
+ }),
4067
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4068
+ className: "dsha-muted",
4069
+ children: t.contextWindowHint
4070
+ }),
4071
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4072
+ className: "dsha-context-settings",
4073
+ children: status?.models.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4074
+ className: "dsha-context-row",
4075
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4076
+ title: model.id,
4077
+ children: [model.name, model.defaultContextWindow < model.contextWindow && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4078
+ className: "dsha-model-meta",
4079
+ children: formatCapacity$1(model.contextWindow)
4080
+ })]
4081
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4082
+ className: "dsha-capacity-control",
4083
+ children: [
4084
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
4085
+ type: "text",
4086
+ "aria-label": `${model.name} context window`,
4087
+ value: contextDrafts[model.id] ?? "",
4088
+ onChange: (event) => setContextDrafts({
4089
+ ...contextDrafts,
4090
+ [model.id]: event.currentTarget.value
4091
+ }),
4092
+ onKeyDown: (event) => {
4093
+ if (event.key === "Enter") handleSaveContextWindow(model.id);
4094
+ }
4095
+ }),
4096
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: t.tokens }),
4097
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4098
+ type: "button",
4099
+ className: "dsha-context-save",
4100
+ disabled: savingModel === model.id,
4101
+ onClick: () => void handleSaveContextWindow(model.id),
4102
+ children: savingModel === model.id ? t.saving : t.save
4103
+ })
4104
+ ]
4105
+ })]
4106
+ }, model.id))
4107
+ })
4108
+ ]
4109
+ }),
4110
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
4111
+ className: "dsha-group",
4112
+ children: [
4113
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4114
+ className: "dsha-grouphead",
4115
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t.quotaSection }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4116
+ className: "dsha-btn",
4117
+ disabled: busy !== null || !status?.authenticated,
4118
+ onClick: () => void handleRefreshQuota(),
4119
+ children: busy === "quota" ? t.refreshingQuota : t.refreshQuota
4120
+ })]
4121
+ }),
4122
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4123
+ className: "dsha-muted",
4124
+ children: t.quotaDesc
4125
+ }),
4126
+ status?.quotaError && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4127
+ className: "dsha-notice",
4128
+ children: status.quotaError
4129
+ }),
4130
+ !status?.authenticated ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4131
+ className: "dsha-empty",
4132
+ children: t.signedOut
4133
+ }) : quota === null || quota === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4134
+ className: "dsha-empty",
4135
+ children: busy === "quota" ? t.refreshingQuota : t.quotaEmpty
4136
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4137
+ className: "dsha-quota-card",
4138
+ children: [
4139
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4140
+ className: "dsha-quota-title",
4141
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t.quotaPlan }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: quota.planName || t.planUnknown })]
4142
+ }),
4143
+ quota.windows.map((window) => {
4144
+ const remaining = Math.max(0, 100 - window.usedPercent);
4145
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4146
+ className: "dsha-meter-wrap",
4147
+ children: [
4148
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4149
+ className: "dsha-meter-label",
4150
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: window.label }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("strong", { children: [remaining, "% left"] })]
4151
+ }),
4152
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4153
+ className: `dsha-meter ${remaining <= 10 ? "dsha-meter-cyan" : "dsha-meter-green"}`,
4154
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { style: { width: `${remaining}%` } })
4155
+ }),
4156
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4157
+ className: "dsha-meter-meta",
4158
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
4159
+ window.used ?? "—",
4160
+ " / ",
4161
+ window.limit ?? "—",
4162
+ " ",
4163
+ t.requests
4164
+ ] }), window.resetsAt !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t.resetAt.replace("{time}", formatReset$1(window.resetsAt)) })]
4165
+ })
4166
+ ]
4167
+ }, window.id);
4168
+ }),
4169
+ quota.extraUsage !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4170
+ className: "dsha-meter-wrap",
4171
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4172
+ className: "dsha-meter-label",
4173
+ 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) })]
4174
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4175
+ className: "dsha-meter-meta",
4176
+ 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: [
4177
+ t.quotaWalletLimit,
4178
+ ": ",
4179
+ formatMoney(quota.extraUsage.monthlyChargeLimitCents, quota.extraUsage.currency)
4180
+ ] })]
4181
+ })]
4182
+ }),
4183
+ quota.windows.length === 0 && quota.extraUsage === null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4184
+ className: "dsha-empty",
4185
+ children: t.quotaEmpty
4186
+ }),
4187
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4188
+ className: "dsha-timestamp",
4189
+ children: t.updatedAt.replace("{time}", new Date(quota.fetchedAt).toLocaleString())
4190
+ })
4191
+ ]
4192
+ })
4193
+ ]
4194
+ }),
4195
+ error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4196
+ className: "dsha-error",
4197
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: error })
4198
+ })
4199
+ ]
4200
+ });
4201
+ }
4202
+ //#endregion
4203
+ //#region src/shared/workbuddy-contracts.ts
4204
+ /** Every level this route can name, in escalating order (the card's sort key). */
4205
+ const WORKBUDDY_REASONING_EFFORTS = [
4206
+ "minimal",
4207
+ "low",
4208
+ "medium",
4209
+ "high",
4210
+ "xhigh",
4211
+ "max"
4212
+ ];
4213
+ //#endregion
4214
+ //#region src/client/workbuddy/locales.ts
4215
+ const NS_WORKBUDDY = "dsh-workbuddy";
4216
+ const zh$1 = {
4217
+ title: "WorkBuddy",
4218
+ pageDesc: "复用本机 CodeBuddy 桌面端登录态,管理 WorkBuddy(CodeBuddy 订阅)模型、上下文窗口与额度。",
4219
+ account: "账号",
4220
+ noAccounts: "暂无账号,点击「添加国区账号 / 添加国际区账号」完成浏览器授权,或登录 CodeBuddy 桌面端后重新扫描。",
4221
+ signedIn: "已找到凭据",
4222
+ signedOut: "未找到凭据",
4223
+ nickname: "昵称",
4224
+ uid: "账号 ID",
4225
+ uin: "UIN",
4226
+ region: "区域",
4227
+ regionCn: "国区",
4228
+ regionIntl: "国际区",
4229
+ accountType: "账号类型",
4230
+ backend: "后端地址",
4231
+ domain: "认证域名",
4232
+ tokenExpires: "登录态有效期",
4233
+ tokenExpired: "已过期",
4234
+ sourceFile: "凭据文件",
4235
+ authDirectory: "凭据目录",
4236
+ storage: "凭据存储",
4237
+ storageNotice: "桌面端账号仍读取 CodeBuddy 自己的凭据文件,插件不会删除这些文件;通过这里添加的账号保存在系统加密存储中(Windows DPAPI / macOS Keychain / Linux Secret Service),token 不进入浏览器。",
4238
+ notFoundTitle: "未找到 WorkBuddy 账号",
4239
+ notFoundHint: "可安装并登录 CodeBuddy 后重新扫描,也可以直接点击下方「添加国区账号」或「添加国际区账号」完成浏览器授权。",
4240
+ routeUnreachable: "无法读取 WorkBuddy 设置路由",
4241
+ routeUnreachableHint: "浏览器里加载的插件版本可能比 Host 进程里运行的新(客户端资源是现读磁盘的,Host 只在启动时加载)。请重启 DSH,让两端来自同一份构建。",
4242
+ rescan: "重新扫描",
4243
+ rescanning: "扫描中...",
4244
+ accounts: "本机凭据",
4245
+ accountsHint: "按账号地区分组。插件添加的账号可删除;CodeBuddy 桌面账号只能从本插件隐藏,绝不删除桌面端原凭据。点击账号会切换模型目录、额度和后续对话。",
4246
+ useAccount: "使用此账号",
4247
+ selectedAccount: "当前账号",
4248
+ addCnAccount: "添加国区账号",
4249
+ addIntlAccount: "添加国际区账号",
4250
+ authorizing: "授权中…",
4251
+ loginWaiting: "请在新打开的浏览器页面完成登录与授权。",
4252
+ loginFailed: "账号授权失败",
4253
+ pluginAccount: "插件账号",
4254
+ desktopAccount: "桌面账号",
4255
+ encryptedStorage: "系统加密存储",
4256
+ hiddenAccount: "已隐藏",
4257
+ hideAccount: "从插件隐藏",
4258
+ restoreAccount: "恢复显示",
4259
+ deleteAccount: "删除账号",
4260
+ deleteConfirm: "确定删除这个插件托管的账号吗?加密存储中的登录凭据会被移除。",
4261
+ connection: "连接",
4262
+ provider: "Provider",
4263
+ providerValue: "WorkBuddy Subscription · workbuddy-subscription",
4264
+ connectionState: "连接状态",
4265
+ connected: "已连接",
4266
+ untested: "未测试",
4267
+ testConnection: "测试连接",
4268
+ testingConnection: "测试中...",
4269
+ latency: "延迟",
4270
+ routeOwned: "模型路由由本插件提供",
4271
+ routeConflict: "模型路由已被其他 Provider 占用:{detail}。请在 DSH 设置中移除重复的 Provider 配置后重试。",
4272
+ modelsSection: "模型",
4273
+ enableProvider: "启用此供应商",
4274
+ modelsHint: "勾选后模型才会出现在对话页的模型选择列表中;未勾选或关闭上方开关可在列表中隐藏该供应商。目录与上下文窗口来自订阅网关的模型目录,并按账号所在区域过滤——跨区调用会被上游拒绝。",
4275
+ refreshCatalog: "刷新模型目录",
4276
+ refreshingCatalog: "刷新中...",
4277
+ selectAll: "全选",
4278
+ unselectAll: "全不选",
4279
+ imageSupport: "图片",
4280
+ textOnly: "纯文本",
4281
+ regionBadge: "区域",
4282
+ unavailableHere: "当前区域不可用",
4283
+ enhanced: "增强功能",
4284
+ defaultReasoningEffort: "默认思考深度",
4285
+ defaultReasoningEffortHint: "控制支持推理的模型在未单独指定时的思考强度。",
4286
+ defaultEffortAuto: "使用模型默认值",
4287
+ effortUnsupported: "当前模型不支持",
4288
+ noReasoningModels: "当前账号的模型都没有声明可用的思考档位,因此没有可选的默认档位。",
4289
+ defaultEffortKept: "已保存的档位不被当前任何模型支持,已保留但不会生效。",
4290
+ contextWindowSection: "模型上下文窗口",
4291
+ contextWindowHint: "默认值来自订阅网关的模型目录(每个模型的真实上下文上限);可覆盖为自定义容量,该值用于 DSH 的压缩与溢出判断,可输入 1M、512K、200K 等容量。",
4292
+ save: "保存",
4293
+ saved: "已保存",
4294
+ saving: "保存中...",
4295
+ quotaSection: "用量与配额",
4296
+ quotaDesc: "数据来自订阅的计费接口,页面可见时最多每 60 秒刷新一次。",
4297
+ refreshQuota: "刷新用量",
4298
+ refreshingQuota: "刷新中...",
4299
+ quotaEmpty: "暂无配额数据,点击「刷新用量」重试。",
4300
+ quotaPackage: "套餐",
4301
+ quotaRemaining: "剩余额度",
4302
+ quotaCycle: "本周期已用",
4303
+ quotaReset: "重置时间",
4304
+ updatedAt: "更新时间:{time}",
4305
+ loading: "加载中...",
4306
+ credits: "credits",
4307
+ composerLabel: "额度"
4308
+ };
4309
+ const en$1 = {
4310
+ title: "WorkBuddy",
4311
+ pageDesc: "Reuse the local CodeBuddy desktop sign-in to manage WorkBuddy (CodeBuddy subscription) models, context windows, and quota.",
4312
+ account: "Account",
4313
+ noAccounts: "No accounts yet. Add a China or international account, or sign in with the CodeBuddy desktop client and rescan.",
4314
+ signedIn: "Credential found",
4315
+ signedOut: "No credential found",
4316
+ nickname: "Nickname",
4317
+ uid: "Account ID",
4318
+ uin: "UIN",
4319
+ region: "Region",
4320
+ regionCn: "China",
4321
+ regionIntl: "International",
4322
+ accountType: "Account type",
4323
+ backend: "Backend",
4324
+ domain: "Auth domain",
4325
+ tokenExpires: "Sign-in valid until",
4326
+ tokenExpired: "Expired",
4327
+ sourceFile: "Credential file",
4328
+ authDirectory: "Credential directory",
4329
+ storage: "Credential storage",
4330
+ storageNotice: "Desktop accounts continue to use CodeBuddy’s credential files, which this plugin never deletes. Accounts added here use encrypted OS storage (Windows DPAPI, macOS Keychain, or Linux Secret Service); tokens never enter the browser.",
4331
+ notFoundTitle: "No WorkBuddy account found",
4332
+ notFoundHint: "Install and sign in to CodeBuddy and rescan, or use “Add China account” / “Add international account” below to authorize in the browser.",
4333
+ routeUnreachable: "The WorkBuddy settings route could not be read",
4334
+ routeUnreachableHint: "The plugin loaded in the browser may be newer than the one running in the Host: client assets are read from disk on demand, while the Host loads them only at startup. Restart DSH so both halves come from the same build.",
4335
+ rescan: "Rescan",
4336
+ rescanning: "Scanning...",
4337
+ accounts: "Local credentials",
4338
+ accountsHint: "Accounts are grouped by region. Plugin-managed accounts can be deleted; CodeBuddy desktop accounts can only be hidden from this plugin and their source files are never deleted. Selecting one switches models, quota, and chats.",
4339
+ useAccount: "Use this account",
4340
+ selectedAccount: "Selected account",
4341
+ addCnAccount: "Add China account",
4342
+ addIntlAccount: "Add international account",
4343
+ authorizing: "Authorizing…",
4344
+ loginWaiting: "Complete sign-in and authorization in the newly opened browser page.",
4345
+ loginFailed: "Account authorization failed",
4346
+ pluginAccount: "Plugin account",
4347
+ desktopAccount: "Desktop account",
4348
+ encryptedStorage: "Encrypted OS storage",
4349
+ hiddenAccount: "Hidden",
4350
+ hideAccount: "Hide from plugin",
4351
+ restoreAccount: "Restore",
4352
+ deleteAccount: "Delete account",
4353
+ deleteConfirm: "Delete this plugin-managed account? Its encrypted credential will be removed.",
4354
+ connection: "Connection",
4355
+ provider: "Provider",
4356
+ providerValue: "WorkBuddy Subscription · workbuddy-subscription",
4357
+ connectionState: "Connection state",
4358
+ connected: "Connected",
4359
+ untested: "Untested",
4360
+ testConnection: "Test connection",
4361
+ testingConnection: "Testing...",
4362
+ latency: "Latency",
4363
+ routeOwned: "This plugin serves the model route",
4364
+ routeConflict: "Another provider already owns the model route: {detail}. Remove the duplicate provider configuration in DSH settings and reload.",
4365
+ modelsSection: "Models",
4366
+ enableProvider: "Enable this provider",
4367
+ modelsHint: "Checked models appear in the DSH conversation model list. The catalog and its context windows come from the subscription gateway and are filtered by the account’s region — the upstream rejects a model asked of the wrong region.",
4368
+ refreshCatalog: "Refresh catalog",
4369
+ refreshingCatalog: "Refreshing...",
4370
+ selectAll: "Select all",
4371
+ unselectAll: "Deselect all",
4372
+ imageSupport: "Images",
4373
+ textOnly: "Text only",
4374
+ regionBadge: "Regions",
4375
+ unavailableHere: "Unavailable in this region",
4376
+ enhanced: "Enhanced features",
4377
+ defaultReasoningEffort: "Default reasoning effort",
4378
+ defaultReasoningEffortHint: "Reasoning effort for models that support it when the conversation does not specify one.",
4379
+ defaultEffortAuto: "Use the model default",
4380
+ effortUnsupported: "unsupported by the current models",
4381
+ noReasoningModels: "None of this account’s models declare a reasoning level, so there is no default to choose.",
4382
+ defaultEffortKept: "The saved level is not supported by any current model; it is kept but has no effect.",
4383
+ contextWindowSection: "Model context window",
4384
+ contextWindowHint: "Defaults come from the subscription gateway’s own catalog (each model’s real context cap); override one with the capacity DSH should use for compaction and overflow decisions (e.g., 1M, 512K, 200K).",
4385
+ save: "Save",
4386
+ saved: "Saved",
4387
+ saving: "Saving...",
4388
+ quotaSection: "Usage & quota",
4389
+ quotaDesc: "Read from the subscription billing endpoint; refreshes at most once every 60 seconds while visible.",
4390
+ refreshQuota: "Refresh quota",
4391
+ refreshingQuota: "Refreshing...",
4392
+ quotaEmpty: "No quota data yet — use “Refresh quota” to retry.",
4393
+ quotaPackage: "Package",
4394
+ quotaRemaining: "Remaining credits",
4395
+ quotaCycle: "Used this cycle",
4396
+ quotaReset: "Resets",
4397
+ updatedAt: "Updated at: {time}",
4398
+ loading: "Loading...",
4399
+ credits: "credits",
4400
+ composerLabel: "Quota"
4401
+ };
4402
+ const dictionaries$1 = {
4403
+ zh: zh$1,
4404
+ "zh-CN": zh$1,
4405
+ en: en$1,
4406
+ "en-US": en$1
4407
+ };
4408
+ //#endregion
4409
+ //#region src/client/workbuddy/WorkBuddySection.tsx
4410
+ /**
4411
+ * Display label per reasoning level. The locale dictionary only accepts flat
4412
+ * string values, so this nested table lives with the control that renders it.
4413
+ */
4414
+ const EFFORT_LABELS = {
4415
+ minimal: "Minimal",
4416
+ low: "Low",
4417
+ medium: "Medium",
4418
+ high: "High",
4419
+ xhigh: "X-High",
4420
+ max: "Max"
4421
+ };
4422
+ const API$4 = "/workbuddy/api";
4423
+ /**
4424
+ * Call one settings route and unwrap its `{ ok, value }` envelope.
4425
+ *
4426
+ * The response body is read as text first, because a route that is not mounted
4427
+ * answers with an empty body — and `res.json()` on that throws a bare
4428
+ * "Unexpected end of JSON input" that says nothing about what actually failed.
4429
+ * That is exactly what happens when the client bundle is newer than the Host
4430
+ * process (a stale Host has no route to answer), so the error is reported as
4431
+ * such instead of as a credential problem.
4432
+ */
4433
+ async function fetchApi$4(path, options) {
4434
+ const res = await fetch(`${API$4}${path}`, {
4435
+ ...options,
4436
+ headers: {
4437
+ "Content-Type": "application/json",
4438
+ ...options?.headers
4439
+ }
4440
+ });
4441
+ const text = await res.text();
4442
+ if (text.trim() === "") throw new Error(`The WorkBuddy settings route did not answer (HTTP ${res.status} ${res.statusText || "no body"}). The plugin loaded in the browser may be newer than the one running in the Host — restart DSH so both halves come from the same build.`);
4443
+ let json;
4444
+ try {
4445
+ json = JSON.parse(text);
4446
+ } catch {
4447
+ throw new Error(`The WorkBuddy settings route returned a non-JSON response (HTTP ${res.status}): ${text.slice(0, 200)}`);
4448
+ }
4449
+ if (!res.ok || json.ok !== true) throw new Error(json.error || `HTTP ${res.status}`);
4450
+ return json.value;
4451
+ }
4452
+ /** Parse "1M", "512K", "200000" into a positive integer token count. */
4453
+ function parsePositiveCapacity(value) {
4454
+ const matched = value.trim().toLowerCase().replace(/[,_\s]/g, "").match(/^(\d+(?:\.\d+)?)(k|m)?$/);
4455
+ if (matched === null) return null;
4456
+ const multiplier = matched[2] === "m" ? 1e6 : matched[2] === "k" ? 1e3 : 1;
4457
+ const parsed = Number(matched[1]) * multiplier;
4458
+ return Number.isSafeInteger(parsed) && parsed >= 1 ? parsed : null;
4459
+ }
4460
+ function formatCapacity(value) {
4461
+ if (value >= 1e6 && value % 1e5 === 0) return `${value / 1e6}M`;
4462
+ if (value >= 1e3 && value % 1e3 === 0) return `${value / 1e3}K`;
4463
+ return String(value);
4464
+ }
4465
+ function formatDate(ms) {
4466
+ if (ms === void 0 || ms === null || ms <= 0) return "—";
4467
+ try {
4468
+ return new Intl.DateTimeFormat(void 0, {
4469
+ dateStyle: "medium",
4470
+ timeStyle: "short"
4471
+ }).format(ms);
4472
+ } catch {
4473
+ return "—";
4474
+ }
4475
+ }
4476
+ function formatReset(resetsAt) {
4477
+ if (resetsAt === void 0 || resetsAt === null || resetsAt <= 0) return "";
4478
+ const diff = resetsAt - Date.now();
4479
+ if (diff <= 0) return "now";
4480
+ const mins = Math.floor(diff / 6e4);
4481
+ const hours = Math.floor(mins / 60);
4482
+ const days = Math.floor(hours / 24);
4483
+ if (days > 0) return `${days}d ${hours % 24}h`;
4484
+ if (hours > 0) return `${hours}h ${mins % 60}m`;
4485
+ return `${mins}m`;
4486
+ }
4487
+ /** Mask a UIN so the card shows identity without publishing the full number. */
4488
+ function maskUin(uin) {
4489
+ if (uin === void 0 || uin === null || uin === "") return "—";
4490
+ if (uin.length <= 6) return uin;
4491
+ return `${uin.slice(0, 4)}****${uin.slice(-2)}`;
4492
+ }
4493
+ /** Shorten an absolute credential path to its file name for display. */
4494
+ function displayFile(file) {
4495
+ if (file === void 0 || file === null || file === "") return "—";
4496
+ const parts = file.split(/[\\/]/);
4497
+ return parts[parts.length - 1] || file;
4498
+ }
4499
+ /**
4500
+ * Reasoning levels the default-effort control may offer.
4501
+ *
4502
+ * The levels come from what this account's models actually declare — never from
4503
+ * the shipped enum alone. The setting is one global default applied to whichever
4504
+ * model a conversation uses, so the list is their union; each entry carries the
4505
+ * models behind it, and a level only some models accept is labelled as such.
4506
+ * Order follows {@link WORKBUDDY_REASONING_EFFORTS}, which is the escalating
4507
+ * order the upstream ladder uses.
4508
+ */
4509
+ function reasoningEffortChoices(models) {
4510
+ const declared = /* @__PURE__ */ new Map();
4511
+ for (const model of models) for (const effort of model.reasoningEfforts ?? []) {
4512
+ const names = declared.get(effort);
4513
+ if (names === void 0) declared.set(effort, [model.name]);
4514
+ else names.push(model.name);
4515
+ }
4516
+ return WORKBUDDY_REASONING_EFFORTS.filter((effort) => declared.has(effort)).map((effort) => ({
4517
+ value: effort,
4518
+ models: declared.get(effort)
4519
+ }));
4520
+ }
4521
+ /**
4522
+ * Whether a saved default is unusable for every model on this account.
4523
+ *
4524
+ * The adapter drops a level the chosen model does not accept instead of sending
4525
+ * it, so a value that no model declares silently does nothing. Reporting it is
4526
+ * what keeps that from looking like the setting was applied.
4527
+ */
4528
+ function unsupportedReasoningEffort(configured, models) {
4529
+ if (configured === null || configured === void 0) return null;
4530
+ return models.some((model) => (model.reasoningEfforts ?? []).includes(configured)) ? null : configured;
4531
+ }
4532
+ /** Defend against a response that omits the arrays the card renders. */
4533
+ function normalizeStatus(data) {
4534
+ return {
4535
+ ...data,
4536
+ models: Array.isArray(data?.models) ? data.models : [],
4537
+ contextWindowOverrides: data?.contextWindowOverrides ?? {},
4538
+ accounts: Array.isArray(data?.accounts) ? data.accounts : []
4539
+ };
4540
+ }
4541
+ /**
4542
+ * The shared card's label set, taken from this tab's dictionary.
4543
+ *
4544
+ * Wording is the shared one so the five provider tabs cannot drift apart; the
4545
+ * couple of WorkBuddy-specific words are supplied here.
4546
+ */
4547
+ function accountPoolLabels(t) {
4548
+ return {
4549
+ ...accountPoolZh,
4550
+ accountPool: t.accounts,
4551
+ noAccounts: t.noAccounts,
4552
+ storage: t.storage,
4553
+ storageNotice: t.storageNotice,
4554
+ deleteAccount: t.deleteAccount,
4555
+ email: t.nickname,
4556
+ accountId: t.uid
4557
+ };
4558
+ }
4559
+ function WorkBuddySection({ onModelChange, loadModelDirectory }) {
4560
+ const [status, setStatus] = (0, react.useState)(null);
4561
+ const [loading, setLoading] = (0, react.useState)(true);
4562
+ const [busy, setBusy] = (0, react.useState)(null);
4563
+ const [error, setError] = (0, react.useState)(null);
4564
+ const [statusFailed, setStatusFailed] = (0, react.useState)(false);
4565
+ const [connection, setConnection] = (0, react.useState)(null);
4566
+ const [accounts, setAccounts] = (0, react.useState)(null);
4567
+ const [loginProgress, setLoginProgress] = (0, react.useState)(null);
4568
+ const loginIntervalRef = (0, react.useRef)(null);
4569
+ const loginTimeoutRef = (0, react.useRef)(null);
4570
+ const [contextDrafts, setContextDrafts] = (0, react.useState)({});
4571
+ const [savingModel, setSavingModel] = (0, react.useState)(null);
4572
+ const t = zh$1;
4573
+ const notifyChange = (0, react.useCallback)(() => {
4574
+ onModelChange?.();
4575
+ loadModelDirectory?.();
4576
+ }, [onModelChange, loadModelDirectory]);
4577
+ const effortChoices = (0, react.useMemo)(() => reasoningEffortChoices(status?.models ?? []), [status?.models]);
4578
+ const allModels = status?.models ?? [];
4579
+ const poolAccounts = (0, react.useMemo)(() => Array.isArray(status?.accounts) ? status.accounts : [], [status?.accounts]);
4580
+ const strayEffort = unsupportedReasoningEffort(status?.defaultReasoningEffort, allModels);
4581
+ const loadStatus = (0, react.useCallback)(async (quiet = false) => {
4582
+ if (!quiet) setError(null);
4583
+ try {
4584
+ const normalized = normalizeStatus(await fetchApi$4("/status"));
4585
+ setStatus(normalized);
4586
+ setStatusFailed(false);
4587
+ const drafts = {};
4588
+ for (const model of normalized.models) drafts[model.id] = formatCapacity(normalized.contextWindowOverrides[model.id] || model.defaultContextWindow);
4589
+ setContextDrafts(drafts);
4590
+ } catch (err) {
4591
+ setStatusFailed(true);
4592
+ if (!quiet) setError(err instanceof Error ? err.message : String(err));
4593
+ } finally {
4594
+ setLoading(false);
4595
+ }
4596
+ }, []);
4597
+ const loadAccounts = (0, react.useCallback)(async () => {
4598
+ try {
4599
+ const payload = await fetchApi$4("/accounts");
4600
+ setAccounts({
4601
+ authDirectory: payload?.authDirectory ?? "",
4602
+ managedStoragePath: payload?.managedStoragePath,
4603
+ accounts: Array.isArray(payload?.accounts) ? payload.accounts : []
4604
+ });
4605
+ } catch {}
4606
+ }, []);
4607
+ (0, react.useEffect)(() => {
4608
+ loadStatus();
4609
+ loadAccounts();
4610
+ const refreshWhenVisible = () => {
4611
+ if (document.visibilityState === "visible") {
4612
+ loadStatus(true);
4613
+ loadAccounts();
4614
+ }
4615
+ };
4616
+ document.addEventListener("visibilitychange", refreshWhenVisible);
4617
+ return () => {
4618
+ document.removeEventListener("visibilitychange", refreshWhenVisible);
4619
+ if (loginIntervalRef.current !== null) window.clearInterval(loginIntervalRef.current);
4620
+ if (loginTimeoutRef.current !== null) window.clearTimeout(loginTimeoutRef.current);
4621
+ };
4622
+ }, [loadStatus, loadAccounts]);
4623
+ const handleRescan = async () => {
4624
+ try {
4625
+ setBusy("rescan");
4626
+ setError(null);
4627
+ const updated = await fetchApi$4("/rescan", { method: "POST" });
4628
+ setStatus(updated);
4629
+ await loadAccounts();
4630
+ notifyChange();
4631
+ } catch (err) {
4632
+ setError(err instanceof Error ? err.message : String(err));
4633
+ } finally {
4634
+ setBusy(null);
4635
+ }
4636
+ };
4637
+ const handleAddAccount = async (region) => {
4638
+ try {
4639
+ setBusy(`login:${region}`);
4640
+ setError(null);
4641
+ setLoginProgress(t.loginWaiting);
4642
+ const flow = await fetchApi$4("/accounts/login", {
4643
+ method: "POST",
4644
+ body: JSON.stringify({ region })
4645
+ });
4646
+ if (flow.authUrl) window.open(flow.authUrl, "_blank", "noopener,noreferrer");
4647
+ if (loginIntervalRef.current !== null) window.clearInterval(loginIntervalRef.current);
4648
+ if (loginTimeoutRef.current !== null) window.clearTimeout(loginTimeoutRef.current);
4649
+ const timer = window.setInterval(() => {
4650
+ (async () => {
4651
+ try {
4652
+ const poll = await fetchApi$4("/accounts/login/status");
4653
+ if (poll.progress) setLoginProgress(poll.progress);
4654
+ if (poll.status === "complete") {
4655
+ window.clearInterval(timer);
4656
+ loginIntervalRef.current = null;
4657
+ if (loginTimeoutRef.current !== null) window.clearTimeout(loginTimeoutRef.current);
4658
+ loginTimeoutRef.current = null;
4659
+ setBusy(null);
4660
+ setLoginProgress(null);
4661
+ if (poll.accountId) await fetchApi$4("/settings", {
4662
+ method: "POST",
4663
+ body: JSON.stringify({ selectedAccountId: poll.accountId })
4664
+ });
4665
+ await loadStatus();
4666
+ await loadAccounts();
4667
+ notifyChange();
4668
+ } else if (poll.status === "error") {
4669
+ window.clearInterval(timer);
4670
+ loginIntervalRef.current = null;
4671
+ if (loginTimeoutRef.current !== null) window.clearTimeout(loginTimeoutRef.current);
4672
+ loginTimeoutRef.current = null;
4673
+ setBusy(null);
4674
+ setLoginProgress(null);
4675
+ setError(poll.error || t.loginFailed);
4676
+ }
4677
+ } catch {}
4678
+ })();
4679
+ }, 1500);
4680
+ loginIntervalRef.current = timer;
4681
+ loginTimeoutRef.current = window.setTimeout(() => {
4682
+ window.clearInterval(timer);
4683
+ loginIntervalRef.current = null;
4684
+ loginTimeoutRef.current = null;
4685
+ setBusy(null);
4686
+ setLoginProgress(null);
4687
+ }, 300 * 1e3);
4688
+ } catch (err) {
4689
+ setBusy(null);
4690
+ setLoginProgress(null);
4691
+ setError(err instanceof Error ? err.message : String(err));
4692
+ }
4693
+ };
4694
+ /**
4695
+ * One account action, whether it is a pool action (`set-primary`, `hide`) or a
4696
+ * credential action (`delete`). The host route dispatches on `action`.
4697
+ */
4698
+ const handlePoolAction = async (action, accountId) => {
4699
+ try {
4700
+ setBusy(`${action}:${accountId}`);
4701
+ setError(null);
4702
+ const updated = await fetchApi$4("/accounts/action", {
4703
+ method: "POST",
4704
+ body: JSON.stringify({
4705
+ action,
4706
+ accountId
4707
+ })
4708
+ });
4709
+ setStatus(normalizeStatus(updated));
4710
+ await loadAccounts();
4711
+ notifyChange();
4712
+ } catch (err) {
4713
+ setError(err instanceof Error ? err.message : String(err));
4714
+ } finally {
4715
+ setBusy(null);
4716
+ }
4717
+ };
4718
+ const handleDeleteAccount = async (accountId) => {
4719
+ if (!window.confirm(t.deleteConfirm)) return;
4720
+ await handlePoolAction("delete", accountId);
4721
+ };
4722
+ const handleSetStrategy = async (strategy) => {
4723
+ try {
4724
+ setBusy("strategy");
4725
+ setError(null);
4726
+ const updated = await fetchApi$4("/accounts/strategy", {
4727
+ method: "POST",
4728
+ body: JSON.stringify({ strategy })
4729
+ });
4730
+ setStatus(normalizeStatus(updated));
4731
+ await loadAccounts();
4732
+ } catch (err) {
4733
+ setError(err instanceof Error ? err.message : String(err));
4734
+ } finally {
4735
+ setBusy(null);
4736
+ }
4737
+ };
4738
+ const handleTestConnection = async () => {
4739
+ try {
4740
+ setBusy("connection");
4741
+ setError(null);
4742
+ setConnection(await fetchApi$4("/connection/test", { method: "POST" }));
4743
+ } catch (err) {
4744
+ setError(err instanceof Error ? err.message : String(err));
4745
+ } finally {
4746
+ setBusy(null);
4747
+ }
4748
+ };
4749
+ const handleRefreshQuota = async () => {
4750
+ try {
4751
+ setBusy("quota");
4752
+ setError(null);
4753
+ const updated = await fetchApi$4("/quota", { method: "POST" });
4754
+ setStatus(updated);
4755
+ } catch (err) {
4756
+ setError(err instanceof Error ? err.message : String(err));
4757
+ } finally {
4758
+ setBusy(null);
4759
+ }
4760
+ };
4761
+ const toggleEnabled = async (enabled) => {
4762
+ try {
4763
+ setBusy("enabled");
4764
+ setError(null);
4765
+ const updated = await fetchApi$4("/settings", {
4766
+ method: "POST",
4767
+ body: JSON.stringify({ enabled })
4768
+ });
4769
+ setStatus(updated);
4770
+ notifyChange();
4771
+ } catch (err) {
4772
+ setError(err instanceof Error ? err.message : String(err));
4773
+ } finally {
4774
+ setBusy(null);
4775
+ }
4776
+ };
4777
+ const toggleModel = async (modelId, enabled) => {
4778
+ if (status === null) return;
4779
+ const current = status.models.filter((model) => model.enabled).map((model) => model.id);
4780
+ const next = enabled ? [.../* @__PURE__ */ new Set([...current, modelId])] : current.filter((id) => id !== modelId);
4781
+ try {
4782
+ setBusy(`model:${modelId}`);
4783
+ setError(null);
4784
+ const updated = await fetchApi$4("/settings", {
4785
+ method: "POST",
4786
+ body: JSON.stringify({ enabledModelIds: next })
4787
+ });
4788
+ setStatus(updated);
4789
+ notifyChange();
4790
+ } catch (err) {
4791
+ setError(err instanceof Error ? err.message : String(err));
4792
+ } finally {
4793
+ setBusy(null);
4794
+ }
4795
+ };
4796
+ const setAllModels = async (enabled) => {
4797
+ if (status === null) return;
4798
+ try {
4799
+ setBusy("models-all");
4800
+ setError(null);
4801
+ const updated = await fetchApi$4("/settings", {
4802
+ method: "POST",
4803
+ body: JSON.stringify({ enabledModelIds: enabled ? status.models.map((model) => model.id) : [] })
4804
+ });
4805
+ setStatus(updated);
4806
+ notifyChange();
4807
+ } catch (err) {
4808
+ setError(err instanceof Error ? err.message : String(err));
4809
+ } finally {
4810
+ setBusy(null);
4811
+ }
4812
+ };
4813
+ const handleUpdateEffort = async (effort) => {
4814
+ try {
4815
+ setBusy("effort");
4816
+ setError(null);
4817
+ const updated = await fetchApi$4("/settings", {
4818
+ method: "POST",
4819
+ body: JSON.stringify({ defaultReasoningEffort: effort })
4820
+ });
4821
+ setStatus(updated);
4822
+ notifyChange();
4823
+ } catch (err) {
4824
+ setError(err instanceof Error ? err.message : String(err));
4825
+ } finally {
4826
+ setBusy(null);
4827
+ }
4828
+ };
4829
+ const handleRefreshCatalog = async () => {
4830
+ try {
4831
+ setBusy("catalog");
4832
+ setError(null);
4833
+ const updated = await fetchApi$4("/catalog/refresh", { method: "POST" });
4834
+ const normalized = {
4835
+ ...updated,
4836
+ models: Array.isArray(updated?.models) ? updated.models : [],
4837
+ contextWindowOverrides: updated?.contextWindowOverrides ?? {}
4838
+ };
4839
+ setStatus(normalized);
4840
+ const drafts = {};
4841
+ for (const model of normalized.models) drafts[model.id] = formatCapacity(normalized.contextWindowOverrides[model.id] || model.defaultContextWindow);
4842
+ setContextDrafts(drafts);
4843
+ notifyChange();
4844
+ } catch (err) {
4845
+ setError(err instanceof Error ? err.message : String(err));
4846
+ } finally {
4847
+ setBusy(null);
4848
+ }
4849
+ };
4850
+ const handleSaveContextWindow = async (modelId) => {
4851
+ const raw = contextDrafts[modelId] || "";
4852
+ const parsed = parsePositiveCapacity(raw);
4853
+ if (parsed === null) {
4854
+ setError(`Invalid context capacity: ${raw}`);
4855
+ return;
4856
+ }
4857
+ try {
4858
+ setSavingModel(modelId);
4859
+ setError(null);
4860
+ const updated = await fetchApi$4("/settings", {
4861
+ method: "POST",
4862
+ body: JSON.stringify({ contextWindowOverrides: { [modelId]: parsed } })
4863
+ });
4864
+ setStatus(updated);
4865
+ setContextDrafts((prev) => ({
4866
+ ...prev,
4867
+ [modelId]: formatCapacity(parsed)
4868
+ }));
4869
+ notifyChange();
4870
+ } catch (err) {
4871
+ setError(err instanceof Error ? err.message : String(err));
4872
+ } finally {
4873
+ setSavingModel(null);
4874
+ }
4875
+ };
4876
+ if (loading) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4877
+ className: "dsha-page",
4878
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4879
+ className: "dsha-empty",
4880
+ children: t.loading
4881
+ })
4882
+ });
4883
+ const quota = status?.quota;
4884
+ const account = status?.account;
4885
+ const authenticated = status?.authenticated === true;
4886
+ const unreachable = statusFailed && status === null;
4887
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4888
+ className: "dsha-page",
4889
+ children: [
4890
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
4891
+ className: "dsha-group",
4892
+ children: [
4893
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4894
+ className: "dsha-grouphead",
4895
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: authenticated ? t.account : t.signedOut }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4896
+ className: "dsha-btn",
4897
+ disabled: busy !== null,
4898
+ onClick: () => void handleRescan(),
4899
+ children: busy === "rescan" ? t.rescanning : t.rescan
4900
+ })]
4901
+ }),
4902
+ unreachable ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4903
+ className: "dsha-empty",
4904
+ children: t.routeUnreachable
4905
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4906
+ className: "dsha-notice",
4907
+ children: t.routeUnreachableHint
4908
+ })] }) : !authenticated ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4909
+ className: "dsha-empty",
4910
+ children: t.notFoundTitle
4911
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4912
+ className: "dsha-notice",
4913
+ children: t.notFoundHint
4914
+ })] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
4915
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4916
+ className: "dsha-row",
4917
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4918
+ className: "dsha-label",
4919
+ children: t.nickname
4920
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4921
+ className: "dsha-value",
4922
+ children: account?.nickname || "—"
4923
+ })]
4924
+ }),
4925
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4926
+ className: "dsha-row",
4927
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4928
+ className: "dsha-label",
4929
+ children: t.uid
4930
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4931
+ className: "dsha-value dshwb-mono",
4932
+ children: account?.uid || "—"
4933
+ })]
4934
+ }),
4935
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4936
+ className: "dsha-row",
4937
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4938
+ className: "dsha-label",
4939
+ children: t.uin
4940
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4941
+ className: "dsha-value dshwb-mono",
4942
+ children: maskUin(account?.uin)
4943
+ })]
4944
+ }),
4945
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4946
+ className: "dsha-row",
4947
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4948
+ className: "dsha-label",
4949
+ children: t.region
4950
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4951
+ className: "dsha-value",
4952
+ children: [account?.region === "intl" ? t.regionIntl : t.regionCn, account?.accountType ? ` · ${account.accountType}` : ""]
4953
+ })]
4954
+ }),
4955
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4956
+ className: "dsha-row",
4957
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4958
+ className: "dsha-label",
4959
+ children: t.domain
4960
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4961
+ className: "dsha-value dshwb-mono",
4962
+ children: account?.domain || "—"
4963
+ })]
4964
+ }),
4965
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4966
+ className: "dsha-row",
4967
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4968
+ className: "dsha-label",
4969
+ children: t.backend
4970
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4971
+ className: "dsha-value dshwb-mono",
4972
+ children: account?.backend || "—"
4973
+ })]
4974
+ }),
4975
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4976
+ className: "dsha-row",
4977
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4978
+ className: "dsha-label",
4979
+ children: t.tokenExpires
4980
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4981
+ className: "dsha-value",
4982
+ children: account?.expiresAt === null || account?.expiresAt === void 0 ? "—" : account.expiresAt <= Date.now() ? t.tokenExpired : formatDate(account.expiresAt)
4983
+ })]
4984
+ }),
4985
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4986
+ className: "dsha-row",
4987
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4988
+ className: "dsha-label",
4989
+ children: t.sourceFile
4990
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4991
+ className: "dsha-value dshwb-mono",
4992
+ title: account?.sourceFile ?? status?.managedStoragePath ?? "",
4993
+ children: account?.source === "managed" ? t.encryptedStorage : displayFile(account?.sourceFile)
4994
+ })]
4995
+ })
4996
+ ] }),
4997
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4998
+ className: "dsha-row",
4999
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5000
+ className: "dsha-label",
5001
+ children: t.authDirectory
5002
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5003
+ className: "dsha-value dshwb-mono",
5004
+ children: status?.authDirectory || "—"
5005
+ })]
5006
+ }),
5007
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5008
+ className: "dsha-notice",
5009
+ children: t.storageNotice
5010
+ })
5011
+ ]
5012
+ }),
5013
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountPoolSection, {
5014
+ accounts: poolAccounts,
5015
+ activeAccountId: status?.activeAccountId,
5016
+ rotationStrategy: status?.rotationStrategy ?? "sequential",
5017
+ labels: accountPoolLabels(t),
5018
+ busy,
5019
+ onLogin: () => void handleAddAccount("cn"),
5020
+ onSetPrimary: (id) => void handlePoolAction("set-primary", id),
5021
+ onDelete: (id) => void handleDeleteAccount(id),
5022
+ onClearCooldown: (id) => void handlePoolAction("clear-cooldown", id),
5023
+ onRelogin: (id) => void handlePoolAction("relogin", id),
5024
+ onSetStrategy: (strategy) => void handleSetStrategy(strategy),
5025
+ storageValue: status?.managedStoragePath,
5026
+ renderLoginActions: () => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5027
+ className: "dsha-account-add-actions",
5028
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5029
+ className: "dsha-btn",
5030
+ disabled: busy !== null,
5031
+ onClick: () => void handleAddAccount("cn"),
5032
+ children: busy === "login:cn" ? t.authorizing : t.addCnAccount
5033
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5034
+ className: "dsha-btn",
5035
+ disabled: busy !== null,
5036
+ onClick: () => void handleAddAccount("intl"),
5037
+ children: busy === "login:intl" ? t.authorizing : t.addIntlAccount
5038
+ })]
5039
+ }),
5040
+ renderAccountActions: (candidate) => candidate.removable ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [candidate.hidden === true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5041
+ className: "dsha-btn",
5042
+ disabled: busy !== null,
5043
+ onClick: () => void handlePoolAction("restore", candidate.id),
5044
+ children: t.restoreAccount
5045
+ }), candidate.hidden !== true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5046
+ className: "dsha-btn",
5047
+ disabled: busy !== null,
5048
+ onClick: () => void handlePoolAction("hide", candidate.id),
5049
+ children: t.hideAccount
5050
+ })] }),
5051
+ renderDetails: (candidate) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
5052
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
5053
+ t.region,
5054
+ ": ",
5055
+ candidate.region === "intl" ? t.regionIntl : t.regionCn
5056
+ ] }),
5057
+ candidate.uin ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
5058
+ t.uin,
5059
+ ": ",
5060
+ maskUin(candidate.uin)
5061
+ ] }) : null,
5062
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5063
+ className: "dshwb-mono",
5064
+ children: candidate.source === "managed" ? t.encryptedStorage : displayFile(candidate.sourceFile)
5065
+ }),
5066
+ candidate.hidden === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t.hiddenAccount }) : null
5067
+ ] })
5068
+ }),
5069
+ loginProgress ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5070
+ className: "dsha-notice",
5071
+ children: loginProgress
5072
+ }) : null,
5073
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
5074
+ className: "dsha-group",
5075
+ children: [
5076
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5077
+ className: "dsha-grouphead",
5078
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t.connection })
5079
+ }),
5080
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5081
+ className: "dsha-row",
5082
+ style: { marginBottom: 12 },
5083
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5084
+ className: "dsha-label",
5085
+ style: { fontWeight: 600 },
5086
+ children: t.enableProvider
5087
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
5088
+ type: "checkbox",
5089
+ checked: status?.enabled !== false,
5090
+ disabled: busy !== null,
5091
+ onChange: (e) => void toggleEnabled(e.currentTarget.checked)
5092
+ })]
5093
+ }),
5094
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5095
+ className: "dsha-row",
5096
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5097
+ className: "dsha-label",
5098
+ children: t.provider
5099
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5100
+ className: "dsha-value",
5101
+ children: t.providerValue
5102
+ })]
5103
+ }),
5104
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5105
+ className: "dsha-row",
5106
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5107
+ className: "dsha-label",
5108
+ children: t.connectionState
5109
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
5110
+ className: "dsha-value",
5111
+ children: [connection?.connected ? t.connected : authenticated ? t.untested : t.signedOut, connection?.connected ? ` · ${connection.latencyMs}ms · ${connection.model}` : ""]
5112
+ })]
5113
+ }),
5114
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5115
+ className: "dsha-notice",
5116
+ children: status?.serving === false && status.conflict ? t.routeConflict.replace("{detail}", status.conflict) : t.routeOwned
5117
+ }),
5118
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5119
+ className: "dsha-actions",
5120
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5121
+ className: "dsha-btn",
5122
+ disabled: busy !== null || !authenticated,
5123
+ onClick: () => void handleTestConnection(),
5124
+ children: busy === "connection" ? t.testingConnection : t.testConnection
5125
+ })
5126
+ })
5127
+ ]
5128
+ }),
5129
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
5130
+ className: "dsha-group",
5131
+ children: [
5132
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5133
+ className: "dsha-grouphead",
5134
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t.modelsSection }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5135
+ className: "dsha-btn",
5136
+ disabled: busy !== null || !authenticated,
5137
+ onClick: () => void handleRefreshCatalog(),
5138
+ children: busy === "catalog" ? t.refreshingCatalog : t.refreshCatalog
5139
+ })]
5140
+ }),
5141
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5142
+ className: "dsha-muted dsha-models-hint",
5143
+ children: t.modelsHint
5144
+ }),
5145
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5146
+ className: "dsha-models",
5147
+ "aria-label": "WorkBuddy Models",
5148
+ children: status?.models.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
5149
+ title: model.description ?? model.id,
5150
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
5151
+ type: "checkbox",
5152
+ checked: model.enabled,
5153
+ disabled: busy !== null,
5154
+ onChange: (event) => void toggleModel(model.id, event.currentTarget.checked)
5155
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
5156
+ className: "dshwb-model",
5157
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: model.name }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
5158
+ className: "dshwb-model-meta",
5159
+ children: [
5160
+ formatCapacity(model.contextWindow),
5161
+ " · ",
5162
+ model.supportsImage ? t.imageSupport : t.textOnly,
5163
+ model.reasoningEfforts && model.reasoningEfforts.length > 0 ? ` · ${model.reasoningEfforts.join("/")}` : ""
5164
+ ]
5165
+ })]
5166
+ })]
5167
+ }, model.id))
5168
+ }),
5169
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5170
+ className: "dsha-actions",
5171
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5172
+ className: "dsha-btn",
5173
+ disabled: busy !== null,
5174
+ onClick: () => void setAllModels(true),
5175
+ children: t.selectAll
5176
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5177
+ className: "dsha-btn",
5178
+ disabled: busy !== null,
5179
+ onClick: () => void setAllModels(false),
5180
+ children: t.unselectAll
5181
+ })]
5182
+ })
5183
+ ]
5184
+ }),
5185
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
5186
+ className: "dsha-group",
5187
+ children: [
5188
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5189
+ className: "dsha-grouphead",
5190
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t.enhanced })
5191
+ }),
5192
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5193
+ className: "dsha-pref-row",
5194
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
5195
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t.defaultReasoningEffort }),
5196
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5197
+ className: "dsha-muted",
5198
+ children: t.defaultReasoningEffortHint
5199
+ }),
5200
+ strayEffort !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5201
+ className: "dsha-muted",
5202
+ children: t.defaultEffortKept
5203
+ }) : null
5204
+ ] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
5205
+ className: "dsha-select",
5206
+ "aria-label": t.defaultReasoningEffort,
5207
+ value: status?.defaultReasoningEffort ?? "",
5208
+ disabled: busy !== null,
5209
+ onChange: (event) => {
5210
+ const value = event.currentTarget.value;
5211
+ handleUpdateEffort(value === "" ? null : value);
5212
+ },
5213
+ children: [
5214
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
5215
+ value: "",
5216
+ children: t.defaultEffortAuto
5217
+ }),
5218
+ strayEffort !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
5219
+ value: strayEffort,
5220
+ children: `${EFFORT_LABELS[strayEffort]} (${t.effortUnsupported})`
5221
+ }) : null,
5222
+ effortChoices.map((choice) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
5223
+ value: choice.value,
5224
+ children: choice.models.length === allModels.length ? EFFORT_LABELS[choice.value] : `${EFFORT_LABELS[choice.value]} (${choice.models.length}/${allModels.length})`
5225
+ }, choice.value))
5226
+ ]
5227
+ })]
5228
+ }),
5229
+ effortChoices.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3896
5230
  className: "dsha-muted",
3897
- children: t.defaultReasoningEffortHint
3898
- })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
3899
- className: "dsha-select",
3900
- "aria-label": t.defaultReasoningEffort,
3901
- value: status?.defaultReasoningEffort ?? "",
3902
- disabled: busy !== null,
3903
- onChange: (event) => {
3904
- const value = event.currentTarget.value;
3905
- handleUpdateEffort(value === "" ? null : value);
3906
- },
3907
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3908
- value: "",
3909
- children: t.defaultEffortAuto
3910
- }), KIMI_CODE_REASONING_EFFORTS.map((effort) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3911
- value: effort,
3912
- children: EFFORT_LABEL[effort]
3913
- }, effort))]
3914
- })]
3915
- })]
5231
+ children: t.noReasoningModels
5232
+ }) : null
5233
+ ]
3916
5234
  }),
3917
5235
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
3918
5236
  className: "dsha-group",
@@ -3929,12 +5247,9 @@ window.__ModuleLoader__.load({
3929
5247
  className: "dsha-context-settings",
3930
5248
  children: status?.models.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3931
5249
  className: "dsha-context-row",
3932
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
5250
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3933
5251
  title: model.id,
3934
- children: [model.name, model.defaultContextWindow < model.contextWindow && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3935
- className: "dsha-model-meta",
3936
- children: formatCapacity(model.contextWindow)
3937
- })]
5252
+ children: model.name
3938
5253
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3939
5254
  className: "dsha-capacity-control",
3940
5255
  children: [
@@ -3950,7 +5265,7 @@ window.__ModuleLoader__.load({
3950
5265
  if (event.key === "Enter") handleSaveContextWindow(model.id);
3951
5266
  }
3952
5267
  }),
3953
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: t.tokens }),
5268
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: "tokens" }),
3954
5269
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3955
5270
  type: "button",
3956
5271
  className: "dsha-context-save",
@@ -3971,7 +5286,7 @@ window.__ModuleLoader__.load({
3971
5286
  className: "dsha-grouphead",
3972
5287
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t.quotaSection }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3973
5288
  className: "dsha-btn",
3974
- disabled: busy !== null || !status?.authenticated,
5289
+ disabled: busy !== null || !authenticated,
3975
5290
  onClick: () => void handleRefreshQuota(),
3976
5291
  children: busy === "quota" ? t.refreshingQuota : t.refreshQuota
3977
5292
  })]
@@ -3980,11 +5295,7 @@ window.__ModuleLoader__.load({
3980
5295
  className: "dsha-muted",
3981
5296
  children: t.quotaDesc
3982
5297
  }),
3983
- status?.quotaError && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3984
- className: "dsha-notice",
3985
- children: status.quotaError
3986
- }),
3987
- !status?.authenticated ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5298
+ !authenticated ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3988
5299
  className: "dsha-empty",
3989
5300
  children: t.signedOut
3990
5301
  }) : quota === null || quota === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
@@ -3995,49 +5306,62 @@ window.__ModuleLoader__.load({
3995
5306
  children: [
3996
5307
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3997
5308
  className: "dsha-quota-title",
3998
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t.quotaPlan }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: quota.planName || t.planUnknown })]
5309
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: quota.packageName || t.quotaPackage }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: quota.remainingCredits !== null ? `${quota.remainingCredits} ${t.credits}` : "—" })]
3999
5310
  }),
4000
- quota.windows.map((window) => {
4001
- const remaining = Math.max(0, 100 - window.usedPercent);
5311
+ quota.cycleUsedCredits !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5312
+ className: "dsha-row",
5313
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5314
+ className: "dsha-label",
5315
+ children: t.quotaCycle
5316
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
5317
+ className: "dsha-value",
5318
+ children: [
5319
+ quota.cycleUsedCredits,
5320
+ quota.cycleCredits !== null ? ` / ${quota.cycleCredits}` : "",
5321
+ ` ${t.credits}`
5322
+ ]
5323
+ })]
5324
+ }),
5325
+ quota.cycleEndsAt !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5326
+ className: "dsha-row",
5327
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5328
+ className: "dsha-label",
5329
+ children: t.quotaReset
5330
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
5331
+ className: "dsha-value",
5332
+ children: [formatDate(quota.cycleEndsAt), formatReset(quota.cycleEndsAt) ? ` · ${formatReset(quota.cycleEndsAt)}` : ""]
5333
+ })]
5334
+ }),
5335
+ quota.meters.map((meter) => {
5336
+ const remaining = meter.remainingFraction;
5337
+ const percent = remaining === null ? null : Math.round(remaining * 100);
4002
5338
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4003
5339
  className: "dsha-meter-wrap",
4004
5340
  children: [
4005
5341
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4006
5342
  className: "dsha-meter-label",
4007
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: window.label }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("strong", { children: [remaining, "% left"] })]
5343
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: meter.label }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: percent === null ? meter.limit ?? "—" : `${percent}% left` })]
4008
5344
  }),
4009
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4010
- className: `dsha-meter ${remaining <= 10 ? "dsha-meter-cyan" : "dsha-meter-green"}`,
4011
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { style: { width: `${remaining}%` } })
5345
+ percent !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5346
+ className: "dsha-meter dsha-meter-green",
5347
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { style: { width: `${percent}%` } })
4012
5348
  }),
4013
5349
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4014
5350
  className: "dsha-meter-meta",
4015
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
4016
- window.used ?? "—",
5351
+ children: [meter.used !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
5352
+ meter.used,
4017
5353
  " / ",
4018
- window.limit ?? "—",
4019
- " ",
4020
- t.requests
4021
- ] }), window.resetsAt !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t.resetAt.replace("{time}", formatReset(window.resetsAt)) })]
5354
+ meter.limit ?? "—"
5355
+ ] }), meter.resetsAt !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
5356
+ t.quotaReset,
5357
+ ": ",
5358
+ formatReset(meter.resetsAt)
5359
+ ] })]
4022
5360
  })
4023
5361
  ]
4024
- }, window.id);
4025
- }),
4026
- quota.extraUsage !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4027
- className: "dsha-meter-wrap",
4028
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4029
- className: "dsha-meter-label",
4030
- 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) })]
4031
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4032
- className: "dsha-meter-meta",
4033
- 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: [
4034
- t.quotaWalletLimit,
4035
- ": ",
4036
- formatMoney(quota.extraUsage.monthlyChargeLimitCents, quota.extraUsage.currency)
4037
- ] })]
4038
- })]
5362
+ }, meter.id);
4039
5363
  }),
4040
- quota.windows.length === 0 && quota.extraUsage === null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5364
+ quota.meters.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4041
5365
  className: "dsha-empty",
4042
5366
  children: t.quotaEmpty
4043
5367
  }),
@@ -4074,6 +5398,10 @@ window.__ModuleLoader__.load({
4074
5398
  {
4075
5399
  id: "kimi-code",
4076
5400
  label: "Kimi Code"
5401
+ },
5402
+ {
5403
+ id: "workbuddy",
5404
+ label: "WorkBuddy"
4077
5405
  }
4078
5406
  ];
4079
5407
  /**
@@ -4131,7 +5459,8 @@ window.__ModuleLoader__.load({
4131
5459
  }) : null,
4132
5460
  active === "antigravity" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AntigravitySection, { onModelChange }) : null,
4133
5461
  active === "command-code" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CommandCodeSection, { onModelChange }) : null,
4134
- active === "kimi-code" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(KimiCodeSection, { onModelChange }) : null
5462
+ active === "kimi-code" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(KimiCodeSection, { onModelChange }) : null,
5463
+ active === "workbuddy" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(WorkBuddySection, { onModelChange }) : null
4135
5464
  ]
4136
5465
  })
4137
5466
  ]
@@ -4142,6 +5471,7 @@ window.__ModuleLoader__.load({
4142
5471
  const NS = "dsh-chatgpt-subscription";
4143
5472
  const dictionaries = {
4144
5473
  zh: {
5474
+ ...accountPoolZh,
4145
5475
  title: "ChatGPT",
4146
5476
  hubTitle: "订阅服务",
4147
5477
  intro: "使用 ChatGPT 账号登录,在 DSH 中使用订阅可用的模型。",
@@ -4270,6 +5600,7 @@ window.__ModuleLoader__.load({
4270
5600
  unknown: "未知"
4271
5601
  },
4272
5602
  en: {
5603
+ ...accountPoolEn,
4273
5604
  title: "ChatGPT",
4274
5605
  hubTitle: "Subscriptions",
4275
5606
  intro: "Sign in with ChatGPT to use models available to your subscription in DSH.",
@@ -4400,7 +5731,7 @@ window.__ModuleLoader__.load({
4400
5731
  };
4401
5732
  //#endregion
4402
5733
  //#region src/client/styles.ts
4403
- const STYLE_ID$2 = "@eddyskywalker/dsh-chatgpt-subscription/main";
5734
+ const STYLE_ID$3 = "@eddyskywalker/dsh-chatgpt-subscription/main";
4404
5735
  const CSS = `
4405
5736
  .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}
4406
5737
  .dsh-codex-page *{box-sizing:border-box}
@@ -4509,21 +5840,21 @@ window.__ModuleLoader__.load({
4509
5840
  @media(prefers-reduced-motion:reduce){.dsh-codex-meter>span{transition:none}.dsh-codex-skeleton span{animation:none}}
4510
5841
  `;
4511
5842
  function installStyles() {
4512
- if (document.querySelector(`style[data-plugin-css="${STYLE_ID$2}"]`) !== null) return () => void 0;
5843
+ if (document.querySelector(`style[data-plugin-css="${STYLE_ID$3}"]`) !== null) return () => void 0;
4513
5844
  const element = document.createElement("style");
4514
5845
  element.dataset.plugin = "@eddyskywalker/dsh-chatgpt-subscription";
4515
- element.dataset.pluginCss = STYLE_ID$2;
5846
+ element.dataset.pluginCss = STYLE_ID$3;
4516
5847
  element.textContent = CSS;
4517
5848
  document.head.appendChild(element);
4518
5849
  return () => element.remove();
4519
5850
  }
4520
5851
  //#endregion
4521
5852
  //#region src/client/antigravity/styles.ts
4522
- const STYLE_ID$1 = "dsh-antigravity-settings-style";
5853
+ const STYLE_ID$2 = "dsh-antigravity-settings-style";
4523
5854
  function installAntigravityStyles() {
4524
5855
  if (typeof document === "undefined" || document.getElementById("dsh-antigravity-settings-style")) return;
4525
5856
  const style = document.createElement("style");
4526
- style.id = STYLE_ID$1;
5857
+ style.id = STYLE_ID$2;
4527
5858
  style.textContent = `
4528
5859
  .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}
4529
5860
  .dsha-page *{box-sizing:border-box}
@@ -4546,7 +5877,7 @@ window.__ModuleLoader__.load({
4546
5877
  .dsha-models{display:flex;flex-wrap:wrap;gap:7px;padding-top:8px}
4547
5878
  .dsha-models label{cursor:pointer;display:block;position:relative}
4548
5879
  .dsha-models input{position:absolute;opacity:0;pointer-events:none}
4549
- .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}
5880
+ .dsha-models>label>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}
4550
5881
  .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}
4551
5882
  .dsha-models input:disabled+span{cursor:default;opacity:.5}
4552
5883
  .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}
@@ -4596,14 +5927,35 @@ window.__ModuleLoader__.load({
4596
5927
  .dsha-account-details{display:flex;flex-wrap:wrap;gap:14px;font-size:12px;color:var(--dsw-alias-label-secondary)}
4597
5928
  .dsha-account-actions{display:flex;flex-wrap:wrap;gap:6px;margin-top:2px}
4598
5929
  .dsha-account-actions .dsha-btn{padding:5px 10px;font-size:12px}
5930
+ `;
5931
+ document.head.append(style);
5932
+ }
5933
+ //#endregion
5934
+ //#region src/client/common/styles.ts
5935
+ const POOL_STYLE_ID = "dsh-provider-pool-style";
5936
+ /**
5937
+ * Styles the shared account-pool card adds on top of the existing provider
5938
+ * stylesheets: the badge variants no single provider tab owned before.
5939
+ *
5940
+ * Additive on purpose — the shared `.dsha-*` block and each provider's own
5941
+ * tweaks keep living in their own modules, in the order the client installs
5942
+ * them, so nothing that already rendered changes appearance.
5943
+ */
5944
+ function installPoolStyles() {
5945
+ if (typeof document === "undefined" || document.getElementById("dsh-provider-pool-style")) return;
5946
+ const style = document.createElement("style");
5947
+ style.id = POOL_STYLE_ID;
5948
+ style.textContent = `
5949
+ .dsha-badge.danger{background:color-mix(in srgb,var(--dsw-alias-label-danger,#d94b4b) 18%,var(--dsw-alias-bg-layer-1));color:var(--dsw-alias-label-danger,#d94b4b);border:0.5px solid color-mix(in srgb,var(--dsw-alias-label-danger,#d94b4b) 40%,transparent)}
5950
+ .dsha-account-card .dsha-btn.danger{color:var(--dsw-alias-label-danger,#d94b4b)}
4599
5951
  `;
4600
5952
  document.head.append(style);
4601
5953
  }
4602
5954
  //#endregion
4603
5955
  //#region src/client/antigravity/AntigravityComposerQuota.tsx
4604
- const API$2 = "/antigravity/api";
4605
- async function fetchApi$2(path, options) {
4606
- const res = await fetch(`${API$2}${path}`, {
5956
+ const API$3 = "/antigravity/api";
5957
+ async function fetchApi$3(path, options) {
5958
+ const res = await fetch(`${API$3}${path}`, {
4607
5959
  ...options,
4608
5960
  headers: {
4609
5961
  "Content-Type": "application/json",
@@ -4644,7 +5996,7 @@ window.__ModuleLoader__.load({
4644
5996
  }
4645
5997
  }
4646
5998
  function AntigravityComposerQuota({ directory, loadModelDirectory }) {
4647
- const modelState = useStore$2(directory);
5999
+ const modelState = useStore$3(directory);
4648
6000
  const [status, setStatus] = (0, react.useState)(null);
4649
6001
  const [loading, setLoading] = (0, react.useState)(false);
4650
6002
  const mountedRef = (0, react.useRef)(false);
@@ -4663,7 +6015,7 @@ window.__ModuleLoader__.load({
4663
6015
  if (!mountedRef.current) return;
4664
6016
  setLoading(true);
4665
6017
  try {
4666
- const data = refresh ? await fetchApi$2("/quota", { method: "POST" }) : await fetchApi$2("/status");
6018
+ const data = refresh ? await fetchApi$3("/quota", { method: "POST" }) : await fetchApi$3("/status");
4667
6019
  if (mountedRef.current) setStatus(data);
4668
6020
  } catch {} finally {
4669
6021
  if (mountedRef.current) setLoading(false);
@@ -4701,14 +6053,14 @@ window.__ModuleLoader__.load({
4701
6053
  })]
4702
6054
  });
4703
6055
  }
4704
- function useStore$2(store) {
6056
+ function useStore$3(store) {
4705
6057
  return (0, react.useSyncExternalStore)((listener) => store.subscribe(listener), () => store.getSnapshot(), () => store.getSnapshot());
4706
6058
  }
4707
6059
  //#endregion
4708
6060
  //#region src/client/command-code/CommandCodeComposerQuota.tsx
4709
- const API$1 = "/command-code/api";
4710
- async function fetchApi$1(path, options) {
4711
- const res = await fetch(`${API$1}${path}`, {
6061
+ const API$2 = "/command-code/api";
6062
+ async function fetchApi$2(path, options) {
6063
+ const res = await fetch(`${API$2}${path}`, {
4712
6064
  ...options,
4713
6065
  headers: {
4714
6066
  "Content-Type": "application/json",
@@ -4725,7 +6077,7 @@ window.__ModuleLoader__.load({
4725
6077
  * A bounded usage window is the number a user actually spends down, so it wins
4726
6078
  * over a credit balance; the balance shows only when no window is reported.
4727
6079
  */
4728
- function selectBadgeFacts$1(status) {
6080
+ function selectBadgeFacts$2(status) {
4729
6081
  const quota = status?.quota;
4730
6082
  if (quota === null || quota === void 0) return {
4731
6083
  text: "—",
@@ -4767,7 +6119,7 @@ window.__ModuleLoader__.load({
4767
6119
  };
4768
6120
  }
4769
6121
  function CommandCodeComposerQuota({ directory, loadModelDirectory }) {
4770
- const modelState = useStore$1(directory);
6122
+ const modelState = useStore$2(directory);
4771
6123
  const [status, setStatus] = (0, react.useState)(null);
4772
6124
  const [loading, setLoading] = (0, react.useState)(false);
4773
6125
  const mountedRef = (0, react.useRef)(false);
@@ -4785,7 +6137,7 @@ window.__ModuleLoader__.load({
4785
6137
  if (!mountedRef.current) return;
4786
6138
  setLoading(true);
4787
6139
  try {
4788
- const data = refresh ? await fetchApi$1("/quota", { method: "POST" }) : await fetchApi$1("/status");
6140
+ const data = refresh ? await fetchApi$2("/quota", { method: "POST" }) : await fetchApi$2("/status");
4789
6141
  if (mountedRef.current) setStatus(data);
4790
6142
  } catch {} finally {
4791
6143
  if (mountedRef.current) setLoading(false);
@@ -4801,7 +6153,7 @@ window.__ModuleLoader__.load({
4801
6153
  window.clearInterval(timer);
4802
6154
  };
4803
6155
  }, [fetchStatus, isCommandCode]);
4804
- const facts = (0, react.useMemo)(() => selectBadgeFacts$1(status), [status]);
6156
+ const facts = (0, react.useMemo)(() => selectBadgeFacts$2(status), [status]);
4805
6157
  if (!isCommandCode || !status?.authenticated || facts === null) return null;
4806
6158
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4807
6159
  className: "dsha-composer-quota",
@@ -4818,14 +6170,14 @@ window.__ModuleLoader__.load({
4818
6170
  })]
4819
6171
  });
4820
6172
  }
4821
- function useStore$1(store) {
6173
+ function useStore$2(store) {
4822
6174
  return (0, react.useSyncExternalStore)((listener) => store.subscribe(listener), () => store.getSnapshot(), () => store.getSnapshot());
4823
6175
  }
4824
6176
  //#endregion
4825
6177
  //#region src/client/kimi-code/KimiCodeComposerQuota.tsx
4826
- const API = "/kimi-code/api";
4827
- async function fetchApi(path, options) {
4828
- const res = await fetch(`${API}${path}`, {
6178
+ const API$1 = "/kimi-code/api";
6179
+ async function fetchApi$1(path, options) {
6180
+ const res = await fetch(`${API$1}${path}`, {
4829
6181
  ...options,
4830
6182
  headers: {
4831
6183
  "Content-Type": "application/json",
@@ -4843,7 +6195,7 @@ window.__ModuleLoader__.load({
4843
6195
  * blocks work soonest, so it wins over the longer pools; the 7-day allowance
4844
6196
  * shows only when no 5-hour window is reported.
4845
6197
  */
4846
- function selectBadgeFacts(status) {
6198
+ function selectBadgeFacts$1(status) {
4847
6199
  const quota = status?.quota;
4848
6200
  if (quota === null || quota === void 0) return {
4849
6201
  text: "—",
@@ -4875,7 +6227,7 @@ window.__ModuleLoader__.load({
4875
6227
  };
4876
6228
  }
4877
6229
  function KimiCodeComposerQuota({ directory, loadModelDirectory }) {
4878
- const modelState = useStore(directory);
6230
+ const modelState = useStore$1(directory);
4879
6231
  const [status, setStatus] = (0, react.useState)(null);
4880
6232
  const [loading, setLoading] = (0, react.useState)(false);
4881
6233
  const mountedRef = (0, react.useRef)(false);
@@ -4893,7 +6245,7 @@ window.__ModuleLoader__.load({
4893
6245
  if (!mountedRef.current) return;
4894
6246
  setLoading(true);
4895
6247
  try {
4896
- const data = refresh ? await fetchApi("/quota", { method: "POST" }) : await fetchApi("/status");
6248
+ const data = refresh ? await fetchApi$1("/quota", { method: "POST" }) : await fetchApi$1("/status");
4897
6249
  if (mountedRef.current) setStatus(data);
4898
6250
  } catch {} finally {
4899
6251
  if (mountedRef.current) setLoading(false);
@@ -4909,7 +6261,7 @@ window.__ModuleLoader__.load({
4909
6261
  window.clearInterval(timer);
4910
6262
  };
4911
6263
  }, [fetchStatus, isKimiCode]);
4912
- const facts = (0, react.useMemo)(() => selectBadgeFacts(status), [status]);
6264
+ const facts = (0, react.useMemo)(() => selectBadgeFacts$1(status), [status]);
4913
6265
  if (!isKimiCode || !status?.authenticated || facts === null) return null;
4914
6266
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4915
6267
  className: "dsha-composer-quota",
@@ -4926,12 +6278,12 @@ window.__ModuleLoader__.load({
4926
6278
  })]
4927
6279
  });
4928
6280
  }
4929
- function useStore(store) {
6281
+ function useStore$1(store) {
4930
6282
  return (0, react.useSyncExternalStore)((listener) => store.subscribe(listener), () => store.getSnapshot(), () => store.getSnapshot());
4931
6283
  }
4932
6284
  //#endregion
4933
6285
  //#region src/client/kimi-code/styles.ts
4934
- const STYLE_ID = "dsh-kimi-code-settings-style";
6286
+ const STYLE_ID$1 = "dsh-kimi-code-settings-style";
4935
6287
  /**
4936
6288
  * Node the base control styles come from.
4937
6289
  *
@@ -4961,7 +6313,7 @@ window.__ModuleLoader__.load({
4961
6313
  .dsha-models{display:flex;flex-wrap:wrap;gap:7px;padding-top:8px}
4962
6314
  .dsha-models label{cursor:pointer;display:block;position:relative}
4963
6315
  .dsha-models input{position:absolute;opacity:0;pointer-events:none}
4964
- .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}
6316
+ .dsha-models>label>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}
4965
6317
  .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}
4966
6318
  .dsha-models input:disabled+span{cursor:default;opacity:.5}
4967
6319
  .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}
@@ -5021,11 +6373,148 @@ window.__ModuleLoader__.load({
5021
6373
  function installKimiCodeStyles() {
5022
6374
  if (typeof document === "undefined" || document.getElementById("dsh-kimi-code-settings-style")) return;
5023
6375
  const style = document.createElement("style");
5024
- style.id = STYLE_ID;
6376
+ style.id = STYLE_ID$1;
5025
6377
  style.textContent = BASE;
5026
6378
  document.head.appendChild(style);
5027
6379
  }
5028
6380
  //#endregion
6381
+ //#region src/client/workbuddy/WorkBuddyComposerQuota.tsx
6382
+ const API = "/workbuddy/api";
6383
+ async function fetchApi(path, options) {
6384
+ const res = await fetch(`${API}${path}`, {
6385
+ ...options,
6386
+ headers: {
6387
+ "Content-Type": "application/json",
6388
+ ...options?.headers
6389
+ }
6390
+ });
6391
+ const json = await res.json();
6392
+ if (!res.ok || !json.ok) throw new Error(json.error || `HTTP ${res.status}`);
6393
+ return json.value;
6394
+ }
6395
+ /**
6396
+ * Pick the most meaningful number for the badge.
6397
+ *
6398
+ * A bounded allowance is what a user spends down, so it wins over a bare credit
6399
+ * balance; the balance shows only when no percentage is reported. The billing
6400
+ * payload reports both a cycle meter and a package meter, and the cycle meter
6401
+ * is the one that actually resets, so it is preferred when both exist.
6402
+ */
6403
+ function selectBadgeFacts(status) {
6404
+ const quota = status?.quota;
6405
+ if (quota === null || quota === void 0) return {
6406
+ text: "—",
6407
+ tooltip: "WorkBuddy quota unavailable — click to refresh",
6408
+ level: "normal"
6409
+ };
6410
+ const meter = (quota.meters ?? []).find((candidate) => candidate.id === "cycle" && candidate.remainingFraction !== null) ?? (quota.meters ?? []).find((candidate) => candidate.remainingFraction !== null);
6411
+ if (meter !== void 0 && meter.remainingFraction !== null) {
6412
+ const remaining = Math.round(meter.remainingFraction * 100);
6413
+ return {
6414
+ text: `${remaining}%`,
6415
+ tooltip: `[WorkBuddy] ${meter.label}: ${remaining}% left`,
6416
+ level: remaining <= 5 ? "danger" : remaining <= 20 ? "warning" : "normal"
6417
+ };
6418
+ }
6419
+ if (quota.remainingCredits !== null) return {
6420
+ text: String(quota.remainingCredits),
6421
+ tooltip: `[WorkBuddy] Remaining credits: ${quota.remainingCredits}`,
6422
+ level: "normal"
6423
+ };
6424
+ return {
6425
+ text: "—",
6426
+ tooltip: "WorkBuddy quota unavailable — click to refresh",
6427
+ level: "normal"
6428
+ };
6429
+ }
6430
+ function WorkBuddyComposerQuota({ directory, loadModelDirectory }) {
6431
+ const modelState = useStore(directory);
6432
+ const [status, setStatus] = (0, react.useState)(null);
6433
+ const [loading, setLoading] = (0, react.useState)(false);
6434
+ const mountedRef = (0, react.useRef)(false);
6435
+ const isWorkBuddy = modelState.current?.provider === "workbuddy-subscription";
6436
+ (0, react.useEffect)(() => {
6437
+ loadModelDirectory();
6438
+ }, [loadModelDirectory]);
6439
+ (0, react.useEffect)(() => {
6440
+ mountedRef.current = true;
6441
+ return () => {
6442
+ mountedRef.current = false;
6443
+ };
6444
+ }, []);
6445
+ const fetchStatus = (0, react.useCallback)(async (refresh = false) => {
6446
+ if (!mountedRef.current) return;
6447
+ setLoading(true);
6448
+ try {
6449
+ const data = refresh ? await fetchApi("/quota", { method: "POST" }) : await fetchApi("/status");
6450
+ if (mountedRef.current) setStatus(data);
6451
+ } catch {} finally {
6452
+ if (mountedRef.current) setLoading(false);
6453
+ }
6454
+ }, []);
6455
+ (0, react.useEffect)(() => {
6456
+ if (!isWorkBuddy) return;
6457
+ fetchStatus(false);
6458
+ const timer = window.setInterval(() => {
6459
+ if (document.visibilityState === "visible") fetchStatus(false);
6460
+ }, 6e4);
6461
+ return () => {
6462
+ window.clearInterval(timer);
6463
+ };
6464
+ }, [fetchStatus, isWorkBuddy]);
6465
+ const facts = (0, react.useMemo)(() => selectBadgeFacts(status), [status]);
6466
+ if (!isWorkBuddy || !status?.authenticated || facts === null) return null;
6467
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
6468
+ className: "dsha-composer-quota",
6469
+ "data-level": facts.level,
6470
+ title: facts.tooltip,
6471
+ "aria-label": facts.tooltip,
6472
+ onClick: () => void fetchStatus(true),
6473
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6474
+ className: "dsha-composer-quota-label",
6475
+ children: "额度"
6476
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", {
6477
+ className: "dsha-composer-quota-val",
6478
+ children: loading ? "…" : facts.text
6479
+ })]
6480
+ });
6481
+ }
6482
+ function useStore(store) {
6483
+ return (0, react.useSyncExternalStore)((listener) => store.subscribe(listener), () => store.getSnapshot(), () => store.getSnapshot());
6484
+ }
6485
+ //#endregion
6486
+ //#region src/client/workbuddy/styles.ts
6487
+ const STYLE_ID = "dsh-workbuddy-settings-style";
6488
+ /**
6489
+ * WorkBuddy-only additions to the shared settings design system.
6490
+ *
6491
+ * The base control styles (`.dsha-page`, `.dsha-group`, `.dsha-models`, …) come
6492
+ * from the stylesheet the sibling routes install, so this file declares only
6493
+ * what is unique to this card: the per-model capability line and the
6494
+ * "unavailable in this region" treatment.
6495
+ */
6496
+ function installWorkBuddyStyles() {
6497
+ if (typeof document === "undefined" || document.getElementById("dsh-workbuddy-settings-style")) return;
6498
+ const style = document.createElement("style");
6499
+ style.id = STYLE_ID;
6500
+ style.textContent = `
6501
+ .dshwb-model{display:flex;flex-direction:column;gap:2px;min-width:0}
6502
+ .dshwb-model-meta{color:var(--dsw-alias-label-tertiary);font-size:10px;font-weight:400;line-height:1.3;white-space:nowrap}
6503
+ .dsha-models input:checked+span .dshwb-model-meta{color:var(--dsw-alias-label-secondary)}
6504
+ .dshwb-region{display:inline-flex;align-items:center;gap:4px}
6505
+ .dshwb-muted-note{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:1.5;margin:8px 0 0}
6506
+ .dshwb-mono{font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:11px}
6507
+ .dsha-account-region{margin-top:14px}
6508
+ .dsha-account-region h4{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:650;margin:0 0 8px}
6509
+ .dsha-account-add-actions{display:flex;flex-wrap:wrap;gap:8px}
6510
+ .dsha-account-card.hidden{opacity:.68}
6511
+ .dshwb-account-select{appearance:none;background:transparent;border:0;color:inherit;cursor:pointer;font:inherit;padding:0;text-align:left;width:100%}
6512
+ .dshwb-account-select:disabled{cursor:default}
6513
+ .dshwb-account-select:hover:not(:disabled) .dsha-account-title{color:var(--dsw-alias-label-link,#397ee8)}
6514
+ `;
6515
+ document.head.append(style);
6516
+ }
6517
+ //#endregion
5029
6518
  //#region src/client/mermaid/sanitize.ts
5030
6519
  const STRIP_ELEMENTS = /* @__PURE__ */ new Set([
5031
6520
  "foreignobject",
@@ -5365,17 +6854,26 @@ window.__ModuleLoader__.load({
5365
6854
  function apply(ctx) {
5366
6855
  ctx.effect(() => ctx.locale.register(NS, dictionaries), "dsh-chatgpt-subscription: dictionaries");
5367
6856
  ctx.effect(() => installStyles(), "dsh-chatgpt-subscription: styles");
5368
- ctx.effect(() => ctx.locale.register(NS_ANTIGRAVITY, dictionaries$3), "dsh-antigravity: dictionaries");
6857
+ ctx.effect(() => ctx.locale.register(NS_ANTIGRAVITY, dictionaries$4), "dsh-antigravity: dictionaries");
5369
6858
  ctx.effect(() => {
5370
6859
  installAntigravityStyles();
5371
6860
  return () => {};
5372
6861
  }, "dsh-antigravity: styles");
5373
- ctx.effect(() => ctx.locale.register(NS_COMMAND_CODE, dictionaries$2), "dsh-command-code: dictionaries");
5374
- ctx.effect(() => ctx.locale.register(NS_KIMI_CODE, dictionaries$1), "dsh-kimi-code: dictionaries");
6862
+ ctx.effect(() => ctx.locale.register(NS_COMMAND_CODE, dictionaries$3), "dsh-command-code: dictionaries");
6863
+ ctx.effect(() => ctx.locale.register(NS_KIMI_CODE, dictionaries$2), "dsh-kimi-code: dictionaries");
5375
6864
  ctx.effect(() => {
5376
6865
  installKimiCodeStyles();
5377
6866
  return () => {};
5378
6867
  }, "dsh-kimi-code: styles");
6868
+ ctx.effect(() => ctx.locale.register(NS_WORKBUDDY, dictionaries$1), "dsh-workbuddy: dictionaries");
6869
+ ctx.effect(() => {
6870
+ installWorkBuddyStyles();
6871
+ return () => {};
6872
+ }, "dsh-workbuddy: styles");
6873
+ ctx.effect(() => {
6874
+ installPoolStyles();
6875
+ return () => {};
6876
+ }, "dsh-chatgpt-subscription: pool styles");
5379
6877
  ctx.effect(() => setupMermaidObserver(), "dsh-mermaid: observer");
5380
6878
  const t = ctx.locale.bind(NS);
5381
6879
  const handleModelDirectoryReload = () => {
@@ -5459,6 +6957,21 @@ window.__ModuleLoader__.load({
5459
6957
  };
5460
6958
  }
5461
6959
  }, KimiCodeComposerQuota));
6960
+ ctx.slots.inject("conversation.input.right", () => ctx.slots.register({
6961
+ name: "conversation.input.right",
6962
+ id: "workbuddy-quota",
6963
+ order: 39,
6964
+ locale: NS_WORKBUDDY,
6965
+ inject: (sessionId) => {
6966
+ const directory = ctx.modelDirectories.directoryFor(sessionId);
6967
+ return {
6968
+ directory: directory.store,
6969
+ loadModelDirectory: () => {
6970
+ directory.load().catch(() => void 0);
6971
+ }
6972
+ };
6973
+ }
6974
+ }, WorkBuddyComposerQuota));
5462
6975
  ctx.slots.inject("tool.call.toolview", () => ctx.slots.register({
5463
6976
  name: "tool.call.toolview",
5464
6977
  key: CODEX_IMAGE_TOOL_NAME,