Skip to content

feat: promptconduit watch [--verbose] — tail outbound HTTP traffic#49

Draft
scotthavird wants to merge 1 commit into
mainfrom
claude/support-envelope-v1.2-Gzsge
Draft

feat: promptconduit watch [--verbose] — tail outbound HTTP traffic#49
scotthavird wants to merge 1 commit into
mainfrom
claude/support-envelope-v1.2-Gzsge

Conversation

@scotthavird
Copy link
Copy Markdown
Contributor

Summary

New top-level watch subcommand that streams every HTTP request the CLI makes to the platform to your terminal in real time. Closes the "events aren't showing up on the dashboard, but I don't know if my hook is even firing" debugging gap.

promptconduit watch                # live tail
promptconduit watch --verbose      # also pretty-print request/response bodies
promptconduit watch --lines 20     # backfill the last 20 entries before going live

Each request shows up as a one-line summary by default:

15:30:42  POST  /v1/events/raw  3.2KB  → 200 (87ms)

ANSI color on status (green 2xx, yellow 3xx, red 4xx/5xx) when stdout is a tty.

Mechanism

  • New internal/outbound package implements an http.RoundTripper that wraps http.DefaultTransport, captures each request+response, and appends one ndjson line to ~/.config/promptconduit/outbound.ndjson (mode 0600). The mirror is wired into client.NewClient so both the foreground command and the hook --send-event subprocess feed into the same file — no edits to the 15+ Send* / Sync* / Get* methods individually.
  • Redaction. Authorization, Cookie, Set-Cookie, X-Api-Key, Proxy-Authorization plus any header name containing token/secret/key (case-insensitive) are replaced with *** before write.
  • Truncation. Request and response bodies are capped at 64KB per row; oversize entries record req_truncated: true + req_original_size_bytes so the user can tell something got clipped.
  • Rotation. When outbound.ndjson crosses 50MB it's renamed to outbound.ndjson.1 (one backup kept) and a fresh file is started.
  • Cross-process concurrency. O_APPEND + per-process sync.Mutex everywhere; on Unix, lines >4KB additionally take an exclusive flock to dodge the 4KB POSIX PIPE_BUF atomicity limit so parent and hook --send-event subprocess writes don't tear.
  • Tail. Stdlib polling (200ms). Recovers across rotation by watching the inode (Unix) or size-shrink (Windows fallback).

Out of scope (documented in README)

internal/updater builds its own *http.Client for the GitHub releases check, so update-check traffic bypasses the mirror. That's intentional — it's predictable and noisy and isn't what watch is for. The upgrade command continues to work; it's just invisible to watch.

Test plan

  • go build ./... && go vet ./... && go test ./... clean
  • 22 new tests in internal/outbound/: httptest round-trip, body capture, file mode 0600, body truncation, rotation under a low threshold, downstream-readable request body after mirror capture, network-error recorded with error field, header redaction table (Authorization/Cookie/X-Api-Key/X-Secret-Token redacted, Content-Type / User-Agent preserved, nil-safe, doesn't mutate input), render summary (default / verbose / color codes / network error), truncateBody pure helper, ParseLine round-trip, tail backfill + live, tail recovery across rotation
  • End-to-end smoke: fired a hook event into a binary built at v0.5.0, confirmed mirror file at $XDG_CONFIG_HOME/promptconduit/outbound.ndjson with mode -rw-------, full envelope JSON captured, Authorization redacted, transport error recorded; then ran watch --verbose --lines 1 and saw the summary + pretty-printed body; clean exit on Ctrl-C
  • Live test against a real Claude Code session sending real envelopes to the platform

https://claude.ai/code/session_019CWBC2E8pQuShejfsKYrvp


Generated by Claude Code

New top-level `watch` subcommand that lets users see every request the
CLI sends to the platform in real time. Useful for debugging the
"events aren't showing up on the dashboard" class of problem without
spelunking the debug log.

Mechanism:

  - New internal/outbound package implements an http.RoundTripper that
    wraps http.DefaultTransport, captures each request+response, and
    appends one ndjson line to ~/.config/promptconduit/outbound.ndjson
    (mode 0600). Wired into internal/client.NewClient so both the
    foreground command and the `hook --send-event` subprocess feed
    into the same file — no edits to the 15+ send methods individually.
  - Authorization / Cookie / X-Api-Key / *secret* / *token* / *key*
    headers redacted to "***" before write.
  - Bodies capped at 64KB per direction; oversized requests record
    `req_truncated: true` plus the original size for context. File
    rotates to outbound.ndjson.1 when it crosses 50MB; one backup kept.
  - Cross-process concurrency: O_APPEND with a per-process sync.Mutex
    on every platform; on Unix, lines >4KB take an exclusive flock so
    they don't tear under the 4KB POSIX PIPE_BUF atomicity limit.
  - Tail uses stdlib polling (200ms) and recovers across rotation by
    watching the inode (Unix) or size-shrink (Windows).

UX:

  - `promptconduit watch` — live tail, one summary line per request:
    `15:30:42  POST  /v1/events/raw  3.2KB  → 200 (87ms)` with ANSI
    status color when stdout is a tty.
  - `promptconduit watch --verbose` — also pretty-prints the JSON
    body indented under each summary.
  - `promptconduit watch --lines N` — backfill the last N entries
    before going live.
  - Ctrl-C / SIGTERM exits cleanly with no usage banner.

Tests in internal/outbound/ (22 cases): round-trip via httptest.Server,
file permissions 0600, body truncation, rotation under a low threshold,
downstream-readable body after capture, network-error recording,
tail backfill+live, tail recovery across rotation, header redaction
table, render summary (default + verbose + color + error path),
truncateBody pure helper, ParseLine round-trip.

Out of scope: internal/updater builds its own *http.Client for the
GitHub releases check, so update-check traffic bypasses the mirror.
Documented in the README — it's predictable and noisy and isn't what
`watch` is for. The hook update notifier and upgrade command continue
to work normally; they're just invisible to `watch`.

https://claude.ai/code/session_019CWBC2E8pQuShejfsKYrvp
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.

2 participants