@magpie-community/opencode-qoder-auth 0.2.17 → 0.2.19

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 (3) hide show
  1. package/README.md +33 -0
  2. package/index.mjs +16 -7
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -247,6 +247,39 @@ International campaign discovery was checked with a real account on
247
247
  omitting either header hid them. This verification only queried the
248
248
  activity; a live claim was not performed.
249
249
 
250
+ ## Credits available, but a request is refused
251
+
252
+ A positive credit balance does not guarantee a queue slot or access to
253
+ every model. Use the model ids listed for that particular account and
254
+ region. Do not infer another account's permission from its balance.
255
+
256
+ Qoder code **10605**, including `RESOURCE_LIMIT`, `serviceAvailable` or
257
+ `isQueued` details, is a resource/queue answer. Keep its suggested retry
258
+ delay; repeatedly signing in does not free that queue. Since **0.2.18**
259
+ ([#185](https://github.com/magpie-community/plugins/pull/185)), a 10605 led
260
+ by a BOM or wrapped in an outer message is read as busy too; see
261
+ [#167](https://github.com/magpie-community/plugins/issues/167) for the broader report.
262
+ That parser fix does not remove the provider's limit or prove that every
263
+ live 401 had the same cause. A timeout likewise does not establish an
264
+ empty wallet or an expired login.
265
+
266
+ For a bounded offline reproduction, run `packages/qoder/busy.test.mjs`
267
+ from #185 in an isolated temporary HOME. It compares plain queue JSON,
268
+ BOM-prefixed JSON and known message wrappers, keeping explicit login and
269
+ credit failures distinct. Extracting a JSON body from a translated log
270
+ cannot recover the original wire bytes; synthetic wrappers demonstrate
271
+ parser behavior, not which wrapper a historical request actually used.
272
+ After selecting a local fix with magpie's `plugin use`, confirm which spec
273
+ serves `qoder` in `plugin list`; a patched checkout alone does not change
274
+ the running provider.
275
+
276
+
277
+ For native check-in, an existing `CLAIMED` result is not a new credit grant.
278
+ The host's recheck/cache behavior is tracked in
279
+ [magpie#2696](https://github.com/yetone/magpie/issues/2696). Verify a new
280
+ claim against the vendor record; a successful activity query alone is not
281
+ proof that credits arrived.
282
+
250
283
  ## Not here
251
284
 
252
285
  - Qoder's usage and quota display.
package/index.mjs CHANGED
@@ -799,16 +799,26 @@ function failure(status, text) {
799
799
  let code = ""
800
800
  let queue
801
801
  try {
802
- const j = JSON.parse(text)
802
+ let j = JSON.parse(msg)
803
+ // #167: an HTTP/message wrapper can hold the actual 10605 body.
804
+ // Unwrap only that known queue code; a real auth/credit/request code
805
+ // on the outer body remains authoritative.
806
+ const outerCode = j?.code == null ? "" : String(j.code)
807
+ if (["", "401", "403"].includes(outerCode) && typeof j?.message === "string") {
808
+ try {
809
+ const inner = JSON.parse(j.message.trim())
810
+ if (inner && !Array.isArray(inner) && String(inner.code) === QUEUED) j = inner
811
+ } catch {}
812
+ }
803
813
  code = j?.code == null ? "" : String(j.code)
804
814
  if (j?.message) msg = String(j.message)
805
815
  // details is a JSON string, or the object itself (gjson reads either)
806
- const d = typeof j?.details === "string" ? JSON.parse(j.details) : j?.details
816
+ const d = typeof j?.details === "string" ? JSON.parse(j.details.trim()) : j?.details
807
817
  if (d?.error?.message) msg += ": " + d.error.message
808
818
  // the queue's state: the message itself, as a JSON string, or a field
809
819
  queue = j?.queue && typeof j.queue === "object" ? j.queue : undefined
810
820
  if (!queue && typeof j?.message === "string" && j.message.trim().startsWith("{")) {
811
- const q = JSON.parse(j.message)
821
+ const q = JSON.parse(j.message.trim())
812
822
  if (q && typeof q === "object" && ("isQueued" in q || "serviceAvailable" in q || "retryAfterSeconds" in q)) queue = q
813
823
  }
814
824
  } catch {}
@@ -1029,7 +1039,7 @@ async function* events(body) {
1029
1039
  if (inner.trim() === "[DONE]") break
1030
1040
  let chunk
1031
1041
  try {
1032
- chunk = JSON.parse(inner)
1042
+ chunk = JSON.parse(inner.trim())
1033
1043
  } catch {
1034
1044
  continue
1035
1045
  }
@@ -1698,9 +1708,8 @@ const makePlugin = (site) => async ({ client }) => {
1698
1708
  try {
1699
1709
  m = (await models(cred)).find((x) => x.key === chat.model) ?? (await models(cred, true)).find((x) => x.key === chat.model)
1700
1710
  } catch (e) {
1701
- // the built-in answered any failure to read the list 400
1702
- // (QoderModelOf), a refused one included, marking nothing
1703
- return errorResponse({ status: 400, message: e.message })
1711
+ if (e instanceof SignInGone || e?.expired) return signedInError(e)
1712
+ return errorResponse({ status: 502, message: e?.message ?? String(e) })
1704
1713
  }
1705
1714
  if (!m) return errorResponse({ status: 400, message: `unknown or disabled model "${chat.model}"` })
1706
1715
  const CHAT_URL = siteOf(site, cred).api + CHAT_PATH
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@magpie-community/opencode-qoder-auth",
3
- "version": "0.2.17",
3
+ "version": "0.2.19",
4
4
  "description": "Qoder subscriptions in OpenCode and magpie: Qoder (qoder.com) and Qoder CN (qoder.cn)",
5
5
  "type": "module",
6
6
  "main": "./index.mjs",