Skip to content

feat: add output formats, event collection, URL blocking, and more#8

Open
aeroxy wants to merge 17 commits into
mainfrom
dev
Open

feat: add output formats, event collection, URL blocking, and more#8
aeroxy wants to merge 17 commits into
mainfrom
dev

Conversation

@aeroxy

@aeroxy aeroxy commented Jun 14, 2026

Copy link
Copy Markdown
Owner
  • Introduce OutputFormat (Text/Json/Toon) with --toon flag alongside --json. TOON produces compact tabular output for LLM agents (toon-format crate).
  • Add console, network, and sw-logs commands for inspecting page console messages, network requests, and extension service worker logs.
  • Add persistent CDP session that stays attached to the current page so events accumulate across commands; console/network drain on demand instead of using blocking --duration windows.
  • Add --block-url/--allow-url under emulate (+ as global flags) for URL blocking via Network.setBlockedURLs; patterns persist in the daemon.
  • Add kill-daemon subcommand for stopping the background daemon cleanly.
  • Rewrite skill/chrome-devtools/SKILL.md with correct target-name workflow, pattern-based usage examples, and critical gotchas.

Summary by CodeRabbit

Release Notes

  • New Features
    • Added console, network, and sw-logs commands with optional filtering and duration-based live mode (plus quick drain mode).
    • Added kill-daemon and enhanced persistent background inspection that reattaches across targets.
    • Extended URL blocking/unblocking with clear options and integrated it into emulation behavior.
  • Documentation
    • Expanded the Chrome DevTools Protocol CLI guide and updated the README for new commands, output modes, and persistent inspection.
  • Tests / Bug Fixes
    • Added tests for per-tab emulation isolation and improved cleanup when closing pages.

…ent session

- Introduce OutputFormat (Text/Json/Toon) with --toon flag alongside --json.
  TOON produces compact tabular output for LLM agents (toon-format crate).
- Add `console`, `network`, and `sw-logs` commands for inspecting page
  console messages, network requests, and extension service worker logs.
- Add persistent CDP session that stays attached to the current page so events
  accumulate across commands; `console`/`network` drain on demand instead of
  using blocking --duration windows.
- Add --block-url/--allow-url under `emulate` (+ as global flags) for URL
  blocking via Network.setBlockedURLs; patterns persist in the daemon.
- Add `kill-daemon` subcommand for stopping the background daemon cleanly.
- Rewrite skill/chrome-devtools/SKILL.md with correct target-name workflow,
  pattern-based usage examples, and critical gotchas.
@coderabbitai

coderabbitai Bot commented Jun 14, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6c5f8b30-38f0-4329-b69a-e792589db0d3

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR introduces an OutputFormat enum (Text/Json/Toon) to replace all boolean json_output flags, adds persistent CDP session management with buffered network/console events and URL blocklist rules to CdpClient, implements three new inspection commands (console, network, sw-logs) plus kill-daemon, extends the emulate command with URL block/unblock list controls, and comprehensively rewrites documentation in the skill guide and README.

Changes

CDP CLI Expansion: OutputFormat, Persistent Sessions, New Commands, URL Blocklist

Layer / File(s) Summary
OutputFormat enum and format_structured
Cargo.toml, src/format.rs, src/lib.rs
Introduces OutputFormat { Text, Json, Toon } with from_flags(json, toon) (Toon precedence), is_text/is_json/is_toon helpers, and format_structured(value, format) to serialize serde_json::Value using pretty JSON or TOON encoding. Adds toon-format = "0.5" dependency. Registers format module in crate root.
DaemonRequest protocol: OutputFormat, blocklists, format()
src/protocol.rs, tests/executor_tests.rs
Extends DaemonRequest with output_format: Option<OutputFormat>, block_url/unblock_url vectors (serde-defaulted), and marks legacy json_output: bool with serde-default. Adds format() method that uses output_format when present, otherwise maps json_output to Json or falls back to Text. Updates executor tests to initialize new fields.
Persistent CDP client: session lifecycle, buffers, network rules
src/cdp.rs
Adds persistent_session/persistent_target_id Options, network_events/console_events buffers capped by MAX_PERSISTENT_EVENTS, blocklist, and per-target emulation cache to CdpClient. Implements ensure_persistent_session to attach/enable/apply rules, forget_target to drop saved state, apply_network_rules, drain helpers, get_all_targets, and read_events_for(duration_ms) for timed event reads. Routes persistent session events to appropriate buffers via updated push_event.
Console, network, and service-worker log collection commands
src/commands/console.rs, src/commands/network.rs, src/commands/sw_logs.rs, src/commands/mod.rs
Adds collect_console (drain/live modes, process and type-filter Runtime events, format text or JSON), collect_network (drain/live modes, process Network events keyed by requestId, merge request/response/failure), and collect_sw_logs (enumerate chrome-extension:// targets, attach sessions, map console/exception events to message objects with extracted extensionId). Each supports dual-mode collection from persistent buffer or live reading with timeout. Registers modules.
URL blocklist management in emulate command
src/commands/emulation.rs
Extends EmulateParams with block_url, unblock_url, clear_blocks fields; has_emulation() now counts them. emulate() adds blocklist maintenance phase before other work, calls apply_network_rules(), and returns status report of active overrides (including blocked URLs) instead of error when no other action taken. Viewport and geolocation parsing split per-field with dedicated error messages.
OutputFormat adoption in existing commands
src/commands/evaluate.rs, src/commands/snapshot.rs, src/commands/pages.rs, src/commands/third_party.rs, src/commands/input.rs, src/commands/navigate.rs
Replaces as_json/json_output bool with OutputFormat in evaluate, snapshot, list_pages, list_3p_tools, and execute_3p_tool. Each branches on format.is_text(), routes non-text through format_structured. close_page adds client.forget_target() call to drop saved emulation state. Minor formatting-only refactors to error handling in input and navigate.
CLI: new subcommands, output flags, global blocklist flags, kill-daemon
src/lib.rs
Adds mutually-exclusive --toon flag (conflicts with --json) and repeatable --block-url/--unblock-url to Cli. Adds Console, Network, SwLogs, KillDaemon command variants. Implements output_format() method and early kill-daemon execution (reads PID/socket, sends SIGTERM on Unix, cleans up). Serializes output_format and block lists in daemon requests. Removes -t alias from Evaluate. Updates direct-mode handlers to use output_format() and propagate blocklist state to emulation params.
Executor: persistent sessions, blocklist merge, format routing, new dispatch
src/commands/executor.rs
Extends known_args validation for new commands and emulate options. Updates is_browser_level for sw-logs/kill-daemon. Conditionally skips clear_events() for collectors. Ensures persistent session and merges top-level block lists (except emulate), applies rules on update. Prefers existing persistent session when target matches. Refactors argument validation. Routes output-capable commands through req.format(). Wires collectors with type filtering and extension id filtering. Tightens numeric parsing for page index.
Comprehensive documentation updates
README.md, skill/chrome-devtools/SKILL.md, tests/telemetry_tests.rs
SKILL.md rewritten with mandatory friendly-name targeting, 11 standard patterns (navigate/emulate/blocking/forms/console/network/evaluation/formats/snapshots/screenshots/extensions/tools), complete command reference with updated flags, critical gotchas with examples, and output format guidance. README expanded with blocklist/console/network/daemon docs, clarified URL blocks target subresources only, refreshed source layout. Minor telemetry test formatting.

Sequence Diagram(s)

sequenceDiagram
  participant Executor
  participant CdpClient
  participant CDP as Chrome CDP
  participant Format as format_structured

  Executor->>CdpClient: ensure_persistent_session(target_id)
  CdpClient->>CDP: Target.attachToTarget (flatten)
  CdpClient->>CDP: Network.enable / Runtime.enable
  CdpClient->>CDP: Network.setBlockedURLs(blocklist)

  alt console/network with duration_ms == 0 (drain)
    Executor->>CdpClient: drain_console_events() / drain_network_events()
    CdpClient-->>Executor: Vec from persistent buffer
  else console/network with duration_ms > 0 (live)
    Executor->>CdpClient: read_events_for(duration_ms)
    loop until timeout
      CDP-->>CdpClient: Runtime/Network event
      CdpClient->>CdpClient: push_event → buffer
    end
    Executor->>CdpClient: drain_*_events()
  else sw-logs
    Executor->>CdpClient: get_all_targets()
    CdpClient-->>Executor: all targets including service_worker
    loop per service_worker
      Executor->>CDP: Target.attachToTarget
      Executor->>CDP: Runtime.enable
    end
    Executor->>CdpClient: read_events_for(duration_ms)
  end

  Executor->>Executor: process_*_events(raw CDP)
  Executor->>Format: format_structured(events, req.format())
  Format-->>Executor: formatted String
  Executor-->>Executor: CommandResult::output(formatted)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

  • aeroxy/chrome-devtools-cli#7: Modifies list_3p_tools and execute_3p_tool in src/commands/third_party.rs, which this PR further updates to accept OutputFormat instead of a boolean flag and use format_structured for non-text output.
  • aeroxy/chrome-devtools-cli#5: Continues evolving the emulation/executor/pages surface—extending EmulateParams and blocklist logic in src/commands/emulation.rs and src/commands/executor.rs.

Poem

🐇 Hop through formats—Text, Json, and Toon,
Persistent sessions dance beneath the moon!
Networks buffered, consoles drained so clean,
Service workers logged in chrome-extension's scene.
Blocklists block, kill-daemon stands ready—
This rabbit's CLI keeps outputs steady! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: output formats (Text/Json/Toon), event collection (console/network/sw-logs), URL blocking (--block-url/--unblock-url), and a persistent CDP session architecture. It's specific, concise, and captures the primary features introduced.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@aeroxy

aeroxy commented Jun 14, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jun 14, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces persistent CDP sessions for continuous event collection, adds new commands for inspecting console logs, network requests, and extension service worker logs, and implements a compact, LLM-friendly TOON output format. The review feedback highlights critical issues that need to be addressed before merging: specifically, the event router in push_event and the service worker log processor both lack session ID filtering, which leads to event pollution from unrelated sessions. Additionally, several commands (sw-logs, console, and network) fail to disable domains or detach sessions if an error occurs during event reading, resulting in potential resource and session leaks.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/cdp.rs Outdated
Comment thread src/commands/sw_logs.rs Outdated
Comment thread src/commands/sw_logs.rs Outdated
Comment thread src/commands/console.rs Outdated
Comment thread src/commands/network.rs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/cdp.rs`:
- Around line 560-570: The match expression in the `read_events_for` function
combines timeout errors and socket/read errors into the same case, causing real
WebSocket failures to be silently treated as normal timeout completion and
partial data returned as success. Differentiate the two error cases: handle the
timeout case (when tokio::time::timeout returns Err) by breaking the loop
normally, and handle the actual read/socket error case (when self.read_text()
returns Ok(Err)) separately to either propagate the error, log it, or return an
appropriate result instead of silently breaking with partial data.
- Around line 146-152: When detaching from an old target in the
`ensure_persistent_session` method, the event buffers for `network_events` and
`console_events` are not being cleared, causing stale events from the previous
target to persist and be drained under the new page context. In the block where
`persistent_session.take()` is called and we detach from the target, add
clearing of both `network_events` and `console_events` buffers alongside the
`persistent_target_id = None` assignment. Apply this same fix at the other
affected location mentioned in the comment (lines 174-176).
- Around line 212-233: The persistent event buffering logic is capturing Network
and Runtime events without verifying they belong to the active persistent
session, causing events from different sessions to be mixed together. Modify the
event handling in the block where you check for Network.requestWillBeSent,
Network.responseReceived, Network.loadingFinished, Network.loadingFailed,
Runtime.consoleAPICalled, and Runtime.exceptionThrown to extract the sessionId
from the incoming event and compare it against the sessionId stored in
self.persistent_session before appending to self.network_events or
self.console_events. Only push events to these buffers if the sessionIds match.
- Around line 166-172: The Network.enable and Runtime.enable calls for
persistent session setup are being silently ignored with let _ = discards, which
means failures go undetected and the session is still marked as persistent even
when critical domain initialization fails. Remove the let _ = pattern and
instead check the results of both send_to_target calls for "Network.enable" and
"Runtime.enable" - either propagate any errors returned or at minimum log them
and prevent the session from being marked as persistent if either call fails.
This ensures downstream collectors cannot skip per-command enables and miss
events due to uninitialized domains.

In `@src/commands/emulation.rs`:
- Around line 102-114: The allow_url processing block removes patterns from
client.blocklist but never adds them to client.allowlist, so allowed rules are
not persisted as daemon state. When iterating through params.allow_url, add each
pattern to client.allowlist to ensure the allow rules are durably stored and
properly included in status output. Apply the same fix to any other location
where allow_url patterns are processed (such as the similar code block around
line 193-207).

In `@src/commands/executor.rs`:
- Around line 412-457: The executor uses inconsistent default values for the
duration parameter compared to the CLI. In both the "console" and "network"
command handlers, change the unwrap_or(3000) calls to unwrap_or(0) to match the
CLI defaults defined in src/lib.rs, ensuring that direct daemon callers sending
raw JSON requests without a duration field get 0ms (instant drain mode) instead
of 3000ms, which aligns with the documented behavior that omitting the duration
field means "drain instantly".

In `@src/commands/sw_logs.rs`:
- Around line 68-79: The code processes Runtime events for all sessionIds
without filtering out events that don't belong to attached service-worker
sessions. Add a validation check immediately after determining the source
variable (after the find operation on the sessions iterator) to skip processing
events where the sessionId is not found in the attached sessions. Specifically,
if source resolves to "unknown" (indicating the sessionId was not matched),
continue to the next event iteration to prevent unrelated page/session logs from
being included in the sw-logs output.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: af8ddc19-cdd3-4738-88ef-c3cb09704232

📥 Commits

Reviewing files that changed from the base of the PR and between 5a5136e and be9776e.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (17)
  • Cargo.toml
  • skill/chrome-devtools/SKILL.md
  • src/cdp.rs
  • src/commands/console.rs
  • src/commands/emulation.rs
  • src/commands/evaluate.rs
  • src/commands/executor.rs
  • src/commands/mod.rs
  • src/commands/network.rs
  • src/commands/pages.rs
  • src/commands/snapshot.rs
  • src/commands/sw_logs.rs
  • src/commands/third_party.rs
  • src/format.rs
  • src/lib.rs
  • src/protocol.rs
  • tests/executor_tests.rs

Comment thread src/cdp.rs
Comment thread src/cdp.rs Outdated
Comment thread src/cdp.rs Outdated
Comment thread src/cdp.rs
Comment thread src/commands/emulation.rs Outdated
Comment thread src/commands/executor.rs
Comment thread src/commands/sw_logs.rs
Correctness
- cdp::push_event now gates persistent-buffer routing on the event's
  sessionId matching the persistent page session, so sw-logs and ad-hoc
  live sessions no longer leak into the page's console_events /
  network_events buffers.
- sw_logs now filters out events that don't match an attached service
  worker session, preventing page/other-session events from appearing
  as SW logs.

Cleanup (URL blocking)
- Removed the dead `allowlist` field on CdpClient, the `--clear-allows`
  flag, and the unreachable "Allowed URLs:" display branch. The doc
  comments claimed allowlist semantics that were never implemented;
  --allow-url already just removed entries from the blocklist.
- Renamed --allow-url to --unblock-url to match its actual semantics
  (and reword --block-url's help to no longer reference --allow-url).

Robustness
- sw-logs / console / network now capture read_events_for's result, run
  session detach / Runtime.disable / Network.disable cleanup, and only
  then `?`-propagate the error. read_events_for is currently infallible,
  so this is dead-code hygiene — but removes a latent trap the moment
  it gains a fallible path.
- kill-daemon now uses libc::kill(SIGTERM) directly instead of shelling
  out to /usr/bin/kill, and distinguishes ESRCH (stale PID file) from
  real signalling failures.

Tests (19 → 31 tests)
- format::from_flags precedence (toon wins over json)
- DaemonRequest::format() precedence (output_format wins over legacy
  json_output bool)
- format_structured round-trips Json / Toon / Text
- process_console_events: arg joining, description fallback, exception
  mapping, type-filter include/exclude, `error` filter includes
  exceptions, ignores unknown methods
- process_network_events: request+response merge by requestId,
  loadingFailed status, type filter, URL sort, ignores unknown methods,
  orphan response dropped
- extract_extension_id: with path, no path, trailing slash, non-extension
  URL, empty string, prefix-only edge case

Misc
- Executor duration fallback for console/network changed 3000 → 0 so
  hand-crafted / malformed requests default to drain mode consistent
  with the CLI's documented contract.
- Console/Network --duration help clarified: events collected in live
  mode remain available to a later drain.
- skill/chrome-devtools/SKILL.md updated for --unblock-url rename.
@aeroxy

aeroxy commented Jun 14, 2026

Copy link
Copy Markdown
Owner Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces significant enhancements to the Chrome DevTools CLI, including a persistent CDP session for continuous Network and Console event collection, new commands (console, network, sw-logs, and kill-daemon), network URL blocking capabilities, and support for the compact TOON output format. The review feedback highlights critical issues regarding duplicate event delivery in live mode, a failure of URL blocking in direct/fallback mode, and opportunities to make the code more idiomatic and efficient by avoiding unnecessary cloning of session variables.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/commands/emulation.rs
Comment thread src/commands/console.rs Outdated
Comment thread src/commands/network.rs Outdated
Comment thread src/cdp.rs Outdated
Comment thread src/cdp.rs
Comment thread src/cdp.rs Outdated
@aeroxy

aeroxy commented Jun 14, 2026

Copy link
Copy Markdown
Owner Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request enhances the Chrome DevTools CLI by introducing persistent CDP sessions for continuous network and console event collection, adding new commands (console, network, sw-logs, and kill-daemon), implementing URL blocking, and supporting a compact TOON output format. The code review identified several important issues to address: redundant subcommand arguments shadowing global flags, a potential CDP session leak if initialization fails, fragile parsing of non-string primitive values in console and service worker logs, event buffer pollution from service worker logs, and premature deletion of socket/PID files when daemon termination fails due to permission errors.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/lib.rs Outdated
Comment thread src/cdp.rs Outdated
Comment thread src/commands/console.rs Outdated
Comment thread src/commands/sw_logs.rs Outdated
Comment thread src/commands/sw_logs.rs
Comment thread src/lib.rs Outdated
aeroxy added 3 commits June 14, 2026 14:54
…k-url flags

Page-level commands previously attached a throwaway per-command CDP session
while blocklist/emulation state lived on the long-lived persistent session, so
that state never governed the commands that actually ran. Unify on the
persistent session (falling back to a per-command attach only when it is
unavailable), and never detach the persistent session during cleanup.

This fixes `emulate --clear-viewport` / `--clear-geolocation` being silent
no-ops: the override was set on one short-lived session and the clear ran on a
different one. Set and clear now run on the same session, so clearing works.
It also lets `--block-url` govern the page's subresource requests during
navigation.

Also dedupe the network flags. `--block-url` / `--unblock-url` were declared
both as global flags and as `emulate` subcommand flags, which shadowed each
other and forced a workaround in the executor. They are now defined once
(global) and inherited by `emulate` via clap; the emulate handler still applies
them itself, ordered before `--clear-blocks` (clear, then add), which is why the
generic merge keeps skipping "emulate".

cdp: store the persistent session id before enabling Network/Runtime so events
arriving during enable are routed to the persistent buffers; reset the session
id and detach if either enable fails.

docs:
- Document that `--block-url` blocks subresources only (images, scripts,
  fetch/XHR, CDN, trackers) and not top-level navigations — a Chrome
  `Network.setBlockedURLs` limitation, not a CLI bug.
- Add README reference for console/network/sw-logs/kill-daemon.
- Correct the live-mode description: `--duration` consumes events, so they do
  not reappear on a later drain.

Verified against live (headless) Chrome on google.com: navigate, evaluate,
snapshot, network, console, fill, screenshot, dialog handling, subresource
blocking, and the `--clear-viewport` fix (756 -> 800 -> 756). cargo build clean,
cargo test 31 passed.
@aeroxy

aeroxy commented Jun 14, 2026

Copy link
Copy Markdown
Owner Author

/gemini review

@aeroxy

aeroxy commented Jun 14, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jun 14, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces persistent CDP sessions to continuously collect network and console events, adds new commands (console, network, sw-logs, and kill-daemon), and implements URL blocking capabilities along with a compact TOON output format for LLM agents. The reviewer feedback highlights critical issues with URL blocking in direct/fallback mode, where the blocklist is silently ignored when a persistent session is unavailable. To resolve this, the reviewer suggests refactoring apply_network_rules to accept an optional session ID and applying it during fallback session attachment. Additionally, the reviewer recommends preserving the chronological order of network requests instead of sorting them alphabetically by URL to improve sequence analysis.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/cdp.rs Outdated
Comment thread src/commands/emulation.rs
Comment thread src/commands/executor.rs Outdated
Comment thread src/commands/executor.rs
Comment thread src/commands/network.rs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
src/cdp.rs (1)

591-601: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don’t treat WebSocket read failures as normal timeout completion.

At Line 600, Ok(Err(_)) | Err(_) => break masks transport errors and returns partial results as success.

Proposed fix
-                Ok(Err(_)) | Err(_) => break,
+                Ok(Err(e)) => return Err(e),
+                Err(_) => break,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/cdp.rs` around lines 591 - 601, The current match statement at the
timeout call site is masking transport errors by treating both timeout
expiration (Err(_)) and read failures (Ok(Err(_))) identically by just breaking
out of the loop. Refactor the match statement to distinguish between these two
cases: when the timeout expires (Err(_)), you can safely break to indicate
normal timeout completion; however, when read_text() actually fails
(Ok(Err(_))), this represents a real transport error that should be propagated
or logged as an error rather than silently returning partial results as success.
This ensures that legitimate WebSocket failures are not hidden from the caller.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/cdp.rs`:
- Around line 203-211: The `apply_network_rules_internal` method is currently
discarding the result of the `send_to_target` call using `let _ = `, which means
failures to apply the Network.setBlockedURLs command to Chrome are silently
ignored and the caller cannot detect failures. Remove the `let _ = ` discard
pattern and instead propagate or handle the error result appropriately, either
by returning the Result type or checking and logging failures. Apply this same
fix to the other similar location mentioned at lines 189-193 where network rule
application also discards results.

In `@src/lib.rs`:
- Around line 685-689: The direct fallback mode for page navigation is
hardcoding empty vectors for block_url and unblock_url instead of using the
global blocklist configuration, causing command-line flags like --block-url and
--unblock-url to be silently ignored. Replace the Vec::new() calls with the
appropriate values from the global blocklist configuration so that the direct
fallback mode respects the same URL filtering rules as daemon mode. This issue
occurs at two locations: around line 685 (for new-page fallback) and around line
787 (for navigate fallback), both of which need to be updated consistently to
use the actual blocklist values instead of empty vectors.
- Around line 556-567: The PID is parsed as u32 but then cast to libc::pid_t (a
signed integer type) before being passed to the libc::kill function. On systems
where pid_t is i32, u32 values exceeding i32::MAX will wrap to negative numbers,
causing unexpected behavior. Add a validation check after parsing the PID string
into the pid variable to ensure the parsed value does not exceed the maximum
value representable by libc::pid_t (typically i32::MAX). If the PID is out of
range, return an error similar to the existing invalid PID error handling. This
validation should occur before the unsafe call to libc::kill to prevent
incorrect signal delivery.

---

Duplicate comments:
In `@src/cdp.rs`:
- Around line 591-601: The current match statement at the timeout call site is
masking transport errors by treating both timeout expiration (Err(_)) and read
failures (Ok(Err(_))) identically by just breaking out of the loop. Refactor the
match statement to distinguish between these two cases: when the timeout expires
(Err(_)), you can safely break to indicate normal timeout completion; however,
when read_text() actually fails (Ok(Err(_))), this represents a real transport
error that should be propagated or logged as an error rather than silently
returning partial results as success. This ensures that legitimate WebSocket
failures are not hidden from the caller.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7f330c19-8245-4c64-a916-c502a65b96fd

📥 Commits

Reviewing files that changed from the base of the PR and between 5a5136e and bbb25c9.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (21)
  • Cargo.toml
  • README.md
  • skill/chrome-devtools/SKILL.md
  • src/cdp.rs
  • src/commands/console.rs
  • src/commands/emulation.rs
  • src/commands/evaluate.rs
  • src/commands/executor.rs
  • src/commands/input.rs
  • src/commands/mod.rs
  • src/commands/navigate.rs
  • src/commands/network.rs
  • src/commands/pages.rs
  • src/commands/snapshot.rs
  • src/commands/sw_logs.rs
  • src/commands/third_party.rs
  • src/format.rs
  • src/lib.rs
  • src/protocol.rs
  • tests/executor_tests.rs
  • tests/telemetry_tests.rs

Comment thread src/cdp.rs Outdated
Comment thread src/lib.rs
Comment thread src/lib.rs Outdated
aeroxy added 2 commits June 14, 2026 17:12
…onologically

Executor
- When the persistent CDP session is unavailable (failed to attach or
  lost), the fallback fresh session now re-applies `Network.setBlockedURLs`
  using the daemon's blocklist. Without this, `--block-url` was silently
  ignored on the degraded path because ensure_persistent_session is what
  normally calls setBlockedURLs.
- `apply_network_rules_internal` promoted to pub(crate) so executor.rs can
  call it on the fresh session.

Network command
- Drop the alphabetical-by-URL sort on `network` output. Events now
  appear in first-seen (chronological) order, with redirects reusing the
  existing requestId in place rather than being repositioned.
- Test `test_process_network_events_sorts_by_url` renamed to
  `test_process_network_events_preserves_chronological_order` and
  expectations flipped (z.example.com before a.example.com).
…ndling

Network rule application used to swallow failures. `apply_network_rules` and
`apply_network_rules_internal` now return `Result` and propagate
`Network.setBlockedURLs` errors; the callers (`emulate`, the executor's global
block-flag handler, and the fallback-session apply) propagate with `?` instead
of discarding the result, so a failed blocklist update surfaces to the user.

`read_events_for` no longer treats a real WebSocket read error the same as the
collection-window timeout. A timeout is the normal completion path; an
`Ok(Err(_))` from `read_text()` is a transport failure and is now logged as a
warning before stopping, so a partial result isn't silently presented as a clean
end-of-window.

`kill-daemon` now validates that the PID from the PID file fits in
`libc::pid_t` (i32) before signaling. A corrupted PID file could otherwise wrap
to a negative value via `as`, and `kill(-pid)` signals an entire process group —
potentially hitting unrelated processes. The guard lives inside `#[cfg(unix)]`
so non-unix builds stay warning-clean.

Also document, via comments, that direct-mode `new-page` / `navigate`
intentionally do not carry the URL blocklist: blocking is daemon-only state and
there is no persistent session to apply it to in the one-shot direct path.
@aeroxy

aeroxy commented Jun 14, 2026

Copy link
Copy Markdown
Owner Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request significantly expands the Chrome DevTools CLI by introducing a persistent CDP session in the background daemon for continuous event tracking. It adds new commands to drain and live-monitor console logs (console), network requests (network), and extension service worker logs (sw-logs), alongside a kill-daemon command. It also implements URL blocking capabilities and introduces a compact, LLM-friendly TOON output format. The review feedback identifies that while the documentation states that running emulate with no flags displays all active overrides, the current implementation only shows blocked URLs. The reviewer provides actionable suggestions to store and display active viewport and geolocation overrides within the persistent CdpClient session.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/cdp.rs
Comment thread src/cdp.rs
Comment thread src/commands/emulation.rs
Comment thread src/commands/emulation.rs
Comment thread src/commands/emulation.rs
…tion per tab

`emulate` with no flags is documented to show all active overrides, but only the
blocklist was tracked — viewport and geolocation weren't stored, so they could
never be displayed. Store them as structured overrides (`ViewportOverride`,
`GeolocationOverride`) on the client: set on apply, reset on
`--clear-viewport` / `--clear-geolocation` / `--clear-all`, and render alongside
the blocklist in the no-flags output.

Make all emulation (viewport, geolocation, URL blocklist) **per-tab** instead of
a single daemon-wide value. The active tab's state lives in the
`blocklist`/`viewport`/`geolocation` fields; `ensure_persistent_session` saves
that state under the outgoing target id and restores the incoming target's saved
state (or empty) on every target switch, then applies it to the new session.
Each tab keeps its own overrides — they persist across navigation within a tab
and don't leak to other tabs, so e.g. a mobile viewport with images blocked on
one tab and a desktop baseline on another can be held at the same time. State is
kept in exactly one place per tab (active fields when current, the saved map
otherwise); restore removes from the map and save writes the leaving tab's own
state, so there's no duplication.

- Re-apply viewport/geolocation on target switch via `apply_emulation_internal`,
  mirroring the existing blocklist re-apply, so overrides survive switches.
- `close-page` calls `forget_target` to drop a closed tab's saved state (and
  reset the active fields + invalid persistent session if it was the active tab).
- Only save tabs that actually have overrides, so the saved-state map doesn't
  accumulate an empty entry for every tab visited.

Docs (README, SKILL) updated from daemon-wide to per-tab semantics.

Verified on live Chrome: tab A held 375px + `*.png` blocked (fetch blocked) while
tab B held 1440px + no block (fetch OK), concurrently, while interleaving
commands; `--clear-*` removes the right override; target_id is stable across
cross-origin navigation so a tab's state stays attached to it. cargo build clean,
cargo test 31 passed.
@aeroxy

aeroxy commented Jun 14, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jun 14, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@aeroxy

aeroxy commented Jun 14, 2026

Copy link
Copy Markdown
Owner Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces persistent CDP sessions to continuously collect network and console events, adding new commands (console, network, sw-logs, and kill-daemon) and per-tab emulation state isolation (viewport, geolocation, and URL blocking). It also adds support for the compact TOON output format. Feedback focuses on a security vulnerability in kill-daemon where a PID of 0 could terminate the entire process group, and performance optimizations in src/cdp.rs to replace Vec with VecDeque for more efficient event buffer capping.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/lib.rs
Comment thread src/cdp.rs Outdated
Comment thread src/cdp.rs Outdated
Comment thread src/cdp.rs
Comment thread src/cdp.rs Outdated
kill-daemon: refuse a PID of 0 before signaling. `libc::kill(0, SIGTERM)` signals
every process in the caller's process group — it would take down the CLI itself
and its siblings — and a corrupted/truncated PID file can read "0", which slips
past the existing `i32` overflow guard (0 is a valid i32). The check sits beside
that guard inside `#[cfg(unix)]`, so the two now cover both kill() footguns: 0
(process group) and negative/overflow (`-pid` process group).

cdp: switch the persistent `network_events` / `console_events` buffers from `Vec`
to `VecDeque`. The size cap runs on every push, so once a buffer is full the old
`Vec` did `drain(..1)` — an O(N) front-shift of ~5000 elements per event. With
`VecDeque` it's `push_back` + `pop_front`, both O(1). `drain_*` still returns
`Vec` via `.into()` (one O(N) conversion per drain command, not per event). This
also matches the generic `events` buffer, which was already a `VecDeque`.

Both verified on live Chrome: console/network still collect and drain correctly;
kill-daemon with a PID file of "0" reports "refusing to signal" instead of
signaling. cargo build clean, cargo test 31 passed.
@aeroxy

aeroxy commented Jun 14, 2026

Copy link
Copy Markdown
Owner Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces persistent CDP sessions to continuously collect console and network events, adds new commands (console, network, sw-logs, and kill-daemon), supports per-tab emulation and URL blocking isolation, and adds a compact --toon output format. The review feedback highlights several high-severity issues, recommending that both network and console type filters be normalized to lowercase for case-insensitive matching, with warn mapped to warning for robustness. Additionally, the reviewer notes that URL blocking rules should be applied directly to the active session ID in direct/fallback mode to ensure they take effect when the persistent session is unavailable.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/commands/network.rs Outdated
Comment thread src/commands/network.rs
Comment thread src/commands/emulation.rs
Comment thread src/commands/console.rs Outdated
Comment thread src/commands/console.rs
@aeroxy

aeroxy commented Jun 14, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jun 14, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/commands/emulation.rs`:
- Around line 104-106: The issue is that apply_network_rules() is called without
verifying that it can actually apply the rules, and it silently skips execution
when persistent_session is None, causing the operation to report success even
though no rules were applied. Before calling apply_network_rules() in the
network_changed conditional block, add a check to ensure that either a
persistent session exists or that an active command session is available. If
neither exists, return an error indicating that a session is required to apply
network rules. This prevents the silent failure and ensures blocklist updates
are only reported as successful when they are actually applied to an available
session.

In `@src/commands/executor.rs`:
- Around line 145-147: The new-page command is hardcoding empty vectors for
block_url and unblock_url fields, which causes the global URL blocklist flags to
be dropped since the command returns through the browser-level path before a
later merge can run. Instead of hardcoding block_url and unblock_url as
Vec::new(), populate these fields with the corresponding global blocklist
configuration values so that when new-page returns through the browser-level
path, the global blocklist flags are properly included and not lost.

In `@src/commands/sw_logs.rs`:
- Around line 41-57: The current code in the match block for
`client.attach_to_target` ignores failures from the `Runtime.enable` call using
`let _ =` but still adds the session to the `sessions` vector even when
`Runtime.enable` fails. This causes the session to be used for event collection
despite not being properly initialized, leading to empty results without
signaling setup failure. Fix this by checking the result of the `send_to_target`
call for `Runtime.enable` and only adding the session to `sessions` if it
succeeds; if it fails, log a warning and skip adding that session to the vector.

In `@src/lib.rs`:
- Around line 591-604: The kill-daemon error handling in the non-ESRCH branch
only logs the failure to stderr but still returns Ok(()) to the caller, causing
the process to exit with code 0 despite the signaling failure. This breaks
automation semantics. Modify the non-ESRCH error branch (the else block that
logs "Failed to signal daemon") to return an Err() containing the actual error
instead of returning Ok(()), while keeping the ESRCH case returning Ok(()) since
that represents a successful cleanup of stale files. This ensures signaling
failures propagate as actual errors up the call stack.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a5420ee1-74a1-406e-b5dd-5791410f9d10

📥 Commits

Reviewing files that changed from the base of the PR and between 5a5136e and b66207f.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (21)
  • Cargo.toml
  • README.md
  • skill/chrome-devtools/SKILL.md
  • src/cdp.rs
  • src/commands/console.rs
  • src/commands/emulation.rs
  • src/commands/evaluate.rs
  • src/commands/executor.rs
  • src/commands/input.rs
  • src/commands/mod.rs
  • src/commands/navigate.rs
  • src/commands/network.rs
  • src/commands/pages.rs
  • src/commands/snapshot.rs
  • src/commands/sw_logs.rs
  • src/commands/third_party.rs
  • src/format.rs
  • src/lib.rs
  • src/protocol.rs
  • tests/executor_tests.rs
  • tests/telemetry_tests.rs

Comment thread src/commands/emulation.rs
Comment thread src/commands/executor.rs Outdated
Comment thread src/commands/sw_logs.rs
Comment thread src/lib.rs
aeroxy added 2 commits June 15, 2026 00:50
Blocklist merge+apply now runs after ensure_persistent_session so
--block-url flags target the resolved tab, not a stale one from a
previous command. Browser-level commands (list-pages, sw-logs, new-page)
are intentional no-ops for --block-url since they have no target tab.

Emulation falls back to the command's session when persistent_session is
None (direct mode, degraded daemon path) instead of silently skipping
network rule application.

Service worker logs checks Runtime.enable result and skips sessions that
fail initialization, preventing empty results without diagnostics.

kill-daemon returns Err on non-ESRCH signal failures (e.g., EPERM) so
automation gets correct exit codes. Removed duplicate error output.

Added TabEmulation struct and unit tests for per-tab isolation invariant.
Network --type filter normalizes both sides to lowercase, closing the
gap between documented lowercase examples (fetch, xhr) and CDP's
capitalized resource types (Fetch, XHR). Silent mismatches are now
caught.

Console --type filter adds warn→warning alias (matches console.warn()
intuition) and case-insensitive matching. Exception path literals
documented as lowercase-contracted against the normalized set.

CLI help text updated to reflect case-insensitive behavior.
@aeroxy

aeroxy commented Jun 14, 2026

Copy link
Copy Markdown
Owner Author

/gemini review

@aeroxy

aeroxy commented Jun 14, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jun 14, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces persistent CDP sessions to the background daemon to enable continuous collection of network and console events, as well as per-tab emulation and URL blocking isolation. It adds new commands (console, network, sw-logs, and kill-daemon), introduces a compact, LLM-friendly TOON output format, and updates documentation and tests accordingly. The reviewer feedback suggests ensuring that global URL blocking flags (--block-url and --unblock-url) are correctly passed and applied during new-page and navigate commands even when running in direct (non-daemon) mode, so that subresources are blocked during the initial page load.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/lib.rs Outdated
Comment thread src/lib.rs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@skill/chrome-devtools/SKILL.md`:
- Around line 242-255: The documentation in the "Pattern 7: Output Formats"
section incorrectly references a `--text` flag that does not exist in the
implementation. Plain text is the default output format (as defined by
`#[default]` on `OutputFormat::Text` in src/format.rs), not a selectable flag.
Update the documentation to clarify that all commands default to human-readable
text output when neither flag is specified, and that only `--json` and `--toon`
are the available flag options for alternate output formats. Remove any mention
of `--text` as a flag option.

In `@src/lib.rs`:
- Around line 615-617: The error handling at the Err branch treats all
read_to_string errors identically, incorrectly reporting any failure as "daemon
not running". Instead, match on the specific error kind: only
io::ErrorKind::NotFound should result in the "No daemon running" message, while
other error kinds (such as permission denied, invalid data, or other IO errors)
should be handled explicitly with proper error logging that includes the actual
error details. This ensures permission and IO failures are not silently
misreported as the daemon not being present.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 3b37a441-05cb-4beb-8d75-8f019f8a0efb

📥 Commits

Reviewing files that changed from the base of the PR and between 5a5136e and 4f84cfd.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (21)
  • Cargo.toml
  • README.md
  • skill/chrome-devtools/SKILL.md
  • src/cdp.rs
  • src/commands/console.rs
  • src/commands/emulation.rs
  • src/commands/evaluate.rs
  • src/commands/executor.rs
  • src/commands/input.rs
  • src/commands/mod.rs
  • src/commands/navigate.rs
  • src/commands/network.rs
  • src/commands/pages.rs
  • src/commands/snapshot.rs
  • src/commands/sw_logs.rs
  • src/commands/third_party.rs
  • src/format.rs
  • src/lib.rs
  • src/protocol.rs
  • tests/executor_tests.rs
  • tests/telemetry_tests.rs

Comment thread skill/chrome-devtools/SKILL.md
Comment thread src/lib.rs Outdated
@aeroxy

aeroxy commented Jun 14, 2026

Copy link
Copy Markdown
Owner Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces persistent CDP sessions to continuously collect network and console events, per-tab emulation isolation (viewport, geolocation, and URL blocking), and new commands for console, network, and extension service worker log inspection, alongside a compact TOON output format. The reviewer feedback highlights several key improvements: resolving event duplication in the event loop by refactoring persistent event routing, returning early in sw-logs if no sessions are attached to prevent blocking, falling back to params["text"] for console events with empty arguments, and preserving HTTP redirect history in network event processing.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/cdp.rs
Comment on lines 358 to 397
fn push_event(&mut self, event: Value) {
// Only route events into the persistent buffers when the event's
// sessionId matches the persistent page session (flatten-mode events
// are tagged with sessionId). Events from ad-hoc sessions (sw-logs
// attach, per-command live sessions) fall through and go into the
// general events buffer — otherwise a subsequent page `console` drain
// would return extension service worker logs.
let from_persistent_session = self.persistent_session.as_deref().is_some_and(|s| {
event
.get("sessionId")
.and_then(|v| v.as_str())
.map(|session_id| session_id == s)
.unwrap_or(false)
});

if from_persistent_session {
let method = event.get("method").and_then(|v| v.as_str()).unwrap_or("");
match method {
"Network.requestWillBeSent"
| "Network.responseReceived"
| "Network.loadingFinished"
| "Network.loadingFailed" => {
self.network_events.push_back(event);
if self.network_events.len() > MAX_PERSISTENT_EVENTS {
self.network_events.pop_front();
}
return;
}
"Runtime.consoleAPICalled" | "Runtime.exceptionThrown" => {
self.console_events.push_back(event);
if self.console_events.len() > MAX_PERSISTENT_EVENTS {
self.console_events.pop_front();
}
return;
}
_ => {}
}
}
Self::push_to_buffer(&mut self.events, event);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The push_event method currently routes persistent session events to network_events or console_events, but falls back to pushing all other events to self.events. During read_events_for, this causes non-persistent events (such as extension service worker logs or direct-mode console/network events) to be pushed to self.events and returned in the result vector. Consequently, these events remain in self.events and are duplicated/re-processed in subsequent event-collecting commands.

To fix this, we should split the persistent routing logic into a separate helper method route_persistent_event and call it directly in read_events_for without pushing non-persistent events to self.events.

    fn route_persistent_event(&mut self, event: Value) -> bool {
        let from_persistent_session = self.persistent_session.as_deref().is_some_and(|s| {
            event
                .get("sessionId")
                .and_then(|v| v.as_str())
                .map(|session_id| session_id == s)
                .unwrap_or(false)
        });

        if from_persistent_session {
            let method = event.get("method").and_then(|v| v.as_str()).unwrap_or("");
            match method {
                "Network.requestWillBeSent"
                | "Network.responseReceived"
                | "Network.loadingFinished"
                | "Network.loadingFailed" => {
                    self.network_events.push_back(event);
                    if self.network_events.len() > MAX_PERSISTENT_EVENTS {
                        self.network_events.pop_front();
                    }
                    return true;
                }
                "Runtime.consoleAPICalled" | "Runtime.exceptionThrown" => {
                    self.console_events.push_back(event);
                    if self.console_events.len() > MAX_PERSISTENT_EVENTS {
                        self.console_events.pop_front();
                    }
                    return true;
                }
                _ => {}
            }
        }
        false
    }

    fn push_event(&mut self, event: Value) {
        if !self.route_persistent_event(event.clone()) {
            Self::push_to_buffer(&mut self.events, event);
        }
    }

Comment thread src/cdp.rs
Comment on lines +725 to +732
Ok(Ok(text)) => {
if let Ok(resp) = serde_json::from_str::<Value>(&text) {
if resp.get("method").is_some() {
self.push_event(resp.clone());
events.push(resp);
}
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Update read_events_for to use the new route_persistent_event helper instead of push_event. This ensures that events collected during the duration window are routed to persistent buffers if they belong to the persistent session, but are not pushed to self.events if they are non-persistent (preventing them from being duplicated in subsequent commands).

            match tokio::time::timeout(remaining, self.read_text()).await {
                Ok(Ok(text)) => {
                    if let Ok(resp) = serde_json::from_str::<Value>(&text) {
                        if resp.get("method").is_some() {
                            self.route_persistent_event(resp.clone());
                            events.push(resp);
                        }
                    }
                }

Comment thread src/commands/sw_logs.rs
Comment on lines +66 to +68
}

let events_result = client.read_events_for(duration_ms).await;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If the CLI fails to attach to any of the discovered extension service workers (e.g., due to target closure or permission issues), the sessions vector will be empty. In this case, calling read_events_for will block the CLI for the entire duration_ms (default 3 seconds) without any possibility of collecting logs.

We should check if sessions is empty and return early with an appropriate message to improve responsiveness and UX.

    }

    if sessions.is_empty() {
        return Ok(CommandResult::output(
            "Failed to attach to any extension service workers.".to_string(),
        ));
    }

    let events_result = client.read_events_for(duration_ms).await;

Comment thread src/commands/console.rs
Comment on lines +102 to +103
let text = args.join(" ");
let timestamp = params["timestamp"].as_f64().unwrap_or(0.0);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

In CDP, Runtime.consoleAPICalled events can sometimes have an empty args array but contain a text field directly on the event parameters (e.g., for certain console call types or when no arguments are passed).

To ensure we don't miss these console messages, we should fall back to params["text"] if the formatted text from args is empty.

                let mut text = args.join(" ");
                if text.is_empty() {
                    if let Some(t) = params["text"].as_str() {
                        text = t.to_string();
                    }
                }
                let timestamp = params["timestamp"].as_f64().unwrap_or(0.0);

Comment thread src/commands/network.rs Outdated
Comment on lines +80 to +91
if !requests.contains_key(&request_id) {
order.push(request_id.clone());
}
requests.insert(
request_id,
json!({
"url": url,
"method": http_method,
"resourceType": resource_type,
"status": null,
}),
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

When an HTTP redirect occurs, Chrome's Network domain fires another Network.requestWillBeSent event with the same requestId but containing a redirectResponse object.

Currently, the implementation overwrites the existing request entry in requests with the new redirected URL and resets the status to null. This completely discards the redirect history (such as the intermediate URLs and their 301/302 status codes), which is critical for debugging network redirect loops or incorrect redirect configurations.

Consider preserving redirects by checking if params contains redirectResponse and, if so, stashing the previous request under a modified ID (e.g., appending a redirect suffix) before inserting the new request.

                if requests.contains_key(&request_id) {
                    if params.get("redirectResponse").is_some() {
                        let mut redirect_id = format!("{}-redirect", request_id);
                        let mut counter = 1;
                        while requests.contains_key(&redirect_id) {
                            redirect_id = format!("{}-redirect-{}", request_id, counter);
                            counter += 1;
                        }
                        if let Some(prev) = requests.get(&request_id).cloned() {
                            requests.insert(redirect_id.clone(), prev);
                            if let Some(pos) = order.iter().position(|id| id == &request_id) {
                                order.insert(pos, redirect_id);
                            }
                        }
                    }
                } else {
                    order.push(request_id.clone());
                }
                requests.insert(
                    request_id,
                    json!({
                        "url": url,
                        "method": http_method,
                        "resourceType": resource_type,
                        "status": null,
                    }),
                );

@aeroxy

aeroxy commented Jun 15, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

…pty sessions

Network: stash intermediate redirect hops (with their real 3xx status) as
separate entries instead of overwriting them when Chrome re-fires
requestWillBeSent with the same requestId. The redirect chain is now
visible in output rather than silently collapsed to the final URL.

sw_logs: return early when all service worker attaches fail instead of
blocking for the full collection window with no sessions to collect from.
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.

1 participant