@ait-co/devtools 0.1.71 → 0.1.73
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{deeplink-CaO6hZVG.js → deeplink-B-94XmWA.js} +19 -3
- package/dist/deeplink-B-94XmWA.js.map +1 -0
- package/dist/{deeplink-BONXxWEO.cjs → deeplink-BLU2_hg6.cjs} +19 -3
- package/dist/deeplink-BLU2_hg6.cjs.map +1 -0
- package/dist/{deeplink-CCGiyoHq.cjs → deeplink-CU6opogq.cjs} +19 -3
- package/dist/deeplink-CU6opogq.cjs.map +1 -0
- package/dist/{deeplink-Cqli4qzm.js → deeplink-CYqDwVYs.js} +19 -3
- package/dist/deeplink-CYqDwVYs.js.map +1 -0
- package/dist/devtools-opener-BbUXBzgA.js.map +1 -1
- package/dist/devtools-opener-Bp671YXu.cjs.map +1 -1
- package/dist/devtools-opener-D84kZFtR.js.map +1 -1
- package/dist/devtools-opener-h6A-UjzC.cjs.map +1 -1
- package/dist/mcp/cli.js +75 -42
- package/dist/mcp/cli.js.map +1 -1
- package/dist/mcp/server.js +2 -2
- package/dist/mcp/server.js.map +1 -1
- package/dist/panel/index.js +18 -10
- package/dist/panel/index.js.map +1 -1
- package/dist/{qr-http-server-BIIMOcuU.cjs → qr-http-server-0uN5jxLW.cjs} +19 -11
- package/dist/qr-http-server-0uN5jxLW.cjs.map +1 -0
- package/dist/{qr-http-server-ClakYBO9.cjs → qr-http-server-BTjpFS3p.cjs} +19 -11
- package/dist/qr-http-server-BTjpFS3p.cjs.map +1 -0
- package/dist/{qr-http-server-JjGU81q7.js → qr-http-server-Ditd2ndz.js} +19 -11
- package/dist/qr-http-server-Ditd2ndz.js.map +1 -0
- package/dist/{qr-http-server-CeEzLS3g.js → qr-http-server-TQG61eI4.js} +19 -11
- package/dist/qr-http-server-TQG61eI4.js.map +1 -0
- package/dist/{relay-secret-store-CLkF8Pa0.cjs → relay-secret-store-BFOEhsLO.cjs} +2 -2
- package/dist/{relay-secret-store-CLkF8Pa0.cjs.map → relay-secret-store-BFOEhsLO.cjs.map} +1 -1
- package/dist/{relay-secret-store-DBcKWUl9.js → relay-secret-store-C_LUxvAp.js} +2 -2
- package/dist/{relay-secret-store-DBcKWUl9.js.map → relay-secret-store-C_LUxvAp.js.map} +1 -1
- package/dist/{relay-secret-store-C4QQN5NA.js → relay-secret-store-D-W-WaSx.js} +2 -2
- package/dist/{relay-secret-store-C4QQN5NA.js.map → relay-secret-store-D-W-WaSx.js.map} +1 -1
- package/dist/{relay-url-store-COG2dSql.cjs → relay-url-store-2sy_l2bf.cjs} +2 -2
- package/dist/{relay-url-store-COG2dSql.cjs.map → relay-url-store-2sy_l2bf.cjs.map} +1 -1
- package/dist/{relay-url-store-WKfo0VQV.js → relay-url-store-DAh5KiJi.js} +2 -2
- package/dist/{relay-url-store-WKfo0VQV.js.map → relay-url-store-DAh5KiJi.js.map} +1 -1
- package/dist/{relay-url-store-Dq3vpd95.js → relay-url-store-DjKJJZ0d.js} +2 -2
- package/dist/{relay-url-store-Dq3vpd95.js.map → relay-url-store-DjKJJZ0d.js.map} +1 -1
- package/dist/{totp-D0a8VwoR.js → totp-BfVk8gQe.js} +6 -3
- package/dist/{totp-D0a8VwoR.js.map → totp-BfVk8gQe.js.map} +1 -1
- package/dist/totp-BxtxuEt4.js.map +1 -1
- package/dist/{totp-DA8vjAi7.cjs → totp-D4iTMA9U.cjs} +6 -3
- package/dist/{totp-DA8vjAi7.cjs.map → totp-D4iTMA9U.cjs.map} +1 -1
- package/dist/totp-D8f6qAEu.js +3 -0
- package/dist/totp-D9rndqg_.cjs.map +1 -1
- package/dist/{totp-BjtKFt88.js → totp-DbEfKQRi.js} +6 -3
- package/dist/{totp-BjtKFt88.js.map → totp-DbEfKQRi.js.map} +1 -1
- package/dist/{tunnel-DwVrcZ56.cjs → tunnel-BXAWl2tI.cjs} +22 -11
- package/dist/tunnel-BXAWl2tI.cjs.map +1 -0
- package/dist/{tunnel-aIy_7nWm.js → tunnel-BxGnLAat.js} +22 -11
- package/dist/tunnel-BxGnLAat.js.map +1 -0
- package/dist/unplugin/index.cjs +17 -7
- package/dist/unplugin/index.cjs.map +1 -1
- package/dist/unplugin/index.d.cts.map +1 -1
- package/dist/unplugin/index.d.ts.map +1 -1
- package/dist/unplugin/index.js +17 -7
- package/dist/unplugin/index.js.map +1 -1
- package/dist/unplugin/tunnel.cjs +20 -9
- package/dist/unplugin/tunnel.cjs.map +1 -1
- package/dist/unplugin/tunnel.d.cts +36 -3
- package/dist/unplugin/tunnel.d.cts.map +1 -1
- package/dist/unplugin/tunnel.d.ts +36 -3
- package/dist/unplugin/tunnel.d.ts.map +1 -1
- package/dist/unplugin/tunnel.js +20 -9
- package/dist/unplugin/tunnel.js.map +1 -1
- package/package.json +1 -1
- package/dist/deeplink-BONXxWEO.cjs.map +0 -1
- package/dist/deeplink-CCGiyoHq.cjs.map +0 -1
- package/dist/deeplink-CaO6hZVG.js.map +0 -1
- package/dist/deeplink-Cqli4qzm.js.map +0 -1
- package/dist/qr-http-server-BIIMOcuU.cjs.map +0 -1
- package/dist/qr-http-server-CeEzLS3g.js.map +0 -1
- package/dist/qr-http-server-ClakYBO9.cjs.map +0 -1
- package/dist/qr-http-server-JjGU81q7.js.map +0 -1
- package/dist/totp-CQFmgOhM.js +0 -3
- package/dist/tunnel-DwVrcZ56.cjs.map +0 -1
- package/dist/tunnel-aIy_7nWm.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"devtools-opener-h6A-UjzC.cjs","names":[],"sources":["../src/mcp/devtools-opener.ts"],"sourcesContent":["/**\n * Auto-opens Chrome DevTools when a page attaches over the Chii relay.\n *\n * When a real device attaches (env 2 / 3 / 4 in the 4-environments fidelity\n * ladder), the Chii relay exposes a standard CDP WebSocket endpoint. Chii\n * also self-hosts its DevTools frontend at:\n *\n * <relay-base>/front_end/chii_app.html\n * ?ws|wss=<encodeURIComponent(\"<relay-host>/client/<uuid>?target=<targetId>&at=<totp>\")>\n *\n * The param name follows the relay base scheme — `ws=` for plain HTTP\n * (env 3/4 local relay), `wss=` for HTTPS (env 2 tunnel) — matching the\n * scheme branch in chii/public/index.js.\n *\n * This is the same URL format that Chii's own index-page inspect-links use\n * (derived from `chii/public/index.js` — the JS that powers the target list\n * page at `<relay-base>/`). Opening this URL in the developer's local browser\n * gives a full Chrome DevTools UI connected to the phone via the relay.\n *\n * IMPORTANT — environment guard:\n * Auto-open only fires in relay environments (env 2 / 3 / 4). In env 1\n * (local browser + mock SDK) the developer already has F12 available; opening\n * a DevTools window pointing at the mock relay would be confusing and useless.\n * The caller (`startAttachWatcher` in `debug-server.ts`) passes the current\n * environment and this module bails out when it is `mock`.\n *\n * Opt-out: set `AIT_AUTO_DEVTOOLS=0` in the environment to suppress auto-open\n * entirely. Any other value (or absent) enables the default behaviour.\n *\n * Duplicate-open guard:\n * `AutoDevtoolsOpener` tracks whether open was already triggered for the\n * current session. The open fires at most once per instance — typically one\n * per `runDebugServer` call.\n *\n * TOTP expiry caveat:\n * The `at=` TOTP code embedded in the `wss=` parameter is minted fresh at the\n * moment `open()` is called. The code is valid for the 30-second RFC 6238\n * window (±1 step skew = 90 s acceptance). If the developer does not open the\n * URL within that window the WebSocket upgrade will be rejected with 4401.\n * In practice the browser opens immediately after the OS `open` command, so\n * the window is always satisfied; if it is not (e.g. the URL is copied and\n * opened later) the developer can copy the wss= param, replace `at=`, and\n * reload. This is documented in the JSDoc below.\n *\n * PWA (WebKit) caveat:\n * The Chii relay injects a chobitsu CDP shim into WebKit-based runtimes (env 2\n * AITC Sandbox PWA). The DevTools frontend will connect and most panels work.\n * However, WebKit does not expose the full CDP domain set that V8/Blink does,\n * so some panels (Network, Layers) may appear empty or show limited data.\n * This is a WebKit runtime constraint, not a relay or devtools-opener issue.\n *\n * Node-only: uses `child_process.spawnSync` to invoke the OS open command.\n */\n\nimport type { McpEnvironment } from './environment.js';\n\n// ---------------------------------------------------------------------------\n// Chii self-hosted DevTools frontend URL\n// ---------------------------------------------------------------------------\n\n/**\n * Assembles the Chii self-hosted DevTools inspector URL for a given relay\n * and target.\n *\n * Chii serves its own DevTools frontend at\n * `<relayHttpBaseUrl>/front_end/chii_app.html`. The `ws=` (plain HTTP relay)\n * or `wss=` (HTTPS relay) query parameter is a URL-encoded string of the form\n * `<relay-host>/client/<uuid>?target=<id>` (and optionally `&at=<totp>`) —\n * the same format used by Chii's own target list page (derived from\n * `chii/public/index.js`).\n *\n * The `at=` TOTP code is minted at call time via `mintTotp()`. It is valid\n * for the current 30-second RFC 6238 step (±1 step skew = 90 s acceptance\n * window). The developer must open the returned URL within that window. If\n * the window expires before the browser connects, the relay will reject the\n * WebSocket upgrade with close code 4401.\n *\n * SECRET-HANDLING: `mintTotp` returns a code, not a secret. The code is\n * embedded in the `wss=` parameter (inside the `at=` param) of the returned\n * URL. Callers MUST NOT log the returned URL to stdout (stderr is OK — it is\n * the intended fallback surface for the developer to copy the URL).\n *\n * @param relayHttpBaseUrl - Local HTTP base URL of the Chii relay, e.g.\n * `http://127.0.0.1:9100`. No trailing slash.\n * @param targetId - Chii target id (from `GET <relay>/targets`).\n * @param mintTotp - Optional function that returns a fresh 6-digit TOTP code\n * string. Called at most once. When omitted (TOTP disabled) no `at=` param\n * is added.\n * @param panel - Initial panel. Defaults to `\"console\"`.\n *\n * @example\n * buildChiiInspectorUrl(\n * 'http://127.0.0.1:9100',\n * 'abc123',\n * () => generateTotp(secret),\n * )\n * // → 'http://127.0.0.1:9100/front_end/chii_app.html?ws=127.0.0.1%3A9100%2Fclient%2F<uuid>%3Ftarget%3Dabc123%26at%3D<code>'\n */\nexport function buildChiiInspectorUrl(\n relayHttpBaseUrl: string,\n targetId: string,\n mintTotp?: () => string,\n panel: 'elements' | 'console' | 'sources' | 'network' = 'console',\n): string {\n // Extract the host (and port) from the relay HTTP base URL, and pick the\n // query param name chii_app.html expects: `ws=` dials `ws://` (plain-HTTP\n // relay — env 3/4 local 127.0.0.1) while `wss=` dials `wss://` (HTTPS\n // tunnel — env 2). chii/public/index.js does the same scheme branch:\n // `location.protocol === 'https:' ? 'wss' : 'ws'`. Always sending `wss=`\n // would make the frontend attempt TLS against the plain-HTTP local relay.\n let relayHost: string;\n let wsParamName: 'ws' | 'wss';\n try {\n const parsed = new URL(relayHttpBaseUrl);\n relayHost = parsed.host; // e.g. \"127.0.0.1:9100\"\n wsParamName = parsed.protocol === 'https:' ? 'wss' : 'ws';\n } catch {\n // Fallback: strip the scheme prefix manually if URL parsing fails.\n relayHost = relayHttpBaseUrl.replace(/^https?:\\/\\//i, '');\n wsParamName = /^https:/i.test(relayHttpBaseUrl) ? 'wss' : 'ws';\n }\n\n // Generate a client UUID that matches the format Chii's index.js uses\n // (6 random alphanumeric characters).\n const clientId = `devtools-opener-${Date.now().toString(36)}`;\n\n // Build the ws=/wss= value: \"<relay-host>/client/<uuid>?target=<id>[&at=<code>]\"\n // This mirrors the format from chii/public/index.js:\n // `${domain}${basePath}client/${randomId(6)}?target=${targetId}`\n let wsPath = `${relayHost}/client/${clientId}?target=${encodeURIComponent(targetId)}`;\n\n if (mintTotp) {\n // SECRET-HANDLING: mintTotp() returns a code (not a secret). The code\n // rides only in the URL's at= param. Callers must not log the URL.\n const code = mintTotp();\n wsPath += `&at=${encodeURIComponent(code)}`;\n }\n\n const params = new URLSearchParams({ [wsParamName]: wsPath, panel });\n return `${relayHttpBaseUrl.replace(/\\/$/, '')}/front_end/chii_app.html?${params.toString()}`;\n}\n\n// ---------------------------------------------------------------------------\n// Opt-out check\n// ---------------------------------------------------------------------------\n\n/**\n * Returns `true` when auto-open is **disabled** via the `AIT_AUTO_DEVTOOLS`\n * env var. Only the explicit `\"0\"` value disables it; anything else (including\n * absent) leaves auto-open enabled.\n */\nexport function isAutoDevtoolsDisabled(): boolean {\n return process.env.AIT_AUTO_DEVTOOLS === '0';\n}\n\n// ---------------------------------------------------------------------------\n// Browser open (Node-only, sync)\n// ---------------------------------------------------------------------------\n\n/**\n * Opens the given URL in the OS default browser using a platform-appropriate\n * command. Returns `true` on success.\n *\n * Failures are silent from the caller's perspective — the caller should log\n * the URL to stderr as a fallback before calling this function.\n */\nexport function openUrlInBrowser(url: string): boolean {\n // Test hook: skip actual spawn when running in vitest / CI where the OS open\n // command may hang or be absent. Production code never sets this.\n if (process.env.AIT_AUTO_DEVTOOLS_TEST_SKIP_SPAWN === '1') return false;\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const { spawnSync } = require('node:child_process') as typeof import('node:child_process');\n const platform = process.platform;\n\n type Candidate = { cmd: string; args: string[] };\n let candidates: Candidate[];\n if (platform === 'darwin') {\n candidates = [{ cmd: 'open', args: [url] }];\n } else if (platform === 'win32') {\n candidates = [{ cmd: 'cmd', args: ['/c', 'start', '', url] }];\n } else {\n // Linux + fallback\n candidates = [\n { cmd: 'xdg-open', args: [url] },\n { cmd: 'sensible-browser', args: [url] },\n { cmd: 'x-www-browser', args: [url] },\n ];\n }\n\n for (const { cmd, args } of candidates) {\n try {\n const result = spawnSync(cmd, args, { encoding: 'utf8', timeout: 5_000 });\n if (!result.error && result.status === 0) return true;\n } catch {\n // Try next candidate.\n }\n }\n return false;\n}\n\n// ---------------------------------------------------------------------------\n// AutoDevtoolsOpener — stateful once-per-session open guard\n// ---------------------------------------------------------------------------\n\n/**\n * Options for {@link AutoDevtoolsOpener.open}.\n *\n * The `relayHttpBaseUrl` and `targetId` fields are required to build a working\n * Chii self-hosted inspector URL. When `relayHttpBaseUrl` is absent the open\n * is skipped (no relay available yet).\n */\nexport interface DevtoolsOpenOptions {\n /**\n * Local HTTP base URL of the Chii relay, e.g. `http://127.0.0.1:9100`.\n * Used to build the `<relay-base>/front_end/chii_app.html?wss=…` URL.\n *\n * For env 3/4 (intoss relay) this is `http://127.0.0.1:<port>`.\n * For env 2 (external PWA relay) this is the relay's external HTTP URL\n * (e.g. `https://<host>.trycloudflare.com`).\n *\n * When absent or empty, `open()` is a no-op.\n *\n * SECRET-HANDLING: this value contains the relay host. Callers MUST NOT\n * log it to stdout; stderr is the intended surface.\n */\n relayHttpBaseUrl: string | null | undefined;\n /**\n * Chii target id of the attached page, from `listTargets()[0].id`.\n * When absent or empty, `open()` is a no-op.\n */\n targetId: string | null | undefined;\n /**\n * Function that mints a fresh TOTP code when called. Called at most once per\n * `open()` invocation, immediately before building the inspector URL.\n *\n * Pass `undefined` when TOTP is disabled (no `at=` param is added).\n *\n * SECRET-HANDLING: the function MUST return only the code (6 digits), not\n * the secret. The code rides in the URL's `at=` param only.\n */\n mintTotp?: () => string;\n /** Current MCP environment (`mock` | `relay`). `open()` no-ops on `mock`. */\n env: McpEnvironment;\n}\n\n/**\n * Manages auto-opening Chrome DevTools exactly once per relay attach session.\n *\n * Create one instance per `runDebugServer` call and pass its `open()` method\n * as the `onFirstAttach` callback to `startAttachWatcher`.\n *\n * The open fires at most once. Subsequent `open()` calls are no-ops.\n * Opt-out and mock-environment guard are checked at call time.\n */\nexport class AutoDevtoolsOpener {\n private _opened = false;\n\n /**\n * Attempts to auto-open Chii DevTools in the developer's browser.\n *\n * Builds a `<relay-base>/front_end/chii_app.html?wss=…` URL pointing at the\n * attached target. A fresh TOTP `at=` code is minted at call time so the\n * relay's WebSocket upgrade gate accepts the connection.\n *\n * No-op when any of the following conditions hold:\n * 1. Already opened this session (`_opened` is true).\n * 2. `AIT_AUTO_DEVTOOLS=0` opt-out is set.\n * 3. `options.env` is `mock` (env 1 — F12 is already available).\n * 4. `options.relayHttpBaseUrl` is null/undefined/empty (relay not up yet).\n * 5. `options.targetId` is null/undefined/empty (no page attached yet).\n *\n * Always writes the DevTools URL to stderr so the developer can copy it\n * if the browser open fails or the popup is blocked.\n *\n * TOTP expiry caveat: the `at=` code embedded in the URL is valid for the\n * current 30-second RFC 6238 step (±1 skew = 90 s). The developer must open\n * the URL within that window; if they miss it, reload the page or re-run\n * `open()` (though the once-per-session guard prevents that — restart the\n * MCP server if needed).\n *\n * SECRET-HANDLING: the inspector URL (written to stderr) contains the relay\n * host and a short-lived TOTP code. Do NOT write it to stdout or any\n * persistent log.\n */\n open(options: DevtoolsOpenOptions): void {\n if (this._opened) return;\n if (isAutoDevtoolsDisabled()) return;\n if (options.env === 'mock') return;\n if (!options.relayHttpBaseUrl) return;\n if (!options.targetId) return;\n\n this._opened = true;\n\n const inspectorUrl = buildChiiInspectorUrl(\n options.relayHttpBaseUrl,\n options.targetId,\n options.mintTotp,\n );\n\n process.stderr.write(\n '[ait-debug] 기기가 연결됐습니다 — Chii DevTools를 자동으로 엽니다.\\n' +\n `[ait-debug] DevTools URL: ${inspectorUrl}\\n` +\n '[ait-debug] (AIT_AUTO_DEVTOOLS=0 으로 자동 열기를 끌 수 있습니다)\\n' +\n '[ait-debug] 주의: URL의 at= 코드는 30초 창 안에서만 유효합니다.\\n',\n );\n\n const opened = openUrlInBrowser(inspectorUrl);\n if (!opened) {\n process.stderr.write(\n '[ait-debug] 브라우저 자동 열기 실패 — 위 URL을 브라우저에서 직접 여세요.\\n',\n );\n }\n }\n\n /** Returns `true` if `open()` has passed all guards and fired once. */\n get opened(): boolean {\n return this._opened;\n }\n}\n"],"mappings":";;;;;;AAuJA,SAAgB,yBAAkC;AAChD,QAAO,QAAQ,IAAI,sBAAsB;;;;;;;;;AAc3C,SAAgB,iBAAiB,KAAsB;AAGrD,KAAI,QAAQ,IAAI,sCAAsC,IAAK,QAAO;CAElE,MAAM,EAAE,cAAc,QAAQ,qBAAqB;CACnD,MAAM,WAAW,QAAQ;CAGzB,IAAI;AACJ,KAAI,aAAa,SACf,cAAa,CAAC;EAAE,KAAK;EAAQ,MAAM,CAAC,IAAI;EAAE,CAAC;UAClC,aAAa,QACtB,cAAa,CAAC;EAAE,KAAK;EAAO,MAAM;GAAC;GAAM;GAAS;GAAI;GAAI;EAAE,CAAC;KAG7D,cAAa;EACX;GAAE,KAAK;GAAY,MAAM,CAAC,IAAI;GAAE;EAChC;GAAE,KAAK;GAAoB,MAAM,CAAC,IAAI;GAAE;EACxC;GAAE,KAAK;GAAiB,MAAM,CAAC,IAAI;GAAE;EACtC;AAGH,MAAK,MAAM,EAAE,KAAK,UAAU,WAC1B,KAAI;EACF,MAAM,SAAS,UAAU,KAAK,MAAM;GAAE,UAAU;GAAQ,SAAS;GAAO,CAAC;AACzE,MAAI,CAAC,OAAO,SAAS,OAAO,WAAW,EAAG,QAAO;SAC3C;AAIV,QAAO"}
|
|
1
|
+
{"version":3,"file":"devtools-opener-h6A-UjzC.cjs","names":[],"sources":["../src/mcp/devtools-opener.ts"],"sourcesContent":["/**\n * Auto-opens Chrome DevTools when a page attaches over the Chii relay.\n *\n * When a real device attaches (env 2 / 3 / 4 in the 4-environments fidelity\n * ladder), the Chii relay exposes a standard CDP WebSocket endpoint. Chii\n * also self-hosts its DevTools frontend at:\n *\n * <relay-base>/front_end/chii_app.html\n * ?ws|wss=<encodeURIComponent(\"<relay-host>/client/<uuid>?target=<targetId>&at=<totp>\")>\n *\n * The param name follows the relay base scheme — `ws=` for plain HTTP\n * (env 3/4 local relay), `wss=` for HTTPS (env 2 tunnel) — matching the\n * scheme branch in chii/public/index.js.\n *\n * This is the same URL format that Chii's own index-page inspect-links use\n * (derived from `chii/public/index.js` — the JS that powers the target list\n * page at `<relay-base>/`). Opening this URL in the developer's local browser\n * gives a full Chrome DevTools UI connected to the phone via the relay.\n *\n * IMPORTANT — environment guard:\n * Auto-open only fires in relay environments (env 2 / 3 / 4). In env 1\n * (local browser + mock SDK) the developer already has F12 available; opening\n * a DevTools window pointing at the mock relay would be confusing and useless.\n * The caller (`startAttachWatcher` in `debug-server.ts`) passes the current\n * environment and this module bails out when it is `mock`.\n *\n * Opt-out: set `AIT_AUTO_DEVTOOLS=0` in the environment to suppress auto-open\n * entirely. Any other value (or absent) enables the default behaviour.\n *\n * Duplicate-open guard:\n * `AutoDevtoolsOpener` tracks whether open was already triggered for the\n * current session. The open fires at most once per instance — typically one\n * per `runDebugServer` call.\n *\n * TOTP expiry caveat:\n * The `at=` TOTP code embedded in the `wss=` parameter is minted fresh at the\n * moment `open()` is called. The code is valid for ~3 minutes (the relay gate\n * accepts ±RELAY_VERIFY_SKEW_STEPS=6 steps = 180–210 s). If the developer\n * does not open the URL within that window the WebSocket upgrade will be\n * rejected with 4401. In practice the browser opens immediately after the OS\n * `open` command; if needed the developer can copy the wss= param, replace\n * `at=`, and reload. This is documented in the JSDoc below.\n *\n * PWA (WebKit) caveat:\n * The Chii relay injects a chobitsu CDP shim into WebKit-based runtimes (env 2\n * AITC Sandbox PWA). The DevTools frontend will connect and most panels work.\n * However, WebKit does not expose the full CDP domain set that V8/Blink does,\n * so some panels (Network, Layers) may appear empty or show limited data.\n * This is a WebKit runtime constraint, not a relay or devtools-opener issue.\n *\n * Node-only: uses `child_process.spawnSync` to invoke the OS open command.\n */\n\nimport type { McpEnvironment } from './environment.js';\n\n// ---------------------------------------------------------------------------\n// Chii self-hosted DevTools frontend URL\n// ---------------------------------------------------------------------------\n\n/**\n * Assembles the Chii self-hosted DevTools inspector URL for a given relay\n * and target.\n *\n * Chii serves its own DevTools frontend at\n * `<relayHttpBaseUrl>/front_end/chii_app.html`. The `ws=` (plain HTTP relay)\n * or `wss=` (HTTPS relay) query parameter is a URL-encoded string of the form\n * `<relay-host>/client/<uuid>?target=<id>` (and optionally `&at=<totp>`) —\n * the same format used by Chii's own target list page (derived from\n * `chii/public/index.js`).\n *\n * The `at=` TOTP code is minted at call time via `mintTotp()`. It is valid\n * for ~3 minutes (relay gate accepts ±RELAY_VERIFY_SKEW_STEPS=6 steps =\n * 180–210 s). The developer must open the returned URL within that window.\n * If the window expires before the browser connects, the relay will reject the\n * WebSocket upgrade with close code 4401.\n *\n * SECRET-HANDLING: `mintTotp` returns a code, not a secret. The code is\n * embedded in the `wss=` parameter (inside the `at=` param) of the returned\n * URL. Callers MUST NOT log the returned URL to stdout (stderr is OK — it is\n * the intended fallback surface for the developer to copy the URL).\n *\n * @param relayHttpBaseUrl - Local HTTP base URL of the Chii relay, e.g.\n * `http://127.0.0.1:9100`. No trailing slash.\n * @param targetId - Chii target id (from `GET <relay>/targets`).\n * @param mintTotp - Optional function that returns a fresh 6-digit TOTP code\n * string. Called at most once. When omitted (TOTP disabled) no `at=` param\n * is added.\n * @param panel - Initial panel. Defaults to `\"console\"`.\n *\n * @example\n * buildChiiInspectorUrl(\n * 'http://127.0.0.1:9100',\n * 'abc123',\n * () => generateTotp(secret),\n * )\n * // → 'http://127.0.0.1:9100/front_end/chii_app.html?ws=127.0.0.1%3A9100%2Fclient%2F<uuid>%3Ftarget%3Dabc123%26at%3D<code>'\n */\nexport function buildChiiInspectorUrl(\n relayHttpBaseUrl: string,\n targetId: string,\n mintTotp?: () => string,\n panel: 'elements' | 'console' | 'sources' | 'network' = 'console',\n): string {\n // Extract the host (and port) from the relay HTTP base URL, and pick the\n // query param name chii_app.html expects: `ws=` dials `ws://` (plain-HTTP\n // relay — env 3/4 local 127.0.0.1) while `wss=` dials `wss://` (HTTPS\n // tunnel — env 2). chii/public/index.js does the same scheme branch:\n // `location.protocol === 'https:' ? 'wss' : 'ws'`. Always sending `wss=`\n // would make the frontend attempt TLS against the plain-HTTP local relay.\n let relayHost: string;\n let wsParamName: 'ws' | 'wss';\n try {\n const parsed = new URL(relayHttpBaseUrl);\n relayHost = parsed.host; // e.g. \"127.0.0.1:9100\"\n wsParamName = parsed.protocol === 'https:' ? 'wss' : 'ws';\n } catch {\n // Fallback: strip the scheme prefix manually if URL parsing fails.\n relayHost = relayHttpBaseUrl.replace(/^https?:\\/\\//i, '');\n wsParamName = /^https:/i.test(relayHttpBaseUrl) ? 'wss' : 'ws';\n }\n\n // Generate a client UUID that matches the format Chii's index.js uses\n // (6 random alphanumeric characters).\n const clientId = `devtools-opener-${Date.now().toString(36)}`;\n\n // Build the ws=/wss= value: \"<relay-host>/client/<uuid>?target=<id>[&at=<code>]\"\n // This mirrors the format from chii/public/index.js:\n // `${domain}${basePath}client/${randomId(6)}?target=${targetId}`\n let wsPath = `${relayHost}/client/${clientId}?target=${encodeURIComponent(targetId)}`;\n\n if (mintTotp) {\n // SECRET-HANDLING: mintTotp() returns a code (not a secret). The code\n // rides only in the URL's at= param. Callers must not log the URL.\n const code = mintTotp();\n wsPath += `&at=${encodeURIComponent(code)}`;\n }\n\n const params = new URLSearchParams({ [wsParamName]: wsPath, panel });\n return `${relayHttpBaseUrl.replace(/\\/$/, '')}/front_end/chii_app.html?${params.toString()}`;\n}\n\n// ---------------------------------------------------------------------------\n// Opt-out check\n// ---------------------------------------------------------------------------\n\n/**\n * Returns `true` when auto-open is **disabled** via the `AIT_AUTO_DEVTOOLS`\n * env var. Only the explicit `\"0\"` value disables it; anything else (including\n * absent) leaves auto-open enabled.\n */\nexport function isAutoDevtoolsDisabled(): boolean {\n return process.env.AIT_AUTO_DEVTOOLS === '0';\n}\n\n// ---------------------------------------------------------------------------\n// Browser open (Node-only, sync)\n// ---------------------------------------------------------------------------\n\n/**\n * Opens the given URL in the OS default browser using a platform-appropriate\n * command. Returns `true` on success.\n *\n * Failures are silent from the caller's perspective — the caller should log\n * the URL to stderr as a fallback before calling this function.\n */\nexport function openUrlInBrowser(url: string): boolean {\n // Test hook: skip actual spawn when running in vitest / CI where the OS open\n // command may hang or be absent. Production code never sets this.\n if (process.env.AIT_AUTO_DEVTOOLS_TEST_SKIP_SPAWN === '1') return false;\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const { spawnSync } = require('node:child_process') as typeof import('node:child_process');\n const platform = process.platform;\n\n type Candidate = { cmd: string; args: string[] };\n let candidates: Candidate[];\n if (platform === 'darwin') {\n candidates = [{ cmd: 'open', args: [url] }];\n } else if (platform === 'win32') {\n candidates = [{ cmd: 'cmd', args: ['/c', 'start', '', url] }];\n } else {\n // Linux + fallback\n candidates = [\n { cmd: 'xdg-open', args: [url] },\n { cmd: 'sensible-browser', args: [url] },\n { cmd: 'x-www-browser', args: [url] },\n ];\n }\n\n for (const { cmd, args } of candidates) {\n try {\n const result = spawnSync(cmd, args, { encoding: 'utf8', timeout: 5_000 });\n if (!result.error && result.status === 0) return true;\n } catch {\n // Try next candidate.\n }\n }\n return false;\n}\n\n// ---------------------------------------------------------------------------\n// AutoDevtoolsOpener — stateful once-per-session open guard\n// ---------------------------------------------------------------------------\n\n/**\n * Options for {@link AutoDevtoolsOpener.open}.\n *\n * The `relayHttpBaseUrl` and `targetId` fields are required to build a working\n * Chii self-hosted inspector URL. When `relayHttpBaseUrl` is absent the open\n * is skipped (no relay available yet).\n */\nexport interface DevtoolsOpenOptions {\n /**\n * Local HTTP base URL of the Chii relay, e.g. `http://127.0.0.1:9100`.\n * Used to build the `<relay-base>/front_end/chii_app.html?wss=…` URL.\n *\n * For env 3/4 (intoss relay) this is `http://127.0.0.1:<port>`.\n * For env 2 (external PWA relay) this is the relay's external HTTP URL\n * (e.g. `https://<host>.trycloudflare.com`).\n *\n * When absent or empty, `open()` is a no-op.\n *\n * SECRET-HANDLING: this value contains the relay host. Callers MUST NOT\n * log it to stdout; stderr is the intended surface.\n */\n relayHttpBaseUrl: string | null | undefined;\n /**\n * Chii target id of the attached page, from `listTargets()[0].id`.\n * When absent or empty, `open()` is a no-op.\n */\n targetId: string | null | undefined;\n /**\n * Function that mints a fresh TOTP code when called. Called at most once per\n * `open()` invocation, immediately before building the inspector URL.\n *\n * Pass `undefined` when TOTP is disabled (no `at=` param is added).\n *\n * SECRET-HANDLING: the function MUST return only the code (6 digits), not\n * the secret. The code rides in the URL's `at=` param only.\n */\n mintTotp?: () => string;\n /** Current MCP environment (`mock` | `relay`). `open()` no-ops on `mock`. */\n env: McpEnvironment;\n}\n\n/**\n * Manages auto-opening Chrome DevTools exactly once per relay attach session.\n *\n * Create one instance per `runDebugServer` call and pass its `open()` method\n * as the `onFirstAttach` callback to `startAttachWatcher`.\n *\n * The open fires at most once. Subsequent `open()` calls are no-ops.\n * Opt-out and mock-environment guard are checked at call time.\n */\nexport class AutoDevtoolsOpener {\n private _opened = false;\n\n /**\n * Attempts to auto-open Chii DevTools in the developer's browser.\n *\n * Builds a `<relay-base>/front_end/chii_app.html?wss=…` URL pointing at the\n * attached target. A fresh TOTP `at=` code is minted at call time so the\n * relay's WebSocket upgrade gate accepts the connection.\n *\n * No-op when any of the following conditions hold:\n * 1. Already opened this session (`_opened` is true).\n * 2. `AIT_AUTO_DEVTOOLS=0` opt-out is set.\n * 3. `options.env` is `mock` (env 1 — F12 is already available).\n * 4. `options.relayHttpBaseUrl` is null/undefined/empty (relay not up yet).\n * 5. `options.targetId` is null/undefined/empty (no page attached yet).\n *\n * Always writes the DevTools URL to stderr so the developer can copy it\n * if the browser open fails or the popup is blocked.\n *\n * TOTP expiry caveat: the `at=` code embedded in the URL is valid for ~3\n * minutes (relay gate ±RELAY_VERIFY_SKEW_STEPS=6 steps = 180–210 s). The\n * developer must open the URL within that window; if they miss it, reload\n * the page or re-run `open()` (though the once-per-session guard prevents\n * that — restart the MCP server if needed).\n *\n * SECRET-HANDLING: the inspector URL (written to stderr) contains the relay\n * host and a short-lived TOTP code. Do NOT write it to stdout or any\n * persistent log.\n */\n open(options: DevtoolsOpenOptions): void {\n if (this._opened) return;\n if (isAutoDevtoolsDisabled()) return;\n if (options.env === 'mock') return;\n if (!options.relayHttpBaseUrl) return;\n if (!options.targetId) return;\n\n this._opened = true;\n\n const inspectorUrl = buildChiiInspectorUrl(\n options.relayHttpBaseUrl,\n options.targetId,\n options.mintTotp,\n );\n\n process.stderr.write(\n '[ait-debug] 기기가 연결됐습니다 — Chii DevTools를 자동으로 엽니다.\\n' +\n `[ait-debug] DevTools URL: ${inspectorUrl}\\n` +\n '[ait-debug] (AIT_AUTO_DEVTOOLS=0 으로 자동 열기를 끌 수 있습니다)\\n' +\n '[ait-debug] 주의: URL의 at= 코드는 ~3분 안에서만 유효합니다.\\n',\n );\n\n const opened = openUrlInBrowser(inspectorUrl);\n if (!opened) {\n process.stderr.write(\n '[ait-debug] 브라우저 자동 열기 실패 — 위 URL을 브라우저에서 직접 여세요.\\n',\n );\n }\n }\n\n /** Returns `true` if `open()` has passed all guards and fired once. */\n get opened(): boolean {\n return this._opened;\n }\n}\n"],"mappings":";;;;;;AAsJA,SAAgB,yBAAkC;AAChD,QAAO,QAAQ,IAAI,sBAAsB;;;;;;;;;AAc3C,SAAgB,iBAAiB,KAAsB;AAGrD,KAAI,QAAQ,IAAI,sCAAsC,IAAK,QAAO;CAElE,MAAM,EAAE,cAAc,QAAQ,qBAAqB;CACnD,MAAM,WAAW,QAAQ;CAGzB,IAAI;AACJ,KAAI,aAAa,SACf,cAAa,CAAC;EAAE,KAAK;EAAQ,MAAM,CAAC,IAAI;EAAE,CAAC;UAClC,aAAa,QACtB,cAAa,CAAC;EAAE,KAAK;EAAO,MAAM;GAAC;GAAM;GAAS;GAAI;GAAI;EAAE,CAAC;KAG7D,cAAa;EACX;GAAE,KAAK;GAAY,MAAM,CAAC,IAAI;GAAE;EAChC;GAAE,KAAK;GAAoB,MAAM,CAAC,IAAI;GAAE;EACxC;GAAE,KAAK;GAAiB,MAAM,CAAC,IAAI;GAAE;EACtC;AAGH,MAAK,MAAM,EAAE,KAAK,UAAU,WAC1B,KAAI;EACF,MAAM,SAAS,UAAU,KAAK,MAAM;GAAE,UAAU;GAAQ,SAAS;GAAO,CAAC;AACzE,MAAI,CAAC,OAAO,SAAS,OAAO,WAAW,EAAG,QAAO;SAC3C;AAIV,QAAO"}
|
package/dist/mcp/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { i as generateTotp, n as assertRelayAuthConfigured, r as buildRelayVerifyAuth } from "../totp-
|
|
3
|
-
import { t as loadRelaySecretReadOnly } from "../relay-secret-store-
|
|
2
|
+
import { i as generateTotp, n as assertRelayAuthConfigured, r as buildRelayVerifyAuth } from "../totp-BfVk8gQe.js";
|
|
3
|
+
import { t as loadRelaySecretReadOnly } from "../relay-secret-store-C_LUxvAp.js";
|
|
4
4
|
import { createRequire } from "node:module";
|
|
5
5
|
import { existsSync, mkdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from "node:fs";
|
|
6
6
|
import { argv } from "node:process";
|
|
@@ -1012,6 +1012,11 @@ const LAUNCHER_URL = "https://devtools.aitc.dev/launcher/";
|
|
|
1012
1012
|
* is injected. `&at=<totpCode>` is added only when a code is provided (same
|
|
1013
1013
|
* conditional as {@link buildDeepLinkAttachUrl}).
|
|
1014
1014
|
*
|
|
1015
|
+
* When `opts.name` is given (non-blank), it is added as `&name=` so the
|
|
1016
|
+
* launcher partner bar shows the app name instead of the generic default (#498).
|
|
1017
|
+
* When `opts.icon` is an absolute https:// URL, it is added as `&icon=` so the
|
|
1018
|
+
* launcher can render an icon next to the title (#498).
|
|
1019
|
+
*
|
|
1015
1020
|
* Unlike `buildDeepLinkAttachUrl` (which splices onto a non-special scheme URL
|
|
1016
1021
|
* via raw string manipulation), this function uses WHATWG `encodeURIComponent`
|
|
1017
1022
|
* because the target is a standard `https:` URL.
|
|
@@ -1026,12 +1031,23 @@ const LAUNCHER_URL = "https://devtools.aitc.dev/launcher/";
|
|
|
1026
1031
|
* @param totpCode - Optional current TOTP code (6 digits). When provided, it
|
|
1027
1032
|
* is appended as `at=<totpCode>`. Must be computed at call time — it rotates
|
|
1028
1033
|
* every 30 s. Omit when TOTP is disabled.
|
|
1034
|
+
* @param opts - Optional app identity hints: `name` and `icon` (#498).
|
|
1029
1035
|
* @returns The launcher deep-link URL with `?url=<enc>&debug=1&relay=<enc>
|
|
1030
|
-
* [&at=<code>]` params.
|
|
1036
|
+
* [&at=<code>][&name=<enc>][&icon=<enc>]` params.
|
|
1031
1037
|
*/
|
|
1032
|
-
function buildLauncherAttachUrl(tunnelUrl, wssUrl, totpCode) {
|
|
1038
|
+
function buildLauncherAttachUrl(tunnelUrl, wssUrl, totpCode, opts) {
|
|
1033
1039
|
let url = `${LAUNCHER_URL}?url=${encodeURIComponent(tunnelUrl)}&debug=1&relay=${encodeURIComponent(wssUrl)}`;
|
|
1034
1040
|
if (totpCode !== void 0 && totpCode !== "") url += `&at=${encodeURIComponent(totpCode)}`;
|
|
1041
|
+
if (opts?.name !== void 0 && opts.name.trim() !== "") url += `&name=${encodeURIComponent(opts.name.trim())}`;
|
|
1042
|
+
if (opts?.icon !== void 0) {
|
|
1043
|
+
let iconParsed;
|
|
1044
|
+
try {
|
|
1045
|
+
iconParsed = new URL(opts.icon);
|
|
1046
|
+
} catch {
|
|
1047
|
+
iconParsed = null;
|
|
1048
|
+
}
|
|
1049
|
+
if (iconParsed?.protocol === "https:") url += `&icon=${encodeURIComponent(opts.icon)}`;
|
|
1050
|
+
}
|
|
1035
1051
|
return url;
|
|
1036
1052
|
}
|
|
1037
1053
|
/**
|
|
@@ -1158,9 +1174,9 @@ function buildDeepLinkAttachUrl(schemeUrl, wssUrl, totpCode) {
|
|
|
1158
1174
|
* `chii/public/index.js`).
|
|
1159
1175
|
*
|
|
1160
1176
|
* The `at=` TOTP code is minted at call time via `mintTotp()`. It is valid
|
|
1161
|
-
* for
|
|
1162
|
-
*
|
|
1163
|
-
* the window expires before the browser connects, the relay will reject the
|
|
1177
|
+
* for ~3 minutes (relay gate accepts ±RELAY_VERIFY_SKEW_STEPS=6 steps =
|
|
1178
|
+
* 180–210 s). The developer must open the returned URL within that window.
|
|
1179
|
+
* If the window expires before the browser connects, the relay will reject the
|
|
1164
1180
|
* WebSocket upgrade with close code 4401.
|
|
1165
1181
|
*
|
|
1166
1182
|
* SECRET-HANDLING: `mintTotp` returns a code, not a secret. The code is
|
|
@@ -1291,11 +1307,11 @@ var AutoDevtoolsOpener = class {
|
|
|
1291
1307
|
* Always writes the DevTools URL to stderr so the developer can copy it
|
|
1292
1308
|
* if the browser open fails or the popup is blocked.
|
|
1293
1309
|
*
|
|
1294
|
-
* TOTP expiry caveat: the `at=` code embedded in the URL is valid for
|
|
1295
|
-
*
|
|
1296
|
-
* the URL within that window; if they miss it, reload
|
|
1297
|
-
* `open()` (though the once-per-session guard prevents
|
|
1298
|
-
* MCP server if needed).
|
|
1310
|
+
* TOTP expiry caveat: the `at=` code embedded in the URL is valid for ~3
|
|
1311
|
+
* minutes (relay gate ±RELAY_VERIFY_SKEW_STEPS=6 steps = 180–210 s). The
|
|
1312
|
+
* developer must open the URL within that window; if they miss it, reload
|
|
1313
|
+
* the page or re-run `open()` (though the once-per-session guard prevents
|
|
1314
|
+
* that — restart the MCP server if needed).
|
|
1299
1315
|
*
|
|
1300
1316
|
* SECRET-HANDLING: the inspector URL (written to stderr) contains the relay
|
|
1301
1317
|
* host and a short-lived TOTP code. Do NOT write it to stdout or any
|
|
@@ -1311,7 +1327,7 @@ var AutoDevtoolsOpener = class {
|
|
|
1311
1327
|
const inspectorUrl = buildChiiInspectorUrl(options.relayHttpBaseUrl, options.targetId, options.mintTotp);
|
|
1312
1328
|
process.stderr.write(`[ait-debug] 기기가 연결됐습니다 — Chii DevTools를 자동으로 엽니다.
|
|
1313
1329
|
[ait-debug] DevTools URL: ${inspectorUrl}\n[ait-debug] (AIT_AUTO_DEVTOOLS=0 으로 자동 열기를 끌 수 있습니다)
|
|
1314
|
-
[ait-debug] 주의: URL의 at= 코드는
|
|
1330
|
+
[ait-debug] 주의: URL의 at= 코드는 ~3분 안에서만 유효합니다.
|
|
1315
1331
|
`);
|
|
1316
1332
|
if (!openUrlInBrowser(inspectorUrl)) process.stderr.write("[ait-debug] 브라우저 자동 열기 실패 — 위 URL을 브라우저에서 직접 여세요.\n");
|
|
1317
1333
|
}
|
|
@@ -2093,7 +2109,7 @@ const en = {
|
|
|
2093
2109
|
"attach.sandbox.step3": "The mini-app opens fullscreen and the debug session attaches automatically.",
|
|
2094
2110
|
"attach.sandbox.faq.notInstalled": "<strong>Launcher is not installed</strong> — open <code>devtools.aitc.dev/launcher/</code> once and add it to your home screen",
|
|
2095
2111
|
"attach.sandbox.faq.cameraApp": "<strong>Scanning with the camera app opens a Safari tab (bottom tab bar visible)</strong> — relaunch from the launcher icon and use the in-app scanner",
|
|
2096
|
-
"attach.sandbox.faq.totp": "<strong>QR expired (TOTP
|
|
2112
|
+
"attach.sandbox.faq.totp": "<strong>QR expired (TOTP ~3 min)</strong> — scan a fresh QR code",
|
|
2097
2113
|
"attach.sandbox.faq.chii": "<strong>Chii injection failure / console is empty</strong> — verify the mini-app bundle has an <code>in-app</code> debug import",
|
|
2098
2114
|
"attach.intoss.step1": "Open the Toss app.",
|
|
2099
2115
|
"attach.intoss.step2": "Scan the QR code with your phone camera app.",
|
|
@@ -2110,7 +2126,6 @@ const en = {
|
|
|
2110
2126
|
"launcher.urlPlaceholder": "https://example.trycloudflare.com",
|
|
2111
2127
|
"launcher.openBtn": "Open",
|
|
2112
2128
|
"launcher.scanBtn": "Scan QR with camera",
|
|
2113
|
-
"launcher.rescanBtn": "Rescan",
|
|
2114
2129
|
"launcher.noCamera": "No camera available — paste the URL instead.",
|
|
2115
2130
|
"launcher.cameraError": "Could not access the camera — paste the URL instead.",
|
|
2116
2131
|
"launcher.invalidUrlHttps": "Enter a valid https:// URL (the tunnel URL from your terminal).",
|
|
@@ -2119,11 +2134,16 @@ const en = {
|
|
|
2119
2134
|
"launcher.debugAuthFailedHint": "The QR code may have expired. Scan a fresh QR code.",
|
|
2120
2135
|
"launcher.debugAuthExpiredHint": "The debug session has expired. Scan a fresh QR from the attach page on your Mac.",
|
|
2121
2136
|
"launcher.debugAuthRescanCta": "Scan a new QR",
|
|
2122
|
-
"launcher.diagFab": "Diag",
|
|
2123
2137
|
"launcher.diagTitle": "Viewport diagnostics",
|
|
2124
2138
|
"launcher.diagYes": "yes",
|
|
2125
2139
|
"launcher.diagNo": "no",
|
|
2126
|
-
"launcher.letterboxDetected": "Display area is {pt}pt short — likely an iOS standalone letterbox. Removing and re-adding the launcher to the home screen may fix it."
|
|
2140
|
+
"launcher.letterboxDetected": "Display area is {pt}pt short — likely an iOS standalone letterbox. Removing and re-adding the launcher to the home screen may fix it.",
|
|
2141
|
+
"launcher.navbar.defaultTitle": "Mini App",
|
|
2142
|
+
"launcher.navbar.menu": "Menu",
|
|
2143
|
+
"launcher.navbar.close": "Close",
|
|
2144
|
+
"launcher.navbar.menuRescan": "Rescan",
|
|
2145
|
+
"launcher.navbar.menuDiag": "Viewport diagnostics",
|
|
2146
|
+
"launcher.navbar.menuLanguage": "Language"
|
|
2127
2147
|
};
|
|
2128
2148
|
//#endregion
|
|
2129
2149
|
//#region src/i18n/index.ts
|
|
@@ -2337,7 +2357,7 @@ const tables = {
|
|
|
2337
2357
|
"attach.sandbox.step3": "미니앱이 풀스크린으로 열리고 디버그 세션이 자동으로 attach됩니다.",
|
|
2338
2358
|
"attach.sandbox.faq.notInstalled": "<strong>launcher가 설치돼 있지 않은 경우</strong> — <code>devtools.aitc.dev/launcher/</code>를 한 번 열어 홈 화면에 추가하세요",
|
|
2339
2359
|
"attach.sandbox.faq.cameraApp": "<strong>카메라 앱으로 스캔하면 Safari 탭으로 열립니다 (하단 탭 바 노출)</strong> — launcher 아이콘으로 다시 실행해 인앱 스캔을 사용하세요",
|
|
2340
|
-
"attach.sandbox.faq.totp": "<strong>QR이 만료된 경우 (TOTP
|
|
2360
|
+
"attach.sandbox.faq.totp": "<strong>QR이 만료된 경우 (TOTP ~3분)</strong> — 새 QR을 다시 스캔하세요",
|
|
2341
2361
|
"attach.sandbox.faq.chii": "<strong>Chii 주입 실패 / 콘솔이 비어 있는 경우</strong> — 미니앱 번들에 <code>in-app</code> debug import가 있는지 확인",
|
|
2342
2362
|
"attach.intoss.step1": "토스 앱을 실행하세요.",
|
|
2343
2363
|
"attach.intoss.step2": "폰 카메라 앱으로 QR 코드를 스캔하세요.",
|
|
@@ -2354,7 +2374,6 @@ const tables = {
|
|
|
2354
2374
|
"launcher.urlPlaceholder": "https://example.trycloudflare.com",
|
|
2355
2375
|
"launcher.openBtn": "Open",
|
|
2356
2376
|
"launcher.scanBtn": "QR 카메라로 스캔",
|
|
2357
|
-
"launcher.rescanBtn": "Rescan",
|
|
2358
2377
|
"launcher.noCamera": "카메라를 사용할 수 없습니다 — URL을 직접 붙여넣으세요.",
|
|
2359
2378
|
"launcher.cameraError": "카메라에 접근할 수 없습니다 — URL을 직접 붙여넣으세요.",
|
|
2360
2379
|
"launcher.invalidUrlHttps": "올바른 https:// URL을 입력하세요 (터미널의 터널 URL).",
|
|
@@ -2363,11 +2382,16 @@ const tables = {
|
|
|
2363
2382
|
"launcher.debugAuthFailedHint": "QR 코드가 만료되었을 수 있어요. 새 QR을 다시 스캔하세요.",
|
|
2364
2383
|
"launcher.debugAuthExpiredHint": "디버그 세션이 만료됐어요. Mac의 attach 페이지에서 새 QR을 스캔하세요.",
|
|
2365
2384
|
"launcher.debugAuthRescanCta": "새 QR 스캔하기",
|
|
2366
|
-
"launcher.diagFab": "진단",
|
|
2367
2385
|
"launcher.diagTitle": "뷰포트 진단",
|
|
2368
2386
|
"launcher.diagYes": "예",
|
|
2369
2387
|
"launcher.diagNo": "아니요",
|
|
2370
|
-
"launcher.letterboxDetected": "표시 영역이 {pt}pt 부족합니다 — iOS standalone letterbox로 보입니다. 런처를 홈 화면에서 제거 후 다시 설치하면 해소될 수 있어요."
|
|
2388
|
+
"launcher.letterboxDetected": "표시 영역이 {pt}pt 부족합니다 — iOS standalone letterbox로 보입니다. 런처를 홈 화면에서 제거 후 다시 설치하면 해소될 수 있어요.",
|
|
2389
|
+
"launcher.navbar.defaultTitle": "미니앱",
|
|
2390
|
+
"launcher.navbar.menu": "메뉴",
|
|
2391
|
+
"launcher.navbar.close": "닫기",
|
|
2392
|
+
"launcher.navbar.menuRescan": "다시 스캔",
|
|
2393
|
+
"launcher.navbar.menuDiag": "뷰포트 진단",
|
|
2394
|
+
"launcher.navbar.menuLanguage": "언어"
|
|
2371
2395
|
},
|
|
2372
2396
|
en
|
|
2373
2397
|
};
|
|
@@ -2514,7 +2538,7 @@ hr { border: none; border-top: 1px solid #21262d; width: 100%; margin: 0.5rem 0;
|
|
|
2514
2538
|
.lang-switcher { display: flex; gap: 0.5rem; font-size: 0.75rem; }
|
|
2515
2539
|
.lang-switcher a { color: #58a6ff; text-decoration: none; opacity: 0.6; }
|
|
2516
2540
|
.lang-switcher a.active { font-weight: 700; text-decoration: underline; opacity: 1; }
|
|
2517
|
-
</style></head><body><h1>AIT 디버그 세션 — QR 스캔</h1>__MODE_LABEL____LANG_SWITCHER__<div id="attach-section"><img class="qr" src="__QR_DATA_URL__" alt="attach QR"/></div><section><h2>스캔 절차</h2><ol><li>홈 화면의 launcher PWA 아이콘으로 실행하세요 (Safari 주소창이 보이면 standalone이 아닙니다).</li><li>launcher 안의 <strong>"QR 카메라로 스캔"</strong>으로 이 QR 코드를 스캔하세요.</li><li>미니앱이 풀스크린으로 열리고 디버그 세션이 자동으로 attach됩니다.</li></ol></section><hr/><section><h2>진단 체크리스트</h2><ul><li><strong>launcher가 설치돼 있지 않은 경우</strong> — <code>devtools.aitc.dev/launcher/</code>를 한 번 열어 홈 화면에 추가하세요</li><li><strong>카메라 앱으로 스캔하면 Safari 탭으로 열립니다 (하단 탭 바 노출)</strong> — launcher 아이콘으로 다시 실행해 인앱 스캔을 사용하세요</li><li><strong>QR이 만료된 경우 (TOTP
|
|
2541
|
+
</style></head><body><h1>AIT 디버그 세션 — QR 스캔</h1>__MODE_LABEL____LANG_SWITCHER__<div id="attach-section"><img class="qr" src="__QR_DATA_URL__" alt="attach QR"/></div><section><h2>스캔 절차</h2><ol><li>홈 화면의 launcher PWA 아이콘으로 실행하세요 (Safari 주소창이 보이면 standalone이 아닙니다).</li><li>launcher 안의 <strong>"QR 카메라로 스캔"</strong>으로 이 QR 코드를 스캔하세요.</li><li>미니앱이 풀스크린으로 열리고 디버그 세션이 자동으로 attach됩니다.</li></ol></section><hr/><section><h2>진단 체크리스트</h2><ul><li><strong>launcher가 설치돼 있지 않은 경우</strong> — <code>devtools.aitc.dev/launcher/</code>를 한 번 열어 홈 화면에 추가하세요</li><li><strong>카메라 앱으로 스캔하면 Safari 탭으로 열립니다 (하단 탭 바 노출)</strong> — launcher 아이콘으로 다시 실행해 인앱 스캔을 사용하세요</li><li><strong>QR이 만료된 경우 (TOTP ~3분)</strong> — 새 QR을 다시 스캔하세요</li><li><strong>Chii 주입 실패 / 콘솔이 비어 있는 경우</strong> — 미니앱 번들에 <code>in-app</code> debug import가 있는지 확인</li></ul></section><hr/><section id="url-section"><h2>URL (fallback)</h2><div class="url-row"><p class="url-box" id="url-box">__SAFE_ATTACH_URL__</p><button class="copy-btn" id="copy-btn" type="button" aria-label="복사">복사</button></div></section></body></html>`;
|
|
2518
2542
|
const attachChromeHtmlKoIntoss = `<!DOCTYPE html>
|
|
2519
2543
|
<html lang="ko"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" as="image" href="__QR_DATA_URL__"/><title>AIT 디버그 세션 — QR 스캔</title><style>
|
|
2520
2544
|
*, *::before, *::after { box-sizing: border-box; }
|
|
@@ -2666,7 +2690,7 @@ hr { border: none; border-top: 1px solid #21262d; width: 100%; margin: 0.5rem 0;
|
|
|
2666
2690
|
.lang-switcher { display: flex; gap: 0.5rem; font-size: 0.75rem; }
|
|
2667
2691
|
.lang-switcher a { color: #58a6ff; text-decoration: none; opacity: 0.6; }
|
|
2668
2692
|
.lang-switcher a.active { font-weight: 700; text-decoration: underline; opacity: 1; }
|
|
2669
|
-
</style></head><body><h1>AIT Debug Session — QR Scan</h1>__MODE_LABEL____LANG_SWITCHER__<div id="attach-section"><img class="qr" src="__QR_DATA_URL__" alt="attach QR"/></div><section><h2>How to scan</h2><ol><li>Launch the launcher PWA icon on your home screen (if the Safari address bar is visible, it is not standalone).</li><li>Scan this QR code with <strong>"Scan QR with camera"</strong> inside the launcher.</li><li>The mini-app opens fullscreen and the debug session attaches automatically.</li></ol></section><hr/><section><h2>Troubleshooting checklist</h2><ul><li><strong>Launcher is not installed</strong> — open <code>devtools.aitc.dev/launcher/</code> once and add it to your home screen</li><li><strong>Scanning with the camera app opens a Safari tab (bottom tab bar visible)</strong> — relaunch from the launcher icon and use the in-app scanner</li><li><strong>QR expired (TOTP
|
|
2693
|
+
</style></head><body><h1>AIT Debug Session — QR Scan</h1>__MODE_LABEL____LANG_SWITCHER__<div id="attach-section"><img class="qr" src="__QR_DATA_URL__" alt="attach QR"/></div><section><h2>How to scan</h2><ol><li>Launch the launcher PWA icon on your home screen (if the Safari address bar is visible, it is not standalone).</li><li>Scan this QR code with <strong>"Scan QR with camera"</strong> inside the launcher.</li><li>The mini-app opens fullscreen and the debug session attaches automatically.</li></ol></section><hr/><section><h2>Troubleshooting checklist</h2><ul><li><strong>Launcher is not installed</strong> — open <code>devtools.aitc.dev/launcher/</code> once and add it to your home screen</li><li><strong>Scanning with the camera app opens a Safari tab (bottom tab bar visible)</strong> — relaunch from the launcher icon and use the in-app scanner</li><li><strong>QR expired (TOTP ~3 min)</strong> — scan a fresh QR code</li><li><strong>Chii injection failure / console is empty</strong> — verify the mini-app bundle has an <code>in-app</code> debug import</li></ul></section><hr/><section id="url-section"><h2>URL (fallback)</h2><div class="url-row"><p class="url-box" id="url-box">__SAFE_ATTACH_URL__</p><button class="copy-btn" id="copy-btn" type="button" aria-label="Copy">Copy</button></div></section></body></html>`;
|
|
2670
2694
|
const attachChromeHtmlEnIntoss = `<!DOCTYPE html>
|
|
2671
2695
|
<html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" as="image" href="__QR_DATA_URL__"/><title>AIT Debug Session — QR Scan</title><style>
|
|
2672
2696
|
*, *::before, *::after { box-sizing: border-box; }
|
|
@@ -3552,7 +3576,7 @@ const DEBUG_TOOL_DEFINITIONS = [
|
|
|
3552
3576
|
},
|
|
3553
3577
|
{
|
|
3554
3578
|
name: "build_attach_url",
|
|
3555
|
-
description: "The tool result already shows the QR to the user directly (Claude Code renders MCP tool output to the user's screen; they press Ctrl+O to expand if it's collapsed). Do NOT re-print or re-render the QR in your reply — that just wastes output tokens. Simply tell the user to scan the QR shown in this tool's output with their phone camera. Builds a self-attaching deep link for the active relay environment and returns a QR code. Scan the QR with the phone camera to open the mini-app and attach it to this debug session (QR is the single entry path — no USB cable or platform CLI needed). Call list_pages first to confirm the relay/tunnel is up. If the tunnel is not up, restart: `npx @ait-co/devtools devtools-mcp`.\n\nEnvironment-specific behaviour:\n • env 3 / relay-staging (start_debug mode=\"relay-staging\"): requires scheme_url — the intoss-private://…?_deploymentId=<uuid> URL from `ait deploy --scheme-only`. Splices debug=1 + relay URL into the scheme URL to produce a self-attach deep link.\n • env 2 / relay-sandbox (start_debug mode=\"relay-sandbox\"): scheme_url is NOT used. Instead, reads AIT_TUNNEL_BASE_URL (the https://*.trycloudflare.com app tunnel from `tunnel:{cdp:true}`) and builds a launcher PWA deep-link (https://devtools.aitc.dev/launcher/?url=…&debug=1&relay=…). Scan the QR with the phone to open the launcher, which frames the tunnel URL and attaches CDP.\n\nSet wait_for_attach=true to block until a page attaches (polls up to 30 s). On timeout, call build_attach_url again to resume polling. When open_in_browser=true (default), saves the QR as a PNG and opens it in the OS default browser — only works when the MCP server runs on a local GUI machine (not headless/remote containers). \n\nTOTP auth: when AIT_DEBUG_TOTP_SECRET is set on the MCP server, the returned attachUrl automatically includes the current one-time code (at=<code>)
|
|
3579
|
+
description: "The tool result already shows the QR to the user directly (Claude Code renders MCP tool output to the user's screen; they press Ctrl+O to expand if it's collapsed). Do NOT re-print or re-render the QR in your reply — that just wastes output tokens. Simply tell the user to scan the QR shown in this tool's output with their phone camera. Builds a self-attaching deep link for the active relay environment and returns a QR code. Scan the QR with the phone camera to open the mini-app and attach it to this debug session (QR is the single entry path — no USB cable or platform CLI needed). Call list_pages first to confirm the relay/tunnel is up. If the tunnel is not up, restart: `npx @ait-co/devtools devtools-mcp`.\n\nEnvironment-specific behaviour:\n • env 3 / relay-staging (start_debug mode=\"relay-staging\"): requires scheme_url — the intoss-private://…?_deploymentId=<uuid> URL from `ait deploy --scheme-only`. Splices debug=1 + relay URL into the scheme URL to produce a self-attach deep link.\n • env 2 / relay-sandbox (start_debug mode=\"relay-sandbox\"): scheme_url is NOT used. Instead, reads AIT_TUNNEL_BASE_URL (the https://*.trycloudflare.com app tunnel from `tunnel:{cdp:true}`) and builds a launcher PWA deep-link (https://devtools.aitc.dev/launcher/?url=…&debug=1&relay=…). When projectRoot is given, the app name from <projectRoot>/package.json is automatically added as name= so the launcher partner bar shows it. Scan the QR with the phone to open the launcher, which frames the tunnel URL and attaches CDP.\n\nSet wait_for_attach=true to block until a page attaches (polls up to 30 s). On timeout, call build_attach_url again to resume polling. When open_in_browser=true (default), saves the QR as a PNG and opens it in the OS default browser — only works when the MCP server runs on a local GUI machine (not headless/remote containers). \n\nTOTP auth: when AIT_DEBUG_TOTP_SECRET is set on the MCP server, the returned attachUrl automatically includes the current one-time code (at=<code>). The code is valid for ~3 minutes (the relay gate accepts ±6 TOTP steps = 180–210 s of backwards acceptance). The response includes a `totp` field with `expiresAt` (ISO timestamp, ~3 min from issuance). If the phone scan happens after expiresAt, the relay will reject the code — just call build_attach_url again to get a fresh URL. Without AIT_DEBUG_TOTP_SECRET, the attachUrl has no expiry.",
|
|
3556
3580
|
inputSchema: {
|
|
3557
3581
|
type: "object",
|
|
3558
3582
|
properties: {
|
|
@@ -3904,10 +3928,10 @@ function listPages(connection, tunnel) {
|
|
|
3904
3928
|
* relay URL to splice in) — the caller surfaces that as a tool error.
|
|
3905
3929
|
*
|
|
3906
3930
|
* When `AIT_DEBUG_TOTP_SECRET` is set, generates the current TOTP code and
|
|
3907
|
-
* splices it as `at=<code>` into the attach URL. The code is valid for
|
|
3908
|
-
*
|
|
3909
|
-
*
|
|
3910
|
-
* `build_attach_url` again to get a fresh code.
|
|
3931
|
+
* splices it as `at=<code>` into the attach URL. The code is valid for ~3
|
|
3932
|
+
* minutes (the relay gate uses {@link RELAY_VERIFY_SKEW_STEPS}=6, accepting
|
|
3933
|
+
* past 6 steps = 180–210 s backwards from issuance). If the scan happens after
|
|
3934
|
+
* `totp.expiresAt`, call `build_attach_url` again to get a fresh code (#490).
|
|
3911
3935
|
*
|
|
3912
3936
|
* Also validates the scheme URL's authority. A suspicious authority (empty,
|
|
3913
3937
|
* "web", "localhost", etc.) is surfaced as a non-fatal `authorityWarning` on
|
|
@@ -3935,10 +3959,10 @@ function buildAttachUrl(schemeUrl, tunnel, totpSecret) {
|
|
|
3935
3959
|
const now = Date.now();
|
|
3936
3960
|
totpCode = generateTotp(totpSecret, now);
|
|
3937
3961
|
const STEP_SECONDS = 30;
|
|
3938
|
-
const expiresAtMs =
|
|
3962
|
+
const expiresAtMs = now + 6 * STEP_SECONDS * 1e3;
|
|
3939
3963
|
totpMeta = {
|
|
3940
3964
|
enabled: true,
|
|
3941
|
-
ttlSeconds: STEP_SECONDS,
|
|
3965
|
+
ttlSeconds: 6 * STEP_SECONDS,
|
|
3942
3966
|
expiresAt: new Date(expiresAtMs).toISOString()
|
|
3943
3967
|
};
|
|
3944
3968
|
}
|
|
@@ -4566,7 +4590,7 @@ async function readMcpSdkVersion() {
|
|
|
4566
4590
|
* some test environments that skip the build step).
|
|
4567
4591
|
*/
|
|
4568
4592
|
function readDevtoolsVersion() {
|
|
4569
|
-
return "0.1.
|
|
4593
|
+
return "0.1.73";
|
|
4570
4594
|
}
|
|
4571
4595
|
/**
|
|
4572
4596
|
* Derives the next recommended action from a completed diagnostics snapshot.
|
|
@@ -4602,7 +4626,7 @@ function computeNextRecommendedAction(tunnel, pages, env, authRejects = null) {
|
|
|
4602
4626
|
};
|
|
4603
4627
|
if (authRejects !== null && authRejects.count > 0 && pages !== null && pages.pages.length === 0) return {
|
|
4604
4628
|
tool: "build_attach_url",
|
|
4605
|
-
reason: `relay 인증(TOTP) 거부 ${authRejects.count}건 발생 (last ${authRejects.lastAt ?? "unknown"}) — QR을 다시 스캔해 새 코드로 attach하세요(코드는
|
|
4629
|
+
reason: `relay 인증(TOTP) 거부 ${authRejects.count}건 발생 (last ${authRejects.lastAt ?? "unknown"}) — QR을 다시 스캔해 새 코드로 attach하세요(코드는 ~3분마다 만료). 반복되면 폰 페이지 URL에 at 파라미터가 전달되는지(target-side TOTP 전달 경로)를 확인하세요`
|
|
4606
4630
|
};
|
|
4607
4631
|
if (isRelayEnv(env) && pages !== null && pages.pages.length === 0 && !pages.crashDetectedAt) return {
|
|
4608
4632
|
tool: "build_attach_url",
|
|
@@ -5070,7 +5094,7 @@ function createDebugServer(deps) {
|
|
|
5070
5094
|
const collector = collectorDep ?? new InMemoryDiagnosticsCollector();
|
|
5071
5095
|
const server = new Server({
|
|
5072
5096
|
name: "ait-debug",
|
|
5073
|
-
version: "0.1.
|
|
5097
|
+
version: "0.1.73"
|
|
5074
5098
|
}, { capabilities: { tools: { listChanged: true } } });
|
|
5075
5099
|
server.setRequestHandler(ListToolsRequestSchema, () => {
|
|
5076
5100
|
const conn = router.active;
|
|
@@ -5149,7 +5173,7 @@ function createDebugServer(deps) {
|
|
|
5149
5173
|
const buildProjectRoot = typeof rawBuildProjectRoot === "string" ? rawBuildProjectRoot : void 0;
|
|
5150
5174
|
let tunnelHttpUrl = process.env.AIT_TUNNEL_BASE_URL?.trim() ?? "";
|
|
5151
5175
|
if (tunnelHttpUrl === "" && buildProjectRoot !== void 0) {
|
|
5152
|
-
const { readRelayUrls } = await import("../relay-url-store-
|
|
5176
|
+
const { readRelayUrls } = await import("../relay-url-store-DjKJJZ0d.js");
|
|
5153
5177
|
tunnelHttpUrl = (await readRelayUrls({ projectRoot: buildProjectRoot }))?.tunnelBaseUrl ?? "";
|
|
5154
5178
|
}
|
|
5155
5179
|
if (tunnelHttpUrl === "") return mcpError("build_attach_url(mobile): AIT_TUNNEL_BASE_URL이 설정되지 않았습니다. dev 서버가 tunnel:{cdp:true}로 기동 중이면 .ait_urls 파일이 자동 생성돼 있어야 합니다. 자동 발견이 되지 않을 경우 앱 HTTP 터널 URL을 AIT_TUNNEL_BASE_URL 환경변수로 직접 전달하세요.");
|
|
@@ -5163,18 +5187,27 @@ function createDebugServer(deps) {
|
|
|
5163
5187
|
const now = Date.now();
|
|
5164
5188
|
totpCode = generateTotp(secret, now);
|
|
5165
5189
|
const STEP_SECONDS = 30;
|
|
5166
|
-
const
|
|
5190
|
+
const expiresAtMs = now + 6 * STEP_SECONDS * 1e3;
|
|
5167
5191
|
totpMeta = {
|
|
5168
5192
|
enabled: true,
|
|
5169
|
-
ttlSeconds: STEP_SECONDS,
|
|
5170
|
-
expiresAt:
|
|
5193
|
+
ttlSeconds: 6 * STEP_SECONDS,
|
|
5194
|
+
expiresAt: new Date(expiresAtMs).toISOString()
|
|
5171
5195
|
};
|
|
5172
5196
|
}
|
|
5173
|
-
|
|
5197
|
+
let launcherAppName;
|
|
5198
|
+
if (buildProjectRoot !== void 0) try {
|
|
5199
|
+
const { readFileSync } = await import("node:fs");
|
|
5200
|
+
const pkgRaw = readFileSync(`${buildProjectRoot}/package.json`, "utf8");
|
|
5201
|
+
const pkg = JSON.parse(pkgRaw);
|
|
5202
|
+
const rawName = typeof pkg.name === "string" ? pkg.name : "";
|
|
5203
|
+
launcherAppName = (rawName.includes("/") ? rawName.slice(rawName.indexOf("/") + 1) : rawName).trim() || void 0;
|
|
5204
|
+
} catch {}
|
|
5205
|
+
const attachUrl = buildLauncherAttachUrl(tunnelHttpUrl, tunnelStatus.wssUrl, totpCode, { name: launcherAppName });
|
|
5174
5206
|
onAttachUrlBuilt?.({
|
|
5175
5207
|
kind: "launcher",
|
|
5176
5208
|
tunnelHttpUrl,
|
|
5177
|
-
wssUrl: tunnelStatus.wssUrl
|
|
5209
|
+
wssUrl: tunnelStatus.wssUrl,
|
|
5210
|
+
appName: launcherAppName
|
|
5178
5211
|
});
|
|
5179
5212
|
const relayUrl = tunnelStatus.wssUrl;
|
|
5180
5213
|
const totp = totpMeta;
|
|
@@ -5610,7 +5643,7 @@ function makeSingleConnectionRouter(connection) {
|
|
|
5610
5643
|
function rebuildAttachUrl(parts) {
|
|
5611
5644
|
const secret = process.env.AIT_DEBUG_TOTP_SECRET;
|
|
5612
5645
|
const code = secret ? generateTotp(secret) : void 0;
|
|
5613
|
-
return parts.kind === "launcher" ? buildLauncherAttachUrl(parts.tunnelHttpUrl, parts.wssUrl, code) : buildDeepLinkAttachUrl(parts.schemeUrl, parts.wssUrl, code);
|
|
5646
|
+
return parts.kind === "launcher" ? buildLauncherAttachUrl(parts.tunnelHttpUrl, parts.wssUrl, code, { name: parts.appName }) : buildDeepLinkAttachUrl(parts.schemeUrl, parts.wssUrl, code);
|
|
5614
5647
|
}
|
|
5615
5648
|
function jsonResult$1(value) {
|
|
5616
5649
|
return { content: [{
|
|
@@ -5904,7 +5937,7 @@ async function readMobileRelayBaseUrl(env = process.env, projectRoot) {
|
|
|
5904
5937
|
const envValue = typeof raw === "string" ? raw.trim() : "";
|
|
5905
5938
|
if (envValue !== "") return envValue;
|
|
5906
5939
|
if (projectRoot !== void 0) {
|
|
5907
|
-
const { readRelayUrls } = await import("../relay-url-store-
|
|
5940
|
+
const { readRelayUrls } = await import("../relay-url-store-DjKJJZ0d.js");
|
|
5908
5941
|
const stored = await readRelayUrls({ projectRoot });
|
|
5909
5942
|
if (stored?.relayBaseUrl !== void 0) return stored.relayBaseUrl;
|
|
5910
5943
|
}
|
|
@@ -6957,7 +6990,7 @@ function createDevServer(deps = {}) {
|
|
|
6957
6990
|
const aitSource = deps.aitSource ?? new HttpAitSource({ stateEndpoint });
|
|
6958
6991
|
const server = new Server({
|
|
6959
6992
|
name: "ait-devtools",
|
|
6960
|
-
version: "0.1.
|
|
6993
|
+
version: "0.1.73"
|
|
6961
6994
|
}, { capabilities: { tools: {} } });
|
|
6962
6995
|
server.setRequestHandler(ListToolsRequestSchema, () => ({ tools: DEV_TOOL_DEFINITIONS.map((tool) => ({ ...tool })) }));
|
|
6963
6996
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|