browser-devtools-mcp 0.5.2 → 0.6.0

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
@@ -64,6 +64,7 @@ Choose the platform by running the appropriate MCP server or CLI:
64
64
  - **Screenshots**: Capture full page or specific elements (PNG/JPEG) with image data; optional `annotate: true` overlays numbered labels (1, 2, ...) on elements from the last ARIA snapshot refs and returns an `annotations` array (ref, number, role, name, box). If the ref map is empty, a snapshot is taken automatically. Set `annotateContent: true` to also include content elements (headings, list items, etc.) in the overlay. Set `annotateCursorInteractive: true` to also include cursor-interactive elements (clickable/focusable by CSS without ARIA role) in the overlay. The `selector` parameter accepts a ref (e.g. `e1`, `@e1`), a getByRole/getByLabel/getByText/etc. expression, or a CSS selector; with `selector` set, only annotations overlapping that element are returned and box coordinates are element-relative; with `fullPage: true` (no selector), box coordinates are document-relative.
65
65
  - **HTML/Text Extraction**: Get page content with filtering, cleaning, and minification options
66
66
  - **PDF Export**: Save pages as PDF documents with customizable format and margins
67
+ - **Video Recording**: Record browser page interactions as WebM video using CDP screencast. Start with `content_start-recording`, stop with `content_stop-recording`. Works in all modes (headless, headed, persistent, CDP attach). Chromium only.
67
68
 
68
69
  ### Interaction Tools
69
70
  - **Click**: Click elements by snapshot ref (e.g. `e1`, `@e1`, `ref=e1`), Playwright-style expression (e.g. `getByRole('button', { name: 'Login' })`, `getByLabel('Email')`, `getByText('Register')`, `getByPlaceholder('demo@example.com')`, `getByTitle('…')`, `getByAltText('…')`, `getByTestId('…')`), or CSS selector. Refs come from `a11y_take-aria-snapshot` and are valid until the next snapshot or navigation.
@@ -81,8 +82,8 @@ Choose the platform by running the appropriate MCP server or CLI:
81
82
  - **Go Back / Go Forward**: Navigate backward or forward in history. Same **waitForNavigation** (default true), **waitForTimeoutMs**, snapshot/refs and optional screenshot behavior as Go To.
82
83
  - **Reload**: Reload the current page. Same **waitForNavigation** (default true), **waitForTimeoutMs**, snapshot/refs and optional screenshot behavior as Go To.
83
84
 
84
- ### Run Tools
85
- - **Execute**: Batch-execute multiple tool calls in a single request via custom JavaScript. Use `callTool(name, input, returnOutput?)` to invoke any registered tool. Reduces round-trips and token usage. Includes wall-clock timeout, max tool call limit (50), console log capture, and fail-fast error handling with `failedTool` diagnostics. On the **browser** platform the VM also receives the session execution context: `page` (Playwright Page) is available — use the Playwright API (e.g. `page.locator()`, `page.goto()`) or `page.evaluate()` to run script in the browser. On the **Node** platform no extra bindings are injected. **Also available via CLI**: use the subcommand `run execute` or call the daemon HTTP API (POST `/call`) with `toolName: "execute"`.
85
+ ### Execute tool (batch)
86
+ - **Execute**: Batch-execute multiple tool calls in a single request via custom JavaScript. Use `callTool(name, input, returnOutput?)` to invoke any registered tool (canonical id such as `navigation_go-to`, or the same name the MCP host exposes when `TOOL_NAME_PREFIX` is set). Reduces round-trips and token usage. Includes wall-clock timeout, max tool call limit (50), console log capture, and fail-fast error handling with `failedTool` diagnostics. On the **browser** platform the VM also receives the session execution context: `page` (Playwright Page) is available — use the Playwright API (e.g. `page.locator()`, `page.goto()`) or `page.evaluate()` to run script in the browser. On the **Node** platform no extra bindings are injected. **CLI**: `run execute --code '…' [--timeout-ms N]` or `run execute --file ./script.js` (same daemon session as other tools; starts the daemon if needed). Boolean tool flags with default `true` also accept `--no-<flag>` (e.g. `--no-wait-for-navigation`). **MCP** or **daemon HTTP API** (POST `/call` with `toolName: "execute"` and `toolInput: { code, timeoutMs? }`) also work.
86
87
 
87
88
  ### Observability (O11Y) Tools
88
89
  - **Console Messages**: Capture and filter browser console logs with advanced filtering (level, search, timestamp, sequence number)
@@ -91,6 +92,12 @@ Choose the platform by running the appropriate MCP server or CLI:
91
92
  - **OpenTelemetry Tracing**: Automatic trace injection into web pages, UI trace collection (document load, fetch, XMLHttpRequest, user interactions), and trace context propagation for backend correlation
92
93
  - **Trace ID Management**: Get, set, and generate OpenTelemetry compatible trace IDs for distributed tracing across API calls
93
94
 
95
+ ### Scenario Tools
96
+ - **Scenario Add/Update/Delete**: Manage reusable test scenarios stored as JS scripts in `.browser-devtools-mcp/scenarios.json` (project-level or global)
97
+ - **Scenario Run**: Execute a saved scenario by name. Runs in the same sandbox as `execute` — supports `callTool()`, composable via nested `scenario-run` calls (max depth: 5)
98
+ - **Scenario List**: List all available scenarios from project and/or global scope
99
+ - **Scenario Search**: Search scenarios by description using configurable strategy (simple default or FTS5)
100
+
94
101
  ### Synchronization Tools
95
102
  - **Wait for Network Idle**: Wait until the page reaches a network-idle condition based on the session’s tracked in-flight request count (tunable via `sync_wait-for-network-idle` like `idleTimeMs` / `maxConnections`), useful for SPA pages and before taking screenshots
96
103
 
@@ -636,7 +643,7 @@ node-devtools-cli
636
643
 
637
644
  ### Browser CLI Commands
638
645
 
639
- `browser-devtools-cli` organizes tools into domain-based subcommands. **Object parameters** (e.g. `screenshotOptions`, `snapshotOptions`) must be passed as a JSON string: `--screenshot-options '{"outputPath":"/tmp","name":"myshot"}'` or `--snapshot-options '{"interactiveOnly":false}'`. Run `browser-devtools-cli navigation go-to --help` (or the relevant subcommand) to see all options.
646
+ `browser-devtools-cli` organizes tools into domain-based subcommands. **Object parameters** (e.g. `screenshotOptions`, `snapshotOptions`) must be passed as a JSON string: `--screenshot-options '{"outputPath":"/tmp","name":"myshot"}'` or `--snapshot-options '{"interactiveOnly":false}'`. **Boolean parameters** with default `true` can be turned off with `--no-<kebab-flag>` (e.g. `--no-wait-for-navigation`). Run `browser-devtools-cli navigation go-to --help` (or the relevant subcommand) to see all options.
640
647
 
641
648
  ```
642
649
  browser-devtools-cli
@@ -687,14 +694,14 @@ browser-devtools-cli
687
694
  │ ├── get-console-messages # Get console logs
688
695
  │ ├── get-http-requests # Get HTTP requests
689
696
  │ ├── get-web-vitals # Get Web Vitals metrics
690
- │ ├── get-trace-id # Get current trace ID
697
+ │ ├── get-trace-context # Get current trace context
691
698
  │ ├── new-trace-id # Generate new trace ID
692
- │ └── set-trace-id # Set trace ID
699
+ │ └── set-trace-context # Set trace context
693
700
  ├── react # React debugging commands
694
701
  │ ├── get-component-for-element
695
702
  │ └── get-element-for-component
696
- ├── run # Script execution commands
697
- │ └── execute # Batch-execute tool calls via JS (VM has page on browser)
703
+ ├── run # Batch execute (MCP parity): run execute --code '…' or --file path.js
704
+ │ └── execute
698
705
  ├── stub # HTTP stubbing commands
699
706
  │ ├── mock-http-response # Mock HTTP responses
700
707
  │ ├── intercept-http-request # Intercept requests
@@ -1023,7 +1030,7 @@ filled: true
1023
1030
  browser> tools search screenshot
1024
1031
  Found 2 tools:
1025
1032
  content_take-screenshot - Take a screenshot of the current page
1026
- content_take-pdf - Generate a PDF of the current page
1033
+ content_save-as-pdf - Save the current page as a PDF file
1027
1034
 
1028
1035
  browser> daemon info
1029
1036
  Version: 0.5.0
@@ -1116,7 +1123,8 @@ The CLI uses a daemon server architecture for efficient browser management:
1116
1123
  2. **Shared browser**: Multiple CLI invocations share the same browser instance
1117
1124
  3. **Session isolation**: Each session ID gets its own isolated browser context
1118
1125
  4. **Auto-cleanup**: Idle sessions are automatically cleaned up after inactivity
1119
- 5. **Full tool set**: The daemon exposes the same tools as MCP (including **execute** for batch execution). Tools can be invoked via CLI subcommands (e.g. `navigation go-to`, `run execute`) or via the daemon HTTP API (POST `/call` with `toolName` and `toolInput`).
1126
+ 5. **Full tool set**: The daemon exposes the same tools as MCP (including **execute** for batch execution). Most tools map to domain subcommands (e.g. `navigation go-to`). **execute** maps to **`run execute`** (not listed under `tools list`, which only reflects the static tool registry).
1127
+ 6. **Platform env**: The browser CLI forces `PLATFORM=browser` on the daemon it spawns (and the Node CLI forces `PLATFORM=node`), so a shell-wide `PLATFORM=node` cannot accidentally make `browser-devtools-cli` start a Node-platform daemon. If you still see errors, check `error.message` on failed `POST /call` responses — the daemon includes the underlying exception message there.
1120
1128
 
1121
1129
  The daemon listens on port 2020 by default. Use `--port` to specify a different port.
1122
1130
 
@@ -1146,6 +1154,7 @@ The server can be configured using environment variables. Configuration is divid
1146
1154
  | `SESSION_IDLE_CHECK_SECONDS` | Interval for checking idle sessions (seconds) | `30` |
1147
1155
  | `SESSION_CLOSE_ON_SOCKET_CLOSE` | Close session when socket closes | `false` |
1148
1156
  | `TOOL_OUTPUT_SCHEMA_DISABLE` | When true, omit tool output schema from MCP tool registration (can reduce token usage for some clients) | `false` |
1157
+ | `TOOL_NAME_PREFIX` | Optional string prepended to every MCP-registered tool name (stdio / streamable HTTP only), including `execute`. MCP-facing TypeScript text is rewritten by `applyNormalizedToolNamesInText` in `src/config.ts`: only **angle-bracket-wrapped** substrings that match a registered canonical tool id are replaced with the prefixed name, so plain words like “execute” are never rewritten. **README and non-MCP comments** should use backticks only, e.g. `navigation_go-to` and `callTool('navigation_go-to', …)`. In normalized sources (tool descriptions, server instructions, execute templates), authors wrap cross-tool references in angle brackets inside those string literals so clients receive the correct prefixed names. `ToolRegistry.runTool` also accepts a name that is a single bracket-wrapped id. Unrelated bracket tokens are left unchanged. CLI is unchanged. | (unset) |
1149
1158
  | `AVAILABLE_TOOL_DOMAINS` | Optional comma-separated list of tool domains to enable. When set, only tools from these domains are registered; unset means all tools. **Browser domains:** `a11y`, `content`, `debug`, `figma`, `interaction`, `navigation`, `o11y`, `react`, `run`, `stub`, `sync`. **Node domains:** `debug`, `run`. Example: `AVAILABLE_TOOL_DOMAINS=navigation,interaction,a11y` | (all tools) |
1150
1159
 
1151
1160
  Tool inputs are validated with a strict schema; unknown or misspelled argument keys (e.g. `port` instead of `inspectorPort`) cause a validation error.
@@ -1180,7 +1189,7 @@ Tool inputs are validated with a strict schema; unknown or misspelled argument k
1180
1189
  | `OTEL_SERVICE_NAME` | OpenTelemetry service name | `frontend` |
1181
1190
  | `OTEL_SERVICE_VERSION` | OpenTelemetry service version | (none) |
1182
1191
  | `OTEL_ASSETS_DIR` | Directory containing OpenTelemetry bundle files | (uses default) |
1183
- | `OTEL_EXPORTER_TYPE` | OpenTelemetry exporter type: "otlp/http", "console", or "none" | `none` |
1192
+ | `OTEL_EXPORTER_TYPE` | OpenTelemetry exporter type: "otlp/http-json", "otlp/http-protobuf", "console", or "none" (alias: "otlp/http" = "otlp/http-json") | `none` |
1184
1193
  | `OTEL_EXPORTER_HTTP_URL` | OpenTelemetry collector base URL (e.g., "http://localhost:4318") | (none) |
1185
1194
  | `OTEL_EXPORTER_HTTP_HEADERS` | OpenTelemetry exporter HTTP headers (comma-separated key=value pairs) | (none) |
1186
1195
  | `OTEL_INSTRUMENTATION_USER_INTERACTION_EVENTS` | User interaction events to instrument (comma-separated, e.g., "click,submit") | `click` |
@@ -1357,6 +1366,32 @@ Once disabled, no data is sent and no network requests are made to PostHog.
1357
1366
  - `filePath` (string): Full path of the saved PDF file
1358
1367
  </details>
1359
1368
 
1369
+ <details>
1370
+ <summary><code>content_start-recording</code> - Starts video recording of the browser page.</summary>
1371
+
1372
+ **Parameters:**
1373
+ - `outputDir` (string, optional): Directory where the video file will be saved (default: OS temp directory)
1374
+ - `name` (string, optional): Name for the video file without extension (default: "recording")
1375
+ **Returns:**
1376
+ - `message` (string): Status message
1377
+
1378
+ **Notes:**
1379
+ - Uses CDP screencast — works in all modes (headless, headed, persistent, CDP attach)
1380
+ - Only supported on Chromium-based browsers
1381
+ - Recording captures all page interactions until `content_stop-recording` is called
1382
+ </details>
1383
+
1384
+ <details>
1385
+ <summary><code>content_stop-recording</code> - Stops video recording and saves the video file.</summary>
1386
+
1387
+ **Returns:**
1388
+ - `filePath` (string, optional): Full path of the saved WebM video file
1389
+
1390
+ **Notes:**
1391
+ - Must be called after `content_start-recording`
1392
+ - The video is saved as a WebM file (VP8 codec)
1393
+ </details>
1394
+
1360
1395
  ### Interaction Tools
1361
1396
 
1362
1397
  <details>
@@ -1502,7 +1537,7 @@ Once disabled, no data is sent and no network requests are made to PostHog.
1502
1537
  - `timeout` (number, optional): Maximum operation time in milliseconds (default: 0 - no timeout)
1503
1538
  - `waitUntil` (enum, optional): Playwright main-frame lifecycle — "load", "domcontentloaded", or "commit" (default: "load"). Not Playwright `networkidle`; use `waitForNavigation` for session network-idle after navigation.
1504
1539
  - `includeSnapshot` (boolean, optional): When true (default), take an ARIA snapshot with refs after navigation and include `output` and `refs` in the response; when false, only url/status/ok are returned.
1505
- - `snapshotOptions` (object, optional): When includeSnapshot is true, options for the snapshot. **interactiveOnly** (boolean, default false): only interactive elements get refs; **cursorInteractive** (boolean, default false): include cursor-interactive elements (same as a11y_take-aria-snapshot).
1540
+ - `snapshotOptions` (object, optional): When includeSnapshot is true, options for the snapshot. **interactiveOnly** (boolean, default false): only interactive elements get refs; **cursorInteractive** (boolean, default false): include cursor-interactive elements (same as `a11y_take-aria-snapshot`).
1506
1541
  - `includeScreenshot` (boolean, optional): When true, take a screenshot after navigation; saved to disk, path returned in `screenshotFilePath`. Default false.
1507
1542
  - `screenshotOptions` (object, optional): When includeScreenshot is true. **outputPath** (string, default: OS temp dir), **name** (string, default: "screenshot"), **fullPage** (boolean, default true), **type** ("png" | "jpeg", default "png"), **annotate** (boolean, default true), **includeBase64** (boolean, default false): include image in response as separate MCP content part — use only when file cannot be read from path (e.g. remote, container).
1508
1543
 
@@ -1517,9 +1552,9 @@ Once disabled, no data is sent and no network requests are made to PostHog.
1517
1552
  <details>
1518
1553
  <summary><code>navigation_go-back-or-forward</code> - Navigates back or forward in browser history.</summary>
1519
1554
 
1520
- **Parameters:** `direction` (required: `"back"` or `"forward"`), `timeout`, `waitUntil`, `includeSnapshot` (default true), `snapshotOptions` (object: interactiveOnly, cursorInteractive), `includeScreenshot` (boolean, default false), `screenshotOptions` (object: outputPath, name, fullPage, type, annotate, includeBase64) — same semantics as navigation_go-to.
1555
+ **Parameters:** `direction` (required: `"back"` or `"forward"`), `timeout`, `waitUntil`, `includeSnapshot` (default true), `snapshotOptions` (object: interactiveOnly, cursorInteractive), `includeScreenshot` (boolean, default false), `screenshotOptions` (object: outputPath, name, fullPage, type, annotate, includeBase64) — same semantics as `navigation_go-to`.
1521
1556
 
1522
- **Returns:** Same shape as navigation_go-to (url, status, statusText, ok, screenshotFilePath, output, refs, image).
1557
+ **Returns:** Same shape as `navigation_go-to` (url, status, statusText, ok, screenshotFilePath, output, refs, image).
1523
1558
  </details>
1524
1559
 
1525
1560
  <details>
@@ -1529,11 +1564,11 @@ Once disabled, no data is sent and no network requests are made to PostHog.
1529
1564
  - `timeout` (number, optional): Maximum operation time in milliseconds (default: 0 - no timeout)
1530
1565
  - `waitUntil` (enum, optional): Playwright main-frame lifecycle — "load", "domcontentloaded", or "commit" (default: "load"). Not Playwright `networkidle`; use `waitForNavigation` for session network-idle after navigation.
1531
1566
  - `includeSnapshot` (boolean, optional): When true (default), take an ARIA snapshot with refs after reload and include `output` and `refs`; when false, only url/status/ok.
1532
- - `snapshotOptions` (object, optional): When includeSnapshot is true. **interactiveOnly** (boolean, default false), **cursorInteractive** (boolean, default false) — same as a11y_take-aria-snapshot.
1567
+ - `snapshotOptions` (object, optional): When includeSnapshot is true. **interactiveOnly** (boolean, default false), **cursorInteractive** (boolean, default false) — same as `a11y_take-aria-snapshot`.
1533
1568
  - `includeScreenshot` (boolean, optional): When true, take a screenshot after reload; saved to disk. Default false.
1534
- - `screenshotOptions` (object, optional): When includeScreenshot is true; same shape as navigation_go-to (outputPath, name, fullPage, type, annotate, includeBase64).
1569
+ - `screenshotOptions` (object, optional): When includeScreenshot is true; same shape as `navigation_go-to` (outputPath, name, fullPage, type, annotate, includeBase64).
1535
1570
 
1536
- **Returns:** Same shape as navigation_go-to (url, status, statusText, ok, screenshotFilePath, output, refs, image).
1571
+ **Returns:** Same shape as `navigation_go-to` (url, status, statusText, ok, screenshotFilePath, output, refs, image).
1537
1572
  </details>
1538
1573
 
1539
1574
  ### Run Tools
@@ -1682,13 +1717,14 @@ await callTool('content_take-screenshot', {}, true);
1682
1717
  </details>
1683
1718
 
1684
1719
  <details>
1685
- <summary><code>o11y_get-trace-id</code> - Gets the OpenTelemetry compatible trace id of the current session.</summary>
1720
+ <summary><code>o11y_get-trace-context</code> - Gets the OpenTelemetry trace context of the current session.</summary>
1686
1721
 
1687
1722
  **Parameters:**
1688
1723
  - No input parameters
1689
1724
 
1690
1725
  **Returns:**
1691
1726
  - `traceId` (string, optional): The OpenTelemetry compatible trace id of the current session if available
1727
+ - `traceState` (string, optional): The W3C tracestate value of the current session if available
1692
1728
 
1693
1729
  **Note:** Requires OpenTelemetry to be enabled (`OTEL_ENABLE=true`).
1694
1730
  </details>
@@ -1706,15 +1742,16 @@ await callTool('content_take-screenshot', {}, true);
1706
1742
  </details>
1707
1743
 
1708
1744
  <details>
1709
- <summary><code>o11y_set-trace-id</code> - Sets the OpenTelemetry compatible trace id of the current session.</summary>
1745
+ <summary><code>o11y_set-trace-context</code> - Sets or clears the OpenTelemetry trace context of the current session.</summary>
1710
1746
 
1711
1747
  **Parameters:**
1712
- - `traceId` (string, optional): The OpenTelemetry compatible trace id to be set. Leave it empty to clear the session trace id, so no OpenTelemetry trace header will be propagated from browser throughout the API calls
1748
+ - `traceId` (string, optional): 32-char lowercase hex trace id (non-all-zero). Pass **empty string** to **clear** the MCP-pinned trace id (new root traces use random ids until you set an id again). Use `o11y_new-trace-id` to pin a fresh random id.
1749
+ - `traceState` (string, optional): W3C `tracestate` list (`key=value` members, comma-separated; max 512 chars / 32 members). Pass **empty string** to **clear** tracestate (no MCP `tracestate` on outgoing requests).
1713
1750
 
1714
1751
  **Returns:**
1715
1752
  - No return value
1716
1753
 
1717
- **Note:** Requires OpenTelemetry to be enabled (`OTEL_ENABLE=true`). When a trace ID is set, it will be propagated in HTTP headers (traceparent) for all API calls, enabling correlation with backend traces.
1754
+ **Note:** Requires OpenTelemetry to be enabled (`OTEL_ENABLE=true`). At least one of `traceId` or `traceState` must appear in the input (each may be an empty string for clear). Invalid `traceState` is rejected with an error. Values are propagated via tracing headers when applicable.
1718
1755
  </details>
1719
1756
 
1720
1757
  ### Synchronization Tools
@@ -1,16 +1,16 @@
1
1
  #!/usr/bin/env node
2
- import{isToolEnabled,platformInfo}from"../core-PKYX4YOY.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-3YBKJFSF.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(`
2
+ import{isToolEnabled,platformInfo}from"../core-MEE5B4UB.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-X3FD2EZG.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 schemaFieldToCliFlag(fieldName){return _toKebabCase(fieldName)}function _createOptionsForField(name,zodType){let{innerType,isOptional,defaultValue}=_unwrapZodType(zodType),description=_getDescription(zodType)||`The ${name} value`,flagName=_toKebabCase(name),typeName=innerType._def.typeName;if(typeName===ZodFirstPartyTypeKind.ZodBoolean){if(defaultValue===!0){let positive=new Option(`--${flagName}`,description);positive.default(!0);let negative=new Option(`--no-${flagName}`,`Disable: ${description}`);return[positive,negative]}let boolOpt=new Option(`--${flagName}`,description);return defaultValue===!1&&boolOpt.default(!1),!isOptional&&defaultValue===void 0&&boolOpt.makeOptionMandatory(!0),[boolOpt]}let 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.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;if(typeof literalValue=="boolean"){if(literalValue===!0){let positive=new Option(`--${flagName}`,description);positive.default(!0);let negative=new Option(`--no-${flagName}`,`Disable: ${description}`);return[positive,negative]}let litFalse=new Option(`--${flagName}`,description);return litFalse.default(!1),[litFalse]}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))name!=="_metadata"&&options.push(..._createOptionsForField(name,zodType));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{existsSync,readFileSync}from"node:fs";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);function _resolveDaemonServerScriptPath(){let dir=__dirname;for(let i=0;i<10;i++){let candidate=path.join(dir,"daemon-server.js");if(existsSync(candidate))return candidate;let parent=path.dirname(dir);if(parent===dir)break;dir=parent}throw new Error("Could not find daemon-server.js (rebuild with npm run build or reinstall the package).")}var 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=_resolveDaemonServerScriptPath(),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(resolve2=>setTimeout(resolve2,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,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(`
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(resolve2=>setTimeout(resolve2,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(resolve2=>setTimeout(resolve2,1e3))),_debug("Starting daemon server..."),_startDaemonDetached(opts);let maxRetries=10,retryDelay=500;for(let i=0;i<maxRetries;i++)if(await new Promise(resolve2=>setTimeout(resolve2,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> [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(`
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))key!=="_metadata"&&params.push({name:key,cliFlag:schemaFieldToCliFlag(key),type:_getZodTypeName(schema),required:!_isZodOptional(schema)&&!_hasZodDefault(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(`
13
- Parameters:`),params.length===0)_output(" (none)");else for(let param of params){let reqStr=param.required?"(required)":"(optional)";_output(` --${param.name} <${param.type}> ${reqStr}`),param.description&&_output(` ${param.description}`),param.default!==void 0&&_output(` Default: ${JSON.stringify(param.default)}`)}_output(`
13
+ Parameters:`),params.length===0)_output(" (none)");else for(let param of params){let reqStr=param.required?"(required)":"(optional)";_output(` --${param.cliFlag} <${param.type}> ${reqStr}`),param.description&&_output(` ${param.description}`),param.default!==void 0&&_output(` Default: ${JSON.stringify(param.default)}`)}_output(`
14
14
  Usage:`),_output(` ${cliProvider.cliName} ${nameParts[0]} ${nameParts[1]||tool.name()} [options]`)}}),toolsCmd.command("search <query>").description("Search tools by name or description").action(query=>{let opts=program.opts(),lowerQuery=query.toLowerCase(),matchingTools=tools.filter(tool=>{let name=tool.name().toLowerCase(),description=tool.description().toLowerCase();return name.includes(lowerQuery)||description.includes(lowerQuery)});if(opts.json){let result=matchingTools.map(t=>{let parts=t.name().split("_");return{name:t.name(),domain:parts[0],description:t.description().trim().split(`
15
15
  `)[0]}});_printOutput(result,!0)}else{if(matchingTools.length===0){_output(`No tools found matching "${query}"`);return}_output(`Tools matching "${query}" (${matchingTools.length} found):
16
16
  `);for(let tool of matchingTools){let parts=tool.name().split("_"),domain=parts[0],name=parts[1]||tool.name(),desc=tool.description().trim().split(`
@@ -39,6 +39,9 @@ ${funcName}() {
39
39
  # Tools subcommands
40
40
  local tools_cmds="list info search"
41
41
 
42
+ # Run subcommands (batch execute)
43
+ local run_cmds="execute"
44
+
42
45
  case "\${prev}" in
43
46
  ${cliProvider.cliName})
44
47
  COMPREPLY=( $(compgen -W "\${commands}" -- "\${cur}") )
@@ -56,6 +59,10 @@ ${funcName}() {
56
59
  COMPREPLY=( $(compgen -W "\${tools_cmds}" -- "\${cur}") )
57
60
  return 0
58
61
  ;;
62
+ run)
63
+ COMPREPLY=( $(compgen -W "\${run_cmds}" -- "\${cur}") )
64
+ return 0
65
+ ;;
59
66
  esac
60
67
 
61
68
  # Global options
@@ -101,6 +108,11 @@ ${commandsStr}
101
108
  'search:Search tools by keyword'
102
109
  )
103
110
 
111
+ local -a run_cmds
112
+ run_cmds=(
113
+ 'execute:Batch-execute tool calls via JavaScript'
114
+ )
115
+
104
116
  _arguments -C \\
105
117
  '--port[Daemon server port]:port' \\
106
118
  '--session-id[Session ID]:session_id' \\
@@ -129,6 +141,9 @@ ${cliProvider.zshCompletionOptions}
129
141
  tools)
130
142
  _describe 'subcommand' tools_cmds
131
143
  ;;
144
+ run)
145
+ _describe 'subcommand' run_cmds
146
+ ;;
132
147
  esac
133
148
  ;;
134
149
  esac
@@ -137,7 +152,7 @@ ${cliProvider.zshCompletionOptions}
137
152
  ${funcName}
138
153
  `;console.log(script),_error(`
139
154
  # To enable, add to your ~/.zshrc:`),_error(`# eval "$(${cliProvider.cliName} completion zsh)"`)}),program.addCommand(completionCmd);function _createReplProgram(parentOpts){let replProgram=new Command2("repl").exitOverride().configureOutput({writeOut:str=>_output(str.trimEnd()),writeErr:str=>_error(str.trimEnd())}),replDaemonCmd=new Command2("daemon").description("Manage daemon server").exitOverride();replDaemonCmd.command("start").description("Start the daemon server").action(async()=>{let opts=parentOpts;await _isDaemonRunning(opts.port)?_output(`Daemon server is already running on port ${opts.port}`):(_startDaemonDetached(opts),await _ensureDaemonRunning(opts),_output(`Daemon server started on port ${opts.port}`))}),replDaemonCmd.command("stop").description("Stop the daemon server").action(async()=>{let opts=parentOpts;await _isDaemonRunning(opts.port)?await _stopDaemon(opts.port,opts.timeout??DEFAULT_TIMEOUT)?_output("Daemon server stopped"):_error("Failed to stop daemon server"):_output("Daemon server is not running")}),replDaemonCmd.command("restart").description("Restart the daemon server").action(async()=>{let opts=parentOpts;await _isDaemonRunning(opts.port)&&(await _stopDaemon(opts.port,opts.timeout??DEFAULT_TIMEOUT),await new Promise(r=>setTimeout(r,1e3))),_startDaemonDetached(opts),await _ensureDaemonRunning(opts),_output(`Daemon server restarted on port ${opts.port}`)}),replDaemonCmd.command("status").description("Check daemon server status").action(async()=>{let opts=parentOpts,running=await _isDaemonRunning(opts.port);_output(running?`Daemon server is running on port ${opts.port}`:"Daemon server is not running")}),replDaemonCmd.command("info").description("Show daemon server information").action(async()=>{let opts=parentOpts,info=await _getDaemonInfo(opts.port,opts.timeout??DEFAULT_TIMEOUT);info?(_output(`Version: ${info.version}`),_output(`Uptime: ${_formatUptime(info.uptime)}`),_output(`Sessions: ${info.sessionCount}`),_output(`Port: ${info.port}`)):_output("Daemon server is not running")}),replProgram.addCommand(replDaemonCmd);let replSessionCmd=new Command2("session").description(cliProvider.sessionDescription).exitOverride();replSessionCmd.command("list").description("List active sessions").action(async()=>{let opts=parentOpts,result=await _listSessions(opts.port,opts.timeout??DEFAULT_TIMEOUT);if(result&&result.sessions.length>0){_output(`Active sessions: ${result.sessions.length}`);for(let session of result.sessions)_output(` ${session.id} (idle: ${_formatUptime(session.idleSeconds)})`)}else _output("No active sessions")}),replSessionCmd.command("info <session-id>").description("Show session information").action(async sessionId=>{let opts=parentOpts;try{let response=await fetch(`http://localhost:${opts.port}/session`,{method:"GET",headers:{"session-id":sessionId},signal:AbortSignal.timeout(opts.timeout??DEFAULT_TIMEOUT)});if(response.ok){let info=await response.json();_output(`Session: ${info.id}`),_output(`Created: ${new Date(info.createdAt).toISOString()}`),_output(`Last Active: ${new Date(info.lastActiveAt).toISOString()}`),_output(`Idle: ${_formatUptime(info.idleSeconds)}`)}else _output(`Session not found: ${sessionId}`)}catch(err){_error(`Error: ${err.message}`)}}),replSessionCmd.command("delete <session-id>").description("Delete a session").action(async sessionId=>{let opts=parentOpts;try{(await fetch(`http://localhost:${opts.port}/session`,{method:"DELETE",headers:{"session-id":sessionId},signal:AbortSignal.timeout(opts.timeout??DEFAULT_TIMEOUT)})).ok?_output(`Session deleted: ${sessionId}`):_output(`Session not found: ${sessionId}`)}catch(err){_error(`Error: ${err.message}`)}}),replProgram.addCommand(replSessionCmd);let replToolsCmd=new Command2("tools").description("Discover and inspect available tools").exitOverride();return replToolsCmd.command("list").description("List all available tools").action(()=>{let domains=new Set;for(let tool of tools)domains.add(tool.name().split("_")[0]);_output(`Available domains: ${Array.from(domains).join(", ")}`),_output(`Total tools: ${tools.length}`)}),replToolsCmd.command("search <query>").description("Search tools by name or description").action(query=>{let lowerQuery=query.toLowerCase(),matchingTools=tools.filter(t=>t.name().toLowerCase().includes(lowerQuery)||t.description().toLowerCase().includes(lowerQuery));if(matchingTools.length>0){_output(`Found ${matchingTools.length} tools:`);for(let t of matchingTools)_output(` ${t.name()} - ${t.description()}`)}else _output(`No tools found matching "${query}"`)}),replToolsCmd.command("info <tool-name>").description("Show detailed information about a tool").action(toolName=>{let tool=tools.find(t=>t.name()===toolName);if(tool){_output(`
140
- Tool: ${tool.name()}`),_output(`Description: ${tool.description()}`),_output("Input Schema:");let schema=tool.inputSchema();for(let[key,value]of Object.entries(schema)){let typeName=_getZodTypeName(value),desc=_getZodDescription(value)||"",optional=value.isOptional();_output(` --${key} <${typeName}>${optional?" (optional)":""} ${desc}`)}}else _output(`Tool not found: ${toolName}`)}),replProgram.addCommand(replToolsCmd),replProgram.command("config").description("Show current configuration").action(()=>{let opts=parentOpts;_output(`
155
+ Tool: ${tool.name()}`),_output(`Description: ${tool.description()}`),_output("Input Schema:");let schema=tool.inputSchema();for(let[key,value]of Object.entries(schema)){if(key==="_metadata")continue;let typeName=_getZodTypeName(value),desc=_getZodDescription(value)||"",optional=value.isOptional();_output(` --${key} <${typeName}>${optional?" (optional)":""} ${desc}`)}}else _output(`Tool not found: ${toolName}`)}),replProgram.addCommand(replToolsCmd),replProgram.command("config").description("Show current configuration").action(()=>{let opts=parentOpts;_output(`
141
156
  Current Configuration:`),_output(` port = ${opts.port}`),_output(` session-id = ${opts.sessionId||"(auto)"}`),_output(cliProvider.formatConfigForRepl(opts)),_output(` timeout = ${opts.timeout??DEFAULT_TIMEOUT}`),_output(` json = ${opts.json??!1}`),_output(` quiet = ${opts.quiet??!1}`),_output(` verbose = ${opts.verbose??!1}`),_output(`
142
157
  Tip: Start interactive mode with options:`),_output(` ${cliProvider.cliExamples[0]}`)}),replProgram.command("update").description("Check for updates").option("--check","Only check for updates without installing").action(async cmdOpts=>{let currentVersion=require2("../../package.json").version,packageName=cliProvider.packageName;_output(`Checking for updates...
143
158
  `);try{let response=await fetch(`https://registry.npmjs.org/${packageName}/latest`,{signal:AbortSignal.timeout(1e4)});if(!response.ok){_error("Failed to check for updates");return}let latestVersion=(await response.json()).version;_output(`Current version: ${currentVersion}`),_output(`Latest version: ${latestVersion}`),currentVersion===latestVersion?_output(`
@@ -149,10 +164,10 @@ Available Commands:`),_output(" status Show daemon status summary"),
149
164
  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
165
  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
166
  `);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
- `),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(`
167
+ `),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(resolve2=>{rl.question("Do you want to update now? (y/N) ",ans=>{rl.close(),resolve2(ans.toLowerCase())})});if(answer!=="y"&&answer!=="yes"){_output(`
153
168
  Update cancelled.`);return}_output(`
154
169
  Updating...
155
170
  `);try{execSync(`npm install -g ${packageName}@latest`,{stdio:"inherit"}),_output(`
156
171
  \x1B[32m\u2713 Update complete!\x1B[0m`),_output("Please restart your terminal or run a new command to use the updated version.")}catch{_error(`
157
172
  \x1B[31m\u2717 Update failed.\x1B[0m`),_error("Try running manually with sudo:"),_error(` sudo npm install -g ${packageName}@latest`),process.exit(1)}}catch(err){opts.json?_printOutput({error:err.message,currentVersion},!0,!0):(_error(`\x1B[31m\u2717 Failed to check for updates: ${err.message}\x1B[0m`),_error(`
158
- You can manually check at:`),_error(` https://www.npmjs.com/package/${packageName}`)),process.exit(1)}});program.addCommand(updateCmd),registerToolCommands(program,tools,async(toolName,toolInput,globalOptions)=>{let opts=globalOptions;try{await _ensureDaemonRunning(opts);let response=await _callTool(opts.port,toolName,toolInput,opts.sessionId,opts.timeout);response.toolError&&(opts.json?_printOutput({error:response.toolError},!0,!0):_error(`Error: ${response.toolError.message||"Unknown error"}`),process.exit(1)),response.toolOutput&&(opts.json?_printOutput(response.toolOutput,!0):_output(_formatOutput(response.toolOutput)))}catch(err){opts.json?_printOutput({error:err.message},!0,!0):_error(`Error: ${err.message}`),process.exit(1)}}),await program.parseAsync(process.argv)}main().catch(err=>{_error(`Fatal error: ${err.message}`),process.exit(1)});
173
+ You can manually check at:`),_error(` https://www.npmjs.com/package/${packageName}`)),process.exit(1)}});program.addCommand(updateCmd);async function invokeDaemonTool(toolName,toolInput,globalOptions){let opts=globalOptions;try{await _ensureDaemonRunning(opts);let response=await _callTool(opts.port,toolName,toolInput,opts.sessionId,opts.timeout);response.toolError&&(opts.json?_printOutput({error:response.toolError},!0,!0):_error(`Error: ${response.toolError.message||"Unknown error"}`),process.exit(1)),response.toolOutput&&(opts.json?_printOutput(response.toolOutput,!0):_output(_formatOutput(response.toolOutput)))}catch(err){opts.json?_printOutput({error:err.message},!0,!0):_error(`Error: ${err.message}`),process.exit(1)}}let runCmd=new Command2("run").description("Script execution commands (batch tool calls via JavaScript)"),runExecuteCmd=new Command2("execute").description("Batch-execute multiple tool calls in one request (same as the MCP execute tool).").addOption(new Option2("--code <string>","JavaScript body (no async wrapper): await callTool(...); return ...")).addOption(new Option2("--file <path>","Read script body from a UTF-8 file (mutually exclusive with --code)")).addOption(new Option2("--timeout-ms <number>","Wall-clock timeout for the whole run in ms (default: 30000, max: 120000)").argParser(value=>{let n=Number(value);if(!Number.isFinite(n))throw new Error(`Invalid number: ${value}`);return n})).action(async cmdOpts=>{let globalOptions=program.opts(),codeArg=cmdOpts.code,fileArg=cmdOpts.file,hasCode=typeof codeArg=="string"&&codeArg.length>0,hasFile=typeof fileArg=="string"&&fileArg.length>0;hasCode&&hasFile&&(_error("Error: use either --code or --file, not both"),process.exit(1)),!hasCode&&!hasFile&&(_error("Error: provide --code <string> or --file <path> with the script body"),process.exit(1));let code;if(hasFile){let resolved=path.resolve(process.cwd(),fileArg);code=readFileSync(resolved,"utf8")}else code=codeArg;let toolInput={code};cmdOpts.timeoutMs!==void 0&&cmdOpts.timeoutMs!==null&&(toolInput.timeoutMs=Number(cmdOpts.timeoutMs)),await invokeDaemonTool("execute",toolInput,globalOptions)});runCmd.addCommand(runExecuteCmd),program.addCommand(runCmd),registerToolCommands(program,tools,async(toolName,toolInput,globalOptions)=>{await invokeDaemonTool(toolName,toolInput,globalOptions)}),await program.parseAsync(process.argv)}main().catch(err=>{_error(`Fatal error: ${err.message}`),process.exit(1)});
@@ -0,0 +1,4 @@
1
+ export { sendToolCallToCollector } from './collector';
2
+ export { uploadArtifact } from './artifact-uploader';
3
+ export type { ArtifactUploadResult } from './artifact-uploader';
4
+ export type { CollectorEvent, ToolCallResult, CollectorConfig, CollectorMetadata, } from './types';
@@ -0,0 +1,27 @@
1
+ import { ToolInputMetadata } from '../tools/types';
2
+ export interface CollectorEvent {
3
+ id: string;
4
+ type: string;
5
+ timestamp: number;
6
+ session_id?: string;
7
+ project_name?: string;
8
+ verification_id?: string;
9
+ trace_id?: string;
10
+ tool_name: string;
11
+ tool_input?: unknown;
12
+ tool_response?: unknown;
13
+ duration: number;
14
+ error?: string;
15
+ source: string;
16
+ [key: string]: unknown;
17
+ }
18
+ export interface ToolCallResult {
19
+ duration: number;
20
+ tool_response?: unknown;
21
+ error?: string;
22
+ }
23
+ export interface CollectorConfig {
24
+ url: string;
25
+ apiKey?: string;
26
+ }
27
+ export type CollectorMetadata = ToolInputMetadata | undefined;
@@ -1 +1 @@
1
- import{DEFAULT_NODE_DEBUG_CONFIG,ProbeKind,addWatchExpression,clearProbes,clearSnapshots,clearSnapshotsByProbe,clearWatchExpressions,createProbe,detachDebugging,disableDebugging,enableDebugging,evaluateInNode,getConsoleMessages,getExceptionBreakpoint,getOriginalSources,getScripts,getSnapshotStats,getSnapshots,getSnapshotsByProbe,getStore,getStoreKey,hasSourceMaps,isDebuggingEnabled,listProbes,listStoreKeys,listWatchExpressions,loadSourceMaps,removeProbe,removeWatchExpression,resolveSourceLocation,setExceptionBreakpoint}from"./core-3YBKJFSF.js";export{DEFAULT_NODE_DEBUG_CONFIG,ProbeKind,addWatchExpression,clearProbes,clearSnapshots,clearSnapshotsByProbe,clearWatchExpressions,createProbe,detachDebugging,disableDebugging,enableDebugging,evaluateInNode,getConsoleMessages,getExceptionBreakpoint,getOriginalSources,getScripts,getSnapshotStats,getSnapshots,getSnapshotsByProbe,getStore,getStoreKey,hasSourceMaps,isDebuggingEnabled,listProbes,listStoreKeys,listWatchExpressions,loadSourceMaps,removeProbe,removeWatchExpression,resolveSourceLocation,setExceptionBreakpoint};
1
+ import{DEFAULT_NODE_DEBUG_CONFIG,ProbeKind,addWatchExpression,clearProbes,clearSnapshots,clearSnapshotsByProbe,clearWatchExpressions,createProbe,detachDebugging,disableDebugging,enableDebugging,evaluateInNode,getConsoleMessages,getExceptionBreakpoint,getOriginalSources,getScripts,getSnapshotStats,getSnapshots,getSnapshotsByProbe,getStore,getStoreKey,hasSourceMaps,isDebuggingEnabled,listProbes,listStoreKeys,listWatchExpressions,loadSourceMaps,removeProbe,removeWatchExpression,resolveSourceLocation,setExceptionBreakpoint}from"./core-X3FD2EZG.js";export{DEFAULT_NODE_DEBUG_CONFIG,ProbeKind,addWatchExpression,clearProbes,clearSnapshots,clearSnapshotsByProbe,clearWatchExpressions,createProbe,detachDebugging,disableDebugging,enableDebugging,evaluateInNode,getConsoleMessages,getExceptionBreakpoint,getOriginalSources,getScripts,getSnapshotStats,getSnapshots,getSnapshotsByProbe,getStore,getStoreKey,hasSourceMaps,isDebuggingEnabled,listProbes,listStoreKeys,listWatchExpressions,loadSourceMaps,removeProbe,removeWatchExpression,resolveSourceLocation,setExceptionBreakpoint};