dsh-subscriptions 0.2.3 → 0.3.1

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 (41) hide show
  1. package/README.md +9 -21
  2. package/lib/auth/rpc.d.ts +2 -20
  3. package/lib/auth/rpc.js +3 -49
  4. package/lib/client/ProviderAccountManager.d.ts +1 -1
  5. package/lib/client/ProviderAccountManager.js +86 -110
  6. package/lib/client/ProviderModelEditor.d.ts +1 -1
  7. package/lib/client/ProviderModelEditor.js +2 -6
  8. package/lib/client/SubscriptionUsageBadge.d.ts +8 -2
  9. package/lib/client/SubscriptionUsageBadge.js +24 -17
  10. package/lib/client/SubscriptionsSection.js +1 -2
  11. package/lib/client/locales.d.ts +2 -32
  12. package/lib/client/locales.js +2 -32
  13. package/lib/client/model-filter.js +1 -1
  14. package/lib/client/subscription-card-styles.d.ts +1 -1
  15. package/lib/client/subscription-card-styles.js +1 -1
  16. package/lib/client.js +234 -518
  17. package/lib/client.js.map +1 -1
  18. package/lib/index.js +208 -3305
  19. package/lib/providers/claude.d.ts +3 -1
  20. package/lib/providers/claude.js +2 -2
  21. package/lib/providers/ollama-settings.d.ts +1 -1
  22. package/lib/providers/ollama-settings.js +1 -1
  23. package/lib/providers/pool-usage.d.ts +10 -9
  24. package/lib/providers/pool-usage.js +30 -19
  25. package/lib/providers/pool.d.ts +6 -6
  26. package/lib/providers/pool.js +24 -12
  27. package/package.json +1 -7
  28. package/lib/client/CursorCard.d.ts +0 -9
  29. package/lib/client/CursorCard.js +0 -151
  30. package/lib/providers/cursor-adapter.d.ts +0 -50
  31. package/lib/providers/cursor-adapter.js +0 -304
  32. package/lib/providers/cursor-auth.d.ts +0 -43
  33. package/lib/providers/cursor-auth.js +0 -221
  34. package/lib/providers/cursor-model-settings.d.ts +0 -15
  35. package/lib/providers/cursor-model-settings.js +0 -60
  36. package/lib/providers/cursor-usage.d.ts +0 -9
  37. package/lib/providers/cursor-usage.js +0 -131
  38. package/vendor/cursor/LICENSE +0 -21
  39. package/vendor/cursor/index.d.ts +0 -18
  40. package/vendor/cursor/index.js +0 -3574
  41. package/vendor/cursor/proto.js +0 -254
@@ -35,6 +35,7 @@ import { en } from './locales.js';
35
35
  import { hostIcon } from './host-icons.js';
36
36
  import { HOST_CONTRACT, markerSelector } from './host-contract.js';
37
37
  import { USAGE_BADGE_REFRESH_EVENT, useUsageBadgeMode, } from './usage-badge-preferences.js';
38
+ import { visibleUsageWindows } from './usage-window-filter.js';
38
39
  /** DSH renamed the data icon in 0.1.7; retain older supported hosts too. */
39
40
  export function usageBadgeIcon(icons) {
40
41
  return hostIcon(icons, 'DataOutline');
@@ -60,7 +61,6 @@ const PROVIDER_NAMES = {
60
61
  grok: 'Grok',
61
62
  copilot: 'Copilot',
62
63
  antigravity: 'Antigravity',
63
- 'cursor-subscription': 'Cursor',
64
64
  'opencode-go': 'OpenCode Go',
65
65
  'kimi-coding': 'Kimi Code',
66
66
  minimax: 'MiniMax',
@@ -110,13 +110,13 @@ export function groupUsageDisplays(roster, windowsOf, plans) {
110
110
  }
111
111
  /** Read each account source independently so a failing endpoint cannot hide the others. */
112
112
  export async function loadBadgeRoster(rpc) {
113
- const [subscriptions, cursor, external] = await Promise.allSettled([
113
+ const [subscriptions, external] = await Promise.allSettled([
114
114
  callSubscriptionsAuth(rpc, 'status', {}),
115
- callSubscriptionsAuth(rpc, 'cursorStatus', {}),
116
115
  callSubscriptionsAuth(rpc, 'externalStatus', {}),
117
116
  ]);
118
117
  const roster = [];
119
118
  const refreshed = new Set();
119
+ const visibleModels = new Map();
120
120
  if (subscriptions.status === 'fulfilled') {
121
121
  for (const provider of [
122
122
  'codex',
@@ -126,18 +126,13 @@ export async function loadBadgeRoster(rpc) {
126
126
  'antigravity',
127
127
  ]) {
128
128
  refreshed.add(provider);
129
+ const visible = subscriptions.value.providers[provider]?.visibleModels;
130
+ if (visible !== undefined)
131
+ visibleModels.set(provider, visible);
129
132
  for (const account of accountsOf(subscriptions.value.providers[provider]))
130
133
  roster.push({ provider, account });
131
134
  }
132
135
  }
133
- if (cursor.status === 'fulfilled') {
134
- refreshed.add('cursor-subscription');
135
- if (cursor.value.authenticated)
136
- roster.push({
137
- provider: 'cursor-subscription',
138
- account: { key: 'cursor', isDefault: true },
139
- });
140
- }
141
136
  if (external.status === 'fulfilled') {
142
137
  for (const [source, provider] of [
143
138
  ['opencode-go', 'opencode-go'],
@@ -154,11 +149,9 @@ export async function loadBadgeRoster(rpc) {
154
149
  });
155
150
  }
156
151
  }
157
- return { roster, refreshed };
152
+ return { roster, refreshed, visibleModels };
158
153
  }
159
154
  export async function usageOf(rpc, { provider, account }) {
160
- if (provider === 'cursor-subscription')
161
- return callSubscriptionsAuth(rpc, 'cursorUsage', {});
162
155
  if (provider === 'opencode-go' ||
163
156
  provider === 'kimi-coding' ||
164
157
  provider === 'minimax' ||
@@ -382,7 +375,7 @@ export function SubscriptionUsageBadge({ rpc, currentModel, t, rotationMs, }) {
382
375
  return;
383
376
  inflightRef.current = true;
384
377
  try {
385
- const { roster, refreshed } = await loadBadgeRoster(rpc);
378
+ const { roster, refreshed, visibleModels } = await loadBadgeRoster(rpc);
386
379
  if (!mountedRef.current)
387
380
  return;
388
381
  const lastKnown = lastKnownRef.current;
@@ -433,6 +426,9 @@ export function SubscriptionUsageBadge({ rpc, currentModel, t, rotationMs, }) {
433
426
  ...display,
434
427
  accounts: display.accounts.map((account) => ({
435
428
  ...account,
429
+ // Filter only the display row, not lastKnown: restoring visibility
430
+ // must work even when the next usage request fails and uses cache.
431
+ windows: visibleUsageWindows(display.provider, account.windows, visibleModels.get(display.provider)),
436
432
  ...observationsRef.current.get(`${display.provider}:${account.key}`),
437
433
  })),
438
434
  })),
@@ -624,7 +620,7 @@ export function SubscriptionUsageBadge({ rpc, currentModel, t, rotationMs, }) {
624
620
  }, onMouseLeave: () => {
625
621
  setHover(false);
626
622
  }, onClick: toggle, children: [_jsx(UsageBadgeIcon, {}), _jsx("span", { style: styles.label, children: label })] }), open &&
627
- createPortal(_jsxs("div", { ref: panelRef, role: "dialog", "aria-label": title, className: "dsh-subscription-usage-panel", style: { ...styles.panel, ...(pos ?? MEASURE_STYLE) }, children: [_jsx("div", { style: styles.title, children: _jsxs("span", { style: styles.titleLabel, children: [_jsx(UsageBadgeIcon, {}), title] }) }), _jsx("div", { style: styles.titleRule, "aria-hidden": true }), expanded.map((d, index) => (_jsxs("section", { style: index === 0 ? undefined : styles.section, children: [_jsxs("div", { style: styles.providerRow, children: [_jsxs("span", { style: styles.providerName, children: [d.name, d.provider === current && (_jsx("span", { style: styles.currentTag, children: translate('usageBadgeCurrent') }))] }), d.accounts.length === 1 && (_jsx(AccountMeta, { account: d.accounts[0], translate: translate }))] }), d.accounts.map((account, accountIndex) => (_jsxs("div", { style: accountIndex === 0 ? undefined : styles.accountBlock, children: [d.accounts.length > 1 && (_jsx("div", { style: styles.accountRow, children: _jsx(AccountMeta, { account: account, translate: translate }) })), _jsx(AccountWindows, { windows: account.windows, observedAt: account.observedAt, stale: account.stale, model: d.provider === dialogProvider ? dialogModel : undefined, provider: d.provider, translate: translate }, `${d.provider}:${dialogModel ?? ''}`)] }, account.key)))] }, d.provider)))] }), document.body)] }));
623
+ createPortal(_jsxs("div", { ref: panelRef, role: "dialog", "aria-label": title, className: "dsh-subscription-usage-panel", style: { ...styles.panel, ...(pos ?? MEASURE_STYLE) }, children: [_jsx("div", { style: styles.title, children: _jsxs("span", { style: styles.titleLabel, children: [_jsx(UsageBadgeIcon, {}), title] }) }), _jsx("div", { style: styles.titleRule, "aria-hidden": true }), expanded.map((d, index) => (_jsxs("section", { style: index === 0 ? undefined : styles.section, children: [_jsxs("div", { style: styles.providerRow, children: [_jsxs("span", { style: styles.providerName, children: [d.name, d.provider === current && (_jsx("span", { style: styles.currentTag, children: translate('usageBadgeCurrent') }))] }), d.accounts.length === 1 && (_jsx(AccountMeta, { account: d.accounts[0], translate: translate }))] }), _jsx(CodexExhaustionNotice, { display: d, translate: translate }), d.accounts.map((account, accountIndex) => (_jsxs("div", { style: accountIndex === 0 ? undefined : styles.accountBlock, children: [d.accounts.length > 1 && (_jsx("div", { style: styles.accountRow, children: _jsx(AccountMeta, { account: account, translate: translate }) })), _jsx(AccountWindows, { windows: account.windows, observedAt: account.observedAt, stale: account.stale, model: d.provider === dialogProvider ? dialogModel : undefined, provider: d.provider, translate: translate }, `${d.provider}:${dialogModel ?? ''}`)] }, account.key)))] }, d.provider)))] }), document.body)] }));
628
624
  return (_jsxs(_Fragment, { children: [seat, statsRow !== null && statsRow.isConnected
629
625
  ? createPortal(pill, statsRow)
630
626
  : pill] }));
@@ -647,6 +643,15 @@ function statsScopeOf(seat) {
647
643
  }
648
644
  return seat.parentElement;
649
645
  }
646
+ /** Warn only when every displayed Codex account has a known exhausted window. */
647
+ export function CodexExhaustionNotice({ display, translate, }) {
648
+ if (display.provider !== 'codex' ||
649
+ display.accounts.length === 0 ||
650
+ display.accounts.some((account) => account.windows.length === 0 ||
651
+ !account.windows.some((window) => window.usedPercent >= 100)))
652
+ return null;
653
+ return (_jsx("p", { role: "status", style: { ...styles.providerMeta, margin: '8px 0 0' }, children: translate('usageBadgeAllCodexExhausted') }));
654
+ }
650
655
  /**
651
656
  * One account's handle and plan: `★ ys@example.com · 计划:pro`. The star
652
657
  * marks the default account (the one direct routes serve and the collapsed
@@ -661,8 +666,10 @@ function AccountMeta({ account, translate, }) {
661
666
  ].filter((part) => part !== undefined && part !== '');
662
667
  return (_jsxs("span", { style: styles.providerMeta, title: account.account, children: [account.isDefault && (_jsxs("span", { style: styles.defaultStar, "aria-label": "default", children: ["\u2605", ' '] })), parts.join(' · ')] }));
663
668
  }
664
- /** Preview each account independently; all remaining quotas stay accessible. */
669
+ /** Preview each account independently; remaining visible quotas stay accessible. */
665
670
  export function AccountWindows({ windows, model, provider, translate, observedAt, stale, }) {
671
+ if (windows.length === 0)
672
+ return (_jsx("p", { style: styles.providerMeta, children: translate('usageNoVisibleModels') }));
666
673
  const { shown, hidden } = previewWindows(windows, model, provider);
667
674
  const rows = (items) => (_jsx("dl", { style: styles.details, children: items.map((w, i) => (_jsx(WindowRow, { label: `${usageWindowLabel(translate, w)}${model !== undefined && w.scope === model ? ` · ${translate('usageBadgeCurrent')}` : ''}`, window: w, t: translate, observedAt: observedAt, stale: stale }, i))) }));
668
675
  return (_jsxs(_Fragment, { children: [rows(shown), hidden.length > 0 && (_jsxs("details", { style: styles.moreWindows, children: [_jsx("summary", { style: styles.moreSummary, children: translate('usageBadgeMoreWindows', { count: hidden.length }) }), rows(hidden)] }))] }));
@@ -19,7 +19,6 @@ import { visibleUsageWindows } from './usage-window-filter.js';
19
19
  import { ProviderAccountManager } from './ProviderAccountManager.js';
20
20
  import { providerSettingsCss } from './provider-settings-styles.js';
21
21
  import { ExternalUsageCards } from './ExternalUsageCards.js';
22
- import { CursorCard } from './CursorCard.js';
23
22
  import { OllamaCard } from './OllamaCard.js';
24
23
  import { UsageMeter } from './UsageMeter.js';
25
24
  import { ResetCreditsDisclosure, ResetCreditsErrorLine, showsResetCredits, } from './reset-credits-view.js';
@@ -720,7 +719,7 @@ export function SubscriptionsSection(props) {
720
719
  })) }), _jsx("button", { type: "button", onClick: () => {
721
720
  void submitManual(id);
722
721
  }, children: t('submit') })] })] }))] }, id));
723
- }), _jsx(CursorCard, { rpc: rpc, t: t }), _jsx(OllamaCard, { rpc: rpc, t: t }), _jsx(ExternalUsageCards, { rpc: rpc, t: t }), managedProvider && (_jsx(ProviderAccountManager, { provider: managedProvider.id, name: managedProvider.name, rpc: rpc, t: t, onClose: () => {
722
+ }), _jsx(OllamaCard, { rpc: rpc, t: t }), _jsx(ExternalUsageCards, { rpc: rpc, t: t }), managedProvider && (_jsx(ProviderAccountManager, { provider: managedProvider.id, name: managedProvider.name, rpc: rpc, t: t, onClose: () => {
724
723
  setManagedProvider(undefined);
725
724
  void refresh();
726
725
  } })), _jsx("style", { children: '.subscriptions-reset-dialog::backdrop { background: var(--dsw-alias-bg-mask-1, #0006); backdrop-filter: var(--dsw-mask-blur, none); }' }), _jsx("dialog", { ref: resetDialogRef, className: "subscriptions-reset-dialog", "aria-labelledby": "subscriptions-reset-title", onCancel: () => setResetConfirmation(undefined), onClose: () => setResetConfirmation(undefined), style: {
@@ -148,22 +148,6 @@ export declare const en: {
148
148
  externalUsageApiKey: string;
149
149
  externalUsageConfigure: string;
150
150
  externalUsageUnavailable: string;
151
- cursorTitle: string;
152
- cursorOpenLogin: string;
153
- cursorWaiting: string;
154
- cursorConnected: string;
155
- cursorManageTitle: string;
156
- cursorManageSignIn: string;
157
- cursorAccountsHint: string;
158
- cursorManageHint: string;
159
- cursorFilterModels: string;
160
- cursorModelsLoading: string;
161
- cursorModelsError: string;
162
- cursorModelsCount: string;
163
- cursorIncluded: string;
164
- cursorModels: string;
165
- cursorOtherModels: string;
166
- cursorIncludedRequests: string;
167
151
  modelDefaultsTitle: string;
168
152
  modelDefaultsHint: string;
169
153
  modelDefaultsFollowProvider: string;
@@ -204,6 +188,7 @@ export declare const en: {
204
188
  usageBadgeModelCount: string;
205
189
  usageBadgeModelUnavailable: string;
206
190
  usageBadgeMoreWindows: string;
191
+ usageBadgeAllCodexExhausted: string;
207
192
  speed: string;
208
193
  speedStandard: string;
209
194
  speedStandardDescription: string;
@@ -394,22 +379,6 @@ export declare const zh: {
394
379
  externalUsageApiKey: string;
395
380
  externalUsageConfigure: string;
396
381
  externalUsageUnavailable: string;
397
- cursorTitle: string;
398
- cursorOpenLogin: string;
399
- cursorWaiting: string;
400
- cursorConnected: string;
401
- cursorManageTitle: string;
402
- cursorManageSignIn: string;
403
- cursorAccountsHint: string;
404
- cursorManageHint: string;
405
- cursorFilterModels: string;
406
- cursorModelsLoading: string;
407
- cursorModelsError: string;
408
- cursorModelsCount: string;
409
- cursorIncluded: string;
410
- cursorModels: string;
411
- cursorOtherModels: string;
412
- cursorIncludedRequests: string;
413
382
  modelDefaultsTitle: string;
414
383
  modelDefaultsHint: string;
415
384
  modelDefaultsFollowProvider: string;
@@ -450,6 +419,7 @@ export declare const zh: {
450
419
  usageBadgeModelCount: string;
451
420
  usageBadgeModelUnavailable: string;
452
421
  usageBadgeMoreWindows: string;
422
+ usageBadgeAllCodexExhausted: string;
453
423
  speed: string;
454
424
  speedStandard: string;
455
425
  speedStandardDescription: string;
@@ -148,22 +148,6 @@ export const en = {
148
148
  externalUsageApiKey: 'API key',
149
149
  externalUsageConfigure: 'No API key found at {ref}. Add one in Settings → Models.',
150
150
  externalUsageUnavailable: 'Usage status unavailable: {message}',
151
- cursorTitle: 'Cursor',
152
- cursorOpenLogin: 'Open Cursor sign-in',
153
- cursorWaiting: 'Waiting for Cursor sign-in…',
154
- cursorConnected: 'Connected',
155
- cursorManageTitle: 'Manage Cursor',
156
- cursorManageSignIn: 'Sign in to see the models available to this Cursor account.',
157
- cursorAccountsHint: 'Cursor uses one connected account. Choose which of its models appear in the model picker.',
158
- cursorManageHint: 'Choose model visibility here. Refresh fetches the current models from Cursor; Save changes updates the model picker.',
159
- cursorFilterModels: 'Filter models',
160
- cursorModelsLoading: 'Loading Cursor models…',
161
- cursorModelsError: 'Could not load Cursor models: {message}',
162
- cursorModelsCount: '{count} models available',
163
- cursorIncluded: 'Included usage',
164
- cursorModels: 'Cursor Models',
165
- cursorOtherModels: 'Other Models',
166
- cursorIncludedRequests: 'Included requests',
167
151
  modelDefaultsTitle: 'Default reasoning effort',
168
152
  modelDefaultsHint: 'The model picker preselects this level when you switch to the model; models without levels always follow the provider.',
169
153
  modelDefaultsFollowProvider: 'Follow provider',
@@ -204,6 +188,7 @@ export const en = {
204
188
  usageBadgeModelCount: '{count} model quotas',
205
189
  usageBadgeModelUnavailable: 'Current model quota unavailable',
206
190
  usageBadgeMoreWindows: 'More quota windows ({count})',
191
+ usageBadgeAllCodexExhausted: 'All displayed Codex accounts report exhausted subscription quota. New requests stop before spending paid credits. After an external reset, use Settings > Subscriptions > Refresh to re-check usage.',
207
192
  speed: 'Speed',
208
193
  speedStandard: 'Standard',
209
194
  speedStandardDescription: 'Default speed',
@@ -402,22 +387,6 @@ export const zh = {
402
387
  externalUsageApiKey: 'API 密钥',
403
388
  externalUsageConfigure: '未在 {ref} 找到 API 密钥。请在「设置 → 模型」中添加。',
404
389
  externalUsageUnavailable: '无法获取用量状态:{message}',
405
- cursorTitle: 'Cursor',
406
- cursorOpenLogin: '打开 Cursor 登录页面',
407
- cursorWaiting: '等待 Cursor 登录…',
408
- cursorConnected: '已连接',
409
- cursorManageTitle: '管理 Cursor',
410
- cursorManageSignIn: '登录后可查看此 Cursor 账号可用的模型。',
411
- cursorAccountsHint: 'Cursor 使用一个已连接账号。可在此选择模型选择器中显示的模型。',
412
- cursorManageHint: '在此选择模型是否显示。刷新可从 Cursor 获取当前模型;保存更改后更新模型选择器。',
413
- cursorFilterModels: '筛选模型',
414
- cursorModelsLoading: '正在加载 Cursor 模型…',
415
- cursorModelsError: '无法加载 Cursor 模型:{message}',
416
- cursorModelsCount: '可用模型:{count} 个',
417
- cursorIncluded: '套餐内用量',
418
- cursorModels: 'Cursor 模型',
419
- cursorOtherModels: '其他模型',
420
- cursorIncludedRequests: '套餐内请求',
421
390
  modelDefaultsTitle: '默认推理档',
422
391
  modelDefaultsHint: '切换到该模型时,模型选择器会预选此档位;没有推理档的模型始终跟随服务商默认。',
423
392
  modelDefaultsFollowProvider: '跟随服务商',
@@ -458,6 +427,7 @@ export const zh = {
458
427
  usageBadgeModelCount: '{count} 个模型配额',
459
428
  usageBadgeModelUnavailable: '当前模型暂无配额数据',
460
429
  usageBadgeMoreWindows: '更多配额窗口({count})',
430
+ usageBadgeAllCodexExhausted: '当前显示的所有 Codex 账号都报告订阅额度已耗尽。新请求会在消耗付费额度前停止;外部额度重置后,请使用“设置 > 订阅 > 刷新”重新检查用量。',
461
431
  speed: '速度',
462
432
  speedStandard: '标准',
463
433
  speedStandardDescription: '默认速度',
@@ -21,7 +21,7 @@ export function savedModelIds(visibleModels, subagentSpeeds) {
21
21
  * accounts with failed discovery need a retry, not another login prompt.
22
22
  */
23
23
  export function modelEditorNotice(accounts) {
24
- // Account-neutral editors such as Cursor use the neutral empty-catalog copy.
24
+ // Missing account evidence uses the neutral empty-catalog copy.
25
25
  if (accounts === undefined)
26
26
  return 'noModels';
27
27
  if (accounts.length === 0)
@@ -1,3 +1,3 @@
1
1
  import type { CSSProperties } from 'react';
2
- /** Shared visual language for OAuth, Cursor, and built-in provider cards. */
2
+ /** Shared visual language for OAuth and built-in provider cards. */
3
3
  export declare const subscriptionCardStyles: Record<string, CSSProperties>;
@@ -1,4 +1,4 @@
1
- /** Shared visual language for OAuth, Cursor, and built-in provider cards. */
1
+ /** Shared visual language for OAuth and built-in provider cards. */
2
2
  export const subscriptionCardStyles = {
3
3
  card: {
4
4
  border: '1px solid var(--dsw-alias-border-l2)',