@eddyskywalker/dsh-chatgpt-subscription 0.5.0 → 0.6.5

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 (45) hide show
  1. package/CHANGELOG.md +36 -1
  2. package/README.md +67 -1
  3. package/lib/client.js +1425 -222
  4. package/lib/client.js.map +1 -1
  5. package/lib/index.js +4884 -1114
  6. package/lib/types/client/ProviderHubSection.d.ts.map +1 -1
  7. package/lib/types/client/common/AccountPoolSection.d.ts +14 -0
  8. package/lib/types/client/common/AccountPoolSection.d.ts.map +1 -1
  9. package/lib/types/client/index.d.ts +1 -0
  10. package/lib/types/client/index.d.ts.map +1 -1
  11. package/lib/types/client/workbuddy/WorkBuddyComposerQuota.d.ts +26 -0
  12. package/lib/types/client/workbuddy/WorkBuddyComposerQuota.d.ts.map +1 -0
  13. package/lib/types/client/workbuddy/WorkBuddySection.d.ts +41 -0
  14. package/lib/types/client/workbuddy/WorkBuddySection.d.ts.map +1 -0
  15. package/lib/types/client/workbuddy/locales.d.ts +485 -0
  16. package/lib/types/client/workbuddy/locales.d.ts.map +1 -0
  17. package/lib/types/client/workbuddy/styles.d.ts +15 -0
  18. package/lib/types/client/workbuddy/styles.d.ts.map +1 -0
  19. package/lib/types/host/common/account-pool.d.ts +18 -0
  20. package/lib/types/host/common/account-pool.d.ts.map +1 -1
  21. package/lib/types/host/workbuddy/account-pool.d.ts +106 -0
  22. package/lib/types/host/workbuddy/account-pool.d.ts.map +1 -0
  23. package/lib/types/host/workbuddy/adapter.d.ts +97 -0
  24. package/lib/types/host/workbuddy/adapter.d.ts.map +1 -0
  25. package/lib/types/host/workbuddy/client.d.ts +90 -0
  26. package/lib/types/host/workbuddy/client.d.ts.map +1 -0
  27. package/lib/types/host/workbuddy/mapper.d.ts +114 -0
  28. package/lib/types/host/workbuddy/mapper.d.ts.map +1 -0
  29. package/lib/types/host/workbuddy/model-catalog.d.ts +108 -0
  30. package/lib/types/host/workbuddy/model-catalog.d.ts.map +1 -0
  31. package/lib/types/host/workbuddy/oauth.d.ts +38 -0
  32. package/lib/types/host/workbuddy/oauth.d.ts.map +1 -0
  33. package/lib/types/host/workbuddy/routes.d.ts +33 -0
  34. package/lib/types/host/workbuddy/routes.d.ts.map +1 -0
  35. package/lib/types/host/workbuddy/token-store.d.ts +216 -0
  36. package/lib/types/host/workbuddy/token-store.d.ts.map +1 -0
  37. package/lib/types/host/workbuddy/types.d.ts +127 -0
  38. package/lib/types/host/workbuddy/types.d.ts.map +1 -0
  39. package/lib/types/index.d.ts +5 -0
  40. package/lib/types/index.d.ts.map +1 -1
  41. package/lib/types/shared/account-pool-contracts.d.ts +8 -0
  42. package/lib/types/shared/account-pool-contracts.d.ts.map +1 -1
  43. package/lib/types/shared/workbuddy-contracts.d.ts +210 -0
  44. package/lib/types/shared/workbuddy-contracts.d.ts.map +1 -0
  45. package/package.json +1 -1
package/lib/client.js CHANGED
@@ -56,7 +56,7 @@ window.__ModuleLoader__.load({
56
56
  //#endregion
57
57
  //#region src/client/CodexComposerQuota.tsx
58
58
  function CodexComposerQuota({ api, directory, loadModelDirectory, t }) {
59
- const modelState = useStore$3(directory);
59
+ const modelState = useStore$4(directory);
60
60
  const [status, setStatus] = (0, react.useState)(null);
61
61
  const [loading, setLoading] = (0, react.useState)(false);
62
62
  const mountedRef = (0, react.useRef)(false);
@@ -103,7 +103,7 @@ window.__ModuleLoader__.load({
103
103
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("quickQuotaLabel") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: quota === null ? loading ? t("quickQuotaLoading") : "—" : formatPercent$1(quota.remainingPercent) })]
104
104
  });
105
105
  }
106
- function useStore$3(store) {
106
+ function useStore$4(store) {
107
107
  return (0, react.useSyncExternalStore)((listener) => store.subscribe(listener), () => store.getSnapshot(), () => store.getSnapshot());
108
108
  }
109
109
  function formatPercent$1(value) {
@@ -479,7 +479,7 @@ window.__ModuleLoader__.load({
479
479
  formatPoolLabel(t.accountCount, { count: accounts.length }),
480
480
  ")"
481
481
  ]
482
- })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
482
+ })] }), props.renderLoginActions !== void 0 ? props.renderLoginActions() : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
483
483
  className: "dsha-btn dsha-btn-primary",
484
484
  disabled: props.busy !== null,
485
485
  onClick: props.onLogin,
@@ -565,6 +565,7 @@ window.__ModuleLoader__.load({
565
565
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
566
566
  className: "dsha-account-actions",
567
567
  children: [
568
+ props.renderAccountActions?.(account),
568
569
  !account.isPrimary && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
569
570
  className: "dsha-btn",
570
571
  disabled: props.busy !== null,
@@ -583,7 +584,7 @@ window.__ModuleLoader__.load({
583
584
  onClick: () => props.onRelogin(account.id),
584
585
  children: t.relogin
585
586
  }),
586
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
587
+ account.removable !== false && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
587
588
  className: "dsha-btn",
588
589
  disabled: props.busy !== null,
589
590
  onClick: () => props.onDelete(account.id),
@@ -1008,7 +1009,7 @@ window.__ModuleLoader__.load({
1008
1009
  }),
1009
1010
  connection !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoRow, {
1010
1011
  label: t("latency"),
1011
- value: `${connection.latencyMs} ms · ${formatDate$2(connection.checkedAt)}`
1012
+ value: `${connection.latencyMs} ms · ${formatDate$3(connection.checkedAt)}`
1012
1013
  }) : null,
1013
1014
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1014
1015
  className: "dsha-muted dsha-models-hint",
@@ -1281,7 +1282,7 @@ window.__ModuleLoader__.load({
1281
1282
  id: inputId,
1282
1283
  type: "text",
1283
1284
  inputMode: "numeric",
1284
- value: draft ?? formatCapacity$3(fallback),
1285
+ value: draft ?? formatCapacity$4(fallback),
1285
1286
  disabled: busy !== null,
1286
1287
  "aria-label": entry.name + " " + t("contextWindow"),
1287
1288
  onChange: (event) => {
@@ -1395,7 +1396,7 @@ window.__ModuleLoader__.load({
1395
1396
  children: [
1396
1397
  t("updated"),
1397
1398
  ": ",
1398
- formatDate$2(quota.fetchedAt)
1399
+ formatDate$3(quota.fetchedAt)
1399
1400
  ]
1400
1401
  }) : null
1401
1402
  ]
@@ -1487,7 +1488,7 @@ window.__ModuleLoader__.load({
1487
1488
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("small", { children: [
1488
1489
  t("resetCreditExpires"),
1489
1490
  ": ",
1490
- resetCredits.expiresAt == null ? t("unknown") : formatReset$2(resetCredits.expiresAt)
1491
+ resetCredits.expiresAt == null ? t("unknown") : formatReset$3(resetCredits.expiresAt)
1491
1492
  ] })
1492
1493
  ]
1493
1494
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
@@ -1520,7 +1521,7 @@ window.__ModuleLoader__.load({
1520
1521
  }),
1521
1522
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1522
1523
  className: "dsh-codex-meter-meta",
1523
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: percent >= 100 ? `${t("exhausted")} · ${formatPercent(remaining)} ${t("remaining")}` : `${formatPercent(percent)} ${t("used")} · ${formatPercent(remaining)} ${t("remaining")}` }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: window?.resetsAt == null ? "—" : `${t("resets")}: ${formatReset$2(window.resetsAt)}` })]
1524
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: percent >= 100 ? `${t("exhausted")} · ${formatPercent(remaining)} ${t("remaining")}` : `${formatPercent(percent)} ${t("used")} · ${formatPercent(remaining)} ${t("remaining")}` }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: window?.resetsAt == null ? "—" : `${t("resets")}: ${formatReset$3(window.resetsAt)}` })]
1524
1525
  })
1525
1526
  ]
1526
1527
  });
@@ -1546,7 +1547,7 @@ window.__ModuleLoader__.load({
1546
1547
  return `${value} ${t("limitWindow")}`;
1547
1548
  }
1548
1549
  }
1549
- function parsePositiveCapacity$3(value) {
1550
+ function parsePositiveCapacity$4(value) {
1550
1551
  const matched = value.trim().toLowerCase().replace(/[,_\s]/g, "").match(/^(\d+(?:\.\d+)?)(k|m)?$/);
1551
1552
  if (matched === null) return null;
1552
1553
  const multiplier = matched[2] === "m" ? 1e6 : matched[2] === "k" ? 1e3 : 1;
@@ -1554,10 +1555,10 @@ window.__ModuleLoader__.load({
1554
1555
  return Number.isSafeInteger(parsed) && parsed >= 1 ? parsed : null;
1555
1556
  }
1556
1557
  function parseCapacity(value, maxContextWindow = GPT_56_MAX_CONTEXT_WINDOW) {
1557
- const parsed = parsePositiveCapacity$3(value);
1558
+ const parsed = parsePositiveCapacity$4(value);
1558
1559
  return parsed !== null && parsed <= maxContextWindow ? parsed : null;
1559
1560
  }
1560
- function formatCapacity$3(value) {
1561
+ function formatCapacity$4(value) {
1561
1562
  if (value % 1e3 === 0) return `${value / 1e3}K`;
1562
1563
  return String(value);
1563
1564
  }
@@ -1574,11 +1575,11 @@ window.__ModuleLoader__.load({
1574
1575
  limit.remainingPercent !== null && limit.remainingPercent !== void 0 ? `${formatPercent(limit.remainingPercent)} ${t("remaining")}` : null,
1575
1576
  limit.limit !== null && limit.limit !== void 0 ? `${t("limit")}: ${limit.limit}` : null,
1576
1577
  limit.used !== null && limit.used !== void 0 ? `${t("used")}: ${limit.used}` : null,
1577
- limit.resetsAt !== null && limit.resetsAt !== void 0 ? `${t("resets")}: ${formatReset$2(limit.resetsAt)}` : null
1578
+ limit.resetsAt !== null && limit.resetsAt !== void 0 ? `${t("resets")}: ${formatReset$3(limit.resetsAt)}` : null
1578
1579
  ].filter((part) => part !== null);
1579
1580
  return parts.length > 0 ? parts.join(" · ") : t("unknown");
1580
1581
  }
1581
- function formatDate$2(seconds) {
1582
+ function formatDate$3(seconds) {
1582
1583
  if (seconds === void 0 || seconds === null || typeof seconds !== "number" || !Number.isFinite(seconds) || seconds <= 0) return "—";
1583
1584
  const ms = seconds > 1e10 ? seconds : seconds * 1e3;
1584
1585
  try {
@@ -1590,10 +1591,10 @@ window.__ModuleLoader__.load({
1590
1591
  return "—";
1591
1592
  }
1592
1593
  }
1593
- function formatReset$2(seconds) {
1594
+ function formatReset$3(seconds) {
1594
1595
  if (seconds === void 0 || seconds === null || typeof seconds !== "number" || !Number.isFinite(seconds) || seconds <= 0) return "—";
1595
1596
  const ms = seconds > 1e10 ? seconds : seconds * 1e3;
1596
- const absolute = formatDate$2(seconds);
1597
+ const absolute = formatDate$3(seconds);
1597
1598
  const diff = ms - Date.now();
1598
1599
  const abs = Math.abs(diff);
1599
1600
  const [amount, unit] = abs >= 864e5 ? [Math.round(diff / 864e5), "day"] : abs >= 36e5 ? [Math.round(diff / 36e5), "hour"] : [Math.round(diff / 6e4), "minute"];
@@ -1609,7 +1610,7 @@ window.__ModuleLoader__.load({
1609
1610
  //#endregion
1610
1611
  //#region src/client/antigravity/locales.ts
1611
1612
  const NS_ANTIGRAVITY = "dsh-antigravity";
1612
- const zh$3 = {
1613
+ const zh$4 = {
1613
1614
  ...accountPoolZh,
1614
1615
  title: "Antigravity",
1615
1616
  pageDesc: "登录 Google Antigravity / Cloud Code Assist,并查看当前账号的共享额度。",
@@ -1675,7 +1676,7 @@ window.__ModuleLoader__.load({
1675
1676
  loginFailed: "登录失败",
1676
1677
  tokens: "tokens"
1677
1678
  };
1678
- const en$3 = {
1679
+ const en$4 = {
1679
1680
  ...accountPoolEn,
1680
1681
  title: "Antigravity",
1681
1682
  pageDesc: "Sign in to Google Antigravity / Cloud Code Assist and view shared quotas.",
@@ -1741,17 +1742,17 @@ window.__ModuleLoader__.load({
1741
1742
  loginFailed: "Login failed",
1742
1743
  tokens: "tokens"
1743
1744
  };
1744
- const dictionaries$3 = {
1745
- zh: zh$3,
1746
- "zh-CN": zh$3,
1747
- en: en$3,
1748
- "en-US": en$3
1745
+ const dictionaries$4 = {
1746
+ zh: zh$4,
1747
+ "zh-CN": zh$4,
1748
+ en: en$4,
1749
+ "en-US": en$4
1749
1750
  };
1750
1751
  //#endregion
1751
1752
  //#region src/client/antigravity/AntigravitySection.tsx
1752
- const API$5 = "/antigravity/api";
1753
- async function fetchApi$5(path, options) {
1754
- const res = await fetch(`${API$5}${path}`, {
1753
+ const API$7 = "/antigravity/api";
1754
+ async function fetchApi$7(path, options) {
1755
+ const res = await fetch(`${API$7}${path}`, {
1755
1756
  ...options,
1756
1757
  headers: {
1757
1758
  "Content-Type": "application/json",
@@ -1762,14 +1763,14 @@ window.__ModuleLoader__.load({
1762
1763
  if (!res.ok || !json.ok) throw new Error(json.error || `HTTP ${res.status}`);
1763
1764
  return json.value;
1764
1765
  }
1765
- function parsePositiveCapacity$2(value) {
1766
+ function parsePositiveCapacity$3(value) {
1766
1767
  const matched = value.trim().toLowerCase().replace(/[,_\s]/g, "").match(/^(\d+(?:\.\d+)?)(k|m)?$/);
1767
1768
  if (matched === null) return null;
1768
1769
  const multiplier = matched[2] === "m" ? 1e6 : matched[2] === "k" ? 1e3 : 1;
1769
1770
  const parsed = Number(matched[1]) * multiplier;
1770
1771
  return Number.isSafeInteger(parsed) && parsed >= 1 ? parsed : null;
1771
1772
  }
1772
- function formatCapacity$2(value) {
1773
+ function formatCapacity$3(value) {
1773
1774
  if (value >= 1e6 && value % 1e5 === 0) return `${value / 1e6}M`;
1774
1775
  if (value % 1e3 === 0) return `${value / 1e3}K`;
1775
1776
  return String(value);
@@ -1785,7 +1786,7 @@ window.__ModuleLoader__.load({
1785
1786
  if (hours > 0) return `${hours}h ${mins % 60}m`;
1786
1787
  return `${mins}m`;
1787
1788
  }
1788
- function formatDate$1(ms) {
1789
+ function formatDate$2(ms) {
1789
1790
  if (!ms || ms <= 0) return "—";
1790
1791
  try {
1791
1792
  return new Intl.DateTimeFormat(void 0, {
@@ -1805,7 +1806,7 @@ window.__ModuleLoader__.load({
1805
1806
  const [loginProgress, setLoginProgress] = (0, react.useState)(null);
1806
1807
  const [contextDrafts, setContextDrafts] = (0, react.useState)({});
1807
1808
  const [savingModel, setSavingModel] = (0, react.useState)(null);
1808
- const t = zh$3;
1809
+ const t = zh$4;
1809
1810
  const notifyChange = (0, react.useCallback)(() => {
1810
1811
  onModelChange?.();
1811
1812
  loadModelDirectory?.();
@@ -1816,12 +1817,12 @@ window.__ModuleLoader__.load({
1816
1817
  setValidationUrl(null);
1817
1818
  }
1818
1819
  try {
1819
- const data = await fetchApi$5("/status");
1820
+ const data = await fetchApi$7("/status");
1820
1821
  setStatus(data);
1821
1822
  const drafts = {};
1822
1823
  for (const m of data.models) {
1823
1824
  const val = data.contextWindowOverrides[m.id] || m.defaultContextWindow;
1824
- drafts[m.id] = formatCapacity$2(val);
1825
+ drafts[m.id] = formatCapacity$3(val);
1825
1826
  }
1826
1827
  setContextDrafts(drafts);
1827
1828
  } catch (err) {
@@ -1848,11 +1849,11 @@ window.__ModuleLoader__.load({
1848
1849
  setError(null);
1849
1850
  setValidationUrl(null);
1850
1851
  setLoginProgress(null);
1851
- const flow = await fetchApi$5("/login", { method: "POST" });
1852
+ const flow = await fetchApi$7("/login", { method: "POST" });
1852
1853
  if (flow.authUrl) window.open(flow.authUrl, "_blank");
1853
1854
  const pollTimer = setInterval(async () => {
1854
1855
  try {
1855
- const pollStatus = await fetchApi$5("/login/status");
1856
+ const pollStatus = await fetchApi$7("/login/status");
1856
1857
  if (pollStatus.progress) setLoginProgress(pollStatus.progress);
1857
1858
  if (pollStatus.status === "complete") {
1858
1859
  clearInterval(pollTimer);
@@ -1884,7 +1885,7 @@ window.__ModuleLoader__.load({
1884
1885
  try {
1885
1886
  setBusy("quota");
1886
1887
  setError(null);
1887
- const updated = await fetchApi$5("/quota", { method: "POST" });
1888
+ const updated = await fetchApi$7("/quota", { method: "POST" });
1888
1889
  setStatus(updated);
1889
1890
  } catch (err) {
1890
1891
  setError(err instanceof Error ? err.message : String(err));
@@ -1896,7 +1897,7 @@ window.__ModuleLoader__.load({
1896
1897
  try {
1897
1898
  setBusy(`primary-${accountId}`);
1898
1899
  setError(null);
1899
- const updated = await fetchApi$5("/accounts", {
1900
+ const updated = await fetchApi$7("/accounts", {
1900
1901
  method: "POST",
1901
1902
  body: JSON.stringify({
1902
1903
  action: "set-primary",
@@ -1915,7 +1916,7 @@ window.__ModuleLoader__.load({
1915
1916
  try {
1916
1917
  setBusy(`delete-${accountId}`);
1917
1918
  setError(null);
1918
- const updated = await fetchApi$5("/accounts", {
1919
+ const updated = await fetchApi$7("/accounts", {
1919
1920
  method: "POST",
1920
1921
  body: JSON.stringify({
1921
1922
  action: "delete",
@@ -1934,7 +1935,7 @@ window.__ModuleLoader__.load({
1934
1935
  try {
1935
1936
  setBusy(`cooldown-${accountId}`);
1936
1937
  setError(null);
1937
- const updated = await fetchApi$5("/accounts", {
1938
+ const updated = await fetchApi$7("/accounts", {
1938
1939
  method: "POST",
1939
1940
  body: JSON.stringify({
1940
1941
  action: "clear-cooldown",
@@ -1952,7 +1953,7 @@ window.__ModuleLoader__.load({
1952
1953
  try {
1953
1954
  setBusy("strategy");
1954
1955
  setError(null);
1955
- const updated = await fetchApi$5("/accounts", {
1956
+ const updated = await fetchApi$7("/accounts", {
1956
1957
  method: "POST",
1957
1958
  body: JSON.stringify({
1958
1959
  action: "strategy",
@@ -1972,7 +1973,7 @@ window.__ModuleLoader__.load({
1972
1973
  enabled
1973
1974
  } : prev);
1974
1975
  try {
1975
- const updated = await fetchApi$5("/settings", {
1976
+ const updated = await fetchApi$7("/settings", {
1976
1977
  method: "POST",
1977
1978
  body: JSON.stringify({ enabled })
1978
1979
  });
@@ -1988,7 +1989,7 @@ window.__ModuleLoader__.load({
1988
1989
  const currentEnabled = status.models.filter((m) => m.enabled).map((m) => m.id);
1989
1990
  const nextEnabled = checked ? [.../* @__PURE__ */ new Set([...currentEnabled, modelId])] : currentEnabled.filter((id) => id !== modelId);
1990
1991
  try {
1991
- const updated = await fetchApi$5("/models", {
1992
+ const updated = await fetchApi$7("/models", {
1992
1993
  method: "POST",
1993
1994
  body: JSON.stringify({ enabledModelIds: nextEnabled })
1994
1995
  });
@@ -2002,7 +2003,7 @@ window.__ModuleLoader__.load({
2002
2003
  if (!status) return;
2003
2004
  const nextEnabled = selectAll ? status.models.map((m) => m.id) : [];
2004
2005
  try {
2005
- const updated = await fetchApi$5("/models", {
2006
+ const updated = await fetchApi$7("/models", {
2006
2007
  method: "POST",
2007
2008
  body: JSON.stringify({ enabledModelIds: nextEnabled })
2008
2009
  });
@@ -2014,7 +2015,7 @@ window.__ModuleLoader__.load({
2014
2015
  };
2015
2016
  const handleUpdateEffort = async (effort) => {
2016
2017
  try {
2017
- const updated = await fetchApi$5("/settings", {
2018
+ const updated = await fetchApi$7("/settings", {
2018
2019
  method: "POST",
2019
2020
  body: JSON.stringify({ defaultReasoningEffort: effort })
2020
2021
  });
@@ -2026,7 +2027,7 @@ window.__ModuleLoader__.load({
2026
2027
  };
2027
2028
  const handleSaveContextWindow = async (modelId) => {
2028
2029
  const raw = contextDrafts[modelId] || "";
2029
- const parsed = parsePositiveCapacity$2(raw);
2030
+ const parsed = parsePositiveCapacity$3(raw);
2030
2031
  if (parsed === null) {
2031
2032
  setError(`无效的上下文容量值: ${raw}`);
2032
2033
  return;
@@ -2034,14 +2035,14 @@ window.__ModuleLoader__.load({
2034
2035
  try {
2035
2036
  setSavingModel(modelId);
2036
2037
  setError(null);
2037
- const updated = await fetchApi$5("/settings", {
2038
+ const updated = await fetchApi$7("/settings", {
2038
2039
  method: "POST",
2039
2040
  body: JSON.stringify({ contextWindowOverrides: { [modelId]: parsed } })
2040
2041
  });
2041
2042
  setStatus(updated);
2042
2043
  setContextDrafts((prev) => ({
2043
2044
  ...prev,
2044
- [modelId]: formatCapacity$2(parsed)
2045
+ [modelId]: formatCapacity$3(parsed)
2045
2046
  }));
2046
2047
  notifyChange();
2047
2048
  } catch (err) {
@@ -2089,12 +2090,12 @@ window.__ModuleLoader__.load({
2089
2090
  account.expiresAt && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
2090
2091
  t.expires,
2091
2092
  ": ",
2092
- formatDate$1(account.expiresAt)
2093
+ formatDate$2(account.expiresAt)
2093
2094
  ] }),
2094
2095
  account.lastUsedAt && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
2095
2096
  t.lastUsed,
2096
2097
  ": ",
2097
- formatDate$1(account.lastUsedAt)
2098
+ formatDate$2(account.lastUsedAt)
2098
2099
  ] })
2099
2100
  ] })
2100
2101
  }),
@@ -2350,7 +2351,7 @@ window.__ModuleLoader__.load({
2350
2351
  //#endregion
2351
2352
  //#region src/client/command-code/locales.ts
2352
2353
  const NS_COMMAND_CODE = "dsh-command-code";
2353
- const zh$2 = {
2354
+ const zh$3 = {
2354
2355
  ...accountPoolZh,
2355
2356
  title: "Command Code",
2356
2357
  pageDesc: "登录 Command Code,管理可用模型、上下文窗口与额度。",
@@ -2418,7 +2419,7 @@ window.__ModuleLoader__.load({
2418
2419
  loginFailed: "登录失败",
2419
2420
  tokens: "tokens"
2420
2421
  };
2421
- const en$2 = {
2422
+ const en$3 = {
2422
2423
  ...accountPoolEn,
2423
2424
  title: "Command Code",
2424
2425
  pageDesc: "Sign in to Command Code and manage models, context windows, and quota.",
@@ -2486,11 +2487,11 @@ window.__ModuleLoader__.load({
2486
2487
  loginFailed: "Sign-in failed",
2487
2488
  tokens: "tokens"
2488
2489
  };
2489
- const dictionaries$2 = {
2490
- zh: zh$2,
2491
- "zh-CN": zh$2,
2492
- en: en$2,
2493
- "en-US": en$2
2490
+ const dictionaries$3 = {
2491
+ zh: zh$3,
2492
+ "zh-CN": zh$3,
2493
+ en: en$3,
2494
+ "en-US": en$3
2494
2495
  };
2495
2496
  //#endregion
2496
2497
  //#region src/client/command-code/CommandCodeSection.tsx
@@ -2498,7 +2499,7 @@ window.__ModuleLoader__.load({
2498
2499
  * Display label per reasoning level. The locale dictionary only accepts flat
2499
2500
  * string values, so this nested table lives with the control that renders it.
2500
2501
  */
2501
- const EFFORT_LABELS = {
2502
+ const EFFORT_LABELS$1 = {
2502
2503
  minimal: "Minimal",
2503
2504
  low: "Low",
2504
2505
  medium: "Medium",
@@ -2506,9 +2507,9 @@ window.__ModuleLoader__.load({
2506
2507
  xhigh: "X-High",
2507
2508
  max: "Max"
2508
2509
  };
2509
- const API$4 = "/command-code/api";
2510
- async function fetchApi$4(path, options) {
2511
- const res = await fetch(`${API$4}${path}`, {
2510
+ const API$6 = "/command-code/api";
2511
+ async function fetchApi$6(path, options) {
2512
+ const res = await fetch(`${API$6}${path}`, {
2512
2513
  ...options,
2513
2514
  headers: {
2514
2515
  "Content-Type": "application/json",
@@ -2520,19 +2521,19 @@ window.__ModuleLoader__.load({
2520
2521
  return json.value;
2521
2522
  }
2522
2523
  /** Parse "1M", "512K", "200000" into a positive integer token count. */
2523
- function parsePositiveCapacity$1(value) {
2524
+ function parsePositiveCapacity$2(value) {
2524
2525
  const matched = value.trim().toLowerCase().replace(/[,_\s]/g, "").match(/^(\d+(?:\.\d+)?)(k|m)?$/);
2525
2526
  if (matched === null) return null;
2526
2527
  const multiplier = matched[2] === "m" ? 1e6 : matched[2] === "k" ? 1e3 : 1;
2527
2528
  const parsed = Number(matched[1]) * multiplier;
2528
2529
  return Number.isSafeInteger(parsed) && parsed >= 1 ? parsed : null;
2529
2530
  }
2530
- function formatCapacity$1(value) {
2531
+ function formatCapacity$2(value) {
2531
2532
  if (value >= 1e6 && value % 1e5 === 0) return `${value / 1e6}M`;
2532
2533
  if (value >= 1e3 && value % 1e3 === 0) return `${value / 1e3}K`;
2533
2534
  return String(value);
2534
2535
  }
2535
- function formatReset$1(resetsAt) {
2536
+ function formatReset$2(resetsAt) {
2536
2537
  if (resetsAt === void 0 || resetsAt === null || resetsAt <= 0) return "";
2537
2538
  const diff = resetsAt - Date.now();
2538
2539
  if (diff <= 0) return "now";
@@ -2552,7 +2553,7 @@ window.__ModuleLoader__.load({
2552
2553
  const [contextDrafts, setContextDrafts] = (0, react.useState)({});
2553
2554
  const [savingModel, setSavingModel] = (0, react.useState)(null);
2554
2555
  const [apiKey, setApiKey] = (0, react.useState)("");
2555
- const t = zh$2;
2556
+ const t = zh$3;
2556
2557
  const notifyChange = (0, react.useCallback)(() => {
2557
2558
  onModelChange?.();
2558
2559
  loadModelDirectory?.();
@@ -2560,10 +2561,10 @@ window.__ModuleLoader__.load({
2560
2561
  const loadStatus = (0, react.useCallback)(async (quiet = false) => {
2561
2562
  if (!quiet) setError(null);
2562
2563
  try {
2563
- const data = await fetchApi$4("/status");
2564
+ const data = await fetchApi$6("/status");
2564
2565
  setStatus(data);
2565
2566
  const drafts = {};
2566
- for (const model of data.models) drafts[model.id] = formatCapacity$1(data.contextWindowOverrides[model.id] || model.defaultContextWindow);
2567
+ for (const model of data.models) drafts[model.id] = formatCapacity$2(data.contextWindowOverrides[model.id] || model.defaultContextWindow);
2567
2568
  setContextDrafts(drafts);
2568
2569
  } catch (err) {
2569
2570
  if (!quiet) setError(err instanceof Error ? err.message : String(err));
@@ -2588,12 +2589,12 @@ window.__ModuleLoader__.load({
2588
2589
  setBusy("login");
2589
2590
  setError(null);
2590
2591
  setLoginProgress(null);
2591
- const flow = await fetchApi$4("/login", { method: "POST" });
2592
+ const flow = await fetchApi$6("/login", { method: "POST" });
2592
2593
  if (flow.authUrl) window.open(flow.authUrl, "_blank");
2593
2594
  const pollTimer = window.setInterval(() => {
2594
2595
  (async () => {
2595
2596
  try {
2596
- const poll = await fetchApi$4("/login/status");
2597
+ const poll = await fetchApi$6("/login/status");
2597
2598
  if (poll.progress) setLoginProgress(poll.progress);
2598
2599
  if (poll.status === "complete") {
2599
2600
  window.clearInterval(pollTimer);
@@ -2625,7 +2626,7 @@ window.__ModuleLoader__.load({
2625
2626
  try {
2626
2627
  setBusy("apikey");
2627
2628
  setError(null);
2628
- const updated = await fetchApi$4("/login/apikey", {
2629
+ const updated = await fetchApi$6("/login/apikey", {
2629
2630
  method: "POST",
2630
2631
  body: JSON.stringify({ apiKey })
2631
2632
  });
@@ -2643,7 +2644,7 @@ window.__ModuleLoader__.load({
2643
2644
  try {
2644
2645
  setBusy(`${action}-${accountId}`);
2645
2646
  setError(null);
2646
- const updated = await fetchApi$4("/accounts", {
2647
+ const updated = await fetchApi$6("/accounts", {
2647
2648
  method: "POST",
2648
2649
  body: JSON.stringify({
2649
2650
  action,
@@ -2662,7 +2663,7 @@ window.__ModuleLoader__.load({
2662
2663
  try {
2663
2664
  setBusy("strategy");
2664
2665
  setError(null);
2665
- const updated = await fetchApi$4("/accounts", {
2666
+ const updated = await fetchApi$6("/accounts", {
2666
2667
  method: "POST",
2667
2668
  body: JSON.stringify({
2668
2669
  action: "strategy",
@@ -2685,7 +2686,7 @@ window.__ModuleLoader__.load({
2685
2686
  const handleRelogin = (accountId) => {
2686
2687
  (async () => {
2687
2688
  try {
2688
- const updated = await fetchApi$4("/accounts", {
2689
+ const updated = await fetchApi$6("/accounts", {
2689
2690
  method: "POST",
2690
2691
  body: JSON.stringify({
2691
2692
  action: "relogin",
@@ -2704,7 +2705,7 @@ window.__ModuleLoader__.load({
2704
2705
  try {
2705
2706
  setBusy("quota");
2706
2707
  setError(null);
2707
- const updated = await fetchApi$4("/quota", { method: "POST" });
2708
+ const updated = await fetchApi$6("/quota", { method: "POST" });
2708
2709
  setStatus(updated);
2709
2710
  } catch (err) {
2710
2711
  setError(err instanceof Error ? err.message : String(err));
@@ -2716,10 +2717,10 @@ window.__ModuleLoader__.load({
2716
2717
  try {
2717
2718
  setBusy("catalog");
2718
2719
  setError(null);
2719
- const updated = await fetchApi$4("/catalog/refresh", { method: "POST" });
2720
+ const updated = await fetchApi$6("/catalog/refresh", { method: "POST" });
2720
2721
  setStatus(updated);
2721
2722
  const drafts = {};
2722
- for (const model of updated.models) drafts[model.id] = formatCapacity$1(updated.contextWindowOverrides[model.id] || model.defaultContextWindow);
2723
+ for (const model of updated.models) drafts[model.id] = formatCapacity$2(updated.contextWindowOverrides[model.id] || model.defaultContextWindow);
2723
2724
  setContextDrafts(drafts);
2724
2725
  notifyChange();
2725
2726
  } catch (err) {
@@ -2732,7 +2733,7 @@ window.__ModuleLoader__.load({
2732
2733
  try {
2733
2734
  setBusy("connection");
2734
2735
  setError(null);
2735
- await fetchApi$4("/connection/test", { method: "POST" });
2736
+ await fetchApi$6("/connection/test", { method: "POST" });
2736
2737
  await loadStatus(true);
2737
2738
  } catch (err) {
2738
2739
  setError(err instanceof Error ? err.message : String(err));
@@ -2742,7 +2743,7 @@ window.__ModuleLoader__.load({
2742
2743
  };
2743
2744
  const applyEnabled = async (enabledModelIds) => {
2744
2745
  try {
2745
- const updated = await fetchApi$4("/models", {
2746
+ const updated = await fetchApi$6("/models", {
2746
2747
  method: "POST",
2747
2748
  body: JSON.stringify({ enabledModelIds })
2748
2749
  });
@@ -2758,7 +2759,7 @@ window.__ModuleLoader__.load({
2758
2759
  enabled
2759
2760
  } : prev);
2760
2761
  try {
2761
- const updated = await fetchApi$4("/settings", {
2762
+ const updated = await fetchApi$6("/settings", {
2762
2763
  method: "POST",
2763
2764
  body: JSON.stringify({ enabled })
2764
2765
  });
@@ -2781,7 +2782,7 @@ window.__ModuleLoader__.load({
2781
2782
  };
2782
2783
  const handleUpdateEffort = async (effort) => {
2783
2784
  try {
2784
- const updated = await fetchApi$4("/settings", {
2785
+ const updated = await fetchApi$6("/settings", {
2785
2786
  method: "POST",
2786
2787
  body: JSON.stringify({ defaultReasoningEffort: effort })
2787
2788
  });
@@ -2793,7 +2794,7 @@ window.__ModuleLoader__.load({
2793
2794
  };
2794
2795
  const handleSaveContextWindow = async (modelId) => {
2795
2796
  const raw = contextDrafts[modelId] || "";
2796
- const parsed = parsePositiveCapacity$1(raw);
2797
+ const parsed = parsePositiveCapacity$2(raw);
2797
2798
  if (parsed === null) {
2798
2799
  setError(`Invalid context capacity: ${raw}`);
2799
2800
  return;
@@ -2801,14 +2802,14 @@ window.__ModuleLoader__.load({
2801
2802
  try {
2802
2803
  setSavingModel(modelId);
2803
2804
  setError(null);
2804
- const updated = await fetchApi$4("/settings", {
2805
+ const updated = await fetchApi$6("/settings", {
2805
2806
  method: "POST",
2806
2807
  body: JSON.stringify({ contextWindowOverrides: { [modelId]: parsed } })
2807
2808
  });
2808
2809
  setStatus(updated);
2809
2810
  setContextDrafts((prev) => ({
2810
2811
  ...prev,
2811
- [modelId]: formatCapacity$1(parsed)
2812
+ [modelId]: formatCapacity$2(parsed)
2812
2813
  }));
2813
2814
  notifyChange();
2814
2815
  } catch (err) {
@@ -3004,7 +3005,7 @@ window.__ModuleLoader__.load({
3004
3005
  children: t.defaultEffortAuto
3005
3006
  }), COMMAND_CODE_REASONING_EFFORTS.map((effort) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3006
3007
  value: effort,
3007
- children: EFFORT_LABELS[effort]
3008
+ children: EFFORT_LABELS$1[effort]
3008
3009
  }, effort))]
3009
3010
  })]
3010
3011
  })]
@@ -3098,7 +3099,7 @@ window.__ModuleLoader__.load({
3098
3099
  }),
3099
3100
  window.resetsAt !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3100
3101
  className: "dsha-meter-meta",
3101
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: ["Reset: ", formatReset$1(window.resetsAt)] })
3102
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: ["Reset: ", formatReset$2(window.resetsAt)] })
3102
3103
  })
3103
3104
  ]
3104
3105
  }, window.id)),
@@ -3123,7 +3124,7 @@ window.__ModuleLoader__.load({
3123
3124
  meter.used ?? "—",
3124
3125
  " / ",
3125
3126
  meter.limit ?? "—"
3126
- ] }), meter.resetsAt !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: ["Reset: ", formatReset$1(meter.resetsAt)] })]
3127
+ ] }), meter.resetsAt !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: ["Reset: ", formatReset$2(meter.resetsAt)] })]
3127
3128
  }) : meter.description !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3128
3129
  className: "dsha-meter-meta",
3129
3130
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: meter.description })
@@ -3162,7 +3163,7 @@ window.__ModuleLoader__.load({
3162
3163
  //#endregion
3163
3164
  //#region src/client/kimi-code/locales.ts
3164
3165
  const NS_KIMI_CODE = "dsh-kimi-code";
3165
- const zh$1 = {
3166
+ const zh$2 = {
3166
3167
  ...accountPoolZh,
3167
3168
  title: "Kimi Code",
3168
3169
  pageDesc: "登录 Kimi Code 订阅,管理模型、思考档位、上下文窗口与额度。",
@@ -3275,7 +3276,7 @@ window.__ModuleLoader__.load({
3275
3276
  requests: "次请求",
3276
3277
  tokens: "tokens"
3277
3278
  };
3278
- const en$1 = {
3279
+ const en$2 = {
3279
3280
  ...accountPoolEn,
3280
3281
  title: "Kimi Code",
3281
3282
  pageDesc: "Sign in to the Kimi Code subscription and manage models, thinking level, context windows, and quota.",
@@ -3388,11 +3389,11 @@ window.__ModuleLoader__.load({
3388
3389
  requests: "requests",
3389
3390
  tokens: "tokens"
3390
3391
  };
3391
- const dictionaries$1 = {
3392
- zh: zh$1,
3393
- "zh-CN": zh$1,
3394
- en: en$1,
3395
- "en-US": en$1
3392
+ const dictionaries$2 = {
3393
+ zh: zh$2,
3394
+ "zh-CN": zh$2,
3395
+ en: en$2,
3396
+ "en-US": en$2
3396
3397
  };
3397
3398
  //#endregion
3398
3399
  //#region src/client/kimi-code/KimiModelCapabilities.tsx
@@ -3405,7 +3406,7 @@ window.__ModuleLoader__.load({
3405
3406
  * Capabilities are now short tags in their own columns, with the per-model
3406
3407
  * detail kept in a hover title.
3407
3408
  */
3408
- const t = zh$1;
3409
+ const t = zh$2;
3409
3410
  function KimiModelCapabilities({ models }) {
3410
3411
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3411
3412
  className: "dsha-cap-table",
@@ -3449,9 +3450,9 @@ window.__ModuleLoader__.load({
3449
3450
  }
3450
3451
  //#endregion
3451
3452
  //#region src/client/kimi-code/KimiCodeSection.tsx
3452
- const API$3 = "/kimi-code/api";
3453
- async function fetchApi$3(path, options) {
3454
- const res = await fetch(`${API$3}${path}`, {
3453
+ const API$5 = "/kimi-code/api";
3454
+ async function fetchApi$5(path, options) {
3455
+ const res = await fetch(`${API$5}${path}`, {
3455
3456
  ...options,
3456
3457
  headers: {
3457
3458
  "Content-Type": "application/json",
@@ -3463,19 +3464,19 @@ window.__ModuleLoader__.load({
3463
3464
  return json.value;
3464
3465
  }
3465
3466
  /** Parse "1M", "512K", "200000" into a positive integer token count. */
3466
- function parsePositiveCapacity(value) {
3467
+ function parsePositiveCapacity$1(value) {
3467
3468
  const matched = value.trim().toLowerCase().replace(/[,_\s]/g, "").match(/^(\d+(?:\.\d+)?)(k|m)?$/);
3468
3469
  if (matched === null) return null;
3469
3470
  const multiplier = matched[2] === "m" ? 1e6 : matched[2] === "k" ? 1e3 : 1;
3470
3471
  const parsed = Number(matched[1]) * multiplier;
3471
3472
  return Number.isSafeInteger(parsed) && parsed >= 1 ? parsed : null;
3472
3473
  }
3473
- function formatCapacity(value) {
3474
+ function formatCapacity$1(value) {
3474
3475
  if (value >= 1e6 && value % 1e5 === 0) return `${value / 1e6}M`;
3475
3476
  if (value >= 1e3 && value % 1e3 === 0) return `${value / 1e3}K`;
3476
3477
  return String(value);
3477
3478
  }
3478
- function formatDate(ms) {
3479
+ function formatDate$1(ms) {
3479
3480
  if (ms === void 0 || ms === null || ms <= 0) return "—";
3480
3481
  try {
3481
3482
  return new Intl.DateTimeFormat(void 0, {
@@ -3486,7 +3487,7 @@ window.__ModuleLoader__.load({
3486
3487
  return "—";
3487
3488
  }
3488
3489
  }
3489
- function formatReset(resetsAt) {
3490
+ function formatReset$1(resetsAt) {
3490
3491
  if (resetsAt === void 0 || resetsAt === null || resetsAt <= 0) return "";
3491
3492
  const diff = resetsAt - Date.now();
3492
3493
  if (diff <= 0) return "now";
@@ -3511,10 +3512,10 @@ window.__ModuleLoader__.load({
3511
3512
  }
3512
3513
  }
3513
3514
  const EFFORT_LABEL = {
3514
- low: zh$1.effortLow,
3515
- high: zh$1.effortHigh,
3516
- max: zh$1.effortMax,
3517
- none: zh$1.effortNone
3515
+ low: zh$2.effortLow,
3516
+ high: zh$2.effortHigh,
3517
+ max: zh$2.effortMax,
3518
+ none: zh$2.effortNone
3518
3519
  };
3519
3520
  function KimiCodeSection({ onModelChange, loadModelDirectory }) {
3520
3521
  const [status, setStatus] = (0, react.useState)(null);
@@ -3526,7 +3527,7 @@ window.__ModuleLoader__.load({
3526
3527
  const [savingModel, setSavingModel] = (0, react.useState)(null);
3527
3528
  const [copied, setCopied] = (0, react.useState)(false);
3528
3529
  const [connectionNotice, setConnectionNotice] = (0, react.useState)(null);
3529
- const t = zh$1;
3530
+ const t = zh$2;
3530
3531
  const notifyChange = (0, react.useCallback)(() => {
3531
3532
  onModelChange?.();
3532
3533
  loadModelDirectory?.();
@@ -3534,10 +3535,10 @@ window.__ModuleLoader__.load({
3534
3535
  const loadStatus = (0, react.useCallback)(async (quiet = false) => {
3535
3536
  if (!quiet) setError(null);
3536
3537
  try {
3537
- const data = await fetchApi$3("/status");
3538
+ const data = await fetchApi$5("/status");
3538
3539
  setStatus(data);
3539
3540
  const drafts = {};
3540
- for (const model of data.models) drafts[model.id] = formatCapacity(data.contextWindowOverrides[model.id] || model.defaultContextWindow);
3541
+ for (const model of data.models) drafts[model.id] = formatCapacity$1(data.contextWindowOverrides[model.id] || model.defaultContextWindow);
3541
3542
  setContextDrafts(drafts);
3542
3543
  } catch (err) {
3543
3544
  if (!quiet) setError(err instanceof Error ? err.message : String(err));
@@ -3562,7 +3563,7 @@ window.__ModuleLoader__.load({
3562
3563
  const timer = window.setInterval(() => {
3563
3564
  (async () => {
3564
3565
  try {
3565
- const poll = await fetchApi$3("/login/status");
3566
+ const poll = await fetchApi$5("/login/status");
3566
3567
  setFlow(poll);
3567
3568
  if (poll.status === "complete") {
3568
3569
  await loadStatus();
@@ -3583,7 +3584,7 @@ window.__ModuleLoader__.load({
3583
3584
  setBusy("login");
3584
3585
  setError(null);
3585
3586
  setCopied(false);
3586
- const next = await fetchApi$3("/login", {
3587
+ const next = await fetchApi$5("/login", {
3587
3588
  method: "POST",
3588
3589
  body: JSON.stringify({})
3589
3590
  });
@@ -3596,7 +3597,7 @@ window.__ModuleLoader__.load({
3596
3597
  };
3597
3598
  const handleCancelLogin = async () => {
3598
3599
  try {
3599
- await fetchApi$3("/login/cancel", { method: "POST" });
3600
+ await fetchApi$5("/login/cancel", { method: "POST" });
3600
3601
  setFlow({ status: "idle" });
3601
3602
  } catch (err) {
3602
3603
  setError(err instanceof Error ? err.message : String(err));
@@ -3616,7 +3617,7 @@ window.__ModuleLoader__.load({
3616
3617
  try {
3617
3618
  setBusy(`${action}-${accountId}`);
3618
3619
  setError(null);
3619
- const updated = await fetchApi$3("/accounts", {
3620
+ const updated = await fetchApi$5("/accounts", {
3620
3621
  method: "POST",
3621
3622
  body: JSON.stringify({
3622
3623
  action,
@@ -3636,7 +3637,7 @@ window.__ModuleLoader__.load({
3636
3637
  try {
3637
3638
  setBusy("strategy");
3638
3639
  setError(null);
3639
- const updated = await fetchApi$3("/accounts", {
3640
+ const updated = await fetchApi$5("/accounts", {
3640
3641
  method: "POST",
3641
3642
  body: JSON.stringify({
3642
3643
  action: "strategy",
@@ -3659,7 +3660,7 @@ window.__ModuleLoader__.load({
3659
3660
  const handleRelogin = (accountId) => {
3660
3661
  (async () => {
3661
3662
  try {
3662
- const updated = await fetchApi$3("/accounts", {
3663
+ const updated = await fetchApi$5("/accounts", {
3663
3664
  method: "POST",
3664
3665
  body: JSON.stringify({
3665
3666
  action: "relogin",
@@ -3678,7 +3679,7 @@ window.__ModuleLoader__.load({
3678
3679
  try {
3679
3680
  setBusy("quota");
3680
3681
  setError(null);
3681
- const updated = await fetchApi$3("/quota", { method: "POST" });
3682
+ const updated = await fetchApi$5("/quota", { method: "POST" });
3682
3683
  setStatus(updated);
3683
3684
  } catch (err) {
3684
3685
  setError(err instanceof Error ? err.message : String(err));
@@ -3691,10 +3692,10 @@ window.__ModuleLoader__.load({
3691
3692
  try {
3692
3693
  setBusy("catalog");
3693
3694
  setError(null);
3694
- const updated = await fetchApi$3("/catalog/refresh", { method: "POST" });
3695
+ const updated = await fetchApi$5("/catalog/refresh", { method: "POST" });
3695
3696
  setStatus(updated);
3696
3697
  const drafts = {};
3697
- for (const model of updated.models) drafts[model.id] = formatCapacity(updated.contextWindowOverrides[model.id] || model.defaultContextWindow);
3698
+ for (const model of updated.models) drafts[model.id] = formatCapacity$1(updated.contextWindowOverrides[model.id] || model.defaultContextWindow);
3698
3699
  setContextDrafts(drafts);
3699
3700
  notifyChange();
3700
3701
  } catch (err) {
@@ -3708,7 +3709,7 @@ window.__ModuleLoader__.load({
3708
3709
  setBusy("connection");
3709
3710
  setError(null);
3710
3711
  setConnectionNotice(null);
3711
- const result = await fetchApi$3("/connection/test", { method: "POST" });
3712
+ const result = await fetchApi$5("/connection/test", { method: "POST" });
3712
3713
  setConnectionNotice(result.connected ? `${t.testSuccess} · ${result.latencyMs} ms` : t.testFailed);
3713
3714
  await loadStatus(true);
3714
3715
  } catch (err) {
@@ -3719,7 +3720,7 @@ window.__ModuleLoader__.load({
3719
3720
  };
3720
3721
  const applyEnabled = async (enabledModelIds) => {
3721
3722
  try {
3722
- const updated = await fetchApi$3("/models", {
3723
+ const updated = await fetchApi$5("/models", {
3723
3724
  method: "POST",
3724
3725
  body: JSON.stringify({ enabledModelIds })
3725
3726
  });
@@ -3735,7 +3736,7 @@ window.__ModuleLoader__.load({
3735
3736
  enabled
3736
3737
  } : prev);
3737
3738
  try {
3738
- const updated = await fetchApi$3("/settings", {
3739
+ const updated = await fetchApi$5("/settings", {
3739
3740
  method: "POST",
3740
3741
  body: JSON.stringify({ enabled })
3741
3742
  });
@@ -3758,7 +3759,7 @@ window.__ModuleLoader__.load({
3758
3759
  };
3759
3760
  const handleUpdateEffort = async (effort) => {
3760
3761
  try {
3761
- const updated = await fetchApi$3("/settings", {
3762
+ const updated = await fetchApi$5("/settings", {
3762
3763
  method: "POST",
3763
3764
  body: JSON.stringify({ defaultReasoningEffort: effort })
3764
3765
  });
@@ -3770,7 +3771,7 @@ window.__ModuleLoader__.load({
3770
3771
  };
3771
3772
  const handleSaveContextWindow = async (modelId) => {
3772
3773
  const raw = contextDrafts[modelId] || "";
3773
- const parsed = parsePositiveCapacity(raw);
3774
+ const parsed = parsePositiveCapacity$1(raw);
3774
3775
  if (parsed === null) {
3775
3776
  setError(`Invalid context capacity: ${raw}`);
3776
3777
  return;
@@ -3778,14 +3779,14 @@ window.__ModuleLoader__.load({
3778
3779
  try {
3779
3780
  setSavingModel(modelId);
3780
3781
  setError(null);
3781
- const updated = await fetchApi$3("/settings", {
3782
+ const updated = await fetchApi$5("/settings", {
3782
3783
  method: "POST",
3783
3784
  body: JSON.stringify({ contextWindowOverrides: { [modelId]: parsed } })
3784
3785
  });
3785
3786
  setStatus(updated);
3786
3787
  setContextDrafts((prev) => ({
3787
3788
  ...prev,
3788
- [modelId]: formatCapacity(parsed)
3789
+ [modelId]: formatCapacity$1(parsed)
3789
3790
  }));
3790
3791
  notifyChange();
3791
3792
  } catch (err) {
@@ -3872,7 +3873,7 @@ window.__ModuleLoader__.load({
3872
3873
  children: t.codeExpires
3873
3874
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3874
3875
  className: "dsha-value",
3875
- children: formatDate(flow?.expiresAt)
3876
+ children: formatDate$1(flow?.expiresAt)
3876
3877
  })]
3877
3878
  }),
3878
3879
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -4075,7 +4076,7 @@ window.__ModuleLoader__.load({
4075
4076
  title: model.id,
4076
4077
  children: [model.name, model.defaultContextWindow < model.contextWindow && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4077
4078
  className: "dsha-model-meta",
4078
- children: formatCapacity(model.contextWindow)
4079
+ children: formatCapacity$1(model.contextWindow)
4079
4080
  })]
4080
4081
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4081
4082
  className: "dsha-capacity-control",
@@ -4160,7 +4161,7 @@ window.__ModuleLoader__.load({
4160
4161
  window.limit ?? "—",
4161
4162
  " ",
4162
4163
  t.requests
4163
- ] }), window.resetsAt !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t.resetAt.replace("{time}", formatReset(window.resetsAt)) })]
4164
+ ] }), window.resetsAt !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t.resetAt.replace("{time}", formatReset$1(window.resetsAt)) })]
4164
4165
  })
4165
4166
  ]
4166
4167
  }, window.id);
@@ -4199,71 +4200,1120 @@ window.__ModuleLoader__.load({
4199
4200
  });
4200
4201
  }
4201
4202
  //#endregion
4202
- //#region src/client/ProviderHubSection.tsx
4203
- const HUB_TABS = [
4204
- {
4205
- id: "chatgpt",
4206
- label: "ChatGPT"
4207
- },
4208
- {
4209
- id: "antigravity",
4210
- label: "Antigravity"
4211
- },
4212
- {
4213
- id: "command-code",
4214
- label: "Command Code"
4215
- },
4216
- {
4217
- id: "kimi-code",
4218
- label: "Kimi Code"
4219
- }
4203
+ //#region src/shared/workbuddy-contracts.ts
4204
+ /** Every level this route can name, in escalating order (the card's sort key). */
4205
+ const WORKBUDDY_REASONING_EFFORTS = [
4206
+ "minimal",
4207
+ "low",
4208
+ "medium",
4209
+ "high",
4210
+ "xhigh",
4211
+ "max"
4220
4212
  ];
4213
+ //#endregion
4214
+ //#region src/client/workbuddy/locales.ts
4215
+ const NS_WORKBUDDY = "dsh-workbuddy";
4216
+ const zh$1 = {
4217
+ title: "WorkBuddy",
4218
+ pageDesc: "复用本机 CodeBuddy 桌面端登录态,管理 WorkBuddy(CodeBuddy 订阅)模型、上下文窗口与额度。",
4219
+ account: "账号",
4220
+ noAccounts: "暂无账号。点击右上角「添加账号」完成浏览器授权,或登录 CodeBuddy 桌面端后重新扫描;桌面账号只能从本插件隐藏,绝不删除 CodeBuddy 的原凭据。",
4221
+ signedOut: "未找到凭据",
4222
+ nickname: "昵称",
4223
+ uin: "UIN",
4224
+ region: "区域",
4225
+ regionCn: "国区",
4226
+ regionIntl: "国际区",
4227
+ accountType: "账号类型",
4228
+ sourceFile: "凭据文件",
4229
+ authDirectory: "凭据目录",
4230
+ storage: "凭据存储",
4231
+ storageNotice: "桌面端账号仍读取 CodeBuddy 自己的凭据文件,插件不会删除这些文件;通过这里添加的账号保存在系统加密存储中(Windows DPAPI / macOS Keychain / Linux Secret Service),token 不进入浏览器。",
4232
+ routeUnreachable: "无法读取 WorkBuddy 设置路由",
4233
+ routeUnreachableHint: "浏览器里加载的插件版本可能比 Host 进程里运行的新(客户端资源是现读磁盘的,Host 只在启动时加载)。请重启 DSH,让两端来自同一份构建。",
4234
+ rescan: "重新扫描",
4235
+ rescanning: "扫描中...",
4236
+ accounts: "账号管理",
4237
+ addCnAccount: "添加国区账号",
4238
+ addIntlAccount: "添加国际区账号",
4239
+ authorizing: "授权中…",
4240
+ loginWaiting: "请在新打开的浏览器页面完成登录与授权。",
4241
+ loginFailed: "账号授权失败",
4242
+ encryptedStorage: "系统加密存储",
4243
+ hiddenAccount: "已隐藏",
4244
+ hideAccount: "从插件隐藏",
4245
+ restoreAccount: "恢复显示",
4246
+ deleteAccount: "删除账号",
4247
+ deleteConfirm: "确定删除这个插件托管的账号吗?加密存储中的登录凭据会被移除。",
4248
+ connection: "连接",
4249
+ provider: "Provider",
4250
+ providerValue: "WorkBuddy Subscription · workbuddy-subscription",
4251
+ connectionState: "连接状态",
4252
+ connected: "已连接",
4253
+ untested: "未测试",
4254
+ testConnection: "测试连接",
4255
+ testingConnection: "测试中...",
4256
+ latency: "延迟",
4257
+ routeOwned: "模型路由由本插件提供",
4258
+ routeConflict: "模型路由已被其他 Provider 占用:{detail}。请在 DSH 设置中移除重复的 Provider 配置后重试。",
4259
+ modelsSection: "模型",
4260
+ enableProvider: "启用此供应商",
4261
+ modelsHint: "勾选后模型才会出现在对话页的模型选择列表中;未勾选或关闭上方开关可在列表中隐藏该供应商。目录与上下文窗口来自订阅网关的模型目录,并按账号所在区域过滤——跨区调用会被上游拒绝。",
4262
+ refreshCatalog: "刷新模型目录",
4263
+ refreshingCatalog: "刷新中...",
4264
+ selectAll: "全选",
4265
+ unselectAll: "全不选",
4266
+ imageSupport: "图片",
4267
+ textOnly: "纯文本",
4268
+ regionBadge: "区域",
4269
+ unavailableHere: "当前区域不可用",
4270
+ enhanced: "增强功能",
4271
+ defaultReasoningEffort: "默认思考深度",
4272
+ defaultReasoningEffortHint: "控制支持推理的模型在未单独指定时的思考强度。",
4273
+ defaultEffortAuto: "使用模型默认值",
4274
+ effortUnsupported: "当前模型不支持",
4275
+ noReasoningModels: "当前账号的模型都没有声明可用的思考档位,因此没有可选的默认档位。",
4276
+ defaultEffortKept: "已保存的档位不被当前任何模型支持,已保留但不会生效。",
4277
+ contextWindowSection: "模型上下文窗口",
4278
+ contextWindowHint: "默认值来自订阅网关的模型目录(每个模型的真实上下文上限);可覆盖为自定义容量,该值用于 DSH 的压缩与溢出判断,可输入 1M、512K、200K 等容量。",
4279
+ save: "保存",
4280
+ saved: "已保存",
4281
+ saving: "保存中...",
4282
+ quotaSection: "用量与配额",
4283
+ quotaDesc: "数据来自订阅的计费接口,页面可见时最多每 60 秒刷新一次。",
4284
+ refreshQuota: "刷新用量",
4285
+ refreshingQuota: "刷新中...",
4286
+ quotaEmpty: "暂无配额数据,点击「刷新用量」重试。",
4287
+ quotaPackage: "套餐",
4288
+ quotaRemaining: "剩余额度",
4289
+ quotaCycle: "本周期已用",
4290
+ quotaReset: "重置时间",
4291
+ updatedAt: "更新时间:{time}",
4292
+ loading: "加载中...",
4293
+ credits: "credits",
4294
+ composerLabel: "额度"
4295
+ };
4296
+ const en$1 = {
4297
+ title: "WorkBuddy",
4298
+ pageDesc: "Reuse the local CodeBuddy desktop sign-in to manage WorkBuddy (CodeBuddy subscription) models, context windows, and quota.",
4299
+ account: "Account",
4300
+ noAccounts: "No accounts yet. Use “Add account” above to authorize, or sign in with the CodeBuddy desktop client and rescan; desktop accounts can only be hidden here, never removed from CodeBuddy.",
4301
+ signedOut: "No credential found",
4302
+ nickname: "Nickname",
4303
+ uin: "UIN",
4304
+ region: "Region",
4305
+ regionCn: "China",
4306
+ regionIntl: "International",
4307
+ accountType: "Account type",
4308
+ sourceFile: "Credential file",
4309
+ authDirectory: "Credential directory",
4310
+ storage: "Credential storage",
4311
+ storageNotice: "Desktop accounts continue to use CodeBuddy’s credential files, which this plugin never deletes. Accounts added here use encrypted OS storage (Windows DPAPI, macOS Keychain, or Linux Secret Service); tokens never enter the browser.",
4312
+ routeUnreachable: "The WorkBuddy settings route could not be read",
4313
+ routeUnreachableHint: "The plugin loaded in the browser may be newer than the one running in the Host: client assets are read from disk on demand, while the Host loads them only at startup. Restart DSH so both halves come from the same build.",
4314
+ rescan: "Rescan",
4315
+ rescanning: "Scanning...",
4316
+ accounts: "Account Management",
4317
+ addCnAccount: "Add China account",
4318
+ addIntlAccount: "Add international account",
4319
+ authorizing: "Authorizing…",
4320
+ loginWaiting: "Complete sign-in and authorization in the newly opened browser page.",
4321
+ loginFailed: "Account authorization failed",
4322
+ encryptedStorage: "Encrypted OS storage",
4323
+ hiddenAccount: "Hidden",
4324
+ hideAccount: "Hide from plugin",
4325
+ restoreAccount: "Restore",
4326
+ deleteAccount: "Delete account",
4327
+ deleteConfirm: "Delete this plugin-managed account? Its encrypted credential will be removed.",
4328
+ connection: "Connection",
4329
+ provider: "Provider",
4330
+ providerValue: "WorkBuddy Subscription · workbuddy-subscription",
4331
+ connectionState: "Connection state",
4332
+ connected: "Connected",
4333
+ untested: "Untested",
4334
+ testConnection: "Test connection",
4335
+ testingConnection: "Testing...",
4336
+ latency: "Latency",
4337
+ routeOwned: "This plugin serves the model route",
4338
+ routeConflict: "Another provider already owns the model route: {detail}. Remove the duplicate provider configuration in DSH settings and reload.",
4339
+ modelsSection: "Models",
4340
+ enableProvider: "Enable this provider",
4341
+ modelsHint: "Checked models appear in the DSH conversation model list. The catalog and its context windows come from the subscription gateway and are filtered by the account’s region — the upstream rejects a model asked of the wrong region.",
4342
+ refreshCatalog: "Refresh catalog",
4343
+ refreshingCatalog: "Refreshing...",
4344
+ selectAll: "Select all",
4345
+ unselectAll: "Deselect all",
4346
+ imageSupport: "Images",
4347
+ textOnly: "Text only",
4348
+ regionBadge: "Regions",
4349
+ unavailableHere: "Unavailable in this region",
4350
+ enhanced: "Enhanced features",
4351
+ defaultReasoningEffort: "Default reasoning effort",
4352
+ defaultReasoningEffortHint: "Reasoning effort for models that support it when the conversation does not specify one.",
4353
+ defaultEffortAuto: "Use the model default",
4354
+ effortUnsupported: "unsupported by the current models",
4355
+ noReasoningModels: "None of this account’s models declare a reasoning level, so there is no default to choose.",
4356
+ defaultEffortKept: "The saved level is not supported by any current model; it is kept but has no effect.",
4357
+ contextWindowSection: "Model context window",
4358
+ contextWindowHint: "Defaults come from the subscription gateway’s own catalog (each model’s real context cap); override one with the capacity DSH should use for compaction and overflow decisions (e.g., 1M, 512K, 200K).",
4359
+ save: "Save",
4360
+ saved: "Saved",
4361
+ saving: "Saving...",
4362
+ quotaSection: "Usage & quota",
4363
+ quotaDesc: "Read from the subscription billing endpoint; refreshes at most once every 60 seconds while visible.",
4364
+ refreshQuota: "Refresh quota",
4365
+ refreshingQuota: "Refreshing...",
4366
+ quotaEmpty: "No quota data yet — use “Refresh quota” to retry.",
4367
+ quotaPackage: "Package",
4368
+ quotaRemaining: "Remaining credits",
4369
+ quotaCycle: "Used this cycle",
4370
+ quotaReset: "Resets",
4371
+ updatedAt: "Updated at: {time}",
4372
+ loading: "Loading...",
4373
+ credits: "credits",
4374
+ composerLabel: "Quota"
4375
+ };
4376
+ const dictionaries$1 = {
4377
+ zh: zh$1,
4378
+ "zh-CN": zh$1,
4379
+ en: en$1,
4380
+ "en-US": en$1
4381
+ };
4382
+ //#endregion
4383
+ //#region src/client/workbuddy/WorkBuddySection.tsx
4221
4384
  /**
4222
- * Single settings page that hosts every subscription provider behind tabs,
4223
- * so the settings sidebar shows one entry instead of one per provider. Only
4224
- * the active tab is mounted, mirroring the shell's previous behavior of
4225
- * mounting just the selected settings page (each provider section refetches
4226
- * its status on mount).
4385
+ * Display label per reasoning level. The locale dictionary only accepts flat
4386
+ * string values, so this nested table lives with the control that renders it.
4227
4387
  */
4228
- function ProviderHubSection({ t, onModelChange, ...runtime }) {
4229
- const [active, setActive] = (0, react.useState)("chatgpt");
4230
- const onTabKeyDown = (0, react.useCallback)((event) => {
4231
- if (event.key !== "ArrowLeft" && event.key !== "ArrowRight") return;
4232
- event.preventDefault();
4233
- const index = HUB_TABS.findIndex((tab) => tab.id === active);
4234
- const delta = event.key === "ArrowRight" ? 1 : -1;
4235
- const next = HUB_TABS[(index + delta + HUB_TABS.length) % HUB_TABS.length];
4236
- setActive(next.id);
4237
- document.getElementById(`dsh-hub-tab-${next.id}`)?.focus();
4238
- }, [active]);
4239
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
4240
- className: "dsh-codex-page",
4241
- "aria-labelledby": "dsh-hub-title",
4242
- children: [
4243
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("header", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
4244
- id: "dsh-hub-title",
4245
- className: "dsh-codex-title",
4246
- children: t("hubTitle")
4247
- }) }),
4248
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4249
- className: "dsh-codex-segments dsh-hub-tabs",
4250
- role: "tablist",
4251
- "aria-label": t("hubTitle"),
4252
- children: HUB_TABS.map((tab) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4253
- type: "button",
4254
- role: "tab",
4255
- id: `dsh-hub-tab-${tab.id}`,
4256
- "aria-selected": active === tab.id,
4257
- "aria-controls": `dsh-hub-panel-${tab.id}`,
4258
- className: active === tab.id ? "active" : void 0,
4259
- tabIndex: active === tab.id ? 0 : -1,
4260
- onClick: () => setActive(tab.id),
4261
- onKeyDown: onTabKeyDown,
4262
- children: tab.label
4263
- }, tab.id))
4264
- }),
4265
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4266
- role: "tabpanel",
4388
+ const EFFORT_LABELS = {
4389
+ minimal: "Minimal",
4390
+ low: "Low",
4391
+ medium: "Medium",
4392
+ high: "High",
4393
+ xhigh: "X-High",
4394
+ max: "Max"
4395
+ };
4396
+ const API$4 = "/workbuddy/api";
4397
+ /**
4398
+ * The shared card's label set.
4399
+ *
4400
+ * Only the three labels WorkBuddy genuinely words differently are taken from
4401
+ * this tab's dictionary; everything else is the shared set, so the five
4402
+ * provider tabs cannot drift apart in wording.
4403
+ */
4404
+ function accountPoolLabels(t) {
4405
+ return {
4406
+ ...accountPoolZh,
4407
+ noAccounts: t.noAccounts,
4408
+ storageNotice: t.storageNotice,
4409
+ email: t.nickname
4410
+ };
4411
+ }
4412
+ /**
4413
+ * Call one settings route and unwrap its `{ ok, value }` envelope.
4414
+ *
4415
+ * The response body is read as text first, because a route that is not mounted
4416
+ * answers with an empty body — and `res.json()` on that throws a bare
4417
+ * "Unexpected end of JSON input" that says nothing about what actually failed.
4418
+ * That is exactly what happens when the client bundle is newer than the Host
4419
+ * process (a stale Host has no route to answer), so the error is reported as
4420
+ * such instead of as a credential problem.
4421
+ */
4422
+ async function fetchApi$4(path, options) {
4423
+ const res = await fetch(`${API$4}${path}`, {
4424
+ ...options,
4425
+ headers: {
4426
+ "Content-Type": "application/json",
4427
+ ...options?.headers
4428
+ }
4429
+ });
4430
+ const text = await res.text();
4431
+ if (text.trim() === "") throw new Error(`The WorkBuddy settings route did not answer (HTTP ${res.status} ${res.statusText || "no body"}). The plugin loaded in the browser may be newer than the one running in the Host — restart DSH so both halves come from the same build.`);
4432
+ let json;
4433
+ try {
4434
+ json = JSON.parse(text);
4435
+ } catch {
4436
+ throw new Error(`The WorkBuddy settings route returned a non-JSON response (HTTP ${res.status}): ${text.slice(0, 200)}`);
4437
+ }
4438
+ if (!res.ok || json.ok !== true) throw new Error(json.error || `HTTP ${res.status}`);
4439
+ return json.value;
4440
+ }
4441
+ /** Parse "1M", "512K", "200000" into a positive integer token count. */
4442
+ function parsePositiveCapacity(value) {
4443
+ const matched = value.trim().toLowerCase().replace(/[,_\s]/g, "").match(/^(\d+(?:\.\d+)?)(k|m)?$/);
4444
+ if (matched === null) return null;
4445
+ const multiplier = matched[2] === "m" ? 1e6 : matched[2] === "k" ? 1e3 : 1;
4446
+ const parsed = Number(matched[1]) * multiplier;
4447
+ return Number.isSafeInteger(parsed) && parsed >= 1 ? parsed : null;
4448
+ }
4449
+ function formatCapacity(value) {
4450
+ if (value >= 1e6 && value % 1e5 === 0) return `${value / 1e6}M`;
4451
+ if (value >= 1e3 && value % 1e3 === 0) return `${value / 1e3}K`;
4452
+ return String(value);
4453
+ }
4454
+ function formatDate(ms) {
4455
+ if (ms === void 0 || ms === null || ms <= 0) return "—";
4456
+ try {
4457
+ return new Intl.DateTimeFormat(void 0, {
4458
+ dateStyle: "medium",
4459
+ timeStyle: "short"
4460
+ }).format(ms);
4461
+ } catch {
4462
+ return "—";
4463
+ }
4464
+ }
4465
+ function formatReset(resetsAt) {
4466
+ if (resetsAt === void 0 || resetsAt === null || resetsAt <= 0) return "";
4467
+ const diff = resetsAt - Date.now();
4468
+ if (diff <= 0) return "now";
4469
+ const mins = Math.floor(diff / 6e4);
4470
+ const hours = Math.floor(mins / 60);
4471
+ const days = Math.floor(hours / 24);
4472
+ if (days > 0) return `${days}d ${hours % 24}h`;
4473
+ if (hours > 0) return `${hours}h ${mins % 60}m`;
4474
+ return `${mins}m`;
4475
+ }
4476
+ /** Mask a UIN so the card shows identity without publishing the full number. */
4477
+ function maskUin(uin) {
4478
+ if (uin === void 0 || uin === null || uin === "") return "—";
4479
+ if (uin.length <= 6) return uin;
4480
+ return `${uin.slice(0, 4)}****${uin.slice(-2)}`;
4481
+ }
4482
+ /** Shorten an absolute credential path to its file name for display. */
4483
+ function displayFile(file) {
4484
+ if (file === void 0 || file === null || file === "") return "—";
4485
+ const parts = file.split(/[\\/]/);
4486
+ return parts[parts.length - 1] || file;
4487
+ }
4488
+ /**
4489
+ * Reasoning levels the default-effort control may offer.
4490
+ *
4491
+ * The levels come from what this account's models actually declare — never from
4492
+ * the shipped enum alone. The setting is one global default applied to whichever
4493
+ * model a conversation uses, so the list is their union; each entry carries the
4494
+ * models behind it, and a level only some models accept is labelled as such.
4495
+ * Order follows {@link WORKBUDDY_REASONING_EFFORTS}, which is the escalating
4496
+ * order the upstream ladder uses.
4497
+ */
4498
+ function reasoningEffortChoices(models) {
4499
+ const declared = /* @__PURE__ */ new Map();
4500
+ for (const model of models) for (const effort of model.reasoningEfforts ?? []) {
4501
+ const names = declared.get(effort);
4502
+ if (names === void 0) declared.set(effort, [model.name]);
4503
+ else names.push(model.name);
4504
+ }
4505
+ return WORKBUDDY_REASONING_EFFORTS.filter((effort) => declared.has(effort)).map((effort) => ({
4506
+ value: effort,
4507
+ models: declared.get(effort)
4508
+ }));
4509
+ }
4510
+ /**
4511
+ * Whether a saved default is unusable for every model on this account.
4512
+ *
4513
+ * The adapter drops a level the chosen model does not accept instead of sending
4514
+ * it, so a value that no model declares silently does nothing. Reporting it is
4515
+ * what keeps that from looking like the setting was applied.
4516
+ */
4517
+ function unsupportedReasoningEffort(configured, models) {
4518
+ if (configured === null || configured === void 0) return null;
4519
+ return models.some((model) => (model.reasoningEfforts ?? []).includes(configured)) ? null : configured;
4520
+ }
4521
+ /** Defend against a response that omits the arrays the card renders. */
4522
+ function normalizeStatus(data) {
4523
+ return {
4524
+ ...data,
4525
+ models: Array.isArray(data?.models) ? data.models : [],
4526
+ contextWindowOverrides: data?.contextWindowOverrides ?? {},
4527
+ accounts: Array.isArray(data?.accounts) ? data.accounts : []
4528
+ };
4529
+ }
4530
+ function WorkBuddySection({ onModelChange, loadModelDirectory }) {
4531
+ const [status, setStatus] = (0, react.useState)(null);
4532
+ const [loading, setLoading] = (0, react.useState)(true);
4533
+ const [busy, setBusy] = (0, react.useState)(null);
4534
+ const [error, setError] = (0, react.useState)(null);
4535
+ const [statusFailed, setStatusFailed] = (0, react.useState)(false);
4536
+ const [connection, setConnection] = (0, react.useState)(null);
4537
+ const [loginProgress, setLoginProgress] = (0, react.useState)(null);
4538
+ const loginIntervalRef = (0, react.useRef)(null);
4539
+ const loginTimeoutRef = (0, react.useRef)(null);
4540
+ const [contextDrafts, setContextDrafts] = (0, react.useState)({});
4541
+ const [savingModel, setSavingModel] = (0, react.useState)(null);
4542
+ const t = zh$1;
4543
+ const notifyChange = (0, react.useCallback)(() => {
4544
+ onModelChange?.();
4545
+ loadModelDirectory?.();
4546
+ }, [onModelChange, loadModelDirectory]);
4547
+ const effortChoices = (0, react.useMemo)(() => reasoningEffortChoices(status?.models ?? []), [status?.models]);
4548
+ const allModels = status?.models ?? [];
4549
+ const poolAccounts = (0, react.useMemo)(() => Array.isArray(status?.accounts) ? status.accounts : [], [status?.accounts]);
4550
+ const strayEffort = unsupportedReasoningEffort(status?.defaultReasoningEffort, allModels);
4551
+ const loadStatus = (0, react.useCallback)(async (quiet = false) => {
4552
+ if (!quiet) setError(null);
4553
+ try {
4554
+ const normalized = normalizeStatus(await fetchApi$4("/status"));
4555
+ setStatus(normalized);
4556
+ setStatusFailed(false);
4557
+ const drafts = {};
4558
+ for (const model of normalized.models) drafts[model.id] = formatCapacity(normalized.contextWindowOverrides[model.id] || model.defaultContextWindow);
4559
+ setContextDrafts(drafts);
4560
+ } catch (err) {
4561
+ setStatusFailed(true);
4562
+ if (!quiet) setError(err instanceof Error ? err.message : String(err));
4563
+ } finally {
4564
+ setLoading(false);
4565
+ }
4566
+ }, []);
4567
+ (0, react.useEffect)(() => {
4568
+ loadStatus();
4569
+ const refreshWhenVisible = () => {
4570
+ if (document.visibilityState === "visible") loadStatus(true);
4571
+ };
4572
+ document.addEventListener("visibilitychange", refreshWhenVisible);
4573
+ return () => {
4574
+ document.removeEventListener("visibilitychange", refreshWhenVisible);
4575
+ if (loginIntervalRef.current !== null) window.clearInterval(loginIntervalRef.current);
4576
+ if (loginTimeoutRef.current !== null) window.clearTimeout(loginTimeoutRef.current);
4577
+ };
4578
+ }, [loadStatus]);
4579
+ const handleRescan = async () => {
4580
+ try {
4581
+ setBusy("rescan");
4582
+ setError(null);
4583
+ const updated = await fetchApi$4("/rescan", { method: "POST" });
4584
+ setStatus(normalizeStatus(updated));
4585
+ notifyChange();
4586
+ } catch (err) {
4587
+ setError(err instanceof Error ? err.message : String(err));
4588
+ } finally {
4589
+ setBusy(null);
4590
+ }
4591
+ };
4592
+ const handleAddAccount = async (region) => {
4593
+ try {
4594
+ setBusy(`login:${region}`);
4595
+ setError(null);
4596
+ setLoginProgress(t.loginWaiting);
4597
+ const flow = await fetchApi$4("/accounts/login", {
4598
+ method: "POST",
4599
+ body: JSON.stringify({ region })
4600
+ });
4601
+ if (flow.authUrl) window.open(flow.authUrl, "_blank", "noopener,noreferrer");
4602
+ if (loginIntervalRef.current !== null) window.clearInterval(loginIntervalRef.current);
4603
+ if (loginTimeoutRef.current !== null) window.clearTimeout(loginTimeoutRef.current);
4604
+ const timer = window.setInterval(() => {
4605
+ (async () => {
4606
+ try {
4607
+ const poll = await fetchApi$4("/accounts/login/status");
4608
+ if (poll.progress) setLoginProgress(poll.progress);
4609
+ if (poll.status === "complete") {
4610
+ window.clearInterval(timer);
4611
+ loginIntervalRef.current = null;
4612
+ if (loginTimeoutRef.current !== null) window.clearTimeout(loginTimeoutRef.current);
4613
+ loginTimeoutRef.current = null;
4614
+ setBusy(null);
4615
+ setLoginProgress(null);
4616
+ if (poll.accountId) await fetchApi$4("/settings", {
4617
+ method: "POST",
4618
+ body: JSON.stringify({ selectedAccountId: poll.accountId })
4619
+ });
4620
+ await loadStatus();
4621
+ notifyChange();
4622
+ } else if (poll.status === "error") {
4623
+ window.clearInterval(timer);
4624
+ loginIntervalRef.current = null;
4625
+ if (loginTimeoutRef.current !== null) window.clearTimeout(loginTimeoutRef.current);
4626
+ loginTimeoutRef.current = null;
4627
+ setBusy(null);
4628
+ setLoginProgress(null);
4629
+ setError(poll.error || t.loginFailed);
4630
+ }
4631
+ } catch {}
4632
+ })();
4633
+ }, 1500);
4634
+ loginIntervalRef.current = timer;
4635
+ loginTimeoutRef.current = window.setTimeout(() => {
4636
+ window.clearInterval(timer);
4637
+ loginIntervalRef.current = null;
4638
+ loginTimeoutRef.current = null;
4639
+ setBusy(null);
4640
+ setLoginProgress(null);
4641
+ }, 300 * 1e3);
4642
+ } catch (err) {
4643
+ setBusy(null);
4644
+ setLoginProgress(null);
4645
+ setError(err instanceof Error ? err.message : String(err));
4646
+ }
4647
+ };
4648
+ /**
4649
+ * One account action, whether it is a pool action (`set-primary`, `hide`) or a
4650
+ * credential action (`delete`). The host route dispatches on `action`.
4651
+ */
4652
+ const handlePoolAction = async (action, accountId) => {
4653
+ try {
4654
+ setBusy(`${action}:${accountId}`);
4655
+ setError(null);
4656
+ const updated = await fetchApi$4("/accounts/action", {
4657
+ method: "POST",
4658
+ body: JSON.stringify({
4659
+ action,
4660
+ accountId
4661
+ })
4662
+ });
4663
+ setStatus(normalizeStatus(updated));
4664
+ notifyChange();
4665
+ } catch (err) {
4666
+ setError(err instanceof Error ? err.message : String(err));
4667
+ } finally {
4668
+ setBusy(null);
4669
+ }
4670
+ };
4671
+ const handleDeleteAccount = async (accountId) => {
4672
+ if (!window.confirm(t.deleteConfirm)) return;
4673
+ await handlePoolAction("delete", accountId);
4674
+ };
4675
+ const handleSetStrategy = async (strategy) => {
4676
+ try {
4677
+ setBusy("strategy");
4678
+ setError(null);
4679
+ const updated = await fetchApi$4("/accounts/strategy", {
4680
+ method: "POST",
4681
+ body: JSON.stringify({ strategy })
4682
+ });
4683
+ setStatus(normalizeStatus(updated));
4684
+ } catch (err) {
4685
+ setError(err instanceof Error ? err.message : String(err));
4686
+ } finally {
4687
+ setBusy(null);
4688
+ }
4689
+ };
4690
+ const handleTestConnection = async () => {
4691
+ try {
4692
+ setBusy("connection");
4693
+ setError(null);
4694
+ setConnection(await fetchApi$4("/connection/test", { method: "POST" }));
4695
+ } catch (err) {
4696
+ setError(err instanceof Error ? err.message : String(err));
4697
+ } finally {
4698
+ setBusy(null);
4699
+ }
4700
+ };
4701
+ const handleRefreshQuota = async () => {
4702
+ try {
4703
+ setBusy("quota");
4704
+ setError(null);
4705
+ const updated = await fetchApi$4("/quota", { method: "POST" });
4706
+ setStatus(updated);
4707
+ } catch (err) {
4708
+ setError(err instanceof Error ? err.message : String(err));
4709
+ } finally {
4710
+ setBusy(null);
4711
+ }
4712
+ };
4713
+ const toggleEnabled = async (enabled) => {
4714
+ try {
4715
+ setBusy("enabled");
4716
+ setError(null);
4717
+ const updated = await fetchApi$4("/settings", {
4718
+ method: "POST",
4719
+ body: JSON.stringify({ enabled })
4720
+ });
4721
+ setStatus(updated);
4722
+ notifyChange();
4723
+ } catch (err) {
4724
+ setError(err instanceof Error ? err.message : String(err));
4725
+ } finally {
4726
+ setBusy(null);
4727
+ }
4728
+ };
4729
+ const toggleModel = async (modelId, enabled) => {
4730
+ if (status === null) return;
4731
+ const current = status.models.filter((model) => model.enabled).map((model) => model.id);
4732
+ const next = enabled ? [.../* @__PURE__ */ new Set([...current, modelId])] : current.filter((id) => id !== modelId);
4733
+ try {
4734
+ setBusy(`model:${modelId}`);
4735
+ setError(null);
4736
+ const updated = await fetchApi$4("/settings", {
4737
+ method: "POST",
4738
+ body: JSON.stringify({ enabledModelIds: next })
4739
+ });
4740
+ setStatus(updated);
4741
+ notifyChange();
4742
+ } catch (err) {
4743
+ setError(err instanceof Error ? err.message : String(err));
4744
+ } finally {
4745
+ setBusy(null);
4746
+ }
4747
+ };
4748
+ const setAllModels = async (enabled) => {
4749
+ if (status === null) return;
4750
+ try {
4751
+ setBusy("models-all");
4752
+ setError(null);
4753
+ const updated = await fetchApi$4("/settings", {
4754
+ method: "POST",
4755
+ body: JSON.stringify({ enabledModelIds: enabled ? status.models.map((model) => model.id) : [] })
4756
+ });
4757
+ setStatus(updated);
4758
+ notifyChange();
4759
+ } catch (err) {
4760
+ setError(err instanceof Error ? err.message : String(err));
4761
+ } finally {
4762
+ setBusy(null);
4763
+ }
4764
+ };
4765
+ const handleUpdateEffort = async (effort) => {
4766
+ try {
4767
+ setBusy("effort");
4768
+ setError(null);
4769
+ const updated = await fetchApi$4("/settings", {
4770
+ method: "POST",
4771
+ body: JSON.stringify({ defaultReasoningEffort: effort })
4772
+ });
4773
+ setStatus(updated);
4774
+ notifyChange();
4775
+ } catch (err) {
4776
+ setError(err instanceof Error ? err.message : String(err));
4777
+ } finally {
4778
+ setBusy(null);
4779
+ }
4780
+ };
4781
+ const handleRefreshCatalog = async () => {
4782
+ try {
4783
+ setBusy("catalog");
4784
+ setError(null);
4785
+ const updated = await fetchApi$4("/catalog/refresh", { method: "POST" });
4786
+ const normalized = {
4787
+ ...updated,
4788
+ models: Array.isArray(updated?.models) ? updated.models : [],
4789
+ contextWindowOverrides: updated?.contextWindowOverrides ?? {}
4790
+ };
4791
+ setStatus(normalized);
4792
+ const drafts = {};
4793
+ for (const model of normalized.models) drafts[model.id] = formatCapacity(normalized.contextWindowOverrides[model.id] || model.defaultContextWindow);
4794
+ setContextDrafts(drafts);
4795
+ notifyChange();
4796
+ } catch (err) {
4797
+ setError(err instanceof Error ? err.message : String(err));
4798
+ } finally {
4799
+ setBusy(null);
4800
+ }
4801
+ };
4802
+ const handleSaveContextWindow = async (modelId) => {
4803
+ const raw = contextDrafts[modelId] || "";
4804
+ const parsed = parsePositiveCapacity(raw);
4805
+ if (parsed === null) {
4806
+ setError(`Invalid context capacity: ${raw}`);
4807
+ return;
4808
+ }
4809
+ try {
4810
+ setSavingModel(modelId);
4811
+ setError(null);
4812
+ const updated = await fetchApi$4("/settings", {
4813
+ method: "POST",
4814
+ body: JSON.stringify({ contextWindowOverrides: { [modelId]: parsed } })
4815
+ });
4816
+ setStatus(updated);
4817
+ setContextDrafts((prev) => ({
4818
+ ...prev,
4819
+ [modelId]: formatCapacity(parsed)
4820
+ }));
4821
+ notifyChange();
4822
+ } catch (err) {
4823
+ setError(err instanceof Error ? err.message : String(err));
4824
+ } finally {
4825
+ setSavingModel(null);
4826
+ }
4827
+ };
4828
+ if (loading) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4829
+ className: "dsha-page",
4830
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4831
+ className: "dsha-empty",
4832
+ children: t.loading
4833
+ })
4834
+ });
4835
+ const quota = status?.quota;
4836
+ const authenticated = status?.authenticated === true;
4837
+ const unreachable = statusFailed && status === null;
4838
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4839
+ className: "dsha-page",
4840
+ children: [
4841
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(AccountPoolSection, {
4842
+ accounts: poolAccounts,
4843
+ activeAccountId: status?.activeAccountId,
4844
+ rotationStrategy: status?.rotationStrategy ?? "sequential",
4845
+ labels: accountPoolLabels(t),
4846
+ busy,
4847
+ onLogin: () => void handleAddAccount("cn"),
4848
+ onSetPrimary: (id) => void handlePoolAction("set-primary", id),
4849
+ onDelete: (id) => void handleDeleteAccount(id),
4850
+ onClearCooldown: (id) => void handlePoolAction("clear-cooldown", id),
4851
+ onRelogin: (id) => void handlePoolAction("relogin", id),
4852
+ onSetStrategy: (strategy) => void handleSetStrategy(strategy),
4853
+ storageValue: status?.managedStoragePath,
4854
+ renderLoginActions: () => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4855
+ className: "dsha-account-add-actions",
4856
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4857
+ className: "dsha-btn dsha-btn-primary",
4858
+ disabled: busy !== null,
4859
+ onClick: () => void handleAddAccount("cn"),
4860
+ children: busy === "login:cn" ? t.authorizing : t.addCnAccount
4861
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4862
+ className: "dsha-btn dsha-btn-primary",
4863
+ disabled: busy !== null,
4864
+ onClick: () => void handleAddAccount("intl"),
4865
+ children: busy === "login:intl" ? t.authorizing : t.addIntlAccount
4866
+ })]
4867
+ }),
4868
+ renderAccountActions: (candidate) => candidate.removable ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [candidate.hidden === true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4869
+ className: "dsha-btn",
4870
+ disabled: busy !== null,
4871
+ onClick: () => void handlePoolAction("restore", candidate.id),
4872
+ children: t.restoreAccount
4873
+ }), candidate.hidden !== true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4874
+ className: "dsha-btn",
4875
+ disabled: busy !== null,
4876
+ onClick: () => void handlePoolAction("hide", candidate.id),
4877
+ children: t.hideAccount
4878
+ })] }),
4879
+ renderDetails: (candidate) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
4880
+ candidate.uin ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
4881
+ t.uin,
4882
+ ": ",
4883
+ maskUin(candidate.uin)
4884
+ ] }) : null,
4885
+ candidate.accountType ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
4886
+ t.accountType,
4887
+ ": ",
4888
+ candidate.accountType
4889
+ ] }) : null,
4890
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
4891
+ t.region,
4892
+ ": ",
4893
+ candidate.region === "intl" ? t.regionIntl : t.regionCn
4894
+ ] }),
4895
+ candidate.domain ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4896
+ className: "dshwb-mono",
4897
+ title: `${candidate.domain} · ${candidate.backend ?? ""}`,
4898
+ children: candidate.domain
4899
+ }) : null,
4900
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4901
+ className: "dshwb-mono",
4902
+ children: candidate.source === "managed" ? t.encryptedStorage : displayFile(candidate.sourceFile)
4903
+ }),
4904
+ candidate.hidden === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t.hiddenAccount }) : null
4905
+ ] }),
4906
+ children: [
4907
+ unreachable ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4908
+ className: "dsha-empty",
4909
+ children: t.routeUnreachable
4910
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4911
+ className: "dsha-notice",
4912
+ children: t.routeUnreachableHint
4913
+ })] }) : null,
4914
+ loginProgress ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4915
+ className: "dsha-notice",
4916
+ children: loginProgress
4917
+ }) : null,
4918
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4919
+ className: "dsha-row",
4920
+ style: { marginTop: 12 },
4921
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4922
+ className: "dsha-label",
4923
+ children: t.authDirectory
4924
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4925
+ className: "dsha-value dshwb-mono",
4926
+ title: status?.authDirectory || "",
4927
+ children: status?.authDirectory || "—"
4928
+ })]
4929
+ }),
4930
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4931
+ className: "dsha-actions",
4932
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4933
+ className: "dsha-btn",
4934
+ disabled: busy !== null,
4935
+ onClick: () => void handleRescan(),
4936
+ children: busy === "rescan" ? t.rescanning : t.rescan
4937
+ })
4938
+ })
4939
+ ]
4940
+ }),
4941
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
4942
+ className: "dsha-group",
4943
+ children: [
4944
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4945
+ className: "dsha-grouphead",
4946
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t.connection })
4947
+ }),
4948
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4949
+ className: "dsha-row",
4950
+ style: { marginBottom: 12 },
4951
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4952
+ className: "dsha-label",
4953
+ style: { fontWeight: 600 },
4954
+ children: t.enableProvider
4955
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
4956
+ type: "checkbox",
4957
+ checked: status?.enabled !== false,
4958
+ disabled: busy !== null,
4959
+ onChange: (e) => void toggleEnabled(e.currentTarget.checked)
4960
+ })]
4961
+ }),
4962
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4963
+ className: "dsha-row",
4964
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4965
+ className: "dsha-label",
4966
+ children: t.provider
4967
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4968
+ className: "dsha-value",
4969
+ children: t.providerValue
4970
+ })]
4971
+ }),
4972
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4973
+ className: "dsha-row",
4974
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4975
+ className: "dsha-label",
4976
+ children: t.connectionState
4977
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4978
+ className: "dsha-value",
4979
+ children: [connection?.connected ? t.connected : authenticated ? t.untested : t.signedOut, connection?.connected ? ` · ${connection.latencyMs}ms · ${connection.model}` : ""]
4980
+ })]
4981
+ }),
4982
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4983
+ className: "dsha-notice",
4984
+ children: status?.serving === false && status.conflict ? t.routeConflict.replace("{detail}", status.conflict) : t.routeOwned
4985
+ }),
4986
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4987
+ className: "dsha-actions",
4988
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4989
+ className: "dsha-btn",
4990
+ disabled: busy !== null || !authenticated,
4991
+ onClick: () => void handleTestConnection(),
4992
+ children: busy === "connection" ? t.testingConnection : t.testConnection
4993
+ })
4994
+ })
4995
+ ]
4996
+ }),
4997
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
4998
+ className: "dsha-group",
4999
+ children: [
5000
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5001
+ className: "dsha-grouphead",
5002
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t.modelsSection }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5003
+ className: "dsha-btn",
5004
+ disabled: busy !== null || !authenticated,
5005
+ onClick: () => void handleRefreshCatalog(),
5006
+ children: busy === "catalog" ? t.refreshingCatalog : t.refreshCatalog
5007
+ })]
5008
+ }),
5009
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5010
+ className: "dsha-muted dsha-models-hint",
5011
+ children: t.modelsHint
5012
+ }),
5013
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5014
+ className: "dsha-models",
5015
+ "aria-label": "WorkBuddy Models",
5016
+ children: status?.models.map((model) => {
5017
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
5018
+ title: [
5019
+ model.id,
5020
+ formatCapacity(model.contextWindow),
5021
+ model.supportsImage ? t.imageSupport : t.textOnly,
5022
+ ...model.reasoningEfforts && model.reasoningEfforts.length > 0 ? [model.reasoningEfforts.join("/")] : []
5023
+ ].join(" · "),
5024
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
5025
+ type: "checkbox",
5026
+ checked: model.enabled,
5027
+ disabled: busy !== null,
5028
+ onChange: (event) => void toggleModel(model.id, event.currentTarget.checked)
5029
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: model.name })]
5030
+ }, model.id);
5031
+ })
5032
+ }),
5033
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5034
+ className: "dsha-actions",
5035
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5036
+ className: "dsha-btn",
5037
+ disabled: busy !== null,
5038
+ onClick: () => void setAllModels(true),
5039
+ children: t.selectAll
5040
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5041
+ className: "dsha-btn",
5042
+ disabled: busy !== null,
5043
+ onClick: () => void setAllModels(false),
5044
+ children: t.unselectAll
5045
+ })]
5046
+ })
5047
+ ]
5048
+ }),
5049
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
5050
+ className: "dsha-group",
5051
+ children: [
5052
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5053
+ className: "dsha-grouphead",
5054
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t.enhanced })
5055
+ }),
5056
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5057
+ className: "dsha-pref-row",
5058
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
5059
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t.defaultReasoningEffort }),
5060
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5061
+ className: "dsha-muted",
5062
+ children: t.defaultReasoningEffortHint
5063
+ }),
5064
+ strayEffort !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5065
+ className: "dsha-muted",
5066
+ children: t.defaultEffortKept
5067
+ }) : null
5068
+ ] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
5069
+ className: "dsha-select",
5070
+ "aria-label": t.defaultReasoningEffort,
5071
+ value: status?.defaultReasoningEffort ?? "",
5072
+ disabled: busy !== null,
5073
+ onChange: (event) => {
5074
+ const value = event.currentTarget.value;
5075
+ handleUpdateEffort(value === "" ? null : value);
5076
+ },
5077
+ children: [
5078
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
5079
+ value: "",
5080
+ children: t.defaultEffortAuto
5081
+ }),
5082
+ strayEffort !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
5083
+ value: strayEffort,
5084
+ children: `${EFFORT_LABELS[strayEffort]} (${t.effortUnsupported})`
5085
+ }) : null,
5086
+ effortChoices.map((choice) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
5087
+ value: choice.value,
5088
+ children: choice.models.length === allModels.length ? EFFORT_LABELS[choice.value] : `${EFFORT_LABELS[choice.value]} (${choice.models.length}/${allModels.length})`
5089
+ }, choice.value))
5090
+ ]
5091
+ })]
5092
+ }),
5093
+ effortChoices.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5094
+ className: "dsha-muted",
5095
+ children: t.noReasoningModels
5096
+ }) : null
5097
+ ]
5098
+ }),
5099
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
5100
+ className: "dsha-group",
5101
+ children: [
5102
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5103
+ className: "dsha-grouphead",
5104
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t.contextWindowSection })
5105
+ }),
5106
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5107
+ className: "dsha-muted",
5108
+ children: t.contextWindowHint
5109
+ }),
5110
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5111
+ className: "dsha-context-settings",
5112
+ children: status?.models.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5113
+ className: "dsha-context-row",
5114
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5115
+ title: model.id,
5116
+ children: model.name
5117
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5118
+ className: "dsha-capacity-control",
5119
+ children: [
5120
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
5121
+ type: "text",
5122
+ "aria-label": `${model.name} context window`,
5123
+ value: contextDrafts[model.id] ?? "",
5124
+ onChange: (event) => setContextDrafts({
5125
+ ...contextDrafts,
5126
+ [model.id]: event.currentTarget.value
5127
+ }),
5128
+ onKeyDown: (event) => {
5129
+ if (event.key === "Enter") handleSaveContextWindow(model.id);
5130
+ }
5131
+ }),
5132
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: "tokens" }),
5133
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5134
+ type: "button",
5135
+ className: "dsha-context-save",
5136
+ disabled: savingModel === model.id,
5137
+ onClick: () => void handleSaveContextWindow(model.id),
5138
+ children: savingModel === model.id ? t.saving : t.save
5139
+ })
5140
+ ]
5141
+ })]
5142
+ }, model.id))
5143
+ })
5144
+ ]
5145
+ }),
5146
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
5147
+ className: "dsha-group",
5148
+ children: [
5149
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5150
+ className: "dsha-grouphead",
5151
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t.quotaSection }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5152
+ className: "dsha-btn",
5153
+ disabled: busy !== null || !authenticated,
5154
+ onClick: () => void handleRefreshQuota(),
5155
+ children: busy === "quota" ? t.refreshingQuota : t.refreshQuota
5156
+ })]
5157
+ }),
5158
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5159
+ className: "dsha-muted",
5160
+ children: t.quotaDesc
5161
+ }),
5162
+ !authenticated ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5163
+ className: "dsha-empty",
5164
+ children: t.signedOut
5165
+ }) : quota === null || quota === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5166
+ className: "dsha-empty",
5167
+ children: busy === "quota" ? t.refreshingQuota : t.quotaEmpty
5168
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5169
+ className: "dsha-quota-card",
5170
+ children: [
5171
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5172
+ className: "dsha-quota-title",
5173
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: quota.packageName || t.quotaPackage }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: quota.remainingCredits !== null ? `${quota.remainingCredits} ${t.credits}` : "—" })]
5174
+ }),
5175
+ quota.cycleUsedCredits !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5176
+ className: "dsha-row",
5177
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5178
+ className: "dsha-label",
5179
+ children: t.quotaCycle
5180
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
5181
+ className: "dsha-value",
5182
+ children: [
5183
+ quota.cycleUsedCredits,
5184
+ quota.cycleCredits !== null ? ` / ${quota.cycleCredits}` : "",
5185
+ ` ${t.credits}`
5186
+ ]
5187
+ })]
5188
+ }),
5189
+ quota.cycleEndsAt !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5190
+ className: "dsha-row",
5191
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5192
+ className: "dsha-label",
5193
+ children: t.quotaReset
5194
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
5195
+ className: "dsha-value",
5196
+ children: [formatDate(quota.cycleEndsAt), formatReset(quota.cycleEndsAt) ? ` · ${formatReset(quota.cycleEndsAt)}` : ""]
5197
+ })]
5198
+ }),
5199
+ quota.meters.map((meter) => {
5200
+ const remaining = meter.remainingFraction;
5201
+ const percent = remaining === null ? null : Math.round(remaining * 100);
5202
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5203
+ className: "dsha-meter-wrap",
5204
+ children: [
5205
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5206
+ className: "dsha-meter-label",
5207
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: meter.label }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: percent === null ? meter.limit ?? "—" : `${percent}% left` })]
5208
+ }),
5209
+ percent !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5210
+ className: "dsha-meter dsha-meter-green",
5211
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { style: { width: `${percent}%` } })
5212
+ }),
5213
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5214
+ className: "dsha-meter-meta",
5215
+ children: [meter.used !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
5216
+ meter.used,
5217
+ " / ",
5218
+ meter.limit ?? "—"
5219
+ ] }), meter.resetsAt !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
5220
+ t.quotaReset,
5221
+ ": ",
5222
+ formatReset(meter.resetsAt)
5223
+ ] })]
5224
+ })
5225
+ ]
5226
+ }, meter.id);
5227
+ }),
5228
+ quota.meters.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5229
+ className: "dsha-empty",
5230
+ children: t.quotaEmpty
5231
+ }),
5232
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5233
+ className: "dsha-timestamp",
5234
+ children: t.updatedAt.replace("{time}", new Date(quota.fetchedAt).toLocaleString())
5235
+ })
5236
+ ]
5237
+ })
5238
+ ]
5239
+ }),
5240
+ error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5241
+ className: "dsha-error",
5242
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: error })
5243
+ })
5244
+ ]
5245
+ });
5246
+ }
5247
+ //#endregion
5248
+ //#region src/client/ProviderHubSection.tsx
5249
+ const HUB_TABS = [
5250
+ {
5251
+ id: "chatgpt",
5252
+ label: "ChatGPT"
5253
+ },
5254
+ {
5255
+ id: "antigravity",
5256
+ label: "Antigravity"
5257
+ },
5258
+ {
5259
+ id: "command-code",
5260
+ label: "Command Code"
5261
+ },
5262
+ {
5263
+ id: "kimi-code",
5264
+ label: "Kimi Code"
5265
+ },
5266
+ {
5267
+ id: "workbuddy",
5268
+ label: "WorkBuddy"
5269
+ }
5270
+ ];
5271
+ /**
5272
+ * Single settings page that hosts every subscription provider behind tabs,
5273
+ * so the settings sidebar shows one entry instead of one per provider. Only
5274
+ * the active tab is mounted, mirroring the shell's previous behavior of
5275
+ * mounting just the selected settings page (each provider section refetches
5276
+ * its status on mount).
5277
+ */
5278
+ function ProviderHubSection({ t, onModelChange, ...runtime }) {
5279
+ const [active, setActive] = (0, react.useState)("chatgpt");
5280
+ const onTabKeyDown = (0, react.useCallback)((event) => {
5281
+ if (event.key !== "ArrowLeft" && event.key !== "ArrowRight") return;
5282
+ event.preventDefault();
5283
+ const index = HUB_TABS.findIndex((tab) => tab.id === active);
5284
+ const delta = event.key === "ArrowRight" ? 1 : -1;
5285
+ const next = HUB_TABS[(index + delta + HUB_TABS.length) % HUB_TABS.length];
5286
+ setActive(next.id);
5287
+ document.getElementById(`dsh-hub-tab-${next.id}`)?.focus();
5288
+ }, [active]);
5289
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
5290
+ className: "dsh-codex-page",
5291
+ "aria-labelledby": "dsh-hub-title",
5292
+ children: [
5293
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("header", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
5294
+ id: "dsh-hub-title",
5295
+ className: "dsh-codex-title",
5296
+ children: t("hubTitle")
5297
+ }) }),
5298
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5299
+ className: "dsh-codex-segments dsh-hub-tabs",
5300
+ role: "tablist",
5301
+ "aria-label": t("hubTitle"),
5302
+ children: HUB_TABS.map((tab) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5303
+ type: "button",
5304
+ role: "tab",
5305
+ id: `dsh-hub-tab-${tab.id}`,
5306
+ "aria-selected": active === tab.id,
5307
+ "aria-controls": `dsh-hub-panel-${tab.id}`,
5308
+ className: active === tab.id ? "active" : void 0,
5309
+ tabIndex: active === tab.id ? 0 : -1,
5310
+ onClick: () => setActive(tab.id),
5311
+ onKeyDown: onTabKeyDown,
5312
+ children: tab.label
5313
+ }, tab.id))
5314
+ }),
5315
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5316
+ role: "tabpanel",
4267
5317
  id: `dsh-hub-panel-${active}`,
4268
5318
  "aria-labelledby": `dsh-hub-tab-${active}`,
4269
5319
  children: [
@@ -4273,7 +5323,8 @@ window.__ModuleLoader__.load({
4273
5323
  }) : null,
4274
5324
  active === "antigravity" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AntigravitySection, { onModelChange }) : null,
4275
5325
  active === "command-code" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CommandCodeSection, { onModelChange }) : null,
4276
- active === "kimi-code" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(KimiCodeSection, { onModelChange }) : null
5326
+ active === "kimi-code" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(KimiCodeSection, { onModelChange }) : null,
5327
+ active === "workbuddy" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(WorkBuddySection, { onModelChange }) : null
4277
5328
  ]
4278
5329
  })
4279
5330
  ]
@@ -4544,7 +5595,7 @@ window.__ModuleLoader__.load({
4544
5595
  };
4545
5596
  //#endregion
4546
5597
  //#region src/client/styles.ts
4547
- const STYLE_ID$2 = "@eddyskywalker/dsh-chatgpt-subscription/main";
5598
+ const STYLE_ID$3 = "@eddyskywalker/dsh-chatgpt-subscription/main";
4548
5599
  const CSS = `
4549
5600
  .dsh-codex-page{box-sizing:border-box;color:var(--dsw-alias-label-primary);display:flex;flex-direction:column;gap:20px;max-width:780px;min-width:0;padding:2px 0 30px}
4550
5601
  .dsh-codex-page *{box-sizing:border-box}
@@ -4653,21 +5704,21 @@ window.__ModuleLoader__.load({
4653
5704
  @media(prefers-reduced-motion:reduce){.dsh-codex-meter>span{transition:none}.dsh-codex-skeleton span{animation:none}}
4654
5705
  `;
4655
5706
  function installStyles() {
4656
- if (document.querySelector(`style[data-plugin-css="${STYLE_ID$2}"]`) !== null) return () => void 0;
5707
+ if (document.querySelector(`style[data-plugin-css="${STYLE_ID$3}"]`) !== null) return () => void 0;
4657
5708
  const element = document.createElement("style");
4658
5709
  element.dataset.plugin = "@eddyskywalker/dsh-chatgpt-subscription";
4659
- element.dataset.pluginCss = STYLE_ID$2;
5710
+ element.dataset.pluginCss = STYLE_ID$3;
4660
5711
  element.textContent = CSS;
4661
5712
  document.head.appendChild(element);
4662
5713
  return () => element.remove();
4663
5714
  }
4664
5715
  //#endregion
4665
5716
  //#region src/client/antigravity/styles.ts
4666
- const STYLE_ID$1 = "dsh-antigravity-settings-style";
5717
+ const STYLE_ID$2 = "dsh-antigravity-settings-style";
4667
5718
  function installAntigravityStyles() {
4668
5719
  if (typeof document === "undefined" || document.getElementById("dsh-antigravity-settings-style")) return;
4669
5720
  const style = document.createElement("style");
4670
- style.id = STYLE_ID$1;
5721
+ style.id = STYLE_ID$2;
4671
5722
  style.textContent = `
4672
5723
  .dsha-page{box-sizing:border-box;color:var(--dsw-alias-label-primary);display:flex;flex-direction:column;gap:20px;max-width:780px;min-width:0;padding:2px 0 30px}
4673
5724
  .dsha-page *{box-sizing:border-box}
@@ -4690,7 +5741,7 @@ window.__ModuleLoader__.load({
4690
5741
  .dsha-models{display:flex;flex-wrap:wrap;gap:7px;padding-top:8px}
4691
5742
  .dsha-models label{cursor:pointer;display:block;position:relative}
4692
5743
  .dsha-models input{position:absolute;opacity:0;pointer-events:none}
4693
- .dsha-models span{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:6px;color:var(--dsw-alias-label-secondary);display:block;font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:11px;padding:5px 8px;transition:all .15s ease}
5744
+ .dsha-models>label>span{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:6px;color:var(--dsw-alias-label-secondary);display:block;font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:11px;padding:5px 8px;transition:all .15s ease}
4694
5745
  .dsha-models input:checked+span{background:color-mix(in srgb,var(--dsw-alias-button-info-fill,#397ee8) 14%,var(--dsw-alias-bg-layer-2));border-color:color-mix(in srgb,var(--dsw-alias-button-info-fill,#397ee8) 55%,var(--dsw-alias-border-l2));color:var(--dsw-alias-label-primary);font-weight:600}
4695
5746
  .dsha-models input:disabled+span{cursor:default;opacity:.5}
4696
5747
  .dsha-pref-row{align-items:center;border-bottom:0.5px solid var(--dsw-alias-border-l2);display:flex;gap:16px;justify-content:space-between;min-height:58px;padding:10px 0}
@@ -4766,9 +5817,9 @@ window.__ModuleLoader__.load({
4766
5817
  }
4767
5818
  //#endregion
4768
5819
  //#region src/client/antigravity/AntigravityComposerQuota.tsx
4769
- const API$2 = "/antigravity/api";
4770
- async function fetchApi$2(path, options) {
4771
- const res = await fetch(`${API$2}${path}`, {
5820
+ const API$3 = "/antigravity/api";
5821
+ async function fetchApi$3(path, options) {
5822
+ const res = await fetch(`${API$3}${path}`, {
4772
5823
  ...options,
4773
5824
  headers: {
4774
5825
  "Content-Type": "application/json",
@@ -4809,7 +5860,7 @@ window.__ModuleLoader__.load({
4809
5860
  }
4810
5861
  }
4811
5862
  function AntigravityComposerQuota({ directory, loadModelDirectory }) {
4812
- const modelState = useStore$2(directory);
5863
+ const modelState = useStore$3(directory);
4813
5864
  const [status, setStatus] = (0, react.useState)(null);
4814
5865
  const [loading, setLoading] = (0, react.useState)(false);
4815
5866
  const mountedRef = (0, react.useRef)(false);
@@ -4828,7 +5879,7 @@ window.__ModuleLoader__.load({
4828
5879
  if (!mountedRef.current) return;
4829
5880
  setLoading(true);
4830
5881
  try {
4831
- const data = refresh ? await fetchApi$2("/quota", { method: "POST" }) : await fetchApi$2("/status");
5882
+ const data = refresh ? await fetchApi$3("/quota", { method: "POST" }) : await fetchApi$3("/status");
4832
5883
  if (mountedRef.current) setStatus(data);
4833
5884
  } catch {} finally {
4834
5885
  if (mountedRef.current) setLoading(false);
@@ -4866,14 +5917,14 @@ window.__ModuleLoader__.load({
4866
5917
  })]
4867
5918
  });
4868
5919
  }
4869
- function useStore$2(store) {
5920
+ function useStore$3(store) {
4870
5921
  return (0, react.useSyncExternalStore)((listener) => store.subscribe(listener), () => store.getSnapshot(), () => store.getSnapshot());
4871
5922
  }
4872
5923
  //#endregion
4873
5924
  //#region src/client/command-code/CommandCodeComposerQuota.tsx
4874
- const API$1 = "/command-code/api";
4875
- async function fetchApi$1(path, options) {
4876
- const res = await fetch(`${API$1}${path}`, {
5925
+ const API$2 = "/command-code/api";
5926
+ async function fetchApi$2(path, options) {
5927
+ const res = await fetch(`${API$2}${path}`, {
4877
5928
  ...options,
4878
5929
  headers: {
4879
5930
  "Content-Type": "application/json",
@@ -4890,7 +5941,7 @@ window.__ModuleLoader__.load({
4890
5941
  * A bounded usage window is the number a user actually spends down, so it wins
4891
5942
  * over a credit balance; the balance shows only when no window is reported.
4892
5943
  */
4893
- function selectBadgeFacts$1(status) {
5944
+ function selectBadgeFacts$2(status) {
4894
5945
  const quota = status?.quota;
4895
5946
  if (quota === null || quota === void 0) return {
4896
5947
  text: "—",
@@ -4932,7 +5983,7 @@ window.__ModuleLoader__.load({
4932
5983
  };
4933
5984
  }
4934
5985
  function CommandCodeComposerQuota({ directory, loadModelDirectory }) {
4935
- const modelState = useStore$1(directory);
5986
+ const modelState = useStore$2(directory);
4936
5987
  const [status, setStatus] = (0, react.useState)(null);
4937
5988
  const [loading, setLoading] = (0, react.useState)(false);
4938
5989
  const mountedRef = (0, react.useRef)(false);
@@ -4950,7 +6001,7 @@ window.__ModuleLoader__.load({
4950
6001
  if (!mountedRef.current) return;
4951
6002
  setLoading(true);
4952
6003
  try {
4953
- const data = refresh ? await fetchApi$1("/quota", { method: "POST" }) : await fetchApi$1("/status");
6004
+ const data = refresh ? await fetchApi$2("/quota", { method: "POST" }) : await fetchApi$2("/status");
4954
6005
  if (mountedRef.current) setStatus(data);
4955
6006
  } catch {} finally {
4956
6007
  if (mountedRef.current) setLoading(false);
@@ -4966,7 +6017,7 @@ window.__ModuleLoader__.load({
4966
6017
  window.clearInterval(timer);
4967
6018
  };
4968
6019
  }, [fetchStatus, isCommandCode]);
4969
- const facts = (0, react.useMemo)(() => selectBadgeFacts$1(status), [status]);
6020
+ const facts = (0, react.useMemo)(() => selectBadgeFacts$2(status), [status]);
4970
6021
  if (!isCommandCode || !status?.authenticated || facts === null) return null;
4971
6022
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4972
6023
  className: "dsha-composer-quota",
@@ -4983,14 +6034,14 @@ window.__ModuleLoader__.load({
4983
6034
  })]
4984
6035
  });
4985
6036
  }
4986
- function useStore$1(store) {
6037
+ function useStore$2(store) {
4987
6038
  return (0, react.useSyncExternalStore)((listener) => store.subscribe(listener), () => store.getSnapshot(), () => store.getSnapshot());
4988
6039
  }
4989
6040
  //#endregion
4990
6041
  //#region src/client/kimi-code/KimiCodeComposerQuota.tsx
4991
- const API = "/kimi-code/api";
4992
- async function fetchApi(path, options) {
4993
- const res = await fetch(`${API}${path}`, {
6042
+ const API$1 = "/kimi-code/api";
6043
+ async function fetchApi$1(path, options) {
6044
+ const res = await fetch(`${API$1}${path}`, {
4994
6045
  ...options,
4995
6046
  headers: {
4996
6047
  "Content-Type": "application/json",
@@ -5008,7 +6059,7 @@ window.__ModuleLoader__.load({
5008
6059
  * blocks work soonest, so it wins over the longer pools; the 7-day allowance
5009
6060
  * shows only when no 5-hour window is reported.
5010
6061
  */
5011
- function selectBadgeFacts(status) {
6062
+ function selectBadgeFacts$1(status) {
5012
6063
  const quota = status?.quota;
5013
6064
  if (quota === null || quota === void 0) return {
5014
6065
  text: "—",
@@ -5040,7 +6091,7 @@ window.__ModuleLoader__.load({
5040
6091
  };
5041
6092
  }
5042
6093
  function KimiCodeComposerQuota({ directory, loadModelDirectory }) {
5043
- const modelState = useStore(directory);
6094
+ const modelState = useStore$1(directory);
5044
6095
  const [status, setStatus] = (0, react.useState)(null);
5045
6096
  const [loading, setLoading] = (0, react.useState)(false);
5046
6097
  const mountedRef = (0, react.useRef)(false);
@@ -5058,7 +6109,7 @@ window.__ModuleLoader__.load({
5058
6109
  if (!mountedRef.current) return;
5059
6110
  setLoading(true);
5060
6111
  try {
5061
- const data = refresh ? await fetchApi("/quota", { method: "POST" }) : await fetchApi("/status");
6112
+ const data = refresh ? await fetchApi$1("/quota", { method: "POST" }) : await fetchApi$1("/status");
5062
6113
  if (mountedRef.current) setStatus(data);
5063
6114
  } catch {} finally {
5064
6115
  if (mountedRef.current) setLoading(false);
@@ -5074,7 +6125,7 @@ window.__ModuleLoader__.load({
5074
6125
  window.clearInterval(timer);
5075
6126
  };
5076
6127
  }, [fetchStatus, isKimiCode]);
5077
- const facts = (0, react.useMemo)(() => selectBadgeFacts(status), [status]);
6128
+ const facts = (0, react.useMemo)(() => selectBadgeFacts$1(status), [status]);
5078
6129
  if (!isKimiCode || !status?.authenticated || facts === null) return null;
5079
6130
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
5080
6131
  className: "dsha-composer-quota",
@@ -5091,12 +6142,12 @@ window.__ModuleLoader__.load({
5091
6142
  })]
5092
6143
  });
5093
6144
  }
5094
- function useStore(store) {
6145
+ function useStore$1(store) {
5095
6146
  return (0, react.useSyncExternalStore)((listener) => store.subscribe(listener), () => store.getSnapshot(), () => store.getSnapshot());
5096
6147
  }
5097
6148
  //#endregion
5098
6149
  //#region src/client/kimi-code/styles.ts
5099
- const STYLE_ID = "dsh-kimi-code-settings-style";
6150
+ const STYLE_ID$1 = "dsh-kimi-code-settings-style";
5100
6151
  /**
5101
6152
  * Node the base control styles come from.
5102
6153
  *
@@ -5126,7 +6177,7 @@ window.__ModuleLoader__.load({
5126
6177
  .dsha-models{display:flex;flex-wrap:wrap;gap:7px;padding-top:8px}
5127
6178
  .dsha-models label{cursor:pointer;display:block;position:relative}
5128
6179
  .dsha-models input{position:absolute;opacity:0;pointer-events:none}
5129
- .dsha-models span{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:6px;color:var(--dsw-alias-label-secondary);display:block;font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:11px;padding:5px 8px;transition:all .15s ease}
6180
+ .dsha-models>label>span{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:6px;color:var(--dsw-alias-label-secondary);display:block;font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:11px;padding:5px 8px;transition:all .15s ease}
5130
6181
  .dsha-models input:checked+span{background:color-mix(in srgb,var(--dsw-alias-button-info-fill,#397ee8) 14%,var(--dsw-alias-bg-layer-2));border-color:color-mix(in srgb,var(--dsw-alias-button-info-fill,#397ee8) 55%,var(--dsw-alias-border-l2));color:var(--dsw-alias-label-primary);font-weight:600}
5131
6182
  .dsha-models input:disabled+span{cursor:default;opacity:.5}
5132
6183
  .dsha-pref-row{align-items:center;border-bottom:0.5px solid var(--dsw-alias-border-l2);display:flex;gap:16px;justify-content:space-between;min-height:58px;padding:10px 0}
@@ -5186,11 +6237,143 @@ window.__ModuleLoader__.load({
5186
6237
  function installKimiCodeStyles() {
5187
6238
  if (typeof document === "undefined" || document.getElementById("dsh-kimi-code-settings-style")) return;
5188
6239
  const style = document.createElement("style");
5189
- style.id = STYLE_ID;
6240
+ style.id = STYLE_ID$1;
5190
6241
  style.textContent = BASE;
5191
6242
  document.head.appendChild(style);
5192
6243
  }
5193
6244
  //#endregion
6245
+ //#region src/client/workbuddy/WorkBuddyComposerQuota.tsx
6246
+ const API = "/workbuddy/api";
6247
+ async function fetchApi(path, options) {
6248
+ const res = await fetch(`${API}${path}`, {
6249
+ ...options,
6250
+ headers: {
6251
+ "Content-Type": "application/json",
6252
+ ...options?.headers
6253
+ }
6254
+ });
6255
+ const json = await res.json();
6256
+ if (!res.ok || !json.ok) throw new Error(json.error || `HTTP ${res.status}`);
6257
+ return json.value;
6258
+ }
6259
+ /**
6260
+ * Pick the most meaningful number for the badge.
6261
+ *
6262
+ * A bounded allowance is what a user spends down, so it wins over a bare credit
6263
+ * balance; the balance shows only when no percentage is reported. The billing
6264
+ * payload reports both a cycle meter and a package meter, and the cycle meter
6265
+ * is the one that actually resets, so it is preferred when both exist.
6266
+ */
6267
+ function selectBadgeFacts(status) {
6268
+ const quota = status?.quota;
6269
+ if (quota === null || quota === void 0) return {
6270
+ text: "—",
6271
+ tooltip: "WorkBuddy quota unavailable — click to refresh",
6272
+ level: "normal"
6273
+ };
6274
+ const meter = (quota.meters ?? []).find((candidate) => candidate.id === "cycle" && candidate.remainingFraction !== null) ?? (quota.meters ?? []).find((candidate) => candidate.remainingFraction !== null);
6275
+ if (meter !== void 0 && meter.remainingFraction !== null) {
6276
+ const remaining = Math.round(meter.remainingFraction * 100);
6277
+ return {
6278
+ text: `${remaining}%`,
6279
+ tooltip: `[WorkBuddy] ${meter.label}: ${remaining}% left`,
6280
+ level: remaining <= 5 ? "danger" : remaining <= 20 ? "warning" : "normal"
6281
+ };
6282
+ }
6283
+ if (quota.remainingCredits !== null) return {
6284
+ text: String(quota.remainingCredits),
6285
+ tooltip: `[WorkBuddy] Remaining credits: ${quota.remainingCredits}`,
6286
+ level: "normal"
6287
+ };
6288
+ return {
6289
+ text: "—",
6290
+ tooltip: "WorkBuddy quota unavailable — click to refresh",
6291
+ level: "normal"
6292
+ };
6293
+ }
6294
+ function WorkBuddyComposerQuota({ directory, loadModelDirectory }) {
6295
+ const modelState = useStore(directory);
6296
+ const [status, setStatus] = (0, react.useState)(null);
6297
+ const [loading, setLoading] = (0, react.useState)(false);
6298
+ const mountedRef = (0, react.useRef)(false);
6299
+ const isWorkBuddy = modelState.current?.provider === "workbuddy-subscription";
6300
+ (0, react.useEffect)(() => {
6301
+ loadModelDirectory();
6302
+ }, [loadModelDirectory]);
6303
+ (0, react.useEffect)(() => {
6304
+ mountedRef.current = true;
6305
+ return () => {
6306
+ mountedRef.current = false;
6307
+ };
6308
+ }, []);
6309
+ const fetchStatus = (0, react.useCallback)(async (refresh = false) => {
6310
+ if (!mountedRef.current) return;
6311
+ setLoading(true);
6312
+ try {
6313
+ const data = refresh ? await fetchApi("/quota", { method: "POST" }) : await fetchApi("/status");
6314
+ if (mountedRef.current) setStatus(data);
6315
+ } catch {} finally {
6316
+ if (mountedRef.current) setLoading(false);
6317
+ }
6318
+ }, []);
6319
+ (0, react.useEffect)(() => {
6320
+ if (!isWorkBuddy) return;
6321
+ fetchStatus(false);
6322
+ const timer = window.setInterval(() => {
6323
+ if (document.visibilityState === "visible") fetchStatus(false);
6324
+ }, 6e4);
6325
+ return () => {
6326
+ window.clearInterval(timer);
6327
+ };
6328
+ }, [fetchStatus, isWorkBuddy]);
6329
+ const facts = (0, react.useMemo)(() => selectBadgeFacts(status), [status]);
6330
+ if (!isWorkBuddy || !status?.authenticated || facts === null) return null;
6331
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
6332
+ className: "dsha-composer-quota",
6333
+ "data-level": facts.level,
6334
+ title: facts.tooltip,
6335
+ "aria-label": facts.tooltip,
6336
+ onClick: () => void fetchStatus(true),
6337
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6338
+ className: "dsha-composer-quota-label",
6339
+ children: "额度"
6340
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", {
6341
+ className: "dsha-composer-quota-val",
6342
+ children: loading ? "…" : facts.text
6343
+ })]
6344
+ });
6345
+ }
6346
+ function useStore(store) {
6347
+ return (0, react.useSyncExternalStore)((listener) => store.subscribe(listener), () => store.getSnapshot(), () => store.getSnapshot());
6348
+ }
6349
+ //#endregion
6350
+ //#region src/client/workbuddy/styles.ts
6351
+ const STYLE_ID = "dsh-workbuddy-settings-style";
6352
+ /**
6353
+ * WorkBuddy-only additions to the shared settings design system.
6354
+ *
6355
+ * The base control styles (`.dsha-page`, `.dsha-group`, `.dsha-models`, the
6356
+ * account card, …) come from the stylesheet the sibling routes install, in the
6357
+ * same order the client installs them, so this file declares only what stays
6358
+ * unique to this card: the mono credential paths and the login-progress line.
6359
+ *
6360
+ * Everything this tab used to declare locally — the per-model capability line
6361
+ * and the account-region headings — is gone: the model pill is name-only like
6362
+ * every sibling tab, and the account card is the shared one.
6363
+ */
6364
+ function installWorkBuddyStyles() {
6365
+ if (typeof document === "undefined" || document.getElementById("dsh-workbuddy-settings-style")) return;
6366
+ const style = document.createElement("style");
6367
+ style.id = STYLE_ID;
6368
+ style.textContent = `
6369
+ .dshwb-mono{font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:11px}
6370
+ .dsha-account-add-actions{display:flex;flex-wrap:wrap;gap:8px}
6371
+ .dsha-account-add-actions .dsha-btn{background:var(--dsw-alias-button-info-fill,#397ee8);border-color:transparent;color:var(--dsw-alias-button-info-label,#fff)}
6372
+ .dsha-account-add-actions .dsha-btn:hover:not(:disabled){opacity:.9;background:var(--dsw-alias-button-info-fill,#397ee8)}
6373
+ `;
6374
+ document.head.append(style);
6375
+ }
6376
+ //#endregion
5194
6377
  //#region src/client/mermaid/sanitize.ts
5195
6378
  const STRIP_ELEMENTS = /* @__PURE__ */ new Set([
5196
6379
  "foreignobject",
@@ -5530,17 +6713,22 @@ window.__ModuleLoader__.load({
5530
6713
  function apply(ctx) {
5531
6714
  ctx.effect(() => ctx.locale.register(NS, dictionaries), "dsh-chatgpt-subscription: dictionaries");
5532
6715
  ctx.effect(() => installStyles(), "dsh-chatgpt-subscription: styles");
5533
- ctx.effect(() => ctx.locale.register(NS_ANTIGRAVITY, dictionaries$3), "dsh-antigravity: dictionaries");
6716
+ ctx.effect(() => ctx.locale.register(NS_ANTIGRAVITY, dictionaries$4), "dsh-antigravity: dictionaries");
5534
6717
  ctx.effect(() => {
5535
6718
  installAntigravityStyles();
5536
6719
  return () => {};
5537
6720
  }, "dsh-antigravity: styles");
5538
- ctx.effect(() => ctx.locale.register(NS_COMMAND_CODE, dictionaries$2), "dsh-command-code: dictionaries");
5539
- ctx.effect(() => ctx.locale.register(NS_KIMI_CODE, dictionaries$1), "dsh-kimi-code: dictionaries");
6721
+ ctx.effect(() => ctx.locale.register(NS_COMMAND_CODE, dictionaries$3), "dsh-command-code: dictionaries");
6722
+ ctx.effect(() => ctx.locale.register(NS_KIMI_CODE, dictionaries$2), "dsh-kimi-code: dictionaries");
5540
6723
  ctx.effect(() => {
5541
6724
  installKimiCodeStyles();
5542
6725
  return () => {};
5543
6726
  }, "dsh-kimi-code: styles");
6727
+ ctx.effect(() => ctx.locale.register(NS_WORKBUDDY, dictionaries$1), "dsh-workbuddy: dictionaries");
6728
+ ctx.effect(() => {
6729
+ installWorkBuddyStyles();
6730
+ return () => {};
6731
+ }, "dsh-workbuddy: styles");
5544
6732
  ctx.effect(() => {
5545
6733
  installPoolStyles();
5546
6734
  return () => {};
@@ -5628,6 +6816,21 @@ window.__ModuleLoader__.load({
5628
6816
  };
5629
6817
  }
5630
6818
  }, KimiCodeComposerQuota));
6819
+ ctx.slots.inject("conversation.input.right", () => ctx.slots.register({
6820
+ name: "conversation.input.right",
6821
+ id: "workbuddy-quota",
6822
+ order: 39,
6823
+ locale: NS_WORKBUDDY,
6824
+ inject: (sessionId) => {
6825
+ const directory = ctx.modelDirectories.directoryFor(sessionId);
6826
+ return {
6827
+ directory: directory.store,
6828
+ loadModelDirectory: () => {
6829
+ directory.load().catch(() => void 0);
6830
+ }
6831
+ };
6832
+ }
6833
+ }, WorkBuddyComposerQuota));
5631
6834
  ctx.slots.inject("tool.call.toolview", () => ctx.slots.register({
5632
6835
  name: "tool.call.toolview",
5633
6836
  key: CODEX_IMAGE_TOOL_NAME,