@ait-co/devtools 0.1.144 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +47 -218
- package/README.md +36 -246
- package/dist/in-app/auto.d.ts +1 -138
- package/dist/in-app/auto.js +28 -1102
- package/dist/in-app/auto.js.map +1 -1
- package/dist/in-app/index.d.ts +38 -547
- package/dist/in-app/index.d.ts.map +1 -1
- package/dist/in-app/index.js +62 -939
- package/dist/in-app/index.js.map +1 -1
- package/dist/mcp/cli.d.ts +1 -54
- package/dist/mcp/cli.js +33 -9720
- package/dist/mcp/cli.js.map +1 -1
- package/dist/mcp/server.d.ts +1 -88
- package/dist/mcp/server.js +36 -1076
- package/dist/mcp/server.js.map +1 -1
- package/dist/mock/index.d.ts +35 -21
- package/dist/mock/index.d.ts.map +1 -1
- package/dist/mock/index.js +81 -2
- package/dist/mock/index.js.map +1 -1
- package/dist/panel/index.js +80 -104
- package/dist/panel/index.js.map +1 -1
- package/dist/relay-url-store-CkVSQZMq.cjs +110 -0
- package/dist/relay-url-store-CkVSQZMq.cjs.map +1 -0
- package/dist/relay-url-store-dkII-DHD.js +109 -0
- package/dist/relay-url-store-dkII-DHD.js.map +1 -0
- package/dist/stubs/bin-devtools-mcp.js +58 -0
- package/dist/stubs/bin-devtools-mcp.js.map +1 -0
- package/dist/stubs/bin-devtools-test.d.ts +2 -0
- package/dist/stubs/bin-devtools-test.js +55 -0
- package/dist/stubs/bin-devtools-test.js.map +1 -0
- package/dist/test-runner/config.d.ts +1 -231
- package/dist/test-runner/config.js +41 -45
- package/dist/test-runner/config.js.map +1 -1
- package/dist/{tunnel-BGT9Curk.cjs → tunnel-BKZkOyQp.cjs} +1 -1
- package/dist/{tunnel-BGT9Curk.cjs.map → tunnel-BKZkOyQp.cjs.map} +1 -1
- package/dist/{tunnel-BOKmLzBO.js → tunnel-CqSCIrdU.js} +1 -1
- package/dist/{tunnel-BOKmLzBO.js.map → tunnel-CqSCIrdU.js.map} +1 -1
- package/dist/unplugin/index.cjs +9 -18
- package/dist/unplugin/index.cjs.map +1 -1
- package/dist/unplugin/index.d.cts +26 -5
- package/dist/unplugin/index.d.cts.map +1 -1
- package/dist/unplugin/index.d.ts +27 -6
- package/dist/unplugin/index.d.ts.map +1 -1
- package/dist/unplugin/index.js +10 -19
- package/dist/unplugin/index.js.map +1 -1
- package/package.json +10 -25
- package/dist/attach-orchestrator-0F0m_UqQ.js +0 -1845
- package/dist/attach-orchestrator-0F0m_UqQ.js.map +0 -1
- package/dist/attach-orchestrator-D65KxFy_.js +0 -1831
- package/dist/attach-orchestrator-D65KxFy_.js.map +0 -1
- package/dist/attach-orchestrator-DL3NQ9ca.js +0 -1846
- package/dist/attach-orchestrator-DL3NQ9ca.js.map +0 -1
- package/dist/bundle-C796JIwG.d.ts +0 -159
- package/dist/bundle-C796JIwG.d.ts.map +0 -1
- package/dist/capture-DsP525OZ.d.ts +0 -58
- package/dist/capture-DsP525OZ.d.ts.map +0 -1
- package/dist/cdp-connection-rP1WdnH5.d.ts +0 -287
- package/dist/cdp-connection-rP1WdnH5.d.ts.map +0 -1
- package/dist/cell-BaLvusOl.js +0 -68
- package/dist/cell-BaLvusOl.js.map +0 -1
- package/dist/cell-CBUS3-nT.js +0 -274
- package/dist/cell-CBUS3-nT.js.map +0 -1
- package/dist/cell-EBKKpAAT.js +0 -307
- package/dist/cell-EBKKpAAT.js.map +0 -1
- package/dist/chii-relay-B3ZhjGMi.js +0 -304
- package/dist/chii-relay-B3ZhjGMi.js.map +0 -1
- package/dist/chii-relay-CGMlePMd.cjs +0 -304
- package/dist/chii-relay-CGMlePMd.cjs.map +0 -1
- package/dist/debug-server-B3ABDrRI.js +0 -456
- package/dist/debug-server-B3ABDrRI.js.map +0 -1
- package/dist/debug-server-BWhwrVXa.js +0 -1158
- package/dist/debug-server-BWhwrVXa.js.map +0 -1
- package/dist/debug-server-CfQNxxGW.js +0 -600
- package/dist/debug-server-CfQNxxGW.js.map +0 -1
- package/dist/devtools-opener-3Drge_RJ.js +0 -75
- package/dist/devtools-opener-3Drge_RJ.js.map +0 -1
- package/dist/devtools-opener-CJpEsXXQ.js +0 -76
- package/dist/devtools-opener-CJpEsXXQ.js.map +0 -1
- package/dist/devtools-opener-CxtryS8c.js +0 -75
- package/dist/devtools-opener-CxtryS8c.js.map +0 -1
- package/dist/in-app/auto.d.ts.map +0 -1
- package/dist/mcp/cli.d.ts.map +0 -1
- package/dist/mcp/server.d.ts.map +0 -1
- package/dist/pool-DcaaOwUq.d.ts +0 -14761
- package/dist/pool-DcaaOwUq.d.ts.map +0 -1
- package/dist/qr-http-server-C_lqOrgc.js +0 -1644
- package/dist/qr-http-server-C_lqOrgc.js.map +0 -1
- package/dist/qr-http-server-CopuMbub.js +0 -1644
- package/dist/qr-http-server-CopuMbub.js.map +0 -1
- package/dist/qr-http-server-DrbIVDjO.js +0 -1645
- package/dist/qr-http-server-DrbIVDjO.js.map +0 -1
- package/dist/relay-factory-N9QobQxG.js +0 -206
- package/dist/relay-factory-N9QobQxG.js.map +0 -1
- package/dist/relay-secret-store-BR0YIkNv.cjs +0 -241
- package/dist/relay-secret-store-BR0YIkNv.cjs.map +0 -1
- package/dist/relay-secret-store-Bmyleu0A.js +0 -154
- package/dist/relay-secret-store-Bmyleu0A.js.map +0 -1
- package/dist/relay-secret-store-CQenfcSL.js +0 -154
- package/dist/relay-secret-store-CQenfcSL.js.map +0 -1
- package/dist/relay-secret-store-CYM8CBIF.js +0 -240
- package/dist/relay-secret-store-CYM8CBIF.js.map +0 -1
- package/dist/relay-secret-store-DKxs7zwq.js +0 -153
- package/dist/relay-secret-store-DKxs7zwq.js.map +0 -1
- package/dist/relay-secret-store-WJ8EGkIl.js +0 -153
- package/dist/relay-secret-store-WJ8EGkIl.js.map +0 -1
- package/dist/relay-url-store-BR2XodiO.js +0 -123
- package/dist/relay-url-store-BR2XodiO.js.map +0 -1
- package/dist/relay-url-store-C1as_m5G.cjs +0 -115
- package/dist/relay-url-store-C1as_m5G.cjs.map +0 -1
- package/dist/relay-url-store-CH63fVCm.js +0 -122
- package/dist/relay-url-store-CH63fVCm.js.map +0 -1
- package/dist/relay-url-store-CzFo_84F.js +0 -114
- package/dist/relay-url-store-CzFo_84F.js.map +0 -1
- package/dist/relay-url-store-DaY1QPes.js +0 -123
- package/dist/relay-url-store-DaY1QPes.js.map +0 -1
- package/dist/relay-url-store-xmUuTjXA.js +0 -122
- package/dist/relay-url-store-xmUuTjXA.js.map +0 -1
- package/dist/relay-worker-B5HKkGUY.js +0 -832
- package/dist/relay-worker-B5HKkGUY.js.map +0 -1
- package/dist/relay-worker-YdlpZQl9.d.ts +0 -214
- package/dist/relay-worker-YdlpZQl9.d.ts.map +0 -1
- package/dist/rolldown-runtime-DGkTqVfb.js +0 -15
- package/dist/rolldown-runtime-DUslC3ob.js +0 -14
- package/dist/runtime-kn9DxOeg.d.ts +0 -249
- package/dist/runtime-kn9DxOeg.d.ts.map +0 -1
- package/dist/test-runner/bin.js +0 -2584
- package/dist/test-runner/bin.js.map +0 -1
- package/dist/test-runner/bridge-stub.d.ts +0 -125
- package/dist/test-runner/bridge-stub.d.ts.map +0 -1
- package/dist/test-runner/bridge-stub.js +0 -92
- package/dist/test-runner/bridge-stub.js.map +0 -1
- package/dist/test-runner/bundle.d.ts +0 -2
- package/dist/test-runner/bundle.js +0 -439
- package/dist/test-runner/bundle.js.map +0 -1
- package/dist/test-runner/capture.d.ts +0 -2
- package/dist/test-runner/capture.js +0 -44
- package/dist/test-runner/capture.js.map +0 -1
- package/dist/test-runner/config.d.ts.map +0 -1
- package/dist/test-runner/method-pace.d.ts +0 -82
- package/dist/test-runner/method-pace.d.ts.map +0 -1
- package/dist/test-runner/method-pace.js +0 -120
- package/dist/test-runner/method-pace.js.map +0 -1
- package/dist/test-runner/pool.d.ts +0 -2
- package/dist/test-runner/pool.js +0 -136
- package/dist/test-runner/pool.js.map +0 -1
- package/dist/test-runner/relay-factory.d.ts +0 -11245
- package/dist/test-runner/relay-factory.d.ts.map +0 -1
- package/dist/test-runner/relay-factory.js +0 -206
- package/dist/test-runner/relay-factory.js.map +0 -1
- package/dist/test-runner/relay-worker.d.ts +0 -2
- package/dist/test-runner/relay-worker.js +0 -2
- package/dist/test-runner/report.d.ts +0 -163
- package/dist/test-runner/report.d.ts.map +0 -1
- package/dist/test-runner/report.js +0 -198
- package/dist/test-runner/report.js.map +0 -1
- package/dist/test-runner/rpc.d.ts +0 -56
- package/dist/test-runner/rpc.d.ts.map +0 -1
- package/dist/test-runner/rpc.js +0 -98
- package/dist/test-runner/rpc.js.map +0 -1
- package/dist/test-runner/runtime.d.ts +0 -2
- package/dist/test-runner/runtime.js +0 -659
- package/dist/test-runner/runtime.js.map +0 -1
- package/dist/test-runner/task-graph.d.ts +0 -38
- package/dist/test-runner/task-graph.d.ts.map +0 -1
- package/dist/test-runner/task-graph.js +0 -182
- package/dist/test-runner/task-graph.js.map +0 -1
- package/dist/throttle-DKKzX1qC.js +0 -59
- package/dist/throttle-DKKzX1qC.js.map +0 -1
- package/dist/totp-BqmCLSNA.js +0 -189
- package/dist/totp-BqmCLSNA.js.map +0 -1
- package/dist/totp-CMHR5lsW.cjs +0 -191
- package/dist/totp-CMHR5lsW.cjs.map +0 -1
- package/dist/totp-CZLLKfOC.js +0 -200
- package/dist/totp-CZLLKfOC.js.map +0 -1
- package/dist/totp-DAxys-r0.js +0 -199
- package/dist/totp-DAxys-r0.js.map +0 -1
- package/dist/totp-DfekTBk3.js +0 -211
- package/dist/totp-DfekTBk3.js.map +0 -1
- package/dist/totp-Dwft0Kz7.js +0 -3
- package/dist/totp-WY6l0ysP.js +0 -190
- package/dist/totp-WY6l0ysP.js.map +0 -1
- /package/dist/{test-runner/bin.d.ts → stubs/bin-devtools-mcp.d.ts} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["hasDebugConsole","INSTALL_HINT","hasInAppWiring","buildInAppSnippet","hasDebugger"],"sources":["../../src/shared/parent-watcher.ts","../../src/unplugin/index.ts"],"sourcesContent":["/**\n * Shared parent-PID watcher — used by both the MCP debug daemon and the\n * unplugin tunnel path to self-terminate when the parent process (e.g. Claude\n * Code, vite) has died or been reparented without sending SIGTERM/SIGHUP.\n *\n * Intentionally react-free and Node-stdlib-only so this module is safe to\n * import from the MCP daemon bundle (`dist/mcp/cli.js`) without violating the\n * install-graph invariant.\n */\n\n// ---------------------------------------------------------------------------\n// isPidAlive — extracted from src/mcp/server-lock.ts\n// ---------------------------------------------------------------------------\n\n/**\n * Returns `true` when the given PID refers to a running process.\n *\n * Uses `process.kill(pid, 0)` — a no-op signal that succeeds when the process\n * exists and we have permission to signal it; throws ESRCH when it doesn't exist.\n */\nexport function isPidAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (err: unknown) {\n // ESRCH = no such process → stale lock.\n // EPERM = process exists but we can't signal it (still alive).\n if ((err as NodeJS.ErrnoException).code === 'EPERM') return true;\n return false;\n }\n}\n\n// ---------------------------------------------------------------------------\n// startParentWatcher — extracted from src/mcp/debug-server.ts\n// ---------------------------------------------------------------------------\n\n/**\n * Starts a periodic watcher that detects when the parent process (e.g. Claude\n * Code) has died without sending SIGTERM/SIGHUP, and calls `onOrphaned` so the\n * daemon can self-terminate rather than running as a zombie.\n *\n * Mirrors the `startAttachWatcher` pattern: `setInterval`-based, returns\n * `{ stop(): void }`, injectable deps for testability.\n *\n * @param onOrphaned - Called once when the parent is gone.\n * @param opts.intervalMs - Poll interval in milliseconds (default 5 000).\n * @param opts.initialPpid - Parent PID to watch (default `process.ppid`).\n * @param opts.isAlive - Predicate to test if a PID is running (default `isPidAlive`).\n * @param opts.getPpid - Supplier of current ppid (default `() => process.ppid`).\n * Detects ppid changes as well as death.\n * @param opts.log - Logger (default `process.stderr.write`).\n *\n * @returns `stop` — call during shutdown to clear the interval.\n */\nexport function startParentWatcher(\n onOrphaned: () => void,\n opts?: {\n intervalMs?: number;\n initialPpid?: number;\n isAlive?: (pid: number) => boolean;\n getPpid?: () => number;\n log?: (msg: string) => void;\n },\n): { stop(): void } {\n const {\n intervalMs = 5_000,\n initialPpid = process.ppid,\n isAlive = isPidAlive,\n getPpid = () => process.ppid,\n log = (msg: string) => process.stderr.write(msg),\n } = opts ?? {};\n\n // PID 1 is init/launchd — running under a process manager or as a detached\n // daemon. There is no meaningful parent to watch; skip the watcher entirely.\n if (initialPpid <= 1) {\n log('[ait-debug] parent-pid watcher: no parent to watch (ppid<=1), skipping\\n');\n return { stop() {} };\n }\n\n let fired = false;\n\n const handle = setInterval(() => {\n if (fired) return;\n\n const currentPpid = getPpid();\n const orphaned = currentPpid !== initialPpid || !isAlive(initialPpid);\n\n if (orphaned) {\n fired = true;\n clearInterval(handle);\n log(\n `[ait-debug] parent-pid watcher: parent PID ${initialPpid} is gone (currentPpid=${currentPpid}) — shutting down\\n`,\n );\n onOrphaned();\n }\n }, intervalMs);\n\n return {\n stop() {\n clearInterval(handle);\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// startMaxAgeWatchdog — FIX 4: daemon lifetime cap\n// ---------------------------------------------------------------------------\n\n/**\n * Starts a periodic watchdog that calls `onExpired` once after `maxAgeMs`\n * milliseconds have elapsed since the watchdog was created.\n *\n * Motivation (issue #571): cloudflared quick-tunnel lifetimes are finite (a\n * few hours). A daemon that has been running for days will have outlived its\n * tunnel regardless of whether the tunnel process exited cleanly. This watchdog\n * caps the daemon's maximum age and forces a fresh start so the tunnel is\n * replaced before it silently expires.\n *\n * @param onExpired - Called once when the maximum age is reached. The caller\n * should call `shutdown()` then `process.exit(0)`.\n * @param opts.maxAgeMs - Maximum daemon lifetime in ms. Default 6 h.\n * @param opts.intervalMs - Check interval in ms. Default 60 000 (1 min).\n * @param opts.now - Time source (injectable for tests). Default `Date.now`.\n *\n * @returns `stop` — call during shutdown to clear the interval.\n */\nexport function startMaxAgeWatchdog(\n onExpired: () => void,\n opts: {\n maxAgeMs?: number;\n intervalMs?: number;\n now?: () => number;\n } = {},\n): { stop(): void } {\n const {\n maxAgeMs = 6 * 60 * 60 * 1_000, // 6 hours\n intervalMs = 60_000,\n now = () => Date.now(),\n } = opts;\n\n const startedAt = now();\n let fired = false;\n\n const handle = setInterval(() => {\n if (fired) return;\n if (now() - startedAt >= maxAgeMs) {\n fired = true;\n clearInterval(handle);\n onExpired();\n }\n }, intervalMs);\n\n return {\n stop() {\n clearInterval(handle);\n },\n };\n}\n","/**\n * @ait-co/devtools unplugin\n *\n * 모든 주요 번들러를 지원하는 단일 플러그인.\n * @apps-in-toss/web-framework → @ait-co/devtools/mock 으로 alias 설정.\n *\n * Usage:\n * import aitDevtools from '@ait-co/devtools/unplugin';\n *\n * // Vite\n * export default { plugins: [aitDevtools.vite()] };\n *\n * // Webpack / Next.js\n * config.plugins.push(aitDevtools.webpack());\n *\n * // Rspack\n * config.plugins.push(aitDevtools.rspack());\n *\n * // esbuild\n * { plugins: [aitDevtools.esbuild()] }\n *\n * // Rollup\n * { plugins: [aitDevtools.rollup()] }\n */\n\nimport { fileURLToPath } from 'node:url';\nimport { createUnplugin } from 'unplugin';\nimport { startParentWatcher } from '../shared/parent-watcher.js';\nimport {\n buildInAppSnippet,\n hasDebugConsole,\n hasDebugger,\n hasInAppWiring,\n INSTALL_HINT,\n} from './optional-peers.js';\n\n/**\n * Resolve `@ait-co/devtools/mock` to its real file path at plugin-load time.\n *\n * Returning the bare specifier from `resolveId` would stop the bundler from\n * walking node_modules for it — Vite 8+ treats such a non-null string as the\n * final resolved id and serves it via the virtual `/@id/` prefix, which 404s\n * because we don't provide a `load` hook. Resolving to an absolute path here\n * lets every supported bundler load the file the normal way.\n */\nconst MOCK_PATH = (() => {\n try {\n return fileURLToPath(import.meta.resolve('@ait-co/devtools/mock'));\n } catch {\n // Fallback for runtimes where `import.meta.resolve` is unavailable.\n return '@ait-co/devtools/mock';\n }\n})();\n\nexport interface AitDevtoolsOptions {\n /**\n * 패널 자동 주입 여부 (default: true)\n * true이면 진입점에 floating panel import를 자동 추가한다.\n */\n panel?: boolean;\n /**\n * In-app debug attach 자동 주입 여부 (default: true).\n *\n * true이면 진입점에 게이트된 dynamic import를 자동 추가한다:\n * `?debug=1` + `relay` URL 파라미터가 모두 존재할 때만 런타임에\n * `@ait-co/debug-console`을 로드하고 `maybeAttach()`를 호출한다.\n *\n * gate가 통과하지 못하면 chunk 자체를 로드하지 않으므로 일반 production\n * 로드에서 dormant하고, 번들러의 DCE 대상이 된다.\n *\n * `@ait-co/debug-console`은 **optional peer**다 (#817). 설치돼 있지 않으면\n * 주입 자체를 하지 않으므로 attach 코드가 번들에 구조적으로 들어갈 수 없다 —\n * 이게 디버그 표면 보안 스코프의 기술적 강제 지점이다. 환경 1(브라우저 mock)만\n * 쓰는 소비자는 아무것도 추가로 설치하지 않아도 된다.\n *\n * 소비자가 이미 직접 배선한 경우 중복 주입을 방지하기 위해 파일에\n * `@ait-co/debug-console`(또는 분리 전 `@ait-co/devtools/in-app`)이 이미 있으면\n * 자동으로 스킵한다.\n */\n inApp?: boolean;\n /**\n * mock alias 활성화 여부. default: true (development), false (production)\n */\n mock?: boolean;\n /**\n * Vite dev server에 MCP state endpoint를 추가할지 여부 (default: false).\n *\n * `true`로 설정하면:\n * - GET /api/ait-devtools/state — 마지막으로 브라우저가 push한 mock state 스냅샷 반환\n * - POST /api/ait-devtools/state — 브라우저 panel이 상태 변경 시 자동 push (panel 내부 처리)\n *\n * 이 endpoint를 `@ait-co/devtools` MCP stdio server가 읽어 AI 에이전트에 mock state를 노출한다.\n * Vite 전용: webpack/rspack/esbuild/rollup 환경에서는 무시된다.\n */\n mcp?: boolean;\n /**\n * 미니앱의 webViewType (`granite.config.ts`의 `webViewProps.type`)을 빌드 상수\n * `__WEB_VIEW_TYPE__`로 주입한다 (#580). **Vite 전용** (다른 번들러는 무시).\n *\n * 이 상수는 in-app self-report(`@ait-co/devtools/in-app`)가 읽어 launcher(env-2\n * PWA)에 webViewType을 postMessage로 알리고, launcher가 game 타입 미니앱에서\n * 수동 `?navBarType=game` URL 편집 없이 game 모드로 자동 진입하게 한다.\n *\n * 미지정 시 `'partner'`(web-framework `webViewProps.type`의 `@default`)로 주입한다.\n * `game`이면 게임 모드로 자동 진입한다. (granite.config.ts를 config 시점에\n * 자동으로 읽는 것은 TS 모듈 로더가 필요해 보류 — 명시 옵션으로 신뢰성 확보, #580.)\n */\n webViewType?: 'partner' | 'game';\n /**\n * 미니앱의 `granite.config.ts` `navigationBar.transparentBackground` 값\n * (SDK `@apps-in-toss/plugins@2.8.0` 신규 필드, #587). `true`이면 env-2 launcher\n * deep-link에 `&navBarTransparent=1`을 주입해 launcher partner bar가 투명 배경으로\n * 렌더된다. granite.config를 직접 읽지 않는다(version-agnostic, #580 원칙) —\n * 소비자 vite.config가 `graniteConfig.navigationBar?.transparentBackground`를\n * import해 이 옵션으로 넘긴다. 미지정 시 주입 안 함(URL 청정, back-compat).\n */\n navBarTransparent?: boolean;\n /**\n * 미니앱의 `granite.config.ts` `navigationBar.theme` 값\n * (SDK `@apps-in-toss/plugins@2.8.0` 신규 필드, #587). `'light'` 또는 `'dark'`이면\n * env-2 launcher deep-link에 `&navBarTheme=<v>`를 주입해 launcher partner bar가\n * 해당 테마 글자/아이콘 색으로 렌더된다. granite.config를 직접 읽지 않는다\n * (version-agnostic, #580 원칙). 미지정 시 주입 안 함(URL 청정, back-compat).\n */\n navBarTheme?: 'light' | 'dark';\n /**\n * Vite dev 서버를 Cloudflare quick tunnel(`*.trycloudflare.com`, 계정 불필요)로\n * 외부 노출해 실제 폰에서 미리보기. **Vite dev 모드 전용** — production에서는\n * 터널을 띄우지 않는다 (의도치 않은 노출 방지). 다른 번들러는\n * 무시. `true`면 기본 동작, 객체로 세부 설정 가능.\n */\n tunnel?:\n | boolean\n | {\n /** 노출할 포트 (미지정 시 dev 서버가 실제 listen한 포트 자동 감지). */\n port?: number;\n /** 터미널 ASCII QR 출력 (default: true). */\n qr?: boolean;\n /**\n * 환경 2(실기기 PWA)에 CDP 디버깅 배선 (default: false).\n *\n * `true`면 dev 서버 HTTP 터널과 **별도로** Chii relay를 띄우고 그 relay에\n * 두 번째 quick tunnel을 붙여, launcher QR deep-link에 `&debug=1&relay=<wss>`를\n * 실어 보낸다. 폰의 PWA iframe이 in-app debug gate를 통과해 target.js를 주입받고,\n * AI host MCP가 그 relay에 client로 붙으면 실기기 WebKit 위에서 CDP 디버깅이 열린다.\n * mock SDK는 그대로라 `call_sdk`는 환경 2에서 mock을 친다 (fidelity 사다리의\n * 설계 의도 — SDK fidelity가 필요하면 환경 3로 올라간다).\n *\n * 이 경로는 **optional peer `@ait-co/debugger`**를 요구한다 (#817).\n * 미설치면 CDP 배선을 건너뛰고 일반 화면 미리보기 터널로 degrade하며,\n * 설치 안내를 한 번 출력한다.\n */\n cdp?: boolean;\n };\n}\n\nconst FRAMEWORK_ID = '@apps-in-toss/web-framework';\nconst BRIDGE_ID = '@apps-in-toss/web-bridge'; // back-compat (2.x)\nconst ANALYTICS_ID = '@apps-in-toss/web-analytics'; // back-compat (2.x)\nconst WEBVIEW_BRIDGE_ID = '@apps-in-toss/webview-bridge'; // 3.0+\n\n/** MCP state endpoint path — browser panel POSTs here, MCP server GETs here */\nconst MCP_STATE_PATH = '/api/ait-devtools/state';\n\n/**\n * Resolves the effective tunnel option (#425).\n *\n * An explicit `tunnel` value (including `false`) always takes priority over\n * env vars — the `??` operator means `undefined` (= omitted) falls through,\n * but `false` / `true` / an object are preserved as-is (non-breaking).\n *\n * When the option is omitted:\n * - `AIT_TUNNEL=1` enables the base screen-preview tunnel.\n * - `AIT_TUNNEL_CDP=1` (requires `AIT_TUNNEL`) upgrades to the CDP relay.\n * - Neither set → `false` (disabled).\n *\n * Extracted as a pure function so it can be unit-tested without standing up\n * a full Vite dev server.\n *\n * @param explicit - The `tunnel` option as passed by the consumer (or `undefined` when omitted).\n * @param env - The process environment (injectable for testing).\n */\nexport function resolveTunnelOption(\n explicit: AitDevtoolsOptions['tunnel'],\n env: Record<string, string | undefined>,\n): AitDevtoolsOptions['tunnel'] {\n return explicit ?? (env.AIT_TUNNEL ? { cdp: !!env.AIT_TUNNEL_CDP } : false);\n}\n\nconst aitDevtoolsPlugin = createUnplugin((options?: AitDevtoolsOptions) => {\n const isDev = process.env.NODE_ENV !== 'production';\n const shouldEnable = isDev;\n const shouldMock = shouldEnable && (options?.mock ?? isDev);\n const shouldPanel = shouldEnable && (options?.panel ?? true);\n // in-app attach 주입: shouldEnable과 동일하게 dev에서 자동.\n // maybeAttach()가 런타임 gate(Layer B·C)를 자체 검증하므로 dev 항상 주입이 안전하다.\n //\n // #817: 주입 대상인 `@ait-co/debug-console`은 optional peer다. 미설치면 주입\n // 자체를 하지 않는다 — attach 코드가 번들에 구조적으로 못 들어가는 게 보안\n // 스코프의 강제 지점이고, 환경 1만 쓰는 다수 소비자는 아무것도 더 설치하지\n // 않아도 된다. 사용자가 `inApp: true`로 명시 요청했는데 패키지가 없을 때만\n // 안내를 출력한다 (기본값 경로는 조용히 degrade — 상시 nag 금지).\n const debugConsoleInstalled = hasDebugConsole();\n const shouldInApp = shouldEnable && (options?.inApp ?? true) && debugConsoleInstalled;\n if (shouldEnable && options?.inApp === true && !debugConsoleInstalled) {\n console.warn(\n `[@ait-co/devtools] inApp: @ait-co/debug-console이 없어 in-app attach를 주입하지 않습니다. 설치: ${INSTALL_HINT}`,\n );\n }\n const shouldMcp = shouldEnable && (options?.mcp ?? false);\n\n // In-memory store for the last state snapshot pushed by the browser panel.\n // Only allocated when mcp: true to avoid any overhead in the common case.\n let lastState: string | null = null;\n\n // Tunnel is dev-only and Vite-only. Never under production, so a production\n // build can't accidentally expose itself.\n //\n // Tunnel toggle resolution (#425): an explicit `tunnel` option always wins;\n // when omitted, fall back to the AIT_TUNNEL / AIT_TUNNEL_CDP env vars so a\n // consumer needs no `tunnel:` line in vite.config to enable env-2 preview.\n // AIT_TUNNEL gates the base (screen preview); AIT_TUNNEL_CDP upgrades to the\n // CDP relay. Production safety is unchanged — the existing\n // `shouldTunnel = isDev && !!tunnelOpt` guard below still blocks prod builds.\n const tunnelOpt = resolveTunnelOption(options?.tunnel, process.env);\n const shouldTunnel = isDev && !!tunnelOpt;\n\n // #580: webViewType build constant. Injected as a Vite `define` so the\n // in-app self-report can post it to the launcher for game-mode auto-entry.\n // Defaults to 'partner' (web-framework webViewProps.type @default).\n const webViewType = options?.webViewType ?? 'partner';\n // #587: navigationBar appearance options (SDK 2.8.0 granite.config fields).\n // Forwarded to printTunnelBanner so the launcher deep-link carries the params.\n const navBarTransparent = options?.navBarTransparent;\n const navBarTheme = options?.navBarTheme;\n const tunnelConfig = typeof tunnelOpt === 'object' ? tunnelOpt : {};\n\n return {\n name: 'ait-co-devtools',\n enforce: 'pre' as const,\n\n resolveId(id: string) {\n if (!shouldMock) return null;\n // @apps-in-toss/web-framework → @ait-co/devtools/mock (absolute path)\n if (\n id === FRAMEWORK_ID ||\n id === WEBVIEW_BRIDGE_ID ||\n id === BRIDGE_ID ||\n id === ANALYTICS_ID\n ) {\n return MOCK_PATH;\n }\n return null;\n },\n\n transformInclude(id: string) {\n // panel 또는 inApp 주입 중 하나라도 필요하면 진입점 파일을 transform 대상으로 포함\n if (!shouldPanel && !shouldInApp) return false;\n // 진입점 파일에만 주입\n return (\n /\\.(tsx?|jsx?)$/.test(id) &&\n /\\/(main|index|entry|app)\\.[tj]sx?$/i.test(id) &&\n !id.includes('node_modules')\n );\n },\n\n transform(code: string) {\n let result = code;\n let changed = false;\n\n // 패널 주입: shouldPanel이 활성화되어 있고 아직 import가 없으면 prepend\n if (shouldPanel && !code.includes('@ait-co/devtools/panel')) {\n result = `import '@ait-co/devtools/panel';\\n${result}`;\n changed = true;\n }\n\n // in-app attach 주입: shouldInApp이 활성화되어 있고 아직 배선이 없으면 prepend.\n // 게이트된 dynamic import로 주입 — ?debug=1 + relay 파라미터가 모두 있을 때만\n // 런타임에 @ait-co/debug-console을 로드하고 maybeAttach()를 호출한다 (#817).\n // production DCE: URLSearchParams gate가 조건을 false로 평가하면\n // dynamic import 자체가 dead code — 번들러가 제거 가능.\n // dedupe는 신·구 specifier를 모두 인정한다 (hasInAppWiring).\n if (shouldInApp && !hasInAppWiring(code)) {\n result = `${buildInAppSnippet()}\\n${result}`;\n changed = true;\n }\n\n return changed ? result : null;\n },\n\n // Vite-only: register the MCP state HTTP endpoint on the dev server, and\n // optionally start a Cloudflare quick tunnel once the dev server is listening.\n // Non-Vite bundlers do not have a dev server concept so this is silently\n // skipped (unplugin passes `vite` key only when building for Vite).\n vite: {\n config() {\n // #580: inject the webViewType build constant for every Vite build so\n // the in-app self-report (@ait-co/devtools/in-app) can read it and post\n // it to the launcher (env-2 PWA) for game-mode auto-entry. JSON.stringify\n // makes it a string literal at the define substitution site.\n const define = { __WEB_VIEW_TYPE__: JSON.stringify(webViewType) };\n if (!shouldTunnel) return { define };\n // Vite blocks requests whose Host header isn't in `server.allowedHosts`\n // (defaults to localhost only). The quick-tunnel hostname is random per\n // run, so allow the whole `.trycloudflare.com` suffix while the tunnel\n // is on. (A leading `.` makes Vite match the domain and its subdomains.)\n return { define, server: { allowedHosts: ['.trycloudflare.com'] } };\n },\n\n configureServer(server: import('vite').ViteDevServer) {\n // MCP state endpoint: browser panel POSTs state here, MCP stdio server GETs it.\n if (shouldMcp) {\n server.middlewares.use(MCP_STATE_PATH, (req, res) => {\n // Allow Claude Code / AI agents (running locally) to read state\n res.setHeader('Access-Control-Allow-Origin', '*');\n res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type');\n\n if (req.method === 'OPTIONS') {\n res.writeHead(204);\n res.end();\n return;\n }\n\n if (req.method === 'GET') {\n if (lastState === null) {\n res.writeHead(503, { 'Content-Type': 'application/json' });\n res.end(\n JSON.stringify({\n error: 'No state received yet. Open the app in a browser first.',\n }),\n );\n return;\n }\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(lastState);\n return;\n }\n\n if (req.method === 'POST') {\n const chunks: Buffer[] = [];\n req.on('data', (chunk: Buffer) => chunks.push(chunk));\n req.on('end', () => {\n try {\n const body = Buffer.concat(chunks).toString('utf-8');\n // Validate it's parseable JSON before caching\n JSON.parse(body);\n lastState = body;\n res.writeHead(204);\n res.end();\n } catch {\n res.writeHead(400, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'Invalid JSON' }));\n }\n });\n return;\n }\n\n res.writeHead(405, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'Method not allowed' }));\n });\n }\n\n // Tunnel: start a Cloudflare quick tunnel once the dev server is listening.\n if (shouldTunnel) {\n let tunnel: { stop: () => void } | null = null;\n // env-2 CDP wiring (tunnel.cdp): a second tunnel + Chii relay, torn\n // down alongside the HTTP tunnel. Fire-and-forget close on teardown.\n let relayTunnel: { stop: () => void } | null = null;\n let relay: { close: () => Promise<void> } | null = null;\n // env-2 HTML dashboard (issue #408): local 127.0.0.1 HTTP server that\n // serves the QR + connect-steps + FAQ page (env 3/4 UX parity), opened\n // in the browser when CDP is wired + GUI present. Torn down with the\n // tunnel. Only set when the dashboard actually started.\n let qrDashboard: { close: () => Promise<void> } | null = null;\n // env-2 URL file store (#424): captured after the first writeRelayUrls\n // call so cleanup() can call deleteRelayUrls without a re-import.\n // SECRET-HANDLING: the stored function reference never carries URL values.\n let relayUrlDeleteFn: ((projectRoot: string) => Promise<void>) | null = null;\n // #420: parent-PID watcher — self-terminate when vite's parent dies so\n // cloudflared children don't become zombies holding stale tunnels.\n let parentWatcher: { stop(): void } | null = null;\n const httpServer = server.httpServer;\n\n httpServer?.once('listening', () => {\n const address = httpServer?.address();\n const port =\n tunnelConfig.port ??\n (address && typeof address === 'object' ? address.port : undefined);\n if (!port) {\n console.warn(\n '[@ait-co/devtools] tunnel: could not determine the dev server port; skipping.',\n );\n return;\n }\n // Dynamic import keeps `cloudflared` / `qrcode-terminal` off the\n // module graph unless the tunnel is actually used.\n import('./tunnel.js')\n .then(async ({ startQuickTunnel, printTunnelBanner, startTunnelDashboard }) => {\n const t = await startQuickTunnel(port);\n tunnel = t;\n\n // env-2 CDP: boot a Chii relay (OS-assigned local port) and a\n // second quick tunnel to it. The relay's https tunnel URL becomes\n // the `wss://` relay the launcher QR carries (&debug=1&relay=).\n let relayWssUrl: string | undefined;\n // SECRET-HANDLING: relayHttpUrl carries the relay host — never logged.\n let relayHttpUrl: string | undefined;\n // LOCAL relay base — loopback URL, safe to surface (issue #530).\n let relayLocalHttpUrl: string | undefined;\n // #817: env-2 CDP는 optional peer `@ait-co/debugger`를 요구한다.\n // 미설치면 relay·dashboard 배선을 통째로 건너뛰고 일반 화면\n // 미리보기 터널로 degrade한다 — 사용자가 명시적으로 cdp를 켠\n // 경로이므로 설치 안내를 한 번 출력한다.\n // SECRET-HANDLING: 고정 문구만 — URL·host·코드 없음.\n if (tunnelConfig.cdp && !hasDebugger()) {\n console.warn(\n `[@ait-co/devtools] tunnel: @ait-co/debugger가 없어 CDP relay를 건너뜁니다 — 화면 미리보기는 그대로 동작합니다. 설치: ${INSTALL_HINT}`,\n );\n } else if (tunnelConfig.cdp) {\n try {\n // NOTE (#817): the relay BOOTSTRAP below still calls this\n // package's local `../mcp/*` modules. `@ait-co/debugger@0.1.1`\n // publishes only `startTunnelDashboard` from `/dev-bridge` —\n // there is no published entry for the secret store / TOTP /\n // chii relay / URL store yet, so those four steps cannot be\n // delegated. The GATE above is nonetheless the real contract:\n // env-2 CDP requires `@ait-co/debugger`, matching the shape\n // this path takes once V2 removes the local copies.\n //\n // Relay-auth baseline (issue #250): the env-2 CDP relay is\n // reachable over a public `*.trycloudflare.com` tunnel, so a\n // configured TOTP secret is MANDATORY and the relay enforces\n // it on every WS upgrade.\n //\n // First-run auto-mint (issue #394, project-local #396): if\n // AIT_DEBUG_TOTP_SECRET is not yet set, ensureRelaySecret()\n // mints a 256-bit random secret, persists it to the project-\n // local file <project>/.ait_relay (0600, anchored at the\n // nearest package.json directory above server.config.root),\n // and injects it into process.env so the following\n // assertRelayAuthConfigured() call succeeds. On subsequent\n // runs the persisted value is loaded silently — no manual\n // export needed. The MCP daemon reads the SAME file read-only\n // via loadRelaySecretReadOnly() when switching to a relay env.\n // SECRET-HANDLING: neither ensureRelaySecret nor the\n // guard/predicate log the secret value.\n const { ensureRelaySecret } = await import('../mcp/relay-secret-store.js');\n await ensureRelaySecret({ projectRoot: server.config.root });\n const { assertRelayAuthConfigured, buildRelayVerifyAuth } = await import(\n '../mcp/totp.js'\n );\n assertRelayAuthConfigured();\n const verifyAuth = buildRelayVerifyAuth();\n const { startChiiRelay } = await import('../mcp/chii-relay.js');\n // Issue #467: this relay lives in the vite process, so the\n // MCP daemon's get_debug_status counter cannot see its 401s.\n // Surface a throttled hint in the vite terminal instead.\n // SECRET-HANDLING: fixed message only — no URL, code, host.\n let lastAuthRejectWarnAt = 0;\n const r = await startChiiRelay({\n port: 0,\n verifyAuth,\n onAuthReject: () => {\n const nowMs = Date.now();\n if (nowMs - lastAuthRejectWarnAt < 10_000) return;\n lastAuthRejectWarnAt = nowMs;\n console.warn(\n '[@ait-co/devtools] tunnel: relay 인증(TOTP) 거부 감지 — 폰에서 QR을 다시 스캔하세요 (코드는 ~3분마다 만료)',\n );\n },\n });\n relay = r;\n const rt = await startQuickTunnel(r.port);\n relayTunnel = rt;\n // SECRET-HANDLING: rt.url is the https relay base — stored in\n // relayHttpUrl for .ait_urls write below; never logged.\n relayHttpUrl = rt.url;\n relayWssUrl = rt.url.replace(/^https:/, 'wss:');\n // LOCAL relay base for MCP inspector URL assembly (issue #530):\n // the relay process runs on this machine, so the inspector\n // front_end + client WS can use the loopback address directly —\n // no tunnel round-trip for the developer's browser.\n // Safe to surface: loopback URL contains no tunnel host.\n relayLocalHttpUrl = `http://127.0.0.1:${r.port}`;\n } catch (err: unknown) {\n console.warn(\n `[@ait-co/devtools] tunnel: CDP relay not started — screen preview works without on-device debugging: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n }\n }\n\n // Read the app name from the project's package.json to add to\n // the launcher deep-link (#498). Failure is silently ignored.\n let tunnelAppName: string | undefined;\n try {\n const { readFileSync } = await import('node:fs');\n const pkgPath = `${server.config.root}/package.json`;\n const pkgRaw = readFileSync(pkgPath, 'utf8');\n const pkg = JSON.parse(pkgRaw) as Record<string, unknown>;\n const rawName = typeof pkg.name === 'string' ? pkg.name : '';\n const stripped = rawName.includes('/')\n ? rawName.slice(rawName.indexOf('/') + 1)\n : rawName;\n tunnelAppName = stripped.trim() || undefined;\n } catch {\n // Silently ignore — fail-open.\n }\n\n await printTunnelBanner(t.url, {\n qr: tunnelConfig.qr,\n relayWssUrl,\n name: tunnelAppName,\n webViewType,\n navBarTransparent,\n navBarTheme,\n });\n\n // env-2 URL file-based discovery (#424): write .ait_urls so the\n // MCP daemon can discover the relay/tunnel URLs without manual env\n // var copy-paste. SECRET-HANDLING: URL values are never logged.\n // Capture deleteRelayUrls in the outer-scope fn so cleanup() can\n // call it without re-importing (no async in signal handlers).\n const { writeRelayUrls, deleteRelayUrls } = await import(\n '../mcp/relay-url-store.js'\n );\n await writeRelayUrls({\n projectRoot: server.config.root,\n tunnelBaseUrl: t.url,\n ...(relayHttpUrl !== undefined ? { relayBaseUrl: relayHttpUrl } : {}),\n // Issue #530: local relay base for inspector URL (loopback, no tunnel host).\n ...(relayLocalHttpUrl !== undefined ? { relayLocalUrl: relayLocalHttpUrl } : {}),\n });\n relayUrlDeleteFn = (root: string) => deleteRelayUrls({ projectRoot: root });\n\n // env-2 HTML dashboard (issue #408): when CDP is wired and a GUI\n // is present, serve the same QR+FAQ dashboard env 3/4 uses and\n // open it in the browser. No-op (returns undefined) for the\n // screen-only tunnel, headless, qr:false, or AIT_AUTO_DEVTOOLS=0\n // — the ASCII QR above remains the fallback in those cases.\n if (relayWssUrl) {\n qrDashboard =\n (await startTunnelDashboard({\n tunnelUrl: t.url,\n relayWssUrl,\n qr: tunnelConfig.qr,\n name: tunnelAppName,\n })) ?? null;\n }\n\n // #420: start watching the parent PID now that tunnel resources\n // are allocated. When the parent dies/reparents, clean up\n // synchronously (stops cloudflared children) then exit.\n parentWatcher = startParentWatcher(() => {\n cleanup();\n process.exit(0);\n });\n })\n .catch((err: unknown) => {\n console.warn(\n `[@ait-co/devtools] tunnel failed to start: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n });\n });\n\n const cleanup = () => {\n parentWatcher?.stop();\n tunnel?.stop();\n relayTunnel?.stop();\n void relay?.close();\n void qrDashboard?.close();\n // env-2 URL file cleanup (#424): remove .ait_urls on teardown so a\n // stale file doesn't cause the MCP daemon to attempt a doomed attach.\n // SECRET-HANDLING: relayUrlDeleteFn never logs the path or URL values.\n void relayUrlDeleteFn?.(server.config.root);\n };\n httpServer?.once('close', cleanup);\n process.once('SIGINT', cleanup);\n process.once('SIGTERM', cleanup);\n process.once('SIGHUP', cleanup);\n process.once('exit', cleanup);\n }\n },\n },\n };\n});\n\nexport const vite = aitDevtoolsPlugin.vite;\nexport const webpack = aitDevtoolsPlugin.webpack;\nexport const rollup = aitDevtoolsPlugin.rollup;\nexport const esbuild = aitDevtoolsPlugin.esbuild;\nexport const rspack = aitDevtoolsPlugin.rspack;\n\nexport default aitDevtoolsPlugin;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["hasDebugConsole","INSTALL_HINT","hasInAppWiring","buildInAppSnippet","hasDebugger","DEBUGGER_DEV_BRIDGE_ID"],"sources":["../../src/shared/parent-watcher.ts","../../src/unplugin/index.ts"],"sourcesContent":["/**\n * Shared parent-PID watcher — used by both the MCP debug daemon and the\n * unplugin tunnel path to self-terminate when the parent process (e.g. Claude\n * Code, vite) has died or been reparented without sending SIGTERM/SIGHUP.\n *\n * Intentionally react-free and Node-stdlib-only so this module is safe to\n * import from the MCP daemon bundle (`dist/mcp/cli.js`) without violating the\n * install-graph invariant.\n */\n\n// ---------------------------------------------------------------------------\n// isPidAlive — extracted from src/mcp/server-lock.ts (that daemon-side module\n// now lives in @ait-co/debugger as packages/debugger/src/mcp/server-lock.ts)\n// ---------------------------------------------------------------------------\n\n/**\n * Returns `true` when the given PID refers to a running process.\n *\n * Uses `process.kill(pid, 0)` — a no-op signal that succeeds when the process\n * exists and we have permission to signal it; throws ESRCH when it doesn't exist.\n */\nexport function isPidAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (err: unknown) {\n // ESRCH = no such process → stale lock.\n // EPERM = process exists but we can't signal it (still alive).\n if ((err as NodeJS.ErrnoException).code === 'EPERM') return true;\n return false;\n }\n}\n\n// ---------------------------------------------------------------------------\n// startParentWatcher — extracted from src/mcp/debug-server.ts (that daemon-side\n// module now lives in @ait-co/debugger as packages/debugger/src/mcp/debug-server.ts)\n// ---------------------------------------------------------------------------\n\n/**\n * Starts a periodic watcher that detects when the parent process (e.g. Claude\n * Code) has died without sending SIGTERM/SIGHUP, and calls `onOrphaned` so the\n * daemon can self-terminate rather than running as a zombie.\n *\n * Mirrors the `startAttachWatcher` pattern: `setInterval`-based, returns\n * `{ stop(): void }`, injectable deps for testability.\n *\n * @param onOrphaned - Called once when the parent is gone.\n * @param opts.intervalMs - Poll interval in milliseconds (default 5 000).\n * @param opts.initialPpid - Parent PID to watch (default `process.ppid`).\n * @param opts.isAlive - Predicate to test if a PID is running (default `isPidAlive`).\n * @param opts.getPpid - Supplier of current ppid (default `() => process.ppid`).\n * Detects ppid changes as well as death.\n * @param opts.log - Logger (default `process.stderr.write`).\n *\n * @returns `stop` — call during shutdown to clear the interval.\n */\nexport function startParentWatcher(\n onOrphaned: () => void,\n opts?: {\n intervalMs?: number;\n initialPpid?: number;\n isAlive?: (pid: number) => boolean;\n getPpid?: () => number;\n log?: (msg: string) => void;\n },\n): { stop(): void } {\n const {\n intervalMs = 5_000,\n initialPpid = process.ppid,\n isAlive = isPidAlive,\n getPpid = () => process.ppid,\n log = (msg: string) => process.stderr.write(msg),\n } = opts ?? {};\n\n // PID 1 is init/launchd — running under a process manager or as a detached\n // daemon. There is no meaningful parent to watch; skip the watcher entirely.\n if (initialPpid <= 1) {\n log('[ait-debug] parent-pid watcher: no parent to watch (ppid<=1), skipping\\n');\n return { stop() {} };\n }\n\n let fired = false;\n\n const handle = setInterval(() => {\n if (fired) return;\n\n const currentPpid = getPpid();\n const orphaned = currentPpid !== initialPpid || !isAlive(initialPpid);\n\n if (orphaned) {\n fired = true;\n clearInterval(handle);\n log(\n `[ait-debug] parent-pid watcher: parent PID ${initialPpid} is gone (currentPpid=${currentPpid}) — shutting down\\n`,\n );\n onOrphaned();\n }\n }, intervalMs);\n\n return {\n stop() {\n clearInterval(handle);\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// startMaxAgeWatchdog — FIX 4: daemon lifetime cap\n// ---------------------------------------------------------------------------\n\n/**\n * Starts a periodic watchdog that calls `onExpired` once after `maxAgeMs`\n * milliseconds have elapsed since the watchdog was created.\n *\n * Motivation (issue #571): cloudflared quick-tunnel lifetimes are finite (a\n * few hours). A daemon that has been running for days will have outlived its\n * tunnel regardless of whether the tunnel process exited cleanly. This watchdog\n * caps the daemon's maximum age and forces a fresh start so the tunnel is\n * replaced before it silently expires.\n *\n * @param onExpired - Called once when the maximum age is reached. The caller\n * should call `shutdown()` then `process.exit(0)`.\n * @param opts.maxAgeMs - Maximum daemon lifetime in ms. Default 6 h.\n * @param opts.intervalMs - Check interval in ms. Default 60 000 (1 min).\n * @param opts.now - Time source (injectable for tests). Default `Date.now`.\n *\n * @returns `stop` — call during shutdown to clear the interval.\n */\nexport function startMaxAgeWatchdog(\n onExpired: () => void,\n opts: {\n maxAgeMs?: number;\n intervalMs?: number;\n now?: () => number;\n } = {},\n): { stop(): void } {\n const {\n maxAgeMs = 6 * 60 * 60 * 1_000, // 6 hours\n intervalMs = 60_000,\n now = () => Date.now(),\n } = opts;\n\n const startedAt = now();\n let fired = false;\n\n const handle = setInterval(() => {\n if (fired) return;\n if (now() - startedAt >= maxAgeMs) {\n fired = true;\n clearInterval(handle);\n onExpired();\n }\n }, intervalMs);\n\n return {\n stop() {\n clearInterval(handle);\n },\n };\n}\n","/**\n * @ait-co/devtools unplugin\n *\n * 모든 주요 번들러를 지원하는 단일 플러그인.\n * @apps-in-toss/web-framework → @ait-co/devtools/mock 으로 alias 설정.\n *\n * Usage:\n * import aitDevtools from '@ait-co/devtools/unplugin';\n *\n * // Vite\n * export default { plugins: [aitDevtools.vite()] };\n *\n * // Webpack / Next.js\n * config.plugins.push(aitDevtools.webpack());\n *\n * // Rspack\n * config.plugins.push(aitDevtools.rspack());\n *\n * // esbuild\n * { plugins: [aitDevtools.esbuild()] }\n *\n * // Rollup\n * { plugins: [aitDevtools.rollup()] }\n */\n\nimport { fileURLToPath } from 'node:url';\nimport { createUnplugin } from 'unplugin';\nimport { startParentWatcher } from '../shared/parent-watcher.js';\nimport {\n buildInAppSnippet,\n DEBUGGER_DEV_BRIDGE_ID,\n hasDebugConsole,\n hasDebugger,\n hasInAppWiring,\n INSTALL_HINT,\n} from './optional-peers.js';\n\n/**\n * The slice of `startQuickTunnel`'s handle that `@ait-co/debugger`'s relay\n * bootstrap consumes. Declared structurally rather than imported from the peer\n * so this module never type-depends on an OPTIONAL peer being installed.\n */\ninterface QuickTunnelLike {\n /** Public `https://` base URL. SECRET-HANDLING: carries the tunnel host — never log. */\n url: string;\n /** Idempotent teardown. */\n stop: () => void;\n}\n\n/**\n * The slice of `@ait-co/debugger/dev-bridge`'s `startDevServerCdpRelay` handle\n * this plugin uses. Structural for the same reason as {@link QuickTunnelLike}.\n */\ninterface DevServerCdpRelayLike {\n /** `http://127.0.0.1:<port>` — loopback, safe to surface (issue #530). */\n localHttpUrl: string;\n /** Public `https://` relay base. SECRET-HANDLING: never log. */\n httpUrl: string;\n /** Public `wss://` relay URL the launcher QR carries. SECRET-HANDLING: never log. */\n wssUrl: string;\n /** Tears down the relay tunnel and then the relay. Idempotent, never rejects. */\n close: () => Promise<void>;\n}\n\n/**\n * Resolve `@ait-co/devtools/mock` to its real file path at plugin-load time.\n *\n * Returning the bare specifier from `resolveId` would stop the bundler from\n * walking node_modules for it — Vite 8+ treats such a non-null string as the\n * final resolved id and serves it via the virtual `/@id/` prefix, which 404s\n * because we don't provide a `load` hook. Resolving to an absolute path here\n * lets every supported bundler load the file the normal way.\n */\nconst MOCK_PATH = (() => {\n try {\n return fileURLToPath(import.meta.resolve('@ait-co/devtools/mock'));\n } catch {\n // Fallback for runtimes where `import.meta.resolve` is unavailable.\n return '@ait-co/devtools/mock';\n }\n})();\n\nexport interface AitDevtoolsOptions {\n /**\n * 패널 자동 주입 여부 (default: true)\n * true이면 진입점에 floating panel import를 자동 추가한다.\n */\n panel?: boolean;\n /**\n * In-app debug attach 자동 주입 여부 (default: true).\n *\n * true이면 진입점에 게이트된 dynamic import를 자동 추가한다:\n * `?debug=1` + `relay` URL 파라미터가 모두 존재할 때만 런타임에\n * `@ait-co/debug-console`을 로드하고 `maybeAttach()`를 호출한다.\n *\n * gate가 통과하지 못하면 chunk 자체를 로드하지 않으므로 일반 production\n * 로드에서 dormant하고, 번들러의 DCE 대상이 된다.\n *\n * `@ait-co/debug-console`은 **optional peer**다 (#817). 설치돼 있지 않으면\n * 주입 자체를 하지 않으므로 attach 코드가 번들에 구조적으로 들어갈 수 없다 —\n * 이게 디버그 표면 보안 스코프의 기술적 강제 지점이다. 환경 1(브라우저 mock)만\n * 쓰는 소비자는 아무것도 추가로 설치하지 않아도 된다.\n *\n * 소비자가 이미 직접 배선한 경우 중복 주입을 방지하기 위해 파일에\n * `@ait-co/debug-console`(또는 분리 전 `@ait-co/devtools/in-app`)이 이미 있으면\n * 자동으로 스킵한다.\n */\n inApp?: boolean;\n /**\n * mock alias 활성화 여부. default: true (development), false (production)\n */\n mock?: boolean;\n /**\n * Vite dev server에 MCP state endpoint를 추가할지 여부 (default: false).\n *\n * `true`로 설정하면:\n * - GET /api/ait-devtools/state — 마지막으로 브라우저가 push한 mock state 스냅샷 반환\n * - POST /api/ait-devtools/state — 브라우저 panel이 상태 변경 시 자동 push (panel 내부 처리)\n *\n * 이 endpoint는 이 패키지가 여는 producer 쪽이고, 읽는 consumer는 `@ait-co/debugger`의\n * MCP stdio 데몬(bin `debugger`, `--mode=dev`)이다 — 그쪽이 AI 에이전트에 mock state를\n * 노출한다. 데몬은 optional peer라 미설치여도 이 endpoint 자체는 정상 동작한다.\n * Vite 전용: webpack/rspack/esbuild/rollup 환경에서는 무시된다.\n */\n mcp?: boolean;\n /**\n * 미니앱의 webViewType (`granite.config.ts`의 `webViewProps.type`)을 빌드 상수\n * `__WEB_VIEW_TYPE__`로 주입한다 (#580). **Vite 전용** (다른 번들러는 무시).\n *\n * 이 상수는 in-app self-report(`@ait-co/debug-console`)가 읽어 launcher(env-2\n * PWA)에 webViewType을 postMessage로 알리고, launcher가 game 타입 미니앱에서\n * 수동 `?navBarType=game` URL 편집 없이 game 모드로 자동 진입하게 한다.\n *\n * 미지정 시 `'partner'`(web-framework `webViewProps.type`의 `@default`)로 주입한다.\n * `game`이면 게임 모드로 자동 진입한다. (granite.config.ts를 config 시점에\n * 자동으로 읽는 것은 TS 모듈 로더가 필요해 보류 — 명시 옵션으로 신뢰성 확보, #580.)\n */\n webViewType?: 'partner' | 'game';\n /**\n * 미니앱의 `granite.config.ts` `navigationBar.transparentBackground` 값\n * (SDK `@apps-in-toss/plugins@2.8.0` 신규 필드, #587). `true`이면 env-2 launcher\n * deep-link에 `&navBarTransparent=1`을 주입해 launcher partner bar가 투명 배경으로\n * 렌더된다. granite.config를 직접 읽지 않는다(version-agnostic, #580 원칙) —\n * 소비자 vite.config가 `graniteConfig.navigationBar?.transparentBackground`를\n * import해 이 옵션으로 넘긴다. 미지정 시 주입 안 함(URL 청정, back-compat).\n */\n navBarTransparent?: boolean;\n /**\n * 미니앱의 `granite.config.ts` `navigationBar.theme` 값\n * (SDK `@apps-in-toss/plugins@2.8.0` 신규 필드, #587). `'light'` 또는 `'dark'`이면\n * env-2 launcher deep-link에 `&navBarTheme=<v>`를 주입해 launcher partner bar가\n * 해당 테마 글자/아이콘 색으로 렌더된다. granite.config를 직접 읽지 않는다\n * (version-agnostic, #580 원칙). 미지정 시 주입 안 함(URL 청정, back-compat).\n */\n navBarTheme?: 'light' | 'dark';\n /**\n * Vite dev 서버를 Cloudflare quick tunnel(`*.trycloudflare.com`, 계정 불필요)로\n * 외부 노출해 실제 폰에서 미리보기. **Vite dev 모드 전용** — production에서는\n * 터널을 띄우지 않는다 (의도치 않은 노출 방지). 다른 번들러는\n * 무시. `true`면 기본 동작, 객체로 세부 설정 가능.\n */\n tunnel?:\n | boolean\n | {\n /** 노출할 포트 (미지정 시 dev 서버가 실제 listen한 포트 자동 감지). */\n port?: number;\n /** 터미널 ASCII QR 출력 (default: true). */\n qr?: boolean;\n /**\n * 환경 2(실기기 PWA)에 CDP 디버깅 배선 (default: false).\n *\n * `true`면 dev 서버 HTTP 터널과 **별도로** Chii relay를 띄우고 그 relay에\n * 두 번째 quick tunnel을 붙여, launcher QR deep-link에 `&debug=1&relay=<wss>`를\n * 실어 보낸다. 폰의 PWA iframe이 in-app debug gate를 통과해 target.js를 주입받고,\n * AI host MCP가 그 relay에 client로 붙으면 실기기 WebKit 위에서 CDP 디버깅이 열린다.\n * mock SDK는 그대로라 `call_sdk`는 환경 2에서 mock을 친다 (fidelity 사다리의\n * 설계 의도 — SDK fidelity가 필요하면 환경 3로 올라간다).\n *\n * 이 경로는 **optional peer `@ait-co/debugger`**를 요구한다 (#817).\n * 미설치면 CDP 배선을 건너뛰고 일반 화면 미리보기 터널로 degrade하며,\n * 설치 안내를 한 번 출력한다.\n */\n cdp?: boolean;\n };\n}\n\nconst FRAMEWORK_ID = '@apps-in-toss/web-framework';\nconst BRIDGE_ID = '@apps-in-toss/web-bridge'; // back-compat (2.x)\nconst ANALYTICS_ID = '@apps-in-toss/web-analytics'; // back-compat (2.x)\nconst WEBVIEW_BRIDGE_ID = '@apps-in-toss/webview-bridge'; // 3.0+\n\n/** MCP state endpoint path — browser panel POSTs here, MCP server GETs here */\nconst MCP_STATE_PATH = '/api/ait-devtools/state';\n\n/**\n * Resolves the effective tunnel option (#425).\n *\n * An explicit `tunnel` value (including `false`) always takes priority over\n * env vars — the `??` operator means `undefined` (= omitted) falls through,\n * but `false` / `true` / an object are preserved as-is (non-breaking).\n *\n * When the option is omitted:\n * - `AIT_TUNNEL=1` enables the base screen-preview tunnel.\n * - `AIT_TUNNEL_CDP=1` (requires `AIT_TUNNEL`) upgrades to the CDP relay.\n * - Neither set → `false` (disabled).\n *\n * Extracted as a pure function so it can be unit-tested without standing up\n * a full Vite dev server.\n *\n * @param explicit - The `tunnel` option as passed by the consumer (or `undefined` when omitted).\n * @param env - The process environment (injectable for testing).\n */\nexport function resolveTunnelOption(\n explicit: AitDevtoolsOptions['tunnel'],\n env: Record<string, string | undefined>,\n): AitDevtoolsOptions['tunnel'] {\n return explicit ?? (env.AIT_TUNNEL ? { cdp: !!env.AIT_TUNNEL_CDP } : false);\n}\n\nconst aitDevtoolsPlugin = createUnplugin((options?: AitDevtoolsOptions) => {\n const isDev = process.env.NODE_ENV !== 'production';\n const shouldEnable = isDev;\n const shouldMock = shouldEnable && (options?.mock ?? isDev);\n const shouldPanel = shouldEnable && (options?.panel ?? true);\n // in-app attach 주입: shouldEnable과 동일하게 dev에서 자동.\n // maybeAttach()가 런타임 gate(Layer B·C)를 자체 검증하므로 dev 항상 주입이 안전하다.\n //\n // #817: 주입 대상인 `@ait-co/debug-console`은 optional peer다. 미설치면 주입\n // 자체를 하지 않는다 — attach 코드가 번들에 구조적으로 못 들어가는 게 보안\n // 스코프의 강제 지점이고, 환경 1만 쓰는 다수 소비자는 아무것도 더 설치하지\n // 않아도 된다. 사용자가 `inApp: true`로 명시 요청했는데 패키지가 없을 때만\n // 안내를 출력한다 (기본값 경로는 조용히 degrade — 상시 nag 금지).\n const debugConsoleInstalled = hasDebugConsole();\n const shouldInApp = shouldEnable && (options?.inApp ?? true) && debugConsoleInstalled;\n if (shouldEnable && options?.inApp === true && !debugConsoleInstalled) {\n console.warn(\n `[@ait-co/devtools] inApp: @ait-co/debug-console이 없어 in-app attach를 주입하지 않습니다. 설치: ${INSTALL_HINT}`,\n );\n }\n const shouldMcp = shouldEnable && (options?.mcp ?? false);\n\n // In-memory store for the last state snapshot pushed by the browser panel.\n // Only allocated when mcp: true to avoid any overhead in the common case.\n let lastState: string | null = null;\n\n // Tunnel is dev-only and Vite-only. Never under production, so a production\n // build can't accidentally expose itself.\n //\n // Tunnel toggle resolution (#425): an explicit `tunnel` option always wins;\n // when omitted, fall back to the AIT_TUNNEL / AIT_TUNNEL_CDP env vars so a\n // consumer needs no `tunnel:` line in vite.config to enable env-2 preview.\n // AIT_TUNNEL gates the base (screen preview); AIT_TUNNEL_CDP upgrades to the\n // CDP relay. Production safety is unchanged — the existing\n // `shouldTunnel = isDev && !!tunnelOpt` guard below still blocks prod builds.\n const tunnelOpt = resolveTunnelOption(options?.tunnel, process.env);\n const shouldTunnel = isDev && !!tunnelOpt;\n\n // #580: webViewType build constant. Injected as a Vite `define` so the\n // in-app self-report can post it to the launcher for game-mode auto-entry.\n // Defaults to 'partner' (web-framework webViewProps.type @default).\n const webViewType = options?.webViewType ?? 'partner';\n // #587: navigationBar appearance options (SDK 2.8.0 granite.config fields).\n // Forwarded to printTunnelBanner so the launcher deep-link carries the params.\n const navBarTransparent = options?.navBarTransparent;\n const navBarTheme = options?.navBarTheme;\n const tunnelConfig = typeof tunnelOpt === 'object' ? tunnelOpt : {};\n\n return {\n name: 'ait-co-devtools',\n enforce: 'pre' as const,\n\n resolveId(id: string) {\n if (!shouldMock) return null;\n // @apps-in-toss/web-framework → @ait-co/devtools/mock (absolute path)\n if (\n id === FRAMEWORK_ID ||\n id === WEBVIEW_BRIDGE_ID ||\n id === BRIDGE_ID ||\n id === ANALYTICS_ID\n ) {\n return MOCK_PATH;\n }\n return null;\n },\n\n transformInclude(id: string) {\n // panel 또는 inApp 주입 중 하나라도 필요하면 진입점 파일을 transform 대상으로 포함\n if (!shouldPanel && !shouldInApp) return false;\n // 진입점 파일에만 주입\n return (\n /\\.(tsx?|jsx?)$/.test(id) &&\n /\\/(main|index|entry|app)\\.[tj]sx?$/i.test(id) &&\n !id.includes('node_modules')\n );\n },\n\n transform(code: string) {\n let result = code;\n let changed = false;\n\n // 패널 주입: shouldPanel이 활성화되어 있고 아직 import가 없으면 prepend\n if (shouldPanel && !code.includes('@ait-co/devtools/panel')) {\n result = `import '@ait-co/devtools/panel';\\n${result}`;\n changed = true;\n }\n\n // in-app attach 주입: shouldInApp이 활성화되어 있고 아직 배선이 없으면 prepend.\n // 게이트된 dynamic import로 주입 — ?debug=1 + relay 파라미터가 모두 있을 때만\n // 런타임에 @ait-co/debug-console을 로드하고 maybeAttach()를 호출한다 (#817).\n // production DCE: URLSearchParams gate가 조건을 false로 평가하면\n // dynamic import 자체가 dead code — 번들러가 제거 가능.\n // dedupe는 신·구 specifier를 모두 인정한다 (hasInAppWiring).\n if (shouldInApp && !hasInAppWiring(code)) {\n result = `${buildInAppSnippet()}\\n${result}`;\n changed = true;\n }\n\n return changed ? result : null;\n },\n\n // Vite-only: register the MCP state HTTP endpoint on the dev server, and\n // optionally start a Cloudflare quick tunnel once the dev server is listening.\n // Non-Vite bundlers do not have a dev server concept so this is silently\n // skipped (unplugin passes `vite` key only when building for Vite).\n vite: {\n config() {\n // #580: inject the webViewType build constant for every Vite build so\n // the in-app self-report (@ait-co/debug-console) can read it and post\n // it to the launcher (env-2 PWA) for game-mode auto-entry. JSON.stringify\n // makes it a string literal at the define substitution site.\n const define = { __WEB_VIEW_TYPE__: JSON.stringify(webViewType) };\n if (!shouldTunnel) return { define };\n // Vite blocks requests whose Host header isn't in `server.allowedHosts`\n // (defaults to localhost only). The quick-tunnel hostname is random per\n // run, so allow the whole `.trycloudflare.com` suffix while the tunnel\n // is on. (A leading `.` makes Vite match the domain and its subdomains.)\n return { define, server: { allowedHosts: ['.trycloudflare.com'] } };\n },\n\n configureServer(server: import('vite').ViteDevServer) {\n // MCP state endpoint: browser panel POSTs state here, the @ait-co/debugger\n // MCP stdio daemon GETs it.\n if (shouldMcp) {\n server.middlewares.use(MCP_STATE_PATH, (req, res) => {\n // Allow Claude Code / AI agents (running locally) to read state\n res.setHeader('Access-Control-Allow-Origin', '*');\n res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type');\n\n if (req.method === 'OPTIONS') {\n res.writeHead(204);\n res.end();\n return;\n }\n\n if (req.method === 'GET') {\n if (lastState === null) {\n res.writeHead(503, { 'Content-Type': 'application/json' });\n res.end(\n JSON.stringify({\n error: 'No state received yet. Open the app in a browser first.',\n }),\n );\n return;\n }\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(lastState);\n return;\n }\n\n if (req.method === 'POST') {\n const chunks: Buffer[] = [];\n req.on('data', (chunk: Buffer) => chunks.push(chunk));\n req.on('end', () => {\n try {\n const body = Buffer.concat(chunks).toString('utf-8');\n // Validate it's parseable JSON before caching\n JSON.parse(body);\n lastState = body;\n res.writeHead(204);\n res.end();\n } catch {\n res.writeHead(400, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'Invalid JSON' }));\n }\n });\n return;\n }\n\n res.writeHead(405, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'Method not allowed' }));\n });\n }\n\n // Tunnel: start a Cloudflare quick tunnel once the dev server is listening.\n if (shouldTunnel) {\n let tunnel: { stop: () => void } | null = null;\n // env-2 CDP wiring (tunnel.cdp): the relay handle returned by\n // `@ait-co/debugger`'s bootstrap. Its `close()` owns BOTH the relay\n // tunnel and the relay, so no second handle is tracked here.\n // Fire-and-forget close on teardown.\n let relay: { close: () => Promise<void> } | null = null;\n // env-2 HTML dashboard (issue #408): local 127.0.0.1 HTTP server that\n // serves the QR + connect-steps + FAQ page (env 3/4 UX parity), opened\n // in the browser when CDP is wired + GUI present. Torn down with the\n // tunnel. Only set when the dashboard actually started.\n let qrDashboard: { close: () => Promise<void> } | null = null;\n // env-2 URL file store (#424): captured after the first writeRelayUrls\n // call so cleanup() can call deleteRelayUrls without a re-import.\n // SECRET-HANDLING: the stored function reference never carries URL values.\n let relayUrlDeleteFn: ((projectRoot: string) => Promise<void>) | null = null;\n // #420: parent-PID watcher — self-terminate when vite's parent dies so\n // cloudflared children don't become zombies holding stale tunnels.\n let parentWatcher: { stop(): void } | null = null;\n const httpServer = server.httpServer;\n\n httpServer?.once('listening', () => {\n const address = httpServer?.address();\n const port =\n tunnelConfig.port ??\n (address && typeof address === 'object' ? address.port : undefined);\n if (!port) {\n console.warn(\n '[@ait-co/devtools] tunnel: could not determine the dev server port; skipping.',\n );\n return;\n }\n // Dynamic import keeps `cloudflared` / `qrcode-terminal` off the\n // module graph unless the tunnel is actually used.\n import('./tunnel.js')\n .then(async ({ startQuickTunnel, printTunnelBanner, startTunnelDashboard }) => {\n const t = await startQuickTunnel(port);\n tunnel = t;\n\n // env-2 CDP: boot a Chii relay (OS-assigned local port) and a\n // second quick tunnel to it. The relay's https tunnel URL becomes\n // the `wss://` relay the launcher QR carries (&debug=1&relay=).\n let relayWssUrl: string | undefined;\n // SECRET-HANDLING: relayHttpUrl carries the relay host — never logged.\n let relayHttpUrl: string | undefined;\n // LOCAL relay base — loopback URL, safe to surface (issue #530).\n let relayLocalHttpUrl: string | undefined;\n // #817: env-2 CDP는 optional peer `@ait-co/debugger`를 요구한다.\n // 미설치면 relay·dashboard 배선을 통째로 건너뛰고 일반 화면\n // 미리보기 터널로 degrade한다 — 사용자가 명시적으로 cdp를 켠\n // 경로이므로 설치 안내를 한 번 출력한다.\n // SECRET-HANDLING: 고정 문구만 — URL·host·코드 없음.\n if (tunnelConfig.cdp && !hasDebugger()) {\n console.warn(\n `[@ait-co/devtools] tunnel: @ait-co/debugger가 없어 CDP relay를 건너뜁니다 — 화면 미리보기는 그대로 동작합니다. 설치: ${INSTALL_HINT}`,\n );\n } else if (tunnelConfig.cdp) {\n try {\n // #818: the whole relay bootstrap is now `@ait-co/debugger`'s.\n // Its `/dev-bridge` entry performs, in one call and in a fixed\n // order, the four steps this file used to run against local\n // `../mcp/*` modules: mint/load the project-local `.ait_relay`\n // TOTP secret, fail fast if relay auth is unconfigured, start\n // the Chii relay behind the TOTP upgrade gate, then open a\n // tunnel to the port it bound.\n //\n // Relay-auth baseline (issue #250): the env-2 CDP relay is\n // reachable over a public `*.trycloudflare.com` tunnel, so a\n // configured TOTP secret is MANDATORY and the relay enforces\n // it on every WS upgrade. The secret file is anchored at the\n // nearest package.json above `server.config.root` — the same\n // anchor the MCP daemon resolves read-only, which is why the\n // dev server's root is what gets passed here (issues #394/#396).\n //\n // `cloudflared` stays on THIS side: `openTunnel` is injected\n // so the spawner (and its sanitising error handling) remains\n // the dev-server plugin's, not the daemon's.\n //\n // SECRET-HANDLING: nothing in this block logs the secret, the\n // TOTP code, the tunnel host, or the relay URL.\n const { startDevServerCdpRelay } = (await import(DEBUGGER_DEV_BRIDGE_ID)) as {\n startDevServerCdpRelay: (opts: {\n projectRoot: string;\n openTunnel: (localPort: number) => Promise<QuickTunnelLike>;\n onAuthReject?: (event: { kind: string }) => void;\n }) => Promise<DevServerCdpRelayLike>;\n };\n // Issue #467: this relay lives in the vite process, so the\n // MCP daemon's get_debug_status counter cannot see its 401s.\n // Surface a throttled hint in the vite terminal instead.\n // SECRET-HANDLING: fixed message only — no URL, code, host.\n let lastAuthRejectWarnAt = 0;\n const r = await startDevServerCdpRelay({\n projectRoot: server.config.root,\n openTunnel: (localPort: number) => startQuickTunnel(localPort),\n onAuthReject: () => {\n const nowMs = Date.now();\n if (nowMs - lastAuthRejectWarnAt < 10_000) return;\n lastAuthRejectWarnAt = nowMs;\n console.warn(\n '[@ait-co/devtools] tunnel: relay 인증(TOTP) 거부 감지 — 폰에서 QR을 다시 스캔하세요 (코드는 ~3분마다 만료)',\n );\n },\n });\n // r.close() tears down the relay tunnel AND the relay, in that\n // order — there is no separate handle to keep here any more.\n relay = r;\n // SECRET-HANDLING: httpUrl/wssUrl carry the relay host — stored\n // for the .ait_urls write and the QR below; never logged.\n relayHttpUrl = r.httpUrl;\n relayWssUrl = r.wssUrl;\n // LOCAL relay base for MCP inspector URL assembly (issue #530):\n // the relay process runs on this machine, so the inspector\n // front_end + client WS can use the loopback address directly —\n // no tunnel round-trip for the developer's browser.\n // Safe to surface: loopback URL contains no tunnel host.\n relayLocalHttpUrl = r.localHttpUrl;\n } catch (err: unknown) {\n console.warn(\n `[@ait-co/devtools] tunnel: CDP relay not started — screen preview works without on-device debugging: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n }\n }\n\n // Read the app name from the project's package.json to add to\n // the launcher deep-link (#498). Failure is silently ignored.\n let tunnelAppName: string | undefined;\n try {\n const { readFileSync } = await import('node:fs');\n const pkgPath = `${server.config.root}/package.json`;\n const pkgRaw = readFileSync(pkgPath, 'utf8');\n const pkg = JSON.parse(pkgRaw) as Record<string, unknown>;\n const rawName = typeof pkg.name === 'string' ? pkg.name : '';\n const stripped = rawName.includes('/')\n ? rawName.slice(rawName.indexOf('/') + 1)\n : rawName;\n tunnelAppName = stripped.trim() || undefined;\n } catch {\n // Silently ignore — fail-open.\n }\n\n await printTunnelBanner(t.url, {\n qr: tunnelConfig.qr,\n relayWssUrl,\n name: tunnelAppName,\n webViewType,\n navBarTransparent,\n navBarTheme,\n });\n\n // env-2 URL file-based discovery (#424): write .ait_urls so the\n // MCP daemon can discover the relay/tunnel URLs without manual env\n // var copy-paste. SECRET-HANDLING: URL values are never logged.\n // Capture deleteRelayUrls in the outer-scope fn so cleanup() can\n // call it without re-importing (no async in signal handlers).\n const { writeRelayUrls, deleteRelayUrls } = await import('./relay-url-store.js');\n await writeRelayUrls({\n projectRoot: server.config.root,\n tunnelBaseUrl: t.url,\n ...(relayHttpUrl !== undefined ? { relayBaseUrl: relayHttpUrl } : {}),\n // Issue #530: local relay base for inspector URL (loopback, no tunnel host).\n ...(relayLocalHttpUrl !== undefined ? { relayLocalUrl: relayLocalHttpUrl } : {}),\n });\n relayUrlDeleteFn = (root: string) => deleteRelayUrls({ projectRoot: root });\n\n // env-2 HTML dashboard (issue #408): when CDP is wired and a GUI\n // is present, serve the same QR+FAQ dashboard env 3/4 uses and\n // open it in the browser. No-op (returns undefined) for the\n // screen-only tunnel, headless, qr:false, or AIT_AUTO_DEVTOOLS=0\n // — the ASCII QR above remains the fallback in those cases.\n if (relayWssUrl) {\n qrDashboard =\n (await startTunnelDashboard({\n tunnelUrl: t.url,\n relayWssUrl,\n qr: tunnelConfig.qr,\n name: tunnelAppName,\n })) ?? null;\n }\n\n // #420: start watching the parent PID now that tunnel resources\n // are allocated. When the parent dies/reparents, clean up\n // synchronously (stops cloudflared children) then exit.\n parentWatcher = startParentWatcher(() => {\n cleanup();\n process.exit(0);\n });\n })\n .catch((err: unknown) => {\n console.warn(\n `[@ait-co/devtools] tunnel failed to start: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n });\n });\n\n const cleanup = () => {\n parentWatcher?.stop();\n tunnel?.stop();\n void relay?.close();\n void qrDashboard?.close();\n // env-2 URL file cleanup (#424): remove .ait_urls on teardown so a\n // stale file doesn't cause the MCP daemon to attempt a doomed attach.\n // SECRET-HANDLING: relayUrlDeleteFn never logs the path or URL values.\n void relayUrlDeleteFn?.(server.config.root);\n };\n httpServer?.once('close', cleanup);\n process.once('SIGINT', cleanup);\n process.once('SIGTERM', cleanup);\n process.once('SIGHUP', cleanup);\n process.once('exit', cleanup);\n }\n },\n },\n };\n});\n\nexport const vite = aitDevtoolsPlugin.vite;\nexport const webpack = aitDevtoolsPlugin.webpack;\nexport const rollup = aitDevtoolsPlugin.rollup;\nexport const esbuild = aitDevtoolsPlugin.esbuild;\nexport const rspack = aitDevtoolsPlugin.rspack;\n\nexport default aitDevtoolsPlugin;\n"],"mappings":"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"}
|
|
@@ -8,8 +8,6 @@ import { EventEmitter } from "node:events";
|
|
|
8
8
|
import { Server as Server$1, ServerOptions as ServerOptions$1 } from "node:https";
|
|
9
9
|
import * as net from "node:net";
|
|
10
10
|
import { Duplex, DuplexOptions, Stream } from "node:stream";
|
|
11
|
-
import * as _$esbuild from "esbuild";
|
|
12
|
-
import esbuild$1 from "esbuild";
|
|
13
11
|
import { SecureContextOptions } from "node:tls";
|
|
14
12
|
import { URL as URL$1 } from "node:url";
|
|
15
13
|
import { ZlibOptions } from "node:zlib";
|
|
@@ -6326,6 +6324,27 @@ interface TransformOptions$1 extends BindingEnhancedTransformOptions {}
|
|
|
6326
6324
|
* @category Utilities
|
|
6327
6325
|
*/
|
|
6328
6326
|
//#endregion
|
|
6327
|
+
//#region node_modules/.pnpm/esbuild@0.28.1/node_modules/esbuild/lib/main.d.ts
|
|
6328
|
+
// Note: These declarations exist to avoid type errors when you omit "dom" from
|
|
6329
|
+
// "lib" in your "tsconfig.json" file. TypeScript confusingly declares the
|
|
6330
|
+
// global "WebAssembly" type in "lib.dom.d.ts" even though it has nothing to do
|
|
6331
|
+
// with the browser DOM and is present in many non-browser JavaScript runtimes
|
|
6332
|
+
// (e.g. node and deno). Declaring it here allows esbuild's API to be used in
|
|
6333
|
+
// these scenarios.
|
|
6334
|
+
//
|
|
6335
|
+
// There's an open issue about getting this problem corrected (although these
|
|
6336
|
+
// declarations will need to remain even if this is fixed for backward
|
|
6337
|
+
// compatibility with older TypeScript versions):
|
|
6338
|
+
//
|
|
6339
|
+
// https://github.com/microsoft/TypeScript-DOM-lib-generator/issues/826
|
|
6340
|
+
//
|
|
6341
|
+
declare global {
|
|
6342
|
+
namespace WebAssembly {
|
|
6343
|
+
interface Module {}
|
|
6344
|
+
}
|
|
6345
|
+
interface URL {}
|
|
6346
|
+
}
|
|
6347
|
+
//#endregion
|
|
6329
6348
|
//#region node_modules/.pnpm/vite@8.0.16_@types+node@25.6.0_esbuild@0.28.1_jiti@2.6.1_terser@5.46.1_tsx@4.22.4_yaml@2.8.3/node_modules/vite/types/internal/esbuildOptions.d.ts
|
|
6330
6349
|
/* eslint-enable @typescript-eslint/ban-ts-comment */
|
|
6331
6350
|
type EsbuildTarget = string | string[];
|
|
@@ -10283,7 +10302,9 @@ interface AitDevtoolsOptions {
|
|
|
10283
10302
|
* - GET /api/ait-devtools/state — 마지막으로 브라우저가 push한 mock state 스냅샷 반환
|
|
10284
10303
|
* - POST /api/ait-devtools/state — 브라우저 panel이 상태 변경 시 자동 push (panel 내부 처리)
|
|
10285
10304
|
*
|
|
10286
|
-
* 이 endpoint
|
|
10305
|
+
* 이 endpoint는 이 패키지가 여는 producer 쪽이고, 읽는 consumer는 `@ait-co/debugger`의
|
|
10306
|
+
* MCP stdio 데몬(bin `debugger`, `--mode=dev`)이다 — 그쪽이 AI 에이전트에 mock state를
|
|
10307
|
+
* 노출한다. 데몬은 optional peer라 미설치여도 이 endpoint 자체는 정상 동작한다.
|
|
10287
10308
|
* Vite 전용: webpack/rspack/esbuild/rollup 환경에서는 무시된다.
|
|
10288
10309
|
*/
|
|
10289
10310
|
mcp?: boolean;
|
|
@@ -10291,7 +10312,7 @@ interface AitDevtoolsOptions {
|
|
|
10291
10312
|
* 미니앱의 webViewType (`granite.config.ts`의 `webViewProps.type`)을 빌드 상수
|
|
10292
10313
|
* `__WEB_VIEW_TYPE__`로 주입한다 (#580). **Vite 전용** (다른 번들러는 무시).
|
|
10293
10314
|
*
|
|
10294
|
-
* 이 상수는 in-app self-report(`@ait-co/
|
|
10315
|
+
* 이 상수는 in-app self-report(`@ait-co/debug-console`)가 읽어 launcher(env-2
|
|
10295
10316
|
* PWA)에 webViewType을 postMessage로 알리고, launcher가 game 타입 미니앱에서
|
|
10296
10317
|
* 수동 `?navBarType=game` URL 편집 없이 game 모드로 자동 진입하게 한다.
|
|
10297
10318
|
*
|
|
@@ -10366,7 +10387,7 @@ declare const aitDevtoolsPlugin: _$unplugin.UnpluginInstance<AitDevtoolsOptions
|
|
|
10366
10387
|
declare const vite: (options?: AitDevtoolsOptions | undefined) => Plugin<any> | Plugin<any>[];
|
|
10367
10388
|
declare const webpack: (options?: AitDevtoolsOptions | undefined) => WebpackPluginInstance;
|
|
10368
10389
|
declare const rollup: (options?: AitDevtoolsOptions | undefined) => _$unplugin.RollupPlugin<any> | _$unplugin.RollupPlugin<any>[];
|
|
10369
|
-
declare const esbuild: (options?: AitDevtoolsOptions | undefined) => _$
|
|
10390
|
+
declare const esbuild: (options?: AitDevtoolsOptions | undefined) => _$unplugin.EsbuildPlugin;
|
|
10370
10391
|
declare const rspack: (options?: AitDevtoolsOptions | undefined) => RspackPluginInstance;
|
|
10371
10392
|
//#endregion
|
|
10372
10393
|
export { AitDevtoolsOptions, aitDevtoolsPlugin as default, esbuild, resolveTunnelOption, rollup, rspack, vite, webpack };
|