dsh-plugin-subscription-oauth 0.12.0 → 0.12.3

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.
package/lib/auth/rpc.js CHANGED
@@ -155,6 +155,8 @@ async function runEndpoint(registry, endpoint, payload, balance) {
155
155
  }
156
156
  case 'usage':
157
157
  return registry.usage(requiredProvider(payload, endpoint), booleanField(payload, 'force'));
158
+ case 'auto-state':
159
+ return registry.autoState();
158
160
  case 'setAutoOn': {
159
161
  const enabled = booleanField(payload, 'enabled');
160
162
  if (enabled === undefined)
@@ -244,6 +244,8 @@ const styles = {
244
244
  },
245
245
  /** The metered windows under one account row: no border, just the meters. */
246
246
  usageInline: { display: 'flex', flexDirection: 'column', gap: 6, marginTop: 4 },
247
+ /** The auto provider's on/off row: a label and its switch. */
248
+ autoSwitchRow: { display: 'flex', alignItems: 'center', gap: 8, justifyContent: 'space-between' },
247
249
  modelRow: {
248
250
  display: 'flex', alignItems: 'flex-start', gap: 8,
249
251
  cursor: 'pointer', fontSize: 13, lineHeight: '20px',
@@ -517,7 +519,7 @@ function ProviderCard(props) {
517
519
  return (_jsxs("div", { style: cardOpen ? { ...styles.card, ...styles.cardOpen } : styles.card, children: [_jsxs("div", { style: styles.cardHeader, role: "button", tabIndex: 0, "aria-expanded": cardOpen, onClick: toggleCardOpen, onKeyDown: (event) => { if (event.key === 'Enter' || event.key === ' ') {
518
520
  event.preventDefault();
519
521
  toggleCardOpen();
520
- } }, children: [_jsx("span", { style: { ...styles.dot, ...dotStyle(status) }, "aria-hidden": "true" }), _jsx("span", { style: styles.name, children: status.displayName }), status.loggedIn && _jsx("span", { style: styles.badgeDefault, children: t('badgeLive') }), _jsx("span", { style: styles.cardSummary, children: summaryLine }), _jsx("button", { type: "button", className: "dsh-subscription-quiet", style: styles.pushRight, "aria-expanded": cardOpen, "aria-controls": `dsh-subscription-card-${status.provider}`, onClick: (event) => { event.stopPropagation(); toggleCardOpen(); }, children: cardOpen ? t('collapse') : t('edit') })] }), _jsxs("div", { id: `dsh-subscription-card-${status.provider}`, style: cardOpen ? styles.cardBody : { ...styles.cardBody, display: 'none' }, children: [connectable && !status.loggedIn && !busy && _jsx("p", { style: styles.statusLine, children: t('statusSignIn') }), status.lastError !== undefined && (_jsx("p", { className: "dsh-subscription-error", children: t('lastError', { message: status.lastError }) })), message?.tone === 'error' && _jsx("p", { className: "dsh-subscription-error", children: message.text }), message?.tone === 'success' && _jsx("p", { className: "dsh-subscription-success", children: message.text }), accounts.map((account) => {
522
+ } }, children: [_jsx("span", { style: { ...styles.dot, ...dotStyle(status) }, "aria-hidden": "true" }), _jsx("span", { style: styles.name, children: status.displayName }), status.loggedIn && _jsx("span", { style: styles.badgeDefault, children: t('badgeLive') }), _jsx("span", { style: styles.cardSummary, children: summaryLine }), _jsx("button", { type: "button", className: "dsh-subscription-quiet", style: styles.pushRight, "aria-expanded": cardOpen, "aria-controls": `dsh-subscription-card-${status.provider}`, onClick: (event) => { event.stopPropagation(); toggleCardOpen(); }, children: cardOpen ? t('collapse') : t('edit') })] }), _jsxs("div", { id: `dsh-subscription-card-${status.provider}`, style: cardOpen ? styles.cardBody : { ...styles.cardBody, display: 'none' }, children: [connectable && !status.loggedIn && !busy && _jsx("p", { style: styles.statusLine, children: t('statusSignIn') }), status.lastError !== undefined && (_jsx("p", { className: "dsh-subscription-error", children: t('lastError', { message: status.lastError }) })), message?.tone === 'error' && _jsx("p", { className: "dsh-subscription-error", children: message.text }), message?.tone === 'success' && _jsx("p", { className: "dsh-subscription-success", children: message.text }), status.provider === 'auto' && props.onSetAutoOn !== undefined && (_jsxs("label", { style: styles.autoSwitchRow, children: [_jsx("span", { style: styles.modelName, children: t('autoSwitchLabel') }), _jsx("input", { type: "checkbox", className: "dsh-subscription-switch", checked: props.autoOn ?? true, disabled: busyAction, onChange: (event) => { props.onSetAutoOn?.(event.target.checked); } })] })), accounts.map((account) => {
521
523
  const usageEntry = (status.usage ?? []).find((entry) => entry.account === account.account);
522
524
  return (_jsxs("div", { style: styles.accountRow, children: [_jsxs("div", { style: styles.accountHeader, children: [_jsx("button", { type: "button", style: styles.starButton, title: account.isDefault ? t('defaultBadge') : t('makeDefault'), "aria-label": account.isDefault ? t('defaultBadge') : t('makeDefault'), disabled: busyAction || account.isDefault, onClick: () => { props.onSetDefault(account.account); }, children: account.isDefault ? '★' : '☆' }), _jsx("span", { style: styles.accountName, children: account.label }), account.isDefault && _jsx("span", { style: styles.badgeDefault, children: t('defaultBadge') }), account.needsLogin === true && (_jsx("span", { style: styles.badgeWarn, title: t('accountNeedsLoginHint'), children: t('accountNeedsLogin') })), usageEntry?.plan !== undefined && (_jsx("span", { style: styles.badge, children: t('usagePlan', { plan: usageEntry.plan }) })), _jsx("span", { style: styles.statusLine, children: account.expired
523
525
  ? t('accountExpired')
@@ -563,8 +565,20 @@ export function SubscriptionsSection(props) {
563
565
  const [error, setError] = useState(undefined);
564
566
  const [messages, setMessages] = useState({});
565
567
  const [busyAction, setBusyAction] = useState(false);
568
+ const [autoOn, setAutoOn] = useState(true);
566
569
  const mounted = useRef(true);
567
570
  const timer = useRef(undefined);
571
+ /** Read whether the `auto` provider is offered, so its card can render the switch. */
572
+ useEffect(() => {
573
+ if (call === undefined)
574
+ return;
575
+ let live = true;
576
+ void call('auto-state', undefined)
577
+ .then((result) => { if (live)
578
+ setAutoOn(result.enabled); })
579
+ .catch(() => { });
580
+ return () => { live = false; };
581
+ }, [call]);
568
582
  /** Fetch the current state; a failure is shown rather than swallowed. */
569
583
  const refresh = useCallback(async () => {
570
584
  if (call === undefined)
@@ -581,6 +595,27 @@ export function SubscriptionsSection(props) {
581
595
  setError(t('actionFailed', { message: messageOf(caught) }));
582
596
  }
583
597
  }, [call, t]);
598
+ /** Flip the `auto` provider on or off. */
599
+ const setAuto = useCallback(async (enabled) => {
600
+ if (call === undefined)
601
+ return;
602
+ setBusyAction(true);
603
+ try {
604
+ const result = await call('setAutoOn', { enabled });
605
+ if (!mounted.current)
606
+ return;
607
+ setAutoOn(result.enabled);
608
+ await refresh();
609
+ }
610
+ catch (caught) {
611
+ if (mounted.current)
612
+ setError(t('actionFailed', { message: messageOf(caught) }));
613
+ }
614
+ finally {
615
+ if (mounted.current)
616
+ setBusyAction(false);
617
+ }
618
+ }, [call, refresh, t]);
584
619
  const stopPolling = useCallback(() => {
585
620
  if (timer.current !== undefined) {
586
621
  clearInterval(timer.current);
@@ -824,5 +859,5 @@ export function SubscriptionsSection(props) {
824
859
  }, [act, call]);
825
860
  if (call === undefined)
826
861
  return _jsx("p", { style: styles.intro, children: t('unavailable') });
827
- return (_jsxs("div", { className: "dsh-subscriptions-settings", style: styles.section, children: [_jsx("style", { children: subscriptionsCss }), _jsxs("div", { style: styles.sectionHeader, children: [_jsx("h2", { style: styles.heading, children: t('nav') }), _jsx("p", { style: styles.intro, children: t('intro') })] }), error !== undefined && _jsx("p", { className: "dsh-subscription-error", children: error }), status === undefined && _jsx("p", { style: styles.statusLine, children: t('loading') }), orderedProviders.map((provider) => (_jsx(ProviderCard, { status: provider, t: t, busyAction: busyAction, ...messages[provider.provider] === undefined ? {} : { message: messages[provider.provider] }, onLogin: () => { login(provider.provider); }, onCancel: () => { cancel(provider.provider); }, onLogout: (account) => { logout(provider.provider, account); }, onSetDefault: (account) => { setDefault(provider.provider, account); }, onRefreshToken: (account) => { refreshToken(provider.provider, account); }, onRefreshModels: () => { refreshModels(provider.provider); }, onSetModelVisible: (id, visible) => { setModelVisible(provider.provider, id, visible); }, onSetModelOrder: (ids) => { setModelOrder(provider.provider, ids); }, onAddModel: (id) => { addModel(provider.provider, id); }, onRemoveModel: (id) => { removeModel(provider.provider, id); }, onRefreshUsage: () => { refreshUsage(provider.provider); }, onReload: () => { reload(provider.provider); } }, provider.provider)))] }));
862
+ return (_jsxs("div", { className: "dsh-subscriptions-settings", style: styles.section, children: [_jsx("style", { children: subscriptionsCss }), _jsxs("div", { style: styles.sectionHeader, children: [_jsx("h2", { style: styles.heading, children: t('nav') }), _jsx("p", { style: styles.intro, children: t('intro') })] }), error !== undefined && _jsx("p", { className: "dsh-subscription-error", children: error }), status === undefined && _jsx("p", { style: styles.statusLine, children: t('loading') }), orderedProviders.map((provider) => (_jsx(ProviderCard, { status: provider, t: t, busyAction: busyAction, ...messages[provider.provider] === undefined ? {} : { message: messages[provider.provider] }, onLogin: () => { login(provider.provider); }, onCancel: () => { cancel(provider.provider); }, onLogout: (account) => { logout(provider.provider, account); }, onSetDefault: (account) => { setDefault(provider.provider, account); }, onRefreshToken: (account) => { refreshToken(provider.provider, account); }, onRefreshModels: () => { refreshModels(provider.provider); }, onSetModelVisible: (id, visible) => { setModelVisible(provider.provider, id, visible); }, onSetModelOrder: (ids) => { setModelOrder(provider.provider, ids); }, onAddModel: (id) => { addModel(provider.provider, id); }, onRemoveModel: (id) => { removeModel(provider.provider, id); }, onRefreshUsage: () => { refreshUsage(provider.provider); }, onReload: () => { reload(provider.provider); }, ...provider.provider !== 'auto' ? {} : { autoOn, onSetAutoOn: (enabled) => { void setAuto(enabled); } } }, provider.provider)))] }));
828
863
  }
@@ -207,12 +207,18 @@ export function mergeAccountWindows(readings) {
207
207
  if (withPercent.length === 0)
208
208
  continue;
209
209
  const usedPercent = withPercent.reduce((sum, window) => sum + (window.usedPercent ?? 0), 0) / withPercent.length;
210
+ // A credit window carries the raw figures too; keep them so the compact line can read
211
+ // money as money (0/1500) rather than as a spent share (0%), which misreads an empty
212
+ // balance as nothing to meter.
213
+ const used = list.reduce((sum, window) => sum + (window.used ?? 0), 0);
214
+ const limit = list.reduce((sum, window) => sum + (window.limit ?? 0), 0);
210
215
  // The pool's reset is the soonest account reset: the window refills when the first
211
216
  // account's does.
212
217
  const resets = list.map((window) => window.resetsAt).filter((at) => typeof at === 'number');
213
218
  merged.push({
214
219
  label,
215
220
  usedPercent: Math.max(0, Math.min(100, usedPercent)),
221
+ ...limit > 0 ? { used, limit } : {},
216
222
  ...resets.length === 0 ? {} : { resetsAt: Math.min(...resets) },
217
223
  });
218
224
  }
@@ -60,6 +60,7 @@ export declare const en: {
60
60
  readonly modelsDrag: "Drag to re-order (or press ↑ / ↓ here)";
61
61
  readonly modelsAutoChainOff: "Take out of Auto";
62
62
  readonly modelsAutoChainEmpty: "No model is enabled, so Auto has nothing to try.";
63
+ readonly autoSwitchLabel: "Offer Auto in the model picker";
63
64
  readonly modelsPinned: "pinned";
64
65
  readonly usageTitle: "Plan and usage";
65
66
  readonly usagePlan: "Plan: {plan}";
@@ -157,6 +158,7 @@ export declare const zh: {
157
158
  modelsDrag: string;
158
159
  modelsAutoChainOff: string;
159
160
  modelsAutoChainEmpty: string;
161
+ autoSwitchLabel: string;
160
162
  modelsPinned: string;
161
163
  usageTitle: string;
162
164
  usagePlan: string;
@@ -60,6 +60,7 @@ export const en = {
60
60
  modelsDrag: 'Drag to re-order (or press ↑ / ↓ here)',
61
61
  modelsAutoChainOff: 'Take out of Auto',
62
62
  modelsAutoChainEmpty: 'No model is enabled, so Auto has nothing to try.',
63
+ autoSwitchLabel: 'Offer Auto in the model picker',
63
64
  modelsPinned: 'pinned',
64
65
  usageTitle: 'Plan and usage',
65
66
  usagePlan: 'Plan: {plan}',
@@ -155,6 +156,7 @@ export const zh = {
155
156
  modelsDrag: '拖动调整顺序(也可在此按 ↑ / ↓)',
156
157
  modelsAutoChainOff: '从自动中移除',
157
158
  modelsAutoChainEmpty: '没有启用任何模型,自动没有可尝试的模型。',
159
+ autoSwitchLabel: '在模型选择器中提供 Auto',
158
160
  modelsPinned: '固定',
159
161
  usageTitle: '套餐与额度',
160
162
  usagePlan: '套餐:{plan}',
package/lib/client.js CHANGED
@@ -91,6 +91,7 @@ const en = {
91
91
  modelsDrag: "Drag to re-order (or press ↑ / ↓ here)",
92
92
  modelsAutoChainOff: "Take out of Auto",
93
93
  modelsAutoChainEmpty: "No model is enabled, so Auto has nothing to try.",
94
+ autoSwitchLabel: "Offer Auto in the model picker",
94
95
  modelsPinned: "pinned",
95
96
  usageTitle: "Plan and usage",
96
97
  usagePlan: "Plan: {plan}",
@@ -186,6 +187,7 @@ const zh = {
186
187
  modelsDrag: "拖动调整顺序(也可在此按 ↑ / ↓)",
187
188
  modelsAutoChainOff: "从自动中移除",
188
189
  modelsAutoChainEmpty: "没有启用任何模型,自动没有可尝试的模型。",
190
+ autoSwitchLabel: "在模型选择器中提供 Auto",
189
191
  modelsPinned: "固定",
190
192
  usageTitle: "套餐与额度",
191
193
  usagePlan: "套餐:{plan}",
@@ -1189,6 +1191,12 @@ const styles = {
1189
1191
  gap: 6,
1190
1192
  marginTop: 4
1191
1193
  },
1194
+ autoSwitchRow: {
1195
+ display: "flex",
1196
+ alignItems: "center",
1197
+ gap: 8,
1198
+ justifyContent: "space-between"
1199
+ },
1192
1200
  modelRow: {
1193
1201
  display: "flex",
1194
1202
  alignItems: "flex-start",
@@ -1628,6 +1636,21 @@ function ProviderCard(props) {
1628
1636
  className: "dsh-subscription-success",
1629
1637
  children: message.text
1630
1638
  }),
1639
+ status.provider === "auto" && props.onSetAutoOn !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
1640
+ style: styles.autoSwitchRow,
1641
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1642
+ style: styles.modelName,
1643
+ children: t("autoSwitchLabel")
1644
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1645
+ type: "checkbox",
1646
+ className: "dsh-subscription-switch",
1647
+ checked: props.autoOn ?? true,
1648
+ disabled: busyAction,
1649
+ onChange: (event) => {
1650
+ props.onSetAutoOn?.(event.target.checked);
1651
+ }
1652
+ })]
1653
+ }),
1631
1654
  accounts.map((account) => {
1632
1655
  const usageEntry = (status.usage ?? []).find((entry) => entry.account === account.account);
1633
1656
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -2044,8 +2067,20 @@ function SubscriptionsSection(props) {
2044
2067
  const [error, setError] = (0, react.useState)(void 0);
2045
2068
  const [messages, setMessages] = (0, react.useState)({});
2046
2069
  const [busyAction, setBusyAction] = (0, react.useState)(false);
2070
+ const [autoOn, setAutoOn] = (0, react.useState)(true);
2047
2071
  const mounted = (0, react.useRef)(true);
2048
2072
  const timer = (0, react.useRef)(void 0);
2073
+ /** Read whether the `auto` provider is offered, so its card can render the switch. */
2074
+ (0, react.useEffect)(() => {
2075
+ if (call === void 0) return;
2076
+ let live = true;
2077
+ call("auto-state", void 0).then((result) => {
2078
+ if (live) setAutoOn(result.enabled);
2079
+ }).catch(() => {});
2080
+ return () => {
2081
+ live = false;
2082
+ };
2083
+ }, [call]);
2049
2084
  /** Fetch the current state; a failure is shown rather than swallowed. */
2050
2085
  const refresh = (0, react.useCallback)(async () => {
2051
2086
  if (call === void 0) return;
@@ -2058,6 +2093,25 @@ function SubscriptionsSection(props) {
2058
2093
  if (mounted.current) setError(t("actionFailed", { message: messageOf(caught) }));
2059
2094
  }
2060
2095
  }, [call, t]);
2096
+ /** Flip the `auto` provider on or off. */
2097
+ const setAuto = (0, react.useCallback)(async (enabled) => {
2098
+ if (call === void 0) return;
2099
+ setBusyAction(true);
2100
+ try {
2101
+ const result = await call("setAutoOn", { enabled });
2102
+ if (!mounted.current) return;
2103
+ setAutoOn(result.enabled);
2104
+ await refresh();
2105
+ } catch (caught) {
2106
+ if (mounted.current) setError(t("actionFailed", { message: messageOf(caught) }));
2107
+ } finally {
2108
+ if (mounted.current) setBusyAction(false);
2109
+ }
2110
+ }, [
2111
+ call,
2112
+ refresh,
2113
+ t
2114
+ ]);
2061
2115
  const stopPolling = (0, react.useCallback)(() => {
2062
2116
  if (timer.current !== void 0) {
2063
2117
  clearInterval(timer.current);
@@ -2371,6 +2425,12 @@ function SubscriptionsSection(props) {
2371
2425
  },
2372
2426
  onReload: () => {
2373
2427
  reload(provider.provider);
2428
+ },
2429
+ ...provider.provider !== "auto" ? {} : {
2430
+ autoOn,
2431
+ onSetAutoOn: (enabled) => {
2432
+ setAuto(enabled);
2433
+ }
2374
2434
  }
2375
2435
  }, provider.provider))
2376
2436
  ]
@@ -2539,10 +2599,16 @@ function mergeAccountWindows(readings) {
2539
2599
  const withPercent = list.filter((window$1) => window$1.usedPercent !== void 0);
2540
2600
  if (withPercent.length === 0) continue;
2541
2601
  const usedPercent = withPercent.reduce((sum, window$1) => sum + (window$1.usedPercent ?? 0), 0) / withPercent.length;
2602
+ const used = list.reduce((sum, window$1) => sum + (window$1.used ?? 0), 0);
2603
+ const limit = list.reduce((sum, window$1) => sum + (window$1.limit ?? 0), 0);
2542
2604
  const resets = list.map((window$1) => window$1.resetsAt).filter((at) => typeof at === "number");
2543
2605
  merged.push({
2544
2606
  label,
2545
2607
  usedPercent: Math.max(0, Math.min(100, usedPercent)),
2608
+ ...limit > 0 ? {
2609
+ used,
2610
+ limit
2611
+ } : {},
2546
2612
  ...resets.length === 0 ? {} : { resetsAt: Math.min(...resets) }
2547
2613
  });
2548
2614
  }