Files
magpie/internal/plugin/testdata/fake/index.js
T
yetone 9e13f18fb9 gateway, provider: a request turning reasoning off stops Factory's DeepSeek V4.1 Flash, Kimi K3 and GLM-5.2 thinking (#899, tedestar) — they offered only low/high/max (GLM-5.2 high/max), so an agent's none (or Claude Code's thinking disabled) was asked for low, or nothing, and Fireworks thought anyway. droid 0.231.0 lists off for them and sends reasoning_effort "none"; through OpenRouter pinned to Fireworks, none came back with 0 reasoning tokens on DeepSeek V4.1 Flash (2 completion tokens; 19 at the default, 16 reasoning at low) and Kimi K3 (8; 71 at the default), and on Baseten's GLM-5.2 (2; 114 at the default), while GLM-5.3 and GLM-5.3-Flash turn none away ("Reasoning is mandatory for this endpoint and cannot be disabled") and stay at low, their least (Qwen3.8 Max and MiniMax have no off in droid either). The three now have none among their levels, in the built-in and in @magpie-community/opencode-factory-auth 0.1.17, which the mover's min is raised to; and a translated request that turned reasoning off (Effort reads it as low) is asked for none on a model with none among its levels, whatever the provider, else as before (a refusal still falls back to the lowest level on). No Factory account was at hand to send through Factory itself. TestFactoryThinkingOff (the built-in: chat, Responses and Anthropic's disabled; GLM-5.3 still low) and TestPluginThinkingOff (the plugin host, a model whose variants have none and one without) are new and fail without the change. go vet, go build (darwin, GOOS=linux nogui, GOOS=windows) and go test -tags nogui ./... under a temp HOME pass (TestSavedButSignedOutClaudeServed timed out once under load and passes alone).
2026-10-05 20:09:00 +08:00

160 lines
8.5 KiB
JavaScript

// A plugin as OpenCode's are written, signing in to a made-up provider
// whose requests go to $FAKE_BASE; $FAKE_ID names it another's id, as a
// built-in's plugin does.
const ID = process.env.FAKE_ID || "fakeco"
// fakeWho is who a team signs in as: team@fake, and a team named name/uid
// is name@fake with that uid, as WorkBuddy's plugin keeps one
function fakeWho(team) {
const [name, uid] = (team ?? "me").split("/")
return uid ? { accountId: name + "@fake", uid } : { accountId: name + "@fake" }
}
export const FakePlugin = async ({ client }) => ({
config: async (cfg) => {
cfg.provider = cfg.provider ?? {}
cfg.provider[ID] = {
name: "FakeCo",
npm: "@ai-sdk/openai-compatible",
api: "https://fake.invalid/v1",
models: {
"fake-1": { name: "Fake One", limit: { context: 1000, output: 100 } },
"fake-claude": { name: "Fake Claude", provider: { npm: "@ai-sdk/anthropic" }, modalities: { input: ["text", "image"], output: ["text"] }, limit: { context: 2000, output: 200 } },
"fake-gemini": { name: "Fake Gemini", provider: { npm: "@ai-sdk/google" }, reasoning: true, modalities: { input: ["text"], output: ["text"] }, limit: { context: 3000, output: 300 } },
},
}
// $FAKE_RESPONSES: one model more, on OpenAI's Responses (Grok's)
// $FAKE_FAST: fake-1 has a fast one, as a Cursor model has its -fast
if (process.env.FAKE_FAST) cfg.provider[ID].models["fake-1-fast"] = { name: "Fake One Fast", limit: { context: 1000, output: 100 } }
if (process.env.FAKE_RESPONSES) cfg.provider[ID].models["fake-resp"] = { name: "Fake Responses", provider: { npm: "@ai-sdk/openai" }, limit: { context: 4000, output: 400 } }
// $FAKE_DEEPSEEK: a DeepSeek model, as Cline's cline-pass/deepseek-v4-pro
if (process.env.FAKE_DEEPSEEK) cfg.provider[ID].models["deepseek-v4-pro"] = { name: "DeepSeek V4 Pro", limit: { context: 1000, output: 100 } }
// $FAKE_OFF: a model that stops thinking at none and one that can't,
// as Factory's plugin has Kimi K3 and GLM-5.3 (#899)
if (process.env.FAKE_OFF) {
const levels = (l) => Object.fromEntries(l.map((e) => [e, { reasoningEffort: e }]))
cfg.provider[ID].models["fake-off"] = { name: "Fake Off", reasoning: true, variants: levels(["none", "low", "high", "max"]), limit: { context: 1000, output: 100 } }
cfg.provider[ID].models["fake-on"] = { name: "Fake On", reasoning: true, variants: levels(["low", "high", "max"]), limit: { context: 1000, output: 100 } }
}
},
auth: {
provider: ID,
methods: [
{ type: "api", label: "API key" },
{
type: "oauth",
label: "Browser",
prompts: [
{ type: "select", key: "where", message: "Where?", options: [{ label: "Home", value: "home" }, { label: "Work", value: "work" }] },
{ type: "text", key: "team", message: "Team?", when: { key: "where", op: "eq", value: "work" }, validate: (v) => (v ? undefined : "Required") },
],
authorize: async (inputs) => ({
url: "https://fake.invalid/auth?where=" + inputs.where,
instructions: "Paste the code",
method: "code",
callback: async (code) =>
code === "good"
? { type: "success", refresh: "r-" + (inputs.team ?? "none"), access: "stale", expires: 0, ...fakeWho(inputs.team) }
: code === "expired"
? { type: "failed", error: "the sign-in page expired" }
: { type: "failed" },
}),
},
],
loader: async (getAuth, provider) => ({
apiKey: "dummy",
baseURL: process.env.FAKE_BASE,
async fetch(url, init) {
let a = await getAuth()
// "r-revoked": the vendor turned the refresh away, said as Zed's says it
if (a.type === "oauth" && a.refresh === "r-revoked") throw Object.assign(new Error("FakeCo turned the sign-in away"), { signIn: "expired" })
if (a.type === "oauth" && a.expires < Date.now()) {
a = { ...a, access: "fresh-" + a.refresh, expires: Date.now() + 3600e3 }
await client.auth.set({ path: { id: ID }, body: a })
}
const h = new Headers(init.headers)
h.set("authorization", "Bearer " + (a.type === "oauth" ? a.access : a.key))
h.set("x-models", Object.keys(provider.models).sort().join(","))
h.set("x-body-type", typeof init.body)
return fetch(url, { ...init, headers: h })
},
}),
// magpie's: the account's allowance; "full@fake" has used its five
// hours, which count only fake-claude
usage: async (getAuth) => {
const a = await getAuth()
// $FAKE_USAGE: the plan is what the vendor's page there says
if (process.env.FAKE_USAGE) {
const r = await fetch(process.env.FAKE_USAGE)
return { plan: await r.text() }
}
if (a.type !== "oauth") return { error: "an API key has no plan" }
if (a.refresh === "r-gone") return { error: `${a.accountId}: the FakeCo sign-in has expired — sign in again` }
if (a.refresh === "r-offline") throw new Error("fetch failed")
// the vendor unreachable, said as Zed's plugin says it
if (a.refresh === "r-unreachable") {
try {
await fetch("http://127.0.0.1:9/me")
} catch (e) {
return { error: e?.message ?? String(e), signIn: "kept" }
}
}
if (a.refresh === "r-kept") return { error: "sign in again to see usage", signIn: "kept" }
if (a.refresh === "r-renewed") return { error: "usage is down", signIn: "renewed" }
const full = a.accountId === "full@fake"
return {
plan: "Fake Pro",
user: full ? "Full@Fake.example" : undefined,
until: "2030-01-02T03:04:05Z",
renew: "auto",
resets: full ? { count: 3, byWindow: true, fiveHour: 2, weekly: 1 } : undefined,
windows: [
{ name: "5 hours", used: full ? 100 : 25, resetsAt: Date.now() + 3600e3, span: 5 * 3600, models: ["fake-claude"] },
{ name: "Week", used: 10, resetsAt: Math.floor(Date.now() / 1000) + 86400, span: 7 * 86400, amount: 120, limit: 1200, unit: "credits" },
{ name: "Extra", used: 250, display: "$2.50", aside: true },
],
}
},
},
// the models an account has: refused for a dead one; a "rot-" sign-in
// spends its refresh token asking, as a rotating one does
provider: {
id: ID,
models: async (p, { auth }) => {
if (auth?.refresh === "r-dead" || auth?.key === "dead") throw new Error("the vendor refused the sign-in")
if (auth?.refresh === "r-models-gone") throw Object.assign(new Error("the vendor refused the sign-in"), { signIn: "expired" })
// a sign-in past its time, said in words only, as Grok's plugin says it
if (auth?.refresh === "r-models-expired") throw new Error("FakeCo's sign-in has expired; run `fake login`")
// the vendor unreachable: the list kept, as Zed's plugin keeps it
if (auth?.refresh === "r-unreachable") await fetch("http://127.0.0.1:9/models").catch(() => {})
if (auth?.type === "oauth" && auth.refresh?.startsWith("rot-")) {
await client.auth.set({ path: { id: ID }, body: { ...auth, refresh: auth.refresh + "+" } })
}
// fake-1 costs the plan nothing, as a WorkBuddy model of x0.00 credits
p.models["fake-1"].free = true
// fake-claude's credits are a number, discounted; fake-gemini's as
// WorkBuddy's picker writes them
if (p.models["fake-claude"]) Object.assign(p.models["fake-claude"], { rate: 0.5, rateWas: 1 })
if (p.models["fake-gemini"]) p.models["fake-gemini"].rate = "x0.03"
if (auth?.key === "few") return { "fake-1": p.models["fake-1"] }
// $FAKE_MODELS: the vendor's list, whose answer names one more model
if (process.env.FAKE_MODELS && auth) {
const r = await fetch(process.env.FAKE_MODELS).then((r) => r.text()).catch((e) => {
// $FAKE_MODELS_THROW: the hook throws, as Cursor's, Grok's and Devin's do
if (process.env.FAKE_MODELS_THROW === "1") throw e
return null
})
// "own": it hands back a table of its own, saying it fell back, as
// Command Code's Go and ZCode do
if (r === null && process.env.FAKE_MODELS_THROW === "own")
return { "fake-1": { ...p.models["fake-1"] }, [Symbol.for("magpie.fellBack")]: true }
if (r) p.models["fake-" + r] = { ...p.models["fake-1"], id: "fake-" + r, name: r }
}
return p.models
},
},
"chat.headers": async (input, output) => {
if (input.model.providerID === ID) output.headers["x-plugin-model"] = input.model.id
},
})