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
@@ -28,7 +28,10 @@ export function withToolResultImages(messages) {
28
28
  if (message.role === 'tool') {
29
29
  const parts = message.content.filter((part) => part.type === 'image' && 'dataBase64' in part);
30
30
  if (parts.length > 0) {
31
- images.push({ type: 'text', text: `Images from tool result ${String(message.toolCallId)}:` }, ...parts);
31
+ images.push({
32
+ type: 'text',
33
+ text: `Images from tool result ${String(message.toolCallId)}:`,
34
+ }, ...parts);
32
35
  }
33
36
  }
34
37
  for (const block of message.content) {
@@ -36,7 +39,10 @@ export function withToolResultImages(messages) {
36
39
  continue;
37
40
  const parts = block.content.filter((part) => part.type === 'image' && 'dataBase64' in part);
38
41
  if (parts.length > 0) {
39
- images.push({ type: 'text', text: `Images from tool result ${String(block.toolCallId)}:` }, ...parts);
42
+ images.push({
43
+ type: 'text',
44
+ text: `Images from tool result ${String(block.toolCallId)}:`,
45
+ }, ...parts);
40
46
  }
41
47
  }
42
48
  }
@@ -59,8 +65,8 @@ export const IMAGE_OFFLOAD_REQUIRED = 'IMAGE_OFFLOAD_REQUIRED';
59
65
  */
60
66
  export function hostSupportsImageOffload() {
61
67
  const exports = llm;
62
- return exports['IMAGE_OFFLOAD_REQUIRED_CODE'] === IMAGE_OFFLOAD_REQUIRED
63
- && typeof exports['offloadedImageText'] === 'function';
68
+ return (exports['IMAGE_OFFLOAD_REQUIRED_CODE'] === IMAGE_OFFLOAD_REQUIRED &&
69
+ typeof exports['offloadedImageText'] === 'function');
64
70
  }
65
71
  /**
66
72
  * How many of the oldest images still sent inline must be offloaded before
@@ -102,7 +108,7 @@ export function imageRequestTarget(ref, limit) {
102
108
  const longEdge = Math.max(ref.width, ref.height);
103
109
  if (!(longEdge > limit.maxEdge))
104
110
  return undefined;
105
- const short = (edge) => Math.max(1, Math.floor(edge * limit.maxEdge / longEdge));
111
+ const short = (edge) => Math.max(1, Math.floor((edge * limit.maxEdge) / longEdge));
106
112
  const width = ref.width >= ref.height ? limit.maxEdge : short(ref.width);
107
113
  const height = ref.width >= ref.height ? short(ref.height) : limit.maxEdge;
108
114
  return { width, height, maxPixels: width * height, maxBytes: limit.maxBytes };
@@ -123,10 +129,10 @@ export async function resolveImages(messages, attachments, signal, limit) {
123
129
  const hasRetainedImage = (block) => block.type === 'image'
124
130
  ? !('offloaded' in block && block.offloaded === true)
125
131
  : block.type === 'tool-result' && block.content.some(hasRetainedImage);
126
- if (attachments === undefined
127
- && messages.some(message => message.content.some(hasRetainedImage))) {
128
- throw new LlmError('dsh-subscriptions: the request carries an image but no attachments service is mounted; '
129
- + 'image input requires the harness attachment store', 'UNSUPPORTED');
132
+ if (attachments === undefined &&
133
+ messages.some((message) => message.content.some(hasRetainedImage))) {
134
+ throw new LlmError('dsh-subscriptions: the request carries an image but no attachments service is mounted; ' +
135
+ 'image input requires the harness attachment store', 'UNSUPPORTED');
130
136
  }
131
137
  const readForRequest = async (ref) => {
132
138
  if (attachments === undefined)
@@ -135,7 +141,11 @@ export async function resolveImages(messages, attachments, signal, limit) {
135
141
  if (target !== undefined) {
136
142
  try {
137
143
  const version = await attachments.readImageRequest(ref, target, signal);
138
- return { data: version.data, mediaType: version.mediaType, ref: version.attachment };
144
+ return {
145
+ data: version.data,
146
+ mediaType: version.mediaType,
147
+ ref: version.attachment,
148
+ };
139
149
  }
140
150
  catch (error) {
141
151
  // A host that cannot derive request images keeps sending the stored bytes, as before.
@@ -144,11 +154,20 @@ export async function resolveImages(messages, attachments, signal, limit) {
144
154
  }
145
155
  }
146
156
  const stored = await attachments.readImage(ref, signal);
147
- return { data: stored.data, mediaType: stored.ref.mediaType, ref: stored.ref };
157
+ return {
158
+ data: stored.data,
159
+ mediaType: stored.ref.mediaType,
160
+ ref: stored.ref,
161
+ };
148
162
  };
149
163
  const resolveBlock = async (block) => {
150
164
  if (block.type === 'tool-result') {
151
- return [{ ...block, content: (await Promise.all(block.content.map(resolveBlock))).flat() }];
165
+ return [
166
+ {
167
+ ...block,
168
+ content: (await Promise.all(block.content.map(resolveBlock))).flat(),
169
+ },
170
+ ];
152
171
  }
153
172
  if (block.type !== 'image' || 'dataBase64' in block)
154
173
  return [block];
@@ -156,26 +175,41 @@ export async function resolveImages(messages, attachments, signal, limit) {
156
175
  // The host owns omission decisions. Never restore offloaded bytes, even
157
176
  // when the original attachment is unreadable or no store is mounted.
158
177
  const format = llm.offloadedImageText;
159
- return [{ type: 'text', text: format?.(block.attachment)
160
- ?? `[image omitted to fit request image limits; ${block.attachment.attachmentId}]` }];
178
+ return [
179
+ {
180
+ type: 'text',
181
+ text: format?.(block.attachment) ??
182
+ `[image omitted to fit request image limits; ${block.attachment.attachmentId}]`,
183
+ },
184
+ ];
161
185
  }
162
- const { data, mediaType: sentType, ref } = await readForRequest(block.attachment);
186
+ const { data, mediaType: sentType, ref, } = await readForRequest(block.attachment);
163
187
  const { attachmentId, mediaType, bytes, width, height, name } = ref;
164
- return [{
188
+ return [
189
+ {
165
190
  type: 'image',
166
191
  mediaType: sentType,
167
192
  dataBase64: Buffer.from(data).toString('base64'),
168
- }, {
193
+ },
194
+ {
169
195
  type: 'text',
170
196
  text: `Image reference (for image_generate.referenceImages): ${JSON.stringify({
171
- attachmentId, mediaType, bytes, width, height, ...name === undefined ? {} : { name },
197
+ attachmentId,
198
+ mediaType,
199
+ bytes,
200
+ width,
201
+ height,
202
+ ...(name === undefined ? {} : { name }),
172
203
  })}`,
173
- }];
204
+ },
205
+ ];
174
206
  };
175
207
  return Promise.all(messages.map(async (message) => ({
176
208
  role: message.role,
177
- ...message.source === undefined ? {} : { source: message.source },
178
- ...message.role === 'tool' ? { toolCallId: message.toolCallId, isError: message.isError } : {},
209
+ ...(message.source === undefined ? {} : { source: message.source }),
210
+ ...(message.role === 'tool'
211
+ ? { toolCallId: message.toolCallId, isError: message.isError }
212
+ : {}),
179
213
  content: (await Promise.all(message.content.map(resolveBlock))).flat(),
180
214
  })));
181
215
  }
@@ -10,7 +10,9 @@ import { parseSse } from './sse.js';
10
10
  import { withToolResultImages } from './resolved.js';
11
11
  /** Flatten a tool result's content to plain text for `function_call_output`. */
12
12
  function toolResultText(block) {
13
- return block.content.map(part => (part.type === 'text' ? part.text : '')).join('');
13
+ return block.content
14
+ .map((part) => (part.type === 'text' ? part.text : ''))
15
+ .join('');
14
16
  }
15
17
  /**
16
18
  * Convert harness messages into Responses `instructions` + `input` items.
@@ -49,14 +51,19 @@ export function toResponsesInput(messages, system, reasoningFor) {
49
51
  // Current harness tool results are first-class role=tool messages.
50
52
  // Responses represents their text as function_call_output items.
51
53
  if (message.role === 'tool') {
52
- const callId = message.toolCallId ?? message.tool_call_id
53
- ?? (message.source?.kind === 'tool' ? String(message.source.callId) : undefined);
54
+ const callId = message.toolCallId ??
55
+ message.tool_call_id ??
56
+ (message.source?.kind === 'tool'
57
+ ? String(message.source.callId)
58
+ : undefined);
54
59
  if (callId === undefined)
55
60
  throw new LlmError('tool result has no call id', 'INVALID_REQUEST');
56
61
  input.push({
57
62
  type: 'function_call_output',
58
63
  call_id: callId,
59
- output: message.content.map(block => block.type === 'text' ? block.text : '').join(''),
64
+ output: message.content
65
+ .map((block) => (block.type === 'text' ? block.text : ''))
66
+ .join(''),
60
67
  });
61
68
  continue;
62
69
  }
@@ -71,7 +78,10 @@ export function toResponsesInput(messages, system, reasoningFor) {
71
78
  for (const block of message.content) {
72
79
  switch (block.type) {
73
80
  case 'text':
74
- content.push({ type: role === 'assistant' ? 'output_text' : 'input_text', text: block.text });
81
+ content.push({
82
+ type: role === 'assistant' ? 'output_text' : 'input_text',
83
+ text: block.text,
84
+ });
75
85
  break;
76
86
  case 'tool-call': {
77
87
  flushMessage();
@@ -80,9 +90,11 @@ export function toResponsesInput(messages, system, reasoningFor) {
80
90
  for (const item of encrypted) {
81
91
  input.push({
82
92
  type: 'reasoning',
83
- ...item.id === undefined ? {} : { id: item.id },
84
- ...item.summary === undefined ? {} : { summary: item.summary },
85
- ...item.status === undefined ? {} : { status: item.status },
93
+ ...(item.id === undefined ? {} : { id: item.id }),
94
+ ...(item.summary === undefined
95
+ ? {}
96
+ : { summary: item.summary }),
97
+ ...(item.status === undefined ? {} : { status: item.status }),
86
98
  encrypted_content: item.encrypted_content,
87
99
  });
88
100
  }
@@ -123,7 +135,7 @@ export function toResponsesInput(messages, system, reasoningFor) {
123
135
  flushMessage();
124
136
  }
125
137
  const instructions = system ?? (systemTexts.length > 0 ? systemTexts.join('\n\n') : undefined);
126
- return { ...instructions === undefined ? {} : { instructions }, input };
138
+ return { ...(instructions === undefined ? {} : { instructions }), input };
127
139
  }
128
140
  /**
129
141
  * Map harness tool schemas to Responses function tools.
@@ -137,12 +149,12 @@ export function toResponsesInput(messages, system, reasoningFor) {
137
149
  * @returns Responses `tools` array entries.
138
150
  */
139
151
  export function toResponsesTools(tools, options = {}) {
140
- return tools.map(tool => ({
152
+ return tools.map((tool) => ({
141
153
  type: 'function',
142
154
  name: tool.name,
143
155
  description: tool.description,
144
156
  parameters: tool.parameters,
145
- ...options.strict === undefined ? {} : { strict: options.strict },
157
+ ...(options.strict === undefined ? {} : { strict: options.strict }),
146
158
  }));
147
159
  }
148
160
  /**
@@ -157,8 +169,8 @@ export function mapResponsesUsage(usage) {
157
169
  return {
158
170
  inputTokens: usage.input_tokens - (cached ?? 0),
159
171
  outputTokens: usage.output_tokens,
160
- ...cached !== undefined ? { cacheReadTokens: cached } : {},
161
- ...reasoning !== undefined ? { reasoningTokens: reasoning } : {},
172
+ ...(cached !== undefined ? { cacheReadTokens: cached } : {}),
173
+ ...(reasoning !== undefined ? { reasoningTokens: reasoning } : {}),
162
174
  };
163
175
  }
164
176
  /**
@@ -172,10 +184,12 @@ export function responsesFailure(code, message) {
172
184
  // not that no secret can exist anywhere. Raw values remain local classification inputs.
173
185
  const detail = `${code ?? ''} ${message ?? ''}`;
174
186
  let classification = 'SERVER';
175
- if (code === 'context_window_exceeded' || isContextWindowExceededError(detail)) {
187
+ if (code === 'context_window_exceeded' ||
188
+ isContextWindowExceededError(detail)) {
176
189
  classification = CONTEXT_WINDOW_EXCEEDED_CODE;
177
190
  }
178
- else if ((code !== undefined && /insufficient|quota/i.test(code)) || isQuotaExceededError(detail)) {
191
+ else if ((code !== undefined && /insufficient|quota/i.test(code)) ||
192
+ isQuotaExceededError(detail)) {
179
193
  classification = QUOTA_EXCEEDED_CODE;
180
194
  }
181
195
  return new LlmError(`Responses failure (${classification}): [provider response body omitted]`, classification);
@@ -216,7 +230,7 @@ export class ResponsesStreamTranslator {
216
230
  kind,
217
231
  text: '',
218
232
  callId,
219
- ...name === undefined ? {} : { name },
233
+ ...(name === undefined ? {} : { name }),
220
234
  };
221
235
  this.blocks.set(key, block);
222
236
  this.order.push(block);
@@ -234,7 +248,11 @@ export class ResponsesStreamTranslator {
234
248
  if (block === undefined)
235
249
  return;
236
250
  this.blocks.delete(key);
237
- chunks.push({ type: 'block-end', index: block.index, block: closeBlock(block) });
251
+ chunks.push({
252
+ type: 'block-end',
253
+ index: block.index,
254
+ block: closeBlock(block),
255
+ });
238
256
  }
239
257
  /** Close every still-open block for one output item (prefix match on the key). */
240
258
  closeItem(itemId, chunks) {
@@ -252,7 +270,11 @@ export class ResponsesStreamTranslator {
252
270
  for (const [key, candidate] of this.blocks) {
253
271
  if (candidate === block) {
254
272
  this.blocks.delete(key);
255
- chunks.push({ type: 'block-end', index: block.index, block: closeBlock(block) });
273
+ chunks.push({
274
+ type: 'block-end',
275
+ index: block.index,
276
+ block: closeBlock(block),
277
+ });
256
278
  return;
257
279
  }
258
280
  }
@@ -277,7 +299,7 @@ export class ResponsesStreamTranslator {
277
299
  type: 'tool-call-delta',
278
300
  index: block.index,
279
301
  id: ToolCallId(callId),
280
- ...item.name === undefined ? {} : { name: item.name },
302
+ ...(item.name === undefined ? {} : { name: item.name }),
281
303
  argumentsDelta: '',
282
304
  });
283
305
  }
@@ -287,7 +309,11 @@ export class ResponsesStreamTranslator {
287
309
  const key = `${event.item_id ?? ''}:text:${String(event.content_index ?? 0)}`;
288
310
  const block = this.textBlock(key, chunks);
289
311
  block.text += event.delta ?? '';
290
- chunks.push({ type: 'text-delta', index: block.index, text: event.delta ?? '' });
312
+ chunks.push({
313
+ type: 'text-delta',
314
+ index: block.index,
315
+ text: event.delta ?? '',
316
+ });
291
317
  return chunks;
292
318
  }
293
319
  case 'response.reasoning_summary_text.delta':
@@ -296,7 +322,11 @@ export class ResponsesStreamTranslator {
296
322
  const key = `${event.item_id ?? ''}:reason:${String(sub)}`;
297
323
  const block = this.reasoningBlock(key, chunks);
298
324
  block.text += event.delta ?? '';
299
- chunks.push({ type: 'reasoning-delta', index: block.index, text: event.delta ?? '' });
325
+ chunks.push({
326
+ type: 'reasoning-delta',
327
+ index: block.index,
328
+ text: event.delta ?? '',
329
+ });
300
330
  return chunks;
301
331
  }
302
332
  case 'response.function_call_arguments.delta': {
@@ -312,7 +342,7 @@ export class ResponsesStreamTranslator {
312
342
  type: 'tool-call-delta',
313
343
  index: block.index,
314
344
  id: ToolCallId(block.callId),
315
- ...block.name === undefined ? {} : { name: block.name },
345
+ ...(block.name === undefined ? {} : { name: block.name }),
316
346
  argumentsDelta: event.delta ?? '',
317
347
  });
318
348
  return chunks;
@@ -325,16 +355,20 @@ export class ResponsesStreamTranslator {
325
355
  const key = `${item.id}:call`;
326
356
  // The provider may deliver the complete arguments only on done.
327
357
  const block = this.blocks.get(key);
328
- if (block !== undefined && block.text.length === 0 && item.arguments !== undefined) {
358
+ if (block !== undefined &&
359
+ block.text.length === 0 &&
360
+ item.arguments !== undefined) {
329
361
  block.text = item.arguments;
330
362
  }
331
363
  this.close(key, chunks);
332
364
  }
333
365
  else if (item.type === 'message') {
334
- if (![...this.blocks.keys()].some(key => key.startsWith(`${item.id}:text:`))) {
366
+ if (![...this.blocks.keys()].some((key) => key.startsWith(`${item.id}:text:`))) {
335
367
  // No deltas arrived for this item; synthesize blocks from the done payload.
336
368
  for (const [partIndex, part] of (item.content ?? []).entries()) {
337
- if (part?.type !== 'output_text' || typeof part.text !== 'string' || part.text.length === 0)
369
+ if (part?.type !== 'output_text' ||
370
+ typeof part.text !== 'string' ||
371
+ part.text.length === 0)
338
372
  continue;
339
373
  const block = this.open(`${item.id}:text:${partIndex}`, 'text', chunks);
340
374
  block.text = part.text;
@@ -359,20 +393,26 @@ export class ResponsesStreamTranslator {
359
393
  type: 'finish',
360
394
  reason: {
361
395
  kind: 'error',
362
- failure: { message: 'model returned a completed response with no content', code: EMPTY_RESPONSE_CODE },
396
+ failure: {
397
+ message: 'model returned a completed response with no content',
398
+ code: EMPTY_RESPONSE_CODE,
399
+ },
363
400
  },
364
401
  });
365
402
  }
366
403
  else {
367
- chunks.push({ type: 'finish', reason: { kind: this.sawToolCall ? 'tool-calls' : 'stop' } });
404
+ chunks.push({
405
+ type: 'finish',
406
+ reason: { kind: this.sawToolCall ? 'tool-calls' : 'stop' },
407
+ });
368
408
  }
369
409
  return chunks;
370
410
  }
371
411
  case 'response.failed':
372
412
  throw responsesFailure(event.response?.error?.code, event.response?.error?.message);
373
413
  case 'response.incomplete':
374
- throw responsesFailure(event.response?.incomplete_details?.reason, event.response?.error?.message
375
- ?? `the provider reported an incomplete response (${event.response?.incomplete_details?.reason ?? 'unknown reason'})`);
414
+ throw responsesFailure(event.response?.incomplete_details?.reason, event.response?.error?.message ??
415
+ `the provider reported an incomplete response (${event.response?.incomplete_details?.reason ?? 'unknown reason'})`);
376
416
  case 'error':
377
417
  throw responsesFailure(event.code, event.message);
378
418
  default:
@@ -33,7 +33,10 @@ export async function* parseSse(stream, onActivity) {
33
33
  line = line.slice(0, -1);
34
34
  if (line.length === 0) {
35
35
  if (dataLines.length > 0) {
36
- yield { data: dataLines.join('\n'), ...eventName === undefined ? {} : { event: eventName } };
36
+ yield {
37
+ data: dataLines.join('\n'),
38
+ ...(eventName === undefined ? {} : { event: eventName }),
39
+ };
37
40
  }
38
41
  dataLines = [];
39
42
  eventName = undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-subscriptions",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Unified subscription providers and usage dashboard for DeepSeek Harness",
5
5
  "icon": "./icon.svg",
6
6
  "license": "MIT",
@@ -71,8 +71,12 @@
71
71
  "build": "tsc && tsdown",
72
72
  "test": "tsc -p tsconfig.test.json && node --import ./test/hermetic.mjs --test \"lib-test/test/**/*.spec.js\"",
73
73
  "test:mutation": "stryker run",
74
- "prepare": "tsdown -c tsdown.prepare.config.ts",
75
- "prepublishOnly": "pnpm build && pnpm test"
74
+ "prepare": "node scripts/install-dev-hooks.mjs && tsdown -c tsdown.prepare.config.ts",
75
+ "prepublishOnly": "pnpm build && pnpm test",
76
+ "format": "prettier --write .",
77
+ "format:check": "prettier --check .",
78
+ "lint": "eslint . --max-warnings 0",
79
+ "lint:fix": "eslint . --fix --max-warnings 0"
76
80
  },
77
81
  "peerDependencies": {
78
82
  "@deepseek-ai/cordis": "~4.0.4",
@@ -102,17 +106,35 @@
102
106
  "@deepseek-ai/dsh-tools": "0.1.7-rc.2",
103
107
  "@deepseek-ai/dsh-web": "0.1.7-rc.2",
104
108
  "@deepseek-ai/schemastery": "3.18.4",
109
+ "@eslint/js": "^10.0.1",
105
110
  "@stryker-mutator/core": "^10.0.0",
106
111
  "@types/node": "^24.0.0",
107
112
  "@types/react": "~18.3.1",
108
113
  "@types/react-dom": "~18.3.7",
114
+ "eslint": "^10.13.0",
115
+ "eslint-config-prettier": "^10.1.8",
116
+ "eslint-plugin-react-hooks": "^7.1.1",
109
117
  "fast-check": "^4.10.2",
118
+ "globals": "^17.13.0",
119
+ "husky": "^9.1.7",
120
+ "lint-staged": "^17.6.0",
121
+ "prettier": "^3.9.10",
110
122
  "react": "^18.2.0",
111
123
  "react-dom": "^18.2.0",
112
124
  "tsdown": "^0.15.0",
113
- "typescript": "^5.8.0"
125
+ "typescript": "^5.8.0",
126
+ "typescript-eslint": "^8.71.2"
114
127
  },
115
128
  "publishConfig": {
116
129
  "access": "public"
130
+ },
131
+ "lint-staged": {
132
+ "*.{js,mjs,cjs,ts,tsx}": [
133
+ "eslint --fix --max-warnings 0 --no-warn-ignored",
134
+ "prettier --write"
135
+ ],
136
+ "*.{json,yml,yaml,css,md}": [
137
+ "prettier --write"
138
+ ]
117
139
  }
118
140
  }