@magpie-community/opencode-zcode-auth 0.1.24 → 0.1.26

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 +17 -1
  2. package/index.mjs +86 -5
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -53,6 +53,13 @@ over Anthropic's Messages API. Provider id: `zcode`.
53
53
  another's credentials: to use both, add the app's sign-in once for each.
54
54
  - **GLM Coding Plan API key**
55
55
  - Paste a key (`<id>.<secret>`) and say which site it is from.
56
+ - A key reaches the GLM Coding Plan alone. ZCode's gift plans (the
57
+ Start Plan, ZCode 1024 Dev Day) are served to ZCode's own session,
58
+ which a key doesn't carry: for an account with only a gift plan, the
59
+ card and each refused request (1113) say to sign in with ZCode
60
+ instead. For an account with a GLM Coding Plan, the card says the
61
+ gift plans' `-Trial` models need ZCode's sign-in, since a key lists
62
+ none of them.
56
63
 
57
64
  ## Where the sign-in is kept
58
65
 
@@ -134,12 +141,21 @@ that fails, or lists nothing, adds no line at all, and a card that is an
134
141
  error keeps its error. Claim the plan in the ZCode app (open it once);
135
142
  the next read shows it as an ordinary gift bucket.
136
143
 
144
+ **No ZCode session.** A card read with a key, or with a sign-in whose
145
+ ZCode session has run out (it can't be renewed), sets aside a line named
146
+ "ZCode gift plans (-Trial models) need ZCode's sign-in", with why in its
147
+ display: such an account reads no gift and lists no `-Trial` entries.
148
+ Sign in again with ZCode to get them back. A gift read that fails
149
+ otherwise is asked again a minute later, not ten.
150
+
137
151
  ## Models
138
152
 
139
153
  The models are the ones ZCode offers for the account's plan. They come
140
154
  from ZCode's provider config: the release zcode.z.ai names, or an
141
155
  installed ZCode's copy if that one is newer. The list is kept for 10
142
- minutes. When the config can't be read, the plugin falls back to this list:
156
+ minutes. When the config can't be read, the plugin falls back to this list
157
+ (and magpie keeps the list it was told last, unless the account's gift
158
+ plans add `-Trial` entries, which are told as read):
143
159
 
144
160
  | Model | Context | Output | Efforts |
145
161
  |---|---|---|---|
package/index.mjs CHANGED
@@ -214,8 +214,15 @@ async function plansOf(s) {
214
214
  let sure = false
215
215
  try {
216
216
  if (!(await plan(s))) start = true
217
- else gift = await startPlan(s.jwt, s.device).catch(() => null)
218
- sure = true
217
+ else {
218
+ // a gift read that failed is unknown, not "no gift": asked again in
219
+ // a minute, so its trial entries come back with the next list (an
220
+ // expired session is known: the card says to sign in again)
221
+ let failed = false
222
+ gift = await startPlan(s.jwt, s.device).catch(() => ((failed = !jwtExpired(s.jwt)), null))
223
+ sure = !failed
224
+ }
225
+ if (start) sure = true
219
226
  } catch {
220
227
  // the plan list could not be read: a key account takes the dual route
221
228
  // (start stays false, the gift is kept, a minute's ttl), so a gift
@@ -437,14 +444,54 @@ async function codingUsage(s) {
437
444
  if (level) out.plan = "GLM Coding " + level[0].toUpperCase() + level.slice(1)
438
445
  out.windows = limitWindows(d)
439
446
  const resets = personalResets(root, s.key)
447
+ let subs = null
440
448
  try {
441
- Object.assign(out, termOf(await call("GET", root + "/api/biz/subscription/list", { auth: s.key })))
449
+ subs = await call("GET", root + "/api/biz/subscription/list", { auth: s.key })
450
+ Object.assign(out, termOf(subs))
442
451
  } catch {}
443
452
  const r = await resets
444
453
  if (r) out.resets = r
454
+ // a key whose account has no plan in force, read as such, says so
455
+ // rather than showing an empty card: what a key can spend is a GLM
456
+ // Coding Plan alone (a ZCode gift plan is never one, see NO_PLAN_KEY).
457
+ // A list that couldn't be read, a level, a counted window or a plan
458
+ // the sign-in saved keeps the card as it was.
459
+ const valid = (Array.isArray(subs) ? subs : []).some((x) => String(x?.status ?? "").toUpperCase() === "VALID")
460
+ if (subs !== null && !valid && !level && !s.plan && !out.windows.some((w) => !w.aside)) return { error: NO_PLAN_KEY }
445
461
  return out
446
462
  }
447
463
 
464
+ // NO_PLAN_KEY is a key with no GLM Coding Plan behind it. ZCode's gift
465
+ // plans (its Start Plan, ZCode 1024 Dev Day) are served by zcode.z.ai
466
+ // to ZCode's own session token, which a sign-in with ZCode (the app's,
467
+ // or Z.ai's or BigModel's in the browser) carries and a key never does:
468
+ // with a key, a gift-only account has nothing to spend, and Z.ai answers
469
+ // each request 1113 (rtbeilin on X, 10-24).
470
+ const NO_PLAN_KEY =
471
+ "this key's account has no GLM Coding Plan; ZCode's gift plans (the Start Plan, ZCode 1024 Dev Day) are spent only with ZCode's sign-in, never with a key — add the account with \"ZCode app's sign-in\" or a ZCode sign-in in the browser"
472
+
473
+ // noPlanKey is a coding plan's 1113 refusal (no plan, no resource
474
+ // package) to a sign-in with no ZCode session, its message followed by
475
+ // NO_PLAN_KEY; anything else goes on as it came. Read only once the
476
+ // answer has failed.
477
+ function noPlanKey(res, text) {
478
+ let v
479
+ try {
480
+ v = JSON.parse(text)
481
+ } catch {
482
+ return null
483
+ }
484
+ // Z.ai names the code in error.code ("1113", as magpie's
485
+ // zhipu_coding_test has it) or error.type (its 401's "1000"), BigModel's
486
+ // envelope in code
487
+ if (![v?.error?.code, v?.error?.type, v?.code].some((c) => String(c ?? "") === "1113")) return null
488
+ const said = first(v?.error?.message, v?.msg, v?.message)
489
+ const out = v?.error && typeof v.error === "object" ? { ...v, error: { ...v.error, message: (said ? said + " — " : "") + NO_PLAN_KEY } } : { ...v, msg: (said ? said + " — " : "") + NO_PLAN_KEY }
490
+ const h = new Headers(res.headers)
491
+ h.set("content-type", "application/json")
492
+ return new Response(JSON.stringify(out), { status: res.status, statusText: res.statusText, headers: h })
493
+ }
494
+
448
495
  // startNum reads a number the balance gives as a number or a string.
449
496
  function startNum(v) {
450
497
  if (isNum(v)) return v
@@ -576,6 +623,21 @@ async function claimHint(s) {
576
623
  return { name: CLAIM_HINT, used: 0, aside: true, display: `${n} · ${plan}` }
577
624
  }
578
625
 
626
+ // GIFT_SIGN_IN is the card's line for an account whose gift plans can't
627
+ // be read: ZCode's gift plans (the Start Plan, ZCode 1024 Dev Day, ZCode
628
+ // Trust Build) are served by zcode.z.ai to ZCode's own session alone, so
629
+ // a GLM Coding Plan key, or a sign-in whose ZCode session has run out
630
+ // (that token can't be renewed), lists no <model>-Trial entries and
631
+ // spends the coding plan alone. A fixed sentence, so magpie can
632
+ // translate it; why goes in the display. Set aside: it stops nothing.
633
+ const GIFT_SIGN_IN = "ZCode gift plans (-Trial models) need ZCode's sign-in"
634
+ function giftSignIn(s, key) {
635
+ if (isTeam(s)) return null
636
+ if (!s.jwt) return { name: GIFT_SIGN_IN, used: 0, aside: true, display: key ? "not read with an API key: add the account with a ZCode sign-in" : "this sign-in has no ZCode session: sign in again" }
637
+ if (jwtExpired(s.jwt)) return { name: GIFT_SIGN_IN, used: 0, aside: true, display: "ZCode's sign-in has expired: sign in again" }
638
+ return null
639
+ }
640
+
579
641
  // startUsage is a gift-only account's card: its buckets, or the words
580
642
  // for having none to show. The card's header already names the plan, so
581
643
  // the windows of one plan keep their own names; with several plans each
@@ -1741,7 +1803,11 @@ export async function ZCodeAuthPlugin({ client }) {
1741
1803
  if (out[id]) continue
1742
1804
  out[id] = model({ ...about(raw), id }, url)
1743
1805
  }
1744
- if (fell) out[Symbol.for("magpie.fellBack")] = true
1806
+ // a fallback tells magpie to keep the list it was told last, which
1807
+ // predates the gift read just made: an account whose gift lists
1808
+ // trial entries is told this list, so ZCode's config out of reach
1809
+ // (a remote box that can't get its CDN) never hides them
1810
+ if (fell && !giftNames(gift).length) out[Symbol.for("magpie.fellBack")] = true
1745
1811
  return out
1746
1812
  },
1747
1813
  },
@@ -1840,6 +1906,14 @@ export async function ZCodeAuthPlugin({ client }) {
1840
1906
  if (startRefused(res.status, text)) return kept(startBlocked(res, text))
1841
1907
  res = new Response(text, { status: res.status, statusText: res.statusText, headers: res.headers })
1842
1908
  }
1909
+ // a key with no plan behind it: what Z.ai said, and why a
1910
+ // ZCode gift plan can't be had with a key (NO_PLAN_KEY)
1911
+ if (!start && !s.jwt && res.status >= 400) {
1912
+ const text = await res.text()
1913
+ const why = noPlanKey(res, text)
1914
+ if (why) return done(kept(why))
1915
+ res = new Response(text, { status: res.status, statusText: res.statusText, headers: res.headers })
1916
+ }
1843
1917
  // a trial entry's gift answer that can't serve the request is
1844
1918
  // the agent's answer, not the coding plan's: the user picked the
1845
1919
  // gift, so a spent bucket says 429 (its quota error inside a 200
@@ -1905,6 +1979,10 @@ export async function ZCodeAuthPlugin({ client }) {
1905
1979
  if (!card.error) {
1906
1980
  const hint = await claimHint(s)
1907
1981
  if (hint) card.windows = [...(card.windows ?? []), hint]
1982
+ // a key, or a ZCode session that ran out: no gift read, so no
1983
+ // -Trial models, said on the card rather than left out unsaid
1984
+ const need = start ? null : giftSignIn(s, auth?.type === "api")
1985
+ if (need) card.windows = [...(card.windows ?? []), need]
1908
1986
  }
1909
1987
  return card
1910
1988
  } catch (e) {
@@ -1954,4 +2032,7 @@ export async function ZCodeAuthPlugin({ client }) {
1954
2032
  }
1955
2033
 
1956
2034
  // for tests
1957
- export const _internal = { entry, limitWindows, termOf, startUsage, routes, blocked, plansOf, giftOf, dualUsage, claimHint, spentUp, giftServes, modelOf, teamKeys, ownSignIn, stateOf, dress, PROMPT }
2035
+ // config sets what zcodeConfig has read, for tests: null is ZCode's config
2036
+ // out of reach, undefined forgets it
2037
+ const config = (cfg) => (cfgCache = cfg === undefined ? null : { cfg, at: Date.now() })
2038
+ export const _internal = { config, entry, limitWindows, termOf, startUsage, routes, blocked, plansOf, giftOf, dualUsage, claimHint, spentUp, giftServes, modelOf, teamKeys, ownSignIn, stateOf, dress, PROMPT }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@magpie-community/opencode-zcode-auth",
3
- "version": "0.1.24",
3
+ "version": "0.1.26",
4
4
  "description": "OpenCode provider plugin: signs in to Z.ai's or BigModel's (智谱) GLM Coding Plan as ZCode does, including team seats and ZCode's Start Plan",
5
5
  "type": "module",
6
6
  "main": "./index.mjs",