Skip to content

feat(cli): live-capture subcommand — studio capture (--ndjson) (closes #57)#65

Merged
sepehr-safari merged 1 commit into
mainfrom
feat/cli-capture
Jul 12, 2026
Merged

feat(cli): live-capture subcommand — studio capture (--ndjson) (closes #57)#65
sepehr-safari merged 1 commit into
mainfrom
feat/cli-capture

Conversation

@sepehr-safari

Copy link
Copy Markdown
Member

Closes #57. Wires the S5 flagship proxy into the headless CLI — the terminal face of live capture.

What's here

studio capture --listen H:P --upstream ws://H:P [--ndjson]

  • Dispatch + handlermaybeRun gains capture; cmdCapture parses the flags, resolves the addresses, and calls proxy.run(io, …, .{ .wall = init.io }) for one CP↔CSMS session. The TimeSource.wall path built in feat(capture): MITM proxy — streaming detection + record-to-trace #56 drops straight in.
  • --ndjson streams each captured event as a JSONL line to stdout as it arrives (studio capture … --ndjson > session.jsonl saves the trace — consistent with the CLI's stdout-only house style). Without it, a one-line summary is printed. The Sink now flushes each record so the stream is genuinely live (a no-op for the fixed-buffer writers in tests).
  • Pure parseCaptureArgs (unit-tested) handles parsing; wss:// is rejected (TLS is post-0.5, ADR-0008); IP addresses only for now (hostname resolution deferred).
  • docs/cli-parity.md gains the Studio-only capture row + notes (the toolkit is offline by charter).

Verification

  • native test -Dplatform=null152/152 (2 new arg-parser tests).
  • native build (ReleaseFast) → clean. Wiring run() into the CLI brings its socket path into the app build's analysis (it was test-only before) — so native build now compiles it too.
  • zig fmt --check clean.
  • On the built binary: every usage/error/help path (missing --listen → exit 2, wss:// rejected, etc.), no regression on existing commands (studio ci → exit 0), and studio capture --listen 127.0.0.1:38765 … binds and waits on accept (run() starts correctly).

Coverage note (honest)

The relay core (relayStreams: handshake + concurrent pump + record + detect) is proven end-to-end by #56's in-memory integration test; run()'s thin socket glue is compiled and shown to bind/wait on the real binary. A full live CP↔proxy↔CSMS socket smoke needs a real CP/CSMS harness — a reasonable follow-up (the CP-sim / CSMS-mock are post-0.5 anyway).

Deferred (noted in cli-parity.md)

wss:// (TLS, post-0.5), hostname resolution (IPs for now), single session per invocation.

Wires the S5 flagship proxy into the headless CLI as `studio capture` — the
second face's live command.

- maybeRun dispatches `capture`; cmdCapture parses --listen host:port /
  --upstream ws://host:port / --ndjson, resolves the addresses, and calls
  proxy.run(io, ..., .{ .wall = init.io }) for one CP<->CSMS session.
- --ndjson streams each captured event as a JSONL line to stdout (redirect to
  save a trace); otherwise a one-line summary is printed. The Sink now flushes
  each record so the stream is live (a no-op for the fixed-buffer test writers).
- A pure parseCaptureArgs (unit-tested) does the parsing; wss:// is rejected
  (TLS is post-0.5), IP addresses only for now (hostname resolution deferred).
- docs/cli-parity.md gains the Studio-only `capture` row + notes.

Wiring run() into the CLI also brings its socket path into the app build's
analysis (it was previously test-only). Verified on the built binary: usage /
error / help paths, no regression on existing commands, and run() binds its
listen socket and waits on accept.

Closes #57.
@sepehr-safari sepehr-safari merged commit 915657c into main Jul 12, 2026
3 checks passed
@sepehr-safari sepehr-safari deleted the feat/cli-capture branch July 12, 2026 13:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(cli): studio capture subcommand (NDJSON event stream)

1 participant