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
Impact: High - Introduces powerful new capabilities for custom automation, allowing users to execute local JavaScript files and domain-specific adapters directly in the browser context.
Key Changes:
Added run-script command to execute custom JavaScript files inside the page.
Added adapter command to run specific functions from domain-aware adapter files.
Implemented an injected ctx automation helper object containing utilities like click, fill, and waitForSelector.
Added auto-navigation logic driven by JSDoc-style comments (@url, @domain) to ensure scripts run on the correct pages.
Introduced comprehensive documentation and examples for these new features.
What Changed: Added new enum variants RunScript and Adapter to the main CLI commands. Introduced a shared argument parsing logic to handle both named (-a key=val) and positional (-- val) arguments flexibly.
What Changed: Implemented run_script and run_adapter execution flows. The commands now read the target file, parse it for metadata (@url, @navigate, @domain), and automatically navigate the active tab if it's not currently on the target domain.
Error Handling Update: Removed verbose hints regarding DOM traversal in the generic evaluate() error output, replacing it with a clean bail string.
What Changed: Introduced build_ctx_object() which constructs a JavaScript snippet that is injected into the evaluation context as an Immediately Invoked Function Expression (IIFE). This exposes a robust set of helpers to user scripts. Added POLL_INTERVAL_MS (100ms) to control polling frequency.
Injected ctx API:
Method
Description
ctx.args
Object containing parsed arguments from the CLI
ctx.wait(ms)
Async sleep utility
ctx.waitForText(text, timeout)
Polls the DOM body until text appears
ctx.waitForSelector(sel, timeout)
Polls until a CSS selector exists
ctx.click(selector)
DOM element clicking helper
ctx.fill(selector, value)
Input value setter that correctly overrides native setters to trigger React/Vue/Angular state updates
Documentation & Examples
What Changed: Created skill/chrome-devtools/CUSTOM_SCRIPTING.md and added reference wikis (wiki/adapter.md, wiki/run-script.md). Included real-world SPA examples for Hacker News (search_hn.js, hn_adapter.js).
4. Impact & Risk Assessment
Breaking Changes: None. The update is purely additive. A minor adjustment was made to the evaluate command's error message format.
Risks:
Security Injection: The function_name for adapters is directly interpolated into the JS IIFE. However, this is mitigated by is_valid_js_identifier() validation.
Auto-Navigation Loops: If a site rapidly redirects after navigation, the domain check might warn about URL mismatches.
Impact: High - Introduces new memory profiling capabilities and enhances the screenshot functionality.
Key Changes:
✨ Added take-heapsnapshot command to capture V8 heap snapshots via CDP streaming without holding the entire snapshot in memory.
✨ Added inspect-heapsnapshot-node command to parse and inspect specific nodes in a local .heapsnapshot file entirely offline.
🖼️ Enhanced the screenshot command with --quality, --max-width, and --max-height options, and improved page layout metrics gathering.
🛠️ Implemented absolute path resolution for CLI arguments (--output, --file-path) to prevent path mismatch issues between the CLI process and the background daemon.
2. Visual Overview (Code & Logic Map)
graphTDsubgraph "CLIEntry&Routing (src/lib.rs)"
D("run()")C("build_request()")C_Abs("absolutize_path()")endsubgraph "MemoryCommands (src/commands/memory.rs)"
E("take_heapsnapshot()")F("inspect_heapsnapshot_node_offline()")G("parse_node_from_snapshot()")endsubgraph "ScreenshotCommands (src/commands/screenshot.rs)"
H("take_screenshot()")end%% Flow for take-heapsnapshot and screenshotC--"Resolves relative paths"-->C_AbsC_Abs--"Sends absolute paths"-->EC_Abs--"Sends absolute paths"-->H%% Flow for inspect-heapsnapshot-nodeD--"Intercepts offline command"-->FF--"Parses JSON schema"-->GstyleDfill:#f3e5f5,color:#7b1fa2styleCfill:#f3e5f5,color:#7b1fa2styleC_Absfill:#f3e5f5,color:#7b1fa2styleEfill:#c8e6c9,color:#1a5e20styleFfill:#bbdefb,color:#0d47a1styleGfill:#bbdefb,color:#0d47a1styleHfill:#fff3e0,color:#e65100
Introduced take_heapsnapshot which enables the HeapProfiler and streams chunks directly to a temporary file via non-blocking CDP events. It atomically renames the temp file upon completion to prevent corrupted files.
Introduced inspect_heapsnapshot_node_offline which parses a .heapsnapshot JSON file to find a specific node by ID. This command is intercepted in run() (Source: src/lib.rs) to execute entirely offline, bypassing the need for a Chrome daemon connection.
Refactored take_screenshot to accept a ScreenshotOptions struct instead of multiple loose parameters.
Added support for quality, max_width, and max_height.
Switched from Runtime.evaluate to Page.getLayoutMetrics to retrieve page dimensions. This avoids a JavaScript execution round-trip and works natively on non-HTML pages (e.g., PDF viewers, chrome:// pages).
Path Resolution (src/lib.rs)
What Changed:
Added absolutize_path to resolve relative paths (like --output ./file.png) using the CLI's current working directory before sending the request to the daemon. This fixes a bug where the daemon would resolve relative paths against its own startup directory instead of the user's current directory.
New API Commands & Parameters
Command
Parameter
Type
Required
Description
take-heapsnapshot
--output
String
Yes
Path to save the heap snapshot file
inspect-heapsnapshot-node
--file-path
String
Yes
Path to the local .heapsnapshot file
inspect-heapsnapshot-node
--node-id
u64
Yes
ID of the node to inspect
screenshot
--quality
u64
No
Compression quality (0-100), ignores PNG
screenshot
--max-width
f64
No
Downscale screenshot to max width
screenshot
--max-height
f64
No
Downscale screenshot to max height
4. Impact & Risk Assessment
Breaking Changes: None. The CLI interface is purely additive. Internal Rust method signatures (like take_screenshot) have changed, but external usage remains compatible.
Impact: High - Introduces a new capability to extract page content as clean Markdown and updates the 3rd-party tools API to support the modern WebMCP standard.
Key Changes:
✨ Added read-page command to extract article content and convert it to LLM-friendly Markdown.
✨ Added WebMCP support (document.modelContext) for third-party DevTools tools, with fallback to the legacy __dtmcp API.
📦 Added dependencies (dom_smoothie, htmd, html-escape) to power HTML-to-Markdown extraction and parsing.
📝 Added extensive documentation, including a new AGENTS.md architecture guide and a dedicated wiki page for read-page.
2. Visual Overview (Code & Logic Map)
3. Detailed Change Analysis
✨ read-page Command Integration
Component Name:commands::read_page / CLI Routing
What Changed: Introduced a new command that serializes the rendered DOM. It uses Mozilla Readability (dom_smoothie) to extract the main article, recursively unwraps nested <iframe> tags (Source: unwrap_iframes), and converts the cleaned HTML to Markdown using htmd. Non-article pages gracefully fall back to full-page conversion. Supports plain text, JSON, and TOON output formats.
Dependencies Added (Source: Cargo.toml):
Package
Version
Description
htmd
0.5.4
HTML to Markdown converter
dom_smoothie
0.18.0
Rust port of Mozilla Readability for article extraction
html-escape
0.2.13
HTML entity decoding (used for fallback <title> extraction)
🛠️ Third-Party Tools WebMCP Support
Component Name:commands::third_party
What Changed: Updated list_3p_tools and execute_3p_tool to prioritize the new WebMCP API via document.modelContext.
Details: The injected JavaScript now checks for document.modelContext.getTools() and document.modelContext.executeTool(). It explicitly reports which API was used (WebMCP or DTMCP (legacy)), and extracts additional metadata like origin and annotations for tools.
API Mode
Execution Context
Description
modelContext
document.modelContext
New: Standardized WebMCP API. Supports Promises and extra metadata.
dtmcp
window.__dtmcp
Legacy: Fallback API to maintain backward compatibility.
none
N/A
Handled gracefully when no APIs are available.
📚 Documentation & Guides
Component Name: Documentation
What Changed:
Added AGENTS.md: A developer guide outlining the repository structure, daemon architecture, persistent sessions, and coding conventions.
Added wiki/read-page.md: Details the behavior, output formats, and inert element stripping pipeline of the read-page command.
Updated README.md and SKILL.md to feature the new read-page command and its use cases versus the existing snapshot command.
4. Impact & Risk Assessment
Breaking Changes: None. The __dtmcp logic is preserved as a fallback, ensuring backward compatibility for pages still using it.
What Changed: Introduced persistent_session and persistent_target_id to continuously collect events even when commands are not actively running. Emulation states (viewport, geolocation, blocklist) are now swapped in and out of a HashMap<String, TabEmulation> when switching targets.
Impact: Ensures that each tab maintains its own isolated environment that persists across page navigations without leaking into other tabs.
What Changed: Added new modules to handle draining or live-collecting events. The network command intelligently merges redirect chains (using redirectResponse) so hops are properly documented. sw-logs filters targets by service_worker and chrome-extension:// prefix.
What Changed: Centralized formatting logic into a new OutputFormat enum (Text, Json, Toon). Replaced raw boolean json_output checks with format_structured() calls across all extraction commands (evaluate, snapshot, list-pages, etc.).
New CLI Capabilities & Flags
Command / Flag
Type
Description
console
Command
Drains accumulated console messages. Supports --type filter and --duration for live collection.
network
Command
Drains accumulated network requests. Supports --type filter and --duration.
sw-logs
Command
Collects logs from extension service workers. Filters by --extension-id.
kill-daemon
Command
Sends SIGTERM to gracefully kill the daemon and clean up socket/PID files.
--toon
Global Flag
Enables Token-Oriented Object Notation output (mutually exclusive with --json).
--block-url
Global Flag
Adds a glob pattern (e.g., *.png) to block subresources on the active tab.
4. Impact & Risk Assessment
Breaking Changes: None strictly, though the internal behavior of emulate is now correctly isolated per tab rather than applying globally to whichever session is active.
Risks:
Memory Buildup: Persistent event buffers (network_events, console_events) could grow indefinitely. The code mitigates this by capping them at MAX_PERSISTENT_EVENTS = 5000.
PID Handling: The kill-daemon logic uses POSIX kill(). A corrupted PID file could inadvertently send signals to the wrong process group. The code adds safeguards (rejecting PID 0, validating libc::pid_t bounds).
Impact: Medium - Enhances the third-party developer tools integration by supporting multiple tool groups per page instead of limiting it to a single group.
Key Changes:
✨ Updated list_3p_tools to parse and format an array of toolGroups while maintaining fallback support for the legacy single toolGroup.
✨ Refactored execute_3p_tool to locate and execute a specific tool by iterating through available tool groups if the global executeTool method is missing.
🐛 Improved terminal output formatting to clearly distinguish between multiple tool groups and their respective tools.
2. Detailed Change Analysis
Component: Third-Party Commands
What Changed:
Tool Listing Update: The injected JavaScript payload inside list_3p_tools() was updated to preferentially look for dtmcp.toolGroups (an array). If not found, it falls back to the legacy dtmcp.toolGroup. The Rust-side JSON parser was adapted to iterate over the new groups key and properly indent the available tools under each group header (Source: src/commands/third_party.rs).
Tool Execution Logic:execute_3p_tool() now checks if a global dtmcp.executeTool exists. If it doesn't, it loops through the registered toolGroups, finds the requested tool by name, and directly invokes its own tool.execute(params) method (Source: src/commands/third_party.rs).
Data Structure Changes
Object / Field
Old Structure
New Structure
Description
JS Listing Payload
{ name, description, tools: [] }
{ groups: [{ name, description, tools: [] }] }
Normalizes the returned payload to always provide an array of groups to Rust.
Execution Call
dtmcp.executeTool(name, params)
tool.execute(params) (fallback)
Allows individual tools inside a group to define their own execute method.
3. Impact & Risk Assessment
⚠️Breaking Changes:None. The changes are fully backward-compatible. Pages utilizing the older window.__dtmcp.toolGroup and window.__dtmcp.executeTool structures will continue to function seamlessly through fallback logic.
Major refactor to a library-first architecture, introducing a centralized command executor and comprehensive page emulation support.
Highlights:
Library-First Architecture:src/lib.rs now contains the core logic, making the binary a thin wrapper.
Centralized Command Execution:executor.rs handles command dispatching and validation for both daemon and direct modes.
Page Emulation: New emulate command and integrated support for viewport, geolocation, and device scale factor in navigate and new-page.
Extra Headers: Support for custom HTTP headers via --extra-headers.
Non-blocking Telemetry: Improved telemetry system with a background worker thread.
Robustness: Proactive JavaScript dialog handling and better navigation event tracking.
New files:
src/commands/emulation.rs
src/commands/executor.rs
1. High-Level Summary (TL;DR)
Impact: High - This is a significant architectural shift that centralizes command logic and expands the feature set with powerful page-level overrides.
Key Changes:
Architecture: Refactored main.rs into lib.rs and introduced a command executor pattern.
Emulation: Added support for viewport (WxH), geolocation (lat,lon), and mobile emulation.
Networking: Added support for custom HTTP headers.
Observability: Rewrote telemetry to use a background worker for non-blocking I/O.
Testing: Added dynamic sync checks between CLI definition and executor arguments.
2. Visual Overview (Code & Logic Map)
3. Detailed Change Analysis
Architecture & Command Execution
What Changed: The core CLI logic was moved from main.rs to lib.rs. A new executor.rs module was introduced to centralize command dispatching, validation, and target resolution. This ensures consistency between daemon-mediated execution and direct fallback mode.
What Changed: Introduced the emulate command and integrated emulation parameters into navigate and new-page. Users can now override viewport size, geolocation, device scale factor, and emulate mobile devices.
What Changed: Telemetry logging was moved to a background worker thread using an MPSC channel. This prevents file I/O from blocking the main execution path.
Source:src/telemetry.rs
4. Impact & Risk Assessment
Breaking Changes:
close-page and select-page now take a single optional argument id_or_index instead of a mandatory index. This is more flexible but technically a signature change in the internal API.
new-page now opens about:blank first if emulation or headers are requested, which might slightly change timing for some scripts.
🐛 Fixes a missing import (CommandExt) for Windows builds in the client module.
Source Code (src/client.rs)
What Changed: Added the std::os::windows::process::CommandExt import behind a #[cfg(windows)] feature flag. This resolves compilation failures on Windows environments where process extensions (e.g., hiding command windows using CREATE_NO_WINDOW) are utilized but the required trait was previously not in scope.
Impact & Risk Assessment
Breaking Changes: None. This is a non-breaking patch release.
Testing Suggestions:
Windows: Verify the application compiles successfully without trait resolution errors (cargo build on a Windows machine).
Impact: High - This is a major structural refactoring of how commands are executed, how results are bubbled up, and how the tool provides observability.
Key Changes:
Structured Results: Commands now return a typed CommandResult containing metadata like navigated_to and error_code instead of plain strings.
Centralized Execution: Introduced executor.rs to handle target resolution, session attachment, and unified command dispatching.
Telemetry Logging: Added an asynchronous background worker to record CLI command metrics (success/fail, duration) to local files.
Direct File Output: Added --output (-o) flags to commands (screenshot, snapshot, evaluate, navigate) for saving output directly to disk via tokio::fs.
Stable Error Codes: Introduced ErrorCode for machine-parseable error identification.
2. Visual Overview (Code & Logic Map)
This diagram illustrates the new execution pipeline, highlighting the centralized execution loop and the flow of the new structured CommandResult.
3. Detailed Change Analysis
🎯 Core Execution Pipeline
What Changed: Replaced disparate target-resolution and CDP session-attachment logic across commands with a unified entry point in src/commands/executor.rs.
Source: executor.rs, commands/mod.rs
The execute_command() function automatically clears stale CdpClient events to prevent memory leaks in daemon mode, attaches to targets, runs inner_execute(), and safely detaches upon completion.
What Changed: Added CdpClientTrait in src/cdp.rs to abstract CDP operations. This is a foundational step for dependency injection and mocking CDP connections in future testing.
📦 Structured Returns & Error Handling
What Changed: Instead of commands returning Result<String>, they now return Result<CommandResult>. This struct encapsulates the text output, target ID, and navigation changes.
Source: result.rs, protocol.rs
What Changed: Added a standardized ErrorCode enum and typed CliError to replace generic anyhow errors in key paths. This enables downstream consumers (like an MCP server) to programmatically handle errors (e.g., ErrorCode::ChromeConnection = 2).
Source: error.rs
💾 File Output & Command Flags
What Changed: Enhanced commands to support writing their outputs directly to disk instead of exclusively printing to stdout. tokio::fs::write is utilized for async I/O. Added a -t flag to evaluate to track URL changes triggered by script execution.
Command
New Flags
Description
screenshot
--output, -o
Saves the Base64/binary image directly to the specified path.
snapshot
--output, -o
Saves the JSON/text accessibility tree directly to the specified path.
evaluate
--output, -o, -t
Saves result to file. Tracks navigations with -t (--track-navigation).
navigate
--output, -o
Writes the success/result message to a file.
📊 Telemetry & Daemon Reliability
What Changed: Created a best-effort, non-blocking telemetry system utilizing an MPSC channel. A background worker thread opens log files in ~/.chrome-devtools-cli/logs and flushes command usage statistics (duration, success/failure).
Source: telemetry.rs
What Changed: Exposed Daemon timeouts as environment variables to allow fine-tuning for slower environments (e.g., CI/CD).
Daemon Configuration:
Environment Variable
Default Value
Description
DAEMON_WAIT_TIMEOUT_SECS
5
Maximum seconds to wait for a daemon to spawn.
DAEMON_IDLE_TIMEOUT_SECS
300
Maximum idle seconds before the daemon terminates itself.
Dependencies Added:
Package
Old Version
New Version
Purpose
chrono
None
0.4.44
Used in telemetry.rs for timestamping log entries.
4. Impact & Risk Assessment
⚠️Breaking Changes (Protocol): The DaemonResponse JSON structure has been modified. Two new fields (navigated_to and error_code) have been added. Any external script strictly parsing the IPC socket responses might need updates, although the changes are additive.
🐛 Testing Suggestions:
File I/O Verification: Test the --output flag on screenshot and snapshot with both valid and invalid (permission denied) file paths to ensure the async write handles errors gracefully.
Telemetry Logs: Run a few commands and inspect ~/.chrome-devtools-cli/logs to verify that the MPSC worker successfully writes log lines and doesn't block execution.
Navigation Tracking: Use evaluate -t "window.location.href = 'https://example.com'" and verify that the navigated_to field in the response accurately captures the URL change.
Impact: Medium - Introduces cross-platform support for the background daemon and improves connection stability.
Key Changes:
✨ Added Windows support by replacing Unix domain sockets with local TCP loops for Windows environments.
🛡️ Improved daemon startup resiliency by implementing an exponential backoff with jitter strategy in wait_for_daemon().
♻️ Refactored the daemon loop in src/daemon.rs by extracting request handling into handle_connection().
🛠️ Enhanced fallback execution in src/main.rs by routing failures cleanly through run_direct_fallback().
2. Visual Overview (Code & Logic Map)
3. Detailed Change Analysis
🖥️ Cross-Platform IPC Architecture
What Changed: The application historically relied on UnixStream for CLI-to-daemon communication. This PR introduces conditional compilation (#[cfg(windows)] and #[cfg(unix)]) to use a TcpStream bound to 127.0.0.1:0 on Windows, while preserving UnixStream for Linux/macOS. Additionally, the daemon process on Windows is now spawned with the CREATE_NO_WINDOW flag (0x08000000).
What Changed: Updated the polling logic inside wait_for_daemon(). Instead of a static 100ms delay, the client now utilizes an exponential backoff (starting at 50ms, doubling up to 500ms) paired with sub-millisecond jitter derived from SystemTime::now().
Source File:src/client.rs.
♻️ Daemon Loop Refactoring
What Changed: Extracted the massive inline loop body of run_daemon() into a new state-driven function called handle_connection(). A run_accept_loop_body! macro was created to orchestrate connection acceptance. A new ConnectionOutcome enum controls whether the loop should Continue or exit via Fatal.
Source File:src/daemon.rs.
⚙️ CLI Execution & Fallback Routing
What Changed: Added run_direct_fallback() to cleanly handle scenarios where the daemon fails to boot (e.g. timeout or permission issues). Error handling for CLI help and version output (ErrorKind::DisplayHelp / ErrorKind::DisplayVersion) was streamlined to exit immediately without noisy error rendering.
Source File:src/main.rs.
4. Impact & Risk Assessment
Breaking Changes: None. This is a purely architectural and platform-support improvement.
Testing Suggestions:
Windows Environment: Validate that the daemon successfully spawns invisibly and communicates via TCP over localhost.
Fallback Logic: Intentionally block the daemon port/socket creation and verify that the CLI correctly warns the user and completes the command using run_direct_fallback().
Concurrency: Trigger multiple CLI commands concurrently in a fresh environment to ensure the exponential backoff jitter prevents thundering herd issues on daemon spawn.
list-3p-tools — lists custom developer tools exposed by the page via window.__dtmcp.toolGroup
execute-3p-tool <name> <params> — executes a named tool via window.__dtmcp.executeTool(name, params) with a JSON params string
Improvements
Automatic dialog handling — evaluate no longer hangs or fails when a script triggers alert, confirm, or prompt. Pass --dialog-action accept, --dialog-action dismiss, or a custom string (used as the prompt input) to handle dialogs automatically.
Native <option> clicking — click now detects <option> elements and selects them by updating the parent <select> value and dispatching native input/change events, instead of attempting a synthetic mouse click that browsers ignore.
Environment variable support — --ws-endpoint, --user-data-dir, and --channel can now be set via CHROME_WS_ENDPOINT, CHROME_USER_DATA_DIR, and CHROME_CHANNEL environment variables.