comber 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. package/LICENSE +18 -0
  2. package/README.md +732 -0
  3. package/action.yml +279 -0
  4. package/comber.config.example.json +64 -0
  5. package/dist/api/classify.js +99 -0
  6. package/dist/api/classify.js.map +1 -0
  7. package/dist/api/driver.js +235 -0
  8. package/dist/api/driver.js.map +1 -0
  9. package/dist/api/graphql-driver.js +150 -0
  10. package/dist/api/graphql-driver.js.map +1 -0
  11. package/dist/api/graphql.js +115 -0
  12. package/dist/api/graphql.js.map +1 -0
  13. package/dist/api/har-driver.js +167 -0
  14. package/dist/api/har-driver.js.map +1 -0
  15. package/dist/api/har.js +100 -0
  16. package/dist/api/har.js.map +1 -0
  17. package/dist/api/load.js +83 -0
  18. package/dist/api/load.js.map +1 -0
  19. package/dist/api/openapi.js +159 -0
  20. package/dist/api/openapi.js.map +1 -0
  21. package/dist/api/request.js +141 -0
  22. package/dist/api/request.js.map +1 -0
  23. package/dist/api/schema.js +126 -0
  24. package/dist/api/schema.js.map +1 -0
  25. package/dist/cli.js +352 -0
  26. package/dist/cli.js.map +1 -0
  27. package/dist/comment.js +164 -0
  28. package/dist/comment.js.map +1 -0
  29. package/dist/config.js +664 -0
  30. package/dist/config.js.map +1 -0
  31. package/dist/connectors/builtin/discord.js +55 -0
  32. package/dist/connectors/builtin/github-checks.js +53 -0
  33. package/dist/connectors/builtin/junit.js +70 -0
  34. package/dist/connectors/builtin/linear.js +58 -0
  35. package/dist/connectors/builtin/slack.js +60 -0
  36. package/dist/connectors/builtin/webhook.js +39 -0
  37. package/dist/connectors/index.js +3 -0
  38. package/dist/connectors/loader.js +72 -0
  39. package/dist/connectors/registry.js +28 -0
  40. package/dist/connectors/types.js +23 -0
  41. package/dist/core/action-gate.js +48 -0
  42. package/dist/core/baseline.js +85 -0
  43. package/dist/core/baseline.js.map +1 -0
  44. package/dist/core/blind-spots.js +22 -0
  45. package/dist/core/blind-spots.js.map +1 -0
  46. package/dist/core/contract-extras.js +1 -0
  47. package/dist/core/contract-extras.js.map +1 -0
  48. package/dist/core/contract.js +1 -0
  49. package/dist/core/contract.js.map +1 -0
  50. package/dist/core/coverage.js +70 -0
  51. package/dist/core/coverage.js.map +1 -0
  52. package/dist/core/crawl.js +229 -0
  53. package/dist/core/crawl.js.map +1 -0
  54. package/dist/core/egress.js +128 -0
  55. package/dist/core/egress.js.map +1 -0
  56. package/dist/core/ledger.js +41 -0
  57. package/dist/core/ledger.js.map +1 -0
  58. package/dist/core/net.js +39 -0
  59. package/dist/core/pipeline.js +191 -0
  60. package/dist/core/pipeline.js.map +1 -0
  61. package/dist/core/plan.js +121 -0
  62. package/dist/core/plan.js.map +1 -0
  63. package/dist/core/redact.js +240 -0
  64. package/dist/core/redact.js.map +1 -0
  65. package/dist/core/replay.js +263 -0
  66. package/dist/core/replay.js.map +1 -0
  67. package/dist/core/report.js +253 -0
  68. package/dist/core/report.js.map +1 -0
  69. package/dist/core/safety-judge.js +116 -0
  70. package/dist/core/safety.js +92 -0
  71. package/dist/core/safety.js.map +1 -0
  72. package/dist/core/signal-normalize.js +190 -0
  73. package/dist/core/signal-normalize.js.map +1 -0
  74. package/dist/core/signals.js +5 -0
  75. package/dist/core/signals.js.map +1 -0
  76. package/dist/core/triage.js +104 -0
  77. package/dist/core/triage.js.map +1 -0
  78. package/dist/core/types.js +1 -0
  79. package/dist/core/types.js.map +1 -0
  80. package/dist/desktop/desktop-profile.js +6 -0
  81. package/dist/desktop/desktop-profile.js.map +1 -0
  82. package/dist/desktop/driver.js +433 -0
  83. package/dist/desktop/driver.js.map +1 -0
  84. package/dist/desktop/evidence.js +206 -0
  85. package/dist/desktop/evidence.js.map +1 -0
  86. package/dist/desktop/macos/accessibility-runner.js +118 -0
  87. package/dist/desktop/macos/accessibility-runner.js.map +1 -0
  88. package/dist/desktop/macos/types.js +1 -0
  89. package/dist/desktop/macos/types.js.map +1 -0
  90. package/dist/desktop/preflight.js +107 -0
  91. package/dist/desktop/preflight.js.map +1 -0
  92. package/dist/desktop/proxy-proof.js +185 -0
  93. package/dist/desktop/proxy-proof.js.map +1 -0
  94. package/dist/desktop/view.js +348 -0
  95. package/dist/desktop/view.js.map +1 -0
  96. package/dist/desktop/windows/types.js +1 -0
  97. package/dist/desktop/windows/types.js.map +1 -0
  98. package/dist/desktop/windows/uia-runner.js +127 -0
  99. package/dist/desktop/windows/uia-runner.js.map +1 -0
  100. package/dist/native/apple.js +14 -0
  101. package/dist/native/apple.js.map +1 -0
  102. package/dist/native/device-profile.js +6 -0
  103. package/dist/native/device-profile.js.map +1 -0
  104. package/dist/native/driver.js +416 -0
  105. package/dist/native/driver.js.map +1 -0
  106. package/dist/native/evidence.js +164 -0
  107. package/dist/native/evidence.js.map +1 -0
  108. package/dist/native/load.js +9 -0
  109. package/dist/native/load.js.map +1 -0
  110. package/dist/native/maestro.js +153 -0
  111. package/dist/native/maestro.js.map +1 -0
  112. package/dist/native/preflight.js +72 -0
  113. package/dist/native/preflight.js.map +1 -0
  114. package/dist/native/proxy-proof.js +182 -0
  115. package/dist/native/proxy-proof.js.map +1 -0
  116. package/dist/native/samsung.js +34 -0
  117. package/dist/native/samsung.js.map +1 -0
  118. package/dist/native/signals.js +50 -0
  119. package/dist/native/signals.js.map +1 -0
  120. package/dist/native/view.js +139 -0
  121. package/dist/native/view.js.map +1 -0
  122. package/dist/profile.js +237 -0
  123. package/dist/profile.js.map +1 -0
  124. package/dist/sink.js +134 -0
  125. package/dist/sink.js.map +1 -0
  126. package/dist/sinks/file.js +49 -0
  127. package/dist/sinks/file.js.map +1 -0
  128. package/dist/sinks/http.js +38 -0
  129. package/dist/sinks/http.js.map +1 -0
  130. package/dist/sinks/index.js +5 -0
  131. package/dist/sinks/index.js.map +1 -0
  132. package/dist/sinks/multi.js +40 -0
  133. package/dist/sinks/multi.js.map +1 -0
  134. package/dist/sinks/paths.js +32 -0
  135. package/dist/sinks/paths.js.map +1 -0
  136. package/dist/sinks/record.js +58 -0
  137. package/dist/sinks/record.js.map +1 -0
  138. package/dist/sinks/types.js +1 -0
  139. package/dist/sinks/types.js.map +1 -0
  140. package/dist/web/breakage.js +210 -0
  141. package/dist/web/breakage.js.map +1 -0
  142. package/dist/web/controls.js +102 -0
  143. package/dist/web/controls.js.map +1 -0
  144. package/dist/web/driver.js +803 -0
  145. package/dist/web/driver.js.map +1 -0
  146. package/dist/web/elements.js +151 -0
  147. package/dist/web/elements.js.map +1 -0
  148. package/dist/web/fingerprint.js +13 -0
  149. package/dist/web/fingerprint.js.map +1 -0
  150. package/dist/web/forms.js +74 -0
  151. package/dist/web/forms.js.map +1 -0
  152. package/dist/web/links.js +194 -0
  153. package/dist/web/links.js.map +1 -0
  154. package/dist/web/types.js +1 -0
  155. package/dist/web/types.js.map +1 -0
  156. package/package.json +80 -0
  157. package/runtime/macos-accessibility-runner.js +275 -0
  158. package/runtime/windows-uia-runner.ps1 +539 -0
@@ -0,0 +1,416 @@
1
+ import { buildDeviceEvidence } from "./evidence.js";
2
+ import { evaluateNativeReadiness, readinessBlindSpots } from "./preflight.js";
3
+ import { blindSpotDetailsOf, screenNameOf, screenSignature, viewToSurfaceNodes } from "./view.js";
4
+ import { NATIVE_ERROR_TEXT, nativeSignal, scanLogs } from "./signals.js";
5
+ const LAUNCH_ID = "launch";
6
+ const DEEPLINK_PREFIX = "deeplink:";
7
+ // Goto handle for a TAP-ONLY screen: a base anchor (launch | deeplink) + the tap sequence that
8
+ // reaches it from that base. `goto` relaunches the base then re-taps the sequence. Self-contained
9
+ // (no in-memory map) so the handle survives into plan.json. Encoding: "tappath:<base>::<t>>><t>…".
10
+ const TAPPATH_PREFIX = "tappath:";
11
+ const TAP_SEP = ">>";
12
+ const BASE_SEP = "::";
13
+ /** A scheme-less, redaction-safe scope token for a deeplink ("myapp://settings/x" → "settings/x"). */
14
+ function deeplinkScope(uri) {
15
+ const stripped = uri.replace(/^[a-z][a-z0-9+.-]*:\/\//i, "").replace(/^\/+/, "");
16
+ return stripped || uri.replace(/[^A-Za-z0-9/_-]+/g, "-");
17
+ }
18
+ /** Encode one tap as a token: by stable id (`i=`), else visible text (`t=`), else index (`x=`). */
19
+ function selectorToken(sel) {
20
+ if (sel.id)
21
+ return `i=${sel.id}`;
22
+ if (sel.text)
23
+ return `t=${sel.text}`;
24
+ return `x=${sel.index ?? 0}`;
25
+ }
26
+ function parseSelectorToken(tok) {
27
+ const eq = tok.indexOf("=");
28
+ const k = tok.slice(0, eq);
29
+ const v = tok.slice(eq + 1);
30
+ if (k === "i")
31
+ return { id: v };
32
+ if (k === "t")
33
+ return { text: v };
34
+ return { index: Number(v) || 0 };
35
+ }
36
+ function encodeTapPath(base, taps) {
37
+ return `${TAPPATH_PREFIX}${base}${BASE_SEP}${taps.map(selectorToken).join(TAP_SEP)}`;
38
+ }
39
+ /** Parse a tap-path handle back into its base anchor + tap sequence (the inverse of encode). */
40
+ function parseTapPath(id) {
41
+ const body = id.slice(TAPPATH_PREFIX.length);
42
+ const sep = body.indexOf(BASE_SEP);
43
+ const base = sep < 0 ? body : body.slice(0, sep);
44
+ const tapsStr = sep < 0 ? "" : body.slice(sep + BASE_SEP.length);
45
+ const taps = tapsStr ? tapsStr.split(TAP_SEP).map(parseSelectorToken) : [];
46
+ return { base, taps };
47
+ }
48
+ const NOOP_COVERAGE = { skip() { }, bumpExtra() { } };
49
+ export class NativeDriver {
50
+ name = "native";
51
+ capabilities;
52
+ runner;
53
+ appId;
54
+ deeplinkAnchors;
55
+ cov = NOOP_COVERAGE;
56
+ signals = [];
57
+ /** The current goto anchor (launch | a deeplink) — Address.id is goto-addressable. */
58
+ anchor;
59
+ /** Set true once an action navigates/kills the current screen, so currentAddress() differs from
60
+ * the baseline and the core's restoreBaseline relaunches to recover. Cleared by goto(). */
61
+ drifted = false;
62
+ /** Set by goto(); consumed by the next snapshot() to fix the baseline screen fingerprint. */
63
+ justWentTo = false;
64
+ /** Structural fingerprint of the post-goto baseline screen (drift reference). */
65
+ anchorFp = "";
66
+ /** The most recent snapshot's view + scope (probe("dom") + exercise scope use these). */
67
+ lastView;
68
+ currentScope = LAUNCH_ID;
69
+ exercised = 0;
70
+ deviceEvidence;
71
+ nativeReadiness;
72
+ deviceEvidenceGapsReported = false;
73
+ /** The base anchor (launch | deeplink) the current screen is rooted at, + the tap sequence
74
+ * from that base to here. A navigating tap extends `currentTaps` to build a re-goto-able
75
+ * tap-path Address; `goto` resets both (or sets them from a parsed tap-path). */
76
+ baseAnchorId = LAUNCH_ID;
77
+ currentTaps = [];
78
+ constructor(opts) {
79
+ this.runner = opts.runner;
80
+ this.appId = opts.appId;
81
+ const deviceProfile = opts.deviceProfile ?? {
82
+ platform: opts.platform,
83
+ appId: opts.appId,
84
+ deviceClass: opts.platform === "ios" ? "simulator" : "unknown",
85
+ };
86
+ const deviceEvidence = buildDeviceEvidence(deviceProfile);
87
+ this.nativeReadiness = evaluateNativeReadiness(deviceProfile);
88
+ const validationCodes = new Set(deviceEvidence.validationErrors.map((error) => error.code));
89
+ this.deviceEvidence = {
90
+ ...deviceEvidence,
91
+ gaps: [
92
+ ...deviceEvidence.gaps,
93
+ ...readinessBlindSpots(this.nativeReadiness, validationCodes, deviceProfile.platform),
94
+ ],
95
+ };
96
+ this.anchor = { id: LAUNCH_ID, scope: LAUNCH_ID, label: "launch" };
97
+ this.deeplinkAnchors = (opts.deeplinks ?? []).map((uri) => ({
98
+ id: `${DEEPLINK_PREFIX}${uri}`,
99
+ scope: deeplinkScope(uri),
100
+ label: deeplinkScope(uri),
101
+ }));
102
+ // Probes registered WITH lifecycle slots (contract v2). session + dom existed pre-v2;
103
+ // crash (after-exercise) + logs (finalize) are phases the old CLOSED vocabulary forbade.
104
+ this.capabilities = {
105
+ screenshot: typeof this.runner.screenshot === "function",
106
+ probes: [
107
+ { name: "session", slot: "after-goto" },
108
+ { name: "dom", slot: "after-snapshot" },
109
+ { name: "crash", slot: "after-exercise" },
110
+ { name: "logs", slot: "finalize" },
111
+ ],
112
+ };
113
+ }
114
+ // -- DriverExtras: the core injects its coverage sink so native sub-metrics + skips land in the
115
+ // one honest ledger the orchestrator owns. --
116
+ attachCoverage(sink) {
117
+ this.cov = sink;
118
+ }
119
+ async start() {
120
+ /* The session is established lazily by the first goto() (relaunch). Nothing to pre-warm. */
121
+ }
122
+ async close() {
123
+ await this.runner.close?.().catch(() => { });
124
+ }
125
+ // -- addressing: the launch screen + declared deeplinks are the goto-addressable frontier. -----
126
+ entryAddress() {
127
+ return this.anchorByLaunch();
128
+ }
129
+ anchorByLaunch() {
130
+ return { id: LAUNCH_ID, scope: LAUNCH_ID, label: "launch" };
131
+ }
132
+ currentAddress() {
133
+ if (this.drifted) {
134
+ return { id: `${this.anchor.id}#drifted`, scope: this.anchor.scope, label: "(navigated away)" };
135
+ }
136
+ return this.anchor;
137
+ }
138
+ inScope(addr) {
139
+ if (addr.id === LAUNCH_ID)
140
+ return true;
141
+ // Tap-path screens are reached from in-scope screens by tapping — in scope by construction
142
+ // (taps that leave the app are caught as `crash`, never yield a tap-path; see exercise()).
143
+ if (addr.id.startsWith(TAPPATH_PREFIX))
144
+ return true;
145
+ return this.deeplinkAnchors.some((a) => a.id === addr.id);
146
+ }
147
+ /** goto = relaunch (no deeplink) · relaunch + open a declared deeplink · relaunch the base
148
+ * anchor + REPLAY the recorded tap sequence (a tap-path). Resets drift + the current path. */
149
+ async goto(addr) {
150
+ if (addr.id.startsWith(TAPPATH_PREFIX)) {
151
+ const { base, taps } = parseTapPath(addr.id);
152
+ await this.relaunch(base, addr.scope);
153
+ for (const sel of taps)
154
+ await this.runner.tap(sel).catch(() => { });
155
+ this.anchor = { id: addr.id, scope: addr.scope, ...(addr.label !== undefined ? { label: addr.label } : {}) };
156
+ this.baseAnchorId = base;
157
+ this.currentTaps = taps;
158
+ }
159
+ else {
160
+ await this.relaunch(addr.id, addr.scope);
161
+ // Re-resolve to the canonical anchor object (so its scope/label are the declared ones).
162
+ this.anchor =
163
+ addr.id === LAUNCH_ID
164
+ ? this.anchorByLaunch()
165
+ : this.deeplinkAnchors.find((a) => a.id === addr.id) ?? { id: addr.id, scope: addr.scope, label: addr.label };
166
+ this.baseAnchorId = addr.id;
167
+ this.currentTaps = [];
168
+ }
169
+ this.drifted = false;
170
+ this.justWentTo = true;
171
+ this.anchorFp = "";
172
+ }
173
+ /** Relaunch the app to a base anchor (launch | deeplink); a launch failure is a `crash`. */
174
+ async relaunch(baseId, scope) {
175
+ const deeplink = baseId.startsWith(DEEPLINK_PREFIX) ? baseId.slice(DEEPLINK_PREFIX.length) : undefined;
176
+ try {
177
+ await this.runner.launch(this.appId, deeplink);
178
+ }
179
+ catch (e) {
180
+ this.signals.push(nativeSignal("crash", scope, `launch ${deeplink ?? "(home)"} failed: ${errMessage(e)}`, { action: "launch" }));
181
+ }
182
+ }
183
+ /** DISCOVER — viewHierarchy → Surface. Never throws: a runner failure is surfaced as a frozen
184
+ * screen (empty nodes + a blind spot) so the core keeps its honest accounting. */
185
+ async snapshot() {
186
+ let view = null;
187
+ try {
188
+ view = await this.runner.viewHierarchy();
189
+ }
190
+ catch (e) {
191
+ this.signals.push(nativeSignal("frozen", this.anchor.scope, `viewHierarchy timed out / failed: ${errMessage(e)}`));
192
+ }
193
+ if (!view) {
194
+ this.justWentTo = false;
195
+ const details = this.takeDeviceEvidenceGaps(this.anchor.scope);
196
+ details.unshift({
197
+ kind: "native-opaque-subtree",
198
+ severity: "blocker-for-exhaustive",
199
+ scope: this.anchor.scope,
200
+ message: "native view hierarchy was unavailable",
201
+ });
202
+ return { address: { ...this.anchor }, title: this.anchor.label, nodes: [], blindSpots: details.length, blindSpotDetails: details };
203
+ }
204
+ this.lastView = view;
205
+ const nodes = viewToSurfaceNodes(view);
206
+ if (this.justWentTo) {
207
+ this.anchorFp = screenSignature(view);
208
+ this.justWentTo = false;
209
+ }
210
+ const label = screenNameOf(view, this.anchor.label ?? this.anchor.scope);
211
+ const address = { id: this.anchor.id, scope: this.anchor.scope, label };
212
+ this.currentScope = address.scope;
213
+ const blindSpotDetails = [...blindSpotDetailsOf(view, address.scope), ...this.takeDeviceEvidenceGaps(address.scope)];
214
+ return { address, title: view.screen, nodes, blindSpots: blindSpotDetails.length, blindSpotDetails };
215
+ }
216
+ takeDeviceEvidenceGaps(scope) {
217
+ if (this.deviceEvidenceGapsReported)
218
+ return [];
219
+ this.deviceEvidenceGapsReported = true;
220
+ return this.deviceEvidence.gaps.map((gap) => ({ ...gap, scope }));
221
+ }
222
+ /** EXERCISE — tap (default) or input (text fields). After the action: detect a crash (foreground
223
+ * app changed / process died), an ANR/crash in the log window, a frozen screen (a timed-out
224
+ * re-read), an on-screen error string, and screen DRIFT (so restore relaunches to recover). */
225
+ async exercise(node) {
226
+ const el = node.handle;
227
+ const selector = { ...(el.id ? { id: el.id } : {}), ...(!el.id && node.label ? { text: node.label } : {}) };
228
+ const scope = this.currentScope;
229
+ const signals = [];
230
+ let crashed = false; // suppresses tap-discovery — a dead/backgrounded app has no new screen
231
+ const t0 = Date.now();
232
+ try {
233
+ if (node.kind === "text-input") {
234
+ await this.runner.input(selector, "comber-probe");
235
+ this.cov.bumpExtra("inputsExercised");
236
+ }
237
+ else {
238
+ await this.runner.tap(selector);
239
+ this.cov.bumpExtra("controlsExercised");
240
+ }
241
+ }
242
+ catch (e) {
243
+ // A timed-out / rejected interaction = the screen stopped responding.
244
+ this.drifted = true;
245
+ signals.push(nativeSignal("frozen", scope, `${node.kind} "${node.label}" did not respond: ${errMessage(e)}`, {
246
+ action: node.label,
247
+ discriminator: node.id,
248
+ }));
249
+ return { signals };
250
+ }
251
+ this.exercised++;
252
+ // crash: the app is no longer in the foreground (process died / kicked to launcher).
253
+ const fg = await this.runner.foregroundApp().catch(() => this.appId);
254
+ if (fg !== this.appId) {
255
+ this.drifted = true;
256
+ crashed = true;
257
+ signals.push(nativeSignal("crash", scope, `app left foreground after "${node.label}" (now: ${fg || "unknown"})`, {
258
+ action: node.label,
259
+ discriminator: node.id,
260
+ }));
261
+ }
262
+ // crash / anr from the device log window since the action started.
263
+ const logs = await this.runner.logsSince(t0).catch(() => []);
264
+ const hit = scanLogs(logs);
265
+ if (hit) {
266
+ signals.push(nativeSignal(hit.kind, scope, `${hit.kind === "crash" ? "crash" : "ANR"} in logs after "${node.label}": ${hit.line.slice(0, 120)}`, {
267
+ action: node.label,
268
+ discriminator: node.id,
269
+ }));
270
+ if (hit.kind === "crash") {
271
+ this.drifted = true;
272
+ crashed = true;
273
+ }
274
+ }
275
+ // re-read the screen: a timeout = frozen; an error string = error-text; a new fingerprint = drift.
276
+ let after = null;
277
+ try {
278
+ after = await this.runner.viewHierarchy();
279
+ }
280
+ catch (e) {
281
+ this.drifted = true;
282
+ signals.push(nativeSignal("frozen", scope, `screen unresponsive after "${node.label}": ${errMessage(e)}`, { action: node.label }));
283
+ }
284
+ if (after) {
285
+ const err = firstErrorText(after);
286
+ if (err)
287
+ signals.push(nativeSignal("error-text", scope, `on-screen error after "${node.label}": ${err.slice(0, 120)}`, { action: node.label }));
288
+ if (this.anchorFp && screenSignature(after) !== this.anchorFp) {
289
+ this.drifted = true;
290
+ // FRONTIER EXPANSION FROM ACTING: a TAP (not an input — replay re-taps) reached a NEW,
291
+ // LIVE screen (not a crash). Hand the core that screen as a re-goto-able tap-path Address
292
+ // so it gets crawled — depth no longer requires a declared deeplink. (Limit #3 resolved.)
293
+ if (!crashed && node.kind !== "text-input") {
294
+ return { signals, discovered: [this.tapPathAddress(after, [...this.currentTaps, selector])] };
295
+ }
296
+ }
297
+ }
298
+ return { signals };
299
+ }
300
+ /** Build a re-goto-able Address for a tap-reached screen: `id` = the replayable tap-path
301
+ * (base anchor + tap sequence), `dedupeKey` = the destination screen fingerprint (so two
302
+ * paths to the same screen enqueue once), `scope`/`label` = the destination screen name. */
303
+ tapPathAddress(after, taps) {
304
+ const fp = screenSignature(after);
305
+ const name = screenNameOf(after, "").trim();
306
+ const scope = name ? name.toLowerCase().replace(/[^a-z0-9/_-]+/g, "-") : `tap/${fp.slice(0, 8)}`;
307
+ return { id: encodeTapPath(this.baseAnchorId, taps), scope, dedupeKey: `tapscreen:${fp}`, label: name || scope };
308
+ }
309
+ /** Driver-registered probes, each fired by the orchestrator at its declared lifecycle slot
310
+ * (contract v2 — the slot set is fixed, but WHICH probes run is driver-defined). Native serves:
311
+ * • session (after-goto) — app-liveness / crash-on-launch
312
+ * • dom (after-snapshot) — on-screen error-text audit of the current screen
313
+ * • crash (after-exercise) — screen-level foreground backstop (a phase the old CLOSED
314
+ * vocabulary forbade — limit #2 resolved)
315
+ * • logs (finalize) — a final whole-session device-log scan for a late crash/ANR */
316
+ async probe(phase) {
317
+ if (phase === "session") {
318
+ const out = [];
319
+ const fg = await this.runner.foregroundApp().catch(() => "");
320
+ if (fg && fg !== this.appId) {
321
+ out.push(nativeSignal("crash", this.anchor.scope, `app not in foreground after launch (now: ${fg})`, { action: "launch" }));
322
+ }
323
+ try {
324
+ await this.runner.viewHierarchy();
325
+ }
326
+ catch (e) {
327
+ out.push(nativeSignal("frozen", this.anchor.scope, `screen unresponsive after launch: ${errMessage(e)}`, { action: "launch" }));
328
+ }
329
+ return out;
330
+ }
331
+ if (phase === "dom") {
332
+ if (!this.lastView)
333
+ return [];
334
+ const err = firstErrorText(this.lastView);
335
+ return err ? [nativeSignal("error-text", this.currentScope, `on-screen error: ${err.slice(0, 120)}`)] : [];
336
+ }
337
+ if (phase === "crash") {
338
+ // Screen-level crash backstop after a state's exercises: is the app still foregrounded?
339
+ const fg = await this.runner.foregroundApp().catch(() => this.appId);
340
+ return fg && fg !== this.appId
341
+ ? [nativeSignal("crash", this.currentScope, `app not in foreground (now: ${fg})`, { action: "after-exercise" })]
342
+ : [];
343
+ }
344
+ if (phase === "logs") {
345
+ // End-of-crawl scan of the whole session's log window for a late/background crash or ANR.
346
+ const hit = scanLogs(await this.runner.logsSince(0).catch(() => []));
347
+ return hit
348
+ ? [nativeSignal(hit.kind, this.currentScope, `${hit.kind === "crash" ? "crash" : "ANR"} in logs: ${hit.line.slice(0, 120)}`)]
349
+ : [];
350
+ }
351
+ return [];
352
+ }
353
+ /** Run a declared Maestro flow whose assertions, on failure, surface as `assertion-fail`. Only
354
+ * callable when the runner supports raw flows; otherwise a no-op returning []. */
355
+ async runAssertionFlow(yaml, scope = this.currentScope) {
356
+ if (!this.runner.runFlow)
357
+ return [];
358
+ try {
359
+ await this.runner.runFlow(yaml);
360
+ return [];
361
+ }
362
+ catch (e) {
363
+ return [nativeSignal("assertion-fail", scope, `flow assertion failed: ${errMessage(e)}`)];
364
+ }
365
+ }
366
+ /** DriverExtras.softReset — the cheap middle restore tier: press system Back (dismiss a dialog /
367
+ * pop a screen) before the core escalates to a full relaunch (regoto). */
368
+ async softReset() {
369
+ await this.runner.back().catch(() => { });
370
+ }
371
+ /** DriverExtras.outboundAddresses — the FOLLOW-without-acting frontier seed: the declared
372
+ * deeplinks. Tap-only-reachable screens are no longer stranded here — they ride
373
+ * `ExerciseResult.discovered` as replayable tap-paths (frontier expansion from acting), so
374
+ * depth beyond the launch screen no longer requires declaring a deeplink for every screen. */
375
+ outboundAddresses() {
376
+ return this.deeplinkAnchors.filter((a) => this.inScope(a));
377
+ }
378
+ drainSignals() {
379
+ const out = this.signals;
380
+ this.signals = [];
381
+ return out;
382
+ }
383
+ async screenshot() {
384
+ return (await this.runner.screenshot?.().catch(() => Buffer.alloc(0))) ?? Buffer.alloc(0);
385
+ }
386
+ /** Per-driver structured audit data merged into CrawlResult.extra (counts only — no secrets). */
387
+ auditExtra() {
388
+ return {
389
+ nativeAudit: {
390
+ appId: this.appId,
391
+ deeplinksDeclared: this.deeplinkAnchors.length,
392
+ controlsExercised: this.exercised,
393
+ // Honest: the web SSRF/write-egress gate is NOT enforced on native v1 (Maestro can't proxy).
394
+ egressEnforced: false,
395
+ },
396
+ deviceEvidence: this.deviceEvidence,
397
+ nativeReadiness: this.nativeReadiness,
398
+ };
399
+ }
400
+ }
401
+ function errMessage(e) {
402
+ return (e instanceof Error ? e.message : String(e)).slice(0, 160);
403
+ }
404
+ /** Walk a view's visible text for the first error-canary match. */
405
+ function firstErrorText(view) {
406
+ const stack = [view.root];
407
+ while (stack.length) {
408
+ const n = stack.pop();
409
+ const text = (n.text ?? "").trim();
410
+ if (text && NATIVE_ERROR_TEXT.test(text))
411
+ return text;
412
+ for (const c of n.children ?? [])
413
+ stack.push(c);
414
+ }
415
+ return undefined;
416
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"driver.js","sourceRoot":"","sources":["../../src/native/driver.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,164 @@
1
+ import { appleEvidenceGaps } from "./apple.js";
2
+ import { normalizeDeviceProfile } from "./device-profile.js";
3
+ import { samsungEvidence } from "./samsung.js";
4
+ const VALID_ARTIFACT_KINDS = new Set([
5
+ "screenshot",
6
+ "video",
7
+ "log",
8
+ "crash-log",
9
+ ]);
10
+ function isRecord(value) {
11
+ return typeof value === "object" && value !== null && !Array.isArray(value);
12
+ }
13
+ function isNonEmptyString(value) {
14
+ return typeof value === "string" && value.trim().length > 0;
15
+ }
16
+ function isArtifactKind(value) {
17
+ return typeof value === "string" && VALID_ARTIFACT_KINDS.has(value);
18
+ }
19
+ function hasPathTraversal(value) {
20
+ return value.replace(/\\/g, "/").split("/").includes("..");
21
+ }
22
+ function addValidationIssue(errors, code, field, message) {
23
+ errors.push({ code, field, message });
24
+ }
25
+ function validateEvidenceArtifacts(errors, artifactsValue) {
26
+ const artifacts = Array.isArray(artifactsValue) ? artifactsValue : [];
27
+ for (let index = 0; index < artifacts.length; index++) {
28
+ const artifact = artifacts[index];
29
+ const field = `evidenceArtifacts[${index}]`;
30
+ if (!isRecord(artifact)) {
31
+ addValidationIssue(errors, "evidence-artifact-kind-invalid", `${field}.kind`, "Evidence artifact must be an object");
32
+ continue;
33
+ }
34
+ if (!isArtifactKind(artifact.kind)) {
35
+ addValidationIssue(errors, "evidence-artifact-kind-invalid", `${field}.kind`, "Evidence artifact kind must be screenshot, video, log, or crash-log");
36
+ }
37
+ if (!isNonEmptyString(artifact.path)) {
38
+ addValidationIssue(errors, "evidence-artifact-path-missing", `${field}.path`, "Evidence artifact path must be non-empty");
39
+ }
40
+ else if (hasPathTraversal(artifact.path)) {
41
+ addValidationIssue(errors, "evidence-artifact-path-traversal", `${field}.path`, "Evidence artifact path must not contain '..'");
42
+ }
43
+ }
44
+ }
45
+ function validateEgressProxy(errors, proxy) {
46
+ if (proxy?.verified !== true)
47
+ return;
48
+ if (!isNonEmptyString(proxy.provider)) {
49
+ addValidationIssue(errors, "egress-proxy-provider-missing", "egressProxy.provider", "Verified proxy evidence requires a provider");
50
+ }
51
+ if (!isNonEmptyString(proxy.command)) {
52
+ addValidationIssue(errors, "egress-proxy-command-missing", "egressProxy.command", "Verified proxy evidence requires the proxy command");
53
+ }
54
+ if (!isNonEmptyString(proxy.artifactPath)) {
55
+ addValidationIssue(errors, "egress-proxy-artifact-missing", "egressProxy.artifactPath", "Verified proxy evidence requires an artifact path");
56
+ }
57
+ }
58
+ export function validateNativeDeviceProfile(input) {
59
+ const profile = normalizeDeviceProfile(input);
60
+ const errors = [];
61
+ if (!isNonEmptyString(profile.appId)) {
62
+ addValidationIssue(errors, "app-id-missing", "appId", "Native evidence requires a non-empty app id");
63
+ }
64
+ if (profile.platform !== "android" && profile.platform !== "ios") {
65
+ addValidationIssue(errors, "platform-invalid", "platform", "Native evidence platform must be android or ios");
66
+ }
67
+ validateEvidenceArtifacts(errors, profile.evidenceArtifacts);
68
+ if (profile.providerRunId !== undefined && !isNonEmptyString(profile.provider)) {
69
+ addValidationIssue(errors, "provider-required-for-run-id", "provider", "Provider run id requires provider metadata");
70
+ }
71
+ validateEgressProxy(errors, profile.egressProxy);
72
+ return { errors };
73
+ }
74
+ function validEvidenceArtifacts(profile) {
75
+ const artifacts = Array.isArray(profile.evidenceArtifacts) ? profile.evidenceArtifacts : [];
76
+ const out = [];
77
+ for (const artifact of artifacts) {
78
+ if (!isRecord(artifact))
79
+ continue;
80
+ if (!isArtifactKind(artifact.kind))
81
+ continue;
82
+ if (!isNonEmptyString(artifact.path))
83
+ continue;
84
+ if (hasPathTraversal(artifact.path))
85
+ continue;
86
+ out.push({ kind: artifact.kind, path: artifact.path });
87
+ }
88
+ return out;
89
+ }
90
+ function pathsForKind(artifacts, kind) {
91
+ const out = [];
92
+ for (const artifact of artifacts) {
93
+ if (artifact.kind === kind)
94
+ out.push(artifact.path);
95
+ }
96
+ return out;
97
+ }
98
+ function hasCompleteNativeEgressProxyEvidence(proxy) {
99
+ return proxy?.verified === true && Boolean(proxy.provider && proxy.command && proxy.artifactPath);
100
+ }
101
+ function nativeEgressGap(profile) {
102
+ if (hasCompleteNativeEgressProxyEvidence(profile.egressProxy))
103
+ return undefined;
104
+ if (profile.egressProxy?.verified === true) {
105
+ return {
106
+ kind: "native-egress-unenforced",
107
+ severity: "blocker-for-exhaustive",
108
+ scope: profile.platform,
109
+ message: "Native network egress proxy was marked verified, but verified proxy evidence is incomplete; provide provider, command, and artifact",
110
+ };
111
+ }
112
+ return {
113
+ kind: "native-egress-unenforced",
114
+ severity: "blocker-for-exhaustive",
115
+ scope: profile.platform,
116
+ message: "Native network egress is not enforced by the Maestro runner without a verified device-level proxy",
117
+ };
118
+ }
119
+ export function buildDeviceEvidence(input) {
120
+ const profile = normalizeDeviceProfile(input);
121
+ const validation = validateNativeDeviceProfile(profile);
122
+ const samsung = samsungEvidence(profile);
123
+ const egressGap = nativeEgressGap(profile);
124
+ const validationGaps = validation.errors.length > 0
125
+ ? [
126
+ {
127
+ kind: "native-evidence-invalid",
128
+ severity: "blocker-for-exhaustive",
129
+ scope: profile.platform,
130
+ message: `Native evidence validation failed: ${validation.errors.map((error) => error.code).join(", ")}`,
131
+ },
132
+ ]
133
+ : [];
134
+ const gaps = [
135
+ ...(egressGap ? [egressGap] : []),
136
+ ...validationGaps,
137
+ ...appleEvidenceGaps(profile),
138
+ ...samsung.gaps,
139
+ ];
140
+ const evidenceArtifacts = validEvidenceArtifacts(profile);
141
+ return {
142
+ platform: profile.platform,
143
+ appId: profile.appId,
144
+ ...(profile.deviceId !== undefined ? { deviceId: profile.deviceId } : {}),
145
+ ...(profile.deviceClass !== undefined ? { deviceClass: profile.deviceClass } : {}),
146
+ ...(profile.model !== undefined ? { model: profile.model } : {}),
147
+ ...(profile.osVersion !== undefined ? { osVersion: profile.osVersion } : {}),
148
+ ...(profile.androidVersion !== undefined ? { androidVersion: profile.androidVersion } : {}),
149
+ ...(profile.appVersion !== undefined ? { appVersion: profile.appVersion } : {}),
150
+ ...(profile.provider !== undefined ? { provider: profile.provider } : {}),
151
+ ...(profile.providerRunId !== undefined ? { providerRunId: profile.providerRunId } : {}),
152
+ ...(profile.command !== undefined ? { command: profile.command } : {}),
153
+ ...(profile.verificationCommand !== undefined ? { verificationCommand: profile.verificationCommand } : {}),
154
+ evidenceArtifacts: evidenceArtifacts.map((artifact) => ({ ...artifact })),
155
+ screenshots: pathsForKind(evidenceArtifacts, "screenshot"),
156
+ videos: pathsForKind(evidenceArtifacts, "video"),
157
+ logs: pathsForKind(evidenceArtifacts, "log"),
158
+ crashLogs: pathsForKind(evidenceArtifacts, "crash-log"),
159
+ ...(profile.egressProxy !== undefined ? { egressProxy: { ...profile.egressProxy } } : {}),
160
+ validationErrors: validation.errors.map((error) => ({ ...error })),
161
+ gaps,
162
+ ...(samsung.samsung !== undefined ? { samsung: samsung.samsung } : {}),
163
+ };
164
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evidence.js","sourceRoot":"","sources":["../../src/native/evidence.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ // Composition helper for the native target — builds the DEFAULT (shell-out) MaestroRunner from
2
+ // the resolved RunConfig. Kept SEPARATE from the driver (which takes an already-built runner) so
3
+ // the driver stays hermetically testable; only the CLI composition root touches this. Building the
4
+ // runner does NOT touch a device — the runner only shells out to `maestro`/`adb` when its methods
5
+ // are actually called at crawl time (which REQUIRES maestro + a booted emulator/simulator).
6
+ import { DefaultMaestroRunner } from "./maestro.js";
7
+ export function buildDefaultRunner(cfg) {
8
+ return new DefaultMaestroRunner({ appId: cfg.appId, platform: cfg.platform, device: cfg.device });
9
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load.js","sourceRoot":"","sources":["../../src/native/load.ts"],"names":[],"mappings":"AAAA,+FAA+F;AAC/F,iGAAiG;AACjG,mGAAmG;AACnG,kGAAkG;AAClG,4FAA4F;AAC5F,OAAO,EAAE,oBAAoB,EAAsB,MAAM,cAAc,CAAC;AAQxE,MAAM,UAAU,kBAAkB,CAAC,GAAuB;IACxD,OAAO,IAAI,oBAAoB,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;AACpG,CAAC"}