@ait-co/devtools 0.1.132 → 0.1.134
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/{attach-orchestrator-ca34r46y.js → attach-orchestrator-CE0S09YU.js} +110 -5
- package/dist/attach-orchestrator-CE0S09YU.js.map +1 -0
- package/dist/{attach-orchestrator-BiaYiNOr.js → attach-orchestrator-CMoDuG2A.js} +110 -5
- package/dist/attach-orchestrator-CMoDuG2A.js.map +1 -0
- package/dist/{cdp-connection-D7AYQck2.d.ts → cdp-connection-BYE9meXe.d.ts} +12 -2
- package/dist/{cdp-connection-D7AYQck2.d.ts.map → cdp-connection-BYE9meXe.d.ts.map} +1 -1
- package/dist/cell-BIdSQb9T.js +201 -0
- package/dist/cell-BIdSQb9T.js.map +1 -0
- package/dist/{cell-Cw4fVOl2.js → cell-D1y4shoV.js} +1 -21
- package/dist/cell-D1y4shoV.js.map +1 -0
- package/dist/cell-DHA578lX.js +200 -0
- package/dist/cell-DHA578lX.js.map +1 -0
- package/dist/{debug-server-Ch5p0ryv.js → debug-server-BcSBrajf.js} +19 -9
- package/dist/debug-server-BcSBrajf.js.map +1 -0
- package/dist/{debug-server-DoWAtTNj.js → debug-server-D9SmprUI.js} +7 -8
- package/dist/{debug-server-DoWAtTNj.js.map → debug-server-D9SmprUI.js.map} +1 -1
- package/dist/{debug-server-BBUzxZSf.js → debug-server-DnNCoNiR.js} +7 -7
- package/dist/{debug-server-BBUzxZSf.js.map → debug-server-DnNCoNiR.js.map} +1 -1
- package/dist/mcp/cli.js +149 -16
- package/dist/mcp/cli.js.map +1 -1
- package/dist/mcp/server.js +1 -1
- package/dist/panel/index.js +1 -1
- package/dist/{pool-BtLh7U1y.d.ts → pool-C6TgrcyW.d.ts} +3 -3
- package/dist/{pool-BtLh7U1y.d.ts.map → pool-C6TgrcyW.d.ts.map} +1 -1
- package/dist/{qr-http-server-KB147M-S.js → qr-http-server-C_DTp9WU.js} +1 -1
- package/dist/{qr-http-server-KB147M-S.js.map → qr-http-server-C_DTp9WU.js.map} +1 -1
- package/dist/{relay-factory-cNgPfrWg.js → relay-factory-CsVO9o25.js} +6 -6
- package/dist/{relay-factory-cNgPfrWg.js.map → relay-factory-CsVO9o25.js.map} +1 -1
- package/dist/{relay-secret-store-CFc9n0OA.js → relay-secret-store-CLEGyHou.js} +2 -2
- package/dist/{relay-secret-store-CFc9n0OA.js.map → relay-secret-store-CLEGyHou.js.map} +1 -1
- package/dist/{relay-secret-store-DzxDsK6S.js → relay-secret-store-DGduVJhs.js} +2 -2
- package/dist/{relay-secret-store-DzxDsK6S.js.map → relay-secret-store-DGduVJhs.js.map} +1 -1
- package/dist/{relay-url-store-D3rY-GJJ.js → relay-url-store-B0X8TsGr.js} +2 -2
- package/dist/{relay-url-store-D3rY-GJJ.js.map → relay-url-store-B0X8TsGr.js.map} +1 -1
- package/dist/{relay-url-store-DGQ-HPQC.js → relay-url-store-CKW8RQzf.js} +2 -2
- package/dist/{relay-url-store-DGQ-HPQC.js.map → relay-url-store-CKW8RQzf.js.map} +1 -1
- package/dist/{relay-worker-BMSnlyls.d.ts → relay-worker-Dw3aIzIK.d.ts} +15 -3
- package/dist/relay-worker-Dw3aIzIK.d.ts.map +1 -0
- package/dist/{relay-worker-l34xNuci.js → relay-worker-UK0nBfDX.js} +25 -11
- package/dist/relay-worker-UK0nBfDX.js.map +1 -0
- package/dist/rolldown-runtime-DGkTqVfb.js +15 -0
- package/dist/rolldown-runtime-DUslC3ob.js +14 -0
- package/dist/{runtime-9xhN9pr8.d.ts → runtime-DfHHZms2.d.ts} +34 -5
- package/dist/runtime-DfHHZms2.d.ts.map +1 -0
- package/dist/test-runner/bin.js +47 -112
- package/dist/test-runner/bin.js.map +1 -1
- package/dist/test-runner/config.d.ts +1 -1
- package/dist/test-runner/config.js +1 -1
- package/dist/test-runner/pool.d.ts +1 -1
- package/dist/test-runner/pool.js +1 -1
- package/dist/test-runner/relay-factory.d.ts +12 -2
- package/dist/test-runner/relay-factory.d.ts.map +1 -1
- package/dist/test-runner/relay-factory.js +3 -3
- package/dist/test-runner/relay-worker.d.ts +1 -1
- package/dist/test-runner/relay-worker.js +1 -1
- package/dist/test-runner/report.d.ts +11 -2
- package/dist/test-runner/report.d.ts.map +1 -1
- package/dist/test-runner/report.js +1 -0
- package/dist/test-runner/report.js.map +1 -1
- package/dist/test-runner/rpc.d.ts +8 -5
- package/dist/test-runner/rpc.d.ts.map +1 -1
- package/dist/test-runner/rpc.js +15 -4
- package/dist/test-runner/rpc.js.map +1 -1
- package/dist/test-runner/runtime.d.ts +2 -2
- package/dist/test-runner/runtime.js +30 -4
- package/dist/test-runner/runtime.js.map +1 -1
- package/dist/test-runner/task-graph.d.ts +1 -1
- package/dist/{totp-DvOYkYim.js → totp-CZLLKfOC.js} +2 -14
- package/dist/{totp-DvOYkYim.js.map → totp-CZLLKfOC.js.map} +1 -1
- package/dist/totp-DAxys-r0.js +199 -0
- package/dist/totp-DAxys-r0.js.map +1 -0
- package/package.json +1 -1
- package/dist/attach-orchestrator-BiaYiNOr.js.map +0 -1
- package/dist/attach-orchestrator-ca34r46y.js.map +0 -1
- package/dist/cell-Bi8MAPA8.js +0 -88
- package/dist/cell-Bi8MAPA8.js.map +0 -1
- package/dist/cell-Cw4fVOl2.js.map +0 -1
- package/dist/cell-rr6gL9AT.js +0 -89
- package/dist/cell-rr6gL9AT.js.map +0 -1
- package/dist/debug-server-Ch5p0ryv.js.map +0 -1
- package/dist/log-CUik_tHp.js +0 -95
- package/dist/log-CUik_tHp.js.map +0 -1
- package/dist/relay-worker-BMSnlyls.d.ts.map +0 -1
- package/dist/relay-worker-l34xNuci.js.map +0 -1
- package/dist/runtime-9xhN9pr8.d.ts.map +0 -1
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { t as __exportAll } from "./rolldown-runtime-DGkTqVfb.js";
|
|
3
|
+
import { a as buildIndicatorExpression } from "./attach-orchestrator-CE0S09YU.js";
|
|
4
|
+
//#region src/test-runner/cell.ts
|
|
5
|
+
var cell_exports = /* @__PURE__ */ __exportAll({
|
|
6
|
+
PERMISSION_PREFLIGHT_TIMEOUT_MS: () => PERMISSION_PREFLIGHT_TIMEOUT_MS,
|
|
7
|
+
buildPermissionPreflightExpression: () => buildPermissionPreflightExpression,
|
|
8
|
+
injectDebugIndicator: () => injectDebugIndicator,
|
|
9
|
+
injectGlobals: () => injectGlobals,
|
|
10
|
+
runPermissionPreflight: () => runPermissionPreflight
|
|
11
|
+
});
|
|
12
|
+
/**
|
|
13
|
+
* Cell injection utility for the `devtools-test` CLI (issue #684 §4.1).
|
|
14
|
+
*
|
|
15
|
+
* Injects arbitrary globals into the page via `Runtime.evaluate` BEFORE the
|
|
16
|
+
* first test bundle is injected. The injected values are session-global — one
|
|
17
|
+
* call covers all files in the run.
|
|
18
|
+
*
|
|
19
|
+
* The primary use-case is injecting `__AIT_CELL__` (sdkLine/platform) so
|
|
20
|
+
* sdk-example's `aitCapture.ts` picks up the correct test-axis values instead
|
|
21
|
+
* of falling back to `'2.x'`/`'mock'`.
|
|
22
|
+
*
|
|
23
|
+
* devtools does NOT know the shape of `__AIT_CELL__` — it only provides the
|
|
24
|
+
* general injection mechanism. The caller (CLI or MCP auto-attach path) is
|
|
25
|
+
* responsible for constructing the cell object.
|
|
26
|
+
*
|
|
27
|
+
* SECRET-HANDLING: cell values (sdkLine/platform) are not secrets and may be
|
|
28
|
+
* logged at the caller's discretion. This module does NOT log them itself.
|
|
29
|
+
*
|
|
30
|
+
* Node-only. react-free. CdpConnection only.
|
|
31
|
+
*/
|
|
32
|
+
/**
|
|
33
|
+
* `__AIT_PERMS__` key → SDK function name whose `.getPermission()` is probed
|
|
34
|
+
* (devtools#739). This is the exact contract sdk-example#265 consumes — do
|
|
35
|
+
* not rename keys without a coordinated sdk-example update.
|
|
36
|
+
*
|
|
37
|
+
* Each SDK function is reached via `window.__sdk` — the SAME page-global
|
|
38
|
+
* bridge `src/in-app/auto.ts` installs for `call_sdk`/`__sdkCall` (see
|
|
39
|
+
* `src/test-runner/bundle.ts`'s `sdkRedirectPlugin`, which redirects bundled
|
|
40
|
+
* `@apps-in-toss/web-framework` imports to this same object at runtime). The
|
|
41
|
+
* test-runner bundle owns no independent SDK import — `window.__sdk` is a
|
|
42
|
+
* page global regardless of whether a bundle has been injected yet, so this
|
|
43
|
+
* preflight runs standalone via `injectGlobals`'s `Runtime.evaluate`
|
|
44
|
+
* mechanism BEFORE the first bundle, rather than being prepended into
|
|
45
|
+
* `rpc.ts#buildRunTestsExpression`. (Design choice documented in PR #739 —
|
|
46
|
+
* bundle-prepend was the fallback plan if the SDK were only reachable from
|
|
47
|
+
* inside bundle scope, which reality ruled out.)
|
|
48
|
+
*/
|
|
49
|
+
const PERMISSION_PROBE_MAP = {
|
|
50
|
+
clipboardRead: "getClipboardText",
|
|
51
|
+
clipboardWrite: "setClipboardText",
|
|
52
|
+
album: "fetchAlbumPhotos",
|
|
53
|
+
camera: "openCamera",
|
|
54
|
+
contacts: "fetchContacts",
|
|
55
|
+
location: "getCurrentLocation"
|
|
56
|
+
};
|
|
57
|
+
/** Non-fatal bound for the permission preflight `Runtime.evaluate` round-trip. */
|
|
58
|
+
const PERMISSION_PREFLIGHT_TIMEOUT_MS = 1e4;
|
|
59
|
+
/**
|
|
60
|
+
* Builds the `Runtime.evaluate` expression for the permission-state preflight
|
|
61
|
+
* (devtools#739). Evaluated ONCE per relay session, before the first test
|
|
62
|
+
* bundle is injected (see `relay-worker.ts#runTestFilesOverRelay`).
|
|
63
|
+
*
|
|
64
|
+
* For each entry in {@link PERMISSION_PROBE_MAP}, probes
|
|
65
|
+
* `window.__sdk.<fn>.getPermission()` — a NON-blocking query (never
|
|
66
|
+
* `openPermissionDialog`/`requestPermission`, which open native UI) — and
|
|
67
|
+
* assigns the resolved `'allowed'|'denied'|'notDetermined'` status to
|
|
68
|
+
* `globalThis.__AIT_PERMS__[<key>]`. A probe that is absent, throws, or
|
|
69
|
+
* rejects resolves to `'unavailable'` — this expression never throws to its
|
|
70
|
+
* caller, and a single slow/broken permission must not fail the others (each
|
|
71
|
+
* probe is awaited independently, not short-circuited).
|
|
72
|
+
*
|
|
73
|
+
* Tests (sdk-example#265) read `globalThis.__AIT_PERMS__` to branch
|
|
74
|
+
* deterministically per permission state instead of blanket outcome-branching.
|
|
75
|
+
*
|
|
76
|
+
* Returns a JSON-serialised string (the same double-serialisation pattern
|
|
77
|
+
* `rpc.ts#buildRunTestsExpression` uses) rather than relying on CDP
|
|
78
|
+
* `returnByValue` structural fidelity for a plain object across the Chii
|
|
79
|
+
* relay — the Node side re-parses via `JSON.parse`.
|
|
80
|
+
*
|
|
81
|
+
* Pure function — no I/O, no CDP call. Callers pass the returned string to
|
|
82
|
+
* `Runtime.evaluate`.
|
|
83
|
+
*/
|
|
84
|
+
function buildPermissionPreflightExpression() {
|
|
85
|
+
return `(async () => { const probe = async (fnName) => { try { const fn = globalThis.__sdk && globalThis.__sdk[fnName]; if (!fn || typeof fn.getPermission !== 'function') return 'unavailable'; const status = await fn.getPermission(); if (status === 'allowed' || status === 'denied' || status === 'notDetermined') return status; return 'unavailable'; } catch (e) { return 'unavailable'; } }; const result = {};${Object.entries(PERMISSION_PROBE_MAP).map(([key, fnName]) => ` result[${JSON.stringify(key)}] = await probe(${JSON.stringify(fnName)});`).join("\n")}\n globalThis.__AIT_PERMS__ = result; return JSON.stringify(result);})()`;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Runs the permission-state preflight (devtools#739) via `Runtime.evaluate`
|
|
89
|
+
* and returns the collected `__AIT_PERMS__` map, or `undefined` when the
|
|
90
|
+
* preflight itself failed or timed out.
|
|
91
|
+
*
|
|
92
|
+
* NON-FATAL by design: any failure (CDP exception, timeout, page-side throw)
|
|
93
|
+
* is caught here, logged as ONE stderr line, and swallowed — the whole test
|
|
94
|
+
* run must proceed even when the preflight cannot complete (e.g. `window.__sdk`
|
|
95
|
+
* not yet installed, or a page navigation mid-attach). Callers that want the
|
|
96
|
+
* result for report provenance (`RelayRunReport.preflight`) get `undefined`
|
|
97
|
+
* in that case, which downstream code treats the same as "not run".
|
|
98
|
+
*
|
|
99
|
+
* SECRET-HANDLING: no relay/wss/secret values are read or logged; only
|
|
100
|
+
* permission-state strings cross this boundary.
|
|
101
|
+
*
|
|
102
|
+
* @param conn - The live CDP connection (relay-attached page).
|
|
103
|
+
* @param timeoutMs - Round-trip bound. Defaults to {@link PERMISSION_PREFLIGHT_TIMEOUT_MS}.
|
|
104
|
+
*/
|
|
105
|
+
async function runPermissionPreflight(conn, timeoutMs = PERMISSION_PREFLIGHT_TIMEOUT_MS) {
|
|
106
|
+
const TIMEOUT_SENTINEL = Symbol("permission-preflight-timeout");
|
|
107
|
+
const timeoutPromise = new Promise((resolve) => setTimeout(() => resolve(TIMEOUT_SENTINEL), timeoutMs));
|
|
108
|
+
try {
|
|
109
|
+
const evalPromise = conn.send("Runtime.evaluate", {
|
|
110
|
+
expression: buildPermissionPreflightExpression(),
|
|
111
|
+
returnByValue: true,
|
|
112
|
+
awaitPromise: true
|
|
113
|
+
});
|
|
114
|
+
const raceResult = await Promise.race([evalPromise.then((v) => ({
|
|
115
|
+
tag: "eval",
|
|
116
|
+
v
|
|
117
|
+
})), timeoutPromise.then(() => ({ tag: "timeout" }))]);
|
|
118
|
+
if (raceResult.tag === "timeout") {
|
|
119
|
+
process.stderr.write(`test-runner: permission preflight timed out after ${timeoutMs}ms — proceeding with __AIT_PERMS__ unset\n`);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (raceResult.v.exceptionDetails) {
|
|
123
|
+
process.stderr.write("test-runner: permission preflight threw — proceeding with __AIT_PERMS__ unset\n");
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const rawValue = raceResult.v.result.value;
|
|
127
|
+
if (typeof rawValue !== "string") return void 0;
|
|
128
|
+
const parsed = JSON.parse(rawValue);
|
|
129
|
+
if (typeof parsed !== "object" || parsed === null) return void 0;
|
|
130
|
+
return parsed;
|
|
131
|
+
} catch (e) {
|
|
132
|
+
process.stderr.write(`test-runner: permission preflight failed (${e instanceof Error ? e.message : String(e)}) — proceeding with __AIT_PERMS__ unset\n`);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Injects each key of `globals` into `globalThis` in the page via a single
|
|
138
|
+
* `Runtime.evaluate` call. Must be called BEFORE the first `bundleTestFile`
|
|
139
|
+
* inject — the cell is session-global and applies to all subsequent files.
|
|
140
|
+
*
|
|
141
|
+
* Throws if the CDP evaluate returns an exception.
|
|
142
|
+
*
|
|
143
|
+
* @param conn - The live CDP connection (relay-attached page).
|
|
144
|
+
* @param globals - Plain-JSON-serialisable key→value map to assign onto `globalThis`.
|
|
145
|
+
*/
|
|
146
|
+
async function injectGlobals(conn, globals) {
|
|
147
|
+
const expr = `(() => { Object.assign(globalThis, ${JSON.stringify(globals)}); return true; })()`;
|
|
148
|
+
const result = await conn.send("Runtime.evaluate", {
|
|
149
|
+
expression: expr,
|
|
150
|
+
returnByValue: true
|
|
151
|
+
});
|
|
152
|
+
if (result.exceptionDetails) {
|
|
153
|
+
const msg = result.exceptionDetails.exception?.description ?? result.exceptionDetails.text ?? "unknown error";
|
|
154
|
+
throw new Error(`injectGlobals: Runtime.evaluate threw: ${msg}`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Injects (or updates) the "Debugger Connected"/"Debugger Disconnected"
|
|
159
|
+
* on-phone indicator via `Runtime.evaluate` (#730).
|
|
160
|
+
*
|
|
161
|
+
* Uses the same CDP mechanism as {@link injectGlobals} — a single
|
|
162
|
+
* `Runtime.evaluate` round-trip with the expression built by
|
|
163
|
+
* {@link buildIndicatorExpression}. The indicator is a LIVE badge rendered at
|
|
164
|
+
* the bottom-left of the page (position:fixed, safe-area-aware): the badge's
|
|
165
|
+
* controller is idempotent, so calling this again (e.g. right before
|
|
166
|
+
* `close()` with `{ state: 'disconnected' }`) UPDATES the existing badge in
|
|
167
|
+
* place instead of injecting a duplicate.
|
|
168
|
+
*
|
|
169
|
+
* **Isolation**: unlike {@link injectGlobals}, this function NEVER throws to
|
|
170
|
+
* its caller. Injection failure (e.g. page detached during inject, CSS not
|
|
171
|
+
* supported) is swallowed and logged as `console.debug` — the badge is
|
|
172
|
+
* informational UI and must never block attach success or test execution.
|
|
173
|
+
* This is the same fire-and-forget spirit as the in-page console mount in
|
|
174
|
+
* `src/in-app/attach.ts`.
|
|
175
|
+
*
|
|
176
|
+
* Call this ONLY on the manual debug paths (start_attach MCP, devtools-test
|
|
177
|
+
* CLI). Do NOT call on the `run_tests` auto-attach path — the badge
|
|
178
|
+
* would contaminate screenshots, measure_safe_area probes, and DOM snapshots
|
|
179
|
+
* taken during automated measurement runs.
|
|
180
|
+
*
|
|
181
|
+
* SECRET-HANDLING: the injected expression contains no secrets, relay URLs,
|
|
182
|
+
* wss addresses, or TOTP codes — it is pure DOM UI text + enum state built by
|
|
183
|
+
* {@link buildIndicatorExpression}.
|
|
184
|
+
*
|
|
185
|
+
* @param conn - The live CDP connection (relay-attached page).
|
|
186
|
+
* @param opts - Optional overrides forwarded to {@link buildIndicatorExpression}.
|
|
187
|
+
*/
|
|
188
|
+
async function injectDebugIndicator(conn, opts) {
|
|
189
|
+
try {
|
|
190
|
+
await conn.send("Runtime.evaluate", {
|
|
191
|
+
expression: buildIndicatorExpression(opts),
|
|
192
|
+
returnByValue: true
|
|
193
|
+
});
|
|
194
|
+
} catch (err) {
|
|
195
|
+
console.debug("[@ait-co/devtools] debug indicator inject skipped:", err);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
//#endregion
|
|
199
|
+
export { runPermissionPreflight as n, cell_exports as t };
|
|
200
|
+
|
|
201
|
+
//# sourceMappingURL=cell-BIdSQb9T.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cell-BIdSQb9T.js","names":[],"sources":["../src/test-runner/cell.ts"],"sourcesContent":["/**\n * Cell injection utility for the `devtools-test` CLI (issue #684 §4.1).\n *\n * Injects arbitrary globals into the page via `Runtime.evaluate` BEFORE the\n * first test bundle is injected. The injected values are session-global — one\n * call covers all files in the run.\n *\n * The primary use-case is injecting `__AIT_CELL__` (sdkLine/platform) so\n * sdk-example's `aitCapture.ts` picks up the correct test-axis values instead\n * of falling back to `'2.x'`/`'mock'`.\n *\n * devtools does NOT know the shape of `__AIT_CELL__` — it only provides the\n * general injection mechanism. The caller (CLI or MCP auto-attach path) is\n * responsible for constructing the cell object.\n *\n * SECRET-HANDLING: cell values (sdkLine/platform) are not secrets and may be\n * logged at the caller's discretion. This module does NOT log them itself.\n *\n * Node-only. react-free. CdpConnection only.\n */\n\n/**\n * `__AIT_PERMS__` key → SDK function name whose `.getPermission()` is probed\n * (devtools#739). This is the exact contract sdk-example#265 consumes — do\n * not rename keys without a coordinated sdk-example update.\n *\n * Each SDK function is reached via `window.__sdk` — the SAME page-global\n * bridge `src/in-app/auto.ts` installs for `call_sdk`/`__sdkCall` (see\n * `src/test-runner/bundle.ts`'s `sdkRedirectPlugin`, which redirects bundled\n * `@apps-in-toss/web-framework` imports to this same object at runtime). The\n * test-runner bundle owns no independent SDK import — `window.__sdk` is a\n * page global regardless of whether a bundle has been injected yet, so this\n * preflight runs standalone via `injectGlobals`'s `Runtime.evaluate`\n * mechanism BEFORE the first bundle, rather than being prepended into\n * `rpc.ts#buildRunTestsExpression`. (Design choice documented in PR #739 —\n * bundle-prepend was the fallback plan if the SDK were only reachable from\n * inside bundle scope, which reality ruled out.)\n */\nconst PERMISSION_PROBE_MAP: Record<string, string> = {\n clipboardRead: 'getClipboardText',\n clipboardWrite: 'setClipboardText',\n album: 'fetchAlbumPhotos',\n camera: 'openCamera',\n contacts: 'fetchContacts',\n location: 'getCurrentLocation',\n};\n\n/** Non-fatal bound for the permission preflight `Runtime.evaluate` round-trip. */\nexport const PERMISSION_PREFLIGHT_TIMEOUT_MS = 10_000;\n\n/**\n * Builds the `Runtime.evaluate` expression for the permission-state preflight\n * (devtools#739). Evaluated ONCE per relay session, before the first test\n * bundle is injected (see `relay-worker.ts#runTestFilesOverRelay`).\n *\n * For each entry in {@link PERMISSION_PROBE_MAP}, probes\n * `window.__sdk.<fn>.getPermission()` — a NON-blocking query (never\n * `openPermissionDialog`/`requestPermission`, which open native UI) — and\n * assigns the resolved `'allowed'|'denied'|'notDetermined'` status to\n * `globalThis.__AIT_PERMS__[<key>]`. A probe that is absent, throws, or\n * rejects resolves to `'unavailable'` — this expression never throws to its\n * caller, and a single slow/broken permission must not fail the others (each\n * probe is awaited independently, not short-circuited).\n *\n * Tests (sdk-example#265) read `globalThis.__AIT_PERMS__` to branch\n * deterministically per permission state instead of blanket outcome-branching.\n *\n * Returns a JSON-serialised string (the same double-serialisation pattern\n * `rpc.ts#buildRunTestsExpression` uses) rather than relying on CDP\n * `returnByValue` structural fidelity for a plain object across the Chii\n * relay — the Node side re-parses via `JSON.parse`.\n *\n * Pure function — no I/O, no CDP call. Callers pass the returned string to\n * `Runtime.evaluate`.\n */\nexport function buildPermissionPreflightExpression(): string {\n const probeEntries = Object.entries(PERMISSION_PROBE_MAP)\n .map(\n ([key, fnName]) =>\n ` result[${JSON.stringify(key)}] = await probe(${JSON.stringify(fnName)});`,\n )\n .join('\\n');\n\n return (\n `(async () => {` +\n ` const probe = async (fnName) => {` +\n ` try {` +\n ` const fn = globalThis.__sdk && globalThis.__sdk[fnName];` +\n ` if (!fn || typeof fn.getPermission !== 'function') return 'unavailable';` +\n ` const status = await fn.getPermission();` +\n ` if (status === 'allowed' || status === 'denied' || status === 'notDetermined') return status;` +\n ` return 'unavailable';` +\n ` } catch (e) {` +\n ` return 'unavailable';` +\n ` }` +\n ` };` +\n ` const result = {};` +\n `${probeEntries}\\n` +\n ` globalThis.__AIT_PERMS__ = result;` +\n ` return JSON.stringify(result);` +\n `})()`\n );\n}\n\n/**\n * Runs the permission-state preflight (devtools#739) via `Runtime.evaluate`\n * and returns the collected `__AIT_PERMS__` map, or `undefined` when the\n * preflight itself failed or timed out.\n *\n * NON-FATAL by design: any failure (CDP exception, timeout, page-side throw)\n * is caught here, logged as ONE stderr line, and swallowed — the whole test\n * run must proceed even when the preflight cannot complete (e.g. `window.__sdk`\n * not yet installed, or a page navigation mid-attach). Callers that want the\n * result for report provenance (`RelayRunReport.preflight`) get `undefined`\n * in that case, which downstream code treats the same as \"not run\".\n *\n * SECRET-HANDLING: no relay/wss/secret values are read or logged; only\n * permission-state strings cross this boundary.\n *\n * @param conn - The live CDP connection (relay-attached page).\n * @param timeoutMs - Round-trip bound. Defaults to {@link PERMISSION_PREFLIGHT_TIMEOUT_MS}.\n */\nexport async function runPermissionPreflight(\n conn: CdpConnection,\n timeoutMs = PERMISSION_PREFLIGHT_TIMEOUT_MS,\n): Promise<Record<string, string> | undefined> {\n const TIMEOUT_SENTINEL = Symbol('permission-preflight-timeout');\n const timeoutPromise = new Promise<typeof TIMEOUT_SENTINEL>((resolve) =>\n setTimeout(() => resolve(TIMEOUT_SENTINEL), timeoutMs),\n );\n\n try {\n const evalPromise = conn.send('Runtime.evaluate', {\n expression: buildPermissionPreflightExpression(),\n returnByValue: true,\n awaitPromise: true,\n });\n\n const raceResult = await Promise.race([\n evalPromise.then((v) => ({ tag: 'eval' as const, v })),\n timeoutPromise.then(() => ({ tag: 'timeout' as const })),\n ]);\n\n if (raceResult.tag === 'timeout') {\n process.stderr.write(\n `test-runner: permission preflight timed out after ${timeoutMs}ms — proceeding with __AIT_PERMS__ unset\\n`,\n );\n return undefined;\n }\n\n if (raceResult.v.exceptionDetails) {\n process.stderr.write(\n 'test-runner: permission preflight threw — proceeding with __AIT_PERMS__ unset\\n',\n );\n return undefined;\n }\n\n const rawValue = raceResult.v.result.value;\n if (typeof rawValue !== 'string') return undefined;\n const parsed: unknown = JSON.parse(rawValue);\n if (typeof parsed !== 'object' || parsed === null) return undefined;\n return parsed as Record<string, string>;\n } catch (e) {\n process.stderr.write(\n `test-runner: permission preflight failed (${\n e instanceof Error ? e.message : String(e)\n }) — proceeding with __AIT_PERMS__ unset\\n`,\n );\n return undefined;\n }\n}\n\nimport { buildIndicatorExpression } from '../mcp/attach-orchestrator.js';\nimport type { CdpConnection } from '../mcp/cdp-connection.js';\n\n/**\n * Injects each key of `globals` into `globalThis` in the page via a single\n * `Runtime.evaluate` call. Must be called BEFORE the first `bundleTestFile`\n * inject — the cell is session-global and applies to all subsequent files.\n *\n * Throws if the CDP evaluate returns an exception.\n *\n * @param conn - The live CDP connection (relay-attached page).\n * @param globals - Plain-JSON-serialisable key→value map to assign onto `globalThis`.\n */\nexport async function injectGlobals(\n conn: CdpConnection,\n globals: Record<string, unknown>,\n): Promise<void> {\n // JSON.stringify is safe here: globals values are plain data (sdkLine/platform\n // strings or simple objects). If a value is not JSON-serialisable this throws\n // at call time, which surfaces the error early and clearly.\n const expr = `(() => { Object.assign(globalThis, ${JSON.stringify(globals)}); return true; })()`;\n const result = await conn.send('Runtime.evaluate', { expression: expr, returnByValue: true });\n if (result.exceptionDetails) {\n const msg =\n result.exceptionDetails.exception?.description ??\n result.exceptionDetails.text ??\n 'unknown error';\n throw new Error(`injectGlobals: Runtime.evaluate threw: ${msg}`);\n }\n}\n\n/**\n * Injects (or updates) the \"Debugger Connected\"/\"Debugger Disconnected\"\n * on-phone indicator via `Runtime.evaluate` (#730).\n *\n * Uses the same CDP mechanism as {@link injectGlobals} — a single\n * `Runtime.evaluate` round-trip with the expression built by\n * {@link buildIndicatorExpression}. The indicator is a LIVE badge rendered at\n * the bottom-left of the page (position:fixed, safe-area-aware): the badge's\n * controller is idempotent, so calling this again (e.g. right before\n * `close()` with `{ state: 'disconnected' }`) UPDATES the existing badge in\n * place instead of injecting a duplicate.\n *\n * **Isolation**: unlike {@link injectGlobals}, this function NEVER throws to\n * its caller. Injection failure (e.g. page detached during inject, CSS not\n * supported) is swallowed and logged as `console.debug` — the badge is\n * informational UI and must never block attach success or test execution.\n * This is the same fire-and-forget spirit as the in-page console mount in\n * `src/in-app/attach.ts`.\n *\n * Call this ONLY on the manual debug paths (start_attach MCP, devtools-test\n * CLI). Do NOT call on the `run_tests` auto-attach path — the badge\n * would contaminate screenshots, measure_safe_area probes, and DOM snapshots\n * taken during automated measurement runs.\n *\n * SECRET-HANDLING: the injected expression contains no secrets, relay URLs,\n * wss addresses, or TOTP codes — it is pure DOM UI text + enum state built by\n * {@link buildIndicatorExpression}.\n *\n * @param conn - The live CDP connection (relay-attached page).\n * @param opts - Optional overrides forwarded to {@link buildIndicatorExpression}.\n */\nexport async function injectDebugIndicator(\n conn: CdpConnection,\n opts?: { label?: string; disconnectedLabel?: string; state?: 'attached' | 'disconnected' },\n): Promise<void> {\n try {\n await conn.send('Runtime.evaluate', {\n expression: buildIndicatorExpression(opts),\n returnByValue: true,\n });\n } catch (err) {\n // Badge injection is informational UI — swallow and log; never propagate.\n console.debug('[@ait-co/devtools] debug indicator inject skipped:', err);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsCA,MAAM,uBAA+C;CACnD,eAAe;CACf,gBAAgB;CAChB,OAAO;CACP,QAAQ;CACR,UAAU;CACV,UAAU;CACX;;AAGD,MAAa,kCAAkC;;;;;;;;;;;;;;;;;;;;;;;;;;AA2B/C,SAAgB,qCAA6C;AAQ3D,QACE,8bARmB,OAAO,QAAQ,qBAAqB,CACtD,KACE,CAAC,KAAK,YACL,cAAc,KAAK,UAAU,IAAI,CAAC,kBAAkB,KAAK,UAAU,OAAO,CAAC,IAC9E,CACA,KAAK,KAAK,CAgBK;;;;;;;;;;;;;;;;;;;;AAyBpB,eAAsB,uBACpB,MACA,YAAY,iCACiC;CAC7C,MAAM,mBAAmB,OAAO,+BAA+B;CAC/D,MAAM,iBAAiB,IAAI,SAAkC,YAC3D,iBAAiB,QAAQ,iBAAiB,EAAE,UAAU,CACvD;AAED,KAAI;EACF,MAAM,cAAc,KAAK,KAAK,oBAAoB;GAChD,YAAY,oCAAoC;GAChD,eAAe;GACf,cAAc;GACf,CAAC;EAEF,MAAM,aAAa,MAAM,QAAQ,KAAK,CACpC,YAAY,MAAM,OAAO;GAAE,KAAK;GAAiB;GAAG,EAAE,EACtD,eAAe,YAAY,EAAE,KAAK,WAAoB,EAAE,CACzD,CAAC;AAEF,MAAI,WAAW,QAAQ,WAAW;AAChC,WAAQ,OAAO,MACb,qDAAqD,UAAU,4CAChE;AACD;;AAGF,MAAI,WAAW,EAAE,kBAAkB;AACjC,WAAQ,OAAO,MACb,kFACD;AACD;;EAGF,MAAM,WAAW,WAAW,EAAE,OAAO;AACrC,MAAI,OAAO,aAAa,SAAU,QAAO,KAAA;EACzC,MAAM,SAAkB,KAAK,MAAM,SAAS;AAC5C,MAAI,OAAO,WAAW,YAAY,WAAW,KAAM,QAAO,KAAA;AAC1D,SAAO;UACA,GAAG;AACV,UAAQ,OAAO,MACb,6CACE,aAAa,QAAQ,EAAE,UAAU,OAAO,EAAE,CAC3C,2CACF;AACD;;;;;;;;;;;;;AAiBJ,eAAsB,cACpB,MACA,SACe;CAIf,MAAM,OAAO,sCAAsC,KAAK,UAAU,QAAQ,CAAC;CAC3E,MAAM,SAAS,MAAM,KAAK,KAAK,oBAAoB;EAAE,YAAY;EAAM,eAAe;EAAM,CAAC;AAC7F,KAAI,OAAO,kBAAkB;EAC3B,MAAM,MACJ,OAAO,iBAAiB,WAAW,eACnC,OAAO,iBAAiB,QACxB;AACF,QAAM,IAAI,MAAM,0CAA0C,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCpE,eAAsB,qBACpB,MACA,MACe;AACf,KAAI;AACF,QAAM,KAAK,KAAK,oBAAoB;GAClC,YAAY,yBAAyB,KAAK;GAC1C,eAAe;GAChB,CAAC;UACK,KAAK;AAEZ,UAAQ,MAAM,sDAAsD,IAAI"}
|
|
@@ -1,26 +1,6 @@
|
|
|
1
1
|
import { buildIndicatorExpression } from "./attach-orchestrator-Cjdhir2U.js";
|
|
2
2
|
//#region src/test-runner/cell.ts
|
|
3
3
|
/**
|
|
4
|
-
* Cell injection utility for the `devtools-test` CLI (issue #684 §4.1).
|
|
5
|
-
*
|
|
6
|
-
* Injects arbitrary globals into the page via `Runtime.evaluate` BEFORE the
|
|
7
|
-
* first test bundle is injected. The injected values are session-global — one
|
|
8
|
-
* call covers all files in the run.
|
|
9
|
-
*
|
|
10
|
-
* The primary use-case is injecting `__AIT_CELL__` (sdkLine/platform) so
|
|
11
|
-
* sdk-example's `aitCapture.ts` picks up the correct test-axis values instead
|
|
12
|
-
* of falling back to `'2.x'`/`'mock'`.
|
|
13
|
-
*
|
|
14
|
-
* devtools does NOT know the shape of `__AIT_CELL__` — it only provides the
|
|
15
|
-
* general injection mechanism. The caller (CLI or MCP auto-attach path) is
|
|
16
|
-
* responsible for constructing the cell object.
|
|
17
|
-
*
|
|
18
|
-
* SECRET-HANDLING: cell values (sdkLine/platform) are not secrets and may be
|
|
19
|
-
* logged at the caller's discretion. This module does NOT log them itself.
|
|
20
|
-
*
|
|
21
|
-
* Node-only. react-free. CdpConnection only.
|
|
22
|
-
*/
|
|
23
|
-
/**
|
|
24
4
|
* Injects each key of `globals` into `globalThis` in the page via a single
|
|
25
5
|
* `Runtime.evaluate` call. Must be called BEFORE the first `bundleTestFile`
|
|
26
6
|
* inject — the cell is session-global and applies to all subsequent files.
|
|
@@ -85,4 +65,4 @@ async function injectDebugIndicator(conn, opts) {
|
|
|
85
65
|
//#endregion
|
|
86
66
|
export { injectDebugIndicator, injectGlobals };
|
|
87
67
|
|
|
88
|
-
//# sourceMappingURL=cell-
|
|
68
|
+
//# sourceMappingURL=cell-D1y4shoV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cell-D1y4shoV.js","names":[],"sources":["../src/test-runner/cell.ts"],"sourcesContent":["/**\n * Cell injection utility for the `devtools-test` CLI (issue #684 §4.1).\n *\n * Injects arbitrary globals into the page via `Runtime.evaluate` BEFORE the\n * first test bundle is injected. The injected values are session-global — one\n * call covers all files in the run.\n *\n * The primary use-case is injecting `__AIT_CELL__` (sdkLine/platform) so\n * sdk-example's `aitCapture.ts` picks up the correct test-axis values instead\n * of falling back to `'2.x'`/`'mock'`.\n *\n * devtools does NOT know the shape of `__AIT_CELL__` — it only provides the\n * general injection mechanism. The caller (CLI or MCP auto-attach path) is\n * responsible for constructing the cell object.\n *\n * SECRET-HANDLING: cell values (sdkLine/platform) are not secrets and may be\n * logged at the caller's discretion. This module does NOT log them itself.\n *\n * Node-only. react-free. CdpConnection only.\n */\n\n/**\n * `__AIT_PERMS__` key → SDK function name whose `.getPermission()` is probed\n * (devtools#739). This is the exact contract sdk-example#265 consumes — do\n * not rename keys without a coordinated sdk-example update.\n *\n * Each SDK function is reached via `window.__sdk` — the SAME page-global\n * bridge `src/in-app/auto.ts` installs for `call_sdk`/`__sdkCall` (see\n * `src/test-runner/bundle.ts`'s `sdkRedirectPlugin`, which redirects bundled\n * `@apps-in-toss/web-framework` imports to this same object at runtime). The\n * test-runner bundle owns no independent SDK import — `window.__sdk` is a\n * page global regardless of whether a bundle has been injected yet, so this\n * preflight runs standalone via `injectGlobals`'s `Runtime.evaluate`\n * mechanism BEFORE the first bundle, rather than being prepended into\n * `rpc.ts#buildRunTestsExpression`. (Design choice documented in PR #739 —\n * bundle-prepend was the fallback plan if the SDK were only reachable from\n * inside bundle scope, which reality ruled out.)\n */\nconst PERMISSION_PROBE_MAP: Record<string, string> = {\n clipboardRead: 'getClipboardText',\n clipboardWrite: 'setClipboardText',\n album: 'fetchAlbumPhotos',\n camera: 'openCamera',\n contacts: 'fetchContacts',\n location: 'getCurrentLocation',\n};\n\n/** Non-fatal bound for the permission preflight `Runtime.evaluate` round-trip. */\nexport const PERMISSION_PREFLIGHT_TIMEOUT_MS = 10_000;\n\n/**\n * Builds the `Runtime.evaluate` expression for the permission-state preflight\n * (devtools#739). Evaluated ONCE per relay session, before the first test\n * bundle is injected (see `relay-worker.ts#runTestFilesOverRelay`).\n *\n * For each entry in {@link PERMISSION_PROBE_MAP}, probes\n * `window.__sdk.<fn>.getPermission()` — a NON-blocking query (never\n * `openPermissionDialog`/`requestPermission`, which open native UI) — and\n * assigns the resolved `'allowed'|'denied'|'notDetermined'` status to\n * `globalThis.__AIT_PERMS__[<key>]`. A probe that is absent, throws, or\n * rejects resolves to `'unavailable'` — this expression never throws to its\n * caller, and a single slow/broken permission must not fail the others (each\n * probe is awaited independently, not short-circuited).\n *\n * Tests (sdk-example#265) read `globalThis.__AIT_PERMS__` to branch\n * deterministically per permission state instead of blanket outcome-branching.\n *\n * Returns a JSON-serialised string (the same double-serialisation pattern\n * `rpc.ts#buildRunTestsExpression` uses) rather than relying on CDP\n * `returnByValue` structural fidelity for a plain object across the Chii\n * relay — the Node side re-parses via `JSON.parse`.\n *\n * Pure function — no I/O, no CDP call. Callers pass the returned string to\n * `Runtime.evaluate`.\n */\nexport function buildPermissionPreflightExpression(): string {\n const probeEntries = Object.entries(PERMISSION_PROBE_MAP)\n .map(\n ([key, fnName]) =>\n ` result[${JSON.stringify(key)}] = await probe(${JSON.stringify(fnName)});`,\n )\n .join('\\n');\n\n return (\n `(async () => {` +\n ` const probe = async (fnName) => {` +\n ` try {` +\n ` const fn = globalThis.__sdk && globalThis.__sdk[fnName];` +\n ` if (!fn || typeof fn.getPermission !== 'function') return 'unavailable';` +\n ` const status = await fn.getPermission();` +\n ` if (status === 'allowed' || status === 'denied' || status === 'notDetermined') return status;` +\n ` return 'unavailable';` +\n ` } catch (e) {` +\n ` return 'unavailable';` +\n ` }` +\n ` };` +\n ` const result = {};` +\n `${probeEntries}\\n` +\n ` globalThis.__AIT_PERMS__ = result;` +\n ` return JSON.stringify(result);` +\n `})()`\n );\n}\n\n/**\n * Runs the permission-state preflight (devtools#739) via `Runtime.evaluate`\n * and returns the collected `__AIT_PERMS__` map, or `undefined` when the\n * preflight itself failed or timed out.\n *\n * NON-FATAL by design: any failure (CDP exception, timeout, page-side throw)\n * is caught here, logged as ONE stderr line, and swallowed — the whole test\n * run must proceed even when the preflight cannot complete (e.g. `window.__sdk`\n * not yet installed, or a page navigation mid-attach). Callers that want the\n * result for report provenance (`RelayRunReport.preflight`) get `undefined`\n * in that case, which downstream code treats the same as \"not run\".\n *\n * SECRET-HANDLING: no relay/wss/secret values are read or logged; only\n * permission-state strings cross this boundary.\n *\n * @param conn - The live CDP connection (relay-attached page).\n * @param timeoutMs - Round-trip bound. Defaults to {@link PERMISSION_PREFLIGHT_TIMEOUT_MS}.\n */\nexport async function runPermissionPreflight(\n conn: CdpConnection,\n timeoutMs = PERMISSION_PREFLIGHT_TIMEOUT_MS,\n): Promise<Record<string, string> | undefined> {\n const TIMEOUT_SENTINEL = Symbol('permission-preflight-timeout');\n const timeoutPromise = new Promise<typeof TIMEOUT_SENTINEL>((resolve) =>\n setTimeout(() => resolve(TIMEOUT_SENTINEL), timeoutMs),\n );\n\n try {\n const evalPromise = conn.send('Runtime.evaluate', {\n expression: buildPermissionPreflightExpression(),\n returnByValue: true,\n awaitPromise: true,\n });\n\n const raceResult = await Promise.race([\n evalPromise.then((v) => ({ tag: 'eval' as const, v })),\n timeoutPromise.then(() => ({ tag: 'timeout' as const })),\n ]);\n\n if (raceResult.tag === 'timeout') {\n process.stderr.write(\n `test-runner: permission preflight timed out after ${timeoutMs}ms — proceeding with __AIT_PERMS__ unset\\n`,\n );\n return undefined;\n }\n\n if (raceResult.v.exceptionDetails) {\n process.stderr.write(\n 'test-runner: permission preflight threw — proceeding with __AIT_PERMS__ unset\\n',\n );\n return undefined;\n }\n\n const rawValue = raceResult.v.result.value;\n if (typeof rawValue !== 'string') return undefined;\n const parsed: unknown = JSON.parse(rawValue);\n if (typeof parsed !== 'object' || parsed === null) return undefined;\n return parsed as Record<string, string>;\n } catch (e) {\n process.stderr.write(\n `test-runner: permission preflight failed (${\n e instanceof Error ? e.message : String(e)\n }) — proceeding with __AIT_PERMS__ unset\\n`,\n );\n return undefined;\n }\n}\n\nimport { buildIndicatorExpression } from '../mcp/attach-orchestrator.js';\nimport type { CdpConnection } from '../mcp/cdp-connection.js';\n\n/**\n * Injects each key of `globals` into `globalThis` in the page via a single\n * `Runtime.evaluate` call. Must be called BEFORE the first `bundleTestFile`\n * inject — the cell is session-global and applies to all subsequent files.\n *\n * Throws if the CDP evaluate returns an exception.\n *\n * @param conn - The live CDP connection (relay-attached page).\n * @param globals - Plain-JSON-serialisable key→value map to assign onto `globalThis`.\n */\nexport async function injectGlobals(\n conn: CdpConnection,\n globals: Record<string, unknown>,\n): Promise<void> {\n // JSON.stringify is safe here: globals values are plain data (sdkLine/platform\n // strings or simple objects). If a value is not JSON-serialisable this throws\n // at call time, which surfaces the error early and clearly.\n const expr = `(() => { Object.assign(globalThis, ${JSON.stringify(globals)}); return true; })()`;\n const result = await conn.send('Runtime.evaluate', { expression: expr, returnByValue: true });\n if (result.exceptionDetails) {\n const msg =\n result.exceptionDetails.exception?.description ??\n result.exceptionDetails.text ??\n 'unknown error';\n throw new Error(`injectGlobals: Runtime.evaluate threw: ${msg}`);\n }\n}\n\n/**\n * Injects (or updates) the \"Debugger Connected\"/\"Debugger Disconnected\"\n * on-phone indicator via `Runtime.evaluate` (#730).\n *\n * Uses the same CDP mechanism as {@link injectGlobals} — a single\n * `Runtime.evaluate` round-trip with the expression built by\n * {@link buildIndicatorExpression}. The indicator is a LIVE badge rendered at\n * the bottom-left of the page (position:fixed, safe-area-aware): the badge's\n * controller is idempotent, so calling this again (e.g. right before\n * `close()` with `{ state: 'disconnected' }`) UPDATES the existing badge in\n * place instead of injecting a duplicate.\n *\n * **Isolation**: unlike {@link injectGlobals}, this function NEVER throws to\n * its caller. Injection failure (e.g. page detached during inject, CSS not\n * supported) is swallowed and logged as `console.debug` — the badge is\n * informational UI and must never block attach success or test execution.\n * This is the same fire-and-forget spirit as the in-page console mount in\n * `src/in-app/attach.ts`.\n *\n * Call this ONLY on the manual debug paths (start_attach MCP, devtools-test\n * CLI). Do NOT call on the `run_tests` auto-attach path — the badge\n * would contaminate screenshots, measure_safe_area probes, and DOM snapshots\n * taken during automated measurement runs.\n *\n * SECRET-HANDLING: the injected expression contains no secrets, relay URLs,\n * wss addresses, or TOTP codes — it is pure DOM UI text + enum state built by\n * {@link buildIndicatorExpression}.\n *\n * @param conn - The live CDP connection (relay-attached page).\n * @param opts - Optional overrides forwarded to {@link buildIndicatorExpression}.\n */\nexport async function injectDebugIndicator(\n conn: CdpConnection,\n opts?: { label?: string; disconnectedLabel?: string; state?: 'attached' | 'disconnected' },\n): Promise<void> {\n try {\n await conn.send('Runtime.evaluate', {\n expression: buildIndicatorExpression(opts),\n returnByValue: true,\n });\n } catch (err) {\n // Badge injection is informational UI — swallow and log; never propagate.\n console.debug('[@ait-co/devtools] debug indicator inject skipped:', err);\n }\n}\n"],"mappings":";;;;;;;;;;;;AAyLA,eAAsB,cACpB,MACA,SACe;CAIf,MAAM,OAAO,sCAAsC,KAAK,UAAU,QAAQ,CAAC;CAC3E,MAAM,SAAS,MAAM,KAAK,KAAK,oBAAoB;EAAE,YAAY;EAAM,eAAe;EAAM,CAAC;AAC7F,KAAI,OAAO,kBAAkB;EAC3B,MAAM,MACJ,OAAO,iBAAiB,WAAW,eACnC,OAAO,iBAAiB,QACxB;AACF,QAAM,IAAI,MAAM,0CAA0C,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCpE,eAAsB,qBACpB,MACA,MACe;AACf,KAAI;AACF,QAAM,KAAK,KAAK,oBAAoB;GAClC,YAAY,yBAAyB,KAAK;GAC1C,eAAe;GAChB,CAAC;UACK,KAAK;AAEZ,UAAQ,MAAM,sDAAsD,IAAI"}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-DUslC3ob.js";
|
|
2
|
+
import { a as buildIndicatorExpression } from "./attach-orchestrator-CMoDuG2A.js";
|
|
3
|
+
//#region src/test-runner/cell.ts
|
|
4
|
+
var cell_exports = /* @__PURE__ */ __exportAll({
|
|
5
|
+
PERMISSION_PREFLIGHT_TIMEOUT_MS: () => PERMISSION_PREFLIGHT_TIMEOUT_MS,
|
|
6
|
+
buildPermissionPreflightExpression: () => buildPermissionPreflightExpression,
|
|
7
|
+
injectDebugIndicator: () => injectDebugIndicator,
|
|
8
|
+
injectGlobals: () => injectGlobals,
|
|
9
|
+
runPermissionPreflight: () => runPermissionPreflight
|
|
10
|
+
});
|
|
11
|
+
/**
|
|
12
|
+
* Cell injection utility for the `devtools-test` CLI (issue #684 §4.1).
|
|
13
|
+
*
|
|
14
|
+
* Injects arbitrary globals into the page via `Runtime.evaluate` BEFORE the
|
|
15
|
+
* first test bundle is injected. The injected values are session-global — one
|
|
16
|
+
* call covers all files in the run.
|
|
17
|
+
*
|
|
18
|
+
* The primary use-case is injecting `__AIT_CELL__` (sdkLine/platform) so
|
|
19
|
+
* sdk-example's `aitCapture.ts` picks up the correct test-axis values instead
|
|
20
|
+
* of falling back to `'2.x'`/`'mock'`.
|
|
21
|
+
*
|
|
22
|
+
* devtools does NOT know the shape of `__AIT_CELL__` — it only provides the
|
|
23
|
+
* general injection mechanism. The caller (CLI or MCP auto-attach path) is
|
|
24
|
+
* responsible for constructing the cell object.
|
|
25
|
+
*
|
|
26
|
+
* SECRET-HANDLING: cell values (sdkLine/platform) are not secrets and may be
|
|
27
|
+
* logged at the caller's discretion. This module does NOT log them itself.
|
|
28
|
+
*
|
|
29
|
+
* Node-only. react-free. CdpConnection only.
|
|
30
|
+
*/
|
|
31
|
+
/**
|
|
32
|
+
* `__AIT_PERMS__` key → SDK function name whose `.getPermission()` is probed
|
|
33
|
+
* (devtools#739). This is the exact contract sdk-example#265 consumes — do
|
|
34
|
+
* not rename keys without a coordinated sdk-example update.
|
|
35
|
+
*
|
|
36
|
+
* Each SDK function is reached via `window.__sdk` — the SAME page-global
|
|
37
|
+
* bridge `src/in-app/auto.ts` installs for `call_sdk`/`__sdkCall` (see
|
|
38
|
+
* `src/test-runner/bundle.ts`'s `sdkRedirectPlugin`, which redirects bundled
|
|
39
|
+
* `@apps-in-toss/web-framework` imports to this same object at runtime). The
|
|
40
|
+
* test-runner bundle owns no independent SDK import — `window.__sdk` is a
|
|
41
|
+
* page global regardless of whether a bundle has been injected yet, so this
|
|
42
|
+
* preflight runs standalone via `injectGlobals`'s `Runtime.evaluate`
|
|
43
|
+
* mechanism BEFORE the first bundle, rather than being prepended into
|
|
44
|
+
* `rpc.ts#buildRunTestsExpression`. (Design choice documented in PR #739 —
|
|
45
|
+
* bundle-prepend was the fallback plan if the SDK were only reachable from
|
|
46
|
+
* inside bundle scope, which reality ruled out.)
|
|
47
|
+
*/
|
|
48
|
+
const PERMISSION_PROBE_MAP = {
|
|
49
|
+
clipboardRead: "getClipboardText",
|
|
50
|
+
clipboardWrite: "setClipboardText",
|
|
51
|
+
album: "fetchAlbumPhotos",
|
|
52
|
+
camera: "openCamera",
|
|
53
|
+
contacts: "fetchContacts",
|
|
54
|
+
location: "getCurrentLocation"
|
|
55
|
+
};
|
|
56
|
+
/** Non-fatal bound for the permission preflight `Runtime.evaluate` round-trip. */
|
|
57
|
+
const PERMISSION_PREFLIGHT_TIMEOUT_MS = 1e4;
|
|
58
|
+
/**
|
|
59
|
+
* Builds the `Runtime.evaluate` expression for the permission-state preflight
|
|
60
|
+
* (devtools#739). Evaluated ONCE per relay session, before the first test
|
|
61
|
+
* bundle is injected (see `relay-worker.ts#runTestFilesOverRelay`).
|
|
62
|
+
*
|
|
63
|
+
* For each entry in {@link PERMISSION_PROBE_MAP}, probes
|
|
64
|
+
* `window.__sdk.<fn>.getPermission()` — a NON-blocking query (never
|
|
65
|
+
* `openPermissionDialog`/`requestPermission`, which open native UI) — and
|
|
66
|
+
* assigns the resolved `'allowed'|'denied'|'notDetermined'` status to
|
|
67
|
+
* `globalThis.__AIT_PERMS__[<key>]`. A probe that is absent, throws, or
|
|
68
|
+
* rejects resolves to `'unavailable'` — this expression never throws to its
|
|
69
|
+
* caller, and a single slow/broken permission must not fail the others (each
|
|
70
|
+
* probe is awaited independently, not short-circuited).
|
|
71
|
+
*
|
|
72
|
+
* Tests (sdk-example#265) read `globalThis.__AIT_PERMS__` to branch
|
|
73
|
+
* deterministically per permission state instead of blanket outcome-branching.
|
|
74
|
+
*
|
|
75
|
+
* Returns a JSON-serialised string (the same double-serialisation pattern
|
|
76
|
+
* `rpc.ts#buildRunTestsExpression` uses) rather than relying on CDP
|
|
77
|
+
* `returnByValue` structural fidelity for a plain object across the Chii
|
|
78
|
+
* relay — the Node side re-parses via `JSON.parse`.
|
|
79
|
+
*
|
|
80
|
+
* Pure function — no I/O, no CDP call. Callers pass the returned string to
|
|
81
|
+
* `Runtime.evaluate`.
|
|
82
|
+
*/
|
|
83
|
+
function buildPermissionPreflightExpression() {
|
|
84
|
+
return `(async () => { const probe = async (fnName) => { try { const fn = globalThis.__sdk && globalThis.__sdk[fnName]; if (!fn || typeof fn.getPermission !== 'function') return 'unavailable'; const status = await fn.getPermission(); if (status === 'allowed' || status === 'denied' || status === 'notDetermined') return status; return 'unavailable'; } catch (e) { return 'unavailable'; } }; const result = {};${Object.entries(PERMISSION_PROBE_MAP).map(([key, fnName]) => ` result[${JSON.stringify(key)}] = await probe(${JSON.stringify(fnName)});`).join("\n")}\n globalThis.__AIT_PERMS__ = result; return JSON.stringify(result);})()`;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Runs the permission-state preflight (devtools#739) via `Runtime.evaluate`
|
|
88
|
+
* and returns the collected `__AIT_PERMS__` map, or `undefined` when the
|
|
89
|
+
* preflight itself failed or timed out.
|
|
90
|
+
*
|
|
91
|
+
* NON-FATAL by design: any failure (CDP exception, timeout, page-side throw)
|
|
92
|
+
* is caught here, logged as ONE stderr line, and swallowed — the whole test
|
|
93
|
+
* run must proceed even when the preflight cannot complete (e.g. `window.__sdk`
|
|
94
|
+
* not yet installed, or a page navigation mid-attach). Callers that want the
|
|
95
|
+
* result for report provenance (`RelayRunReport.preflight`) get `undefined`
|
|
96
|
+
* in that case, which downstream code treats the same as "not run".
|
|
97
|
+
*
|
|
98
|
+
* SECRET-HANDLING: no relay/wss/secret values are read or logged; only
|
|
99
|
+
* permission-state strings cross this boundary.
|
|
100
|
+
*
|
|
101
|
+
* @param conn - The live CDP connection (relay-attached page).
|
|
102
|
+
* @param timeoutMs - Round-trip bound. Defaults to {@link PERMISSION_PREFLIGHT_TIMEOUT_MS}.
|
|
103
|
+
*/
|
|
104
|
+
async function runPermissionPreflight(conn, timeoutMs = PERMISSION_PREFLIGHT_TIMEOUT_MS) {
|
|
105
|
+
const TIMEOUT_SENTINEL = Symbol("permission-preflight-timeout");
|
|
106
|
+
const timeoutPromise = new Promise((resolve) => setTimeout(() => resolve(TIMEOUT_SENTINEL), timeoutMs));
|
|
107
|
+
try {
|
|
108
|
+
const evalPromise = conn.send("Runtime.evaluate", {
|
|
109
|
+
expression: buildPermissionPreflightExpression(),
|
|
110
|
+
returnByValue: true,
|
|
111
|
+
awaitPromise: true
|
|
112
|
+
});
|
|
113
|
+
const raceResult = await Promise.race([evalPromise.then((v) => ({
|
|
114
|
+
tag: "eval",
|
|
115
|
+
v
|
|
116
|
+
})), timeoutPromise.then(() => ({ tag: "timeout" }))]);
|
|
117
|
+
if (raceResult.tag === "timeout") {
|
|
118
|
+
process.stderr.write(`test-runner: permission preflight timed out after ${timeoutMs}ms — proceeding with __AIT_PERMS__ unset\n`);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if (raceResult.v.exceptionDetails) {
|
|
122
|
+
process.stderr.write("test-runner: permission preflight threw — proceeding with __AIT_PERMS__ unset\n");
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
const rawValue = raceResult.v.result.value;
|
|
126
|
+
if (typeof rawValue !== "string") return void 0;
|
|
127
|
+
const parsed = JSON.parse(rawValue);
|
|
128
|
+
if (typeof parsed !== "object" || parsed === null) return void 0;
|
|
129
|
+
return parsed;
|
|
130
|
+
} catch (e) {
|
|
131
|
+
process.stderr.write(`test-runner: permission preflight failed (${e instanceof Error ? e.message : String(e)}) — proceeding with __AIT_PERMS__ unset\n`);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Injects each key of `globals` into `globalThis` in the page via a single
|
|
137
|
+
* `Runtime.evaluate` call. Must be called BEFORE the first `bundleTestFile`
|
|
138
|
+
* inject — the cell is session-global and applies to all subsequent files.
|
|
139
|
+
*
|
|
140
|
+
* Throws if the CDP evaluate returns an exception.
|
|
141
|
+
*
|
|
142
|
+
* @param conn - The live CDP connection (relay-attached page).
|
|
143
|
+
* @param globals - Plain-JSON-serialisable key→value map to assign onto `globalThis`.
|
|
144
|
+
*/
|
|
145
|
+
async function injectGlobals(conn, globals) {
|
|
146
|
+
const expr = `(() => { Object.assign(globalThis, ${JSON.stringify(globals)}); return true; })()`;
|
|
147
|
+
const result = await conn.send("Runtime.evaluate", {
|
|
148
|
+
expression: expr,
|
|
149
|
+
returnByValue: true
|
|
150
|
+
});
|
|
151
|
+
if (result.exceptionDetails) {
|
|
152
|
+
const msg = result.exceptionDetails.exception?.description ?? result.exceptionDetails.text ?? "unknown error";
|
|
153
|
+
throw new Error(`injectGlobals: Runtime.evaluate threw: ${msg}`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Injects (or updates) the "Debugger Connected"/"Debugger Disconnected"
|
|
158
|
+
* on-phone indicator via `Runtime.evaluate` (#730).
|
|
159
|
+
*
|
|
160
|
+
* Uses the same CDP mechanism as {@link injectGlobals} — a single
|
|
161
|
+
* `Runtime.evaluate` round-trip with the expression built by
|
|
162
|
+
* {@link buildIndicatorExpression}. The indicator is a LIVE badge rendered at
|
|
163
|
+
* the bottom-left of the page (position:fixed, safe-area-aware): the badge's
|
|
164
|
+
* controller is idempotent, so calling this again (e.g. right before
|
|
165
|
+
* `close()` with `{ state: 'disconnected' }`) UPDATES the existing badge in
|
|
166
|
+
* place instead of injecting a duplicate.
|
|
167
|
+
*
|
|
168
|
+
* **Isolation**: unlike {@link injectGlobals}, this function NEVER throws to
|
|
169
|
+
* its caller. Injection failure (e.g. page detached during inject, CSS not
|
|
170
|
+
* supported) is swallowed and logged as `console.debug` — the badge is
|
|
171
|
+
* informational UI and must never block attach success or test execution.
|
|
172
|
+
* This is the same fire-and-forget spirit as the in-page console mount in
|
|
173
|
+
* `src/in-app/attach.ts`.
|
|
174
|
+
*
|
|
175
|
+
* Call this ONLY on the manual debug paths (start_attach MCP, devtools-test
|
|
176
|
+
* CLI). Do NOT call on the `run_tests` auto-attach path — the badge
|
|
177
|
+
* would contaminate screenshots, measure_safe_area probes, and DOM snapshots
|
|
178
|
+
* taken during automated measurement runs.
|
|
179
|
+
*
|
|
180
|
+
* SECRET-HANDLING: the injected expression contains no secrets, relay URLs,
|
|
181
|
+
* wss addresses, or TOTP codes — it is pure DOM UI text + enum state built by
|
|
182
|
+
* {@link buildIndicatorExpression}.
|
|
183
|
+
*
|
|
184
|
+
* @param conn - The live CDP connection (relay-attached page).
|
|
185
|
+
* @param opts - Optional overrides forwarded to {@link buildIndicatorExpression}.
|
|
186
|
+
*/
|
|
187
|
+
async function injectDebugIndicator(conn, opts) {
|
|
188
|
+
try {
|
|
189
|
+
await conn.send("Runtime.evaluate", {
|
|
190
|
+
expression: buildIndicatorExpression(opts),
|
|
191
|
+
returnByValue: true
|
|
192
|
+
});
|
|
193
|
+
} catch (err) {
|
|
194
|
+
console.debug("[@ait-co/devtools] debug indicator inject skipped:", err);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
//#endregion
|
|
198
|
+
export { runPermissionPreflight as n, cell_exports as t };
|
|
199
|
+
|
|
200
|
+
//# sourceMappingURL=cell-DHA578lX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cell-DHA578lX.js","names":[],"sources":["../src/test-runner/cell.ts"],"sourcesContent":["/**\n * Cell injection utility for the `devtools-test` CLI (issue #684 §4.1).\n *\n * Injects arbitrary globals into the page via `Runtime.evaluate` BEFORE the\n * first test bundle is injected. The injected values are session-global — one\n * call covers all files in the run.\n *\n * The primary use-case is injecting `__AIT_CELL__` (sdkLine/platform) so\n * sdk-example's `aitCapture.ts` picks up the correct test-axis values instead\n * of falling back to `'2.x'`/`'mock'`.\n *\n * devtools does NOT know the shape of `__AIT_CELL__` — it only provides the\n * general injection mechanism. The caller (CLI or MCP auto-attach path) is\n * responsible for constructing the cell object.\n *\n * SECRET-HANDLING: cell values (sdkLine/platform) are not secrets and may be\n * logged at the caller's discretion. This module does NOT log them itself.\n *\n * Node-only. react-free. CdpConnection only.\n */\n\n/**\n * `__AIT_PERMS__` key → SDK function name whose `.getPermission()` is probed\n * (devtools#739). This is the exact contract sdk-example#265 consumes — do\n * not rename keys without a coordinated sdk-example update.\n *\n * Each SDK function is reached via `window.__sdk` — the SAME page-global\n * bridge `src/in-app/auto.ts` installs for `call_sdk`/`__sdkCall` (see\n * `src/test-runner/bundle.ts`'s `sdkRedirectPlugin`, which redirects bundled\n * `@apps-in-toss/web-framework` imports to this same object at runtime). The\n * test-runner bundle owns no independent SDK import — `window.__sdk` is a\n * page global regardless of whether a bundle has been injected yet, so this\n * preflight runs standalone via `injectGlobals`'s `Runtime.evaluate`\n * mechanism BEFORE the first bundle, rather than being prepended into\n * `rpc.ts#buildRunTestsExpression`. (Design choice documented in PR #739 —\n * bundle-prepend was the fallback plan if the SDK were only reachable from\n * inside bundle scope, which reality ruled out.)\n */\nconst PERMISSION_PROBE_MAP: Record<string, string> = {\n clipboardRead: 'getClipboardText',\n clipboardWrite: 'setClipboardText',\n album: 'fetchAlbumPhotos',\n camera: 'openCamera',\n contacts: 'fetchContacts',\n location: 'getCurrentLocation',\n};\n\n/** Non-fatal bound for the permission preflight `Runtime.evaluate` round-trip. */\nexport const PERMISSION_PREFLIGHT_TIMEOUT_MS = 10_000;\n\n/**\n * Builds the `Runtime.evaluate` expression for the permission-state preflight\n * (devtools#739). Evaluated ONCE per relay session, before the first test\n * bundle is injected (see `relay-worker.ts#runTestFilesOverRelay`).\n *\n * For each entry in {@link PERMISSION_PROBE_MAP}, probes\n * `window.__sdk.<fn>.getPermission()` — a NON-blocking query (never\n * `openPermissionDialog`/`requestPermission`, which open native UI) — and\n * assigns the resolved `'allowed'|'denied'|'notDetermined'` status to\n * `globalThis.__AIT_PERMS__[<key>]`. A probe that is absent, throws, or\n * rejects resolves to `'unavailable'` — this expression never throws to its\n * caller, and a single slow/broken permission must not fail the others (each\n * probe is awaited independently, not short-circuited).\n *\n * Tests (sdk-example#265) read `globalThis.__AIT_PERMS__` to branch\n * deterministically per permission state instead of blanket outcome-branching.\n *\n * Returns a JSON-serialised string (the same double-serialisation pattern\n * `rpc.ts#buildRunTestsExpression` uses) rather than relying on CDP\n * `returnByValue` structural fidelity for a plain object across the Chii\n * relay — the Node side re-parses via `JSON.parse`.\n *\n * Pure function — no I/O, no CDP call. Callers pass the returned string to\n * `Runtime.evaluate`.\n */\nexport function buildPermissionPreflightExpression(): string {\n const probeEntries = Object.entries(PERMISSION_PROBE_MAP)\n .map(\n ([key, fnName]) =>\n ` result[${JSON.stringify(key)}] = await probe(${JSON.stringify(fnName)});`,\n )\n .join('\\n');\n\n return (\n `(async () => {` +\n ` const probe = async (fnName) => {` +\n ` try {` +\n ` const fn = globalThis.__sdk && globalThis.__sdk[fnName];` +\n ` if (!fn || typeof fn.getPermission !== 'function') return 'unavailable';` +\n ` const status = await fn.getPermission();` +\n ` if (status === 'allowed' || status === 'denied' || status === 'notDetermined') return status;` +\n ` return 'unavailable';` +\n ` } catch (e) {` +\n ` return 'unavailable';` +\n ` }` +\n ` };` +\n ` const result = {};` +\n `${probeEntries}\\n` +\n ` globalThis.__AIT_PERMS__ = result;` +\n ` return JSON.stringify(result);` +\n `})()`\n );\n}\n\n/**\n * Runs the permission-state preflight (devtools#739) via `Runtime.evaluate`\n * and returns the collected `__AIT_PERMS__` map, or `undefined` when the\n * preflight itself failed or timed out.\n *\n * NON-FATAL by design: any failure (CDP exception, timeout, page-side throw)\n * is caught here, logged as ONE stderr line, and swallowed — the whole test\n * run must proceed even when the preflight cannot complete (e.g. `window.__sdk`\n * not yet installed, or a page navigation mid-attach). Callers that want the\n * result for report provenance (`RelayRunReport.preflight`) get `undefined`\n * in that case, which downstream code treats the same as \"not run\".\n *\n * SECRET-HANDLING: no relay/wss/secret values are read or logged; only\n * permission-state strings cross this boundary.\n *\n * @param conn - The live CDP connection (relay-attached page).\n * @param timeoutMs - Round-trip bound. Defaults to {@link PERMISSION_PREFLIGHT_TIMEOUT_MS}.\n */\nexport async function runPermissionPreflight(\n conn: CdpConnection,\n timeoutMs = PERMISSION_PREFLIGHT_TIMEOUT_MS,\n): Promise<Record<string, string> | undefined> {\n const TIMEOUT_SENTINEL = Symbol('permission-preflight-timeout');\n const timeoutPromise = new Promise<typeof TIMEOUT_SENTINEL>((resolve) =>\n setTimeout(() => resolve(TIMEOUT_SENTINEL), timeoutMs),\n );\n\n try {\n const evalPromise = conn.send('Runtime.evaluate', {\n expression: buildPermissionPreflightExpression(),\n returnByValue: true,\n awaitPromise: true,\n });\n\n const raceResult = await Promise.race([\n evalPromise.then((v) => ({ tag: 'eval' as const, v })),\n timeoutPromise.then(() => ({ tag: 'timeout' as const })),\n ]);\n\n if (raceResult.tag === 'timeout') {\n process.stderr.write(\n `test-runner: permission preflight timed out after ${timeoutMs}ms — proceeding with __AIT_PERMS__ unset\\n`,\n );\n return undefined;\n }\n\n if (raceResult.v.exceptionDetails) {\n process.stderr.write(\n 'test-runner: permission preflight threw — proceeding with __AIT_PERMS__ unset\\n',\n );\n return undefined;\n }\n\n const rawValue = raceResult.v.result.value;\n if (typeof rawValue !== 'string') return undefined;\n const parsed: unknown = JSON.parse(rawValue);\n if (typeof parsed !== 'object' || parsed === null) return undefined;\n return parsed as Record<string, string>;\n } catch (e) {\n process.stderr.write(\n `test-runner: permission preflight failed (${\n e instanceof Error ? e.message : String(e)\n }) — proceeding with __AIT_PERMS__ unset\\n`,\n );\n return undefined;\n }\n}\n\nimport { buildIndicatorExpression } from '../mcp/attach-orchestrator.js';\nimport type { CdpConnection } from '../mcp/cdp-connection.js';\n\n/**\n * Injects each key of `globals` into `globalThis` in the page via a single\n * `Runtime.evaluate` call. Must be called BEFORE the first `bundleTestFile`\n * inject — the cell is session-global and applies to all subsequent files.\n *\n * Throws if the CDP evaluate returns an exception.\n *\n * @param conn - The live CDP connection (relay-attached page).\n * @param globals - Plain-JSON-serialisable key→value map to assign onto `globalThis`.\n */\nexport async function injectGlobals(\n conn: CdpConnection,\n globals: Record<string, unknown>,\n): Promise<void> {\n // JSON.stringify is safe here: globals values are plain data (sdkLine/platform\n // strings or simple objects). If a value is not JSON-serialisable this throws\n // at call time, which surfaces the error early and clearly.\n const expr = `(() => { Object.assign(globalThis, ${JSON.stringify(globals)}); return true; })()`;\n const result = await conn.send('Runtime.evaluate', { expression: expr, returnByValue: true });\n if (result.exceptionDetails) {\n const msg =\n result.exceptionDetails.exception?.description ??\n result.exceptionDetails.text ??\n 'unknown error';\n throw new Error(`injectGlobals: Runtime.evaluate threw: ${msg}`);\n }\n}\n\n/**\n * Injects (or updates) the \"Debugger Connected\"/\"Debugger Disconnected\"\n * on-phone indicator via `Runtime.evaluate` (#730).\n *\n * Uses the same CDP mechanism as {@link injectGlobals} — a single\n * `Runtime.evaluate` round-trip with the expression built by\n * {@link buildIndicatorExpression}. The indicator is a LIVE badge rendered at\n * the bottom-left of the page (position:fixed, safe-area-aware): the badge's\n * controller is idempotent, so calling this again (e.g. right before\n * `close()` with `{ state: 'disconnected' }`) UPDATES the existing badge in\n * place instead of injecting a duplicate.\n *\n * **Isolation**: unlike {@link injectGlobals}, this function NEVER throws to\n * its caller. Injection failure (e.g. page detached during inject, CSS not\n * supported) is swallowed and logged as `console.debug` — the badge is\n * informational UI and must never block attach success or test execution.\n * This is the same fire-and-forget spirit as the in-page console mount in\n * `src/in-app/attach.ts`.\n *\n * Call this ONLY on the manual debug paths (start_attach MCP, devtools-test\n * CLI). Do NOT call on the `run_tests` auto-attach path — the badge\n * would contaminate screenshots, measure_safe_area probes, and DOM snapshots\n * taken during automated measurement runs.\n *\n * SECRET-HANDLING: the injected expression contains no secrets, relay URLs,\n * wss addresses, or TOTP codes — it is pure DOM UI text + enum state built by\n * {@link buildIndicatorExpression}.\n *\n * @param conn - The live CDP connection (relay-attached page).\n * @param opts - Optional overrides forwarded to {@link buildIndicatorExpression}.\n */\nexport async function injectDebugIndicator(\n conn: CdpConnection,\n opts?: { label?: string; disconnectedLabel?: string; state?: 'attached' | 'disconnected' },\n): Promise<void> {\n try {\n await conn.send('Runtime.evaluate', {\n expression: buildIndicatorExpression(opts),\n returnByValue: true,\n });\n } catch (err) {\n // Badge injection is informational UI — swallow and log; never propagate.\n console.debug('[@ait-co/devtools] debug indicator inject skipped:', err);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsCA,MAAM,uBAA+C;CACnD,eAAe;CACf,gBAAgB;CAChB,OAAO;CACP,QAAQ;CACR,UAAU;CACV,UAAU;CACX;;AAGD,MAAa,kCAAkC;;;;;;;;;;;;;;;;;;;;;;;;;;AA2B/C,SAAgB,qCAA6C;AAQ3D,QACE,8bARmB,OAAO,QAAQ,qBAAqB,CACtD,KACE,CAAC,KAAK,YACL,cAAc,KAAK,UAAU,IAAI,CAAC,kBAAkB,KAAK,UAAU,OAAO,CAAC,IAC9E,CACA,KAAK,KAAK,CAgBK;;;;;;;;;;;;;;;;;;;;AAyBpB,eAAsB,uBACpB,MACA,YAAY,iCACiC;CAC7C,MAAM,mBAAmB,OAAO,+BAA+B;CAC/D,MAAM,iBAAiB,IAAI,SAAkC,YAC3D,iBAAiB,QAAQ,iBAAiB,EAAE,UAAU,CACvD;AAED,KAAI;EACF,MAAM,cAAc,KAAK,KAAK,oBAAoB;GAChD,YAAY,oCAAoC;GAChD,eAAe;GACf,cAAc;GACf,CAAC;EAEF,MAAM,aAAa,MAAM,QAAQ,KAAK,CACpC,YAAY,MAAM,OAAO;GAAE,KAAK;GAAiB;GAAG,EAAE,EACtD,eAAe,YAAY,EAAE,KAAK,WAAoB,EAAE,CACzD,CAAC;AAEF,MAAI,WAAW,QAAQ,WAAW;AAChC,WAAQ,OAAO,MACb,qDAAqD,UAAU,4CAChE;AACD;;AAGF,MAAI,WAAW,EAAE,kBAAkB;AACjC,WAAQ,OAAO,MACb,kFACD;AACD;;EAGF,MAAM,WAAW,WAAW,EAAE,OAAO;AACrC,MAAI,OAAO,aAAa,SAAU,QAAO,KAAA;EACzC,MAAM,SAAkB,KAAK,MAAM,SAAS;AAC5C,MAAI,OAAO,WAAW,YAAY,WAAW,KAAM,QAAO,KAAA;AAC1D,SAAO;UACA,GAAG;AACV,UAAQ,OAAO,MACb,6CACE,aAAa,QAAQ,EAAE,UAAU,OAAO,EAAE,CAC3C,2CACF;AACD;;;;;;;;;;;;;AAiBJ,eAAsB,cACpB,MACA,SACe;CAIf,MAAM,OAAO,sCAAsC,KAAK,UAAU,QAAQ,CAAC;CAC3E,MAAM,SAAS,MAAM,KAAK,KAAK,oBAAoB;EAAE,YAAY;EAAM,eAAe;EAAM,CAAC;AAC7F,KAAI,OAAO,kBAAkB;EAC3B,MAAM,MACJ,OAAO,iBAAiB,WAAW,eACnC,OAAO,iBAAiB,QACxB;AACF,QAAM,IAAI,MAAM,0CAA0C,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCpE,eAAsB,qBACpB,MACA,MACe;AACf,KAAI;AACF,QAAM,KAAK,KAAK,oBAAoB;GAClC,YAAY,yBAAyB,KAAK;GAC1C,eAAe;GAChB,CAAC;UACK,KAAK;AAEZ,UAAQ,MAAM,sDAAsD,IAAI"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { n as buildRelayVerifyAuth, r as generateTotp, t as assertRelayAuthConfigured } from "./totp-DfekTBk3.js";
|
|
2
2
|
import "./test-runner/relay-factory.js";
|
|
3
3
|
import { a as startTunnelHealthProbe, i as startQuickTunnel, n as makeTunnelStatus, o as logError, r as printAttachBanner, s as logInfo, t as generateAttachToken } from "./attach-orchestrator-Cjdhir2U.js";
|
|
4
|
-
import "./cell-
|
|
4
|
+
import "./cell-D1y4shoV.js";
|
|
5
5
|
import "./qr-http-server-DeRrKp_R.js";
|
|
6
6
|
import "./relay-secret-store-BcVrWwTq.js";
|
|
7
7
|
import { createRequire } from "node:module";
|
|
@@ -347,9 +347,13 @@ var ChiiCdpConnection = class {
|
|
|
347
347
|
/**
|
|
348
348
|
* Issue a CDP command and resolve with its result (Phase 2). Rejects on a CDP
|
|
349
349
|
* error frame or when no websocket is open (no page attached yet).
|
|
350
|
+
*
|
|
351
|
+
* @param opts.timeoutMs - Per-call override for this connection's command
|
|
352
|
+
* watchdog (devtools#747) — see the `CdpConnection.send` docblock for the
|
|
353
|
+
* contract callers racing their own longer timeout must follow.
|
|
350
354
|
*/
|
|
351
|
-
send(method, params) {
|
|
352
|
-
return this.sendCommand(method, params ?? {});
|
|
355
|
+
send(method, params, opts) {
|
|
356
|
+
return this.sendCommand(method, params ?? {}, opts);
|
|
353
357
|
}
|
|
354
358
|
/**
|
|
355
359
|
* Issue an arbitrary request→response command over the relay and resolve with
|
|
@@ -360,16 +364,22 @@ var ChiiCdpConnection = class {
|
|
|
360
364
|
* auto-reconnect). Caller should re-run `list_pages` or `enableDomains` to
|
|
361
365
|
* reattach.
|
|
362
366
|
*
|
|
363
|
-
* Times out after `
|
|
364
|
-
* `AIT_CDP_COMMAND_TIMEOUT_MS`)
|
|
365
|
-
*
|
|
367
|
+
* Times out after `opts.timeoutMs` when given, else `commandTimeoutMs`
|
|
368
|
+
* (default 30s, env `AIT_CDP_COMMAND_TIMEOUT_MS`) — see devtools#747: the
|
|
369
|
+
* default 30s watchdog used to undercut the test-runner's own longer
|
|
370
|
+
* file-evaluate race no matter what `--timeout` the caller asked for. On
|
|
371
|
+
* timeout the pending entry is cleaned up and the promise rejects with a
|
|
372
|
+
* descriptive Korean error. `Number.isFinite` guards against a non-finite
|
|
373
|
+
* override (e.g. `Infinity`, mirroring `waitForFirstTarget`'s convention)
|
|
374
|
+
* so an intentional "no watchdog" override doesn't get clamped by
|
|
375
|
+
* `setTimeout`.
|
|
366
376
|
*/
|
|
367
|
-
sendCommand(method, params = {}) {
|
|
377
|
+
sendCommand(method, params = {}, opts) {
|
|
368
378
|
if (this.connectionState === "disconnected") return Promise.reject(/* @__PURE__ */ new Error(`relay에 연결되어 있지 않습니다 (${method}). list_pages로 attach 상태를 확인하고 enableDomains()로 재연결하세요.`));
|
|
369
379
|
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return Promise.reject(/* @__PURE__ */ new Error("No mini-app page attached to the Chii relay yet. Call enableDomains() first."));
|
|
370
380
|
const id = this.nextCommandId++;
|
|
371
381
|
const ws = this.ws;
|
|
372
|
-
const timeoutMs = this.commandTimeoutMs;
|
|
382
|
+
const timeoutMs = opts?.timeoutMs !== void 0 && Number.isFinite(opts.timeoutMs) && opts.timeoutMs > 0 ? opts.timeoutMs : this.commandTimeoutMs;
|
|
373
383
|
return new Promise((resolve, reject) => {
|
|
374
384
|
const handle = setTimeout(() => {
|
|
375
385
|
this.pending.delete(id);
|
|
@@ -998,4 +1008,4 @@ async function bootRelayFamily(options = {}) {
|
|
|
998
1008
|
//#endregion
|
|
999
1009
|
export { bootRelayFamily, buildRelayVerifyAuth };
|
|
1000
1010
|
|
|
1001
|
-
//# sourceMappingURL=debug-server-
|
|
1011
|
+
//# sourceMappingURL=debug-server-BcSBrajf.js.map
|