@eddyskywalker/dsh-chatgpt-subscription 0.4.0 → 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 (78) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/lib/client.js +703 -534
  3. package/lib/client.js.map +1 -1
  4. package/lib/index.js +5106 -3793
  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 +24 -192
  9. package/lib/types/client/antigravity/locales.d.ts.map +1 -1
  10. package/lib/types/client/api.d.ts +13 -1
  11. package/lib/types/client/api.d.ts.map +1 -1
  12. package/lib/types/client/command-code/CommandCodeSection.d.ts.map +1 -1
  13. package/lib/types/client/command-code/locales.d.ts +66 -201
  14. package/lib/types/client/command-code/locales.d.ts.map +1 -1
  15. package/lib/types/client/common/AccountPoolSection.d.ts +41 -0
  16. package/lib/types/client/common/AccountPoolSection.d.ts.map +1 -0
  17. package/lib/types/client/common/account-pool-labels.d.ts +43 -0
  18. package/lib/types/client/common/account-pool-labels.d.ts.map +1 -0
  19. package/lib/types/client/common/styles.d.ts +11 -0
  20. package/lib/types/client/common/styles.d.ts.map +1 -0
  21. package/lib/types/client/index.d.ts.map +1 -1
  22. package/lib/types/client/kimi-code/KimiCodeSection.d.ts.map +1 -1
  23. package/lib/types/client/kimi-code/locales.d.ts +63 -336
  24. package/lib/types/client/kimi-code/locales.d.ts.map +1 -1
  25. package/lib/types/client/locales.d.ts +41 -1
  26. package/lib/types/client/locales.d.ts.map +1 -1
  27. package/lib/types/host/antigravity/account-pool.d.ts +15 -5
  28. package/lib/types/host/antigravity/account-pool.d.ts.map +1 -1
  29. package/lib/types/host/antigravity/routes.d.ts.map +1 -1
  30. package/lib/types/host/codex-account-pool.d.ts +64 -0
  31. package/lib/types/host/codex-account-pool.d.ts.map +1 -0
  32. package/lib/types/host/command-code/account-pool.d.ts +60 -0
  33. package/lib/types/host/command-code/account-pool.d.ts.map +1 -0
  34. package/lib/types/host/command-code/adapter.d.ts +11 -1
  35. package/lib/types/host/command-code/adapter.d.ts.map +1 -1
  36. package/lib/types/host/command-code/client.d.ts +17 -1
  37. package/lib/types/host/command-code/client.d.ts.map +1 -1
  38. package/lib/types/host/command-code/oauth.d.ts +4 -1
  39. package/lib/types/host/command-code/oauth.d.ts.map +1 -1
  40. package/lib/types/host/command-code/routes.d.ts +11 -2
  41. package/lib/types/host/command-code/routes.d.ts.map +1 -1
  42. package/lib/types/host/common/account-pool.d.ts +179 -0
  43. package/lib/types/host/common/account-pool.d.ts.map +1 -0
  44. package/lib/types/host/common/home.d.ts +8 -0
  45. package/lib/types/host/common/home.d.ts.map +1 -0
  46. package/lib/types/host/kimi-code/account-pool.d.ts +48 -0
  47. package/lib/types/host/kimi-code/account-pool.d.ts.map +1 -0
  48. package/lib/types/host/kimi-code/adapter.d.ts +10 -1
  49. package/lib/types/host/kimi-code/adapter.d.ts.map +1 -1
  50. package/lib/types/host/kimi-code/client.d.ts +16 -0
  51. package/lib/types/host/kimi-code/client.d.ts.map +1 -1
  52. package/lib/types/host/kimi-code/oauth.d.ts +2 -0
  53. package/lib/types/host/kimi-code/oauth.d.ts.map +1 -1
  54. package/lib/types/host/kimi-code/routes.d.ts +10 -2
  55. package/lib/types/host/kimi-code/routes.d.ts.map +1 -1
  56. package/lib/types/host/oauth-service.d.ts +41 -2
  57. package/lib/types/host/oauth-service.d.ts.map +1 -1
  58. package/lib/types/host/preset-sync.d.ts +7 -1
  59. package/lib/types/host/preset-sync.d.ts.map +1 -1
  60. package/lib/types/host/responses-client.d.ts +15 -0
  61. package/lib/types/host/responses-client.d.ts.map +1 -1
  62. package/lib/types/host/routes.d.ts +2 -1
  63. package/lib/types/host/routes.d.ts.map +1 -1
  64. package/lib/types/host/usage-service.d.ts +15 -0
  65. package/lib/types/host/usage-service.d.ts.map +1 -1
  66. package/lib/types/index.d.ts +5 -0
  67. package/lib/types/index.d.ts.map +1 -1
  68. package/lib/types/shared/account-pool-contracts.d.ts +47 -0
  69. package/lib/types/shared/account-pool-contracts.d.ts.map +1 -0
  70. package/lib/types/shared/antigravity-contracts.d.ts +4 -10
  71. package/lib/types/shared/antigravity-contracts.d.ts.map +1 -1
  72. package/lib/types/shared/command-code-contracts.d.ts +14 -0
  73. package/lib/types/shared/command-code-contracts.d.ts.map +1 -1
  74. package/lib/types/shared/contracts.d.ts +6 -0
  75. package/lib/types/shared/contracts.d.ts.map +1 -1
  76. package/lib/types/shared/kimi-code-contracts.d.ts +14 -0
  77. package/lib/types/shared/kimi-code-contracts.d.ts.map +1 -1
  78. 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());
@@ -531,14 +812,71 @@ window.__ModuleLoader__.load({
531
812
  await updatePreferences({ customProxyUrl: draft ? draft : null });
532
813
  setCustomProxyDraft(null);
533
814
  };
534
- const logout = async () => run("logout", async () => {
535
- await apiRef.current.logout();
536
- eventSourceRef.current?.close();
537
- eventSourceRef.current = null;
538
- setAuthUrl(null);
539
- setConnection(null);
540
- await load();
541
- });
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
+ };
542
880
  const run = async (action, task) => {
543
881
  setBusy(action);
544
882
  setError(null);
@@ -551,14 +889,14 @@ window.__ModuleLoader__.load({
551
889
  setBusy(null);
552
890
  }
553
891
  };
554
- const account = status?.account;
892
+ status?.account;
555
893
  const preferences = status?.preferences;
556
894
  const quota = status?.quota;
557
895
  const storage = status?.storage;
558
896
  const login = status?.login;
559
897
  const visibleModelIds = preferences?.visibleModelIds ?? DEFAULT_VISIBLE_CODEX_MODEL_IDS;
560
898
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
561
- className: "dsh-codex-page",
899
+ className: "dsha-page",
562
900
  "aria-labelledby": "dsh-codex-title",
563
901
  children: [
564
902
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
@@ -579,37 +917,36 @@ window.__ModuleLoader__.load({
579
917
  }) : null]
580
918
  }) : null,
581
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: [
582
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Section, {
583
- 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
+ ] })] }),
584
943
  children: [
585
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoRow, {
586
- label: status?.authenticated ? t("signedIn") : t("signedOut"),
587
- value: account?.email ?? "—"
588
- }),
589
- status?.authenticated ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
590
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoRow, {
591
- label: t("plan"),
592
- value: account?.planType ?? t("unknown")
593
- }),
594
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoRow, {
595
- label: t("accountId"),
596
- value: account?.accountIdSuffix ?? "—"
597
- }),
598
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoRow, {
599
- label: t("expires"),
600
- value: formatDate$3(account?.tokenExpiresAt)
601
- })
602
- ] }) : null,
603
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoRow, {
604
- label: t("storage"),
605
- value: storageLabel(storage, t)
606
- }),
607
944
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
608
- className: "dsh-codex-notice",
945
+ className: "dsha-notice",
609
946
  children: storageNotice(storage, t)
610
947
  }),
611
948
  login?.active ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
612
- className: "dsh-codex-muted",
949
+ className: "dsha-muted",
613
950
  role: "status",
614
951
  children: t("pending")
615
952
  }) : null,
@@ -625,25 +962,16 @@ window.__ModuleLoader__.load({
625
962
  children: t("continueLogin")
626
963
  }) : null,
627
964
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
628
- className: "dsh-codex-actions",
965
+ className: "dsha-actions",
629
966
  children: [login?.active ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
630
967
  disabled: busy !== null,
631
968
  onClick: cancelLogin,
632
969
  children: t("cancel")
633
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
634
- primary: true,
635
- disabled: busy !== null || storage?.available === false,
636
- onClick: startLogin,
637
- children: status?.authenticated ? t("signInAgain") : t("signIn")
638
- }), status?.authenticated ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
970
+ }) : null, status?.authenticated ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
639
971
  disabled: busy !== null,
640
972
  onClick: refreshToken,
641
973
  children: t("refreshToken")
642
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
643
- disabled: busy !== null,
644
- onClick: logout,
645
- children: t("signOut")
646
- })] }) : null]
974
+ }) : null]
647
975
  })
648
976
  ]
649
977
  }),
@@ -651,7 +979,7 @@ window.__ModuleLoader__.load({
651
979
  title: t("connection"),
652
980
  children: [
653
981
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
654
- className: "dsh-codex-pref-row",
982
+ className: "dsha-pref-row",
655
983
  style: { marginBottom: 12 },
656
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", {
657
985
  type: "checkbox",
@@ -680,14 +1008,14 @@ window.__ModuleLoader__.load({
680
1008
  }),
681
1009
  connection !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoRow, {
682
1010
  label: t("latency"),
683
- value: `${connection.latencyMs} ms · ${formatDate$3(connection.checkedAt)}`
1011
+ value: `${connection.latencyMs} ms · ${formatDate$2(connection.checkedAt)}`
684
1012
  }) : null,
685
1013
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
686
- className: "dsh-codex-muted dsh-codex-models-hint",
1014
+ className: "dsha-muted dsha-models-hint",
687
1015
  children: t("modelsHint")
688
1016
  }),
689
1017
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
690
- className: "dsh-codex-models",
1018
+ className: "dsha-models",
691
1019
  "aria-label": t("models"),
692
1020
  children: CODEX_MODEL_CATALOG.map((model) => {
693
1021
  const checked = visibleModelIds.some((id) => id === model.id);
@@ -720,7 +1048,7 @@ window.__ModuleLoader__.load({
720
1048
  })]
721
1049
  }),
722
1050
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
723
- className: "dsh-codex-actions",
1051
+ className: "dsha-actions",
724
1052
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
725
1053
  disabled: !status?.authenticated || busy !== null,
726
1054
  onClick: testConnection,
@@ -733,12 +1061,12 @@ window.__ModuleLoader__.load({
733
1061
  title: t("proxySettings"),
734
1062
  children: [
735
1063
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
736
- className: "dsh-codex-pref-row",
1064
+ className: "dsha-pref-row",
737
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", {
738
- className: "dsh-codex-muted",
1066
+ className: "dsha-muted",
739
1067
  children: t("proxyModeHint")
740
1068
  })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
741
- className: "dsh-codex-select",
1069
+ className: "dsha-select",
742
1070
  "aria-label": t("proxyMode"),
743
1071
  value: preferences?.proxyMode ?? "auto",
744
1072
  disabled: busy !== null,
@@ -782,22 +1110,22 @@ window.__ModuleLoader__.load({
782
1110
  children: t("proxyDetectedEffective")
783
1111
  })
784
1112
  ] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
785
- className: "dsh-codex-muted",
1113
+ className: "dsha-muted",
786
1114
  children: t("proxyNoneDetected")
787
1115
  })
788
1116
  }) : null,
789
1117
  preferences?.proxyMode === "direct" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
790
- className: "dsh-codex-muted",
1118
+ className: "dsha-muted",
791
1119
  style: { margin: "8px 0 0" },
792
1120
  children: t("proxyDirectHint")
793
1121
  }) : null,
794
1122
  preferences?.proxyMode === "custom" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
795
- className: "dsh-codex-context-settings",
1123
+ className: "dsha-context-settings",
796
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", {
797
- className: "dsh-codex-muted",
1125
+ className: "dsha-muted",
798
1126
  children: t("customProxyUrlHint")
799
1127
  })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
800
- className: "dsh-codex-context-row",
1128
+ className: "dsha-context-row",
801
1129
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
802
1130
  className: "dsh-codex-proxy-control",
803
1131
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
@@ -816,7 +1144,7 @@ window.__ModuleLoader__.load({
816
1144
  updateCustomProxyUrl();
817
1145
  }
818
1146
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
819
- className: "dsh-codex-context-save",
1147
+ className: "dsha-context-save",
820
1148
  type: "button",
821
1149
  "aria-label": t("saveProxyUrl"),
822
1150
  disabled: busy !== null || customProxyDraft === null,
@@ -832,9 +1160,9 @@ window.__ModuleLoader__.load({
832
1160
  title: t("enhancements"),
833
1161
  children: [
834
1162
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
835
- className: "dsh-codex-pref-row",
1163
+ className: "dsha-pref-row",
836
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", {
837
- className: "dsh-codex-muted",
1165
+ className: "dsha-muted",
838
1166
  children: t("searchProviderHint")
839
1167
  })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
840
1168
  className: "dsh-codex-segments",
@@ -858,12 +1186,12 @@ window.__ModuleLoader__.load({
858
1186
  })]
859
1187
  }),
860
1188
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
861
- className: "dsh-codex-pref-row",
1189
+ className: "dsha-pref-row",
862
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", {
863
- className: "dsh-codex-muted",
1191
+ className: "dsha-muted",
864
1192
  children: t("outputVerbosityHint")
865
1193
  })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
866
- className: "dsh-codex-select",
1194
+ className: "dsha-select",
867
1195
  "aria-label": t("outputVerbosity"),
868
1196
  value: preferences?.outputVerbosity ?? "",
869
1197
  disabled: busy !== null,
@@ -892,12 +1220,12 @@ window.__ModuleLoader__.load({
892
1220
  })]
893
1221
  }),
894
1222
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
895
- className: "dsh-codex-pref-row",
1223
+ className: "dsha-pref-row",
896
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", {
897
- className: "dsh-codex-muted",
1225
+ className: "dsha-muted",
898
1226
  children: t("reasoningSummaryHint")
899
1227
  })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
900
- className: "dsh-codex-select",
1228
+ className: "dsha-select",
901
1229
  "aria-label": t("reasoningSummary"),
902
1230
  value: preferences?.reasoningSummary ?? "",
903
1231
  disabled: busy !== null,
@@ -930,9 +1258,9 @@ window.__ModuleLoader__.load({
930
1258
  })]
931
1259
  }),
932
1260
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
933
- className: "dsh-codex-context-settings",
1261
+ className: "dsha-context-settings",
934
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", {
935
- className: "dsh-codex-muted",
1263
+ className: "dsha-muted",
936
1264
  children: t("contextWindowsHint")
937
1265
  })] }), CONFIGURABLE_CONTEXT_MODEL_IDS.map((model) => {
938
1266
  const entry = resolveCodexCatalogEntry(model);
@@ -942,12 +1270,12 @@ window.__ModuleLoader__.load({
942
1270
  const dirty = draft !== void 0 && parsedDraft !== fallback;
943
1271
  const inputId = `dsh-codex-context-${model}`;
944
1272
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
945
- className: "dsh-codex-context-row",
1273
+ className: "dsha-context-row",
946
1274
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
947
1275
  htmlFor: inputId,
948
1276
  children: entry.name
949
1277
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
950
- className: "dsh-codex-capacity-control",
1278
+ className: "dsha-capacity-control",
951
1279
  children: [
952
1280
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
953
1281
  id: inputId,
@@ -971,7 +1299,7 @@ window.__ModuleLoader__.load({
971
1299
  }),
972
1300
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: t("tokens") }),
973
1301
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
974
- className: "dsh-codex-context-save",
1302
+ className: "dsha-context-save",
975
1303
  type: "button",
976
1304
  "data-model": model,
977
1305
  "aria-label": entry.name + " " + t("saveContextWindow"),
@@ -1013,7 +1341,7 @@ window.__ModuleLoader__.load({
1013
1341
  }),
1014
1342
  children: [
1015
1343
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1016
- className: "dsh-codex-muted",
1344
+ className: "dsha-muted",
1017
1345
  children: t("quotaIntro")
1018
1346
  }),
1019
1347
  quota?.state === "signed-out" || !status?.authenticated ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
@@ -1067,7 +1395,7 @@ window.__ModuleLoader__.load({
1067
1395
  children: [
1068
1396
  t("updated"),
1069
1397
  ": ",
1070
- formatDate$3(quota.fetchedAt)
1398
+ formatDate$2(quota.fetchedAt)
1071
1399
  ]
1072
1400
  }) : null
1073
1401
  ]
@@ -1083,16 +1411,16 @@ window.__ModuleLoader__.load({
1083
1411
  }
1084
1412
  function Section({ title, aside, children }) {
1085
1413
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1086
- className: "dsh-codex-group",
1414
+ className: "dsha-group",
1087
1415
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1088
- className: "dsh-codex-grouphead",
1416
+ className: "dsha-grouphead",
1089
1417
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: title }), aside]
1090
1418
  }), children]
1091
1419
  });
1092
1420
  }
1093
1421
  function Button({ primary = false, disabled, onClick, children }) {
1094
1422
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1095
- className: `dsh-codex-button${primary ? " dsh-codex-button-primary" : ""}`,
1423
+ className: `dsha-btn${primary ? " dsha-btn-primary" : ""}`,
1096
1424
  type: "button",
1097
1425
  disabled,
1098
1426
  onClick: () => void onClick(),
@@ -1101,12 +1429,12 @@ window.__ModuleLoader__.load({
1101
1429
  }
1102
1430
  function InfoRow({ label, value }) {
1103
1431
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1104
- className: "dsh-codex-row",
1432
+ className: "dsha-row",
1105
1433
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1106
- className: "dsh-codex-label",
1434
+ className: "dsha-label",
1107
1435
  children: label
1108
1436
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1109
- className: "dsh-codex-value",
1437
+ className: "dsha-value",
1110
1438
  children: value
1111
1439
  })]
1112
1440
  });
@@ -1250,7 +1578,7 @@ window.__ModuleLoader__.load({
1250
1578
  ].filter((part) => part !== null);
1251
1579
  return parts.length > 0 ? parts.join(" · ") : t("unknown");
1252
1580
  }
1253
- function formatDate$3(seconds) {
1581
+ function formatDate$2(seconds) {
1254
1582
  if (seconds === void 0 || seconds === null || typeof seconds !== "number" || !Number.isFinite(seconds) || seconds <= 0) return "—";
1255
1583
  const ms = seconds > 1e10 ? seconds : seconds * 1e3;
1256
1584
  try {
@@ -1265,7 +1593,7 @@ window.__ModuleLoader__.load({
1265
1593
  function formatReset$2(seconds) {
1266
1594
  if (seconds === void 0 || seconds === null || typeof seconds !== "number" || !Number.isFinite(seconds) || seconds <= 0) return "—";
1267
1595
  const ms = seconds > 1e10 ? seconds : seconds * 1e3;
1268
- const absolute = formatDate$3(seconds);
1596
+ const absolute = formatDate$2(seconds);
1269
1597
  const diff = ms - Date.now();
1270
1598
  const abs = Math.abs(diff);
1271
1599
  const [amount, unit] = abs >= 864e5 ? [Math.round(diff / 864e5), "day"] : abs >= 36e5 ? [Math.round(diff / 36e5), "hour"] : [Math.round(diff / 6e4), "minute"];
@@ -1282,6 +1610,7 @@ window.__ModuleLoader__.load({
1282
1610
  //#region src/client/antigravity/locales.ts
1283
1611
  const NS_ANTIGRAVITY = "dsh-antigravity";
1284
1612
  const zh$3 = {
1613
+ ...accountPoolZh,
1285
1614
  title: "Antigravity",
1286
1615
  pageDesc: "登录 Google Antigravity / Cloud Code Assist,并查看当前账号的共享额度。",
1287
1616
  account: "已登录",
@@ -1298,6 +1627,7 @@ window.__ModuleLoader__.load({
1298
1627
  rotationStrategy: "调度策略",
1299
1628
  strategySequential: "顺序耗尽(当前优先,遇限流自动切号)",
1300
1629
  strategyRoundRobin: "轮询调度(按账号循环均匀分摊)",
1630
+ strategySticky: "会话粘性(保持当前账号直到被限流)",
1301
1631
  noAccounts: "暂无账号,点击「添加账号」完成登录授权。",
1302
1632
  plan: "套餐",
1303
1633
  accountId: "账号 ID",
@@ -1346,6 +1676,7 @@ window.__ModuleLoader__.load({
1346
1676
  tokens: "tokens"
1347
1677
  };
1348
1678
  const en$3 = {
1679
+ ...accountPoolEn,
1349
1680
  title: "Antigravity",
1350
1681
  pageDesc: "Sign in to Google Antigravity / Cloud Code Assist and view shared quotas.",
1351
1682
  account: "Signed in",
@@ -1362,6 +1693,7 @@ window.__ModuleLoader__.load({
1362
1693
  rotationStrategy: "Scheduling Strategy",
1363
1694
  strategySequential: "Sequential Drain (Primary first, failover on 429)",
1364
1695
  strategyRoundRobin: "Round-Robin (Evenly rotate across accounts)",
1696
+ strategySticky: "Sticky session (hold the current account until it is limited)",
1365
1697
  noAccounts: "No accounts yet. Click \"Add Account\" to authorize.",
1366
1698
  plan: "Plan",
1367
1699
  accountId: "Account ID",
@@ -1453,7 +1785,7 @@ window.__ModuleLoader__.load({
1453
1785
  if (hours > 0) return `${hours}h ${mins % 60}m`;
1454
1786
  return `${mins}m`;
1455
1787
  }
1456
- function formatDate$2(ms) {
1788
+ function formatDate$1(ms) {
1457
1789
  if (!ms || ms <= 0) return "—";
1458
1790
  try {
1459
1791
  return new Intl.DateTimeFormat(void 0, {
@@ -1731,148 +2063,40 @@ window.__ModuleLoader__.load({
1731
2063
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1732
2064
  className: "dsha-page",
1733
2065
  children: [
1734
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1735
- className: "dsha-group",
1736
- children: [
1737
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1738
- className: "dsha-grouphead",
1739
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("h3", { children: [t.accountPool, status?.accounts && status.accounts.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1740
- className: "dsha-muted",
1741
- style: {
1742
- fontWeight: 400,
1743
- marginLeft: 8
1744
- },
1745
- children: [
1746
- "(已登录 ",
1747
- status.accounts.length,
1748
- " 个账号)"
1749
- ]
1750
- })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1751
- className: "dsha-btn dsha-btn-primary",
1752
- disabled: busy !== null,
1753
- onClick: handleLogin,
1754
- children: busy === "login" ? loginProgress || t.signingIn : t.addAccount
1755
- })]
1756
- }),
1757
- status?.accounts && status.accounts.length > 1 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1758
- className: "dsha-pref-row",
1759
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t.rotationStrategy }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1760
- className: "dsha-muted",
1761
- style: {
1762
- fontSize: 12,
1763
- marginTop: 2
1764
- },
1765
- children: status.rotationStrategy === "round-robin" ? t.strategyRoundRobin : t.strategySequential
1766
- })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
1767
- className: "dsha-select",
1768
- value: status.rotationStrategy || "sequential",
1769
- disabled: busy !== null,
1770
- onChange: (e) => void handleSetStrategy(e.target.value),
1771
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1772
- value: "sequential",
1773
- children: "顺序耗尽"
1774
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1775
- value: "round-robin",
1776
- children: "轮询调度"
1777
- })]
1778
- })]
1779
- }),
1780
- !status?.accounts || status.accounts.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1781
- className: "dsha-empty",
1782
- children: t.noAccounts
1783
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1784
- className: "dsha-accounts-list",
1785
- children: status.accounts.map((acc) => {
1786
- const isCurrentActive = acc.id === status.activeAccountId;
1787
- const isCooling = typeof acc.cooldownUntil === "number" && acc.cooldownUntil > Date.now();
1788
- const cooldownLeftSec = isCooling ? Math.ceil((acc.cooldownUntil - Date.now()) / 1e3) : 0;
1789
- const cooldownLeftMin = Math.ceil(cooldownLeftSec / 60);
1790
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1791
- className: `dsha-account-card ${isCurrentActive ? "active" : ""}`,
1792
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1793
- className: "dsha-account-header",
1794
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1795
- className: "dsha-account-identity",
1796
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1797
- className: "dsha-account-title",
1798
- children: acc.alias || acc.email || acc.id
1799
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1800
- className: "dsha-badges",
1801
- children: [
1802
- acc.isPrimary && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1803
- className: "dsha-badge primary",
1804
- children: t.primaryAccount
1805
- }),
1806
- isCurrentActive && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1807
- className: "dsha-badge active",
1808
- children: t.activeAccount
1809
- }),
1810
- isCooling && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1811
- className: "dsha-badge cooldown",
1812
- children: [
1813
- t.cooling,
1814
- "(剩 ",
1815
- cooldownLeftMin,
1816
- " 分)"
1817
- ]
1818
- })
1819
- ]
1820
- })]
1821
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1822
- className: "dsha-account-actions",
1823
- children: [
1824
- !acc.isPrimary && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1825
- className: "dsha-btn",
1826
- disabled: busy !== null,
1827
- onClick: () => void handleSetPrimary(acc.id),
1828
- children: t.setPrimary
1829
- }),
1830
- isCooling && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1831
- className: "dsha-btn",
1832
- disabled: busy !== null,
1833
- onClick: () => void handleClearCooldown(acc.id),
1834
- children: t.clearCooldown
1835
- }),
1836
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1837
- className: "dsha-btn",
1838
- disabled: busy !== null,
1839
- onClick: () => void handleDeleteAccount(acc.id),
1840
- children: t.deleteAccount
1841
- })
1842
- ]
1843
- })]
1844
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1845
- className: "dsha-account-details",
1846
- children: [
1847
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
1848
- t.accountId,
1849
- ": ",
1850
- acc.projectId || "antigravity-default"
1851
- ] }),
1852
- acc.email && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: ["邮箱: ", acc.email] }),
1853
- acc.expiresAt && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: ["令牌到期: ", formatDate$2(acc.expiresAt)] }),
1854
- acc.lastUsedAt && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: ["上次调用: ", formatDate$2(acc.lastUsedAt)] })
1855
- ]
1856
- })]
1857
- }, acc.id);
1858
- })
1859
- }),
1860
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1861
- className: "dsha-row",
1862
- style: { marginTop: 12 },
1863
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1864
- className: "dsha-label",
1865
- children: t.storage
1866
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1867
- className: "dsha-value",
1868
- children: "本地安全存储 (JSON/DPAPI)"
1869
- })]
1870
- }),
1871
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1872
- className: "dsha-notice",
1873
- children: t.storageNotice
1874
- })
1875
- ]
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
+ ] })
1876
2100
  }),
1877
2101
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1878
2102
  className: "dsha-group",
@@ -2127,6 +2351,7 @@ window.__ModuleLoader__.load({
2127
2351
  //#region src/client/command-code/locales.ts
2128
2352
  const NS_COMMAND_CODE = "dsh-command-code";
2129
2353
  const zh$2 = {
2354
+ ...accountPoolZh,
2130
2355
  title: "Command Code",
2131
2356
  pageDesc: "登录 Command Code,管理可用模型、上下文窗口与额度。",
2132
2357
  account: "账号",
@@ -2194,6 +2419,7 @@ window.__ModuleLoader__.load({
2194
2419
  tokens: "tokens"
2195
2420
  };
2196
2421
  const en$2 = {
2422
+ ...accountPoolEn,
2197
2423
  title: "Command Code",
2198
2424
  pageDesc: "Sign in to Command Code and manage models, context windows, and quota.",
2199
2425
  account: "Account",
@@ -2306,17 +2532,6 @@ window.__ModuleLoader__.load({
2306
2532
  if (value >= 1e3 && value % 1e3 === 0) return `${value / 1e3}K`;
2307
2533
  return String(value);
2308
2534
  }
2309
- function formatDate$1(ms) {
2310
- if (ms === void 0 || ms === null || ms <= 0) return "—";
2311
- try {
2312
- return new Intl.DateTimeFormat(void 0, {
2313
- dateStyle: "medium",
2314
- timeStyle: "short"
2315
- }).format(ms);
2316
- } catch {
2317
- return "—";
2318
- }
2319
- }
2320
2535
  function formatReset$1(resetsAt) {
2321
2536
  if (resetsAt === void 0 || resetsAt === null || resetsAt <= 0) return "";
2322
2537
  const diff = resetsAt - Date.now();
@@ -2423,11 +2638,18 @@ window.__ModuleLoader__.load({
2423
2638
  setBusy(null);
2424
2639
  }
2425
2640
  };
2426
- const handleLogout = async () => {
2641
+ /** One account-level action on the shared pool card. */
2642
+ const handleAccountAction = async (action, accountId) => {
2427
2643
  try {
2428
- setBusy("logout");
2644
+ setBusy(`${action}-${accountId}`);
2429
2645
  setError(null);
2430
- 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
+ });
2431
2653
  setStatus(updated);
2432
2654
  notifyChange();
2433
2655
  } catch (err) {
@@ -2436,6 +2658,48 @@ window.__ModuleLoader__.load({
2436
2658
  setBusy(null);
2437
2659
  }
2438
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
+ };
2439
2703
  const handleRefreshQuota = async () => {
2440
2704
  try {
2441
2705
  setBusy("quota");
@@ -2561,155 +2825,57 @@ window.__ModuleLoader__.load({
2561
2825
  })
2562
2826
  });
2563
2827
  const quota = status?.quota;
2564
- const account = status?.account;
2828
+ status?.account;
2565
2829
  status?.models.filter((model) => model.enabled).length;
2566
2830
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2567
2831
  className: "dsha-page",
2568
2832
  children: [
2569
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2570
- className: "dsha-group",
2571
- children: [
2572
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2573
- className: "dsha-grouphead",
2574
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: status?.authenticated ? t.account : t.signedOut })
2575
- }),
2576
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2577
- className: "dsha-row",
2578
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2579
- className: "dsha-label",
2580
- children: status?.authenticated ? t.signedIn : t.signedOut
2581
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2582
- className: "dsha-value",
2583
- children: account?.email || account?.userName || ""
2584
- })]
2585
- }),
2586
- status?.authenticated && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
2587
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2588
- className: "dsha-row",
2589
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2590
- className: "dsha-label",
2591
- children: t.plan
2592
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2593
- className: "dsha-value",
2594
- children: quota?.planName || account?.planLabel || t.planUnknown
2595
- })]
2596
- }),
2597
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2598
- className: "dsha-row",
2599
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2600
- className: "dsha-label",
2601
- children: t.keyName
2602
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2603
- className: "dsha-value",
2604
- children: account?.keyName || "—"
2605
- })]
2606
- }),
2607
- quota?.subscriptionStatus && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2608
- className: "dsha-row",
2609
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2610
- className: "dsha-label",
2611
- children: t.subscription
2612
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2613
- className: "dsha-value",
2614
- children: [quota.subscriptionStatus, quota.periodEndsAt !== null ? ` · ${t.renews} ${formatDate$1(quota.periodEndsAt)}` : ""]
2615
- })]
2616
- }),
2617
- account?.organizationName && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2618
- className: "dsha-row",
2619
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2620
- className: "dsha-label",
2621
- children: t.organization
2622
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2623
- className: "dsha-value",
2624
- children: account.organizationName
2625
- })]
2626
- }),
2627
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2628
- className: "dsha-row",
2629
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2630
- className: "dsha-label",
2631
- children: t.authenticatedAt
2632
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2633
- className: "dsha-value",
2634
- children: formatDate$1(account?.authenticatedAt)
2635
- })]
2636
- })
2637
- ] }),
2638
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2639
- className: "dsha-row",
2640
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2641
- className: "dsha-label",
2642
- children: t.storage
2643
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2644
- className: "dsha-value",
2645
- children: status?.storagePath || "—"
2646
- })]
2647
- }),
2648
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2649
- className: "dsha-notice",
2650
- children: t.storageNotice
2651
- }),
2652
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2653
- className: "dsha-actions",
2654
- children: !status?.authenticated ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2655
- className: "dsha-btn dsha-btn-primary",
2656
- disabled: busy !== null,
2657
- onClick: () => void handleLogin(),
2658
- children: busy === "login" ? loginProgress || t.signingIn : t.signIn
2659
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
2660
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2661
- className: "dsha-btn dsha-btn-primary",
2662
- disabled: busy !== null,
2663
- onClick: () => void handleLogin(),
2664
- children: busy === "login" ? loginProgress || t.signingIn : t.signInAgain
2665
- }),
2666
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2667
- className: "dsha-btn",
2668
- disabled: busy !== null,
2669
- onClick: () => void handleRefreshQuota(),
2670
- children: busy === "quota" ? t.refreshingQuota : t.refreshQuota
2671
- }),
2672
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2673
- className: "dsha-btn",
2674
- disabled: busy !== null,
2675
- onClick: () => void handleLogout(),
2676
- children: t.signOut
2677
- })
2678
- ] })
2679
- })
2680
- ]
2681
- }),
2682
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2683
- className: "dsha-group",
2684
- children: [
2685
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2686
- className: "dsha-grouphead",
2687
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t.apiKeySection })
2688
- }),
2689
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2690
- className: "dsha-muted",
2691
- children: t.apiKeyHint
2692
- }),
2693
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2694
- className: "dsha-capacity-control",
2695
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2696
- type: "password",
2697
- "aria-label": t.apiKeySection,
2698
- placeholder: t.apiKeyPlaceholder,
2699
- value: apiKey,
2700
- onChange: (event) => setApiKey(event.currentTarget.value),
2701
- onKeyDown: (event) => {
2702
- if (event.key === "Enter" && apiKey.trim() !== "") handleApiKey();
2703
- }
2704
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2705
- type: "button",
2706
- className: "dsha-context-save",
2707
- disabled: busy !== null || apiKey.trim() === "",
2708
- onClick: () => void handleApiKey(),
2709
- children: busy === "apikey" ? t.apiKeySaving : t.apiKeySave
2710
- })]
2711
- })
2712
- ]
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
+ })]
2713
2879
  }),
2714
2880
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2715
2881
  className: "dsha-group",
@@ -2997,6 +3163,7 @@ window.__ModuleLoader__.load({
2997
3163
  //#region src/client/kimi-code/locales.ts
2998
3164
  const NS_KIMI_CODE = "dsh-kimi-code";
2999
3165
  const zh$1 = {
3166
+ ...accountPoolZh,
3000
3167
  title: "Kimi Code",
3001
3168
  pageDesc: "登录 Kimi Code 订阅,管理模型、思考档位、上下文窗口与额度。",
3002
3169
  account: "账号",
@@ -3109,6 +3276,7 @@ window.__ModuleLoader__.load({
3109
3276
  tokens: "tokens"
3110
3277
  };
3111
3278
  const en$1 = {
3279
+ ...accountPoolEn,
3112
3280
  title: "Kimi Code",
3113
3281
  pageDesc: "Sign in to the Kimi Code subscription and manage models, thinking level, context windows, and quota.",
3114
3282
  account: "Account",
@@ -3443,11 +3611,18 @@ window.__ModuleLoader__.load({
3443
3611
  window.setTimeout(() => setCopied(false), 2e3);
3444
3612
  } catch {}
3445
3613
  };
3446
- const handleLogout = async () => {
3614
+ /** One account-level action on the shared pool card. */
3615
+ const handleAccountAction = async (action, accountId) => {
3447
3616
  try {
3448
- setBusy("logout");
3617
+ setBusy(`${action}-${accountId}`);
3449
3618
  setError(null);
3450
- 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
+ });
3451
3626
  setStatus(updated);
3452
3627
  setFlow({ status: "idle" });
3453
3628
  notifyChange();
@@ -3457,6 +3632,48 @@ window.__ModuleLoader__.load({
3457
3632
  setBusy(null);
3458
3633
  }
3459
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
+ };
3460
3677
  const handleRefreshQuota = async () => {
3461
3678
  try {
3462
3679
  setBusy("quota");
@@ -3585,171 +3802,96 @@ window.__ModuleLoader__.load({
3585
3802
  })
3586
3803
  });
3587
3804
  const quota = status?.quota;
3588
- const account = status?.account;
3805
+ status?.account;
3589
3806
  status?.models.filter((model) => model.enabled).length;
3590
3807
  const loginPending = flow?.status === "pending";
3591
3808
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3592
3809
  className: "dsha-page",
3593
3810
  children: [
3594
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
3595
- className: "dsha-group",
3596
- children: [
3597
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3598
- className: "dsha-grouphead",
3599
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: status?.authenticated ? t.account : t.signedOut })
3600
- }),
3601
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3602
- className: "dsha-row",
3603
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3604
- className: "dsha-label",
3605
- children: status?.authenticated ? t.signedIn : t.signedOut
3606
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3607
- className: "dsha-value",
3608
- children: account?.nickname || account?.email || account?.userId || "—"
3609
- })]
3610
- }),
3611
- status?.authenticated && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
3612
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3613
- className: "dsha-row",
3614
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3615
- className: "dsha-label",
3616
- children: t.plan
3617
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3618
- className: "dsha-value",
3619
- children: quota?.planName || account?.planName || t.planUnknown
3620
- })]
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
3621
3849
  }),
3622
- account?.email && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3623
- className: "dsha-row",
3850
+ flow?.userCode && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3851
+ className: "dsha-code-row",
3624
3852
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3625
- className: "dsha-label",
3626
- children: t.email
3627
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3628
- className: "dsha-value",
3629
- children: account.email
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
3630
3859
  })]
3631
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
+ }),
3632
3868
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3633
3869
  className: "dsha-row",
3634
3870
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3635
3871
  className: "dsha-label",
3636
- children: t.region
3872
+ children: t.codeExpires
3637
3873
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3638
3874
  className: "dsha-value",
3639
- children: status.region === "global" ? t.regionGlobal : t.regionMainland
3875
+ children: formatDate(flow?.expiresAt)
3640
3876
  })]
3641
3877
  }),
3642
3878
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3643
- className: "dsha-row",
3644
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3645
- className: "dsha-label",
3646
- children: t.authenticatedAt
3647
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3648
- className: "dsha-value",
3649
- children: formatDate(account?.authenticatedAt)
3650
- })]
3651
- })
3652
- ] }),
3653
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3654
- className: "dsha-row",
3655
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3656
- className: "dsha-label",
3657
- children: t.storage
3658
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3659
- className: "dsha-value",
3660
- children: status?.storagePath || "—"
3661
- })]
3662
- }),
3663
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3664
- className: "dsha-notice",
3665
- children: t.storageNotice
3666
- }),
3667
- status?.credentialsRejected && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3668
- className: "dsha-notice",
3669
- children: t.refreshRejected
3670
- }),
3671
- loginPending && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3672
- className: "dsha-device-box",
3673
- children: [
3674
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t.deviceCodeSection }),
3675
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3676
- className: "dsha-muted",
3677
- children: t.deviceCodeHint
3678
- }),
3679
- flow?.userCode && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3680
- className: "dsha-code-row",
3681
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3682
- className: "dsha-code",
3683
- children: flow.userCode
3684
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3685
- className: "dsha-btn",
3686
- onClick: () => void handleCopyCode(),
3687
- children: copied ? t.copied : t.copyCode
3688
- })]
3689
- }),
3690
- flow?.verificationUriComplete && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
3691
- className: "dsha-link",
3879
+ className: "dsha-actions",
3880
+ children: [flow?.verificationUriComplete && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
3881
+ className: "dsha-btn",
3692
3882
  href: flow.verificationUriComplete,
3693
3883
  target: "_blank",
3694
3884
  rel: "noreferrer noopener",
3695
- children: flow.verificationUriComplete
3696
- }),
3697
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3698
- className: "dsha-row",
3699
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3700
- className: "dsha-label",
3701
- children: t.codeExpires
3702
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3703
- className: "dsha-value",
3704
- children: formatDate(flow?.expiresAt)
3705
- })]
3706
- }),
3707
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3708
- className: "dsha-actions",
3709
- children: [flow?.verificationUriComplete && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
3710
- className: "dsha-btn",
3711
- href: flow.verificationUriComplete,
3712
- target: "_blank",
3713
- rel: "noreferrer noopener",
3714
- children: t.openSignInPage
3715
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3716
- className: "dsha-btn",
3717
- disabled: busy !== null,
3718
- onClick: () => void handleCancelLogin(),
3719
- children: t.cancelSignIn
3720
- })]
3721
- })
3722
- ]
3723
- }),
3724
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3725
- className: "dsha-actions",
3726
- children: !status?.authenticated ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3727
- className: "dsha-btn dsha-btn-primary",
3728
- disabled: busy !== null || loginPending,
3729
- onClick: () => void handleLogin(),
3730
- children: loginPending ? t.waitingAuthorization : busy === "login" ? t.requestingCode : t.signIn
3731
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
3732
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3733
- className: "dsha-btn dsha-btn-primary",
3734
- disabled: busy !== null || loginPending,
3735
- onClick: () => void handleLogin(),
3736
- children: loginPending ? t.waitingAuthorization : t.signInAgain
3737
- }),
3738
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3885
+ children: t.openSignInPage
3886
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3739
3887
  className: "dsha-btn",
3740
3888
  disabled: busy !== null,
3741
- onClick: () => void handleRefreshQuota(),
3742
- children: busy === "quota" ? t.refreshingQuota : t.refreshQuota
3743
- }),
3744
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3745
- className: "dsha-btn",
3746
- disabled: busy !== null,
3747
- onClick: () => void handleLogout(),
3748
- children: t.signOut
3749
- })
3750
- ] })
3751
- })
3752
- ]
3889
+ onClick: () => void handleCancelLogin(),
3890
+ children: t.cancelSignIn
3891
+ })]
3892
+ })
3893
+ ]
3894
+ })
3753
3895
  }),
3754
3896
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
3755
3897
  className: "dsha-group",
@@ -4142,6 +4284,7 @@ window.__ModuleLoader__.load({
4142
4284
  const NS = "dsh-chatgpt-subscription";
4143
4285
  const dictionaries = {
4144
4286
  zh: {
4287
+ ...accountPoolZh,
4145
4288
  title: "ChatGPT",
4146
4289
  hubTitle: "订阅服务",
4147
4290
  intro: "使用 ChatGPT 账号登录,在 DSH 中使用订阅可用的模型。",
@@ -4270,6 +4413,7 @@ window.__ModuleLoader__.load({
4270
4413
  unknown: "未知"
4271
4414
  },
4272
4415
  en: {
4416
+ ...accountPoolEn,
4273
4417
  title: "ChatGPT",
4274
4418
  hubTitle: "Subscriptions",
4275
4419
  intro: "Sign in with ChatGPT to use models available to your subscription in DSH.",
@@ -4596,6 +4740,27 @@ window.__ModuleLoader__.load({
4596
4740
  .dsha-account-details{display:flex;flex-wrap:wrap;gap:14px;font-size:12px;color:var(--dsw-alias-label-secondary)}
4597
4741
  .dsha-account-actions{display:flex;flex-wrap:wrap;gap:6px;margin-top:2px}
4598
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)}
4599
4764
  `;
4600
4765
  document.head.append(style);
4601
4766
  }
@@ -5376,6 +5541,10 @@ window.__ModuleLoader__.load({
5376
5541
  installKimiCodeStyles();
5377
5542
  return () => {};
5378
5543
  }, "dsh-kimi-code: styles");
5544
+ ctx.effect(() => {
5545
+ installPoolStyles();
5546
+ return () => {};
5547
+ }, "dsh-chatgpt-subscription: pool styles");
5379
5548
  ctx.effect(() => setupMermaidObserver(), "dsh-mermaid: observer");
5380
5549
  const t = ctx.locale.bind(NS);
5381
5550
  const handleModelDirectoryReload = () => {