@goodandready/dsh-subscriptions 0.6.34 → 0.6.36

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.
@@ -5,7 +5,7 @@ import {
5
5
  antigravityIdentityHeaders,
6
6
  } from './code-assist.js'
7
7
 
8
- export async function enrichAntigravityAccount(slot, info, store, fetch) {
8
+ async function enrichAntigravityAccount(slot, info, store, fetch) {
9
9
  if (!info.configured || slot.provider !== 'antigravity') return info
10
10
  let blob
11
11
  try { blob = await store.loadBlob(slot.ref) } catch { return info }
@@ -4,7 +4,7 @@ import { normalizeSlots, vendorConfig } from './accounts.js'
4
4
  import { PROVIDERS, isProvider, oauthRef } from './refs.js'
5
5
  import { credentialRef } from '@deepseek-ai/dsh-credentials'
6
6
  import { getVendor } from './vendors/index.js'
7
- import { Config } from './config-schema.js'
7
+ import { Config, plainConfig } from './config-schema.js'
8
8
 
9
9
  export function createAdapterManager({
10
10
  ctx,
@@ -187,7 +187,7 @@ export function createAdapterManager({
187
187
  }
188
188
  const currentSlots = Array.isArray(live().slots) ? live().slots : []
189
189
  const newSlots = [...currentSlots, ...additions]
190
- const parsed = Config({ ...live(), slots: stripLegacySlots(newSlots) })
190
+ const parsed = plainConfig(Config({ ...live(), slots: stripLegacySlots(newSlots) }))
191
191
  await api.replace(parsed)
192
192
  syncCustomVendors()
193
193
  await syncAdapter()
package/lib/client.js CHANGED
@@ -2180,16 +2180,6 @@ const cssId = 'dsh-subscriptions/settings.module.css'
2180
2180
  ))
2181
2181
  if (typeof un2 === 'function') cleanups.push(un2)
2182
2182
 
2183
- const un3 = ctx.slots.inject('settings.plugin.item', () => ctx.slots.register(
2184
- {
2185
- name: 'settings.plugin.item',
2186
- key: NS,
2187
- locale: NS,
2188
- inject: () => ({ ctx: ctx }),
2189
- },
2190
- PluginCard,
2191
- ))
2192
- if (typeof un3 === 'function') cleanups.push(un3)
2193
2183
  }
2194
2184
  } catch {
2195
2185
  // Slot system not available — expected on headless / non-web profiles.
@@ -1,6 +1,14 @@
1
1
  import z from '@deepseek-ai/schemastery'
2
2
  import { PROVIDERS } from './refs.js'
3
3
 
4
+ // Defensive polyfill for environments / runners with schemastery < 3.18.4
5
+ const proto = z?.Schema?.prototype || (typeof z?.number === "function" ? Object.getPrototypeOf(z.number()) : null)
6
+ if (proto && typeof proto.volatile !== "function") {
7
+ proto.volatile = function volatile() {
8
+ return typeof this.extra === "function" ? this.extra("volatile", true) : this
9
+ }
10
+ }
11
+
4
12
  export const Slot = z.object({
5
13
  provider: z.string().default('codex')
6
14
  .description('One of: codex, claude, grok, antigravity.'),
@@ -16,68 +24,77 @@ export const Slot = z.object({
16
24
 
17
25
  export const defaultSlots = PROVIDERS.map((provider) => ({ provider, index: 1, label: '' }))
18
26
 
27
+ // Unwrap Volatile boxes before any caller reads a value. A volatile field holds a box
28
+ // rather than its value, and the Loader mutates those boxes in place.
29
+ export function plainConfig(value) {
30
+ if (value === null || typeof value !== 'object') return value
31
+ if (Array.isArray(value)) return value.map(plainConfig)
32
+ if (typeof value.get === 'function') return plainConfig(value.get())
33
+ return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, plainConfig(v)]))
34
+ }
35
+
19
36
  export const Config = z.object({
20
- cooldownMs: z.number().default(30 * 60 * 1000)
37
+ cooldownMs: z.number().volatile().default(30 * 60 * 1000)
21
38
  .description('After RATE_LIMIT/QUOTA/429, skip that account for this many milliseconds.'),
22
- switchAtRemaining: z.number().default(0.01)
39
+ switchAtRemaining: z.number().volatile().default(0.01)
23
40
  .description('If remaining <= this (absolute or <1 fraction), treat as exhausted before request. 0 disables.'),
24
- refreshAheadMs: z.number().default(5 * 60 * 1000)
41
+ refreshAheadMs: z.number().volatile().default(5 * 60 * 1000)
25
42
  .description('Background refresh when expiry within this many ms.'),
26
- refreshRetryMs: z.number().default(10 * 60 * 1000)
43
+ refreshRetryMs: z.number().volatile().default(10 * 60 * 1000)
27
44
  .description('Do not retry background refresh more often than this after failure.'),
28
- probeIntervalMin: z.number().default(15)
45
+ probeIntervalMin: z.number().volatile().default(15)
29
46
  .description('Background health-check interval in minutes. 0 disables.'),
30
- notifyLimits: z.boolean().default(true)
47
+ notifyLimits: z.boolean().volatile().default(true)
31
48
  .description('Emit log notices when usage crosses 70/90/100% of a window.'),
32
- expiryNotifyDays: z.number().default(7)
49
+ expiryNotifyDays: z.number().volatile().default(7)
33
50
  .description('#67 Warn in the header chip this many days before a subscription expiry (expiresAt). 0 disables.'),
34
- privacyMask: z.boolean().default(false)
51
+ privacyMask: z.boolean().volatile().default(false)
35
52
  .description('#98 Hide personal data in the UI: emails show as j***n@example.com.'),
36
- slots: z.array(Slot).default(defaultSlots)
53
+ slots: z.array(Slot).volatile().default(defaultSlots)
37
54
  .description('Account slots. Secrets are not stored here; only the credential ref names.'),
38
- useWebCallback: z.boolean().default(false)
55
+ useWebCallback: z.boolean().volatile().default(false)
39
56
  .description('When on, redirect_uri is this Web UI origin + /dsh-subscriptions/oauth/callback. When off, the vendor CLI registered redirect is used and you paste the redirected URL.'),
40
- autoLoopback: z.boolean().default(true)
57
+ autoLoopback: z.boolean().volatile().default(true)
41
58
  .description('#89 When on and the vendor redirect_uri is a loopback address (codex :1455, grok :56121), a temporary local server catches the OAuth callback automatically - no paste needed. Paste fallback stays available.'),
42
- ollamaBaseUrl: z.string().default('http://127.0.0.1:11434')
59
+ ollamaBaseUrl: z.string().volatile().default('http://127.0.0.1:11434')
43
60
  .description('#91 Local Ollama base URL. Served as the ollama provider in the native model picker when reachable.'),
44
- ollamaFallback: z.boolean().default(true)
61
+ ollamaFallback: z.boolean().volatile().default(true)
45
62
  .description('#91 When every account of a provider is exhausted, continue the chat on local Ollama instead of failing.'),
46
- ollamaFallbackModel: z.string().default('')
63
+ ollamaFallbackModel: z.string().volatile().default('')
47
64
  .description('#91 Ollama model used for the fallback (for example qwen2.5-coder). Empty = first model from /api/tags.'),
48
- hideDeprecatedModels: z.boolean().default(false)
65
+ hideDeprecatedModels: z.boolean().volatile().default(false)
49
66
  .description('#94 Hide test/preview/beta/legacy model ids from the native model picker.'),
50
- codexClientId: z.string().default(''),
51
- codexVerbosity: z.string().default('')
67
+ codexClientId: z.string().volatile().default(''),
68
+ codexVerbosity: z.string().volatile().default('')
52
69
  .description('#93 Response verbosity for Codex reasoning models: low, medium or high. Empty = protocol default.'),
53
- codexFastMode: z.boolean().default(false)
70
+ codexFastMode: z.boolean().volatile().default(false)
54
71
  .description('#92 Fast Mode for Codex: sends service_tier priority (1.5x speed billing tier) with every request.'),
55
- composerQuota: z.string().default('off')
72
+ composerQuota: z.string().volatile().default('off')
56
73
  .description('#84 Composer quota indicator mode: off, percent, bar or forecast (predictive runway from a sliding window).'),
57
- codexRedirectUri: z.string().default(''),
58
- codexBaseUrl: z.string().default(''),
59
- codexClientVersion: z.string().default('')
74
+ codexRedirectUri: z.string().volatile().default(''),
75
+ codexBaseUrl: z.string().volatile().default(''),
76
+ codexClientVersion: z.string().volatile().default('')
60
77
  .description('Codex CLI identity version for /models catalog gating (e.g. 0.157.1). Empty uses the built-in default.'),
61
- claudeClientId: z.string().default(''),
62
- claudeRedirectUri: z.string().default(''),
63
- grokClientId: z.string().default(''),
64
- grokRedirectUri: z.string().default(''),
65
- grokBaseUrl: z.string().default(''),
66
- grokClientVersion: z.string().default('')
78
+ claudeClientId: z.string().volatile().default(''),
79
+ claudeRedirectUri: z.string().volatile().default(''),
80
+ grokClientId: z.string().volatile().default(''),
81
+ grokRedirectUri: z.string().volatile().default(''),
82
+ grokBaseUrl: z.string().volatile().default(''),
83
+ grokClientVersion: z.string().volatile().default('')
67
84
  .description('Grok CLI identity version header. Empty uses the built-in default.'),
68
- antigravityClientId: z.string().default(''),
69
- antigravityRedirectUri: z.string().default(''),
70
- cascadingFallback: z.boolean().default(false)
85
+ antigravityClientId: z.string().volatile().default(''),
86
+ antigravityRedirectUri: z.string().volatile().default(''),
87
+ cascadingFallback: z.boolean().volatile().default(false)
71
88
  .description('#349 Opt-in cross-vendor fallback when all accounts of a provider are exhausted or rate limited.'),
72
- cascadingChain: z.any().default({})
89
+ cascadingChain: z.any().volatile().default({})
73
90
  .description('#349 Custom cross-vendor fallback chains (e.g. { claude: ["copilot", "cursor"] }).'),
74
- webhookUrl: z.string().default('')
91
+ webhookUrl: z.string().volatile().default('')
75
92
  .description('#351 External webhook URL to receive quota and session alerts (JSON POST).'),
76
- alertThresholds: z.array(z.number()).default([80, 90, 95])
93
+ alertThresholds: z.array(z.number()).volatile().default([80, 90, 95])
77
94
  .description('#351 Quota usage percentage thresholds for firing alerts.'),
78
- autoPacing: z.boolean().default(true)
95
+ autoPacing: z.boolean().volatile().default(true)
79
96
  .description('#352 Predictive burn-rate load balancing between accounts.'),
80
- customVendors: z.array(z.any()).default([])
97
+ customVendors: z.array(z.any()).volatile().default([])
81
98
  .description('Declarative OpenAI-Responses-compatible providers. See README.'),
82
99
  })
83
100
 
@@ -2,7 +2,7 @@ import { maskText } from './mask.js'
2
2
  import { normalizeSlots } from './accounts.js'
3
3
  import { PROVIDERS } from './refs.js'
4
4
 
5
- export function scrubReport(v) {
5
+ function scrubReport(v) {
6
6
  if (typeof v === 'string') return maskText(v)
7
7
  if (Array.isArray(v)) return v.map(scrubReport)
8
8
  if (v && typeof v === 'object') {
package/lib/index.js CHANGED
@@ -1,3 +1,5 @@
1
+ import { plainConfig } from './config-schema.js'
2
+ export { plainConfig } from './config-schema.js'
1
3
  import { readFileSync } from 'node:fs'
2
4
  import { bestEffort } from './utils.js'
3
5
  export { Config, publicConfig, Slot, defaultSlots } from './config-schema.js'
@@ -66,9 +68,14 @@ const OK_HTML = `<!doctype html>
66
68
 
67
69
  export function apply(ctx, rawConfig) {
68
70
  const logger = typeof ctx.logger === 'function' ? ctx.logger('subscriptions') : (ctx.logger || undefined)
69
- let settingsApi
70
71
 
71
- let cachedConfig = Config(rawConfig || {})
72
+ // rawConfig still holds Volatile boxes, so unwrap before validating.
73
+ const resolveEntry = () => {
74
+ // rawConfig still holds Volatile boxes, so unwrap before validating; Config then
75
+ // re-creates boxes for the volatile fields, so unwrap the result as well.
76
+ return plainConfig(Config(plainConfig(rawConfig || {})))
77
+ }
78
+ let cachedConfig = resolveEntry()
72
79
  const live = () => cachedConfig
73
80
 
74
81
  function syncCustomVendors() {
@@ -95,65 +102,93 @@ export function apply(ctx, rawConfig) {
95
102
 
96
103
  const syncSnapshot = (next) => {
97
104
  try {
98
- const fresh = next !== undefined ? next : (typeof settingsApi?.get === 'function' ? settingsApi.get() : cachedConfig)
99
- cachedConfig = Config(fresh || {})
105
+ const fresh = next !== undefined ? next : resolveEntry()
106
+ cachedConfig = fresh
100
107
  syncCustomVendors()
101
108
  syncAdapter().catch(() => {})
102
109
  syncOllama().catch(() => {})
103
110
  } catch { /* keep existing snapshot on error */ }
104
111
  }
105
112
 
106
- if (typeof ctx.inject === 'function') {
107
- ctx.inject(['settings'], (sctx) => {
108
- const scope = sctx.settings.register(NS, Config, { base: rawConfig })
109
- settingsApi = {
110
- ...scope,
111
- get: () => (typeof scope.get === 'function' ? scope.get() : cachedConfig),
112
- async replace(next) {
113
- syncSnapshot(next)
114
- if (typeof scope.replace === 'function') {
115
- return await scope.replace(next)
116
- }
117
- },
118
- }
113
+ // settings.register was removed before DSH 0.1.7-rc.2, so the scope this plugin used
114
+ // to build from it never existed: getSettingsApi() returned null and writes only
115
+ // touched memory. Back it with the real settings service instead, whose
116
+ // describe()/update()/replace() are the contract both releases share.
117
+ let settingsSvc = null
119
118
 
120
- if (typeof scope.get === 'function') {
121
- syncSnapshot(scope.get())
122
- }
119
+ const describeRow = () => {
120
+ if (typeof settingsSvc?.describe !== 'function') return null
121
+ try {
122
+ return settingsSvc.describe().find((r) => r && r.ns === NS) || null
123
+ } catch (err) {
124
+ if (logger && logger.warn) logger.warn('[dsh-subscriptions] describe failed: ' + String(err && err.message || err))
125
+ return null
126
+ }
127
+ }
123
128
 
124
- if (typeof scope.watch === 'function') {
125
- try {
126
- const unwatch = scope.watch((next) => syncSnapshot(next))
127
- if (typeof unwatch === 'function') {
128
- sctx.effect(() => unwatch)
129
- }
130
- } catch { /* ignore watch error */ }
129
+ const settingsApi = {
130
+ get: () => live(),
131
+ replace: async (next) => {
132
+ const parsed = plainConfig(next)
133
+ cachedConfig = parsed
134
+ syncCustomVendors()
135
+ if (typeof settingsSvc?.update === 'function') {
136
+ await settingsSvc.update(NS, structuredClone(parsed), describeRow()?.revision)
137
+ } else if (typeof settingsSvc?.replace === 'function') {
138
+ await settingsSvc.replace(NS, structuredClone(parsed), describeRow()?.revision)
131
139
  }
140
+ return parsed
141
+ },
142
+ }
143
+ // Consumers (routes, adapter manager) call getSettingsApi().replace(config). The host
144
+ // service offers describe/update/replace with a revision, so adapt it here: the adapter
145
+ // keeps the cached snapshot current and persists through the real service.
146
+ const getSettingsApi = () => (settingsSvc ? settingsApi : null)
132
147
 
133
- Promise.resolve().then(() => {
134
- reconcileSlots().catch((err) => {
135
- if (logger && logger.warn) logger.warn('[dsh-subscriptions] reconcileSlots initial error: ' + (err && err.message || err))
136
- })
137
- })
138
-
139
- const onDocUpdated = (ns) => {
140
- if (!ns || ns === NS) {
141
- syncSnapshot()
142
- }
148
+ ctx.inject(['settings'], (sctx) => {
149
+ try {
150
+ settingsSvc = sctx.settings || (typeof sctx.get === 'function' ? sctx.get('settings') : null) || null
151
+ if (!settingsSvc) return
152
+ const served = describeRow()
153
+ if (served && served.value) {
154
+ try { syncSnapshot(plainConfig(Config(plainConfig(served.value)))) } catch { /* keep current */ }
143
155
  }
144
- if (typeof sctx.on === 'function') {
145
- sctx.on('settings/document-updated', onDocUpdated)
146
- } else if (typeof ctx.on === 'function') {
147
- ctx.on('settings/document-updated', onDocUpdated)
156
+ if (typeof sctx.effect === 'function') {
157
+ sctx.effect(() => () => { settingsSvc = null })
148
158
  }
159
+ } catch (err) {
160
+ // The profile may be mounted without the settings service; the static config stays
161
+ // authoritative and the volatile-update handler below still applies later changes.
162
+ if (logger && logger.warn) logger.warn('[dsh-subscriptions] settings service unavailable: ' + String(err && err.message || err))
163
+ }
164
+ })
149
165
 
150
- sctx.effect(() => () => {
151
- settingsApi = undefined
152
- cachedConfig = Config(rawConfig || {})
153
- syncCustomVendors()
154
- })
155
- })
166
+ // The host announces configuration changes with settings/document-updated; the Loader
167
+ // also re-announces the volatile boxes in place. Refresh the cached snapshot on both.
168
+ const onSettingsChanged = () => {
169
+ try {
170
+ const row = describeRow()
171
+ if (row && row.value) syncSnapshot(plainConfig(Config(plainConfig(row.value))))
172
+ else syncSnapshot()
173
+ reconcileSlots().catch(() => {})
174
+ } catch { /* keep existing snapshot on error */ }
156
175
  }
176
+ if (typeof ctx.on === 'function') {
177
+ ctx.effect(() => {
178
+ ctx.on('settings/document-updated', onSettingsChanged)
179
+ ctx.on('loader/volatile-update', onSettingsChanged)
180
+ return () => {
181
+ ctx.off?.('settings/document-updated', onSettingsChanged)
182
+ ctx.off?.('loader/volatile-update', onSettingsChanged)
183
+ }
184
+ }, 'dsh-subscriptions: settings change listener')
185
+ }
186
+
187
+ Promise.resolve().then(() => {
188
+ reconcileSlots().catch((err) => {
189
+ if (logger && logger.warn) logger.warn('[dsh-subscriptions] reconcileSlots initial error: ' + (err && err.message || err))
190
+ })
191
+ })
157
192
 
158
193
  const fetchForRef = (ref) => {
159
194
  const parsed = parseOauthRef(ref)
@@ -250,7 +285,7 @@ export function apply(ctx, rawConfig) {
250
285
  fetch,
251
286
  fetchForRef,
252
287
  recordHistory,
253
- getSettingsApi: () => settingsApi,
288
+ getSettingsApi,
254
289
  syncCustomVendors,
255
290
  syncOllama,
256
291
  stripLegacySlots,
@@ -259,6 +294,7 @@ export function apply(ctx, rawConfig) {
259
294
  adapterManager.registerAdapterLifecycle()
260
295
 
261
296
  const subscriptions = createSubscriptionsService({
297
+ live,
262
298
  listAccounts: (provider) => store.listAccounts(provider),
263
299
  loadBlob: (ref) => store.loadBlob(ref),
264
300
  ensureFresh: (provider, blob, ref) => store.ensureFresh(provider, blob, ref),
@@ -437,7 +473,7 @@ export function apply(ctx, rawConfig) {
437
473
  NS,
438
474
  live,
439
475
  accountsView,
440
- getSettingsApi: () => settingsApi,
476
+ getSettingsApi,
441
477
  syncCustomVendors,
442
478
  syncAdapter,
443
479
  stripLegacySlots,
package/lib/jwt.js CHANGED
@@ -1,4 +1,4 @@
1
- export function decodeJwtPayload(token) {
1
+ function decodeJwtPayload(token) {
2
2
  const raw = String(token || '')
3
3
  const parts = raw.split('.')
4
4
  if (parts.length < 2) return null
package/lib/messages.js CHANGED
@@ -102,7 +102,7 @@ export function openaiTools(options) {
102
102
  }))
103
103
  }
104
104
 
105
- export function reconcileResponsesInput(items) {
105
+ function reconcileResponsesInput(items) {
106
106
  const result = []
107
107
  const callsSeen = new Set()
108
108
  const outputsSeen = new Set()
@@ -1,6 +1,6 @@
1
1
  import { exportVault, importVault } from '../vault.js'
2
2
  import { normalizeSlots } from '../accounts.js'
3
- import { Config, publicConfig } from '../config-schema.js'
3
+ import { Config, plainConfig, publicConfig } from '../config-schema.js'
4
4
  import { decryptWithPassphrase, encryptWithPassphrase } from '../crypto.js'
5
5
  import { isTrustedSettingsRequest, queryOf, readBody, safeJsonHandler, writeJson } from '../http.js'
6
6
  import { discoverLocalCliSessions, loadLocalCliBlob } from '../import-auth.js'
@@ -159,7 +159,7 @@ export function registerAccountsRoutes(ctx, state) {
159
159
  index: idx,
160
160
  label: blob.email || '',
161
161
  }])
162
- const parsed = Config({ ...live(), slots: stripLegacySlots(nextSlots) })
162
+ const parsed = plainConfig(Config({ ...live(), slots: stripLegacySlots(nextSlots) }))
163
163
  await getSettingsApi().replace(parsed)
164
164
  syncCustomVendors()
165
165
  }
@@ -424,7 +424,7 @@ export function registerAccountsRoutes(ctx, state) {
424
424
  curSlots.push(s)
425
425
  }
426
426
  }
427
- parsed = Config({ ...live(), slots: stripLegacySlots(curSlots) })
427
+ parsed = plainConfig(Config({ ...live(), slots: stripLegacySlots(curSlots) }))
428
428
  }
429
429
  let blobCount = 0
430
430
  for (const [ref, blob] of Object.entries(result.blobs)) {
@@ -1,6 +1,6 @@
1
1
  import { getRecentAlerts, clearAlerts } from '../alerts.js'
2
2
  import { normalizeSlots, vendorConfig } from '../accounts.js'
3
- import { Config, publicConfig } from '../config-schema.js'
3
+ import { Config, plainConfig, publicConfig } from '../config-schema.js'
4
4
  import { isTrustedSettingsRequest, queryOf, readBody, safeJsonHandler, writeJson } from '../http.js'
5
5
  import { quotaSnapshot } from '../ratelimit.js'
6
6
  import { PROVIDERS, displayName, droppedCredentialRefs, isProvider, oauthRef } from '../refs.js'
@@ -80,7 +80,7 @@ export function registerStatusRoutes(ctx, state) {
80
80
  if (payload && typeof payload.config === 'object') payload = payload.config
81
81
  try {
82
82
  if (Array.isArray(payload.slots)) payload.slots = stripLegacySlots(payload.slots)
83
- const parsed = Config(payload)
83
+ const parsed = plainConfig(Config(payload))
84
84
  const dropped = droppedCredentialRefs(live().slots, parsed.slots)
85
85
  await getSettingsApi().replace(parsed)
86
86
  currentRevision++
@@ -58,6 +58,9 @@ function headersFor(provider, blob, cfg) {
58
58
 
59
59
  export function createSubscriptionsService(deps) {
60
60
  return {
61
+ // The resolved config snapshot. Cached: identical between loader/volatile-update
62
+ // events, which is the guarantee issue #367 calls for.
63
+ live: () => deps.live(),
61
64
  async available(provider) {
62
65
  if (provider) {
63
66
  if (!isProvider(provider)) return false
@@ -133,7 +133,7 @@ export async function listModels(blob, cfg) {
133
133
  // Advertise adaptive thinking + effort only for models whose capability table
134
134
  // declares the chosen level; anything else (unsupported family, 'off', no
135
135
  // selection) leaves the request untouched so behavior stays opt-in.
136
- export function applyClaudeThinking(payload, options) {
136
+ function applyClaudeThinking(payload, options) {
137
137
  const effort = options && String(options.reasoningEffort || '')
138
138
  if (!effort || effort === 'off') return payload
139
139
  const levels = effortLevelsForModel(payload && payload.model)
@@ -14,7 +14,7 @@ const USAGE = 'https://chatgpt.com/backend-api/wham/usage'
14
14
  // code_verifier server-side; we finish with the normal PKCE exchange.
15
15
  const DEVICE_USERCODE_URL = 'https://auth.openai.com/api/accounts/deviceauth/usercode'
16
16
  const DEVICE_TOKEN_URL = 'https://auth.openai.com/api/accounts/deviceauth/token'
17
- export const DEVICE_AUTH_URL = 'https://auth.openai.com/codex/device'
17
+ const DEVICE_AUTH_URL = 'https://auth.openai.com/codex/device'
18
18
  const DEVICE_REDIRECT_URI = 'https://auth.openai.com/deviceauth/callback'
19
19
  const SCOPE = 'openid profile email offline_access api.connectors.read api.connectors.invoke'
20
20
  const INSTRUCTIONS = 'You are a coding assistant using a ChatGPT Codex subscription.'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goodandready/dsh-subscriptions",
3
- "version": "0.6.34",
3
+ "version": "0.6.36",
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",
@@ -58,7 +58,7 @@
58
58
  "@deepseek-ai/dsh-host-webserver": "^0.1.7-rc.2 || ^0.2.0-rc.1",
59
59
  "@deepseek-ai/dsh-llm": "^0.1.7-rc.2 || ^0.2.0-rc.1",
60
60
  "@deepseek-ai/dsh-settings": "^0.1.7-rc.2 || ^0.2.0-rc.1",
61
- "@deepseek-ai/schemastery": "^3.18.1"
61
+ "@deepseek-ai/schemastery": "^3.18.4"
62
62
  },
63
63
  "publishConfig": {
64
64
  "access": "public"
@@ -70,6 +70,7 @@
70
70
  "devDependencies": {
71
71
  "@deepseek-ai/dsh-llm": "0.1.0-rc.8",
72
72
  "@types/node": "^22.20.2",
73
- "typescript": "^7.0.2"
73
+ "typescript": "^7.0.2",
74
+ "@deepseek-ai/schemastery": "^3.18.4"
74
75
  }
75
76
  }