@magpie-community/opencode-zcode-auth 0.1.19 → 0.1.21

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 +12 -2
  2. package/index.mjs +55 -14
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -21,8 +21,9 @@ over Anthropic's Messages API. Provider id: `zcode`.
21
21
  - zcode.z.ai serves the Start Plan only to requests that look like the
22
22
  ZCode client's own, and refuses others with HTTP 405 "request has been
23
23
  blocked due to unusual activity". So a Start Plan request goes as
24
- ZCode's does: ZCode's headers, ZCode's system prompt ahead of the
25
- agent's, the day in a `<system-reminder>` turn before the agent's
24
+ ZCode's does: ZCode's headers, ZCode's three system blocks with the
25
+ agent's system prompt at the end of the third (a fourth block is
26
+ refused, #343), the day in a `<system-reminder>` turn before the agent's
26
27
  turns, and the sign-in's device id in `metadata.user_id`. GLM Coding
27
28
  Plan and team requests go as the agent sent them. One it still turns
28
29
  away (a 405, the block page or code 3012) comes back with what to do
@@ -32,6 +33,15 @@ over Anthropic's Messages API. Provider id: `zcode`.
32
33
  group".
33
34
  - The Start Plan's token can't be refreshed. When it runs out, sign in
34
35
  again.
36
+ - **ZCode: a team's GLM Coding Plan**
37
+ - For an account with a plan of its own and a seat on a team's plan
38
+ (#285). The sign-ins above take the account's own plan, as ZCode does
39
+ by default; this one asks which site, signs in the same way, and
40
+ takes the team seat's `zcode-team-api-key`, as ZCode does when its
41
+ plan picker is switched to the team's plan.
42
+ - It is kept as an account of its own, named `<email> (team)`, beside
43
+ the account's own plan, so both can be used.
44
+ - With no seat on a team's plan, it says so and signs in to nothing.
35
45
  - **ZCode app's sign-in**
36
46
  - Uses the account the ZCode app on this computer is signed in to. For
37
47
  every request it reads ZCode's own credentials (`~/.zcode/v2`), so it
package/index.mjs CHANGED
@@ -847,13 +847,29 @@ async function teamSignIn(site, root, auth, info) {
847
847
 
848
848
  // signedIn is what a sign-in gives: the account's coding plan key and plan;
849
849
  // or, with none of its own, a seat on a team's plan; or else ZCode's token
850
- // for its Start Plan while the account has one.
851
- async function signedIn(site, token, jwt, device) {
850
+ // for its Start Plan while the account has one. Asked for the team's plan
851
+ // (onTeam), it is the team seat alone, as ZCode's plan picker gives a team
852
+ // plan beside the account's own (a "team-coding-plan" selection): an
853
+ // account with both is signed in to each as an account of its own (#285).
854
+ async function signedIn(site, token, jwt, device, onTeam = false) {
852
855
  const name = SITES[site].name
853
856
  const root = SITES[site].api
854
857
  let s = null
855
858
  let err = null
856
859
  let team = ""
860
+ if (onTeam) {
861
+ const auth = await bizAuth(site, token)
862
+ let info
863
+ try {
864
+ info = await call("GET", root + "/api/biz/customer/getCustomerInfo", { auth })
865
+ } catch (e) {
866
+ throw new Error(`${name} account: ${e.message}`)
867
+ }
868
+ const t = await teamSignIn(site, root, auth, info)
869
+ if (t.s) return { ...t.s, jwt, plan: t.plan }
870
+ if (t.none) throw new Error(`this ${name} account has no seat on a team's GLM Coding Plan — sign in with "ZCode: ${name} GLM Coding Plan" for its own plan`)
871
+ throw new Error(t.why ?? t.err)
872
+ }
857
873
  try {
858
874
  const auth = await bizAuth(site, token)
859
875
  let info
@@ -891,11 +907,15 @@ async function signedIn(site, token, jwt, device) {
891
907
  )
892
908
  }
893
909
 
910
+ // TEAM marks a team seat's account name beside the account's own.
911
+ const TEAM = "team"
894
912
  const who = (u) => first(String(u?.email ?? "").replace(/@phone\.local$/, ""), u?.name, u?.user_id, "ZCode")
895
913
  const sleep = (ms) => new Promise((r) => setTimeout(r, ms))
896
914
 
897
915
  // authorize opens ZCode's sign-in flow on site; its callback polls it.
898
- async function authorize(site, log) {
916
+ // onTeam signs in to the account's seat on a team's plan, named apart from
917
+ // the account's own plan (the host keeps one entry per account name).
918
+ async function authorize(site, log, onTeam = false) {
899
919
  const poll = "Bearer " + Buffer.from(crypto.getRandomValues(new Uint8Array(32))).toString("hex")
900
920
  const device = uuid()
901
921
  let flow
@@ -936,7 +956,7 @@ async function authorize(site, log) {
936
956
  if (got.status !== "ready" || !token) return fail("ZCode sign-in: unexpected answer " + got.status)
937
957
  let s
938
958
  try {
939
- s = await signedIn(site, token, first(got.token), device)
959
+ s = await signedIn(site, token, first(got.token), device, onTeam)
940
960
  } catch (e) {
941
961
  return fail(e.message)
942
962
  }
@@ -947,7 +967,7 @@ async function authorize(site, log) {
947
967
  access: s.key || s.jwt || "",
948
968
  // the key doesn't run out; ZCode's token, when it is all there is, does
949
969
  expires: s.key || isTeam(s) ? 0 : jwtExpiry(s.jwt),
950
- accountId: who(got.user),
970
+ accountId: onTeam ? `${who(got.user)} (${TEAM})` : who(got.user),
951
971
  }
952
972
  }
953
973
  },
@@ -1464,7 +1484,8 @@ function contextPrefix() {
1464
1484
  }
1465
1485
 
1466
1486
  // dress is a Messages request body as ZCode sends it: ZCode's three cached
1467
- // system blocks, then the agent's own system text uncached; the context
1487
+ // system blocks and no more, the agent's own system text at the end of the
1488
+ // third (#343); the context
1468
1489
  // prefix as the first turn; no cache mark in the turns but on the last
1469
1490
  // block of the last; none on the tools; metadata.user_id naming the
1470
1491
  // sign-in's device. site names the powered-by line's provider, place where
@@ -1482,18 +1503,20 @@ function dress(text, site, device, place) {
1482
1503
  const cached = (t) => ({ type: "text", text: t, cache_control: { type: "ephemeral" } })
1483
1504
  const provider = site === "bigmodel" ? "bigmodel-api" : "zai-api"
1484
1505
  const model = typeof b.model === "string" ? b.model : ""
1506
+ // The agent's own system text joins the third block, as ZCode's
1507
+ // ContextBuilder (3.14.3, glm/zcode.cjs assembleSystemMessages) joins
1508
+ // every dynamic system section, an added one too, with "\n\n" into
1509
+ // that one block: ZCode never sends more than three. zcode.z.ai turns a
1510
+ // fourth block away with 405/3012 (#343), and the first two must stay
1511
+ // byte for byte ZCode's.
1485
1512
  const own = []
1486
1513
  if (typeof b.system === "string") {
1487
- if (b.system.trim()) own.push({ type: "text", text: b.system })
1514
+ if (b.system.trim()) own.push(b.system)
1488
1515
  } else if (Array.isArray(b.system)) {
1489
- for (const x of b.system) if (x?.type === "text" && typeof x.text === "string" && x.text) own.push({ type: "text", text: x.text })
1516
+ for (const x of b.system) if (x?.type === "text" && typeof x.text === "string" && x.text.trim()) own.push(x.text)
1490
1517
  }
1491
- b.system = [
1492
- cached(PROMPT.cliPrefix),
1493
- cached(PROMPT.stableSections.join("\n\n")),
1494
- cached("\n\n" + [PROMPT.beforeEnvironment, environment(provider, model, place), PROMPT.afterEnvironment].join("\n\n")),
1495
- ...own,
1496
- ]
1518
+ const dynamic = [PROMPT.beforeEnvironment, environment(provider, model, place), PROMPT.afterEnvironment, ...own]
1519
+ b.system = [cached(PROMPT.cliPrefix), cached(PROMPT.stableSections.join("\n\n")), cached("\n\n" + dynamic.join("\n\n"))]
1497
1520
  b.messages = [contextPrefix(), ...b.messages]
1498
1521
  let last = null
1499
1522
  for (const m of b.messages) {
@@ -1733,6 +1756,24 @@ export async function ZCodeAuthPlugin({ client }) {
1733
1756
  methods: [
1734
1757
  oauth("zai"),
1735
1758
  oauth("bigmodel"),
1759
+ // a team's plan beside the account's own (#285): the sign-ins above
1760
+ // take the account's own plan when it has one
1761
+ {
1762
+ type: "oauth",
1763
+ label: "ZCode: a team's GLM Coding Plan",
1764
+ prompts: [
1765
+ {
1766
+ type: "select",
1767
+ key: "site",
1768
+ message: "Where is the team's plan?",
1769
+ options: [
1770
+ { label: "Z.ai", value: "zai", hint: "z.ai" },
1771
+ { label: "BigModel (智谱)", value: "bigmodel", hint: "bigmodel.cn" },
1772
+ ],
1773
+ },
1774
+ ],
1775
+ authorize: (inputs) => authorize(inputs?.site === "bigmodel" ? "bigmodel" : "zai", log, true),
1776
+ },
1736
1777
  { type: "oauth", label: "ZCode app's sign-in", authorize: async () => asZCode() },
1737
1778
  {
1738
1779
  type: "api",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@magpie-community/opencode-zcode-auth",
3
- "version": "0.1.19",
3
+ "version": "0.1.21",
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",