Files
yetone fa9daa6945 docs: every built-in subscription a plugin serves once moved says so in its code, and AGENTS.md says where a fix for it goes (user: 对内置的订阅 provider 在代码层面和文档层面加上注释,防止又有别的 agent 在改内置订阅 provider 的代码而不知道生效的其实是对应的插件代码) — a moved Cursor, Devin, Grok, Command Code plan, Kiro, Zed, Qoder, Factory, Xiaomi MiMo, ZCode or WorkBuddy (provider.Moved; since v0.1.642 also the default for a new sign-in, provider.Adopt) is signed in, listed, requested and metered by its @magpie-community plugin, and none of the built-in's code in internal/provider/<name>*.go, internal/gateway/<name>.go, internal/zed or internal/qoder serves it; a fix made there alone never reaches those users. Each of those files now opens with a PLUGIN-SERVED notice naming the subscription's id, its plugin package, where the plugin lives (magpie-community/plugins, packages/<name>) and the mover whose min to raise so moved users get the fixed plugin (keepMovedCurrent); Qoder's adds that Qoder CN isn't moved and still runs there. AGENTS.md (CLAUDE.md imports it) lists the moved built-ins with their packages and movers, how to tell whether a user's is moved (move.state "plugin", onPlugins), the order a fix goes in (the plugin, publish, raise min, the built-in only for those not moved), the magpie code that does run for a moved one (internal/plugin, plugins.go, plugin_usage.go, pluginsignin.go, migrate*.go, app.js's subOf/pluginSubs/startPluginSignIn), and the parity tests. movers in migrate.go and SUBS in app.js point there too. TestMovedBuiltinsSayTheirPlugin fails for a mover whose code carries no PLUGIN-SERVED notice naming its id and package (it failed while a wrapped package name was split). Comments and docs only, no behaviour changed. go vet ./internal/..., go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui -count=1 ./... under a temp HOME pass (TestProvidersAnswerWhileListsComeIn and TestClaudeSignedOut failed once on their timing under load, pass alone; untouched).
2026-10-02 19:37:02 +08:00

74 lines
2.5 KiB
Go

package provider
// PLUGIN-SERVED (see AGENTS.md): ZCode ("zcode") is a deprecated built-in
// subscription served by its plugin, @magpie-community/opencode-zcode-auth,
// once moved onto it (provider.Moved; the default for a new sign-in). A
// moved one's sign-ins, models, requests and usage are all the plugin's,
// never this code's (only the move, in migrate*.go, still reads its
// accounts). A fix here alone doesn't reach those users; fix the plugin
// (github.com/magpie-community/plugins, packages/zcode) and raise the
// mover's min in internal/provider/migrate_zcode.go.
// ZCode names the machine it runs on to zcode.z.ai: its apiClient sends
// X-Device-Mid, a UUID it keeps as deviceMid, on every request, and
// /api/v1/zcode-plan/billing/balance refuses one without it (400, code
// 3001 "parameter error", #282). The server only asks that it is there, so
// magpie keeps a UUID of its own, made once for this machine beside
// providers.json — never ZCode's.
import (
"crypto/rand"
"encoding/hex"
"net/http"
"os"
"path/filepath"
"regexp"
"strings"
"sync"
)
var zcodeDevices = struct {
sync.Mutex
m map[string]string // by the file it is kept in
}{m: map[string]string{}}
var zcodeUUIDRe = regexp.MustCompile(`^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$`)
func zcodeDevicePath() string { return filepath.Join(filepath.Dir(Path()), "zcode-device-mid") }
// zcodeDeviceMid is this machine's X-Device-Mid: the one kept, or a new
// one, kept for next time (and used for now if it can't be).
func zcodeDeviceMid() string {
p := zcodeDevicePath()
zcodeDevices.Lock()
defer zcodeDevices.Unlock()
if id := zcodeDevices.m[p]; id != "" {
return id
}
if b, err := os.ReadFile(p); err == nil {
if id := strings.ToLower(strings.TrimSpace(string(b))); zcodeUUIDRe.MatchString(id) {
zcodeDevices.m[p] = id
return id
}
}
var b [16]byte
_, _ = rand.Read(b[:])
b[6] = b[6]&0x0f | 0x40 // version 4
b[8] = b[8]&0x3f | 0x80 // variant 10
h := hex.EncodeToString(b[:])
id := h[0:8] + "-" + h[8:12] + "-" + h[12:16] + "-" + h[16:20] + "-" + h[20:32]
if os.MkdirAll(filepath.Dir(p), 0o755) == nil {
_ = os.WriteFile(p, []byte(id+"\n"), 0o600)
}
zcodeDevices.m[p] = id
return id
}
// zcodeDeviceHeader names the machine on a request to zcode.z.ai, as
// ZCode does; Z.ai's and BigModel's business APIs are asked without it.
func zcodeDeviceHeader(req *http.Request) {
if strings.HasPrefix(req.URL.String(), zcodeAPI+"/") {
req.Header.Set("X-Device-Mid", zcodeDeviceMid())
}
}