diff --git a/src/cli/account-api.ts b/src/cli/account-api.ts index e0e573e251..aff16677d1 100644 --- a/src/cli/account-api.ts +++ b/src/cli/account-api.ts @@ -360,7 +360,7 @@ async function fetchOAuthRows( needsReauth: a.needsReauth, // Forward the server's answer verbatim, including `null`. Collapsing null to "absent" here // would destroy the one distinction this field exists to make. - plan: a.plan ?? null, + ...(Object.hasOwn(a, "plan") ? { plan: a.plan } : {}), ...(a.quota !== undefined ? { quota: a.quota } : {}), ...(a.quotaUnavailable !== undefined ? { quotaUnavailable: a.quotaUnavailable } : {}), })); diff --git a/tests/cli/cli-account.test.ts b/tests/cli/cli-account.test.ts index 214270e4be..032d1fdb81 100644 --- a/tests/cli/cli-account.test.ts +++ b/tests/cli/cli-account.test.ts @@ -697,6 +697,22 @@ describe("ocx account CLI (issue #180 matrix)", () => { expect(new Set(parsed.accounts.map(row => row.type))).toEqual(new Set(["codex", "oauth", "api-key"])); }); + test("OAuth JSON preserves the plan presence signal from older proxies", async () => { + oauthAccounts = [ + { id: "legacy" }, + { id: "unknown", plan: null }, + { id: "known", plan: "max" }, + ]; + + const result = await run(["list", "anthropic", "--json"]); + const parsed = JSON.parse(result.stdout) as { accounts: Array> }; + + expect(result.code).toBe(0); + expect(parsed.accounts[0]).not.toHaveProperty("plan"); + expect(parsed.accounts[1]).toHaveProperty("plan", null); + expect(parsed.accounts[2]).toHaveProperty("plan", "max"); + }); + test("3: empty providers are skipped by default and shown with --all", async () => { const normal = await run(["list"]); const withAll = await run(["list", "--all"]);