@magpie-community/opencode-zcode-auth 0.1.18 → 0.1.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.
- package/README.md +15 -1
- package/index.mjs +71 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,7 +24,12 @@ over Anthropic's Messages API. Provider id: `zcode`.
|
|
|
24
24
|
ZCode's does: ZCode's headers, ZCode's system prompt ahead of the
|
|
25
25
|
agent's, the day in a `<system-reminder>` turn before the agent's
|
|
26
26
|
turns, and the sign-in's device id in `metadata.user_id`. GLM Coding
|
|
27
|
-
Plan and team requests go as the agent sent them.
|
|
27
|
+
Plan and team requests go as the agent sent them. One it still turns
|
|
28
|
+
away (a 405, the block page or code 3012) comes back with what to do
|
|
29
|
+
about it, as magpie's built-in said: "ZCode's Start Plan still turned
|
|
30
|
+
this request away, though magpie sends it as the ZCode app does; …
|
|
31
|
+
Use an account with a GLM Coding Plan, or add another provider to this
|
|
32
|
+
group".
|
|
28
33
|
- The Start Plan's token can't be refreshed. When it runs out, sign in
|
|
29
34
|
again.
|
|
30
35
|
- **ZCode app's sign-in**
|
|
@@ -96,6 +101,15 @@ behind an error, so a gift hiccup must not black out a working Coding
|
|
|
96
101
|
Plan. A gift that goes out of date while requests use it simply stops
|
|
97
102
|
showing.
|
|
98
103
|
|
|
104
|
+
**Resets.** A GLM Coding Plan of the account's own, and a team seat,
|
|
105
|
+
show the resets the vendor's usage page lists
|
|
106
|
+
(`/api/biz/customer-package-reset/list`, `targetType=PERSONAL` with the
|
|
107
|
+
plan's key, `TEAM` with the team's sign-in): how many of the five hours
|
|
108
|
+
and of the week are still available, and when each runs out, soonest
|
|
109
|
+
first. A team's are marked a team's. They are only read; a reset is
|
|
110
|
+
spent on the vendor's own page. A list that can't be read, or has none
|
|
111
|
+
left, shows no count.
|
|
112
|
+
|
|
99
113
|
**Gift plans ZCode holds but has not granted.** ZCode hands out one-off
|
|
100
114
|
trust-build / gift plans (a day's ZCode Trust Build, say) that are not
|
|
101
115
|
on the account until the ZCode app claims them. The plugin reads the
|
package/index.mjs
CHANGED
|
@@ -436,9 +436,12 @@ async function codingUsage(s) {
|
|
|
436
436
|
const level = typeof d.level === "string" ? d.level : ""
|
|
437
437
|
if (level) out.plan = "GLM Coding " + level[0].toUpperCase() + level.slice(1)
|
|
438
438
|
out.windows = limitWindows(d)
|
|
439
|
+
const resets = personalResets(root, s.key)
|
|
439
440
|
try {
|
|
440
441
|
Object.assign(out, termOf(await call("GET", root + "/api/biz/subscription/list", { auth: s.key })))
|
|
441
442
|
} catch {}
|
|
443
|
+
const r = await resets
|
|
444
|
+
if (r) out.resets = r
|
|
442
445
|
return out
|
|
443
446
|
}
|
|
444
447
|
|
|
@@ -708,31 +711,56 @@ async function teamUsage(s, key) {
|
|
|
708
711
|
}
|
|
709
712
|
|
|
710
713
|
// teamResets is how many resets a team member may spend, of the five hours
|
|
711
|
-
// and of the week:
|
|
712
|
-
// null when it can't be told or there are none.
|
|
714
|
+
// and of the week, marked a team's (team: true), as the built-in's
|
|
715
|
+
// zhipuTeamResets did. null when it can't be told or there are none.
|
|
713
716
|
async function teamResets(root, auth, headers) {
|
|
717
|
+
const r = await resetsOf(root + "/api/biz/customer-package-reset/list?targetType=TEAM", auth, headers)
|
|
718
|
+
if (r) r.team = true
|
|
719
|
+
return r
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
// personalResets is how many resets a GLM Coding Plan of a person's own
|
|
723
|
+
// holds, as bigmodel.cn's usage page lists them, asked with the plan's
|
|
724
|
+
// key (the built-in's zhipuPersonalResets). Only read: a reset is spent
|
|
725
|
+
// on the vendor's own page, never from here.
|
|
726
|
+
async function personalResets(root, key) {
|
|
727
|
+
return resetsOf(root + "/api/biz/customer-package-reset/list?targetType=PERSONAL", key)
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
// resetsOf reads customer-package-reset/list: the resets of the five
|
|
731
|
+
// hours and of the week, of which only those still available count (one
|
|
732
|
+
// run out stays listed a week, available false), each with its own end
|
|
733
|
+
// (each, soonest first, one with no end last) and the soonest as until.
|
|
734
|
+
// null when the list can't be read, so no count is shown rather than 0,
|
|
735
|
+
// and when it holds none.
|
|
736
|
+
async function resetsOf(url, auth, headers) {
|
|
714
737
|
let d
|
|
715
738
|
try {
|
|
716
|
-
d = (await call("GET",
|
|
739
|
+
d = (await call("GET", url, { auth, headers })) ?? {}
|
|
717
740
|
} catch {
|
|
718
741
|
return null
|
|
719
742
|
}
|
|
720
743
|
let until = 0
|
|
721
|
-
const
|
|
744
|
+
const each = []
|
|
745
|
+
const count = (rs, window) => {
|
|
722
746
|
let n = 0
|
|
723
747
|
for (const x of Array.isArray(rs) ? rs : []) {
|
|
724
748
|
if (x?.available !== true) continue
|
|
725
749
|
n++
|
|
726
750
|
const t = when(x.expireTime)
|
|
727
751
|
if (t && (!until || t < until)) until = t
|
|
752
|
+
each.push(t ? { until: new Date(t).toISOString(), window } : { window })
|
|
728
753
|
}
|
|
729
754
|
return n
|
|
730
755
|
}
|
|
731
|
-
const fiveHour = count(d.fiveHourResets)
|
|
732
|
-
const weekly = count(d.weekResets)
|
|
756
|
+
const fiveHour = count(d.fiveHourResets, "fiveHour")
|
|
757
|
+
const weekly = count(d.weekResets, "weekly")
|
|
733
758
|
if (fiveHour + weekly === 0) return null
|
|
759
|
+
const end = (c) => (c.until ? Date.parse(c.until) : Infinity)
|
|
760
|
+
each.sort((a, b) => (end(a) === end(b) ? 0 : end(a) < end(b) ? -1 : 1))
|
|
734
761
|
const r = { count: fiveHour + weekly, byWindow: true, fiveHour, weekly }
|
|
735
762
|
if (until) r.until = new Date(until).toISOString()
|
|
763
|
+
r.each = each
|
|
736
764
|
return r
|
|
737
765
|
}
|
|
738
766
|
|
|
@@ -1311,6 +1339,36 @@ function model(m, url) {
|
|
|
1311
1339
|
// the plan serves (ARNO's "Freeflow", provider zcode-start) sends it: its
|
|
1312
1340
|
// headers (startHeaders) and its body (dress). The texts are prompt.mjs.
|
|
1313
1341
|
|
|
1342
|
+
// START_BLOCKED is what the Start Plan still turning a request away means,
|
|
1343
|
+
// word for word magpie's provider.ZCodeStartBlockedHint (#425), which
|
|
1344
|
+
// magpie's Routing page knows and says in the user's language. It is put
|
|
1345
|
+
// in place of the block page, whose words magpie would otherwise read as a
|
|
1346
|
+
// network firewall's (provider.BlockedHint).
|
|
1347
|
+
const START_BLOCKED = "ZCode's Start Plan still turned this request away, though magpie sends it as the ZCode app does; it can be a network block of this IP, or ZCode checking for something new. Use an account with a GLM Coding Plan, or add another provider to this group"
|
|
1348
|
+
const EDGE_BLOCKED = /request has been blocked|errors\.aliyun\.com/i
|
|
1349
|
+
const START_REFUSED = /unusual activity|"code"\s*:\s*"?3012\b/i
|
|
1350
|
+
|
|
1351
|
+
// startRefused says a Start Plan answer is that refusal, as the built-in's
|
|
1352
|
+
// zcodeStartExplain read it: a 405, a block page or code 3012.
|
|
1353
|
+
const startRefused = (status, text) => status >= 400 && (status === 405 || EDGE_BLOCKED.test(text) || START_REFUSED.test(text))
|
|
1354
|
+
|
|
1355
|
+
// startBlocked is the refusal's answer as the built-in passed it on: what
|
|
1356
|
+
// the vendor said (its status for a block page), then START_BLOCKED.
|
|
1357
|
+
function startBlocked(res, text) {
|
|
1358
|
+
let said = ""
|
|
1359
|
+
if (!EDGE_BLOCKED.test(text)) {
|
|
1360
|
+
try {
|
|
1361
|
+
const v = JSON.parse(text)
|
|
1362
|
+
said = first(v?.error?.message, typeof v?.error === "string" ? v.error : "", v?.message, v?.msg)
|
|
1363
|
+
} catch {}
|
|
1364
|
+
}
|
|
1365
|
+
said ||= `${res.status} ${STATUS_TEXT[res.status] ?? res.statusText}`.trim()
|
|
1366
|
+
const h = new Headers(res.headers)
|
|
1367
|
+
h.set("content-type", "application/json")
|
|
1368
|
+
const error = { type: "permission_error", message: `${said} — ${START_BLOCKED}` }
|
|
1369
|
+
return new Response(JSON.stringify({ type: "error", error }), { status: res.status, statusText: res.statusText, headers: h })
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1314
1372
|
const osVersion = () => `${process.platform} ${release()} ${arch()}`
|
|
1315
1373
|
const osCategory = () => (process.platform === "darwin" ? "macos" : process.platform === "win32" ? "windows" : "linux")
|
|
1316
1374
|
|
|
@@ -1594,6 +1652,13 @@ export async function ZCodeAuthPlugin({ client }) {
|
|
|
1594
1652
|
h.set("Authorization", "Bearer " + key)
|
|
1595
1653
|
}
|
|
1596
1654
|
let res = await fetch(url, { ...opts, headers: h })
|
|
1655
|
+
// the Start Plan turning the request away says what to do about
|
|
1656
|
+
// it, as the built-in's zcodeStartExplain did
|
|
1657
|
+
if (start && res.status >= 400) {
|
|
1658
|
+
const text = await res.text()
|
|
1659
|
+
if (startRefused(res.status, text)) return kept(startBlocked(res, text))
|
|
1660
|
+
res = new Response(text, { status: res.status, statusText: res.statusText, headers: res.headers })
|
|
1661
|
+
}
|
|
1597
1662
|
// a trial entry's gift answer that can't serve the request is
|
|
1598
1663
|
// the agent's answer, not the coding plan's: the user picked the
|
|
1599
1664
|
// gift, so a spent bucket says 429 (its quota error inside a 200
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@magpie-community/opencode-zcode-auth",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.19",
|
|
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",
|