@ait-co/devtools 0.1.23 → 0.1.25

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.
@@ -3,8 +3,8 @@
3
3
  * 3-layer activation gate for the in-app debug surface.
4
4
  *
5
5
  * Spec: docs/superpowers/specs/2026-05-18-in-app-debug-mcp.md
6
- * "3-layer activation gate" Phase 1 implementation (gate logic only).
7
- * Chii client, WebSocket transport, and QR UI are later phases.
6
+ * "3-layer activation gate". This is the pure gate decision; the Chii client,
7
+ * WebSocket transport, MCP server, and CLI that consume it live in src/mcp/.
8
8
  *
9
9
  * Decision matrix:
10
10
  *
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/in-app/gate.ts","../../src/in-app/attach.ts","../../src/in-app/index.ts"],"sourcesContent":["/**\n * 3-layer activation gate for the in-app debug surface.\n *\n * Spec: docs/superpowers/specs/2026-05-18-in-app-debug-mcp.md\n * \"3-layer activation gate\" — Phase 1 implementation (gate logic only).\n * Chii client, WebSocket transport, and QR UI are later phases.\n *\n * Decision matrix:\n *\n * build channel | _deploymentId | debug=1 | result\n * release | (any) | (any) | BLOCKED (Layer A — code absent via DCE)\n * dogfood | absent | (any) | BLOCKED (Layer B — entry gate)\n * dogfood | present | absent | BLOCKED (Layer C — opt-in gate)\n * dogfood | present | present | ATTACH\n */\n\n/** Shape returned when the gate allows attachment. */\nexport interface GateResultAttach {\n readonly attach: true;\n /** The validated `wss:` relay URL from the `relay` query param. */\n readonly relayUrl: string;\n /** The deployment ID extracted from the `_deploymentId` query param. */\n readonly deploymentId: string;\n}\n\n/** Shape returned when the gate blocks attachment, with a reason code. */\nexport interface GateResultBlocked {\n readonly attach: false;\n /**\n * - `'build'` Layer A: `__DEBUG_BUILD__` is false (release build).\n * - `'entry'` Layer B: `_deploymentId` param is absent or empty.\n * - `'opt-in'` Layer C: `debug=1` param is absent.\n * - `'invalid-relay'` Layer C: `relay` param is absent, empty, or not a `wss:` URL.\n */\n readonly reason: 'build' | 'entry' | 'opt-in' | 'invalid-relay';\n}\n\nexport type GateResult = GateResultAttach | GateResultBlocked;\n\n/**\n * Input for {@link evaluateDebugGate}.\n *\n * Keeping each field explicit makes the function trivially testable without\n * needing to manipulate `window.location`.\n */\nexport interface GateInput {\n /**\n * Whether this is a debug build. Corresponds to the `__DEBUG_BUILD__`\n * compile-time constant injected by tsdown.\n *\n * In source code consumed via `@ait-co/devtools/in-app`, the thin\n * `src/in-app/index.ts` entry reads `__DEBUG_BUILD__` and passes it here.\n * Tests supply it directly.\n */\n readonly isDebugBuild: boolean;\n\n /**\n * The URL search params to inspect for gate signals.\n *\n * Prefer `URLSearchParams` so callers can pass `new URLSearchParams(location.search)`\n * without coupling the pure function to `window`.\n *\n * Layer B open seam (spec open question 2): if the Toss SDK ever exposes\n * `getEntryScheme()` or a similar API that reliably signals a dogfood entry,\n * that signal should be checked before `_deploymentId` here. For now only the\n * `_deploymentId` query param fallback is implemented. Pass a custom\n * `URLSearchParams` to inject the SDK signal at the call site without\n * modifying this function.\n */\n readonly searchParams: URLSearchParams;\n}\n\n/**\n * Pure function that evaluates the 3-layer debug activation gate.\n *\n * Has no side effects. All inputs are explicit. Returns a discriminated union\n * so callers can pattern-match on `result.attach`.\n *\n * @example\n * ```ts\n * const result = evaluateDebugGate({\n * isDebugBuild: __DEBUG_BUILD__,\n * searchParams: new URLSearchParams(window.location.search),\n * });\n * if (result.attach) {\n * // Proceed to load Chii client\n * }\n * ```\n */\nexport function evaluateDebugGate(input: GateInput): GateResult {\n // Layer A — build-time gate.\n // When false, the entire in-app entry + Chii imports are dead-code-eliminated\n // by the bundler (tsdown/Rolldown constant folding). Release builds never\n // contain this branch at all.\n if (!input.isDebugBuild) {\n return { attach: false, reason: 'build' };\n }\n\n // Layer B — runtime entry scheme gate.\n // `_deploymentId` must be present and non-empty. The `intoss-private://`\n // scheme used for dogfood entries includes this param; general user entry\n // paths do not.\n //\n // Open seam (spec open question 2): if the Toss SDK exposes getEntryScheme()\n // or similar, that should be the 1st-priority signal checked here, with\n // `_deploymentId` as fallback. Extend this check at the call site by\n // pre-populating `searchParams` with the SDK signal, or add an optional\n // `entryScheme` field to `GateInput` in a later phase.\n const deploymentId = input.searchParams.get('_deploymentId') ?? '';\n if (deploymentId === '') {\n return { attach: false, reason: 'entry' };\n }\n\n // Layer C — explicit opt-in gate.\n // Require `debug=1` so that an operator who opens a dogfood URL by accident\n // does not inadvertently trigger the debug surface.\n const debugParam = input.searchParams.get('debug');\n if (debugParam !== '1') {\n return { attach: false, reason: 'opt-in' };\n }\n\n // Layer C continued — relay URL validation.\n // `relay=<wss-url>` must be present and must use the `wss:` scheme.\n // Plain `ws:` is rejected (no TLS). `http:`/`https:` are rejected.\n const relayRaw = input.searchParams.get('relay') ?? '';\n if (relayRaw === '') {\n return { attach: false, reason: 'invalid-relay' };\n }\n\n let relayUrl: URL;\n try {\n relayUrl = new URL(relayRaw);\n } catch {\n return { attach: false, reason: 'invalid-relay' };\n }\n\n if (relayUrl.protocol !== 'wss:') {\n return { attach: false, reason: 'invalid-relay' };\n }\n\n return { attach: true, relayUrl: relayUrl.href, deploymentId };\n}\n","/**\n * In-app Chii target injection for the debug attach flow.\n *\n * Spec: docs/superpowers/specs/2026-05-18-in-app-debug-mcp.md\n * \"MCP attach\" topology section — Phase 1 browser-side implementation.\n *\n * This module bridges the 3-layer gate result to a Chii `target.js` script\n * injection. The Chii npm package is the relay SERVER — the in-app side is\n * a plain `<script src=\"…/target.js\">` pointing at the relay host. No chii\n * npm dependency is needed here.\n */\n\nimport { checkDebugGate, type GateResult } from './index.js';\n\n/**\n * Converts a validated `wss:` relay URL into the Chii `target.js` script URL.\n *\n * Scheme is mapped `wss:` → `https:`. Host and port are preserved.\n * Pathname is set to `/target.js` regardless of the relay path.\n * Query params and hash from the relay URL are dropped — the target script\n * URL is a static asset path on the same host.\n *\n * @example\n * deriveTargetScriptUrl('wss://abc.trycloudflare.com/relay')\n * // → 'https://abc.trycloudflare.com/target.js'\n *\n * deriveTargetScriptUrl('wss://h.example.com:9100/')\n * // → 'https://h.example.com:9100/target.js'\n */\nexport function deriveTargetScriptUrl(relayUrl: string): string {\n const u = new URL(relayUrl);\n u.protocol = 'https:';\n u.pathname = '/target.js';\n u.search = '';\n u.hash = '';\n return u.toString();\n}\n\n/** Module-level guard against double-injection within a page lifecycle. */\nlet attached = false;\n\n/**\n * Evaluates the 3-layer debug gate and, if the gate passes, injects the Chii\n * `target.js` script into `document.head`.\n *\n * Idempotent — calling more than once is safe. The second call is a no-op if\n * a script with the same `src` is already present in the document, and the\n * module-level `attached` flag prevents redundant DOM queries after the first\n * successful injection.\n *\n * Safe to call even if `document` is somehow unavailable (defensive boundary\n * guard — in practice this always runs in a real WebView).\n *\n * @param gateResult - Optional pre-evaluated gate result for testability.\n * Defaults to `checkDebugGate()` which reads the current page URL and the\n * `__DEBUG_BUILD__` compile-time constant. Passing a custom value avoids\n * the need to manipulate `window.location` in tests.\n */\nexport function maybeAttach(gateResult: GateResult = checkDebugGate()): void {\n if (!gateResult.attach) {\n console.debug(\n `[@ait-co/devtools] debug attach skipped — gate blocked (reason: ${gateResult.reason})`,\n );\n return;\n }\n\n // Guard against double-injection across repeated calls.\n if (attached) {\n return;\n }\n\n // Defensive: if document is not available (unusual, but possible in some\n // SSR-adjacent edge cases), bail silently rather than throwing.\n if (typeof document === 'undefined') {\n return;\n }\n\n const src = deriveTargetScriptUrl(gateResult.relayUrl);\n\n // Also guard against a script with the same src already in the DOM\n // (e.g. injected by a different code path or a page reload within SPA).\n const existing = document.querySelector<HTMLScriptElement>(`script[src=\"${src}\"]`);\n if (existing !== null) {\n attached = true;\n return;\n }\n\n const script = document.createElement('script');\n script.src = src;\n script.async = true;\n (document.head ?? document.documentElement).appendChild(script);\n\n attached = true;\n}\n","/**\n * @ait-co/devtools/in-app entry point.\n *\n * Spec: docs/superpowers/specs/2026-05-18-in-app-debug-mcp.md\n *\n * Phase 1 — gate + browser-side Chii target injection.\n * WebSocket relay, QR/paste UI, and AI-host MCP bin are later phases that\n * require real-device validation and are not included here.\n *\n * This thin entry reads `__DEBUG_BUILD__` and `window.location`, then calls\n * the pure {@link evaluateDebugGate} function. All testable logic lives in\n * `./gate.ts` and `./attach.ts`, not here.\n */\n\nimport { evaluateDebugGate, type GateResult } from './gate.js';\n\nexport { deriveTargetScriptUrl, maybeAttach } from './attach.js';\nexport type { GateInput, GateResult, GateResultAttach, GateResultBlocked } from './gate.js';\nexport { evaluateDebugGate } from './gate.js';\n\n/**\n * Evaluates the 3-layer debug activation gate against the current page URL.\n *\n * Returns the gate result. Callers can check `result.attach` to decide whether\n * to proceed with debug surface attachment.\n *\n * This function reads `window.location` and the `__DEBUG_BUILD__` compile-time\n * constant. It has no other side effects.\n */\nexport function checkDebugGate(): GateResult {\n return evaluateDebugGate({\n isDebugBuild: __DEBUG_BUILD__,\n searchParams: new URLSearchParams(window.location.search),\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAyFA,SAAgB,kBAAkB,OAA8B;AAK9D,KAAI,CAAC,MAAM,aACT,QAAO;EAAE,QAAQ;EAAO,QAAQ;EAAS;CAa3C,MAAM,eAAe,MAAM,aAAa,IAAI,gBAAgB,IAAI;AAChE,KAAI,iBAAiB,GACnB,QAAO;EAAE,QAAQ;EAAO,QAAQ;EAAS;AAO3C,KADmB,MAAM,aAAa,IAAI,QAAQ,KAC/B,IACjB,QAAO;EAAE,QAAQ;EAAO,QAAQ;EAAU;CAM5C,MAAM,WAAW,MAAM,aAAa,IAAI,QAAQ,IAAI;AACpD,KAAI,aAAa,GACf,QAAO;EAAE,QAAQ;EAAO,QAAQ;EAAiB;CAGnD,IAAI;AACJ,KAAI;AACF,aAAW,IAAI,IAAI,SAAS;SACtB;AACN,SAAO;GAAE,QAAQ;GAAO,QAAQ;GAAiB;;AAGnD,KAAI,SAAS,aAAa,OACxB,QAAO;EAAE,QAAQ;EAAO,QAAQ;EAAiB;AAGnD,QAAO;EAAE,QAAQ;EAAM,UAAU,SAAS;EAAM;EAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC/GhE,SAAgB,sBAAsB,UAA0B;CAC9D,MAAM,IAAI,IAAI,IAAI,SAAS;AAC3B,GAAE,WAAW;AACb,GAAE,WAAW;AACb,GAAE,SAAS;AACX,GAAE,OAAO;AACT,QAAO,EAAE,UAAU;;;AAIrB,IAAI,WAAW;;;;;;;;;;;;;;;;;;AAmBf,SAAgB,YAAY,aAAyB,gBAAgB,EAAQ;AAC3E,KAAI,CAAC,WAAW,QAAQ;AACtB,UAAQ,MACN,mEAAmE,WAAW,OAAO,GACtF;AACD;;AAIF,KAAI,SACF;AAKF,KAAI,OAAO,aAAa,YACtB;CAGF,MAAM,MAAM,sBAAsB,WAAW,SAAS;AAKtD,KADiB,SAAS,cAAiC,eAAe,IAAI,IAAI,KACjE,MAAM;AACrB,aAAW;AACX;;CAGF,MAAM,SAAS,SAAS,cAAc,SAAS;AAC/C,QAAO,MAAM;AACb,QAAO,QAAQ;AACf,EAAC,SAAS,QAAQ,SAAS,iBAAiB,YAAY,OAAO;AAE/D,YAAW;;;;;;;;;;;;;;;;;;;;;;;;;;AC/Db,SAAgB,iBAA6B;AAC3C,QAAO,kBAAkB;EACvB,cAAA;EACA,cAAc,IAAI,gBAAgB,OAAO,SAAS,OAAO;EAC1D,CAAC"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/in-app/gate.ts","../../src/in-app/attach.ts","../../src/in-app/index.ts"],"sourcesContent":["/**\n * 3-layer activation gate for the in-app debug surface.\n *\n * Spec: docs/superpowers/specs/2026-05-18-in-app-debug-mcp.md\n * \"3-layer activation gate\". This is the pure gate decision; the Chii client,\n * WebSocket transport, MCP server, and CLI that consume it live in src/mcp/.\n *\n * Decision matrix:\n *\n * build channel | _deploymentId | debug=1 | result\n * release | (any) | (any) | BLOCKED (Layer A — code absent via DCE)\n * dogfood | absent | (any) | BLOCKED (Layer B — entry gate)\n * dogfood | present | absent | BLOCKED (Layer C — opt-in gate)\n * dogfood | present | present | ATTACH\n */\n\n/** Shape returned when the gate allows attachment. */\nexport interface GateResultAttach {\n readonly attach: true;\n /** The validated `wss:` relay URL from the `relay` query param. */\n readonly relayUrl: string;\n /** The deployment ID extracted from the `_deploymentId` query param. */\n readonly deploymentId: string;\n}\n\n/** Shape returned when the gate blocks attachment, with a reason code. */\nexport interface GateResultBlocked {\n readonly attach: false;\n /**\n * - `'build'` Layer A: `__DEBUG_BUILD__` is false (release build).\n * - `'entry'` Layer B: `_deploymentId` param is absent or empty.\n * - `'opt-in'` Layer C: `debug=1` param is absent.\n * - `'invalid-relay'` Layer C: `relay` param is absent, empty, or not a `wss:` URL.\n */\n readonly reason: 'build' | 'entry' | 'opt-in' | 'invalid-relay';\n}\n\nexport type GateResult = GateResultAttach | GateResultBlocked;\n\n/**\n * Input for {@link evaluateDebugGate}.\n *\n * Keeping each field explicit makes the function trivially testable without\n * needing to manipulate `window.location`.\n */\nexport interface GateInput {\n /**\n * Whether this is a debug build. Corresponds to the `__DEBUG_BUILD__`\n * compile-time constant injected by tsdown.\n *\n * In source code consumed via `@ait-co/devtools/in-app`, the thin\n * `src/in-app/index.ts` entry reads `__DEBUG_BUILD__` and passes it here.\n * Tests supply it directly.\n */\n readonly isDebugBuild: boolean;\n\n /**\n * The URL search params to inspect for gate signals.\n *\n * Prefer `URLSearchParams` so callers can pass `new URLSearchParams(location.search)`\n * without coupling the pure function to `window`.\n *\n * Layer B open seam (spec open question 2): if the Toss SDK ever exposes\n * `getEntryScheme()` or a similar API that reliably signals a dogfood entry,\n * that signal should be checked before `_deploymentId` here. For now only the\n * `_deploymentId` query param fallback is implemented. Pass a custom\n * `URLSearchParams` to inject the SDK signal at the call site without\n * modifying this function.\n */\n readonly searchParams: URLSearchParams;\n}\n\n/**\n * Pure function that evaluates the 3-layer debug activation gate.\n *\n * Has no side effects. All inputs are explicit. Returns a discriminated union\n * so callers can pattern-match on `result.attach`.\n *\n * @example\n * ```ts\n * const result = evaluateDebugGate({\n * isDebugBuild: __DEBUG_BUILD__,\n * searchParams: new URLSearchParams(window.location.search),\n * });\n * if (result.attach) {\n * // Proceed to load Chii client\n * }\n * ```\n */\nexport function evaluateDebugGate(input: GateInput): GateResult {\n // Layer A — build-time gate.\n // When false, the entire in-app entry + Chii imports are dead-code-eliminated\n // by the bundler (tsdown/Rolldown constant folding). Release builds never\n // contain this branch at all.\n if (!input.isDebugBuild) {\n return { attach: false, reason: 'build' };\n }\n\n // Layer B — runtime entry scheme gate.\n // `_deploymentId` must be present and non-empty. The `intoss-private://`\n // scheme used for dogfood entries includes this param; general user entry\n // paths do not.\n //\n // Open seam (spec open question 2): if the Toss SDK exposes getEntryScheme()\n // or similar, that should be the 1st-priority signal checked here, with\n // `_deploymentId` as fallback. Extend this check at the call site by\n // pre-populating `searchParams` with the SDK signal, or add an optional\n // `entryScheme` field to `GateInput` in a later phase.\n const deploymentId = input.searchParams.get('_deploymentId') ?? '';\n if (deploymentId === '') {\n return { attach: false, reason: 'entry' };\n }\n\n // Layer C — explicit opt-in gate.\n // Require `debug=1` so that an operator who opens a dogfood URL by accident\n // does not inadvertently trigger the debug surface.\n const debugParam = input.searchParams.get('debug');\n if (debugParam !== '1') {\n return { attach: false, reason: 'opt-in' };\n }\n\n // Layer C continued — relay URL validation.\n // `relay=<wss-url>` must be present and must use the `wss:` scheme.\n // Plain `ws:` is rejected (no TLS). `http:`/`https:` are rejected.\n const relayRaw = input.searchParams.get('relay') ?? '';\n if (relayRaw === '') {\n return { attach: false, reason: 'invalid-relay' };\n }\n\n let relayUrl: URL;\n try {\n relayUrl = new URL(relayRaw);\n } catch {\n return { attach: false, reason: 'invalid-relay' };\n }\n\n if (relayUrl.protocol !== 'wss:') {\n return { attach: false, reason: 'invalid-relay' };\n }\n\n return { attach: true, relayUrl: relayUrl.href, deploymentId };\n}\n","/**\n * In-app Chii target injection for the debug attach flow.\n *\n * Spec: docs/superpowers/specs/2026-05-18-in-app-debug-mcp.md\n * \"MCP attach\" topology section — Phase 1 browser-side implementation.\n *\n * This module bridges the 3-layer gate result to a Chii `target.js` script\n * injection. The Chii npm package is the relay SERVER — the in-app side is\n * a plain `<script src=\"…/target.js\">` pointing at the relay host. No chii\n * npm dependency is needed here.\n */\n\nimport { checkDebugGate, type GateResult } from './index.js';\n\n/**\n * Converts a validated `wss:` relay URL into the Chii `target.js` script URL.\n *\n * Scheme is mapped `wss:` → `https:`. Host and port are preserved.\n * Pathname is set to `/target.js` regardless of the relay path.\n * Query params and hash from the relay URL are dropped — the target script\n * URL is a static asset path on the same host.\n *\n * @example\n * deriveTargetScriptUrl('wss://abc.trycloudflare.com/relay')\n * // → 'https://abc.trycloudflare.com/target.js'\n *\n * deriveTargetScriptUrl('wss://h.example.com:9100/')\n * // → 'https://h.example.com:9100/target.js'\n */\nexport function deriveTargetScriptUrl(relayUrl: string): string {\n const u = new URL(relayUrl);\n u.protocol = 'https:';\n u.pathname = '/target.js';\n u.search = '';\n u.hash = '';\n return u.toString();\n}\n\n/** Module-level guard against double-injection within a page lifecycle. */\nlet attached = false;\n\n/**\n * Evaluates the 3-layer debug gate and, if the gate passes, injects the Chii\n * `target.js` script into `document.head`.\n *\n * Idempotent — calling more than once is safe. The second call is a no-op if\n * a script with the same `src` is already present in the document, and the\n * module-level `attached` flag prevents redundant DOM queries after the first\n * successful injection.\n *\n * Safe to call even if `document` is somehow unavailable (defensive boundary\n * guard — in practice this always runs in a real WebView).\n *\n * @param gateResult - Optional pre-evaluated gate result for testability.\n * Defaults to `checkDebugGate()` which reads the current page URL and the\n * `__DEBUG_BUILD__` compile-time constant. Passing a custom value avoids\n * the need to manipulate `window.location` in tests.\n */\nexport function maybeAttach(gateResult: GateResult = checkDebugGate()): void {\n if (!gateResult.attach) {\n console.debug(\n `[@ait-co/devtools] debug attach skipped — gate blocked (reason: ${gateResult.reason})`,\n );\n return;\n }\n\n // Guard against double-injection across repeated calls.\n if (attached) {\n return;\n }\n\n // Defensive: if document is not available (unusual, but possible in some\n // SSR-adjacent edge cases), bail silently rather than throwing.\n if (typeof document === 'undefined') {\n return;\n }\n\n const src = deriveTargetScriptUrl(gateResult.relayUrl);\n\n // Also guard against a script with the same src already in the DOM\n // (e.g. injected by a different code path or a page reload within SPA).\n const existing = document.querySelector<HTMLScriptElement>(`script[src=\"${src}\"]`);\n if (existing !== null) {\n attached = true;\n return;\n }\n\n const script = document.createElement('script');\n script.src = src;\n script.async = true;\n (document.head ?? document.documentElement).appendChild(script);\n\n attached = true;\n}\n","/**\n * @ait-co/devtools/in-app entry point.\n *\n * Spec: docs/superpowers/specs/2026-05-18-in-app-debug-mcp.md\n *\n * Phase 1 — gate + browser-side Chii target injection.\n * WebSocket relay, QR/paste UI, and AI-host MCP bin are later phases that\n * require real-device validation and are not included here.\n *\n * This thin entry reads `__DEBUG_BUILD__` and `window.location`, then calls\n * the pure {@link evaluateDebugGate} function. All testable logic lives in\n * `./gate.ts` and `./attach.ts`, not here.\n */\n\nimport { evaluateDebugGate, type GateResult } from './gate.js';\n\nexport { deriveTargetScriptUrl, maybeAttach } from './attach.js';\nexport type { GateInput, GateResult, GateResultAttach, GateResultBlocked } from './gate.js';\nexport { evaluateDebugGate } from './gate.js';\n\n/**\n * Evaluates the 3-layer debug activation gate against the current page URL.\n *\n * Returns the gate result. Callers can check `result.attach` to decide whether\n * to proceed with debug surface attachment.\n *\n * This function reads `window.location` and the `__DEBUG_BUILD__` compile-time\n * constant. It has no other side effects.\n */\nexport function checkDebugGate(): GateResult {\n return evaluateDebugGate({\n isDebugBuild: __DEBUG_BUILD__,\n searchParams: new URLSearchParams(window.location.search),\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAyFA,SAAgB,kBAAkB,OAA8B;AAK9D,KAAI,CAAC,MAAM,aACT,QAAO;EAAE,QAAQ;EAAO,QAAQ;EAAS;CAa3C,MAAM,eAAe,MAAM,aAAa,IAAI,gBAAgB,IAAI;AAChE,KAAI,iBAAiB,GACnB,QAAO;EAAE,QAAQ;EAAO,QAAQ;EAAS;AAO3C,KADmB,MAAM,aAAa,IAAI,QAAQ,KAC/B,IACjB,QAAO;EAAE,QAAQ;EAAO,QAAQ;EAAU;CAM5C,MAAM,WAAW,MAAM,aAAa,IAAI,QAAQ,IAAI;AACpD,KAAI,aAAa,GACf,QAAO;EAAE,QAAQ;EAAO,QAAQ;EAAiB;CAGnD,IAAI;AACJ,KAAI;AACF,aAAW,IAAI,IAAI,SAAS;SACtB;AACN,SAAO;GAAE,QAAQ;GAAO,QAAQ;GAAiB;;AAGnD,KAAI,SAAS,aAAa,OACxB,QAAO;EAAE,QAAQ;EAAO,QAAQ;EAAiB;AAGnD,QAAO;EAAE,QAAQ;EAAM,UAAU,SAAS;EAAM;EAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC/GhE,SAAgB,sBAAsB,UAA0B;CAC9D,MAAM,IAAI,IAAI,IAAI,SAAS;AAC3B,GAAE,WAAW;AACb,GAAE,WAAW;AACb,GAAE,SAAS;AACX,GAAE,OAAO;AACT,QAAO,EAAE,UAAU;;;AAIrB,IAAI,WAAW;;;;;;;;;;;;;;;;;;AAmBf,SAAgB,YAAY,aAAyB,gBAAgB,EAAQ;AAC3E,KAAI,CAAC,WAAW,QAAQ;AACtB,UAAQ,MACN,mEAAmE,WAAW,OAAO,GACtF;AACD;;AAIF,KAAI,SACF;AAKF,KAAI,OAAO,aAAa,YACtB;CAGF,MAAM,MAAM,sBAAsB,WAAW,SAAS;AAKtD,KADiB,SAAS,cAAiC,eAAe,IAAI,IAAI,KACjE,MAAM;AACrB,aAAW;AACX;;CAGF,MAAM,SAAS,SAAS,cAAc,SAAS;AAC/C,QAAO,MAAM;AACb,QAAO,QAAQ;AACf,EAAC,SAAS,QAAQ,SAAS,iBAAiB,YAAY,OAAO;AAE/D,YAAW;;;;;;;;;;;;;;;;;;;;;;;;;;AC/Db,SAAgB,iBAA6B;AAC3C,QAAO,kBAAkB;EACvB,cAAA;EACA,cAAc,IAAI,gBAAgB,OAAO,SAAS,OAAO;EAC1D,CAAC"}
package/dist/mcp/cli.js CHANGED
@@ -274,7 +274,7 @@ async function startChiiRelay(options = {}) {
274
274
  }
275
275
  //#endregion
276
276
  //#region src/mcp/tools.ts
277
- /** Static MCP tool descriptors (name + JSONSchema) for the Phase 1 surface. */
277
+ /** Static MCP tool descriptors (name + JSONSchema) for the full debug tool surface. */
278
278
  const DEBUG_TOOL_DEFINITIONS = [
279
279
  {
280
280
  name: "list_console_messages",
@@ -461,14 +461,14 @@ function getOperationalEnvironment(source) {
461
461
  *
462
462
  * On spawn, the debug server opens an accountless `*.trycloudflare.com` quick
463
463
  * tunnel to the local Chii relay so the phone can attach over a public wss URL,
464
- * then prints that URL + a secret token + an ASCII QR to the terminal. The
464
+ * then prints that URL + an attach token + an ASCII QR to the terminal. The
465
465
  * phone scans the QR (or pastes the URL) to attach; the in-app side passes the
466
- * token back. Phase 1 only generates + displays the token and makes it
467
- * available — full ACL enforcement is a later phase.
466
+ * token back. The token is generated + displayed as a pairing hint; relay-side
467
+ * validation (ACL enforcement) is a later phase.
468
468
  *
469
469
  * Node-only: spawns the cloudflared binary and writes to stdout/stderr.
470
470
  */
471
- /** Generates a 32-byte hex secret token used to gate attach. */
471
+ /** Generates a 32-byte hex attach token shown as a pairing hint (relay-side validation is a later phase). */
472
472
  function generateAttachToken() {
473
473
  return randomBytes(32).toString("hex");
474
474
  }
@@ -524,8 +524,9 @@ async function renderAttachBanner(input) {
524
524
  "",
525
525
  "AIT debug — attach a mini-app to this session",
526
526
  "",
527
- ` relay (wss): ${input.wssUrl}`,
528
- ` token: ${input.token}`,
527
+ ` relay (wss): ${input.wssUrl}`,
528
+ ` attach token: ${input.token}`,
529
+ ` (token is a pairing hint — relay-side validation lands in a later phase)`,
529
530
  "",
530
531
  " Open the dogfood mini-app with ?debug=1, then scan the QR",
531
532
  " (or paste the relay URL + token in the in-app attach form):",
@@ -541,7 +542,7 @@ async function printAttachBanner(input) {
541
542
  //#endregion
542
543
  //#region src/mcp/debug-server.ts
543
544
  /**
544
- * @ait-co/devtools debug-mode MCP server (stdio) — Phase 1–3.
545
+ * @ait-co/devtools debug-mode MCP server (stdio).
545
546
  *
546
547
  * Lets an AI coding agent attach to a running mini-app (real Toss WebView, or a
547
548
  * browser in dev mode) and read its console/network/DOM/screenshot over CDP plus
@@ -555,7 +556,7 @@ async function printAttachBanner(input) {
555
556
  * The tool layer reads from an injectable `CdpConnection` (CDP) and `AitSource`
556
557
  * (AIT.*), so every tool is unit-testable with a fake (no phone). This module
557
558
  * wires the live pieces (relay + tunnel + production connection); the phone
558
- * roundtrip itself is phone-gated and deferred.
559
+ * roundtrip is fully wired and pending only on-device acceptance.
559
560
  *
560
561
  * Node-only.
561
562
  */
@@ -568,7 +569,7 @@ function createDebugServer(deps) {
568
569
  const { connection, aitSource, getTunnelStatus } = deps;
569
570
  const server = new Server({
570
571
  name: "ait-debug",
571
- version: "0.1.23"
572
+ version: "0.1.25"
572
573
  }, { capabilities: { tools: {} } });
573
574
  server.setRequestHandler(ListToolsRequestSchema, () => ({ tools: DEBUG_TOOL_DEFINITIONS.map((tool) => ({ ...tool })) }));
574
575
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
@@ -815,7 +816,7 @@ function createDevServer(deps = {}) {
815
816
  const aitSource = deps.aitSource ?? new HttpAitSource({ stateEndpoint });
816
817
  const server = new Server({
817
818
  name: "ait-devtools",
818
- version: "0.1.23"
819
+ version: "0.1.25"
819
820
  }, { capabilities: { tools: {} } });
820
821
  server.setRequestHandler(ListToolsRequestSchema, () => ({ tools: DEV_TOOL_DEFINITIONS.map((tool) => ({ ...tool })) }));
821
822
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","names":["isObject","isObject","jsonResult"],"sources":["../../src/mcp/ait-chii-source.ts","../../src/mcp/chii-connection.ts","../../src/mcp/chii-relay.ts","../../src/mcp/tools.ts","../../src/mcp/tunnel.ts","../../src/mcp/debug-server.ts","../../src/mcp/ait-http-source.ts","../../src/mcp/server.ts","../../src/mcp/cli.ts"],"sourcesContent":["/**\n * Debug-mode `AitSource` — forwards `AIT.*` methods over the Chii channel.\n *\n * The AIT domain (`AIT.getSdkCallHistory` / `getMockState` /\n * `getOperationalEnvironment`) is non-standard CDP: the in-app side registers a\n * handler for these methods and answers them over the same Chii websocket the\n * CDP commands use. Building the AIT source on `ChiiCdpConnection.sendCommand`\n * means both domains share one transport (spec: \"the same MCP server forwards\n * both CDP and AIT domains\").\n *\n * The in-app `AIT.*` handler is a downstream concern (Phase 4 sdk-example\n * integration). Here we build the MCP-server-side forwarding + the injectable\n * seam; tests inject a fake `AitSource` returning canned responses, so this\n * forwarding layer needs no phone.\n *\n * Node-only (wraps the relay websocket connection).\n */\n\nimport type {\n AitMethodMap,\n AitMethodName,\n AitMockState,\n AitOperationalEnvironment,\n AitSdkCallHistory,\n AitSource,\n} from './ait-source.js';\n\n/** The slice of `ChiiCdpConnection` this source needs (keeps it testable). */\nexport interface AitCommandSender {\n sendCommand(method: string, params?: Record<string, unknown>): Promise<unknown>;\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\n/** Narrows an `AIT.getSdkCallHistory` response, tolerating a missing array. */\nfunction asSdkCallHistory(raw: unknown): AitSdkCallHistory {\n if (isObject(raw) && Array.isArray(raw.calls)) {\n return { calls: raw.calls as AitSdkCallHistory['calls'] };\n }\n return { calls: [] };\n}\n\n/** Narrows an `AIT.getMockState` response to an opaque record. */\nfunction asMockState(raw: unknown): AitMockState {\n return isObject(raw) ? raw : {};\n}\n\n/** Narrows an `AIT.getOperationalEnvironment` response. */\nfunction asOperationalEnvironment(raw: unknown): AitOperationalEnvironment {\n const environment =\n isObject(raw) && typeof raw.environment === 'string' ? raw.environment : 'unknown';\n const sdkVersion = isObject(raw) && typeof raw.sdkVersion === 'string' ? raw.sdkVersion : null;\n return { environment, sdkVersion };\n}\n\nexport class ChiiAitSource implements AitSource {\n constructor(private readonly sender: AitCommandSender) {}\n\n async get<M extends AitMethodName>(method: M): Promise<AitMethodMap[M]> {\n const raw = await this.sender.sendCommand(method);\n // The map's value type is resolved per-key below; the cast is the single\n // narrowing point (each branch returns the precise shape for `method`).\n switch (method) {\n case 'AIT.getSdkCallHistory':\n return asSdkCallHistory(raw) as AitMethodMap[M];\n case 'AIT.getMockState':\n return asMockState(raw) as AitMethodMap[M];\n case 'AIT.getOperationalEnvironment':\n return asOperationalEnvironment(raw) as AitMethodMap[M];\n default:\n throw new Error(`Unknown AIT method: ${String(method)}`);\n }\n }\n}\n","/**\n * Production `CdpConnection` backed by the local Chii relay.\n *\n * Topology (debug mode):\n * phone target.js --WS--> Chii relay :9100 <--WS-- this connection\n *\n * The phone connects to the relay as a `target`; this module connects as a\n * `client` (the role a CDP frontend would take) so CDP events the page emits\n * (`Runtime.consoleAPICalled`, `Network.*`) flow back here. We buffer recent\n * events in ring buffers the tool layer reads via `getBufferedEvents`.\n *\n * Node-only: imports `ws`. Never bundled into the browser/in-app entries.\n */\n\nimport { EventEmitter } from 'node:events';\nimport { WebSocket } from 'ws';\nimport type {\n CdpCommandMap,\n CdpCommandName,\n CdpConnection,\n CdpEventMap,\n CdpEventName,\n CdpTarget,\n} from './cdp-connection.js';\n\n/** Max events retained per domain ring buffer. */\nconst DEFAULT_BUFFER_SIZE = 500;\n\n/** A CDP message arriving over the relay websocket. */\ninterface CdpInboundMessage {\n id?: number;\n method?: string;\n params?: unknown;\n result?: unknown;\n error?: { message: string };\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction parseInbound(raw: string): CdpInboundMessage | null {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return null;\n }\n if (!isObject(parsed)) return null;\n const message: CdpInboundMessage = {};\n if (typeof parsed.id === 'number') message.id = parsed.id;\n if (typeof parsed.method === 'string') message.method = parsed.method;\n if ('params' in parsed) message.params = parsed.params;\n if ('result' in parsed) message.result = parsed.result;\n if (isObject(parsed.error) && typeof parsed.error.message === 'string') {\n message.error = { message: parsed.error.message };\n }\n return message;\n}\n\nconst PHASE_1_EVENTS: readonly CdpEventName[] = [\n 'Runtime.consoleAPICalled',\n 'Network.requestWillBeSent',\n 'Network.responseReceived',\n];\n\nexport interface ChiiCdpConnectionOptions {\n /** Base URL of the local Chii relay HTTP/WS server, e.g. `http://127.0.0.1:9100`. */\n relayBaseUrl: string;\n /** Per-domain ring buffer size. */\n bufferSize?: number;\n}\n\n/**\n * Production CDP connection. Polls the relay for the first attached target,\n * opens a client websocket to it, enables Phase 1 domains, and buffers events.\n */\nexport class ChiiCdpConnection implements CdpConnection {\n private readonly relayBaseUrl: string;\n private readonly bufferSize: number;\n private readonly emitter = new EventEmitter();\n private readonly buffers = new Map<CdpEventName, unknown[]>();\n private readonly targets = new Map<string, CdpTarget>();\n\n private ws: WebSocket | null = null;\n private nextCommandId = 1;\n /** In-flight enableDomains() promise — concurrent callers share it. */\n private enablingPromise: Promise<void> | null = null;\n /** Pending request→response commands keyed by CDP message id. */\n private readonly pending = new Map<\n number,\n { resolve: (result: unknown) => void; reject: (err: Error) => void }\n >();\n\n constructor(options: ChiiCdpConnectionOptions) {\n this.relayBaseUrl = options.relayBaseUrl.replace(/\\/$/, '');\n this.bufferSize = options.bufferSize ?? DEFAULT_BUFFER_SIZE;\n for (const event of PHASE_1_EVENTS) this.buffers.set(event, []);\n // EventEmitter caps listeners at 10 by default; the tool layer may add\n // several short-lived subscriptions, so lift the cap.\n this.emitter.setMaxListeners(0);\n }\n\n /** Refresh the attached-target list from the relay's `GET /targets`. */\n async refreshTargets(): Promise<CdpTarget[]> {\n const res = await fetch(`${this.relayBaseUrl}/targets`);\n if (!res.ok) {\n throw new Error(`Chii relay /targets returned HTTP ${res.status} ${res.statusText}`);\n }\n const body: unknown = await res.json();\n const list = isObject(body) && Array.isArray(body.targets) ? body.targets : [];\n this.targets.clear();\n for (const item of list) {\n if (!isObject(item) || typeof item.id !== 'string') continue;\n this.targets.set(item.id, {\n id: item.id,\n title: typeof item.title === 'string' ? item.title : '',\n url: typeof item.url === 'string' ? item.url : '',\n });\n }\n return [...this.targets.values()];\n }\n\n listTargets(): CdpTarget[] {\n return [...this.targets.values()];\n }\n\n /**\n * Connect a client websocket to the first attached target and enable Phase 1\n * domains. Resolves once the socket is open and enable commands are sent.\n */\n async enableDomains(): Promise<void> {\n if (this.ws && this.ws.readyState === WebSocket.OPEN) return;\n // If a connect attempt is already in-flight, await it rather than racing\n // to open a second websocket that would overwrite `this.ws` and leak the first.\n if (this.enablingPromise) return this.enablingPromise;\n this.enablingPromise = this._doEnableDomains().finally(() => {\n this.enablingPromise = null;\n });\n return this.enablingPromise;\n }\n\n private async _doEnableDomains(): Promise<void> {\n const targets = await this.refreshTargets();\n const target = targets[0];\n if (!target) {\n throw new Error('No mini-app page attached to the Chii relay yet.');\n }\n\n const wsBase = this.relayBaseUrl.replace(/^http/, 'ws');\n const clientId = `devtools-mcp-${Date.now()}`;\n const ws = new WebSocket(\n `${wsBase}/client/${clientId}?target=${encodeURIComponent(target.id)}`,\n );\n this.ws = ws;\n\n await new Promise<void>((resolve, reject) => {\n ws.once('open', () => resolve());\n ws.once('error', (err: Error) => reject(err));\n });\n\n ws.on('message', (data: WebSocket.RawData) => this.handleMessage(data.toString()));\n\n this.sendFireAndForget('Runtime.enable');\n this.sendFireAndForget('Network.enable');\n // DOM/Page domains back the Phase 2 command tools; Chii answers their\n // request→response commands once enabled.\n this.sendFireAndForget('DOM.enable');\n this.sendFireAndForget('Page.enable');\n }\n\n /** Fire-and-forget CDP message (used for `*.enable`, no result awaited). */\n private sendFireAndForget(method: string, params: Record<string, unknown> = {}): void {\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return;\n const id = this.nextCommandId++;\n this.ws.send(JSON.stringify({ id, method, params }));\n }\n\n /**\n * Issue a CDP command and resolve with its result (Phase 2). Rejects on a CDP\n * error frame or when no websocket is open (no page attached yet).\n */\n send<M extends CdpCommandName>(\n method: M,\n params?: CdpCommandMap[M]['params'],\n ): Promise<CdpCommandMap[M]['result']> {\n return this.sendCommand(method, params ?? {}) as Promise<CdpCommandMap[M]['result']>;\n }\n\n /**\n * Issue an arbitrary request→response command over the relay and resolve with\n * its raw result. Both the typed CDP {@link send} and the AIT domain (Phase 3\n * `AIT.*` methods, forwarded over the same Chii channel) build on this.\n */\n sendCommand(method: string, params: Record<string, unknown> = {}): Promise<unknown> {\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n return Promise.reject(\n new Error('No mini-app page attached to the Chii relay yet. Call enableDomains() first.'),\n );\n }\n const id = this.nextCommandId++;\n const ws = this.ws;\n return new Promise<unknown>((resolve, reject) => {\n this.pending.set(id, { resolve, reject });\n ws.send(JSON.stringify({ id, method, params }));\n });\n }\n\n private handleMessage(raw: string): void {\n const message = parseInbound(raw);\n if (!message) return;\n\n // Command response (has an id matching a pending request).\n if (typeof message.id === 'number' && this.pending.has(message.id)) {\n const waiter = this.pending.get(message.id);\n this.pending.delete(message.id);\n if (waiter) {\n if (message.error) waiter.reject(new Error(message.error.message));\n else waiter.resolve(message.result);\n }\n return;\n }\n\n // Event (buffered for the Phase 1 stream tools).\n if (typeof message.method !== 'string') return;\n if (!this.buffers.has(message.method as CdpEventName)) return;\n const event = message.method as CdpEventName;\n const buffer = this.buffers.get(event);\n if (!buffer) return;\n buffer.push(message.params);\n if (buffer.length > this.bufferSize) buffer.shift();\n this.emitter.emit(event, message.params);\n }\n\n getBufferedEvents<E extends CdpEventName>(event: E): ReadonlyArray<CdpEventMap[E]> {\n const buffer = this.buffers.get(event);\n return (buffer ?? []) as ReadonlyArray<CdpEventMap[E]>;\n }\n\n on<E extends CdpEventName>(event: E, listener: (payload: CdpEventMap[E]) => void): () => void {\n this.emitter.on(event, listener as (payload: unknown) => void);\n return () => this.emitter.off(event, listener as (payload: unknown) => void);\n }\n\n /** Close the relay client websocket and reject any in-flight commands. */\n close(): void {\n this.ws?.close();\n this.ws = null;\n for (const waiter of this.pending.values()) {\n waiter.reject(new Error('Chii relay connection closed.'));\n }\n this.pending.clear();\n }\n}\n","/**\n * Boots the local Chii relay server.\n *\n * Chii (liriliri/chii) is a chobitsu-based CDP relay that lets non-Chrome\n * WebViews (iOS WKWebView / Android WebView — i.e. the Toss app) expose CDP.\n * The relay accepts a `target` websocket from the phone's injected `target.js`\n * and `client` websockets from CDP frontends (our MCP connection).\n *\n * Node-only: `chii` pulls in Koa + ws. Never bundled into the browser/in-app\n * entries.\n */\n\nimport { createServer, type Server } from 'node:http';\nimport { createRequire } from 'node:module';\n\nconst require = createRequire(import.meta.url);\n\n/** `chii/server` is CommonJS and shipped without TypeScript types. */\ninterface ChiiServerModule {\n start(options: {\n port?: number;\n host?: string;\n domain?: string;\n server?: Server;\n basePath?: string;\n }): Promise<void>;\n}\n\nfunction loadChiiServer(): ChiiServerModule {\n // `chii`'s package `main` is `./server/index.js`, exposing `{ start }`.\n const mod: unknown = require('chii');\n if (\n typeof mod === 'object' &&\n mod !== null &&\n 'start' in mod &&\n typeof (mod as { start: unknown }).start === 'function'\n ) {\n return mod as ChiiServerModule;\n }\n throw new Error('chii server module did not expose start()');\n}\n\nexport interface ChiiRelay {\n port: number;\n /** Base URL for the relay HTTP/WS server, e.g. `http://127.0.0.1:9100`. */\n baseUrl: string;\n close(): Promise<void>;\n}\n\nexport interface StartChiiRelayOptions {\n /** Local port for the relay. Default 9100. */\n port?: number;\n /** Bind host. Default 127.0.0.1 (tunnel reaches it locally). */\n host?: string;\n}\n\n/** Starts the Chii relay on the given port and resolves once listening. */\nexport async function startChiiRelay(options: StartChiiRelayOptions = {}): Promise<ChiiRelay> {\n const port = options.port ?? 9100;\n const host = options.host ?? '127.0.0.1';\n\n const httpServer = createServer();\n const chii = loadChiiServer();\n // Passing an existing `server` makes chii attach its Koa handler + WS upgrade\n // to our HTTP server rather than creating its own listener.\n await chii.start({ server: httpServer, domain: `${host}:${port}`, port });\n\n await new Promise<void>((resolve, reject) => {\n httpServer.once('error', reject);\n httpServer.listen(port, host, () => {\n httpServer.off('error', reject);\n resolve();\n });\n });\n\n return {\n port,\n baseUrl: `http://${host}:${port}`,\n close: () =>\n new Promise<void>((resolve) => {\n httpServer.close(() => resolve());\n }),\n };\n}\n","/**\n * Debug-mode MCP tools (Phase 1–3).\n *\n * Read-only tools that normalize CDP / AIT data into `chrome-devtools-mcp`-\n * compatible shapes. The tools never touch a websocket or HTTP endpoint\n * directly — they read from an injected `CdpConnection` (CDP events/commands)\n * or `AitSource` (AIT.* domain), which is what makes them unit-testable with a\n * fake. No phone and no running dev server are needed in tests.\n *\n * Phase 1 (CDP events):\n * - `list_console_messages` ← Runtime.consoleAPICalled\n * - `list_network_requests` ← Network.requestWillBeSent + responseReceived\n * - `list_pages` ← Chii relay target list + tunnel status\n * Phase 2 (CDP commands):\n * - `get_dom_document` ← DOM.getDocument\n * - `take_snapshot` ← DOMSnapshot.captureSnapshot\n * - `take_screenshot` ← Page.captureScreenshot\n * Phase 3 (AIT.* domain — CDP can't cover these):\n * - `AIT.getSdkCallHistory`\n * - `AIT.getMockState`\n * - `AIT.getOperationalEnvironment`\n */\n\nimport type {\n AitMockState,\n AitOperationalEnvironment,\n AitSdkCallHistory,\n AitSource,\n} from './ait-source.js';\nimport type {\n CdpConnection,\n CdpRemoteObject,\n ConsoleApiCalledEvent,\n DomGetDocumentResult,\n DomSnapshotResult,\n NetworkRequestWillBeSentEvent,\n NetworkResponseReceivedEvent,\n} from './cdp-connection.js';\n\n/** Tunnel state surfaced by `list_pages`. */\nexport interface TunnelStatus {\n /** Whether the cloudflared quick tunnel is up. */\n up: boolean;\n /** Public `wss://*.trycloudflare.com` relay URL the phone attaches to. */\n wssUrl: string | null;\n}\n\n/** Static MCP tool descriptors (name + JSONSchema) for the Phase 1 surface. */\nexport const DEBUG_TOOL_DEFINITIONS = [\n {\n name: 'list_console_messages',\n description:\n 'Lists recent console messages (console.log/warn/error/info) captured from the attached ' +\n 'mini-app page over CDP (Runtime.consoleAPICalled). Read-only. Returns level, text, ' +\n 'timestamp, and stringified args, oldest-first.',\n inputSchema: { type: 'object', properties: {}, required: [] },\n },\n {\n name: 'list_network_requests',\n description:\n 'Lists recent network requests (XHR/fetch) captured from the attached mini-app page over ' +\n 'CDP (Network.requestWillBeSent + Network.responseReceived). Read-only. Returns url, ' +\n 'method, status, and timing, oldest-first.',\n inputSchema: { type: 'object', properties: {}, required: [] },\n },\n {\n name: 'list_pages',\n description:\n 'Lists the mini-app page(s) the Chii relay currently sees attached, plus whether the ' +\n 'cloudflared tunnel is up and the public wss relay URL the phone uses to attach. ' +\n 'Call this first to confirm a page is attached before reading console/network.',\n inputSchema: { type: 'object', properties: {}, required: [] },\n },\n {\n name: 'get_dom_document',\n description:\n 'Returns the DOM tree of the attached mini-app page over CDP (DOM.getDocument). Read-only. ' +\n 'Use for structural/layout regression diagnosis (e.g. confirming an element exists, ' +\n 'inspecting attributes). Returns the document root node with children.',\n inputSchema: { type: 'object', properties: {}, required: [] },\n },\n {\n name: 'take_snapshot',\n description:\n 'Captures a serialized snapshot of the attached page over CDP (DOMSnapshot.captureSnapshot). ' +\n 'Read-only. Returns the documents + interned strings table for visual-regression diagnosis ' +\n '(e.g. checking computed CSS custom properties like --sat against the live layout).',\n inputSchema: { type: 'object', properties: {}, required: [] },\n },\n {\n name: 'take_screenshot',\n description:\n 'Captures a PNG screenshot of the attached mini-app page over CDP (Page.captureScreenshot) ' +\n 'so the agent can see the phone screen directly. Read-only. Returns an image content block.',\n inputSchema: { type: 'object', properties: {}, required: [] },\n },\n {\n name: 'AIT.getSdkCallHistory',\n description:\n 'Returns the recent Apps In Toss SDK call trace (method, args, result/error, timestamp) that ' +\n 'raw CDP cannot observe. Read-only. Use to confirm an SDK call fired and how it resolved ' +\n '(e.g. a saveBase64Data permission regression).',\n inputSchema: { type: 'object', properties: {}, required: [] },\n },\n {\n name: 'AIT.getMockState',\n description:\n 'Returns the devtools mock state snapshot (window.__ait) — environment, permissions, location, ' +\n 'auth, network, IAP, and more. Read-only. In dev mode this is the live browser mock state; in ' +\n 'debug mode the in-app side reports it over the AIT domain.',\n inputSchema: { type: 'object', properties: {}, required: [] },\n },\n {\n name: 'AIT.getOperationalEnvironment',\n description:\n 'Returns getOperationalEnvironment() plus the resolved SDK version — metadata raw CDP cannot ' +\n 'observe. Read-only.',\n inputSchema: { type: 'object', properties: {}, required: [] },\n },\n] as const;\n\nexport type DebugToolName = (typeof DEBUG_TOOL_DEFINITIONS)[number]['name'];\n\nconst DEBUG_TOOL_NAMES = new Set<string>(DEBUG_TOOL_DEFINITIONS.map((t) => t.name));\n\nexport function isDebugToolName(name: string): name is DebugToolName {\n return DEBUG_TOOL_NAMES.has(name);\n}\n\n/** Normalized console message returned by `list_console_messages`. */\nexport interface ConsoleMessage {\n level: string;\n text: string;\n timestamp: number;\n args: string[];\n}\n\n/** Normalized network request returned by `list_network_requests`. */\nexport interface NetworkRequest {\n requestId: string;\n url: string;\n method: string;\n /** HTTP status once a response was seen, else null (still in-flight). */\n status: number | null;\n statusText: string | null;\n /** Request start (CDP timestamp). */\n startTime: number;\n /** Response received (CDP timestamp), else null. */\n endTime: number | null;\n}\n\n/** Renders a CDP `RemoteObject` console arg to a stable display string. */\nfunction renderRemoteObject(arg: CdpRemoteObject): string {\n if (arg.value !== undefined) {\n if (typeof arg.value === 'string') return arg.value;\n try {\n return JSON.stringify(arg.value);\n } catch {\n return String(arg.value);\n }\n }\n if (arg.description !== undefined) return arg.description;\n if (arg.className !== undefined) return arg.className;\n return arg.subtype ?? arg.type;\n}\n\nexport function normalizeConsoleMessage(event: ConsoleApiCalledEvent): ConsoleMessage {\n const args = event.args.map(renderRemoteObject);\n return {\n level: event.type,\n text: args.join(' '),\n timestamp: event.timestamp,\n args,\n };\n}\n\nexport function listConsoleMessages(connection: CdpConnection): ConsoleMessage[] {\n return connection\n .getBufferedEvents('Runtime.consoleAPICalled')\n .map((event) => normalizeConsoleMessage(event));\n}\n\nexport function listNetworkRequests(connection: CdpConnection): NetworkRequest[] {\n const requests = connection.getBufferedEvents('Network.requestWillBeSent');\n const responses = connection.getBufferedEvents('Network.responseReceived');\n\n const responseByRequestId = new Map<string, NetworkResponseReceivedEvent>();\n for (const response of responses) {\n responseByRequestId.set(response.requestId, response);\n }\n\n return requests.map((request: NetworkRequestWillBeSentEvent) => {\n const response = responseByRequestId.get(request.requestId);\n return {\n requestId: request.requestId,\n url: request.request.url,\n method: request.request.method,\n status: response ? response.response.status : null,\n statusText: response ? response.response.statusText : null,\n startTime: request.timestamp,\n endTime: response ? response.timestamp : null,\n };\n });\n}\n\n/** Result of `list_pages`: attach status + tunnel state. */\nexport interface ListPagesResult {\n pages: ReturnType<CdpConnection['listTargets']>;\n tunnel: TunnelStatus;\n}\n\nexport function listPages(connection: CdpConnection, tunnel: TunnelStatus): ListPagesResult {\n return { pages: connection.listTargets(), tunnel };\n}\n\n/* -------------------------------------------------------------------------- */\n/* Phase 2 — DOM / snapshot / screenshot (CDP commands) */\n/* -------------------------------------------------------------------------- */\n\n/** Returns the DOM tree of the attached page (`DOM.getDocument`). */\nexport function getDomDocument(connection: CdpConnection): Promise<DomGetDocumentResult> {\n // `pierce: true` flattens shadow roots; depth -1 returns the whole subtree so\n // a single call yields the full tree for structural diagnosis.\n return connection.send('DOM.getDocument', { depth: -1, pierce: true });\n}\n\n/** Returns a serialized page snapshot (`DOMSnapshot.captureSnapshot`). */\nexport function takeSnapshot(connection: CdpConnection): Promise<DomSnapshotResult> {\n return connection.send('DOMSnapshot.captureSnapshot', {});\n}\n\n/** A `take_screenshot` result: the raw base64 PNG plus a ready-to-use data URI. */\nexport interface ScreenshotResult {\n /** Base64-encoded PNG bytes (no data-URI prefix). */\n data: string;\n /** `data:image/png;base64,…` form for clients that render a URI. */\n dataUri: string;\n mimeType: 'image/png';\n}\n\n/** Captures a PNG screenshot of the attached page (`Page.captureScreenshot`). */\nexport async function takeScreenshot(connection: CdpConnection): Promise<ScreenshotResult> {\n const { data } = await connection.send('Page.captureScreenshot', { format: 'png' });\n return { data, dataUri: `data:image/png;base64,${data}`, mimeType: 'image/png' };\n}\n\n/* -------------------------------------------------------------------------- */\n/* Phase 3 — AIT.* domain (CDP can't cover these) */\n/* -------------------------------------------------------------------------- */\n\n/** Set of tool names served by the AIT source rather than the CDP connection. */\nconst AIT_TOOL_NAMES = new Set<string>([\n 'AIT.getSdkCallHistory',\n 'AIT.getMockState',\n 'AIT.getOperationalEnvironment',\n]);\n\n/** True for the Phase 3 AIT.* tools (served by an `AitSource`, not CDP). */\nexport function isAitToolName(name: string): boolean {\n return AIT_TOOL_NAMES.has(name);\n}\n\n/** Returns the recent SDK call trace (`AIT.getSdkCallHistory`). */\nexport function getSdkCallHistory(source: AitSource): Promise<AitSdkCallHistory> {\n return source.get('AIT.getSdkCallHistory');\n}\n\n/** Returns the devtools mock-state snapshot (`AIT.getMockState`). */\nexport function getMockState(source: AitSource): Promise<AitMockState> {\n return source.get('AIT.getMockState');\n}\n\n/** Returns the operational environment + SDK version (`AIT.getOperationalEnvironment`). */\nexport function getOperationalEnvironment(source: AitSource): Promise<AitOperationalEnvironment> {\n return source.get('AIT.getOperationalEnvironment');\n}\n","/**\n * cloudflared quick tunnel + attach banner for the debug-mode MCP server.\n *\n * On spawn, the debug server opens an accountless `*.trycloudflare.com` quick\n * tunnel to the local Chii relay so the phone can attach over a public wss URL,\n * then prints that URL + a secret token + an ASCII QR to the terminal. The\n * phone scans the QR (or pastes the URL) to attach; the in-app side passes the\n * token back. Phase 1 only generates + displays the token and makes it\n * available — full ACL enforcement is a later phase.\n *\n * Node-only: spawns the cloudflared binary and writes to stdout/stderr.\n */\n\nimport { randomBytes } from 'node:crypto';\nimport { bin, install, Tunnel } from 'cloudflared';\nimport qrcode from 'qrcode-terminal';\n\n/** Generates a 32-byte hex secret token used to gate attach. */\nexport function generateAttachToken(): string {\n return randomBytes(32).toString('hex');\n}\n\nexport interface QuickTunnel {\n /** Public `https://*.trycloudflare.com` URL the tunnel exposes. */\n url: string;\n /** Same host as `wss://` — the relay endpoint the phone attaches to. */\n wssUrl: string;\n stop(): void;\n}\n\n/** Ensures the cloudflared binary is installed (downloads + caches on first run). */\nasync function ensureCloudflaredBin(): Promise<void> {\n const { existsSync } = await import('node:fs');\n if (!existsSync(bin)) {\n await install(bin);\n }\n}\n\n/**\n * Opens a cloudflared quick tunnel to the local relay port and resolves once\n * the public URL is assigned.\n */\nexport async function startQuickTunnel(localPort: number): Promise<QuickTunnel> {\n await ensureCloudflaredBin();\n\n const tunnel = Tunnel.quick(`http://127.0.0.1:${localPort}`);\n\n const url = await new Promise<string>((resolve, reject) => {\n const onUrl = (assigned: string) => {\n cleanup();\n resolve(assigned);\n };\n const onError = (err: Error) => {\n cleanup();\n reject(err);\n };\n const onExit = (code: number | null) => {\n cleanup();\n reject(new Error(`cloudflared exited before assigning a URL (code ${code})`));\n };\n const cleanup = () => {\n tunnel.off('url', onUrl);\n tunnel.off('error', onError);\n tunnel.off('exit', onExit);\n };\n tunnel.once('url', onUrl);\n tunnel.once('error', onError);\n tunnel.once('exit', onExit);\n });\n\n return {\n url,\n wssUrl: url.replace(/^https/, 'wss'),\n stop: () => {\n tunnel.stop();\n },\n };\n}\n\nexport interface AttachBannerInput {\n wssUrl: string;\n token: string;\n}\n\n/** Renders the attach banner (URL + token + ASCII QR) as a string. */\nexport async function renderAttachBanner(input: AttachBannerInput): Promise<string> {\n // Encode the attach payload as a URL so a QR scan opens directly.\n const payload = `${input.wssUrl}?token=${input.token}`;\n const qr = await new Promise<string>((resolve) => {\n qrcode.generate(payload, { small: true }, (rendered) => resolve(rendered));\n });\n return [\n '',\n 'AIT debug — attach a mini-app to this session',\n '',\n ` relay (wss): ${input.wssUrl}`,\n ` token: ${input.token}`,\n '',\n ' Open the dogfood mini-app with ?debug=1, then scan the QR',\n ' (or paste the relay URL + token in the in-app attach form):',\n '',\n qr,\n ].join('\\n');\n}\n\n/** Prints the attach banner to stderr (stdout is the MCP stdio channel). */\nexport async function printAttachBanner(input: AttachBannerInput): Promise<void> {\n const banner = await renderAttachBanner(input);\n process.stderr.write(`${banner}\\n`);\n}\n","/**\n * @ait-co/devtools debug-mode MCP server (stdio) — Phase 1–3.\n *\n * Lets an AI coding agent attach to a running mini-app (real Toss WebView, or a\n * browser in dev mode) and read its console/network/DOM/screenshot over CDP plus\n * the AIT.* domain, without a human watching a phone. Transport is CDP-via-Chii:\n * a local Chii relay :9100 exposed through a cloudflared quick tunnel; the phone\n * attaches over the public wss URL.\n *\n * AI host --stdio--> this server --CDP client WS--> Chii relay :9100\n * ^-- target WS -- phone\n *\n * The tool layer reads from an injectable `CdpConnection` (CDP) and `AitSource`\n * (AIT.*), so every tool is unit-testable with a fake (no phone). This module\n * wires the live pieces (relay + tunnel + production connection); the phone\n * roundtrip itself is phone-gated and deferred.\n *\n * Node-only.\n */\n\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';\nimport { ChiiAitSource } from './ait-chii-source.js';\nimport type { AitSource } from './ait-source.js';\nimport type { CdpConnection } from './cdp-connection.js';\nimport { ChiiCdpConnection } from './chii-connection.js';\nimport { startChiiRelay } from './chii-relay.js';\nimport {\n DEBUG_TOOL_DEFINITIONS,\n getDomDocument,\n getMockState,\n getOperationalEnvironment,\n getSdkCallHistory,\n isAitToolName,\n isDebugToolName,\n listConsoleMessages,\n listNetworkRequests,\n listPages,\n type TunnelStatus,\n takeScreenshot,\n takeSnapshot,\n} from './tools.js';\nimport {\n generateAttachToken,\n printAttachBanner,\n type QuickTunnel,\n startQuickTunnel,\n} from './tunnel.js';\n\n/** Live infra the connection reads tunnel status from. */\nexport interface DebugServerDeps {\n connection: CdpConnection;\n /** AIT.* domain source — forwarded over the same Chii channel in production. */\n aitSource: AitSource;\n /** Returns current tunnel status (URL changes per spawn). */\n getTunnelStatus(): TunnelStatus;\n}\n\n/**\n * Builds the debug-mode MCP server around an injected CDP connection + AIT\n * source + tunnel status getter. Pure wiring — does not start a relay or\n * tunnel, which is what makes the tool surface unit-testable.\n */\nexport function createDebugServer(deps: DebugServerDeps): Server {\n const { connection, aitSource, getTunnelStatus } = deps;\n\n const server = new Server(\n { name: 'ait-debug', version: __VERSION__ },\n { capabilities: { tools: {} } },\n );\n\n server.setRequestHandler(ListToolsRequestSchema, () => ({\n tools: DEBUG_TOOL_DEFINITIONS.map((tool) => ({ ...tool })),\n }));\n\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const name = request.params.name;\n if (!isDebugToolName(name)) {\n return {\n content: [{ type: 'text', text: `Unknown tool: ${name}` }],\n isError: true,\n };\n }\n\n // AIT.* tools are served by the AIT source. In production it rides the same\n // Chii websocket as CDP, so the connection must be attached first; the AIT\n // source's sendCommand rejects with a clear message if no page is attached.\n if (isAitToolName(name)) {\n try {\n await connection.enableDomains();\n switch (name) {\n case 'AIT.getSdkCallHistory':\n return jsonResult(await getSdkCallHistory(aitSource));\n case 'AIT.getMockState':\n return jsonResult(await getMockState(aitSource));\n case 'AIT.getOperationalEnvironment':\n return jsonResult(await getOperationalEnvironment(aitSource));\n default:\n return unknownTool(name);\n }\n } catch (err) {\n return errorResult(err, name);\n }\n }\n\n try {\n // Ensure CDP domains are enabled before reading. No-op once attached;\n // throws a clear message while no page is attached yet.\n await connection.enableDomains();\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n if (name === 'list_pages') {\n // list_pages is still useful pre-attach: report tunnel + empty pages.\n return jsonResult(listPages(connection, getTunnelStatus()));\n }\n return {\n content: [\n {\n type: 'text',\n text: `${message}\\nCall list_pages to confirm a mini-app has attached over the relay.`,\n },\n ],\n isError: true,\n };\n }\n\n try {\n switch (name) {\n case 'list_console_messages':\n return jsonResult(listConsoleMessages(connection));\n case 'list_network_requests':\n return jsonResult(listNetworkRequests(connection));\n case 'list_pages':\n return jsonResult(listPages(connection, getTunnelStatus()));\n case 'get_dom_document':\n return jsonResult(await getDomDocument(connection));\n case 'take_snapshot':\n return jsonResult(await takeSnapshot(connection));\n case 'take_screenshot': {\n const shot = await takeScreenshot(connection);\n return {\n content: [{ type: 'image' as const, data: shot.data, mimeType: shot.mimeType }],\n };\n }\n default:\n return unknownTool(name);\n }\n } catch (err) {\n return errorResult(err, name);\n }\n });\n\n return server;\n}\n\nfunction jsonResult(value: unknown) {\n return { content: [{ type: 'text' as const, text: JSON.stringify(value, null, 2) }] };\n}\n\nfunction unknownTool(name: string) {\n return { content: [{ type: 'text' as const, text: `Unknown tool: ${name}` }], isError: true };\n}\n\nfunction errorResult(err: unknown, name: string) {\n const message = err instanceof Error ? err.message : String(err);\n return {\n content: [\n {\n type: 'text' as const,\n text: `${name} failed: ${message}\\nCall list_pages to confirm a mini-app has attached over the relay.`,\n },\n ],\n isError: true,\n };\n}\n\nexport interface RunDebugServerOptions {\n /** Local Chii relay port. Default 9100. */\n relayPort?: number;\n}\n\n/**\n * Boots the live debug stack and serves it over stdio:\n * 1. start the Chii relay,\n * 2. open a cloudflared quick tunnel to it,\n * 3. print QR + secret token,\n * 4. expose the debug tools backed by a `ChiiCdpConnection` + `ChiiAitSource`.\n */\nexport async function runDebugServer(options: RunDebugServerOptions = {}): Promise<void> {\n const relayPort = options.relayPort ?? 9100;\n\n const relay = await startChiiRelay({ port: relayPort });\n\n let tunnel: QuickTunnel | null = null;\n let tunnelStatus: TunnelStatus = { up: false, wssUrl: null };\n const token = generateAttachToken();\n\n try {\n tunnel = await startQuickTunnel(relayPort);\n tunnelStatus = { up: true, wssUrl: tunnel.wssUrl };\n await printAttachBanner({ wssUrl: tunnel.wssUrl, token });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n process.stderr.write(\n `[ait-debug] Failed to open cloudflared quick tunnel: ${message}\\n` +\n '[ait-debug] The relay is up locally; attach over the public URL is unavailable until the tunnel starts.\\n',\n );\n }\n\n const connection = new ChiiCdpConnection({ relayBaseUrl: relay.baseUrl });\n // AIT.* methods ride the same Chii channel as CDP commands.\n const aitSource = new ChiiAitSource(connection);\n const server = createDebugServer({\n connection,\n aitSource,\n getTunnelStatus: () => tunnelStatus,\n });\n\n const transport = new StdioServerTransport();\n\n const shutdown = () => {\n connection.close();\n tunnel?.stop();\n void relay.close();\n void server.close();\n };\n process.once('SIGINT', shutdown);\n process.once('SIGTERM', shutdown);\n\n await server.connect(transport);\n}\n","/**\n * Dev-mode `AitSource` — backed by the Vite dev server's mock-state endpoint.\n *\n * The dev server already exposes the live browser mock state at\n * `GET /api/ait-devtools/state` (registered by the unplugin with `mcp: true`).\n * Phase 3 aligns dev mode and debug mode on the same `AIT.*` tool surface, so\n * dev mode serves those tools off this one HTTP source instead of a CDP channel:\n *\n * - `AIT.getMockState` → the full state snapshot (verbatim).\n * - `AIT.getOperationalEnvironment` → derived from the snapshot's\n * `environment` + `appVersion` fields.\n * - `AIT.getSdkCallHistory` → empty (the dev endpoint does not record\n * an SDK call trace — honest, not faked).\n *\n * An AI agent thus sees the same `AIT.getMockState` tool whether attached to a\n * phone (debug) or a dev browser (dev). Tests inject a fake `fetch`.\n */\n\nimport type {\n AitMethodMap,\n AitMethodName,\n AitMockState,\n AitOperationalEnvironment,\n AitSdkCallHistory,\n AitSource,\n} from './ait-source.js';\n\n/** Minimal `fetch` shape this source needs (injectable in tests). */\nexport type FetchLike = (url: string) => Promise<{\n ok: boolean;\n status: number;\n statusText: string;\n json(): Promise<unknown>;\n}>;\n\nexport interface HttpAitSourceOptions {\n /** Full URL of the mock-state endpoint, e.g. `http://localhost:5173/api/ait-devtools/state`. */\n stateEndpoint: string;\n /** Injected for tests; defaults to global `fetch`. */\n fetchImpl?: FetchLike;\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nexport class HttpAitSource implements AitSource {\n private readonly stateEndpoint: string;\n private readonly fetchImpl: FetchLike;\n\n constructor(options: HttpAitSourceOptions) {\n this.stateEndpoint = options.stateEndpoint;\n this.fetchImpl = options.fetchImpl ?? ((url) => fetch(url));\n }\n\n private async fetchState(): Promise<AitMockState> {\n const res = await this.fetchImpl(this.stateEndpoint);\n if (!res.ok) {\n throw new Error(\n `Failed to fetch mock state from ${this.stateEndpoint}: HTTP ${res.status} ${res.statusText}. ` +\n 'Ensure the Vite dev server is running with the @ait-co/devtools unplugin option `mcp: true`.',\n );\n }\n const body = await res.json();\n return isObject(body) ? body : {};\n }\n\n async get<M extends AitMethodName>(method: M): Promise<AitMethodMap[M]> {\n switch (method) {\n case 'AIT.getMockState': {\n const state = await this.fetchState();\n return state as AitMethodMap[M];\n }\n case 'AIT.getOperationalEnvironment': {\n const state = await this.fetchState();\n const environment = typeof state.environment === 'string' ? state.environment : 'unknown';\n const sdkVersion = typeof state.appVersion === 'string' ? state.appVersion : null;\n const result: AitOperationalEnvironment = { environment, sdkVersion };\n return result as AitMethodMap[M];\n }\n case 'AIT.getSdkCallHistory': {\n // Dev endpoint records no SDK call trace; return empty rather than fake.\n const result: AitSdkCallHistory = { calls: [] };\n return result as AitMethodMap[M];\n }\n default:\n throw new Error(`Unknown AIT method: ${String(method)}`);\n }\n }\n}\n","/**\n * @ait-co/devtools dev-mode MCP server (stdio).\n *\n * Exposes the live browser mock state from a running Vite dev server to AI\n * coding agents via the Model Context Protocol (MCP).\n *\n * Architecture:\n * Browser (aitState) → Vite dev server endpoint (/api/ait-devtools/state)\n * ← HTTP GET ← this stdio MCP server ← AI agent\n *\n * The Vite endpoint is registered by the unplugin when `mcp: true` is set in\n * the plugin options (see `src/unplugin/index.ts`).\n *\n * Phase 3 tool-surface alignment: dev mode and debug mode now expose the same\n * `AIT.*` tools (`AIT.getMockState`, `AIT.getOperationalEnvironment`,\n * `AIT.getSdkCallHistory`). In dev mode they are backed by the HTTP mock-state\n * endpoint (see `HttpAitSource`); in debug mode by the Chii channel. So an AI\n * sees a coherent tool whether attached to a phone (debug) or a dev browser\n * (dev). `devtools_get_mock_state` (the original devtools#130 name) is kept as a\n * backward-compatible alias of `AIT.getMockState`.\n *\n * This module is reached via the `devtools-mcp --mode=dev` CLI entry (see\n * `cli.ts`); the default (no flag) bin mode is the debug-mode CDP/Chii server.\n *\n * Usage (in your MCP client config, e.g. Claude Desktop):\n * {\n * \"mcpServers\": {\n * \"ait-devtools\": {\n * \"command\": \"pnpm\",\n * \"args\": [\"exec\", \"devtools-mcp\", \"--mode=dev\"],\n * \"env\": { \"AIT_DEVTOOLS_URL\": \"http://localhost:5173\" }\n * }\n * }\n * }\n */\n\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';\nimport { HttpAitSource } from './ait-http-source.js';\nimport type { AitSource } from './ait-source.js';\nimport {\n getMockState,\n getOperationalEnvironment,\n getSdkCallHistory,\n isAitToolName,\n} from './tools.js';\n\n/** Tool descriptors served by the dev-mode server. */\nconst DEV_TOOL_DEFINITIONS = [\n {\n name: 'AIT.getMockState',\n description:\n 'Returns the devtools mock state snapshot (window.__ait) from the running browser session — ' +\n 'environment, permissions, location, auth, network, IAP, and more. Read-only. ' +\n 'Requires the Vite dev server running with the @ait-co/devtools unplugin option `mcp: true`. ' +\n 'Same tool as in debug mode, where the in-app side reports it over the AIT domain.',\n inputSchema: { type: 'object', properties: {}, required: [] },\n },\n {\n name: 'AIT.getOperationalEnvironment',\n description:\n 'Returns the operational environment + SDK/app version derived from the dev mock state. ' +\n 'Read-only.',\n inputSchema: { type: 'object', properties: {}, required: [] },\n },\n {\n name: 'AIT.getSdkCallHistory',\n description:\n 'Returns the SDK call trace. In dev mode the HTTP mock-state endpoint records no trace, so ' +\n 'this returns an empty list; in debug mode it is populated over the AIT domain. Read-only.',\n inputSchema: { type: 'object', properties: {}, required: [] },\n },\n {\n name: 'devtools_get_mock_state',\n description:\n 'Backward-compatible alias of AIT.getMockState (the original devtools#130 name). Returns the ' +\n 'current AIT DevTools mock state snapshot. Read-only. Prefer AIT.getMockState in new configs.',\n inputSchema: { type: 'object', properties: {}, required: [] },\n },\n] as const;\n\nconst DEV_TOOL_NAMES = new Set<string>(DEV_TOOL_DEFINITIONS.map((t) => t.name));\n\nexport interface CreateDevServerDeps {\n /** AIT source for the dev tools. Defaults to an HTTP source over the dev server. */\n aitSource?: AitSource;\n}\n\n/** Builds the dev-mode MCP server (does not connect a transport). */\nexport function createDevServer(deps: CreateDevServerDeps = {}): Server {\n const devtoolsUrl = process.env.AIT_DEVTOOLS_URL ?? 'http://localhost:5173';\n const stateEndpoint = `${devtoolsUrl}/api/ait-devtools/state`;\n const aitSource = deps.aitSource ?? new HttpAitSource({ stateEndpoint });\n\n const server = new Server(\n { name: 'ait-devtools', version: __VERSION__ },\n { capabilities: { tools: {} } },\n );\n\n server.setRequestHandler(ListToolsRequestSchema, () => ({\n tools: DEV_TOOL_DEFINITIONS.map((tool) => ({ ...tool })),\n }));\n\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const name = request.params.name;\n if (!DEV_TOOL_NAMES.has(name)) {\n return { content: [{ type: 'text', text: `Unknown tool: ${name}` }], isError: true };\n }\n\n try {\n // `devtools_get_mock_state` is an alias of `AIT.getMockState`.\n const effective = name === 'devtools_get_mock_state' ? 'AIT.getMockState' : name;\n if (!isAitToolName(effective)) {\n return { content: [{ type: 'text', text: `Unknown tool: ${name}` }], isError: true };\n }\n switch (effective) {\n case 'AIT.getMockState':\n return jsonResult(await getMockState(aitSource));\n case 'AIT.getOperationalEnvironment':\n return jsonResult(await getOperationalEnvironment(aitSource));\n case 'AIT.getSdkCallHistory':\n return jsonResult(await getSdkCallHistory(aitSource));\n default:\n return { content: [{ type: 'text', text: `Unknown tool: ${name}` }], isError: true };\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return {\n content: [\n {\n type: 'text',\n text:\n `${message}\\n` +\n 'Is the Vite dev server running with the @ait-co/devtools unplugin option `mcp: true`? ' +\n 'Is AIT_DEVTOOLS_URL set correctly?',\n },\n ],\n isError: true,\n };\n }\n });\n\n return server;\n}\n\nfunction jsonResult(value: unknown) {\n return { content: [{ type: 'text' as const, text: JSON.stringify(value, null, 2) }] };\n}\n\n/** Builds the dev-mode server and connects it over stdio. */\nexport async function runDevServer(): Promise<void> {\n const server = createDevServer();\n const transport = new StdioServerTransport();\n await server.connect(transport);\n}\n","#!/usr/bin/env node\n/**\n * `devtools-mcp` bin entry.\n *\n * Single bin, two transports selected by `--mode`:\n * - (default, no flag) debug mode — CDP/Chii relay + cloudflared quick tunnel.\n * Attach a running mini-app (real Toss WebView or a browser) and read its\n * console + network over CDP without a human watching a phone.\n * - `--mode=dev` — dev mode — reads the live browser mock state from a running\n * Vite dev server (the devtools#130 `devtools_get_mock_state` surface).\n *\n * Node-only stdio process.\n */\n\nimport { argv } from 'node:process';\nimport { fileURLToPath } from 'node:url';\nimport { runDebugServer } from './debug-server.js';\nimport { runDevServer } from './server.js';\n\ntype Mode = 'debug' | 'dev';\n\n/** Parses `--mode=<value>` / `--mode <value>` from argv; default `debug`. */\nexport function parseMode(argv: readonly string[]): Mode {\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i];\n if (arg === undefined) continue;\n if (arg.startsWith('--mode=')) {\n return normalizeMode(arg.slice('--mode='.length));\n }\n if (arg === '--mode') {\n const next = argv[i + 1];\n if (next === undefined) {\n throw new Error(\"--mode requires a value: 'debug' (default) or 'dev'.\");\n }\n return normalizeMode(next);\n }\n }\n return 'debug';\n}\n\nfunction normalizeMode(value: string): Mode {\n if (value === 'dev') return 'dev';\n if (value === 'debug') return 'debug';\n throw new Error(`Unknown --mode '${value}'. Expected 'debug' (default) or 'dev'.`);\n}\n\nasync function main(): Promise<void> {\n const mode = parseMode(process.argv.slice(2));\n if (mode === 'dev') {\n await runDevServer();\n } else {\n await runDebugServer();\n }\n}\n\n/** True when this file is the process entry (the bin), not an import. */\nfunction isEntrypoint(): boolean {\n const entry = argv[1];\n if (entry === undefined) return false;\n try {\n return fileURLToPath(import.meta.url) === entry;\n } catch {\n return false;\n }\n}\n\nif (isEntrypoint()) {\n main().catch((err: unknown) => {\n const message = err instanceof Error ? err.message : String(err);\n process.stderr.write(`[devtools-mcp] fatal: ${message}\\n`);\n process.exitCode = 1;\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","names":["isObject","isObject","jsonResult"],"sources":["../../src/mcp/ait-chii-source.ts","../../src/mcp/chii-connection.ts","../../src/mcp/chii-relay.ts","../../src/mcp/tools.ts","../../src/mcp/tunnel.ts","../../src/mcp/debug-server.ts","../../src/mcp/ait-http-source.ts","../../src/mcp/server.ts","../../src/mcp/cli.ts"],"sourcesContent":["/**\n * Debug-mode `AitSource` — forwards `AIT.*` methods over the Chii channel.\n *\n * The AIT domain (`AIT.getSdkCallHistory` / `getMockState` /\n * `getOperationalEnvironment`) is non-standard CDP: the in-app side registers a\n * handler for these methods and answers them over the same Chii websocket the\n * CDP commands use. Building the AIT source on `ChiiCdpConnection.sendCommand`\n * means both domains share one transport (spec: \"the same MCP server forwards\n * both CDP and AIT domains\").\n *\n * The in-app `AIT.*` handler lives downstream in sdk-example. Here we build\n * the MCP-server-side forwarding + the injectable seam; tests inject a fake\n * `AitSource` returning canned responses, so this forwarding layer needs no\n * phone.\n *\n * Node-only (wraps the relay websocket connection).\n */\n\nimport type {\n AitMethodMap,\n AitMethodName,\n AitMockState,\n AitOperationalEnvironment,\n AitSdkCallHistory,\n AitSource,\n} from './ait-source.js';\n\n/** The slice of `ChiiCdpConnection` this source needs (keeps it testable). */\nexport interface AitCommandSender {\n sendCommand(method: string, params?: Record<string, unknown>): Promise<unknown>;\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\n/** Narrows an `AIT.getSdkCallHistory` response, tolerating a missing array. */\nfunction asSdkCallHistory(raw: unknown): AitSdkCallHistory {\n if (isObject(raw) && Array.isArray(raw.calls)) {\n return { calls: raw.calls as AitSdkCallHistory['calls'] };\n }\n return { calls: [] };\n}\n\n/** Narrows an `AIT.getMockState` response to an opaque record. */\nfunction asMockState(raw: unknown): AitMockState {\n return isObject(raw) ? raw : {};\n}\n\n/** Narrows an `AIT.getOperationalEnvironment` response. */\nfunction asOperationalEnvironment(raw: unknown): AitOperationalEnvironment {\n const environment =\n isObject(raw) && typeof raw.environment === 'string' ? raw.environment : 'unknown';\n const sdkVersion = isObject(raw) && typeof raw.sdkVersion === 'string' ? raw.sdkVersion : null;\n return { environment, sdkVersion };\n}\n\nexport class ChiiAitSource implements AitSource {\n constructor(private readonly sender: AitCommandSender) {}\n\n async get<M extends AitMethodName>(method: M): Promise<AitMethodMap[M]> {\n const raw = await this.sender.sendCommand(method);\n // The map's value type is resolved per-key below; the cast is the single\n // narrowing point (each branch returns the precise shape for `method`).\n switch (method) {\n case 'AIT.getSdkCallHistory':\n return asSdkCallHistory(raw) as AitMethodMap[M];\n case 'AIT.getMockState':\n return asMockState(raw) as AitMethodMap[M];\n case 'AIT.getOperationalEnvironment':\n return asOperationalEnvironment(raw) as AitMethodMap[M];\n default:\n throw new Error(`Unknown AIT method: ${String(method)}`);\n }\n }\n}\n","/**\n * Production `CdpConnection` backed by the local Chii relay.\n *\n * Topology (debug mode):\n * phone target.js --WS--> Chii relay :9100 <--WS-- this connection\n *\n * The phone connects to the relay as a `target`; this module connects as a\n * `client` (the role a CDP frontend would take) so CDP events the page emits\n * (`Runtime.consoleAPICalled`, `Network.*`) flow back here. We buffer recent\n * events in ring buffers the tool layer reads via `getBufferedEvents`.\n *\n * Node-only: imports `ws`. Never bundled into the browser/in-app entries.\n */\n\nimport { EventEmitter } from 'node:events';\nimport { WebSocket } from 'ws';\nimport type {\n CdpCommandMap,\n CdpCommandName,\n CdpConnection,\n CdpEventMap,\n CdpEventName,\n CdpTarget,\n} from './cdp-connection.js';\n\n/** Max events retained per domain ring buffer. */\nconst DEFAULT_BUFFER_SIZE = 500;\n\n/** A CDP message arriving over the relay websocket. */\ninterface CdpInboundMessage {\n id?: number;\n method?: string;\n params?: unknown;\n result?: unknown;\n error?: { message: string };\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction parseInbound(raw: string): CdpInboundMessage | null {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return null;\n }\n if (!isObject(parsed)) return null;\n const message: CdpInboundMessage = {};\n if (typeof parsed.id === 'number') message.id = parsed.id;\n if (typeof parsed.method === 'string') message.method = parsed.method;\n if ('params' in parsed) message.params = parsed.params;\n if ('result' in parsed) message.result = parsed.result;\n if (isObject(parsed.error) && typeof parsed.error.message === 'string') {\n message.error = { message: parsed.error.message };\n }\n return message;\n}\n\nconst PHASE_1_EVENTS: readonly CdpEventName[] = [\n 'Runtime.consoleAPICalled',\n 'Network.requestWillBeSent',\n 'Network.responseReceived',\n];\n\nexport interface ChiiCdpConnectionOptions {\n /** Base URL of the local Chii relay HTTP/WS server, e.g. `http://127.0.0.1:9100`. */\n relayBaseUrl: string;\n /** Per-domain ring buffer size. */\n bufferSize?: number;\n}\n\n/**\n * Production CDP connection. Polls the relay for the first attached target,\n * opens a client websocket to it, enables Phase 1 domains, and buffers events.\n */\nexport class ChiiCdpConnection implements CdpConnection {\n private readonly relayBaseUrl: string;\n private readonly bufferSize: number;\n private readonly emitter = new EventEmitter();\n private readonly buffers = new Map<CdpEventName, unknown[]>();\n private readonly targets = new Map<string, CdpTarget>();\n\n private ws: WebSocket | null = null;\n private nextCommandId = 1;\n /** In-flight enableDomains() promise — concurrent callers share it. */\n private enablingPromise: Promise<void> | null = null;\n /** Pending request→response commands keyed by CDP message id. */\n private readonly pending = new Map<\n number,\n { resolve: (result: unknown) => void; reject: (err: Error) => void }\n >();\n\n constructor(options: ChiiCdpConnectionOptions) {\n this.relayBaseUrl = options.relayBaseUrl.replace(/\\/$/, '');\n this.bufferSize = options.bufferSize ?? DEFAULT_BUFFER_SIZE;\n for (const event of PHASE_1_EVENTS) this.buffers.set(event, []);\n // EventEmitter caps listeners at 10 by default; the tool layer may add\n // several short-lived subscriptions, so lift the cap.\n this.emitter.setMaxListeners(0);\n }\n\n /** Refresh the attached-target list from the relay's `GET /targets`. */\n async refreshTargets(): Promise<CdpTarget[]> {\n const res = await fetch(`${this.relayBaseUrl}/targets`);\n if (!res.ok) {\n throw new Error(`Chii relay /targets returned HTTP ${res.status} ${res.statusText}`);\n }\n const body: unknown = await res.json();\n const list = isObject(body) && Array.isArray(body.targets) ? body.targets : [];\n this.targets.clear();\n for (const item of list) {\n if (!isObject(item) || typeof item.id !== 'string') continue;\n this.targets.set(item.id, {\n id: item.id,\n title: typeof item.title === 'string' ? item.title : '',\n url: typeof item.url === 'string' ? item.url : '',\n });\n }\n return [...this.targets.values()];\n }\n\n listTargets(): CdpTarget[] {\n return [...this.targets.values()];\n }\n\n /**\n * Connect a client websocket to the first attached target and enable Phase 1\n * domains. Resolves once the socket is open and enable commands are sent.\n */\n async enableDomains(): Promise<void> {\n if (this.ws && this.ws.readyState === WebSocket.OPEN) return;\n // If a connect attempt is already in-flight, await it rather than racing\n // to open a second websocket that would overwrite `this.ws` and leak the first.\n if (this.enablingPromise) return this.enablingPromise;\n this.enablingPromise = this._doEnableDomains().finally(() => {\n this.enablingPromise = null;\n });\n return this.enablingPromise;\n }\n\n private async _doEnableDomains(): Promise<void> {\n const targets = await this.refreshTargets();\n const target = targets[0];\n if (!target) {\n throw new Error('No mini-app page attached to the Chii relay yet.');\n }\n\n const wsBase = this.relayBaseUrl.replace(/^http/, 'ws');\n const clientId = `devtools-mcp-${Date.now()}`;\n const ws = new WebSocket(\n `${wsBase}/client/${clientId}?target=${encodeURIComponent(target.id)}`,\n );\n this.ws = ws;\n\n await new Promise<void>((resolve, reject) => {\n ws.once('open', () => resolve());\n ws.once('error', (err: Error) => reject(err));\n });\n\n ws.on('message', (data: WebSocket.RawData) => this.handleMessage(data.toString()));\n\n this.sendFireAndForget('Runtime.enable');\n this.sendFireAndForget('Network.enable');\n // DOM/Page domains back the Phase 2 command tools; Chii answers their\n // request→response commands once enabled.\n this.sendFireAndForget('DOM.enable');\n this.sendFireAndForget('Page.enable');\n }\n\n /** Fire-and-forget CDP message (used for `*.enable`, no result awaited). */\n private sendFireAndForget(method: string, params: Record<string, unknown> = {}): void {\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return;\n const id = this.nextCommandId++;\n this.ws.send(JSON.stringify({ id, method, params }));\n }\n\n /**\n * Issue a CDP command and resolve with its result (Phase 2). Rejects on a CDP\n * error frame or when no websocket is open (no page attached yet).\n */\n send<M extends CdpCommandName>(\n method: M,\n params?: CdpCommandMap[M]['params'],\n ): Promise<CdpCommandMap[M]['result']> {\n return this.sendCommand(method, params ?? {}) as Promise<CdpCommandMap[M]['result']>;\n }\n\n /**\n * Issue an arbitrary request→response command over the relay and resolve with\n * its raw result. Both the typed CDP {@link send} and the AIT domain (Phase 3\n * `AIT.*` methods, forwarded over the same Chii channel) build on this.\n */\n sendCommand(method: string, params: Record<string, unknown> = {}): Promise<unknown> {\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n return Promise.reject(\n new Error('No mini-app page attached to the Chii relay yet. Call enableDomains() first.'),\n );\n }\n const id = this.nextCommandId++;\n const ws = this.ws;\n return new Promise<unknown>((resolve, reject) => {\n this.pending.set(id, { resolve, reject });\n ws.send(JSON.stringify({ id, method, params }));\n });\n }\n\n private handleMessage(raw: string): void {\n const message = parseInbound(raw);\n if (!message) return;\n\n // Command response (has an id matching a pending request).\n if (typeof message.id === 'number' && this.pending.has(message.id)) {\n const waiter = this.pending.get(message.id);\n this.pending.delete(message.id);\n if (waiter) {\n if (message.error) waiter.reject(new Error(message.error.message));\n else waiter.resolve(message.result);\n }\n return;\n }\n\n // Event (buffered for the Phase 1 stream tools).\n if (typeof message.method !== 'string') return;\n if (!this.buffers.has(message.method as CdpEventName)) return;\n const event = message.method as CdpEventName;\n const buffer = this.buffers.get(event);\n if (!buffer) return;\n buffer.push(message.params);\n if (buffer.length > this.bufferSize) buffer.shift();\n this.emitter.emit(event, message.params);\n }\n\n getBufferedEvents<E extends CdpEventName>(event: E): ReadonlyArray<CdpEventMap[E]> {\n const buffer = this.buffers.get(event);\n return (buffer ?? []) as ReadonlyArray<CdpEventMap[E]>;\n }\n\n on<E extends CdpEventName>(event: E, listener: (payload: CdpEventMap[E]) => void): () => void {\n this.emitter.on(event, listener as (payload: unknown) => void);\n return () => this.emitter.off(event, listener as (payload: unknown) => void);\n }\n\n /** Close the relay client websocket and reject any in-flight commands. */\n close(): void {\n this.ws?.close();\n this.ws = null;\n for (const waiter of this.pending.values()) {\n waiter.reject(new Error('Chii relay connection closed.'));\n }\n this.pending.clear();\n }\n}\n","/**\n * Boots the local Chii relay server.\n *\n * Chii (liriliri/chii) is a chobitsu-based CDP relay that lets non-Chrome\n * WebViews (iOS WKWebView / Android WebView — i.e. the Toss app) expose CDP.\n * The relay accepts a `target` websocket from the phone's injected `target.js`\n * and `client` websockets from CDP frontends (our MCP connection).\n *\n * Node-only: `chii` pulls in Koa + ws. Never bundled into the browser/in-app\n * entries.\n */\n\nimport { createServer, type Server } from 'node:http';\nimport { createRequire } from 'node:module';\n\nconst require = createRequire(import.meta.url);\n\n/** `chii/server` is CommonJS and shipped without TypeScript types. */\ninterface ChiiServerModule {\n start(options: {\n port?: number;\n host?: string;\n domain?: string;\n server?: Server;\n basePath?: string;\n }): Promise<void>;\n}\n\nfunction loadChiiServer(): ChiiServerModule {\n // `chii`'s package `main` is `./server/index.js`, exposing `{ start }`.\n const mod: unknown = require('chii');\n if (\n typeof mod === 'object' &&\n mod !== null &&\n 'start' in mod &&\n typeof (mod as { start: unknown }).start === 'function'\n ) {\n return mod as ChiiServerModule;\n }\n throw new Error('chii server module did not expose start()');\n}\n\nexport interface ChiiRelay {\n port: number;\n /** Base URL for the relay HTTP/WS server, e.g. `http://127.0.0.1:9100`. */\n baseUrl: string;\n close(): Promise<void>;\n}\n\nexport interface StartChiiRelayOptions {\n /** Local port for the relay. Default 9100. */\n port?: number;\n /** Bind host. Default 127.0.0.1 (tunnel reaches it locally). */\n host?: string;\n}\n\n/** Starts the Chii relay on the given port and resolves once listening. */\nexport async function startChiiRelay(options: StartChiiRelayOptions = {}): Promise<ChiiRelay> {\n const port = options.port ?? 9100;\n const host = options.host ?? '127.0.0.1';\n\n const httpServer = createServer();\n const chii = loadChiiServer();\n // Passing an existing `server` makes chii attach its Koa handler + WS upgrade\n // to our HTTP server rather than creating its own listener.\n await chii.start({ server: httpServer, domain: `${host}:${port}`, port });\n\n await new Promise<void>((resolve, reject) => {\n httpServer.once('error', reject);\n httpServer.listen(port, host, () => {\n httpServer.off('error', reject);\n resolve();\n });\n });\n\n return {\n port,\n baseUrl: `http://${host}:${port}`,\n close: () =>\n new Promise<void>((resolve) => {\n httpServer.close(() => resolve());\n }),\n };\n}\n","/**\n * Debug-mode MCP tools (Phase 1–3).\n *\n * Read-only tools that normalize CDP / AIT data into `chrome-devtools-mcp`-\n * compatible shapes. The tools never touch a websocket or HTTP endpoint\n * directly — they read from an injected `CdpConnection` (CDP events/commands)\n * or `AitSource` (AIT.* domain), which is what makes them unit-testable with a\n * fake. No phone and no running dev server are needed in tests.\n *\n * Phase 1 (CDP events):\n * - `list_console_messages` ← Runtime.consoleAPICalled\n * - `list_network_requests` ← Network.requestWillBeSent + responseReceived\n * - `list_pages` ← Chii relay target list + tunnel status\n * Phase 2 (CDP commands):\n * - `get_dom_document` ← DOM.getDocument\n * - `take_snapshot` ← DOMSnapshot.captureSnapshot\n * - `take_screenshot` ← Page.captureScreenshot\n * Phase 3 (AIT.* domain — CDP can't cover these):\n * - `AIT.getSdkCallHistory`\n * - `AIT.getMockState`\n * - `AIT.getOperationalEnvironment`\n */\n\nimport type {\n AitMockState,\n AitOperationalEnvironment,\n AitSdkCallHistory,\n AitSource,\n} from './ait-source.js';\nimport type {\n CdpConnection,\n CdpRemoteObject,\n ConsoleApiCalledEvent,\n DomGetDocumentResult,\n DomSnapshotResult,\n NetworkRequestWillBeSentEvent,\n NetworkResponseReceivedEvent,\n} from './cdp-connection.js';\n\n/** Tunnel state surfaced by `list_pages`. */\nexport interface TunnelStatus {\n /** Whether the cloudflared quick tunnel is up. */\n up: boolean;\n /** Public `wss://*.trycloudflare.com` relay URL the phone attaches to. */\n wssUrl: string | null;\n}\n\n/** Static MCP tool descriptors (name + JSONSchema) for the full debug tool surface. */\nexport const DEBUG_TOOL_DEFINITIONS = [\n {\n name: 'list_console_messages',\n description:\n 'Lists recent console messages (console.log/warn/error/info) captured from the attached ' +\n 'mini-app page over CDP (Runtime.consoleAPICalled). Read-only. Returns level, text, ' +\n 'timestamp, and stringified args, oldest-first.',\n inputSchema: { type: 'object', properties: {}, required: [] },\n },\n {\n name: 'list_network_requests',\n description:\n 'Lists recent network requests (XHR/fetch) captured from the attached mini-app page over ' +\n 'CDP (Network.requestWillBeSent + Network.responseReceived). Read-only. Returns url, ' +\n 'method, status, and timing, oldest-first.',\n inputSchema: { type: 'object', properties: {}, required: [] },\n },\n {\n name: 'list_pages',\n description:\n 'Lists the mini-app page(s) the Chii relay currently sees attached, plus whether the ' +\n 'cloudflared tunnel is up and the public wss relay URL the phone uses to attach. ' +\n 'Call this first to confirm a page is attached before reading console/network.',\n inputSchema: { type: 'object', properties: {}, required: [] },\n },\n {\n name: 'get_dom_document',\n description:\n 'Returns the DOM tree of the attached mini-app page over CDP (DOM.getDocument). Read-only. ' +\n 'Use for structural/layout regression diagnosis (e.g. confirming an element exists, ' +\n 'inspecting attributes). Returns the document root node with children.',\n inputSchema: { type: 'object', properties: {}, required: [] },\n },\n {\n name: 'take_snapshot',\n description:\n 'Captures a serialized snapshot of the attached page over CDP (DOMSnapshot.captureSnapshot). ' +\n 'Read-only. Returns the documents + interned strings table for visual-regression diagnosis ' +\n '(e.g. checking computed CSS custom properties like --sat against the live layout).',\n inputSchema: { type: 'object', properties: {}, required: [] },\n },\n {\n name: 'take_screenshot',\n description:\n 'Captures a PNG screenshot of the attached mini-app page over CDP (Page.captureScreenshot) ' +\n 'so the agent can see the phone screen directly. Read-only. Returns an image content block.',\n inputSchema: { type: 'object', properties: {}, required: [] },\n },\n {\n name: 'AIT.getSdkCallHistory',\n description:\n 'Returns the recent Apps In Toss SDK call trace (method, args, result/error, timestamp) that ' +\n 'raw CDP cannot observe. Read-only. Use to confirm an SDK call fired and how it resolved ' +\n '(e.g. a saveBase64Data permission regression).',\n inputSchema: { type: 'object', properties: {}, required: [] },\n },\n {\n name: 'AIT.getMockState',\n description:\n 'Returns the devtools mock state snapshot (window.__ait) — environment, permissions, location, ' +\n 'auth, network, IAP, and more. Read-only. In dev mode this is the live browser mock state; in ' +\n 'debug mode the in-app side reports it over the AIT domain.',\n inputSchema: { type: 'object', properties: {}, required: [] },\n },\n {\n name: 'AIT.getOperationalEnvironment',\n description:\n 'Returns getOperationalEnvironment() plus the resolved SDK version — metadata raw CDP cannot ' +\n 'observe. Read-only.',\n inputSchema: { type: 'object', properties: {}, required: [] },\n },\n] as const;\n\nexport type DebugToolName = (typeof DEBUG_TOOL_DEFINITIONS)[number]['name'];\n\nconst DEBUG_TOOL_NAMES = new Set<string>(DEBUG_TOOL_DEFINITIONS.map((t) => t.name));\n\nexport function isDebugToolName(name: string): name is DebugToolName {\n return DEBUG_TOOL_NAMES.has(name);\n}\n\n/** Normalized console message returned by `list_console_messages`. */\nexport interface ConsoleMessage {\n level: string;\n text: string;\n timestamp: number;\n args: string[];\n}\n\n/** Normalized network request returned by `list_network_requests`. */\nexport interface NetworkRequest {\n requestId: string;\n url: string;\n method: string;\n /** HTTP status once a response was seen, else null (still in-flight). */\n status: number | null;\n statusText: string | null;\n /** Request start (CDP timestamp). */\n startTime: number;\n /** Response received (CDP timestamp), else null. */\n endTime: number | null;\n}\n\n/** Renders a CDP `RemoteObject` console arg to a stable display string. */\nfunction renderRemoteObject(arg: CdpRemoteObject): string {\n if (arg.value !== undefined) {\n if (typeof arg.value === 'string') return arg.value;\n try {\n return JSON.stringify(arg.value);\n } catch {\n return String(arg.value);\n }\n }\n if (arg.description !== undefined) return arg.description;\n if (arg.className !== undefined) return arg.className;\n return arg.subtype ?? arg.type;\n}\n\nexport function normalizeConsoleMessage(event: ConsoleApiCalledEvent): ConsoleMessage {\n const args = event.args.map(renderRemoteObject);\n return {\n level: event.type,\n text: args.join(' '),\n timestamp: event.timestamp,\n args,\n };\n}\n\nexport function listConsoleMessages(connection: CdpConnection): ConsoleMessage[] {\n return connection\n .getBufferedEvents('Runtime.consoleAPICalled')\n .map((event) => normalizeConsoleMessage(event));\n}\n\nexport function listNetworkRequests(connection: CdpConnection): NetworkRequest[] {\n const requests = connection.getBufferedEvents('Network.requestWillBeSent');\n const responses = connection.getBufferedEvents('Network.responseReceived');\n\n const responseByRequestId = new Map<string, NetworkResponseReceivedEvent>();\n for (const response of responses) {\n responseByRequestId.set(response.requestId, response);\n }\n\n return requests.map((request: NetworkRequestWillBeSentEvent) => {\n const response = responseByRequestId.get(request.requestId);\n return {\n requestId: request.requestId,\n url: request.request.url,\n method: request.request.method,\n status: response ? response.response.status : null,\n statusText: response ? response.response.statusText : null,\n startTime: request.timestamp,\n endTime: response ? response.timestamp : null,\n };\n });\n}\n\n/** Result of `list_pages`: attach status + tunnel state. */\nexport interface ListPagesResult {\n pages: ReturnType<CdpConnection['listTargets']>;\n tunnel: TunnelStatus;\n}\n\nexport function listPages(connection: CdpConnection, tunnel: TunnelStatus): ListPagesResult {\n return { pages: connection.listTargets(), tunnel };\n}\n\n/* -------------------------------------------------------------------------- */\n/* Phase 2 — DOM / snapshot / screenshot (CDP commands) */\n/* -------------------------------------------------------------------------- */\n\n/** Returns the DOM tree of the attached page (`DOM.getDocument`). */\nexport function getDomDocument(connection: CdpConnection): Promise<DomGetDocumentResult> {\n // `pierce: true` flattens shadow roots; depth -1 returns the whole subtree so\n // a single call yields the full tree for structural diagnosis.\n return connection.send('DOM.getDocument', { depth: -1, pierce: true });\n}\n\n/** Returns a serialized page snapshot (`DOMSnapshot.captureSnapshot`). */\nexport function takeSnapshot(connection: CdpConnection): Promise<DomSnapshotResult> {\n return connection.send('DOMSnapshot.captureSnapshot', {});\n}\n\n/** A `take_screenshot` result: the raw base64 PNG plus a ready-to-use data URI. */\nexport interface ScreenshotResult {\n /** Base64-encoded PNG bytes (no data-URI prefix). */\n data: string;\n /** `data:image/png;base64,…` form for clients that render a URI. */\n dataUri: string;\n mimeType: 'image/png';\n}\n\n/** Captures a PNG screenshot of the attached page (`Page.captureScreenshot`). */\nexport async function takeScreenshot(connection: CdpConnection): Promise<ScreenshotResult> {\n const { data } = await connection.send('Page.captureScreenshot', { format: 'png' });\n return { data, dataUri: `data:image/png;base64,${data}`, mimeType: 'image/png' };\n}\n\n/* -------------------------------------------------------------------------- */\n/* Phase 3 — AIT.* domain (CDP can't cover these) */\n/* -------------------------------------------------------------------------- */\n\n/** Set of tool names served by the AIT source rather than the CDP connection. */\nconst AIT_TOOL_NAMES = new Set<string>([\n 'AIT.getSdkCallHistory',\n 'AIT.getMockState',\n 'AIT.getOperationalEnvironment',\n]);\n\n/** True for the Phase 3 AIT.* tools (served by an `AitSource`, not CDP). */\nexport function isAitToolName(name: string): boolean {\n return AIT_TOOL_NAMES.has(name);\n}\n\n/** Returns the recent SDK call trace (`AIT.getSdkCallHistory`). */\nexport function getSdkCallHistory(source: AitSource): Promise<AitSdkCallHistory> {\n return source.get('AIT.getSdkCallHistory');\n}\n\n/** Returns the devtools mock-state snapshot (`AIT.getMockState`). */\nexport function getMockState(source: AitSource): Promise<AitMockState> {\n return source.get('AIT.getMockState');\n}\n\n/** Returns the operational environment + SDK version (`AIT.getOperationalEnvironment`). */\nexport function getOperationalEnvironment(source: AitSource): Promise<AitOperationalEnvironment> {\n return source.get('AIT.getOperationalEnvironment');\n}\n","/**\n * cloudflared quick tunnel + attach banner for the debug-mode MCP server.\n *\n * On spawn, the debug server opens an accountless `*.trycloudflare.com` quick\n * tunnel to the local Chii relay so the phone can attach over a public wss URL,\n * then prints that URL + an attach token + an ASCII QR to the terminal. The\n * phone scans the QR (or pastes the URL) to attach; the in-app side passes the\n * token back. The token is generated + displayed as a pairing hint; relay-side\n * validation (ACL enforcement) is a later phase.\n *\n * Node-only: spawns the cloudflared binary and writes to stdout/stderr.\n */\n\nimport { randomBytes } from 'node:crypto';\nimport { bin, install, Tunnel } from 'cloudflared';\nimport qrcode from 'qrcode-terminal';\n\n/** Generates a 32-byte hex attach token shown as a pairing hint (relay-side validation is a later phase). */\nexport function generateAttachToken(): string {\n return randomBytes(32).toString('hex');\n}\n\nexport interface QuickTunnel {\n /** Public `https://*.trycloudflare.com` URL the tunnel exposes. */\n url: string;\n /** Same host as `wss://` — the relay endpoint the phone attaches to. */\n wssUrl: string;\n stop(): void;\n}\n\n/** Ensures the cloudflared binary is installed (downloads + caches on first run). */\nasync function ensureCloudflaredBin(): Promise<void> {\n const { existsSync } = await import('node:fs');\n if (!existsSync(bin)) {\n await install(bin);\n }\n}\n\n/**\n * Opens a cloudflared quick tunnel to the local relay port and resolves once\n * the public URL is assigned.\n */\nexport async function startQuickTunnel(localPort: number): Promise<QuickTunnel> {\n await ensureCloudflaredBin();\n\n const tunnel = Tunnel.quick(`http://127.0.0.1:${localPort}`);\n\n const url = await new Promise<string>((resolve, reject) => {\n const onUrl = (assigned: string) => {\n cleanup();\n resolve(assigned);\n };\n const onError = (err: Error) => {\n cleanup();\n reject(err);\n };\n const onExit = (code: number | null) => {\n cleanup();\n reject(new Error(`cloudflared exited before assigning a URL (code ${code})`));\n };\n const cleanup = () => {\n tunnel.off('url', onUrl);\n tunnel.off('error', onError);\n tunnel.off('exit', onExit);\n };\n tunnel.once('url', onUrl);\n tunnel.once('error', onError);\n tunnel.once('exit', onExit);\n });\n\n return {\n url,\n wssUrl: url.replace(/^https/, 'wss'),\n stop: () => {\n tunnel.stop();\n },\n };\n}\n\nexport interface AttachBannerInput {\n wssUrl: string;\n token: string;\n}\n\n/** Renders the attach banner (URL + token + ASCII QR) as a string. */\nexport async function renderAttachBanner(input: AttachBannerInput): Promise<string> {\n // Encode the attach payload as a URL so a QR scan opens directly.\n const payload = `${input.wssUrl}?token=${input.token}`;\n const qr = await new Promise<string>((resolve) => {\n qrcode.generate(payload, { small: true }, (rendered) => resolve(rendered));\n });\n return [\n '',\n 'AIT debug — attach a mini-app to this session',\n '',\n ` relay (wss): ${input.wssUrl}`,\n ` attach token: ${input.token}`,\n ` (token is a pairing hint — relay-side validation lands in a later phase)`,\n '',\n ' Open the dogfood mini-app with ?debug=1, then scan the QR',\n ' (or paste the relay URL + token in the in-app attach form):',\n '',\n qr,\n ].join('\\n');\n}\n\n/** Prints the attach banner to stderr (stdout is the MCP stdio channel). */\nexport async function printAttachBanner(input: AttachBannerInput): Promise<void> {\n const banner = await renderAttachBanner(input);\n process.stderr.write(`${banner}\\n`);\n}\n","/**\n * @ait-co/devtools debug-mode MCP server (stdio).\n *\n * Lets an AI coding agent attach to a running mini-app (real Toss WebView, or a\n * browser in dev mode) and read its console/network/DOM/screenshot over CDP plus\n * the AIT.* domain, without a human watching a phone. Transport is CDP-via-Chii:\n * a local Chii relay :9100 exposed through a cloudflared quick tunnel; the phone\n * attaches over the public wss URL.\n *\n * AI host --stdio--> this server --CDP client WS--> Chii relay :9100\n * ^-- target WS -- phone\n *\n * The tool layer reads from an injectable `CdpConnection` (CDP) and `AitSource`\n * (AIT.*), so every tool is unit-testable with a fake (no phone). This module\n * wires the live pieces (relay + tunnel + production connection); the phone\n * roundtrip is fully wired and pending only on-device acceptance.\n *\n * Node-only.\n */\n\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';\nimport { ChiiAitSource } from './ait-chii-source.js';\nimport type { AitSource } from './ait-source.js';\nimport type { CdpConnection } from './cdp-connection.js';\nimport { ChiiCdpConnection } from './chii-connection.js';\nimport { startChiiRelay } from './chii-relay.js';\nimport {\n DEBUG_TOOL_DEFINITIONS,\n getDomDocument,\n getMockState,\n getOperationalEnvironment,\n getSdkCallHistory,\n isAitToolName,\n isDebugToolName,\n listConsoleMessages,\n listNetworkRequests,\n listPages,\n type TunnelStatus,\n takeScreenshot,\n takeSnapshot,\n} from './tools.js';\nimport {\n generateAttachToken,\n printAttachBanner,\n type QuickTunnel,\n startQuickTunnel,\n} from './tunnel.js';\n\n/** Live infra the connection reads tunnel status from. */\nexport interface DebugServerDeps {\n connection: CdpConnection;\n /** AIT.* domain source — forwarded over the same Chii channel in production. */\n aitSource: AitSource;\n /** Returns current tunnel status (URL changes per spawn). */\n getTunnelStatus(): TunnelStatus;\n}\n\n/**\n * Builds the debug-mode MCP server around an injected CDP connection + AIT\n * source + tunnel status getter. Pure wiring — does not start a relay or\n * tunnel, which is what makes the tool surface unit-testable.\n */\nexport function createDebugServer(deps: DebugServerDeps): Server {\n const { connection, aitSource, getTunnelStatus } = deps;\n\n const server = new Server(\n { name: 'ait-debug', version: __VERSION__ },\n { capabilities: { tools: {} } },\n );\n\n server.setRequestHandler(ListToolsRequestSchema, () => ({\n tools: DEBUG_TOOL_DEFINITIONS.map((tool) => ({ ...tool })),\n }));\n\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const name = request.params.name;\n if (!isDebugToolName(name)) {\n return {\n content: [{ type: 'text', text: `Unknown tool: ${name}` }],\n isError: true,\n };\n }\n\n // AIT.* tools are served by the AIT source. In production it rides the same\n // Chii websocket as CDP, so the connection must be attached first; the AIT\n // source's sendCommand rejects with a clear message if no page is attached.\n if (isAitToolName(name)) {\n try {\n await connection.enableDomains();\n switch (name) {\n case 'AIT.getSdkCallHistory':\n return jsonResult(await getSdkCallHistory(aitSource));\n case 'AIT.getMockState':\n return jsonResult(await getMockState(aitSource));\n case 'AIT.getOperationalEnvironment':\n return jsonResult(await getOperationalEnvironment(aitSource));\n default:\n return unknownTool(name);\n }\n } catch (err) {\n return errorResult(err, name);\n }\n }\n\n try {\n // Ensure CDP domains are enabled before reading. No-op once attached;\n // throws a clear message while no page is attached yet.\n await connection.enableDomains();\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n if (name === 'list_pages') {\n // list_pages is still useful pre-attach: report tunnel + empty pages.\n return jsonResult(listPages(connection, getTunnelStatus()));\n }\n return {\n content: [\n {\n type: 'text',\n text: `${message}\\nCall list_pages to confirm a mini-app has attached over the relay.`,\n },\n ],\n isError: true,\n };\n }\n\n try {\n switch (name) {\n case 'list_console_messages':\n return jsonResult(listConsoleMessages(connection));\n case 'list_network_requests':\n return jsonResult(listNetworkRequests(connection));\n case 'list_pages':\n return jsonResult(listPages(connection, getTunnelStatus()));\n case 'get_dom_document':\n return jsonResult(await getDomDocument(connection));\n case 'take_snapshot':\n return jsonResult(await takeSnapshot(connection));\n case 'take_screenshot': {\n const shot = await takeScreenshot(connection);\n return {\n content: [{ type: 'image' as const, data: shot.data, mimeType: shot.mimeType }],\n };\n }\n default:\n return unknownTool(name);\n }\n } catch (err) {\n return errorResult(err, name);\n }\n });\n\n return server;\n}\n\nfunction jsonResult(value: unknown) {\n return { content: [{ type: 'text' as const, text: JSON.stringify(value, null, 2) }] };\n}\n\nfunction unknownTool(name: string) {\n return { content: [{ type: 'text' as const, text: `Unknown tool: ${name}` }], isError: true };\n}\n\nfunction errorResult(err: unknown, name: string) {\n const message = err instanceof Error ? err.message : String(err);\n return {\n content: [\n {\n type: 'text' as const,\n text: `${name} failed: ${message}\\nCall list_pages to confirm a mini-app has attached over the relay.`,\n },\n ],\n isError: true,\n };\n}\n\nexport interface RunDebugServerOptions {\n /** Local Chii relay port. Default 9100. */\n relayPort?: number;\n}\n\n/**\n * Boots the live debug stack and serves it over stdio:\n * 1. start the Chii relay,\n * 2. open a cloudflared quick tunnel to it,\n * 3. print QR + secret token,\n * 4. expose the debug tools backed by a `ChiiCdpConnection` + `ChiiAitSource`.\n */\nexport async function runDebugServer(options: RunDebugServerOptions = {}): Promise<void> {\n const relayPort = options.relayPort ?? 9100;\n\n const relay = await startChiiRelay({ port: relayPort });\n\n let tunnel: QuickTunnel | null = null;\n let tunnelStatus: TunnelStatus = { up: false, wssUrl: null };\n const token = generateAttachToken();\n\n try {\n tunnel = await startQuickTunnel(relayPort);\n tunnelStatus = { up: true, wssUrl: tunnel.wssUrl };\n await printAttachBanner({ wssUrl: tunnel.wssUrl, token });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n process.stderr.write(\n `[ait-debug] Failed to open cloudflared quick tunnel: ${message}\\n` +\n '[ait-debug] The relay is up locally; attach over the public URL is unavailable until the tunnel starts.\\n',\n );\n }\n\n const connection = new ChiiCdpConnection({ relayBaseUrl: relay.baseUrl });\n // AIT.* methods ride the same Chii channel as CDP commands.\n const aitSource = new ChiiAitSource(connection);\n const server = createDebugServer({\n connection,\n aitSource,\n getTunnelStatus: () => tunnelStatus,\n });\n\n const transport = new StdioServerTransport();\n\n const shutdown = () => {\n connection.close();\n tunnel?.stop();\n void relay.close();\n void server.close();\n };\n process.once('SIGINT', shutdown);\n process.once('SIGTERM', shutdown);\n\n await server.connect(transport);\n}\n","/**\n * Dev-mode `AitSource` — backed by the Vite dev server's mock-state endpoint.\n *\n * The dev server already exposes the live browser mock state at\n * `GET /api/ait-devtools/state` (registered by the unplugin with `mcp: true`).\n * Phase 3 aligns dev mode and debug mode on the same `AIT.*` tool surface, so\n * dev mode serves those tools off this one HTTP source instead of a CDP channel:\n *\n * - `AIT.getMockState` → the full state snapshot (verbatim).\n * - `AIT.getOperationalEnvironment` → derived from the snapshot's\n * `environment` + `appVersion` fields.\n * - `AIT.getSdkCallHistory` → empty (the dev endpoint does not record\n * an SDK call trace — honest, not faked).\n *\n * An AI agent thus sees the same `AIT.getMockState` tool whether attached to a\n * phone (debug) or a dev browser (dev). Tests inject a fake `fetch`.\n */\n\nimport type {\n AitMethodMap,\n AitMethodName,\n AitMockState,\n AitOperationalEnvironment,\n AitSdkCallHistory,\n AitSource,\n} from './ait-source.js';\n\n/** Minimal `fetch` shape this source needs (injectable in tests). */\nexport type FetchLike = (url: string) => Promise<{\n ok: boolean;\n status: number;\n statusText: string;\n json(): Promise<unknown>;\n}>;\n\nexport interface HttpAitSourceOptions {\n /** Full URL of the mock-state endpoint, e.g. `http://localhost:5173/api/ait-devtools/state`. */\n stateEndpoint: string;\n /** Injected for tests; defaults to global `fetch`. */\n fetchImpl?: FetchLike;\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nexport class HttpAitSource implements AitSource {\n private readonly stateEndpoint: string;\n private readonly fetchImpl: FetchLike;\n\n constructor(options: HttpAitSourceOptions) {\n this.stateEndpoint = options.stateEndpoint;\n this.fetchImpl = options.fetchImpl ?? ((url) => fetch(url));\n }\n\n private async fetchState(): Promise<AitMockState> {\n const res = await this.fetchImpl(this.stateEndpoint);\n if (!res.ok) {\n throw new Error(\n `Failed to fetch mock state from ${this.stateEndpoint}: HTTP ${res.status} ${res.statusText}. ` +\n 'Ensure the Vite dev server is running with the @ait-co/devtools unplugin option `mcp: true`.',\n );\n }\n const body = await res.json();\n return isObject(body) ? body : {};\n }\n\n async get<M extends AitMethodName>(method: M): Promise<AitMethodMap[M]> {\n switch (method) {\n case 'AIT.getMockState': {\n const state = await this.fetchState();\n return state as AitMethodMap[M];\n }\n case 'AIT.getOperationalEnvironment': {\n const state = await this.fetchState();\n const environment = typeof state.environment === 'string' ? state.environment : 'unknown';\n const sdkVersion = typeof state.appVersion === 'string' ? state.appVersion : null;\n const result: AitOperationalEnvironment = { environment, sdkVersion };\n return result as AitMethodMap[M];\n }\n case 'AIT.getSdkCallHistory': {\n // Dev endpoint records no SDK call trace; return empty rather than fake.\n const result: AitSdkCallHistory = { calls: [] };\n return result as AitMethodMap[M];\n }\n default:\n throw new Error(`Unknown AIT method: ${String(method)}`);\n }\n }\n}\n","/**\n * @ait-co/devtools dev-mode MCP server (stdio).\n *\n * Exposes the live browser mock state from a running Vite dev server to AI\n * coding agents via the Model Context Protocol (MCP).\n *\n * Architecture:\n * Browser (aitState) → Vite dev server endpoint (/api/ait-devtools/state)\n * ← HTTP GET ← this stdio MCP server ← AI agent\n *\n * The Vite endpoint is registered by the unplugin when `mcp: true` is set in\n * the plugin options (see `src/unplugin/index.ts`).\n *\n * Phase 3 tool-surface alignment: dev mode and debug mode now expose the same\n * `AIT.*` tools (`AIT.getMockState`, `AIT.getOperationalEnvironment`,\n * `AIT.getSdkCallHistory`). In dev mode they are backed by the HTTP mock-state\n * endpoint (see `HttpAitSource`); in debug mode by the Chii channel. So an AI\n * sees a coherent tool whether attached to a phone (debug) or a dev browser\n * (dev). `devtools_get_mock_state` (the original devtools#130 name) is kept as a\n * backward-compatible alias of `AIT.getMockState`.\n *\n * This module is reached via the `devtools-mcp --mode=dev` CLI entry (see\n * `cli.ts`); the default (no flag) bin mode is the debug-mode CDP/Chii server.\n *\n * Usage (in your MCP client config, e.g. Claude Desktop):\n * {\n * \"mcpServers\": {\n * \"ait-devtools\": {\n * \"command\": \"pnpm\",\n * \"args\": [\"exec\", \"devtools-mcp\", \"--mode=dev\"],\n * \"env\": { \"AIT_DEVTOOLS_URL\": \"http://localhost:5173\" }\n * }\n * }\n * }\n */\n\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';\nimport { HttpAitSource } from './ait-http-source.js';\nimport type { AitSource } from './ait-source.js';\nimport {\n getMockState,\n getOperationalEnvironment,\n getSdkCallHistory,\n isAitToolName,\n} from './tools.js';\n\n/** Tool descriptors served by the dev-mode server. */\nconst DEV_TOOL_DEFINITIONS = [\n {\n name: 'AIT.getMockState',\n description:\n 'Returns the devtools mock state snapshot (window.__ait) from the running browser session — ' +\n 'environment, permissions, location, auth, network, IAP, and more. Read-only. ' +\n 'Requires the Vite dev server running with the @ait-co/devtools unplugin option `mcp: true`. ' +\n 'Same tool as in debug mode, where the in-app side reports it over the AIT domain.',\n inputSchema: { type: 'object', properties: {}, required: [] },\n },\n {\n name: 'AIT.getOperationalEnvironment',\n description:\n 'Returns the operational environment + SDK/app version derived from the dev mock state. ' +\n 'Read-only.',\n inputSchema: { type: 'object', properties: {}, required: [] },\n },\n {\n name: 'AIT.getSdkCallHistory',\n description:\n 'Returns the SDK call trace. In dev mode the HTTP mock-state endpoint records no trace, so ' +\n 'this returns an empty list; in debug mode it is populated over the AIT domain. Read-only.',\n inputSchema: { type: 'object', properties: {}, required: [] },\n },\n {\n name: 'devtools_get_mock_state',\n description:\n 'Backward-compatible alias of AIT.getMockState (the original devtools#130 name). Returns the ' +\n 'current AIT DevTools mock state snapshot. Read-only. Prefer AIT.getMockState in new configs.',\n inputSchema: { type: 'object', properties: {}, required: [] },\n },\n] as const;\n\nconst DEV_TOOL_NAMES = new Set<string>(DEV_TOOL_DEFINITIONS.map((t) => t.name));\n\nexport interface CreateDevServerDeps {\n /** AIT source for the dev tools. Defaults to an HTTP source over the dev server. */\n aitSource?: AitSource;\n}\n\n/** Builds the dev-mode MCP server (does not connect a transport). */\nexport function createDevServer(deps: CreateDevServerDeps = {}): Server {\n const devtoolsUrl = process.env.AIT_DEVTOOLS_URL ?? 'http://localhost:5173';\n const stateEndpoint = `${devtoolsUrl}/api/ait-devtools/state`;\n const aitSource = deps.aitSource ?? new HttpAitSource({ stateEndpoint });\n\n const server = new Server(\n { name: 'ait-devtools', version: __VERSION__ },\n { capabilities: { tools: {} } },\n );\n\n server.setRequestHandler(ListToolsRequestSchema, () => ({\n tools: DEV_TOOL_DEFINITIONS.map((tool) => ({ ...tool })),\n }));\n\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const name = request.params.name;\n if (!DEV_TOOL_NAMES.has(name)) {\n return { content: [{ type: 'text', text: `Unknown tool: ${name}` }], isError: true };\n }\n\n try {\n // `devtools_get_mock_state` is an alias of `AIT.getMockState`.\n const effective = name === 'devtools_get_mock_state' ? 'AIT.getMockState' : name;\n if (!isAitToolName(effective)) {\n return { content: [{ type: 'text', text: `Unknown tool: ${name}` }], isError: true };\n }\n switch (effective) {\n case 'AIT.getMockState':\n return jsonResult(await getMockState(aitSource));\n case 'AIT.getOperationalEnvironment':\n return jsonResult(await getOperationalEnvironment(aitSource));\n case 'AIT.getSdkCallHistory':\n return jsonResult(await getSdkCallHistory(aitSource));\n default:\n return { content: [{ type: 'text', text: `Unknown tool: ${name}` }], isError: true };\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return {\n content: [\n {\n type: 'text',\n text:\n `${message}\\n` +\n 'Is the Vite dev server running with the @ait-co/devtools unplugin option `mcp: true`? ' +\n 'Is AIT_DEVTOOLS_URL set correctly?',\n },\n ],\n isError: true,\n };\n }\n });\n\n return server;\n}\n\nfunction jsonResult(value: unknown) {\n return { content: [{ type: 'text' as const, text: JSON.stringify(value, null, 2) }] };\n}\n\n/** Builds the dev-mode server and connects it over stdio. */\nexport async function runDevServer(): Promise<void> {\n const server = createDevServer();\n const transport = new StdioServerTransport();\n await server.connect(transport);\n}\n","#!/usr/bin/env node\n/**\n * `devtools-mcp` bin entry.\n *\n * Single bin, two transports selected by `--mode`:\n * - (default, no flag) debug mode — CDP/Chii relay + cloudflared quick tunnel.\n * Attach a running mini-app (real Toss WebView or a browser) and read its\n * console + network over CDP without a human watching a phone.\n * - `--mode=dev` — dev mode — reads the live browser mock state from a running\n * Vite dev server (the devtools#130 `devtools_get_mock_state` surface).\n *\n * Node-only stdio process.\n */\n\nimport { argv } from 'node:process';\nimport { fileURLToPath } from 'node:url';\nimport { runDebugServer } from './debug-server.js';\nimport { runDevServer } from './server.js';\n\ntype Mode = 'debug' | 'dev';\n\n/** Parses `--mode=<value>` / `--mode <value>` from argv; default `debug`. */\nexport function parseMode(argv: readonly string[]): Mode {\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i];\n if (arg === undefined) continue;\n if (arg.startsWith('--mode=')) {\n return normalizeMode(arg.slice('--mode='.length));\n }\n if (arg === '--mode') {\n const next = argv[i + 1];\n if (next === undefined) {\n throw new Error(\"--mode requires a value: 'debug' (default) or 'dev'.\");\n }\n return normalizeMode(next);\n }\n }\n return 'debug';\n}\n\nfunction normalizeMode(value: string): Mode {\n if (value === 'dev') return 'dev';\n if (value === 'debug') return 'debug';\n throw new Error(`Unknown --mode '${value}'. Expected 'debug' (default) or 'dev'.`);\n}\n\nasync function main(): Promise<void> {\n const mode = parseMode(process.argv.slice(2));\n if (mode === 'dev') {\n await runDevServer();\n } else {\n await runDebugServer();\n }\n}\n\n/** True when this file is the process entry (the bin), not an import. */\nfunction isEntrypoint(): boolean {\n const entry = argv[1];\n if (entry === undefined) return false;\n try {\n return fileURLToPath(import.meta.url) === entry;\n } catch {\n return false;\n }\n}\n\nif (isEntrypoint()) {\n main().catch((err: unknown) => {\n const message = err instanceof Error ? err.message : String(err);\n process.stderr.write(`[devtools-mcp] fatal: ${message}\\n`);\n process.exitCode = 1;\n });\n}\n"],"mappings":"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"}
@@ -222,7 +222,7 @@ function createDevServer(deps = {}) {
222
222
  const aitSource = deps.aitSource ?? new HttpAitSource({ stateEndpoint });
223
223
  const server = new Server({
224
224
  name: "ait-devtools",
225
- version: "0.1.23"
225
+ version: "0.1.25"
226
226
  }, { capabilities: { tools: {} } });
227
227
  server.setRequestHandler(ListToolsRequestSchema, () => ({ tools: DEV_TOOL_DEFINITIONS.map((tool) => ({ ...tool })) }));
228
228
  server.setRequestHandler(CallToolRequestSchema, async (request) => {