dsh-subscriptions 0.2.0 → 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 (99) hide show
  1. package/README.md +212 -180
  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.js +74 -23
  6. package/lib/auth/store.js +80 -31
  7. package/lib/client/CursorCard.d.ts +1 -1
  8. package/lib/client/CursorCard.js +50 -16
  9. package/lib/client/ExternalUsageCards.d.ts +1 -1
  10. package/lib/client/ExternalUsageCards.js +37 -16
  11. package/lib/client/ImageGallery.d.ts +2 -2
  12. package/lib/client/ImageGallery.js +92 -33
  13. package/lib/client/ImageGenerateToolview.js +50 -14
  14. package/lib/client/OllamaCard.d.ts +1 -1
  15. package/lib/client/OllamaCard.js +26 -8
  16. package/lib/client/OllamaSettingsManager.js +113 -30
  17. package/lib/client/ProviderAccountManager.d.ts +1 -1
  18. package/lib/client/ProviderAccountManager.js +164 -49
  19. package/lib/client/ProviderModelEditor.js +107 -26
  20. package/lib/client/SpeedSelect.d.ts +62 -14
  21. package/lib/client/SpeedSelect.js +257 -46
  22. package/lib/client/SubscriptionUsageBadge.d.ts +4 -10
  23. package/lib/client/SubscriptionUsageBadge.js +300 -112
  24. package/lib/client/SubscriptionsSection.js +258 -85
  25. package/lib/client/UsageBadgeDisplaySetting.js +35 -12
  26. package/lib/client/UsageMeter.d.ts +1 -1
  27. package/lib/client/UsageMeter.js +62 -24
  28. package/lib/client/VideoGenerateToolview.js +69 -17
  29. package/lib/client/account-preferences.d.ts +8 -0
  30. package/lib/client/account-preferences.js +20 -5
  31. package/lib/client/host-contract.d.ts +11 -2
  32. package/lib/client/host-contract.js +20 -3
  33. package/lib/client/index.d.ts +5 -5
  34. package/lib/client/index.js +34 -12
  35. package/lib/client/locales.d.ts +10 -0
  36. package/lib/client/locales.js +61 -51
  37. package/lib/client/ollama-draft.js +21 -12
  38. package/lib/client/reset-action.js +6 -4
  39. package/lib/client/reset-credits-view.d.ts +1 -1
  40. package/lib/client/reset-credits-view.js +66 -17
  41. package/lib/client/subscription-card-styles.js +94 -24
  42. package/lib/client/usage-color-preferences.js +4 -2
  43. package/lib/client/usage-pace.js +30 -12
  44. package/lib/client.js +287 -47
  45. package/lib/client.js.map +1 -1
  46. package/lib/http.js +1 -1
  47. package/lib/index.d.ts +3 -3
  48. package/lib/index.js +118 -64
  49. package/lib/model-defaults.js +4 -2
  50. package/lib/provider-settings.d.ts +12 -0
  51. package/lib/provider-settings.js +58 -16
  52. package/lib/providers/account-preferences.js +51 -22
  53. package/lib/providers/accounts.js +15 -9
  54. package/lib/providers/antigravity-oauth-client.js +2 -2
  55. package/lib/providers/antigravity.js +141 -68
  56. package/lib/providers/catalog-store.js +120 -41
  57. package/lib/providers/claude-cli-version.js +3 -3
  58. package/lib/providers/claude.d.ts +9 -2
  59. package/lib/providers/claude.js +153 -90
  60. package/lib/providers/codex-client-version.js +3 -3
  61. package/lib/providers/codex-search.js +40 -13
  62. package/lib/providers/codex.js +196 -112
  63. package/lib/providers/common.js +71 -39
  64. package/lib/providers/copilot.js +143 -82
  65. package/lib/providers/cursor-adapter.js +42 -18
  66. package/lib/providers/cursor-auth.d.ts +1 -1
  67. package/lib/providers/cursor-auth.js +69 -24
  68. package/lib/providers/cursor-model-settings.js +14 -5
  69. package/lib/providers/cursor-usage.js +27 -13
  70. package/lib/providers/external-usage-controller.js +14 -5
  71. package/lib/providers/external-usage.js +59 -23
  72. package/lib/providers/grok.js +152 -89
  73. package/lib/providers/image-pool.js +29 -11
  74. package/lib/providers/minimax-usage.js +31 -11
  75. package/lib/providers/npm-cli-version.js +10 -3
  76. package/lib/providers/ollama-settings.js +24 -8
  77. package/lib/providers/ollama-web.js +29 -11
  78. package/lib/providers/ollama.js +129 -66
  79. package/lib/providers/pool-family.js +11 -3
  80. package/lib/providers/pool-health.js +28 -8
  81. package/lib/providers/pool-usage.js +16 -9
  82. package/lib/providers/pool.d.ts +8 -0
  83. package/lib/providers/pool.js +81 -42
  84. package/lib/providers/rate-limit.js +8 -2
  85. package/lib/providers/replay.js +40 -17
  86. package/lib/providers/reset-redemption.js +18 -6
  87. package/lib/tools/image-generate.js +123 -65
  88. package/lib/tools/registration.js +5 -2
  89. package/lib/tools/video-generate.js +51 -31
  90. package/lib/tools/x-search.js +50 -22
  91. package/lib/translate/anthropic.js +85 -37
  92. package/lib/translate/antigravity-schema.js +71 -28
  93. package/lib/translate/antigravity-thinking.js +22 -9
  94. package/lib/translate/antigravity.js +141 -50
  95. package/lib/translate/chat-completions.js +67 -23
  96. package/lib/translate/resolved.js +55 -21
  97. package/lib/translate/responses.js +69 -29
  98. package/lib/translate/sse.js +4 -1
  99. 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
  }
@@ -137,6 +137,12 @@ export interface ClaudeAdapterOptions {
137
137
  resolveCliVersion?: () => Promise<string>;
138
138
  /** Cache breakpoint lifetime; absent means the API's five-minute default. */
139
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;
140
146
  }
141
147
  /**
142
148
  * Inline image data a Claude request may carry, in base64 bytes. Every turn
@@ -161,8 +167,9 @@ export declare const CLAUDE_REQUEST_IMAGE_BUDGET: number;
161
167
  * @param effort - the reasoning effort, when the model advertises efforts.
162
168
  * @param cacheTtl - lifetime of every cache breakpoint; defaults to five minutes.
163
169
  * Requests that carry a `purpose` (compaction, session titles) always use five
164
- * minutes to preserve their existing auxiliary-request behavior. A five-minute
165
- * 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.
166
173
  * @returns the JSON body to POST.
167
174
  */
168
175
  export declare function claudeRequestBody(options: GenerateOptions, messages: readonly TranslatableMessage[], maxTokens: number, thinking?: Record<string, unknown>, effort?: string, cacheTtl?: PromptCacheTtl): Record<string, unknown>;