@goodandready/dsh-subscriptions 0.6.45 → 0.6.47

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/adapter.js CHANGED
@@ -309,7 +309,19 @@ export class SubscriptionAdapter extends LlmAdapter {
309
309
  }
310
310
  throw err
311
311
  } finally {
312
- if (errorCaught) {
312
+ const isFinishError = finishReason && (
313
+ finishReason.kind === 'error' ||
314
+ finishReason === 'error' ||
315
+ (typeof finishReason === 'object' && Boolean(finishReason.failure))
316
+ )
317
+ if (errorCaught || isFinishError) {
318
+ if (!errorCaught && isFinishError) {
319
+ errorCaught = new Error(
320
+ typeof finishReason === 'object' && finishReason.failure
321
+ ? (finishReason.failure.message || finishReason.failure.code || 'stream finished with error')
322
+ : 'stream finished with error'
323
+ )
324
+ }
313
325
  finalizeOnce('error')
314
326
  } else if (opts && opts._runawayState && opts._runawayState.tripped) {
315
327
  finalizeOnce('runaway')
@@ -2,6 +2,7 @@ import { isProvider } from "./refs.js"
2
2
  import { pickAccount, markCooldown, isSwitchableError, modelFamily } from "./rotate.js"
3
3
  import { quotaSnapshot } from "./ratelimit.js"
4
4
  import { pickFetch } from "./proxy.js"
5
+ import { fetchWithTimeout } from "./http.js"
5
6
 
6
7
  // ponytail: allowlist per provider — add path to extend without touching request logic
7
8
  export const ALLOWLIST = {
@@ -75,7 +76,7 @@ export function createSubscriptionsService(deps) {
75
76
  }
76
77
  return out
77
78
  },
78
- async request({ provider, path, method = "GET", headers = {}, body, signal }) {
79
+ async request({ provider, path, method = "GET", headers = {}, body, signal, timeoutMs }) {
79
80
  if (!isProvider(provider)) {
80
81
  const err = new Error("unknown provider: " + provider)
81
82
  err.code = "PROVIDER"
@@ -144,8 +145,9 @@ export function createSubscriptionsService(deps) {
144
145
  const url = p
145
146
  const extraHeaders = headersFor(provider, blob, cfg)
146
147
  const fetchImpl = pickFetch(deps, account.ref)
148
+ const requestTimeoutMs = Number(timeoutMs) > 0 ? Number(timeoutMs) : 30000
147
149
  const t0 = Date.now()
148
- const res = await fetchImpl(url, {
150
+ const res = await fetchWithTimeout(fetchImpl, url, {
149
151
  method,
150
152
  headers: {
151
153
  Authorization: "Bearer " + blob.accessToken,
@@ -156,7 +158,7 @@ export function createSubscriptionsService(deps) {
156
158
  },
157
159
  body: body && typeof body === "object" && !(body instanceof Uint8Array) && typeof body !== "string" ? JSON.stringify(body) : body,
158
160
  signal,
159
- })
161
+ }, { timeoutMs: requestTimeoutMs })
160
162
  // capture quota
161
163
  try {
162
164
  const snap = quotaSnapshot(provider, res.headers, null, Date.now())
@@ -35,8 +35,8 @@ export function normalizeJsonSchema(schema, depth = 0) {
35
35
  out.type = "object"
36
36
  } else if (schema.items) {
37
37
  out.type = "array"
38
- } else if (schema.anyOf || schema.oneOf || schema.allOf) {
39
- // Schema combinator without explicit type
38
+ } else if (schema.anyOf || schema.oneOf || schema.allOf || schema.$ref || schema.const !== undefined) {
39
+ // Schema combinator, reference, or const without explicit type
40
40
  } else {
41
41
  out.type = depth === 0 ? "object" : "string"
42
42
  }
@@ -53,6 +53,26 @@ export function normalizeJsonSchema(schema, depth = 0) {
53
53
  out.default = schema.default
54
54
  }
55
55
 
56
+ if (typeof schema.$ref === "string") {
57
+ out.$ref = schema.$ref
58
+ }
59
+
60
+ if (schema.const !== undefined) {
61
+ out.const = schema.const
62
+ }
63
+
64
+ const defs = schema.$defs || schema.definitions
65
+ if (defs && typeof defs === "object" && !Array.isArray(defs)) {
66
+ const normDefs = {}
67
+ for (const [k, v] of Object.entries(defs)) {
68
+ if (typeof k === "string" && k) {
69
+ normDefs[k] = normalizeJsonSchema(v, depth + 1)
70
+ }
71
+ }
72
+ if (schema.$defs) out.$defs = normDefs
73
+ if (schema.definitions) out.definitions = normDefs
74
+ }
75
+
56
76
  if (out.type === "object" || (!out.type && (schema.properties || schema.required || schema.additionalProperties !== undefined))) {
57
77
  if (schema.properties && typeof schema.properties === "object" && !Array.isArray(schema.properties)) {
58
78
  out.properties = {}
@@ -1,4 +1,4 @@
1
- import { isTimeoutError } from "../http.js"
1
+ import { isTimeoutError, fetchWithTimeout } from "../http.js"
2
2
  import { randomUUID, createHash } from 'node:crypto'
3
3
  import { buildAuthorizeUrl } from '../oauth.js'
4
4
  import { codexResponsesBody, modelCatalog } from '../messages.js'
@@ -152,9 +152,9 @@ export async function listModels(blob, cfg, fetchImpl) {
152
152
  try {
153
153
  const base = (cfg.baseUrl || defaults().baseUrl).replace(/\/$/, '')
154
154
  const version = cfg.clientVersion || defaults().clientVersion
155
- const res = await impl(`${base}/models?client_version=${encodeURIComponent(version)}`, {
155
+ const res = await fetchWithTimeout(impl, `${base}/models?client_version=${encodeURIComponent(version)}`, {
156
156
  headers: identityHeaders(blob, cfg, { Accept: 'application/json' }),
157
- })
157
+ }, { timeoutMs: 15000 })
158
158
  const json = await readJson(res)
159
159
  const rows = []
160
160
  for (const entry of json.models || json.data || []) {
@@ -186,9 +186,9 @@ export async function check(blob, cfg, fetchImpl) {
186
186
  const base = (cfg.baseUrl || defaults().baseUrl).replace(/\/$/, "")
187
187
  const version = cfg.clientVersion || defaults().clientVersion
188
188
  const impl = fetchImpl || fetch
189
- const res = await impl(base + "/models?client_version=" + encodeURIComponent(version), {
189
+ const res = await fetchWithTimeout(impl, base + "/models?client_version=" + encodeURIComponent(version), {
190
190
  headers: identityHeaders(blob, cfg, { Accept: "application/json" }),
191
- })
191
+ }, { timeoutMs: 15000 })
192
192
  if (!res.ok) throw httpError(res.status, await res.text())
193
193
  const json = await readJson(res)
194
194
  // quota headers may contain limits
@@ -197,7 +197,7 @@ export async function check(blob, cfg, fetchImpl) {
197
197
 
198
198
  export async function usage(blob, cfg, fetchImpl) {
199
199
  try {
200
- const res = await (fetchImpl || fetch)(USAGE, {
200
+ const res = await fetchWithTimeout(fetchImpl || fetch, USAGE, {
201
201
  headers: identityHeaders(blob, cfg, { Accept: 'application/json' }),
202
202
  })
203
203
  const json = await readJson(res)
package/lib/wire.js CHANGED
@@ -88,7 +88,7 @@ export function toTokenUsage(raw) {
88
88
  "inputTokens" in raw || "input_tokens" in raw || "promptTokens" in raw || "prompt_tokens" in raw || "input" in raw ||
89
89
  "outputTokens" in raw || "output_tokens" in raw || "completionTokens" in raw || "completion_tokens" in raw || "output" in raw ||
90
90
  "totalTokens" in raw || "total_tokens" in raw || "totalTokenCount" in raw ||
91
- "promptTokenCount" in raw || "candidatesTokenCount" in raw
91
+ "promptTokenCount" in raw || "candidatesTokenCount" in raw || "thoughtsTokenCount" in raw
92
92
  )
93
93
  if (!hasKnownField) return null
94
94
 
@@ -135,7 +135,8 @@ export function toTokenUsage(raw) {
135
135
  raw.reasoningTokens ??
136
136
  raw.output_token_details?.reasoning_tokens ??
137
137
  raw.completion_tokens_details?.reasoning_tokens ??
138
- raw.reasoning_tokens
138
+ raw.reasoning_tokens ??
139
+ raw.thoughtsTokenCount
139
140
  )
140
141
  const rawTotal = toCount(raw.totalTokens ?? raw.total_tokens ?? raw.totalTokenCount)
141
142
 
@@ -150,7 +151,7 @@ export function toTokenUsage(raw) {
150
151
  if (cacheWriteTokens > 0 || raw.cacheWriteTokens !== undefined || raw.cache_creation_input_tokens !== undefined) {
151
152
  out.cacheWriteTokens = cacheWriteTokens
152
153
  }
153
- if (reasoningTokens > 0 || raw.reasoningTokens !== undefined || raw.output_token_details?.reasoning_tokens !== undefined || raw.completion_tokens_details?.reasoning_tokens !== undefined) {
154
+ if (reasoningTokens > 0 || raw.reasoningTokens !== undefined || raw.output_token_details?.reasoning_tokens !== undefined || raw.completion_tokens_details?.reasoning_tokens !== undefined || raw.thoughtsTokenCount !== undefined) {
154
155
  out.reasoningTokens = reasoningTokens
155
156
  }
156
157
  if (rawTotal > 0 || raw.totalTokens !== undefined || raw.total_tokens !== undefined || raw.totalTokenCount !== undefined) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goodandready/dsh-subscriptions",
3
- "version": "0.6.45",
3
+ "version": "0.6.47",
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",