@eddyskywalker/dsh-chatgpt-subscription 0.3.9 → 0.5.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 (97) hide show
  1. package/CHANGELOG.md +51 -1
  2. package/lib/client.js +959 -503
  3. package/lib/client.js.map +1 -1
  4. package/lib/index.js +5592 -3670
  5. package/lib/types/client/CodexSubscriptionSection.d.ts +1 -1
  6. package/lib/types/client/CodexSubscriptionSection.d.ts.map +1 -1
  7. package/lib/types/client/antigravity/AntigravitySection.d.ts.map +1 -1
  8. package/lib/types/client/antigravity/locales.d.ts +63 -153
  9. package/lib/types/client/antigravity/locales.d.ts.map +1 -1
  10. package/lib/types/client/antigravity/styles.d.ts.map +1 -1
  11. package/lib/types/client/api.d.ts +13 -1
  12. package/lib/types/client/api.d.ts.map +1 -1
  13. package/lib/types/client/command-code/CommandCodeSection.d.ts.map +1 -1
  14. package/lib/types/client/command-code/locales.d.ts +72 -201
  15. package/lib/types/client/command-code/locales.d.ts.map +1 -1
  16. package/lib/types/client/common/AccountPoolSection.d.ts +41 -0
  17. package/lib/types/client/common/AccountPoolSection.d.ts.map +1 -0
  18. package/lib/types/client/common/account-pool-labels.d.ts +43 -0
  19. package/lib/types/client/common/account-pool-labels.d.ts.map +1 -0
  20. package/lib/types/client/common/styles.d.ts +11 -0
  21. package/lib/types/client/common/styles.d.ts.map +1 -0
  22. package/lib/types/client/index.d.ts.map +1 -1
  23. package/lib/types/client/kimi-code/KimiCodeSection.d.ts.map +1 -1
  24. package/lib/types/client/kimi-code/locales.d.ts +69 -336
  25. package/lib/types/client/kimi-code/locales.d.ts.map +1 -1
  26. package/lib/types/client/locales.d.ts +49 -3
  27. package/lib/types/client/locales.d.ts.map +1 -1
  28. package/lib/types/host/antigravity/account-pool.d.ts +60 -0
  29. package/lib/types/host/antigravity/account-pool.d.ts.map +1 -0
  30. package/lib/types/host/antigravity/adapter.d.ts +3 -1
  31. package/lib/types/host/antigravity/adapter.d.ts.map +1 -1
  32. package/lib/types/host/antigravity/mapper.d.ts.map +1 -1
  33. package/lib/types/host/antigravity/oauth.d.ts +1 -1
  34. package/lib/types/host/antigravity/oauth.d.ts.map +1 -1
  35. package/lib/types/host/antigravity/routes.d.ts +3 -2
  36. package/lib/types/host/antigravity/routes.d.ts.map +1 -1
  37. package/lib/types/host/antigravity/token-store.d.ts +3 -0
  38. package/lib/types/host/antigravity/token-store.d.ts.map +1 -1
  39. package/lib/types/host/codex-account-pool.d.ts +64 -0
  40. package/lib/types/host/codex-account-pool.d.ts.map +1 -0
  41. package/lib/types/host/command-code/account-pool.d.ts +60 -0
  42. package/lib/types/host/command-code/account-pool.d.ts.map +1 -0
  43. package/lib/types/host/command-code/adapter.d.ts +11 -1
  44. package/lib/types/host/command-code/adapter.d.ts.map +1 -1
  45. package/lib/types/host/command-code/client.d.ts +17 -1
  46. package/lib/types/host/command-code/client.d.ts.map +1 -1
  47. package/lib/types/host/command-code/mapper.d.ts.map +1 -1
  48. package/lib/types/host/command-code/oauth.d.ts +4 -1
  49. package/lib/types/host/command-code/oauth.d.ts.map +1 -1
  50. package/lib/types/host/command-code/routes.d.ts +12 -3
  51. package/lib/types/host/command-code/routes.d.ts.map +1 -1
  52. package/lib/types/host/command-code/token-store.d.ts +3 -0
  53. package/lib/types/host/command-code/token-store.d.ts.map +1 -1
  54. package/lib/types/host/common/account-pool.d.ts +179 -0
  55. package/lib/types/host/common/account-pool.d.ts.map +1 -0
  56. package/lib/types/host/common/home.d.ts +8 -0
  57. package/lib/types/host/common/home.d.ts.map +1 -0
  58. package/lib/types/host/kimi-code/account-pool.d.ts +48 -0
  59. package/lib/types/host/kimi-code/account-pool.d.ts.map +1 -0
  60. package/lib/types/host/kimi-code/adapter.d.ts +10 -1
  61. package/lib/types/host/kimi-code/adapter.d.ts.map +1 -1
  62. package/lib/types/host/kimi-code/client.d.ts +16 -0
  63. package/lib/types/host/kimi-code/client.d.ts.map +1 -1
  64. package/lib/types/host/kimi-code/mapper.d.ts.map +1 -1
  65. package/lib/types/host/kimi-code/oauth.d.ts +2 -0
  66. package/lib/types/host/kimi-code/oauth.d.ts.map +1 -1
  67. package/lib/types/host/kimi-code/routes.d.ts +11 -3
  68. package/lib/types/host/kimi-code/routes.d.ts.map +1 -1
  69. package/lib/types/host/kimi-code/token-store.d.ts +3 -0
  70. package/lib/types/host/kimi-code/token-store.d.ts.map +1 -1
  71. package/lib/types/host/model-catalog.d.ts.map +1 -1
  72. package/lib/types/host/oauth-service.d.ts +41 -2
  73. package/lib/types/host/oauth-service.d.ts.map +1 -1
  74. package/lib/types/host/preferences.d.ts.map +1 -1
  75. package/lib/types/host/preset-sync.d.ts +7 -1
  76. package/lib/types/host/preset-sync.d.ts.map +1 -1
  77. package/lib/types/host/responses-client.d.ts +15 -0
  78. package/lib/types/host/responses-client.d.ts.map +1 -1
  79. package/lib/types/host/routes.d.ts +2 -1
  80. package/lib/types/host/routes.d.ts.map +1 -1
  81. package/lib/types/host/token-store-windows.d.ts.map +1 -1
  82. package/lib/types/host/usage-service.d.ts +15 -0
  83. package/lib/types/host/usage-service.d.ts.map +1 -1
  84. package/lib/types/index.d.ts +6 -0
  85. package/lib/types/index.d.ts.map +1 -1
  86. package/lib/types/shared/account-pool-contracts.d.ts +47 -0
  87. package/lib/types/shared/account-pool-contracts.d.ts.map +1 -0
  88. package/lib/types/shared/antigravity-contracts.d.ts +12 -0
  89. package/lib/types/shared/antigravity-contracts.d.ts.map +1 -1
  90. package/lib/types/shared/command-code-contracts.d.ts +16 -0
  91. package/lib/types/shared/command-code-contracts.d.ts.map +1 -1
  92. package/lib/types/shared/contracts.d.ts +8 -0
  93. package/lib/types/shared/contracts.d.ts.map +1 -1
  94. package/lib/types/shared/kimi-code-contracts.d.ts +16 -0
  95. package/lib/types/shared/kimi-code-contracts.d.ts.map +1 -1
  96. package/lib/types/shared/preferences.d.ts.map +1 -1
  97. package/package.json +1 -1
package/lib/client.js CHANGED
@@ -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,274 @@ 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
+ })] }), /* @__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
+ !account.isPrimary && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
569
+ className: "dsha-btn",
570
+ disabled: props.busy !== null,
571
+ onClick: () => props.onSetPrimary(account.id),
572
+ children: t.setPrimary
573
+ }),
574
+ cooling && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
575
+ className: "dsha-btn",
576
+ disabled: props.busy !== null,
577
+ onClick: () => props.onClearCooldown(account.id),
578
+ children: t.clearCooldown
579
+ }),
580
+ needsRelogin && props.onRelogin && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
581
+ className: "dsha-btn",
582
+ disabled: props.busy !== null,
583
+ onClick: () => props.onRelogin(account.id),
584
+ children: t.relogin
585
+ }),
586
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
587
+ className: "dsha-btn",
588
+ disabled: props.busy !== null,
589
+ onClick: () => props.onDelete(account.id),
590
+ children: t.deleteAccount
591
+ })
592
+ ]
593
+ })]
594
+ }),
595
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
596
+ className: "dsha-account-details",
597
+ children: [
598
+ props.renderDetails?.(account),
599
+ account.email && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
600
+ t.email,
601
+ ": ",
602
+ account.email
603
+ ] }),
604
+ account.expiresAt !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
605
+ t.expires,
606
+ ": ",
607
+ formatAccountDate(account.expiresAt)
608
+ ] }),
609
+ account.lastUsedAt !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
610
+ t.lastUsed,
611
+ ": ",
612
+ formatAccountDate(account.lastUsedAt)
613
+ ] })
614
+ ]
615
+ }),
616
+ needsRelogin && account.authFailedReason && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
617
+ className: "dsha-account-details",
618
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: account.authFailedReason })
619
+ })
620
+ ]
621
+ }, account.id);
622
+ })
623
+ }),
624
+ props.children,
625
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
626
+ className: "dsha-row",
627
+ style: { marginTop: 12 },
628
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
629
+ className: "dsha-label",
630
+ children: t.storage
631
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
632
+ className: "dsha-value",
633
+ children: props.storageValue ?? "本地安全存储 (JSON/DPAPI)"
634
+ })]
635
+ }),
636
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
637
+ className: "dsha-notice",
638
+ children: t.storageNotice
639
+ })
640
+ ]
641
+ });
642
+ }
643
+ //#endregion
363
644
  //#region src/client/CodexSubscriptionSection.tsx
364
645
  function CodexSubscriptionSection({ t }) {
365
646
  const apiRef = (0, react.useRef)(new SubscriptionApi());
@@ -507,7 +788,10 @@ window.__ModuleLoader__.load({
507
788
  const toggleVisibleModel = async (modelId, checked) => {
508
789
  const current = status?.preferences.visibleModelIds ?? [...DEFAULT_VISIBLE_CODEX_MODEL_IDS];
509
790
  const visibleModelIds = checked ? [...current, modelId] : current.filter((id) => id !== modelId);
510
- if (visibleModelIds.length === 0) return;
791
+ await updatePreferences({ visibleModelIds });
792
+ };
793
+ const setAllVisibleModels = async (selectAll) => {
794
+ const visibleModelIds = selectAll ? CODEX_MODEL_CATALOG.map((m) => m.id) : [];
511
795
  await updatePreferences({ visibleModelIds });
512
796
  };
513
797
  const updateContextWindow = async (model) => {
@@ -528,14 +812,71 @@ window.__ModuleLoader__.load({
528
812
  await updatePreferences({ customProxyUrl: draft ? draft : null });
529
813
  setCustomProxyDraft(null);
530
814
  };
531
- const logout = async () => run("logout", async () => {
532
- await apiRef.current.logout();
533
- eventSourceRef.current?.close();
534
- eventSourceRef.current = null;
535
- setAuthUrl(null);
536
- setConnection(null);
537
- await load();
538
- });
815
+ /**
816
+ * The shared card takes a label object while this tab translates by key, so
817
+ * every pool label is resolved through the tab's own translator and follows
818
+ * whichever locale is active.
819
+ */
820
+ const poolLabels = (() => {
821
+ const keys = [
822
+ "accountPool",
823
+ "addAccount",
824
+ "accountCount",
825
+ "primaryAccount",
826
+ "activeAccount",
827
+ "setPrimary",
828
+ "deleteAccount",
829
+ "cooling",
830
+ "cooldownLeft",
831
+ "clearCooldown",
832
+ "needsRelogin",
833
+ "relogin",
834
+ "rotationStrategy",
835
+ "strategySequential",
836
+ "strategyRoundRobin",
837
+ "strategySticky",
838
+ "noAccounts",
839
+ "storage",
840
+ "storageNotice",
841
+ "email",
842
+ "expires",
843
+ "lastUsed",
844
+ "accountId"
845
+ ];
846
+ const labels = {};
847
+ for (const key of keys) labels[key] = t(key);
848
+ return labels;
849
+ })();
850
+ const accountAction = async (action, accountId) => {
851
+ await run(action, async () => {
852
+ const next = await apiRef.current.accountAction(action, { accountId });
853
+ setStatus(next);
854
+ });
855
+ };
856
+ const setRotationStrategy = async (strategy) => {
857
+ await run("strategy", async () => {
858
+ const next = await apiRef.current.accountAction("strategy", { strategy });
859
+ setStatus(next);
860
+ });
861
+ };
862
+ /**
863
+ * Re-authorize one account.
864
+ *
865
+ * The failure marker is cleared first and nothing is deleted: the account
866
+ * keeps its alias and place in the rotation.
867
+ */
868
+ const relogin = (accountId) => {
869
+ (async () => {
870
+ try {
871
+ const next = await apiRef.current.accountAction("relogin", { accountId });
872
+ setStatus(next);
873
+ } catch (cause) {
874
+ setError(messageOf(cause));
875
+ return;
876
+ }
877
+ await startLogin();
878
+ })();
879
+ };
539
880
  const run = async (action, task) => {
540
881
  setBusy(action);
541
882
  setError(null);
@@ -548,14 +889,14 @@ window.__ModuleLoader__.load({
548
889
  setBusy(null);
549
890
  }
550
891
  };
551
- const account = status?.account;
892
+ status?.account;
552
893
  const preferences = status?.preferences;
553
894
  const quota = status?.quota;
554
895
  const storage = status?.storage;
555
896
  const login = status?.login;
556
897
  const visibleModelIds = preferences?.visibleModelIds ?? DEFAULT_VISIBLE_CODEX_MODEL_IDS;
557
898
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
558
- className: "dsh-codex-page",
899
+ className: "dsha-page",
559
900
  "aria-labelledby": "dsh-codex-title",
560
901
  children: [
561
902
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
@@ -576,37 +917,36 @@ window.__ModuleLoader__.load({
576
917
  }) : null]
577
918
  }) : null,
578
919
  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: [
579
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Section, {
580
- title: t("account"),
920
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(AccountPoolSection, {
921
+ accounts: status?.accounts ?? [],
922
+ activeAccountId: status?.activeAccountId,
923
+ rotationStrategy: status?.rotationStrategy ?? "sequential",
924
+ busy,
925
+ labels: poolLabels,
926
+ loginBusyLabel: login?.active ? t("pending") : void 0,
927
+ onLogin: () => void startLogin(),
928
+ onSetPrimary: (accountId) => void accountAction("set-primary", accountId),
929
+ onDelete: (accountId) => void accountAction("delete", accountId),
930
+ onClearCooldown: (accountId) => void accountAction("clear-cooldown", accountId),
931
+ onRelogin: (accountId) => relogin(accountId),
932
+ onSetStrategy: (strategy) => void setRotationStrategy(strategy),
933
+ storageValue: storageLabel(storage, t),
934
+ renderDetails: (entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [entry.planLabel && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
935
+ t("plan"),
936
+ ": ",
937
+ entry.planLabel
938
+ ] }), entry.email && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
939
+ t("email"),
940
+ ": ",
941
+ entry.email
942
+ ] })] }),
581
943
  children: [
582
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoRow, {
583
- label: status?.authenticated ? t("signedIn") : t("signedOut"),
584
- value: account?.email ?? "—"
585
- }),
586
- status?.authenticated ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
587
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoRow, {
588
- label: t("plan"),
589
- value: account?.planType ?? t("unknown")
590
- }),
591
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoRow, {
592
- label: t("accountId"),
593
- value: account?.accountIdSuffix ?? "—"
594
- }),
595
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoRow, {
596
- label: t("expires"),
597
- value: formatDate$3(account?.tokenExpiresAt)
598
- })
599
- ] }) : null,
600
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoRow, {
601
- label: t("storage"),
602
- value: storageLabel(storage, t)
603
- }),
604
944
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
605
- className: "dsh-codex-notice",
945
+ className: "dsha-notice",
606
946
  children: storageNotice(storage, t)
607
947
  }),
608
948
  login?.active ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
609
- className: "dsh-codex-muted",
949
+ className: "dsha-muted",
610
950
  role: "status",
611
951
  children: t("pending")
612
952
  }) : null,
@@ -622,31 +962,42 @@ window.__ModuleLoader__.load({
622
962
  children: t("continueLogin")
623
963
  }) : null,
624
964
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
625
- className: "dsh-codex-actions",
965
+ className: "dsha-actions",
626
966
  children: [login?.active ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
627
967
  disabled: busy !== null,
628
968
  onClick: cancelLogin,
629
969
  children: t("cancel")
630
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
631
- primary: true,
632
- disabled: busy !== null || storage?.available === false,
633
- onClick: startLogin,
634
- children: status?.authenticated ? t("signInAgain") : t("signIn")
635
- }), status?.authenticated ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
970
+ }) : null, status?.authenticated ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
636
971
  disabled: busy !== null,
637
972
  onClick: refreshToken,
638
973
  children: t("refreshToken")
639
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
640
- disabled: busy !== null,
641
- onClick: logout,
642
- children: t("signOut")
643
- })] }) : null]
974
+ }) : null]
644
975
  })
645
976
  ]
646
977
  }),
647
978
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Section, {
648
979
  title: t("connection"),
649
980
  children: [
981
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
982
+ className: "dsha-pref-row",
983
+ style: { marginBottom: 12 },
984
+ 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", {
985
+ type: "checkbox",
986
+ checked: preferences?.enabled ?? true,
987
+ disabled: busy !== null,
988
+ onChange: (event) => {
989
+ const enabled = event.currentTarget.checked;
990
+ setStatus((cur) => cur === null ? cur : {
991
+ ...cur,
992
+ preferences: {
993
+ ...cur.preferences,
994
+ enabled
995
+ }
996
+ });
997
+ updatePreferences({ enabled });
998
+ }
999
+ })]
1000
+ }),
650
1001
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoRow, {
651
1002
  label: t("provider"),
652
1003
  value: "Codex(ChatGPT 订阅) · codex-chatgpt"
@@ -657,31 +1008,47 @@ window.__ModuleLoader__.load({
657
1008
  }),
658
1009
  connection !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoRow, {
659
1010
  label: t("latency"),
660
- value: `${connection.latencyMs} ms · ${formatDate$3(connection.checkedAt)}`
1011
+ value: `${connection.latencyMs} ms · ${formatDate$2(connection.checkedAt)}`
661
1012
  }) : null,
662
1013
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
663
- className: "dsh-codex-muted dsh-codex-models-hint",
1014
+ className: "dsha-muted dsha-models-hint",
664
1015
  children: t("modelsHint")
665
1016
  }),
666
1017
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
667
- className: "dsh-codex-models",
1018
+ className: "dsha-models",
668
1019
  "aria-label": t("models"),
669
1020
  children: CODEX_MODEL_CATALOG.map((model) => {
670
1021
  const checked = visibleModelIds.some((id) => id === model.id);
671
- const lastVisible = checked && visibleModelIds.length === 1;
672
1022
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
673
1023
  title: model.id,
674
1024
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
675
1025
  type: "checkbox",
676
1026
  checked,
677
- disabled: busy !== null || lastVisible,
1027
+ disabled: busy !== null,
678
1028
  onChange: (event) => void toggleVisibleModel(model.id, event.currentTarget.checked)
679
1029
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: model.name })]
680
1030
  }, model.id);
681
1031
  })
682
1032
  }),
1033
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1034
+ style: {
1035
+ display: "flex",
1036
+ gap: 8,
1037
+ marginTop: 8,
1038
+ marginBottom: 12
1039
+ },
1040
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
1041
+ disabled: busy !== null,
1042
+ onClick: () => void setAllVisibleModels(true),
1043
+ children: t("selectAll")
1044
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
1045
+ disabled: busy !== null,
1046
+ onClick: () => void setAllVisibleModels(false),
1047
+ children: t("unselectAll")
1048
+ })]
1049
+ }),
683
1050
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
684
- className: "dsh-codex-actions",
1051
+ className: "dsha-actions",
685
1052
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
686
1053
  disabled: !status?.authenticated || busy !== null,
687
1054
  onClick: testConnection,
@@ -694,12 +1061,12 @@ window.__ModuleLoader__.load({
694
1061
  title: t("proxySettings"),
695
1062
  children: [
696
1063
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
697
- className: "dsh-codex-pref-row",
1064
+ className: "dsha-pref-row",
698
1065
  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", {
699
- className: "dsh-codex-muted",
1066
+ className: "dsha-muted",
700
1067
  children: t("proxyModeHint")
701
1068
  })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
702
- className: "dsh-codex-select",
1069
+ className: "dsha-select",
703
1070
  "aria-label": t("proxyMode"),
704
1071
  value: preferences?.proxyMode ?? "auto",
705
1072
  disabled: busy !== null,
@@ -743,22 +1110,22 @@ window.__ModuleLoader__.load({
743
1110
  children: t("proxyDetectedEffective")
744
1111
  })
745
1112
  ] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
746
- className: "dsh-codex-muted",
1113
+ className: "dsha-muted",
747
1114
  children: t("proxyNoneDetected")
748
1115
  })
749
1116
  }) : null,
750
1117
  preferences?.proxyMode === "direct" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
751
- className: "dsh-codex-muted",
1118
+ className: "dsha-muted",
752
1119
  style: { margin: "8px 0 0" },
753
1120
  children: t("proxyDirectHint")
754
1121
  }) : null,
755
1122
  preferences?.proxyMode === "custom" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
756
- className: "dsh-codex-context-settings",
1123
+ className: "dsha-context-settings",
757
1124
  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", {
758
- className: "dsh-codex-muted",
1125
+ className: "dsha-muted",
759
1126
  children: t("customProxyUrlHint")
760
1127
  })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
761
- className: "dsh-codex-context-row",
1128
+ className: "dsha-context-row",
762
1129
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
763
1130
  className: "dsh-codex-proxy-control",
764
1131
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
@@ -777,7 +1144,7 @@ window.__ModuleLoader__.load({
777
1144
  updateCustomProxyUrl();
778
1145
  }
779
1146
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
780
- className: "dsh-codex-context-save",
1147
+ className: "dsha-context-save",
781
1148
  type: "button",
782
1149
  "aria-label": t("saveProxyUrl"),
783
1150
  disabled: busy !== null || customProxyDraft === null,
@@ -793,9 +1160,9 @@ window.__ModuleLoader__.load({
793
1160
  title: t("enhancements"),
794
1161
  children: [
795
1162
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
796
- className: "dsh-codex-pref-row",
1163
+ className: "dsha-pref-row",
797
1164
  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", {
798
- className: "dsh-codex-muted",
1165
+ className: "dsha-muted",
799
1166
  children: t("searchProviderHint")
800
1167
  })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
801
1168
  className: "dsh-codex-segments",
@@ -819,12 +1186,12 @@ window.__ModuleLoader__.load({
819
1186
  })]
820
1187
  }),
821
1188
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
822
- className: "dsh-codex-pref-row",
1189
+ className: "dsha-pref-row",
823
1190
  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", {
824
- className: "dsh-codex-muted",
1191
+ className: "dsha-muted",
825
1192
  children: t("outputVerbosityHint")
826
1193
  })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
827
- className: "dsh-codex-select",
1194
+ className: "dsha-select",
828
1195
  "aria-label": t("outputVerbosity"),
829
1196
  value: preferences?.outputVerbosity ?? "",
830
1197
  disabled: busy !== null,
@@ -853,12 +1220,12 @@ window.__ModuleLoader__.load({
853
1220
  })]
854
1221
  }),
855
1222
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
856
- className: "dsh-codex-pref-row",
1223
+ className: "dsha-pref-row",
857
1224
  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", {
858
- className: "dsh-codex-muted",
1225
+ className: "dsha-muted",
859
1226
  children: t("reasoningSummaryHint")
860
1227
  })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
861
- className: "dsh-codex-select",
1228
+ className: "dsha-select",
862
1229
  "aria-label": t("reasoningSummary"),
863
1230
  value: preferences?.reasoningSummary ?? "",
864
1231
  disabled: busy !== null,
@@ -891,9 +1258,9 @@ window.__ModuleLoader__.load({
891
1258
  })]
892
1259
  }),
893
1260
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
894
- className: "dsh-codex-context-settings",
1261
+ className: "dsha-context-settings",
895
1262
  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", {
896
- className: "dsh-codex-muted",
1263
+ className: "dsha-muted",
897
1264
  children: t("contextWindowsHint")
898
1265
  })] }), CONFIGURABLE_CONTEXT_MODEL_IDS.map((model) => {
899
1266
  const entry = resolveCodexCatalogEntry(model);
@@ -903,12 +1270,12 @@ window.__ModuleLoader__.load({
903
1270
  const dirty = draft !== void 0 && parsedDraft !== fallback;
904
1271
  const inputId = `dsh-codex-context-${model}`;
905
1272
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
906
- className: "dsh-codex-context-row",
1273
+ className: "dsha-context-row",
907
1274
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
908
1275
  htmlFor: inputId,
909
1276
  children: entry.name
910
1277
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
911
- className: "dsh-codex-capacity-control",
1278
+ className: "dsha-capacity-control",
912
1279
  children: [
913
1280
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
914
1281
  id: inputId,
@@ -932,7 +1299,7 @@ window.__ModuleLoader__.load({
932
1299
  }),
933
1300
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: t("tokens") }),
934
1301
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
935
- className: "dsh-codex-context-save",
1302
+ className: "dsha-context-save",
936
1303
  type: "button",
937
1304
  "data-model": model,
938
1305
  "aria-label": entry.name + " " + t("saveContextWindow"),
@@ -974,7 +1341,7 @@ window.__ModuleLoader__.load({
974
1341
  }),
975
1342
  children: [
976
1343
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
977
- className: "dsh-codex-muted",
1344
+ className: "dsha-muted",
978
1345
  children: t("quotaIntro")
979
1346
  }),
980
1347
  quota?.state === "signed-out" || !status?.authenticated ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
@@ -1028,7 +1395,7 @@ window.__ModuleLoader__.load({
1028
1395
  children: [
1029
1396
  t("updated"),
1030
1397
  ": ",
1031
- formatDate$3(quota.fetchedAt)
1398
+ formatDate$2(quota.fetchedAt)
1032
1399
  ]
1033
1400
  }) : null
1034
1401
  ]
@@ -1044,16 +1411,16 @@ window.__ModuleLoader__.load({
1044
1411
  }
1045
1412
  function Section({ title, aside, children }) {
1046
1413
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1047
- className: "dsh-codex-group",
1414
+ className: "dsha-group",
1048
1415
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1049
- className: "dsh-codex-grouphead",
1416
+ className: "dsha-grouphead",
1050
1417
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: title }), aside]
1051
1418
  }), children]
1052
1419
  });
1053
1420
  }
1054
1421
  function Button({ primary = false, disabled, onClick, children }) {
1055
1422
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1056
- className: `dsh-codex-button${primary ? " dsh-codex-button-primary" : ""}`,
1423
+ className: `dsha-btn${primary ? " dsha-btn-primary" : ""}`,
1057
1424
  type: "button",
1058
1425
  disabled,
1059
1426
  onClick: () => void onClick(),
@@ -1062,12 +1429,12 @@ window.__ModuleLoader__.load({
1062
1429
  }
1063
1430
  function InfoRow({ label, value }) {
1064
1431
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1065
- className: "dsh-codex-row",
1432
+ className: "dsha-row",
1066
1433
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1067
- className: "dsh-codex-label",
1434
+ className: "dsha-label",
1068
1435
  children: label
1069
1436
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1070
- className: "dsh-codex-value",
1437
+ className: "dsha-value",
1071
1438
  children: value
1072
1439
  })]
1073
1440
  });
@@ -1211,7 +1578,7 @@ window.__ModuleLoader__.load({
1211
1578
  ].filter((part) => part !== null);
1212
1579
  return parts.length > 0 ? parts.join(" · ") : t("unknown");
1213
1580
  }
1214
- function formatDate$3(seconds) {
1581
+ function formatDate$2(seconds) {
1215
1582
  if (seconds === void 0 || seconds === null || typeof seconds !== "number" || !Number.isFinite(seconds) || seconds <= 0) return "—";
1216
1583
  const ms = seconds > 1e10 ? seconds : seconds * 1e3;
1217
1584
  try {
@@ -1226,7 +1593,7 @@ window.__ModuleLoader__.load({
1226
1593
  function formatReset$2(seconds) {
1227
1594
  if (seconds === void 0 || seconds === null || typeof seconds !== "number" || !Number.isFinite(seconds) || seconds <= 0) return "—";
1228
1595
  const ms = seconds > 1e10 ? seconds : seconds * 1e3;
1229
- const absolute = formatDate$3(seconds);
1596
+ const absolute = formatDate$2(seconds);
1230
1597
  const diff = ms - Date.now();
1231
1598
  const abs = Math.abs(diff);
1232
1599
  const [amount, unit] = abs >= 864e5 ? [Math.round(diff / 864e5), "day"] : abs >= 36e5 ? [Math.round(diff / 36e5), "hour"] : [Math.round(diff / 6e4), "minute"];
@@ -1243,11 +1610,25 @@ window.__ModuleLoader__.load({
1243
1610
  //#region src/client/antigravity/locales.ts
1244
1611
  const NS_ANTIGRAVITY = "dsh-antigravity";
1245
1612
  const zh$3 = {
1613
+ ...accountPoolZh,
1246
1614
  title: "Antigravity",
1247
1615
  pageDesc: "登录 Google Antigravity / Cloud Code Assist,并查看当前账号的共享额度。",
1248
1616
  account: "已登录",
1249
1617
  signedIn: "已登录",
1250
1618
  signedOut: "未登录",
1619
+ accountPool: "账号管理",
1620
+ addAccount: "添加账号",
1621
+ primaryAccount: "主账号",
1622
+ activeAccount: "当前使用",
1623
+ setPrimary: "设为主账号",
1624
+ deleteAccount: "删除",
1625
+ cooling: "冷却中",
1626
+ clearCooldown: "重置冷却",
1627
+ rotationStrategy: "调度策略",
1628
+ strategySequential: "顺序耗尽(当前优先,遇限流自动切号)",
1629
+ strategyRoundRobin: "轮询调度(按账号循环均匀分摊)",
1630
+ strategySticky: "会话粘性(保持当前账号直到被限流)",
1631
+ noAccounts: "暂无账号,点击「添加账号」完成登录授权。",
1251
1632
  plan: "套餐",
1252
1633
  accountId: "账号 ID",
1253
1634
  expires: "令牌到期",
@@ -1264,7 +1645,8 @@ window.__ModuleLoader__.load({
1264
1645
  connectionState: "连接状态",
1265
1646
  connected: "已连接",
1266
1647
  untested: "未测试",
1267
- modelsHint: "勾选后模型才会显示在对话页的模型选择列表中;至少保留一个。",
1648
+ enableProvider: "启用此供应商",
1649
+ modelsHint: "勾选后模型才会显示在对话页的模型选择列表中;全部取消勾选或关闭上方开关可在列表中隐藏该供应商。",
1268
1650
  selectAll: "全选",
1269
1651
  unselectAll: "全不选",
1270
1652
  enhanced: "增强功能",
@@ -1294,11 +1676,25 @@ window.__ModuleLoader__.load({
1294
1676
  tokens: "tokens"
1295
1677
  };
1296
1678
  const en$3 = {
1679
+ ...accountPoolEn,
1297
1680
  title: "Antigravity",
1298
1681
  pageDesc: "Sign in to Google Antigravity / Cloud Code Assist and view shared quotas.",
1299
1682
  account: "Signed in",
1300
1683
  signedIn: "Signed in",
1301
1684
  signedOut: "Not signed in",
1685
+ accountPool: "Account Management",
1686
+ addAccount: "Add Account",
1687
+ primaryAccount: "Primary",
1688
+ activeAccount: "In Use",
1689
+ setPrimary: "Set as Primary",
1690
+ deleteAccount: "Delete",
1691
+ cooling: "In Cooldown",
1692
+ clearCooldown: "Clear Cooldown",
1693
+ rotationStrategy: "Scheduling Strategy",
1694
+ strategySequential: "Sequential Drain (Primary first, failover on 429)",
1695
+ strategyRoundRobin: "Round-Robin (Evenly rotate across accounts)",
1696
+ strategySticky: "Sticky session (hold the current account until it is limited)",
1697
+ noAccounts: "No accounts yet. Click \"Add Account\" to authorize.",
1302
1698
  plan: "Plan",
1303
1699
  accountId: "Account ID",
1304
1700
  expires: "Token expires",
@@ -1315,7 +1711,8 @@ window.__ModuleLoader__.load({
1315
1711
  connectionState: "Connection state",
1316
1712
  connected: "Connected",
1317
1713
  untested: "Untested",
1318
- modelsHint: "Checked models will appear in DSH conversation model list; keep at least one.",
1714
+ enableProvider: "Enable this provider",
1715
+ modelsHint: "Checked models will appear in DSH conversation model list; unchecking all or turning off the switch hides this provider.",
1319
1716
  selectAll: "Select all",
1320
1717
  unselectAll: "Deselect all",
1321
1718
  enhanced: "Enhanced Features",
@@ -1388,7 +1785,7 @@ window.__ModuleLoader__.load({
1388
1785
  if (hours > 0) return `${hours}h ${mins % 60}m`;
1389
1786
  return `${mins}m`;
1390
1787
  }
1391
- function formatDate$2(ms) {
1788
+ function formatDate$1(ms) {
1392
1789
  if (!ms || ms <= 0) return "—";
1393
1790
  try {
1394
1791
  return new Intl.DateTimeFormat(void 0, {
@@ -1495,11 +1892,36 @@ window.__ModuleLoader__.load({
1495
1892
  setBusy(null);
1496
1893
  }
1497
1894
  };
1498
- const handleLogout = async () => {
1895
+ const handleSetPrimary = async (accountId) => {
1499
1896
  try {
1500
- setBusy("logout");
1897
+ setBusy(`primary-${accountId}`);
1501
1898
  setError(null);
1502
- const updated = await fetchApi$5("/logout", { method: "POST" });
1899
+ const updated = await fetchApi$5("/accounts", {
1900
+ method: "POST",
1901
+ body: JSON.stringify({
1902
+ action: "set-primary",
1903
+ accountId
1904
+ })
1905
+ });
1906
+ setStatus(updated);
1907
+ notifyChange();
1908
+ } catch (err) {
1909
+ setError(err instanceof Error ? err.message : String(err));
1910
+ } finally {
1911
+ setBusy(null);
1912
+ }
1913
+ };
1914
+ const handleDeleteAccount = async (accountId) => {
1915
+ try {
1916
+ setBusy(`delete-${accountId}`);
1917
+ setError(null);
1918
+ const updated = await fetchApi$5("/accounts", {
1919
+ method: "POST",
1920
+ body: JSON.stringify({
1921
+ action: "delete",
1922
+ accountId
1923
+ })
1924
+ });
1503
1925
  setStatus(updated);
1504
1926
  notifyChange();
1505
1927
  } catch (err) {
@@ -1508,11 +1930,63 @@ window.__ModuleLoader__.load({
1508
1930
  setBusy(null);
1509
1931
  }
1510
1932
  };
1933
+ const handleClearCooldown = async (accountId) => {
1934
+ try {
1935
+ setBusy(`cooldown-${accountId}`);
1936
+ setError(null);
1937
+ const updated = await fetchApi$5("/accounts", {
1938
+ method: "POST",
1939
+ body: JSON.stringify({
1940
+ action: "clear-cooldown",
1941
+ accountId
1942
+ })
1943
+ });
1944
+ setStatus(updated);
1945
+ } catch (err) {
1946
+ setError(err instanceof Error ? err.message : String(err));
1947
+ } finally {
1948
+ setBusy(null);
1949
+ }
1950
+ };
1951
+ const handleSetStrategy = async (strategy) => {
1952
+ try {
1953
+ setBusy("strategy");
1954
+ setError(null);
1955
+ const updated = await fetchApi$5("/accounts", {
1956
+ method: "POST",
1957
+ body: JSON.stringify({
1958
+ action: "strategy",
1959
+ strategy
1960
+ })
1961
+ });
1962
+ setStatus(updated);
1963
+ } catch (err) {
1964
+ setError(err instanceof Error ? err.message : String(err));
1965
+ } finally {
1966
+ setBusy(null);
1967
+ }
1968
+ };
1969
+ const toggleEnabled = async (enabled) => {
1970
+ setStatus((prev) => prev ? {
1971
+ ...prev,
1972
+ enabled
1973
+ } : prev);
1974
+ try {
1975
+ const updated = await fetchApi$5("/settings", {
1976
+ method: "POST",
1977
+ body: JSON.stringify({ enabled })
1978
+ });
1979
+ setStatus(updated);
1980
+ notifyChange();
1981
+ } catch (err) {
1982
+ setError(err instanceof Error ? err.message : String(err));
1983
+ loadStatus(true);
1984
+ }
1985
+ };
1511
1986
  const toggleModel = async (modelId, checked) => {
1512
1987
  if (!status) return;
1513
1988
  const currentEnabled = status.models.filter((m) => m.enabled).map((m) => m.id);
1514
1989
  const nextEnabled = checked ? [.../* @__PURE__ */ new Set([...currentEnabled, modelId])] : currentEnabled.filter((id) => id !== modelId);
1515
- if (nextEnabled.length === 0) return;
1516
1990
  try {
1517
1991
  const updated = await fetchApi$5("/models", {
1518
1992
  method: "POST",
@@ -1526,7 +2000,7 @@ window.__ModuleLoader__.load({
1526
2000
  };
1527
2001
  const setAllModels = async (selectAll) => {
1528
2002
  if (!status) return;
1529
- const nextEnabled = selectAll ? status.models.map((m) => m.id) : [status.models[0].id];
2003
+ const nextEnabled = selectAll ? status.models.map((m) => m.id) : [];
1530
2004
  try {
1531
2005
  const updated = await fetchApi$5("/models", {
1532
2006
  method: "POST",
@@ -1585,109 +2059,65 @@ window.__ModuleLoader__.load({
1585
2059
  });
1586
2060
  const quota = status?.quota;
1587
2061
  const groups = quota?.groups || [];
1588
- const visibleCount = status?.models.filter((m) => m.enabled).length || 0;
2062
+ status?.models.filter((m) => m.enabled).length;
1589
2063
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1590
2064
  className: "dsha-page",
1591
2065
  children: [
2066
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountPoolSection, {
2067
+ accounts: status?.accounts ?? [],
2068
+ activeAccountId: status?.activeAccountId,
2069
+ rotationStrategy: status?.rotationStrategy ?? "sequential",
2070
+ busy,
2071
+ labels: t,
2072
+ loginBusyLabel: busy === "login" ? loginProgress || t.signingIn : void 0,
2073
+ onLogin: () => void handleLogin(),
2074
+ onSetPrimary: (accountId) => void handleSetPrimary(accountId),
2075
+ onDelete: (accountId) => void handleDeleteAccount(accountId),
2076
+ onClearCooldown: (accountId) => void handleClearCooldown(accountId),
2077
+ onSetStrategy: (strategy) => void handleSetStrategy(strategy),
2078
+ renderDetails: (account) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
2079
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
2080
+ t.accountId,
2081
+ ": ",
2082
+ account.projectId || "antigravity-default"
2083
+ ] }),
2084
+ account.email && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
2085
+ t.email,
2086
+ ": ",
2087
+ account.email
2088
+ ] }),
2089
+ account.expiresAt && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
2090
+ t.expires,
2091
+ ": ",
2092
+ formatDate$1(account.expiresAt)
2093
+ ] }),
2094
+ account.lastUsedAt && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
2095
+ t.lastUsed,
2096
+ ": ",
2097
+ formatDate$1(account.lastUsedAt)
2098
+ ] })
2099
+ ] })
2100
+ }),
1592
2101
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1593
2102
  className: "dsha-group",
1594
2103
  children: [
1595
2104
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1596
2105
  className: "dsha-grouphead",
1597
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: status?.authenticated ? t.account : t.signedOut })
1598
- }),
1599
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1600
- className: "dsha-row",
1601
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1602
- className: "dsha-label",
1603
- children: status?.authenticated ? t.signedIn : t.signedOut
1604
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1605
- className: "dsha-value",
1606
- children: status?.email || "—"
1607
- })]
2106
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t.connection })
1608
2107
  }),
1609
- status?.authenticated && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
1610
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1611
- className: "dsha-row",
1612
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1613
- className: "dsha-label",
1614
- children: t.plan
1615
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1616
- className: "dsha-value",
1617
- children: quota?.planLabel || "Google AI Ultra"
1618
- })]
1619
- }),
1620
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1621
- className: "dsha-row",
1622
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1623
- className: "dsha-label",
1624
- children: t.accountId
1625
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1626
- className: "dsha-value",
1627
- children: status.projectId || "antigravity-default"
1628
- })]
1629
- }),
1630
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1631
- className: "dsha-row",
1632
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1633
- className: "dsha-label",
1634
- children: t.expires
1635
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1636
- className: "dsha-value",
1637
- children: formatDate$2(Date.now() + 864e5 * 30)
1638
- })]
1639
- })
1640
- ] }),
1641
2108
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1642
2109
  className: "dsha-row",
2110
+ style: { marginBottom: 12 },
1643
2111
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1644
2112
  className: "dsha-label",
1645
- children: t.storage
1646
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1647
- className: "dsha-value",
1648
- children: "本地安全存储 (JSON)"
1649
- })]
1650
- }),
1651
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1652
- className: "dsha-notice",
1653
- children: t.storageNotice
1654
- }),
1655
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1656
- className: "dsha-actions",
1657
- children: !status?.authenticated ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1658
- className: "dsha-btn dsha-btn-primary",
2113
+ style: { fontWeight: 600 },
2114
+ children: t.enableProvider
2115
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2116
+ type: "checkbox",
2117
+ checked: status?.enabled !== false,
1659
2118
  disabled: busy !== null,
1660
- onClick: handleLogin,
1661
- children: busy === "login" ? loginProgress || t.signingIn : t.signIn
1662
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
1663
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1664
- className: "dsha-btn dsha-btn-primary",
1665
- disabled: busy !== null,
1666
- onClick: handleLogin,
1667
- children: busy === "login" ? loginProgress || t.signingIn : t.signInAgain
1668
- }),
1669
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1670
- className: "dsha-btn",
1671
- disabled: busy !== null,
1672
- onClick: handleRefreshQuota,
1673
- children: busy === "quota" ? t.refreshingQuota : t.refreshToken
1674
- }),
1675
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1676
- className: "dsha-btn",
1677
- disabled: busy !== null,
1678
- onClick: handleLogout,
1679
- children: t.signOut
1680
- })
1681
- ] })
1682
- })
1683
- ]
1684
- }),
1685
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1686
- className: "dsha-group",
1687
- children: [
1688
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1689
- className: "dsha-grouphead",
1690
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t.connection })
2119
+ onChange: (e) => void toggleEnabled(e.currentTarget.checked)
2120
+ })]
1691
2121
  }),
1692
2122
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1693
2123
  className: "dsha-row",
@@ -1718,13 +2148,12 @@ window.__ModuleLoader__.load({
1718
2148
  "aria-label": "Antigravity Models",
1719
2149
  children: status?.models.map((model) => {
1720
2150
  const checked = model.enabled;
1721
- const lastVisible = checked && visibleCount === 1;
1722
2151
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
1723
2152
  title: model.id,
1724
2153
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1725
2154
  type: "checkbox",
1726
2155
  checked,
1727
- disabled: busy !== null || lastVisible,
2156
+ disabled: busy !== null,
1728
2157
  onChange: (e) => void toggleModel(model.id, e.currentTarget.checked)
1729
2158
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: model.name })]
1730
2159
  }, model.id);
@@ -1922,6 +2351,7 @@ window.__ModuleLoader__.load({
1922
2351
  //#region src/client/command-code/locales.ts
1923
2352
  const NS_COMMAND_CODE = "dsh-command-code";
1924
2353
  const zh$2 = {
2354
+ ...accountPoolZh,
1925
2355
  title: "Command Code",
1926
2356
  pageDesc: "登录 Command Code,管理可用模型、上下文窗口与额度。",
1927
2357
  account: "账号",
@@ -1959,7 +2389,8 @@ window.__ModuleLoader__.load({
1959
2389
  routeOwned: "模型路由由本插件提供",
1960
2390
  routeConflict: "模型路由已被其他 Provider 占用:{detail}。请在 DSH 设置中移除重复的 Provider 配置后重试。",
1961
2391
  modelsSection: "模型",
1962
- modelsHint: "勾选后模型才会出现在对话页的模型选择列表中;至少保留一个。",
2392
+ enableProvider: "启用此供应商",
2393
+ modelsHint: "勾选后模型才会出现在对话页的模型选择列表中;全部取消勾选或关闭上方开关可在列表中隐藏该供应商。",
1963
2394
  refreshCatalog: "刷新模型目录",
1964
2395
  refreshingCatalog: "刷新中...",
1965
2396
  selectAll: "全选",
@@ -1988,6 +2419,7 @@ window.__ModuleLoader__.load({
1988
2419
  tokens: "tokens"
1989
2420
  };
1990
2421
  const en$2 = {
2422
+ ...accountPoolEn,
1991
2423
  title: "Command Code",
1992
2424
  pageDesc: "Sign in to Command Code and manage models, context windows, and quota.",
1993
2425
  account: "Account",
@@ -2025,7 +2457,8 @@ window.__ModuleLoader__.load({
2025
2457
  routeOwned: "This plugin serves the model route",
2026
2458
  routeConflict: "Another provider already owns the model route: {detail}. Remove the duplicate provider configuration in DSH settings and reload.",
2027
2459
  modelsSection: "Models",
2028
- modelsHint: "Checked models appear in the DSH conversation model list; keep at least one.",
2460
+ enableProvider: "Enable this provider",
2461
+ modelsHint: "Checked models appear in the DSH conversation model list; unchecking all or turning off the switch hides this provider.",
2029
2462
  refreshCatalog: "Refresh catalog",
2030
2463
  refreshingCatalog: "Refreshing...",
2031
2464
  selectAll: "Select all",
@@ -2099,17 +2532,6 @@ window.__ModuleLoader__.load({
2099
2532
  if (value >= 1e3 && value % 1e3 === 0) return `${value / 1e3}K`;
2100
2533
  return String(value);
2101
2534
  }
2102
- function formatDate$1(ms) {
2103
- if (ms === void 0 || ms === null || ms <= 0) return "—";
2104
- try {
2105
- return new Intl.DateTimeFormat(void 0, {
2106
- dateStyle: "medium",
2107
- timeStyle: "short"
2108
- }).format(ms);
2109
- } catch {
2110
- return "—";
2111
- }
2112
- }
2113
2535
  function formatReset$1(resetsAt) {
2114
2536
  if (resetsAt === void 0 || resetsAt === null || resetsAt <= 0) return "";
2115
2537
  const diff = resetsAt - Date.now();
@@ -2216,11 +2638,18 @@ window.__ModuleLoader__.load({
2216
2638
  setBusy(null);
2217
2639
  }
2218
2640
  };
2219
- const handleLogout = async () => {
2641
+ /** One account-level action on the shared pool card. */
2642
+ const handleAccountAction = async (action, accountId) => {
2220
2643
  try {
2221
- setBusy("logout");
2644
+ setBusy(`${action}-${accountId}`);
2222
2645
  setError(null);
2223
- const updated = await fetchApi$4("/logout", { method: "POST" });
2646
+ const updated = await fetchApi$4("/accounts", {
2647
+ method: "POST",
2648
+ body: JSON.stringify({
2649
+ action,
2650
+ accountId
2651
+ })
2652
+ });
2224
2653
  setStatus(updated);
2225
2654
  notifyChange();
2226
2655
  } catch (err) {
@@ -2229,6 +2658,48 @@ window.__ModuleLoader__.load({
2229
2658
  setBusy(null);
2230
2659
  }
2231
2660
  };
2661
+ const handleSetStrategy = async (strategy) => {
2662
+ try {
2663
+ setBusy("strategy");
2664
+ setError(null);
2665
+ const updated = await fetchApi$4("/accounts", {
2666
+ method: "POST",
2667
+ body: JSON.stringify({
2668
+ action: "strategy",
2669
+ strategy
2670
+ })
2671
+ });
2672
+ setStatus(updated);
2673
+ } catch (err) {
2674
+ setError(err instanceof Error ? err.message : String(err));
2675
+ } finally {
2676
+ setBusy(null);
2677
+ }
2678
+ };
2679
+ /**
2680
+ * Re-authorize one key.
2681
+ *
2682
+ * Nothing is deleted: the failure marker is cleared and the ordinary sign-in
2683
+ * flow runs again, so the account keeps its alias and place in the rotation.
2684
+ */
2685
+ const handleRelogin = (accountId) => {
2686
+ (async () => {
2687
+ try {
2688
+ const updated = await fetchApi$4("/accounts", {
2689
+ method: "POST",
2690
+ body: JSON.stringify({
2691
+ action: "relogin",
2692
+ accountId
2693
+ })
2694
+ });
2695
+ setStatus(updated);
2696
+ } catch (err) {
2697
+ setError(err instanceof Error ? err.message : String(err));
2698
+ return;
2699
+ }
2700
+ await handleLogin();
2701
+ })();
2702
+ };
2232
2703
  const handleRefreshQuota = async () => {
2233
2704
  try {
2234
2705
  setBusy("quota");
@@ -2281,16 +2752,32 @@ window.__ModuleLoader__.load({
2281
2752
  setError(err instanceof Error ? err.message : String(err));
2282
2753
  }
2283
2754
  };
2755
+ const toggleEnabled = async (enabled) => {
2756
+ setStatus((prev) => prev ? {
2757
+ ...prev,
2758
+ enabled
2759
+ } : prev);
2760
+ try {
2761
+ const updated = await fetchApi$4("/settings", {
2762
+ method: "POST",
2763
+ body: JSON.stringify({ enabled })
2764
+ });
2765
+ setStatus(updated);
2766
+ notifyChange();
2767
+ } catch (err) {
2768
+ setError(err instanceof Error ? err.message : String(err));
2769
+ loadStatus(true);
2770
+ }
2771
+ };
2284
2772
  const toggleModel = (modelId, checked) => {
2285
2773
  if (!status) return;
2286
2774
  const current = status.models.filter((model) => model.enabled).map((model) => model.id);
2287
2775
  const next = checked ? [.../* @__PURE__ */ new Set([...current, modelId])] : current.filter((id) => id !== modelId);
2288
- if (next.length === 0) return;
2289
2776
  applyEnabled(next);
2290
2777
  };
2291
2778
  const setAllModels = (selectAll) => {
2292
2779
  if (!status || status.models.length === 0) return;
2293
- applyEnabled(selectAll ? status.models.map((model) => model.id) : [status.models[0].id]);
2780
+ applyEnabled(selectAll ? status.models.map((model) => model.id) : []);
2294
2781
  };
2295
2782
  const handleUpdateEffort = async (effort) => {
2296
2783
  try {
@@ -2338,162 +2825,78 @@ window.__ModuleLoader__.load({
2338
2825
  })
2339
2826
  });
2340
2827
  const quota = status?.quota;
2341
- const account = status?.account;
2342
- const visibleCount = status?.models.filter((model) => model.enabled).length || 0;
2828
+ status?.account;
2829
+ status?.models.filter((model) => model.enabled).length;
2343
2830
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2344
2831
  className: "dsha-page",
2345
2832
  children: [
2833
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(AccountPoolSection, {
2834
+ accounts: status?.accounts ?? [],
2835
+ activeAccountId: status?.activeAccountId,
2836
+ rotationStrategy: status?.rotationStrategy ?? "sequential",
2837
+ busy,
2838
+ labels: t,
2839
+ loginBusyLabel: busy === "login" ? loginProgress || t.signingIn : void 0,
2840
+ onLogin: () => void handleLogin(),
2841
+ onSetPrimary: (accountId) => void handleAccountAction("set-primary", accountId),
2842
+ onDelete: (accountId) => void handleAccountAction("delete", accountId),
2843
+ onClearCooldown: (accountId) => void handleAccountAction("clear-cooldown", accountId),
2844
+ onRelogin: (accountId) => handleRelogin(accountId),
2845
+ onSetStrategy: (strategy) => void handleSetStrategy(strategy),
2846
+ storageValue: status?.storagePath || "—",
2847
+ renderDetails: (entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [entry.keyName && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
2848
+ t.keyName,
2849
+ ": ",
2850
+ entry.keyName
2851
+ ] }), entry.planLabel && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
2852
+ t.plan,
2853
+ ": ",
2854
+ entry.planLabel
2855
+ ] })] }),
2856
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2857
+ className: "dsha-muted",
2858
+ style: { paddingTop: 12 },
2859
+ children: t.apiKeyHint
2860
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2861
+ className: "dsha-capacity-control",
2862
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2863
+ type: "password",
2864
+ "aria-label": t.apiKeySection,
2865
+ placeholder: t.apiKeyPlaceholder,
2866
+ value: apiKey,
2867
+ onChange: (event) => setApiKey(event.currentTarget.value),
2868
+ onKeyDown: (event) => {
2869
+ if (event.key === "Enter" && apiKey.trim() !== "") handleApiKey();
2870
+ }
2871
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2872
+ type: "button",
2873
+ className: "dsha-context-save",
2874
+ disabled: busy !== null || apiKey.trim() === "",
2875
+ onClick: () => void handleApiKey(),
2876
+ children: busy === "apikey" ? t.apiKeySaving : t.apiKeySave
2877
+ })]
2878
+ })]
2879
+ }),
2346
2880
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2347
2881
  className: "dsha-group",
2348
2882
  children: [
2349
2883
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2350
2884
  className: "dsha-grouphead",
2351
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: status?.authenticated ? t.account : t.signedOut })
2352
- }),
2353
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2354
- className: "dsha-row",
2355
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2356
- className: "dsha-label",
2357
- children: status?.authenticated ? t.signedIn : t.signedOut
2358
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2359
- className: "dsha-value",
2360
- children: account?.email || account?.userName || "—"
2361
- })]
2885
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t.connection })
2362
2886
  }),
2363
- status?.authenticated && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
2364
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2365
- className: "dsha-row",
2366
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2367
- className: "dsha-label",
2368
- children: t.plan
2369
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2370
- className: "dsha-value",
2371
- children: quota?.planName || account?.planLabel || t.planUnknown
2372
- })]
2373
- }),
2374
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2375
- className: "dsha-row",
2376
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2377
- className: "dsha-label",
2378
- children: t.keyName
2379
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2380
- className: "dsha-value",
2381
- children: account?.keyName || "—"
2382
- })]
2383
- }),
2384
- quota?.subscriptionStatus && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2385
- className: "dsha-row",
2386
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2387
- className: "dsha-label",
2388
- children: t.subscription
2389
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2390
- className: "dsha-value",
2391
- children: [quota.subscriptionStatus, quota.periodEndsAt !== null ? ` · ${t.renews} ${formatDate$1(quota.periodEndsAt)}` : ""]
2392
- })]
2393
- }),
2394
- account?.organizationName && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2395
- className: "dsha-row",
2396
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2397
- className: "dsha-label",
2398
- children: t.organization
2399
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2400
- className: "dsha-value",
2401
- children: account.organizationName
2402
- })]
2403
- }),
2404
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2405
- className: "dsha-row",
2406
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2407
- className: "dsha-label",
2408
- children: t.authenticatedAt
2409
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2410
- className: "dsha-value",
2411
- children: formatDate$1(account?.authenticatedAt)
2412
- })]
2413
- })
2414
- ] }),
2415
2887
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2416
2888
  className: "dsha-row",
2889
+ style: { marginBottom: 12 },
2417
2890
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2418
2891
  className: "dsha-label",
2419
- children: t.storage
2420
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2421
- className: "dsha-value",
2422
- children: status?.storagePath || ""
2423
- })]
2424
- }),
2425
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2426
- className: "dsha-notice",
2427
- children: t.storageNotice
2428
- }),
2429
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2430
- className: "dsha-actions",
2431
- children: !status?.authenticated ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2432
- className: "dsha-btn dsha-btn-primary",
2892
+ style: { fontWeight: 600 },
2893
+ children: t.enableProvider
2894
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2895
+ type: "checkbox",
2896
+ checked: status?.enabled !== false,
2433
2897
  disabled: busy !== null,
2434
- onClick: () => void handleLogin(),
2435
- children: busy === "login" ? loginProgress || t.signingIn : t.signIn
2436
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
2437
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2438
- className: "dsha-btn dsha-btn-primary",
2439
- disabled: busy !== null,
2440
- onClick: () => void handleLogin(),
2441
- children: busy === "login" ? loginProgress || t.signingIn : t.signInAgain
2442
- }),
2443
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2444
- className: "dsha-btn",
2445
- disabled: busy !== null,
2446
- onClick: () => void handleRefreshQuota(),
2447
- children: busy === "quota" ? t.refreshingQuota : t.refreshQuota
2448
- }),
2449
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2450
- className: "dsha-btn",
2451
- disabled: busy !== null,
2452
- onClick: () => void handleLogout(),
2453
- children: t.signOut
2454
- })
2455
- ] })
2456
- })
2457
- ]
2458
- }),
2459
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2460
- className: "dsha-group",
2461
- children: [
2462
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2463
- className: "dsha-grouphead",
2464
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t.apiKeySection })
2465
- }),
2466
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2467
- className: "dsha-muted",
2468
- children: t.apiKeyHint
2469
- }),
2470
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2471
- className: "dsha-capacity-control",
2472
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2473
- type: "password",
2474
- "aria-label": t.apiKeySection,
2475
- placeholder: t.apiKeyPlaceholder,
2476
- value: apiKey,
2477
- onChange: (event) => setApiKey(event.currentTarget.value),
2478
- onKeyDown: (event) => {
2479
- if (event.key === "Enter" && apiKey.trim() !== "") handleApiKey();
2480
- }
2481
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2482
- type: "button",
2483
- className: "dsha-context-save",
2484
- disabled: busy !== null || apiKey.trim() === "",
2485
- onClick: () => void handleApiKey(),
2486
- children: busy === "apikey" ? t.apiKeySaving : t.apiKeySave
2898
+ onChange: (e) => void toggleEnabled(e.currentTarget.checked)
2487
2899
  })]
2488
- })
2489
- ]
2490
- }),
2491
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2492
- className: "dsha-group",
2493
- children: [
2494
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2495
- className: "dsha-grouphead",
2496
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t.connection })
2497
2900
  }),
2498
2901
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2499
2902
  className: "dsha-row",
@@ -2550,13 +2953,12 @@ window.__ModuleLoader__.load({
2550
2953
  className: "dsha-models",
2551
2954
  "aria-label": "Command Code Models",
2552
2955
  children: status?.models.map((model) => {
2553
- const lastVisible = model.enabled && visibleCount === 1;
2554
2956
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
2555
2957
  title: `${model.id} · ${model.wire === "anthropic" ? t.wireAnthropic : t.wireOpenai}`,
2556
2958
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2557
2959
  type: "checkbox",
2558
2960
  checked: model.enabled,
2559
- disabled: busy !== null || lastVisible,
2961
+ disabled: busy !== null,
2560
2962
  onChange: (event) => toggleModel(model.id, event.currentTarget.checked)
2561
2963
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: model.name })]
2562
2964
  }, model.id);
@@ -2761,6 +3163,7 @@ window.__ModuleLoader__.load({
2761
3163
  //#region src/client/kimi-code/locales.ts
2762
3164
  const NS_KIMI_CODE = "dsh-kimi-code";
2763
3165
  const zh$1 = {
3166
+ ...accountPoolZh,
2764
3167
  title: "Kimi Code",
2765
3168
  pageDesc: "登录 Kimi Code 订阅,管理模型、思考档位、上下文窗口与额度。",
2766
3169
  account: "账号",
@@ -2806,7 +3209,8 @@ window.__ModuleLoader__.load({
2806
3209
  routeOwned: "模型路由由本插件提供",
2807
3210
  routeConflict: "模型路由已被其他 Provider 占用:{detail}。请在 DSH 设置中移除重复的 Provider 配置后重试。",
2808
3211
  modelsSection: "模型",
2809
- modelsHint: "勾选后模型才会出现在对话页的模型选择列表中;至少保留一个。",
3212
+ enableProvider: "启用此供应商",
3213
+ modelsHint: "勾选后模型才会出现在对话页的模型选择列表中;全部取消勾选或关闭上方开关可在列表中隐藏该供应商。",
2810
3214
  refreshCatalog: "刷新模型目录",
2811
3215
  refreshingCatalog: "刷新中...",
2812
3216
  selectAll: "全选",
@@ -2872,6 +3276,7 @@ window.__ModuleLoader__.load({
2872
3276
  tokens: "tokens"
2873
3277
  };
2874
3278
  const en$1 = {
3279
+ ...accountPoolEn,
2875
3280
  title: "Kimi Code",
2876
3281
  pageDesc: "Sign in to the Kimi Code subscription and manage models, thinking level, context windows, and quota.",
2877
3282
  account: "Account",
@@ -2917,7 +3322,8 @@ window.__ModuleLoader__.load({
2917
3322
  routeOwned: "This plugin serves the model route",
2918
3323
  routeConflict: "Another provider already owns the model route: {detail}. Remove the duplicate provider configuration in DSH settings and reload.",
2919
3324
  modelsSection: "Models",
2920
- modelsHint: "Checked models appear in the DSH conversation model list; keep at least one.",
3325
+ enableProvider: "Enable this provider",
3326
+ modelsHint: "Checked models appear in the DSH conversation model list; unchecking all or turning off the switch hides this provider.",
2921
3327
  refreshCatalog: "Refresh catalog",
2922
3328
  refreshingCatalog: "Refreshing...",
2923
3329
  selectAll: "Select all",
@@ -3205,11 +3611,18 @@ window.__ModuleLoader__.load({
3205
3611
  window.setTimeout(() => setCopied(false), 2e3);
3206
3612
  } catch {}
3207
3613
  };
3208
- const handleLogout = async () => {
3614
+ /** One account-level action on the shared pool card. */
3615
+ const handleAccountAction = async (action, accountId) => {
3209
3616
  try {
3210
- setBusy("logout");
3617
+ setBusy(`${action}-${accountId}`);
3211
3618
  setError(null);
3212
- const updated = await fetchApi$3("/logout", { method: "POST" });
3619
+ const updated = await fetchApi$3("/accounts", {
3620
+ method: "POST",
3621
+ body: JSON.stringify({
3622
+ action,
3623
+ accountId
3624
+ })
3625
+ });
3213
3626
  setStatus(updated);
3214
3627
  setFlow({ status: "idle" });
3215
3628
  notifyChange();
@@ -3219,6 +3632,48 @@ window.__ModuleLoader__.load({
3219
3632
  setBusy(null);
3220
3633
  }
3221
3634
  };
3635
+ const handleSetStrategy = async (strategy) => {
3636
+ try {
3637
+ setBusy("strategy");
3638
+ setError(null);
3639
+ const updated = await fetchApi$3("/accounts", {
3640
+ method: "POST",
3641
+ body: JSON.stringify({
3642
+ action: "strategy",
3643
+ strategy
3644
+ })
3645
+ });
3646
+ setStatus(updated);
3647
+ } catch (err) {
3648
+ setError(err instanceof Error ? err.message : String(err));
3649
+ } finally {
3650
+ setBusy(null);
3651
+ }
3652
+ };
3653
+ /**
3654
+ * Re-authorize one account.
3655
+ *
3656
+ * Nothing is deleted: the failure marker is cleared and the device-code flow
3657
+ * runs again, so the account keeps its alias and place in the rotation.
3658
+ */
3659
+ const handleRelogin = (accountId) => {
3660
+ (async () => {
3661
+ try {
3662
+ const updated = await fetchApi$3("/accounts", {
3663
+ method: "POST",
3664
+ body: JSON.stringify({
3665
+ action: "relogin",
3666
+ accountId
3667
+ })
3668
+ });
3669
+ setStatus(updated);
3670
+ } catch (err) {
3671
+ setError(err instanceof Error ? err.message : String(err));
3672
+ return;
3673
+ }
3674
+ await handleLogin();
3675
+ })();
3676
+ };
3222
3677
  const handleRefreshQuota = async () => {
3223
3678
  try {
3224
3679
  setBusy("quota");
@@ -3274,16 +3729,32 @@ window.__ModuleLoader__.load({
3274
3729
  setError(err instanceof Error ? err.message : String(err));
3275
3730
  }
3276
3731
  };
3732
+ const toggleEnabled = async (enabled) => {
3733
+ setStatus((prev) => prev ? {
3734
+ ...prev,
3735
+ enabled
3736
+ } : prev);
3737
+ try {
3738
+ const updated = await fetchApi$3("/settings", {
3739
+ method: "POST",
3740
+ body: JSON.stringify({ enabled })
3741
+ });
3742
+ setStatus(updated);
3743
+ notifyChange();
3744
+ } catch (err) {
3745
+ setError(err instanceof Error ? err.message : String(err));
3746
+ loadStatus(true);
3747
+ }
3748
+ };
3277
3749
  const toggleModel = (modelId, checked) => {
3278
3750
  if (!status) return;
3279
3751
  const current = status.models.filter((model) => model.enabled).map((model) => model.id);
3280
3752
  const next = checked ? [.../* @__PURE__ */ new Set([...current, modelId])] : current.filter((id) => id !== modelId);
3281
- if (next.length === 0) return;
3282
3753
  applyEnabled(next);
3283
3754
  };
3284
3755
  const setAllModels = (selectAll) => {
3285
3756
  if (!status || status.models.length === 0) return;
3286
- applyEnabled(selectAll ? status.models.map((model) => model.id) : [status.models[0].id]);
3757
+ applyEnabled(selectAll ? status.models.map((model) => model.id) : []);
3287
3758
  };
3288
3759
  const handleUpdateEffort = async (effort) => {
3289
3760
  try {
@@ -3331,171 +3802,96 @@ window.__ModuleLoader__.load({
3331
3802
  })
3332
3803
  });
3333
3804
  const quota = status?.quota;
3334
- const account = status?.account;
3335
- const visibleCount = status?.models.filter((model) => model.enabled).length || 0;
3805
+ status?.account;
3806
+ status?.models.filter((model) => model.enabled).length;
3336
3807
  const loginPending = flow?.status === "pending";
3337
3808
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3338
3809
  className: "dsha-page",
3339
3810
  children: [
3340
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
3341
- className: "dsha-group",
3342
- children: [
3343
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3344
- className: "dsha-grouphead",
3345
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: status?.authenticated ? t.account : t.signedOut })
3346
- }),
3347
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3348
- className: "dsha-row",
3349
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3350
- className: "dsha-label",
3351
- children: status?.authenticated ? t.signedIn : t.signedOut
3352
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3353
- className: "dsha-value",
3354
- children: account?.nickname || account?.email || account?.userId || "—"
3355
- })]
3356
- }),
3357
- status?.authenticated && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
3358
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3359
- className: "dsha-row",
3360
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3361
- className: "dsha-label",
3362
- children: t.plan
3363
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3364
- className: "dsha-value",
3365
- children: quota?.planName || account?.planName || t.planUnknown
3366
- })]
3811
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountPoolSection, {
3812
+ accounts: status?.accounts ?? [],
3813
+ activeAccountId: status?.activeAccountId,
3814
+ rotationStrategy: status?.rotationStrategy ?? "sequential",
3815
+ busy,
3816
+ labels: t,
3817
+ loginBusyLabel: loginPending ? t.waitingAuthorization : busy === "login" ? t.requestingCode : void 0,
3818
+ onLogin: () => void handleLogin(),
3819
+ onSetPrimary: (accountId) => void handleAccountAction("set-primary", accountId),
3820
+ onDelete: (accountId) => void handleAccountAction("delete", accountId),
3821
+ onClearCooldown: (accountId) => void handleAccountAction("clear-cooldown", accountId),
3822
+ onRelogin: (accountId) => handleRelogin(accountId),
3823
+ onSetStrategy: (strategy) => void handleSetStrategy(strategy),
3824
+ storageValue: status?.storagePath || "",
3825
+ renderDetails: (entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
3826
+ entry.planName && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
3827
+ t.plan,
3828
+ ": ",
3829
+ entry.planName
3830
+ ] }),
3831
+ entry.region && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
3832
+ t.region,
3833
+ ": ",
3834
+ entry.region === "global" ? t.regionGlobal : t.regionMainland
3835
+ ] }),
3836
+ entry.email && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
3837
+ t.email,
3838
+ ": ",
3839
+ entry.email
3840
+ ] })
3841
+ ] }),
3842
+ children: loginPending && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3843
+ className: "dsha-device-box",
3844
+ children: [
3845
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t.deviceCodeSection }),
3846
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3847
+ className: "dsha-muted",
3848
+ children: t.deviceCodeHint
3367
3849
  }),
3368
- account?.email && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3369
- className: "dsha-row",
3850
+ flow?.userCode && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3851
+ className: "dsha-code-row",
3370
3852
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3371
- className: "dsha-label",
3372
- children: t.email
3373
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3374
- className: "dsha-value",
3375
- children: account.email
3853
+ className: "dsha-code",
3854
+ children: flow.userCode
3855
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3856
+ className: "dsha-btn",
3857
+ onClick: () => void handleCopyCode(),
3858
+ children: copied ? t.copied : t.copyCode
3376
3859
  })]
3377
3860
  }),
3861
+ flow?.verificationUriComplete && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
3862
+ className: "dsha-link",
3863
+ href: flow.verificationUriComplete,
3864
+ target: "_blank",
3865
+ rel: "noreferrer noopener",
3866
+ children: flow.verificationUriComplete
3867
+ }),
3378
3868
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3379
3869
  className: "dsha-row",
3380
3870
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3381
3871
  className: "dsha-label",
3382
- children: t.region
3872
+ children: t.codeExpires
3383
3873
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3384
3874
  className: "dsha-value",
3385
- children: status.region === "global" ? t.regionGlobal : t.regionMainland
3875
+ children: formatDate(flow?.expiresAt)
3386
3876
  })]
3387
3877
  }),
3388
3878
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3389
- className: "dsha-row",
3390
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3391
- className: "dsha-label",
3392
- children: t.authenticatedAt
3393
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3394
- className: "dsha-value",
3395
- children: formatDate(account?.authenticatedAt)
3396
- })]
3397
- })
3398
- ] }),
3399
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3400
- className: "dsha-row",
3401
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3402
- className: "dsha-label",
3403
- children: t.storage
3404
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3405
- className: "dsha-value",
3406
- children: status?.storagePath || "—"
3407
- })]
3408
- }),
3409
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3410
- className: "dsha-notice",
3411
- children: t.storageNotice
3412
- }),
3413
- status?.credentialsRejected && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3414
- className: "dsha-notice",
3415
- children: t.refreshRejected
3416
- }),
3417
- loginPending && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3418
- className: "dsha-device-box",
3419
- children: [
3420
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t.deviceCodeSection }),
3421
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3422
- className: "dsha-muted",
3423
- children: t.deviceCodeHint
3424
- }),
3425
- flow?.userCode && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3426
- className: "dsha-code-row",
3427
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3428
- className: "dsha-code",
3429
- children: flow.userCode
3430
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3431
- className: "dsha-btn",
3432
- onClick: () => void handleCopyCode(),
3433
- children: copied ? t.copied : t.copyCode
3434
- })]
3435
- }),
3436
- flow?.verificationUriComplete && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
3437
- className: "dsha-link",
3879
+ className: "dsha-actions",
3880
+ children: [flow?.verificationUriComplete && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
3881
+ className: "dsha-btn",
3438
3882
  href: flow.verificationUriComplete,
3439
3883
  target: "_blank",
3440
3884
  rel: "noreferrer noopener",
3441
- children: flow.verificationUriComplete
3442
- }),
3443
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3444
- className: "dsha-row",
3445
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3446
- className: "dsha-label",
3447
- children: t.codeExpires
3448
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3449
- className: "dsha-value",
3450
- children: formatDate(flow?.expiresAt)
3451
- })]
3452
- }),
3453
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3454
- className: "dsha-actions",
3455
- children: [flow?.verificationUriComplete && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
3456
- className: "dsha-btn",
3457
- href: flow.verificationUriComplete,
3458
- target: "_blank",
3459
- rel: "noreferrer noopener",
3460
- children: t.openSignInPage
3461
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3462
- className: "dsha-btn",
3463
- disabled: busy !== null,
3464
- onClick: () => void handleCancelLogin(),
3465
- children: t.cancelSignIn
3466
- })]
3467
- })
3468
- ]
3469
- }),
3470
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3471
- className: "dsha-actions",
3472
- children: !status?.authenticated ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3473
- className: "dsha-btn dsha-btn-primary",
3474
- disabled: busy !== null || loginPending,
3475
- onClick: () => void handleLogin(),
3476
- children: loginPending ? t.waitingAuthorization : busy === "login" ? t.requestingCode : t.signIn
3477
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
3478
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3479
- className: "dsha-btn dsha-btn-primary",
3480
- disabled: busy !== null || loginPending,
3481
- onClick: () => void handleLogin(),
3482
- children: loginPending ? t.waitingAuthorization : t.signInAgain
3483
- }),
3484
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3485
- className: "dsha-btn",
3486
- disabled: busy !== null,
3487
- onClick: () => void handleRefreshQuota(),
3488
- children: busy === "quota" ? t.refreshingQuota : t.refreshQuota
3489
- }),
3490
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3885
+ children: t.openSignInPage
3886
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3491
3887
  className: "dsha-btn",
3492
3888
  disabled: busy !== null,
3493
- onClick: () => void handleLogout(),
3494
- children: t.signOut
3495
- })
3496
- ] })
3497
- })
3498
- ]
3889
+ onClick: () => void handleCancelLogin(),
3890
+ children: t.cancelSignIn
3891
+ })]
3892
+ })
3893
+ ]
3894
+ })
3499
3895
  }),
3500
3896
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
3501
3897
  className: "dsha-group",
@@ -3504,6 +3900,20 @@ window.__ModuleLoader__.load({
3504
3900
  className: "dsha-grouphead",
3505
3901
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t.connection })
3506
3902
  }),
3903
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3904
+ className: "dsha-row",
3905
+ style: { marginBottom: 12 },
3906
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3907
+ className: "dsha-label",
3908
+ style: { fontWeight: 600 },
3909
+ children: t.enableProvider
3910
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
3911
+ type: "checkbox",
3912
+ checked: status?.enabled !== false,
3913
+ disabled: busy !== null,
3914
+ onChange: (e) => void toggleEnabled(e.currentTarget.checked)
3915
+ })]
3916
+ }),
3507
3917
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3508
3918
  className: "dsha-row",
3509
3919
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
@@ -3583,7 +3993,6 @@ window.__ModuleLoader__.load({
3583
3993
  className: "dsha-models",
3584
3994
  "aria-label": "Kimi Code Models",
3585
3995
  children: status?.models.map((model) => {
3586
- const lastVisible = model.enabled && visibleCount === 1;
3587
3996
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
3588
3997
  title: [
3589
3998
  model.id,
@@ -3595,7 +4004,7 @@ window.__ModuleLoader__.load({
3595
4004
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
3596
4005
  type: "checkbox",
3597
4006
  checked: model.enabled,
3598
- disabled: busy !== null || lastVisible,
4007
+ disabled: busy !== null,
3599
4008
  onChange: (event) => toggleModel(model.id, event.currentTarget.checked)
3600
4009
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: model.name })]
3601
4010
  }, model.id);
@@ -3875,6 +4284,7 @@ window.__ModuleLoader__.load({
3875
4284
  const NS = "dsh-chatgpt-subscription";
3876
4285
  const dictionaries = {
3877
4286
  zh: {
4287
+ ...accountPoolZh,
3878
4288
  title: "ChatGPT",
3879
4289
  hubTitle: "订阅服务",
3880
4290
  intro: "使用 ChatGPT 账号登录,在 DSH 中使用订阅可用的模型。",
@@ -3912,8 +4322,11 @@ window.__ModuleLoader__.load({
3912
4322
  testConnection: "测试连接",
3913
4323
  testing: "测试中…",
3914
4324
  latency: "最近延迟",
4325
+ enableProvider: "启用此供应商",
4326
+ selectAll: "全选",
4327
+ unselectAll: "全不选",
3915
4328
  models: "可用模型",
3916
- modelsHint: "勾选后模型才会显示在对话页的模型选择列表中;至少保留一个。",
4329
+ modelsHint: "勾选后模型才会显示在对话页的模型选择列表中;全部取消勾选或关闭上方开关可在列表中隐藏该供应商。",
3917
4330
  proxySettings: "网络代理",
3918
4331
  proxyMode: "代理模式",
3919
4332
  proxyModeHint: "GPT 与 Antigravity(Gemini)共用此代理设置;支持自动读取系统代理,修改后对后续请求生效。",
@@ -4000,6 +4413,7 @@ window.__ModuleLoader__.load({
4000
4413
  unknown: "未知"
4001
4414
  },
4002
4415
  en: {
4416
+ ...accountPoolEn,
4003
4417
  title: "ChatGPT",
4004
4418
  hubTitle: "Subscriptions",
4005
4419
  intro: "Sign in with ChatGPT to use models available to your subscription in DSH.",
@@ -4037,8 +4451,11 @@ window.__ModuleLoader__.load({
4037
4451
  testConnection: "Test connection",
4038
4452
  testing: "Testing…",
4039
4453
  latency: "Last latency",
4454
+ enableProvider: "Enable this provider",
4455
+ selectAll: "Select all",
4456
+ unselectAll: "Unselect all",
4040
4457
  models: "Available models",
4041
- modelsHint: "Checked models appear in the conversation model picker; at least one must remain enabled.",
4458
+ modelsHint: "Checked models appear in the conversation model picker; unchecking all or turning off the switch hides this provider from the picker.",
4042
4459
  proxySettings: "Network Proxy",
4043
4460
  proxyMode: "Proxy Mode",
4044
4461
  proxyModeHint: "GPT and Antigravity (Gemini) share these proxy settings. Supports system proxy detection; changes apply to subsequent requests.",
@@ -4309,6 +4726,41 @@ window.__ModuleLoader__.load({
4309
4726
  .dsha-composer-quota strong{color:var(--dsw-alias-label-primary);font-size:11px;font-weight:650}
4310
4727
  .dsha-composer-quota[data-level=warning] strong{color:var(--dsw-alias-label-warning,#d58a24)}
4311
4728
  .dsha-composer-quota[data-level=danger] strong{color:var(--dsw-alias-label-danger,#d94b4b)}
4729
+ .dsha-accounts-list{display:flex;flex-direction:column;gap:10px;margin-top:10px}
4730
+ .dsha-account-card{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:8px;padding:12px 14px;display:flex;flex-direction:column;gap:8px;transition:border-color .15s ease}
4731
+ .dsha-account-card.active{border-color:color-mix(in srgb,var(--dsw-alias-button-info-fill,#397ee8) 60%,var(--dsw-alias-border-l2))}
4732
+ .dsha-account-header{display:flex;align-items:center;justify-content:space-between;gap:8px}
4733
+ .dsha-account-identity{display:flex;align-items:center;gap:8px;min-width:0}
4734
+ .dsha-account-title{font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
4735
+ .dsha-badges{display:flex;align-items:center;gap:6px}
4736
+ .dsha-badge{font-size:11px;line-height:1;padding:3px 7px;border-radius:4px;font-weight:550;white-space:nowrap}
4737
+ .dsha-badge.primary{background:color-mix(in srgb,var(--dsw-alias-button-info-fill,#397ee8) 18%,var(--dsw-alias-bg-layer-1));color:var(--dsw-alias-button-info-fill,#397ee8);border:0.5px solid color-mix(in srgb,var(--dsw-alias-button-info-fill,#397ee8) 40%,transparent)}
4738
+ .dsha-badge.active{background:color-mix(in srgb,var(--dsw-alias-label-success,#10b981) 18%,var(--dsw-alias-bg-layer-1));color:var(--dsw-alias-label-success,#10b981);border:0.5px solid color-mix(in srgb,var(--dsw-alias-label-success,#10b981) 40%,transparent)}
4739
+ .dsha-badge.cooldown{background:color-mix(in srgb,var(--dsw-alias-label-warning,#d58a24) 18%,var(--dsw-alias-bg-layer-1));color:var(--dsw-alias-label-warning,#d58a24);border:0.5px solid color-mix(in srgb,var(--dsw-alias-label-warning,#d58a24) 40%,transparent)}
4740
+ .dsha-account-details{display:flex;flex-wrap:wrap;gap:14px;font-size:12px;color:var(--dsw-alias-label-secondary)}
4741
+ .dsha-account-actions{display:flex;flex-wrap:wrap;gap:6px;margin-top:2px}
4742
+ .dsha-account-actions .dsha-btn{padding:5px 10px;font-size:12px}
4743
+ `;
4744
+ document.head.append(style);
4745
+ }
4746
+ //#endregion
4747
+ //#region src/client/common/styles.ts
4748
+ const POOL_STYLE_ID = "dsh-provider-pool-style";
4749
+ /**
4750
+ * Styles the shared account-pool card adds on top of the existing provider
4751
+ * stylesheets: the badge variants no single provider tab owned before.
4752
+ *
4753
+ * Additive on purpose — the shared `.dsha-*` block and each provider's own
4754
+ * tweaks keep living in their own modules, in the order the client installs
4755
+ * them, so nothing that already rendered changes appearance.
4756
+ */
4757
+ function installPoolStyles() {
4758
+ if (typeof document === "undefined" || document.getElementById("dsh-provider-pool-style")) return;
4759
+ const style = document.createElement("style");
4760
+ style.id = POOL_STYLE_ID;
4761
+ style.textContent = `
4762
+ .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)}
4763
+ .dsha-account-card .dsha-btn.danger{color:var(--dsw-alias-label-danger,#d94b4b)}
4312
4764
  `;
4313
4765
  document.head.append(style);
4314
4766
  }
@@ -5089,6 +5541,10 @@ window.__ModuleLoader__.load({
5089
5541
  installKimiCodeStyles();
5090
5542
  return () => {};
5091
5543
  }, "dsh-kimi-code: styles");
5544
+ ctx.effect(() => {
5545
+ installPoolStyles();
5546
+ return () => {};
5547
+ }, "dsh-chatgpt-subscription: pool styles");
5092
5548
  ctx.effect(() => setupMermaidObserver(), "dsh-mermaid: observer");
5093
5549
  const t = ctx.locale.bind(NS);
5094
5550
  const handleModelDirectoryReload = () => {