@goodandready/dsh-subscriptions 0.6.19 → 0.6.21

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.
package/lib/accounts.js CHANGED
@@ -163,7 +163,8 @@ export function createAccountStore({ credentials, getConfig, fetchImpl, fetchFor
163
163
  }
164
164
 
165
165
  async function listAccounts(provider) {
166
- const slots = normalizeSlots(getConfig().slots).filter((s) => s.provider === provider)
166
+ const curConfig = getConfig()
167
+ const slots = normalizeSlots(curConfig.slots).filter((s) => s.provider === provider)
167
168
  const out = []
168
169
  const now = Date.now()
169
170
  for (const slot of slots) {
@@ -187,8 +188,10 @@ export function createAccountStore({ credentials, getConfig, fetchImpl, fetchFor
187
188
  }
188
189
 
189
190
  async function loggedInProviders() {
191
+ const curConfig = getConfig()
192
+ const slots = normalizeSlots(curConfig.slots)
190
193
  const found = new Set()
191
- for (const slot of normalizeSlots(getConfig().slots)) {
194
+ for (const slot of slots) {
192
195
  const info = await describeRef(slot.ref)
193
196
  if (info.configured) found.add(slot.provider)
194
197
  }
@@ -202,7 +205,8 @@ export function createAccountStore({ credentials, getConfig, fetchImpl, fetchFor
202
205
  if (refreshLocks.has(lockKey)) return refreshLocks.get(lockKey)
203
206
  const promise = (async () => {
204
207
  try {
205
- const cfg = vendorConfig(provider, getConfig())
208
+ const curConfig = getConfig()
209
+ const cfg = vendorConfig(provider, curConfig)
206
210
  const next = await executeWithRetry(
207
211
  () => getVendor(provider).refresh(cfg, blob, fetchFor(ref)),
208
212
  {
@@ -238,9 +242,11 @@ export function createAccountStore({ credentials, getConfig, fetchImpl, fetchFor
238
242
  }
239
243
 
240
244
  async function refreshUsage(provider) {
241
- const cfg = vendorConfig(provider, getConfig())
245
+ const curConfig = getConfig()
246
+ const cfg = vendorConfig(provider, curConfig)
242
247
  const vendor = getVendor(provider)
243
- for (const slot of normalizeSlots(getConfig().slots).filter((s) => s.provider === provider)) {
248
+ const slots = normalizeSlots(curConfig.slots).filter((s) => s.provider === provider)
249
+ for (const slot of slots) {
244
250
  const last = usageFetched.get(slot.ref) || 0
245
251
  if (Date.now() - last < USAGE_TTL_MS) continue
246
252
  let raw = ''
package/lib/adapter.js CHANGED
@@ -43,15 +43,16 @@ export class SubscriptionAdapter extends LlmAdapter {
43
43
 
44
44
  async listModels(provider) {
45
45
  const accounts = await this.deps.listAccounts(provider)
46
- if (!accounts.some((a) => a.hasToken)) return []
46
+ const targetAccount = accounts.find((a) => a.hasToken)
47
+ if (!targetAccount) return []
47
48
  const cfg = this.deps.vendorConfig(provider)
48
- const fetchImpl = this.deps.fetchImpl || fetch
49
+ const fetchImpl = pickFetch(this.deps, targetAccount.ref)
49
50
  let models = null
50
51
  try {
51
52
  const blob = await this.deps.ensureFresh(
52
53
  provider,
53
- await this.deps.loadBlob(accounts.find((a) => a.hasToken).ref),
54
- accounts.find((a) => a.hasToken).ref,
54
+ await this.deps.loadBlob(targetAccount.ref),
55
+ targetAccount.ref,
55
56
  )
56
57
  const live = await getVendor(provider).listModels(blob, cfg, fetchImpl)
57
58
  if (Array.isArray(live) && live.length) models = live
@@ -82,7 +82,7 @@ export const Config = z.object({
82
82
  })
83
83
 
84
84
  export function publicConfig(cfg) {
85
- const clone = structuredClone(cfg)
85
+ const clone = { ...cfg }
86
86
  // #242: redact all *ClientSecret fields before exposing config via GET /config.
87
87
  // Secrets must never leave the server; the UI does not need them.
88
88
  for (const key of Object.keys(clone)) {
package/lib/index.js CHANGED
@@ -64,22 +64,30 @@ const OK_HTML = '<!doctype html><meta charset="utf-8"><title>Subscriptions</titl
64
64
 
65
65
  export function apply(ctx, config) {
66
66
  const logger = ctx.logger ? ctx.logger('subscriptions') : console
67
- let getConfig = () => config
68
67
  let settingsApi
69
- ctx.inject(['settings'], (sctx) => {
70
- const scope = sctx.settings.register(NS, Config, { base: config })
71
- settingsApi = scope
72
- getConfig = () => scope.get() ?? config
73
- Promise.resolve().then(() => {
74
- reconcileSlots().catch((err) => {
75
- if (logger && logger.warn) logger.warn('[dsh-subscriptions] reconcileSlots initial error: ' + (err && err.message || err))
76
- })
77
- })
78
- sctx.effect(() => () => {
79
- settingsApi = undefined
80
- getConfig = () => config
81
- })
82
- })
68
+ // #367: maintain a single immutable config snapshot without structuredClone on hot path
69
+ let cachedConfig = Config(config ?? {}) ?? config
70
+
71
+ function syncSnapshot(raw) {
72
+ if (raw !== undefined && raw !== null) {
73
+ try {
74
+ cachedConfig = Config(raw ?? {}) ?? config
75
+ } catch {
76
+ cachedConfig = config
77
+ }
78
+ } else if (settingsApi && typeof settingsApi.get === 'function') {
79
+ try {
80
+ const rawSettings = settingsApi.get()
81
+ cachedConfig = Config(rawSettings ?? {}) ?? config
82
+ } catch {
83
+ cachedConfig = config
84
+ }
85
+ }
86
+ syncCustomVendors()
87
+ return cachedConfig
88
+ }
89
+
90
+ const live = () => cachedConfig
83
91
 
84
92
  // Registering custom vendors from Config. Runs at startup and on
85
93
  // every config change (replace) - the registry is rebuilt from scratch.
@@ -104,9 +112,58 @@ export function apply(ctx, config) {
104
112
  }
105
113
  }
106
114
 
107
- const live = () => Config(structuredClone(getConfig() ?? {})) ?? config
108
115
  syncCustomVendors()
109
116
 
117
+ ctx.inject(['settings'], (sctx) => {
118
+ const scope = sctx.settings.register(NS, Config, { base: config })
119
+ settingsApi = {
120
+ ...scope,
121
+ get: () => (typeof scope.get === 'function' ? scope.get() : cachedConfig),
122
+ async replace(next) {
123
+ syncSnapshot(next)
124
+ if (typeof scope.replace === 'function') {
125
+ return await scope.replace(next)
126
+ }
127
+ },
128
+ }
129
+
130
+ if (typeof scope.get === 'function') {
131
+ syncSnapshot(scope.get())
132
+ }
133
+
134
+ if (typeof scope.watch === 'function') {
135
+ try {
136
+ const unwatch = scope.watch((next) => syncSnapshot(next))
137
+ if (typeof unwatch === 'function') {
138
+ sctx.effect(() => unwatch)
139
+ }
140
+ } catch { /* ignore watch error */ }
141
+ }
142
+
143
+ Promise.resolve().then(() => {
144
+ reconcileSlots().catch((err) => {
145
+ if (logger && logger.warn) logger.warn('[dsh-subscriptions] reconcileSlots initial error: ' + (err && err.message || err))
146
+ })
147
+ })
148
+
149
+ const onDocUpdated = (ns) => {
150
+ if (!ns || ns === NS) {
151
+ syncSnapshot()
152
+ }
153
+ }
154
+ if (typeof sctx.on === 'function') {
155
+ sctx.on('settings/document-updated', onDocUpdated)
156
+ } else if (typeof ctx.on === 'function') {
157
+ ctx.on('settings/document-updated', onDocUpdated)
158
+ }
159
+
160
+ sctx.effect(() => () => {
161
+ settingsApi = undefined
162
+ cachedConfig = Config(config ?? {}) ?? config
163
+ syncCustomVendors()
164
+ })
165
+ })
166
+
110
167
  // #88: fetch bound to the per-account proxy (slot.proxyUrl), or null for direct.
111
168
  const fetchForRef = (ref) => {
112
169
  const parsed = parseOauthRef(ref)
package/lib/messages.js CHANGED
@@ -10,11 +10,13 @@ function flattenText(content) {
10
10
  }
11
11
 
12
12
  function toolResults(content) {
13
- return Array.isArray(content) ? content.filter((b) => b && b.type === 'tool-result') : []
13
+ if (!Array.isArray(content)) return []
14
+ return content.filter((b) => b && (b.type === 'tool-result' || b.type === 'tool_result' || b.type === 'function_call_output'))
14
15
  }
15
16
 
16
17
  function toolCalls(content) {
17
- return Array.isArray(content) ? content.filter((b) => b && b.type === 'tool-call') : []
18
+ if (!Array.isArray(content)) return []
19
+ return content.filter((b) => b && (b.type === 'tool-call' || b.type === 'tool_use' || b.type === 'function_call'))
18
20
  }
19
21
 
20
22
  export function openaiMessages(options) {
@@ -26,11 +28,21 @@ export function openaiMessages(options) {
26
28
  continue
27
29
  }
28
30
  if (message.role === 'assistant') {
29
- const calls = toolCalls(message.content).map((block) => ({
30
- id: block.id,
31
- type: 'function',
32
- function: { name: block.name, arguments: block.arguments || '{}' },
33
- }))
31
+ const calls = [
32
+ ...toolCalls(message.content).map((block) => ({
33
+ id: block.id,
34
+ type: 'function',
35
+ function: { name: block.name, arguments: block.arguments || '{}' },
36
+ })),
37
+ ...((message.tool_calls || message.toolCalls || []).map((c) => ({
38
+ id: c.id || c.call_id,
39
+ type: 'function',
40
+ function: {
41
+ name: c.name || (c.function && c.function.name),
42
+ arguments: typeof c.arguments === 'string' ? c.arguments : (c.function && c.function.arguments) || '{}',
43
+ },
44
+ }))),
45
+ ]
34
46
  out.push({
35
47
  role: 'assistant',
36
48
  content: flattenText(message.content) || (calls.length ? null : ''),
@@ -38,14 +50,26 @@ export function openaiMessages(options) {
38
50
  })
39
51
  continue
40
52
  }
53
+ if (message.role === 'tool') {
54
+ const toolCallId = String(message.toolCallId || message.tool_call_id || message.call_id || message.id || '')
55
+ const content = typeof message.content === 'string'
56
+ ? message.content
57
+ : (flattenText(message.content) || JSON.stringify(message.content || ''))
58
+ out.push({
59
+ role: 'tool',
60
+ tool_call_id: toolCallId,
61
+ content: content || '(no output)',
62
+ })
63
+ continue
64
+ }
41
65
  const results = toolResults(message.content)
42
66
  const text = flattenText(message.content)
43
67
  if (text || !results.length) out.push({ role: 'user', content: text })
44
68
  for (const result of results) {
45
69
  out.push({
46
70
  role: 'tool',
47
- tool_call_id: result.toolCallId,
48
- content: flattenText(result.content) || '(no output)',
71
+ tool_call_id: String(result.toolCallId || result.tool_call_id || result.call_id || result.id || ''),
72
+ content: typeof result.content === 'string' ? result.content : (flattenText(result.content) || '(no output)'),
49
73
  })
50
74
  }
51
75
  }
@@ -65,16 +89,74 @@ export function openaiTools(options) {
65
89
  }))
66
90
  }
67
91
 
92
+ export function reconcileResponsesInput(items) {
93
+ const result = []
94
+ const callsSeen = new Set()
95
+ const outputsSeen = new Set()
96
+
97
+ for (const it of items) {
98
+ if (it && it.type === 'function_call_output' && it.call_id) {
99
+ outputsSeen.add(it.call_id)
100
+ }
101
+ }
102
+
103
+ const pendingCalls = []
104
+ for (const it of items) {
105
+ if (!it) continue
106
+ if (it.type === 'function_call') {
107
+ if (it.call_id) {
108
+ callsSeen.add(it.call_id)
109
+ if (!outputsSeen.has(it.call_id)) {
110
+ pendingCalls.push(it.call_id)
111
+ }
112
+ }
113
+ result.push(it)
114
+ continue
115
+ }
116
+ if (it.type === 'function_call_output') {
117
+ // Drop orphan output if no matching preceding call
118
+ if (it.call_id && callsSeen.has(it.call_id)) {
119
+ result.push(it)
120
+ }
121
+ continue
122
+ }
123
+ // Flush synthetic outputs for any calls that did not get output before user/assistant turn
124
+ while (pendingCalls.length) {
125
+ const orphanId = pendingCalls.shift()
126
+ result.push({
127
+ type: 'function_call_output',
128
+ call_id: orphanId,
129
+ output: '{"status":"interrupted"}',
130
+ })
131
+ }
132
+ result.push(it)
133
+ }
134
+
135
+ while (pendingCalls.length) {
136
+ const orphanId = pendingCalls.shift()
137
+ result.push({
138
+ type: 'function_call_output',
139
+ call_id: orphanId,
140
+ output: '{"status":"interrupted"}',
141
+ })
142
+ }
143
+
144
+ if (!result.length) {
145
+ result.push({ role: 'user', content: [{ type: 'input_text', text: '' }] })
146
+ }
147
+ return result
148
+ }
149
+
68
150
  export function codexResponsesBody(options, fallbackInstructions, vendorCfg) {
69
151
  const systemParts = []
70
152
  if (options.system) systemParts.push(options.system)
71
- const input = []
153
+ const rawInput = []
72
154
  let pendingRole = null
73
155
  let pendingContent = []
74
156
  function flush() {
75
157
  if (!pendingRole) return
76
158
  const type = pendingRole === 'assistant' ? 'output_text' : 'input_text'
77
- input.push({
159
+ rawInput.push({
78
160
  role: pendingRole,
79
161
  content: pendingContent.length ? pendingContent : [{ type, text: '' }],
80
162
  })
@@ -95,30 +177,61 @@ export function codexResponsesBody(options, fallbackInstructions, vendorCfg) {
95
177
  if (message.role === 'assistant') {
96
178
  const text = flattenText(message.content)
97
179
  if (text) addText('assistant', text)
98
- for (const call of toolCalls(message.content)) {
180
+ const calls = [
181
+ ...toolCalls(message.content),
182
+ ...((message.tool_calls || message.toolCalls || []).map((c) => ({
183
+ id: c.id || c.call_id,
184
+ name: c.name || (c.function && c.function.name),
185
+ arguments: typeof c.arguments === 'string' ? c.arguments : (c.function && c.function.arguments) || c.arguments || '{}',
186
+ }))),
187
+ ]
188
+ for (const call of calls) {
189
+ if (!call.id) continue
99
190
  flush()
100
- input.push({
191
+ rawInput.push({
101
192
  type: 'function_call',
102
- call_id: String(call.id || ''),
193
+ call_id: String(call.id),
103
194
  name: call.name,
104
195
  arguments: typeof call.arguments === 'string' ? call.arguments : JSON.stringify(call.arguments || {}),
105
196
  })
106
197
  }
107
198
  continue
108
199
  }
200
+ if (message.role === 'tool') {
201
+ flush()
202
+ const callId = String(message.toolCallId || message.tool_call_id || message.call_id || message.id || '')
203
+ const outText = typeof message.content === 'string'
204
+ ? message.content
205
+ : (flattenText(message.content) || JSON.stringify(message.content || ''))
206
+ if (callId) {
207
+ rawInput.push({
208
+ type: 'function_call_output',
209
+ call_id: callId,
210
+ output: outText,
211
+ })
212
+ }
213
+ continue
214
+ }
215
+ const results = toolResults(message.content)
109
216
  const text = flattenText(message.content)
110
217
  if (text) addText('user', text)
111
- for (const result of toolResults(message.content)) {
218
+ for (const result of results) {
112
219
  flush()
113
- input.push({
114
- type: 'function_call_output',
115
- call_id: String(result.toolCallId || ''),
116
- output: flattenText(result.content) || '',
117
- })
220
+ const callId = String(result.toolCallId || result.tool_call_id || result.call_id || result.id || '')
221
+ const outText = typeof result.content === 'string'
222
+ ? result.content
223
+ : (flattenText(result.content) || (result.output != null ? (typeof result.output === 'string' ? result.output : JSON.stringify(result.output)) : ''))
224
+ if (callId) {
225
+ rawInput.push({
226
+ type: 'function_call_output',
227
+ call_id: callId,
228
+ output: outText,
229
+ })
230
+ }
118
231
  }
119
232
  }
120
233
  flush()
121
- if (!input.length) input.push({ role: 'user', content: [{ type: 'input_text', text: '' }] })
234
+ const input = reconcileResponsesInput(rawInput)
122
235
  const tools = openaiTools(options)
123
236
  const responsesTools = tools && tools.map((tool) => ({
124
237
  type: 'function',
@@ -160,7 +273,16 @@ export function anthropicPayload(options, extraSystem) {
160
273
  const blocks = []
161
274
  const text = flattenText(message.content)
162
275
  if (text) blocks.push({ type: 'text', text })
163
- for (const call of toolCalls(message.content)) {
276
+ const calls = [
277
+ ...toolCalls(message.content),
278
+ ...((message.tool_calls || message.toolCalls || []).map((c) => ({
279
+ id: c.id || c.call_id,
280
+ name: c.name || (c.function && c.function.name),
281
+ arguments: c.arguments || (c.function && c.function.arguments) || '{}',
282
+ }))),
283
+ ]
284
+ for (const call of calls) {
285
+ if (!call.id) continue
164
286
  blocks.push({
165
287
  type: 'tool_use',
166
288
  id: call.id,
@@ -171,14 +293,33 @@ export function anthropicPayload(options, extraSystem) {
171
293
  messages.push({ role: 'assistant', content: blocks.length ? blocks : [{ type: 'text', text: '' }] })
172
294
  continue
173
295
  }
296
+ if (message.role === 'tool') {
297
+ const toolUseId = String(message.toolCallId || message.tool_call_id || message.tool_use_id || message.id || '')
298
+ const content = typeof message.content === 'string'
299
+ ? message.content
300
+ : (flattenText(message.content) || JSON.stringify(message.content || ''))
301
+ messages.push({
302
+ role: 'user',
303
+ content: [{
304
+ type: 'tool_result',
305
+ tool_use_id: toolUseId,
306
+ content: content || '',
307
+ }],
308
+ })
309
+ continue
310
+ }
174
311
  const blocks = []
175
312
  const text = flattenText(message.content)
176
313
  if (text) blocks.push({ type: 'text', text })
177
314
  for (const result of toolResults(message.content)) {
315
+ const toolUseId = String(result.toolCallId || result.tool_call_id || result.tool_use_id || result.id || '')
316
+ const content = typeof result.content === 'string'
317
+ ? result.content
318
+ : (flattenText(result.content) || (result.output != null ? (typeof result.output === 'string' ? result.output : JSON.stringify(result.output)) : ''))
178
319
  blocks.push({
179
320
  type: 'tool_result',
180
- tool_use_id: result.toolCallId,
181
- content: flattenText(result.content) || '',
321
+ tool_use_id: toolUseId,
322
+ content: content || '',
182
323
  })
183
324
  }
184
325
  messages.push({ role: 'user', content: blocks.length ? blocks : [{ type: 'text', text: '' }] })
@@ -210,18 +351,45 @@ export function googleContents(options) {
210
351
  systemParts.push({ text: flattenText(message.content) })
211
352
  continue
212
353
  }
354
+ if (message.role === 'tool') {
355
+ const name = message.toolName || message.name || 'tool'
356
+ const output = typeof message.content === 'string'
357
+ ? message.content
358
+ : (flattenText(message.content) || JSON.stringify(message.content || ''))
359
+ contents.push({
360
+ role: 'user',
361
+ parts: [{
362
+ functionResponse: {
363
+ name,
364
+ response: { output },
365
+ },
366
+ }],
367
+ })
368
+ continue
369
+ }
213
370
  const role = message.role === 'assistant' ? 'model' : 'user'
214
371
  const parts = []
215
372
  const text = flattenText(message.content)
216
373
  if (text) parts.push({ text })
217
- for (const call of toolCalls(message.content)) {
374
+ const calls = [
375
+ ...toolCalls(message.content),
376
+ ...((message.tool_calls || message.toolCalls || []).map((c) => ({
377
+ name: c.name || (c.function && c.function.name),
378
+ arguments: c.arguments || (c.function && c.function.arguments) || '{}',
379
+ }))),
380
+ ]
381
+ for (const call of calls) {
218
382
  parts.push({ functionCall: { name: call.name, args: safeJson(call.arguments) } })
219
383
  }
220
384
  for (const result of toolResults(message.content)) {
385
+ const name = result.toolName || result.name || 'tool'
386
+ const output = typeof result.content === 'string'
387
+ ? result.content
388
+ : (flattenText(result.content) || (result.output != null ? (typeof result.output === 'string' ? result.output : JSON.stringify(result.output)) : ''))
221
389
  parts.push({
222
390
  functionResponse: {
223
- name: result.toolName || result.name || 'tool',
224
- response: { output: flattenText(result.content) },
391
+ name,
392
+ response: { output },
225
393
  },
226
394
  })
227
395
  }
@@ -242,24 +410,32 @@ export function googleContents(options) {
242
410
  }
243
411
 
244
412
  function safeJson(text) {
245
- if (text && typeof text === 'object') return text
246
- try { return JSON.parse(text || '{}') } catch { return {} }
413
+ if (typeof text === 'object' && text !== null) return text
414
+ try { return JSON.parse(text) } catch { return {} }
247
415
  }
248
416
 
249
- export function modelCatalog(provider, entries) {
250
- return (entries || []).map((entry) => {
251
- if (!entry) return null
252
- if (typeof entry === 'string') return { provider, id: entry, name: entry }
253
- const id = entry.id || entry.slug || entry.name
254
- if (!id) return null
255
- return {
256
- provider,
257
- id,
258
- name: entry.name || entry.display_name || id,
259
- ...(entry.description ? { description: entry.description } : {}),
260
- ...(entry.contextWindow ? { contextWindow: entry.contextWindow } : {}),
261
- ...(entry.inputModalities ? { inputModalities: entry.inputModalities } : {}),
262
- ...(entry.reasoning ? { reasoning: entry.reasoning } : {}),
417
+ export function modelCatalog(provider, list) {
418
+ if (!Array.isArray(list)) return []
419
+ const out = []
420
+ for (const entry of list) {
421
+ if (!entry) continue
422
+ if (typeof entry === 'string') {
423
+ out.push({ provider, id: entry, name: entry })
424
+ continue
263
425
  }
264
- }).filter(Boolean)
265
- }
426
+ if (typeof entry === 'object') {
427
+ const id = entry.id || entry.slug || entry.name
428
+ if (!id) continue
429
+ out.push({
430
+ provider,
431
+ id,
432
+ name: entry.name || entry.display_name || id,
433
+ ...(entry.description ? { description: entry.description } : {}),
434
+ ...(entry.contextWindow || entry.context_window ? { contextWindow: entry.contextWindow || entry.context_window } : {}),
435
+ ...(entry.reasoning != null ? { reasoning: entry.reasoning } : {}),
436
+ ...(entry.inputModalities || entry.input_modalities ? { inputModalities: entry.inputModalities || entry.input_modalities } : {}),
437
+ })
438
+ }
439
+ }
440
+ return out
441
+ }
@@ -30,6 +30,9 @@ export function defaults() {
30
30
  baseUrl: 'https://chatgpt.com/backend-api/codex',
31
31
  originator: 'codex_cli_rs',
32
32
  models: [
33
+ { id: 'gpt-5.6-luna', name: 'GPT-5.6 Luna' },
34
+ { id: 'gpt-5.3-codex', name: 'GPT-5.3 Codex' },
35
+ { id: 'gpt-5.2-codex', name: 'GPT-5.2 Codex' },
33
36
  { id: 'gpt-5.1-codex', name: 'GPT-5.1 Codex' },
34
37
  { id: 'gpt-5.1-codex-mini', name: 'GPT-5.1 Codex Mini' },
35
38
  { id: 'gpt-5.1', name: 'GPT-5.1' },
@@ -141,7 +144,8 @@ export async function refresh(cfg, blob, fetchImpl) {
141
144
  }
142
145
 
143
146
  export async function listModels(blob, cfg, fetchImpl) {
144
- const catalog = modelCatalog(id, cfg.models || defaults().models)
147
+ const rawModels = (cfg && cfg.models && cfg.models.length) ? cfg.models : defaults().models
148
+ const catalog = modelCatalog(id, rawModels)
145
149
  const impl = fetchImpl || fetch
146
150
  try {
147
151
  const base = (cfg.baseUrl || defaults().baseUrl).replace(/\/$/, '')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goodandready/dsh-subscriptions",
3
- "version": "0.6.19",
3
+ "version": "0.6.21",
4
4
  "description": "Use ChatGPT Codex, Claude, Grok, Antigravity, Kimi, GLM, Cursor, Kiro, Copilot, Qwen, ERNIE, Spark, JetBrains, Perplexity, Replit, and Cody subscriptions as DeepSeek Harness LLM providers via OAuth.",
5
5
  "license": "MIT",
6
6
  "type": "module",