browser-devtools-mcp 0.2.27 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -127,8 +127,6 @@ Non-blocking debugging tools that capture snapshots without pausing execution. I
127
127
  - **Tracepoint**: Captures call stack, local variables, and async stack traces at a code location
128
128
  - **Logpoint**: Evaluates and logs expressions without full debug context (lightweight)
129
129
  - **Exceptionpoint**: Captures snapshots when exceptions occur (uncaught or all)
130
- - **Dompoint**: Monitors DOM mutations (subtree-modified, attribute-modified, node-removed)
131
- - **Netpoint**: Monitors network requests/responses matching a URL pattern
132
130
 
133
131
  **Core Operations (per probe type):**
134
132
  - `put-*`: Create a probe at a location
@@ -552,6 +550,7 @@ node-devtools-cli --help
552
550
  | `--quiet` | Suppress log messages, only show output | `false` |
553
551
  | `--verbose` | Enable verbose/debug output for troubleshooting | `false` |
554
552
  | `--timeout <ms>` | Timeout for operations in milliseconds | `30000` |
553
+ | `--no-telemetry` | Disable anonymous usage telemetry for this invocation | `false` |
555
554
 
556
555
  ### Browser Options
557
556
 
@@ -581,7 +580,7 @@ node-devtools-cli
581
580
  ├── run # Script execution commands
582
581
  │ └── js-in-node # Run JavaScript in the connected Node process
583
582
  └── debug # Debug commands
584
- ├── connect # Connect to Node.js process (pid, processName, port, containerId, etc.)
583
+ ├── connect # Connect to Node.js process (pid, processName, inspectorPort, containerId, etc.)
585
584
  ├── disconnect # Disconnect from current process
586
585
  ├── status # Show connection status
587
586
  ├── put-tracepoint # Set a tracepoint
@@ -618,8 +617,7 @@ browser-devtools-cli
618
617
  ├── update # Check for and install updates
619
618
  ├── navigation # Navigation commands
620
619
  │ ├── go-to # Navigate to a URL
621
- │ ├── go-back # Navigate backward
622
- │ ├── go-forward # Navigate forward
620
+ │ ├── go-back-or-forward # Navigate back or forward in history (direction: back | forward)
623
621
  │ └── reload # Reload the page
624
622
  ├── content # Content extraction commands
625
623
  │ ├── take-screenshot # Take a screenshot
@@ -662,36 +660,14 @@ browser-devtools-cli
662
660
  │ └── wait-for-network-idle # Wait for network idle
663
661
  ├── debug # Non-blocking debugging commands
664
662
  │ ├── put-tracepoint # Set a tracepoint (captures call stack)
665
- │ ├── remove-tracepoint # Remove a tracepoint
666
- │ ├── list-tracepoints # List all tracepoints
667
- │ ├── clear-tracepoints # Clear all tracepoints
663
+ │ ├── remove-probe # Remove a tracepoint, logpoint, or watch by ID (type + id)
664
+ │ ├── list-probes # List tracepoints, logpoints, and/or watches (optional types; omit to list all)
668
665
  │ ├── put-logpoint # Set a logpoint (evaluates expression)
669
- │ ├── remove-logpoint # Remove a logpoint
670
- │ ├── list-logpoints # List all logpoints
671
- │ ├── clear-logpoints # Clear all logpoints
672
666
  │ ├── put-exceptionpoint # Enable exception catching
673
- │ ├── put-dompoint # Set DOM mutation breakpoint
674
- │ ├── remove-dompoint # Remove a DOM breakpoint
675
- │ ├── list-dompoints # List all DOM breakpoints
676
- │ ├── clear-dompoints # Clear all DOM breakpoints
677
- │ ├── put-netpoint # Set network request breakpoint
678
- │ ├── remove-netpoint # Remove a network breakpoint
679
- │ ├── list-netpoints # List all network breakpoints
680
- │ ├── clear-netpoints # Clear all network breakpoints
681
667
  │ ├── add-watch # Add a watch expression
682
- │ ├── remove-watch # Remove a watch expression
683
- │ ├── list-watches # List all watch expressions
684
- │ ├── clear-watches # Clear all watch expressions
685
- │ ├── get-tracepoint-snapshots # Get tracepoint snapshots
686
- │ ├── clear-tracepoint-snapshots # Clear tracepoint snapshots
687
- │ ├── get-logpoint-snapshots # Get logpoint snapshots
688
- │ ├── clear-logpoint-snapshots # Clear logpoint snapshots
689
- │ ├── get-exceptionpoint-snapshots # Get exception snapshots
690
- │ ├── clear-exceptionpoint-snapshots # Clear exception snapshots
691
- │ ├── get-dompoint-snapshots # Get DOM mutation snapshots
692
- │ ├── clear-dompoint-snapshots # Clear DOM snapshots
693
- │ ├── get-netpoint-snapshots # Get network snapshots
694
- │ ├── clear-netpoint-snapshots # Clear network snapshots
668
+ │ ├── clear-probes # Clear tracepoints, logpoints, and/or watches (optional types; omit to clear all)
669
+ │ ├── get-probe-snapshots # Get tracepoint/logpoint/exceptionpoint snapshots (optional types; response: tracepointSnapshots, logpointSnapshots, exceptionpointSnapshots)
670
+ │ ├── clear-probe-snapshots # Clear tracepoint/logpoint/exceptionpoint snapshots (optional types; omit to clear all)
695
671
  │ └── status # Get debugging status
696
672
  └── figma # Figma integration commands
697
673
  └── compare-page-with-design
@@ -827,7 +803,7 @@ browser-devtools-cli tools list
827
803
  #
828
804
  # navigation:
829
805
  # go-to Navigate the browser to the given URL...
830
- # go-back Navigate back in browser history
806
+ # go-back-or-forward Navigate back or forward in history (direction: back | forward)
831
807
  # ...
832
808
 
833
809
  # Filter tools by domain
@@ -1120,6 +1096,7 @@ The server can be configured using environment variables. Configuration is divid
1120
1096
  | Variable | Description | Default |
1121
1097
  |----------|-------------|---------|
1122
1098
  | `PORT` | Port for HTTP transport | `3000` |
1099
+ | `TELEMETRY_ENABLE` | Set to `false` to disable anonymous usage telemetry | (unset) |
1123
1100
  | `SESSION_IDLE_SECONDS` | Idle session timeout (seconds) | `300` |
1124
1101
  | `SESSION_IDLE_CHECK_SECONDS` | Interval for checking idle sessions (seconds) | `30` |
1125
1102
  | `SESSION_CLOSE_ON_SOCKET_CLOSE` | Close session when socket closes | `false` |
@@ -1131,7 +1108,7 @@ The server can be configured using environment variables. Configuration is divid
1131
1108
  | Variable | Description | Default |
1132
1109
  |----------|-------------|---------|
1133
1110
  | `NODE_CONSOLE_MESSAGES_BUFFER_SIZE` | Maximum console messages to buffer from Node.js process | `1000` |
1134
- | `NODE_INSPECTOR_HOST` | Inspector host for `debug_connect` when MCP runs in Docker (e.g. `host.docker.internal`). Use with host-mapped `port` so the MCP connects to the right address. | `127.0.0.1` |
1111
+ | `NODE_INSPECTOR_HOST` | Inspector host for `debug_connect` when MCP runs in Docker (e.g. `host.docker.internal`). Use with host-mapped `inspectorPort` so the MCP connects to the right address. | `127.0.0.1` |
1135
1112
  | `PLATFORM` | Platform to use: `browser` or `node` | `browser` |
1136
1113
 
1137
1114
  ### Browser Platform Configuration
@@ -1156,6 +1133,86 @@ The server can be configured using environment variables. Configuration is divid
1156
1133
  | `FIGMA_ACCESS_TOKEN` | Figma API access token for design comparison | (none) |
1157
1134
  | `FIGMA_API_BASE_URL` | Figma API base URL | `https://api.figma.com/v1` |
1158
1135
 
1136
+ ## Telemetry
1137
+
1138
+ Browser DevTools MCP collects **anonymous usage data** to understand which tools are used, detect errors, and improve the product over time. Telemetry is **opt-out** — it is enabled by default and can be disabled at any time with zero friction.
1139
+
1140
+ ### What is collected
1141
+
1142
+ Only non-personal, non-sensitive data is sent. No page content, URLs, error messages, or any application-specific data is ever included.
1143
+
1144
+ | Property | Description |
1145
+ |----------|-------------|
1146
+ | `tool_name` | Name of the tool that was called |
1147
+ | `source` | How the tool was invoked (see table below) |
1148
+ | `duration_ms` | Tool execution time in milliseconds |
1149
+ | `success` | Whether the call succeeded (`true` / `false`) |
1150
+ | `error_type` | Error class name (e.g. `TypeError`) — only on failure |
1151
+ | `error_code` | Error code (e.g. `ECONNREFUSED`) — only on failure |
1152
+ | `browser_devtools_version` | Package version (e.g. `0.2.27`) |
1153
+ | `node_version` | Node.js runtime version (e.g. `v20.10.0`) |
1154
+ | `os_platform` | Operating system (e.g. `darwin`, `linux`, `win32`) |
1155
+ | `os_arch` | CPU architecture (e.g. `x64`, `arm64`) |
1156
+ | `timezone` | Local timezone (e.g. `America/New_York`) |
1157
+ | `timestamp` | UTC timestamp of the event |
1158
+ | `session_id` | MCP session ID from the transport (MCP only) |
1159
+ | `client_name` | Raw MCP client name from the initialize handshake (MCP only) |
1160
+
1161
+ **What is never collected:**
1162
+
1163
+ - Any personally identifiable information (PII)
1164
+ - `error.message` content — only the error class name and code are sent
1165
+ - URLs, page content, screenshots, or any application data
1166
+ - IP addresses — PostHog is configured to discard them
1167
+
1168
+ A persistent anonymous UUID is stored locally at `~/.browser-devtools-mcp/config.json`. It is never linked to any user identity.
1169
+
1170
+ ### Source values
1171
+
1172
+ The `source` field identifies the calling context:
1173
+
1174
+ | Value | Meaning |
1175
+ |-------|---------|
1176
+ | `cli` | Called via `browser-devtools-cli` or `node-devtools-cli` |
1177
+ | `cli_cursor` | CLI invoked from within Cursor (env var with `CURSOR_` prefix detected) |
1178
+ | `cli_claude` | CLI invoked from within Claude (env var with `CLAUDE_` prefix detected) |
1179
+ | `cli_codex` | CLI invoked from within Codex (env var with `CODEX_` prefix detected) |
1180
+ | `mcp-cursor` | MCP client is Cursor |
1181
+ | `mcp-claude` | MCP client is Claude Desktop or Claude Code |
1182
+ | `mcp-codex` | MCP client is OpenAI Codex CLI |
1183
+ | `mcp-unknown` | MCP client could not be identified |
1184
+
1185
+ ### How to disable telemetry
1186
+
1187
+ **MCP server** — set `TELEMETRY_ENABLE=false` in your MCP client config:
1188
+
1189
+ ```json
1190
+ {
1191
+ "mcpServers": {
1192
+ "browser-devtools": {
1193
+ "command": "npx",
1194
+ "args": ["-y", "browser-devtools-mcp"],
1195
+ "env": { "TELEMETRY_ENABLE": "false" }
1196
+ }
1197
+ }
1198
+ }
1199
+ ```
1200
+
1201
+ **CLI** — pass `--no-telemetry` to any command:
1202
+
1203
+ ```bash
1204
+ browser-devtools-cli --no-telemetry navigation go-to --url "https://example.com"
1205
+ ```
1206
+
1207
+ To disable telemetry permanently for all CLI invocations, add an alias to your shell profile:
1208
+
1209
+ ```bash
1210
+ # ~/.zshrc or ~/.bashrc
1211
+ alias browser-devtools-cli="browser-devtools-cli --no-telemetry"
1212
+ ```
1213
+
1214
+ Once disabled, no data is sent and no network requests are made to PostHog.
1215
+
1159
1216
  ## Browser Platform Tools
1160
1217
 
1161
1218
  ### Content Tools
@@ -1387,17 +1444,9 @@ The server can be configured using environment variables. Configuration is divid
1387
1444
  </details>
1388
1445
 
1389
1446
  <details>
1390
- <summary><code>navigation_go-back</code> - Navigates backward in browser history.</summary>
1391
-
1392
- **Parameters:** `timeout`, `waitUntil`, `includeSnapshot` (default true), `snapshotInteractiveOnly`, `snapshotCursorInteractive` — same semantics as navigation_go-to for snapshot/refs.
1393
-
1394
- **Returns:** `url`, `status`, `statusText`, `ok`; when includeSnapshot is true also `output` and `refs` (ARIA snapshot with refs).
1395
- </details>
1396
-
1397
- <details>
1398
- <summary><code>navigation_go-forward</code> - Navigates forward in browser history.</summary>
1447
+ <summary><code>navigation_go-back-or-forward</code> - Navigates back or forward in browser history.</summary>
1399
1448
 
1400
- **Parameters:** `timeout`, `waitUntil`, `includeSnapshot` (default true), `snapshotInteractiveOnly`, `snapshotCursorInteractive` — same semantics as navigation_go-to for snapshot/refs.
1449
+ **Parameters:** `direction` (required: `"back"` or `"forward"`), `timeout`, `waitUntil`, `includeSnapshot` (default true), `snapshotInteractiveOnly`, `snapshotCursorInteractive` — same semantics as navigation_go-to for snapshot/refs.
1401
1450
 
1402
1451
  **Returns:** `url`, `status`, `statusText`, `ok`; when includeSnapshot is true also `output` and `refs` (ARIA snapshot with refs).
1403
1452
  </details>
@@ -1675,13 +1724,13 @@ The server can be configured using environment variables. Configuration is divid
1675
1724
  - `output` (string): Includes the page URL, title, and a YAML-formatted accessibility tree
1676
1725
 
1677
1726
  **Usage:**
1678
- - Use in combination with `accessibility_take-ax-tree-snapshot` for comprehensive UI analysis
1727
+ - Use in combination with `a11y_take-ax-tree-snapshot` for comprehensive UI analysis
1679
1728
  - Provides semantic structure and accessibility roles
1680
1729
  - Helps identify accessibility issues and page hierarchy problems
1681
1730
  </details>
1682
1731
 
1683
1732
  <details>
1684
- <summary><code>accessibility_take-ax-tree-snapshot</code> - Captures a UI-focused snapshot by combining Chromium's Accessibility (AX) tree with runtime visual diagnostics.</summary>
1733
+ <summary><code>a11y_take-ax-tree-snapshot</code> - Captures a UI-focused snapshot by combining Chromium's Accessibility (AX) tree with runtime visual diagnostics.</summary>
1685
1734
 
1686
1735
  **Parameters:**
1687
1736
  - `roles` (array, optional): Optional role allowlist (button, link, textbox, checkbox, radio, combobox, switch, tab, menuitem, dialog, heading, listbox, listitem, option). If omitted, a built-in set of interactive roles is used
@@ -2007,6 +2056,52 @@ npm run build
2007
2056
  - `npm run inspector:http` - Run MCP Inspector (HTTP)
2008
2057
  - `npm run lint:check` - Check code formatting
2009
2058
  - `npm run lint:format` - Format code
2059
+ - `npm run tools:token-report` - Generate tool definition token consumption report (see below)
2060
+
2061
+ #### Tool definition token report
2062
+
2063
+ The script counts tokens for each tool’s MCP definition (name, description, inputSchema, outputSchema) using **gpt-tokenizer** (OpenAI-style BPE). Useful for understanding context size when clients load the tool list.
2064
+
2065
+ When run without `--platform`, **both browser and node** are measured (default). The script starts the MCP server separately for each platform (`PLATFORM=browser` then `PLATFORM=node`), connects as an MCP client, calls `tools/list` for each, and counts characters from the **actual payload** (so the report matches what clients receive per platform). **Requires a built server** (`npm run build`). Run from the repo root.
2066
+
2067
+ ```bash
2068
+ # Via npm (recommended): MCP-based, writes to docs/TOOL-DEFINITION-TOKENS.md
2069
+ npm run tools:token-report
2070
+ ```
2071
+
2072
+ **Options:**
2073
+
2074
+ | Option | Description |
2075
+ |--------|-------------|
2076
+ | *(default)* | Run server with output schema disabled (measure name + description + inputSchema only; Output schema column omitted); write to `docs/TOOL-DEFINITION-TOKENS.md` |
2077
+ | `--platform browser` or `--platform node` | Run only one platform (faster; report has a single section) |
2078
+ | `--output-schema` | Run server with output schema enabled (include output schema in measurement and table) |
2079
+ | `--no-output-schema` | Explicitly run without output schema (same as default) |
2080
+ | `--stdout` | Print report to stdout instead of writing to the default file |
2081
+ | `--output=path` or `-o path` | Write report to the given file path |
2082
+
2083
+ Examples:
2084
+
2085
+ ```bash
2086
+ # Default: measure without output schema, write to docs/TOOL-DEFINITION-TOKENS.md
2087
+ npm run tools:token-report
2088
+
2089
+ # Include output schema in the report
2090
+ npm run tools:token-report -- --output-schema
2091
+
2092
+ # Print to console
2093
+ npm run tools:token-report -- --stdout
2094
+
2095
+ # Only browser or only node platform
2096
+ npm run tools:token-report -- --platform browser
2097
+ npm run tools:token-report -- --platform node
2098
+
2099
+ # Write to a custom file
2100
+ npm run tools:token-report -- --output=./my-report.md
2101
+ npm run tools:token-report -- -o reports/tokens.md
2102
+ ```
2103
+
2104
+ The generated report is [docs/TOOL-DEFINITION-TOKENS.md](docs/TOOL-DEFINITION-TOKENS.md).
2010
2105
 
2011
2106
  ## Use Cases
2012
2107
 
@@ -2017,7 +2112,7 @@ The Node platform enables AI assistants to:
2017
2112
  1. **Debug Node.js APIs**: Connect to a running server, set tracepoints at API handlers, capture request/response context
2018
2113
  2. **Inspect Backend State**: Use watch expressions and tracepoints to understand variable values, call stacks
2019
2114
  3. **Catch Exceptions**: Enable exception breakpoints to capture uncaught errors with full stack traces
2020
- 4. **Docker Debugging**: Connect to Node.js processes running inside Docker containers. When the MCP runs in a container, set `NODE_INSPECTOR_HOST=host.docker.internal` and pass the host-mapped debug port to `debug_connect` (e.g. `debug_connect({ containerName: "my-service", port: 30019 })`).
2115
+ 4. **Docker Debugging**: Connect to Node.js processes running inside Docker containers. When the MCP runs in a container, set `NODE_INSPECTOR_HOST=host.docker.internal` and pass the host-mapped debug port to `debug_connect` (e.g. `debug_connect({ containerName: "my-service", inspectorPort: 30019 })`).
2021
2116
 
2022
2117
  ### Browser Platform Use Cases
2023
2118
 
@@ -2040,7 +2135,7 @@ The Browser platform enables AI assistants to:
2040
2135
  3. Take a screenshot with `content_take-screenshot` to see the current state
2041
2136
  4. Check console messages with `o11y_get-console-messages` for errors
2042
2137
  5. Monitor HTTP requests with `o11y_get-http-requests` to see API calls
2043
- 6. Capture accessibility snapshots with `a11y_take-aria-snapshot` and `accessibility_take-ax-tree-snapshot` to understand page structure
2138
+ 6. Capture accessibility snapshots with `a11y_take-aria-snapshot` and `a11y_take-ax-tree-snapshot` to understand page structure
2044
2139
  7. Compare page with Figma design using `figma_compare-page-with-design` to validate design parity
2045
2140
  8. Interact with elements using `interaction_click`, `interaction_fill`, etc.
2046
2141
  9. Extract content using `content_get-as-html` or `content_get-as-text`
package/SECURITY.md CHANGED
@@ -66,8 +66,6 @@ Browser DevTools MCP provides powerful browser automation capabilities. Users sh
66
66
  - Tracepoints capture call stack and local variables (may include sensitive data)
67
67
  - Logpoints evaluate expressions in page context
68
68
  - Exception snapshots capture error state and stack traces
69
- - Network monitors (netpoints) capture request/response bodies
70
- - DOM monitors (dompoints) capture element mutations
71
69
  - Snapshots are stored in memory and cleared on session end
72
70
 
73
71
  ### API Keys and Secrets
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
- import{platformInfo}from"../core-GIHDFCBF.js";import{AMAZON_BEDROCK_ENABLE,AMAZON_BEDROCK_IMAGE_EMBED_MODEL_ID,AMAZON_BEDROCK_TEXT_EMBED_MODEL_ID,AMAZON_BEDROCK_VISION_MODEL_ID,AWS_PROFILE,AWS_REGION,DAEMON_PORT,DAEMON_SESSION_IDLE_CHECK_SECONDS,DAEMON_SESSION_IDLE_SECONDS,FIGMA_ACCESS_TOKEN,FIGMA_API_BASE_URL,OTEL_ENABLE,OTEL_EXPORTER_HTTP_URL,OTEL_EXPORTER_TYPE,OTEL_SERVICE_NAME,OTEL_SERVICE_VERSION}from"../core-247YVH6X.js";import{Command,Option}from"commander";import{ZodFirstPartyTypeKind}from"zod";function _unwrapZodType(zodType){let current=zodType,isOptional=!1,defaultValue;for(;;){let typeName=current._def.typeName;if(typeName===ZodFirstPartyTypeKind.ZodOptional)isOptional=!0,current=current._def.innerType;else if(typeName===ZodFirstPartyTypeKind.ZodDefault)isOptional=!0,defaultValue=current._def.defaultValue(),current=current._def.innerType;else if(typeName===ZodFirstPartyTypeKind.ZodNullable)isOptional=!0,current=current._def.innerType;else break}return{innerType:current,isOptional,defaultValue}}function _getDescription(zodType){return zodType._def.description}function _toCamelCase(str){return str.replace(/[-_]([a-z])/g,(_,letter)=>letter.toUpperCase())}function _toKebabCase(str){return str.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase()}function _createOption(name,zodType){let{innerType,isOptional,defaultValue}=_unwrapZodType(zodType),description=_getDescription(zodType)||`The ${name} value`,flagName=_toKebabCase(name),typeName=innerType._def.typeName,option;switch(typeName){case ZodFirstPartyTypeKind.ZodString:option=new Option(`--${flagName} <string>`,description);break;case ZodFirstPartyTypeKind.ZodNumber:option=new Option(`--${flagName} <number>`,description),option.argParser(value=>{let n=Number(value);if(!Number.isFinite(n))throw new Error(`Invalid number: ${value}`);return n});break;case ZodFirstPartyTypeKind.ZodBoolean:option=new Option(`--${flagName}`,description);break;case ZodFirstPartyTypeKind.ZodEnum:let enumValues=innerType._def.values;option=new Option(`--${flagName} <choice>`,description).choices(enumValues);break;case ZodFirstPartyTypeKind.ZodArray:option=new Option(`--${flagName} <value...>`,description);break;case ZodFirstPartyTypeKind.ZodObject:case ZodFirstPartyTypeKind.ZodRecord:option=new Option(`--${flagName} <json>`,description),option.argParser(value=>{try{return JSON.parse(value)}catch{throw new Error(`Invalid JSON: ${value}`)}});break;case ZodFirstPartyTypeKind.ZodAny:case ZodFirstPartyTypeKind.ZodUnknown:option=new Option(`--${flagName} <value>`,description),option.argParser(value=>{try{return JSON.parse(value)}catch{return value}});break;case ZodFirstPartyTypeKind.ZodLiteral:let literalValue=innerType._def.value;typeof literalValue=="boolean"?option=new Option(`--${flagName}`,description):(option=new Option(`--${flagName} <value>`,description),option.default(literalValue));break;case ZodFirstPartyTypeKind.ZodUnion:let unionOptions=innerType._def.options;if(unionOptions.every(opt=>opt._def.typeName===ZodFirstPartyTypeKind.ZodLiteral)){let choices=unionOptions.map(opt=>String(opt._def.value));option=new Option(`--${flagName} <choice>`,description).choices(choices)}else option=new Option(`--${flagName} <value>`,description),option.argParser(value=>{try{return JSON.parse(value)}catch{return value}});break;default:option=new Option(`--${flagName} <value>`,description);break}return defaultValue!==void 0&&option.default(defaultValue),!isOptional&&defaultValue===void 0&&option.makeOptionMandatory(!0),option}function _generateOptionsFromSchema(schema){let options=[];for(let[name,zodType]of Object.entries(schema)){let option=_createOption(name,zodType);option&&options.push(option)}return options}function _parseOptionsToToolInput(options){let result={};for(let[key,value]of Object.entries(options)){let camelKey=_toCamelCase(key);value!==void 0&&(result[camelKey]=value)}return result}function _parseToolName(toolName){let underscoreIndex=toolName.indexOf("_");return underscoreIndex===-1?{domain:"default",commandName:toolName}:{domain:toolName.substring(0,underscoreIndex),commandName:toolName.substring(underscoreIndex+1)}}function registerToolCommands(program,tools2,handler){let domainCommands=new Map;for(let tool of tools2){let{domain,commandName}=_parseToolName(tool.name()),domainCommand=domainCommands.get(domain);domainCommand||(domainCommand=new Command(domain).description(`${domain.charAt(0).toUpperCase()+domain.slice(1)} commands`),domainCommands.set(domain,domainCommand),program.addCommand(domainCommand));let toolCommand=new Command(commandName).description(tool.description().trim()),options=_generateOptionsFromSchema(tool.inputSchema());for(let option of options)toolCommand.addOption(option);toolCommand.action(async opts=>{let toolInput=_parseOptionsToToolInput(opts),globalOptions=program.opts();await handler(tool.name(),toolInput,globalOptions)}),domainCommand.addCommand(toolCommand)}}import{spawn,execSync}from"node:child_process";import{createRequire}from"node:module";import*as path from"node:path";import*as readline from"node:readline";import{fileURLToPath}from"node:url";import{Command as Command2,Option as Option2}from"commander";var require2=createRequire(import.meta.url),__filename=fileURLToPath(import.meta.url),__dirname=path.dirname(__filename),cliProvider=platformInfo.cliInfo.cliProvider,tools=cliProvider.tools,DEFAULT_TIMEOUT=3e4,verboseEnabled=!1,quietEnabled=!1;function _debug(message,data){if(verboseEnabled){let timestamp=new Date().toISOString();data!==void 0?console.error(`[${timestamp}] [DEBUG] ${message}`,data):console.error(`[${timestamp}] [DEBUG] ${message}`)}}function _output(message){quietEnabled||console.log(message)}function _error(message){console.error(message)}async function _isDaemonRunning(port){_debug(`Checking if daemon is running on port ${port}`);try{let response=await fetch(`http://localhost:${port}/health`,{method:"GET",signal:AbortSignal.timeout(3e3)});if(response.ok){let isRunning=(await response.json()).status==="ok";return _debug(`Daemon health check result: ${isRunning?"running":"not running"}`),isRunning}return _debug(`Daemon health check failed: HTTP ${response.status}`),!1}catch(err){return _debug(`Daemon health check error: ${err.message}`),!1}}function _buildDaemonEnv(opts){return cliProvider.buildEnv(opts)}function _startDaemonDetached(opts){let daemonServerPath=path.join(__dirname,"..","daemon-server.js"),env=_buildDaemonEnv(opts);_debug(`Starting daemon server from: ${daemonServerPath}`),_debug(`Daemon port: ${opts.port}`);let child=spawn(process.execPath,[daemonServerPath,"--port",String(opts.port)],{detached:!0,stdio:"ignore",env});child.unref(),_debug(`Daemon process spawned with PID: ${child.pid}`),_output(`Started daemon server as detached process (PID: ${child.pid})`)}async function _ensureDaemonRunning(opts){if(await _isDaemonRunning(opts.port))_debug("Daemon is already running");else{_output(`Daemon server is not running on port ${opts.port}, starting...`),_startDaemonDetached(opts);let maxRetries=10,retryDelay=500;_debug(`Waiting for daemon to be ready (max ${maxRetries} retries, ${retryDelay}ms delay)`);for(let i=0;i<maxRetries;i++)if(await new Promise(resolve=>setTimeout(resolve,retryDelay)),_debug(`Retry ${i+1}/${maxRetries}: checking daemon status...`),await _isDaemonRunning(opts.port)){_debug("Daemon is now ready"),_output("Daemon server is ready");return}throw new Error(`Daemon server failed to start within ${maxRetries*retryDelay/1e3} seconds`)}}async function _stopDaemon(port,timeout){try{return(await fetch(`http://localhost:${port}/shutdown`,{method:"POST",signal:AbortSignal.timeout(timeout)})).ok}catch{return!1}}async function _callTool(port,toolName,toolInput,sessionId,timeout){let headers={"Content-Type":"application/json"};sessionId&&(headers["session-id"]=sessionId);let request={toolName,toolInput};_debug(`Calling tool: ${toolName}`),_debug("Tool input:",toolInput),_debug(`Session ID: ${sessionId||"(default)"}`),_debug(`Timeout: ${timeout||"none"}`);let startTime=Date.now(),response=await fetch(`http://localhost:${port}/call`,{method:"POST",headers,body:JSON.stringify(request),signal:timeout?AbortSignal.timeout(timeout):void 0}),duration=Date.now()-startTime;if(_debug(`Tool call completed in ${duration}ms, status: ${response.status}`),!response.ok){let errorBody=await response.json().catch(()=>({}));throw _debug("Tool call error:",errorBody),new Error(errorBody?.error?.message||`HTTP ${response.status}: ${response.statusText}`)}let result=await response.json();return _debug("Tool call result:",result.toolError?{error:result.toolError}:{success:!0}),result}async function _deleteSession(port,sessionId,timeout){try{return(await fetch(`http://localhost:${port}/session`,{method:"DELETE",headers:{"session-id":sessionId},signal:AbortSignal.timeout(timeout)})).ok}catch{return!1}}async function _getDaemonInfo(port,timeout){try{let response=await fetch(`http://localhost:${port}/info`,{method:"GET",signal:AbortSignal.timeout(timeout)});return response.ok?await response.json():null}catch{return null}}async function _listSessions(port,timeout){try{let response=await fetch(`http://localhost:${port}/sessions`,{method:"GET",signal:AbortSignal.timeout(timeout)});return response.ok?await response.json():null}catch{return null}}async function _getSessionInfo(port,sessionId,timeout){try{let response=await fetch(`http://localhost:${port}/session`,{method:"GET",headers:{"session-id":sessionId},signal:AbortSignal.timeout(timeout)});return response.ok?await response.json():null}catch{return null}}function _formatUptime(seconds){let days=Math.floor(seconds/86400),hours=Math.floor(seconds%86400/3600),minutes=Math.floor(seconds%3600/60),secs=seconds%60,parts=[];return days>0&&parts.push(`${days}d`),hours>0&&parts.push(`${hours}h`),minutes>0&&parts.push(`${minutes}m`),parts.push(`${secs}s`),parts.join(" ")}function _formatTimestamp(timestamp){return new Date(timestamp).toISOString()}function _getZodTypeName(schema){let typeName=schema._def.typeName;return typeName==="ZodOptional"||typeName==="ZodNullable"||typeName==="ZodDefault"?_getZodTypeName(schema._def.innerType):typeName==="ZodArray"?`${_getZodTypeName(schema._def.type)}[]`:typeName==="ZodEnum"?schema._def.values.join(" | "):typeName==="ZodLiteral"?JSON.stringify(schema._def.value):typeName==="ZodUnion"?schema._def.options.map(opt=>_getZodTypeName(opt)).join(" | "):{ZodString:"string",ZodNumber:"number",ZodBoolean:"boolean",ZodObject:"object",ZodRecord:"Record<string, any>",ZodAny:"any"}[typeName]||typeName.replace("Zod","").toLowerCase()}function _getZodDescription(schema){if(schema._def.description)return schema._def.description;if(schema._def.typeName==="ZodOptional"||schema._def.typeName==="ZodNullable"||schema._def.typeName==="ZodDefault")return _getZodDescription(schema._def.innerType)}function _isZodOptional(schema){let typeName=schema._def.typeName;return typeName==="ZodOptional"||typeName==="ZodNullable"}function _hasZodDefault(schema){return schema._def.typeName==="ZodDefault"?!0:schema._def.typeName==="ZodOptional"||schema._def.typeName==="ZodNullable"?_hasZodDefault(schema._def.innerType):!1}function _getZodDefault(schema){if(schema._def.typeName==="ZodDefault")return schema._def.defaultValue();if(schema._def.typeName==="ZodOptional"||schema._def.typeName==="ZodNullable")return _getZodDefault(schema._def.innerType)}function _formatOutput(output,indent=0){let prefix=" ".repeat(indent);if(output==null)return`${prefix}(empty)`;if(typeof output=="string")return output.split(`
2
+ import{isToolEnabled,platformInfo}from"../core-RRWTV5B5.js";import{AMAZON_BEDROCK_ENABLE,AMAZON_BEDROCK_IMAGE_EMBED_MODEL_ID,AMAZON_BEDROCK_TEXT_EMBED_MODEL_ID,AMAZON_BEDROCK_VISION_MODEL_ID,AWS_PROFILE,AWS_REGION,DAEMON_PORT,DAEMON_SESSION_IDLE_CHECK_SECONDS,DAEMON_SESSION_IDLE_SECONDS,FIGMA_ACCESS_TOKEN,FIGMA_API_BASE_URL,OTEL_ENABLE,OTEL_EXPORTER_HTTP_URL,OTEL_EXPORTER_TYPE,OTEL_SERVICE_NAME,OTEL_SERVICE_VERSION}from"../core-DOXUXYCD.js";import{Command,Option}from"commander";import{ZodFirstPartyTypeKind}from"zod";function _unwrapZodType(zodType){let current=zodType,isOptional=!1,defaultValue;for(;;){let typeName=current._def.typeName;if(typeName===ZodFirstPartyTypeKind.ZodOptional)isOptional=!0,current=current._def.innerType;else if(typeName===ZodFirstPartyTypeKind.ZodDefault)isOptional=!0,defaultValue=current._def.defaultValue(),current=current._def.innerType;else if(typeName===ZodFirstPartyTypeKind.ZodNullable)isOptional=!0,current=current._def.innerType;else break}return{innerType:current,isOptional,defaultValue}}function _getDescription(zodType){return zodType._def.description}function _toCamelCase(str){return str.replace(/[-_]([a-z])/g,(_,letter)=>letter.toUpperCase())}function _toKebabCase(str){return str.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase()}function _createOption(name,zodType){let{innerType,isOptional,defaultValue}=_unwrapZodType(zodType),description=_getDescription(zodType)||`The ${name} value`,flagName=_toKebabCase(name),typeName=innerType._def.typeName,option;switch(typeName){case ZodFirstPartyTypeKind.ZodString:option=new Option(`--${flagName} <string>`,description);break;case ZodFirstPartyTypeKind.ZodNumber:option=new Option(`--${flagName} <number>`,description),option.argParser(value=>{let n=Number(value);if(!Number.isFinite(n))throw new Error(`Invalid number: ${value}`);return n});break;case ZodFirstPartyTypeKind.ZodBoolean:option=new Option(`--${flagName}`,description);break;case ZodFirstPartyTypeKind.ZodEnum:let enumValues=innerType._def.values;option=new Option(`--${flagName} <choice>`,description).choices(enumValues);break;case ZodFirstPartyTypeKind.ZodArray:option=new Option(`--${flagName} <value...>`,description);break;case ZodFirstPartyTypeKind.ZodObject:case ZodFirstPartyTypeKind.ZodRecord:option=new Option(`--${flagName} <json>`,description),option.argParser(value=>{try{return JSON.parse(value)}catch{throw new Error(`Invalid JSON: ${value}`)}});break;case ZodFirstPartyTypeKind.ZodAny:case ZodFirstPartyTypeKind.ZodUnknown:option=new Option(`--${flagName} <value>`,description),option.argParser(value=>{try{return JSON.parse(value)}catch{return value}});break;case ZodFirstPartyTypeKind.ZodLiteral:let literalValue=innerType._def.value;typeof literalValue=="boolean"?option=new Option(`--${flagName}`,description):(option=new Option(`--${flagName} <value>`,description),option.default(literalValue));break;case ZodFirstPartyTypeKind.ZodUnion:let unionOptions=innerType._def.options;if(unionOptions.every(opt=>opt._def.typeName===ZodFirstPartyTypeKind.ZodLiteral)){let choices=unionOptions.map(opt=>String(opt._def.value));option=new Option(`--${flagName} <choice>`,description).choices(choices)}else option=new Option(`--${flagName} <value>`,description),option.argParser(value=>{try{return JSON.parse(value)}catch{return value}});break;default:option=new Option(`--${flagName} <value>`,description);break}return defaultValue!==void 0&&option.default(defaultValue),!isOptional&&defaultValue===void 0&&option.makeOptionMandatory(!0),option}function _generateOptionsFromSchema(schema,toolName){let options=[];for(let[name,zodType]of Object.entries(schema)){let option=_createOption(name,zodType);option&&options.push(option)}return options}function _parseOptionsToToolInput(options){let result={};for(let[key,value]of Object.entries(options)){let camelKey=_toCamelCase(key);value!==void 0&&(result[camelKey]=value)}return result}function _parseToolName(toolName){let underscoreIndex=toolName.indexOf("_");return underscoreIndex===-1?{domain:"default",commandName:toolName}:{domain:toolName.substring(0,underscoreIndex),commandName:toolName.substring(underscoreIndex+1)}}function registerToolCommands(program,tools2,handler){let domainCommands=new Map;for(let tool of tools2){let{domain,commandName}=_parseToolName(tool.name()),domainCommand=domainCommands.get(domain);domainCommand||(domainCommand=new Command(domain).description(`${domain.charAt(0).toUpperCase()+domain.slice(1)} commands`),domainCommands.set(domain,domainCommand),program.addCommand(domainCommand));let toolCommand=new Command(commandName).description(tool.description().trim()),options=_generateOptionsFromSchema(tool.inputSchema(),tool.name());for(let option of options)toolCommand.addOption(option);toolCommand.action(async opts=>{let toolInput=_parseOptionsToToolInput(opts),globalOptions=program.opts();await handler(tool.name(),toolInput,globalOptions)}),domainCommand.addCommand(toolCommand)}}import{spawn,execSync}from"node:child_process";import{createRequire}from"node:module";import*as path from"node:path";import*as readline from"node:readline";import{fileURLToPath}from"node:url";import{Command as Command2,Option as Option2}from"commander";var require2=createRequire(import.meta.url),__filename=fileURLToPath(import.meta.url),__dirname=path.dirname(__filename),cliProvider=platformInfo.cliInfo.cliProvider,tools=cliProvider.tools.filter(isToolEnabled),DEFAULT_TIMEOUT=3e4,verboseEnabled=!1,quietEnabled=!1;function _debug(message,data){if(verboseEnabled){let timestamp=new Date().toISOString();data!==void 0?console.error(`[${timestamp}] [DEBUG] ${message}`,data):console.error(`[${timestamp}] [DEBUG] ${message}`)}}function _output(message){quietEnabled||console.log(message)}function _error(message){console.error(message)}async function _isDaemonRunning(port){_debug(`Checking if daemon is running on port ${port}`);try{let response=await fetch(`http://localhost:${port}/health`,{method:"GET",signal:AbortSignal.timeout(3e3)});if(response.ok){let isRunning=(await response.json()).status==="ok";return _debug(`Daemon health check result: ${isRunning?"running":"not running"}`),isRunning}return _debug(`Daemon health check failed: HTTP ${response.status}`),!1}catch(err){return _debug(`Daemon health check error: ${err.message}`),!1}}function _buildDaemonEnv(opts){return cliProvider.buildEnv(opts)}function _startDaemonDetached(opts){let daemonServerPath=path.join(__dirname,"..","daemon-server.js"),env=_buildDaemonEnv(opts);_debug(`Starting daemon server from: ${daemonServerPath}`),_debug(`Daemon port: ${opts.port}`);let child=spawn(process.execPath,[daemonServerPath,"--port",String(opts.port)],{detached:!0,stdio:"ignore",env});child.unref(),_debug(`Daemon process spawned with PID: ${child.pid}`),_output(`Started daemon server as detached process (PID: ${child.pid})`)}async function _ensureDaemonRunning(opts){if(await _isDaemonRunning(opts.port))_debug("Daemon is already running");else{_output(`Daemon server is not running on port ${opts.port}, starting...`),_startDaemonDetached(opts);let maxRetries=10,retryDelay=500;_debug(`Waiting for daemon to be ready (max ${maxRetries} retries, ${retryDelay}ms delay)`);for(let i=0;i<maxRetries;i++)if(await new Promise(resolve=>setTimeout(resolve,retryDelay)),_debug(`Retry ${i+1}/${maxRetries}: checking daemon status...`),await _isDaemonRunning(opts.port)){_debug("Daemon is now ready"),_output("Daemon server is ready");return}throw new Error(`Daemon server failed to start within ${maxRetries*retryDelay/1e3} seconds`)}}async function _stopDaemon(port,timeout){try{return(await fetch(`http://localhost:${port}/shutdown`,{method:"POST",signal:AbortSignal.timeout(timeout)})).ok}catch{return!1}}async function _callTool(port,toolName,toolInput,sessionId,timeout){let headers={"Content-Type":"application/json"};sessionId&&(headers["session-id"]=sessionId);let request={toolName,toolInput};_debug(`Calling tool: ${toolName}`),_debug("Tool input:",toolInput),_debug(`Session ID: ${sessionId||"(default)"}`),_debug(`Timeout: ${timeout||"none"}`);let startTime=Date.now(),response=await fetch(`http://localhost:${port}/call`,{method:"POST",headers,body:JSON.stringify(request),signal:timeout?AbortSignal.timeout(timeout):void 0}),duration=Date.now()-startTime;if(_debug(`Tool call completed in ${duration}ms, status: ${response.status}`),!response.ok){let errorBody=await response.json().catch(()=>({}));_debug("Tool call error:",errorBody);let message=errorBody?.toolError?.message||errorBody?.error?.message||`HTTP ${response.status}: ${response.statusText}`;throw new Error(message)}let result=await response.json();return _debug("Tool call result:",result.toolError?{error:result.toolError}:{success:!0}),result}async function _deleteSession(port,sessionId,timeout){try{return(await fetch(`http://localhost:${port}/session`,{method:"DELETE",headers:{"session-id":sessionId},signal:AbortSignal.timeout(timeout)})).ok}catch{return!1}}async function _getDaemonInfo(port,timeout){try{let response=await fetch(`http://localhost:${port}/info`,{method:"GET",signal:AbortSignal.timeout(timeout)});return response.ok?await response.json():null}catch{return null}}async function _listSessions(port,timeout){try{let response=await fetch(`http://localhost:${port}/sessions`,{method:"GET",signal:AbortSignal.timeout(timeout)});return response.ok?await response.json():null}catch{return null}}async function _getSessionInfo(port,sessionId,timeout){try{let response=await fetch(`http://localhost:${port}/session`,{method:"GET",headers:{"session-id":sessionId},signal:AbortSignal.timeout(timeout)});return response.ok?await response.json():null}catch{return null}}function _formatUptime(seconds){let days=Math.floor(seconds/86400),hours=Math.floor(seconds%86400/3600),minutes=Math.floor(seconds%3600/60),secs=seconds%60,parts=[];return days>0&&parts.push(`${days}d`),hours>0&&parts.push(`${hours}h`),minutes>0&&parts.push(`${minutes}m`),parts.push(`${secs}s`),parts.join(" ")}function _formatTimestamp(timestamp){return new Date(timestamp).toISOString()}function _getZodTypeName(schema){let typeName=schema._def.typeName;return typeName==="ZodOptional"||typeName==="ZodNullable"||typeName==="ZodDefault"?_getZodTypeName(schema._def.innerType):typeName==="ZodArray"?`${_getZodTypeName(schema._def.type)}[]`:typeName==="ZodEnum"?schema._def.values.join(" | "):typeName==="ZodLiteral"?JSON.stringify(schema._def.value):typeName==="ZodUnion"?schema._def.options.map(opt=>_getZodTypeName(opt)).join(" | "):{ZodString:"string",ZodNumber:"number",ZodBoolean:"boolean",ZodObject:"object",ZodRecord:"Record<string, any>",ZodAny:"any"}[typeName]||typeName.replace("Zod","").toLowerCase()}function _getZodDescription(schema){if(schema._def.description)return schema._def.description;if(schema._def.typeName==="ZodOptional"||schema._def.typeName==="ZodNullable"||schema._def.typeName==="ZodDefault")return _getZodDescription(schema._def.innerType)}function _isZodOptional(schema){let typeName=schema._def.typeName;return typeName==="ZodOptional"||typeName==="ZodNullable"}function _hasZodDefault(schema){return schema._def.typeName==="ZodDefault"?!0:schema._def.typeName==="ZodOptional"||schema._def.typeName==="ZodNullable"?_hasZodDefault(schema._def.innerType):!1}function _getZodDefault(schema){if(schema._def.typeName==="ZodDefault")return schema._def.defaultValue();if(schema._def.typeName==="ZodOptional"||schema._def.typeName==="ZodNullable")return _getZodDefault(schema._def.innerType)}function _formatOutput(output,indent=0){let prefix=" ".repeat(indent);if(output==null)return`${prefix}(empty)`;if(typeof output=="string")return output.split(`
3
3
  `).map(line=>`${prefix}${line}`).join(`
4
4
  `);if(typeof output=="number"||typeof output=="boolean")return`${prefix}${output}`;if(Array.isArray(output))return output.length===0?`${prefix}[]`:output.map(item=>_formatOutput(item,indent)).join(`
5
5
  `);if(typeof output=="object"){let lines=[];for(let[key,value]of Object.entries(output))value!==void 0&&(typeof value=="object"&&value!==null&&!Array.isArray(value)?(lines.push(`${prefix}${key}:`),lines.push(_formatOutput(value,indent+1))):Array.isArray(value)?(lines.push(`${prefix}${key}:`),lines.push(_formatOutput(value,indent+1))):lines.push(`${prefix}${key}: ${value}`));return lines.join(`
6
- `)}return`${prefix}${String(output)}`}function _printOutput(data,json,isError=!1){let output=json?JSON.stringify(data,null,2):String(data);isError?console.error(output):console.log(output)}function _addGlobalOptions(cmd){return cmd.addOption(new Option2("--port <number>","Daemon server port").argParser(value=>{let n=Number(value);if(!Number.isInteger(n)||n<1||n>65535)throw new Error("Port must be an integer between 1 and 65535");return n}).default(DAEMON_PORT)).addOption(new Option2("--session-id <string>","Session ID for maintaining state across commands")).addOption(new Option2("--json","Output results as JSON")).addOption(new Option2("--quiet","Suppress log messages, only show output")).addOption(new Option2("--verbose","Enable verbose/debug output")).addOption(new Option2("--timeout <ms>","Timeout for operations in milliseconds").argParser(value=>{let n=Number(value);if(!Number.isFinite(n)||n<0)throw new Error("Timeout must be a positive number");return n}).default(DEFAULT_TIMEOUT)),cliProvider.addOptions(cmd)}async function main(){let program=_addGlobalOptions(new Command2(cliProvider.cliName).description(cliProvider.cliDescription).version(require2("../../package.json").version));program.hook("preAction",thisCommand=>{let opts=thisCommand.opts();opts.verbose&&(verboseEnabled=!0),opts.quiet&&(quietEnabled=!0),_debug("Verbose mode enabled"),_debug("CLI version:",require2("../../package.json").version),_debug("Node version:",process.version),_debug("Platform:",process.platform)});let daemonCmd=new Command2("daemon").description("Manage the daemon server");daemonCmd.command("start").description("Start the daemon server").action(async()=>{let opts=program.opts();if(await _isDaemonRunning(opts.port)){opts.json?_printOutput({status:"already_running",port:opts.port},!0):_output(`Daemon server is already running on port ${opts.port}`);return}_startDaemonDetached(opts);let maxRetries=10,retryDelay=500;for(let i=0;i<maxRetries;i++)if(await new Promise(resolve=>setTimeout(resolve,retryDelay)),await _isDaemonRunning(opts.port)){opts.json?_printOutput({status:"started",port:opts.port},!0):_output(`Daemon server started on port ${opts.port}`);return}opts.json?_printOutput({status:"failed",error:"Daemon server failed to start"},!0,!0):_error("Failed to start daemon server"),process.exit(1)}),daemonCmd.command("stop").description("Stop the daemon server").action(async()=>{let opts=program.opts();if(!await _isDaemonRunning(opts.port)){opts.json?_printOutput({status:"not_running",port:opts.port},!0):_output(`Daemon server is not running on port ${opts.port}`);return}await _stopDaemon(opts.port,opts.timeout??DEFAULT_TIMEOUT)?opts.json?_printOutput({status:"stopped",port:opts.port},!0):_output(`Daemon server stopped on port ${opts.port}`):(opts.json?_printOutput({status:"failed",error:"Failed to stop daemon server"},!0,!0):_error("Failed to stop daemon server"),process.exit(1))}),daemonCmd.command("restart").description("Restart the daemon server (stop + start)").action(async()=>{let opts=program.opts(),wasRunning=await _isDaemonRunning(opts.port);wasRunning&&(_debug("Stopping daemon server..."),await _stopDaemon(opts.port,opts.timeout??DEFAULT_TIMEOUT)||(opts.json?_printOutput({status:"failed",error:"Failed to stop daemon server"},!0,!0):_error("Failed to stop daemon server"),process.exit(1)),_debug("Waiting for port to be released..."),await new Promise(resolve=>setTimeout(resolve,1e3))),_debug("Starting daemon server..."),_startDaemonDetached(opts);let maxRetries=10,retryDelay=500;for(let i=0;i<maxRetries;i++)if(await new Promise(resolve=>setTimeout(resolve,retryDelay)),await _isDaemonRunning(opts.port)){opts.json?_printOutput({status:"restarted",port:opts.port},!0):_output(`Daemon server ${wasRunning?"restarted":"started"} on port ${opts.port}`);return}opts.json?_printOutput({status:"failed",error:"Daemon server failed to start"},!0,!0):_error("Failed to start daemon server"),process.exit(1)}),daemonCmd.command("status").description("Check daemon server status").action(async()=>{let opts=program.opts(),isRunning=await _isDaemonRunning(opts.port);opts.json?_printOutput({status:isRunning?"running":"stopped",port:opts.port},!0):_output(isRunning?`Daemon server is running on port ${opts.port}`:`Daemon server is not running on port ${opts.port}`)}),daemonCmd.command("info").description("Get detailed daemon server information").action(async()=>{let opts=program.opts();await _isDaemonRunning(opts.port)||(opts.json?_printOutput({status:"not_running",port:opts.port},!0,!0):_error(`Daemon server is not running on port ${opts.port}`),process.exit(1));let info=await _getDaemonInfo(opts.port,opts.timeout??DEFAULT_TIMEOUT);info?opts.json?_printOutput(info,!0):(_output("Daemon Server Information:"),_output(` Version: ${info.version}`),_output(` Port: ${info.port}`),_output(` Uptime: ${_formatUptime(info.uptime)}`),_output(` Sessions: ${info.sessionCount}`)):(opts.json?_printOutput({status:"error",error:"Failed to get daemon info"},!0,!0):_error("Failed to get daemon info"),process.exit(1))}),program.addCommand(daemonCmd);let sessionCmd=new Command2("session").description(cliProvider.sessionDescription);sessionCmd.command("list").description("List all active sessions").action(async()=>{let opts=program.opts();try{await _ensureDaemonRunning(opts);let result=await _listSessions(opts.port,opts.timeout??DEFAULT_TIMEOUT);if(result)if(opts.json)_printOutput(result,!0);else if(result.sessions.length===0)_output("No active sessions");else{_output(`Active Sessions (${result.sessions.length}):`);for(let session of result.sessions)_output(` ${session.id}`),_output(` Created: ${_formatTimestamp(session.createdAt)}`),_output(` Last Active: ${_formatTimestamp(session.lastActiveAt)}`),_output(` Idle: ${_formatUptime(session.idleSeconds)}`)}else opts.json?_printOutput({status:"error",error:"Failed to list sessions"},!0,!0):_error("Failed to list sessions"),process.exit(1)}catch(err){opts.json?_printOutput({status:"error",error:err.message},!0,!0):_error(`Error: ${err.message}`),process.exit(1)}}),sessionCmd.command("info <session-id>").description("Get information about a specific session").action(async sessionId=>{let opts=program.opts();try{await _ensureDaemonRunning(opts);let info=await _getSessionInfo(opts.port,sessionId,opts.timeout??DEFAULT_TIMEOUT);info?opts.json?_printOutput(info,!0):(_output(`Session: ${info.id}`),_output(` Created: ${_formatTimestamp(info.createdAt)}`),_output(` Last Active: ${_formatTimestamp(info.lastActiveAt)}`),_output(` Idle: ${_formatUptime(info.idleSeconds)}`)):(opts.json?_printOutput({status:"not_found",sessionId},!0,!0):_error(`Session '${sessionId}' not found`),process.exit(1))}catch(err){opts.json?_printOutput({status:"error",error:err.message},!0,!0):_error(`Error: ${err.message}`),process.exit(1)}}),sessionCmd.command("delete <session-id>").description("Delete a specific session").action(async sessionId=>{let opts=program.opts();try{await _ensureDaemonRunning(opts),await _deleteSession(opts.port,sessionId,opts.timeout??DEFAULT_TIMEOUT)?opts.json?_printOutput({status:"deleted",sessionId},!0):_output(`Session '${sessionId}' deleted`):(opts.json?_printOutput({status:"not_found",sessionId},!0,!0):_error(`Session '${sessionId}' not found or already deleted`),process.exit(1))}catch(err){opts.json?_printOutput({status:"error",error:err.message},!0,!0):_error(`Error: ${err.message}`),process.exit(1)}}),program.addCommand(sessionCmd);let toolsCmd=new Command2("tools").description("List and inspect available tools");toolsCmd.command("list").description("List all available tools").option("--domain <domain>","Filter by domain (e.g., navigation, content, interaction)").action(cmdOpts=>{let opts=program.opts(),toolsByDomain=new Map;for(let tool of tools){let domain=tool.name().split("_")[0];cmdOpts.domain&&domain!==cmdOpts.domain||(toolsByDomain.has(domain)||toolsByDomain.set(domain,[]),toolsByDomain.get(domain).push(tool))}if(opts.json){let result=[];for(let[domain,domainTools]of toolsByDomain)result.push({domain,tools:domainTools.map(t=>({name:t.name(),description:t.description().trim().split(`
6
+ `)}return`${prefix}${String(output)}`}function _printOutput(data,json,isError=!1){let output=json?JSON.stringify(data,null,2):String(data);isError?console.error(output):console.log(output)}function _addGlobalOptions(cmd){return cmd.addOption(new Option2("--port <number>","Daemon server port").argParser(value=>{let n=Number(value);if(!Number.isInteger(n)||n<1||n>65535)throw new Error("Port must be an integer between 1 and 65535");return n}).default(DAEMON_PORT)).addOption(new Option2("--session-id <string>","Session ID for maintaining state across commands")).addOption(new Option2("--json","Output results as JSON")).addOption(new Option2("--quiet","Suppress log messages, only show output")).addOption(new Option2("--verbose","Enable verbose/debug output")).addOption(new Option2("--timeout <ms>","Timeout for operations in milliseconds").argParser(value=>{let n=Number(value);if(!Number.isFinite(n)||n<0)throw new Error("Timeout must be a positive number");return n}).default(DEFAULT_TIMEOUT)),cliProvider.addOptions(cmd)}async function main(){let program=_addGlobalOptions(new Command2(cliProvider.cliName).description(cliProvider.cliDescription).version(require2("../../package.json").version));program.hook("preAction",(thisCommand,actionCommand)=>{let opts=thisCommand.opts();opts.verbose&&(verboseEnabled=!0),opts.quiet&&(quietEnabled=!0)});let daemonCmd=new Command2("daemon").description("Manage the daemon server");daemonCmd.command("start").description("Start the daemon server").action(async()=>{let opts=program.opts();if(await _isDaemonRunning(opts.port)){opts.json?_printOutput({status:"already_running",port:opts.port},!0):_output(`Daemon server is already running on port ${opts.port}`);return}_startDaemonDetached(opts);let maxRetries=10,retryDelay=500;for(let i=0;i<maxRetries;i++)if(await new Promise(resolve=>setTimeout(resolve,retryDelay)),await _isDaemonRunning(opts.port)){opts.json?_printOutput({status:"started",port:opts.port},!0):_output(`Daemon server started on port ${opts.port}`);return}opts.json?_printOutput({status:"failed",error:"Daemon server failed to start"},!0,!0):_error("Failed to start daemon server"),process.exit(1)}),daemonCmd.command("stop").description("Stop the daemon server").action(async()=>{let opts=program.opts();if(!await _isDaemonRunning(opts.port)){opts.json?_printOutput({status:"not_running",port:opts.port},!0):_output(`Daemon server is not running on port ${opts.port}`);return}await _stopDaemon(opts.port,opts.timeout??DEFAULT_TIMEOUT)?opts.json?_printOutput({status:"stopped",port:opts.port},!0):_output(`Daemon server stopped on port ${opts.port}`):(opts.json?_printOutput({status:"failed",error:"Failed to stop daemon server"},!0,!0):_error("Failed to stop daemon server"),process.exit(1))}),daemonCmd.command("restart").description("Restart the daemon server (stop + start)").action(async()=>{let opts=program.opts(),wasRunning=await _isDaemonRunning(opts.port);wasRunning&&(_debug("Stopping daemon server..."),await _stopDaemon(opts.port,opts.timeout??DEFAULT_TIMEOUT)||(opts.json?_printOutput({status:"failed",error:"Failed to stop daemon server"},!0,!0):_error("Failed to stop daemon server"),process.exit(1)),_debug("Waiting for port to be released..."),await new Promise(resolve=>setTimeout(resolve,1e3))),_debug("Starting daemon server..."),_startDaemonDetached(opts);let maxRetries=10,retryDelay=500;for(let i=0;i<maxRetries;i++)if(await new Promise(resolve=>setTimeout(resolve,retryDelay)),await _isDaemonRunning(opts.port)){opts.json?_printOutput({status:"restarted",port:opts.port},!0):_output(`Daemon server ${wasRunning?"restarted":"started"} on port ${opts.port}`);return}opts.json?_printOutput({status:"failed",error:"Daemon server failed to start"},!0,!0):_error("Failed to start daemon server"),process.exit(1)}),daemonCmd.command("status").description("Check daemon server status").action(async()=>{let opts=program.opts(),isRunning=await _isDaemonRunning(opts.port);opts.json?_printOutput({status:isRunning?"running":"stopped",port:opts.port},!0):_output(isRunning?`Daemon server is running on port ${opts.port}`:`Daemon server is not running on port ${opts.port}`)}),daemonCmd.command("info").description("Get detailed daemon server information").action(async()=>{let opts=program.opts();await _isDaemonRunning(opts.port)||(opts.json?_printOutput({status:"not_running",port:opts.port},!0,!0):_error(`Daemon server is not running on port ${opts.port}`),process.exit(1));let info=await _getDaemonInfo(opts.port,opts.timeout??DEFAULT_TIMEOUT);info?opts.json?_printOutput(info,!0):(_output("Daemon Server Information:"),_output(` Version: ${info.version}`),_output(` Port: ${info.port}`),_output(` Uptime: ${_formatUptime(info.uptime)}`),_output(` Sessions: ${info.sessionCount}`)):(opts.json?_printOutput({status:"error",error:"Failed to get daemon info"},!0,!0):_error("Failed to get daemon info"),process.exit(1))}),program.addCommand(daemonCmd);let sessionCmd=new Command2("session").description(cliProvider.sessionDescription);sessionCmd.command("list").description("List all active sessions").action(async()=>{let opts=program.opts();try{await _ensureDaemonRunning(opts);let result=await _listSessions(opts.port,opts.timeout??DEFAULT_TIMEOUT);if(result)if(opts.json)_printOutput(result,!0);else if(result.sessions.length===0)_output("No active sessions");else{_output(`Active Sessions (${result.sessions.length}):`);for(let session of result.sessions)_output(` ${session.id}`),_output(` Created: ${_formatTimestamp(session.createdAt)}`),_output(` Last Active: ${_formatTimestamp(session.lastActiveAt)}`),_output(` Idle: ${_formatUptime(session.idleSeconds)}`)}else opts.json?_printOutput({status:"error",error:"Failed to list sessions"},!0,!0):_error("Failed to list sessions"),process.exit(1)}catch(err){opts.json?_printOutput({status:"error",error:err.message},!0,!0):_error(`Error: ${err.message}`),process.exit(1)}}),sessionCmd.command("info <session-id>").description("Get information about a specific session").action(async sessionId=>{let opts=program.opts();try{await _ensureDaemonRunning(opts);let info=await _getSessionInfo(opts.port,sessionId,opts.timeout??DEFAULT_TIMEOUT);info?opts.json?_printOutput(info,!0):(_output(`Session: ${info.id}`),_output(` Created: ${_formatTimestamp(info.createdAt)}`),_output(` Last Active: ${_formatTimestamp(info.lastActiveAt)}`),_output(` Idle: ${_formatUptime(info.idleSeconds)}`)):(opts.json?_printOutput({status:"not_found",sessionId},!0,!0):_error(`Session '${sessionId}' not found`),process.exit(1))}catch(err){opts.json?_printOutput({status:"error",error:err.message},!0,!0):_error(`Error: ${err.message}`),process.exit(1)}}),sessionCmd.command("delete <session-id>").description("Delete a specific session").action(async sessionId=>{let opts=program.opts();try{await _ensureDaemonRunning(opts),await _deleteSession(opts.port,sessionId,opts.timeout??DEFAULT_TIMEOUT)?opts.json?_printOutput({status:"deleted",sessionId},!0):_output(`Session '${sessionId}' deleted`):(opts.json?_printOutput({status:"not_found",sessionId},!0,!0):_error(`Session '${sessionId}' not found or already deleted`),process.exit(1))}catch(err){opts.json?_printOutput({status:"error",error:err.message},!0,!0):_error(`Error: ${err.message}`),process.exit(1)}}),program.addCommand(sessionCmd);let toolsCmd=new Command2("tools").description("List and inspect available tools");toolsCmd.command("list").description("List all available tools").option("--domain <domain>","Filter by domain (e.g., navigation, content, interaction)").action(cmdOpts=>{let opts=program.opts(),toolsByDomain=new Map;for(let tool of tools){let domain=tool.name().split("_")[0];cmdOpts.domain&&domain!==cmdOpts.domain||(toolsByDomain.has(domain)||toolsByDomain.set(domain,[]),toolsByDomain.get(domain).push(tool))}if(opts.json){let result=[];for(let[domain,domainTools]of toolsByDomain)result.push({domain,tools:domainTools.map(t=>({name:t.name(),description:t.description().trim().split(`
7
7
  `)[0]}))});_printOutput(result,!0)}else{if(toolsByDomain.size===0){_output("No tools found");return}_output(`Available Tools (${tools.length} total):
8
8
  `);for(let[domain,domainTools]of toolsByDomain){_output(` ${domain}:`);for(let tool of domainTools){let name=tool.name().split("_")[1]||tool.name(),desc=tool.description().trim().split(`
9
- `)[0];_output(` ${name.padEnd(30)} ${desc}`)}_output("")}}}),toolsCmd.command("info <tool-name>").description("Get detailed information about a specific tool").action(toolName=>{let opts=program.opts(),tool=tools.find(t=>t.name()===toolName);tool||(tool=tools.find(t=>t.name().split("_")[1]===toolName)),tool||(opts.json?_printOutput({status:"not_found",toolName},!0,!0):_error(`Tool '${toolName}' not found`),process.exit(1));let inputSchema=tool.inputSchema(),params=[];for(let[key,schema]of Object.entries(inputSchema))params.push({name:key,type:_getZodTypeName(schema),required:!_isZodOptional(schema),description:_getZodDescription(schema),default:_hasZodDefault(schema)?_getZodDefault(schema):void 0});if(opts.json)_printOutput({name:tool.name(),description:tool.description().trim(),parameters:params},!0);else{let nameParts=tool.name().split("_");if(_output(`Tool: ${tool.name()}`),_output(`Domain: ${nameParts[0]}`),_output(`
9
+ `)[0];_output(` ${name.padEnd(30)} ${desc}`)}_output("")}}}),toolsCmd.command("info <tool-name> [other-parts...]").description('Get detailed information about a specific tool (e.g. "tools info navigation go-to" or "tools info navigation_go-to")').action((toolName,otherParts=[])=>{let opts=program.opts(),resolvedName=otherParts.length>0?[toolName,...otherParts].join("_"):toolName,tool=tools.find(t=>t.name()===resolvedName);tool||(tool=tools.find(t=>t.name().split("_")[1]===resolvedName)),tool||(opts.json?_printOutput({status:"not_found",toolName:resolvedName},!0,!0):_error(`Tool '${resolvedName}' not found`),process.exit(1));let inputSchema=tool.inputSchema(),params=[];for(let[key,schema]of Object.entries(inputSchema))params.push({name:key,type:_getZodTypeName(schema),required:!_isZodOptional(schema),description:_getZodDescription(schema),default:_hasZodDefault(schema)?_getZodDefault(schema):void 0});if(opts.json)_printOutput({name:tool.name(),description:tool.description().trim(),parameters:params},!0);else{let nameParts=tool.name().split("_");if(_output(`Tool: ${tool.name()}`),_output(`Domain: ${nameParts[0]}`),_output(`
10
10
  Description:`),_output(tool.description().trim().split(`
11
11
  `).map(line=>` ${line}`).join(`
12
12
  `)),_output(`
@@ -146,7 +146,7 @@ A new version is available!`),cmdOpts.check||_output(`Run: npm install -g ${pack
146
146
  `);try{await _ensureDaemonRunning(opts)}catch(err){_error(`Error: ${err.message}`),process.exit(1)}let replProgram=_createReplProgram(opts),rl=readline.createInterface({input:process.stdin,output:process.stdout,prompt:cliProvider.replPrompt});rl.prompt(),rl.on("line",async line=>{let input=line.trim();if(!input){rl.prompt();return}if((input==="exit"||input==="quit")&&(_output("Goodbye!"),rl.close(),process.exit(0)),input==="help"){_output(`
147
147
  REPL Commands:`),_output(" help Show this help"),_output(" exit, quit Exit interactive mode"),_output(`
148
148
  Available Commands:`),_output(" status Show daemon status summary"),_output(" config Show current configuration"),_output(" update Check for CLI updates"),_output(" daemon <cmd> Daemon management (start, stop, restart, status, info)"),_output(" session <cmd> Session management (list, info, delete)"),_output(" tools <cmd> Tool discovery (list, search, info)"),_output(" <domain> <tool> Execute a tool (e.g., navigation go-to --url ...)"),_output(`
149
- Examples:`),_output(" # Daemon & Session"),_output(" daemon status"),_output(" daemon info"),_output(" session list"),_output(" session delete my-session"),_output(""),_output(" # Tool Discovery"),_output(" tools list"),_output(" tools search screenshot"),_output(" tools info navigation_go-to"),_output(""),_output(" # Navigation"),_output(' navigation go-to --url "https://example.com"'),_output(" navigation go-back"),_output(" navigation reload"),_output(""),_output(" # Content"),_output(' content take-screenshot --name "test"'),_output(" content get-as-text"),_output(' content get-as-html --selector "#main"'),_output(""),_output(" # Interaction"),_output(' interaction click --ref "Submit"'),_output(' interaction fill --ref "Email" --value "test@example.com"'),_output(' interaction hover --ref "Menu"'),_output(""),_output(" # Accessibility"),_output(" a11y get-snapshot"),_output(" a11y get-ax-tree-snapshot"),_output(`
149
+ Examples:`),_output(" # Daemon & Session"),_output(" daemon status"),_output(" daemon info"),_output(" session list"),_output(" session delete my-session"),_output(""),_output(" # Tool Discovery"),_output(" tools list"),_output(" tools search screenshot"),_output(" tools info navigation_go-to"),_output(""),_output(" # Navigation"),_output(' navigation go-to --url "https://example.com"'),_output(" navigation go-back-or-forward --direction back"),_output(" navigation reload"),_output(""),_output(" # Content"),_output(' content take-screenshot --name "test"'),_output(" content get-as-text"),_output(' content get-as-html --selector "#main"'),_output(""),_output(" # Interaction"),_output(' interaction click --ref "Submit"'),_output(' interaction fill --ref "Email" --value "test@example.com"'),_output(' interaction hover --ref "Menu"'),_output(""),_output(" # Accessibility"),_output(" a11y get-snapshot"),_output(" a11y get-ax-tree-snapshot"),_output(`
150
150
  Tip: Use global options when starting interactive mode:`);for(let example of cliProvider.cliExamples)_output(` ${example}`);_output(""),rl.prompt();return}try{let args=_parseReplInput(input);await replProgram.parseAsync(["node","repl",...args])}catch(err){err.code==="commander.help"||(err.code==="commander.unknownCommand"?(_output(`Unknown command: ${input}`),_output('Type "help" for available commands')):err.code==="commander.missingArgument"?_error(`Missing argument: ${err.message}`):err.code==="commander.invalidArgument"?_error(`Invalid argument: ${err.message}`):err.code&&err.code.startsWith("commander.")||_error(`Error: ${err.message}`))}rl.prompt()}),rl.on("close",()=>{process.exit(0)})});program.addCommand(interactiveCmd);let updateCmd=new Command2("update").description("Check for updates and optionally install them").option("--check","Only check for updates without installing").action(async cmdOpts=>{let opts=program.opts(),currentVersion=require2("../../package.json").version,packageName=cliProvider.packageName;_output(`Checking for updates...
151
151
  `);try{let response=await fetch(`https://registry.npmjs.org/${packageName}/latest`,{method:"GET",signal:AbortSignal.timeout(1e4)});if(!response.ok)throw new Error(`Failed to check npm registry: HTTP ${response.status}`);let latestVersion=(await response.json()).version;if(opts.json){_printOutput({currentVersion,latestVersion,updateAvailable:latestVersion!==currentVersion},!0);return}if(_output(` Current version: ${currentVersion}`),_output(` Latest version: ${latestVersion}`),_output(""),latestVersion===currentVersion){_output("\x1B[32m\u2713 You are using the latest version!\x1B[0m");return}let currentParts=currentVersion.split(".").map(Number),latestParts=latestVersion.split(".").map(Number),isNewer=!1;for(let i=0;i<3;i++)if(latestParts[i]>currentParts[i]){isNewer=!0;break}else if(latestParts[i]<currentParts[i])break;if(!isNewer){_output("\x1B[32m\u2713 You are using a newer version than published!\x1B[0m");return}if(_output(`\x1B[33m\u26A0 Update available: ${currentVersion} \u2192 ${latestVersion}\x1B[0m
152
152
  `),cmdOpts.check){_output("To update, run:"),_output(` npm install -g ${packageName}@latest`),_output("or"),_output(` npx ${packageName}@latest`);return}let rl=readline.createInterface({input:process.stdin,output:process.stdout}),answer=await new Promise(resolve=>{rl.question("Do you want to update now? (y/N) ",ans=>{rl.close(),resolve(ans.toLowerCase())})});if(answer!=="y"&&answer!=="yes"){_output(`