You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Consolidate Chrome responsibility that was scattered across DynamicCommand,
CdpDriver and Recorder behind ChromeLauncher and a new ChromeSession bridge,
add a persistent-profile launch so a real logged-in session can be traced, and
switch the journey to always open a fresh tab in the target window instead of
reusing an existing one.
- ChromeSession (new): the bridge between a held browser (launched or attached)
and the CDP transport. Holds the port, knows whether it OWNS the process (so
teardown is real for a throwaway and a no-op for an attached window), and
exposes Chrome target discovery (pageTargets/openBlankTab). The raw websocket
connect stays in CdpDriver (shared with Node).
- ChromeLauncher.acquire(spec): one entry point that decides attach vs throwaway
headless vs persistent profile and returns a ChromeSession. Spawn is
parametrized for headless/headed and ephemeral/persistent profiles; a
user-owned profile dir is never deleted on teardown. DynamicCommand's
launch-vs-attach branch collapses into a single acquire() + session.kill().
- Authenticated sessions: new --chrome-profile <dir> (headed, persistent
--user-data-dir, reuses saved logins/cookies) and --headed flags, with
validation. Recorder's inline /json fetch now goes through the bridge too.
- No tab targeting: JourneyRunner always opens its OWN tab and drives that, so
an attached real window keeps its existing tabs untouched while our tab rides
the same profile (same logins). Removed the now-dead urlMatch tab matcher from
the Chrome path and ChromeSession.
Verified: build clean, 47/48 unit tests pass (1 Postgres skip); end-to-end a
--chrome-profile run traces the React bug and the profile persists across two
separate CLI invocations; an attach run opens one fresh tab and leaves all
pre-existing tabs intact.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
# trace-cli — unified execution tracer for Node & Chrome
8
8
9
9
- Attaches to a running debug target, sets breakpoints, fires a trigger, prints the full execution trace in one shot. One engine, one protocol driver — **CDP** for Node and Chrome. You read the trace; you never drive the debugger by hand.
10
-
-**Chrome can auto-launch:**`--chrome <port>` attaches to a browser you started (a real, logged-in session); bare `--chrome` (no port) launches a throwaway headless Chrome, traces, records, and tears it down — a frontend trace needs only the app running.
10
+
-**Chrome can auto-launch:**`--chrome <port>` attaches to a browser you started (a real, logged-in session); bare `--chrome` (no port) launches a throwaway headless Chrome, traces, records, and tears it down — a frontend trace needs only the app running.`--chrome-profile <dir>` launches a headed browser on a persistent profile, so a saved-login session is reused without a hand-started browser (use a copy of your profile; the dir is never deleted on teardown).
11
11
-**Static analysis needs no running app:**`trace-cli graph` is a call graph (flow tree) via **LSP call hierarchy** — map what a route/function calls, and find breakpoint coordinates before a runtime trace. TS/JS bundled; other languages via `--server` (`gopls` · `pyright --stdio` · `rust-analyzer` · `clangd`, must expose `callHierarchyProvider`). The other analyses are `deps`/`complexity`/`symbols` — run `trace-cli --help`.
if(options.chrome!=null&&options.node!=null)usage("pick one target: --node or --chrome, not both");
109
+
if(options.chromeProfile&&options.node!=null)usage("--chrome-profile is a chrome option — don't combine it with --node");
110
+
// --chrome-profile launches a browser on that profile; an explicit --chrome <port> means attach to a running one.
111
+
if(options.chromeProfile&&typeofoptions.chrome==="string")usage("pick one: --chrome-profile launches a logged-in browser, or --chrome <port> attaches to a running one — not both");
112
+
if(options.headed&&!(options.chrome!=null||options.chromeProfile))usage("--headed only applies when launching Chrome (use with --chrome or --chrome-profile)");
104
113
if(options.concise&&options.detailed)usage("pick one envelope verbosity: --concise or --detailed, not both");
.description("breakpoints + a trigger → a full execution trace. Breakpoints are non-pausing logpoints: each hit ships its stack + in-scope locals + exprs without halting the VM, so the app runs at full speed. Node (CDP): a --curl trigger. Chrome (CDP): a scripted UI journey (--url/--step) recorded as a screen + trace-panel replay — debug and video together.")
261
270
.option("--node [port]",`Node --inspect target (default; port ${DEFAULT_NODE_PORT})`)
262
271
.option("--chrome [port]","Chrome target: a running browser's --remote-debugging-port, or omit the port to launch a throwaway headless Chrome")
272
+
.option("--chrome-profile <dir>","Chrome: launch a (headed) browser on this persistent --user-data-dir so saved logins/cookies carry over — trace a real, authenticated session. Use a COPY of your profile (Chrome 136+ blocks remote-debugging on the default dir; one process per dir). Implies launching, so don't combine with --chrome <port>.")
273
+
.option("--headed","Chrome: launch the browser visibly instead of headless (applies to --chrome / --chrome-profile launch modes; implied by --chrome-profile)")
263
274
.option("--breakpoint <file:line>","breakpoint, repeatable: file:line or file@substring (non-pausing; in-scope locals are captured automatically)",collect,[])
264
275
.option("--expression <js>","extra expression captured at every hit, repeatable — for computed/derived values beyond the auto-captured locals (e.g. user.id, cart.length)",collect,[])
265
276
.option("--root <dir>","project root for resolving --breakpoint file paths and source maps (default: cwd) — needed when a file@substring breakpoint or a built app's sources live outside cwd")
0 commit comments