dsh-subscriptions 0.1.5 → 0.2.2

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 (110) hide show
  1. package/README.md +212 -154
  2. package/lib/auth/claude-code-creds.js +31 -9
  3. package/lib/auth/device-flow.js +16 -9
  4. package/lib/auth/oauth-flow.js +13 -10
  5. package/lib/auth/rpc.d.ts +73 -2
  6. package/lib/auth/rpc.js +124 -23
  7. package/lib/auth/store.js +80 -31
  8. package/lib/client/CursorCard.d.ts +1 -1
  9. package/lib/client/CursorCard.js +50 -16
  10. package/lib/client/ExternalUsageCards.d.ts +1 -1
  11. package/lib/client/ExternalUsageCards.js +37 -16
  12. package/lib/client/ImageGallery.d.ts +2 -2
  13. package/lib/client/ImageGallery.js +92 -33
  14. package/lib/client/ImageGenerateToolview.js +50 -14
  15. package/lib/client/OllamaCard.d.ts +13 -0
  16. package/lib/client/OllamaCard.js +94 -0
  17. package/lib/client/OllamaSettingsManager.d.ts +24 -0
  18. package/lib/client/OllamaSettingsManager.js +329 -0
  19. package/lib/client/ProviderAccountManager.d.ts +1 -1
  20. package/lib/client/ProviderAccountManager.js +164 -49
  21. package/lib/client/ProviderModelEditor.js +109 -26
  22. package/lib/client/SpeedSelect.d.ts +62 -14
  23. package/lib/client/SpeedSelect.js +257 -46
  24. package/lib/client/SubscriptionUsageBadge.d.ts +5 -11
  25. package/lib/client/SubscriptionUsageBadge.js +301 -112
  26. package/lib/client/SubscriptionsSection.d.ts +2 -0
  27. package/lib/client/SubscriptionsSection.js +259 -85
  28. package/lib/client/UsageBadgeDisplaySetting.js +35 -12
  29. package/lib/client/UsageMeter.d.ts +1 -1
  30. package/lib/client/UsageMeter.js +69 -27
  31. package/lib/client/VideoGenerateToolview.js +69 -17
  32. package/lib/client/account-preferences.d.ts +8 -0
  33. package/lib/client/account-preferences.js +20 -5
  34. package/lib/client/host-contract.d.ts +11 -2
  35. package/lib/client/host-contract.js +20 -3
  36. package/lib/client/index.d.ts +5 -5
  37. package/lib/client/index.js +34 -12
  38. package/lib/client/locales.d.ts +78 -0
  39. package/lib/client/locales.js +121 -34
  40. package/lib/client/ollama-draft.d.ts +229 -0
  41. package/lib/client/ollama-draft.js +255 -0
  42. package/lib/client/reset-action.js +6 -4
  43. package/lib/client/reset-credits-view.d.ts +1 -1
  44. package/lib/client/reset-credits-view.js +66 -17
  45. package/lib/client/subscription-card-styles.js +94 -24
  46. package/lib/client/usage-color-preferences.js +4 -2
  47. package/lib/client/usage-pace.d.ts +7 -0
  48. package/lib/client/usage-pace.js +43 -10
  49. package/lib/client.js +1647 -266
  50. package/lib/client.js.map +1 -1
  51. package/lib/http.js +1 -1
  52. package/lib/index.d.ts +28 -3
  53. package/lib/index.js +9329 -8217
  54. package/lib/model-defaults.js +4 -2
  55. package/lib/provider-settings.d.ts +14 -1
  56. package/lib/provider-settings.js +64 -20
  57. package/lib/providers/account-preferences.js +51 -22
  58. package/lib/providers/accounts.js +15 -9
  59. package/lib/providers/antigravity-oauth-client.js +2 -2
  60. package/lib/providers/antigravity.js +141 -68
  61. package/lib/providers/catalog-store.js +120 -41
  62. package/lib/providers/claude-cli-version.js +3 -3
  63. package/lib/providers/claude.d.ts +23 -3
  64. package/lib/providers/claude.js +167 -92
  65. package/lib/providers/codex-client-version.js +3 -3
  66. package/lib/providers/codex-search.js +40 -13
  67. package/lib/providers/codex.js +196 -112
  68. package/lib/providers/common.d.ts +14 -0
  69. package/lib/providers/common.js +118 -38
  70. package/lib/providers/copilot.js +143 -82
  71. package/lib/providers/cursor-adapter.js +42 -18
  72. package/lib/providers/cursor-auth.d.ts +1 -1
  73. package/lib/providers/cursor-auth.js +69 -24
  74. package/lib/providers/cursor-model-settings.js +14 -5
  75. package/lib/providers/cursor-usage.js +27 -13
  76. package/lib/providers/external-usage-controller.d.ts +9 -2
  77. package/lib/providers/external-usage-controller.js +30 -7
  78. package/lib/providers/external-usage.d.ts +8 -0
  79. package/lib/providers/external-usage.js +140 -20
  80. package/lib/providers/grok.js +152 -89
  81. package/lib/providers/image-pool.js +29 -11
  82. package/lib/providers/minimax-usage.js +31 -11
  83. package/lib/providers/npm-cli-version.js +10 -3
  84. package/lib/providers/ollama-settings.d.ts +90 -0
  85. package/lib/providers/ollama-settings.js +213 -0
  86. package/lib/providers/ollama-web.d.ts +46 -0
  87. package/lib/providers/ollama-web.js +231 -0
  88. package/lib/providers/ollama.d.ts +107 -0
  89. package/lib/providers/ollama.js +608 -0
  90. package/lib/providers/pool-family.js +11 -3
  91. package/lib/providers/pool-health.js +28 -8
  92. package/lib/providers/pool-usage.js +16 -9
  93. package/lib/providers/pool.d.ts +8 -0
  94. package/lib/providers/pool.js +81 -42
  95. package/lib/providers/rate-limit.js +8 -2
  96. package/lib/providers/replay.js +40 -17
  97. package/lib/providers/reset-redemption.js +18 -6
  98. package/lib/tools/image-generate.js +123 -65
  99. package/lib/tools/registration.js +5 -2
  100. package/lib/tools/video-generate.js +51 -31
  101. package/lib/tools/x-search.js +50 -22
  102. package/lib/translate/anthropic.js +85 -37
  103. package/lib/translate/antigravity-schema.js +71 -28
  104. package/lib/translate/antigravity-thinking.js +22 -9
  105. package/lib/translate/antigravity.js +141 -50
  106. package/lib/translate/chat-completions.js +67 -23
  107. package/lib/translate/resolved.js +55 -21
  108. package/lib/translate/responses.js +69 -29
  109. package/lib/translate/sse.js +4 -1
  110. package/package.json +26 -4
@@ -25,7 +25,9 @@ export function modelsFilePath() {
25
25
  /** The per-account section of the file, or undefined when absent or malformed. */
26
26
  function accountSection(store, provider) {
27
27
  const accounts = store.accounts;
28
- if (typeof accounts !== 'object' || accounts === null || Array.isArray(accounts))
28
+ if (typeof accounts !== 'object' ||
29
+ accounts === null ||
30
+ Array.isArray(accounts))
29
31
  return undefined;
30
32
  const section = accounts[provider];
31
33
  if (typeof section !== 'object' || section === null || Array.isArray(section))
@@ -45,24 +47,31 @@ function sanitizeReasoning(value) {
45
47
  if (typeof entry !== 'object' || entry === null)
46
48
  return undefined;
47
49
  const effort = entry;
48
- if (typeof effort.id !== 'string' || effort.id.length === 0
49
- || typeof effort.name !== 'string' || effort.name.length === 0
50
- || (effort.description !== undefined && typeof effort.description !== 'string')
51
- || seen.has(effort.id))
50
+ if (typeof effort.id !== 'string' ||
51
+ effort.id.length === 0 ||
52
+ typeof effort.name !== 'string' ||
53
+ effort.name.length === 0 ||
54
+ (effort.description !== undefined &&
55
+ typeof effort.description !== 'string') ||
56
+ seen.has(effort.id))
52
57
  return undefined;
53
58
  seen.add(effort.id);
54
59
  efforts.push({
55
60
  id: ReasoningEffortId(effort.id),
56
61
  name: effort.name,
57
- ...effort.description === undefined ? {} : { description: effort.description },
62
+ ...(effort.description === undefined
63
+ ? {}
64
+ : { description: effort.description }),
58
65
  });
59
66
  }
60
- if (raw.defaultEffort !== undefined
61
- && (typeof raw.defaultEffort !== 'string' || !seen.has(raw.defaultEffort)))
67
+ if (raw.defaultEffort !== undefined &&
68
+ (typeof raw.defaultEffort !== 'string' || !seen.has(raw.defaultEffort)))
62
69
  return undefined;
63
70
  return {
64
71
  efforts,
65
- ...raw.defaultEffort === undefined ? {} : { defaultEffort: ReasoningEffortId(raw.defaultEffort) },
72
+ ...(raw.defaultEffort === undefined
73
+ ? {}
74
+ : { defaultEffort: ReasoningEffortId(raw.defaultEffort) }),
66
75
  };
67
76
  }
68
77
  /** Validate one persisted model, or undefined when malformed. */
@@ -70,53 +79,80 @@ function sanitizeModel(value) {
70
79
  if (typeof value !== 'object' || value === null)
71
80
  return undefined;
72
81
  const raw = value;
73
- if (typeof raw.id !== 'string' || raw.id.length === 0
74
- || typeof raw.name !== 'string' || raw.name.length === 0
75
- || (raw.description !== undefined && typeof raw.description !== 'string')
76
- || (raw.contextWindow !== undefined
77
- && (typeof raw.contextWindow !== 'number' || !Number.isInteger(raw.contextWindow) || raw.contextWindow <= 0))
78
- || (raw.maxContextWindow !== undefined
79
- && (typeof raw.maxContextWindow !== 'number' || !Number.isSafeInteger(raw.maxContextWindow) || raw.maxContextWindow <= 0))
80
- || (raw.maxOutputTokens !== undefined
81
- && (typeof raw.maxOutputTokens !== 'number' || !Number.isSafeInteger(raw.maxOutputTokens) || raw.maxOutputTokens <= 0))
82
- || (raw.priority !== undefined
83
- && (typeof raw.priority !== 'number' || !Number.isFinite(raw.priority))))
82
+ if (typeof raw.id !== 'string' ||
83
+ raw.id.length === 0 ||
84
+ typeof raw.name !== 'string' ||
85
+ raw.name.length === 0 ||
86
+ (raw.description !== undefined && typeof raw.description !== 'string') ||
87
+ (raw.contextWindow !== undefined &&
88
+ (typeof raw.contextWindow !== 'number' ||
89
+ !Number.isInteger(raw.contextWindow) ||
90
+ raw.contextWindow <= 0)) ||
91
+ (raw.maxContextWindow !== undefined &&
92
+ (typeof raw.maxContextWindow !== 'number' ||
93
+ !Number.isSafeInteger(raw.maxContextWindow) ||
94
+ raw.maxContextWindow <= 0)) ||
95
+ (raw.maxOutputTokens !== undefined &&
96
+ (typeof raw.maxOutputTokens !== 'number' ||
97
+ !Number.isSafeInteger(raw.maxOutputTokens) ||
98
+ raw.maxOutputTokens <= 0)) ||
99
+ (raw.priority !== undefined &&
100
+ (typeof raw.priority !== 'number' || !Number.isFinite(raw.priority))))
84
101
  return undefined;
85
102
  const reasoning = raw.reasoning === undefined ? undefined : sanitizeReasoning(raw.reasoning);
86
103
  if (raw.reasoning !== undefined && reasoning === undefined)
87
104
  return undefined;
88
105
  const thinkingType = raw.thinkingType;
89
- if (thinkingType !== undefined && thinkingType !== 'enabled' && thinkingType !== 'adaptive')
106
+ if (thinkingType !== undefined &&
107
+ thinkingType !== 'enabled' &&
108
+ thinkingType !== 'adaptive')
90
109
  return undefined;
91
110
  const fastTier = raw.fastTier;
92
111
  if (fastTier !== undefined && typeof fastTier !== 'boolean')
93
112
  return undefined;
94
113
  const copilotWire = raw.copilotWire;
95
- if (copilotWire !== undefined && copilotWire !== 'chat-completions' && copilotWire !== 'responses') {
114
+ if (copilotWire !== undefined &&
115
+ copilotWire !== 'chat-completions' &&
116
+ copilotWire !== 'responses') {
96
117
  return undefined;
97
118
  }
98
119
  const copilotResponses = raw.copilotResponses;
99
120
  if (copilotResponses !== undefined && typeof copilotResponses !== 'boolean')
100
121
  return undefined;
101
122
  const inputModalities = raw.inputModalities;
102
- if (inputModalities !== undefined
103
- && (!Array.isArray(inputModalities) || inputModalities.length === 0
104
- || inputModalities.some(modality => modality !== 'text' && modality !== 'image')))
123
+ if (inputModalities !== undefined &&
124
+ (!Array.isArray(inputModalities) ||
125
+ inputModalities.length === 0 ||
126
+ inputModalities.some((modality) => modality !== 'text' && modality !== 'image')))
105
127
  return undefined;
106
128
  return {
107
129
  id: raw.id,
108
130
  name: raw.name,
109
- ...raw.description === undefined ? {} : { description: raw.description },
110
- ...raw.contextWindow === undefined ? {} : { contextWindow: raw.contextWindow },
111
- ...raw.maxContextWindow === undefined ? {} : { maxContextWindow: raw.maxContextWindow },
112
- ...raw.maxOutputTokens === undefined ? {} : { maxOutputTokens: raw.maxOutputTokens },
113
- ...raw.priority === undefined ? {} : { priority: raw.priority },
114
- ...reasoning === undefined ? {} : { reasoning },
115
- ...thinkingType === undefined ? {} : { thinkingType: thinkingType },
116
- ...fastTier === undefined ? {} : { fastTier },
117
- ...copilotWire === undefined ? {} : { copilotWire: copilotWire },
118
- ...copilotResponses === undefined ? {} : { copilotResponses },
119
- ...inputModalities === undefined ? {} : { inputModalities: [...inputModalities] },
131
+ ...(raw.description === undefined
132
+ ? {}
133
+ : { description: raw.description }),
134
+ ...(raw.contextWindow === undefined
135
+ ? {}
136
+ : { contextWindow: raw.contextWindow }),
137
+ ...(raw.maxContextWindow === undefined
138
+ ? {}
139
+ : { maxContextWindow: raw.maxContextWindow }),
140
+ ...(raw.maxOutputTokens === undefined
141
+ ? {}
142
+ : { maxOutputTokens: raw.maxOutputTokens }),
143
+ ...(raw.priority === undefined ? {} : { priority: raw.priority }),
144
+ ...(reasoning === undefined ? {} : { reasoning }),
145
+ ...(thinkingType === undefined
146
+ ? {}
147
+ : { thinkingType: thinkingType }),
148
+ ...(fastTier === undefined ? {} : { fastTier }),
149
+ ...(copilotWire === undefined
150
+ ? {}
151
+ : { copilotWire: copilotWire }),
152
+ ...(copilotResponses === undefined ? {} : { copilotResponses }),
153
+ ...(inputModalities === undefined
154
+ ? {}
155
+ : { inputModalities: [...inputModalities] }),
120
156
  };
121
157
  }
122
158
  /**
@@ -164,13 +200,46 @@ async function readCatalogFile(path) {
164
200
  return {};
165
201
  }
166
202
  }
203
+ /**
204
+ * Windows refuses to replace a file another handle holds open without
205
+ * `FILE_SHARE_DELETE` (an indexer, antivirus scan, or a second dsh profile
206
+ * reading the cache), surfacing as a transient `EPERM`/`EBUSY`/`EACCES` on
207
+ * `rename` (V1ki/dsh-plugin-subscriptions#129). On Windows only, retry
208
+ * briefly before giving the write up: on POSIX those codes are real
209
+ * failures, so retrying would only delay surfacing them.
210
+ */
211
+ const RENAME_RETRY_CODES = new Set([
212
+ 'EPERM',
213
+ 'EBUSY',
214
+ 'EACCES',
215
+ ]);
216
+ const RENAME_RETRIES = 5;
217
+ const RENAME_RETRY_DELAY_MS = 20;
218
+ /** `rename` with a short bounded retry on Windows share-mode conflicts. */
219
+ async function renameWithRetry(from, to) {
220
+ for (let attempt = 0;; attempt++) {
221
+ try {
222
+ await rename(from, to);
223
+ return;
224
+ }
225
+ catch (error) {
226
+ const code = error.code;
227
+ if (process.platform !== 'win32' ||
228
+ attempt >= RENAME_RETRIES ||
229
+ code === undefined ||
230
+ !RENAME_RETRY_CODES.has(code))
231
+ throw error;
232
+ await new Promise((resolve) => setTimeout(resolve, RENAME_RETRY_DELAY_MS * (attempt + 1)));
233
+ }
234
+ }
235
+ }
167
236
  /** Persist the whole file atomically (tmp file + rename). */
168
237
  async function writeCatalogFile(store, path) {
169
238
  await mkdir(dirname(path), { recursive: true });
170
239
  const tmp = `${path}.tmp-${process.pid}-${Math.random().toString(36).slice(2)}`;
171
240
  try {
172
241
  await writeFile(tmp, JSON.stringify(store, null, 2));
173
- await rename(tmp, path);
242
+ await renameWithRetry(tmp, path);
174
243
  }
175
244
  catch (error) {
176
245
  await rm(tmp, { force: true });
@@ -245,15 +314,22 @@ export function accountCatalogStore(provider, account, path = modelsFilePath())
245
314
  return {
246
315
  async load() {
247
316
  const section = accountSection(await readCatalogFile(path), provider);
248
- return section !== undefined && Object.hasOwn(section, account) ? sanitizeSnapshot(section[account]) : undefined;
317
+ return section !== undefined && Object.hasOwn(section, account)
318
+ ? sanitizeSnapshot(section[account])
319
+ : undefined;
249
320
  },
250
321
  async save(snapshot) {
251
322
  return serialize(path, async () => {
252
323
  const store = await readCatalogFile(path);
253
- const accounts = typeof store.accounts === 'object' && store.accounts !== null && !Array.isArray(store.accounts)
324
+ const accounts = typeof store.accounts === 'object' &&
325
+ store.accounts !== null &&
326
+ !Array.isArray(store.accounts)
254
327
  ? store.accounts
255
328
  : {};
256
- const section = { ...accountSection(store, provider) ?? {}, [account]: snapshot };
329
+ const section = {
330
+ ...(accountSection(store, provider) ?? {}),
331
+ [account]: snapshot,
332
+ };
257
333
  store.accounts = { ...accounts, [provider]: section };
258
334
  await writeCatalogFile(store, path);
259
335
  });
@@ -266,7 +342,10 @@ export function accountCatalogStore(provider, account, path = modelsFilePath())
266
342
  return;
267
343
  const rest = { ...section };
268
344
  delete rest[account];
269
- store.accounts = { ...store.accounts, [provider]: rest };
345
+ store.accounts = {
346
+ ...store.accounts,
347
+ [provider]: rest,
348
+ };
270
349
  await writeCatalogFile(store, path);
271
350
  });
272
351
  },
@@ -16,9 +16,9 @@ export class ClaudeCliVersionCache extends NpmCliVersionCache {
16
16
  url: CLAUDE_CLI_VERSION_URL,
17
17
  label: 'Claude Code',
18
18
  floor: () => claudeCliVersionFloor(detect),
19
- ...fetchFn === undefined ? {} : { fetchFn },
20
- ...now === undefined ? {} : { now },
21
- ...timeoutMs === undefined ? {} : { timeoutMs },
19
+ ...(fetchFn === undefined ? {} : { fetchFn }),
20
+ ...(now === undefined ? {} : { now }),
21
+ ...(timeoutMs === undefined ? {} : { timeoutMs }),
22
22
  });
23
23
  }
24
24
  }
@@ -127,10 +127,22 @@ export interface ClaudeAdapterOptions {
127
127
  * the provider's own default.
128
128
  */
129
129
  defaultEffortOf?: (model: string) => string | undefined;
130
+ /**
131
+ * The user's local context-window override for one model (the Settings
132
+ * editor). Returns undefined to follow the advertised window. The result
133
+ * is capped at that window: Anthropic advertises no separate maximum.
134
+ */
135
+ contextWindowOf?: (model: string) => number | undefined;
130
136
  /** Resolves the Claude Code version to present (npm-backed); absent means the local floor. */
131
137
  resolveCliVersion?: () => Promise<string>;
132
138
  /** Cache breakpoint lifetime; absent means the API's five-minute default. */
133
139
  promptCacheTtl?: PromptCacheTtl;
140
+ /**
141
+ * The Settings page's per-route TTL override, consulted on every request so
142
+ * a saved change applies from the next turn without a restart. Returning
143
+ * undefined follows {@link ClaudeAdapterOptions.promptCacheTtl}.
144
+ */
145
+ promptCacheTtlOverride?: () => PromptCacheTtl | undefined;
134
146
  }
135
147
  /**
136
148
  * Inline image data a Claude request may carry, in base64 bytes. Every turn
@@ -155,8 +167,9 @@ export declare const CLAUDE_REQUEST_IMAGE_BUDGET: number;
155
167
  * @param effort - the reasoning effort, when the model advertises efforts.
156
168
  * @param cacheTtl - lifetime of every cache breakpoint; defaults to five minutes.
157
169
  * Requests that carry a `purpose` (compaction, session titles) always use five
158
- * minutes to preserve their existing auxiliary-request behavior. A five-minute
159
- * mark can still read an entry a one-hour mark wrote.
170
+ * minutes to preserve their existing auxiliary-request behavior — the override
171
+ * changes the TTL for ordinary turns only, never the auxiliary floor. A
172
+ * five-minute mark can still read an entry a one-hour mark wrote.
160
173
  * @returns the JSON body to POST.
161
174
  */
162
175
  export declare function claudeRequestBody(options: GenerateOptions, messages: readonly TranslatableMessage[], maxTokens: number, thinking?: Record<string, unknown>, effort?: string, cacheTtl?: PromptCacheTtl): Record<string, unknown>;
@@ -183,7 +196,14 @@ export declare class ClaudeAdapter extends LlmAdapter {
183
196
  listOwnModels(provider: string, account?: string, signal?: AbortSignal): Promise<readonly LlmModelInfo[]>;
184
197
  resolveModel(provider: string, model: string): Promise<LlmResolvedModelInfo>;
185
198
  /** Capability resolution of the provider's own models (the pool resolves members here). */
186
- resolveOwnModel(provider: string, model: string): Promise<LlmResolvedModelInfo>;
199
+ resolveOwnModel(provider: string, model: string, account?: string): Promise<LlmResolvedModelInfo>;
200
+ /** The advertised window is both the default and the ceiling for a local override. */
201
+ private contextLimitsFor;
202
+ /** Account-specific bounds the Settings editor shows next to a model's override input. */
203
+ contextLimits(model: string, account?: string): Promise<{
204
+ default: number;
205
+ max: number;
206
+ }>;
187
207
  stream(options: GenerateOptions): AsyncIterable<StreamChunk>;
188
208
  /** Pool seam: stream through one specific account instead of the default. */
189
209
  streamAccount(options: GenerateOptions, account: string): AsyncIterable<StreamChunk>;