feat(cli): M4 observability + runtime — usage / logs / quota / chat + provider oauth (25 cmds)#13
Merged
Conversation
…der oauth (25 cmds)
Adds an HTTP runtime client (src/cli/runtime.rs) that resolves the local
server endpoint from CLI flags, pid sidecar, or default port, probes
/api/health with exit code 6 on failure, and provides GET/POST/SSE
helpers (no client-side timeout for streams). All M4 subcommands speak
through it.
New top-level commands:
- usage: summary, daily, chart, history, stats, providers, logs,
request-logs, stream (SSE → NDJSON), pricing
- logs: tail (--follow → SSE), export, clear, stats
- quota: list, get, reset, refresh (built atop /api/usage/providers)
- chat: models, tags, send, stream (POST SSE → NDJSON)
Nested under `provider`:
- provider oauth: start, poll, status, refresh, import-kiro,
iflow-cookie, gitlab-pat
Server-side support:
- Re-introduces /api/observability/{logs,stream,stats,clear} backed by
the existing console-log broadcast channel, requiring api-key auth
so CLI sessions can subscribe.
- Relaxes /api/usage/stats and /api/usage/stream from dashboard-session
to usage-access auth so CLI keys can read live counters.
Schema list / show / example now covers the new envelope shapes
(openproxy.v1.{usage,log,chat}.event, quota, oauth.status), so agents
can introspect without browsing the dashboard.
Test infra:
- assert_cmd + wiremock + serde_json round-trips through the real
openproxy binary against a mocked server (tests/cli_m4_robot_envelopes.rs,
10 cases, all green).
- Existing src/cli/runtime.rs unit tests cover SseFrames parsing and
error-code → exit-code mapping.
- New dev-deps: assert_cmd, predicates, insta (for future golden snapshots).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements M4 — Observability & runtime from the PLAN v3: ~25 new CLI subcommands that wrap the running server's
/api/usage/*,/api/observability/*,/api/oauth/*,/v1/models, and/api/dashboard/chat/completionsendpoints. Single PR per user request — no M4a/M4b split.New runtime plumbing (
src/cli/runtime.rs)--url/OPENPROXY_URL(remote mode){data_dir}/openproxy.endpointsidecar127.0.0.1:4623fallbackOPENPROXY_API_KEYenv → first active key in localdb.json.ensure_alive()probes/api/healthwith a 1.5s timeout and maps connection failures to a stable exit code 6 +server_unreachableenvelope.stream_sse()/post_stream_sse()open SSE responses with no client-side timeout — streaming subcommands follow indefinitely until Ctrl+C, per spec.SseFramesadapter extractsdata:payloads from raw SSE bytes (handles both LF and CRLF, skips:-prefixed pings); unit-tested.New CLI commands
usagesummary,daily,chart,history,stats,providers,logs,request-logs,stream,pricing/api/usage/*logstail [--follow],export,clear,stats/api/observability/*quotalist,get,reset --yes,refresh/api/usage/providers+/api/observability/clearchatmodels,tags,send,stream/v1/models,/api/tags,/api/dashboard/chat/completionsprovider oauthstart,poll,status,refresh,import-kiro [--auto],iflow-cookie,gitlab-pat/api/oauth/*All commands honor the global
--robotflag and emit stableopenproxy.v1.*envelopes (NDJSON for streams). Streaming commands flush per chunk.Server-side support
/api/observability/{logs,stream,stats,clear}(moved from inline/api/logs/listinmod.rs), backed by the existingconsole_logsbroadcast channel + 25-second SSE keepalive pings. All four endpoints require API-key auth so CLI sessions can subscribe./api/usage/statsand/api/usage/streamfromrequire_dashboard_session→require_usage_access, so CLI API keys can read live counters without forging a dashboard session cookie.Schema introspection
openproxy schema list/show/examplenow covers the new envelope shapes so agents can introspect without browsing the dashboard:usage-event,log-event,chat-event— NDJSON event envelopesquota— per-provider aggregate rowoauth-status—provider oauth statusreturn shapeTest infrastructure
assert_cmd,predicates,insta(last reserved for future golden-snapshot tests).tests/cli_m4_robot_envelopes.rs(10 cases, all green): launches the realopenproxybinary viaassert_cmdagainst awiremockmock server, round-trips--robotstdout throughserde_json, and asserts envelope shape. Covers happy paths for every M4 group plus the exit-6 /server_unreachablepath.src/cli/runtime.rsunit tests cover SSE frame extraction andRuntimeError → exit_codemapping.Review & Testing Checklist for Human
src/cli/runtime.rs— this is the dependency of every M4 command. Pay attention to howapi_keyis resolved (cfg → env → first active db.json key) and where streams disable the request timeout (stream_client()).target/debug/openproxy --robot usage summaryagainst a running server. Confirm shape is{schema, ok, data, error, meta}.openproxy logs tail --followandopenproxy usage streamshould run until Ctrl+C with no spurious disconnects. Server emits an SSE:keepaliveping every 25s.OPENPROXY_URL=http://127.0.0.1:1 openproxy --robot usage summary; echo $?→ must print exit 6 withserver_unreachable.cargo test --test cli_m4_robot_envelopeslocally — 10 tests should pass in <1s.Notes
provider oauthis nested underprovider(not top-level) because the M3 layout already groups everything provider-related under that namespace; the dispatcher in bothmain.rsandcli/mod.rs::Cli::runpeels off theOauthvariant before falling through torun_provider(which still owns the DB-mode subcommands).chat streamreads stdin via--prompt -(or accepts an inline--prompt "hello"); same forchat send.quota resetis a thin alias over the observabilityclearendpoint — true per-key quota enforcement is a separate M6 task.