@ait-co/devtools 0.1.144 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. package/README.en.md +33 -217
  2. package/README.md +33 -246
  3. package/dist/in-app/auto.d.ts +1 -138
  4. package/dist/in-app/auto.js +28 -1102
  5. package/dist/in-app/auto.js.map +1 -1
  6. package/dist/in-app/index.d.ts +38 -547
  7. package/dist/in-app/index.d.ts.map +1 -1
  8. package/dist/in-app/index.js +62 -939
  9. package/dist/in-app/index.js.map +1 -1
  10. package/dist/mcp/cli.d.ts +1 -54
  11. package/dist/mcp/cli.js +33 -9720
  12. package/dist/mcp/cli.js.map +1 -1
  13. package/dist/mcp/server.d.ts +1 -88
  14. package/dist/mcp/server.js +36 -1076
  15. package/dist/mcp/server.js.map +1 -1
  16. package/dist/mock/index.d.ts +19 -20
  17. package/dist/mock/index.d.ts.map +1 -1
  18. package/dist/mock/index.js.map +1 -1
  19. package/dist/panel/index.js +1 -103
  20. package/dist/panel/index.js.map +1 -1
  21. package/dist/relay-url-store-CPZAn-T5.js +107 -0
  22. package/dist/relay-url-store-CPZAn-T5.js.map +1 -0
  23. package/dist/relay-url-store-DLjlvMSA.cjs +108 -0
  24. package/dist/relay-url-store-DLjlvMSA.cjs.map +1 -0
  25. package/dist/stubs/bin-devtools-mcp.js +58 -0
  26. package/dist/stubs/bin-devtools-mcp.js.map +1 -0
  27. package/dist/stubs/bin-devtools-test.d.ts +2 -0
  28. package/dist/stubs/bin-devtools-test.js +55 -0
  29. package/dist/stubs/bin-devtools-test.js.map +1 -0
  30. package/dist/test-runner/config.d.ts +1 -231
  31. package/dist/test-runner/config.js +41 -45
  32. package/dist/test-runner/config.js.map +1 -1
  33. package/dist/{tunnel-BGT9Curk.cjs → tunnel-BKZkOyQp.cjs} +1 -1
  34. package/dist/{tunnel-BGT9Curk.cjs.map → tunnel-BKZkOyQp.cjs.map} +1 -1
  35. package/dist/{tunnel-BOKmLzBO.js → tunnel-CqSCIrdU.js} +1 -1
  36. package/dist/{tunnel-BOKmLzBO.js.map → tunnel-CqSCIrdU.js.map} +1 -1
  37. package/dist/unplugin/index.cjs +9 -18
  38. package/dist/unplugin/index.cjs.map +1 -1
  39. package/dist/unplugin/index.d.cts +22 -3
  40. package/dist/unplugin/index.d.cts.map +1 -1
  41. package/dist/unplugin/index.d.ts +23 -4
  42. package/dist/unplugin/index.d.ts.map +1 -1
  43. package/dist/unplugin/index.js +10 -19
  44. package/dist/unplugin/index.js.map +1 -1
  45. package/package.json +10 -25
  46. package/dist/attach-orchestrator-0F0m_UqQ.js +0 -1845
  47. package/dist/attach-orchestrator-0F0m_UqQ.js.map +0 -1
  48. package/dist/attach-orchestrator-D65KxFy_.js +0 -1831
  49. package/dist/attach-orchestrator-D65KxFy_.js.map +0 -1
  50. package/dist/attach-orchestrator-DL3NQ9ca.js +0 -1846
  51. package/dist/attach-orchestrator-DL3NQ9ca.js.map +0 -1
  52. package/dist/bundle-C796JIwG.d.ts +0 -159
  53. package/dist/bundle-C796JIwG.d.ts.map +0 -1
  54. package/dist/capture-DsP525OZ.d.ts +0 -58
  55. package/dist/capture-DsP525OZ.d.ts.map +0 -1
  56. package/dist/cdp-connection-rP1WdnH5.d.ts +0 -287
  57. package/dist/cdp-connection-rP1WdnH5.d.ts.map +0 -1
  58. package/dist/cell-BaLvusOl.js +0 -68
  59. package/dist/cell-BaLvusOl.js.map +0 -1
  60. package/dist/cell-CBUS3-nT.js +0 -274
  61. package/dist/cell-CBUS3-nT.js.map +0 -1
  62. package/dist/cell-EBKKpAAT.js +0 -307
  63. package/dist/cell-EBKKpAAT.js.map +0 -1
  64. package/dist/chii-relay-B3ZhjGMi.js +0 -304
  65. package/dist/chii-relay-B3ZhjGMi.js.map +0 -1
  66. package/dist/chii-relay-CGMlePMd.cjs +0 -304
  67. package/dist/chii-relay-CGMlePMd.cjs.map +0 -1
  68. package/dist/debug-server-B3ABDrRI.js +0 -456
  69. package/dist/debug-server-B3ABDrRI.js.map +0 -1
  70. package/dist/debug-server-BWhwrVXa.js +0 -1158
  71. package/dist/debug-server-BWhwrVXa.js.map +0 -1
  72. package/dist/debug-server-CfQNxxGW.js +0 -600
  73. package/dist/debug-server-CfQNxxGW.js.map +0 -1
  74. package/dist/devtools-opener-3Drge_RJ.js +0 -75
  75. package/dist/devtools-opener-3Drge_RJ.js.map +0 -1
  76. package/dist/devtools-opener-CJpEsXXQ.js +0 -76
  77. package/dist/devtools-opener-CJpEsXXQ.js.map +0 -1
  78. package/dist/devtools-opener-CxtryS8c.js +0 -75
  79. package/dist/devtools-opener-CxtryS8c.js.map +0 -1
  80. package/dist/in-app/auto.d.ts.map +0 -1
  81. package/dist/mcp/cli.d.ts.map +0 -1
  82. package/dist/mcp/server.d.ts.map +0 -1
  83. package/dist/pool-DcaaOwUq.d.ts +0 -14761
  84. package/dist/pool-DcaaOwUq.d.ts.map +0 -1
  85. package/dist/qr-http-server-C_lqOrgc.js +0 -1644
  86. package/dist/qr-http-server-C_lqOrgc.js.map +0 -1
  87. package/dist/qr-http-server-CopuMbub.js +0 -1644
  88. package/dist/qr-http-server-CopuMbub.js.map +0 -1
  89. package/dist/qr-http-server-DrbIVDjO.js +0 -1645
  90. package/dist/qr-http-server-DrbIVDjO.js.map +0 -1
  91. package/dist/relay-factory-N9QobQxG.js +0 -206
  92. package/dist/relay-factory-N9QobQxG.js.map +0 -1
  93. package/dist/relay-secret-store-BR0YIkNv.cjs +0 -241
  94. package/dist/relay-secret-store-BR0YIkNv.cjs.map +0 -1
  95. package/dist/relay-secret-store-Bmyleu0A.js +0 -154
  96. package/dist/relay-secret-store-Bmyleu0A.js.map +0 -1
  97. package/dist/relay-secret-store-CQenfcSL.js +0 -154
  98. package/dist/relay-secret-store-CQenfcSL.js.map +0 -1
  99. package/dist/relay-secret-store-CYM8CBIF.js +0 -240
  100. package/dist/relay-secret-store-CYM8CBIF.js.map +0 -1
  101. package/dist/relay-secret-store-DKxs7zwq.js +0 -153
  102. package/dist/relay-secret-store-DKxs7zwq.js.map +0 -1
  103. package/dist/relay-secret-store-WJ8EGkIl.js +0 -153
  104. package/dist/relay-secret-store-WJ8EGkIl.js.map +0 -1
  105. package/dist/relay-url-store-BR2XodiO.js +0 -123
  106. package/dist/relay-url-store-BR2XodiO.js.map +0 -1
  107. package/dist/relay-url-store-C1as_m5G.cjs +0 -115
  108. package/dist/relay-url-store-C1as_m5G.cjs.map +0 -1
  109. package/dist/relay-url-store-CH63fVCm.js +0 -122
  110. package/dist/relay-url-store-CH63fVCm.js.map +0 -1
  111. package/dist/relay-url-store-CzFo_84F.js +0 -114
  112. package/dist/relay-url-store-CzFo_84F.js.map +0 -1
  113. package/dist/relay-url-store-DaY1QPes.js +0 -123
  114. package/dist/relay-url-store-DaY1QPes.js.map +0 -1
  115. package/dist/relay-url-store-xmUuTjXA.js +0 -122
  116. package/dist/relay-url-store-xmUuTjXA.js.map +0 -1
  117. package/dist/relay-worker-B5HKkGUY.js +0 -832
  118. package/dist/relay-worker-B5HKkGUY.js.map +0 -1
  119. package/dist/relay-worker-YdlpZQl9.d.ts +0 -214
  120. package/dist/relay-worker-YdlpZQl9.d.ts.map +0 -1
  121. package/dist/rolldown-runtime-DGkTqVfb.js +0 -15
  122. package/dist/rolldown-runtime-DUslC3ob.js +0 -14
  123. package/dist/runtime-kn9DxOeg.d.ts +0 -249
  124. package/dist/runtime-kn9DxOeg.d.ts.map +0 -1
  125. package/dist/test-runner/bin.js +0 -2584
  126. package/dist/test-runner/bin.js.map +0 -1
  127. package/dist/test-runner/bridge-stub.d.ts +0 -125
  128. package/dist/test-runner/bridge-stub.d.ts.map +0 -1
  129. package/dist/test-runner/bridge-stub.js +0 -92
  130. package/dist/test-runner/bridge-stub.js.map +0 -1
  131. package/dist/test-runner/bundle.d.ts +0 -2
  132. package/dist/test-runner/bundle.js +0 -439
  133. package/dist/test-runner/bundle.js.map +0 -1
  134. package/dist/test-runner/capture.d.ts +0 -2
  135. package/dist/test-runner/capture.js +0 -44
  136. package/dist/test-runner/capture.js.map +0 -1
  137. package/dist/test-runner/config.d.ts.map +0 -1
  138. package/dist/test-runner/method-pace.d.ts +0 -82
  139. package/dist/test-runner/method-pace.d.ts.map +0 -1
  140. package/dist/test-runner/method-pace.js +0 -120
  141. package/dist/test-runner/method-pace.js.map +0 -1
  142. package/dist/test-runner/pool.d.ts +0 -2
  143. package/dist/test-runner/pool.js +0 -136
  144. package/dist/test-runner/pool.js.map +0 -1
  145. package/dist/test-runner/relay-factory.d.ts +0 -11245
  146. package/dist/test-runner/relay-factory.d.ts.map +0 -1
  147. package/dist/test-runner/relay-factory.js +0 -206
  148. package/dist/test-runner/relay-factory.js.map +0 -1
  149. package/dist/test-runner/relay-worker.d.ts +0 -2
  150. package/dist/test-runner/relay-worker.js +0 -2
  151. package/dist/test-runner/report.d.ts +0 -163
  152. package/dist/test-runner/report.d.ts.map +0 -1
  153. package/dist/test-runner/report.js +0 -198
  154. package/dist/test-runner/report.js.map +0 -1
  155. package/dist/test-runner/rpc.d.ts +0 -56
  156. package/dist/test-runner/rpc.d.ts.map +0 -1
  157. package/dist/test-runner/rpc.js +0 -98
  158. package/dist/test-runner/rpc.js.map +0 -1
  159. package/dist/test-runner/runtime.d.ts +0 -2
  160. package/dist/test-runner/runtime.js +0 -659
  161. package/dist/test-runner/runtime.js.map +0 -1
  162. package/dist/test-runner/task-graph.d.ts +0 -38
  163. package/dist/test-runner/task-graph.d.ts.map +0 -1
  164. package/dist/test-runner/task-graph.js +0 -182
  165. package/dist/test-runner/task-graph.js.map +0 -1
  166. package/dist/throttle-DKKzX1qC.js +0 -59
  167. package/dist/throttle-DKKzX1qC.js.map +0 -1
  168. package/dist/totp-BqmCLSNA.js +0 -189
  169. package/dist/totp-BqmCLSNA.js.map +0 -1
  170. package/dist/totp-CMHR5lsW.cjs +0 -191
  171. package/dist/totp-CMHR5lsW.cjs.map +0 -1
  172. package/dist/totp-CZLLKfOC.js +0 -200
  173. package/dist/totp-CZLLKfOC.js.map +0 -1
  174. package/dist/totp-DAxys-r0.js +0 -199
  175. package/dist/totp-DAxys-r0.js.map +0 -1
  176. package/dist/totp-DfekTBk3.js +0 -211
  177. package/dist/totp-DfekTBk3.js.map +0 -1
  178. package/dist/totp-Dwft0Kz7.js +0 -3
  179. package/dist/totp-WY6l0ysP.js +0 -190
  180. package/dist/totp-WY6l0ysP.js.map +0 -1
  181. /package/dist/{test-runner/bin.d.ts → stubs/bin-devtools-mcp.d.ts} +0 -0
@@ -0,0 +1,107 @@
1
+ import { dirname, join } from "node:path";
2
+ //#region src/unplugin/relay-url-store.ts
3
+ /**
4
+ * Project-local ephemeral URL store — WRITE/DELETE half (#424, relocated #818).
5
+ *
6
+ * Environment-2 ("AITC Sandbox PWA") cold-start needs two ephemeral URLs that
7
+ * change on every run: the CDP relay's https base and the app tunnel's https
8
+ * base. Rather than making the developer copy-paste them into env vars each
9
+ * time, the dev server writes them to `<project>/.ait_urls` and the MCP daemon
10
+ * (`@ait-co/debugger`) discovers them from there.
11
+ *
12
+ * Why this module is here and not in `@ait-co/debugger`: the writer IS the vite
13
+ * dev-server plugin. `@ait-co/debugger` owns the READ half only — its daemon
14
+ * reads `.ait_urls` read-only and never writes it. Splitting the file that way
15
+ * matches who is allowed to mutate it, and it keeps the write path available
16
+ * when `@ait-co/debugger` is not installed at all. The file format is the
17
+ * contract between the two packages; changing it requires changing both sides.
18
+ *
19
+ * Kept deliberately minimal compared with the pre-split `src/mcp/relay-url-store.ts`:
20
+ * the read path (`readRelayUrls`) moved out with the daemon, so only
21
+ * {@link writeRelayUrls} and {@link deleteRelayUrls} remain.
22
+ *
23
+ * SECRET-HANDLING: `relayBaseUrl` and `tunnelBaseUrl` carry the tunnel host —
24
+ * the same sensitivity class as the `.ait_relay` TOTP secret. The raw values,
25
+ * partial values, and the resolved file path MUST NOT appear in any log, error
26
+ * message, stdout, stderr, or assertion output here or at any call site. Only
27
+ * boolean pass/fail signals are safe to surface. The file is written mode 0600.
28
+ */
29
+ /** Project-local ephemeral URL file name (single file, not a directory). */
30
+ const URLS_FILE_NAME = ".ait_urls";
31
+ /**
32
+ * Walks upward from `start` and returns the nearest directory containing a
33
+ * `package.json`. Falls back to `start` when none is found, so a write still
34
+ * lands somewhere deterministic.
35
+ *
36
+ * The writer (this module) and the reader (`@ait-co/debugger`'s daemon) use the
37
+ * SAME anchor rule, so URLs written by `pnpm dev` are the ones the daemon finds:
38
+ * real mini-apps keep `vite.config.ts` and `package.json` in one directory, so
39
+ * `server.config.root === package.json-dir`. In a monorepo subdir the anchor is
40
+ * the package's own directory — the one the daemon also reaches via its
41
+ * per-session projectRoot.
42
+ */
43
+ function nearestPackageJsonDir(start, existsSyncFn) {
44
+ let dir = start;
45
+ while (true) {
46
+ if (existsSyncFn(join(dir, "package.json"))) return dir;
47
+ const parent = dirname(dir);
48
+ if (parent === dir) return start;
49
+ dir = parent;
50
+ }
51
+ }
52
+ /**
53
+ * Absolute path to the project-local `.ait_urls` file for a given start
54
+ * directory (resolved against the nearest package.json directory).
55
+ *
56
+ * Exported so tests can compute the expected path without duplicating the
57
+ * resolution logic.
58
+ */
59
+ function urlsFilePath(start, existsSyncFn) {
60
+ return join(nearestPackageJsonDir(start, existsSyncFn), URLS_FILE_NAME);
61
+ }
62
+ /**
63
+ * Writes the present URL keys to `<projectRoot>/.ait_urls` (mode 0600),
64
+ * overwriting (`flag: 'w'`) because the URLs are ephemeral — a fresh URL
65
+ * replaces the previous one on every boot.
66
+ *
67
+ * Unlike the `.ait_relay` secret store this does NOT use `O_EXCL` (`'wx'`):
68
+ * only the dev server writes this file, so there is no race to guard, and the
69
+ * value must be fresh on every cold-start.
70
+ *
71
+ * SECRET-HANDLING: URL values are never logged.
72
+ */
73
+ async function writeRelayUrls(deps) {
74
+ const { projectRoot, relayBaseUrl, relayLocalUrl, tunnelBaseUrl, fs: fsDep, existsSync: existsSyncDep } = deps;
75
+ const fs = fsDep ?? await import("node:fs");
76
+ const filePath = urlsFilePath(projectRoot, existsSyncDep ?? fs.existsSync);
77
+ const payload = {};
78
+ if (typeof relayBaseUrl === "string" && relayBaseUrl !== "") payload.relayBaseUrl = relayBaseUrl;
79
+ if (typeof relayLocalUrl === "string" && relayLocalUrl !== "") payload.relayLocalUrl = relayLocalUrl;
80
+ if (typeof tunnelBaseUrl === "string" && tunnelBaseUrl !== "") payload.tunnelBaseUrl = tunnelBaseUrl;
81
+ fs.writeFileSync(filePath, JSON.stringify(payload), {
82
+ mode: 384,
83
+ flag: "w"
84
+ });
85
+ }
86
+ /**
87
+ * Removes `<projectRoot>/.ait_urls` if present, swallowing every error so
88
+ * cleanup always succeeds.
89
+ *
90
+ * Called from the unplugin's `cleanup()` on `httpServer 'close'` + signals. A
91
+ * stale `.ait_urls` pointing at a dead tunnel would make the daemon attempt a
92
+ * doomed attach on the next cold-start — deletion is non-negotiable.
93
+ *
94
+ * SECRET-HANDLING: the file path is never logged.
95
+ */
96
+ async function deleteRelayUrls(deps) {
97
+ const { projectRoot, fs: fsDep, existsSync: existsSyncDep } = deps;
98
+ const fs = fsDep ?? await import("node:fs");
99
+ const filePath = urlsFilePath(projectRoot, existsSyncDep ?? fs.existsSync);
100
+ try {
101
+ if (fs.existsSync(filePath)) fs.unlinkSync(filePath);
102
+ } catch {}
103
+ }
104
+ //#endregion
105
+ export { deleteRelayUrls, writeRelayUrls };
106
+
107
+ //# sourceMappingURL=relay-url-store-CPZAn-T5.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relay-url-store-CPZAn-T5.js","names":[],"sources":["../src/unplugin/relay-url-store.ts"],"sourcesContent":["/**\n * Project-local ephemeral URL store — WRITE/DELETE half (#424, relocated #818).\n *\n * Environment-2 (\"AITC Sandbox PWA\") cold-start needs two ephemeral URLs that\n * change on every run: the CDP relay's https base and the app tunnel's https\n * base. Rather than making the developer copy-paste them into env vars each\n * time, the dev server writes them to `<project>/.ait_urls` and the MCP daemon\n * (`@ait-co/debugger`) discovers them from there.\n *\n * Why this module is here and not in `@ait-co/debugger`: the writer IS the vite\n * dev-server plugin. `@ait-co/debugger` owns the READ half only — its daemon\n * reads `.ait_urls` read-only and never writes it. Splitting the file that way\n * matches who is allowed to mutate it, and it keeps the write path available\n * when `@ait-co/debugger` is not installed at all. The file format is the\n * contract between the two packages; changing it requires changing both sides.\n *\n * Kept deliberately minimal compared with the pre-split `src/mcp/relay-url-store.ts`:\n * the read path (`readRelayUrls`) moved out with the daemon, so only\n * {@link writeRelayUrls} and {@link deleteRelayUrls} remain.\n *\n * SECRET-HANDLING: `relayBaseUrl` and `tunnelBaseUrl` carry the tunnel host —\n * the same sensitivity class as the `.ait_relay` TOTP secret. The raw values,\n * partial values, and the resolved file path MUST NOT appear in any log, error\n * message, stdout, stderr, or assertion output here or at any call site. Only\n * boolean pass/fail signals are safe to surface. The file is written mode 0600.\n */\n\nimport { dirname, join } from 'node:path';\n\n/** Project-local ephemeral URL file name (single file, not a directory). */\nexport const URLS_FILE_NAME = '.ait_urls';\n\n/** Minimal fs subset needed by {@link writeRelayUrls} — injectable for tests. */\nexport interface RelayUrlWriteFs {\n writeFileSync(path: string, data: string, options: { mode: number; flag: string }): void;\n existsSync(path: string): boolean;\n}\n\n/** Minimal fs subset needed by {@link deleteRelayUrls} — injectable for tests. */\nexport interface RelayUrlDeleteFs {\n existsSync(path: string): boolean;\n unlinkSync(path: string): void;\n}\n\n/**\n * Walks upward from `start` and returns the nearest directory containing a\n * `package.json`. Falls back to `start` when none is found, so a write still\n * lands somewhere deterministic.\n *\n * The writer (this module) and the reader (`@ait-co/debugger`'s daemon) use the\n * SAME anchor rule, so URLs written by `pnpm dev` are the ones the daemon finds:\n * real mini-apps keep `vite.config.ts` and `package.json` in one directory, so\n * `server.config.root === package.json-dir`. In a monorepo subdir the anchor is\n * the package's own directory — the one the daemon also reaches via its\n * per-session projectRoot.\n */\nexport function nearestPackageJsonDir(\n start: string,\n existsSyncFn: (path: string) => boolean,\n): string {\n let dir = start;\n // Stop at the filesystem root (dirname of root === root).\n while (true) {\n if (existsSyncFn(join(dir, 'package.json'))) return dir;\n const parent = dirname(dir);\n if (parent === dir) return start;\n dir = parent;\n }\n}\n\n/**\n * Absolute path to the project-local `.ait_urls` file for a given start\n * directory (resolved against the nearest package.json directory).\n *\n * Exported so tests can compute the expected path without duplicating the\n * resolution logic.\n */\nexport function urlsFilePath(start: string, existsSyncFn: (path: string) => boolean): string {\n return join(nearestPackageJsonDir(start, existsSyncFn), URLS_FILE_NAME);\n}\n\nexport interface WriteRelayUrlsDeps {\n /** Project root (typically Vite `server.config.root`). */\n projectRoot: string;\n /**\n * The CDP relay's https base URL. Omit when the relay was not started.\n * SECRET-HANDLING: never log this value.\n */\n relayBaseUrl?: string;\n /**\n * The CDP relay's LOCAL http base URL (`http://127.0.0.1:<relay-port>`), used\n * by the daemon to build the Chii inspector URL without a tunnel round-trip\n * (issue #530). Loopback only — no tunnel host, safe to surface.\n */\n relayLocalUrl?: string;\n /**\n * The app tunnel's https base URL. Omit when unavailable.\n * SECRET-HANDLING: never log this value.\n */\n tunnelBaseUrl?: string;\n /** Filesystem operations. Defaults to node:fs synchronous functions. */\n fs?: RelayUrlWriteFs;\n /** existsSync used to resolve the nearest package.json directory. Defaults to node:fs. */\n existsSync?: (path: string) => boolean;\n}\n\n/**\n * Writes the present URL keys to `<projectRoot>/.ait_urls` (mode 0600),\n * overwriting (`flag: 'w'`) because the URLs are ephemeral — a fresh URL\n * replaces the previous one on every boot.\n *\n * Unlike the `.ait_relay` secret store this does NOT use `O_EXCL` (`'wx'`):\n * only the dev server writes this file, so there is no race to guard, and the\n * value must be fresh on every cold-start.\n *\n * SECRET-HANDLING: URL values are never logged.\n */\nexport async function writeRelayUrls(deps: WriteRelayUrlsDeps): Promise<void> {\n const {\n projectRoot,\n relayBaseUrl,\n relayLocalUrl,\n tunnelBaseUrl,\n fs: fsDep,\n existsSync: existsSyncDep,\n } = deps;\n\n const fs: RelayUrlWriteFs = fsDep ?? (await import('node:fs'));\n const existsSyncFn: (path: string) => boolean = existsSyncDep ?? fs.existsSync;\n\n const filePath = urlsFilePath(projectRoot, existsSyncFn);\n\n // Build the payload — omit keys whose values are absent or blank.\n const payload: { relayBaseUrl?: string; relayLocalUrl?: string; tunnelBaseUrl?: string } = {};\n if (typeof relayBaseUrl === 'string' && relayBaseUrl !== '') payload.relayBaseUrl = relayBaseUrl;\n if (typeof relayLocalUrl === 'string' && relayLocalUrl !== '') {\n payload.relayLocalUrl = relayLocalUrl;\n }\n if (typeof tunnelBaseUrl === 'string' && tunnelBaseUrl !== '') {\n payload.tunnelBaseUrl = tunnelBaseUrl;\n }\n\n // SECRET-HANDLING: the JSON content (which includes URL values) goes to the\n // file only — never to any log, stdout, or stderr.\n fs.writeFileSync(filePath, JSON.stringify(payload), { mode: 0o600, flag: 'w' });\n}\n\nexport interface DeleteRelayUrlsDeps {\n /** Project root. */\n projectRoot: string;\n /** Filesystem operations. Defaults to node:fs (existsSync + unlinkSync). */\n fs?: RelayUrlDeleteFs;\n /** existsSync used to resolve the nearest package.json directory. */\n existsSync?: (path: string) => boolean;\n}\n\n/**\n * Removes `<projectRoot>/.ait_urls` if present, swallowing every error so\n * cleanup always succeeds.\n *\n * Called from the unplugin's `cleanup()` on `httpServer 'close'` + signals. A\n * stale `.ait_urls` pointing at a dead tunnel would make the daemon attempt a\n * doomed attach on the next cold-start — deletion is non-negotiable.\n *\n * SECRET-HANDLING: the file path is never logged.\n */\nexport async function deleteRelayUrls(deps: DeleteRelayUrlsDeps): Promise<void> {\n const { projectRoot, fs: fsDep, existsSync: existsSyncDep } = deps;\n\n const fs: RelayUrlDeleteFs = fsDep ?? (await import('node:fs'));\n const existsSyncFn: (path: string) => boolean = existsSyncDep ?? fs.existsSync;\n\n const filePath = urlsFilePath(projectRoot, existsSyncFn);\n\n try {\n if (fs.existsSync(filePath)) fs.unlinkSync(filePath);\n } catch {\n // Swallow ENOENT and any other error — cleanup is best-effort.\n // SECRET-HANDLING: the path is not logged.\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BA,MAAa,iBAAiB;;;;;;;;;;;;;AA0B9B,SAAgB,sBACd,OACA,cACQ;CACR,IAAI,MAAM;AAEV,QAAO,MAAM;AACX,MAAI,aAAa,KAAK,KAAK,eAAe,CAAC,CAAE,QAAO;EACpD,MAAM,SAAS,QAAQ,IAAI;AAC3B,MAAI,WAAW,IAAK,QAAO;AAC3B,QAAM;;;;;;;;;;AAWV,SAAgB,aAAa,OAAe,cAAiD;AAC3F,QAAO,KAAK,sBAAsB,OAAO,aAAa,EAAE,eAAe;;;;;;;;;;;;;AAuCzE,eAAsB,eAAe,MAAyC;CAC5E,MAAM,EACJ,aACA,cACA,eACA,eACA,IAAI,OACJ,YAAY,kBACV;CAEJ,MAAM,KAAsB,SAAU,MAAM,OAAO;CAGnD,MAAM,WAAW,aAAa,aAFkB,iBAAiB,GAAG,WAEZ;CAGxD,MAAM,UAAqF,EAAE;AAC7F,KAAI,OAAO,iBAAiB,YAAY,iBAAiB,GAAI,SAAQ,eAAe;AACpF,KAAI,OAAO,kBAAkB,YAAY,kBAAkB,GACzD,SAAQ,gBAAgB;AAE1B,KAAI,OAAO,kBAAkB,YAAY,kBAAkB,GACzD,SAAQ,gBAAgB;AAK1B,IAAG,cAAc,UAAU,KAAK,UAAU,QAAQ,EAAE;EAAE,MAAM;EAAO,MAAM;EAAK,CAAC;;;;;;;;;;;;AAsBjF,eAAsB,gBAAgB,MAA0C;CAC9E,MAAM,EAAE,aAAa,IAAI,OAAO,YAAY,kBAAkB;CAE9D,MAAM,KAAuB,SAAU,MAAM,OAAO;CAGpD,MAAM,WAAW,aAAa,aAFkB,iBAAiB,GAAG,WAEZ;AAExD,KAAI;AACF,MAAI,GAAG,WAAW,SAAS,CAAE,IAAG,WAAW,SAAS;SAC9C"}
@@ -0,0 +1,108 @@
1
+ let node_path = require("node:path");
2
+ //#region src/unplugin/relay-url-store.ts
3
+ /**
4
+ * Project-local ephemeral URL store — WRITE/DELETE half (#424, relocated #818).
5
+ *
6
+ * Environment-2 ("AITC Sandbox PWA") cold-start needs two ephemeral URLs that
7
+ * change on every run: the CDP relay's https base and the app tunnel's https
8
+ * base. Rather than making the developer copy-paste them into env vars each
9
+ * time, the dev server writes them to `<project>/.ait_urls` and the MCP daemon
10
+ * (`@ait-co/debugger`) discovers them from there.
11
+ *
12
+ * Why this module is here and not in `@ait-co/debugger`: the writer IS the vite
13
+ * dev-server plugin. `@ait-co/debugger` owns the READ half only — its daemon
14
+ * reads `.ait_urls` read-only and never writes it. Splitting the file that way
15
+ * matches who is allowed to mutate it, and it keeps the write path available
16
+ * when `@ait-co/debugger` is not installed at all. The file format is the
17
+ * contract between the two packages; changing it requires changing both sides.
18
+ *
19
+ * Kept deliberately minimal compared with the pre-split `src/mcp/relay-url-store.ts`:
20
+ * the read path (`readRelayUrls`) moved out with the daemon, so only
21
+ * {@link writeRelayUrls} and {@link deleteRelayUrls} remain.
22
+ *
23
+ * SECRET-HANDLING: `relayBaseUrl` and `tunnelBaseUrl` carry the tunnel host —
24
+ * the same sensitivity class as the `.ait_relay` TOTP secret. The raw values,
25
+ * partial values, and the resolved file path MUST NOT appear in any log, error
26
+ * message, stdout, stderr, or assertion output here or at any call site. Only
27
+ * boolean pass/fail signals are safe to surface. The file is written mode 0600.
28
+ */
29
+ /** Project-local ephemeral URL file name (single file, not a directory). */
30
+ const URLS_FILE_NAME = ".ait_urls";
31
+ /**
32
+ * Walks upward from `start` and returns the nearest directory containing a
33
+ * `package.json`. Falls back to `start` when none is found, so a write still
34
+ * lands somewhere deterministic.
35
+ *
36
+ * The writer (this module) and the reader (`@ait-co/debugger`'s daemon) use the
37
+ * SAME anchor rule, so URLs written by `pnpm dev` are the ones the daemon finds:
38
+ * real mini-apps keep `vite.config.ts` and `package.json` in one directory, so
39
+ * `server.config.root === package.json-dir`. In a monorepo subdir the anchor is
40
+ * the package's own directory — the one the daemon also reaches via its
41
+ * per-session projectRoot.
42
+ */
43
+ function nearestPackageJsonDir(start, existsSyncFn) {
44
+ let dir = start;
45
+ while (true) {
46
+ if (existsSyncFn((0, node_path.join)(dir, "package.json"))) return dir;
47
+ const parent = (0, node_path.dirname)(dir);
48
+ if (parent === dir) return start;
49
+ dir = parent;
50
+ }
51
+ }
52
+ /**
53
+ * Absolute path to the project-local `.ait_urls` file for a given start
54
+ * directory (resolved against the nearest package.json directory).
55
+ *
56
+ * Exported so tests can compute the expected path without duplicating the
57
+ * resolution logic.
58
+ */
59
+ function urlsFilePath(start, existsSyncFn) {
60
+ return (0, node_path.join)(nearestPackageJsonDir(start, existsSyncFn), URLS_FILE_NAME);
61
+ }
62
+ /**
63
+ * Writes the present URL keys to `<projectRoot>/.ait_urls` (mode 0600),
64
+ * overwriting (`flag: 'w'`) because the URLs are ephemeral — a fresh URL
65
+ * replaces the previous one on every boot.
66
+ *
67
+ * Unlike the `.ait_relay` secret store this does NOT use `O_EXCL` (`'wx'`):
68
+ * only the dev server writes this file, so there is no race to guard, and the
69
+ * value must be fresh on every cold-start.
70
+ *
71
+ * SECRET-HANDLING: URL values are never logged.
72
+ */
73
+ async function writeRelayUrls(deps) {
74
+ const { projectRoot, relayBaseUrl, relayLocalUrl, tunnelBaseUrl, fs: fsDep, existsSync: existsSyncDep } = deps;
75
+ const fs = fsDep ?? await import("node:fs");
76
+ const filePath = urlsFilePath(projectRoot, existsSyncDep ?? fs.existsSync);
77
+ const payload = {};
78
+ if (typeof relayBaseUrl === "string" && relayBaseUrl !== "") payload.relayBaseUrl = relayBaseUrl;
79
+ if (typeof relayLocalUrl === "string" && relayLocalUrl !== "") payload.relayLocalUrl = relayLocalUrl;
80
+ if (typeof tunnelBaseUrl === "string" && tunnelBaseUrl !== "") payload.tunnelBaseUrl = tunnelBaseUrl;
81
+ fs.writeFileSync(filePath, JSON.stringify(payload), {
82
+ mode: 384,
83
+ flag: "w"
84
+ });
85
+ }
86
+ /**
87
+ * Removes `<projectRoot>/.ait_urls` if present, swallowing every error so
88
+ * cleanup always succeeds.
89
+ *
90
+ * Called from the unplugin's `cleanup()` on `httpServer 'close'` + signals. A
91
+ * stale `.ait_urls` pointing at a dead tunnel would make the daemon attempt a
92
+ * doomed attach on the next cold-start — deletion is non-negotiable.
93
+ *
94
+ * SECRET-HANDLING: the file path is never logged.
95
+ */
96
+ async function deleteRelayUrls(deps) {
97
+ const { projectRoot, fs: fsDep, existsSync: existsSyncDep } = deps;
98
+ const fs = fsDep ?? await import("node:fs");
99
+ const filePath = urlsFilePath(projectRoot, existsSyncDep ?? fs.existsSync);
100
+ try {
101
+ if (fs.existsSync(filePath)) fs.unlinkSync(filePath);
102
+ } catch {}
103
+ }
104
+ //#endregion
105
+ exports.deleteRelayUrls = deleteRelayUrls;
106
+ exports.writeRelayUrls = writeRelayUrls;
107
+
108
+ //# sourceMappingURL=relay-url-store-DLjlvMSA.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relay-url-store-DLjlvMSA.cjs","names":[],"sources":["../src/unplugin/relay-url-store.ts"],"sourcesContent":["/**\n * Project-local ephemeral URL store — WRITE/DELETE half (#424, relocated #818).\n *\n * Environment-2 (\"AITC Sandbox PWA\") cold-start needs two ephemeral URLs that\n * change on every run: the CDP relay's https base and the app tunnel's https\n * base. Rather than making the developer copy-paste them into env vars each\n * time, the dev server writes them to `<project>/.ait_urls` and the MCP daemon\n * (`@ait-co/debugger`) discovers them from there.\n *\n * Why this module is here and not in `@ait-co/debugger`: the writer IS the vite\n * dev-server plugin. `@ait-co/debugger` owns the READ half only — its daemon\n * reads `.ait_urls` read-only and never writes it. Splitting the file that way\n * matches who is allowed to mutate it, and it keeps the write path available\n * when `@ait-co/debugger` is not installed at all. The file format is the\n * contract between the two packages; changing it requires changing both sides.\n *\n * Kept deliberately minimal compared with the pre-split `src/mcp/relay-url-store.ts`:\n * the read path (`readRelayUrls`) moved out with the daemon, so only\n * {@link writeRelayUrls} and {@link deleteRelayUrls} remain.\n *\n * SECRET-HANDLING: `relayBaseUrl` and `tunnelBaseUrl` carry the tunnel host —\n * the same sensitivity class as the `.ait_relay` TOTP secret. The raw values,\n * partial values, and the resolved file path MUST NOT appear in any log, error\n * message, stdout, stderr, or assertion output here or at any call site. Only\n * boolean pass/fail signals are safe to surface. The file is written mode 0600.\n */\n\nimport { dirname, join } from 'node:path';\n\n/** Project-local ephemeral URL file name (single file, not a directory). */\nexport const URLS_FILE_NAME = '.ait_urls';\n\n/** Minimal fs subset needed by {@link writeRelayUrls} — injectable for tests. */\nexport interface RelayUrlWriteFs {\n writeFileSync(path: string, data: string, options: { mode: number; flag: string }): void;\n existsSync(path: string): boolean;\n}\n\n/** Minimal fs subset needed by {@link deleteRelayUrls} — injectable for tests. */\nexport interface RelayUrlDeleteFs {\n existsSync(path: string): boolean;\n unlinkSync(path: string): void;\n}\n\n/**\n * Walks upward from `start` and returns the nearest directory containing a\n * `package.json`. Falls back to `start` when none is found, so a write still\n * lands somewhere deterministic.\n *\n * The writer (this module) and the reader (`@ait-co/debugger`'s daemon) use the\n * SAME anchor rule, so URLs written by `pnpm dev` are the ones the daemon finds:\n * real mini-apps keep `vite.config.ts` and `package.json` in one directory, so\n * `server.config.root === package.json-dir`. In a monorepo subdir the anchor is\n * the package's own directory — the one the daemon also reaches via its\n * per-session projectRoot.\n */\nexport function nearestPackageJsonDir(\n start: string,\n existsSyncFn: (path: string) => boolean,\n): string {\n let dir = start;\n // Stop at the filesystem root (dirname of root === root).\n while (true) {\n if (existsSyncFn(join(dir, 'package.json'))) return dir;\n const parent = dirname(dir);\n if (parent === dir) return start;\n dir = parent;\n }\n}\n\n/**\n * Absolute path to the project-local `.ait_urls` file for a given start\n * directory (resolved against the nearest package.json directory).\n *\n * Exported so tests can compute the expected path without duplicating the\n * resolution logic.\n */\nexport function urlsFilePath(start: string, existsSyncFn: (path: string) => boolean): string {\n return join(nearestPackageJsonDir(start, existsSyncFn), URLS_FILE_NAME);\n}\n\nexport interface WriteRelayUrlsDeps {\n /** Project root (typically Vite `server.config.root`). */\n projectRoot: string;\n /**\n * The CDP relay's https base URL. Omit when the relay was not started.\n * SECRET-HANDLING: never log this value.\n */\n relayBaseUrl?: string;\n /**\n * The CDP relay's LOCAL http base URL (`http://127.0.0.1:<relay-port>`), used\n * by the daemon to build the Chii inspector URL without a tunnel round-trip\n * (issue #530). Loopback only — no tunnel host, safe to surface.\n */\n relayLocalUrl?: string;\n /**\n * The app tunnel's https base URL. Omit when unavailable.\n * SECRET-HANDLING: never log this value.\n */\n tunnelBaseUrl?: string;\n /** Filesystem operations. Defaults to node:fs synchronous functions. */\n fs?: RelayUrlWriteFs;\n /** existsSync used to resolve the nearest package.json directory. Defaults to node:fs. */\n existsSync?: (path: string) => boolean;\n}\n\n/**\n * Writes the present URL keys to `<projectRoot>/.ait_urls` (mode 0600),\n * overwriting (`flag: 'w'`) because the URLs are ephemeral — a fresh URL\n * replaces the previous one on every boot.\n *\n * Unlike the `.ait_relay` secret store this does NOT use `O_EXCL` (`'wx'`):\n * only the dev server writes this file, so there is no race to guard, and the\n * value must be fresh on every cold-start.\n *\n * SECRET-HANDLING: URL values are never logged.\n */\nexport async function writeRelayUrls(deps: WriteRelayUrlsDeps): Promise<void> {\n const {\n projectRoot,\n relayBaseUrl,\n relayLocalUrl,\n tunnelBaseUrl,\n fs: fsDep,\n existsSync: existsSyncDep,\n } = deps;\n\n const fs: RelayUrlWriteFs = fsDep ?? (await import('node:fs'));\n const existsSyncFn: (path: string) => boolean = existsSyncDep ?? fs.existsSync;\n\n const filePath = urlsFilePath(projectRoot, existsSyncFn);\n\n // Build the payload — omit keys whose values are absent or blank.\n const payload: { relayBaseUrl?: string; relayLocalUrl?: string; tunnelBaseUrl?: string } = {};\n if (typeof relayBaseUrl === 'string' && relayBaseUrl !== '') payload.relayBaseUrl = relayBaseUrl;\n if (typeof relayLocalUrl === 'string' && relayLocalUrl !== '') {\n payload.relayLocalUrl = relayLocalUrl;\n }\n if (typeof tunnelBaseUrl === 'string' && tunnelBaseUrl !== '') {\n payload.tunnelBaseUrl = tunnelBaseUrl;\n }\n\n // SECRET-HANDLING: the JSON content (which includes URL values) goes to the\n // file only — never to any log, stdout, or stderr.\n fs.writeFileSync(filePath, JSON.stringify(payload), { mode: 0o600, flag: 'w' });\n}\n\nexport interface DeleteRelayUrlsDeps {\n /** Project root. */\n projectRoot: string;\n /** Filesystem operations. Defaults to node:fs (existsSync + unlinkSync). */\n fs?: RelayUrlDeleteFs;\n /** existsSync used to resolve the nearest package.json directory. */\n existsSync?: (path: string) => boolean;\n}\n\n/**\n * Removes `<projectRoot>/.ait_urls` if present, swallowing every error so\n * cleanup always succeeds.\n *\n * Called from the unplugin's `cleanup()` on `httpServer 'close'` + signals. A\n * stale `.ait_urls` pointing at a dead tunnel would make the daemon attempt a\n * doomed attach on the next cold-start — deletion is non-negotiable.\n *\n * SECRET-HANDLING: the file path is never logged.\n */\nexport async function deleteRelayUrls(deps: DeleteRelayUrlsDeps): Promise<void> {\n const { projectRoot, fs: fsDep, existsSync: existsSyncDep } = deps;\n\n const fs: RelayUrlDeleteFs = fsDep ?? (await import('node:fs'));\n const existsSyncFn: (path: string) => boolean = existsSyncDep ?? fs.existsSync;\n\n const filePath = urlsFilePath(projectRoot, existsSyncFn);\n\n try {\n if (fs.existsSync(filePath)) fs.unlinkSync(filePath);\n } catch {\n // Swallow ENOENT and any other error — cleanup is best-effort.\n // SECRET-HANDLING: the path is not logged.\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BA,MAAa,iBAAiB;;;;;;;;;;;;;AA0B9B,SAAgB,sBACd,OACA,cACQ;CACR,IAAI,MAAM;AAEV,QAAO,MAAM;AACX,MAAI,cAAA,GAAA,UAAA,MAAkB,KAAK,eAAe,CAAC,CAAE,QAAO;EACpD,MAAM,UAAA,GAAA,UAAA,SAAiB,IAAI;AAC3B,MAAI,WAAW,IAAK,QAAO;AAC3B,QAAM;;;;;;;;;;AAWV,SAAgB,aAAa,OAAe,cAAiD;AAC3F,SAAA,GAAA,UAAA,MAAY,sBAAsB,OAAO,aAAa,EAAE,eAAe;;;;;;;;;;;;;AAuCzE,eAAsB,eAAe,MAAyC;CAC5E,MAAM,EACJ,aACA,cACA,eACA,eACA,IAAI,OACJ,YAAY,kBACV;CAEJ,MAAM,KAAsB,SAAU,MAAM,OAAO;CAGnD,MAAM,WAAW,aAAa,aAFkB,iBAAiB,GAAG,WAEZ;CAGxD,MAAM,UAAqF,EAAE;AAC7F,KAAI,OAAO,iBAAiB,YAAY,iBAAiB,GAAI,SAAQ,eAAe;AACpF,KAAI,OAAO,kBAAkB,YAAY,kBAAkB,GACzD,SAAQ,gBAAgB;AAE1B,KAAI,OAAO,kBAAkB,YAAY,kBAAkB,GACzD,SAAQ,gBAAgB;AAK1B,IAAG,cAAc,UAAU,KAAK,UAAU,QAAQ,EAAE;EAAE,MAAM;EAAO,MAAM;EAAK,CAAC;;;;;;;;;;;;AAsBjF,eAAsB,gBAAgB,MAA0C;CAC9E,MAAM,EAAE,aAAa,IAAI,OAAO,YAAY,kBAAkB;CAE9D,MAAM,KAAuB,SAAU,MAAM,OAAO;CAGpD,MAAM,WAAW,aAAa,aAFkB,iBAAiB,GAAG,WAEZ;AAExD,KAAI;AACF,MAAI,GAAG,WAAW,SAAS,CAAE,IAAG,WAAW,SAAS;SAC9C"}
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env node
2
+ //#region src/stubs/moved.ts
3
+ /**
4
+ * Shared copy for the transition stubs left behind by the debug-surface split
5
+ * (#818).
6
+ *
7
+ * TRANSITION — REMOVE IN 1.0.0. Everything in `src/stubs/` exists only so a
8
+ * consumer still on a pre-0.2.0 wiring gets a sentence telling them where the
9
+ * code went, instead of a bare `ERR_PACKAGE_PATH_NOT_EXPORTED`. Once 1.0.0
10
+ * drops them, the same import fails with the resolver's own error — which is
11
+ * the correct end state, just a worse first encounter.
12
+ *
13
+ * Two stub shapes, and the difference between them is a safety property, not a
14
+ * style choice:
15
+ *
16
+ * - Node-side tooling subpaths (`/mcp/server`, `/mcp/cli`, `/test-runner`)
17
+ * THROW on import. They only ever run in a developer's terminal, so failing
18
+ * loudly at the earliest possible moment is right.
19
+ * - The in-app subpaths (`/in-app`, `/in-app/auto`) MUST NOT throw. That code
20
+ * could be sitting in a shipped production bundle; a throw there takes the
21
+ * user's mini-app down. They degrade to a no-op plus one `console.error`.
22
+ */
23
+ /** The package that now owns the MCP daemon, test runner, and dev bridge. */
24
+ const DEBUGGER_PACKAGE = "@ait-co/debugger";
25
+ /**
26
+ * Builds the migration sentence for a moved subpath.
27
+ *
28
+ * SECRET-HANDLING: fixed text plus the two specifiers only — no paths, hosts,
29
+ * URLs, or environment values.
30
+ */
31
+ function movedMessage(oldSubpath, newPackage, install) {
32
+ return [
33
+ `[@ait-co/devtools] '${oldSubpath}' 는 0.2.0에서 제거되었습니다.`,
34
+ `이 기능은 '${newPackage}' 로 이동했습니다.`,
35
+ `설치: ${install}`
36
+ ].join(" ");
37
+ }
38
+ //#endregion
39
+ //#region src/stubs/bin-devtools-mcp.ts
40
+ /**
41
+ * TRANSITION STUB — REMOVE IN 1.0.0.
42
+ *
43
+ * The `devtools-mcp` bin moved to `@ait-co/debugger`, where it is named
44
+ * `debugger` (#818). The rename is why this stub can exist at all: had the new
45
+ * package kept the old bin name, both packages installed side by side would
46
+ * fight over the same `node_modules/.bin/devtools-mcp` symlink and whichever
47
+ * won would be arbitrary. Different names, no collision.
48
+ *
49
+ * Prints the migration notice to stderr (not stdout — an MCP host may be
50
+ * reading stdout as a JSON-RPC stream) and exits non-zero so a script that
51
+ * still calls the old bin fails rather than silently continuing.
52
+ */
53
+ process.stderr.write(`${movedMessage("devtools-mcp", `${DEBUGGER_PACKAGE} (bin: debugger)`, `pnpm add -D ${DEBUGGER_PACKAGE}`)}\n`);
54
+ process.exit(1);
55
+ //#endregion
56
+ export {};
57
+
58
+ //# sourceMappingURL=bin-devtools-mcp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin-devtools-mcp.js","names":[],"sources":["../../src/stubs/moved.ts","../../src/stubs/bin-devtools-mcp.ts"],"sourcesContent":["/**\n * Shared copy for the transition stubs left behind by the debug-surface split\n * (#818).\n *\n * TRANSITION — REMOVE IN 1.0.0. Everything in `src/stubs/` exists only so a\n * consumer still on a pre-0.2.0 wiring gets a sentence telling them where the\n * code went, instead of a bare `ERR_PACKAGE_PATH_NOT_EXPORTED`. Once 1.0.0\n * drops them, the same import fails with the resolver's own error — which is\n * the correct end state, just a worse first encounter.\n *\n * Two stub shapes, and the difference between them is a safety property, not a\n * style choice:\n *\n * - Node-side tooling subpaths (`/mcp/server`, `/mcp/cli`, `/test-runner`)\n * THROW on import. They only ever run in a developer's terminal, so failing\n * loudly at the earliest possible moment is right.\n * - The in-app subpaths (`/in-app`, `/in-app/auto`) MUST NOT throw. That code\n * could be sitting in a shipped production bundle; a throw there takes the\n * user's mini-app down. They degrade to a no-op plus one `console.error`.\n */\n\n/** The package that now owns the MCP daemon, test runner, and dev bridge. */\nexport const DEBUGGER_PACKAGE = '@ait-co/debugger';\n\n/** The package that now owns the on-device attach + eruda console. */\nexport const DEBUG_CONSOLE_PACKAGE = '@ait-co/debug-console';\n\n/**\n * Builds the migration sentence for a moved subpath.\n *\n * SECRET-HANDLING: fixed text plus the two specifiers only — no paths, hosts,\n * URLs, or environment values.\n */\nexport function movedMessage(oldSubpath: string, newPackage: string, install: string): string {\n return [\n `[@ait-co/devtools] '${oldSubpath}' 는 0.2.0에서 제거되었습니다.`,\n `이 기능은 '${newPackage}' 로 이동했습니다.`,\n `설치: ${install}`,\n ].join(' ');\n}\n","/**\n * TRANSITION STUB — REMOVE IN 1.0.0.\n *\n * The `devtools-mcp` bin moved to `@ait-co/debugger`, where it is named\n * `debugger` (#818). The rename is why this stub can exist at all: had the new\n * package kept the old bin name, both packages installed side by side would\n * fight over the same `node_modules/.bin/devtools-mcp` symlink and whichever\n * won would be arbitrary. Different names, no collision.\n *\n * Prints the migration notice to stderr (not stdout — an MCP host may be\n * reading stdout as a JSON-RPC stream) and exits non-zero so a script that\n * still calls the old bin fails rather than silently continuing.\n */\n\nimport { DEBUGGER_PACKAGE, movedMessage } from './moved.js';\n\nprocess.stderr.write(\n `${movedMessage('devtools-mcp', `${DEBUGGER_PACKAGE} (bin: debugger)`, `pnpm add -D ${DEBUGGER_PACKAGE}`)}\\n`,\n);\nprocess.exit(1);\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAsBA,MAAa,mBAAmB;;;;;;;AAWhC,SAAgB,aAAa,YAAoB,YAAoB,SAAyB;AAC5F,QAAO;EACL,uBAAuB,WAAW;EAClC,UAAU,WAAW;EACrB,OAAO;EACR,CAAC,KAAK,IAAI;;;;;;;;;;;;;;;;;ACtBb,QAAQ,OAAO,MACb,GAAG,aAAa,gBAAgB,GAAG,iBAAiB,mBAAmB,eAAe,mBAAmB,CAAC,IAC3G;AACD,QAAQ,KAAK,EAAE"}
@@ -0,0 +1,2 @@
1
+
2
+ export { };
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env node
2
+ //#region src/stubs/moved.ts
3
+ /**
4
+ * Shared copy for the transition stubs left behind by the debug-surface split
5
+ * (#818).
6
+ *
7
+ * TRANSITION — REMOVE IN 1.0.0. Everything in `src/stubs/` exists only so a
8
+ * consumer still on a pre-0.2.0 wiring gets a sentence telling them where the
9
+ * code went, instead of a bare `ERR_PACKAGE_PATH_NOT_EXPORTED`. Once 1.0.0
10
+ * drops them, the same import fails with the resolver's own error — which is
11
+ * the correct end state, just a worse first encounter.
12
+ *
13
+ * Two stub shapes, and the difference between them is a safety property, not a
14
+ * style choice:
15
+ *
16
+ * - Node-side tooling subpaths (`/mcp/server`, `/mcp/cli`, `/test-runner`)
17
+ * THROW on import. They only ever run in a developer's terminal, so failing
18
+ * loudly at the earliest possible moment is right.
19
+ * - The in-app subpaths (`/in-app`, `/in-app/auto`) MUST NOT throw. That code
20
+ * could be sitting in a shipped production bundle; a throw there takes the
21
+ * user's mini-app down. They degrade to a no-op plus one `console.error`.
22
+ */
23
+ /** The package that now owns the MCP daemon, test runner, and dev bridge. */
24
+ const DEBUGGER_PACKAGE = "@ait-co/debugger";
25
+ /**
26
+ * Builds the migration sentence for a moved subpath.
27
+ *
28
+ * SECRET-HANDLING: fixed text plus the two specifiers only — no paths, hosts,
29
+ * URLs, or environment values.
30
+ */
31
+ function movedMessage(oldSubpath, newPackage, install) {
32
+ return [
33
+ `[@ait-co/devtools] '${oldSubpath}' 는 0.2.0에서 제거되었습니다.`,
34
+ `이 기능은 '${newPackage}' 로 이동했습니다.`,
35
+ `설치: ${install}`
36
+ ].join(" ");
37
+ }
38
+ //#endregion
39
+ //#region src/stubs/bin-devtools-test.ts
40
+ /**
41
+ * TRANSITION STUB — REMOVE IN 1.0.0.
42
+ *
43
+ * The `devtools-test` bin moved to `@ait-co/debugger`, where it is named
44
+ * `debugger-test` (#818). See `src/stubs/bin-devtools-mcp.ts` for why the
45
+ * rename is what makes a stub bin safe to keep.
46
+ *
47
+ * Prints the migration notice to stderr and exits non-zero so a CI step still
48
+ * calling the old bin fails loudly instead of reporting a vacuous success.
49
+ */
50
+ process.stderr.write(`${movedMessage("devtools-test", `${DEBUGGER_PACKAGE} (bin: debugger-test)`, `pnpm add -D ${DEBUGGER_PACKAGE}`)}\n`);
51
+ process.exit(1);
52
+ //#endregion
53
+ export {};
54
+
55
+ //# sourceMappingURL=bin-devtools-test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin-devtools-test.js","names":[],"sources":["../../src/stubs/moved.ts","../../src/stubs/bin-devtools-test.ts"],"sourcesContent":["/**\n * Shared copy for the transition stubs left behind by the debug-surface split\n * (#818).\n *\n * TRANSITION — REMOVE IN 1.0.0. Everything in `src/stubs/` exists only so a\n * consumer still on a pre-0.2.0 wiring gets a sentence telling them where the\n * code went, instead of a bare `ERR_PACKAGE_PATH_NOT_EXPORTED`. Once 1.0.0\n * drops them, the same import fails with the resolver's own error — which is\n * the correct end state, just a worse first encounter.\n *\n * Two stub shapes, and the difference between them is a safety property, not a\n * style choice:\n *\n * - Node-side tooling subpaths (`/mcp/server`, `/mcp/cli`, `/test-runner`)\n * THROW on import. They only ever run in a developer's terminal, so failing\n * loudly at the earliest possible moment is right.\n * - The in-app subpaths (`/in-app`, `/in-app/auto`) MUST NOT throw. That code\n * could be sitting in a shipped production bundle; a throw there takes the\n * user's mini-app down. They degrade to a no-op plus one `console.error`.\n */\n\n/** The package that now owns the MCP daemon, test runner, and dev bridge. */\nexport const DEBUGGER_PACKAGE = '@ait-co/debugger';\n\n/** The package that now owns the on-device attach + eruda console. */\nexport const DEBUG_CONSOLE_PACKAGE = '@ait-co/debug-console';\n\n/**\n * Builds the migration sentence for a moved subpath.\n *\n * SECRET-HANDLING: fixed text plus the two specifiers only — no paths, hosts,\n * URLs, or environment values.\n */\nexport function movedMessage(oldSubpath: string, newPackage: string, install: string): string {\n return [\n `[@ait-co/devtools] '${oldSubpath}' 는 0.2.0에서 제거되었습니다.`,\n `이 기능은 '${newPackage}' 로 이동했습니다.`,\n `설치: ${install}`,\n ].join(' ');\n}\n","/**\n * TRANSITION STUB — REMOVE IN 1.0.0.\n *\n * The `devtools-test` bin moved to `@ait-co/debugger`, where it is named\n * `debugger-test` (#818). See `src/stubs/bin-devtools-mcp.ts` for why the\n * rename is what makes a stub bin safe to keep.\n *\n * Prints the migration notice to stderr and exits non-zero so a CI step still\n * calling the old bin fails loudly instead of reporting a vacuous success.\n */\n\nimport { DEBUGGER_PACKAGE, movedMessage } from './moved.js';\n\nprocess.stderr.write(\n `${movedMessage('devtools-test', `${DEBUGGER_PACKAGE} (bin: debugger-test)`, `pnpm add -D ${DEBUGGER_PACKAGE}`)}\\n`,\n);\nprocess.exit(1);\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAsBA,MAAa,mBAAmB;;;;;;;AAWhC,SAAgB,aAAa,YAAoB,YAAoB,SAAyB;AAC5F,QAAO;EACL,uBAAuB,WAAW;EAClC,UAAU,WAAW;EACrB,OAAO;EACR,CAAC,KAAK,IAAI;;;;;;;;;;;;;;ACzBb,QAAQ,OAAO,MACb,GAAG,aAAa,iBAAiB,GAAG,iBAAiB,wBAAwB,eAAe,mBAAmB,CAAC,IACjH;AACD,QAAQ,KAAK,EAAE"}
@@ -1,231 +1 @@
1
- import { i as createRelayPool, n as RelayConnectionFactory, t as RELAY_POOL_NAME } from "../pool-DcaaOwUq.js";
2
-
3
- //#region src/test-runner/relay-factory.d.ts
4
- /** Options for {@link createRelayConnectionFactory}. */
5
- interface RelayConnectionFactoryOptions {
6
- /**
7
- * intoss-private:// scheme URL from `ait deploy --scheme-only` (env3). The
8
- * phone cold-loads the candidate bundle this URL points at. SECRET-HANDLING:
9
- * never logged.
10
- *
11
- * Ignored when {@link attachLauncher} is `true` — the env-2 launcher path
12
- * builds its attach URL from {@link appUrl} + the relay wss, not a scheme URL.
13
- * Pass `''` in that case.
14
- */
15
- schemeUrl: string;
16
- /**
17
- * env-2 (AITC Sandbox PWA) attach mode (devtools#774). When `true`, `open()`
18
- * builds the attach URL as a **launcher deep-link**
19
- * (`buildLauncherAttachUrl(appUrl, wssUrl, totp)` — the same env-2 path the
20
- * MCP daemon takes via `prepareAttach(deps, 'relay-mobile', …)`, issue #378)
21
- * instead of an intoss-private scheme deep-link. The relay boot, QR
22
- * rendering, dashboard, and attach-wait are byte-for-byte identical to env 3;
23
- * only the URL family differs. Omitted/false keeps the env-3 scheme path
24
- * unchanged (zero behavior change).
25
- */
26
- attachLauncher?: boolean;
27
- /**
28
- * The consumer dev server's HTTP tunnel URL (e.g. an sdk-example
29
- * `dev:phone:cdp` `*.trycloudflare.com` URL) that the launcher PWA frames.
30
- * REQUIRED when {@link attachLauncher} is `true`; ignored otherwise.
31
- *
32
- * Threaded to the `relay-mobile` `prepareAttach` branch via the
33
- * `AIT_TUNNEL_BASE_URL` env var (the same key the MCP daemon reads), so the
34
- * exact same env-2 assembly runs — no divergent code path.
35
- *
36
- * SECRET-HANDLING: this is a tunnel host at the same sensitivity as the relay
37
- * wss — NEVER logged. It rides only inside the QR/dashboard attach capsule.
38
- */
39
- appUrl?: string;
40
- /**
41
- * Project root for the `.ait_relay` secret lookup (read-only). Defaults to
42
- * `process.cwd()`.
43
- */
44
- projectRoot?: string;
45
- /**
46
- * Attach wait timeout in ms — how long `open()` waits for the phone to scan
47
- * the QR and attach. Omitted (the default) means **wait indefinitely** —
48
- * the runner stays up until the user stops it (Ctrl-C/SIGTERM). QR-scan
49
- * wait is a human-paced action; there is no sound default bound for it
50
- * (devtools#735). Pass an explicit value to opt into a bounded wait (CI/
51
- * headless callers — `--attach-timeout` on the CLI). (The per-file evaluate
52
- * timeout is separate, passed via the pool's `run` options.)
53
- */
54
- timeoutMs?: number;
55
- /** Disable browser auto-open of the QR dashboard (text QR only). */
56
- headless?: boolean;
57
- /**
58
- * Cell axes injected as `__AIT_CELL__` before the first test bundle runs, so
59
- * sdk-example's capture picks up the correct sdkLine/platform. Optional — when
60
- * omitted no cell is injected. The values are not secrets.
61
- */
62
- cell?: {
63
- sdkLine: string;
64
- platform: string;
65
- };
66
- /**
67
- * When `true`, injects `__AIT_STUB_BLOCKING__ = true` before the first test
68
- * bundle runs (devtools#740, DT-2) — `bundle.ts`'s sdk-redirect module reads
69
- * this flag via `isStubBlockingEnabled()` and answers the blocking-UI APIs
70
- * in `bridge-stub.ts`'s `STUB_REGISTRY` from fixtures instead of forwarding
71
- * them to native. Optional — omitted/false means no flag is injected, which
72
- * is byte-for-byte the pre-#740 behavior (the sdk-redirect module treats
73
- * absence the same as `false`). Not a secret.
74
- */
75
- stubBlocking?: boolean;
76
- /**
77
- * Injects `__AIT_PACE_MS__ = paceMs` before the first test bundle runs
78
- * (devtools#767) — `runtime.ts`'s test-execution loop reads this page
79
- * global and waits `paceMs` before every test after the first one that
80
- * actually runs. Optional — omitted/0 means no flag is injected, which is
81
- * byte-for-byte the pre-#767 behavior (the loop treats an absent/non-
82
- * positive value as "no pacing"). Not a secret. This is the PAGE-side half
83
- * of `--pace`; the RUNNER-side (file-to-file) half is a plain `opts.paceMs`
84
- * forwarded to `runTestFilesOverRelay` by the caller (cli.ts) — this
85
- * factory only owns the page-global injection.
86
- */
87
- paceMs?: number;
88
- /**
89
- * Injects `__AIT_PACE_METHOD_MS__ = paceMethodMs` before the first test
90
- * bundle runs (devtools#769) — `bundle.ts`'s sdk-redirect virtual module
91
- * reads this page global via `method-pace.ts#getPaceMethodMs()` and paces
92
- * every SDK call to a minimum interval BETWEEN calls to the SAME named
93
- * function, independent of `--pace`'s test-to-test/file-to-file spacing.
94
- * Optional — omitted/0 means no flag is injected, which is byte-for-byte
95
- * the pre-#769 behavior (`getPaceMethodMs()` treats an absent/non-positive
96
- * value as "no pacing", and `wrapWithMethodPacing` returns the SDK object
97
- * untouched in that case). Not a secret.
98
- */
99
- paceMethodMs?: number;
100
- /**
101
- * Overrides the dashboard HTTP server's bind base port (devtools#752).
102
- * Threaded straight through to `startQrHttpServer`'s `dashboardPort`
103
- * option — see that option's doc for the increment-on-EADDRINUSE scan
104
- * behaviour and the `AIT_DEBUG_HTTP_PORT` env fallback. Omitted means
105
- * `startQrHttpServer` resolves its own default (env → fixed default).
106
- */
107
- dashboardPort?: number;
108
- /**
109
- * Receives the QR/attach render content (text chunks) from
110
- * `renderAndMaybeWait`, so the caller decides whether to print them — e.g.
111
- * suppress on non-interactive stdout.
112
- *
113
- * REQUIRED (not optional) on purpose: these chunks contain the QR payload
114
- * (which encodes the relay wss + TOTP `at=` code) and the attach JSON block.
115
- * Making the hook mandatory means the factory never falls back to printing
116
- * them itself — a downstream `vitest.config.ts` consumer that wires this via
117
- * the `@ait-co/devtools/test-runner` barrel is forced to make an explicit
118
- * stdout decision rather than silently leaking the secret-bearing block.
119
- *
120
- * SECRET-HANDLING: when a non-interactive caller is detected the hook MUST
121
- * suppress the WHOLE chunk (not just `attachUrl`), since `relayUrl` rides in
122
- * the same block.
123
- */
124
- onQrContent: (textChunks: string[]) => void;
125
- }
126
- /**
127
- * Builds a {@link RelayConnectionFactory} that opens a standalone relay
128
- * connection for env 3 (intoss-private scheme, default) or env 2 (AITC Sandbox
129
- * PWA launcher deep-link, when `opts.attachLauncher` is set — devtools#774).
130
- *
131
- * `open()` performs the full attach lifecycle and BLOCKS while a human scans
132
- * the rendered QR with their phone — there is no way around the manual scan.
133
- * By default the wait is UNBOUNDED (`opts.timeoutMs` omitted): the
134
- * runner stays up until the user stops it (Ctrl-C/SIGTERM), since QR-scan is
135
- * a human-paced action with no sound default bound (devtools#735). Passing an
136
- * explicit `timeoutMs` opts into the old bounded behavior (CI/headless
137
- * callers). It resolves with the live `CdpConnection` once a matching page
138
- * attaches; `close()` tears the relay family down.
139
- *
140
- * The factory holds the booted relay family in a closure so `close()` can stop
141
- * it. A second `open()` on the same factory boots a fresh family (the previous
142
- * one should have been `close()`d first).
143
- */
144
- declare function createRelayConnectionFactory(opts: RelayConnectionFactoryOptions): RelayConnectionFactory;
145
- //#endregion
146
- //#region src/test-runner/config.d.ts
147
- /**
148
- * Resolved phone-test configuration returned by `definePhoneTestConfig`.
149
- */
150
- interface PhoneTestConfig {
151
- /**
152
- * Glob patterns (relative to cwd) for test files to run on the device.
153
- * Defaults to `['**\/*.ait.test.ts']`.
154
- */
155
- include: string[];
156
- /**
157
- * Per-file evaluate timeout in milliseconds. Defaults to 30 000.
158
- */
159
- timeoutMs: number;
160
- /**
161
- * Additional esbuild `external` patterns for bundling.
162
- * The SDK package is always external; add more here if needed.
163
- */
164
- extraExternals: string[];
165
- }
166
- /** User-facing config shape accepted by `definePhoneTestConfig`. */
167
- interface PhoneTestUserConfig {
168
- include?: string[];
169
- timeoutMs?: number;
170
- extraExternals?: string[];
171
- }
172
- /**
173
- * Define a phone-relay test configuration.
174
- *
175
- * Merges user overrides with sensible defaults and returns a resolved
176
- * `PhoneTestConfig`. This object can be passed to `runTestFilesOverRelay`
177
- * (via the CLI or custom scripts) to run tests on a real device WebView.
178
- */
179
- declare function definePhoneTestConfig(userConfig?: PhoneTestUserConfig): PhoneTestConfig;
180
- /**
181
- * The slice of a Vitest `test` config this helper produces — `pool`,
182
- * `include`, and `testTimeout`. Typed structurally (not against Vitest's
183
- * `InlineConfig`) so consumers can spread it into their own `defineConfig`
184
- * without this module taking a value dependency on `vitest`.
185
- */
186
- interface PhoneVitestTestConfig {
187
- /** The relay `PoolRunnerInitializer`, matched by `getFilePoolName`. */
188
- pool: ReturnType<typeof createRelayPool>;
189
- /** Glob patterns for device test files. */
190
- include: string[];
191
- /** Per-test timeout in ms (mirrors the relay per-file evaluate timeout). */
192
- testTimeout: number;
193
- }
194
- /** User config accepted by {@link definePhoneVitestConfig}. */
195
- interface PhoneVitestUserConfig extends PhoneTestUserConfig {
196
- /**
197
- * Opens/closes the CDP relay connection that tests run over. Required —
198
- * without it there is no device to dispatch files to.
199
- *
200
- * SECRET-HANDLING: the factory owns the relay wss/TOTP; this config object
201
- * never stores or logs those values.
202
- */
203
- connection: RelayConnectionFactory;
204
- /**
205
- * When `true`, the relay pool harvests `__AIT_CAPTURE__` console lines during
206
- * the run (forwarded as `collectCaptures` to `runTestFilesOverRelay`), so
207
- * `RelayRunReport.captures` is populated; a downstream consumer persists the
208
- * artifacts (pool-side artifact writing + cell-meta wiring lands in PR-S1).
209
- * Omitted/`false` = no capture harvest (build-only, zero listener overhead).
210
- */
211
- collectCaptures?: boolean;
212
- }
213
- /**
214
- * Build the Vitest `test` config slice for running tests on a device over the
215
- * relay. Spread the result into your Vitest config:
216
- *
217
- * @example
218
- * // vitest.config.ts
219
- * import { defineConfig } from 'vitest/config';
220
- * import { definePhoneVitestConfig } from '@ait-co/devtools/test-runner';
221
- * export default defineConfig({
222
- * test: definePhoneVitestConfig({ connection: myRelayFactory }),
223
- * });
224
- *
225
- * Files matching `include` are dispatched to the relay pool (named
226
- * {@link RELAY_POOL_NAME}); everything else runs in Vitest's default pool.
227
- */
228
- declare function definePhoneVitestConfig(userConfig: PhoneVitestUserConfig): PhoneVitestTestConfig;
229
- //#endregion
230
- export { PhoneTestConfig, PhoneTestUserConfig, PhoneVitestTestConfig, PhoneVitestUserConfig, RELAY_POOL_NAME, type RelayConnectionFactory, type RelayConnectionFactoryOptions, createRelayConnectionFactory, createRelayPool, definePhoneTestConfig, definePhoneVitestConfig };
231
- //# sourceMappingURL=config.d.ts.map
1
+ export { };