@goodandready/dsh-subscriptions 0.6.22 → 0.6.24

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/mask.js CHANGED
@@ -30,7 +30,7 @@ export function cleanErrorMessage(raw) {
30
30
  if (parsed.error && parsed.error.message) text = parsed.error.message
31
31
  else if (parsed.message) text = parsed.message
32
32
  else if (parsed.code) text = parsed.code
33
- } catch (err) { /* ignore JSON parse failure */ }
33
+ } catch { /* ignore JSON parse failure */ }
34
34
  }
35
35
  const firstLine = text.split('\n')[0].trim()
36
36
  if (firstLine.length > 160) {
package/lib/ollama.js CHANGED
@@ -37,19 +37,19 @@ export class OllamaAdapter extends LlmAdapter {
37
37
  return { id: provider, name: 'Ollama (local)' }
38
38
  }
39
39
 
40
- providerRetryPolicy(_provider) {
40
+ providerRetryPolicy() {
41
41
  return undefined
42
42
  }
43
43
 
44
- async listModels(provider) {
44
+ async listModels() {
45
45
  return ollamaModels(this.deps.baseUrl(), this.deps.fetchImpl || fetch)
46
46
  }
47
47
 
48
- imageRequestPricing(_provider, _model) {
48
+ imageRequestPricing() {
49
49
  return undefined
50
50
  }
51
51
 
52
- async resolveModel(provider, model, _signal) {
52
+ async resolveModel(provider, model) {
53
53
  return { provider, id: model, name: model }
54
54
  }
55
55
 
@@ -111,6 +111,10 @@ export function registerAccountsRoutes(ctx, state) {
111
111
  writeJson(res, 405, { ok: false, error: { code: 'method', message: 'GET only' } })
112
112
  return
113
113
  }
114
+ if (!isTrustedSettingsRequest(req)) {
115
+ writeJson(res, 403, { ok: false, error: { code: 'forbidden', message: 'same-origin only' } })
116
+ return
117
+ }
114
118
  try {
115
119
  const detected = await discoverLocalCliSessions()
116
120
  writeJson(res, 200, { ok: true, detected })
@@ -250,7 +254,7 @@ export function registerAccountsRoutes(ctx, state) {
250
254
  let bundle
251
255
  try {
252
256
  bundle = JSON.parse(decryptWithPassphrase(encrypted, passphrase))
253
- } catch (e) {
257
+ } catch {
254
258
  writeJson(res, 400, { ok: false, error: { code: 'decrypt', message: 'wrong passphrase or corrupted bundle' } })
255
259
  return
256
260
  }
@@ -21,7 +21,7 @@ export function registerProxyRoutes(ctx, state) {
21
21
  }
22
22
  const raw = await readBody(req, 128 * 1024).catch(() => Buffer.alloc(0))
23
23
  let body = {}
24
- try { body = JSON.parse(raw.toString('utf8') || '{}') } catch (err) { body = {} }
24
+ try { body = JSON.parse(raw.toString('utf8') || '{}') } catch { body = {} }
25
25
  const events = Array.isArray(body && body.events) ? body.events : []
26
26
  const analysis = analyzeSessionEvents(events)
27
27
  writeJson(res, 200, { ok: true, analysis })
@@ -1,4 +1,4 @@
1
- import { getRecentAlerts } from '../alerts.js'
1
+ import { getRecentAlerts, clearAlerts } from '../alerts.js'
2
2
  import { normalizeSlots, vendorConfig } from '../accounts.js'
3
3
  import { Config, publicConfig } from '../config-schema.js'
4
4
  import { isTrustedSettingsRequest, queryOf, readBody, safeJsonHandler, writeJson } from '../http.js'
@@ -22,9 +22,12 @@ export function registerStatusRoutes(ctx, state) {
22
22
  fetchForRef,
23
23
  } = state
24
24
 
25
+ let currentRevision = 1
26
+
25
27
  async function configResponse() {
26
28
  return {
27
29
  ok: true,
30
+ revision: currentRevision,
28
31
  config: publicConfig(live()),
29
32
  accounts: await accountsView(),
30
33
  providers: PROVIDERS.map((id) => ({ id, name: displayName(id) })),
@@ -36,6 +39,10 @@ export function registerStatusRoutes(ctx, state) {
36
39
  path: '/dsh-subscriptions/config',
37
40
  handler: safeJsonHandler(async (req, res) => {
38
41
  if (req.method === 'GET') {
42
+ if (!isTrustedSettingsRequest(req)) {
43
+ writeJson(res, 403, { ok: false, error: { code: 'forbidden', message: 'same-origin only' } })
44
+ return
45
+ }
39
46
  writeJson(res, 200, await configResponse())
40
47
  return
41
48
  }
@@ -56,12 +63,27 @@ export function registerStatusRoutes(ctx, state) {
56
63
  writeJson(res, 400, { ok: false, error: { code: 'json', message: 'invalid json' } })
57
64
  return
58
65
  }
66
+ // #379: revision conflict detection (CAS)
67
+ const baseRev = payload?.revision != null ? Number(payload.revision) : (req.headers?.['if-match'] ? Number(req.headers['if-match']) : null)
68
+ if (baseRev != null && !Number.isNaN(baseRev) && baseRev !== currentRevision) {
69
+ writeJson(res, 409, {
70
+ ok: false,
71
+ error: {
72
+ code: 'conflict',
73
+ message: 'configuration has been modified concurrently',
74
+ currentRevision,
75
+ },
76
+ })
77
+ return
78
+ }
79
+
59
80
  if (payload && typeof payload.config === 'object') payload = payload.config
60
81
  try {
61
82
  if (Array.isArray(payload.slots)) payload.slots = stripLegacySlots(payload.slots)
62
83
  const parsed = Config(payload)
63
84
  const dropped = droppedCredentialRefs(live().slots, parsed.slots)
64
85
  await getSettingsApi().replace(parsed)
86
+ currentRevision++
65
87
  syncCustomVendors()
66
88
  for (const ref of dropped) await store.clearRef(ref)
67
89
  await syncAdapter()
@@ -77,8 +99,17 @@ export function registerStatusRoutes(ctx, state) {
77
99
  kind: 'exact',
78
100
  path: '/dsh-subscriptions/status',
79
101
  handler: safeJsonHandler(async (req, res) => {
102
+ if (req.method === 'DELETE') {
103
+ if (!isTrustedSettingsRequest(req)) {
104
+ writeJson(res, 403, { ok: false, error: { code: 'forbidden', message: 'same-origin only' } })
105
+ return
106
+ }
107
+ clearAlerts()
108
+ writeJson(res, 200, { ok: true })
109
+ return
110
+ }
80
111
  if (req.method !== 'GET') {
81
- writeJson(res, 405, { ok: false, error: { code: 'method', message: 'GET only' } })
112
+ writeJson(res, 405, { ok: false, error: { code: 'method', message: 'GET or DELETE only' } })
82
113
  return
83
114
  }
84
115
  if (!isTrustedSettingsRequest(req)) {
@@ -101,7 +132,7 @@ export function registerStatusRoutes(ctx, state) {
101
132
  expires[slot.provider] = Math.max(expires[slot.provider] || 0, slot.expiresAt)
102
133
  labels[slot.provider] = pmL(slot.label || info.label || slot.provider)
103
134
  }
104
- } catch (err) { /* ignore status inspection error */ }
135
+ } catch { /* ignore status inspection error */ }
105
136
  }
106
137
  // #69/#72: active subscription = last successful request (newest first).
107
138
  let active = null
@@ -124,7 +155,7 @@ export function registerStatusRoutes(ctx, state) {
124
155
  .filter((w) => w && typeof w.usedPercent === 'number')
125
156
  .map((w) => ({ id: w.id || w.en || w.ru, label: w.en || w.ru || w.id, usedPercent: w.usedPercent }))
126
157
  }
127
- } catch (err) { /* ignore status inspection error */ }
158
+ } catch { /* ignore status inspection error */ }
128
159
  active = {
129
160
  provider: lastRow.provider,
130
161
  index,
@@ -160,6 +191,10 @@ export function registerStatusRoutes(ctx, state) {
160
191
  writeJson(res, 405, { ok: false, error: { code: 'method', message: 'GET only' } })
161
192
  return
162
193
  }
194
+ if (!isTrustedSettingsRequest(req)) {
195
+ writeJson(res, 403, { ok: false, error: { code: 'forbidden', message: 'same-origin only' } })
196
+ return
197
+ }
163
198
  writeJson(res, 200, { ok: true, report: await diagnosticsReport() })
164
199
  }),
165
200
  }), 'dsh-subscriptions: /diagnostics')
@@ -224,7 +259,7 @@ export function registerStatusRoutes(ctx, state) {
224
259
  try {
225
260
  const snap = quotaSnapshot(provider, res2.headers, null, Date.now())
226
261
  if (snap) { capturedQuota = snap; store.rememberQuota(ref, snap) }
227
- } catch (err) { /* ignore status inspection error */ }
262
+ } catch { /* ignore status inspection error */ }
228
263
  return res2
229
264
  }
230
265
  try {
@@ -302,7 +337,7 @@ export function registerStatusRoutes(ctx, state) {
302
337
  return
303
338
  }
304
339
  let payload = {}
305
- try { payload = JSON.parse((await readBody(req, 64 * 1024)).toString('utf8') || '{}') } catch (err) { /* ignore status inspection error */ }
340
+ try { payload = JSON.parse((await readBody(req, 64 * 1024)).toString('utf8') || '{}') } catch { /* ignore status inspection error */ }
306
341
 
307
342
  let provider = payload.provider
308
343
  let index = payload.index || 1
@@ -351,7 +386,7 @@ export function registerStatusRoutes(ctx, state) {
351
386
  try {
352
387
  const snap = quotaSnapshot(provider, res2.headers, null, Date.now())
353
388
  if (snap) { capturedQuota = snap; store.rememberQuota(ref, snap) }
354
- } catch (err) { /* ignore status inspection error */ }
389
+ } catch { /* ignore status inspection error */ }
355
390
  return res2
356
391
  }
357
392
  try {
package/lib/routes.js CHANGED
@@ -10,7 +10,6 @@ import { registerPluginUpdater } from './updater.js'
10
10
  export function registerRoutes(ctx, state) {
11
11
  const {
12
12
  accountsView,
13
- history,
14
13
  live,
15
14
  } = state
16
15
 
@@ -41,11 +41,3 @@ export function getPinnedAccountRef(sessionId) {
41
41
  }
42
42
  return entry.accountRef
43
43
  }
44
-
45
- export function unpinSession(sessionId) {
46
- if (sessionId) sessionPins.delete(sessionId)
47
- }
48
-
49
- export function clearSessionPins() {
50
- sessionPins.clear()
51
- }
@@ -1,7 +1,6 @@
1
1
  import { isProvider } from "./refs.js"
2
2
  import { pickAccount, markCooldown, isSwitchableError, modelFamily } from "./rotate.js"
3
3
  import { quotaSnapshot } from "./ratelimit.js"
4
- import { getVendor } from "./vendors/index.js"
5
4
  import { pickFetch } from "./proxy.js"
6
5
 
7
6
  // ponytail: allowlist per provider — add path to extend without touching request logic
@@ -20,7 +19,7 @@ export function isAllowed(provider, path) {
20
19
  p = p.split("?")[0].split("#")[0]
21
20
  try {
22
21
  if (p.startsWith("http://") || p.startsWith("https://")) p = new URL(p).pathname
23
- } catch (err) { /* ignore subscriptions error */ }
22
+ } catch { /* ignore subscriptions error */ }
24
23
  if (!p.startsWith("/")) p = "/" + p
25
24
  for (const allowed of list) {
26
25
  const a = allowed.split("?")[0]
@@ -140,17 +139,17 @@ export function createSubscriptionsService(deps) {
140
139
  try {
141
140
  const snap = quotaSnapshot(provider, res.headers, null, Date.now())
142
141
  if (snap && typeof deps.rememberQuota === "function") deps.rememberQuota(account.ref, snap)
143
- } catch (err) { /* ignore subscriptions error */ }
142
+ } catch { /* ignore subscriptions error */ }
144
143
  if (!res.ok && res.clone) {
145
144
  try {
146
145
  const txt = await res.clone().text()
147
146
  let j = null
148
- try { j = JSON.parse(txt) } catch (err) { /* ignore subscriptions error */ }
147
+ try { j = JSON.parse(txt) } catch { /* ignore subscriptions error */ }
149
148
  if (j) {
150
149
  const snap2 = quotaSnapshot(provider, res.headers, j, Date.now())
151
150
  if (snap2 && typeof deps.rememberQuota === "function") deps.rememberQuota(account.ref, snap2)
152
151
  }
153
- } catch (err) { /* ignore subscriptions error */ }
152
+ } catch { /* ignore subscriptions error */ }
154
153
  }
155
154
  if (!res.ok) {
156
155
  const txt = await res.text().catch(() => "")
@@ -174,7 +173,7 @@ export function createSubscriptionsService(deps) {
174
173
  ms: Date.now() - t0,
175
174
  kind: "request",
176
175
  })
177
- } catch (err) { /* ignore subscriptions error */ }
176
+ } catch { /* ignore subscriptions error */ }
178
177
  }
179
178
  return res
180
179
  } catch (err) {
package/lib/vault.js CHANGED
@@ -50,14 +50,14 @@ export function importVault(encryptedPayload, passphrase) {
50
50
  let jsonStr
51
51
  try {
52
52
  jsonStr = decryptWithPassphrase(raw, pass)
53
- } catch (err) {
53
+ } catch {
54
54
  throw new Error('failed to decrypt vault: invalid passphrase or corrupted payload')
55
55
  }
56
56
 
57
57
  let data
58
58
  try {
59
59
  data = JSON.parse(jsonStr)
60
- } catch (err) {
60
+ } catch {
61
61
  throw new Error('malformed vault content: invalid JSON')
62
62
  }
63
63
 
@@ -12,7 +12,6 @@ import { buildAuthorizeUrl } from "./oauth.js"
12
12
  import { codexResponsesBody, modelCatalog } from "./messages.js"
13
13
  import { formTokenRequest, jsonTokenRequest, codexResponsesStream, readJson, tokenBlobFromOAuth, httpError } from "./wire.js"
14
14
  import { emailFromToken } from "./jwt.js"
15
- import { asUsageSnapshot } from "./usage.js"
16
15
 
17
16
  function defaultModels(models) {
18
17
  return modelCatalog("custom", (models || []).map((row) => (typeof row === "string" ? { id: row, name: row } : row)))
@@ -152,7 +152,7 @@ export async function usage(blob, _cfg, fetchImpl) {
152
152
  return retrieveQuotaPercent(fetchImpl || fetch, blob.accessToken, headersFor(blob.projectId || ''))
153
153
  }
154
154
 
155
- export async function* streamOnce({ blob, options, fetchImpl, headers, config, signal, saveBlob }) {
155
+ export async function* streamOnce({ blob, options, fetchImpl, headers, signal, saveBlob }) {
156
156
  // Abort must cover the project pre-flight as well as the stream.
157
157
  const preflight = (u, i) => fetchImpl(u, { ...(i || {}), signal })
158
158
  const ready = await withProject(blob, preflight, saveBlob)
@@ -30,14 +30,15 @@ export function defaults() {
30
30
  baseUrl: 'https://chatgpt.com/backend-api/codex',
31
31
  originator: 'codex_cli_rs',
32
32
  models: [
33
+ { id: 'gpt-6-astra', name: 'GPT-6 Astra' },
34
+ { id: 'gpt-6-sol', name: 'GPT-6 Sol' },
35
+ { id: 'gpt-6-luna', name: 'GPT-6 Luna' },
36
+ { id: 'gpt-5.6-sol', name: 'GPT-5.6 Sol' },
37
+ { id: 'gpt-5.6-terra', name: 'GPT-5.6 Terra' },
33
38
  { 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' },
36
- { id: 'gpt-5.1-codex', name: 'GPT-5.1 Codex' },
37
- { id: 'gpt-5.1-codex-mini', name: 'GPT-5.1 Codex Mini' },
38
- { id: 'gpt-5.1', name: 'GPT-5.1' },
39
+ { id: 'gpt-5.5', name: 'GPT-5.5' },
39
40
  ],
40
- clientVersion: '0.147.0',
41
+ clientVersion: '0.157.1',
41
42
  }
42
43
  }
43
44
 
@@ -174,7 +175,7 @@ export async function listModels(blob, cfg, fetchImpl) {
174
175
  }
175
176
  if (!rows.length) throw new Error('empty codex catalog')
176
177
  rows.sort((a, b) => a.priority - b.priority)
177
- return modelCatalog(id, rows.map(({ priority, ...row }) => row))
178
+ return modelCatalog(id, rows.map((row) => { const copy = { ...row }; delete copy.priority; return copy }))
178
179
  } catch { /* catalog fallback */ }
179
180
  return catalog
180
181
  }
@@ -220,7 +220,7 @@ export async function usage(blob, cfg, fetchImpl) {
220
220
  const decoded = decodeGrokCreditsFrame(Buffer.from(buf))
221
221
  if (decoded != null) pct = decoded
222
222
  }
223
- } catch (err) { /* ignore grok usage capture error */ }
223
+ } catch { /* ignore grok usage capture error */ }
224
224
  }
225
225
  const snap = asUsageSnapshot(pct)
226
226
  if (snap) snap.windows = usageWindows(json)
@@ -14,7 +14,6 @@ import * as grok from './grok.js'
14
14
  import * as antigravity from './antigravity.js'
15
15
  import * as kimi from './kimi.js'
16
16
  import * as glm from './glm.js'
17
- import { createVendorFromProfile } from '../vendor-factory.js'
18
17
 
19
18
  const builtins = {
20
19
  codex,
@@ -1,6 +1,6 @@
1
1
  import { LlmError } from '@deepseek-ai/dsh-llm'
2
2
  import { openaiMessages, openaiTools } from '../messages.js'
3
- import { openaiChatStream, readJson, httpError } from '../wire.js'
3
+ import { openaiChatStream, httpError } from '../wire.js'
4
4
  import { asUsageSnapshot } from '../usage.js'
5
5
 
6
6
  export const id = 'qwen'
@@ -1,6 +1,6 @@
1
1
  import { LlmError } from '@deepseek-ai/dsh-llm'
2
2
  import { openaiMessages } from '../messages.js'
3
- import { openaiChatStream, readJson, httpError } from '../wire.js'
3
+ import { openaiChatStream, httpError } from '../wire.js'
4
4
  import { asUsageSnapshot } from '../usage.js'
5
5
 
6
6
  export const id = 'spark'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goodandready/dsh-subscriptions",
3
- "version": "0.6.22",
3
+ "version": "0.6.24",
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",
@@ -15,6 +15,8 @@
15
15
  "lib/",
16
16
  "cordis.patch.yml",
17
17
  "README.md",
18
+ "README.ru.md",
19
+ "README.zh.md",
18
20
  "LICENSE"
19
21
  ],
20
22
  "keywords": [