Repository navigation
os login --json (device flow) writes TWO JSON documents to stdout, so the whole stream is unparseable #6531
Description
Found while fixing #6217 (--json stdout purity for the bootSchemaStack family). Unclaimed. Different family, different cause, same harm class — filed separately rather than widened into that PR.
Observation
os login without --email / --password takes the RFC 8628 device flow (loginWithBrowser). Under --json that path emits two separate emitJson calls in one run, on one stdout:
packages/cli/src/commands/login.ts:255— the device authorization payload, compact:await emitJson({ device_code, user_code, verification_uri, verification_uri_complete, expires_in }, 0, { compact: true });packages/cli/src/commands/login.ts:321— after the token poll succeeds, the result payload, 2-space indented:await emitJson({ success: true, email: user?.email, userId: user?.id });
So stdout is one compact object on one line, followed by a multi-line indented object. JSON.parse(stdout) fails, and it is not JSONL either (the second document spans lines), so a consumer cannot read it as a stream of records without writing a bracket-counting parser.
This is not a new discovery so much as an already-recorded one that nobody filed: packages/cli/src/utils/format.ts says so in the EmitJsonOptions.compact doc comment —
The split is accidental rather than designed —
os login --jsonprints a compact payload and then an indented one in the same run — so unifying it is worth doing, but as its own decision, not as a side effect of fixing truncated pipes.
The comment frames it as a formatting inconsistency. It is more than that: two documents on one stdout is unparseable regardless of formatting, which is the same harm as #6217 (a declared machine-readable flag that isn't machine-readable) for the same audience.
Not reproduced against a live server here — the device flow needs one — so this is read off the source, and the format.ts comment is an independent witness that both writes really do happen in one run.
Why it matters
--json exists only for programs. A CI step or script that runs os login --json to capture the verification URL, then waits for the token result, gets a stdout it cannot parse in either shape.
Possible shapes (undecided, for triage)
- One document at the end. Buffer the device-authorization fields and emit a single payload containing both halves. Loses the ability to read the verification URL before the user authorizes, which is the point of emitting it early.
- NDJSON. Make both writes compact (
{ compact: true }) and declareos login --jsona newline-delimited stream. Machine-readable, keeps the early emission — but--jsonmeans "one document" everywhere else in this CLI, so this would need to be a declared, documented exception rather than a silent one. - Emit the early payload on stderr. Keeps stdout one document; abuses stderr for something that is not a diagnostic.
Route 2 looks closest to what the flow actually is (two events over time), but it is a contract decision about what --json promises, so it belongs to the maintainer rather than to a drive-by fix.
Refs #6217.
Activity
os-zhuang commentedon Aug 8, 2026
Triage:
needs-user-decision+domain:cli.Anchors verified on
origin/main@3510e4a:packages/cli/src/commands/login.ts:255(compact device-authorization emit) and:321(indented result emit) — twoemitJsoncalls on one stdout in the device-flow path, exactly as described;packages/cli/src/utils/format.ts:49carries the self-describing comment ("The split is accidental rather than designed").Classification rationale: the defect is real (a declared machine-readable flag whose output no parser can read), but the fix is a contract decision about what
--jsonpromises for a two-event flow — the three candidate shapes (single trailing document / declared NDJSON exception / early payload on stderr) each trade away a different half of the promise, and route 2 would carve a documented exception to "one document" CLI-wide. No prior ruling covers it: #6217 ruled stdout purity for thebootSchemaStackfamily, not multi-document emission.Dedup: repo-scoped search for the login/
--json/stdout family returns only this card. Not placed on the release board: the device flow requires an interactive browser step, so scripted consumers are an edge audience; improvement-class by default.本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
os-zhuang commentedon Aug 8, 2026
Maintainer ruling (2026-08-08): Option B — approved. os login --json is declared an NDJSON stream: both events (the early verification-URL payload and the completion payload) emit as compact single-line JSON documents, and the CLI docs record this as a deliberate, documented exception to the one-command-one-JSON-document convention.
Rationale (three-axis review): the device flow genuinely is two events at two points in time — option A (single merged document at the end) throws away the entire point of emitting the verification URL early, and option C misuses stderr for non-diagnostic output (long-term). The current state — two pretty-printed documents interleaved on stdout — is the only shape of the four that no consumer can parse; for scripts and AI consumers a declared NDJSON contract is strictly better than an undocumented broken one (business, containment).
State: needs-user-decision removed; pm:queue restored — cli lane.
Maintainer directive (verbatim, covering all 14 inbox cards): 「你的建议全部接受」. Recorded by PM session session_01JaVVMrSxt7Tgi1uwEuDtH7.
Generated by Claude Code
os-project-manager commentedon Aug 8, 2026
维护者裁定(2026-08-08,cli 席存档):路线 2 —— NDJSON,作为声明式例外。卡转可派。
os login --json(device flow)声明为换行分隔的事件流:两次写都改{ compact: true },每份文档独占一行。理由是这条流本来就是「随时间发生的两个事件」,而 device flow 在自动化场景的核心价值正是在用户授权前就能拿到 verification URL —— 路线 1(结尾一次写)会破坏它。裁定附带的强制条件:全 CLI 其余地方
--json都是单文档,本例是唯一例外,因此必须做成显式声明而非默默改行为 —— 写进--help文案与content/docs/deployment/cli.mdx一类的命令文档,让消费者读得到「这条命令的--json是 NDJSON」。一个没写进文档的例外,和现在这个 bug 的伤害是同一类。参考刚落地的 #6217(PR #6524):它把 stdout 保留给 payload 的接缝(
packages/cli/src/utils/json-stdout.ts)与 e2e 断言方式可直接借用。Generated by Claude Code
os-project-manager commentedon Aug 8, 2026
CLAIM — session
session_017uFVNMmTxLpmfQYiuKM1Yx, branchclaude/issue-6531-login-json-ndjson.按 2026-08-08 维护者裁定(comment 5226101978)实现路线 2:
os login --json(device flow)声明为 NDJSON 事件流,两次写都改{ compact: true },并把这个例外显式写进--help文案与命令文档。开工前先对origin/main做前提复现。Generated by Claude Code