Skip to main content

browser_automation_cli/
cli.rs

1//! Clap derive surface for browser-automation-cli (PRD Layer L).
2//!
3//! Help text on flags is the primary documentation for this module.
4#![allow(missing_docs)]
5
6use clap::{Parser, Subcommand, ValueEnum};
7
8/// One-shot browser automation CLI for AI agents.
9#[derive(Debug, Parser)]
10#[command(
11    name = "browser-automation-cli",
12    version,
13    about = "One-shot browser automation CLI (Chrome CDP). BORN, EXECUTE, FINALIZE, DIE.",
14    long_about = None,
15    propagate_version = true
16)]
17pub struct Cli {
18    #[command(flatten)]
19    pub globals: GlobalOpts,
20
21    #[command(subcommand)]
22    pub command: Commands,
23}
24
25#[derive(Debug, Clone, Parser)]
26pub struct GlobalOpts {
27    #[arg(long, global = true)]
28    pub json: bool,
29
30    /// Suppress non-error human logs on stderr
31    #[arg(short = 'q', long = "quiet", global = true)]
32    pub quiet: bool,
33
34    /// Increase stderr verbosity (`--verbose` once = info; or `config set log_level debug`)
35    #[arg(long = "verbose", global = true)]
36    pub verbose: bool,
37
38    /// Maximum tracing detail on stderr (debug/trace)
39    #[arg(long = "debug", global = true)]
40    pub debug: bool,
41
42    #[arg(long, global = true, default_value_t = 0)]
43    pub timeout: u64,
44
45    /// Per-step timeout in seconds for `run` scripts (0 = inherit global timeout)
46    #[arg(long, global = true, default_value_t = 0)]
47    pub step_timeout: u64,
48
49    /// Launch Chrome with a visible window (debug; default headless=new)
50    #[arg(long, global = true)]
51    pub headed: bool,
52
53    #[arg(long, global = true)]
54    pub artifacts_dir: Option<std::path::PathBuf>,
55
56    #[arg(long, global = true)]
57    pub lang: Option<String>,
58
59    #[arg(long, global = true)]
60    pub capture_console: bool,
61
62    #[arg(long, global = true)]
63    pub capture_network: bool,
64
65    #[arg(long, global = true)]
66    pub ignore_robots: bool,
67
68    #[arg(long, global = true)]
69    pub i_accept_robots_risk: bool,
70
71    /// Enable deep heap analysis tools (PRD category-memory)
72    #[arg(long, global = true)]
73    pub category_memory: bool,
74
75    /// Enable extension management tools
76    #[arg(long, global = true)]
77    pub category_extensions: bool,
78
79    /// Enable third-party developer tool surface
80    #[arg(long, global = true)]
81    pub category_third_party: bool,
82
83    /// Enable WebMCP-compatible tool surface
84    #[arg(long, global = true)]
85    pub category_webmcp: bool,
86
87    /// Enable experimental screencast (may require ffmpeg for file export)
88    #[arg(long, global = true)]
89    pub experimental_screencast: bool,
90
91    /// Enable coordinate click-at (vision) tools
92    #[arg(long, global = true)]
93    pub experimental_vision: bool,
94}
95
96#[derive(Debug, Subcommand)]
97pub enum Commands {
98    /// Diagnose Chrome install and one-shot readiness
99    Doctor {
100        #[arg(long)]
101        offline: bool,
102        #[arg(long)]
103        quick: bool,
104        #[arg(long)]
105        fix: bool,
106        #[arg(long)]
107        json: bool,
108    },
109    /// List available commands
110    Commands {
111        #[arg(long)]
112        json: bool,
113    },
114    /// JSON Schema fragment for a command (agent discovery)
115    Schema {
116        #[arg(long = "cmd", value_name = "CMD")]
117        cmd: String,
118    },
119    /// Print CLI version
120    Version,
121    /// Navigate to a URL (one-shot)
122    Goto {
123        url: String,
124        /// JS to evaluate before navigation (tool-ref initScript)
125        #[arg(long)]
126        init_script: Option<String>,
127        /// Accept beforeunload dialogs automatically
128        #[arg(long)]
129        handle_before_unload: bool,
130        /// Navigation timeout override in milliseconds
131        #[arg(long)]
132        navigation_timeout_ms: Option<u64>,
133    },
134    /// Accessibility snapshot with @eN refs
135    View {
136        #[arg(long)]
137        verbose: bool,
138        #[arg(long)]
139        path: Option<std::path::PathBuf>,
140    },
141    /// Click an element (selector or @eN)
142    Press {
143        target: String,
144        #[arg(long)]
145        dblclick: bool,
146        /// Attach slim a11y snapshot in the same process after the action
147        #[arg(long)]
148        include_snapshot: bool,
149    },
150    /// Click at page CSS coordinates (requires --experimental-vision)
151    ClickAt {
152        #[arg(long)]
153        x: f64,
154        #[arg(long)]
155        y: f64,
156        #[arg(long)]
157        dblclick: bool,
158        /// Attach slim a11y snapshot after the click
159        #[arg(long)]
160        include_snapshot: bool,
161    },
162    /// Fill an input value (select/checkbox/radio/text smart fill)
163    Write {
164        target: String,
165        value: String,
166        /// Attach slim a11y snapshot after fill
167        #[arg(long)]
168        include_snapshot: bool,
169    },
170    /// Press a keyboard key
171    Keys {
172        key: String,
173        /// Attach slim a11y snapshot after the key press
174        #[arg(long)]
175        include_snapshot: bool,
176    },
177    /// Type text (tool-ref type_text). Use --target or --focus-only.
178    Type {
179        /// Text to type (required positional)
180        text: String,
181        /// CSS selector or @eN (optional; use --focus-only for focused element)
182        #[arg(long)]
183        target: Option<String>,
184        #[arg(long)]
185        clear: bool,
186        /// Optional key to press after typing (e.g. Enter)
187        #[arg(long)]
188        submit: Option<String>,
189        /// Type into currently focused element without resolving a target
190        #[arg(long)]
191        focus_only: bool,
192    },
193    /// Wait for ms and/or text and/or selector and/or load state
194    Wait {
195        #[arg(long, default_value_t = 0)]
196        ms: u64,
197        /// Text to wait for (repeatable; resolves when any value appears — tool-ref OR)
198        #[arg(long = "text", action = clap::ArgAction::Append)]
199        text: Vec<String>,
200        #[arg(long)]
201        selector: Option<String>,
202        /// Page lifecycle: load | domcontentloaded | networkidle | none
203        #[arg(long)]
204        state: Option<String>,
205        /// Max wait time in milliseconds for text/selector/state (0 = default)
206        #[arg(long)]
207        wait_timeout_ms: Option<u64>,
208        /// Attach slim a11y snapshot after the wait succeeds
209        #[arg(long)]
210        include_snapshot: bool,
211    },
212    /// Hover an element
213    Hover {
214        target: String,
215        /// Attach slim a11y snapshot after hover
216        #[arg(long)]
217        include_snapshot: bool,
218    },
219    /// Drag from one target to another
220    Drag {
221        #[arg(long)]
222        from: String,
223        #[arg(long)]
224        to: String,
225        /// Attach slim a11y snapshot after drag
226        #[arg(long)]
227        include_snapshot: bool,
228    },
229    /// Fill multiple form fields from JSON `[{target|uid,value},...]`
230    FillForm {
231        #[arg(long)]
232        json: String,
233        /// Attach slim a11y snapshot after fill-form
234        #[arg(long)]
235        include_snapshot: bool,
236    },
237    /// Upload a file to a file input
238    Upload {
239        target: String,
240        path: std::path::PathBuf,
241        /// Attach slim a11y snapshot after upload
242        #[arg(long)]
243        include_snapshot: bool,
244    },
245    /// History back
246    Back,
247    /// History forward
248    Forward,
249    /// Reload current page
250    Reload {
251        #[arg(long)]
252        ignore_cache: bool,
253        /// JS to run before navigation/reload (tool-ref initScript)
254        #[arg(long)]
255        init_script: Option<String>,
256        /// Accept beforeunload dialogs automatically
257        #[arg(long)]
258        handle_before_unload: bool,
259    },
260    /// Evaluate JavaScript (expression or function declaration)
261    Eval {
262        /// JS expression or function declaration `() => ...`
263        expression: String,
264        /// Snapshot uids (@eN) passed as function args (JSON array of strings)
265        #[arg(long)]
266        args: Option<String>,
267        /// accept | dismiss | prompt response text (default accept)
268        #[arg(long)]
269        dialog_action: Option<String>,
270        /// Write evaluate result JSON to this path
271        #[arg(long)]
272        file_path: Option<std::path::PathBuf>,
273    },
274    /// Capture a screenshot
275    Grab {
276        #[arg(long)]
277        path: Option<std::path::PathBuf>,
278        #[arg(long, value_enum, default_value_t = GrabFormat::Png)]
279        format: GrabFormat,
280        #[arg(long)]
281        full_page: bool,
282        #[arg(long)]
283        quality: Option<i32>,
284        /// CSS selector or @eN element to capture
285        #[arg(long)]
286        element: Option<String>,
287    },
288    /// Print current page to PDF via CDP Page.printToPDF (one-shot)
289    PrintPdf {
290        /// Output path for the PDF artifact
291        #[arg(long)]
292        path: Option<std::path::PathBuf>,
293        /// Optional URL to navigate before printing (one-shot)
294        #[arg(long)]
295        url: Option<String>,
296    },
297    /// One-shot change check against a baseline file (hash/text)
298    Monitor {
299        #[command(subcommand)]
300        action: MonitorAction,
301    },
302    /// Run multi-step NDJSON script in one process
303    Run {
304        #[arg(long)]
305        script: std::path::PathBuf,
306    },
307    /// Single-step inline command (same surface as `run` steps: goto, wait, view, press, …)
308    Exec {
309        // Do NOT set allow_hyphen_values: global flags like --json after `exec`
310        // must stay on GlobalOpts, not be swallowed into trailing args.
311        #[arg(trailing_var_arg = true)]
312        args: Vec<String>,
313    },
314    /// Extract text/attribute from a target, or LLM extract with --llm
315    Extract {
316        /// Selector, @eN ref, about:blank target, or http(s) URL for LLM/text path
317        target: String,
318        #[arg(long)]
319        attr: Option<String>,
320        /// Opt-in LLM HTTP extract (requires XDG openrouter_api_key)
321        #[arg(long)]
322        llm: bool,
323        /// Question for LLM extract
324        #[arg(long)]
325        question: Option<String>,
326        /// Path to JSON schema file for structured LLM extract
327        #[arg(long)]
328        schema_json: Option<std::path::PathBuf>,
329    },
330    /// Extract visible text from a target (PRD §7 `text`)
331    Text { target: String },
332    /// Scroll page or element by delta pixels (PRD §7 `scroll`)
333    Scroll {
334        /// CSS selector or @eN (optional; omit for window scroll)
335        #[arg(long)]
336        target: Option<String>,
337        #[arg(long, default_value_t = 0.0)]
338        delta_x: f64,
339        #[arg(long, default_value_t = 0.0)]
340        delta_y: f64,
341    },
342    /// Cookie jar helpers for the active page (Network domain)
343    Cookie {
344        #[command(subcommand)]
345        action: CookieAction,
346    },
347    /// Read one attribute from a target
348    Attr { target: String, name: String },
349    /// Assertions (url / text / console)
350    Assert {
351        #[command(subcommand)]
352        kind: AssertKind,
353    },
354    /// Captured console messages (--capture-console)
355    Console {
356        #[command(subcommand)]
357        action: ConsoleAction,
358    },
359    /// Captured network requests (--capture-network)
360    Net {
361        #[command(subcommand)]
362        action: NetAction,
363    },
364    /// Page info or multi-tab management
365    Page {
366        #[command(subcommand)]
367        action: Option<PageAction>,
368    },
369    /// Accept or dismiss dialogs
370    Dialog {
371        #[command(subcommand)]
372        action: DialogAction,
373    },
374    /// Navigate and return body text / formats (local HTTP or CDP scrape)
375    Scrape {
376        url: String,
377        /// text | markdown | html | links | metadata | raw-html | screenshot
378        #[arg(long, default_value = "text")]
379        format: String,
380        /// http (reqwest+scraper) or browser (CDP)
381        #[arg(long, default_value = "browser")]
382        engine: String,
383        /// Prefer main/article content heuristics
384        #[arg(long)]
385        only_main_content: bool,
386        /// Optional one-shot webhook POST of the result envelope data (127.0.0.1/operator URL)
387        #[arg(long)]
388        webhook_url: Option<String>,
389    },
390    /// Scrape many URLs from a file (HTTP engine, one-shot)
391    BatchScrape {
392        #[arg(long)]
393        urls_file: std::path::PathBuf,
394        #[arg(long, default_value = "text")]
395        format: String,
396        #[arg(long, default_value_t = 2)]
397        concurrency: usize,
398    },
399    /// Crawl from a seed URL (HTTP BFS, one-shot)
400    Crawl {
401        url: String,
402        #[arg(long, default_value_t = 20)]
403        limit: usize,
404        #[arg(long, default_value_t = 2)]
405        max_depth: usize,
406        #[arg(long, default_value = "text")]
407        format: String,
408        /// Stay on seed host
409        #[arg(long, default_value_t = true)]
410        same_host: bool,
411    },
412    /// Map site URLs from a seed (HTTP)
413    Map {
414        url: String,
415        #[arg(long, default_value_t = 50)]
416        limit: usize,
417        #[arg(long, default_value_t = 2)]
418        max_depth: usize,
419    },
420    /// Local search (HTTP SERP links or URL map)
421    Search {
422        query: String,
423        #[arg(long, default_value_t = 10)]
424        limit: usize,
425    },
426    /// Parse a local file (html/md/txt/pdf/docx/xlsx text extract)
427    Parse {
428        path: std::path::PathBuf,
429        /// Mask email/phone/card-like patterns in text output
430        #[arg(long)]
431        redact_pii: bool,
432    },
433    /// QR encode/decode one-shot (no Chrome)
434    Qr {
435        #[command(subcommand)]
436        action: QrAction,
437    },
438    /// Discover filesystem paths (fd-like UX; binary remains browser-automation-cli)
439    FindPaths {
440        /// Regex pattern on name/path (optional)
441        pattern: Option<String>,
442        /// Root paths to search
443        #[arg(num_args = 0..)]
444        paths: Vec<String>,
445        /// Filter by extension (e.g. rs, html)
446        #[arg(long)]
447        extension: Option<String>,
448        /// Include hidden files
449        #[arg(long)]
450        hidden: bool,
451        /// Do not respect .gitignore
452        #[arg(long)]
453        no_ignore: bool,
454        /// Max directory depth
455        #[arg(long)]
456        max_depth: Option<usize>,
457        /// Entry type: f|d
458        #[arg(long = "type")]
459        entry_type: Option<String>,
460        /// Max results
461        #[arg(long, default_value_t = 10000)]
462        limit: usize,
463    },
464    /// MITM capture / CA / HAR (one-shot local)
465    Mitm {
466        #[command(subcommand)]
467        action: MitmAction,
468    },
469    /// Workflow journal DAG (petgraph + SQLite)
470    Workflow {
471        #[command(subcommand)]
472        action: WorkflowAction,
473    },
474    /// XDG config and path management (no .env at runtime)
475    Config {
476        #[command(subcommand)]
477        action: ConfigAction,
478    },
479    /// Emulate device / network / UA / geo / CPU
480    Emulate {
481        #[arg(long)]
482        user_agent: Option<String>,
483        #[arg(long)]
484        locale: Option<String>,
485        #[arg(long)]
486        timezone: Option<String>,
487        #[arg(long)]
488        offline: bool,
489        #[arg(long)]
490        latitude: Option<f64>,
491        #[arg(long)]
492        longitude: Option<f64>,
493        #[arg(long)]
494        media: Option<String>,
495        /// Network preset: Offline, No throttling, Slow 3G, Fast 3G, Slow 4G, Fast 4G
496        #[arg(long)]
497        network_conditions: Option<String>,
498        /// CPU slowdown factor 1..=20 (1 disables)
499        #[arg(long)]
500        cpu_throttling_rate: Option<f64>,
501        /// prefers-color-scheme: dark | light | auto
502        #[arg(long)]
503        color_scheme: Option<String>,
504        /// Extra HTTP headers as JSON object string
505        #[arg(long)]
506        extra_headers: Option<String>,
507        /// Viewport `WxHxDPR` with optional `,mobile`, `,touch`, `,landscape` flags
508        #[arg(long)]
509        viewport: Option<String>,
510    },
511    /// Resize page viewport
512    Resize {
513        #[arg(long)]
514        width: i32,
515        #[arg(long)]
516        height: i32,
517        #[arg(long, default_value_t = 1.0)]
518        scale: f64,
519        #[arg(long)]
520        mobile: bool,
521    },
522    /// Performance trace / metrics
523    Perf {
524        #[command(subcommand)]
525        action: PerfAction,
526    },
527    /// Run Lighthouse audit (external binary)
528    Lighthouse {
529        url: String,
530        #[arg(long)]
531        out_dir: Option<std::path::PathBuf>,
532        #[arg(long, default_value = "desktop")]
533        device: String,
534        /// navigation (default) or snapshot (maps to navigation in one-shot CLI)
535        #[arg(long, default_value = "navigation")]
536        mode: String,
537        #[arg(long)]
538        lighthouse_path: Option<std::path::PathBuf>,
539    },
540    /// Screencast start/stop (experimental)
541    Screencast {
542        #[command(subcommand)]
543        action: ScreencastAction,
544    },
545    /// Heap snapshot tools (requires --category-memory for deep analysis)
546    Heap {
547        #[command(subcommand)]
548        action: HeapAction,
549    },
550    /// Chrome extension tools (requires --category-extensions)
551    Extension {
552        #[command(subcommand)]
553        action: ExtensionAction,
554    },
555    /// Third-party developer tools surface (requires --category-third-party)
556    Devtools3p {
557        #[command(subcommand)]
558        action: Devtools3pAction,
559    },
560    /// Web surface tools (requires --category-webmcp)
561    Webmcp {
562        #[command(subcommand)]
563        action: WebmcpAction,
564    },
565    /// Generate shell completions (path leve, no Chrome)
566    Completions {
567        #[arg(value_enum)]
568        shell: CompletionShell,
569    },
570}
571
572#[derive(Debug, Clone, Subcommand)]
573pub enum PerfAction {
574    Start {
575        #[arg(long)]
576        path: Option<std::path::PathBuf>,
577        #[arg(long)]
578        reload: bool,
579        /// Auto-stop after page load/reload (tool-ref autoStop)
580        #[arg(long)]
581        auto_stop: bool,
582    },
583    Stop {
584        #[arg(long)]
585        path: Option<std::path::PathBuf>,
586    },
587    Insight {
588        /// Insight name (e.g. DocumentLatency, LCPBreakdown)
589        #[arg(long)]
590        name: Option<String>,
591        /// Insight set id from perf stop "available_insight_sets"
592        #[arg(long)]
593        insight_set_id: Option<String>,
594        /// Alias for --name (tool-ref insightName)
595        #[arg(long)]
596        insight_name: Option<String>,
597    },
598}
599
600#[derive(Debug, Clone, Subcommand)]
601pub enum ScreencastAction {
602    Start {
603        #[arg(long)]
604        path: Option<std::path::PathBuf>,
605    },
606    Stop {
607        /// Output path (.webm/.mp4 encodes via ffmpeg; otherwise PNG frames dir)
608        #[arg(long)]
609        path: Option<std::path::PathBuf>,
610    },
611}
612
613#[derive(Debug, Clone, Subcommand)]
614pub enum HeapAction {
615    Take {
616        #[arg(long)]
617        path: std::path::PathBuf,
618    },
619    Close {
620        #[arg(long)]
621        path: std::path::PathBuf,
622    },
623    Compare {
624        #[arg(long)]
625        base: std::path::PathBuf,
626        #[arg(long)]
627        current: std::path::PathBuf,
628        /// Optional class index filter (tool-ref classIndex)
629        #[arg(long)]
630        class_index: Option<u64>,
631    },
632    Summary {
633        #[arg(long)]
634        path: std::path::PathBuf,
635    },
636    Details {
637        #[arg(long)]
638        path: std::path::PathBuf,
639        #[arg(long)]
640        filter_name: Option<String>,
641        #[arg(long)]
642        page_idx: Option<usize>,
643        #[arg(long)]
644        page_size: Option<usize>,
645    },
646    ClassNodes {
647        #[arg(long)]
648        path: std::path::PathBuf,
649        #[arg(long)]
650        id: u64,
651        #[arg(long)]
652        filter_name: Option<String>,
653        #[arg(long)]
654        page_idx: Option<usize>,
655        #[arg(long)]
656        page_size: Option<usize>,
657    },
658    Dominators {
659        #[arg(long)]
660        path: std::path::PathBuf,
661        #[arg(long)]
662        node: u64,
663    },
664    DupStrings {
665        #[arg(long)]
666        path: std::path::PathBuf,
667        #[arg(long)]
668        page_idx: Option<usize>,
669        #[arg(long)]
670        page_size: Option<usize>,
671    },
672    Edges {
673        #[arg(long)]
674        path: std::path::PathBuf,
675        #[arg(long)]
676        node: u64,
677        #[arg(long)]
678        page_idx: Option<usize>,
679        #[arg(long)]
680        page_size: Option<usize>,
681    },
682    Retainers {
683        #[arg(long)]
684        path: std::path::PathBuf,
685        #[arg(long)]
686        node: u64,
687        #[arg(long)]
688        page_idx: Option<usize>,
689        #[arg(long)]
690        page_size: Option<usize>,
691    },
692    Paths {
693        #[arg(long)]
694        path: std::path::PathBuf,
695        #[arg(long)]
696        node: u64,
697        #[arg(long, default_value_t = 8)]
698        max_depth: u64,
699        #[arg(long)]
700        max_nodes: Option<u64>,
701        #[arg(long)]
702        max_siblings: Option<u64>,
703    },
704    /// Detailed info for one heap object (size, distance, retained size, detachedness)
705    ObjectDetails {
706        #[arg(long)]
707        path: std::path::PathBuf,
708        #[arg(long)]
709        node: u64,
710    },
711}
712
713#[derive(Debug, Clone, Subcommand)]
714pub enum ExtensionAction {
715    List,
716    Install {
717        path: std::path::PathBuf,
718    },
719    Reload {
720        id: String,
721        /// Unpacked extension dir so one-shot can --load-extension before reload
722        #[arg(long)]
723        path: Option<std::path::PathBuf>,
724    },
725    Trigger {
726        id: String,
727        /// Unpacked extension dir so one-shot can --load-extension before trigger
728        #[arg(long)]
729        path: Option<std::path::PathBuf>,
730    },
731    Uninstall {
732        id: String,
733    },
734}
735
736#[derive(Debug, Clone, Subcommand)]
737pub enum Devtools3pAction {
738    List {
739        /// Optional page URL to open before discovery
740        #[arg(long)]
741        url: Option<String>,
742    },
743    Exec {
744        name: String,
745        #[arg(long)]
746        params: Option<String>,
747        #[arg(long)]
748        url: Option<String>,
749    },
750}
751
752#[derive(Debug, Clone, Subcommand)]
753pub enum WebmcpAction {
754    List {
755        #[arg(long)]
756        url: Option<String>,
757    },
758    Exec {
759        name: String,
760        #[arg(long)]
761        input: Option<String>,
762        #[arg(long)]
763        url: Option<String>,
764    },
765}
766
767#[derive(Debug, Clone, Copy, ValueEnum)]
768pub enum CompletionShell {
769    Bash,
770    Zsh,
771    Fish,
772    Elvish,
773    Powershell,
774}
775
776#[derive(Debug, Clone, Subcommand)]
777pub enum QrAction {
778    /// Encode text to PNG, SVG, or terminal matrix
779    Encode {
780        #[arg(long)]
781        text: String,
782        /// png | svg | terminal
783        #[arg(long, default_value = "png")]
784        format: String,
785        #[arg(long)]
786        path: Option<std::path::PathBuf>,
787    },
788    /// Decode QR payload from an image file
789    Decode {
790        #[arg(long)]
791        path: std::path::PathBuf,
792    },
793}
794
795#[derive(Debug, Clone, Subcommand)]
796pub enum MonitorAction {
797    /// Compare URL body hash/text to a baseline file and exit
798    Check {
799        /// URL to fetch/scrape one-shot
800        #[arg(long)]
801        url: String,
802        /// Baseline file path (created on first run if missing when --write-baseline)
803        #[arg(long)]
804        baseline: std::path::PathBuf,
805        /// Write/update baseline after check
806        #[arg(long)]
807        write_baseline: bool,
808        /// Use browser engine instead of HTTP
809        #[arg(long, default_value = "http")]
810        engine: String,
811    },
812}
813
814#[derive(Debug, Clone, Subcommand)]
815pub enum PageAction {
816    /// Current page url and title (default when bare `page`)
817    Info,
818    /// List tabs in this one-shot process
819    List,
820    /// Open a new tab
821    New {
822        #[arg(long)]
823        url: Option<String>,
824        /// Open without focusing (tool-ref background)
825        #[arg(long)]
826        background: bool,
827        /// Create isolated browser context when supported
828        #[arg(long)]
829        isolated_context: bool,
830    },
831    /// Select tab by zero-based index (alias: --page-id)
832    Select {
833        #[arg(value_name = "INDEX")]
834        index: Option<usize>,
835        /// Tool-ref pageId alias for index
836        #[arg(long = "page-id")]
837        page_id: Option<usize>,
838        /// Bring selected tab to front (tool-ref bringToFront)
839        #[arg(long, default_value_t = true)]
840        bring_to_front: bool,
841    },
842    /// Close a tab (default: active)
843    Close {
844        #[arg(long)]
845        index: Option<usize>,
846        /// Tool-ref pageId alias for index
847        #[arg(long = "page-id")]
848        page_id: Option<usize>,
849    },
850}
851
852#[derive(Debug, Clone, Subcommand)]
853pub enum CookieAction {
854    /// List cookies (optional URL filter)
855    List {
856        #[arg(long)]
857        url: Option<String>,
858    },
859    /// Set cookies from a JSON array of cookie objects
860    Set {
861        /// JSON array: [{"name":"a","value":"b","url":"https://..."}]
862        #[arg(long)]
863        json: String,
864    },
865    /// Clear all browser cookies in this one-shot process
866    Clear,
867}
868
869#[derive(Debug, Clone, Copy, ValueEnum, Default)]
870pub enum GrabFormat {
871    #[default]
872    Png,
873    Jpeg,
874    Webp,
875}
876
877#[derive(Debug, Clone, Subcommand)]
878pub enum AssertKind {
879    Url {
880        value: String,
881        #[arg(long)]
882        contains: bool,
883    },
884    Text {
885        value: String,
886        #[arg(long)]
887        target: Option<String>,
888    },
889    Console {
890        #[arg(long, default_value = "error")]
891        level: String,
892        #[arg(long, default_value_t = 0)]
893        max: u64,
894    },
895}
896
897#[derive(Debug, Clone, Subcommand)]
898pub enum ConsoleAction {
899    List {
900        /// 0-based page index for pagination
901        #[arg(long)]
902        page_idx: Option<usize>,
903        /// Max messages per page
904        #[arg(long)]
905        page_size: Option<usize>,
906        /// Filter by types (comma-separated: log,warning,error,info,debug)
907        #[arg(long)]
908        types: Option<String>,
909        /// Include messages preserved across navigations in this process
910        #[arg(long)]
911        include_preserved: bool,
912        /// Optional service worker id filter
913        #[arg(long)]
914        service_worker_id: Option<String>,
915    },
916    Get {
917        id: usize,
918    },
919    Clear,
920    Dump {
921        #[arg(long)]
922        path: std::path::PathBuf,
923    },
924}
925
926#[derive(Debug, Clone, Subcommand)]
927pub enum NetAction {
928    List {
929        /// 0-based page index for pagination
930        #[arg(long)]
931        page_idx: Option<usize>,
932        /// Max requests per page
933        #[arg(long)]
934        page_size: Option<usize>,
935        /// Filter resource types (comma-separated: Document,Script,XHR,Fetch,...)
936        #[arg(long)]
937        resource_types: Option<String>,
938        /// Include requests preserved over recent navigations in this process
939        #[arg(long)]
940        include_preserved: bool,
941    },
942    Get {
943        /// 0-based index in net list, or CDP requestId string
944        id: String,
945        #[arg(long)]
946        request_path: Option<std::path::PathBuf>,
947        #[arg(long)]
948        response_path: Option<std::path::PathBuf>,
949    },
950}
951
952#[derive(Debug, Clone, Subcommand)]
953pub enum DialogAction {
954    Accept {
955        #[arg(long)]
956        text: Option<String>,
957    },
958    Dismiss,
959}
960
961#[derive(Debug, Clone, Subcommand)]
962pub enum MitmAction {
963    /// CA paths, capture count, bind policy
964    Status,
965    /// List captured exchanges
966    List {
967        #[arg(long)]
968        host: Option<String>,
969        #[arg(long, default_value_t = 100)]
970        limit: usize,
971    },
972    /// Get one exchange by id
973    Get { id: u64 },
974    /// Export HAR 1.2 JSON
975    Har {
976        #[arg(long)]
977        out: std::path::PathBuf,
978    },
979    /// Export capture as JSON/NDJSON
980    Export {
981        #[arg(long, default_value = "json")]
982        format: String,
983        #[arg(long)]
984        out: std::path::PathBuf,
985    },
986    /// Unique hosts seen
987    Domains,
988    /// REST/GraphQL endpoint discovery
989    Apis {
990        #[arg(long)]
991        kind: Option<String>,
992    },
993    /// Ensure local CA under XDG data
994    InitCa,
995    /// Start one-shot MITM proxy on 127.0.0.1 (ephemeral port); captures until timeout
996    Start {
997        /// Seconds to keep the proxy alive (one-shot; default 30)
998        #[arg(long, default_value_t = 30)]
999        seconds: u64,
1000    },
1001}
1002
1003#[derive(Debug, Clone, Subcommand)]
1004pub enum WorkflowAction {
1005    /// Validate DAG and execute offline steps; journal under XDG state
1006    Run {
1007        #[arg(long)]
1008        manifest: std::path::PathBuf,
1009        #[arg(long)]
1010        journal: Option<std::path::PathBuf>,
1011    },
1012    /// Resume / re-run from journal + manifest
1013    Resume {
1014        #[arg(long)]
1015        manifest: std::path::PathBuf,
1016        #[arg(long)]
1017        journal: Option<std::path::PathBuf>,
1018    },
1019    /// Show journal step statuses
1020    Status {
1021        #[arg(long)]
1022        journal: Option<std::path::PathBuf>,
1023        #[arg(long)]
1024        name: Option<String>,
1025    },
1026}
1027
1028#[derive(Debug, Clone, Subcommand)]
1029pub enum ConfigAction {
1030    /// Print resolved XDG paths
1031    Path,
1032    /// Create XDG layout + default config.toml
1033    Init,
1034    /// Show config values
1035    Show,
1036    /// Set a config key (lang|timeout|artifacts_dir|ignore_robots|namespace|encryption_key|color|log_level|chrome_path|lighthouse_path|openrouter_api_key|llm_base_url|llm_model)
1037    Set { key: String, value: String },
1038    /// Get one config key
1039    Get { key: Option<String> },
1040}