@ait-co/devtools 0.1.108 → 0.1.109
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bundle-KFs4t-wc.d.ts +96 -0
- package/dist/bundle-KFs4t-wc.d.ts.map +1 -0
- package/dist/mcp/cli.js +186 -40
- package/dist/mcp/cli.js.map +1 -1
- package/dist/mcp/server.js +3 -3
- package/dist/mcp/server.js.map +1 -1
- package/dist/panel/index.js +1 -1
- package/dist/{pool-Dkp7I9Bf.d.ts → pool-CuVMzWGB.d.ts} +5 -5
- package/dist/{pool-Dkp7I9Bf.d.ts.map → pool-CuVMzWGB.d.ts.map} +1 -1
- package/dist/{relay-worker-BzFQ3fv9.d.ts → relay-worker-xxanNQGs.d.ts} +3 -3
- package/dist/relay-worker-xxanNQGs.d.ts.map +1 -0
- package/dist/{runtime-ORdrpizY.d.ts → runtime-Wi5d6Ywz.d.ts} +3 -3
- package/dist/{runtime-ORdrpizY.d.ts.map → runtime-Wi5d6Ywz.d.ts.map} +1 -1
- package/dist/test-runner/bundle.d.ts +1 -1
- package/dist/test-runner/bundle.js +148 -11
- package/dist/test-runner/bundle.js.map +1 -1
- package/dist/test-runner/cli.d.ts +59 -14
- package/dist/test-runner/cli.d.ts.map +1 -1
- package/dist/test-runner/cli.js +171 -32
- package/dist/test-runner/cli.js.map +1 -1
- package/dist/test-runner/config.d.ts +1 -1
- package/dist/test-runner/pool.d.ts +1 -1
- package/dist/test-runner/relay-worker.d.ts +1 -1
- package/dist/test-runner/relay-worker.js.map +1 -1
- package/dist/test-runner/rpc.d.ts +1 -1
- package/dist/test-runner/rpc.d.ts.map +1 -1
- package/dist/test-runner/rpc.js +1 -1
- package/dist/test-runner/rpc.js.map +1 -1
- package/dist/test-runner/task-graph.d.ts +1 -1
- package/package.json +1 -1
- package/dist/bundle-BJm5jk56.d.ts +0 -49
- package/dist/bundle-BJm5jk56.d.ts.map +0 -1
- package/dist/relay-worker-BzFQ3fv9.d.ts.map +0 -1
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
//#region src/test-runner/bundle.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* esbuild-based bundler for user test files.
|
|
4
|
+
*
|
|
5
|
+
* Bundles a single test file into a self-contained IIFE string that can be
|
|
6
|
+
* injected into a WebView via `Runtime.evaluate`. The bundle includes the
|
|
7
|
+
* test runtime (`runtime.ts`), which provides `describe/it/test/expect` and
|
|
8
|
+
* the `runTestModule(factory)` entry point.
|
|
9
|
+
*
|
|
10
|
+
* ## How the wiring works
|
|
11
|
+
*
|
|
12
|
+
* The bundle exposes two exports on `globalThis.__testBundle`:
|
|
13
|
+
* - `runTestModule` — the runtime's entry function.
|
|
14
|
+
* - `__userFactory` — an async function whose body is the user's top-level
|
|
15
|
+
* test registration code (describe/it/test calls).
|
|
16
|
+
*
|
|
17
|
+
* The Node-side RPC (`rpc.ts`) calls:
|
|
18
|
+
* `globalThis.__testBundle.runTestModule(globalThis.__testBundle.__userFactory)`
|
|
19
|
+
*
|
|
20
|
+
* `runTestModule` then installs `describe/it/test/expect` as globals, invokes
|
|
21
|
+
* the factory (which registers all tests), runs them, and returns a `RunReport`.
|
|
22
|
+
*
|
|
23
|
+
* ## Why a factory wrapper is needed
|
|
24
|
+
*
|
|
25
|
+
* Naively adding the runtime to `entryPoints` and bundling the user file would
|
|
26
|
+
* fail for two reasons:
|
|
27
|
+
* 1. `describe/it/test/expect` from the runtime are module-local in the IIFE
|
|
28
|
+
* scope. The user's top-level `describe(...)` calls expect them as globals —
|
|
29
|
+
* they are not globals until `runTestModule` installs them.
|
|
30
|
+
* 2. Even with globals pre-installed, the user file runs at IIFE-evaluation
|
|
31
|
+
* time, before the RPC layer calls `runTestModule` to reset state and start
|
|
32
|
+
* the test clock.
|
|
33
|
+
*
|
|
34
|
+
* The factory approach solves both: the user's registration code is deferred
|
|
35
|
+
* into a function that `runTestModule` calls AFTER installing the globals.
|
|
36
|
+
*
|
|
37
|
+
* ## Factory extraction algorithm
|
|
38
|
+
*
|
|
39
|
+
* The `userFactoryPlugin` reads the user file and splits lines into:
|
|
40
|
+
* - **top-level**: `import …` and re-export lines — kept at module scope
|
|
41
|
+
* (the only valid position for static `import` in ESM).
|
|
42
|
+
* - **body**: all other statements — moved into the body of the exported
|
|
43
|
+
* `__userFactory` async function.
|
|
44
|
+
*
|
|
45
|
+
* esbuild processes the re-generated module, following each static import
|
|
46
|
+
* through the normal dependency graph (including the SDK-redirect plugin).
|
|
47
|
+
*
|
|
48
|
+
* ## SDK redirect
|
|
49
|
+
*
|
|
50
|
+
* Imports of `@apps-in-toss/web-framework` (and sub-paths) are intercepted via
|
|
51
|
+
* the `sdkRedirectPlugin` and replaced with a virtual `window.__sdk` proxy that
|
|
52
|
+
* `src/in-app/auto.ts` installs at runtime. This works for both 2.x and 3.x SDK.
|
|
53
|
+
*
|
|
54
|
+
* SECRET-HANDLING: the returned bundle code is caller-managed; never log it.
|
|
55
|
+
*/
|
|
56
|
+
/** Options accepted by `bundleTestFile`. */
|
|
57
|
+
interface BundleOptions {
|
|
58
|
+
/**
|
|
59
|
+
* Additional esbuild `external` patterns. The SDK package
|
|
60
|
+
* (`@apps-in-toss/web-framework` and `@apps-in-toss/web-framework/*`) is
|
|
61
|
+
* always handled by the SDK redirect plugin — callers may add more patterns
|
|
62
|
+
* to be left as globals.
|
|
63
|
+
*/
|
|
64
|
+
extraExternals?: string[];
|
|
65
|
+
/**
|
|
66
|
+
* Global name for the IIFE output object. Defaults to `__testBundle`.
|
|
67
|
+
* The runtime entry uses this to call `__testBundle.runTestModule(__userFactory)`.
|
|
68
|
+
*/
|
|
69
|
+
globalName?: string;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* The result of bundling a test file.
|
|
73
|
+
* `code` is a self-contained IIFE string ready for `Runtime.evaluate`.
|
|
74
|
+
*/
|
|
75
|
+
interface BundleResult {
|
|
76
|
+
code: string;
|
|
77
|
+
warnings: string[];
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Bundles `absPath` into a single IIFE string suitable for `Runtime.evaluate`.
|
|
81
|
+
*
|
|
82
|
+
* The IIFE installs `window.__testBundle` (or the custom `globalName`) with:
|
|
83
|
+
* - `runTestModule` — the runtime entry (from `runtime.ts`).
|
|
84
|
+
* - `__userFactory` — an async function wrapping the user's test registration
|
|
85
|
+
* code so it runs AFTER `runTestModule` installs the globals.
|
|
86
|
+
*
|
|
87
|
+
* Callers (rpc.ts) invoke:
|
|
88
|
+
* `globalThis.__testBundle.runTestModule(globalThis.__testBundle.__userFactory)`
|
|
89
|
+
*
|
|
90
|
+
* @param absPath - Absolute path to the user test file.
|
|
91
|
+
* @param opts - Optional bundling overrides.
|
|
92
|
+
*/
|
|
93
|
+
declare function bundleTestFile(absPath: string, opts?: BundleOptions): Promise<BundleResult>;
|
|
94
|
+
//#endregion
|
|
95
|
+
export { BundleResult as n, bundleTestFile as r, BundleOptions as t };
|
|
96
|
+
//# sourceMappingURL=bundle-KFs4t-wc.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bundle-KFs4t-wc.d.ts","names":[],"sources":["../src/test-runner/bundle.ts"],"mappings":";;AAqEA;;;;;AAmBA;;;;;AA2LA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UA9MiB,aAAA;;;;;;;EAOf,cAAA;;;;;EAKA,UAAA;AAAA;;;;;UAOe,YAAA;EACf,IAAA;EACA,QAAA;AAAA;;;;;;;;;;;;;;;iBAyLoB,cAAA,CAAe,OAAA,UAAiB,IAAA,GAAO,aAAA,GAAgB,OAAA,CAAQ,YAAA"}
|
package/dist/mcp/cli.js
CHANGED
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
import { i as generateTotp, n as assertRelayAuthConfigured, r as buildRelayVerifyAuth } from "../totp-WY6l0ysP.js";
|
|
3
3
|
import { t as loadRelaySecretReadOnly } from "../relay-secret-store-DhzAnnj-.js";
|
|
4
4
|
import { createRequire } from "node:module";
|
|
5
|
-
import { existsSync, mkdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { accessSync, existsSync, mkdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from "node:fs";
|
|
6
6
|
import { argv } from "node:process";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
8
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
9
9
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
10
10
|
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
11
11
|
import { parseArgs } from "node:util";
|
|
12
|
+
import * as fs from "node:fs/promises";
|
|
12
13
|
import { glob } from "node:fs/promises";
|
|
13
14
|
import * as path from "node:path";
|
|
14
15
|
import { isAbsolute, join, resolve } from "node:path";
|
|
@@ -158,11 +159,53 @@ async function discoverTestFiles(patterns, cwd) {
|
|
|
158
159
|
* esbuild-based bundler for user test files.
|
|
159
160
|
*
|
|
160
161
|
* Bundles a single test file into a self-contained IIFE string that can be
|
|
161
|
-
* injected into a WebView via `Runtime.evaluate`. The
|
|
162
|
-
* (
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
162
|
+
* injected into a WebView via `Runtime.evaluate`. The bundle includes the
|
|
163
|
+
* test runtime (`runtime.ts`), which provides `describe/it/test/expect` and
|
|
164
|
+
* the `runTestModule(factory)` entry point.
|
|
165
|
+
*
|
|
166
|
+
* ## How the wiring works
|
|
167
|
+
*
|
|
168
|
+
* The bundle exposes two exports on `globalThis.__testBundle`:
|
|
169
|
+
* - `runTestModule` — the runtime's entry function.
|
|
170
|
+
* - `__userFactory` — an async function whose body is the user's top-level
|
|
171
|
+
* test registration code (describe/it/test calls).
|
|
172
|
+
*
|
|
173
|
+
* The Node-side RPC (`rpc.ts`) calls:
|
|
174
|
+
* `globalThis.__testBundle.runTestModule(globalThis.__testBundle.__userFactory)`
|
|
175
|
+
*
|
|
176
|
+
* `runTestModule` then installs `describe/it/test/expect` as globals, invokes
|
|
177
|
+
* the factory (which registers all tests), runs them, and returns a `RunReport`.
|
|
178
|
+
*
|
|
179
|
+
* ## Why a factory wrapper is needed
|
|
180
|
+
*
|
|
181
|
+
* Naively adding the runtime to `entryPoints` and bundling the user file would
|
|
182
|
+
* fail for two reasons:
|
|
183
|
+
* 1. `describe/it/test/expect` from the runtime are module-local in the IIFE
|
|
184
|
+
* scope. The user's top-level `describe(...)` calls expect them as globals —
|
|
185
|
+
* they are not globals until `runTestModule` installs them.
|
|
186
|
+
* 2. Even with globals pre-installed, the user file runs at IIFE-evaluation
|
|
187
|
+
* time, before the RPC layer calls `runTestModule` to reset state and start
|
|
188
|
+
* the test clock.
|
|
189
|
+
*
|
|
190
|
+
* The factory approach solves both: the user's registration code is deferred
|
|
191
|
+
* into a function that `runTestModule` calls AFTER installing the globals.
|
|
192
|
+
*
|
|
193
|
+
* ## Factory extraction algorithm
|
|
194
|
+
*
|
|
195
|
+
* The `userFactoryPlugin` reads the user file and splits lines into:
|
|
196
|
+
* - **top-level**: `import …` and re-export lines — kept at module scope
|
|
197
|
+
* (the only valid position for static `import` in ESM).
|
|
198
|
+
* - **body**: all other statements — moved into the body of the exported
|
|
199
|
+
* `__userFactory` async function.
|
|
200
|
+
*
|
|
201
|
+
* esbuild processes the re-generated module, following each static import
|
|
202
|
+
* through the normal dependency graph (including the SDK-redirect plugin).
|
|
203
|
+
*
|
|
204
|
+
* ## SDK redirect
|
|
205
|
+
*
|
|
206
|
+
* Imports of `@apps-in-toss/web-framework` (and sub-paths) are intercepted via
|
|
207
|
+
* the `sdkRedirectPlugin` and replaced with a virtual `window.__sdk` proxy that
|
|
208
|
+
* `src/in-app/auto.ts` installs at runtime. This works for both 2.x and 3.x SDK.
|
|
166
209
|
*
|
|
167
210
|
* SECRET-HANDLING: the returned bundle code is caller-managed; never log it.
|
|
168
211
|
*/
|
|
@@ -212,10 +255,90 @@ module.exports = __proxy;
|
|
|
212
255
|
};
|
|
213
256
|
}
|
|
214
257
|
/**
|
|
258
|
+
* esbuild plugin that transforms the user test file into a module that exports
|
|
259
|
+
* an async `__userFactory` function. The factory defers the user's top-level
|
|
260
|
+
* test registration code (describe/it/test calls) so it only runs when
|
|
261
|
+
* `runTestModule(__userFactory)` explicitly invokes it — AFTER the runtime has
|
|
262
|
+
* installed describe/it/test/expect as globals.
|
|
263
|
+
*
|
|
264
|
+
* Algorithm:
|
|
265
|
+
* - Lines matching import declarations or re-export statements are kept at
|
|
266
|
+
* module top-level (the only valid ESM position for static `import`).
|
|
267
|
+
* - All other lines (describe/it/test calls, local declarations, etc.) are
|
|
268
|
+
* moved into the body of the exported async factory function.
|
|
269
|
+
*
|
|
270
|
+
* This preserves SDK import resolution (the sdk-redirect plugin processes
|
|
271
|
+
* top-level imports normally) while deferring test registration to the factory.
|
|
272
|
+
*/
|
|
273
|
+
function userFactoryPlugin(absPath) {
|
|
274
|
+
const NAMESPACE = "user-test-factory";
|
|
275
|
+
return {
|
|
276
|
+
name: "user-test-factory",
|
|
277
|
+
setup(build) {
|
|
278
|
+
build.onResolve({ filter: /^user-test-factory$/ }, () => ({
|
|
279
|
+
path: absPath,
|
|
280
|
+
namespace: NAMESPACE
|
|
281
|
+
}));
|
|
282
|
+
build.onLoad({
|
|
283
|
+
filter: /.*/,
|
|
284
|
+
namespace: NAMESPACE
|
|
285
|
+
}, async (args) => {
|
|
286
|
+
const lines = (await fs.readFile(args.path, "utf8")).split("\n");
|
|
287
|
+
const topLevelLines = [];
|
|
288
|
+
const bodyLines = [];
|
|
289
|
+
const EXPORT_DECLARATION_RE = /^(export\s+)(default\s+|async\s+function\s+|function\s+|class\s+|const\s+|let\s+|var\s+)/;
|
|
290
|
+
for (const line of lines) {
|
|
291
|
+
const trimmed = line.trimStart();
|
|
292
|
+
const indent = line.slice(0, line.length - trimmed.length);
|
|
293
|
+
if (trimmed.startsWith("import ") || trimmed.startsWith("import{") || trimmed.startsWith("import'") || trimmed.startsWith("import\"")) topLevelLines.push(line);
|
|
294
|
+
else if (trimmed.startsWith("export ")) if (trimmed.match(EXPORT_DECLARATION_RE)) bodyLines.push(indent + trimmed.slice(7));
|
|
295
|
+
else topLevelLines.push(line);
|
|
296
|
+
else bodyLines.push(line);
|
|
297
|
+
}
|
|
298
|
+
return {
|
|
299
|
+
contents: [
|
|
300
|
+
...topLevelLines,
|
|
301
|
+
"",
|
|
302
|
+
"// biome-ignore lint: generated factory wrapper",
|
|
303
|
+
"export default async function __userFactory(): Promise<void> {",
|
|
304
|
+
...bodyLines.map((l) => ` ${l}`),
|
|
305
|
+
"}"
|
|
306
|
+
].join("\n"),
|
|
307
|
+
loader: "ts",
|
|
308
|
+
resolveDir: path.dirname(absPath)
|
|
309
|
+
};
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Returns the absolute path to the co-located runtime module.
|
|
316
|
+
*
|
|
317
|
+
* In the source tree (running via tsx / ts-node) the file is `runtime.ts`.
|
|
318
|
+
* After `tsdown` compiles to `dist/test-runner/`, it becomes `runtime.js`.
|
|
319
|
+
* We try both extensions to support both environments.
|
|
320
|
+
*/
|
|
321
|
+
function getRuntimePath() {
|
|
322
|
+
const dir = path.dirname(fileURLToPath(import.meta.url));
|
|
323
|
+
for (const ext of [".ts", ".js"]) {
|
|
324
|
+
const candidate = path.join(dir, `runtime${ext}`);
|
|
325
|
+
try {
|
|
326
|
+
accessSync(candidate);
|
|
327
|
+
return candidate;
|
|
328
|
+
} catch {}
|
|
329
|
+
}
|
|
330
|
+
return path.join(dir, "runtime.js");
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
215
333
|
* Bundles `absPath` into a single IIFE string suitable for `Runtime.evaluate`.
|
|
216
334
|
*
|
|
217
|
-
* The IIFE installs `window.__testBundle` (or the custom `globalName`) with
|
|
218
|
-
* `runTestModule`
|
|
335
|
+
* The IIFE installs `window.__testBundle` (or the custom `globalName`) with:
|
|
336
|
+
* - `runTestModule` — the runtime entry (from `runtime.ts`).
|
|
337
|
+
* - `__userFactory` — an async function wrapping the user's test registration
|
|
338
|
+
* code so it runs AFTER `runTestModule` installs the globals.
|
|
339
|
+
*
|
|
340
|
+
* Callers (rpc.ts) invoke:
|
|
341
|
+
* `globalThis.__testBundle.runTestModule(globalThis.__testBundle.__userFactory)`
|
|
219
342
|
*
|
|
220
343
|
* @param absPath - Absolute path to the user test file.
|
|
221
344
|
* @param opts - Optional bundling overrides.
|
|
@@ -223,17 +346,29 @@ module.exports = __proxy;
|
|
|
223
346
|
async function bundleTestFile(absPath, opts) {
|
|
224
347
|
const globalName = opts?.globalName ?? "__testBundle";
|
|
225
348
|
const extraExternals = opts?.extraExternals ?? [];
|
|
226
|
-
const
|
|
227
|
-
|
|
349
|
+
const esbuild = await import("esbuild");
|
|
350
|
+
const runtimePath = getRuntimePath();
|
|
351
|
+
const wrapperContent = [
|
|
352
|
+
`import { runTestModule } from ${JSON.stringify(runtimePath)};`,
|
|
353
|
+
`import __userFactory from "user-test-factory";`,
|
|
354
|
+
`export { runTestModule, __userFactory };`
|
|
355
|
+
].join("\n");
|
|
356
|
+
const result = await esbuild.build({
|
|
357
|
+
stdin: {
|
|
358
|
+
contents: wrapperContent,
|
|
359
|
+
loader: "ts",
|
|
360
|
+
resolveDir: path.dirname(absPath)
|
|
361
|
+
},
|
|
228
362
|
bundle: true,
|
|
229
363
|
format: "iife",
|
|
230
364
|
globalName,
|
|
231
365
|
platform: "browser",
|
|
232
366
|
target: "es2022",
|
|
233
367
|
write: false,
|
|
234
|
-
plugins: [sdkRedirectPlugin()],
|
|
368
|
+
plugins: [userFactoryPlugin(absPath), sdkRedirectPlugin()],
|
|
235
369
|
external: extraExternals,
|
|
236
|
-
treeShaking: true
|
|
370
|
+
treeShaking: true,
|
|
371
|
+
footer: { js: `globalThis[${JSON.stringify(globalName)}] = ${globalName};` }
|
|
237
372
|
});
|
|
238
373
|
const warnings = result.warnings.map((w) => `${path.relative(process.cwd(), w.location?.file ?? "")}:${w.location?.line ?? "?"}: ${w.text}`);
|
|
239
374
|
const outputFile = result.outputFiles?.[0];
|
|
@@ -260,7 +395,7 @@ const DEFAULT_TIMEOUT_MS = 3e4;
|
|
|
260
395
|
* SECRET-HANDLING: `bundleCode` MUST NOT be logged by callers.
|
|
261
396
|
*/
|
|
262
397
|
function buildRunTestsExpression(bundleCode) {
|
|
263
|
-
return `(async () => { try { ${bundleCode} } catch(e) { return JSON.stringify({ok:false,error:'bundle-eval: ' + String(e && e.message || e)}); } if (typeof globalThis.__testBundle !== 'object' || typeof globalThis.__testBundle.runTestModule !== 'function') { return JSON.stringify({ok:false,error:'bundle-missing-export: __testBundle.runTestModule is not a function'}); } try { const report = await globalThis.__testBundle.runTestModule(); return JSON.stringify({ok:true,value:report}); } catch(e) { return JSON.stringify({ok:false,error:'test-run: ' + String(e && e.message || e)}); }})()`;
|
|
398
|
+
return `(async () => { try { ${bundleCode} } catch(e) { return JSON.stringify({ok:false,error:'bundle-eval: ' + String(e && e.message || e)}); } if (typeof globalThis.__testBundle !== 'object' || typeof globalThis.__testBundle.runTestModule !== 'function' || typeof globalThis.__testBundle.__userFactory !== 'function') { return JSON.stringify({ok:false,error:'bundle-missing-export: __testBundle.runTestModule or __userFactory is not a function'}); } try { const report = await globalThis.__testBundle.runTestModule(globalThis.__testBundle.__userFactory); return JSON.stringify({ok:true,value:report}); } catch(e) { return JSON.stringify({ok:false,error:'test-run: ' + String(e && e.message || e)}); }})()`;
|
|
264
399
|
}
|
|
265
400
|
/**
|
|
266
401
|
* Parses the raw CDP `returnByValue` result from a `buildRunTestsExpression`
|
|
@@ -393,14 +528,15 @@ async function runTestFilesOverRelay(connection, files, opts) {
|
|
|
393
528
|
//#endregion
|
|
394
529
|
//#region src/test-runner/cli.ts
|
|
395
530
|
/**
|
|
396
|
-
* `devtools-test` CLI
|
|
397
|
-
*
|
|
398
|
-
* Parses argv, prints usage, and delegates to `runTestFilesOverRelay` when
|
|
399
|
-
* a live CDP connection is provided. The relay connection wiring
|
|
400
|
-
* (attach → run → detach) is tracked in issue #645 / #646.
|
|
531
|
+
* `devtools-test` CLI.
|
|
401
532
|
*
|
|
402
|
-
*
|
|
403
|
-
*
|
|
533
|
+
* Shares test-file discovery with the `run_tests` MCP tool (`discoverTestFiles`)
|
|
534
|
+
* and exposes `runWithConnection` — the pure run core that bundles, injects, and
|
|
535
|
+
* collects each file over a CDP connection. Today the run path that has a live
|
|
536
|
+
* connection is the `run_tests` MCP tool (it runs these files against the
|
|
537
|
+
* daemon's attached page); the CLI's own standalone relay attach (resolve CDP
|
|
538
|
+
* URL → attach → run → close) is not wired yet, so `main()` resolves the matched
|
|
539
|
+
* files and points the operator at the MCP tool.
|
|
404
540
|
*
|
|
405
541
|
* NOTE: no shebang in this source file — the tsdown entry's `banner` option
|
|
406
542
|
* injects `#!/usr/bin/env node` into the compiled output (same pattern as
|
|
@@ -421,12 +557,10 @@ DESCRIPTION
|
|
|
421
557
|
window.__sdk), injects the bundle into the attached WebView via
|
|
422
558
|
Runtime.evaluate, and returns a RunReport.
|
|
423
559
|
|
|
424
|
-
A live CDP relay connection must be active before running tests.
|
|
425
|
-
|
|
426
|
-
the
|
|
427
|
-
|
|
428
|
-
Full Vitest pool integration and the \`run_tests\` MCP tool are tracked in
|
|
429
|
-
issues #645 and #646 respectively. This MVP provides the transport layer.
|
|
560
|
+
A live CDP relay connection must be active before running tests. Use the
|
|
561
|
+
\`run_tests\` MCP tool (via \`devtools-mcp\` / \`/ait debug\`) to run these files
|
|
562
|
+
against an attached page — the CLI's own standalone relay attach is not wired
|
|
563
|
+
yet (it currently resolves the matched files and defers to that tool).
|
|
430
564
|
|
|
431
565
|
EXAMPLE
|
|
432
566
|
devtools-test 'src/**/*.phone.test.ts' --timeout 60000
|
|
@@ -434,11 +568,12 @@ EXAMPLE
|
|
|
434
568
|
`.trimStart();
|
|
435
569
|
/**
|
|
436
570
|
* Runs `files` over `connection` and returns the aggregate report.
|
|
437
|
-
* This pure function is the testable core of the CLI
|
|
438
|
-
* `
|
|
571
|
+
* This pure function is the testable core of the CLI (and is what the
|
|
572
|
+
* `run_tests` MCP tool calls against the daemon's attached connection); it is
|
|
573
|
+
* separate from `main()` so tests can call it without spawning a subprocess.
|
|
439
574
|
*
|
|
440
|
-
*
|
|
441
|
-
*
|
|
575
|
+
* A standalone CLI relay attach/detach lifecycle (connect via Chii relay URL,
|
|
576
|
+
* `enableDomains`, run, then close) is not wired into `main()` yet.
|
|
442
577
|
*/
|
|
443
578
|
async function runWithConnection(connection, files, opts) {
|
|
444
579
|
const report = await runTestFilesOverRelay(connection, files, opts);
|
|
@@ -451,8 +586,10 @@ async function runWithConnection(connection, files, opts) {
|
|
|
451
586
|
/**
|
|
452
587
|
* CLI entry point.
|
|
453
588
|
*
|
|
454
|
-
*
|
|
455
|
-
* (resolve CDP URL, attach, run, close) is
|
|
589
|
+
* Resolves the matched test files and prints a "relay attach required" notice:
|
|
590
|
+
* the CLI's own standalone relay attach (resolve CDP URL, attach, run, close) is
|
|
591
|
+
* not wired yet, so today these files run via the `run_tests` MCP tool against
|
|
592
|
+
* the daemon's attached page.
|
|
456
593
|
*/
|
|
457
594
|
async function main$1(argv = process.argv.slice(2)) {
|
|
458
595
|
let parsed;
|
|
@@ -483,7 +620,7 @@ async function main$1(argv = process.argv.slice(2)) {
|
|
|
483
620
|
process.exitCode = 1;
|
|
484
621
|
return;
|
|
485
622
|
}
|
|
486
|
-
process.stderr.write(`devtools-test: matched ${files.length} test file(s), but direct CLI relay attach is not yet wired.\n Use the devtools-mcp server (\`devtools-mcp\`) to start a debug session,\n then the \`run_tests\` MCP tool to run these files against the attached page.\n
|
|
623
|
+
process.stderr.write(`devtools-test: matched ${files.length} test file(s), but direct CLI relay attach is not yet wired.\n Use the devtools-mcp server (\`devtools-mcp\`) to start a debug session,\n then the \`run_tests\` MCP tool to run these files against the attached page.\n`);
|
|
487
624
|
process.exitCode = 1;
|
|
488
625
|
}
|
|
489
626
|
if (import.meta.url === new URL(process.argv[1], "file://").href) main$1().catch((e) => {
|
|
@@ -4412,7 +4549,7 @@ const DEBUG_TOOL_DEFINITIONS = [
|
|
|
4412
4549
|
},
|
|
4413
4550
|
{
|
|
4414
4551
|
name: "run_tests",
|
|
4415
|
-
description: "Runs mini-app test files on the attached page over CDP (Runtime.evaluate). Each matched file is bundled with esbuild (SDK imports redirected to the live mock/SDK), injected into the attached WebView, and executed; returns per-file results plus flattened totals (passed/failed/skipped/total). Requires an attached page — call list_pages first to confirm one is attached. Files run SEQUENTIALLY (single-attach model: the relay/local target serves one page), and one run_tests call runs at a time (a concurrent call is rejected). Test verification (assert/snapshot) is delegated to the in-page Vitest runtime; this tool is the transport + report. The per-file results array is the progress record — on partial failure you see exactly which files passed/failed/timed-out. In a relay-live session this is a state-mutating injection and is blocked unless confirm=true (ignored in mock/local
|
|
4552
|
+
description: "Runs mini-app test files on the attached page over CDP (Runtime.evaluate). Each matched file is bundled with esbuild (SDK imports redirected to the live mock/SDK), injected into the attached WebView, and executed; returns per-file results plus flattened totals (passed/failed/skipped/total). Requires an attached page — call list_pages first to confirm one is attached. Files run SEQUENTIALLY (single-attach model: the relay/local target serves one page), and one run_tests call runs at a time (a concurrent call is rejected). Test verification (assert/snapshot) is delegated to the in-page Vitest runtime; this tool is the transport + report. The per-file results array is the progress record — on partial failure you see exactly which files passed/failed/timed-out. In a relay-live session this is a state-mutating injection and is blocked unless confirm=true (confirm is ignored in every non-live session: mock/local, relay-dev, relay-mobile). debug-mode only — dev-mode (--mode=dev) has no CDP. Tier C (both mock/local and relay). The devtools-test CLI shares this run core and file discovery, but its standalone relay attach is not wired yet — run via this tool for now.",
|
|
4416
4553
|
inputSchema: {
|
|
4417
4554
|
type: "object",
|
|
4418
4555
|
properties: {
|
|
@@ -4431,7 +4568,7 @@ const DEBUG_TOOL_DEFINITIONS = [
|
|
|
4431
4568
|
},
|
|
4432
4569
|
confirm: {
|
|
4433
4570
|
type: "boolean",
|
|
4434
|
-
description: "Required (true) to run in a relay-live session — test injection mutates page state. Ignored in mock/local
|
|
4571
|
+
description: "Required (true) to run in a relay-live session — test injection mutates page state. Ignored in every non-live session (mock/local, relay-dev, relay-mobile)."
|
|
4435
4572
|
}
|
|
4436
4573
|
},
|
|
4437
4574
|
required: ["files"]
|
|
@@ -5259,7 +5396,7 @@ async function readMcpSdkVersion() {
|
|
|
5259
5396
|
* some test environments that skip the build step).
|
|
5260
5397
|
*/
|
|
5261
5398
|
function readDevtoolsVersion() {
|
|
5262
|
-
return "0.1.
|
|
5399
|
+
return "0.1.109";
|
|
5263
5400
|
}
|
|
5264
5401
|
/**
|
|
5265
5402
|
* Derives the next recommended action from a completed diagnostics snapshot.
|
|
@@ -5507,9 +5644,11 @@ async function renderQr(text) {
|
|
|
5507
5644
|
return `${lines.join("\n")}\n`;
|
|
5508
5645
|
}
|
|
5509
5646
|
/**
|
|
5510
|
-
* Renders the attach banner (relay URL +
|
|
5647
|
+
* Renders the attach banner (relay URL + unicode half-block QR) as a string.
|
|
5511
5648
|
*
|
|
5512
|
-
* The QR
|
|
5649
|
+
* The QR is produced by `renderQr` (a half-block matrix, not the
|
|
5650
|
+
* `qrcode-terminal` ASCII art used by the unplugin banner) and encodes the
|
|
5651
|
+
* base `wssUrl` only. When `totpEnabled` is true, a note
|
|
5513
5652
|
* is added that attach URLs generated by `build_attach_url` will include a
|
|
5514
5653
|
* live TOTP code (`at=`) appended at call time.
|
|
5515
5654
|
*
|
|
@@ -5809,6 +5948,12 @@ let runTestsInFlight = false;
|
|
|
5809
5948
|
* to resolve before the relay had observed the first inbound CDP message from
|
|
5810
5949
|
* the phone.
|
|
5811
5950
|
*
|
|
5951
|
+
* Timeout note: callers (e.g. the `build_attach_url` path) always pass an
|
|
5952
|
+
* explicit `timeoutMs`, sourced from the factory's `waitForAttachTimeoutMs`
|
|
5953
|
+
* (default 60 000). That value is forwarded to `waitForFirstTarget`, so it
|
|
5954
|
+
* overrides that method's own 90 000 signature default — the effective
|
|
5955
|
+
* wait on the tool path is 60 s, not 90 s.
|
|
5956
|
+
*
|
|
5812
5957
|
* @param connection - The CDP connection (production or fake).
|
|
5813
5958
|
* @param filterFn - Resolves when this predicate is satisfied.
|
|
5814
5959
|
* @param timeoutMs - Maximum wait time in ms.
|
|
@@ -5861,7 +6006,7 @@ function createDebugServer(deps) {
|
|
|
5861
6006
|
const collector = collectorDep ?? new InMemoryDiagnosticsCollector();
|
|
5862
6007
|
const server = new Server({
|
|
5863
6008
|
name: "ait-debug",
|
|
5864
|
-
version: "0.1.
|
|
6009
|
+
version: "0.1.109"
|
|
5865
6010
|
}, { capabilities: { tools: { listChanged: true } } });
|
|
5866
6011
|
server.setRequestHandler(ListToolsRequestSchema, () => {
|
|
5867
6012
|
const conn = router.active;
|
|
@@ -6495,6 +6640,7 @@ function toRunTestsResult(report) {
|
|
|
6495
6640
|
error: f.result.error
|
|
6496
6641
|
} : {
|
|
6497
6642
|
file: f.file,
|
|
6643
|
+
duration: f.result.duration,
|
|
6498
6644
|
passed: f.result.passed,
|
|
6499
6645
|
failed: f.result.failed,
|
|
6500
6646
|
skipped: f.result.skipped,
|
|
@@ -8081,7 +8227,7 @@ function createDevServer(deps = {}) {
|
|
|
8081
8227
|
const aitSource = deps.aitSource ?? new HttpAitSource({ stateEndpoint });
|
|
8082
8228
|
const server = new Server({
|
|
8083
8229
|
name: "ait-devtools",
|
|
8084
|
-
version: "0.1.
|
|
8230
|
+
version: "0.1.109"
|
|
8085
8231
|
}, { capabilities: { tools: {} } });
|
|
8086
8232
|
server.setRequestHandler(ListToolsRequestSchema, () => ({ tools: DEV_TOOL_DEFINITIONS.map((tool) => ({ ...tool })) }));
|
|
8087
8233
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|