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
package/README.md CHANGED
@@ -10,9 +10,9 @@ reported quota in **Settings → Subscriptions**.
10
10
 
11
11
  ## What it adds
12
12
 
13
- - **Six subscription model sources:** Connect ChatGPT/Codex, Claude, Grok,
14
- GitHub Copilot, Google Antigravity, or Cursor and choose their models in DSH's
15
- picker. These routes use account sign-in, without provider API keys.
13
+ - **Five subscription model sources:** Connect ChatGPT/Codex, Claude, Grok,
14
+ GitHub Copilot, or Google Antigravity and choose their models in DSH's picker.
15
+ These routes use account sign-in, without provider API keys.
16
16
  - **Ollama Cloud key route:** Add the `ollama-cloud` route with an
17
17
  `OLLAMA_API_KEY` credential. The route ships no model catalog: configure it on
18
18
  the Settings → Subscriptions card, which fetches the cloud list, ticks the
@@ -46,10 +46,10 @@ reported quota in **Settings → Subscriptions**.
46
46
 
47
47
  ## Screenshots
48
48
 
49
- Captured against DSH 0.2.0-rc.2 in the web UI with a local stub serving frozen
50
- provider responses, so each image shows a product state rather than a real
51
- account. Identities in the images are fabricated, and the quota percentages,
52
- catalogs, and reset countdowns are sample data.
49
+ Captured against DSH 0.2.0-rc.2 and 0.2.1-rc.5 in the web UI with a local stub
50
+ serving frozen provider responses, so each image shows a product state rather
51
+ than a real account. Identities in the images are fabricated, and the quota
52
+ percentages, catalogs, and reset countdowns are sample data.
53
53
 
54
54
  ### Subscriptions
55
55
 
@@ -161,8 +161,8 @@ text. No live account was used to verify what Anthropic returns. See
161
161
 
162
162
  ## Install
163
163
 
164
- Current source is tested with DSH `0.1.7-rc.2`, `0.2.0-rc.2`, `0.2.1-rc.2`, and
165
- `0.2.1-rc.5`. The package's peers admit `>=0.1.7-rc.2 <0.3.0-0`: untested
164
+ Current source is tested with DSH `0.1.7-rc.2`, `0.2.0-rc.2`, `0.2.1-rc.5`, and
165
+ `0.2.2-rc.1`. The package's peers admit `>=0.1.7-rc.2 <0.3.0-0`: untested
166
166
  versions inside that range install but are unsupported, and versions outside it
167
167
  need an explicit `dsh plugin allow-version` exemption. `dsh plugin add` checks
168
168
  those ranges with `includePrerelease` semantics, so the admitted set covers
@@ -224,12 +224,6 @@ the checks and their limits.
224
224
  A manual check in an isolated `0.1.7-rc.2` web profile found the client bundle
225
225
  served and the auth and usage-status RPCs answering. Live sign-in and model
226
226
  requests on the supported DSH lines remain to be checked.
227
- - **Cursor:** Browser sign-in and live model discovery were verified in an
228
- isolated `0.1.7-rc.2` profile. A successful live model request remains to be
229
- verified. Its transport comes from
230
- [orrinzeng/dsh-cursor-subscription](https://github.com/orrinzeng/dsh-cursor-subscription)
231
- and uses an undocumented Cursor protocol; see the
232
- [source note](docs/cursor-origin.md).
233
227
  - **OpenCode Go and Kimi Code:** Live usage responses were verified. Their API
234
228
  keys stay on the DSH host; the browser receives configured status and quota
235
229
  windows, not the keys.
@@ -239,12 +233,6 @@ the checks and their limits.
239
233
  browser test. Live chat, listing, usage, and web calls against `ollama.com`
240
234
  remain to be verified with a real key.
241
235
 
242
- Cursor uses one connected account and a live model catalog. Its model visibility
243
- selection persists under the DSH home directory, and automatic display follows
244
- newly discovered models. It ships no fallback model list and does not import the
245
- local `cursor-agent` credential cache. Individual usage comes from Cursor
246
- dashboard endpoints; the documented Admin API covers team usage.
247
-
248
236
  The plugin follows DSH network settings. Its former proxy configuration and UI
249
237
  have been retired; an old plugin proxy config file is ignored.
250
238
 
package/lib/auth/rpc.d.ts CHANGED
@@ -12,7 +12,6 @@ import { type ProviderId } from './store.js';
12
12
  import type { ProviderUsage } from '../providers/common.js';
13
13
  import type { CliVersion } from '../providers/npm-cli-version.js';
14
14
  import type { ExternalUsageSource, ExternalUsageStatus } from '../providers/external-usage-controller.js';
15
- import type { CursorAuthStatus } from '../providers/cursor-auth.js';
16
15
  /**
17
16
  * Endpoint-name prefix under the shared `/api` channel: endpoint `status`
18
17
  * is served at `/api/subscriptions-auth.status`. The browser half calls
@@ -23,7 +22,7 @@ export declare const SUBSCRIPTIONS_AUTH_PREFIX = "subscriptions-auth.";
23
22
  * Every endpoint {@link dispatch} answers; each gets one exact Fetch route.
24
23
  * Kept in one place so the route table and the switch cannot drift apart.
25
24
  */
26
- export declare const SUBSCRIPTIONS_AUTH_ENDPOINTS: readonly ["providerSettings", "setProviderSettings", "status", "login", "manual", "cancel", "logout", "setDefault", "usage", "externalStatus", "externalUsage", "cursorStatus", "cursorLogin", "cursorCancel", "cursorLogout", "cursorUsage", "cursorModels", "cursorSettings", "cursorSetSettings", "ollamaSettings", "ollamaSetSettings", "ollamaModels", "image", "video", "prepareReset", "consumeReset", "speed", "setSpeed", "modelDefaults", "setModelDefault"];
25
+ export declare const SUBSCRIPTIONS_AUTH_ENDPOINTS: readonly ["providerSettings", "setProviderSettings", "status", "login", "manual", "cancel", "logout", "setDefault", "usage", "externalStatus", "externalUsage", "ollamaSettings", "ollamaSetSettings", "ollamaModels", "image", "video", "prepareReset", "consumeReset", "speed", "setSpeed", "modelDefaults", "setModelDefault"];
27
26
  /** Decoded image bytes returned by the `image` endpoint. */
28
27
  export interface ImageBytesResult {
29
28
  mediaType: string;
@@ -189,22 +188,6 @@ export interface ExternalUsageRpcController {
189
188
  status(): Promise<Record<ExternalUsageSource, ExternalUsageStatus>>;
190
189
  usage(source: ExternalUsageSource, signal: AbortSignal): Promise<ProviderUsage>;
191
190
  }
192
- /** Cursor account actions; OAuth tokens remain in the host credential service. */
193
- export interface CursorRpcController {
194
- status(): Promise<CursorAuthStatus>;
195
- login(): Promise<{
196
- authorizeUrl: string;
197
- }>;
198
- cancel(): Promise<void>;
199
- logout(): Promise<void>;
200
- usage(signal: AbortSignal): Promise<ProviderUsage>;
201
- models(force: boolean, signal: AbortSignal): Promise<{
202
- id: string;
203
- name: string;
204
- }[]>;
205
- settings(force: boolean, signal: AbortSignal): Promise<unknown>;
206
- setSettings(input: unknown): Promise<void>;
207
- }
208
191
  /** One model in the Ollama Cloud picker catalog, with its capability flags. */
209
192
  export interface OllamaModelView {
210
193
  id: string;
@@ -280,7 +263,6 @@ export interface OllamaRpcController {
280
263
  * @param modelDefaults - optional per-model default-effort state backing `modelDefaults`/`setModelDefault`.
281
264
  * @param providerSettings - optional OAuth provider/account preferences.
282
265
  * @param externalUsage - optional credential-backed quota readers.
283
- * @param cursor - optional Cursor account and model-visibility actions.
284
266
  * @param ollama - optional Ollama Cloud catalog and connection settings.
285
267
  */
286
- export declare function registerAuthRpc(ctx: Context, controller: AuthController, speed: SpeedController, modelDefaults?: ModelDefaultsController | undefined, providerSettings?: ProviderSettingsController | undefined, externalUsage?: ExternalUsageRpcController | undefined, cursor?: CursorRpcController | undefined, ollama?: OllamaRpcController | undefined): void;
268
+ export declare function registerAuthRpc(ctx: Context, controller: AuthController, speed: SpeedController, modelDefaults?: ModelDefaultsController | undefined, providerSettings?: ProviderSettingsController | undefined, externalUsage?: ExternalUsageRpcController | undefined, ollama?: OllamaRpcController | undefined): void;
package/lib/auth/rpc.js CHANGED
@@ -32,14 +32,6 @@ export const SUBSCRIPTIONS_AUTH_ENDPOINTS = [
32
32
  'usage',
33
33
  'externalStatus',
34
34
  'externalUsage',
35
- 'cursorStatus',
36
- 'cursorLogin',
37
- 'cursorCancel',
38
- 'cursorLogout',
39
- 'cursorUsage',
40
- 'cursorModels',
41
- 'cursorSettings',
42
- 'cursorSetSettings',
43
35
  'ollamaSettings',
44
36
  'ollamaSetSettings',
45
37
  'ollamaModels',
@@ -284,7 +276,7 @@ function readSessionId(payload) {
284
276
  throw new BadRequest('payload must be an object');
285
277
  return readString(payload, 'sessionId');
286
278
  }
287
- async function dispatch(controller, speed, modelDefaults, endpoint, payload, signal, providerSettings, externalUsage, cursor, ollama) {
279
+ async function dispatch(controller, speed, modelDefaults, endpoint, payload, signal, providerSettings, externalUsage, ollama) {
288
280
  switch (endpoint) {
289
281
  case 'providerSettings':
290
282
  if (!providerSettings)
@@ -361,43 +353,6 @@ async function dispatch(controller, speed, modelDefaults, endpoint, payload, sig
361
353
  }
362
354
  return ok(await externalUsage.usage(source, signal));
363
355
  }
364
- case 'cursorStatus':
365
- if (!cursor)
366
- throw new BadRequest('Cursor account is unavailable');
367
- return ok(await cursor.status());
368
- case 'cursorLogin':
369
- if (!cursor)
370
- throw new BadRequest('Cursor account is unavailable');
371
- return ok(await cursor.login());
372
- case 'cursorCancel':
373
- if (!cursor)
374
- throw new BadRequest('Cursor account is unavailable');
375
- await cursor.cancel();
376
- return ok({ ok: true });
377
- case 'cursorLogout':
378
- if (!cursor)
379
- throw new BadRequest('Cursor account is unavailable');
380
- await cursor.logout();
381
- return ok({ ok: true });
382
- case 'cursorUsage':
383
- if (!cursor)
384
- throw new BadRequest('Cursor account is unavailable');
385
- return ok(await cursor.usage(signal));
386
- case 'cursorModels':
387
- if (!cursor)
388
- throw new BadRequest('Cursor account is unavailable');
389
- return ok(await cursor.models(readForce(payload), signal));
390
- case 'cursorSettings':
391
- if (!cursor)
392
- throw new BadRequest('Cursor account is unavailable');
393
- return ok(await cursor.settings(readForce(payload), signal));
394
- case 'cursorSetSettings':
395
- if (!cursor)
396
- throw new BadRequest('Cursor account is unavailable');
397
- if (payload === null || typeof payload !== 'object')
398
- throw new BadRequest('payload must be an object');
399
- await cursor.setSettings(payload.settings);
400
- return ok({ ok: true });
401
356
  case 'ollamaSettings':
402
357
  if (!ollama)
403
358
  throw new BadRequest('Ollama settings are unavailable');
@@ -475,10 +430,9 @@ async function dispatch(controller, speed, modelDefaults, endpoint, payload, sig
475
430
  * @param modelDefaults - optional per-model default-effort state backing `modelDefaults`/`setModelDefault`.
476
431
  * @param providerSettings - optional OAuth provider/account preferences.
477
432
  * @param externalUsage - optional credential-backed quota readers.
478
- * @param cursor - optional Cursor account and model-visibility actions.
479
433
  * @param ollama - optional Ollama Cloud catalog and connection settings.
480
434
  */
481
- export function registerAuthRpc(ctx, controller, speed, modelDefaults = undefined, providerSettings = undefined, externalUsage = undefined, cursor = undefined, ollama = undefined) {
435
+ export function registerAuthRpc(ctx, controller, speed, modelDefaults = undefined, providerSettings = undefined, externalUsage = undefined, ollama = undefined) {
482
436
  // `connection` is not in this plugin's inject list (headless compositions
483
437
  // lack it), so its startup order is unconstrained: defer registration until
484
438
  // the service exists instead of probing once at apply time.
@@ -495,7 +449,7 @@ export function registerAuthRpc(ctx, controller, speed, modelDefaults = undefine
495
449
  const connection = ctx.get('connection');
496
450
  const handler = async (endpoint, payload, signal) => {
497
451
  try {
498
- return await dispatch(controller, speed, modelDefaults, endpoint, payload, signal, providerSettings, externalUsage, cursor, ollama);
452
+ return await dispatch(controller, speed, modelDefaults, endpoint, payload, signal, providerSettings, externalUsage, ollama);
499
453
  }
500
454
  catch (error) {
501
455
  return failure(error);
@@ -2,7 +2,7 @@ import type { ConnectionHandle } from '@deepseek-ai/dsh-api-remotes/client';
2
2
  import type { SubscriptionProvider } from './SubscriptionsSection.js';
3
3
  import type { SubscriptionsKey } from './locales.js';
4
4
  interface Props {
5
- provider: SubscriptionProvider | 'cursor-subscription';
5
+ provider: SubscriptionProvider;
6
6
  name: string;
7
7
  rpc: ConnectionHandle['rpc'];
8
8
  t: (key: SubscriptionsKey, params?: Record<string, unknown>) => string;
@@ -47,9 +47,7 @@ export function ProviderAccountManager({ provider, name, rpc, t, onClose, }) {
47
47
  let current = true;
48
48
  setLoading(true);
49
49
  setError('');
50
- void callSubscriptionsAuth(rpc, provider === 'cursor-subscription'
51
- ? 'cursorSettings'
52
- : 'providerSettings', { provider })
50
+ void callSubscriptionsAuth(rpc, 'providerSettings', { provider })
53
51
  .then((data) => {
54
52
  if (current) {
55
53
  setCatalog(data);
@@ -94,43 +92,32 @@ export function ProviderAccountManager({ provider, name, rpc, t, onClose, }) {
94
92
  setSaveError('');
95
93
  let savedEfforts = 0;
96
94
  try {
97
- if (provider !== 'cursor-subscription') {
98
- for (const { model, effort } of models.efforts) {
99
- await callSubscriptionsAuth(rpc, 'setModelDefault', {
100
- provider,
101
- model,
102
- ...(effort ? { effort } : {}),
103
- });
104
- savedEfforts++;
105
- if (!alive.current)
106
- return;
107
- }
95
+ for (const { model, effort } of models.efforts) {
96
+ await callSubscriptionsAuth(rpc, 'setModelDefault', {
97
+ provider,
98
+ model,
99
+ ...(effort ? { effort } : {}),
100
+ });
101
+ savedEfforts++;
102
+ if (!alive.current)
103
+ return;
108
104
  }
109
105
  // Re-read before writing: another client may have saved since this
110
106
  // dialog loaded, and a stale snapshot must never be written back.
111
- const latest = await callSubscriptionsAuth(rpc, provider === 'cursor-subscription'
112
- ? 'cursorSettings'
113
- : 'providerSettings', { provider });
107
+ const latest = await callSubscriptionsAuth(rpc, 'providerSettings', { provider });
114
108
  if (!alive.current)
115
109
  return;
116
- if (provider === 'cursor-subscription') {
117
- await callSubscriptionsAuth(rpc, 'cursorSetSettings', {
118
- settings: models.settings ?? latest.settings,
119
- });
120
- }
121
- else {
122
- const base = models.settings === undefined
123
- ? latest.settings
124
- : mergeLatestAccounts(models.settings, latest.settings);
125
- // The editor's draft carries the TTL it loaded, so the dialog always
126
- // replaces it: the picker's choice when touched, otherwise the value
127
- // from the freshly re-read settings.
128
- const withTtl = mergePromptCacheTtl(base, ttlDirty ? ttlDraft : latest.settings.promptCacheTtl);
129
- await callSubscriptionsAuth(rpc, 'setProviderSettings', {
130
- provider,
131
- settings: mergeAccountChanges(withTtl, changes),
132
- });
133
- }
110
+ const base = models.settings === undefined
111
+ ? latest.settings
112
+ : mergeLatestAccounts(models.settings, latest.settings);
113
+ // The editor's draft carries the TTL it loaded, so the dialog always
114
+ // replaces it: the picker's choice when touched, otherwise the value
115
+ // from the freshly re-read settings.
116
+ const withTtl = mergePromptCacheTtl(base, ttlDirty ? ttlDraft : latest.settings.promptCacheTtl);
117
+ await callSubscriptionsAuth(rpc, 'setProviderSettings', {
118
+ provider,
119
+ settings: mergeAccountChanges(withTtl, changes),
120
+ });
134
121
  if (alive.current)
135
122
  onClose();
136
123
  }
@@ -155,81 +142,70 @@ export function ProviderAccountManager({ provider, name, rpc, t, onClose, }) {
155
142
  maxWidth: 'calc(100vw - 32px)',
156
143
  maxHeight: 'calc(100dvh - 32px)',
157
144
  overflow: 'auto',
158
- }, children: [_jsx("style", { children: providerSettingsCss }), _jsxs("div", { style: { ...stack, padding: 20 }, children: [_jsxs("header", { style: { ...actions, justifyContent: 'space-between' }, children: [_jsx("h2", { id: title, style: { margin: 0 }, children: t('accountsTitle', { provider: name }) }), _jsx("button", { type: "button", autoFocus: true, disabled: saving, onClick: onClose, children: t('imageClose') })] }), _jsx("p", { id: description, className: "dsh-subscription-hint", children: t(provider === 'cursor-subscription'
159
- ? 'cursorAccountsHint'
160
- : 'accountsHint') }), error && _jsx("p", { role: "alert", children: error }), loading && (_jsx("p", { role: "status", className: "dsh-subscription-hint", children: t('accountsLoading') })), !loading && !catalog && (_jsx("button", { type: "button", onClick: () => setAttempt((value) => value + 1), children: t('modelDefaultsRetry') })), catalog && (_jsxs("fieldset", { disabled: saving || loading, style: { ...stack, border: 0, margin: 0, padding: 0 }, children: [catalog.accounts.length === 0 && (_jsx("p", { className: "dsh-subscription-hint", children: t(provider === 'cursor-subscription'
161
- ? 'cursorManageSignIn'
162
- : 'accountsEmpty') })), provider === 'cursor-subscription' &&
163
- catalog.accounts.length > 0 && (_jsxs("fieldset", { style: {
164
- border,
165
- borderRadius: 'var(--dsw-radius-lg, 12px)',
166
- padding: 14,
167
- }, children: [_jsx("legend", { style: { padding: '0 6px' }, children: name }), _jsx("p", { className: "dsh-subscription-hint", children: t('cursorConnected') })] })), provider !== 'cursor-subscription' &&
168
- catalog.accounts.map((account) => {
169
- const preferences = changes[account.key] ??
170
- catalog.settings.accounts?.[account.key] ??
171
- {};
172
- const selected = accountPoolSelection(preferences, account.models);
173
- const models = accountModelRows(account, preferences);
174
- return (_jsxs("fieldset", { style: {
175
- ...stack,
176
- border,
177
- borderRadius: 'var(--dsw-radius-lg, 12px)',
178
- padding: 14,
179
- margin: 0,
180
- }, children: [_jsx("legend", { style: {
181
- padding: '0 6px',
182
- overflowWrap: 'anywhere',
183
- maxWidth: '100%',
184
- }, children: account.label }), account.unavailable && (_jsx("p", { className: "dsh-subscription-hint", children: t('accountsCatalogUnavailable') })), _jsxs("label", { style: { ...stack, gap: 6 }, children: [_jsx("span", { className: "dsh-subscription-label", children: t('accountsAlias') }), _jsx("input", { value: preferences.alias ?? '', placeholder: account.label, onChange: (event) => {
185
- const next = { ...preferences };
186
- if (event.target.value.trim())
187
- next.alias = event.target.value;
188
- else
189
- delete next.alias;
190
- edit(account.key, next);
191
- } })] }), _jsxs("label", { children: [_jsx("input", { type: "checkbox", checked: preferences.poolEnabled !== false, onChange: (event) => edit(account.key, {
192
- ...preferences,
193
- poolEnabled: event.target.checked,
194
- }) }), ' ', t('accountsPoolEnabled')] }), _jsxs("label", { children: [_jsx("input", { type: "checkbox", checked: preferences.independentEntry === true, onChange: (event) => edit(account.key, {
195
- ...preferences,
196
- independentEntry: event.target.checked,
197
- }) }), ' ', t('accountsIndependent')] }), _jsx("p", { className: "dsh-subscription-hint", children: t('accountsIndependentHint') }), _jsxs("details", { children: [_jsxs("summary", { style: { cursor: 'pointer', fontSize: 13 }, children: [t('accountsModels'), " \u00B7", ' ', preferences.poolModels === undefined
198
- ? t('accountsModelsAll')
199
- : t('accountsModelsCount', {
200
- count: preferences.poolModels.length,
201
- })] }), _jsxs("div", { style: { ...stack, marginTop: 12 }, children: [_jsx("p", { className: "dsh-subscription-hint", children: t('accountsModelsHint') }), _jsxs("label", { children: [_jsx("input", { type: "checkbox", checked: preferences.poolModels === undefined, onChange: (event) => {
202
- const next = { ...preferences };
203
- if (event.target.checked)
204
- delete next.poolModels;
205
- else
206
- next.poolModels = account.models.map((model) => model.id);
207
- edit(account.key, next);
208
- } }), ' ', t('accountsModelsAutomatic')] }), _jsxs("div", { style: actions, children: [_jsx("button", { type: "button", onClick: () => edit(account.key, {
209
- ...preferences,
210
- poolModels: models.map((model) => model.id),
211
- }), children: t('modelsSelectAll') }), _jsx("button", { type: "button", onClick: () => edit(account.key, {
212
- ...preferences,
213
- poolModels: [],
214
- }), children: t('modelsSelectNone') })] }), _jsxs("div", { style: {
215
- ...stack,
216
- gap: 8,
217
- maxHeight: 240,
218
- overflowY: 'auto',
219
- }, children: [models.map((model) => (_jsxs("label", { style: { overflowWrap: 'anywhere' }, children: [_jsx("input", { type: "checkbox", checked: selected.has(model.id), onChange: (event) => {
220
- const next = new Set(selected);
221
- if (event.target.checked)
222
- next.add(model.id);
223
- else
224
- next.delete(model.id);
225
- edit(account.key, {
226
- ...preferences,
227
- poolModels: [...next],
228
- });
229
- } }), ' ', model.name, model.unavailable &&
230
- ` (${t('modelsUnavailable')})`] }, model.id))), models.length === 0 && (_jsx("p", { className: "dsh-subscription-hint", children: t('accountsNoModels') }))] })] })] })] }, account.key));
231
- })] })), (provider !== 'cursor-subscription' ||
232
- (catalog?.accounts.length ?? 0) > 0) && (_jsx(ProviderModelEditor, { ref: editor, provider: provider, rpc: rpc, t: t, disabled: saving, onDirtyChange: setModelsDirty })), provider === 'claude' && catalog && (_jsxs("div", { style: {
145
+ }, children: [_jsx("style", { children: providerSettingsCss }), _jsxs("div", { style: { ...stack, padding: 20 }, children: [_jsxs("header", { style: { ...actions, justifyContent: 'space-between' }, children: [_jsx("h2", { id: title, style: { margin: 0 }, children: t('accountsTitle', { provider: name }) }), _jsx("button", { type: "button", autoFocus: true, disabled: saving, onClick: onClose, children: t('imageClose') })] }), _jsx("p", { id: description, className: "dsh-subscription-hint", children: t('accountsHint') }), error && _jsx("p", { role: "alert", children: error }), loading && (_jsx("p", { role: "status", className: "dsh-subscription-hint", children: t('accountsLoading') })), !loading && !catalog && (_jsx("button", { type: "button", onClick: () => setAttempt((value) => value + 1), children: t('modelDefaultsRetry') })), catalog && (_jsxs("fieldset", { disabled: saving || loading, style: { ...stack, border: 0, margin: 0, padding: 0 }, children: [catalog.accounts.length === 0 && (_jsx("p", { className: "dsh-subscription-hint", children: t('accountsEmpty') })), catalog.accounts.map((account) => {
146
+ const preferences = changes[account.key] ??
147
+ catalog.settings.accounts?.[account.key] ??
148
+ {};
149
+ const selected = accountPoolSelection(preferences, account.models);
150
+ const models = accountModelRows(account, preferences);
151
+ return (_jsxs("fieldset", { style: {
152
+ ...stack,
153
+ border,
154
+ borderRadius: 'var(--dsw-radius-lg, 12px)',
155
+ padding: 14,
156
+ margin: 0,
157
+ }, children: [_jsx("legend", { style: {
158
+ padding: '0 6px',
159
+ overflowWrap: 'anywhere',
160
+ maxWidth: '100%',
161
+ }, children: account.label }), account.unavailable && (_jsx("p", { className: "dsh-subscription-hint", children: t('accountsCatalogUnavailable') })), _jsxs("label", { style: { ...stack, gap: 6 }, children: [_jsx("span", { className: "dsh-subscription-label", children: t('accountsAlias') }), _jsx("input", { value: preferences.alias ?? '', placeholder: account.label, onChange: (event) => {
162
+ const next = { ...preferences };
163
+ if (event.target.value.trim())
164
+ next.alias = event.target.value;
165
+ else
166
+ delete next.alias;
167
+ edit(account.key, next);
168
+ } })] }), _jsxs("label", { children: [_jsx("input", { type: "checkbox", checked: preferences.poolEnabled !== false, onChange: (event) => edit(account.key, {
169
+ ...preferences,
170
+ poolEnabled: event.target.checked,
171
+ }) }), ' ', t('accountsPoolEnabled')] }), _jsxs("label", { children: [_jsx("input", { type: "checkbox", checked: preferences.independentEntry === true, onChange: (event) => edit(account.key, {
172
+ ...preferences,
173
+ independentEntry: event.target.checked,
174
+ }) }), ' ', t('accountsIndependent')] }), _jsx("p", { className: "dsh-subscription-hint", children: t('accountsIndependentHint') }), _jsxs("details", { children: [_jsxs("summary", { style: { cursor: 'pointer', fontSize: 13 }, children: [t('accountsModels'), " \u00B7", ' ', preferences.poolModels === undefined
175
+ ? t('accountsModelsAll')
176
+ : t('accountsModelsCount', {
177
+ count: preferences.poolModels.length,
178
+ })] }), _jsxs("div", { style: { ...stack, marginTop: 12 }, children: [_jsx("p", { className: "dsh-subscription-hint", children: t('accountsModelsHint') }), _jsxs("label", { children: [_jsx("input", { type: "checkbox", checked: preferences.poolModels === undefined, onChange: (event) => {
179
+ const next = { ...preferences };
180
+ if (event.target.checked)
181
+ delete next.poolModels;
182
+ else
183
+ next.poolModels = account.models.map((model) => model.id);
184
+ edit(account.key, next);
185
+ } }), ' ', t('accountsModelsAutomatic')] }), _jsxs("div", { style: actions, children: [_jsx("button", { type: "button", onClick: () => edit(account.key, {
186
+ ...preferences,
187
+ poolModels: models.map((model) => model.id),
188
+ }), children: t('modelsSelectAll') }), _jsx("button", { type: "button", onClick: () => edit(account.key, {
189
+ ...preferences,
190
+ poolModels: [],
191
+ }), children: t('modelsSelectNone') })] }), _jsxs("div", { style: {
192
+ ...stack,
193
+ gap: 8,
194
+ maxHeight: 240,
195
+ overflowY: 'auto',
196
+ }, children: [models.map((model) => (_jsxs("label", { style: { overflowWrap: 'anywhere' }, children: [_jsx("input", { type: "checkbox", checked: selected.has(model.id), onChange: (event) => {
197
+ const next = new Set(selected);
198
+ if (event.target.checked)
199
+ next.add(model.id);
200
+ else
201
+ next.delete(model.id);
202
+ edit(account.key, {
203
+ ...preferences,
204
+ poolModels: [...next],
205
+ });
206
+ } }), ' ', model.name, model.unavailable &&
207
+ ` (${t('modelsUnavailable')})`] }, model.id))), models.length === 0 && (_jsx("p", { className: "dsh-subscription-hint", children: t('accountsNoModels') }))] })] })] })] }, account.key));
208
+ })] })), _jsx(ProviderModelEditor, { ref: editor, provider: provider, rpc: rpc, t: t, disabled: saving, onDirtyChange: setModelsDirty }), provider === 'claude' && catalog && (_jsxs("div", { style: {
233
209
  ...stack,
234
210
  gap: 6,
235
211
  borderTop: border,
@@ -3,7 +3,7 @@ import type { ProviderPreferences } from '../provider-settings.js';
3
3
  import type { SubscriptionProvider } from './SubscriptionsSection.js';
4
4
  import type { SubscriptionsKey } from './locales.js';
5
5
  interface Props {
6
- provider: SubscriptionProvider | 'cursor-subscription';
6
+ provider: SubscriptionProvider;
7
7
  rpc: ConnectionHandle['rpc'];
8
8
  t: (key: SubscriptionsKey, params?: Record<string, unknown>) => string;
9
9
  /** Freeze the editor while the owning dialog submits. */
@@ -48,9 +48,7 @@ export const ProviderModelEditor = forwardRef(function ProviderModelEditor({ pro
48
48
  setBusy(true);
49
49
  setError('');
50
50
  try {
51
- const data = await callSubscriptionsAuth(rpc, provider === 'cursor-subscription'
52
- ? 'cursorSettings'
53
- : 'providerSettings', { provider, force });
51
+ const data = await callSubscriptionsAuth(rpc, 'providerSettings', { provider, force });
54
52
  if (generation.current === request)
55
53
  reset(data);
56
54
  }
@@ -124,9 +122,7 @@ export const ProviderModelEditor = forwardRef(function ProviderModelEditor({ pro
124
122
  });
125
123
  const selected = new Set(draft.visibleModels ??
126
124
  [...allModels, ...missing].map((model) => model.id));
127
- return (_jsxs("div", { style: { borderTop: border, marginTop: 12, paddingTop: 12 }, children: [_jsx("h3", { style: { margin: 0 }, children: t('modelsEdit') }), _jsxs("div", { style: { display: 'grid', gap: 12, marginTop: 12 }, children: [_jsx("p", { className: "dsh-subscription-hint", children: t(provider === 'cursor-subscription'
128
- ? 'cursorManageHint'
129
- : 'modelsHint') }), error && _jsx("p", { role: "alert", children: error }), _jsxs("div", { style: actions, children: [_jsx("button", { type: "button", className: "dsh-subscription-quiet", disabled: busy || disabled || dirty, onClick: () => {
125
+ return (_jsxs("div", { style: { borderTop: border, marginTop: 12, paddingTop: 12 }, children: [_jsx("h3", { style: { margin: 0 }, children: t('modelsEdit') }), _jsxs("div", { style: { display: 'grid', gap: 12, marginTop: 12 }, children: [_jsx("p", { className: "dsh-subscription-hint", children: t('modelsHint') }), error && _jsx("p", { role: "alert", children: error }), _jsxs("div", { style: actions, children: [_jsx("button", { type: "button", className: "dsh-subscription-quiet", disabled: busy || disabled || dirty, onClick: () => {
130
126
  void load(true);
131
127
  }, children: t('usageRefresh') }), busy && _jsx("span", { role: "status", children: t('modelDefaultsLoading') })] }), !busy && catalog && (_jsxs("fieldset", { disabled: busy || disabled, style: {
132
128
  border: 0,
@@ -48,7 +48,7 @@ export interface ProviderUsageDisplay {
48
48
  /** The account the collapsed pill reads: the default one, else the first listed. */
49
49
  export declare function pillAccountOf(d: ProviderUsageDisplay): AccountUsageDisplay;
50
50
  /** Brand display names (short form for the compact badge). */
51
- export type BadgeProvider = SubscriptionProvider | 'cursor-subscription' | 'opencode-go' | 'kimi-coding' | 'minimax' | 'minimax-cn' | 'ollama-cloud';
51
+ export type BadgeProvider = SubscriptionProvider | 'opencode-go' | 'kimi-coding' | 'minimax' | 'minimax-cn' | 'ollama-cloud';
52
52
  export interface UsageRosterEntry {
53
53
  provider: BadgeProvider;
54
54
  account: AccountStatus;
@@ -68,6 +68,7 @@ export declare function groupUsageDisplays(roster: readonly UsageRosterEntry[],
68
68
  export declare function loadBadgeRoster(rpc: ConnectionHandle['rpc']): Promise<{
69
69
  roster: UsageRosterEntry[];
70
70
  refreshed: Set<BadgeProvider>;
71
+ visibleModels: Map<BadgeProvider, string[]>;
71
72
  }>;
72
73
  export declare function usageOf(rpc: ConnectionHandle['rpc'], { provider, account }: UsageRosterEntry): Promise<ProviderUsage>;
73
74
  /**
@@ -119,7 +120,12 @@ export declare function rotatingDisplay(displays: readonly ProviderUsageDisplay[
119
120
  export declare function expandedDisplays(displays: readonly ProviderUsageDisplay[], current: string | undefined): readonly ProviderUsageDisplay[];
120
121
  type Translate = (key: SubscriptionsKey, params?: Record<string, unknown>) => string;
121
122
  export declare function SubscriptionUsageBadge({ rpc, currentModel, t, rotationMs, }: SubscriptionUsageBadgeProps): import("react").JSX.Element;
122
- /** Preview each account independently; all remaining quotas stay accessible. */
123
+ /** Warn only when every displayed Codex account has a known exhausted window. */
124
+ export declare function CodexExhaustionNotice({ display, translate, }: {
125
+ display: ProviderUsageDisplay;
126
+ translate: Translate;
127
+ }): import("react").JSX.Element | null;
128
+ /** Preview each account independently; remaining visible quotas stay accessible. */
123
129
  export declare function AccountWindows({ windows, model, provider, translate, observedAt, stale, }: {
124
130
  windows: readonly UsageWindow[];
125
131
  model: string | undefined;