Files
yetone 927da8c41a provider: moved ZCode users get plugin 0.1.14, which lists a gift-only account's GLM-5.3 as GLM-5.3 (#1261)
sun1090's ZCode account has no GLM Coding Plan, only the Start Plan, whose
buckets serve GLM-5.3 and GLM-5.3-Flash. The card counted GLM-5.3, but the
plugin listed the Start Plan's config models (GLM-5.3-Flash, GLM-5.2,
GLM-5-Turbo) and GLM-5.3 only as GLM-5.3-Trial. ZCode itself lists a Start
Plan account's models as its balance's buckets name them, so 0.1.14
(magpie-community/plugins aca7c59) lists them by their own names too, and
keeps the -Trial entries. keepMovedCurrent updates every moved ZCode user.
The builtin check's note says the plugin now lists GLM-5.3 while a gift
bucket serves it; the check itself is unchanged.

0.1.14 resolves with npm view and its publish run passed. The plugin's
bun test (75) passes and its new test fails on 0.1.13. go test -tags nogui
./internal/provider under a temp HOME passes; GOOS=linux and GOOS=windows
go vet pass. Not tried with a real Start Plan account here.
2026-10-08 16:23:52 +08:00

118 lines
4.8 KiB
Go

package provider
import (
"context"
"encoding/json"
"errors"
"strings"
"time"
)
// ZCode's accounts on @magpie-community/opencode-zcode-auth: the plan's
// key (else ZCode's session token) as the oauth entry's access, the rest
// the requests need as its refresh, as the plugin's own sign-in keeps
// them. Nothing here rotates: a key lasts until it is deleted.
//
// ZCode's own sign-in goes marked as ZCode's (source "zcode"), with a copy
// of what magpie reads now: the plugin, 0.1.2 on (the move installs no
// older), reads ZCode's own credentials in its place for each request, as
// the built-in does, so it follows ZCode's switches and the session token
// ZCode renews (the Start Plan).
func init() {
movers["zcode"] = &mover{
pkg: "@magpie-community/opencode-zcode-auth",
min: "0.1.14", // a gift-only account lists its gift's models by their own names, as ZCode does, so a Start Plan GLM-5.3 bucket's model is listed as GLM-5.3, not only GLM-5.3-Trial (#1261, plugins aca7c59); 0.1.13's claim line has one fixed name, which the GUI translates, so it no longer shares the "ZCode Trust Build" bucket window's name (plugins #43); 0.1.12's counted window carries its amount and limit, so the card's count follows used/left (#659, plugins #35); 0.1.11's gift plan ZCode holds but hasn't granted is named on the card, to claim in the ZCode app (plugins #30, #1001); 0.1.10's gift-only buckets and 0.1.9's trial entries (plugins #21) kept
agents: []string{"zcode"},
// the built-in never served GLM-5.3 to a Start Plan account, so
// its pick is no loss when the plugin doesn't list it (the plugin
// lists it, as ZCode does, only while a gift bucket serves it);
// its plan as its requests found it, else as it showed it, nothing
// asked of Z.ai
builtin: func(_ context.Context, a Moving, model string) bool {
for _, l := range zcodeLogins() {
if strings.EqualFold(l.User, a.User) {
return !zcodeOnStartAs(nil, l.key, l.Plan) || zcodeStartServes()(model)
}
}
return true
},
out: func() ([]Moving, error) {
var out []Moving
for _, l := range zcodeLogins() {
// the own account's copy goes back to nothing: ZCode keeps it
out = append(out, Moving{User: l.User, First: l.Active, On: l.On, Lapsed: l.Lapsed != "", Plan: l.Plan, Own: l.Own, Auth: zcodeOut(l.User, l.Plan, l.key, l.Own)})
}
return out, nil
},
back: func(ls []savedLogin, user string, auth map[string]any) ([]savedLogin, string, error) {
var s struct {
Site, Device, Key, Base, JWT, Token, Org, Project, Plan, Source string
}
if json.Unmarshal([]byte(str(auth["refresh"])), &s) != nil {
// a key signed in to in the plugin
if str(auth["type"]) != "api" || str(auth["key"]) == "" {
return ls, "", errors.New("ZCode: an unreadable plugin sign-in")
}
s.Key = str(auth["key"])
if m, _ := auth["metadata"].(map[string]any); m != nil {
s.Site = str(m["site"])
}
} else if s.Key == "" && s.JWT == "" {
s.Key = str(auth["access"])
}
// ZCode's own goes back to nothing: ZCode keeps it
if _, _, ok := zcodeOwn(); ok && s.Source == "zcode" {
return ls, ownUser(ls, "zcode", firstNonEmpty(user, str(auth["accountId"]))), nil
}
k := zcodeKey{Key: s.Key, Base: s.Base, JWT: s.JWT, Token: s.Token, Org: s.Org, Project: s.Project}
if k.Base == "" {
k.Base = ZCodeZaiBase
if s.Site == "bigmodel" {
k.Base = ZCodeBigModelBase
}
}
if k.Key == "" && k.JWT == "" && !k.team() {
return ls, "", errors.New("ZCode: an unreadable plugin sign-in")
}
if user == "" {
user = firstNonEmpty(str(auth["accountId"]), "ZCode")
}
i := backInto(&ls, "zcode", user)
b, err := json.Marshal(k)
if err != nil {
return ls, "", err
}
ls[i].Auth, ls[i].Lapsed, ls[i].Seen = b, "", time.Now().UTC().Truncate(time.Second)
if s.Plan != "" {
ls[i].Plan = s.Plan
}
return ls, ls[i].User, nil
},
}
}
// zcodeOut is a ZCode account as the plugin's sign-in keeps one;
// ZCode's own is marked as ZCode's, for the plugin to follow.
func zcodeOut(user, plan string, k zcodeKey, own bool) map[string]any {
site := "zai"
if strings.Contains(k.Base, "bigmodel.cn") {
site = "bigmodel"
}
state := map[string]any{"site": site, "device": zcodeDeviceMid(), "base": k.Base}
if own {
state["source"] = "zcode"
}
for name, v := range map[string]string{"key": k.Key, "jwt": k.JWT, "token": k.Token, "org": k.Org, "project": k.Project, "plan": plan} {
if v != "" {
state[name] = v
}
}
expires := int64(0)
if k.Key == "" && !k.team() && !own { // ZCode's own is read anew each time
if exp, _ := jwtClaims(k.JWT)["exp"].(float64); exp > 0 {
expires = int64(exp) * 1000
}
}
return map[string]any{"type": "oauth", "access": firstNonEmpty(k.Key, k.JWT), "refresh": jsonText(state), "expires": expires, "accountId": user}
}