prowl-tools 0.1.7 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -1
- package/dist/{chunk-R7NUH44M.js → chunk-CWLRDV5P.js} +290 -399
- package/dist/chunk-CWLRDV5P.js.map +1 -0
- package/dist/{chunk-O3OUTZ2P.js → chunk-JFJQNJSJ.js} +394 -2
- package/dist/chunk-JFJQNJSJ.js.map +1 -0
- package/dist/index.cjs +930 -641
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/lib.cjs +626 -346
- package/dist/lib.cjs.map +1 -1
- package/dist/lib.d.cts +71 -17
- package/dist/lib.d.ts +71 -17
- package/dist/lib.js +3 -3
- package/dist/{loader-X37URHUV.js → loader-JTHA4BYG.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-O3OUTZ2P.js.map +0 -1
- package/dist/chunk-R7NUH44M.js.map +0 -1
- /package/dist/{loader-X37URHUV.js.map → loader-JTHA4BYG.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/config/target.ts","../src/browser/mac-driver.ts","../src/browser/macdriver-release.ts","../src/browser/mac-helper.ts","../src/analyzer/xml.ts","../src/selector/native.ts","../src/browser/android-driver.ts","../src/browser/touch-gestures.ts","../src/browser/android-adb.ts","../src/browser/android-agent.ts","../src/browser/android-helper.ts","../src/browser/ios-driver.ts","../src/browser/ios-simctl.ts","../src/browser/ios-agent.ts","../src/browser/ios-helper.ts","../src/runner/healing.ts","../src/runner/history.ts","../src/runner/index.ts","../src/browser/playwright-driver.ts","../src/runner/steps.ts","../src/runner/policy.ts","../src/generator/ai.ts","../src/runner/assertions.ts","../src/runner/tracing.ts","../src/reporter/result.ts","../src/reporter/summary.ts","../src/reporter/junit.ts","../src/reporter/index.ts","../src/utils/timestamp.ts","../src/runner/flaky.ts","../src/backlog/fingerprint.ts","../src/runner/clustering.ts","../src/backlog/index.ts","../src/backlog/parse.ts","../src/backlog/write.ts","../src/runner/suite.ts","../src/reporter/ci-summary.ts","../src/utils/concurrency.ts","../src/analyzer/index.ts","../src/analyzer/mac.ts","../src/analyzer/android.ts","../src/analyzer/ios.ts","../src/generator/index.ts","../src/browser/engines.ts","../src/generator/prompt.ts","../src/browser/macdriver-install.ts"],"sourcesContent":["/**\n * PROWL-048 / ARCH-002 — target ⇄ step compatibility.\n *\n * The runtime capability gate in the step runner already rejects steps a driver\n * cannot honor. This adds a friendlier, earlier validation-time rejection: when\n * the selected target is macOS, hunts using web-only steps fail fast with a\n * clear message before anything launches.\n */\nimport type { Step, Target } from \"../types/index.js\";\nimport { execFileSync } from \"node:child_process\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\n\n/**\n * Step types that only make sense on the web target. Everything else\n * (`click`, `fill`, `type`, `press`, `wait`, `assert visible/notVisible`,\n * `screenshot`, `assertScreenshot`, `repeat`, `runHunt`, `if`, `copyText`,\n * `hover`, `scrollTo`, `waitForSelector`) is portable across targets. The\n * directional `scroll` step is the one per-target exception — see\n * {@link MACOS_UNSUPPORTED_STEP_TYPES}.\n */\nexport const WEB_ONLY_STEP_TYPES: ReadonlySet<string> = new Set([\n \"navigate\",\n \"waitForUrl\",\n \"waitForNetworkIdle\",\n \"mockRoute\",\n \"unmockRoute\",\n \"evalScript\",\n \"runScript\",\n \"onDialog\",\n \"select\",\n \"selectOption\",\n \"setInputFiles\",\n \"waitForDownload\"\n]);\n\n/**\n * Steps rejected on the macOS target specifically (beyond the web-only set).\n *\n * The scroll verbs are asymmetric across targets, so gate them per verb:\n * - `scroll` (directional): a synthesized touch swipe on iOS/Android\n * (PROWL-080) with no AX equivalent, so it is UNSUPPORTED on macOS. It is\n * not web-only either (web runs `window.scrollBy`), hence this macOS-only\n * rejection rather than {@link WEB_ONLY_STEP_TYPES}.\n * - `scrollTo` (by selector): supported on every target and therefore NOT\n * listed here — macOS resolves it to AXScrollToVisible (a real, shipped AX\n * capability), while iOS/Android run a bounded swipe-loop. Web scrolls the\n * element into view.\n */\nexport const MACOS_UNSUPPORTED_STEP_TYPES: ReadonlySet<string> = new Set([\"scroll\"]);\n\n/** The reason a step is unsupported on macOS specifically, or null. */\nexport function macosUnsupportedReason(step: Step): string | null {\n for (const type of MACOS_UNSUPPORTED_STEP_TYPES) {\n if (type in step) {\n return type;\n }\n }\n return null;\n}\n\n/** The web-only reason a step is unsupported on a non-web target, or null if portable. */\nexport function webOnlyReason(step: Step): string | null {\n for (const type of WEB_ONLY_STEP_TYPES) {\n if (type in step) {\n return type;\n }\n }\n // URL assertions are web-only; visible/notVisible assertions are portable.\n if (\"assert\" in step) {\n const assertion = step.assert;\n if (assertion.urlIncludes !== undefined || assertion.urlEquals !== undefined) {\n return \"assert (url)\";\n }\n }\n return null;\n}\n\n/**\n * Assertion types that are portable to native (non-web) targets: they resolve a\n * selector against the driver, which every target supports. Everything else in\n * the {@link Assertion} union (`urlIncludes`, `urlEquals`, `noConsoleErrors`,\n * `noNetworkErrors`) is web-only — see {@link WEB_ONLY_ASSERTION_TYPES}.\n */\nexport const NATIVE_APPLICABLE_ASSERTION_TYPES: ReadonlySet<string> = new Set([\n \"selectorExists\",\n \"selectorNotExists\"\n]);\n\n/**\n * Assertion types that only have meaning on the web target (a URL, or the\n * browser console / network layer). On a native target these are reported as\n * `skipped (web-only)` rather than evaluated — see the native run path.\n */\nexport const WEB_ONLY_ASSERTION_TYPES: ReadonlySet<string> = new Set([\n \"urlIncludes\",\n \"urlEquals\",\n \"noConsoleErrors\",\n \"noNetworkErrors\"\n]);\n\n/** Human-facing label for a native (non-web) target, used in messages. */\nexport function nativeTargetLabel(target: Target[\"type\"]): string {\n if (target === \"android\") {\n return \"Android\";\n }\n if (target === \"ios\") {\n return \"iOS\";\n }\n return \"macOS\";\n}\n\n/**\n * Throw if any step in `steps` (recursing into `if`/`repeat` bodies) is not\n * supported by `target`. `runHunt` references are validated when the referenced\n * hunt itself runs. No-op for the web target; every native target rejects the\n * same web-only step vocabulary, and macOS additionally rejects the directional\n * `scroll` swipe (no AX equivalent) — but NOT `scrollTo`, which macOS maps to\n * AXScrollToVisible just as iOS/Android map it to a swipe loop.\n */\nexport function assertStepsSupportedByTarget(steps: Step[], target: Target[\"type\"]): void {\n if (target === \"web\") {\n return;\n }\n const label = nativeTargetLabel(target);\n for (const step of steps) {\n const reason = webOnlyReason(step);\n if (reason) {\n throw new Error(\n `Step \"${reason}\" is not supported by the ${label} target. It is web-only; ` +\n \"use a portable step (click, fill, type, press, wait, assert visible, screenshot, etc.).\"\n );\n }\n if (target === \"macos\") {\n const macReason = macosUnsupportedReason(step);\n if (macReason) {\n throw new Error(\n `Step \"${macReason}\" is not supported by the macOS target. Directional scroll is a ` +\n \"touch swipe available on the iOS and Android targets; there is no macOS accessibility \" +\n \"equivalent (use scrollTo to bring a specific element into view instead).\"\n );\n }\n }\n if (\"if\" in step) {\n assertStepsSupportedByTarget(step.if.then, target);\n if (step.if.else) {\n assertStepsSupportedByTarget(step.if.else, target);\n }\n }\n if (\"repeat\" in step) {\n assertStepsSupportedByTarget(step.repeat.steps, target);\n }\n }\n}\n\n/**\n * Reject hunt-level assertions declared on a **sub-hunt** invoked via `runHunt`\n * on a native target. Hunt-level assertions are a top-level concept — a sub-hunt\n * contributes only its steps, and its `assertions:` block is never evaluated on\n * any target (the web path silently ignores it). On a native target that silent\n * drop is surfaced as a hard error so a factored-out `selectorExists` assertion\n * cannot vanish unnoticed. The top-level native run path does NOT use this: it\n * evaluates the applicable subset and reports web-only types as skipped (see\n * {@link NATIVE_APPLICABLE_ASSERTION_TYPES} / {@link WEB_ONLY_ASSERTION_TYPES}).\n */\nexport function assertHuntAssertionsSupportedByTarget(\n assertions: unknown[] | undefined,\n target: Target[\"type\"]\n): void {\n if (target === \"web\" || !assertions || assertions.length === 0) {\n return;\n }\n throw new Error(\n `Hunt-level assertions are not supported by the ${nativeTargetLabel(target)} target. ` +\n \"Use inline assert visible/notVisible steps instead.\"\n );\n}\n\nfunction trimTrailingPathSeparators(value: string): string {\n return value.replace(/[\\\\/]+$/g, \"\");\n}\n\nfunction looksLikeAppBundlePath(app: string): boolean {\n const trimmed = trimTrailingPathSeparators(app);\n return trimmed.includes(\"/\") || trimmed.toLowerCase().endsWith(\".app\");\n}\n\n/**\n * Whether an iOS `target.app` value is a `.app` bundle path rather than a bundle\n * id. iOS bundle ids commonly end in `.app`/`.App` (e.g. `com.company.app`), which\n * the macOS heuristic would misclassify, so a bare `*.app` value counts as a path\n * only when it actually exists on disk as a directory. A path separator is always\n * decisive.\n */\nexport function looksLikeIosAppPath(app: string): boolean {\n const trimmed = trimTrailingPathSeparators(app);\n if (trimmed.includes(\"/\") || trimmed.includes(\"\\\\\")) {\n return true;\n }\n if (trimmed.toLowerCase().endsWith(\".app\")) {\n try {\n return fs.statSync(normalizeAppPath(app)).isDirectory();\n } catch {\n return false;\n }\n }\n return false;\n}\n\nfunction normalizeAppPath(app: string): string {\n return path.resolve(trimTrailingPathSeparators(app));\n}\n\nfunction parseBundleIdentifier(plist: string): string | null {\n const match = /<key>\\s*CFBundleIdentifier\\s*<\\/key>\\s*<string>\\s*([^<]+?)\\s*<\\/string>/s.exec(plist);\n return match?.[1]?.trim() || null;\n}\n\n/**\n * Read `CFBundleIdentifier` from an app bundle's `Info.plist`. macOS `.app`\n * bundles keep it under `Contents/`, while iOS simulator `.app` bundles keep it\n * at the bundle root, so `plistSubPath` names the plist's location within the\n * bundle. XML plists are parsed directly; binary plists fall back to `plutil`.\n */\nfunction readBundleIdentifier(appPath: string, ...plistSubPath: string[]): string | null {\n const infoPlistPath = path.join(normalizeAppPath(appPath), ...plistSubPath);\n if (!fs.existsSync(infoPlistPath)) {\n return null;\n }\n\n try {\n const parsed = parseBundleIdentifier(fs.readFileSync(infoPlistPath, \"utf-8\"));\n if (parsed) {\n return parsed;\n }\n } catch {\n // Fall through to plutil for binary plists on macOS.\n }\n\n if (process.platform !== \"darwin\" || !fs.existsSync(\"/usr/bin/plutil\")) {\n return null;\n }\n\n try {\n const output = execFileSync(\n \"/usr/bin/plutil\",\n [\"-extract\", \"CFBundleIdentifier\", \"raw\", \"-o\", \"-\", infoPlistPath],\n { encoding: \"utf-8\", stdio: [\"ignore\", \"pipe\", \"ignore\"], timeout: 1000 }\n );\n return output.trim() || null;\n } catch {\n return null;\n }\n}\n\n/** Read the bundle id from a macOS `.app` (`Contents/Info.plist`), or null. */\nexport function readMacosBundleIdentifier(appPath: string): string | null {\n return readBundleIdentifier(appPath, \"Contents\", \"Info.plist\");\n}\n\n/** Read the bundle id from an iOS `.app` (root `Info.plist`), or null. */\nexport function readIosBundleIdentifier(appPath: string): string | null {\n return readBundleIdentifier(appPath, \"Info.plist\");\n}\n\n/**\n * Accepted identities for a native app-bundle target: the raw value, and for a\n * `.app` path also its trimmed/normalized path, bundle name, and\n * `CFBundleIdentifier`. `readBundleId` locates the plist per platform.\n */\nfunction appBundleAllowedIdentities(\n app: string,\n readBundleId: (appPath: string) => string | null,\n isAppPath: (app: string) => boolean = looksLikeAppBundlePath\n): string[] {\n const identities = new Set<string>([app]);\n\n if (isAppPath(app)) {\n const normalizedPath = normalizeAppPath(app);\n identities.add(trimTrailingPathSeparators(app));\n identities.add(normalizedPath);\n\n const bundleName = path.basename(normalizedPath).replace(/\\.app$/i, \"\");\n if (bundleName) {\n identities.add(bundleName);\n }\n\n const bundleId = readBundleId(app);\n if (bundleId) {\n identities.add(bundleId);\n }\n }\n\n return [...identities];\n}\n\nexport function macosAppAllowedIdentities(app: string): string[] {\n return appBundleAllowedIdentities(app, readMacosBundleIdentifier);\n}\n\n/**\n * Accepted identities for an iOS target app. A bare bundle id matches itself; a\n * `.app` path is authorized by its path, bundle name, or the `CFBundleIdentifier`\n * read from the bundle's root `Info.plist`.\n */\nexport function iosAppAllowedIdentities(app: string): string[] {\n return appBundleAllowedIdentities(app, readIosBundleIdentifier, looksLikeIosAppPath);\n}\n\n/**\n * Native scope guardrail: if `allowedApps` is non-empty it must include an\n * accepted identity for the target app: bundle id, app bundle name, or `.app`\n * path. An empty list means the scope is unset and the target app is implicitly\n * allowed — mirroring how allowedDomains auto-includes the web target's host.\n */\nexport function assertTargetAppAllowed(allowedApps: string[], app: string): void {\n assertNativeAppAllowed(allowedApps, app, macosAppAllowedIdentities);\n}\n\n/**\n * iOS scope guardrail (PROWL-059): mirrors {@link assertTargetAppAllowed} but\n * resolves identities via {@link iosAppAllowedIdentities} (bundle id or `.app`\n * path with the id read from the bundle's root `Info.plist`).\n */\nexport function assertIosAppAllowed(allowedApps: string[], app: string): void {\n assertNativeAppAllowed(allowedApps, app, iosAppAllowedIdentities);\n}\n\nfunction looksLikeApkPath(app: string): boolean {\n const trimmed = trimTrailingPathSeparators(app);\n return trimmed.includes(\"/\") || trimmed.includes(\"\\\\\") || trimmed.toLowerCase().endsWith(\".apk\");\n}\n\nfunction normalizeAndroidApkPath(app: string): string {\n const resolved = path.resolve(trimTrailingPathSeparators(app));\n try {\n return fs.realpathSync.native(resolved);\n } catch {\n return resolved;\n }\n}\n\n/**\n * Accepted identities for an Android target app. A bare package name matches\n * itself. An `.apk` path is authorized only by its canonical full path; pass the\n * resolved package name when validating an APK after `aapt` resolution so\n * package-ID allowlists can authorize it before install.\n */\nexport function androidAppAllowedIdentities(app: string, resolvedPackage?: string): string[] {\n if (looksLikeApkPath(app)) {\n return resolvedPackage\n ? [normalizeAndroidApkPath(app), resolvedPackage]\n : [normalizeAndroidApkPath(app)];\n }\n return [app];\n}\n\n/**\n * Android scope guardrail (PROWL-058): mirrors {@link assertTargetAppAllowed} but\n * resolves identities via {@link androidAppAllowedIdentities} (package name or\n * canonical APK path) rather than macOS bundle identities.\n */\nexport function assertAndroidAppAllowed(\n allowedApps: string[],\n app: string,\n resolvedPackage?: string\n): void {\n assertNativeAppAllowed(\n allowedApps,\n app,\n (value) => androidAppAllowedIdentities(value, value === app ? resolvedPackage : undefined)\n );\n}\n\nfunction assertNativeAppAllowed(\n allowedApps: string[],\n app: string,\n resolveIdentities: (value: string) => string[]\n): void {\n if (allowedApps.length === 0) {\n return;\n }\n const allowedIdentities = new Set(allowedApps.flatMap((allowedApp) => resolveIdentities(allowedApp)));\n if (resolveIdentities(app).some((identity) => allowedIdentities.has(identity))) {\n return;\n }\n throw new Error(\n `Target app \"${app}\" is not in guardrails.allowedApps (${allowedApps.join(\", \")}).`\n );\n}\n","/**\n * PROWL-048 / ARCH-002 — macOS native implementation of {@link SessionDriver}.\n *\n * `MacDriver` drives a native macOS app through the `prowl-macdriver` Swift\n * helper (Accessibility / AXUIElement) over a long-lived JSON-over-stdio\n * transport ({@link MacHelperClient}). It implements only the portable subset of\n * the driver surface — its `capabilities` set is honest: `query`, `interact`,\n * `wait`, `screenshot`. Web-only verbs (navigation, network, dialogs, files,\n * downloads, script evaluation) are unsupported stubs; the runner never reaches\n * them because both the target step-compatibility check and the runtime\n * capability gate reject web-only steps for the macOS target.\n *\n * Selector dialect (parsed here, so the Swift helper only matches attributes):\n * id=openSettings → accessibility identifier\n * role=button[name=\"Save\"] → AX role + accessible name\n * label=\"Email\" → exact accessibility label\n * text=\"Save\" | Save → title/description/value substring\n * statusItem → open the app's menu bar status-item menu\n * menu=Preferences… → open the status-item menu and click an item\n */\nimport type {\n DialogAction,\n DriverCapability,\n DriverDownload,\n DriverResponse,\n DriverRoute,\n NavigateOptions,\n SessionDriver\n} from \"./driver.js\";\n\n/** A structured accessibility query understood by the Swift helper. */\nexport type MacQuery =\n | { by: \"id\"; value: string }\n | { by: \"role\"; role: string; name?: string }\n | { by: \"text\"; value: string }\n | { by: \"label\"; value: string }\n | { by: \"focused\" };\n\n/** The transport MacDriver talks to: one request → one response, matched by id. */\nexport interface MacHelperClient {\n /** Send a command; resolve with its `result` payload or reject with its error. */\n request(cmd: string, params?: Record<string, unknown>): Promise<Record<string, unknown>>;\n /** Shut the helper down. */\n close(): Promise<void>;\n}\n\nexport type MacDriverOptions = {\n /** Bundle id / app label, used only for the informational `currentUrl()` value. */\n appLabel?: string;\n};\n\nconst MAC_CAPABILITIES: ReadonlySet<DriverCapability> = new Set<DriverCapability>([\n \"query\",\n \"interact\",\n \"wait\",\n \"screenshot\"\n]);\n\nconst STATUS_ITEM_SELECTOR = \"statusitem\";\nconst MENU_PREFIX = \"menu=\";\n\nfunction unquote(value: string): string {\n const trimmed = value.trim();\n const first = trimmed[0];\n if ((first === '\"' || first === \"'\") && trimmed.endsWith(first) && trimmed.length >= 2) {\n return trimmed.slice(1, -1);\n }\n return trimmed;\n}\n\n/** Parse a Prowl selector string into a {@link MacQuery}. Bare text matches by text. */\nexport function parseMacSelector(selector: string): MacQuery {\n const trimmed = selector.trim();\n\n const idMatch = /^id=(.+)$/s.exec(trimmed);\n if (idMatch) {\n return { by: \"id\", value: unquote(idMatch[1]) };\n }\n\n const roleMatch = /^role=([A-Za-z][\\w-]*)(?:\\[name=(.+)\\])?$/s.exec(trimmed);\n if (roleMatch) {\n const name = roleMatch[2] !== undefined ? unquote(roleMatch[2]) : undefined;\n return name !== undefined && name.length > 0\n ? { by: \"role\", role: roleMatch[1], name }\n : { by: \"role\", role: roleMatch[1] };\n }\n\n const labelMatch = /^label=(.+)$/s.exec(trimmed);\n if (labelMatch) {\n return { by: \"label\", value: unquote(labelMatch[1]) };\n }\n\n const textMatch = /^text=(.+)$/s.exec(trimmed);\n if (textMatch) {\n return { by: \"text\", value: unquote(textMatch[1]) };\n }\n\n return { by: \"text\", value: trimmed };\n}\n\n/** The literal text a `text=` selector matches, else null (mirrors the web driver). */\nexport function unwrapMacTextSelector(selector: string): string | null {\n const trimmed = selector.trim();\n if (!trimmed.startsWith(\"text=\")) {\n return null;\n }\n return unquote(trimmed.slice(5));\n}\n\nfunction num(value: unknown): number {\n return typeof value === \"number\" ? value : Number(value ?? 0);\n}\n\n/** Wrap a live {@link MacHelperClient} as a {@link SessionDriver}. */\nexport function createMacDriver(client: MacHelperClient, options: MacDriverOptions = {}): SessionDriver {\n const unsupported = (verb: string): Error => new Error(`${verb} is not supported by the macOS target`);\n const rejectUnsupported = (verb: string): Promise<never> => Promise.reject(unsupported(verb));\n\n async function query(cmd: string, selector: string, extra?: Record<string, unknown>): Promise<Record<string, unknown>> {\n return client.request(cmd, { query: parseMacSelector(selector), ...extra });\n }\n\n async function clickSelector(selector: string): Promise<void> {\n const trimmed = selector.trim();\n if (trimmed.toLowerCase() === STATUS_ITEM_SELECTOR) {\n await client.request(\"openMenu\");\n return;\n }\n if (trimmed.toLowerCase().startsWith(MENU_PREFIX)) {\n await client.request(\"clickMenu\", { title: trimmed.slice(MENU_PREFIX.length).trim() });\n return;\n }\n await query(\"click\", selector);\n }\n\n async function fillSelector(selector: string, value: string): Promise<void> {\n if (selector.trim() === \":focus\") {\n await client.request(\"fill\", { query: { by: \"focused\" } as MacQuery, value });\n return;\n }\n await query(\"fill\", selector, { value });\n }\n\n return {\n capabilities: MAC_CAPABILITIES,\n\n // navigation -----------------------------------------------------------\n goto(_url: string, _options?: NavigateOptions): Promise<void> {\n return rejectUnsupported(\"navigate\");\n },\n currentUrl(): string {\n return `macos:${options.appLabel ?? \"\"}`;\n },\n\n // queries --------------------------------------------------------------\n async count(selector: string): Promise<number> {\n const result = await query(\"count\", selector);\n return num(result.count);\n },\n async textContent(selector: string): Promise<string | null> {\n const result = await query(\"text\", selector);\n return result.text === undefined || result.text === null ? null : String(result.text);\n },\n\n // interactions ---------------------------------------------------------\n click: clickSelector,\n clickFirst: clickSelector,\n fill: fillSelector,\n fillFirst: fillSelector,\n async press(selector: string, key: string): Promise<void> {\n await query(\"press\", selector, { key });\n },\n selectOption(): Promise<void> {\n return rejectUnsupported(\"select\");\n },\n selectOptionFirst(): Promise<void> {\n return rejectUnsupported(\"select\");\n },\n async hover(selector: string): Promise<void> {\n await query(\"hover\", selector);\n },\n scroll(): Promise<void> {\n // Directional `scroll` is a synthesized touch swipe (a mobile concept);\n // there is no AX equivalent, so it is unsupported on macOS (PROWL-080).\n // `scrollTo` below is different — it maps to a real AX capability.\n return rejectUnsupported(\"scroll\");\n },\n async scrollIntoView(selector: string): Promise<void> {\n // macOS `scrollTo` is a shipped capability: the Swift helper resolves the\n // element and calls AXScrollToVisible (Commands.swift `scrollTo`). This is\n // NOT a touch swipe — unlike the mobile drivers' swipe-loop scrollTo.\n await query(\"scrollTo\", selector);\n },\n setInputFiles(): Promise<void> {\n return rejectUnsupported(\"setInputFiles\");\n },\n\n // semantic locators ----------------------------------------------------\n async countByRole(role: string, name: string): Promise<number> {\n const result = await client.request(\"count\", { query: { by: \"role\", role, name } });\n return num(result.count);\n },\n async clickFirstByRole(role: string, name: string): Promise<void> {\n await client.request(\"click\", { query: { by: \"role\", role, name } });\n },\n async countByLabel(label: string): Promise<number> {\n const result = await client.request(\"count\", { query: { by: \"label\", value: label } });\n return num(result.count);\n },\n async fillFirstByLabel(label: string, value: string): Promise<void> {\n await client.request(\"fill\", { query: { by: \"label\", value: label }, value });\n },\n selectOptionFirstByLabel(): Promise<void> {\n return rejectUnsupported(\"select\");\n },\n\n // waiting --------------------------------------------------------------\n async waitForSelector(selector: string, waitOptions?: { timeout?: number }): Promise<void> {\n const extra = waitOptions?.timeout !== undefined ? { timeout: waitOptions.timeout / 1000 } : undefined;\n await query(\"waitFor\", selector, extra);\n },\n waitForUrl(): Promise<void> {\n return rejectUnsupported(\"waitForUrl\");\n },\n waitForNetworkIdle(): Promise<void> {\n return rejectUnsupported(\"waitForNetworkIdle\");\n },\n\n // scripting & artifacts ------------------------------------------------\n evaluate<R = unknown>(): Promise<R> {\n return rejectUnsupported(\"evalScript\") as Promise<R>;\n },\n async screenshot(screenshotOptions: { path: string; fullPage?: boolean }): Promise<void> {\n // The helper captures the app's frontmost window (window-scoped baselines);\n // `fullPage` has no analogue on a native window and is intentionally ignored.\n // When it can't find a capturable window it falls back to a full-screen grab\n // and reports a `warning` — surface it rather than swallowing it, so a flaky\n // full-screen baseline isn't produced silently.\n const result = await client.request(\"screenshot\", { path: screenshotOptions.path });\n const warning = result.warning;\n if (typeof warning === \"string\" && warning.length > 0) {\n console.warn(`macOS screenshot fell back to full screen: ${warning}`);\n }\n },\n\n // network / dialogs / downloads (all web-only) -------------------------\n onResponse(_handler: (response: DriverResponse) => void): void {\n throw unsupported(\"onResponse\");\n },\n route(_url: string, _handler: (route: DriverRoute) => void | Promise<void>): Promise<void> {\n return rejectUnsupported(\"mockRoute\");\n },\n unroute(): Promise<void> {\n return rejectUnsupported(\"unmockRoute\");\n },\n onDialog(_action: DialogAction): void {\n throw unsupported(\"onDialog\");\n },\n waitForDownloadEvent(): Promise<DriverDownload> {\n return rejectUnsupported(\"waitForDownload\") as Promise<DriverDownload>;\n },\n\n parseTextSelector(selector: string): string | null {\n return unwrapMacTextSelector(selector);\n }\n };\n}\n","/**\n * PROWL-074 / PROWL-052 — release coordinates for the prebuilt, signed\n * `prowl-macdriver` helper.\n *\n * The CLI pins ONE helper version (`MACDRIVER_VERSION`) so a given CLI build\n * always fetches a known-good binary. To ship a new helper: bump the constant\n * here (and `macdriverVersion` in `macdriver/Sources/prowl-macdriver/DriverCLI.swift`),\n * then cut a matching `macdriver-v<version>` GitHub Release — the\n * `.github/workflows/macdriver-release.yml` workflow builds, signs, notarizes,\n * and attaches the assets named below. See `macdriver/RELEASING.md`.\n *\n * This module is pure (no IO), so `mac-helper.ts` and the installer can both\n * depend on it without a cycle.\n */\nimport os from \"node:os\";\nimport path from \"node:path\";\n\n/** Basename of the helper executable, in the tarball and on disk. */\nexport const HELPER_BINARY = \"prowl-macdriver\";\n\n/** Pinned helper version. A CLI release always installs this exact version. */\nexport const MACDRIVER_VERSION = \"0.1.0\";\n\n/** GitHub `owner/repo` that hosts the helper releases. */\nexport const MACDRIVER_REPO = \"prowl-tools/prowl\";\n\n/** Code-signing identifier assigned to release builds. */\nexport const MACDRIVER_SIGNING_IDENTIFIER = \"tools.prowl.macdriver\";\n\n/** Developer ID Application common-name prefix expected on release builds. */\nexport const MACDRIVER_SIGNING_AUTHORITY_PREFIX = \"Developer ID Application: Genkei Labs\";\n\n/** Accepted helper release version token. */\nexport const MACDRIVER_VERSION_PATTERN =\n /^\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z][0-9A-Za-z.-]*)?(?:\\+[0-9A-Za-z][0-9A-Za-z.-]*)?$/;\n\n/** Validate the helper release version before using it in URLs or paths. */\nexport function validateMacdriverVersion(version: string): string {\n if (!MACDRIVER_VERSION_PATTERN.test(version)) {\n throw new Error(\n `Invalid prowl-macdriver version \"${version}\". Expected a release version like 0.1.0.`\n );\n }\n return version;\n}\n\n/** Git tag for a helper version — the release the workflow builds. */\nexport function macdriverReleaseTag(version: string = MACDRIVER_VERSION): string {\n return `macdriver-v${validateMacdriverVersion(version)}`;\n}\n\n/** Release asset file name for the universal (arm64 + x86_64) binary zip. */\nexport function macdriverAssetName(version: string = MACDRIVER_VERSION): string {\n return `prowl-macdriver-v${validateMacdriverVersion(version)}-universal.zip`;\n}\n\n/** Release asset file name for the SHA-256 checksum sidecar. */\nexport function macdriverChecksumName(version: string = MACDRIVER_VERSION): string {\n return `${macdriverAssetName(version)}.sha256`;\n}\n\n/** Download URL for a named asset of the pinned release (follows redirects). */\nexport function macdriverAssetUrl(assetName: string, version: string = MACDRIVER_VERSION): string {\n return `https://github.com/${MACDRIVER_REPO}/releases/download/${macdriverReleaseTag(version)}/${assetName}`;\n}\n\n/** Root of all user-level installs: `~/.prowl/macdriver`. */\nexport function macdriverInstallRoot(homedir: string = os.homedir()): string {\n return path.join(homedir, \".prowl\", \"macdriver\");\n}\n\n/** Directory that holds a specific installed version. */\nexport function macdriverVersionDir(\n version: string = MACDRIVER_VERSION,\n homedir: string = os.homedir()\n): string {\n const root = path.resolve(macdriverInstallRoot(homedir));\n const versionDir = path.resolve(root, validateMacdriverVersion(version));\n if (!versionDir.startsWith(root + path.sep)) {\n throw new Error(`Resolved prowl-macdriver version directory escaped install root: ${versionDir}`);\n }\n return versionDir;\n}\n\n/** Absolute path to the installed helper binary for a version. */\nexport function macdriverInstalledBinary(\n version: string = MACDRIVER_VERSION,\n homedir: string = os.homedir()\n): string {\n return path.join(macdriverVersionDir(version, homedir), HELPER_BINARY);\n}\n","/**\n * PROWL-048 / ARCH-002 — transport + launch/teardown for the macOS target.\n *\n * Spawns the `prowl-macdriver` Swift helper in `serve` mode and speaks its\n * newline-delimited JSON protocol (one request/response per line, matched by\n * id). The helper is resolved by {@link resolveHelperBinary}; when none is\n * found, resolution fails with a clear message pointing at\n * `prowl macdriver install` (with build-from-source as the contributor\n * fallback) rather than crashing.\n */\nimport { spawn, type ChildProcess } from \"node:child_process\";\nimport fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { createMacDriver, type MacHelperClient } from \"./mac-driver.js\";\nimport type { SessionDriver } from \"./driver.js\";\nimport { HELPER_BINARY, MACDRIVER_VERSION, macdriverInstalledBinary } from \"./macdriver-release.js\";\n\nexport { HELPER_BINARY };\n\n/**\n * Guidance shown when the helper can't be resolved. Leads with the\n * two-minute `prowl macdriver install` path; source build and the\n * `PROWL_MACDRIVER_BIN` override are the contributor fallbacks.\n */\nexport function macdriverBuildInstructions(): string {\n return (\n \"The macOS target needs the `prowl-macdriver` helper. Install the prebuilt, signed \" +\n \"binary (recommended):\\n\" +\n \" prowl macdriver install\\n\" +\n \"Contributors building from source can instead run:\\n\" +\n \" cd macdriver && swift build -c release\\n\" +\n \"or point Prowl at a prebuilt binary via the PROWL_MACDRIVER_BIN environment variable.\"\n );\n}\n\nfunction getPackageRoot(): string {\n let dir = path.dirname(fileURLToPath(import.meta.url));\n const root = path.parse(dir).root;\n while (dir !== root) {\n if (fs.existsSync(path.join(dir, \"package.json\"))) {\n return dir;\n }\n dir = path.dirname(dir);\n }\n return root;\n}\n\nexport type ResolveHelperOptions = {\n /** Home directory for the user-level install lookup (defaults to `os.homedir()`). */\n homedir?: string;\n};\n\n/**\n * Resolve the helper binary path. Search order (documented in the README's\n * macOS Target section):\n * 1. `PROWL_MACDRIVER_BIN` env override (absolute path to the binary);\n * 2. the user-level install of the pinned version at\n * `~/.prowl/macdriver/<MACDRIVER_VERSION>/prowl-macdriver`\n * (what `prowl macdriver install` writes);\n * 3. the contributor's repo-local source build under `macdriver/.build/`\n * (`release` then `debug`).\n * Throws with install-first guidance when none is found.\n */\nexport function resolveHelperBinary(\n env: NodeJS.ProcessEnv = process.env,\n options: ResolveHelperOptions = {}\n): string {\n const override = env.PROWL_MACDRIVER_BIN;\n if (override) {\n if (!fs.existsSync(override)) {\n throw new Error(\n `PROWL_MACDRIVER_BIN points at a missing file: ${override}\\n${macdriverBuildInstructions()}`\n );\n }\n return override;\n }\n\n // 2. User-level install of the pinned version (`prowl macdriver install`).\n const homedir = options.homedir ?? os.homedir();\n const userBinary = macdriverInstalledBinary(MACDRIVER_VERSION, homedir);\n if (fs.existsSync(userBinary)) {\n return userBinary;\n }\n\n // 3. Repo-local source build (contributor fallback).\n const root = getPackageRoot();\n const candidates = [\n path.join(root, \"macdriver\", \".build\", \"release\", HELPER_BINARY),\n path.join(root, \"macdriver\", \".build\", \"debug\", HELPER_BINARY)\n ];\n for (const candidate of candidates) {\n if (fs.existsSync(candidate)) {\n return candidate;\n }\n }\n throw new Error(`Could not find the ${HELPER_BINARY} helper binary.\\n${macdriverBuildInstructions()}`);\n}\n\ntype Pending = {\n cmd: string;\n resolve: (result: Record<string, unknown>) => void;\n reject: (error: Error) => void;\n timer: ReturnType<typeof setTimeout>;\n};\n\n/** Default per-request deadline for the helper transport. */\nexport const DEFAULT_REQUEST_TIMEOUT_MS = 30000;\n\nexport type SpawnMacHelperOptions = {\n /** Per-request deadline; a request that gets no response by then rejects. */\n requestTimeoutMs?: number;\n};\n\n/** A {@link MacHelperClient} backed by a spawned `prowl-macdriver serve` process. */\nexport class SpawnMacHelperClient implements MacHelperClient {\n private readonly child: ChildProcess;\n private readonly pending = new Map<number, Pending>();\n private readonly requestTimeoutMs: number;\n private stdoutBuffer = \"\";\n private stderrBuffer = \"\";\n private nextId = 1;\n private closed = false;\n private terminalError: Error | undefined;\n\n constructor(binaryPath: string, options: SpawnMacHelperOptions = {}) {\n this.requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;\n this.child = spawn(binaryPath, [\"serve\"], { stdio: [\"pipe\", \"pipe\", \"pipe\"] });\n this.child.stdout?.setEncoding(\"utf-8\");\n this.child.stderr?.setEncoding(\"utf-8\");\n this.child.stdout?.on(\"data\", (chunk: string) => this.onStdout(chunk));\n this.child.stderr?.on(\"data\", (chunk: string) => {\n this.stderrBuffer = (this.stderrBuffer + chunk).slice(-4000);\n });\n this.child.on(\"error\", (error) => this.recordTerminalFailure(error));\n this.child.on(\"exit\", (code) => {\n if (!this.closed) {\n const detail = this.stderrBuffer.trim();\n this.recordTerminalFailure(\n new Error(`prowl-macdriver exited unexpectedly (code ${code ?? \"null\"})${detail ? `: ${detail}` : \"\"}`)\n );\n }\n });\n }\n\n private onStdout(chunk: string): void {\n this.stdoutBuffer += chunk;\n let newlineIndex = this.stdoutBuffer.indexOf(\"\\n\");\n while (newlineIndex !== -1) {\n const line = this.stdoutBuffer.slice(0, newlineIndex).trim();\n this.stdoutBuffer = this.stdoutBuffer.slice(newlineIndex + 1);\n if (line.length > 0) {\n this.dispatch(line);\n }\n newlineIndex = this.stdoutBuffer.indexOf(\"\\n\");\n }\n }\n\n private dispatch(line: string): void {\n let message: Record<string, unknown>;\n try {\n message = JSON.parse(line) as Record<string, unknown>;\n } catch {\n return; // ignore non-JSON noise\n }\n const id = typeof message.id === \"number\" ? message.id : undefined;\n if (id === undefined) {\n return;\n }\n const pending = this.pending.get(id);\n if (!pending) {\n return;\n }\n this.pending.delete(id);\n clearTimeout(pending.timer);\n if (message.ok === true) {\n pending.resolve((message.result as Record<string, unknown>) ?? {});\n } else {\n pending.reject(new Error(typeof message.error === \"string\" ? message.error : \"prowl-macdriver error\"));\n }\n }\n\n private failAll(error: Error): void {\n for (const pending of this.pending.values()) {\n clearTimeout(pending.timer);\n pending.reject(error);\n }\n this.pending.clear();\n }\n\n private recordTerminalFailure(error: Error): void {\n this.terminalError ??= error;\n this.closed = true;\n this.failAll(this.terminalError);\n }\n\n /** Number of in-flight requests awaiting a response (for teardown/tests). */\n get pendingCount(): number {\n return this.pending.size;\n }\n\n request(cmd: string, params: Record<string, unknown> = {}): Promise<Record<string, unknown>> {\n if (this.terminalError) {\n return Promise.reject(this.terminalError);\n }\n if (this.closed) {\n return Promise.reject(new Error(\"prowl-macdriver client is closed\"));\n }\n const id = this.nextId++;\n const payload = JSON.stringify({ id, cmd, ...params });\n return new Promise<Record<string, unknown>>((resolve, reject) => {\n const timer = setTimeout(() => {\n if (this.pending.delete(id)) {\n const shown =\n this.requestTimeoutMs >= 1000\n ? `${Math.round(this.requestTimeoutMs / 1000)}s`\n : `${this.requestTimeoutMs}ms`;\n reject(new Error(`prowl-macdriver request \"${cmd}\" timed out after ${shown}`));\n }\n }, this.requestTimeoutMs);\n // Don't let a pending deadline keep the event loop alive on its own.\n timer.unref?.();\n this.pending.set(id, { cmd, resolve, reject, timer });\n this.child.stdin?.write(payload + \"\\n\", (error) => {\n if (error && this.pending.delete(id)) {\n clearTimeout(timer);\n reject(error);\n }\n });\n });\n }\n\n async close(): Promise<void> {\n if (this.closed) {\n return;\n }\n this.closed = true;\n try {\n this.child.stdin?.write(JSON.stringify({ cmd: \"shutdown\" }) + \"\\n\");\n this.child.stdin?.end();\n } catch {\n // best effort\n }\n await new Promise<void>((resolve) => {\n if (this.child.exitCode !== null || this.child.signalCode !== null) {\n resolve();\n return;\n }\n const timer = setTimeout(() => {\n this.child.kill(\"SIGKILL\");\n resolve();\n }, 2000);\n this.child.once(\"exit\", () => {\n clearTimeout(timer);\n resolve();\n });\n });\n this.failAll(new Error(\"prowl-macdriver client is closed\"));\n }\n}\n\nexport type MacSession = {\n client: MacHelperClient;\n driver: SessionDriver;\n bundleId: string;\n};\n\nexport type LaunchMacOptions = {\n /** Bundle id or absolute `.app` path. */\n app: string;\n timeoutMs?: number;\n /** Inject a helper client (tests / a prebuilt binary); defaults to spawning the helper. */\n clientFactory?: () => MacHelperClient;\n};\n\n/** Launch/attach the target app through the helper and build a {@link MacDriver}. */\nexport async function launchMacSession(options: LaunchMacOptions): Promise<MacSession> {\n // Give the transport headroom over the app-level timeout so a legitimately\n // slow verb (launch, waitFor) isn't killed early by the request deadline.\n const requestTimeoutMs = Math.max(options.timeoutMs ?? 10000, DEFAULT_REQUEST_TIMEOUT_MS) + 5000;\n const client = options.clientFactory\n ? options.clientFactory()\n : new SpawnMacHelperClient(resolveHelperBinary(), { requestTimeoutMs });\n const timeoutSeconds = (options.timeoutMs ?? 10000) / 1000;\n\n try {\n const trust = await client.request(\"check\");\n if (trust.trusted !== true) {\n throw new Error(\n \"Prowl's macOS target is not trusted for Accessibility. Grant the hosting terminal/app \" +\n \"permission in System Settings → Privacy & Security → Accessibility, then retry.\"\n );\n }\n const launched = await client.request(\"launch\", { app: options.app, timeout: timeoutSeconds });\n const bundleId = String(launched.bundleId ?? options.app);\n const driver = createMacDriver(client, { appLabel: bundleId });\n return { client, driver, bundleId };\n } catch (error) {\n await client.close().catch(() => undefined);\n throw error;\n }\n}\n\n/** Quit the target app (best effort) and shut the helper down. */\nexport async function closeMacSession(session: MacSession): Promise<void> {\n try {\n await session.client.request(\"quit\");\n } catch {\n // best effort — the app may already be gone\n } finally {\n await session.client.close();\n }\n}\n","/**\n * PROWL-061 — a tiny, dependency-free XML parser for on-device UI hierarchies.\n *\n * Both native mobile analyzers read an XML page source: Android via the\n * uiautomator2 agent's `GET /source` (a `<hierarchy>` of `<node>` elements) and\n * iOS via WebDriverAgent's `GET /source` (a tree of `<XCUIElementType…>`\n * elements). Both dialects share the same shape — a tree of elements whose data\n * lives entirely in double-quoted attributes, with no meaningful text between\n * tags — so one small scanner serves both, keeping with the repo's \"no heavy\n * SDK\" ethos (there is no XML parser in our own dependency set, only transitive\n * ones we must not rely on).\n *\n * The scanner is deliberately narrow: it understands element start/end/self-close\n * tags, quoted attributes (single or double), XML declarations, comments, and the\n * five predefined entities plus numeric character references. It ignores text\n * nodes and CDATA (the UI dumps carry none). It never throws on malformed input —\n * it returns the best-effort root element, or null when there is no element at\n * all — so a surprising payload degrades to an empty analysis rather than a crash.\n */\n\n/** A parsed XML element: its tag name, attributes, and child elements. */\nexport type XmlElement = {\n tag: string;\n attrs: Record<string, string>;\n children: XmlElement[];\n};\n\nconst ENTITIES: Readonly<Record<string, string>> = {\n amp: \"&\",\n lt: \"<\",\n gt: \">\",\n quot: '\"',\n apos: \"'\"\n};\n\n/** Decode the five predefined XML entities and numeric character references. */\nexport function decodeXmlEntities(value: string): string {\n if (!value.includes(\"&\")) {\n return value;\n }\n return value.replace(/&(#(?:[xX][0-9a-fA-F]+|[0-9]+)|[a-zA-Z]+);/g, (match, code: string) => {\n if (code[0] === \"#\") {\n const hex = code[1] === \"x\" || code[1] === \"X\";\n const num = Number.parseInt(code.slice(hex ? 2 : 1), hex ? 16 : 10);\n return Number.isSafeInteger(num) && num <= 0x10ffff ? String.fromCodePoint(num) : match;\n }\n const named = ENTITIES[code];\n return named ?? match;\n });\n}\n\nconst ATTR_RE = /([^\\s=/]+)\\s*=\\s*(\"([^\"]*)\"|'([^']*)')/g;\n\n/** Parse a start-tag body (`tag attr=\"v\" …`) into its name and decoded attributes. */\nfunction parseTagBody(body: string): { tag: string; attrs: Record<string, string> } {\n const trimmed = body.trim();\n const nameMatch = /^([^\\s/>]+)/.exec(trimmed);\n const tag = nameMatch ? nameMatch[1] : \"\";\n const attrs: Record<string, string> = {};\n const rest = trimmed.slice(tag.length);\n ATTR_RE.lastIndex = 0;\n let m: RegExpExecArray | null;\n while ((m = ATTR_RE.exec(rest)) !== null) {\n const rawValue = m[3] !== undefined ? m[3] : (m[4] ?? \"\");\n attrs[m[1]] = decodeXmlEntities(rawValue);\n }\n return { tag, attrs };\n}\n\n/**\n * Parse an XML document into its root {@link XmlElement} (best-effort). Returns\n * null when the input contains no element. Malformed markup is tolerated: unknown\n * constructs are skipped and mismatched end tags simply pop the stack.\n */\nexport function parseXml(input: string): XmlElement | null {\n const n = input.length;\n const stack: XmlElement[] = [];\n let root: XmlElement | null = null;\n let i = 0;\n\n while (i < n) {\n const lt = input.indexOf(\"<\", i);\n if (lt < 0) {\n break;\n }\n i = lt + 1;\n const ch = input[i];\n\n if (ch === \"?\") {\n // XML declaration / processing instruction.\n const end = input.indexOf(\"?>\", i);\n i = end < 0 ? n : end + 2;\n continue;\n }\n if (ch === \"!\") {\n // Comment, DOCTYPE, or CDATA — skip to the closing marker.\n if (input.startsWith(\"!--\", i)) {\n const end = input.indexOf(\"-->\", i);\n i = end < 0 ? n : end + 3;\n } else {\n const end = input.indexOf(\">\", i);\n i = end < 0 ? n : end + 1;\n }\n continue;\n }\n if (ch === \"/\") {\n // End tag — pop the current element.\n const gt = input.indexOf(\">\", i);\n i = gt < 0 ? n : gt + 1;\n stack.pop();\n continue;\n }\n\n // Start tag (possibly self-closing). Scan to the matching '>' while\n // respecting quoted attribute values (which may contain '>').\n let j = i;\n let quote: string | null = null;\n while (j < n) {\n const c = input[j];\n if (quote !== null) {\n if (c === quote) {\n quote = null;\n }\n } else if (c === '\"' || c === \"'\") {\n quote = c;\n } else if (c === \">\") {\n break;\n }\n j += 1;\n }\n const inner = input.slice(i, j);\n i = j + 1;\n\n const selfClose = inner.endsWith(\"/\");\n const body = selfClose ? inner.slice(0, -1) : inner;\n const { tag, attrs } = parseTagBody(body);\n if (tag.length === 0) {\n continue;\n }\n const element: XmlElement = { tag, attrs, children: [] };\n const parent = stack[stack.length - 1];\n if (parent) {\n parent.children.push(element);\n }\n if (root === null) {\n root = element;\n }\n if (!selfClose) {\n stack.push(element);\n }\n }\n\n return root;\n}\n","/**\n * PROWL-060 / ARCH-011 — Unified native selector engine (snapshot-then-match).\n *\n * This module is the single source of truth for what Prowl's Android/iOS native\n * selector dialect (`id=` / `label=` / `text=` / `role=`, plus `:focus`) means.\n * It also carries the macOS compatibility mapping for the deferred macdriver\n * migration. Before this module the mobile dialect was defined three times over\n * — once in each mobile driver's locator translation (`androidQueryToLocator` /\n * `iosQueryToLocator`) and once in each mobile analyzer's selector ranking\n * (`rankAndroidSelectors` / `rankIosSelectors`). Consolidating the grammar, the\n * per-platform attribute mapping tables, the ranking order, and the host-side\n * matching semantics here means the mobile dialect (and its documentation,\n * including the `label=`-in-assertions trap) lives in exactly one place.\n *\n * It has three surfaces:\n * 1. {@link parseNativeSelector} — the ONE grammar that turns a Prowl selector\n * string into a neutral `{ kind, value, roleName? }`. Both mobile drivers\n * parse through it (then map the neutral kind onto their own wire query),\n * so the accepted syntax can never drift between platforms.\n * 2. {@link rankNativeSelectors} + {@link NATIVE_ATTRIBUTE_MAP} — the ranking\n * order and per-platform attribute mapping tables that the mobile\n * analyzers' selector ranking derives from.\n * 3. {@link nodeMatchesSelector} / {@link matchNativeTree} — a dependency-free,\n * host-side \"snapshot-then-match\" engine: given a parsed selector and a\n * hierarchy node (projected from an agent's `/source` dump via\n * {@link parseXml}), decide what matches, using each platform's real\n * attribute + match-mode semantics. The analyzers expose this host-side (it\n * is read-only and unit-verifiable); the runners keep their on-device\n * matching for now (see the migration note at the foot of this file).\n *\n * =====================================================================\n * Selector compatibility matrix — web / macOS / Android / iOS\n * =====================================================================\n * How each selector kind resolves on every target. Android and iOS consume this\n * shared grammar/mapping/matcher today; macOS still uses its existing driver and\n * analyzer implementation, with migration deferred, but follows the same\n * documented selector shape. The web target speaks Playwright's own selector\n * engines and is included for contrast.\n *\n * Kind Web (Playwright) macOS (AX) Android (uiautomator2) iOS (WebDriverAgent)\n * -------- ------------------------ ---------------------- ----------------------------- ------------------------------\n * id= (use CSS `#id` / AXIdentifier resource-id, EXACT accessibility id (name),\n * `[data-testid]`) (exact) (bare names are package- EXACT\n * qualified: `save` →\n * `<pkg>:id/save`)\n * label= (no native kind; the title ?? description, content-desc, EXACT accessibilityLabel, EXACT\n * analyzer surfaces the EXACT (`label == \"…\"`)\n * associated <label> text)\n * text= Playwright text engine, title/description/ visible text, SUBSTRING label OR value, SUBSTRING\n * substring, trimmed, value, SUBSTRING (`textContains`) (`label CONTAINS … OR\n * case-insensitive value CONTAINS …`)\n * role= Playwright role engine AX role (e.g. widget class name element type (`XCUIElementType…`;\n * (ARIA roles) `AXButton`) (e.g. `android.widget.Button`) shorthand `Button` accepted)\n * role=X role + accessible name role + name class + visible-text type + (label OR value)\n * [name=Y] (substring) (substring) substring substring\n * :focus (n/a) focused element `UiSelector().focused(true)` `hasKeyboardFocus == 1`\n *\n * THE `label=`-IN-ASSERTIONS TRAP\n * -------------------------------\n * On every native target `label=` is an EXACT match on the accessibility label\n * (content-desc on Android, accessibilityLabel on iOS, title/description on\n * macOS) — unlike `text=`, which is a SUBSTRING match, and unlike the web, where\n * text-ish matching is forgiving. So a hunt author who writes an assertion like\n * `assert: selectorExists: label=\"Save\"` expecting substring/partial behavior\n * gets nothing when the real label is \"Save changes\": the assertion silently\n * fails to match rather than partially matching. Reach for `text=` when you want\n * substring behavior in an assertion, and keep `label=` for the exact\n * accessibility label. On iOS there is a second, related trap: WDA's `/source`\n * exposes a single `name` attribute that is the `accessibilityIdentifier` when\n * one is set and otherwise the label. The analyzer only ranks `id=` when `name`\n * differs from `label`, so it does not recommend label-shaped ids, but matching\n * still follows WDA and resolves `id=` against `name`.\n */\nimport { parseXml } from \"../analyzer/xml.js\";\n\n/* ===================================================================== *\n * 1. The neutral parsed selector + shared grammar\n * ===================================================================== */\n\n/** The selector kinds the native dialect understands. */\nexport type NativeSelectorKind = \"id\" | \"label\" | \"text\" | \"role\" | \"focused\";\n\n/**\n * A Prowl native selector parsed into a neutral, platform-independent shape.\n * `role` optionally carries a `name` (the `role=Type[name=\"…\"]` form); `name` is\n * present only when the bracket was supplied and non-empty.\n */\nexport type NativeSelector =\n | { kind: \"id\"; value: string }\n | { kind: \"label\"; value: string }\n | { kind: \"text\"; value: string }\n | { kind: \"role\"; role: string; name?: string }\n | { kind: \"focused\" };\n\n/**\n * Strip one layer of matching single/double quotes from a selector value. Mirrors\n * the historical per-driver `unquote`, kept here so every native target unquotes\n * identically.\n */\nexport function unquoteSelectorValue(value: string): string {\n const trimmed = value.trim();\n const first = trimmed[0];\n if ((first === '\"' || first === \"'\") && trimmed.endsWith(first) && trimmed.length >= 2) {\n return trimmed.slice(1, -1);\n }\n return trimmed;\n}\n\n/** Wrap a selector value in double quotes (the ranker's canonical emitted form). */\nexport function quoteSelectorValue(value: string): string {\n return `\"${value}\"`;\n}\n\nconst ROLE_SELECTOR_RE = /^role=([A-Za-z][\\w.$-]*)(?:\\[name=(.+)\\])?$/s;\nconst NATIVE_SELECTOR_PREFIX_RE = /^(id|label|text|role)=/;\n\nfunction invalidSelectorMessage(selector: string, reason: string): string {\n return `Invalid native selector ${JSON.stringify(selector)}: ${reason}.`;\n}\n\n/**\n * Parse a Prowl selector string into a neutral {@link NativeSelector}. This is\n * the single grammar shared by both mobile drivers (which then map the neutral\n * kind onto their own wire query). Precedence — `:focus`, then `id=`, `role=`,\n * `label=`, `text=`, and finally a bare string treated as `text=` — matches the\n * behavior the per-driver parsers had before consolidation.\n */\nexport function parseNativeSelector(selector: string): NativeSelector {\n const trimmed = selector.trim();\n\n if (trimmed.length === 0) {\n throw new Error(\n invalidSelectorMessage(\n selector,\n \"selector is empty; use id=, label=, text=, role=, :focus, or a bare text value\"\n )\n );\n }\n\n if (trimmed === \":focus\") {\n return { kind: \"focused\" };\n }\n\n const idMatch = /^id=(.+)$/s.exec(trimmed);\n if (idMatch) {\n return { kind: \"id\", value: unquoteSelectorValue(idMatch[1]) };\n }\n\n const roleMatch = ROLE_SELECTOR_RE.exec(trimmed);\n if (roleMatch) {\n const name = roleMatch[2] !== undefined ? unquoteSelectorValue(roleMatch[2]) : undefined;\n return name !== undefined && name.length > 0\n ? { kind: \"role\", role: roleMatch[1], name }\n : { kind: \"role\", role: roleMatch[1] };\n }\n\n const labelMatch = /^label=(.+)$/s.exec(trimmed);\n if (labelMatch) {\n return { kind: \"label\", value: unquoteSelectorValue(labelMatch[1]) };\n }\n\n const textMatch = /^text=(.+)$/s.exec(trimmed);\n if (textMatch) {\n return { kind: \"text\", value: unquoteSelectorValue(textMatch[1]) };\n }\n\n const prefix = NATIVE_SELECTOR_PREFIX_RE.exec(trimmed);\n if (prefix) {\n throw new Error(\n invalidSelectorMessage(\n selector,\n `malformed ${prefix[1]}= selector; expected id=<value>, label=<value>, text=<value>, or role=<Type>[name=<value>]`\n )\n );\n }\n\n return { kind: \"text\", value: trimmed };\n}\n\n/**\n * The literal a `text=` selector matches, or null when `selector` is not an\n * explicit `text=` form (a bare string is intentionally excluded — it mirrors the\n * web driver's `parseTextSelector`, which `forbiddenSelectors` relies on). Shared\n * so Android and iOS unwrap text selectors identically.\n */\nexport function unwrapNativeTextSelector(selector: string): string | null {\n const trimmed = selector.trim();\n if (!trimmed.startsWith(\"text=\")) {\n return null;\n }\n return unquoteSelectorValue(trimmed.slice(\"text=\".length));\n}\n\n/* ===================================================================== *\n * 2. Per-platform id/role normalization (part of the dialect)\n * ===================================================================== */\n\n/**\n * Qualify a bare Android `resource-id` with the app's package. The raw\n * uiautomator2 server matches resource-ids exactly (bare names return no\n * elements), so `id=save` becomes `<appPackage>:id/save`. Values that already\n * contain a `:` (e.g. `android:id/title`) — or calls without a package — pass\n * through untouched. Both the Android driver's locator translation and the\n * host-side matcher qualify through this one function.\n */\nexport function qualifyResourceId(value: string, appPackage?: string): string {\n if (value.includes(\":\") || !appPackage) {\n return value;\n }\n return `${appPackage}:id/${value}`;\n}\n\n/** Prefix `XCUIElementType` onto an iOS role shorthand (e.g. `Button`) when missing. */\nexport function normalizeXcuiClassName(role: string): string {\n return role.startsWith(\"XCUIElementType\") ? role : `XCUIElementType${role}`;\n}\n\n/** Strip the `XCUIElementType` prefix for a friendlier `role=` shorthand. */\nexport function shortIosType(type: string): string {\n return type.startsWith(\"XCUIElementType\") ? type.slice(\"XCUIElementType\".length) : type;\n}\n\n/* ===================================================================== *\n * 3. Ranking — the analyzers' single source of truth\n * ===================================================================== */\n\n/**\n * The per-node ingredients the ranker needs, already projected out of a\n * platform's own node shape:\n * - `id` the native identifier (Android resource-id, iOS accessibility id,\n * macOS AXIdentifier) → emitted as `id=`.\n * - `label` the exact accessibility label (Android content-desc, iOS label,\n * macOS title/description) → emitted as `label=\"…\"`.\n * - `role` the class/type/role string to emit verbatim (already shortened for\n * iOS) → emitted as `role=` and in `role=…[name=\"…\"]`.\n * - `name` the representative visible-text name used for `role=…[name]` and\n * the `text=` fallback.\n */\nexport type NativeRankFields = {\n id?: string;\n label?: string;\n role?: string;\n name?: string;\n};\n\n/**\n * Ranked selector candidates for a node, best → last resort:\n * `id=` > `label=\"…\"` > `role=…[name=\"…\"]` > `text=\"…\"`, with a bare `role=`\n * fallback so any node carrying a role stays addressable. This is the one ranking\n * algorithm every native analyzer emits through, so a change to selector priority\n * happens in exactly one place. Returns an empty array when a node exposes nothing\n * addressable.\n */\nexport function rankNativeSelectors(fields: NativeRankFields): string[] {\n const selectors: string[] = [];\n if (fields.id) {\n selectors.push(`id=${fields.id}`);\n }\n if (fields.label) {\n selectors.push(`label=${quoteSelectorValue(fields.label)}`);\n }\n if (fields.role && fields.name) {\n selectors.push(`role=${fields.role}[name=${quoteSelectorValue(fields.name)}]`);\n }\n if (fields.name) {\n selectors.push(`text=${quoteSelectorValue(fields.name)}`);\n }\n if (selectors.length === 0 && fields.role) {\n selectors.push(`role=${fields.role}`);\n }\n return selectors;\n}\n\n/* ===================================================================== *\n * 4. Per-platform attribute mapping tables (documentation-as-data)\n * ===================================================================== */\n\n/** The native targets whose selector dialect this module defines. */\nexport type NativePlatform = \"android\" | \"ios\" | \"macos\";\n\n/** How a selector kind resolves on one platform (mirrors the matrix above). */\nexport type SelectorKindMapping = {\n /** The native attribute(s) the kind targets. */\n attribute: string;\n /** The comparison mode against that attribute. */\n match: \"exact\" | \"exact (package-qualified)\" | \"substring\";\n};\n\n/**\n * The per-platform attribute mapping tables — the machine-readable form of the\n * compatibility matrix, so the mapping is documented once and can be asserted in\n * tests. Keyed by platform, then by the four addressable selector kinds. (`role`\n * describes the bare `role=` form; the `role=…[name]` composite pairs an exact\n * role with a substring name, per the matrix.)\n */\nexport const NATIVE_ATTRIBUTE_MAP: Readonly<\n Record<NativePlatform, Readonly<Record<\"id\" | \"label\" | \"text\" | \"role\", SelectorKindMapping>>>\n> = {\n android: {\n id: { attribute: \"resource-id\", match: \"exact (package-qualified)\" },\n label: { attribute: \"content-desc\", match: \"exact\" },\n text: { attribute: \"text\", match: \"substring\" },\n role: { attribute: \"class\", match: \"exact\" }\n },\n ios: {\n id: { attribute: \"accessibility id (name)\", match: \"exact\" },\n label: { attribute: \"label\", match: \"exact\" },\n text: { attribute: \"label | value\", match: \"substring\" },\n role: { attribute: \"type (XCUIElementType…)\", match: \"exact\" }\n },\n macos: {\n id: { attribute: \"AXIdentifier\", match: \"exact\" },\n label: { attribute: \"title | description\", match: \"exact\" },\n text: { attribute: \"title | description | value\", match: \"substring\" },\n role: { attribute: \"AXRole\", match: \"exact\" }\n }\n} as const;\n\n/* ===================================================================== *\n * 5. Host-side snapshot-then-match engine\n * ===================================================================== */\n\n/**\n * A hierarchy node projected into the neutral attributes the matcher compares\n * against. Platform code (analyzers, and later the runners/macdriver) projects\n * its own node shape into this:\n * - `id` native identifier for exact `id=` matching.\n * - `label` exact accessibility label for exact `label=` matching.\n * - `role` class/type/role in the platform's canonical (full) form; the\n * dialect's {@link NativeMatchDialect.normalizeRole} reconciles a\n * shorthand selector (`Button`) with a full node type.\n * - `textValues` every string a `text=` / `[name]` substring should test\n * (Android: `[text]`; iOS: `[label, value]`; macOS:\n * `[title, description, value]`).\n * - `focused` whether the node currently holds keyboard focus (`:focus`).\n */\nexport type NativeNode = {\n id?: string;\n label?: string;\n role?: string;\n textValues: string[];\n focused?: boolean;\n};\n\n/** Options threaded into matching (currently only Android id package-qualification). */\nexport type NativeMatchOptions = {\n /** App package used to qualify a bare Android `id=` before an exact compare. */\n appPackage?: string;\n};\n\n/**\n * The platform-specific part of matching: how to normalize a role/type string for\n * comparison, and how to normalize an `id=` value before an exact id compare.\n * Everything else (exact id/label, substring text, focus) is platform-independent.\n */\nexport type NativeMatchDialect = {\n platform: NativePlatform;\n /** Canonicalize a role/type string so a shorthand selector matches a full node type. */\n normalizeRole: (role: string) => string;\n /** Canonicalize an `id=` value (Android package-qualifies bare ids; others identity). */\n normalizeId: (value: string, options: NativeMatchOptions) => string;\n};\n\n/** Android matching dialect: identity role compare, package-qualified ids. */\nexport const ANDROID_MATCH_DIALECT: NativeMatchDialect = {\n platform: \"android\",\n normalizeRole: (role) => role,\n normalizeId: (value, options) => qualifyResourceId(value, options.appPackage)\n};\n\n/** iOS matching dialect: `XCUIElementType…`-normalized role compare, identity ids. */\nexport const IOS_MATCH_DIALECT: NativeMatchDialect = {\n platform: \"ios\",\n normalizeRole: (role) => normalizeXcuiClassName(role),\n normalizeId: (value) => value\n};\n\n/** macOS matching dialect (exposed for the future macdriver migration). */\nexport const MACOS_MATCH_DIALECT: NativeMatchDialect = {\n platform: \"macos\",\n normalizeRole: (role) => role,\n normalizeId: (value) => value\n};\n\n/**\n * Decide whether a single projected {@link NativeNode} satisfies a parsed\n * {@link NativeSelector}, using the platform's attribute + match-mode semantics:\n * - `id` exact match on `node.id` (Android value package-qualified first).\n * - `label` exact match on `node.label`.\n * - `text` substring match against any of `node.textValues`.\n * - `role` exact (normalized) role match; with `name`, additionally a\n * substring match against any of `node.textValues`.\n * - `focused` node currently holds keyboard focus.\n */\nexport function nodeMatchesSelector(\n dialect: NativeMatchDialect,\n selector: NativeSelector,\n node: NativeNode,\n options: NativeMatchOptions = {}\n): boolean {\n switch (selector.kind) {\n case \"id\":\n return node.id !== undefined && node.id === dialect.normalizeId(selector.value, options);\n case \"label\":\n return node.label !== undefined && node.label === selector.value;\n case \"text\":\n return node.textValues.some((t) => t.includes(selector.value));\n case \"focused\":\n return node.focused === true;\n case \"role\": {\n if (node.role === undefined) {\n return false;\n }\n if (dialect.normalizeRole(node.role) !== dialect.normalizeRole(selector.role)) {\n return false;\n }\n if (selector.name === undefined || selector.name.length === 0) {\n return true;\n }\n const name = selector.name;\n return node.textValues.some((t) => t.includes(name));\n }\n }\n}\n\n/**\n * Walk a hierarchy of platform nodes depth-first and collect every node matching\n * `selector`, in document order. Generic over the caller's own node type so the\n * analyzers get their original, fully-typed nodes back: pass a `project` that maps\n * a node to its {@link NativeNode} attributes and a `children` accessor.\n */\nexport function matchNativeTree<T>(\n dialect: NativeMatchDialect,\n selector: NativeSelector,\n root: T,\n project: (node: T) => NativeNode,\n children: (node: T) => readonly T[],\n options: NativeMatchOptions = {}\n): T[] {\n const out: T[] = [];\n const visit = (node: T): void => {\n if (nodeMatchesSelector(dialect, selector, project(node), options)) {\n out.push(node);\n }\n for (const child of children(node)) {\n visit(child);\n }\n };\n visit(root);\n return out;\n}\n\n/**\n * Parse a raw agent `/source` XML dump into an element tree (best-effort, via the\n * dependency-free {@link parseXml}) so a caller can match against a snapshot\n * without a device. Returns null when the payload holds no element.\n */\nexport function parseSnapshot(xml: string) {\n return parseXml(xml);\n}\n\n/*\n * MIGRATION NOTE (macdriver + runners) — PROWL-060 deliberately consolidates the\n * *dialect definition* (grammar, attribute tables, ranking, and this host-side\n * matcher) without changing any observable runtime behavior. The mobile runners\n * still match on-device through their agents (uiautomator2 / WDA), which this\n * branch does not touch; and the macOS driver's own matching (`src/analyzer/\n * mac.ts` + the Swift helper) is left entirely alone. The extension points for a\n * later migration are in place: {@link MACOS_MATCH_DIALECT} plus this module's\n * matcher mean a future branch can move macdriver — or the runners' host-side\n * matching — onto this one engine and delete the remaining per-platform matching,\n * once that change can be device-verified.\n */\n","/**\n * PROWL-058 / ARCH-009 — Android native implementation of {@link SessionDriver}.\n *\n * `AndroidDriver` drives a native Android app through the on-device\n * `appium-uiautomator2-server` agent over its W3C-shaped HTTP/JSON API\n * ({@link AndroidAgentClient}). Like the macOS driver it implements only the\n * portable subset of the driver surface — its `capabilities` set is honest:\n * `query`, `interact`, `wait`, `screenshot`. Web-only verbs (navigation,\n * network, dialogs, files, downloads, script evaluation) are unsupported stubs;\n * the runner never reaches them because both the target step-compatibility check\n * and the runtime capability gate reject web-only steps for native targets.\n *\n * Selector dialect (parsed by the shared native selector engine\n * `../selector/native.ts` (PROWL-060) — the single source of truth for the\n * grammar and attribute mapping — then mapped here to an {@link AndroidQuery} the\n * agent matches on the device). Semantics mirror the macOS/iOS drivers so\n * `id=`/`label=`/`text=`/`role=` mean the same thing across native targets:\n * id=save → resource-id (bare name or full `pkg:id/name`)\n * label=\"Submit\" → content-desc (exact)\n * role=android.widget.Button → widget class name\n * role=X[name=\"Save\"] → widget class + visible text (substring)\n * text=\"Save\" | Save → visible text (substring)\n *\n * Compose caveat: Jetpack Compose nodes only expose a `resource-id` when the app\n * sets `Modifier.testTag(...)` together with `testTagsAsResourceId = true`;\n * otherwise prefer `text=`/`label=`.\n */\nimport fs from \"node:fs\";\nimport {\n parseNativeSelector,\n qualifyResourceId,\n unwrapNativeTextSelector,\n type NativeSelector\n} from \"../selector/native.js\";\nimport {\n buildDirectionalSwipe,\n MAX_SCROLL_TO_SWIPES,\n probeScrollIntoView,\n scrollToProbeDistanceFor,\n type PointerActionSequence,\n type ScreenSize,\n type SwipeDirection\n} from \"./touch-gestures.js\";\nimport type {\n DialogAction,\n DriverCapability,\n DriverDownload,\n DriverResponse,\n DriverRoute,\n NavigateOptions,\n SessionDriver\n} from \"./driver.js\";\n\n// Re-export the id-qualification rule from the shared native selector engine\n// (PROWL-060), which is its single source of truth, so existing importers of\n// `qualifyResourceId` from this module keep working.\nexport { qualifyResourceId } from \"../selector/native.js\";\n\n/** A structured query the {@link AndroidAgentClient} resolves against the device. */\nexport type AndroidQuery =\n | { by: \"id\"; value: string }\n | { by: \"accessibilityId\"; value: string }\n | { by: \"role\"; role: string; name?: string }\n | { by: \"text\"; value: string }\n | { by: \"focused\" };\n\n/**\n * A locator in the uiautomator2 server's native wire shape. The raw on-device\n * server does NOT accept W3C `{using, value}` — that translation normally lives\n * in Appium's driver layer, which we bypass — it requires `{strategy, selector}`\n * (plus a `context` field, empty for a root-scoped search). Device-verified\n * against appium-uiautomator2-server 10.6.2 (2026-08-19).\n */\nexport type AndroidLocator = { strategy: string; selector: string; context: string };\n\n/**\n * The semantic transport `AndroidDriver` talks to: element lookups return opaque\n * element ids, and interactions take those ids. The HTTP/UiAutomator2\n * implementation lives in {@link ./android-agent.js}; tests fake this interface.\n */\nexport interface AndroidAgentClient {\n /** Resolve the first element matching `query`, or null when none match. */\n findElement(query: AndroidQuery): Promise<string | null>;\n /** Resolve every element id matching `query` (empty when none match). */\n findElements(query: AndroidQuery): Promise<string[]>;\n click(elementId: string): Promise<void>;\n /** Replace an element's text (unicode-safe; W3C `element/value`). */\n setValue(elementId: string, text: string): Promise<void>;\n getText(elementId: string): Promise<string | null>;\n /** Dispatch a global key event by Android key code (goes to the focused view). */\n pressKeyCode(keyCode: number): Promise<void>;\n /** Current screen size in pixels (uiautomator2 `/window/current/size`), for gestures. */\n windowSize(): Promise<ScreenSize>;\n /** Perform a W3C pointer action sequence (`POST /session/:id/actions`). */\n performActions(actions: PointerActionSequence): Promise<void>;\n /** Capture the current screen as PNG bytes. */\n screenshotPng(): Promise<Buffer>;\n /**\n * Return the current UI hierarchy as uiautomator2 `/source` XML. Present on\n * live clients and consumed by the analyzer (PROWL-061); optional so lighter\n * fakes that only drive/query need not implement it.\n */\n source?(): Promise<string>;\n close(): Promise<void>;\n}\n\nexport type AndroidDriverOptions = {\n /** Package name, used only for the informational `currentUrl()` value. */\n appLabel?: string;\n};\n\nconst ANDROID_CAPABILITIES: ReadonlySet<DriverCapability> = new Set<DriverCapability>([\n \"query\",\n \"interact\",\n \"wait\",\n \"screenshot\"\n]);\n\n/** Poll interval while waiting for a selector to appear. */\nconst WAIT_POLL_INTERVAL_MS = 250;\n/** Default wait deadline when a step does not specify one. */\nconst DEFAULT_WAIT_TIMEOUT_MS = 5000;\n\n/**\n * Android key names accepted by the `press` step, mapped to KeyEvent key codes.\n * Names are matched case-insensitively.\n */\nexport const ANDROID_KEYCODES: Readonly<Record<string, number>> = {\n enter: 66,\n return: 66,\n tab: 61,\n space: 62,\n backspace: 67,\n delete: 67,\n del: 67,\n escape: 111,\n esc: 111,\n back: 4,\n home: 3,\n menu: 82,\n search: 84,\n up: 19,\n arrowup: 19,\n down: 20,\n arrowdown: 20,\n left: 21,\n arrowleft: 21,\n right: 22,\n arrowright: 22,\n pageup: 92,\n pagedown: 93\n};\n\n/** Escape a string for embedding inside a `new UiSelector()...(\"...\")` argument. */\nexport function escapeUiSelectorArg(value: string): string {\n return value.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"');\n}\n\n/**\n * Map a neutral {@link NativeSelector} (parsed by the shared engine) onto this\n * driver's on-device query shape. Android's `label=` targets the content-desc\n * (`accessibility id` strategy); everything else maps one-to-one.\n */\nfunction toAndroidQuery(selector: NativeSelector): AndroidQuery {\n switch (selector.kind) {\n case \"focused\":\n return { by: \"focused\" };\n case \"id\":\n return { by: \"id\", value: selector.value };\n case \"role\":\n return selector.name !== undefined\n ? { by: \"role\", role: selector.role, name: selector.name }\n : { by: \"role\", role: selector.role };\n case \"label\":\n return { by: \"accessibilityId\", value: selector.value };\n case \"text\":\n return { by: \"text\", value: selector.value };\n }\n}\n\n/**\n * Parse a Prowl selector string into an {@link AndroidQuery}. Bare text matches by\n * text. The grammar (and its `label=`-in-assertions trap) is defined once in the\n * shared native selector engine ({@link parseNativeSelector}, PROWL-060); this\n * only maps the neutral result onto Android's on-device query.\n */\nexport function parseAndroidSelector(selector: string): AndroidQuery {\n return toAndroidQuery(parseNativeSelector(selector));\n}\n\nfunction locator(strategy: string, selector: string): AndroidLocator {\n return { strategy, selector, context: \"\" };\n}\n\n/**\n * Translate an {@link AndroidQuery} into the uiautomator2 server's native\n * locator shape. `id`/`accessibility id` are native strategies;\n * text/role(+name)/focused compose a `-android uiautomator` `UiSelector`.\n * `appPackage` qualifies bare `id=` names ({@link qualifyResourceId}).\n */\nexport function androidQueryToLocator(\n query: AndroidQuery,\n options: { appPackage?: string } = {}\n): AndroidLocator {\n switch (query.by) {\n case \"id\":\n return locator(\"id\", qualifyResourceId(query.value, options.appPackage));\n case \"accessibilityId\":\n // content-desc, exact match.\n return locator(\"accessibility id\", query.value);\n case \"text\":\n // Visible text, substring match — mirrors the macOS `text=` semantics.\n return locator(\n \"-android uiautomator\",\n `new UiSelector().textContains(\"${escapeUiSelectorArg(query.value)}\")`\n );\n case \"focused\":\n return locator(\"-android uiautomator\", \"new UiSelector().focused(true)\");\n case \"role\": {\n const className = escapeUiSelectorArg(query.role);\n if (query.name === undefined || query.name.length === 0) {\n return locator(\"class name\", query.role);\n }\n // Widget class + visible-text (substring) name. Content-desc-only names are\n // not covered by this composed form — use `label=` for those (see the\n // shared dialect's compatibility matrix in ../selector/native.ts).\n return locator(\n \"-android uiautomator\",\n `new UiSelector().className(\"${className}\").textContains(\"${escapeUiSelectorArg(query.name)}\")`\n );\n }\n }\n}\n\n/** The literal text a `text=` selector matches, else null (mirrors the web driver). */\nexport function unwrapAndroidTextSelector(selector: string): string | null {\n return unwrapNativeTextSelector(selector);\n}\n\nfunction keyCodeFor(key: string): number {\n const code = ANDROID_KEYCODES[key.trim().toLowerCase()];\n if (code === undefined) {\n throw new Error(\n `Unsupported key \"${key}\" for the Android target. Supported keys: ${Object.keys(ANDROID_KEYCODES)\n .sort()\n .join(\", \")}.`\n );\n }\n return code;\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n}\n\n/** Wrap a live {@link AndroidAgentClient} as a {@link SessionDriver}. */\nexport function createAndroidDriver(\n client: AndroidAgentClient,\n options: AndroidDriverOptions = {}\n): SessionDriver {\n const unsupported = (verb: string): Error => new Error(`${verb} is not supported by the Android target`);\n const rejectUnsupported = (verb: string): Promise<never> => Promise.reject(unsupported(verb));\n\n async function resolveOne(selector: string): Promise<string> {\n const id = await client.findElement(parseAndroidSelector(selector));\n if (id === null) {\n throw new Error(`No element matched selector: ${selector}`);\n }\n return id;\n }\n\n async function clickSelector(selector: string): Promise<void> {\n await client.click(await resolveOne(selector));\n }\n\n async function fillSelector(selector: string, value: string): Promise<void> {\n await client.setValue(await resolveOne(selector), value);\n }\n\n async function swipe(direction: SwipeDirection, amount?: number, size?: ScreenSize): Promise<void> {\n const actualSize = size ?? (await client.windowSize());\n const { actions } = buildDirectionalSwipe(direction, actualSize, amount);\n await client.performActions(actions);\n }\n\n return {\n capabilities: ANDROID_CAPABILITIES,\n\n // navigation -----------------------------------------------------------\n goto(_url: string, _options?: NavigateOptions): Promise<void> {\n return rejectUnsupported(\"navigate\");\n },\n currentUrl(): string {\n return `android:${options.appLabel ?? \"\"}`;\n },\n\n // queries --------------------------------------------------------------\n async count(selector: string): Promise<number> {\n return (await client.findElements(parseAndroidSelector(selector))).length;\n },\n async textContent(selector: string): Promise<string | null> {\n const id = await client.findElement(parseAndroidSelector(selector));\n if (id === null) {\n return null;\n }\n return client.getText(id);\n },\n\n // interactions ---------------------------------------------------------\n click: clickSelector,\n clickFirst: clickSelector,\n fill: fillSelector,\n fillFirst: fillSelector,\n async press(_selector: string, key: string): Promise<void> {\n // Android key events dispatch to the focused view, so the selector is\n // advisory; callers should focus the field first (e.g. click / fill).\n await client.pressKeyCode(keyCodeFor(key));\n },\n selectOption(): Promise<void> {\n return rejectUnsupported(\"select\");\n },\n selectOptionFirst(): Promise<void> {\n return rejectUnsupported(\"select\");\n },\n hover(): Promise<void> {\n // No hover concept on touch devices.\n return rejectUnsupported(\"hover\");\n },\n // Screen-centred swipe via the W3C actions endpoint (PROWL-080). Direction\n // semantics match the web step: scrolling \"down\" reveals lower content, so\n // the finger drags up. See ./touch-gestures.ts.\n async scroll(direction: \"up\" | \"down\" | \"left\" | \"right\", amount?: number): Promise<void> {\n await swipe(direction, amount);\n },\n // Resolve the element, short-circuiting if it is already present in the\n // hierarchy; otherwise use the shared bounded down/up mobile probe before\n // failing with a message naming the selector and attempts.\n async scrollIntoView(selector: string): Promise<void> {\n const query = parseAndroidSelector(selector);\n let probeSize: ScreenSize | undefined;\n const found = await probeScrollIntoView({\n isVisible: async () => (await client.findElements(query)).length > 0,\n swipe: async (direction) => {\n probeSize ??= await client.windowSize();\n await swipe(direction, scrollToProbeDistanceFor(direction, probeSize), probeSize);\n }\n });\n if (found) {\n return;\n }\n throw new Error(\n `scrollTo: element \"${selector}\" not visible after ${MAX_SCROLL_TO_SWIPES} scroll attempts on the Android target`\n );\n },\n setInputFiles(): Promise<void> {\n return rejectUnsupported(\"setInputFiles\");\n },\n\n // semantic locators ----------------------------------------------------\n async countByRole(role: string, name: string): Promise<number> {\n return (await client.findElements({ by: \"role\", role, name })).length;\n },\n async clickFirstByRole(role: string, name: string): Promise<void> {\n const id = await client.findElement({ by: \"role\", role, name });\n if (id === null) {\n throw new Error(`No element matched role=${role}[name=\"${name}\"]`);\n }\n await client.click(id);\n },\n async countByLabel(label: string): Promise<number> {\n return (await client.findElements({ by: \"accessibilityId\", value: label })).length;\n },\n async fillFirstByLabel(label: string, value: string): Promise<void> {\n const id = await client.findElement({ by: \"accessibilityId\", value: label });\n if (id === null) {\n throw new Error(`No element matched label=\"${label}\"`);\n }\n await client.setValue(id, value);\n },\n selectOptionFirstByLabel(): Promise<void> {\n return rejectUnsupported(\"select\");\n },\n\n // waiting --------------------------------------------------------------\n async waitForSelector(selector: string, waitOptions?: { timeout?: number }): Promise<void> {\n const query = parseAndroidSelector(selector);\n const timeoutMs = waitOptions?.timeout ?? DEFAULT_WAIT_TIMEOUT_MS;\n const deadline = Date.now() + timeoutMs;\n for (;;) {\n if ((await client.findElements(query)).length > 0) {\n return;\n }\n if (Date.now() >= deadline) {\n throw new Error(`Timed out after ${timeoutMs}ms waiting for selector: ${selector}`);\n }\n await delay(Math.min(WAIT_POLL_INTERVAL_MS, Math.max(0, deadline - Date.now())));\n }\n },\n waitForUrl(): Promise<void> {\n return rejectUnsupported(\"waitForUrl\");\n },\n waitForNetworkIdle(): Promise<void> {\n return rejectUnsupported(\"waitForNetworkIdle\");\n },\n\n // scripting & artifacts ------------------------------------------------\n evaluate<R = unknown>(): Promise<R> {\n return rejectUnsupported(\"evalScript\") as Promise<R>;\n },\n async screenshot(screenshotOptions: { path: string; fullPage?: boolean }): Promise<void> {\n // A device screenshot is always the whole screen; `fullPage` has no\n // analogue and is intentionally ignored.\n const png = await client.screenshotPng();\n fs.writeFileSync(screenshotOptions.path, png);\n },\n\n // network / dialogs / downloads (all web-only) -------------------------\n onResponse(_handler: (response: DriverResponse) => void): void {\n throw unsupported(\"onResponse\");\n },\n route(_url: string, _handler: (route: DriverRoute) => void | Promise<void>): Promise<void> {\n return rejectUnsupported(\"mockRoute\");\n },\n unroute(): Promise<void> {\n return rejectUnsupported(\"unmockRoute\");\n },\n onDialog(_action: DialogAction): void {\n throw unsupported(\"onDialog\");\n },\n waitForDownloadEvent(): Promise<DriverDownload> {\n return rejectUnsupported(\"waitForDownload\") as Promise<DriverDownload>;\n },\n\n parseTextSelector(selector: string): string | null {\n return unwrapAndroidTextSelector(selector);\n }\n };\n}\n","/**\n * PROWL-080 / ARCH-014 — synthesized touch gestures for the mobile targets.\n *\n * Both on-device agents (WebDriverAgent on iOS, appium-uiautomator2-server on\n * Android) speak the plain W3C WebDriver **actions** endpoint\n * (`POST /session/:id/actions`), so a single builder here produces the pointer\n * action sequence for a swipe and both drivers post it verbatim over their\n * existing raw-`fetch` transports. We deliberately avoid nonstandard `mobile:`\n * execute shortcuts — portability across the two agents beats convenience.\n *\n * Direction semantics match the web `scroll` step: a scroll *direction* names\n * where the content moves, so the finger swipes the opposite way. Scrolling\n * \"down\" (reveal content further down the page) drags the finger *up* the\n * screen; \"right\" drags the finger *left*; and so on. Swipes are centred on the\n * screen and span a distance derived from the step's `amount` (see\n * {@link swipeDistanceFor}).\n */\n\n/** A scroll/swipe direction, matching the web `scroll` step's vocabulary. */\nexport type SwipeDirection = \"up\" | \"down\" | \"left\" | \"right\";\n\n/** Screen dimensions in device points, as reported by the agent. */\nexport interface ScreenSize {\n width: number;\n height: number;\n}\n\n/** An (x, y) point in device points. */\nexport interface Point {\n x: number;\n y: number;\n}\n\n/** One item in a W3C `pointer` action sequence. */\nexport type PointerActionItem =\n | { type: \"pointerMove\"; duration: number; x: number; y: number; origin?: \"viewport\" }\n | { type: \"pointerDown\"; button: number }\n | { type: \"pointerUp\"; button: number }\n | { type: \"pause\"; duration: number };\n\n/** A single `touch` pointer input source and its ordered actions. */\nexport interface PointerActionSequence {\n type: \"pointer\";\n id: string;\n parameters: { pointerType: \"touch\" };\n actions: PointerActionItem[];\n}\n\n/**\n * Default swipe span as a fraction of the relevant screen axis when the step\n * gives no explicit `amount`. Three-quarters of the axis is a long, reliable\n * drag that still leaves margin at both ends so the endpoints never land on the\n * screen edge (where the OS may steal the gesture for system UI).\n */\nexport const DEFAULT_SWIPE_FRACTION = 0.75;\n\n/**\n * `scrollTo` probe swipes use a shorter span than the default one-shot scroll:\n * live mobile screens often have sticky search/header chrome near the top, and\n * an upward probe that starts in that chrome can be ignored by the scrollable\n * content underneath.\n */\nexport const SCROLL_TO_PROBE_SWIPE_FRACTION = 0.6;\n\n/**\n * Hard cap on swipe span as a fraction of the axis. A centred swipe reaches\n * `distance / 2` either side of centre, so 0.9 keeps endpoints within the inner\n * 5%–95% band even at the maximum.\n */\nexport const MAX_SWIPE_FRACTION = 0.9;\n\n/** Milliseconds the finger holds still after touching down, before dragging. */\nexport const SWIPE_HOLD_MS = 100;\n\n/** Milliseconds the drag itself takes (a natural, inertia-free swipe). */\nexport const SWIPE_MOVE_DURATION_MS = 300;\n\n/** Number of downward swipes `scrollTo` tries before sweeping back upward. */\nexport const SCROLL_TO_SWEEP_DEPTH = 10;\n\n/**\n * Shared vertical probe order for mobile `scrollTo`: first preserve the old\n * downward search depth, then reverse far enough to cross the starting viewport\n * and search above it too.\n */\nexport const SCROLL_TO_PROBE_DIRECTIONS: readonly SwipeDirection[] = [\n ...Array.from({ length: SCROLL_TO_SWEEP_DEPTH }, () => \"down\" as const),\n ...Array.from({ length: SCROLL_TO_SWEEP_DEPTH * 2 }, () => \"up\" as const)\n];\n\n/** Maximum directional swipes `scrollTo` attempts before giving up. */\nexport const MAX_SCROLL_TO_SWIPES = SCROLL_TO_PROBE_DIRECTIONS.length;\n\nexport type ScrollIntoViewProbe = {\n isVisible: () => Promise<boolean>;\n swipe: (direction: SwipeDirection) => Promise<void>;\n directions?: readonly SwipeDirection[];\n};\n\nconst OPPOSITE_SWIPE_DIRECTIONS: Record<SwipeDirection, SwipeDirection> = {\n up: \"down\",\n down: \"up\",\n left: \"right\",\n right: \"left\"\n};\n\n/** Run the shared mobile `scrollTo` probe, returning true once the target is visible. */\nexport async function probeScrollIntoView({\n isVisible,\n swipe,\n directions = SCROLL_TO_PROBE_DIRECTIONS\n}: ScrollIntoViewProbe): Promise<boolean> {\n if (await isVisible()) {\n return true;\n }\n for (const direction of directions) {\n await swipe(direction);\n if (await isVisible()) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Coerce an agent's window-size payload into a {@link ScreenSize}. Accepts the\n * `{ width, height }` shape returned by WDA `/window/size` and uiautomator2\n * `/window/current/size`; extra fields are ignored. Throws if either dimension\n * is missing or not a positive number, so gesture math never runs on a bad\n * screen size.\n */\nexport function toScreenSize(value: unknown, source: string): ScreenSize {\n const record = (value ?? {}) as Record<string, unknown>;\n const width = Number(record.width);\n const height = Number(record.height);\n if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {\n throw new Error(`${source} did not return a usable screen size`);\n }\n return { width, height };\n}\n\n/** Whether a direction scrolls along the vertical (height) axis. */\nfunction isVertical(direction: SwipeDirection): boolean {\n return direction === \"up\" || direction === \"down\";\n}\n\n/**\n * Resolve the swipe distance in device points for `direction` on a screen of\n * `size`. `amount` (the web step's pixel amount) maps 1:1 to swipe distance;\n * negative values use their absolute distance (direction reversal happens in\n * {@link buildDirectionalSwipe}), and omitted values default to\n * {@link DEFAULT_SWIPE_FRACTION} of the axis. The result is always clamped to\n * [1, {@link MAX_SWIPE_FRACTION} · axis] so a swipe can never run off-screen or\n * collapse to nothing.\n */\nexport function swipeDistanceFor(direction: SwipeDirection, size: ScreenSize, amount?: number): number {\n if (amount !== undefined && !Number.isFinite(amount)) {\n throw new Error(\"scroll amount must be a finite number\");\n }\n const axis = isVertical(direction) ? size.height : size.width;\n const requested = amount === undefined ? axis * DEFAULT_SWIPE_FRACTION : Math.abs(amount);\n const max = axis * MAX_SWIPE_FRACTION;\n return Math.max(1, Math.round(Math.min(requested, max)));\n}\n\n/** Distance used by mobile `scrollTo` probes to avoid sticky top/bottom chrome. */\nexport function scrollToProbeDistanceFor(direction: SwipeDirection, size: ScreenSize): number {\n const axis = isVertical(direction) ? size.height : size.width;\n return Math.max(1, Math.round(axis * SCROLL_TO_PROBE_SWIPE_FRACTION));\n}\n\n/**\n * Compute the start/end points of a screen-centred swipe. The finger travels\n * *opposite* to the scroll direction (see the module note), split evenly either\n * side of centre so the gesture stays centred regardless of distance.\n */\nexport function swipeEndpoints(\n direction: SwipeDirection,\n size: ScreenSize,\n distance: number\n): { start: Point; end: Point } {\n const cx = Math.round(size.width / 2);\n const cy = Math.round(size.height / 2);\n const half = Math.round(distance / 2);\n switch (direction) {\n case \"down\":\n // Reveal lower content ⇒ finger moves up.\n return { start: { x: cx, y: cy + half }, end: { x: cx, y: cy - half } };\n case \"up\":\n // Reveal upper content ⇒ finger moves down.\n return { start: { x: cx, y: cy - half }, end: { x: cx, y: cy + half } };\n case \"right\":\n // Reveal content to the right ⇒ finger moves left.\n return { start: { x: cx + half, y: cy }, end: { x: cx - half, y: cy } };\n case \"left\":\n // Reveal content to the left ⇒ finger moves right.\n return { start: { x: cx - half, y: cy }, end: { x: cx + half, y: cy } };\n }\n}\n\n/**\n * Build the W3C `touch` pointer action sequence for a swipe from `start` to\n * `end`: move to the origin, press, hold briefly, drag over\n * {@link SWIPE_MOVE_DURATION_MS}, then release. The shape is identical on both\n * agents; it is posted as `{ actions: [<this>] }` to the actions endpoint.\n */\nexport function buildSwipeActions(start: Point, end: Point): PointerActionSequence {\n return {\n type: \"pointer\",\n id: \"finger1\",\n parameters: { pointerType: \"touch\" },\n actions: [\n { type: \"pointerMove\", duration: 0, x: start.x, y: start.y, origin: \"viewport\" },\n { type: \"pointerDown\", button: 0 },\n { type: \"pause\", duration: SWIPE_HOLD_MS },\n { type: \"pointerMove\", duration: SWIPE_MOVE_DURATION_MS, x: end.x, y: end.y, origin: \"viewport\" },\n { type: \"pointerUp\", button: 0 }\n ]\n };\n}\n\n/**\n * One-shot helper: resolve the distance and endpoints for a centred directional\n * swipe and build its action sequence. Returns the derived geometry too so\n * callers (and tests) can assert the exact gesture.\n */\nexport function buildDirectionalSwipe(\n direction: SwipeDirection,\n size: ScreenSize,\n amount?: number\n): { actions: PointerActionSequence; distance: number; start: Point; end: Point } {\n const normalizedDirection =\n amount !== undefined && amount < 0 ? OPPOSITE_SWIPE_DIRECTIONS[direction] : direction;\n const distance = swipeDistanceFor(normalizedDirection, size, amount);\n const { start, end } = swipeEndpoints(normalizedDirection, size, distance);\n return { actions: buildSwipeActions(start, end), distance, start, end };\n}\n","/**\n * PROWL-058 / ARCH-009 — adb lifecycle for the Android target.\n *\n * Thin, injectable wrappers around the `adb` CLI plus the pure parsers they\n * depend on. Every command flows through an {@link AdbRunner} (device\n * lifecycle, install, forward) or an {@link AdbSpawner} (the long-running\n * `am instrument` agent process), so the whole surface is unit-testable with a\n * fake — `npm test` never needs a real device or emulator.\n */\nimport { execFile, spawn } from \"node:child_process\";\n\n/** Result of one adb invocation. */\nexport type AdbResult = { stdout: string; stderr: string; code: number };\n\n/** Runs one adb command to completion and resolves with its captured output. */\nexport type AdbRunner = (args: string[], options?: { timeoutMs?: number }) => Promise<AdbResult>;\n\n/** A handle to a spawned long-running adb process (the instrumentation server). */\nexport type AdbProcessHandle = { kill(): void };\n\n/** Spawns a long-running adb command (e.g. `am instrument -w`) in the background. */\nexport type AdbSpawner = (args: string[]) => AdbProcessHandle;\n\n/** One row of `adb devices -l`. */\nexport type AdbDevice = { serial: string; state: string; description: Record<string, string> };\n\n/** Default {@link AdbRunner}: shells out to the real `adb` on PATH. */\nexport const execFileAdbRunner: AdbRunner = (args, options) =>\n new Promise<AdbResult>((resolve) => {\n execFile(\n \"adb\",\n args,\n { encoding: \"utf-8\", timeout: options?.timeoutMs, maxBuffer: 16 * 1024 * 1024 },\n (error, stdout, stderr) => {\n const code =\n error && typeof (error as { code?: unknown }).code === \"number\"\n ? ((error as { code: number }).code)\n : error\n ? 1\n : 0;\n const capturedStderr = stderr ?? \"\";\n resolve({\n stdout: stdout ?? \"\",\n stderr: capturedStderr.trim() ? capturedStderr : (error?.message ?? \"\"),\n code\n });\n }\n );\n });\n\n/** Default {@link AdbSpawner}: spawns a detached-output `adb` child. */\nexport const spawnAdbProcess: AdbSpawner = (args) => {\n const child = spawn(\"adb\", args, { stdio: \"ignore\" });\n child.on(\"error\", () => {\n /* surfaced via readiness/preflight, not here */\n });\n return { kill: () => child.kill() };\n};\n\n/** Prefix adb args with `-s <serial>` when a serial is set. */\nexport function withSerial(serial: string | undefined, args: string[]): string[] {\n return serial ? [\"-s\", serial, ...args] : args;\n}\n\n/**\n * Parse `adb devices -l` output into structured rows. The header line\n * (`List of devices attached`) and blank lines are skipped.\n */\nexport function parseAdbDevices(stdout: string): AdbDevice[] {\n const devices: AdbDevice[] = [];\n for (const rawLine of stdout.split(/\\r?\\n/)) {\n const line = rawLine.trim();\n if (!line || /^list of devices attached/i.test(line)) {\n continue;\n }\n const [serial, state, ...rest] = line.split(/\\s+/);\n if (!serial || !state) {\n continue;\n }\n const description: Record<string, string> = {};\n for (const token of rest) {\n const eq = token.indexOf(\":\");\n if (eq > 0) {\n description[token.slice(0, eq)] = token.slice(eq + 1);\n }\n }\n devices.push({ serial, state, description });\n }\n return devices;\n}\n\n/** Devices that are fully booted and usable (`device` state). */\nexport function bootedDevices(devices: AdbDevice[]): AdbDevice[] {\n return devices.filter((device) => device.state === \"device\");\n}\n\n/**\n * Choose which device to drive. With `requested` set it must be attached and\n * booted. Otherwise exactly one booted device is required; zero or many raise an\n * actionable error listing what was found.\n */\nexport function selectDeviceSerial(devices: AdbDevice[], requested?: string): string {\n const booted = bootedDevices(devices);\n if (requested) {\n const match = devices.find((device) => device.serial === requested);\n if (!match) {\n const attached = devices.length > 0 ? devices.map((d) => d.serial).join(\", \") : \"none\";\n throw new Error(\n `Android device \"${requested}\" is not attached. Attached devices: ${attached}. ` +\n \"Check `adb devices -l`.\"\n );\n }\n if (match.state !== \"device\") {\n throw new Error(\n `Android device \"${requested}\" is present but not ready (state: ${match.state}). ` +\n \"Boot or authorize it, then retry.\"\n );\n }\n return requested;\n }\n\n if (booted.length === 0) {\n throw new Error(\n \"No booted Android device found. Start an emulator or connect a device with USB debugging, \" +\n \"then confirm it appears in `adb devices -l`.\"\n );\n }\n if (booted.length > 1) {\n throw new Error(\n `Multiple Android devices attached (${booted.map((d) => d.serial).join(\", \")}). ` +\n \"Set target.deviceSerial to pick one.\"\n );\n }\n return booted[0].serial;\n}\n\n/** List attached devices via `adb devices -l`. */\nexport async function listDevices(runner: AdbRunner): Promise<AdbDevice[]> {\n const result = await runner([\"devices\", \"-l\"], { timeoutMs: 10000 });\n if (result.code !== 0) {\n throw new Error(\n `\\`adb devices\\` failed (exit ${result.code}). Is adb on PATH and the server running? ` +\n (result.stderr.trim() || \"\").slice(0, 400)\n );\n }\n return parseAdbDevices(result.stdout);\n}\n\n/**\n * Parse the local port `adb forward tcp:0 tcp:<remote>` prints (dynamic port\n * allocation). adb echoes the chosen local port on stdout.\n */\nexport function parseForwardPort(stdout: string): number {\n const port = Number.parseInt(stdout.trim(), 10);\n if (!Number.isInteger(port) || port <= 0 || port > 65535) {\n throw new Error(`Could not parse a forwarded port from adb output: \"${stdout.trim()}\"`);\n }\n return port;\n}\n\n/** Parse the package name out of `aapt dump badging <apk>` output. */\nexport function parseAaptPackage(stdout: string): string | null {\n const match = /package:\\s*name='([^']+)'/.exec(stdout);\n return match?.[1] ?? null;\n}\n\n/** Forward a dynamically allocated local port to the device's `remotePort`. */\nexport async function forwardDynamicPort(\n runner: AdbRunner,\n serial: string,\n remotePort: number\n): Promise<number> {\n const result = await runner(withSerial(serial, [\"forward\", \"tcp:0\", `tcp:${remotePort}`]), {\n timeoutMs: 10000\n });\n if (result.code !== 0) {\n throw new Error(`adb forward failed (exit ${result.code}): ${result.stderr.trim()}`);\n }\n return parseForwardPort(result.stdout);\n}\n\n/** Remove a previously created port forward (best effort). */\nexport async function removeForward(runner: AdbRunner, serial: string, localPort: number): Promise<void> {\n await runner(withSerial(serial, [\"forward\", \"--remove\", `tcp:${localPort}`]), { timeoutMs: 10000 }).catch(\n () => undefined\n );\n}\n\n/** Install an APK (`-r` replace, `-g` grant runtime perms, `-t` allow test apks). */\nexport async function installApk(runner: AdbRunner, serial: string, apkPath: string): Promise<void> {\n const result = await runner(withSerial(serial, [\"install\", \"-r\", \"-t\", \"-g\", apkPath]), {\n timeoutMs: 120000\n });\n if (result.code !== 0 || /failure/i.test(result.stdout)) {\n throw new Error(\n `Failed to install APK \"${apkPath}\" (exit ${result.code}): ${\n (result.stderr.trim() || result.stdout.trim()).slice(0, 400)\n }`\n );\n }\n}\n\nfunction formatUnknownError(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction runnerPhaseError(message: string, error: unknown): Error {\n return new Error(`${message}: ${formatUnknownError(error)}`, { cause: error });\n}\n\n/**\n * Parse the launcher component (`pkg/.Activity`) out of\n * `cmd package resolve-activity --brief` output — the last non-empty line.\n * Returns null when nothing resolves (package absent or has no launcher).\n */\nexport function parseResolvedComponent(stdout: string, pkg: string): string | null {\n const lines = stdout.split(/\\r?\\n/).map((l) => l.trim()).filter(Boolean);\n for (let i = lines.length - 1; i >= 0; i--) {\n // A component line looks like `com.pkg/.Activity` or `com.pkg/com.pkg.Activity`.\n if (/^[\\w.]+\\/[\\w.$]+$/.test(lines[i]) && lines[i].startsWith(`${pkg}/`)) {\n return lines[i];\n }\n }\n return null;\n}\n\n/**\n * Launch a package's default LAUNCHER activity. Resolves the launcher component\n * with `cmd package resolve-activity` and starts it with `am start -n` — a\n * deterministic, TTY-independent path. (The previous `monkey`-based launch was\n * unreliable when adb runs it without a PTY — e.g. execFile in CI — where some\n * emulator images, notably API 35 `google_apis`, emit debug noise and exit\n * non-zero even on success. `am start` has none of that ambiguity.)\n */\nexport async function launchPackage(runner: AdbRunner, serial: string, pkg: string): Promise<void> {\n let resolved: AdbResult;\n try {\n resolved = await runner(\n withSerial(serial, [\n \"shell\",\n \"cmd\",\n \"package\",\n \"resolve-activity\",\n \"--brief\",\n \"-c\",\n \"android.intent.category.LAUNCHER\",\n pkg\n ]),\n { timeoutMs: 30000 }\n );\n } catch (error) {\n throw runnerPhaseError(\n `Failed to resolve the launcher activity for Android package \"${pkg}\" via adb`,\n error\n );\n }\n const component = parseResolvedComponent(resolved.stdout, pkg);\n if (resolved.code !== 0 || !component) {\n throw new Error(\n `Could not resolve a launcher activity for Android package \"${pkg}\" (exit ${resolved.code})` +\n `${(resolved.stdout + resolved.stderr).trim() ? `: ${(resolved.stdout + resolved.stderr).trim().slice(0, 300)}` : \"\"}. Is it installed?`\n );\n }\n let start: AdbResult;\n try {\n start = await runner(withSerial(serial, [\"shell\", \"am\", \"start\", \"-n\", component]), {\n timeoutMs: 30000\n });\n } catch (error) {\n throw runnerPhaseError(\n `Failed to launch Android package \"${pkg}\" with \\`am start\\` (${component}) via adb`,\n error\n );\n }\n if (start.code !== 0 || /error|does not exist|cannot start/i.test(start.stdout + start.stderr)) {\n throw new Error(\n `Failed to launch Android package \"${pkg}\" (${component}, exit ${start.code}): ${\n (start.stdout + start.stderr).trim().slice(0, 400)\n }.`\n );\n }\n}\n\n/** Force-stop a package (teardown). */\nexport async function forceStop(runner: AdbRunner, serial: string, pkg: string): Promise<void> {\n await runner(withSerial(serial, [\"shell\", \"am\", \"force-stop\", pkg]), { timeoutMs: 30000 });\n}\n\n/** Clear a package's data for a deterministic cold start (`pm clear`). */\nexport async function clearPackage(runner: AdbRunner, serial: string, pkg: string): Promise<void> {\n const result = await runner(withSerial(serial, [\"shell\", \"pm\", \"clear\", pkg]), { timeoutMs: 30000 });\n if (result.code !== 0 || !/success/i.test(result.stdout)) {\n throw new Error(\n `Failed to clear Android package \"${pkg}\" for cold start (exit ${result.code}): ${\n (result.stdout + result.stderr).trim().slice(0, 300)\n }`\n );\n }\n}\n\n/**\n * Start the uiautomator2 instrumentation server as a background process. The\n * `am instrument -w` call blocks for the server's lifetime, so it is spawned,\n * not awaited; the returned handle is killed during teardown.\n */\nexport function startInstrumentation(spawner: AdbSpawner, serial: string): AdbProcessHandle {\n return spawner(\n withSerial(serial, [\n \"shell\",\n \"am\",\n \"instrument\",\n \"-w\",\n \"-e\",\n \"disableAnalytics\",\n \"true\",\n \"io.appium.uiautomator2.server.test/androidx.test.runner.AndroidJUnitRunner\"\n ])\n );\n}\n","/**\n * PROWL-058 / ARCH-009 — HTTP/JSON transport for the on-device uiautomator2 agent.\n *\n * `appium-uiautomator2-server` exposes W3C-WebDriver-shaped endpoints over plain\n * HTTP. This module speaks them with the global `fetch` (no heavy WebDriver SDK,\n * per the `ai.ts` ethos), mirroring the mac-helper client's ergonomics: a\n * per-request deadline via `AbortController`, and cleanup on\n * every path. It exposes the semantic {@link AndroidAgentClient} the driver\n * consumes; tests fake either the `fetch` implementation or the client itself.\n */\nimport type { AndroidAgentClient, AndroidQuery } from \"./android-driver.js\";\nimport { androidQueryToLocator } from \"./android-driver.js\";\nimport { toScreenSize, type PointerActionSequence, type ScreenSize } from \"./touch-gestures.js\";\n\n/** The subset of `fetch` this module uses; overridable in tests. */\nexport type FetchLike = (url: string, init: RequestInit) => Promise<Response>;\n\n/** Default per-request deadline for the agent transport. */\nexport const DEFAULT_AGENT_REQUEST_TIMEOUT_MS = 30000;\n\n/** The W3C element-reference key both current and legacy servers may use. */\nconst W3C_ELEMENT_KEY = \"element-6066-11e4-a52e-4f735466cecf\";\n\nexport type Uia2TransportOptions = {\n /** Base URL including the `/wd/hub` prefix, e.g. `http://127.0.0.1:6790/wd/hub`. */\n baseUrl: string;\n requestTimeoutMs?: number;\n fetchImpl?: FetchLike;\n};\n\n/** An HTTP-level failure from the agent, carrying the status and parsed body. */\nexport class Uia2HttpError extends Error {\n constructor(\n message: string,\n readonly status: number,\n readonly webdriverError?: string\n ) {\n super(message);\n this.name = \"Uia2HttpError\";\n }\n}\n\n/** Low-level request/response transport with a per-request deadline. */\nexport class Uia2Transport {\n private readonly baseUrl: string;\n private readonly requestTimeoutMs: number;\n private readonly fetchImpl: FetchLike;\n\n constructor(options: Uia2TransportOptions) {\n this.baseUrl = options.baseUrl.replace(/\\/+$/, \"\");\n this.requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_AGENT_REQUEST_TIMEOUT_MS;\n const injected = options.fetchImpl;\n if (injected) {\n this.fetchImpl = injected;\n } else if (typeof fetch === \"function\") {\n this.fetchImpl = (url, init) => fetch(url, init);\n } else {\n throw new Error(\"global fetch is unavailable; Node 20+ is required for the Android target\");\n }\n }\n\n /**\n * Send one request and return the parsed `value` field. Rejects with a\n * {@link Uia2HttpError} on a non-2xx response, or a timeout error when the\n * per-request deadline elapses.\n */\n async request(method: string, path: string, body?: unknown, timeoutMs?: number): Promise<unknown> {\n const requestTimeoutMs = timeoutMs ?? this.requestTimeoutMs;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), requestTimeoutMs);\n timer.unref?.();\n const url = `${this.baseUrl}${path}`;\n let response: Response;\n try {\n response = await this.fetchImpl(url, {\n method,\n signal: controller.signal,\n headers: body !== undefined ? { \"content-type\": \"application/json\" } : undefined,\n body: body !== undefined ? JSON.stringify(body) : undefined\n });\n } catch (error) {\n if (controller.signal.aborted) {\n const shown =\n requestTimeoutMs >= 1000\n ? `${Math.round(requestTimeoutMs / 1000)}s`\n : `${requestTimeoutMs}ms`;\n throw new Error(`uiautomator2 request ${method} ${path} timed out after ${shown}`);\n }\n throw error instanceof Error ? error : new Error(String(error));\n } finally {\n clearTimeout(timer);\n }\n\n const text = await response.text();\n const parsed = parseJson(text);\n if (!response.ok) {\n const wdError = extractWebdriverError(parsed);\n throw new Uia2HttpError(\n `uiautomator2 ${method} ${path} failed (${response.status})${wdError ? `: ${wdError}` : \"\"}`,\n response.status,\n wdError\n );\n }\n return (parsed as { value?: unknown } | undefined)?.value;\n }\n}\n\nfunction parseJson(text: string): unknown {\n if (!text) {\n return undefined;\n }\n try {\n return JSON.parse(text);\n } catch {\n return undefined;\n }\n}\n\nfunction extractWebdriverError(parsed: unknown): string | undefined {\n const value = (parsed as { value?: unknown } | undefined)?.value;\n if (value && typeof value === \"object\") {\n const record = value as Record<string, unknown>;\n const error = typeof record.error === \"string\" ? record.error : undefined;\n const message = typeof record.message === \"string\" ? record.message : undefined;\n return error ?? message;\n }\n return undefined;\n}\n\n/** Extract an element id from a W3C element-reference object, or null. */\nexport function extractElementId(value: unknown): string | null {\n if (!value || typeof value !== \"object\") {\n return null;\n }\n const record = value as Record<string, unknown>;\n const id = record[W3C_ELEMENT_KEY] ?? record.ELEMENT;\n return typeof id === \"string\" ? id : null;\n}\n\nfunction isNoSuchElement(error: unknown): boolean {\n if (error instanceof Uia2HttpError) {\n return error.status === 404 || (error.webdriverError ?? \"\").includes(\"no such element\");\n }\n return false;\n}\n\n/**\n * Create a uiautomator2 session and return its id. Uses the W3C `capabilities`\n * envelope; the server ignores the empty match set and starts a default session.\n */\nexport async function createUia2Session(transport: Uia2Transport): Promise<string> {\n const value = await transport.request(\"POST\", \"/session\", {\n capabilities: { alwaysMatch: {}, firstMatch: [{}] }\n });\n const record = (value ?? {}) as Record<string, unknown>;\n const sessionId = record.sessionId;\n if (typeof sessionId === \"string\" && sessionId.length > 0) {\n return sessionId;\n }\n throw new Error(\"uiautomator2 did not return a session id\");\n}\n\n/** Poll `GET /status` until the agent reports ready or the deadline elapses. */\nexport async function waitForAgentReady(\n transport: Uia2Transport,\n options: { deadlineMs: number; intervalMs?: number } = { deadlineMs: 30000 }\n): Promise<void> {\n const interval = options.intervalMs ?? 300;\n const deadline = Date.now() + options.deadlineMs;\n let lastError: unknown;\n for (;;) {\n try {\n const remainingMs = Math.max(1, deadline - Date.now());\n const value = await transport.request(\"GET\", \"/status\", undefined, Math.min(remainingMs, 5000));\n const ready = (value as { ready?: unknown } | undefined)?.ready;\n if (ready === undefined || ready === true) {\n return;\n }\n } catch (error) {\n lastError = error;\n }\n if (Date.now() >= deadline) {\n const detail = lastError instanceof Error ? `: ${lastError.message}` : \"\";\n throw new Error(`uiautomator2 agent did not become ready within ${options.deadlineMs}ms${detail}`);\n }\n await sleep(Math.min(interval, Math.max(0, deadline - Date.now())));\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => {\n const timer = setTimeout(resolve, ms);\n timer.unref?.();\n });\n}\n\n/**\n * Build the semantic {@link AndroidAgentClient} over a live session. `close`\n * deletes the session (best effort); the transport itself is stateless.\n */\nexport function createUia2AgentClient(\n transport: Uia2Transport,\n sessionId: string,\n options: { appPackage?: string } = {}\n): AndroidAgentClient {\n const base = `/session/${sessionId}`;\n\n async function locate(query: AndroidQuery, path: string): Promise<unknown> {\n return transport.request(\n \"POST\",\n `${base}${path}`,\n androidQueryToLocator(query, { appPackage: options.appPackage })\n );\n }\n\n return {\n async findElement(query: AndroidQuery): Promise<string | null> {\n try {\n return extractElementId(await locate(query, \"/element\"));\n } catch (error) {\n if (isNoSuchElement(error)) {\n return null;\n }\n throw error;\n }\n },\n async findElements(query: AndroidQuery): Promise<string[]> {\n const value = await locate(query, \"/elements\");\n if (!Array.isArray(value)) {\n return [];\n }\n return value.map((entry) => extractElementId(entry)).filter((id): id is string => id !== null);\n },\n async click(elementId: string): Promise<void> {\n await transport.request(\"POST\", `${base}/element/${elementId}/click`, {});\n },\n async setValue(elementId: string, text: string): Promise<void> {\n // W3C `element/value` takes `{ text }`; uiautomator2 sets it unicode-safely.\n await transport.request(\"POST\", `${base}/element/${elementId}/value`, { text });\n },\n async getText(elementId: string): Promise<string | null> {\n const value = await transport.request(\"GET\", `${base}/element/${elementId}/text`);\n return typeof value === \"string\" ? value : value == null ? null : String(value);\n },\n async pressKeyCode(keyCode: number): Promise<void> {\n await transport.request(\"POST\", `${base}/appium/device/press_keycode`, { keycode: keyCode });\n },\n async windowSize(): Promise<ScreenSize> {\n // The direct uiautomator2 server exposes JSONWP window-size routes.\n const value = await transport.request(\"GET\", `${base}/window/current/size`);\n return toScreenSize(value, \"uiautomator2 /window/current/size\");\n },\n async performActions(actions: PointerActionSequence): Promise<void> {\n // W3C actions endpoint; uiautomator2 replays the touch pointer sequence.\n await transport.request(\"POST\", `${base}/actions`, { actions: [actions] });\n },\n async screenshotPng(): Promise<Buffer> {\n const value = await transport.request(\"GET\", `${base}/screenshot`);\n if (typeof value !== \"string\") {\n throw new Error(\"uiautomator2 screenshot did not return base64 data\");\n }\n return Buffer.from(value, \"base64\");\n },\n async source(): Promise<string> {\n // uiautomator2 returns the `uiautomator dump` XML hierarchy as a string.\n const value = await transport.request(\"GET\", `${base}/source`);\n if (typeof value !== \"string\") {\n throw new Error(\"uiautomator2 /source did not return XML text; cannot analyze Android UI hierarchy\");\n }\n return value;\n },\n async close(): Promise<void> {\n await transport.request(\"DELETE\", base).catch(() => undefined);\n }\n };\n}\n","/**\n * PROWL-058 / ARCH-009 — launch/teardown orchestration for the Android target.\n *\n * Ties together the three layers: adb lifecycle ({@link ./android-adb.js}), the\n * uiautomator2 HTTP agent ({@link ./android-agent.js}), and the driver\n * ({@link ./android-driver.js}). The two prebuilt agent APKs ship inside the\n * `appium-uiautomator2-server` npm package (Apache-2.0) and are resolved from\n * node_modules — never committed to this repo. Every external dependency (adb\n * runner, background spawner, agent connection) is injectable so the whole flow\n * is unit-testable without a device.\n */\nimport { createRequire } from \"node:module\";\nimport { execFile } from \"node:child_process\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { createAndroidDriver, type AndroidAgentClient } from \"./android-driver.js\";\nimport {\n clearPackage,\n execFileAdbRunner,\n forceStop,\n forwardDynamicPort,\n installApk,\n launchPackage,\n listDevices,\n parseAaptPackage,\n removeForward,\n selectDeviceSerial,\n spawnAdbProcess,\n startInstrumentation,\n type AdbProcessHandle,\n type AdbRunner,\n type AdbSpawner\n} from \"./android-adb.js\";\nimport {\n createUia2AgentClient,\n createUia2Session,\n DEFAULT_AGENT_REQUEST_TIMEOUT_MS,\n Uia2Transport,\n waitForAgentReady\n} from \"./android-agent.js\";\nimport { assertAndroidAppAllowed } from \"../config/target.js\";\nimport type { SessionDriver } from \"./driver.js\";\n\n/** The remote port the uiautomator2 server listens on inside the device. */\nexport const UIA2_REMOTE_PORT = 6790;\n\n/** Locations of the two prebuilt agent APKs. */\nexport type AgentApks = { serverApk: string; testApk: string };\n\n/** Establishes a live {@link AndroidAgentClient} against a forwarded local port. */\nexport type AgentConnector = (options: {\n host: string;\n port: number;\n requestTimeoutMs: number;\n readyDeadlineMs: number;\n /** Target app's package name; qualifies bare `id=` selectors on the device. */\n appPackage?: string;\n}) => Promise<AndroidAgentClient>;\n\n/** Resolve a package name from an `.apk` file, or null when it can't be determined. */\nexport type AaptResolver = (apkPath: string) => Promise<string | null>;\n\n/**\n * Resolve the two agent APKs shipped inside `appium-uiautomator2-server`. They\n * live under the package's `apks/` folder; the server APK is version-stamped.\n */\nexport function resolveAgentApks(requireFn: NodeRequire = createRequire(import.meta.url)): AgentApks {\n let pkgJsonPath: string;\n try {\n pkgJsonPath = requireFn.resolve(\"appium-uiautomator2-server/package.json\");\n } catch {\n throw new Error(\n \"The Android target requires the `appium-uiautomator2-server` package (its prebuilt APKs). \" +\n \"It is an optional dependency of prowl-tools; it may have been skipped (--omit=optional) \" +\n \"or failed to install. Restore it for a global Prowl install with: \" +\n \"npm install -g appium-uiautomator2-server@10.6.2. If Prowl is installed locally in a \" +\n \"project, run: npm install appium-uiautomator2-server@10.6.2\"\n );\n }\n const pkgDir = path.dirname(pkgJsonPath);\n const version = (requireFn(pkgJsonPath) as { version: string }).version;\n const serverApk = path.join(pkgDir, \"apks\", `appium-uiautomator2-server-v${version}.apk`);\n const testApk = path.join(pkgDir, \"apks\", \"appium-uiautomator2-server-debug-androidTest.apk\");\n for (const apk of [serverApk, testApk]) {\n if (!fs.existsSync(apk)) {\n throw new Error(`Expected uiautomator2 agent APK is missing: ${apk}. Reinstall dependencies.`);\n }\n }\n return { serverApk, testApk };\n}\n\nfunction looksLikeApk(app: string): boolean {\n return app.toLowerCase().endsWith(\".apk\");\n}\n\n/** Default aapt-based package resolver: tries `aapt` then `aapt2 dump badging`. */\nexport const execFileAaptResolver: AaptResolver = async (apkPath) => {\n for (const tool of [\"aapt\", \"aapt2\"]) {\n const output = await new Promise<string | null>((resolve) => {\n execFile(\n tool,\n [\"dump\", \"badging\", apkPath],\n { encoding: \"utf-8\", timeout: 20000, maxBuffer: 8 * 1024 * 1024 },\n (error, stdout) => resolve(error ? null : stdout)\n );\n });\n const pkg = output ? parseAaptPackage(output) : null;\n if (pkg) {\n return pkg;\n }\n }\n return null;\n};\n\n/** Default connector: builds the HTTP transport, waits for readiness, opens a session. */\nexport const defaultAgentConnector: AgentConnector = async ({\n host,\n port,\n requestTimeoutMs,\n readyDeadlineMs,\n appPackage\n}) => {\n const transport = new Uia2Transport({\n baseUrl: `http://${host}:${port}/wd/hub`,\n requestTimeoutMs\n });\n await waitForAgentReady(transport, { deadlineMs: readyDeadlineMs });\n const sessionId = await createUia2Session(transport);\n return createUia2AgentClient(transport, sessionId, { appPackage });\n};\n\nexport type AndroidSession = {\n client: AndroidAgentClient;\n driver: SessionDriver;\n /** The resolved package name being driven. */\n package: string;\n /** The adb serial of the device being driven. */\n serial: string;\n /** Tear down the session, agent, port forward, and app (best effort). */\n teardown(): Promise<void>;\n};\n\nexport type LaunchAndroidOptions = {\n /** Package name or `.apk` path. */\n app: string;\n /** adb serial when more than one device is attached. */\n deviceSerial?: string;\n /** `pm clear` the package before launch for a deterministic cold start. */\n coldStart?: boolean;\n timeoutMs?: number;\n // --- injectables (tests / advanced use) ---\n runner?: AdbRunner;\n spawner?: AdbSpawner;\n agentConnector?: AgentConnector;\n apks?: AgentApks;\n aaptResolver?: AaptResolver;\n /** Optional app scope guardrail from config.guardrails.allowedApps. */\n allowedApps?: string[];\n};\n\n/**\n * Resolve the package to launch. A bare package name is used directly; an `.apk`\n * is installed, then its package name is read via aapt (or an actionable error\n * asks the user to name the package).\n */\nasync function resolvePackage(\n app: string,\n runner: AdbRunner,\n serial: string,\n aaptResolver: AaptResolver,\n allowedApps: string[]\n): Promise<string> {\n if (!looksLikeApk(app)) {\n assertAndroidAppAllowed(allowedApps, app);\n return app;\n }\n const apkPath = path.resolve(app);\n if (!fs.existsSync(apkPath)) {\n throw new Error(`APK not found: ${apkPath}`);\n }\n const pkg = await aaptResolver(apkPath);\n if (!pkg) {\n throw new Error(\n `Could not determine the package name for \"${app}\". Put Android build-tools ` +\n \"`aapt`/`aapt2` on PATH, or set target.app to the package name instead of the .apk path.\"\n );\n }\n assertAndroidAppAllowed(allowedApps, apkPath, pkg);\n await installApk(runner, serial, apkPath);\n return pkg;\n}\n\n/**\n * Preflight, install, launch, and attach the uiautomator2 agent, returning a\n * live {@link AndroidSession}. Actionable errors cover each failure mode: adb\n * missing / no booted device (device selection), agent unreachable (readiness).\n */\nexport async function launchAndroidSession(options: LaunchAndroidOptions): Promise<AndroidSession> {\n const runner = options.runner ?? execFileAdbRunner;\n const spawner: AdbSpawner = options.spawner ?? spawnAdbProcess;\n const connector = options.agentConnector ?? defaultAgentConnector;\n const aaptResolver = options.aaptResolver ?? execFileAaptResolver;\n const apks = options.apks ?? resolveAgentApks();\n\n const requestTimeoutMs = Math.max(options.timeoutMs ?? 10000, DEFAULT_AGENT_REQUEST_TIMEOUT_MS) + 5000;\n const readyDeadlineMs = Math.max(options.timeoutMs ?? 10000, DEFAULT_AGENT_REQUEST_TIMEOUT_MS);\n\n // Preflight: adb reachable + exactly one (or the requested) booted device.\n const devices = await listDevices(runner);\n const serial = selectDeviceSerial(devices, options.deviceSerial);\n\n const pkg = await resolvePackage(options.app, runner, serial, aaptResolver, options.allowedApps ?? []);\n\n // Install the on-device agent (both APKs) before instrumenting.\n await installApk(runner, serial, apks.serverApk);\n await installApk(runner, serial, apks.testApk);\n\n if (options.coldStart) {\n await clearPackage(runner, serial, pkg);\n }\n await launchPackage(runner, serial, pkg);\n\n let instrumentation: AdbProcessHandle | undefined;\n let localPort: number | undefined;\n let client: AndroidAgentClient | undefined;\n\n let tornDown = false;\n const teardown = async (): Promise<void> => {\n if (tornDown) {\n return;\n }\n tornDown = true;\n if (client) {\n await client.close().catch(() => undefined);\n }\n instrumentation?.kill();\n const forwardedPort = localPort;\n localPort = undefined;\n if (forwardedPort !== undefined) {\n await removeForward(runner, serial, forwardedPort);\n }\n await forceStop(runner, serial, pkg).catch(() => undefined);\n };\n\n try {\n instrumentation = startInstrumentation(spawner, serial);\n localPort = await forwardDynamicPort(runner, serial, UIA2_REMOTE_PORT);\n client = await connector({\n host: \"127.0.0.1\",\n port: localPort,\n requestTimeoutMs,\n readyDeadlineMs,\n appPackage: pkg\n });\n // Some emulator images briefly foreground the instrumentation process; put\n // the target app back on top after the agent session is ready.\n await launchPackage(runner, serial, pkg);\n const driver = createAndroidDriver(client, { appLabel: pkg });\n return { client, driver, package: pkg, serial, teardown };\n } catch (error) {\n await teardown();\n throw error;\n }\n}\n\n/** Tear down an Android session (agent session, instrumentation, forward, app). */\nexport async function closeAndroidSession(session: AndroidSession): Promise<void> {\n await session.teardown();\n}\n","/**\n * PROWL-059 / ARCH-010 — iOS simulator implementation of {@link SessionDriver}.\n *\n * `IosDriver` drives a native iOS app through a prebuilt WebDriverAgent (WDA)\n * runner over its W3C-shaped HTTP/JSON API ({@link IosAgentClient}). Like the\n * macOS and Android drivers it implements only the portable subset of the driver\n * surface — its `capabilities` set is honest: `query`, `interact`, `wait`,\n * `screenshot`. Web-only verbs (navigation, network, dialogs, files, downloads,\n * script evaluation) are unsupported stubs; the runner never reaches them because\n * both the target step-compatibility check and the runtime capability gate reject\n * web-only steps for native targets.\n *\n * Screenshots are captured via `simctl` (injected as `captureScreenshot`), not\n * WDA, so artifacts still work even if the agent wedges.\n *\n * Selector dialect (parsed by the shared native selector engine\n * `../selector/native.ts` (PROWL-060) — the single source of truth for the\n * grammar and attribute mapping — then mapped here to an {@link IosQuery} the\n * agent matches on the simulator). Semantics mirror the macOS/Android drivers so\n * `id=`/`label=`/`text=`/`role=` mean the same thing across native targets:\n * id=save → accessibility id (accessibilityIdentifier / name)\n * label=\"Submit\" → predicate `label == \"Submit\"` (exact)\n * role=XCUIElementTypeButton → element class (shorthand `Button` is accepted too)\n * role=Button[name=\"Save\"] → class + visible text (label/value substring)\n * text=\"Save\" | Save → label/value substring\n * :focus → predicate `hasKeyboardFocus == 1`\n */\nimport {\n normalizeXcuiClassName,\n parseNativeSelector,\n unwrapNativeTextSelector,\n type NativeSelector\n} from \"../selector/native.js\";\nimport {\n buildDirectionalSwipe,\n MAX_SCROLL_TO_SWIPES,\n probeScrollIntoView,\n scrollToProbeDistanceFor,\n type PointerActionSequence,\n type ScreenSize,\n type SwipeDirection\n} from \"./touch-gestures.js\";\nimport type {\n DialogAction,\n DriverCapability,\n DriverDownload,\n DriverResponse,\n DriverRoute,\n NavigateOptions,\n SessionDriver\n} from \"./driver.js\";\n\n// Re-export the role-normalization rule from the shared native selector engine\n// (PROWL-060), its single source of truth, so existing importers of\n// `normalizeXcuiClassName` from this module keep working.\nexport { normalizeXcuiClassName } from \"../selector/native.js\";\n\n/** A structured query the {@link IosAgentClient} resolves against the simulator. */\nexport type IosQuery =\n | { by: \"accessibilityId\"; value: string }\n | { by: \"label\"; value: string }\n | { by: \"role\"; role: string; name?: string }\n | { by: \"text\"; value: string }\n | { by: \"focused\" };\n\n/** A WDA locator strategy ({@code using}) + its value, as the agent expects. */\nexport type IosLocator = { using: string; value: string };\n\n/**\n * The semantic transport `IosDriver` talks to: element lookups return opaque\n * element ids, and interactions take those ids. The HTTP/WDA implementation lives\n * in {@link ./ios-agent.js}; tests fake this interface.\n */\nexport interface IosAgentClient {\n /** Resolve the first element matching `query`, or null when none match. */\n findElement(query: IosQuery): Promise<string | null>;\n /** Resolve every element id matching `query` (empty when none match). */\n findElements(query: IosQuery): Promise<string[]>;\n click(elementId: string): Promise<void>;\n /** Replace an element's text (W3C `element/value`). */\n setValue(elementId: string, text: string): Promise<void>;\n getText(elementId: string): Promise<string | null>;\n /** True only when WDA reports the element is displayed in the current viewport. */\n isDisplayed(elementId: string): Promise<boolean>;\n /** Send raw key sequences to the focused element (WDA `/wda/keys`). */\n sendKeys(keys: string[]): Promise<void>;\n /** Return to the springboard home screen (WDA `/wda/homescreen`). */\n homescreen(): Promise<void>;\n /** Current screen size in points (WDA `/window/size`), for gesture geometry. */\n windowSize(): Promise<ScreenSize>;\n /** Perform a W3C pointer action sequence (WDA `POST /session/:id/actions`). */\n performActions(actions: PointerActionSequence): Promise<void>;\n /**\n * Return the current UI hierarchy as WebDriverAgent `/source` XML. Present on\n * live clients and consumed by the analyzer (PROWL-061); optional so lighter\n * fakes that only drive/query need not implement it.\n */\n source?(): Promise<string>;\n close(): Promise<void>;\n}\n\nexport type IosDriverOptions = {\n /** Bundle id, used only for the informational `currentUrl()` value. */\n appLabel?: string;\n /** Capture a PNG screenshot to `path` (injected simctl capture). */\n captureScreenshot: (path: string) => Promise<void>;\n};\n\nconst IOS_CAPABILITIES: ReadonlySet<DriverCapability> = new Set<DriverCapability>([\n \"query\",\n \"interact\",\n \"wait\",\n \"screenshot\"\n]);\n\n/** Poll interval while waiting for a selector to appear. */\nconst WAIT_POLL_INTERVAL_MS = 250;\n/** Default wait deadline when a step does not specify one. */\nconst DEFAULT_WAIT_TIMEOUT_MS = 5000;\n/** Maximum concurrent WDA `/displayed` probes used for visibility checks. */\nconst DISPLAYED_CHECK_CONCURRENCY = 4;\n\n/**\n * Key names the `press` step supports on iOS, sorted for error messages. `enter`/\n * `return` send a newline, `delete`/`backspace` send a backspace (both via WDA's\n * key endpoint), and `home` returns to the springboard.\n */\nexport const IOS_PRESS_KEYS: readonly string[] = [\"backspace\", \"del\", \"delete\", \"enter\", \"home\", \"return\"];\n\n/** Escape a string for embedding inside a double-quoted NSPredicate string literal. */\nexport function escapePredicateArg(value: string): string {\n return value.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"');\n}\n\n/**\n * Map a neutral {@link NativeSelector} (parsed by the shared engine) onto this\n * driver's WDA query shape. iOS's `id=` targets the accessibility id and `label=`\n * the accessibility label; everything else maps one-to-one.\n */\nfunction toIosQuery(selector: NativeSelector): IosQuery {\n switch (selector.kind) {\n case \"focused\":\n return { by: \"focused\" };\n case \"id\":\n return { by: \"accessibilityId\", value: selector.value };\n case \"role\":\n return selector.name !== undefined\n ? { by: \"role\", role: selector.role, name: selector.name }\n : { by: \"role\", role: selector.role };\n case \"label\":\n return { by: \"label\", value: selector.value };\n case \"text\":\n return { by: \"text\", value: selector.value };\n }\n}\n\n/**\n * Parse a Prowl selector string into an {@link IosQuery}. Bare text matches by\n * text. The grammar (and its `label=`-in-assertions trap) is defined once in the\n * shared native selector engine ({@link parseNativeSelector}, PROWL-060); this\n * only maps the neutral result onto iOS's WDA query.\n */\nexport function parseIosSelector(selector: string): IosQuery {\n return toIosQuery(parseNativeSelector(selector));\n}\n\n/**\n * Translate an {@link IosQuery} into a WDA locator strategy. `id` uses the native\n * `accessibility id` strategy and bare roles use `class name`; everything else\n * composes an NSPredicate string (WDA's `predicate string` strategy).\n */\nexport function iosQueryToLocator(query: IosQuery): IosLocator {\n switch (query.by) {\n case \"accessibilityId\":\n return { using: \"accessibility id\", value: query.value };\n case \"label\":\n // Exact accessibilityLabel match (mirrors Android content-desc exactness).\n return { using: \"predicate string\", value: `label == \"${escapePredicateArg(query.value)}\"` };\n case \"text\": {\n // Visible text (label/value) substring — mirrors macOS/Android text= semantics.\n const escaped = escapePredicateArg(query.value);\n return {\n using: \"predicate string\",\n value: `label CONTAINS \"${escaped}\" OR value CONTAINS \"${escaped}\"`\n };\n }\n case \"focused\":\n return { using: \"predicate string\", value: \"hasKeyboardFocus == 1\" };\n case \"role\": {\n const className = normalizeXcuiClassName(query.role);\n if (query.name === undefined || query.name.length === 0) {\n return { using: \"class name\", value: className };\n }\n const escapedClass = escapePredicateArg(className);\n const escapedName = escapePredicateArg(query.name);\n // Class + visible-text (substring) name, mirroring Android role+name.\n return {\n using: \"predicate string\",\n value:\n `type == \"${escapedClass}\" AND ` +\n `(label CONTAINS \"${escapedName}\" OR value CONTAINS \"${escapedName}\")`\n };\n }\n }\n}\n\n/** The literal text a `text=` selector matches, else null (mirrors the web driver). */\nexport function unwrapIosTextSelector(selector: string): string | null {\n return unwrapNativeTextSelector(selector);\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n}\n\n/** Wrap a live {@link IosAgentClient} as a {@link SessionDriver}. */\nexport function createIosDriver(client: IosAgentClient, options: IosDriverOptions): SessionDriver {\n const unsupported = (verb: string): Error => new Error(`${verb} is not supported by the iOS target`);\n const rejectUnsupported = (verb: string): Promise<never> => Promise.reject(unsupported(verb));\n\n async function resolveOne(selector: string): Promise<string> {\n const id = await client.findElement(parseIosSelector(selector));\n if (id === null) {\n throw new Error(`No element matched selector: ${selector}`);\n }\n return id;\n }\n\n async function clickSelector(selector: string): Promise<void> {\n await client.click(await resolveOne(selector));\n }\n\n async function fillSelector(selector: string, value: string): Promise<void> {\n await client.setValue(await resolveOne(selector), value);\n }\n\n async function swipe(direction: SwipeDirection, amount?: number, size?: ScreenSize): Promise<void> {\n const actualSize = size ?? (await client.windowSize());\n const { actions } = buildDirectionalSwipe(direction, actualSize, amount);\n await client.performActions(actions);\n }\n\n async function visibleElementIds(query: IosQuery): Promise<string[]> {\n const ids = await client.findElements(query);\n const visible: Array<string | null> = Array.from({ length: ids.length }, () => null);\n let nextIndex = 0;\n const workerCount = Math.min(DISPLAYED_CHECK_CONCURRENCY, ids.length);\n\n await Promise.all(\n Array.from({ length: workerCount }, async () => {\n for (;;) {\n const index = nextIndex;\n nextIndex += 1;\n if (index >= ids.length) {\n return;\n }\n const id = ids[index];\n if (await client.isDisplayed(id)) {\n visible[index] = id;\n }\n }\n })\n );\n\n return visible.filter((id): id is string => id !== null);\n }\n\n async function hasVisibleElement(query: IosQuery): Promise<boolean> {\n const ids = await client.findElements(query);\n if (ids.length === 0) {\n return false;\n }\n if (await client.isDisplayed(ids[0])) {\n return true;\n }\n let found = false;\n let nextIndex = 1;\n const workerCount = Math.min(DISPLAYED_CHECK_CONCURRENCY, ids.length - 1);\n\n await Promise.all(\n Array.from({ length: workerCount }, async () => {\n for (;;) {\n if (found) {\n return;\n }\n const index = nextIndex;\n nextIndex += 1;\n if (index >= ids.length) {\n return;\n }\n if (await client.isDisplayed(ids[index])) {\n found = true;\n return;\n }\n }\n })\n );\n\n return found;\n }\n\n async function pressKey(key: string): Promise<void> {\n const name = key.trim().toLowerCase();\n if (name === \"enter\" || name === \"return\") {\n await client.sendKeys([\"\\n\"]);\n return;\n }\n if (name === \"delete\" || name === \"backspace\" || name === \"del\") {\n await client.sendKeys([\"\\b\"]);\n return;\n }\n if (name === \"home\") {\n await client.homescreen();\n return;\n }\n throw new Error(\n `Unsupported key \"${key}\" for the iOS target. Supported keys: ${IOS_PRESS_KEYS.join(\", \")}.`\n );\n }\n\n return {\n capabilities: IOS_CAPABILITIES,\n\n // navigation -----------------------------------------------------------\n goto(_url: string, _options?: NavigateOptions): Promise<void> {\n return rejectUnsupported(\"navigate\");\n },\n currentUrl(): string {\n return `ios:${options.appLabel ?? \"\"}`;\n },\n\n // queries --------------------------------------------------------------\n async count(selector: string): Promise<number> {\n return (await client.findElements(parseIosSelector(selector))).length;\n },\n async visibleCount(selector: string): Promise<number> {\n return (await visibleElementIds(parseIosSelector(selector))).length;\n },\n async textContent(selector: string): Promise<string | null> {\n const id = await client.findElement(parseIosSelector(selector));\n if (id === null) {\n return null;\n }\n return client.getText(id);\n },\n\n // interactions ---------------------------------------------------------\n click: clickSelector,\n clickFirst: clickSelector,\n fill: fillSelector,\n fillFirst: fillSelector,\n async press(_selector: string, key: string): Promise<void> {\n // WDA keys dispatch to the focused element, so the selector is advisory;\n // callers should focus the field first (e.g. click / fill).\n await pressKey(key);\n },\n selectOption(): Promise<void> {\n return rejectUnsupported(\"select\");\n },\n selectOptionFirst(): Promise<void> {\n return rejectUnsupported(\"select\");\n },\n hover(): Promise<void> {\n // No hover concept on touch devices.\n return rejectUnsupported(\"hover\");\n },\n // Screen-centred swipe via the W3C actions endpoint (PROWL-080). Direction\n // semantics match the web step: scrolling \"down\" reveals lower content, so\n // the finger drags up. See ./touch-gestures.ts.\n async scroll(direction: \"up\" | \"down\" | \"left\" | \"right\", amount?: number): Promise<void> {\n await swipe(direction, amount);\n },\n // Resolve the element, short-circuiting only if WDA reports a matching\n // element displayed in the viewport; hierarchy-only matches can be offscreen.\n // Otherwise use the shared bounded down/up mobile probe before failing.\n async scrollIntoView(selector: string): Promise<void> {\n const query = parseIosSelector(selector);\n let probeSize: ScreenSize | undefined;\n const found = await probeScrollIntoView({\n isVisible: () => hasVisibleElement(query),\n swipe: async (direction) => {\n probeSize ??= await client.windowSize();\n await swipe(direction, scrollToProbeDistanceFor(direction, probeSize), probeSize);\n }\n });\n if (found) {\n return;\n }\n throw new Error(\n `scrollTo: element \"${selector}\" not visible after ${MAX_SCROLL_TO_SWIPES} scroll attempts on the iOS target`\n );\n },\n setInputFiles(): Promise<void> {\n return rejectUnsupported(\"setInputFiles\");\n },\n\n // semantic locators ----------------------------------------------------\n async countByRole(role: string, name: string): Promise<number> {\n return (await visibleElementIds({ by: \"role\", role, name })).length;\n },\n async clickFirstByRole(role: string, name: string): Promise<void> {\n const id = await client.findElement({ by: \"role\", role, name });\n if (id === null) {\n throw new Error(`No element matched role=${role}[name=\"${name}\"]`);\n }\n await client.click(id);\n },\n async countByLabel(label: string): Promise<number> {\n return (await visibleElementIds({ by: \"label\", value: label })).length;\n },\n async fillFirstByLabel(label: string, value: string): Promise<void> {\n const id = await client.findElement({ by: \"label\", value: label });\n if (id === null) {\n throw new Error(`No element matched label=\"${label}\"`);\n }\n await client.setValue(id, value);\n },\n selectOptionFirstByLabel(): Promise<void> {\n return rejectUnsupported(\"select\");\n },\n\n // waiting --------------------------------------------------------------\n async waitForSelector(selector: string, waitOptions?: { timeout?: number }): Promise<void> {\n const query = parseIosSelector(selector);\n const timeoutMs = waitOptions?.timeout ?? DEFAULT_WAIT_TIMEOUT_MS;\n const deadline = Date.now() + timeoutMs;\n for (;;) {\n if (await hasVisibleElement(query)) {\n return;\n }\n if (Date.now() >= deadline) {\n throw new Error(`Timed out after ${timeoutMs}ms waiting for selector: ${selector}`);\n }\n await delay(Math.min(WAIT_POLL_INTERVAL_MS, Math.max(0, deadline - Date.now())));\n }\n },\n waitForUrl(): Promise<void> {\n return rejectUnsupported(\"waitForUrl\");\n },\n waitForNetworkIdle(): Promise<void> {\n return rejectUnsupported(\"waitForNetworkIdle\");\n },\n\n // scripting & artifacts ------------------------------------------------\n evaluate<R = unknown>(): Promise<R> {\n return rejectUnsupported(\"evalScript\") as Promise<R>;\n },\n async screenshot(screenshotOptions: { path: string; fullPage?: boolean }): Promise<void> {\n // A simulator screenshot is always the whole screen; `fullPage` has no\n // analogue and is intentionally ignored.\n await options.captureScreenshot(screenshotOptions.path);\n },\n\n // network / dialogs / downloads (all web-only) -------------------------\n onResponse(_handler: (response: DriverResponse) => void): void {\n throw unsupported(\"onResponse\");\n },\n route(_url: string, _handler: (route: DriverRoute) => void | Promise<void>): Promise<void> {\n return rejectUnsupported(\"mockRoute\");\n },\n unroute(): Promise<void> {\n return rejectUnsupported(\"unmockRoute\");\n },\n onDialog(_action: DialogAction): void {\n throw unsupported(\"onDialog\");\n },\n waitForDownloadEvent(): Promise<DriverDownload> {\n return rejectUnsupported(\"waitForDownload\") as Promise<DriverDownload>;\n },\n\n parseTextSelector(selector: string): string | null {\n return unwrapIosTextSelector(selector);\n }\n };\n}\n","/**\n * PROWL-059 / ARCH-010 — `xcrun simctl` lifecycle for the iOS simulator target.\n *\n * Thin, injectable wrappers around `xcrun` (both `simctl` for device lifecycle /\n * screenshots and `xcodebuild` for the one-time WebDriverAgent build) plus the\n * pure parsers they depend on. Every command flows through an {@link SimctlRunner}\n * so the whole surface is unit-testable with a fake — `npm test` never needs a\n * booted simulator or Xcode.\n */\nimport { execFile, spawn } from \"node:child_process\";\nimport { mkdir, readFile, rm, writeFile } from \"node:fs/promises\";\nimport net from \"node:net\";\nimport os from \"node:os\";\nimport path from \"node:path\";\n\n/** Result of one `xcrun` invocation. */\nexport type SimctlResult = { stdout: string; stderr: string; code: number };\n\n/** Runs one `xcrun <args>` command to completion and resolves with its output. */\nexport type SimctlRunner = (\n args: string[],\n options?: { timeoutMs?: number; env?: NodeJS.ProcessEnv }\n) => Promise<SimctlResult>;\n\n/** A handle to a spawned long-running `xcrun` process (the WDA xcodebuild test host). */\nexport type XcrunProcessHandle = { kill(): void };\n\n/**\n * Spawns a long-running `xcrun` command in the background — used for the\n * `xcodebuild test-without-building` run that hosts WebDriverAgent (which never\n * exits on its own; it serves HTTP until killed). Mirrors the Android\n * `AdbSpawner` so the launch flow stays unit-testable with a fake.\n */\nexport type XcrunSpawner = (\n args: string[],\n options?: { env?: NodeJS.ProcessEnv }\n) => XcrunProcessHandle;\n\n/** Default {@link XcrunSpawner}: spawns a detached-output `xcrun` child. */\nexport const spawnXcrunProcess: XcrunSpawner = (args, options) => {\n const child = spawn(\"xcrun\", args, {\n stdio: \"ignore\",\n env: options?.env ? { ...process.env, ...options.env } : process.env\n });\n child.on(\"error\", () => {\n /* surfaced via readiness/preflight, not here */\n });\n return {\n kill: () => {\n child.kill();\n }\n };\n};\n\n/** One simulator device from `simctl list devices --json`. */\nexport type SimDevice = {\n udid: string;\n name: string;\n state: string;\n runtime: string;\n isAvailable: boolean;\n};\n\nexport type SimulatorReservation = {\n udid: string;\n release(): Promise<void>;\n};\n\nexport type ReserveSimulatorOptions = {\n /** Override the lock directory root; intended for tests. */\n lockRoot?: string;\n};\n\nexport const DEFAULT_SIMULATOR_LOCK_ROOT = path.join(os.tmpdir(), \"prowl-ios-simulator-locks\");\n\nconst SIMULATOR_LOCK_OWNER_FILE = \"owner.json\";\n\n/** Default {@link SimctlRunner}: shells out to the real `xcrun` on PATH. */\nexport const execFileXcrunRunner: SimctlRunner = (args, options) =>\n new Promise<SimctlResult>((resolve) => {\n execFile(\n \"xcrun\",\n args,\n {\n encoding: \"utf-8\",\n timeout: options?.timeoutMs,\n maxBuffer: 32 * 1024 * 1024,\n env: options?.env ? { ...process.env, ...options.env } : process.env\n },\n (error, stdout, stderr) => {\n const code =\n error && typeof (error as { code?: unknown }).code === \"number\"\n ? (error as { code: number }).code\n : error\n ? 1\n : 0;\n const capturedStderr = stderr ?? \"\";\n resolve({\n stdout: stdout ?? \"\",\n stderr: capturedStderr.trim() ? capturedStderr : (error?.message ?? \"\"),\n code\n });\n }\n );\n });\n\nfunction simulatorLockName(udid: string): string {\n const safe = udid.replace(/[^A-Za-z0-9_.-]/g, \"_\");\n return `${safe || \"simulator\"}.lock`;\n}\n\nfunction isErrno(error: unknown, code: string): boolean {\n return (error as NodeJS.ErrnoException | undefined)?.code === code;\n}\n\nfunction isProcessAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (error) {\n return isErrno(error, \"EPERM\");\n }\n}\n\nasync function removeStaleSimulatorLock(lockPath: string): Promise<boolean> {\n try {\n const ownerText = await readFile(path.join(lockPath, SIMULATOR_LOCK_OWNER_FILE), \"utf8\");\n const owner = JSON.parse(ownerText) as { pid?: unknown };\n if (\n typeof owner.pid === \"number\" &&\n Number.isInteger(owner.pid) &&\n owner.pid > 0 &&\n !isProcessAlive(owner.pid)\n ) {\n await rm(lockPath, { recursive: true, force: true });\n return true;\n }\n } catch {\n return false;\n }\n return false;\n}\n\nfunction simulatorReservedError(udid: string): Error {\n return new Error(\n `iOS simulator \"${udid}\" is already reserved by another Prowl process. ` +\n \"Wait for that run to finish, or boot/select a different simulator with target.udid.\"\n );\n}\n\n/**\n * Reserve a simulator UDID across processes. The lock is held until `release` is\n * called, preventing one session's teardown from terminating another session's\n * WDA runner or target app on the same simulator.\n */\nexport async function reserveSimulatorUdid(\n udid: string,\n options: ReserveSimulatorOptions = {}\n): Promise<SimulatorReservation> {\n const lockRoot = options.lockRoot ?? DEFAULT_SIMULATOR_LOCK_ROOT;\n const lockPath = path.join(lockRoot, simulatorLockName(udid));\n await mkdir(lockRoot, { recursive: true });\n\n for (let attempt = 0; attempt < 2; attempt += 1) {\n try {\n await mkdir(lockPath);\n } catch (error) {\n if (!isErrno(error, \"EEXIST\")) {\n throw error instanceof Error ? error : new Error(String(error));\n }\n if (attempt === 0 && (await removeStaleSimulatorLock(lockPath))) {\n continue;\n }\n throw simulatorReservedError(udid);\n }\n\n let released = false;\n const release = async (): Promise<void> => {\n if (released) {\n return;\n }\n released = true;\n await rm(lockPath, { recursive: true, force: true });\n };\n\n try {\n await writeFile(\n path.join(lockPath, SIMULATOR_LOCK_OWNER_FILE),\n `${JSON.stringify({ pid: process.pid, udid, createdAt: new Date().toISOString() })}\\n`,\n { flag: \"wx\" }\n );\n } catch (error) {\n await release().catch(() => undefined);\n throw error instanceof Error ? error : new Error(String(error));\n }\n\n return { udid, release };\n }\n\n throw simulatorReservedError(udid);\n}\n\n/**\n * Allocate a free local TCP port by binding to :0 and releasing it. The returned\n * port is advisory after release; callers that hand it to another process must\n * handle a later bind/readiness failure.\n */\nexport function findFreePort(): Promise<number> {\n return new Promise<number>((resolve, reject) => {\n const server = net.createServer();\n server.on(\"error\", reject);\n server.listen(0, \"127.0.0.1\", () => {\n const address = server.address();\n if (address && typeof address === \"object\") {\n const { port } = address;\n server.close(() => resolve(port));\n } else {\n server.close(() => reject(new Error(\"Could not allocate a local port\")));\n }\n });\n });\n}\n\n/**\n * Parse `simctl list devices --json` into a flat device list. The JSON maps\n * runtime identifiers to arrays of devices; the runtime is folded into each row.\n */\nexport function parseSimctlDevices(json: string): SimDevice[] {\n let parsed: unknown;\n try {\n parsed = JSON.parse(json);\n } catch {\n throw new Error(\"Could not parse `simctl list devices --json` output.\");\n }\n const byRuntime = (parsed as { devices?: Record<string, unknown> } | undefined)?.devices;\n if (!byRuntime || typeof byRuntime !== \"object\") {\n return [];\n }\n const devices: SimDevice[] = [];\n for (const [runtime, entries] of Object.entries(byRuntime)) {\n if (!Array.isArray(entries)) {\n continue;\n }\n for (const entry of entries) {\n if (!entry || typeof entry !== \"object\") {\n continue;\n }\n const record = entry as Record<string, unknown>;\n const udid = typeof record.udid === \"string\" ? record.udid : undefined;\n const name = typeof record.name === \"string\" ? record.name : undefined;\n const state = typeof record.state === \"string\" ? record.state : \"Unknown\";\n if (!udid || !name) {\n continue;\n }\n devices.push({\n udid,\n name,\n state,\n runtime,\n isAvailable: record.isAvailable !== false\n });\n }\n }\n return devices;\n}\n\n/** Simulators that are currently booted. */\nexport function bootedSimulators(devices: SimDevice[]): SimDevice[] {\n return devices.filter((device) => device.state === \"Booted\");\n}\n\n/** Short human label for a simulator (name + short runtime), for error lists. */\nfunction describeSimulator(device: SimDevice): string {\n const runtime = device.runtime.replace(/^com\\.apple\\.CoreSimulator\\.SimRuntime\\./, \"\");\n return `${device.name} [${runtime}] (${device.udid})`;\n}\n\n/**\n * Choose which simulator to drive. With `requested` set it must exist and be\n * booted. Otherwise exactly one booted simulator is required; zero or many raise\n * an actionable error listing what was found.\n */\nexport function selectSimulatorUdid(devices: SimDevice[], requested?: string): string {\n const booted = bootedSimulators(devices);\n if (requested) {\n const match = devices.find((device) => device.udid === requested);\n if (!match) {\n const known = devices.length > 0 ? devices.map((d) => d.udid).join(\", \") : \"none\";\n throw new Error(\n `iOS simulator \"${requested}\" was not found. Known simulators: ${known}. ` +\n \"Check `xcrun simctl list devices`.\"\n );\n }\n if (match.state !== \"Booted\") {\n throw new Error(\n `iOS simulator \"${requested}\" is not booted (state: ${match.state}). ` +\n `Boot it with \\`xcrun simctl boot ${requested}\\`, then retry.`\n );\n }\n return requested;\n }\n\n if (booted.length === 0) {\n throw new Error(\n \"No booted iOS simulator found. Boot one from Xcode or with \" +\n \"`xcrun simctl boot <udid>` (see `xcrun simctl list devices available`), then retry.\"\n );\n }\n if (booted.length > 1) {\n throw new Error(\n `Multiple iOS simulators are booted (${booted.map(describeSimulator).join(\"; \")}). ` +\n \"Set target.udid to pick one.\"\n );\n }\n return booted[0].udid;\n}\n\n/** List all simulators via `simctl list devices --json`. */\nexport async function listSimulators(runner: SimctlRunner): Promise<SimDevice[]> {\n const result = await runner([\"simctl\", \"list\", \"devices\", \"--json\"], { timeoutMs: 30000 });\n if (result.code !== 0) {\n throw new Error(\n \"`xcrun simctl list devices` failed. Is Xcode installed and are the command-line tools \" +\n `selected (\\`xcode-select -p\\`)? ${(result.stderr.trim() || \"\").slice(0, 400)}`\n );\n }\n return parseSimctlDevices(result.stdout);\n}\n\n/** Install a `.app` bundle onto the simulator. */\nexport async function installApp(runner: SimctlRunner, udid: string, appPath: string): Promise<void> {\n const result = await runner([\"simctl\", \"install\", udid, appPath], { timeoutMs: 120000 });\n if (result.code !== 0) {\n throw new Error(\n `Failed to install \"${appPath}\" onto simulator ${udid}: ${\n (result.stderr.trim() || result.stdout.trim()).slice(0, 400)\n }`\n );\n }\n}\n\n/** Uninstall an app by bundle id (best effort; ignores \"not installed\"). */\nexport async function uninstallApp(runner: SimctlRunner, udid: string, bundleId: string): Promise<void> {\n await runner([\"simctl\", \"uninstall\", udid, bundleId], { timeoutMs: 60000 }).catch(() => undefined);\n}\n\n/**\n * Launch an installed app by bundle id. `childEnv` is forwarded to the launched\n * process via `SIMCTL_CHILD_*` variables (simctl's env-passing convention).\n */\nexport async function launchApp(\n runner: SimctlRunner,\n udid: string,\n bundleId: string,\n childEnv: Record<string, string> = {}\n): Promise<void> {\n const env: NodeJS.ProcessEnv = {};\n for (const [key, value] of Object.entries(childEnv)) {\n env[`SIMCTL_CHILD_${key}`] = value;\n }\n const result = await runner([\"simctl\", \"launch\", udid, bundleId], {\n timeoutMs: 60000,\n env: Object.keys(env).length > 0 ? env : undefined\n });\n if (result.code !== 0) {\n throw new Error(\n `Failed to launch \"${bundleId}\" on simulator ${udid}: ${\n (result.stderr.trim() || result.stdout.trim()).slice(0, 400)\n }. Is it installed?`\n );\n }\n}\n\n/** Terminate a running app by bundle id (best effort; ignores \"not running\"). */\nexport async function terminateApp(runner: SimctlRunner, udid: string, bundleId: string): Promise<void> {\n await runner([\"simctl\", \"terminate\", udid, bundleId], { timeoutMs: 30000 }).catch(() => undefined);\n}\n\n/** Capture a PNG screenshot of the simulator directly to `outPath`. */\nexport async function captureScreenshot(\n runner: SimctlRunner,\n udid: string,\n outPath: string\n): Promise<void> {\n const result = await runner([\"simctl\", \"io\", udid, \"screenshot\", outPath], { timeoutMs: 30000 });\n if (result.code !== 0) {\n throw new Error(\n `Failed to capture a simulator screenshot (${udid}): ${\n (result.stderr.trim() || result.stdout.trim()).slice(0, 300)\n }`\n );\n }\n}\n\n/** Parse the `x.y[.z]` version out of `xcodebuild -version` output. */\nexport function parseXcodeVersion(stdout: string): string | null {\n const match = /Xcode\\s+([\\d.]+)/i.exec(stdout);\n return match?.[1] ?? null;\n}\n\n/** Read the installed Xcode version (e.g. `\"26.2\"`), or null if unavailable. */\nexport async function xcodeVersion(runner: SimctlRunner): Promise<string | null> {\n const result = await runner([\"xcodebuild\", \"-version\"], { timeoutMs: 30000 });\n if (result.code !== 0) {\n return null;\n }\n return parseXcodeVersion(result.stdout);\n}\n","/**\n * PROWL-059 / ARCH-010 — HTTP/JSON transport for the on-simulator WebDriverAgent.\n *\n * WebDriverAgent (WDA) exposes W3C-WebDriver-shaped endpoints over plain HTTP. On\n * a simulator it is reachable directly at `http://127.0.0.1:<port>` (no tunnel),\n * so this module speaks it with the global `fetch` (no heavy WebDriver SDK, per\n * the `ai.ts` ethos), mirroring the Android agent's ergonomics: a per-request\n * deadline via `AbortController`, unref'd timers, and W3C element-key extraction.\n * It exposes the semantic {@link IosAgentClient} the driver consumes; tests fake\n * either the `fetch` implementation or the client itself.\n */\nimport type { IosAgentClient, IosQuery } from \"./ios-driver.js\";\nimport { iosQueryToLocator } from \"./ios-driver.js\";\nimport { toScreenSize, type PointerActionSequence, type ScreenSize } from \"./touch-gestures.js\";\n\n/** The subset of `fetch` this module uses; overridable in tests. */\nexport type FetchLike = (url: string, init: RequestInit) => Promise<Response>;\n\n/** Default per-request deadline for the agent transport. */\nexport const DEFAULT_WDA_REQUEST_TIMEOUT_MS = 30000;\n\n/** The W3C element-reference key both current and legacy servers may use. */\nconst W3C_ELEMENT_KEY = \"element-6066-11e4-a52e-4f735466cecf\";\n\nexport type WdaTransportOptions = {\n /** Base URL, e.g. `http://127.0.0.1:8100`. */\n baseUrl: string;\n requestTimeoutMs?: number;\n fetchImpl?: FetchLike;\n};\n\n/** An HTTP-level failure from WDA, carrying the status and parsed body. */\nexport class WdaHttpError extends Error {\n constructor(\n message: string,\n readonly status: number,\n readonly webdriverError?: string\n ) {\n super(message);\n this.name = \"WdaHttpError\";\n }\n}\n\n/** Low-level request/response transport with a per-request deadline. */\nexport class WdaTransport {\n private readonly baseUrl: string;\n private readonly requestTimeoutMs: number;\n private readonly fetchImpl: FetchLike;\n\n constructor(options: WdaTransportOptions) {\n this.baseUrl = options.baseUrl.replace(/\\/+$/, \"\");\n this.requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_WDA_REQUEST_TIMEOUT_MS;\n const injected = options.fetchImpl;\n if (injected) {\n this.fetchImpl = injected;\n } else if (typeof fetch === \"function\") {\n this.fetchImpl = (url, init) => fetch(url, init);\n } else {\n throw new Error(\"global fetch is unavailable; Node 20+ is required for the iOS target\");\n }\n }\n\n /**\n * Send one request and return the full parsed JSON body. Rejects with a\n * {@link WdaHttpError} on a non-2xx response, or a timeout error when the\n * per-request deadline elapses.\n */\n async requestFull(method: string, path: string, body?: unknown, timeoutMs?: number): Promise<unknown> {\n const requestTimeoutMs = timeoutMs ?? this.requestTimeoutMs;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), requestTimeoutMs);\n timer.unref?.();\n const url = `${this.baseUrl}${path}`;\n const timeoutError = (): Error => {\n const shown =\n requestTimeoutMs >= 1000\n ? `${Math.round(requestTimeoutMs / 1000)}s`\n : `${requestTimeoutMs}ms`;\n return new Error(`WebDriverAgent request ${method} ${path} timed out after ${shown}`);\n };\n let response: Response;\n try {\n response = await this.fetchImpl(url, {\n method,\n signal: controller.signal,\n headers: body !== undefined ? { \"content-type\": \"application/json\" } : undefined,\n body: body !== undefined ? JSON.stringify(body) : undefined\n });\n } catch (error) {\n clearTimeout(timer);\n if (controller.signal.aborted) {\n throw timeoutError();\n }\n throw error instanceof Error ? error : new Error(String(error));\n }\n\n let text: string;\n try {\n text = await response.text();\n } catch (error) {\n if (controller.signal.aborted) {\n throw timeoutError();\n }\n throw error instanceof Error ? error : new Error(String(error));\n } finally {\n clearTimeout(timer);\n }\n const parsed = parseJson(text);\n if (!response.ok) {\n const wdError = extractWebdriverError(parsed);\n throw new WdaHttpError(\n `WebDriverAgent ${method} ${path} failed (${response.status})${wdError ? `: ${wdError}` : \"\"}`,\n response.status,\n wdError\n );\n }\n return parsed;\n }\n\n /** Like {@link requestFull} but returns just the `value` field. */\n async request(method: string, path: string, body?: unknown, timeoutMs?: number): Promise<unknown> {\n const parsed = await this.requestFull(method, path, body, timeoutMs);\n return (parsed as { value?: unknown } | undefined)?.value;\n }\n}\n\nfunction parseJson(text: string): unknown {\n if (!text) {\n return undefined;\n }\n try {\n return JSON.parse(text);\n } catch {\n return undefined;\n }\n}\n\nfunction extractWebdriverError(parsed: unknown): string | undefined {\n const value = (parsed as { value?: unknown } | undefined)?.value;\n if (value && typeof value === \"object\") {\n const record = value as Record<string, unknown>;\n const error = typeof record.error === \"string\" ? record.error : undefined;\n const message = typeof record.message === \"string\" ? record.message : undefined;\n return error ?? message;\n }\n return undefined;\n}\n\n/** Extract an element id from a W3C element-reference object, or null. */\nexport function extractElementId(value: unknown): string | null {\n if (!value || typeof value !== \"object\") {\n return null;\n }\n const record = value as Record<string, unknown>;\n const id = record[W3C_ELEMENT_KEY] ?? record.ELEMENT;\n return typeof id === \"string\" ? id : null;\n}\n\nfunction isNoSuchElement(error: unknown): boolean {\n if (error instanceof WdaHttpError) {\n return error.status === 404 || (error.webdriverError ?? \"\").includes(\"no such element\");\n }\n return false;\n}\n\n/**\n * Create a WDA session bound to `bundleId` and return its id. WDA activates the\n * app under test named in `alwaysMatch.bundleId`. The session id may arrive at\n * the envelope root or inside `value`, so both are checked.\n */\nexport async function createWdaSession(transport: WdaTransport, bundleId: string): Promise<string> {\n const body = await transport.requestFull(\"POST\", \"/session\", {\n capabilities: { alwaysMatch: { bundleId }, firstMatch: [{}] }\n });\n const envelope = (body ?? {}) as Record<string, unknown>;\n const value = (envelope.value ?? {}) as Record<string, unknown>;\n const sessionId =\n (typeof value.sessionId === \"string\" && value.sessionId) ||\n (typeof envelope.sessionId === \"string\" && envelope.sessionId) ||\n \"\";\n if (sessionId.length > 0) {\n return sessionId;\n }\n throw new Error(\"WebDriverAgent did not return a session id\");\n}\n\n/** Poll `GET /status` until WDA reports ready or the deadline elapses. */\nexport async function waitForWdaReady(\n transport: WdaTransport,\n options: { deadlineMs: number; intervalMs?: number } = { deadlineMs: 60000 }\n): Promise<void> {\n const interval = options.intervalMs ?? 300;\n const deadline = Date.now() + options.deadlineMs;\n let lastError: unknown;\n for (;;) {\n try {\n const remainingMs = Math.max(1, deadline - Date.now());\n // WDA's /status returns a rich object (no `ready` flag); a 2xx is readiness.\n await transport.request(\"GET\", \"/status\", undefined, Math.min(remainingMs, 5000));\n return;\n } catch (error) {\n lastError = error;\n }\n if (Date.now() >= deadline) {\n const detail = lastError instanceof Error ? `: ${lastError.message}` : \"\";\n throw new Error(`WebDriverAgent did not become ready within ${options.deadlineMs}ms${detail}`);\n }\n // A ref'd sleep here (unlike the per-request abort timer) keeps the event loop\n // alive between probes: WDA is reached directly, so before its HTTP server\n // binds the port is refused and each fetch rejects immediately — an unref'd\n // timer would let Node go idle and leave this poll pending forever.\n await sleep(Math.min(interval, Math.max(0, deadline - Date.now())));\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n}\n\n/**\n * Build the semantic {@link IosAgentClient} over a live session. `close` deletes\n * the session (best effort); the transport itself is stateless.\n */\nexport function createWdaAgentClient(transport: WdaTransport, sessionId: string): IosAgentClient {\n const base = `/session/${sessionId}`;\n\n async function locate(query: IosQuery, path: string): Promise<unknown> {\n return transport.request(\"POST\", `${base}${path}`, iosQueryToLocator(query));\n }\n\n return {\n async findElement(query: IosQuery): Promise<string | null> {\n try {\n return extractElementId(await locate(query, \"/element\"));\n } catch (error) {\n if (isNoSuchElement(error)) {\n return null;\n }\n throw error;\n }\n },\n async findElements(query: IosQuery): Promise<string[]> {\n const value = await locate(query, \"/elements\");\n if (!Array.isArray(value)) {\n return [];\n }\n return value.map((entry) => extractElementId(entry)).filter((id): id is string => id !== null);\n },\n async click(elementId: string): Promise<void> {\n await transport.request(\"POST\", `${base}/element/${elementId}/click`, {});\n },\n async setValue(elementId: string, text: string): Promise<void> {\n // W3C `element/value` takes `{ text }`; WDA types it into the element.\n await transport.request(\"POST\", `${base}/element/${elementId}/value`, { text });\n },\n async getText(elementId: string): Promise<string | null> {\n const value = await transport.request(\"GET\", `${base}/element/${elementId}/text`);\n return typeof value === \"string\" ? value : value == null ? null : String(value);\n },\n async isDisplayed(elementId: string): Promise<boolean> {\n try {\n return (await transport.request(\"GET\", `${base}/element/${elementId}/displayed`)) === true;\n } catch (error) {\n if (isNoSuchElement(error)) {\n return false;\n }\n throw error;\n }\n },\n async sendKeys(keys: string[]): Promise<void> {\n // Session-scoped key input to the active element (WDA `/wda/keys`).\n await transport.request(\"POST\", `${base}/wda/keys`, { value: keys });\n },\n async homescreen(): Promise<void> {\n // Session-independent springboard route.\n await transport.request(\"POST\", \"/wda/homescreen\", {});\n },\n async windowSize(): Promise<ScreenSize> {\n // WDA exposes the classic `/window/size` ({ width, height }) endpoint.\n const value = await transport.request(\"GET\", `${base}/window/size`);\n return toScreenSize(value, \"WebDriverAgent /window/size\");\n },\n async performActions(actions: PointerActionSequence): Promise<void> {\n // W3C actions endpoint; WDA replays the touch pointer sequence.\n await transport.request(\"POST\", `${base}/actions`, { actions: [actions] });\n },\n async source(): Promise<string> {\n // WDA returns the XML page source of the active app (session-independent).\n const value = await transport.request(\"GET\", \"/source\");\n if (typeof value !== \"string\") {\n throw new Error(\"WebDriverAgent /source did not return XML text; cannot analyze iOS UI hierarchy\");\n }\n return value;\n },\n async close(): Promise<void> {\n await transport.request(\"DELETE\", base).catch(() => undefined);\n }\n };\n}\n","/**\n * PROWL-059 / ARCH-010 — launch/teardown orchestration for the iOS simulator target.\n *\n * Ties together the three layers: `simctl` lifecycle ({@link ./ios-simctl.js}), the\n * WebDriverAgent HTTP agent ({@link ./ios-agent.js}), and the driver\n * ({@link ./ios-driver.js}). The WDA Xcode project ships inside the\n * `appium-webdriveragent` npm package (Apache-2.0); it is built once with\n * `xcodebuild build-for-testing` and cached under `~/.prowl/wda/` keyed on the\n * WDA + Xcode versions.\n *\n * PROWL-069 / ARCH-013 — WDA is launched via the standard XCTest host launch\n * (`xcodebuild test-without-building` driven by the generated `.xctestrun`), not\n * by `simctl launch` of the runner `.app`. On iOS 26+ a bare `simctl launch` of\n * `com.facebook.WebDriverAgentRunner.xctrunner` is terminated by RunningBoard\n * (\"had no entitlements\"): the xctrunner must be hosted by the test runner, which\n * supplies the entitlements the bare launch lacks. This is now the single launch\n * path (the older preinstalled-runner fast path is retired) — it is how\n * Appium/WebDriverAgent launch on modern runtimes and it works on iOS 18 and 26+\n * alike. Every external dependency (simctl runner, xcodebuild spawner, xctestrun\n * preparer, port allocator, agent connector) is injectable so the whole flow is\n * unit-testable without a simulator or Xcode.\n */\nimport { createRequire } from \"node:module\";\nimport { execFile } from \"node:child_process\";\nimport fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { createIosDriver, type IosAgentClient } from \"./ios-driver.js\";\nimport {\n captureScreenshot,\n execFileXcrunRunner,\n findFreePort,\n installApp,\n launchApp,\n listSimulators,\n reserveSimulatorUdid,\n selectSimulatorUdid,\n spawnXcrunProcess,\n terminateApp,\n uninstallApp,\n xcodeVersion,\n type SimctlRunner,\n type XcrunProcessHandle,\n type XcrunSpawner\n} from \"./ios-simctl.js\";\nimport {\n createWdaAgentClient,\n createWdaSession,\n DEFAULT_WDA_REQUEST_TIMEOUT_MS,\n waitForWdaReady,\n WdaTransport\n} from \"./ios-agent.js\";\nimport { assertIosAppAllowed, looksLikeIosAppPath, readIosBundleIdentifier } from \"../config/target.js\";\nimport type { SessionDriver } from \"./driver.js\";\n\n/** Bundle id of the prebuilt WebDriverAgent runner (its xctest host app). */\nexport const WDA_RUNNER_BUNDLE_ID = \"com.facebook.WebDriverAgentRunner.xctrunner\";\n/** Env var WDA reads to pick its HTTP port (injected into the runner's xctestrun env). */\nexport const WDA_USE_PORT_ENV = \"USE_PORT\";\n/** Filename prefix each launch's port-injected xctestrun (a Products-dir sibling) carries. */\nconst PREPARED_XCTESTRUN_PREFIX = \"prowl-wda-xctestrun-\";\n/** One retry covers the race where a released dynamic port is claimed before WDA binds. */\nconst WDA_STARTUP_ATTEMPTS = 2;\n\n/** Establishes a live {@link IosAgentClient} against a running WDA HTTP server. */\nexport type IosAgentConnector = (options: {\n host: string;\n port: number;\n bundleId: string;\n requestTimeoutMs: number;\n readyDeadlineMs: number;\n}) => Promise<IosAgentClient>;\n\n/** Default connector: builds the HTTP transport, waits for readiness, opens a session. */\nexport const defaultIosAgentConnector: IosAgentConnector = async ({\n host,\n port,\n bundleId,\n requestTimeoutMs,\n readyDeadlineMs\n}) => {\n const transport = new WdaTransport({ baseUrl: `http://${host}:${port}`, requestTimeoutMs });\n await waitForWdaReady(transport, { deadlineMs: readyDeadlineMs });\n const sessionId = await createWdaSession(transport, bundleId);\n return createWdaAgentClient(transport, sessionId);\n};\n\n/** Resolve the WDA Xcode project bundled inside `appium-webdriveragent`. */\nexport function resolveWdaProject(requireFn: NodeRequire = createRequire(import.meta.url)): {\n projectPath: string;\n version: string;\n} {\n let pkgJsonPath: string;\n try {\n pkgJsonPath = requireFn.resolve(\"appium-webdriveragent/package.json\");\n } catch {\n throw new Error(\n \"The iOS target requires the `appium-webdriveragent` package (its WDA Xcode project). \" +\n \"It is an optional dependency of prowl-tools; it may have been skipped (--omit=optional) \" +\n \"or failed to install. Restore it for a global Prowl install with: \" +\n \"npm install -g appium-webdriveragent@16.4.0. If Prowl is installed locally in a \" +\n \"project, run: npm install appium-webdriveragent@16.4.0\"\n );\n }\n const pkgDir = path.dirname(pkgJsonPath);\n const version = (requireFn(pkgJsonPath) as { version: string }).version;\n const projectPath = path.join(pkgDir, \"WebDriverAgent.xcodeproj\");\n if (!fs.existsSync(projectPath)) {\n throw new Error(`Expected WebDriverAgent project is missing: ${projectPath}. Reinstall dependencies.`);\n }\n return { projectPath, version };\n}\n\n/** Cache directory for a built WDA runner, keyed on the WDA + Xcode versions. */\nexport function wdaCacheDir(wdaVersion: string, xcode: string, homeDir: string = os.homedir()): string {\n return path.join(homeDir, \".prowl\", \"wda\", `${wdaVersion}-xcode${xcode}`);\n}\n\n/** The `Build/Products` directory a `-derivedDataPath` build writes the xctestrun into. */\nfunction productsDir(derivedDataPath: string): string {\n return path.join(derivedDataPath, \"Build\", \"Products\");\n}\n\n/** First `*.xctestrun` file in `dir`, or null when the directory is missing/empty. */\nfunction findXctestrunIn(dir: string): string | null {\n let entries: string[];\n try {\n entries = fs.readdirSync(dir);\n } catch {\n return null;\n }\n const match = entries\n .filter((name) => name.endsWith(\".xctestrun\") && !name.startsWith(PREPARED_XCTESTRUN_PREFIX))\n .sort()[0];\n return match ? path.join(dir, match) : null;\n}\n\n/**\n * Resolve the `.xctestrun` that a `PROWL_WDA_RUNNER` override points at. The\n * override may name the xctestrun directly, a derived-data / Products directory\n * that contains one, or (for backward compatibility) the runner `.app` whose\n * sibling `Build/Products/*.xctestrun` we can locate.\n */\nfunction resolveOverrideXctestrun(override: string): string {\n if (!fs.existsSync(override)) {\n throw new Error(`PROWL_WDA_RUNNER points at a missing path: ${override}`);\n }\n if (override.endsWith(\".xctestrun\")) {\n return override;\n }\n const candidates: string[] = [];\n const stat = fs.statSync(override);\n if (override.endsWith(\".app\")) {\n // Build/Products/<Config>-iphonesimulator/Runner.app → Build/Products/*.xctestrun\n candidates.push(path.dirname(path.dirname(override)));\n } else if (stat.isDirectory()) {\n // A derived-data dir, a Products dir, or any dir that directly holds one.\n candidates.push(override, productsDir(override));\n }\n for (const dir of candidates) {\n const found = findXctestrunIn(dir);\n if (found) {\n return found;\n }\n }\n throw new Error(\n `PROWL_WDA_RUNNER (${override}) does not resolve to a WebDriverAgent .xctestrun. ` +\n \"Point it at the generated `*.xctestrun`, at the `-derivedDataPath` directory from \" +\n \"`xcodebuild build-for-testing`, or at that build's runner `.app`.\"\n );\n}\n\nexport type ResolveWdaTestRunOptions = {\n runner?: SimctlRunner;\n env?: NodeJS.ProcessEnv;\n homeDir?: string;\n requireFn?: NodeRequire;\n /** One-line progress notice sink (defaults to stderr). */\n logger?: (message: string) => void;\n};\n\n/**\n * Resolve a WebDriverAgent `.xctestrun` file (the input to the XCTest host\n * launch). Order: (a) the `PROWL_WDA_RUNNER` env override; (b) a previously built\n * xctestrun in the version-keyed cache; (c) a one-time\n * `xcodebuild build-for-testing` that populates the cache. Simulators need no\n * code signing. Throws actionable errors when Xcode is missing or the build fails.\n */\nexport async function resolveWdaTestRun(options: ResolveWdaTestRunOptions = {}): Promise<string> {\n const runner = options.runner ?? execFileXcrunRunner;\n const env = options.env ?? process.env;\n const homeDir = options.homeDir ?? os.homedir();\n const log = options.logger ?? ((message: string) => process.stderr.write(`${message}\\n`));\n\n const override = env.PROWL_WDA_RUNNER;\n if (override) {\n return resolveOverrideXctestrun(override);\n }\n\n const { projectPath, version } = resolveWdaProject(options.requireFn);\n const xcode = await xcodeVersion(runner);\n if (!xcode) {\n throw new Error(\n \"Could not determine the Xcode version (`xcrun xcodebuild -version`). The iOS target \" +\n \"requires Xcode on macOS. Install it and run `xcode-select --switch`, then retry.\"\n );\n }\n\n const cacheDir = wdaCacheDir(version, xcode, homeDir);\n const cached = findXctestrunIn(productsDir(cacheDir));\n if (cached) {\n return cached;\n }\n\n log(\n `Prowl: building WebDriverAgent for the iOS target (first run only; this can take a few minutes)…`\n );\n fs.mkdirSync(cacheDir, { recursive: true });\n const result = await runner(\n [\n \"xcodebuild\",\n \"build-for-testing\",\n \"-project\",\n projectPath,\n \"-scheme\",\n \"WebDriverAgentRunner\",\n \"-destination\",\n \"generic/platform=iOS Simulator\",\n \"-derivedDataPath\",\n cacheDir,\n \"CODE_SIGNING_ALLOWED=NO\"\n ],\n { timeoutMs: 1_200_000 }\n );\n if (result.code !== 0) {\n throw new Error(\n \"Failed to build WebDriverAgent with `xcodebuild build-for-testing`. Ensure a full Xcode \" +\n \"(not just the command-line tools) is installed and selected (`xcode-select -p`). \" +\n `Details: ${(result.stderr.trim() || result.stdout.trim()).slice(-800)}`\n );\n }\n const built = findXctestrunIn(productsDir(cacheDir));\n if (!built) {\n throw new Error(\n `WebDriverAgent build succeeded but no .xctestrun was found under ${productsDir(cacheDir)}. ` +\n \"This may indicate an Xcode layout change; set PROWL_WDA_RUNNER to a prebuilt runner/xctestrun.\"\n );\n }\n return built;\n}\n\n/**\n * Return a deep copy of a parsed `.xctestrun` plist with the WDA HTTP port\n * (`USE_PORT`) injected into every test target's `EnvironmentVariables`. WDA\n * reads `USE_PORT` from its process environment; under `xcodebuild test` that\n * environment comes from the xctestrun, so the dynamic port must be written here.\n *\n * Handles both xctestrun layouts: format 1 (top-level dict keyed by test-target\n * name) and format 2 (a `TestConfigurations[].TestTargets[]` tree). A test target\n * is recognized by a `TestBundlePath`/`TestHostPath` string; any object that\n * already carries an `EnvironmentVariables` dict is updated too. Throws when no\n * target is found, so a future format change fails loudly rather than launching\n * WDA on the wrong port.\n */\nexport function injectUsePortIntoXctestrun(plist: unknown, port: number): unknown {\n const clone = structuredClone(plist);\n let injected = 0;\n const visit = (node: unknown): void => {\n if (Array.isArray(node)) {\n for (const entry of node) {\n visit(entry);\n }\n return;\n }\n if (!node || typeof node !== \"object\") {\n return;\n }\n const obj = node as Record<string, unknown>;\n const isTarget =\n typeof obj.TestBundlePath === \"string\" || typeof obj.TestHostPath === \"string\";\n const existingEnv =\n obj.EnvironmentVariables && typeof obj.EnvironmentVariables === \"object\" &&\n !Array.isArray(obj.EnvironmentVariables)\n ? (obj.EnvironmentVariables as Record<string, unknown>)\n : undefined;\n if (isTarget || existingEnv) {\n const env = existingEnv ?? {};\n env[WDA_USE_PORT_ENV] = String(port);\n obj.EnvironmentVariables = env;\n injected += 1;\n }\n for (const value of Object.values(obj)) {\n visit(value);\n }\n };\n visit(clone);\n if (injected === 0) {\n throw new Error(\n \"Could not find a test target in the WebDriverAgent .xctestrun to set USE_PORT. \" +\n \"The xctestrun format may have changed; set PROWL_WDA_RUNNER to a compatible runner.\"\n );\n }\n return clone;\n}\n\n/**\n * Produce a launch-specific `.xctestrun` with `USE_PORT` set to `port`, returning\n * its path. The base xctestrun (built/cached) is never mutated in place.\n */\nexport type WdaTestRunPreparer = (options: {\n xctestrunPath: string;\n port: number;\n}) => Promise<string>;\n\n/** Run `plutil`, resolving its stdout; throws an actionable error on failure. */\nfunction runPlutil(args: string[]): Promise<string> {\n return new Promise<string>((resolve, reject) => {\n execFile(\n \"plutil\",\n args,\n { encoding: \"utf-8\", maxBuffer: 32 * 1024 * 1024 },\n (error, stdout, stderr) => {\n if (error) {\n reject(\n new Error(\n `\\`plutil ${args.join(\" \")}\\` failed: ${(stderr || error.message).slice(0, 400)}`\n )\n );\n return;\n }\n resolve(stdout ?? \"\");\n }\n );\n });\n}\n\n/**\n * Default preparer: reads the base xctestrun with `plutil` (JSON), injects\n * `USE_PORT`, and writes a fresh, uniquely-named xctestrun **next to the base\n * one**. This placement is required, not incidental: an xctestrun's product\n * paths are relative to `__TESTROOT__`, which xcodebuild resolves to the\n * directory containing the xctestrun file — so a copy written to a temp dir\n * makes `xcodebuild test-without-building` fail with \"Missing test product\".\n * Writing the sibling into the build's Products dir keeps `__TESTROOT__` pointing\n * at the real products. The intermediate JSON goes to a temp dir; only the\n * `.xctestrun` lands beside the products and is removed on teardown. `plutil`\n * ships with macOS, so no extra dependency is added.\n */\nexport const defaultWdaTestRunPreparer: WdaTestRunPreparer = async ({ xctestrunPath, port }) => {\n const json = await runPlutil([\"-convert\", \"json\", \"-o\", \"-\", xctestrunPath]);\n let parsed: unknown;\n try {\n parsed = JSON.parse(json);\n } catch {\n throw new Error(`Could not parse the WebDriverAgent .xctestrun as JSON: ${xctestrunPath}`);\n }\n const injected = injectUsePortIntoXctestrun(parsed, port);\n // Unique per launch: the dynamic port is already unique on this host, and the\n // pid disambiguates concurrent processes sharing the same cache.\n const stem = `${PREPARED_XCTESTRUN_PREFIX}${port}-${process.pid}`;\n const outPath = path.join(path.dirname(xctestrunPath), `${stem}.xctestrun`);\n const jsonPath = path.join(os.tmpdir(), `${stem}.json`);\n fs.writeFileSync(jsonPath, JSON.stringify(injected));\n try {\n await runPlutil([\"-convert\", \"xml1\", jsonPath, \"-o\", outPath]);\n } finally {\n fs.rmSync(jsonPath, { force: true });\n }\n return outPath;\n};\n\n/**\n * Remove a prepared sibling xctestrun (best effort). Only ever deletes a single\n * file whose name carries our prefix — never a directory, so the WDA build cache\n * it lives in is safe.\n */\nfunction cleanupPreparedTestRun(preparedPath: string | undefined): void {\n if (!preparedPath) {\n return;\n }\n if (!path.basename(preparedPath).startsWith(PREPARED_XCTESTRUN_PREFIX)) {\n return;\n }\n fs.rmSync(preparedPath, { force: true });\n}\n\n/** The `xcodebuild test-without-building` args that host WDA against `udid`. */\nexport function wdaTestRunArgs(xctestrunPath: string, udid: string): string[] {\n return [\n \"xcodebuild\",\n \"test-without-building\",\n \"-xctestrun\",\n xctestrunPath,\n \"-destination\",\n `id=${udid}`\n ];\n}\n\nexport type IosSession = {\n client: IosAgentClient;\n driver: SessionDriver;\n /** The resolved bundle id being driven. */\n bundleId: string;\n /** The UDID of the simulator being driven. */\n udid: string;\n /** Tear down the session, WDA runner, and target app (best effort). */\n teardown(): Promise<void>;\n};\n\nexport type LaunchIosOptions = {\n /** Bundle id or `.app` path. */\n app: string;\n /** Simulator UDID when more than one is booted. */\n udid?: string;\n /** Uninstall+reinstall the app before launch (requires a `.app` path). */\n coldStart?: boolean;\n timeoutMs?: number;\n // --- injectables (tests / advanced use) ---\n runner?: SimctlRunner;\n /** Spawner for the long-running `xcodebuild test-without-building` WDA host. */\n spawner?: XcrunSpawner;\n /** Builds the per-launch port-injected xctestrun. */\n testRunPreparer?: WdaTestRunPreparer;\n portAllocator?: () => Promise<number>;\n agentConnector?: IosAgentConnector;\n /** Skip WDA build/resolution by supplying the base `.xctestrun` path directly. */\n wdaTestRun?: string;\n /** Optional app scope guardrail from config.guardrails.allowedApps. */\n allowedApps?: string[];\n /** Override the simulator lock root; intended for tests. */\n simulatorLockRoot?: string;\n logger?: (message: string) => void;\n};\n\n/**\n * Resolve the bundle id to drive. A bare bundle id is used directly; a `.app`\n * path is validated, its `CFBundleIdentifier` read from the bundle's root\n * `Info.plist`, and (after guardrail check) installed. `coldStart` reinstalls a\n * `.app`; with a bare bundle id it is a hard, actionable error.\n */\nasync function resolveBundleId(\n app: string,\n runner: SimctlRunner,\n udid: string,\n coldStart: boolean,\n allowedApps: string[]\n): Promise<string> {\n if (!looksLikeIosAppPath(app)) {\n assertIosAppAllowed(allowedApps, app);\n if (coldStart) {\n throw new Error(\n `coldStart requires target.app to be a built .app bundle path (a bare bundle id like ` +\n `\"${app}\" cannot be reinstalled). Point target.app at the .app, or drop coldStart.`\n );\n }\n return app;\n }\n const appPath = path.resolve(app);\n if (!fs.existsSync(appPath)) {\n throw new Error(`.app bundle not found: ${appPath}`);\n }\n const bundleId = readIosBundleIdentifier(appPath);\n if (!bundleId) {\n throw new Error(\n `Could not read CFBundleIdentifier from \"${appPath}\" (root Info.plist). ` +\n \"Ensure target.app points at a built iOS .app bundle.\"\n );\n }\n assertIosAppAllowed(allowedApps, appPath);\n if (coldStart) {\n await uninstallApp(runner, udid, bundleId);\n }\n await installApp(runner, udid, appPath);\n return bundleId;\n}\n\n/**\n * Preflight, host WDA via `xcodebuild test-without-building`, launch the target\n * app, and attach, returning a live {@link IosSession}. Actionable errors cover\n * each failure mode: Xcode/simctl missing, no booted simulator (device\n * selection), WDA build failures, agent unreachable (readiness).\n */\nexport async function launchIosSession(options: LaunchIosOptions): Promise<IosSession> {\n const runner = options.runner ?? execFileXcrunRunner;\n const spawner: XcrunSpawner = options.spawner ?? spawnXcrunProcess;\n const preparer: WdaTestRunPreparer = options.testRunPreparer ?? defaultWdaTestRunPreparer;\n const portAllocator = options.portAllocator ?? findFreePort;\n const connector = options.agentConnector ?? defaultIosAgentConnector;\n\n const requestTimeoutMs = Math.max(options.timeoutMs ?? 10000, DEFAULT_WDA_REQUEST_TIMEOUT_MS) + 5000;\n // WDA's HTTP server can take a while to come up on a cold simulator.\n const readyDeadlineMs = Math.max(options.timeoutMs ?? 10000, 60000);\n\n // Preflight: simctl reachable + exactly one (or the requested) booted simulator.\n const devices = await listSimulators(runner);\n const udid = selectSimulatorUdid(devices, options.udid);\n const reservation = await reserveSimulatorUdid(udid, { lockRoot: options.simulatorLockRoot });\n\n let reservationReleased = false;\n const releaseReservation = async (): Promise<void> => {\n if (reservationReleased) {\n return;\n }\n reservationReleased = true;\n await reservation.release();\n };\n\n try {\n const bundleId = await resolveBundleId(\n options.app,\n runner,\n udid,\n options.coldStart ?? false,\n options.allowedApps ?? []\n );\n\n const baseTestRun =\n options.wdaTestRun ?? (await resolveWdaTestRun({ runner, logger: options.logger }));\n\n for (let attempt = 1; attempt <= WDA_STARTUP_ATTEMPTS; attempt += 1) {\n let client: IosAgentClient | undefined;\n let wdaProcess: XcrunProcessHandle | undefined;\n let preparedTestRun: string | undefined;\n let tornDown = false;\n let terminateTarget = false;\n let stage: \"prepare\" | \"wda-launch\" | \"target-launch\" | \"connect\" = \"prepare\";\n const teardownAttempt = async (): Promise<void> => {\n if (tornDown) {\n return;\n }\n tornDown = true;\n if (client) {\n await client.close().catch(() => undefined);\n }\n // Killing the xcodebuild test process ends the hosted WDA runner; the\n // best-effort terminate is a belt-and-braces cleanup on the simulator.\n wdaProcess?.kill();\n await terminateApp(runner, udid, WDA_RUNNER_BUNDLE_ID);\n if (terminateTarget) {\n await terminateApp(runner, udid, bundleId);\n }\n cleanupPreparedTestRun(preparedTestRun);\n };\n\n try {\n const port = await portAllocator();\n preparedTestRun = await preparer({ xctestrunPath: baseTestRun, port });\n\n stage = \"wda-launch\";\n wdaProcess = spawner(wdaTestRunArgs(preparedTestRun, udid));\n\n stage = \"target-launch\";\n terminateTarget = true;\n await launchApp(runner, udid, bundleId);\n\n stage = \"connect\";\n client = await connector({ host: \"127.0.0.1\", port, bundleId, requestTimeoutMs, readyDeadlineMs });\n const driver = createIosDriver(client, {\n appLabel: bundleId,\n captureScreenshot: (outPath: string) => captureScreenshot(runner, udid, outPath)\n });\n const teardown = async (): Promise<void> => {\n await teardownAttempt();\n await releaseReservation();\n };\n return { client, driver, bundleId, udid, teardown };\n } catch (error) {\n await teardownAttempt();\n if (stage === \"target-launch\" || attempt >= WDA_STARTUP_ATTEMPTS) {\n throw error instanceof Error ? error : new Error(String(error));\n }\n }\n }\n\n throw new Error(\"WebDriverAgent startup failed without an error.\");\n } catch (error) {\n await releaseReservation().catch(() => undefined);\n throw error;\n }\n}\n\n/** Tear down an iOS session (agent session, WDA runner, target app). */\nexport async function closeIosSession(session: IosSession): Promise<void> {\n await session.teardown();\n}\n","/**\n * The minimal probe surface healing needs: count matches for a candidate\n * selector. A live Playwright page and a {@link SessionDriver}-backed adapter\n * both satisfy it, so healing carries no Playwright dependency.\n */\nexport type SelectorProbe = {\n locator(selector: string): { count(): Promise<number> };\n};\n\n/**\n * Self-healing selectors (PROWL-023). When an explicit selector matches nothing,\n * derive the human \"intent\" from the selector and try alternative strategies —\n * fuzzy text, ARIA label, and structural (interactive element + text) — healing\n * ONLY to a candidate that resolves to exactly one element. Opt-in via\n * `guardrails.selfHealing`; never guesses among multiple matches.\n */\n\nexport type HealResult = {\n /** The candidate selector that uniquely matched. */\n selector: string;\n /** The original selector that failed. */\n healedFrom: string;\n /** Which strategy produced the match (for reporting). */\n strategy: \"text\" | \"aria\" | \"structural\";\n};\n\nconst INTERACTIVE_TAGS = [\"button\", \"a\", \"input\", \"select\", \"textarea\"];\n\n/**\n * Pull human-meaningful words out of a raw selector. Reads id (`#submit-btn`),\n * class tokens (`.login-form`), and attribute values (`[data-testid=\"sign-in\"]`,\n * `[aria-label='Close']`), splitting on separators and camelCase. Returns the\n * lowercased words (de-duped, in order) and a space-joined label. Mapping is\n * intentionally literal/predictable — no noise-word filtering.\n */\nexport function extractSelectorIntent(selector: string): { words: string[]; label: string } {\n const raw: string[] = [];\n\n // #id and .class tokens\n for (const match of selector.matchAll(/[#.]([A-Za-z_][\\w-]*)/g)) {\n raw.push(match[1]);\n }\n // attribute values: [attr=\"value\"] / [attr='value'] / [attr=value]\n for (const match of selector.matchAll(/\\[[A-Za-z_:-]+\\s*[~|^$*]?=\\s*(?:\"([^\"]*)\"|'([^']*)'|([^\\]\\s]+))\\]/g)) {\n const value = match[1] ?? match[2] ?? match[3];\n if (value) raw.push(value);\n }\n\n const words: string[] = [];\n for (const token of raw) {\n for (const part of splitToken(token)) {\n const lower = part.toLowerCase();\n if (lower.length > 0 && !words.includes(lower)) {\n words.push(lower);\n }\n }\n }\n\n return { words, label: words.join(\" \") };\n}\n\nfunction splitToken(token: string): string[] {\n return token\n // camelCase / PascalCase boundaries\n .replace(/([a-z0-9])([A-Z])/g, \"$1 $2\")\n // separators\n .split(/[\\s\\-_.:]+/)\n .filter((part) => part.length > 0);\n}\n\n/**\n * Build candidate Playwright selectors for a derived intent, in AC priority order:\n * (1) fuzzy text, (2) ARIA label, (3) structural (interactive element + text).\n * Returns [] when the selector carried no usable words.\n */\nexport function buildHealCandidates(selector: string): Array<{ selector: string; strategy: HealResult[\"strategy\"] }> {\n const { words, label } = extractSelectorIntent(selector);\n if (words.length === 0) return [];\n\n const escaped = label.replace(/\"/g, '\\\\\"');\n const candidates: Array<{ selector: string; strategy: HealResult[\"strategy\"] }> = [];\n\n // 1. Similar text content (case-insensitive substring via Playwright text engine)\n candidates.push({ selector: `text=${label}`, strategy: \"text\" });\n\n // 2. Similar ARIA label (case-insensitive attribute substring)\n candidates.push({ selector: `[aria-label*=\"${escaped}\" i]`, strategy: \"aria\" });\n\n // 3. Nearby element with matching structure: an interactive element containing the text\n for (const tag of INTERACTIVE_TAGS) {\n candidates.push({ selector: `${tag}:has-text(\"${escaped}\")`, strategy: \"structural\" });\n }\n\n return candidates;\n}\n\n/**\n * Attempt to heal a failed selector. Returns a HealResult only when a candidate\n * resolves to exactly one element; otherwise null. Counting is delegated so this\n * is unit-testable with a fake page.\n */\nexport async function healSelector(\n probe: SelectorProbe,\n selector: string,\n options: { enabled: boolean }\n): Promise<HealResult | null> {\n if (!options.enabled) return null;\n\n for (const candidate of buildHealCandidates(selector)) {\n let count: number;\n try {\n const locator = probe.locator(candidate.selector);\n count = await locator.count();\n } catch {\n continue; // ignore candidates the engine cannot parse\n }\n if (count === 1) {\n return { selector: candidate.selector, healedFrom: selector, strategy: candidate.strategy };\n }\n }\n\n return null;\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { HistoryEntry, HistoryFile } from \"../types/index.js\";\n\nconst HISTORY_FILE = \"history.json\";\nconst LOCK_FILE_SUFFIX = \".lock\";\nconst LOCK_RETRY_MS = 10;\nconst LOCK_TIMEOUT_MS = 5000;\nconst SLEEP_BUFFER = new Int32Array(new SharedArrayBuffer(4));\n\nfunction historyPath(configDir: string): string {\n return path.join(configDir, HISTORY_FILE);\n}\n\nfunction isHistoryEntry(value: unknown): value is HistoryEntry {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n\n const entry = value as Record<string, unknown>;\n return (\n typeof entry.hunt === \"string\" &&\n (entry.status === \"pass\" || entry.status === \"fail\") &&\n typeof entry.durationMs === \"number\" &&\n Number.isFinite(entry.durationMs) &&\n typeof entry.startedAt === \"string\" &&\n (entry.runDir === undefined || typeof entry.runDir === \"string\")\n );\n}\n\nexport function readHistory(configDir: string): HistoryFile {\n const filePath = historyPath(configDir);\n if (!fs.existsSync(filePath)) {\n return { entries: [] };\n }\n try {\n const raw = fs.readFileSync(filePath, \"utf-8\");\n const parsed = JSON.parse(raw) as unknown;\n if (\n parsed &&\n typeof parsed === \"object\" &&\n \"entries\" in parsed &&\n Array.isArray((parsed as { entries: unknown }).entries)\n ) {\n const validatedEntries = (parsed as { entries: unknown[] }).entries.filter(isHistoryEntry);\n return { entries: validatedEntries };\n }\n return { entries: [] };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n console.warn(`Failed to read history file at ${filePath}: ${message}`);\n return { entries: [] };\n }\n}\n\nexport function readHuntHistory(configDir: string, huntName: string): HistoryEntry[] {\n const { entries } = readHistory(configDir);\n return entries.filter((entry) => entry.hunt === huntName);\n}\n\nexport function pruneEntries(entries: HistoryEntry[], maxRuns: number): HistoryEntry[] {\n const perHunt = new Map<string, HistoryEntry[]>();\n for (const entry of entries) {\n const list = perHunt.get(entry.hunt) ?? [];\n list.push(entry);\n perHunt.set(entry.hunt, list);\n }\n\n const keptEntries = new Set<HistoryEntry>();\n for (const list of perHunt.values()) {\n const kept = list.length > maxRuns ? list.slice(list.length - maxRuns) : list;\n for (const entry of kept) {\n keptEntries.add(entry);\n }\n }\n return entries.filter((entry) => keptEntries.has(entry));\n}\n\nfunction sleepSync(ms: number): void {\n Atomics.wait(SLEEP_BUFFER, 0, 0, ms);\n}\n\nfunction withHistoryLock<T>(configDir: string, fn: () => T): T {\n const filePath = historyPath(configDir);\n const lockPath = `${filePath}${LOCK_FILE_SUFFIX}`;\n fs.mkdirSync(path.dirname(filePath), { recursive: true });\n const startedAt = Date.now();\n\n while (Date.now() - startedAt < LOCK_TIMEOUT_MS) {\n let fd: number;\n try {\n fd = fs.openSync(lockPath, \"wx\");\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"EEXIST\") {\n sleepSync(LOCK_RETRY_MS);\n continue;\n }\n throw error;\n }\n\n try {\n return fn();\n } finally {\n try {\n fs.closeSync(fd);\n } catch {\n // Ignore close failures during cleanup.\n }\n fs.rmSync(lockPath, { force: true });\n }\n }\n\n throw new Error(\n `Failed to acquire history lock before timeout (${LOCK_TIMEOUT_MS}ms): ${lockPath}; started waiting at ${new Date(startedAt).toISOString()}`\n );\n}\n\nexport function appendEntry(\n configDir: string,\n entry: HistoryEntry,\n maxRuns: number\n): void {\n const filePath = historyPath(configDir);\n withHistoryLock(configDir, () => {\n const current = readHistory(configDir);\n const next = pruneEntries([...current.entries, entry], maxRuns);\n const tempPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;\n fs.writeFileSync(tempPath, `${JSON.stringify({ entries: next }, null, 2)}\\n`);\n fs.renameSync(tempPath, filePath);\n });\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { AndroidTarget, AssertionResult, BrowserChannel, Config, IosTarget, MacosTarget, RunResult, Step, StepResult, TraceCorrelation } from \"../types/index.js\";\nimport { loadConfig, loadHunt, ensureAllowedDomain, resolveViewport } from \"../config/loader.js\";\nimport { interpolateHunt } from \"../config/interpolate.js\";\nimport {\n assertAndroidAppAllowed,\n assertIosAppAllowed,\n assertStepsSupportedByTarget,\n assertTargetAppAllowed,\n nativeTargetLabel\n} from \"../config/target.js\";\nimport { launchBrowser, closeBrowser, createPlaywrightDriver } from \"../browser/controller.js\";\nimport { launchMacSession, closeMacSession, type MacSession } from \"../browser/mac-helper.js\";\nimport type { MacHelperClient } from \"../browser/mac-driver.js\";\nimport type { SessionDriver } from \"../browser/driver.js\";\nimport {\n launchAndroidSession,\n closeAndroidSession,\n type AndroidSession,\n type LaunchAndroidOptions\n} from \"../browser/android-helper.js\";\nimport {\n launchIosSession,\n closeIosSession,\n type IosSession,\n type LaunchIosOptions\n} from \"../browser/ios-helper.js\";\nimport { captureFinalScreenshot, executeSteps, type StepCallback } from \"./steps.js\";\nimport {\n evaluateAssertions,\n evaluateNativeAssertions,\n type ConsoleEntry,\n type NetworkEntry\n} from \"./assertions.js\";\nimport { createRunPolicy } from \"./policy.js\";\nimport { captureTraceCorrelation, DEFAULT_TRACE_HEADER } from \"./tracing.js\";\nimport { writeReports } from \"../reporter/index.js\";\nimport { timestamp } from \"../utils/timestamp.js\";\nimport { appendEntry as appendHistoryEntry } from \"./history.js\";\n\ntype NativeTargetType = \"macos\" | \"android\" | \"ios\";\ntype NativeRunTarget = MacosTarget | AndroidTarget | IosTarget;\ntype InterpolatedHunt = ReturnType<typeof interpolateHunt>[\"hunt\"];\ntype InterpolationRandomVars = ReturnType<typeof interpolateHunt>[\"randomVars\"];\ntype HuntOutcome = { result: RunResult; runDir: string; steps: Step[] };\n\ntype NativeAttemptOptions<TSession> = {\n targetType: NativeTargetType;\n targetApp: string;\n launchSession: () => Promise<TSession>;\n closeSession: (session: TSession) => Promise<void>;\n sessionDriver: (session: TSession) => SessionDriver;\n sessionAppIdentity: (session: TSession) => string;\n};\n\ntype NativeAttemptFunction<TTarget extends NativeRunTarget> = (\n options: RunOptions,\n config: Config,\n configDir: string,\n target: TTarget,\n interpolatedHunt: InterpolatedHunt,\n redactedFillSteps: Set<string>,\n randomVars: InterpolationRandomVars,\n allowedApps: string[]\n) => Promise<HuntOutcome>;\n\nexport type RunOptions = {\n huntName: string;\n urlOverride?: string;\n headed?: boolean;\n slowMo?: number;\n trace?: boolean;\n configPath?: string;\n onStep?: StepCallback;\n browser?: \"chromium\" | \"firefox\" | \"webkit\";\n channel?: BrowserChannel;\n viewport?: string;\n junit?: boolean;\n /** Inject a macOS helper client (tests / a prebuilt binary); defaults to spawning the helper. */\n macClientFactory?: () => MacHelperClient;\n /** Inject an Android session factory (tests); defaults to {@link launchAndroidSession}. */\n androidSessionFactory?: (options: LaunchAndroidOptions) => Promise<AndroidSession>;\n /** Inject an iOS session factory (tests); defaults to {@link launchIosSession}. */\n iosSessionFactory?: (options: LaunchIosOptions) => Promise<IosSession>;\n};\n\nfunction parseViewportFlag(value: string): string | { width: number; height: number } {\n const match = /^(\\d+)x(\\d+)$/i.exec(value);\n if (match) {\n return { width: Number(match[1]), height: Number(match[2]) };\n }\n return value;\n}\n\nfunction resolvePath(configDir: string, inputPath: string): string {\n if (path.isAbsolute(inputPath)) {\n return inputPath;\n }\n const projectRoot = path.dirname(configDir);\n return path.join(projectRoot, inputPath);\n}\n\nfunction buildRunResult(options: {\n status: \"pass\" | \"fail\";\n startedAt: string;\n durationMs: number;\n hunt: string;\n targetUrl: string;\n steps: StepResult[];\n assertions: AssertionResult[];\n artifacts: RunResult[\"artifacts\"];\n traceCorrelations?: TraceCorrelation[];\n}): RunResult {\n return {\n status: options.status,\n exitCode: options.status === \"pass\" ? 0 : 1,\n startedAt: options.startedAt,\n durationMs: options.durationMs,\n hunt: options.hunt,\n targetUrl: options.targetUrl,\n steps: options.steps,\n assertions: options.assertions,\n artifacts: options.artifacts,\n // Omit entirely when there are no correlations, so passing/clean runs stay tidy.\n ...(options.traceCorrelations && options.traceCorrelations.length > 0\n ? { traceCorrelations: options.traceCorrelations }\n : {})\n };\n}\n\nfunction writeConsoleLog(runDir: string, entries: ConsoleEntry[]): string {\n const fileName = \"console.log\";\n const filePath = path.join(runDir, fileName);\n const lines = entries.map((entry) => {\n const location = entry.location ? ` (${entry.location})` : \"\";\n return `[${entry.type}] ${entry.text}${location}`;\n });\n fs.writeFileSync(filePath, `${lines.join(\"\\n\")}\\n`);\n return fileName;\n}\n\nasync function executeHuntAttempt(\n options: RunOptions,\n config: ReturnType<typeof loadConfig>[\"config\"],\n configDir: string,\n interpolatedHunt: ReturnType<typeof interpolateHunt>[\"hunt\"],\n redactedFillSteps: Set<string>,\n randomVars: ReturnType<typeof interpolateHunt>[\"randomVars\"],\n redactionValues: readonly string[],\n targetUrl: string,\n allowedDomains: string[]\n): Promise<{ result: RunResult; runDir: string; steps: Step[] }> {\n const headless = options.headed ? false : config.browser.headless;\n const slowMo = options.slowMo ?? config.browser.slowMo;\n const maxSteps = config.guardrails.maxSteps;\n\n const runDir = path.join(configDir, \"runs\", timestamp());\n fs.mkdirSync(runDir, { recursive: true });\n\n const storageStatePath = config.auth.storageStatePath\n ? resolvePath(configDir, config.auth.storageStatePath)\n : undefined;\n\n const engine = options.browser ?? config.browser.engine;\n const channel = options.channel ?? config.browser.channel;\n const viewport = options.viewport\n ? resolveViewport(parseViewportFlag(options.viewport))\n : config.browser.viewport;\n\n const session = await launchBrowser({\n headless,\n slowMo,\n timeout: config.browser.timeout,\n storageStatePath,\n trace: Boolean(options.trace),\n recordHar: config.artifacts.networkHar,\n runDir,\n engine,\n channel,\n viewport\n });\n\n let result: RunResult;\n try {\n const driver = createPlaywrightDriver(session.page);\n const consoleEntries: ConsoleEntry[] = [];\n const networkEntries: NetworkEntry[] = [];\n const traceCorrelations: TraceCorrelation[] = [];\n const traceHeader = config.tracing?.header ?? DEFAULT_TRACE_HEADER;\n\n session.page.on(\"console\", (message) => {\n consoleEntries.push({\n type: message.type(),\n text: message.text(),\n location: message.location().url\n });\n });\n\n driver.onResponse((response) => {\n if (response.status() >= 400) {\n networkEntries.push({ url: response.url(), status: response.status() });\n captureTraceCorrelation(response, traceHeader, traceCorrelations, redactionValues);\n }\n });\n\n const startedAt = new Date().toISOString();\n const startTime = Date.now();\n\n let stepResults: StepResult[] = [];\n let stepScreenshots: string[] = [];\n let stepFailed = false;\n\n try {\n const stepExecution = await executeSteps({\n page: session.page,\n driver,\n steps: interpolatedHunt.steps,\n targetUrl,\n runDir,\n screenshotsMode: config.artifacts.screenshots,\n forbiddenSelectors: config.guardrails.forbiddenSelectors,\n allowedDomains,\n maxSteps,\n maxTotalTimeMs: config.assertions.maxTotalTimeMs,\n selfHealing: config.guardrails.selfHealing,\n redactedFillSteps,\n randomVars,\n configDir,\n huntStack: [options.huntName],\n onStep: options.onStep\n });\n\n stepResults = stepExecution.results;\n stepScreenshots = stepExecution.screenshots;\n stepFailed = stepExecution.failed;\n } catch (error) {\n const message = error instanceof Error ? error.message : \"Step execution failed\";\n stepResults = [\n {\n type: \"steps\",\n status: \"fail\",\n durationMs: 0,\n error: message\n }\n ];\n stepFailed = true;\n }\n\n let finalScreenshot: string | undefined;\n try {\n finalScreenshot = await captureFinalScreenshot(driver, runDir);\n } catch {\n finalScreenshot = undefined;\n }\n\n const assertionResults = await evaluateAssertions({\n page: session.page,\n config,\n huntAssertions: interpolatedHunt.assertions,\n consoleEntries,\n networkEntries\n });\n\n const durationMs = Date.now() - startTime;\n const assertionsFailed = assertionResults.some((assertion) => assertion.status === \"fail\");\n\n const status: \"pass\" | \"fail\" = stepFailed || assertionsFailed ? \"fail\" : \"pass\";\n\n const artifacts: RunResult[\"artifacts\"] = {\n screenshots: finalScreenshot\n ? [...stepScreenshots, finalScreenshot]\n : stepScreenshots,\n trace: session.tracePath ? \"trace.zip\" : undefined,\n networkHar: config.artifacts.networkHar ? \"network.har\" : undefined\n };\n\n if (config.artifacts.console) {\n artifacts.console = writeConsoleLog(runDir, consoleEntries);\n }\n\n const runResult = buildRunResult({\n status,\n startedAt,\n durationMs,\n hunt: options.huntName,\n targetUrl,\n steps: stepResults,\n assertions: assertionResults,\n artifacts,\n traceCorrelations\n });\n\n result = writeReports(runDir, runResult, { junit: options.junit ?? config.artifacts.junit });\n } finally {\n await closeBrowser(session);\n }\n\n return { result, runDir, steps: interpolatedHunt.steps };\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport async function runHunt(\n options: RunOptions\n): Promise<{ result: RunResult; runDir: string; steps: Step[] }> {\n const { config, configDir } = loadConfig(options.configPath);\n\n if (config.target.type === \"macos\") {\n return runMacHunt(options, config, configDir, config.target);\n }\n\n if (config.target.type === \"android\") {\n return runAndroidHunt(options, config, configDir, config.target);\n }\n\n if (config.target.type === \"ios\") {\n return runIosHunt(options, config, configDir, config.target);\n }\n\n const hunt = loadHunt(options.huntName, configDir);\n const {\n hunt: interpolatedHunt,\n redactedFillSteps,\n randomVars,\n redactionValues = []\n } = interpolateHunt(\n hunt,\n process.env\n );\n\n const targetUrl = options.urlOverride ?? config.target.url;\n const allowedDomains = ensureAllowedDomain([...config.guardrails.allowedDomains], targetUrl);\n const maxSteps = config.guardrails.maxSteps;\n\n if (interpolatedHunt.steps.length > maxSteps) {\n throw new Error(`Hunt has ${interpolatedHunt.steps.length} steps. Max allowed is ${maxSteps}.`);\n }\n\n const maxRetries = hunt.retry?.maxRetries ?? 0;\n const retryDelay = hunt.retry?.delay ?? 0;\n\n let lastResult: { result: RunResult; runDir: string; steps: Step[] } | undefined;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n if (attempt > 0 && retryDelay > 0) {\n await delay(retryDelay);\n }\n\n lastResult = await executeHuntAttempt(\n options,\n config,\n configDir,\n interpolatedHunt,\n redactedFillSteps,\n randomVars,\n redactionValues,\n targetUrl,\n allowedDomains\n );\n\n if (lastResult.result.status === \"pass\") {\n if (attempt > 0) {\n lastResult.result.artifacts.summary =\n `Passed on attempt ${attempt + 1} of ${maxRetries + 1}`;\n }\n recordHistory(configDir, lastResult, config.history.maxRuns);\n return lastResult;\n }\n }\n\n if (maxRetries > 0 && lastResult) {\n lastResult.result.artifacts.summary =\n `Failed after ${maxRetries + 1} attempts`;\n }\n\n if (lastResult) {\n recordHistory(configDir, lastResult, config.history.maxRuns);\n }\n\n return lastResult!;\n}\n\n// ---------------------------------------------------------------------------\n// macOS native target (PROWL-048). A dedicated run path: no browser, no console/\n// network assertions or HAR/trace (all web concepts). Steps run through the\n// MacDriver over the prowl-macdriver helper; artifacts are step + final\n// screenshots and the usual reports.\n// ---------------------------------------------------------------------------\n\nasync function executeNativeHuntAttempt<TSession>(\n options: RunOptions,\n config: Config,\n configDir: string,\n interpolatedHunt: InterpolatedHunt,\n redactedFillSteps: Set<string>,\n randomVars: InterpolationRandomVars,\n allowedApps: string[],\n native: NativeAttemptOptions<TSession>\n): Promise<HuntOutcome> {\n const maxSteps = config.guardrails.maxSteps;\n const runDir = path.join(configDir, \"runs\", timestamp());\n fs.mkdirSync(runDir, { recursive: true });\n\n const session = await native.launchSession();\n\n let result: RunResult;\n try {\n const driver = native.sessionDriver(session);\n const appIdentity = native.sessionAppIdentity(session);\n const targetLabel = `${native.targetType}:${appIdentity}`;\n const effectiveAllowedApps = [...new Set([...allowedApps, native.targetApp, appIdentity])];\n const assertionPolicy = createRunPolicy(driver, {\n forbiddenSelectors: config.guardrails.forbiddenSelectors,\n allowedDomains: [],\n allowedApps: effectiveAllowedApps,\n maxSteps,\n selfHealing: config.guardrails.selfHealing\n });\n const startedAt = new Date().toISOString();\n const startTime = Date.now();\n\n let stepResults: StepResult[] = [];\n let stepScreenshots: string[] = [];\n let stepFailed = false;\n\n try {\n const stepExecution = await executeSteps({\n driver,\n targetType: native.targetType,\n steps: interpolatedHunt.steps,\n targetUrl: targetLabel,\n runDir,\n screenshotsMode: config.artifacts.screenshots,\n forbiddenSelectors: config.guardrails.forbiddenSelectors,\n allowedDomains: [],\n allowedApps: effectiveAllowedApps,\n maxSteps,\n maxTotalTimeMs: config.assertions.maxTotalTimeMs,\n selfHealing: config.guardrails.selfHealing,\n redactedFillSteps,\n randomVars,\n configDir,\n huntStack: [options.huntName],\n onStep: options.onStep\n });\n stepResults = stepExecution.results;\n stepScreenshots = stepExecution.screenshots;\n stepFailed = stepExecution.failed;\n } catch (error) {\n const message = error instanceof Error ? error.message : \"Step execution failed\";\n stepResults = [{ type: \"steps\", status: \"fail\", durationMs: 0, error: message }];\n stepFailed = true;\n }\n\n let finalScreenshot: string | undefined;\n try {\n finalScreenshot = await captureFinalScreenshot(driver, runDir);\n } catch {\n finalScreenshot = undefined;\n }\n\n // Evaluate hunt-/config-level assertions after steps complete, matching the\n // web path's semantics (assertions run even when a step failed). Applicable\n // types (selectorExists/selectorNotExists) run against the driver; web-only\n // types are reported as skipped and warned, never silently dropped.\n const { results: assertionResults, warnings: assertionWarnings } =\n await evaluateNativeAssertions({\n driver,\n config,\n huntAssertions: interpolatedHunt.assertions,\n assertAllowedSelector: assertionPolicy.assertAllowedSelector,\n targetLabel: nativeTargetLabel(native.targetType)\n });\n for (const warning of assertionWarnings) {\n console.warn(warning);\n }\n\n const durationMs = Date.now() - startTime;\n const assertionsFailed = assertionResults.some((assertion) => assertion.status === \"fail\");\n const status: \"pass\" | \"fail\" = stepFailed || assertionsFailed ? \"fail\" : \"pass\";\n const artifacts: RunResult[\"artifacts\"] = {\n screenshots: finalScreenshot ? [...stepScreenshots, finalScreenshot] : stepScreenshots\n };\n\n const runResult = buildRunResult({\n status,\n startedAt,\n durationMs,\n hunt: options.huntName,\n targetUrl: targetLabel,\n steps: stepResults,\n assertions: assertionResults,\n artifacts\n });\n\n result = writeReports(runDir, runResult, { junit: options.junit ?? config.artifacts.junit });\n } finally {\n await native.closeSession(session);\n }\n\n return { result, runDir, steps: interpolatedHunt.steps };\n}\n\nasync function executeMacHuntAttempt(\n options: RunOptions,\n config: Config,\n configDir: string,\n target: MacosTarget,\n interpolatedHunt: InterpolatedHunt,\n redactedFillSteps: Set<string>,\n randomVars: InterpolationRandomVars,\n allowedApps: string[]\n): Promise<HuntOutcome> {\n return executeNativeHuntAttempt<MacSession>(\n options,\n config,\n configDir,\n interpolatedHunt,\n redactedFillSteps,\n randomVars,\n allowedApps,\n {\n targetType: \"macos\",\n targetApp: target.app,\n launchSession: () =>\n launchMacSession({\n app: target.app,\n timeoutMs: config.browser.timeout,\n clientFactory: options.macClientFactory\n }),\n closeSession: closeMacSession,\n sessionDriver: (session) => session.driver,\n sessionAppIdentity: (session) => session.bundleId\n }\n );\n}\n\nasync function runMacHunt(\n options: RunOptions,\n config: Config,\n configDir: string,\n target: MacosTarget\n): Promise<HuntOutcome> {\n return runNativeHunt(options, config, configDir, target, {\n targetType: \"macos\",\n assertAppAllowed: (allowedApps, nativeTarget) => assertTargetAppAllowed(allowedApps, nativeTarget.app),\n attempt: executeMacHuntAttempt\n });\n}\n\n// ---------------------------------------------------------------------------\n// Android native target (PROWL-058). Mirrors the macOS run path: no browser, no\n// console/network assertions or HAR/trace. Steps run through the AndroidDriver\n// over the on-device uiautomator2 agent; artifacts are step + final screenshots\n// and the usual reports.\n// ---------------------------------------------------------------------------\n\nasync function executeAndroidHuntAttempt(\n options: RunOptions,\n config: Config,\n configDir: string,\n target: AndroidTarget,\n interpolatedHunt: InterpolatedHunt,\n redactedFillSteps: Set<string>,\n randomVars: InterpolationRandomVars,\n allowedApps: string[]\n): Promise<HuntOutcome> {\n const launch = options.androidSessionFactory ?? launchAndroidSession;\n return executeNativeHuntAttempt<AndroidSession>(\n options,\n config,\n configDir,\n interpolatedHunt,\n redactedFillSteps,\n randomVars,\n allowedApps,\n {\n targetType: \"android\",\n targetApp: target.app,\n launchSession: () =>\n launch({\n app: target.app,\n deviceSerial: target.deviceSerial,\n coldStart: target.coldStart,\n timeoutMs: config.browser.timeout,\n allowedApps\n }),\n closeSession: closeAndroidSession,\n sessionDriver: (session) => session.driver,\n sessionAppIdentity: (session) => session.package\n }\n );\n}\n\nasync function runAndroidHunt(\n options: RunOptions,\n config: Config,\n configDir: string,\n target: AndroidTarget\n): Promise<HuntOutcome> {\n return runNativeHunt(options, config, configDir, target, {\n targetType: \"android\",\n assertAppAllowed: (allowedApps, nativeTarget) => {\n if (!nativeTarget.app.toLowerCase().endsWith(\".apk\")) {\n assertAndroidAppAllowed(allowedApps, nativeTarget.app);\n }\n },\n attempt: executeAndroidHuntAttempt\n });\n}\n\n// ---------------------------------------------------------------------------\n// iOS simulator native target (PROWL-059). Mirrors the Android run path: no\n// browser, no console/network assertions or HAR/trace. Steps run through the\n// IosDriver over the on-simulator WebDriverAgent; screenshots come from simctl.\n// ---------------------------------------------------------------------------\n\nasync function executeIosHuntAttempt(\n options: RunOptions,\n config: Config,\n configDir: string,\n target: IosTarget,\n interpolatedHunt: InterpolatedHunt,\n redactedFillSteps: Set<string>,\n randomVars: InterpolationRandomVars,\n allowedApps: string[]\n): Promise<HuntOutcome> {\n const launch = options.iosSessionFactory ?? launchIosSession;\n return executeNativeHuntAttempt<IosSession>(\n options,\n config,\n configDir,\n interpolatedHunt,\n redactedFillSteps,\n randomVars,\n allowedApps,\n {\n targetType: \"ios\",\n targetApp: target.app,\n launchSession: () =>\n launch({\n app: target.app,\n udid: target.udid,\n coldStart: target.coldStart,\n timeoutMs: config.browser.timeout,\n allowedApps\n }),\n closeSession: closeIosSession,\n sessionDriver: (session) => session.driver,\n sessionAppIdentity: (session) => session.bundleId\n }\n );\n}\n\nasync function runIosHunt(\n options: RunOptions,\n config: Config,\n configDir: string,\n target: IosTarget\n): Promise<HuntOutcome> {\n return runNativeHunt(options, config, configDir, target, {\n targetType: \"ios\",\n assertAppAllowed: (allowedApps, nativeTarget) => assertIosAppAllowed(allowedApps, nativeTarget.app),\n attempt: executeIosHuntAttempt\n });\n}\n\nasync function runNativeHunt<TTarget extends NativeRunTarget>(\n options: RunOptions,\n config: Config,\n configDir: string,\n target: TTarget,\n native: {\n targetType: NativeTargetType;\n assertAppAllowed: (allowedApps: string[], target: TTarget) => void;\n attempt: NativeAttemptFunction<TTarget>;\n }\n): Promise<HuntOutcome> {\n const hunt = loadHunt(options.huntName, configDir);\n const { hunt: interpolatedHunt, redactedFillSteps, randomVars } = interpolateHunt(hunt, process.env);\n\n // Fail fast on web-only steps and out-of-scope apps before launching anything.\n // Hunt-level assertions are NOT rejected here: the applicable subset runs after\n // steps and web-only types are reported as skipped (see executeNativeHuntAttempt).\n assertStepsSupportedByTarget(interpolatedHunt.steps, native.targetType);\n native.assertAppAllowed(config.guardrails.allowedApps, target);\n\n const maxSteps = config.guardrails.maxSteps;\n if (interpolatedHunt.steps.length > maxSteps) {\n throw new Error(`Hunt has ${interpolatedHunt.steps.length} steps. Max allowed is ${maxSteps}.`);\n }\n\n const maxRetries = hunt.retry?.maxRetries ?? 0;\n const retryDelay = hunt.retry?.delay ?? 0;\n let lastResult: HuntOutcome | undefined;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n if (attempt > 0 && retryDelay > 0) {\n await delay(retryDelay);\n }\n lastResult = await native.attempt(\n options,\n config,\n configDir,\n target,\n interpolatedHunt,\n redactedFillSteps,\n randomVars,\n config.guardrails.allowedApps\n );\n if (lastResult.result.status === \"pass\") {\n if (attempt > 0) {\n lastResult.result.artifacts.summary = `Passed on attempt ${attempt + 1} of ${maxRetries + 1}`;\n }\n recordHistory(configDir, lastResult, config.history.maxRuns);\n return lastResult;\n }\n }\n\n if (maxRetries > 0 && lastResult) {\n lastResult.result.artifacts.summary = `Failed after ${maxRetries + 1} attempts`;\n }\n if (lastResult) {\n recordHistory(configDir, lastResult, config.history.maxRuns);\n }\n return lastResult!;\n}\n\nfunction recordHistory(\n configDir: string,\n outcome: { result: RunResult; runDir: string },\n maxRuns: number\n): void {\n try {\n const relativeRunDir = path.relative(configDir, outcome.runDir);\n appendHistoryEntry(\n configDir,\n {\n hunt: outcome.result.hunt,\n status: outcome.result.status,\n durationMs: outcome.result.durationMs,\n startedAt: outcome.result.startedAt,\n runDir: relativeRunDir || undefined\n },\n maxRuns\n );\n } catch {\n // History is observability only; a write failure must never break a run.\n }\n}\n","/**\n * PROWL-047 / ARCH-001 — Playwright implementation of {@link SessionDriver}.\n *\n * This is the ONLY module in the codebase that imports Playwright at runtime;\n * everything else drives the session through the engine-neutral `SessionDriver`\n * interface. Browser launch/teardown lives here too (re-exported by\n * `controller.ts` to keep the public import path stable), so the Playwright\n * dependency is confined to this file.\n *\n * Every driver method is a faithful pass-through to the exact Playwright\n * Page/Locator call the legacy runner made — this refactor changes structure,\n * not behaviour.\n */\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport {\n chromium,\n firefox,\n webkit,\n type Browser,\n type BrowserContext,\n type Page\n} from \"playwright\";\nimport type { BrowserChannel, BrowserEngine, Viewport } from \"../types/index.js\";\nimport type {\n DialogAction,\n DriverCapability,\n DriverDownload,\n DriverResponse,\n DriverRoute,\n NavigateOptions,\n SessionDriver\n} from \"./driver.js\";\n\nconst ENGINES = { chromium, firefox, webkit } as const;\n\nexport type BrowserSession = {\n browser: Browser;\n context: BrowserContext;\n page: Page;\n tracePath?: string;\n};\n\nexport type BrowserOptions = {\n headless: boolean;\n slowMo: number;\n timeout: number;\n storageStatePath?: string;\n trace: boolean;\n recordHar: boolean;\n runDir: string;\n engine?: BrowserEngine;\n channel?: BrowserChannel;\n viewport?: Viewport;\n};\n\nexport async function launchBrowser(options: BrowserOptions): Promise<BrowserSession> {\n const engineName = options.engine ?? \"chromium\";\n const engine = Object.prototype.hasOwnProperty.call(ENGINES, engineName)\n ? ENGINES[engineName as keyof typeof ENGINES]\n : undefined;\n if (!engine) {\n throw new Error(\n `Unsupported browser engine \"${String(engineName)}\". Available engines: ${Object.keys(ENGINES).join(\", \")}.`\n );\n }\n const browser = await engine.launch({\n headless: options.headless,\n slowMo: options.slowMo,\n channel: options.channel\n });\n\n try {\n const contextOptions: Parameters<typeof browser.newContext>[0] = {};\n\n if (options.viewport) {\n contextOptions.viewport = options.viewport;\n }\n\n if (options.storageStatePath) {\n if (fs.existsSync(options.storageStatePath)) {\n contextOptions.storageState = options.storageStatePath;\n } else {\n console.warn(`Auth state file not found: ${options.storageStatePath}. Run \"prowl login\" to create it.`);\n }\n }\n\n if (options.recordHar) {\n contextOptions.recordHar = { path: path.join(options.runDir, \"network.har\") };\n }\n\n const context = await browser.newContext(contextOptions);\n const page = await context.newPage();\n page.setDefaultTimeout(options.timeout);\n page.setDefaultNavigationTimeout(options.timeout);\n\n let tracePath: string | undefined;\n if (options.trace) {\n tracePath = path.join(options.runDir, \"trace.zip\");\n await context.tracing.start({ screenshots: true, snapshots: true, sources: true });\n }\n\n return { browser, context, page, tracePath };\n } catch (error) {\n try {\n await browser.close();\n } catch (closeError) {\n console.warn(`Failed to close browser after setup error: ${formatError(closeError)}`);\n }\n throw error;\n }\n}\n\nexport async function closeBrowser(session: BrowserSession): Promise<void> {\n try {\n if (session.tracePath) {\n await session.context.tracing.stop({ path: session.tracePath });\n }\n await session.context.close();\n } finally {\n await session.browser.close();\n }\n}\n\n/** Persist the session's storage state (cookies + localStorage) to disk. */\nexport async function saveStorageState(session: BrowserSession, storageStatePath: string): Promise<void> {\n await session.context.storageState({ path: storageStatePath });\n}\n\nconst ALL_CAPABILITIES: ReadonlySet<DriverCapability> = new Set<DriverCapability>([\n \"navigate\",\n \"query\",\n \"interact\",\n \"wait\",\n \"screenshot\",\n \"evaluate\",\n \"response\",\n \"route\",\n \"dialog\",\n \"files\",\n \"download\"\n]);\n\ntype PlaywrightRole = Parameters<Page[\"getByRole\"]>[0];\n\nfunction formatError(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction unwrapTextSelector(value: string): string | null {\n const trimmed = value.trim();\n if (!trimmed.startsWith(\"text=\")) {\n return null;\n }\n const raw = trimmed.slice(5);\n const first = raw[0];\n if (first === '\"' || first === \"'\") {\n const unquoted = raw.slice(1);\n return unquoted.endsWith(first) ? unquoted.slice(0, -1) : unquoted;\n }\n return raw;\n}\n\n/**\n * Wrap a live Playwright {@link Page} as a {@link SessionDriver}. Each method\n * delegates to the same Page/Locator call the runner previously made inline.\n */\nexport function createPlaywrightDriver(page: Page): SessionDriver {\n return {\n capabilities: ALL_CAPABILITIES,\n\n async goto(url: string, options?: NavigateOptions): Promise<void> {\n if (options?.waitUntil !== undefined) {\n await page.goto(url, { waitUntil: options.waitUntil });\n } else {\n await page.goto(url);\n }\n },\n\n currentUrl(): string {\n return page.url();\n },\n\n count(selector: string): Promise<number> {\n return page.locator(selector).count();\n },\n\n textContent(selector: string): Promise<string | null> {\n return page.locator(selector).textContent();\n },\n\n async click(selector: string): Promise<void> {\n await page.locator(selector).click();\n },\n\n async clickFirst(selector: string): Promise<void> {\n await page.locator(selector).first().click();\n },\n\n async fill(selector: string, value: string): Promise<void> {\n await page.locator(selector).fill(value);\n },\n\n async fillFirst(selector: string, value: string): Promise<void> {\n await page.locator(selector).first().fill(value);\n },\n\n async press(selector: string, key: string): Promise<void> {\n await page.locator(selector).press(key);\n },\n\n async selectOption(selector: string, value: string): Promise<void> {\n await page.locator(selector).selectOption(value);\n },\n\n async selectOptionFirst(selector: string, value: string): Promise<void> {\n await page.locator(selector).first().selectOption(value);\n },\n\n async hover(selector: string): Promise<void> {\n await page.locator(selector).hover();\n },\n\n async scroll(direction: \"up\" | \"down\" | \"left\" | \"right\", amount = 500): Promise<void> {\n const deltas: Record<string, [number, number]> = {\n up: [0, -amount],\n down: [0, amount],\n left: [-amount, 0],\n right: [amount, 0]\n };\n const [x, y] = deltas[direction];\n await page.evaluate(([sx, sy]) => window.scrollBy(sx, sy), [x, y] as [number, number]);\n },\n\n async scrollIntoView(selector: string): Promise<void> {\n await page.locator(selector).scrollIntoViewIfNeeded();\n },\n\n async setInputFiles(selector: string, files: string | string[]): Promise<void> {\n await page.locator(selector).setInputFiles(files);\n },\n\n countByRole(role: string, name: string): Promise<number> {\n return page.getByRole(role as PlaywrightRole, { name }).count();\n },\n\n async clickFirstByRole(role: string, name: string): Promise<void> {\n await page.getByRole(role as PlaywrightRole, { name }).first().click();\n },\n\n countByLabel(label: string): Promise<number> {\n return page.getByLabel(label, { exact: true }).count();\n },\n\n async fillFirstByLabel(label: string, value: string): Promise<void> {\n await page.getByLabel(label, { exact: true }).first().fill(value);\n },\n\n async selectOptionFirstByLabel(label: string, value: string): Promise<void> {\n await page.getByLabel(label, { exact: true }).first().selectOption(value);\n },\n\n async waitForSelector(selector: string, options?: { timeout?: number }): Promise<void> {\n await page.waitForSelector(selector, { timeout: options?.timeout });\n },\n\n async waitForUrl(predicate: (url: string) => boolean, options?: { timeout?: number }): Promise<void> {\n await page.waitForURL((url) => predicate(url.toString()), { timeout: options?.timeout });\n },\n\n async waitForNetworkIdle(options?: { timeout?: number }): Promise<void> {\n await page.waitForLoadState(\"networkidle\", { timeout: options?.timeout });\n },\n\n evaluate<R = unknown, A = unknown>(\n pageFunction: string | ((arg: A) => R | Promise<R>),\n arg?: A\n ): Promise<R> {\n const raw = page.evaluate as unknown as (fn: unknown, a?: unknown) => Promise<unknown>;\n const result = arg === undefined\n ? raw.call(page, pageFunction)\n : raw.call(page, pageFunction, arg);\n return result as Promise<R>;\n },\n\n async screenshot(options: { path: string; fullPage?: boolean }): Promise<void> {\n await page.screenshot({ path: options.path, fullPage: options.fullPage });\n },\n\n onResponse(handler: (response: DriverResponse) => void): void {\n page.on(\"response\", handler);\n },\n\n async route(url: string, handler: (route: DriverRoute) => void | Promise<void>): Promise<void> {\n await page.route(url, async (pwRoute) => {\n try {\n await handler({\n fulfill: (response) => pwRoute.fulfill(response)\n });\n } catch (error) {\n try {\n await pwRoute.abort(\"failed\");\n } catch (abortError) {\n throw new Error(\n `Route handler failed for ${url}: ${formatError(error)}. Route abort also failed: ${formatError(abortError)}`\n );\n }\n throw new Error(`Route handler failed for ${url}: ${formatError(error)}`);\n }\n });\n },\n\n async unroute(url: string): Promise<void> {\n await page.unroute(url);\n },\n\n onDialog(action: DialogAction): void {\n page.once(\"dialog\", (dialog) => {\n const response = action === \"accept\"\n ? dialog.accept()\n : dialog.dismiss();\n response.catch((error: unknown) => {\n console.warn(`Failed to ${action} dialog: ${formatError(error)}`);\n });\n });\n },\n\n waitForDownloadEvent(options?: { timeout?: number }): Promise<DriverDownload> {\n return page.waitForEvent(\"download\", { timeout: options?.timeout });\n },\n\n parseTextSelector(selector: string): string | null {\n return unwrapTextSelector(selector);\n }\n };\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\n// Type-only: the runner hands executeSteps a live Playwright page as the\n// driver entrypoint. All *runtime* Playwright use lives in the driver\n// (src/browser/playwright-driver.ts); this import is erased at build.\nimport type { Page } from \"playwright\";\nimport { createPlaywrightDriver } from \"../browser/controller.js\";\nimport type { DriverCapability, DriverDownload, SessionDriver } from \"../browser/driver.js\";\nimport type { Step, StepResult, Target } from \"../types/index.js\";\nimport { loadHunt } from \"../config/loader.js\";\nimport { interpolateHunt } from \"../config/interpolate.js\";\nimport { assertHuntAssertionsSupportedByTarget, assertStepsSupportedByTarget } from \"../config/target.js\";\nimport { createRunPolicy, type RunPolicy } from \"./policy.js\";\nimport {\n assertWithAiVision,\n tryResolveAiConfig,\n type AiConfig,\n type AiVisionInput,\n type AiVisionVerdict\n} from \"../generator/ai.js\";\n\nexport type StepCallback = (result: StepResult, step: Step, index: number) => void;\n\nexport type StepExecutionContext = {\n /** Playwright page entrypoint for the web target; omitted for non-web drivers. */\n page?: Page;\n /** Pre-built driver; required when `page` is omitted (e.g. the macOS/Android target). */\n driver?: SessionDriver;\n /**\n * The execution target type, used to gate web-only steps inside sub-hunts.\n * Optional for back-compat: when omitted it is inferred from the driver's\n * capabilities (navigate ⇒ web, otherwise macOS).\n */\n targetType?: Target[\"type\"];\n steps: Step[];\n targetUrl: string;\n runDir: string;\n screenshotsMode: \"on-failure\" | \"all\";\n forbiddenSelectors: string[];\n allowedDomains: string[];\n /** Allow-listed bundle IDs / process names for the macOS target (optional). */\n allowedApps?: string[];\n maxSteps: number;\n selfHealing?: boolean;\n maxTotalTimeMs: number;\n redactedFillSteps: Set<string>;\n configDir: string;\n onStep?: StepCallback;\n huntStack?: string[];\n activeMocks?: Map<string, () => Promise<void>>;\n runtimeVars?: Map<string, string>;\n randomVars?: Record<string, string>;\n pendingDownload?: Promise<DriverDownload>;\n runStartedAtMs?: number;\n stepPathPrefix?: string;\n /**\n * Test seam: resolve the AI config for `assertWithAI` (returns `null` when no\n * provider/key is configured, which makes the step skip with a warning).\n * Defaults to env-based {@link tryResolveAiConfig}.\n */\n resolveAiConfig?: () => AiConfig | null;\n /**\n * Test seam: perform the vision assertion. Defaults to the real fetch-based\n * {@link assertWithAiVision}. Injected in tests so the runner never hits the\n * network.\n */\n assertVision?: (input: AiVisionInput, config: AiConfig) => Promise<AiVisionVerdict>;\n};\n\nexport type StepExecutionResult = {\n results: StepResult[];\n screenshots: string[];\n failed: boolean;\n error?: string;\n};\n\n/** Anything that can take a full-page screenshot (a driver or a Playwright page). */\ntype ScreenshotTaker = {\n screenshot(options: { path: string; fullPage?: boolean }): Promise<unknown>;\n};\n\nfunction getStepType(step: Step): string {\n if (\"navigate\" in step) return \"navigate\";\n if (\"click\" in step) return \"click\";\n if (\"fill\" in step) return \"fill\";\n if (\"type\" in step) return \"type\";\n if (\"selectOption\" in step) return \"selectOption\";\n if (\"select\" in step) return \"select\";\n if (\"onDialog\" in step) return \"onDialog\";\n if (\"setInputFiles\" in step) return \"setInputFiles\";\n if (\"runHunt\" in step) return \"runHunt\";\n if (\"assert\" in step) return \"assert\";\n if (\"press\" in step) return \"press\";\n if (\"wait\" in step) return \"wait\";\n if (\"waitForSelector\" in step) return \"waitForSelector\";\n if (\"waitForUrl\" in step) return \"waitForUrl\";\n if (\"waitForNetworkIdle\" in step) return \"waitForNetworkIdle\";\n if (\"hover\" in step) return \"hover\";\n if (\"scroll\" in step) return \"scroll\";\n if (\"scrollTo\" in step) return \"scrollTo\";\n if (\"screenshot\" in step) return \"screenshot\";\n if (\"if\" in step) return \"if\";\n if (\"repeat\" in step) return \"repeat\";\n if (\"mockRoute\" in step) return \"mockRoute\";\n if (\"unmockRoute\" in step) return \"unmockRoute\";\n if (\"evalScript\" in step) return \"evalScript\";\n if (\"runScript\" in step) return \"runScript\";\n if (\"assertScreenshot\" in step) return \"assertScreenshot\";\n if (\"assertWithAI\" in step) return \"assertWithAI\";\n if (\"copyText\" in step) return \"copyText\";\n if (\"waitForDownload\" in step) return \"waitForDownload\";\n return \"step\";\n}\n\nconst RUNTIME_VAR_PATTERN = /\\{\\{([A-Z0-9_]+)\\}\\}/g;\n\nfunction substituteRuntimeVars(input: string, vars: Map<string, string>): string {\n return input.replace(RUNTIME_VAR_PATTERN, (match, name: string) => {\n const value = vars.get(name);\n return value !== undefined ? value : match;\n });\n}\n\nfunction applyRuntimeVars(step: Step, vars: Map<string, string>): Step {\n const sub = (s: string) => substituteRuntimeVars(s, vars);\n\n if (\"navigate\" in step) return { navigate: sub(step.navigate) };\n if (\"click\" in step) {\n if (typeof step.click === \"string\") return { click: sub(step.click) };\n return { click: { selector: sub(step.click.selector) } };\n }\n if (\"fill\" in step) {\n if (\"selector\" in step.fill && \"value\" in step.fill) {\n const f = step.fill as { selector: string; value: string };\n return { fill: { selector: sub(f.selector), value: sub(f.value) } };\n }\n const [key, value] = Object.entries(step.fill)[0];\n return { fill: { [sub(key)]: sub(value) } };\n }\n if (\"type\" in step) return { type: sub(step.type) };\n if (\"assert\" in step) {\n const a = step.assert;\n if (a.visible !== undefined) return { assert: { visible: sub(a.visible) } };\n if (a.notVisible !== undefined) return { assert: { notVisible: sub(a.notVisible) } };\n if (a.urlIncludes !== undefined) return { assert: { urlIncludes: sub(a.urlIncludes) } };\n if (a.urlEquals !== undefined) return { assert: { urlEquals: sub(a.urlEquals) } };\n return step;\n }\n if (\"wait\" in step) {\n if (typeof step.wait === \"string\") return { wait: sub(step.wait) };\n return { wait: { for: sub(step.wait.for), timeout: step.wait.timeout } };\n }\n if (\"waitForSelector\" in step) {\n return { waitForSelector: { selector: sub(step.waitForSelector.selector), timeout: step.waitForSelector.timeout } };\n }\n if (\"evalScript\" in step) {\n if (typeof step.evalScript === \"string\") return { evalScript: sub(step.evalScript) };\n return {\n evalScript: {\n expression: sub(step.evalScript.expression),\n ...(step.evalScript.as !== undefined ? { as: step.evalScript.as } : {})\n }\n };\n }\n if (\"assertScreenshot\" in step) {\n return {\n assertScreenshot: {\n name: sub(step.assertScreenshot.name),\n ...(step.assertScreenshot.threshold !== undefined ? { threshold: step.assertScreenshot.threshold } : {})\n }\n };\n }\n if (\"assertWithAI\" in step) {\n return { assertWithAI: sub(step.assertWithAI) };\n }\n if (\"copyText\" in step) {\n return { copyText: { selector: sub(step.copyText.selector), as: step.copyText.as } };\n }\n if (\"waitForDownload\" in step) {\n if (step.waitForDownload === null) return step;\n return {\n waitForDownload: {\n ...(step.waitForDownload.filename !== undefined ? { filename: sub(step.waitForDownload.filename) } : {}),\n ...(step.waitForDownload.timeout !== undefined ? { timeout: step.waitForDownload.timeout } : {})\n }\n };\n }\n return step;\n}\n\nfunction isExplicitFillStep(\n value: { selector: string; value: string } | Record<string, string>\n): value is { selector: string; value: string } {\n return (\n typeof (value as { selector?: unknown }).selector === \"string\" &&\n typeof (value as { value?: unknown }).value === \"string\"\n );\n}\n\nfunction resolveNavigationTarget(targetUrl: string, value: string): string {\n try {\n return new URL(value, targetUrl).toString();\n } catch {\n return value;\n }\n}\n\nfunction escapeForText(value: string): string {\n return value.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"');\n}\n\nfunction escapeForAttribute(value: string): string {\n return value.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"');\n}\n\nfunction exactTextSelector(text: string): string {\n return `text=\"${escapeForText(text)}\"`;\n}\n\nfunction textContainsSelector(text: string): string {\n return `text=${escapeForText(text)}`;\n}\n\nfunction getSinglePair(value: Record<string, string>, stepType: string): [string, string] {\n const entries = Object.entries(value);\n if (entries.length !== 1) {\n throw new Error(`${stepType} shorthand expects exactly one key-value pair`);\n }\n return entries[0];\n}\n\nasync function clickByTextWithFallback(\n driver: SessionDriver,\n policy: RunPolicy,\n text: string\n): Promise<string> {\n const roleSelector = `role=button[name=\"${escapeForAttribute(text)}\"]`;\n policy.assertAllowedSelector(roleSelector);\n if (await driver.countByRole(\"button\", text)) {\n await driver.clickFirstByRole(\"button\", text);\n return roleSelector;\n }\n\n const selector = exactTextSelector(text);\n policy.assertAllowedSelector(selector);\n await driver.clickFirst(selector);\n return selector;\n}\n\nasync function fillByLabelOrPlaceholder(\n driver: SessionDriver,\n policy: RunPolicy,\n label: string,\n value: string\n): Promise<string> {\n const labelSelector = `label=\"${escapeForAttribute(label)}\"`;\n policy.assertAllowedSelector(labelSelector);\n if (await driver.countByLabel(label)) {\n await driver.fillFirstByLabel(label, value);\n return labelSelector;\n }\n\n const placeholder = `input[placeholder=\"${escapeForAttribute(label)}\"], textarea[placeholder=\"${escapeForAttribute(label)}\"]`;\n policy.assertAllowedSelector(placeholder);\n if (await driver.count(placeholder)) {\n await driver.fillFirst(placeholder, value);\n return placeholder;\n }\n\n throw new Error(`Could not resolve fill shorthand for \"${label}\"`);\n}\n\nasync function selectByLabelOrFallback(\n driver: SessionDriver,\n policy: RunPolicy,\n label: string,\n value: string\n): Promise<string> {\n const labelSelector = `label=\"${escapeForAttribute(label)}\"`;\n policy.assertAllowedSelector(labelSelector);\n if (await driver.countByLabel(label)) {\n await driver.selectOptionFirstByLabel(label, value);\n return labelSelector;\n }\n\n const ariaSelector = `select[aria-label=\"${escapeForAttribute(label)}\"]`;\n policy.assertAllowedSelector(ariaSelector);\n if (await driver.count(ariaSelector)) {\n await driver.selectOptionFirst(ariaSelector, value);\n return ariaSelector;\n }\n\n const placeholderSelector = `select[placeholder=\"${escapeForAttribute(label)}\"]`;\n policy.assertAllowedSelector(placeholderSelector);\n if (await driver.count(placeholderSelector)) {\n await driver.selectOptionFirst(placeholderSelector, value);\n return placeholderSelector;\n }\n\n throw new Error(`Could not resolve select shorthand for \"${label}\"`);\n}\n\n// Playwright engine prefixes (e.g. `css=`, `xpath=…`, `text=\"…\"`) that mark a\n// value as an explicit selector rather than text to match.\nconst SELECTOR_ENGINE_PREFIX = /^(?:css|xpath|text|id|role|data-testid)=/i;\nconst HTML_TYPE_SELECTORS = new Set([\n \"a\",\n \"article\",\n \"aside\",\n \"body\",\n \"button\",\n \"canvas\",\n \"dialog\",\n \"div\",\n \"fieldset\",\n \"footer\",\n \"form\",\n \"h1\",\n \"h2\",\n \"h3\",\n \"h4\",\n \"h5\",\n \"h6\",\n \"header\",\n \"html\",\n \"iframe\",\n \"img\",\n \"input\",\n \"label\",\n \"li\",\n \"main\",\n \"nav\",\n \"ol\",\n \"option\",\n \"p\",\n \"section\",\n \"select\",\n \"span\",\n \"table\",\n \"tbody\",\n \"td\",\n \"textarea\",\n \"th\",\n \"thead\",\n \"tr\",\n \"ul\"\n]);\n\nfunction isKnownCssTypeSelector(value: string): boolean {\n return value === \"*\" || value.includes(\"-\") || HTML_TYPE_SELECTORS.has(value.toLowerCase());\n}\n\nfunction readCssTypeSelector(value: string, start: number): { end: number; isKnown: boolean } | null {\n const match = /^(?:[A-Za-z][\\w-]*|\\*)/.exec(value.slice(start));\n if (!match) return null;\n return { end: start + match[0].length, isKnown: isKnownCssTypeSelector(match[0]) };\n}\n\nfunction readCssStructuralSelectorPart(value: string, start: number): number | null {\n const rest = value.slice(start);\n const classOrId = /^[.#][A-Za-z_][\\w-]*/.exec(rest);\n if (classOrId) return start + classOrId[0].length;\n\n const attribute = /^\\[[A-Za-z_][\\w:-]*(?:\\s*(?:[~|^$*]?=)\\s*(?:\"[^\"]*\"|'[^']*'|[^\\]\\s]+))?\\]/.exec(rest);\n if (attribute) return start + attribute[0].length;\n\n return null;\n}\n\nfunction readCssCompoundSelector(value: string, start: number): { end: number; hasStructuralPart: boolean } | null {\n let cursor = start;\n const type = readCssTypeSelector(value, cursor);\n if (type) {\n cursor = type.end;\n }\n\n let hasStructuralPart = false;\n for (;;) {\n const next = readCssStructuralSelectorPart(value, cursor);\n if (next === null) break;\n hasStructuralPart = true;\n cursor = next;\n }\n\n if (cursor === start) return null;\n if (type && !type.isKnown) return null;\n return { end: cursor, hasStructuralPart };\n}\n\nfunction readCssSelectorSeparator(value: string, start: number): number | null {\n let cursor = start;\n let sawWhitespace = false;\n while (/\\s/.test(value[cursor] ?? \"\")) {\n sawWhitespace = true;\n cursor += 1;\n }\n\n if (/[>+~]/.test(value[cursor] ?? \"\")) {\n cursor += 1;\n while (/\\s/.test(value[cursor] ?? \"\")) {\n cursor += 1;\n }\n return cursor;\n }\n\n return sawWhitespace ? cursor : null;\n}\n\nfunction isCssSelectorSequence(value: string): boolean {\n const first = readCssCompoundSelector(value, 0);\n if (!first) return false;\n\n let cursor = first.end;\n let sawSeparator = false;\n let hasStructuralPart = first.hasStructuralPart;\n\n while (cursor < value.length) {\n const afterSeparator = readCssSelectorSeparator(value, cursor);\n if (afterSeparator === null) return false;\n\n const next = readCssCompoundSelector(value, afterSeparator);\n if (!next) return false;\n\n sawSeparator = true;\n hasStructuralPart = hasStructuralPart || next.hasStructuralPart;\n cursor = next.end;\n }\n\n return sawSeparator && hasStructuralPart;\n}\n\n// A visibility value is treated as a selector only when it has a clear\n// structural signature: a leading class/id/attribute token, a supported CSS\n// compound/sequence, or explicit Playwright engine prefix (incl. `//` xpath).\n// Everything else — including prose that merely contains punctuation such as\n// \"name:\" or a sentence ending in \".\" — is matched as text, so assertions read\n// the way they are written. For exotic selectors (pseudo-classes), use an\n// explicit engine prefix like `css=input:checked`.\nexport function looksLikeSelector(value: string): boolean {\n const trimmed = value.trim();\n if (trimmed.length === 0) return false;\n if (SELECTOR_ENGINE_PREFIX.test(trimmed) || trimmed.startsWith(\"//\")) return true;\n if (/^[.#]/.test(trimmed)) return true; // leading class or id selector\n const compound = readCssCompoundSelector(trimmed, 0);\n if (compound?.end === trimmed.length && compound.hasStructuralPart) return true;\n if (isCssSelectorSequence(trimmed)) return true;\n return false;\n}\n\nexport function toVisibilitySelector(value: string): string {\n if (looksLikeSelector(value)) return value;\n return textContainsSelector(value);\n}\n\nfunction countVisible(driver: SessionDriver, selector: string): Promise<number> {\n return driver.visibleCount?.(selector) ?? driver.count(selector);\n}\n\nasync function runInlineAssert(\n driver: SessionDriver,\n policy: RunPolicy,\n assertion: {\n visible?: string;\n notVisible?: string;\n urlIncludes?: string;\n urlEquals?: string;\n }\n): Promise<string> {\n if (assertion.visible !== undefined) {\n const selector = toVisibilitySelector(assertion.visible);\n policy.assertAllowedSelector(selector);\n const count = await countVisible(driver, selector);\n if (count === 0) {\n throw new Error(`Expected visible: ${assertion.visible}`);\n }\n return `visible:${assertion.visible}`;\n }\n\n if (assertion.notVisible !== undefined) {\n const selector = toVisibilitySelector(assertion.notVisible);\n policy.assertAllowedSelector(selector);\n const count = await countVisible(driver, selector);\n if (count > 0) {\n throw new Error(`Expected not visible: ${assertion.notVisible}`);\n }\n return `notVisible:${assertion.notVisible}`;\n }\n\n if (assertion.urlIncludes !== undefined) {\n const current = driver.currentUrl();\n if (!current.includes(assertion.urlIncludes)) {\n throw new Error(`URL did not include ${assertion.urlIncludes}`);\n }\n return `urlIncludes:${assertion.urlIncludes}`;\n }\n\n if (assertion.urlEquals !== undefined) {\n const current = driver.currentUrl();\n if (current !== assertion.urlEquals) {\n throw new Error(`URL did not equal ${assertion.urlEquals}`);\n }\n return `urlEquals:${assertion.urlEquals}`;\n }\n\n throw new Error(\"assert step is missing an assertion type\");\n}\n\nfunction screenshotPath(screenshotsDir: string, fileName: string): string {\n return path.join(screenshotsDir, fileName);\n}\n\nfunction stepPath(prefix: string | undefined, index: number): string {\n return prefix ? `${prefix}.${index}` : `${index}`;\n}\n\nfunction isWaitForDownloadStep(step: Step | undefined): step is Extract<Step, { waitForDownload: unknown }> {\n return step !== undefined && \"waitForDownload\" in step;\n}\n\nfunction armDownloadListener(driver: SessionDriver, timeout: number): Promise<DriverDownload> {\n const downloadPromise = driver.waitForDownloadEvent({ timeout });\n void downloadPromise.catch(() => undefined);\n return downloadPromise;\n}\n\nfunction validateDownloadFilename(suggestedFilename: string): string {\n const safeFilename = suggestedFilename.trim();\n const allowedFilenamePattern = /^[^<>:\"/\\\\|?*]+$/;\n const hasControlCharacter = Array.from(safeFilename).some((char) => char.charCodeAt(0) < 32);\n\n if (\n safeFilename.length === 0\n || safeFilename !== suggestedFilename\n || safeFilename !== path.basename(safeFilename)\n || safeFilename.includes(\"..\")\n || /[/\\\\]/.test(safeFilename)\n || hasControlCharacter\n || !allowedFilenamePattern.test(safeFilename)\n ) {\n throw new Error(`Invalid download filename: \"${suggestedFilename}\"`);\n }\n\n return safeFilename;\n}\n\nasync function captureScreenshot(taker: ScreenshotTaker, filePath: string): Promise<void> {\n try {\n await taker.screenshot({ path: filePath, fullPage: true });\n } catch (error) {\n const message = error instanceof Error ? error.message : \"Screenshot failed\";\n throw new Error(`Failed to capture screenshot at ${filePath}: ${message}`);\n }\n}\n\nasync function executeNestedSteps(\n context: StepExecutionContext,\n overrides: Partial<StepExecutionContext> & Pick<StepExecutionContext, \"steps\">\n): Promise<StepExecutionResult> {\n const nestedContext: StepExecutionContext = {\n ...context,\n ...overrides,\n pendingDownload: context.pendingDownload\n };\n const result = await executeSteps(nestedContext);\n context.pendingDownload = nestedContext.pendingDownload;\n if (nestedContext.randomVars !== undefined) {\n context.randomVars = nestedContext.randomVars;\n }\n return result;\n}\n\n/**\n * A step handler's outcome. `result` is a single completed step (which goes\n * through the common tail — \"all\"-mode screenshot + onStep callback). `abort`\n * signals that a nested execution failed and the whole run must stop; the\n * handler has already pushed its nested results/screenshots.\n */\ntype HandlerOutcome =\n | { kind: \"result\"; result: StepResult }\n | { kind: \"abort\"; error?: string };\n\n/** Everything a step handler needs from the executing loop. */\ntype StepHandlerContext = {\n driver: SessionDriver;\n policy: RunPolicy;\n context: StepExecutionContext;\n step: Step;\n index: number;\n stepPath: string;\n stepStart: number;\n runtimeVars: Map<string, string>;\n results: StepResult[];\n screenshots: string[];\n addScreenshot: (fileName: string) => Promise<string>;\n executeNested: (\n overrides: Partial<StepExecutionContext> & Pick<StepExecutionContext, \"steps\">\n ) => Promise<StepExecutionResult>;\n};\n\ntype StepHandler = {\n /** Driver capabilities this handler requires; checked before dispatch. */\n capabilities: DriverCapability[];\n run: (h: StepHandlerContext) => Promise<HandlerOutcome>;\n};\n\nfunction unknownStep(): never {\n throw new Error(\"Unknown step type\");\n}\n\nconst STEP_HANDLERS: Record<string, StepHandler> = {\n navigate: {\n capabilities: [\"navigate\"],\n run: async (h) => {\n if (!(\"navigate\" in h.step)) unknownStep();\n const destination = resolveNavigationTarget(h.context.targetUrl, h.step.navigate);\n h.policy.ensureUrlAllowed(destination);\n await h.driver.goto(destination);\n h.policy.ensureLocationAllowed(h.driver);\n return { kind: \"result\", result: { type: \"navigate\", status: \"pass\", durationMs: Date.now() - h.stepStart } };\n }\n },\n\n click: {\n capabilities: [\"interact\", \"query\"],\n run: async (h) => {\n if (!(\"click\" in h.step)) unknownStep();\n let selector: string;\n let healedFrom: string | undefined;\n if (typeof h.step.click === \"string\") {\n selector = await clickByTextWithFallback(h.driver, h.policy, h.step.click);\n } else {\n const resolved = await h.policy.resolveActionSelector(h.step.click.selector);\n await h.driver.click(resolved.selector);\n selector = resolved.selector;\n healedFrom = resolved.healedFrom;\n }\n h.policy.ensureLocationAllowed(h.driver);\n return {\n kind: \"result\",\n result: {\n type: \"click\",\n status: \"pass\",\n durationMs: Date.now() - h.stepStart,\n selector,\n ...(healedFrom ? { healedFrom } : {})\n }\n };\n }\n },\n\n fill: {\n capabilities: [\"interact\", \"query\"],\n run: async (h) => {\n if (!(\"fill\" in h.step)) unknownStep();\n let selector: string;\n let value: string;\n let healedFrom: string | undefined;\n if (isExplicitFillStep(h.step.fill)) {\n const resolved = await h.policy.resolveActionSelector(h.step.fill.selector);\n await h.driver.fill(resolved.selector, h.step.fill.value);\n selector = resolved.selector;\n healedFrom = resolved.healedFrom;\n value = h.step.fill.value;\n } else {\n const [label, shorthandValue] = getSinglePair(h.step.fill, \"fill\");\n selector = await fillByLabelOrPlaceholder(h.driver, h.policy, label, shorthandValue);\n value = shorthandValue;\n }\n h.policy.ensureLocationAllowed(h.driver);\n return {\n kind: \"result\",\n result: {\n type: \"fill\",\n status: \"pass\",\n durationMs: Date.now() - h.stepStart,\n selector,\n value: h.context.redactedFillSteps.has(h.stepPath) ? \"[REDACTED]\" : value,\n ...(healedFrom ? { healedFrom } : {})\n }\n };\n }\n },\n\n type: {\n capabilities: [\"interact\"],\n run: async (h) => {\n if (!(\"type\" in h.step)) unknownStep();\n h.policy.assertAllowedSelector(\":focus\");\n await h.driver.fill(\":focus\", h.step.type);\n h.policy.ensureLocationAllowed(h.driver);\n return {\n kind: \"result\",\n result: {\n type: \"type\",\n status: \"pass\",\n durationMs: Date.now() - h.stepStart,\n selector: \":focus\",\n value: h.context.redactedFillSteps.has(h.stepPath) ? \"[REDACTED]\" : h.step.type\n }\n };\n }\n },\n\n selectOption: {\n capabilities: [\"navigate\", \"interact\", \"query\"],\n run: async (h) => {\n if (!(\"selectOption\" in h.step)) unknownStep();\n const resolved = await h.policy.resolveActionSelector(h.step.selectOption.selector);\n await h.driver.selectOption(resolved.selector, h.step.selectOption.value);\n h.policy.ensureLocationAllowed(h.driver);\n return {\n kind: \"result\",\n result: {\n type: \"selectOption\",\n status: \"pass\",\n durationMs: Date.now() - h.stepStart,\n selector: resolved.selector,\n value: h.step.selectOption.value,\n ...(resolved.healedFrom ? { healedFrom: resolved.healedFrom } : {})\n }\n };\n }\n },\n\n select: {\n capabilities: [\"navigate\", \"interact\", \"query\"],\n run: async (h) => {\n if (!(\"select\" in h.step)) unknownStep();\n const [label, value] = getSinglePair(h.step.select, \"select\");\n const selector = await selectByLabelOrFallback(h.driver, h.policy, label, value);\n h.policy.ensureLocationAllowed(h.driver);\n return {\n kind: \"result\",\n result: {\n type: \"select\",\n status: \"pass\",\n durationMs: Date.now() - h.stepStart,\n selector,\n value\n }\n };\n }\n },\n\n onDialog: {\n capabilities: [\"dialog\"],\n run: async (h) => {\n if (!(\"onDialog\" in h.step)) unknownStep();\n h.driver.onDialog(h.step.onDialog.action);\n return {\n kind: \"result\",\n result: {\n type: \"onDialog\",\n status: \"pass\",\n durationMs: Date.now() - h.stepStart,\n value: h.step.onDialog.action\n }\n };\n }\n },\n\n setInputFiles: {\n capabilities: [\"navigate\", \"interact\", \"query\", \"files\"],\n run: async (h) => {\n if (!(\"setInputFiles\" in h.step)) unknownStep();\n const resolvedInput = await h.policy.resolveActionSelector(h.step.setInputFiles.selector);\n const rawFiles = h.step.setInputFiles.files;\n const resolveFile = (f: string) => (path.isAbsolute(f) ? f : path.join(h.context.configDir, f));\n const resolvedFiles = Array.isArray(rawFiles) ? rawFiles.map(resolveFile) : resolveFile(rawFiles);\n await h.driver.setInputFiles(resolvedInput.selector, resolvedFiles);\n h.policy.ensureLocationAllowed(h.driver);\n const filesLabel = Array.isArray(rawFiles) ? rawFiles.join(\", \") : rawFiles;\n return {\n kind: \"result\",\n result: {\n type: \"setInputFiles\",\n status: \"pass\",\n durationMs: Date.now() - h.stepStart,\n selector: resolvedInput.selector,\n ...(resolvedInput.healedFrom ? { healedFrom: resolvedInput.healedFrom } : {}),\n value: filesLabel\n }\n };\n }\n },\n\n runHunt: {\n capabilities: [],\n run: async (h) => {\n if (!(\"runHunt\" in h.step)) unknownStep();\n const huntName = typeof h.step.runHunt === \"string\" ? h.step.runHunt : h.step.runHunt.name;\n const overrideVars = typeof h.step.runHunt === \"string\" ? undefined : h.step.runHunt.vars;\n const stack = h.context.huntStack ?? [];\n if (stack.includes(huntName)) {\n throw new Error(`Circular hunt dependency: ${[...stack, huntName].join(\" → \")}`);\n }\n const subHunt = loadHunt(huntName, h.context.configDir);\n if (overrideVars) {\n subHunt.vars = { ...subHunt.vars, ...overrideVars };\n }\n const {\n hunt: interpolatedSubHunt,\n redactedFillSteps: subRedacted,\n randomVars\n } = interpolateHunt(subHunt, process.env, h.context.randomVars);\n // Sub-hunts bypass the top-level target check, so re-run it here against\n // the sub-hunt's own steps. The target type comes from the run context\n // (set by the native run paths); when absent it is derived from the\n // driver: a driver without the `navigate` capability is a native target,\n // where web-only steps — including `assert: urlIncludes`/`urlEquals`\n // against a native `currentUrl()` — must be rejected, not silently run.\n const subTargetType =\n h.context.targetType ?? (h.driver.capabilities.has(\"navigate\") ? \"web\" : \"macos\");\n assertStepsSupportedByTarget(interpolatedSubHunt.steps, subTargetType);\n assertHuntAssertionsSupportedByTarget(interpolatedSubHunt.assertions, subTargetType);\n h.policy.assertWithinMaxSteps(interpolatedSubHunt.steps.length, huntName);\n const subResult = await h.executeNested({\n steps: interpolatedSubHunt.steps,\n redactedFillSteps: subRedacted,\n randomVars,\n stepPathPrefix: undefined,\n huntStack: [...stack, huntName],\n onStep: h.context.onStep\n });\n for (const sr of subResult.results) {\n h.results.push({ ...sr, type: `${huntName} > ${sr.type}` });\n }\n h.screenshots.push(...subResult.screenshots);\n if (subResult.failed) {\n return { kind: \"abort\", error: `Sub-hunt \"${huntName}\" failed: ${subResult.error}` };\n }\n return {\n kind: \"result\",\n result: { type: \"runHunt\", status: \"pass\", durationMs: Date.now() - h.stepStart, value: huntName }\n };\n }\n },\n\n press: {\n capabilities: [\"interact\", \"query\"],\n run: async (h) => {\n if (!(\"press\" in h.step)) unknownStep();\n const resolved = await h.policy.resolveActionSelector(h.step.press.selector);\n await h.driver.press(resolved.selector, h.step.press.key);\n h.policy.ensureLocationAllowed(h.driver);\n return {\n kind: \"result\",\n result: {\n type: \"press\",\n status: \"pass\",\n durationMs: Date.now() - h.stepStart,\n selector: resolved.selector,\n ...(resolved.healedFrom ? { healedFrom: resolved.healedFrom } : {})\n }\n };\n }\n },\n\n assert: {\n capabilities: [\"query\"],\n run: async (h) => {\n if (!(\"assert\" in h.step)) unknownStep();\n const value = await runInlineAssert(h.driver, h.policy, h.step.assert);\n return {\n kind: \"result\",\n result: { type: \"assert\", status: \"pass\", durationMs: Date.now() - h.stepStart, value }\n };\n }\n },\n\n wait: {\n capabilities: [\"wait\"],\n run: async (h) => {\n if (!(\"wait\" in h.step)) unknownStep();\n const text = typeof h.step.wait === \"string\" ? h.step.wait : h.step.wait.for;\n const timeout = typeof h.step.wait === \"string\" ? undefined : h.step.wait.timeout;\n const selector = `text=${escapeForText(text)}`;\n h.policy.assertAllowedSelector(selector);\n await h.driver.waitForSelector(selector, { timeout });\n return {\n kind: \"result\",\n result: { type: \"wait\", status: \"pass\", durationMs: Date.now() - h.stepStart, selector }\n };\n }\n },\n\n waitForSelector: {\n capabilities: [\"wait\"],\n run: async (h) => {\n if (!(\"waitForSelector\" in h.step)) unknownStep();\n h.policy.assertAllowedSelector(h.step.waitForSelector.selector);\n await h.driver.waitForSelector(h.step.waitForSelector.selector, {\n timeout: h.step.waitForSelector.timeout\n });\n return {\n kind: \"result\",\n result: {\n type: \"waitForSelector\",\n status: \"pass\",\n durationMs: Date.now() - h.stepStart,\n selector: h.step.waitForSelector.selector\n }\n };\n }\n },\n\n waitForUrl: {\n capabilities: [\"navigate\", \"wait\"],\n run: async (h) => {\n if (!(\"waitForUrl\" in h.step)) unknownStep();\n const value = h.step.waitForUrl.value;\n await h.driver.waitForUrl((url) => url.includes(value), { timeout: h.step.waitForUrl.timeout });\n h.policy.ensureLocationAllowed(h.driver);\n return {\n kind: \"result\",\n result: { type: \"waitForUrl\", status: \"pass\", durationMs: Date.now() - h.stepStart, value }\n };\n }\n },\n\n waitForNetworkIdle: {\n capabilities: [\"wait\"],\n run: async (h) => {\n if (!(\"waitForNetworkIdle\" in h.step)) unknownStep();\n await h.driver.waitForNetworkIdle({ timeout: h.step.waitForNetworkIdle.timeout });\n return {\n kind: \"result\",\n result: { type: \"waitForNetworkIdle\", status: \"pass\", durationMs: Date.now() - h.stepStart }\n };\n }\n },\n\n hover: {\n capabilities: [\"interact\", \"query\"],\n run: async (h) => {\n if (!(\"hover\" in h.step)) unknownStep();\n const resolved = await h.policy.resolveActionSelector(h.step.hover.selector);\n await h.driver.hover(resolved.selector);\n h.policy.ensureLocationAllowed(h.driver);\n return {\n kind: \"result\",\n result: {\n type: \"hover\",\n status: \"pass\",\n durationMs: Date.now() - h.stepStart,\n selector: resolved.selector,\n ...(resolved.healedFrom ? { healedFrom: resolved.healedFrom } : {})\n }\n };\n }\n },\n\n scroll: {\n // Dispatched through the driver's `scroll` verb (interact), so native mobile\n // targets synthesize a touch swipe while web keeps its `window.scrollBy`\n // behavior. The per-target step gate rejects `scroll` on macOS before here.\n capabilities: [\"interact\"],\n run: async (h) => {\n if (!(\"scroll\" in h.step)) unknownStep();\n const { direction, amount } = h.step.scroll;\n await h.driver.scroll(direction, amount);\n return {\n kind: \"result\",\n result: {\n type: \"scroll\",\n status: \"pass\",\n durationMs: Date.now() - h.stepStart,\n value: amount === undefined ? direction : `${direction} ${amount}px`\n }\n };\n }\n },\n\n scrollTo: {\n capabilities: [\"interact\", \"query\"],\n run: async (h) => {\n if (!(\"scrollTo\" in h.step)) unknownStep();\n const resolved = await h.policy.resolveActionSelector(h.step.scrollTo.selector);\n await h.driver.scrollIntoView(resolved.selector);\n return {\n kind: \"result\",\n result: {\n type: \"scrollTo\",\n status: \"pass\",\n durationMs: Date.now() - h.stepStart,\n selector: resolved.selector,\n ...(resolved.healedFrom ? { healedFrom: resolved.healedFrom } : {})\n }\n };\n }\n },\n\n screenshot: {\n capabilities: [\"screenshot\"],\n run: async (h) => {\n if (!(\"screenshot\" in h.step)) unknownStep();\n const name = h.step.screenshot.name ?? `manual_step_${h.index + 1}.png`;\n if (/[/\\\\]|\\.\\./.test(name)) {\n throw new Error(`Invalid screenshot name: \"${name}\" must not contain path separators or \"..\"`);\n }\n const fileName = name.endsWith(\".png\") ? name : `${name}.png`;\n const relative = await h.addScreenshot(fileName);\n return {\n kind: \"result\",\n result: { type: \"screenshot\", status: \"pass\", durationMs: Date.now() - h.stepStart, screenshot: relative }\n };\n }\n },\n\n if: {\n capabilities: [\"query\"],\n run: async (h) => {\n if (!(\"if\" in h.step)) unknownStep();\n const condition = h.step.if;\n const selector = condition.visible ?? condition.notVisible!;\n h.policy.assertAllowedSelector(selector);\n const count = await countVisible(h.driver, selector);\n const conditionMet = condition.visible !== undefined ? count > 0 : count === 0;\n\n if (conditionMet) {\n const subResult = await h.executeNested({\n steps: condition.then,\n stepPathPrefix: `${h.stepPath}.if.then`\n });\n for (const sr of subResult.results) {\n h.results.push({ ...sr, type: `if > ${sr.type}` });\n }\n h.screenshots.push(...subResult.screenshots);\n if (subResult.failed) {\n return { kind: \"abort\", error: subResult.error };\n }\n return {\n kind: \"result\",\n result: {\n type: \"if\",\n status: \"pass\",\n durationMs: Date.now() - h.stepStart,\n value: `condition met, executed ${condition.then.length} steps`\n }\n };\n }\n\n if (condition.else && condition.else.length > 0) {\n const subResult = await h.executeNested({\n steps: condition.else,\n stepPathPrefix: `${h.stepPath}.if.else`\n });\n for (const sr of subResult.results) {\n h.results.push({ ...sr, type: `if > ${sr.type}` });\n }\n h.screenshots.push(...subResult.screenshots);\n if (subResult.failed) {\n return { kind: \"abort\", error: subResult.error };\n }\n return {\n kind: \"result\",\n result: {\n type: \"if\",\n status: \"pass\",\n durationMs: Date.now() - h.stepStart,\n value: `condition not met, executed ${condition.else.length} else steps`\n }\n };\n }\n\n return {\n kind: \"result\",\n result: {\n type: \"if\",\n status: \"pass\",\n durationMs: Date.now() - h.stepStart,\n value: \"condition not met, skipped\"\n }\n };\n }\n },\n\n repeat: {\n capabilities: [\"query\"],\n run: async (h) => {\n if (!(\"repeat\" in h.step)) unknownStep();\n const repeat = h.step.repeat;\n let totalSubSteps = 0;\n\n if (repeat.times !== undefined) {\n const totalPlanned = repeat.times * repeat.steps.length;\n if (totalPlanned + totalSubSteps > h.context.maxSteps) {\n throw new Error(`Repeat exceeded maxSteps guardrail (${h.context.maxSteps})`);\n }\n for (let i = 0; i < repeat.times; i++) {\n totalSubSteps += repeat.steps.length;\n const subResult = await h.executeNested({\n steps: repeat.steps,\n stepPathPrefix: `${h.stepPath}.repeat.steps`\n });\n for (const sr of subResult.results) {\n h.results.push({ ...sr, type: `repeat[${i}] > ${sr.type}` });\n }\n h.screenshots.push(...subResult.screenshots);\n if (subResult.failed) {\n return { kind: \"abort\", error: subResult.error };\n }\n }\n } else if (repeat.while !== undefined) {\n const maxIter = repeat.maxIterations!;\n const whileSelector = repeat.while.visible ?? repeat.while.notVisible!;\n h.policy.assertAllowedSelector(whileSelector);\n for (let i = 0; i < maxIter; i++) {\n const whileCount = await countVisible(h.driver, whileSelector);\n const shouldContinue = repeat.while.visible !== undefined ? whileCount > 0 : whileCount === 0;\n if (!shouldContinue) break;\n\n totalSubSteps += repeat.steps.length;\n if (totalSubSteps > h.context.maxSteps) {\n throw new Error(`Repeat exceeded maxSteps guardrail (${h.context.maxSteps})`);\n }\n const subResult = await h.executeNested({\n steps: repeat.steps,\n stepPathPrefix: `${h.stepPath}.repeat.steps`\n });\n for (const sr of subResult.results) {\n h.results.push({ ...sr, type: `repeat[${i}] > ${sr.type}` });\n }\n h.screenshots.push(...subResult.screenshots);\n if (subResult.failed) {\n return { kind: \"abort\", error: subResult.error };\n }\n }\n }\n\n return {\n kind: \"result\",\n result: { type: \"repeat\", status: \"pass\", durationMs: Date.now() - h.stepStart }\n };\n }\n },\n\n mockRoute: {\n capabilities: [\"route\"],\n run: async (h) => {\n if (!(\"mockRoute\" in h.step)) unknownStep();\n const mock = h.step.mockRoute;\n const mocks = h.context.activeMocks ?? new Map<string, () => Promise<void>>();\n h.context.activeMocks = mocks;\n\n let responseBody: string;\n if (mock.response.body !== undefined) {\n responseBody = mock.response.body;\n } else {\n const responseFile = mock.response.file;\n if (!responseFile) {\n throw new Error(\"mock.response must include either body or file\");\n }\n const candidateFilePath = path.isAbsolute(responseFile)\n ? responseFile\n : path.join(h.context.configDir, responseFile);\n const resolvedConfigDir = path.resolve(h.context.configDir);\n const resolvedFilePath = path.resolve(candidateFilePath);\n const relativePath = path.relative(resolvedConfigDir, resolvedFilePath);\n const isWithinConfigDir =\n relativePath === \"\"\n || (\n relativePath !== \"..\"\n && !relativePath.startsWith(`..${path.sep}`)\n && !path.isAbsolute(relativePath)\n );\n if (!isWithinConfigDir) {\n throw new Error(\"mock.response.file must resolve within config directory\");\n }\n responseBody = await fs.promises.readFile(resolvedFilePath, \"utf-8\");\n }\n\n const contentType = mock.response.contentType ?? \"application/json\";\n const status = mock.response.status;\n\n await h.driver.route(mock.url, async (route) => {\n await route.fulfill({\n status,\n contentType,\n body: responseBody\n });\n });\n\n mocks.set(mock.url, async () => {\n await h.driver.unroute(mock.url);\n });\n\n return {\n kind: \"result\",\n result: { type: \"mockRoute\", status: \"pass\", durationMs: Date.now() - h.stepStart, value: mock.url }\n };\n }\n },\n\n unmockRoute: {\n capabilities: [\"route\"],\n run: async (h) => {\n if (!(\"unmockRoute\" in h.step)) unknownStep();\n const url = typeof h.step.unmockRoute === \"string\" ? h.step.unmockRoute : h.step.unmockRoute.url;\n const mocks = h.context.activeMocks;\n if (!mocks || !mocks.has(url)) {\n throw new Error(`No active mock for URL: ${url}`);\n }\n const cleanup = mocks.get(url)!;\n await cleanup();\n mocks.delete(url);\n\n return {\n kind: \"result\",\n result: { type: \"unmockRoute\", status: \"pass\", durationMs: Date.now() - h.stepStart, value: url }\n };\n }\n },\n\n evalScript: {\n capabilities: [\"evaluate\"],\n run: async (h) => {\n if (!(\"evalScript\" in h.step)) unknownStep();\n const expression = typeof h.step.evalScript === \"string\" ? h.step.evalScript : h.step.evalScript.expression;\n const result = await h.driver.evaluate(expression);\n const resultStr = String(result);\n if (typeof h.step.evalScript !== \"string\" && h.step.evalScript.as) {\n h.runtimeVars.set(h.step.evalScript.as, resultStr);\n }\n return {\n kind: \"result\",\n result: {\n type: \"evalScript\",\n status: \"pass\",\n durationMs: Date.now() - h.stepStart,\n value: resultStr.length > 200 ? resultStr.slice(0, 200) + \"…\" : resultStr\n }\n };\n }\n },\n\n runScript: {\n capabilities: [\"evaluate\"],\n run: async (h) => {\n if (!(\"runScript\" in h.step)) unknownStep();\n const filePath = path.isAbsolute(h.step.runScript.file)\n ? h.step.runScript.file\n : path.join(h.context.configDir, h.step.runScript.file);\n const fileContents = fs.readFileSync(filePath, \"utf-8\");\n await h.driver.evaluate(fileContents);\n return {\n kind: \"result\",\n result: {\n type: \"runScript\",\n status: \"pass\",\n durationMs: Date.now() - h.stepStart,\n value: h.step.runScript.file\n }\n };\n }\n },\n\n assertScreenshot: {\n capabilities: [\"screenshot\"],\n run: async (h) => {\n if (!(\"assertScreenshot\" in h.step)) unknownStep();\n const { compareScreenshots, ensureBaselineDir } = await import(\"./visual.js\");\n const name = h.step.assertScreenshot.name;\n const threshold = h.step.assertScreenshot.threshold ?? 0.1;\n const baselineDir = ensureBaselineDir(h.context.configDir);\n const baselinePath = path.join(baselineDir, `${name}.png`);\n const currentScreenshotPath = path.join(h.context.runDir, \"screenshots\", `${name}-current.png`);\n fs.mkdirSync(path.dirname(currentScreenshotPath), { recursive: true });\n await h.driver.screenshot({ path: currentScreenshotPath, fullPage: true });\n h.screenshots.push(path.join(\"screenshots\", `${name}-current.png`));\n\n if (!fs.existsSync(baselinePath)) {\n fs.copyFileSync(currentScreenshotPath, baselinePath);\n return {\n kind: \"result\",\n result: { type: \"assertScreenshot\", status: \"pass\", durationMs: Date.now() - h.stepStart, value: \"baseline created\" }\n };\n }\n\n const diffPath = path.join(h.context.runDir, \"screenshots\", `${name}-diff.png`);\n const comparison = await compareScreenshots(baselinePath, currentScreenshotPath, diffPath, threshold);\n if (comparison.match) {\n return {\n kind: \"result\",\n result: {\n type: \"assertScreenshot\",\n status: \"pass\",\n durationMs: Date.now() - h.stepStart,\n value: `diff: ${(comparison.diffPercentage * 100).toFixed(2)}%`\n }\n };\n }\n h.screenshots.push(path.join(\"screenshots\", `${name}-diff.png`));\n throw new Error(\n `Visual regression: ${(comparison.diffPercentage * 100).toFixed(2)}% diff exceeds threshold ${(threshold * 100).toFixed(0)}%`\n );\n }\n },\n\n assertWithAI: {\n capabilities: [\"screenshot\"],\n run: async (h) => {\n if (!(\"assertWithAI\" in h.step)) unknownStep();\n const assertion = h.step.assertWithAI;\n\n // Resolve BYOK config. A future managed-credit path (BIZ-002) can slot in\n // behind this same seam without touching the handler.\n const resolve = h.context.resolveAiConfig ?? tryResolveAiConfig;\n const aiConfig = resolve();\n if (!aiConfig) {\n // Graceful degradation: no AI provider configured. Skip with a warning\n // — never a hard failure, never a silent pass.\n return {\n kind: \"result\",\n result: {\n type: \"assertWithAI\",\n status: \"warn\",\n durationMs: Date.now() - h.stepStart,\n value: `skipped: no AI provider configured (set PROWL_AI_KEY to enable) — \"${assertion}\"`\n }\n };\n }\n\n const fileName = `assertWithAI_step_${h.index + 1}.png`;\n const relative = await h.addScreenshot(fileName);\n const screenshotFullPath = path.join(h.context.runDir, relative);\n\n const imageBase64 = fs.readFileSync(screenshotFullPath).toString(\"base64\");\n const assertVision = h.context.assertVision ?? assertWithAiVision;\n const verdict = await assertVision(\n { imageBase64, mediaType: \"image/png\", assertion },\n aiConfig\n );\n\n if (verdict.pass) {\n return {\n kind: \"result\",\n result: {\n type: \"assertWithAI\",\n status: \"pass\",\n durationMs: Date.now() - h.stepStart,\n value: verdict.reason,\n screenshot: relative\n }\n };\n }\n // Fail with the model's explanation as the developer-facing message.\n throw new Error(`AI assertion failed: ${verdict.reason}`);\n }\n },\n\n copyText: {\n capabilities: [\"query\"],\n run: async (h) => {\n if (!(\"copyText\" in h.step)) unknownStep();\n h.policy.assertAllowedSelector(h.step.copyText.selector);\n const text = await h.driver.textContent(h.step.copyText.selector);\n if (text === null) {\n throw new Error(`No text content found for selector: ${h.step.copyText.selector}`);\n }\n h.runtimeVars.set(h.step.copyText.as, text);\n return {\n kind: \"result\",\n result: {\n type: \"copyText\",\n status: \"pass\",\n durationMs: Date.now() - h.stepStart,\n selector: h.step.copyText.selector,\n value: \"[REDACTED]\"\n }\n };\n }\n },\n\n waitForDownload: {\n capabilities: [\"download\"],\n run: async (h) => {\n if (!(\"waitForDownload\" in h.step)) unknownStep();\n const opts = h.step.waitForDownload;\n const downloadPromise = h.context.pendingDownload ?? armDownloadListener(h.driver, opts?.timeout ?? 30000);\n h.context.pendingDownload = undefined;\n const download = await downloadPromise;\n const suggestedFilename = validateDownloadFilename(download.suggestedFilename());\n if (opts?.filename !== undefined && suggestedFilename !== opts.filename) {\n throw new Error(\n `Download filename mismatch: expected \"${opts.filename}\", got \"${suggestedFilename}\"`\n );\n }\n const savePath = path.join(h.context.runDir, suggestedFilename);\n await download.saveAs(savePath);\n return {\n kind: \"result\",\n result: {\n type: \"waitForDownload\",\n status: \"pass\",\n durationMs: Date.now() - h.stepStart,\n value: suggestedFilename\n }\n };\n }\n }\n};\n\nexport async function executeSteps(context: StepExecutionContext): Promise<StepExecutionResult> {\n let driver = context.driver;\n if (!driver) {\n if (!context.page) {\n throw new Error(\"executeSteps requires a driver or a Playwright page\");\n }\n driver = createPlaywrightDriver(context.page);\n }\n const policy = createRunPolicy(driver, {\n forbiddenSelectors: context.forbiddenSelectors,\n allowedDomains: context.allowedDomains,\n allowedApps: context.allowedApps,\n maxSteps: context.maxSteps,\n selfHealing: context.selfHealing\n });\n\n const screenshotsDir = path.join(context.runDir, \"screenshots\");\n fs.mkdirSync(screenshotsDir, { recursive: true });\n const currentHuntName = context.huntStack?.[context.huntStack.length - 1];\n policy.assertWithinMaxSteps(context.steps.length, currentHuntName);\n\n const results: StepResult[] = [];\n const screenshots: string[] = [];\n const runStartedAtMs = context.runStartedAtMs ?? Date.now();\n context.runStartedAtMs = runStartedAtMs;\n\n const addScreenshot = async (fileName: string): Promise<string> => {\n const fullPath = screenshotPath(screenshotsDir, fileName);\n await captureScreenshot(driver, fullPath);\n const relative = path.join(\"screenshots\", fileName);\n screenshots.push(relative);\n return relative;\n };\n\n const executeNested = (\n overrides: Partial<StepExecutionContext> & Pick<StepExecutionContext, \"steps\">\n ): Promise<StepExecutionResult> => executeNestedSteps(context, { driver, ...overrides });\n\n for (let index = 0; index < context.steps.length; index += 1) {\n const currentStepPath = stepPath(context.stepPathPrefix, index);\n if (Date.now() - runStartedAtMs > context.maxTotalTimeMs) {\n results.push({\n type: \"timeout\",\n status: \"fail\",\n durationMs: 0,\n error: `Max total time exceeded (${context.maxTotalTimeMs}ms)`\n });\n return { results, screenshots, failed: true, error: \"Max total time exceeded\" };\n }\n\n const runtimeVars = context.runtimeVars ?? new Map<string, string>();\n context.runtimeVars = runtimeVars;\n\n let step = context.steps[index];\n if (runtimeVars.size > 0) {\n step = applyRuntimeVars(step, runtimeVars);\n }\n const nextStep = context.steps[index + 1];\n if (\n !isWaitForDownloadStep(step)\n && context.pendingDownload === undefined\n && isWaitForDownloadStep(nextStep)\n ) {\n context.pendingDownload = armDownloadListener(\n driver,\n nextStep.waitForDownload?.timeout ?? 30000\n );\n }\n const stepStart = Date.now();\n const stepType = getStepType(step);\n let stepResult: StepResult | null = null;\n\n try {\n const handler = STEP_HANDLERS[stepType];\n if (!handler) {\n throw new Error(\"Unknown step type\");\n }\n for (const capability of handler.capabilities) {\n if (!driver.capabilities.has(capability)) {\n throw new Error(\n `Driver does not support capability \"${capability}\" required by step \"${stepType}\"`\n );\n }\n }\n\n const outcome = await handler.run({\n driver,\n policy,\n context,\n step,\n index,\n stepPath: currentStepPath,\n stepStart,\n runtimeVars,\n results,\n screenshots,\n addScreenshot,\n executeNested\n });\n\n if (outcome.kind === \"abort\") {\n return { results, screenshots, failed: true, error: outcome.error };\n }\n\n stepResult = outcome.result;\n\n if (context.screenshotsMode === \"all\" && stepResult.type !== \"screenshot\") {\n const fileName = `step_${index + 1}.png`;\n await addScreenshot(fileName);\n }\n\n results.push(stepResult);\n context.onStep?.(stepResult, step, index);\n } catch (error) {\n const message = error instanceof Error ? error.message : \"Step failed\";\n stepResult = {\n type: stepResult?.type ?? stepType,\n status: \"fail\",\n durationMs: Date.now() - stepStart,\n error: message\n };\n\n if (context.screenshotsMode === \"on-failure\") {\n const fileName = `failure_step_${index + 1}.png`;\n await addScreenshot(fileName);\n }\n\n results.push(stepResult);\n context.onStep?.(stepResult, step, index);\n return { results, screenshots, failed: true, error: message };\n }\n }\n\n return { results, screenshots, failed: false };\n}\n\nexport async function captureFinalScreenshot(page: ScreenshotTaker, runDir: string): Promise<string> {\n const screenshotsDir = path.join(runDir, \"screenshots\");\n fs.mkdirSync(screenshotsDir, { recursive: true });\n const fileName = \"final.png\";\n const filePath = screenshotPath(screenshotsDir, fileName);\n await captureScreenshot(page, filePath);\n return path.join(\"screenshots\", fileName);\n}\n","/**\n * PROWL-047 / ARCH-001 — Guardrail policy layer.\n *\n * The guardrails that used to be enforced inline in `steps.ts`\n * (`allowedDomains`, `forbiddenSelectors`, `maxSteps`, and self-healing) now\n * live here, wrapping a {@link SessionDriver}. Handlers call the policy for\n * selector/URL/step-count checks and to resolve (guard + optionally heal) the\n * selector an explicit-selector action should run against.\n *\n * The forbidden-selector matcher uses the driver-supplied selector parser\n * (`driver.parseTextSelector`) so it can interpret text-engine selectors\n * without hard-coding the engine's dialect.\n */\nimport type { SessionDriver } from \"../browser/driver.js\";\nimport { healSelector, type SelectorProbe } from \"./healing.js\";\n\nconst ALWAYS_ALLOWED_PROTOCOLS = [\"about:\", \"data:\"];\n\nexport type RunPolicyOptions = {\n forbiddenSelectors: string[];\n allowedDomains: string[];\n /**\n * Allow-listed bundle IDs / process names for the macOS target. Defaults to\n * `[]`, which denies every app. This is the inverse of\n * `assertTargetAppAllowed`, where an empty list allows the configured target.\n */\n allowedApps?: string[];\n maxSteps: number;\n selfHealing?: boolean;\n};\n\nexport type RunPolicy = {\n /** Throw if a hunt/step list exceeds the configured `maxSteps`. */\n assertWithinMaxSteps(stepCount: number, huntName?: string): void;\n /** Throw if navigating/landing on a URL whose host is not allow-listed. */\n ensureUrlAllowed(urlValue: string): void;\n /** Throw if a bundle id / process name is not allow-listed; an empty list denies all apps. */\n ensureAppAllowed(app: string): void;\n /**\n * Re-assert scope after an action, in a target-aware way: on a URL-capable\n * driver (web) this is the allowed-domain check on the current URL; on a\n * driver without the `navigate` capability (e.g. macOS) there is no URL to\n * check and app scope is enforced at launch, so this is a no-op.\n */\n ensureLocationAllowed(driver: SessionDriver): void;\n /** Throw if a selector matches the forbidden list. */\n assertAllowedSelector(selector: string): void;\n /**\n * Guard a selector, then — when self-healing is on and it matches nothing —\n * attempt to heal to an equivalent selector (re-checked against the forbidden\n * list). Returns the selector to use plus, when healed, the original.\n */\n resolveActionSelector(selector: string): Promise<{ selector: string; healedFrom?: string }>;\n};\n\nexport function createRunPolicy(driver: SessionDriver, options: RunPolicyOptions): RunPolicy {\n const { forbiddenSelectors, allowedDomains, maxSteps, selfHealing } = options;\n const allowedApps = options.allowedApps ?? [];\n\n // Substring match: if both the selector and forbidden pattern are text=\n // selectors, the selector's text is checked for whether it *contains* the\n // forbidden text. For example, forbidden 'text=\"Delete\"' matches selector\n // 'text=\"Delete All\"'. The driver supplies the text-selector parser.\n function matchesForbiddenPattern(selector: string, forbidden: string): boolean {\n const selectorText = driver.parseTextSelector(selector);\n if (selectorText === null) {\n return false;\n }\n const forbiddenText = driver.parseTextSelector(forbidden);\n if (forbiddenText !== null) {\n return selectorText.includes(forbiddenText);\n }\n return selectorText.includes(forbidden);\n }\n\n // A selector is forbidden if it contains any forbidden pattern as a substring\n // (e.g. forbidden \"[data-danger]\" matches \"[data-danger].active\"), or if the\n // text-based pattern match above succeeds.\n function isForbiddenSelector(selector: string): boolean {\n return forbiddenSelectors.some(\n (forbidden) => selector.includes(forbidden) || matchesForbiddenPattern(selector, forbidden)\n );\n }\n\n function assertAllowedSelector(selector: string): void {\n if (isForbiddenSelector(selector)) {\n throw new Error(`Forbidden selector: ${selector}`);\n }\n }\n\n function assertWithinMaxSteps(stepCount: number, huntName?: string): void {\n if (stepCount > maxSteps) {\n if (huntName) {\n throw new Error(`Hunt \"${huntName}\" has ${stepCount} steps. Max allowed is ${maxSteps}.`);\n }\n throw new Error(`Hunt has ${stepCount} steps. Max allowed is ${maxSteps}.`);\n }\n }\n\n function ensureUrlAllowed(urlValue: string): void {\n for (const protocol of ALWAYS_ALLOWED_PROTOCOLS) {\n if (urlValue.startsWith(protocol)) {\n return;\n }\n }\n let url: URL;\n try {\n url = new URL(urlValue);\n } catch {\n throw new Error(`Navigation target is not a valid absolute URL: ${urlValue}`);\n }\n if (!allowedDomains.includes(url.hostname)) {\n throw new Error(`Navigation to disallowed domain: ${url.hostname}`);\n }\n }\n\n function ensureAppAllowed(app: string): void {\n if (!allowedApps.includes(app)) {\n throw new Error(`Interaction with disallowed app: ${app}`);\n }\n }\n\n function ensureLocationAllowed(activeDriver: SessionDriver): void {\n // URL scope only applies to URL-capable (web) drivers. Non-navigating\n // drivers (macOS) have no URL; their app scope is enforced at launch.\n if (activeDriver.capabilities.has(\"navigate\")) {\n ensureUrlAllowed(activeDriver.currentUrl());\n }\n }\n\n // Bridge the driver's `count` verb to the healing probe surface, so healing\n // stays driver-agnostic.\n const healProbe: SelectorProbe = {\n locator: (selector: string) => ({ count: () => driver.count(selector) })\n };\n\n async function resolveActionSelector(\n selector: string\n ): Promise<{ selector: string; healedFrom?: string }> {\n assertAllowedSelector(selector);\n\n if (!selfHealing) {\n return { selector };\n }\n\n let matched = false;\n try {\n matched = (await driver.count(selector)) > 0;\n } catch {\n // Unparseable/odd selector — let the real action surface the error.\n return { selector };\n }\n if (matched) {\n return { selector };\n }\n\n const healed = await healSelector(healProbe, selector, { enabled: true });\n if (!healed) {\n return { selector };\n }\n\n assertAllowedSelector(healed.selector);\n console.warn(\n `Self-healed selector: \"${selector}\" → \"${healed.selector}\" (${healed.strategy}). ` +\n \"Update your hunt to use a stable selector.\"\n );\n return { selector: healed.selector, healedFrom: healed.healedFrom };\n }\n\n return {\n assertWithinMaxSteps,\n ensureUrlAllowed,\n ensureAppAllowed,\n ensureLocationAllowed,\n assertAllowedSelector,\n resolveActionSelector\n };\n}\n","export type AiProvider = \"anthropic\" | \"openai\";\n\nexport type AiConfig = {\n provider: AiProvider;\n model: string;\n apiKey: string;\n /**\n * API root the request is sent to (no trailing slash, no path). When omitted,\n * falls back to the provider's public API. Set via `PROWL_AI_BASE_URL` so a\n * self-hosted gateway or (later) a managed Prowl proxy can slot in without\n * code changes.\n */\n baseUrl?: string;\n};\n\nconst DEFAULT_BASE_URL: Record<AiProvider, string> = {\n anthropic: \"https://api.anthropic.com\",\n openai: \"https://api.openai.com\"\n};\n\nconst AI_REQUEST_TIMEOUT_MS = 30000;\n\ntype ProviderLabel = \"Anthropic\" | \"OpenAI\";\n\ntype AnthropicTextResponse = {\n content?: Array<{ type: string; text?: string }>;\n};\n\ntype OpenAiTextResponse = {\n choices?: Array<{ message?: { content?: string } }>;\n};\n\n/** The API root for a config, defaulting to the provider's public endpoint. */\nfunction apiRoot(config: AiConfig): string {\n return config.baseUrl ?? DEFAULT_BASE_URL[config.provider];\n}\n\nfunction defaultModelFor(provider: AiProvider): string {\n return provider === \"anthropic\" ? \"claude-sonnet-4-5-20250929\" : \"gpt-4o\";\n}\n\n/**\n * Resolve the provider/model/base-url from the environment WITHOUT requiring a\n * key. Returns the partial shape plus the (possibly undefined) key so callers\n * can decide whether a missing key is fatal (generation) or a graceful skip\n * (`assertWithAI`). Throws only on an unsupported provider — a genuine\n * misconfiguration that no caller should silently swallow.\n */\nfunction resolveAiEnv(): { provider: AiProvider; model: string; baseUrl: string; apiKey: string | undefined } {\n const provider = (process.env.PROWL_AI_PROVIDER ?? \"anthropic\") as AiProvider;\n if (provider !== \"anthropic\" && provider !== \"openai\") {\n throw new Error(`Unsupported AI provider: ${provider}. Use \"anthropic\" or \"openai\".`);\n }\n\n const model = process.env.PROWL_AI_MODEL ?? defaultModelFor(provider);\n const baseUrl = normalizeBaseUrl(process.env.PROWL_AI_BASE_URL) ?? DEFAULT_BASE_URL[provider];\n\n return { provider, model, baseUrl, apiKey: process.env.PROWL_AI_KEY };\n}\n\nfunction normalizeBaseUrl(value: string | undefined): string | undefined {\n if (value === undefined) return undefined;\n const trimmed = value.trim().replace(/\\/+$/, \"\");\n return trimmed.length > 0 ? trimmed : undefined;\n}\n\nexport function resolveAiConfig(): AiConfig {\n const env = resolveAiEnv();\n if (!env.apiKey) {\n throw new Error(\n \"PROWL_AI_KEY environment variable is required. Set it to your Anthropic or OpenAI API key.\"\n );\n }\n return { provider: env.provider, model: env.model, apiKey: env.apiKey, baseUrl: env.baseUrl };\n}\n\n/**\n * Like {@link resolveAiConfig} but returns `null` when no API key is configured\n * instead of throwing. Used by BYOK-optional features (e.g. `assertWithAI`) that\n * must degrade gracefully — skip with a warning — rather than fail the run when\n * the operator has not opted into AI. Forward-compatible with a future managed\n * credit path (BIZ-002): a later resolution step can return a config here even\n * when `PROWL_AI_KEY` is unset, and every caller picks it up unchanged.\n */\nexport function tryResolveAiConfig(): AiConfig | null {\n const env = resolveAiEnv();\n if (!env.apiKey) {\n return null;\n }\n return { provider: env.provider, model: env.model, apiKey: env.apiKey, baseUrl: env.baseUrl };\n}\n\nexport async function generateWithAi(prompt: string, config: AiConfig): Promise<string> {\n if (config.provider === \"anthropic\") {\n return generateWithAnthropic(prompt, config);\n }\n return generateWithOpenAi(prompt, config);\n}\n\nasync function generateWithAnthropic(prompt: string, config: AiConfig): Promise<string> {\n const data = await postJson<AnthropicTextResponse>(\n \"Anthropic\",\n `${apiRoot(config)}/v1/messages`,\n anthropicHeaders(config),\n {\n model: config.model,\n max_tokens: 4096,\n messages: [\n { role: \"user\", content: prompt }\n ]\n }\n );\n\n return extractAnthropicText(data);\n}\n\nasync function generateWithOpenAi(prompt: string, config: AiConfig): Promise<string> {\n const data = await postJson<OpenAiTextResponse>(\n \"OpenAI\",\n `${apiRoot(config)}/v1/chat/completions`,\n openAiHeaders(config),\n {\n model: config.model,\n messages: [\n { role: \"user\", content: prompt }\n ],\n max_tokens: 4096\n }\n );\n\n return extractOpenAiText(data);\n}\n\n// --- Vision assertions (assertWithAI) -------------------------------------\n\n/** A screenshot plus the natural-language claim to check against it. */\nexport type AiVisionInput = {\n /** Base64-encoded image bytes (no data: URI prefix). */\n imageBase64: string;\n /** MIME type of the image, e.g. \"image/png\". */\n mediaType: string;\n /** The natural-language assertion the model must judge. */\n assertion: string;\n};\n\n/** The model's verdict on a vision assertion. */\nexport type AiVisionVerdict = {\n pass: boolean;\n reason: string;\n};\n\n/**\n * Build the vision prompt. The model is instructed to return a strict,\n * machine-parseable JSON verdict so the runner never has to interpret prose.\n */\nexport function buildVisionPrompt(assertion: string): string {\n return [\n \"You are a meticulous QA reviewer. You are given a screenshot of an application\",\n \"and a single assertion describing what should be true about it.\",\n \"\",\n \"Assertion:\",\n assertion,\n \"\",\n \"Decide whether the assertion holds for the screenshot. Judge ONLY what is\",\n \"visible; do not assume behavior you cannot see. Be strict: if the assertion\",\n \"is not clearly satisfied, it fails.\",\n \"\",\n 'Reply with ONLY a single JSON object on one line, no markdown, no code fences:',\n '{\"pass\": <true|false>, \"reason\": \"<one concise sentence explaining the verdict>\"}'\n ].join(\"\\n\");\n}\n\n/**\n * Parse the model's raw text into a verdict. Robust to the model wrapping the\n * JSON in prose or ```json fences, but treats genuinely unparseable output as an\n * ERROR — never a silent pass. That keeps a confused/misbehaving model from\n * quietly greenlighting a broken screen.\n */\nexport function parseVisionVerdict(raw: string): AiVisionVerdict {\n const cleaned = stripCodeFences(raw).trim();\n\n // Prefer a full-string parse; fall back to the widest {...} span so leading or\n // trailing prose (\"Here is my verdict: {...}\") still parses.\n const candidate = extractJsonObject(cleaned);\n if (candidate === null) {\n throw new Error(\n `Could not parse a JSON verdict from the AI response: ${truncateForError(raw)}`\n );\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(candidate);\n } catch {\n throw new Error(\n `AI verdict was not valid JSON: ${truncateForError(raw)}`\n );\n }\n\n if (typeof parsed !== \"object\" || parsed === null) {\n throw new Error(`AI verdict was not a JSON object: ${truncateForError(raw)}`);\n }\n\n const record = parsed as Record<string, unknown>;\n if (typeof record.pass !== \"boolean\") {\n throw new Error(\n `AI verdict is missing a boolean \"pass\" field: ${truncateForError(raw)}`\n );\n }\n const reason = typeof record.reason === \"string\" && record.reason.trim().length > 0\n ? record.reason.trim()\n : (record.pass ? \"Assertion satisfied.\" : \"Assertion not satisfied.\");\n\n return { pass: record.pass, reason };\n}\n\nfunction stripCodeFences(text: string): string {\n const fence = text.match(/```(?:json)?\\s*([\\s\\S]*?)```/i);\n return fence ? fence[1] : text;\n}\n\nfunction extractJsonObject(text: string): string | null {\n const trimmed = text.trim();\n if (trimmed.startsWith(\"{\") && trimmed.endsWith(\"}\")) {\n return trimmed;\n }\n const first = trimmed.indexOf(\"{\");\n const last = trimmed.lastIndexOf(\"}\");\n if (first === -1 || last === -1 || last <= first) {\n return null;\n }\n return trimmed.slice(first, last + 1);\n}\n\nfunction truncateForError(text: string): string {\n const collapsed = text.replace(/\\s+/g, \" \").trim();\n return collapsed.length > 200 ? `${collapsed.slice(0, 199)}…` : collapsed;\n}\n\n/**\n * Send a screenshot + assertion to a vision-capable model and return its\n * pass/fail verdict with an explanation. Implemented for both providers via raw\n * `fetch` (no SDKs). Temperature is pinned to 0 for the most deterministic\n * verdict the model can give — AI assertions are inherently non-deterministic,\n * so we minimize the variance and always surface the explanation for audit.\n */\nexport async function assertWithAiVision(\n input: AiVisionInput,\n config: AiConfig\n): Promise<AiVisionVerdict> {\n const raw = config.provider === \"anthropic\"\n ? await visionWithAnthropic(input, config)\n : await visionWithOpenAi(input, config);\n return parseVisionVerdict(raw);\n}\n\nasync function visionWithAnthropic(input: AiVisionInput, config: AiConfig): Promise<string> {\n const data = await postJson<AnthropicTextResponse>(\n \"Anthropic\",\n `${apiRoot(config)}/v1/messages`,\n anthropicHeaders(config),\n {\n model: config.model,\n max_tokens: 1024,\n temperature: 0,\n messages: [\n {\n role: \"user\",\n content: [\n {\n type: \"image\",\n source: {\n type: \"base64\",\n media_type: input.mediaType,\n data: input.imageBase64\n }\n },\n { type: \"text\", text: buildVisionPrompt(input.assertion) }\n ]\n }\n ]\n }\n );\n\n return extractAnthropicText(data);\n}\n\nasync function visionWithOpenAi(input: AiVisionInput, config: AiConfig): Promise<string> {\n const data = await postJson<OpenAiTextResponse>(\n \"OpenAI\",\n `${apiRoot(config)}/v1/chat/completions`,\n openAiHeaders(config),\n {\n model: config.model,\n max_tokens: 1024,\n temperature: 0,\n messages: [\n {\n role: \"user\",\n content: [\n { type: \"text\", text: buildVisionPrompt(input.assertion) },\n {\n type: \"image_url\",\n image_url: { url: `data:${input.mediaType};base64,${input.imageBase64}` }\n }\n ]\n }\n ]\n }\n );\n\n return extractOpenAiText(data);\n}\n\nasync function postJson<T>(\n provider: ProviderLabel,\n url: string,\n headers: Record<string, string>,\n body: unknown\n): Promise<T> {\n const response = await fetch(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(AI_REQUEST_TIMEOUT_MS)\n });\n\n if (!response.ok) {\n const responseBody = await response.text();\n throw new Error(`${provider} API error (${response.status}): ${responseBody}`);\n }\n\n return await response.json() as T;\n}\n\nfunction anthropicHeaders(config: AiConfig): Record<string, string> {\n return {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": config.apiKey,\n \"anthropic-version\": \"2023-06-01\"\n };\n}\n\nfunction openAiHeaders(config: AiConfig): Record<string, string> {\n return {\n \"Content-Type\": \"application/json\",\n \"Authorization\": `Bearer ${config.apiKey}`\n };\n}\n\nfunction extractAnthropicText(data: AnthropicTextResponse): string {\n const textBlock = data.content?.find((c) => c.type === \"text\");\n if (!textBlock?.text) {\n throw new Error(\"Anthropic API returned no text content\");\n }\n\n return textBlock.text;\n}\n\nfunction extractOpenAiText(data: OpenAiTextResponse): string {\n if (!data.choices?.[0]?.message?.content) {\n throw new Error(\"OpenAI API returned no content\");\n }\n\n return data.choices[0].message.content;\n}\n","import type { Assertion, AssertionResult, Config } from \"../types/index.js\";\nimport type { RunPolicy } from \"./policy.js\";\n\n/**\n * The minimal session surface assertions read from. Both a live Playwright page\n * and a {@link SessionDriver}-backed page satisfy it structurally, so\n * `evaluateAssertions` has no Playwright dependency.\n */\ntype AssertionSession = {\n url(): string;\n locator(selector: string): { count(): Promise<number> };\n};\n\nexport type ConsoleEntry = {\n type: string;\n text: string;\n location?: string;\n};\n\nexport type NetworkEntry = {\n url: string;\n status: number;\n};\n\nfunction shouldIgnoreNetwork(url: string, patterns: string[]): boolean {\n return patterns.some((pattern) => url.includes(pattern));\n}\n\nfunction filterNetworkEntries(entries: NetworkEntry[], patterns: string[]): NetworkEntry[] {\n if (patterns.length === 0) {\n return entries;\n }\n return entries.filter((entry) => !shouldIgnoreNetwork(entry.url, patterns));\n}\n\nfunction mergeAssertions(config: Config, huntAssertions: Assertion[] = []): Assertion[] {\n let noConsoleErrors = config.assertions.noConsoleErrors;\n let noNetworkErrors = config.assertions.noNetworkErrors;\n\n for (const assertion of huntAssertions) {\n if (\"noConsoleErrors\" in assertion) {\n noConsoleErrors = assertion.noConsoleErrors;\n }\n if (\"noNetworkErrors\" in assertion) {\n noNetworkErrors = assertion.noNetworkErrors;\n }\n }\n\n const merged: Assertion[] = [];\n if (noConsoleErrors) {\n merged.push({ noConsoleErrors: true });\n }\n if (noNetworkErrors) {\n merged.push({ noNetworkErrors: true });\n }\n\n for (const assertion of huntAssertions) {\n if (\"noConsoleErrors\" in assertion || \"noNetworkErrors\" in assertion) {\n continue;\n }\n merged.push(assertion);\n }\n\n return merged;\n}\n\nexport async function evaluateAssertions(options: {\n page: AssertionSession;\n config: Config;\n huntAssertions?: Assertion[];\n consoleEntries: ConsoleEntry[];\n networkEntries: NetworkEntry[];\n}): Promise<AssertionResult[]> {\n const assertions = mergeAssertions(options.config, options.huntAssertions);\n const results: AssertionResult[] = [];\n const networkEntries = filterNetworkEntries(\n options.networkEntries,\n options.config.assertions.networkIgnorePatterns\n );\n\n for (const assertion of assertions) {\n try {\n if (\"selectorExists\" in assertion) {\n const count = await options.page.locator(assertion.selectorExists).count();\n results.push({\n type: \"selectorExists\",\n value: assertion.selectorExists,\n status: count > 0 ? \"pass\" : \"fail\",\n error: count > 0 ? undefined : \"Selector not found\"\n });\n continue;\n }\n if (\"selectorNotExists\" in assertion) {\n const count = await options.page.locator(assertion.selectorNotExists).count();\n results.push({\n type: \"selectorNotExists\",\n value: assertion.selectorNotExists,\n status: count === 0 ? \"pass\" : \"fail\",\n error: count === 0 ? undefined : \"Selector exists\"\n });\n continue;\n }\n if (\"urlIncludes\" in assertion) {\n const current = options.page.url();\n const pass = current.includes(assertion.urlIncludes);\n results.push({\n type: \"urlIncludes\",\n value: assertion.urlIncludes,\n status: pass ? \"pass\" : \"fail\",\n error: pass ? undefined : `URL did not include ${assertion.urlIncludes}`\n });\n continue;\n }\n if (\"urlEquals\" in assertion) {\n const current = options.page.url();\n const pass = current === assertion.urlEquals;\n results.push({\n type: \"urlEquals\",\n value: assertion.urlEquals,\n status: pass ? \"pass\" : \"fail\",\n error: pass ? undefined : `URL did not equal ${assertion.urlEquals}`\n });\n continue;\n }\n if (\"noConsoleErrors\" in assertion) {\n const errors = options.consoleEntries.filter((entry) => entry.type === \"error\");\n const pass = errors.length === 0;\n results.push({\n type: \"noConsoleErrors\",\n value: true,\n status: pass ? \"pass\" : \"fail\",\n error: pass ? undefined : `${errors.length} console error(s)`\n });\n continue;\n }\n if (\"noNetworkErrors\" in assertion) {\n const pass = networkEntries.length === 0;\n results.push({\n type: \"noNetworkErrors\",\n value: true,\n status: pass ? \"pass\" : \"fail\",\n error: pass ? undefined : `${networkEntries.length} network error(s)`\n });\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : \"Assertion failed\";\n const type = Object.keys(assertion)[0] ?? \"assertion\";\n results.push({\n type,\n status: \"fail\",\n error: message\n });\n }\n }\n\n return results;\n}\n\n/** The minimal driver surface the native assertion evaluator queries. */\ntype NativeAssertionDriver = {\n count(selector: string): Promise<number>;\n};\n\nexport type NativeAssertionEvaluation = {\n results: AssertionResult[];\n /** Human-facing warnings (one per web-only assertion skipped), to log. */\n warnings: string[];\n};\n\n/**\n * PROWL-050 / ARCH-004 — evaluate hunt- and config-level assertions on a native\n * (macOS / Android / iOS) target. Config- and hunt-level blocks are merged with\n * exactly the same {@link mergeAssertions} the web path uses, so precedence is\n * identical. Assertion types that resolve a selector\n * ({@link NATIVE_APPLICABLE_ASSERTION_TYPES}) run against the driver's `count`;\n * web-only types (URL / console / network) are reported with status `skipped`,\n * never silently dropped and never a hard error. Mirrors the web path's\n * semantics: config- and hunt-level blocks merge identically and assertions are\n * evaluated after steps regardless of whether a step failed.\n *\n * A console warning is emitted only for web-only assertions the **hunt** authored\n * (clear intent), not for the `noConsoleErrors`/`noNetworkErrors` config defaults\n * that apply to every run — those still surface as skipped results, so they are\n * fully auditable without spamming a warning on every native run.\n */\nexport async function evaluateNativeAssertions(options: {\n driver: NativeAssertionDriver;\n config: Config;\n huntAssertions?: Assertion[];\n assertAllowedSelector?: RunPolicy[\"assertAllowedSelector\"];\n /** Display label for the target, e.g. \"macOS\" / \"Android\" / \"iOS\". */\n targetLabel: string;\n}): Promise<NativeAssertionEvaluation> {\n const assertions = mergeAssertions(options.config, options.huntAssertions);\n const authoredTypes = new Set(\n (options.huntAssertions ?? []).map((assertion) => Object.keys(assertion)[0])\n );\n const results: AssertionResult[] = [];\n const warnings: string[] = [];\n\n for (const assertion of assertions) {\n const type = Object.keys(assertion)[0] ?? \"assertion\";\n try {\n if (\"selectorExists\" in assertion) {\n options.assertAllowedSelector?.(assertion.selectorExists);\n const count = await options.driver.count(assertion.selectorExists);\n results.push({\n type: \"selectorExists\",\n value: assertion.selectorExists,\n status: count > 0 ? \"pass\" : \"fail\",\n error: count > 0 ? undefined : \"Selector not found\"\n });\n continue;\n }\n if (\"selectorNotExists\" in assertion) {\n options.assertAllowedSelector?.(assertion.selectorNotExists);\n const count = await options.driver.count(assertion.selectorNotExists);\n results.push({\n type: \"selectorNotExists\",\n value: assertion.selectorNotExists,\n status: count === 0 ? \"pass\" : \"fail\",\n error: count === 0 ? undefined : \"Selector exists\"\n });\n continue;\n }\n\n // Every remaining Assertion type is web-only on a native target.\n const rawValue = (assertion as Record<string, string | boolean>)[type];\n results.push({\n type,\n value: rawValue,\n status: \"skipped\",\n error: `skipped (web-only): not supported on the ${options.targetLabel} target`\n });\n if (authoredTypes.has(type)) {\n warnings.push(`${type} is web-only; skipped on ${options.targetLabel} target`);\n }\n } catch (error) {\n const detail = error instanceof Error ? error.message : \"no error details\";\n results.push({\n type,\n status: \"fail\",\n error: `Native assertion \"${type}\" failed: ${detail}`\n });\n }\n }\n\n return { results, warnings };\n}\n","import type { DriverResponse } from \"../browser/driver.js\";\nimport type { TraceCorrelation } from \"../types/index.js\";\n\n/** Default response header carrying a distributed-trace id (W3C Trace Context). */\nexport const DEFAULT_TRACE_HEADER = \"traceparent\";\n\n/**\n * Extract a trace id from a trace header value.\n *\n * For the W3C `traceparent` format (`version-traceid-spanid-flags`, e.g.\n * `00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01`) the 32-hex trace id\n * is returned. For a non-standard header that simply carries an id, the trimmed\n * raw value is used. Returns undefined for an empty value.\n */\nexport function parseTraceId(headerValue: string): string | undefined {\n const raw = headerValue.trim();\n if (raw.length === 0) return undefined;\n\n const parts = raw.split(\"-\");\n if (parts.length >= 3 && /^[0-9a-f]{32}$/i.test(parts[1])) {\n return parts[1];\n }\n\n return raw;\n}\n\nfunction readHeader(headers: Record<string, string>, headerName: string): string | undefined {\n const normalizedName = headerName.toLowerCase();\n return headers[normalizedName];\n}\n\nfunction redactValues(text: string, values: readonly string[]): string {\n let redacted = text;\n for (const value of values) {\n if (value.length === 0) continue;\n redacted = redacted.split(value).join(\"[REDACTED]\");\n }\n return redacted;\n}\n\n/**\n * If a failing response carries the configured trace header, record a correlation\n * linking the response URL/status to its trace id. No-op when the header is absent\n * or empty, so passing apps produce no noise.\n */\nexport function captureTraceCorrelation(\n response: DriverResponse,\n headerName: string,\n sink: TraceCorrelation[],\n redactionValues: readonly string[] = []\n): void {\n const value = readHeader(response.headers(), headerName);\n if (!value) return;\n\n const traceId = parseTraceId(value);\n if (!traceId) return;\n\n sink.push({\n url: redactValues(response.url(), redactionValues),\n status: response.status(),\n traceId,\n header: value\n });\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { RunResult } from \"../types/index.js\";\n\nexport function writeResult(runDir: string, result: RunResult): string {\n const fileName = \"result.json\";\n const fullPath = path.join(runDir, fileName);\n fs.writeFileSync(fullPath, JSON.stringify(result, null, 2));\n return fileName;\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { AssertionResult, RunResult, StepResult } from \"../types/index.js\";\n\nexport function escapeMd(text: string): string {\n return text.replace(/([|`*_{}[\\]()#+\\-!\\\\])/g, \"\\\\$1\");\n}\n\nfunction formatStep(step: StepResult): string {\n const base = `- [${step.status.toUpperCase()}] ${step.type} (${step.durationMs}ms)`;\n const selector = step.selector ? ` selector=${step.selector}` : \"\";\n const healed = step.healedFrom ? ` healed-from=${escapeMd(step.healedFrom)}` : \"\";\n const value = step.value ? ` value=${escapeMd(step.value)}` : \"\";\n const error = step.error ? ` error=${escapeMd(step.error)}` : \"\";\n return `${base}${selector}${healed}${value}${error}`;\n}\n\nfunction formatAssertion(assertion: AssertionResult): string {\n const value = assertion.value !== undefined ? ` value=${typeof assertion.value === \"string\" ? escapeMd(assertion.value) : assertion.value}` : \"\";\n const error = assertion.error ? ` error=${escapeMd(assertion.error)}` : \"\";\n return `- [${assertion.status.toUpperCase()}] ${assertion.type}${value}${error}`;\n}\n\nexport function writeSummary(runDir: string, result: RunResult): string {\n const lines: string[] = [];\n lines.push(\"# Prowl Run Summary\");\n lines.push(\"\");\n lines.push(`Status: ${result.status.toUpperCase()}`);\n lines.push(`Hunt: ${result.hunt}`);\n lines.push(`Target: ${result.targetUrl}`);\n lines.push(`Started: ${result.startedAt}`);\n lines.push(`Duration: ${result.durationMs}ms`);\n lines.push(\"\");\n lines.push(\"## Steps\");\n for (const step of result.steps) {\n lines.push(formatStep(step));\n }\n\n const healed = result.steps.filter((step) => step.healedFrom);\n if (healed.length > 0) {\n lines.push(\"\");\n lines.push(\"## Self-Healed Selectors\");\n lines.push(\"These selectors no longer matched and were auto-healed. Update your hunt to use the healed selector (or a stable `data-testid`):\");\n for (const step of healed) {\n lines.push(`- ${escapeMd(step.healedFrom ?? \"\")} → ${escapeMd(step.selector ?? \"\")}`);\n }\n }\n\n lines.push(\"\");\n lines.push(\"## Assertions\");\n for (const assertion of result.assertions) {\n lines.push(formatAssertion(assertion));\n }\n if (result.traceCorrelations && result.traceCorrelations.length > 0) {\n lines.push(\"\");\n lines.push(\"## Trace Correlations\");\n for (const correlation of result.traceCorrelations) {\n lines.push(\n `- [${correlation.status}] ${escapeMd(correlation.url)} traceId=${escapeMd(correlation.traceId)}`\n );\n }\n }\n\n lines.push(\"\");\n lines.push(\"## Artifacts\");\n const artifacts = result.artifacts;\n if (artifacts.summary) {\n lines.push(`- summary: ${artifacts.summary}`);\n }\n if (artifacts.console) {\n lines.push(`- console: ${artifacts.console}`);\n }\n if (artifacts.trace) {\n lines.push(`- trace: ${artifacts.trace}`);\n }\n if (artifacts.networkHar) {\n lines.push(`- network: ${artifacts.networkHar}`);\n }\n if (artifacts.junit) {\n lines.push(`- junit: ${artifacts.junit}`);\n }\n if (artifacts.screenshots && artifacts.screenshots.length > 0) {\n for (const screenshot of artifacts.screenshots) {\n lines.push(`- screenshot: ${screenshot}`);\n }\n }\n\n const fileName = \"summary.md\";\n const fullPath = path.join(runDir, fileName);\n fs.writeFileSync(fullPath, `${lines.join(\"\\n\")}\\n`);\n return fileName;\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { RunResult } from \"../types/index.js\";\n\nexport function escapeXml(text: string): string {\n return text\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n}\n\nexport function writeJunit(runDir: string, result: RunResult): string {\n const totalTests = result.steps.length + result.assertions.length;\n const failures =\n result.steps.filter((s) => s.status === \"fail\").length +\n result.assertions.filter((a) => a.status === \"fail\").length;\n const skipped = result.assertions.filter((a) => a.status === \"skipped\").length;\n const timeSeconds = (result.durationMs / 1000).toFixed(3);\n const huntName = escapeXml(result.hunt);\n\n const lines: string[] = [];\n lines.push('<?xml version=\"1.0\" encoding=\"UTF-8\"?>');\n lines.push(\"<testsuites>\");\n lines.push(\n ` <testsuite name=\"${huntName}\" tests=\"${totalTests}\" failures=\"${failures}\" errors=\"0\" skipped=\"${skipped}\" time=\"${timeSeconds}\" timestamp=\"${escapeXml(result.startedAt)}\">`\n );\n\n for (let i = 0; i < result.steps.length; i++) {\n const step = result.steps[i];\n const stepTime = (step.durationMs / 1000).toFixed(3);\n const caseName = escapeXml(`step ${i + 1}: ${step.type}`);\n\n if (step.status === \"fail\") {\n const failureText = step.error ?? `Step ${step.type} failed with no error provided`;\n const escapedFailureText = escapeXml(failureText);\n lines.push(` <testcase name=\"${caseName}\" classname=\"${huntName}\" time=\"${stepTime}\">`);\n lines.push(` <failure message=\"${escapedFailureText}\" type=\"step\">${escapedFailureText}</failure>`);\n lines.push(\" </testcase>\");\n } else {\n lines.push(` <testcase name=\"${caseName}\" classname=\"${huntName}\" time=\"${stepTime}\"/>`);\n }\n }\n\n for (const assertion of result.assertions) {\n const caseName = escapeXml(`assertion: ${assertion.type}`);\n\n if (assertion.status === \"fail\") {\n const failureText = assertion.error ?? `Assertion ${assertion.type} failed with no error provided`;\n const escapedFailureText = escapeXml(failureText);\n lines.push(` <testcase name=\"${caseName}\" classname=\"${huntName}\" time=\"0\">`);\n lines.push(` <failure message=\"${escapedFailureText}\" type=\"assertion\">${escapedFailureText}</failure>`);\n lines.push(\" </testcase>\");\n } else if (assertion.status === \"skipped\") {\n const skipText = escapeXml(assertion.error ?? \"skipped\");\n lines.push(` <testcase name=\"${caseName}\" classname=\"${huntName}\" time=\"0\">`);\n lines.push(` <skipped message=\"${skipText}\"/>`);\n lines.push(\" </testcase>\");\n } else {\n lines.push(` <testcase name=\"${caseName}\" classname=\"${huntName}\" time=\"0\"/>`);\n }\n }\n\n lines.push(\" </testsuite>\");\n lines.push(\"</testsuites>\");\n\n const fileName = \"junit.xml\";\n const fullPath = path.join(runDir, fileName);\n fs.writeFileSync(fullPath, `${lines.join(\"\\n\")}\\n`);\n return fileName;\n}\n","import type { RunResult } from \"../types/index.js\";\nimport { writeResult } from \"./result.js\";\nimport { writeSummary } from \"./summary.js\";\nimport { writeJunit } from \"./junit.js\";\n\nexport type ReportOptions = {\n junit?: boolean;\n};\n\nexport function writeReports(runDir: string, result: RunResult, options?: ReportOptions): RunResult {\n const summary = writeSummary(runDir, result);\n const updated: RunResult = {\n ...result,\n artifacts: {\n ...result.artifacts,\n summary\n }\n };\n\n if (options?.junit) {\n updated.artifacts.junit = writeJunit(runDir, updated);\n }\n\n writeResult(runDir, updated);\n\n return updated;\n}\n","export function timestamp(prefix?: string): string {\n const now = new Date();\n const pad = (value: number): string => value.toString().padStart(2, \"0\");\n const pad3 = (value: number): string => value.toString().padStart(3, \"0\");\n const ts = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}_${pad(\n now.getHours()\n )}-${pad(now.getMinutes())}-${pad(now.getSeconds())}-${pad3(now.getMilliseconds())}`;\n return prefix ? `${prefix}-${ts}` : ts;\n}\n","import type { HistoryEntry } from \"../types/index.js\";\nimport { readHistory } from \"./history.js\";\n\nexport const DEFAULT_FLAKY_THRESHOLD = 0.3;\n\nexport type FlakyScore = {\n hunt: string;\n /** Oscillation rate in [0,1]: share of consecutive run pairs whose status changed. */\n score: number;\n runs: number;\n flaky: boolean;\n};\n\n/**\n * Flake score for a single hunt's run history (oldest→newest): the fraction of\n * consecutive run pairs where the status flipped (pass↔fail). 0 = perfectly\n * stable, 1 = flips every run. Needs at least 2 runs; fewer returns 0.\n */\nexport function computeFlakeScore(entries: HistoryEntry[], lastN?: number): number {\n const slice = lastN !== undefined && lastN > 0 ? entries.slice(-lastN) : entries;\n if (slice.length < 2) return 0;\n\n let transitions = 0;\n for (let i = 1; i < slice.length; i++) {\n if (slice[i].status !== slice[i - 1].status) {\n transitions += 1;\n }\n }\n return transitions / (slice.length - 1);\n}\n\nexport type RankFlakyOptions = {\n /** Only score the most recent N runs per hunt. */\n lastN?: number;\n /** Score at/above this is flagged flaky. Defaults to DEFAULT_FLAKY_THRESHOLD. */\n threshold?: number;\n};\n\n/**\n * Rank every hunt in the project's history by flake score, highest first.\n * Hunts are tie-broken by run count (more runs first) then name for stable output.\n */\nexport function rankFlaky(configDir: string, options: RankFlakyOptions = {}): FlakyScore[] {\n const threshold = options.threshold ?? DEFAULT_FLAKY_THRESHOLD;\n const { entries } = readHistory(configDir);\n\n const byHunt = new Map<string, HistoryEntry[]>();\n for (const entry of entries) {\n const list = byHunt.get(entry.hunt) ?? [];\n list.push(entry);\n byHunt.set(entry.hunt, list);\n }\n\n const scores: FlakyScore[] = [];\n for (const [hunt, huntEntries] of byHunt) {\n const considered = options.lastN !== undefined && options.lastN > 0\n ? huntEntries.slice(-options.lastN)\n : huntEntries;\n const score = computeFlakeScore(considered);\n scores.push({\n hunt,\n score,\n runs: considered.length,\n flaky: score >= threshold\n });\n }\n\n scores.sort((a, b) => b.score - a.score || b.runs - a.runs || a.hunt.localeCompare(b.hunt));\n return scores;\n}\n","import { createHash } from \"node:crypto\";\n\n/**\n * A single failure worth logging as a bug: a specific hunt failing at a specific\n * spot. `stepType`/`selector`/`stepIndex` are absent when the hunt threw before\n * producing step results (e.g. a missing hunt file).\n */\nexport interface BugFailure {\n hunt: string;\n stepIndex?: number;\n stepType?: string;\n selector?: string;\n error: string;\n runDir?: string;\n}\n\n/**\n * Reduce an error message to its stable \"class\" by removing volatile details\n * (timeout durations, pixel coordinates, hex ids) so the same underlying failure\n * keeps the same fingerprint across runs.\n */\nexport function normalizeError(error: string): string {\n return error\n .toLowerCase()\n .replace(/0x[0-9a-f]+/g, \"\") // hex ids\n .replace(/\\b\\d+(?:\\.\\d+)?\\s*(?:ms|s|px)\\b/g, \"\") // unit-qualified timings/coordinates\n .replace(/\\s+/g, \" \")\n .trim();\n}\n\n/**\n * Stable label for the failing spot within a hunt, e.g. `4:click@#submit`.\n * Used in the human-readable marker. Returns `-` when no step is known.\n */\nexport function stepLabel(failure: BugFailure): string {\n if (failure.stepType === undefined && failure.selector === undefined) {\n return \"-\";\n }\n const index = failure.stepIndex === undefined ? \"?\" : String(failure.stepIndex);\n const type = failure.stepType ?? \"?\";\n const selector = failure.selector ? `@${failure.selector}` : \"\";\n return `${index}:${type}${selector}`;\n}\n\n/** Keep untrusted metadata from changing the hidden HTML-comment shape. */\nfunction sanitizeMarkerValue(value: string): string {\n return value\n .replace(/\\r?\\n/g, \" \")\n .replace(/-->/g, \"-->\")\n .trim();\n}\n\n/**\n * Identifies \"the same bug\" across runs: the hunt, the failing step's type and\n * selector, and the normalized error class. The step *index* is deliberately\n * excluded so reordering/inserting steps in a hunt does not spawn a duplicate.\n */\nexport function computeFingerprint(failure: BugFailure): string {\n const parts = [\n failure.hunt,\n failure.stepType ?? \"\",\n failure.selector ?? \"\",\n normalizeError(failure.error)\n ];\n return createHash(\"sha1\").update(parts.join(\"|\")).digest(\"hex\").slice(0, 8);\n}\n\n/** Hidden HTML-comment marker embedded in each ticket so re-runs can recognize it. */\nexport function buildMarker(failure: BugFailure, hash: string): string {\n const hunt = sanitizeMarkerValue(failure.hunt);\n const step = sanitizeMarkerValue(stepLabel(failure));\n return `<!-- prowl:fp=${hash} hunt=${hunt} step=${step} -->`;\n}\n","import type { BugFailure } from \"../backlog/fingerprint.js\";\nimport { normalizeError } from \"../backlog/fingerprint.js\";\nimport type { CiFailureCluster } from \"../types/index.js\";\n\n/**\n * Failure clustering (PROWL-034). Groups failures that share a common cause —\n * the same normalized error on the same step type and selector — so a single\n * root cause (e.g. one renamed selector breaking 5 hunts) surfaces as one\n * cluster instead of N independent failures.\n */\nexport type FailureCluster = CiFailureCluster;\n\nfunction clusterKey(failure: BugFailure, normalizedError: string): string {\n return [failure.stepType ?? \"\", failure.selector ?? \"\", normalizedError].join(\"|\");\n}\n\nfunction describeCause(failure: BugFailure, error: string): string {\n const where = failure.stepType\n ? `${failure.stepType}${failure.selector ? ` (${failure.selector})` : \"\"}`\n : \"run\";\n return `${where}: ${error}`;\n}\n\n/**\n * Group failures by shared cause. Returns clusters sorted by size (largest first),\n * then by cause for stable output. Every failure lands in a cluster — single-hunt\n * clusters are included so the full picture is preserved; callers can filter to\n * `count > 1` to show only shared root causes.\n */\nexport function clusterFailures(failures: BugFailure[]): FailureCluster[] {\n const groups = new Map<string, { sample: BugFailure; error: string; hunts: Set<string> }>();\n\n for (const failure of failures) {\n const error = normalizeError(failure.error);\n const key = clusterKey(failure, error);\n const existing = groups.get(key);\n if (existing) {\n existing.hunts.add(failure.hunt);\n } else {\n groups.set(key, { sample: failure, error, hunts: new Set([failure.hunt]) });\n }\n }\n\n const clusters: FailureCluster[] = [];\n for (const { sample, error, hunts } of groups.values()) {\n clusters.push({\n cause: describeCause(sample, error),\n stepType: sample.stepType,\n selector: sample.selector,\n error,\n count: hunts.size,\n hunts: [...hunts].sort()\n });\n }\n\n clusters.sort((a, b) => b.count - a.count || a.cause.localeCompare(b.cause));\n return clusters;\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { CiHuntResult, RunResult } from \"../types/index.js\";\nimport type { RunSuiteResult } from \"../runner/suite.js\";\nimport { type BugFailure, buildMarker, computeFingerprint } from \"./fingerprint.js\";\nimport { classifyFingerprint, extractFingerprints, nextTicketId } from \"./parse.js\";\nimport { insertTickets, renderTicket } from \"./write.js\";\n\nexport type { BugFailure } from \"./fingerprint.js\";\nexport { SECTION_HEADING } from \"./write.js\";\n\nexport interface UpdateBacklogOptions {\n /** Project root containing docs/. Defaults to process.cwd(). */\n projectRoot?: string;\n /** Overrides the backlog path (default: <projectRoot>/docs/backlog.md). */\n backlogPath?: string;\n /** Overrides the resolved path (default: <projectRoot>/docs/resolved.md). */\n resolvedPath?: string;\n /** Date stamp for new tickets (default: today, YYYY-MM-DD). */\n date?: string;\n}\n\nexport interface BugLogSummary {\n /** QA-NNN ids created for brand-new failures. */\n created: string[];\n /** QA-NNN ids created for failures that recurred after being resolved. */\n regressions: string[];\n /** QA-NNN ids of already-open tickets that were left untouched. */\n skipped: string[];\n backlogPath: string;\n}\n\n/** Read optional backlog state, returning empty content only when the file is absent. */\nfunction readFileOrEmpty(filePath: string): string {\n try {\n return fs.readFileSync(filePath, \"utf-8\");\n } catch (error) {\n const err = error as NodeJS.ErrnoException;\n if (err.code === \"ENOENT\") return \"\";\n throw new Error(`Failed to read \"${filePath}\": ${err.message}`);\n }\n}\n\n/** Convert a failed CI hunt result into the most specific backlog failure available. */\nfunction buildFailure(hunt: CiHuntResult): BugFailure {\n const failure: BugFailure = {\n hunt: hunt.hunt,\n error: hunt.error ?? \"Run failed\",\n runDir: hunt.runDir\n };\n\n if (!hunt.runDir) return failure;\n\n let run: RunResult;\n try {\n const resultJson = readFileOrEmpty(path.join(hunt.runDir, \"result.json\"));\n if (!resultJson) return failure;\n run = JSON.parse(resultJson) as RunResult;\n } catch (error) {\n if (!(error instanceof SyntaxError)) throw error;\n return failure; // Malformed result.json; keep the message-only failure.\n }\n if (!run || !Array.isArray(run.steps)) return failure;\n\n const stepIndex = run.steps.findIndex((step) => step.status === \"fail\");\n if (stepIndex !== -1) {\n const step = run.steps[stepIndex];\n failure.stepIndex = stepIndex;\n failure.stepType = step.type;\n failure.selector = step.selector;\n if (step.error) failure.error = step.error;\n return failure;\n }\n\n const failedAssertion = run.assertions?.find((assertion) => assertion.status === \"fail\");\n if (failedAssertion) {\n failure.stepType = `assert:${failedAssertion.type}`;\n if (failedAssertion.error) failure.error = failedAssertion.error;\n }\n\n return failure;\n}\n\n/** Extract one BugFailure per failed hunt from a completed suite run. */\nexport function extractFailures(suiteResult: RunSuiteResult): BugFailure[] {\n return suiteResult.result.hunts\n .filter((hunt) => hunt.status === \"fail\")\n .map(buildFailure);\n}\n\n/**\n * Logs failures from a completed suite run as deduplicated bug tickets in the\n * target project's backlog. New failures get a fresh QA-NNN ticket; failures that\n * already have an open ticket are skipped; failures matching a resolved ticket are\n * logged as regressions that reference the old id. Idempotent across runs.\n */\nexport function updateBacklogFromSuite(\n suiteResult: RunSuiteResult,\n options: UpdateBacklogOptions = {}\n): BugLogSummary {\n const projectRoot = options.projectRoot ?? process.cwd();\n const backlogPath = options.backlogPath ?? path.join(projectRoot, \"docs\", \"backlog.md\");\n const resolvedPath = options.resolvedPath ?? path.join(projectRoot, \"docs\", \"resolved.md\");\n const date = options.date ?? new Date().toISOString().slice(0, 10);\n\n const summary: BugLogSummary = { created: [], regressions: [], skipped: [], backlogPath };\n\n const failures = extractFailures(suiteResult);\n if (failures.length === 0) return summary;\n\n const backlogContent = readFileOrEmpty(backlogPath);\n const resolvedContent = readFileOrEmpty(resolvedPath);\n\n const activeFps = extractFingerprints(backlogContent);\n const resolvedFps = extractFingerprints(resolvedContent);\n\n let counter = Number(nextTicketId([backlogContent, resolvedContent]).slice(3));\n const makeId = (): string => `QA-${String(counter++).padStart(3, \"0\")}`;\n\n const seenThisRun = new Set<string>();\n const ticketsToAdd: string[] = [];\n\n for (const failure of failures) {\n const fp = computeFingerprint(failure);\n if (seenThisRun.has(fp)) continue;\n seenThisRun.add(fp);\n\n const classification = classifyFingerprint(fp, activeFps, resolvedFps);\n if (classification.kind === \"open\") {\n summary.skipped.push(classification.ticketId);\n continue;\n }\n\n const id = makeId();\n const regressionOf = classification.kind === \"regression\" ? classification.resolvedId : undefined;\n ticketsToAdd.push(renderTicket({ id, failure, marker: buildMarker(failure, fp), regressionOf, date }));\n\n if (regressionOf) {\n summary.regressions.push(id);\n } else {\n summary.created.push(id);\n }\n }\n\n if (ticketsToAdd.length > 0) {\n fs.mkdirSync(path.dirname(backlogPath), { recursive: true });\n fs.writeFileSync(backlogPath, insertTickets(backlogContent, ticketsToAdd));\n }\n\n return summary;\n}\n","const MARKER_FP = /<!--\\s*prowl:fp=([0-9a-f]+)/;\nconst TICKET_ID = /\\bQA-(\\d+)\\b/;\nconst TICKET_ID_GLOBAL = /\\bQA-(\\d+)\\b/g;\nconst HEADING = /^#{1,6}\\s/;\n\n/**\n * Map each embedded fingerprint to the QA-NNN ticket id that owns it, by pairing\n * every `prowl:fp=` marker with the most recent ticket heading above it.\n */\nexport function extractFingerprints(content: string): Map<string, string> {\n const map = new Map<string, string>();\n let currentId: string | undefined;\n\n for (const line of content.split(\"\\n\")) {\n if (HEADING.test(line)) {\n const idMatch = TICKET_ID.exec(line);\n currentId = idMatch ? `QA-${idMatch[1]}` : undefined;\n }\n const fpMatch = MARKER_FP.exec(line);\n if (fpMatch && currentId) {\n map.set(fpMatch[1], currentId);\n }\n }\n\n return map;\n}\n\n/** The next available QA-NNN id across all provided file contents (max + 1, zero-padded). */\nexport function nextTicketId(contents: string[]): string {\n let max = 0;\n for (const content of contents) {\n for (const match of content.matchAll(TICKET_ID_GLOBAL)) {\n const n = Number(match[1]);\n if (n > max) max = n;\n }\n }\n return `QA-${String(max + 1).padStart(3, \"0\")}`;\n}\n\nexport type Classification =\n | { kind: \"new\" }\n | { kind: \"open\"; ticketId: string }\n | { kind: \"regression\"; resolvedId: string };\n\n/**\n * Decide how to handle a failure fingerprint:\n * - already tracked in the active backlog -> `open` (skip, no duplicate)\n * - previously resolved -> `regression` (link the old id)\n * - otherwise -> `new`\n */\nexport function classifyFingerprint(\n fp: string,\n activeFps: Map<string, string>,\n resolvedFps: Map<string, string>\n): Classification {\n const openId = activeFps.get(fp);\n if (openId) return { kind: \"open\", ticketId: openId };\n\n const resolvedId = resolvedFps.get(fp);\n if (resolvedId) return { kind: \"regression\", resolvedId };\n\n return { kind: \"new\" };\n}\n","import type { BugFailure } from \"./fingerprint.js\";\n\n/** Heading of the dedicated, agent-owned section in the target project's backlog. */\nexport const SECTION_HEADING = \"## QA Findings (automated)\";\n\nexport interface RenderTicketOptions {\n id: string;\n failure: BugFailure;\n marker: string;\n /** When set, this failure previously had a resolved ticket — link it as a regression. */\n regressionOf?: string;\n /** YYYY-MM-DD */\n date: string;\n}\n\n/** Render one failure as a markdown QA ticket with stable machine-readable metadata. */\nexport function renderTicket(opts: RenderTicketOptions): string {\n const { id, failure, marker, regressionOf, date } = opts;\n\n const spot = failure.stepType\n ? `${failure.stepType}${failure.selector ? ` (${failure.selector})` : \"\"}`\n : \"run failed before steps executed\";\n\n const lines: string[] = [];\n lines.push(`### ${id}: ${failure.hunt} — ${spot}`);\n lines.push(marker);\n lines.push(`**Logged**: ${date}`);\n if (regressionOf) {\n lines.push(`**Regression of**: ${regressionOf} (previously resolved — see resolved.md)`);\n }\n lines.push(`**Hunt**: ${failure.hunt}`);\n const stepDesc = failure.stepType\n ? `step ${failure.stepIndex ?? \"?\"} — ${failure.stepType}${failure.selector ? ` ${failure.selector}` : \"\"}`\n : \"n/a (hunt did not produce step results)\";\n lines.push(`**Failing step**: ${stepDesc}`);\n lines.push(`**Error**: ${failure.error}`);\n if (failure.runDir) {\n lines.push(`**Artifacts**: ${failure.runDir}`);\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Append rendered tickets into the agent-owned section, creating the section at\n * the end of the file if it does not exist. Existing content is preserved.\n */\nexport function insertTickets(content: string, tickets: string[]): string {\n if (tickets.length === 0) return content;\n const block = tickets.join(\"\\n\\n\");\n\n const headingIndex = content.indexOf(SECTION_HEADING);\n if (headingIndex === -1) {\n const base = content.replace(/\\n*$/, \"\");\n const prefix = base ? `${base}\\n\\n` : \"\";\n return `${prefix}${SECTION_HEADING}\\n\\n${block}\\n`;\n }\n\n // Insert at the end of the section: just before the next top-level heading, or EOF.\n const afterHeading = headingIndex + SECTION_HEADING.length;\n const rest = content.slice(afterHeading);\n const nextHeadingRel = rest.search(/\\n## /);\n const insertAt = nextHeadingRel === -1 ? content.length : afterHeading + nextHeadingRel;\n\n const before = content.slice(0, insertAt).replace(/\\n*$/, \"\");\n const after = content.slice(insertAt);\n return `${before}\\n\\n${block}\\n${after}`;\n}\n","import path from \"node:path\";\nimport { runHunt, type RunOptions } from \"./index.js\";\nimport { loadConfig, listHunts, loadHuntTags } from \"../config/loader.js\";\nimport { writeCiResult, resolveCiStatus, countCiResults } from \"../reporter/ci-summary.js\";\nimport { timestamp } from \"../utils/timestamp.js\";\nimport { runWithConcurrency } from \"../utils/concurrency.js\";\nimport type { CiFailureCluster, CiFlakyHunt, CiHuntResult, CiResult, RunResult } from \"../types/index.js\";\nimport { rankFlaky, DEFAULT_FLAKY_THRESHOLD } from \"./flaky.js\";\nimport { clusterFailures } from \"./clustering.js\";\nimport { extractFailures } from \"../backlog/index.js\";\n\nexport type SkipReason = \"include\" | \"exclude\";\ntype SuiteHookResult = void | Promise<void>;\n\nexport interface RunSuiteHooks {\n onHuntStart?: (huntName: string) => SuiteHookResult;\n onStep?: RunOptions[\"onStep\"];\n onHuntSuccess?: (huntName: string, result: RunResult, runDir: string) => SuiteHookResult;\n onHuntFailure?: (huntName: string, message: string) => SuiteHookResult;\n onHuntSkipped?: (huntName: string, reason: SkipReason) => SuiteHookResult;\n}\n\nexport interface RunSuiteOptions {\n configPath?: string;\n urlOverride?: string;\n headed?: boolean;\n slowMo?: number;\n trace?: boolean;\n browser?: RunOptions[\"browser\"];\n channel?: RunOptions[\"channel\"];\n viewport?: string;\n junit?: boolean;\n includeTags?: string[];\n excludeTags?: string[];\n parallel?: number;\n hooks?: RunSuiteHooks;\n}\n\nexport interface RunSuiteResult {\n result: CiResult;\n /** Path to the written ci-result.json, or null when there were no hunts to run. */\n resultPath: string | null;\n}\n\nfunction normalizeTagFilter(tags: string[] | undefined): string[] | undefined {\n const normalized = tags?.map((tag) => tag.trim()).filter(Boolean);\n return normalized && normalized.length > 0 ? normalized : undefined;\n}\n\nasync function callHook(callback: (() => SuiteHookResult | undefined) | undefined): Promise<void> {\n if (!callback) return;\n try {\n await callback();\n } catch {\n // Presentation hooks must never change suite or hunt outcomes.\n }\n}\n\nfunction safeOnStep(hooks: RunSuiteHooks): RunOptions[\"onStep\"] {\n if (!hooks.onStep) return undefined;\n return (result, step, index) => {\n try {\n hooks.onStep?.(result, step, index);\n } catch {\n // Presentation hooks must never change suite or hunt outcomes.\n }\n };\n}\n\nfunction firstRunFailureMessage(result: RunResult): string | undefined {\n const failedStep = result.steps.find((step) => step.status === \"fail\" && step.error);\n if (failedStep?.error) return failedStep.error;\n\n const failedAssertion = result.assertions.find((assertion) => assertion.status === \"fail\" && assertion.error);\n return failedAssertion?.error;\n}\n\n/**\n * Runs every hunt in the project and aggregates a CiResult. Side-effect-free with\n * respect to the console and process exit — callers provide hooks for presentation\n * and inspect the returned status for exit codes.\n */\nexport async function runSuite(options: RunSuiteOptions = {}): Promise<RunSuiteResult> {\n const startedAt = new Date().toISOString();\n const startTime = Date.now();\n const hooks = options.hooks ?? {};\n\n const { config, configDir } = loadConfig(options.configPath);\n const hunts = listHunts(configDir);\n\n if (hunts.length === 0) {\n return {\n result: {\n status: \"no-hunts\",\n startedAt,\n durationMs: 0,\n totalHunts: 0,\n passed: 0,\n failed: 0,\n skipped: 0,\n hunts: []\n },\n resultPath: null\n };\n }\n\n const includeTags = normalizeTagFilter(options.includeTags);\n const excludeTags = normalizeTagFilter(options.excludeTags);\n const resultsByIndex: Array<CiHuntResult | undefined> = new Array(hunts.length);\n const onStep = safeOnStep(hooks);\n\n // Phase 1: Tag filtering (always sequential, preserves hunt order)\n const huntsToRun: Array<{ huntName: string; index: number }> = [];\n for (let index = 0; index < hunts.length; index++) {\n const huntName = hunts[index];\n if (includeTags || excludeTags) {\n const tags = loadHuntTags(huntName, configDir);\n\n if (includeTags && !includeTags.some((t) => tags.includes(t))) {\n await callHook(() => hooks.onHuntSkipped?.(huntName, \"include\"));\n resultsByIndex[index] = { hunt: huntName, status: \"skipped\", durationMs: 0 };\n continue;\n }\n if (excludeTags && excludeTags.some((t) => tags.includes(t))) {\n await callHook(() => hooks.onHuntSkipped?.(huntName, \"exclude\"));\n resultsByIndex[index] = { hunt: huntName, status: \"skipped\", durationMs: 0 };\n continue;\n }\n }\n huntsToRun.push({ huntName, index });\n }\n\n // Phase 2: Build a task per hunt to run\n const buildTask = (huntName: string) => async (): Promise<CiHuntResult> => {\n const huntStart = Date.now();\n try {\n await callHook(() => hooks.onHuntStart?.(huntName));\n\n const { result, runDir } = await runHunt({\n huntName,\n urlOverride: options.urlOverride,\n headed: options.headed,\n slowMo: options.slowMo,\n trace: options.trace,\n browser: options.browser,\n channel: options.channel,\n viewport: options.viewport,\n junit: options.junit,\n configPath: options.configPath,\n onStep\n });\n\n const error = result.status === \"fail\" ? firstRunFailureMessage(result) ?? \"Run failed\" : undefined;\n if (error) {\n await callHook(() => hooks.onHuntFailure?.(huntName, error));\n } else {\n await callHook(() => hooks.onHuntSuccess?.(huntName, result, runDir));\n }\n\n return {\n hunt: huntName,\n status: result.status,\n durationMs: result.durationMs,\n runDir,\n error\n };\n } catch (error) {\n const durationMs = Date.now() - huntStart;\n const message = error instanceof Error ? error.message : \"Run failed\";\n await callHook(() => hooks.onHuntFailure?.(huntName, message));\n return {\n hunt: huntName,\n status: \"fail\",\n durationMs,\n error: message\n };\n }\n };\n\n // Phase 3: Execute (parallel when requested, otherwise sequential in hunt order)\n const parallel = options.parallel;\n if (parallel !== undefined && parallel > 1) {\n const tasks = huntsToRun.map((entry) => ({ ...entry, task: buildTask(entry.huntName) }));\n const parallelResults = await runWithConcurrency(\n tasks.map((entry) => entry.task),\n parallel\n );\n for (let i = 0; i < parallelResults.length; i++) {\n const pr = parallelResults[i];\n const task = tasks[i];\n if (pr.status === \"fulfilled\") {\n resultsByIndex[task.index] = pr.value;\n } else {\n const message = pr.reason instanceof Error ? pr.reason.message : \"Run failed\";\n resultsByIndex[task.index] = {\n hunt: task.huntName,\n status: \"fail\",\n durationMs: 0,\n error: message\n };\n }\n }\n } else {\n for (const { huntName, index } of huntsToRun) {\n resultsByIndex[index] = await buildTask(huntName)();\n }\n }\n\n const totalDurationMs = Date.now() - startTime;\n const results: CiHuntResult[] = resultsByIndex.map((result, index) => {\n return result ?? {\n hunt: hunts[index],\n status: \"fail\",\n durationMs: 0,\n error: \"Run did not produce a result\"\n };\n });\n\n // Flag flaky hunts among those that actually ran this suite, using accumulated\n // run history (which already includes this run's entries). Omitted when none.\n const threshold = config.reliability?.flakyThreshold ?? DEFAULT_FLAKY_THRESHOLD;\n const ranThisSuite = new Set(\n results.filter((r) => r.status !== \"skipped\").map((r) => r.hunt)\n );\n const flaky: CiFlakyHunt[] = rankFlaky(configDir, { threshold })\n .filter((entry) => entry.flaky && ranThisSuite.has(entry.hunt))\n .map((entry) => ({ hunt: entry.hunt, score: entry.score }));\n\n // Cluster failures from this suite by shared cause; surface only multi-hunt\n // clusters (a single failing hunt is just that failure, not a \"cluster\").\n const clusters: CiFailureCluster[] = clusterFailures(\n extractFailures({ result: { hunts: results } as CiResult, resultPath: null })\n ).filter((cluster) => cluster.count > 1);\n\n const ciRunDir = path.join(configDir, \"runs\", timestamp(\"ci\"));\n const resultPath = writeCiResult(ciRunDir, results, startedAt, totalDurationMs, flaky, clusters);\n\n const { passed, failed, skipped } = countCiResults(results);\n\n return {\n result: {\n status: resolveCiStatus(results),\n startedAt,\n durationMs: totalDurationMs,\n totalHunts: results.length,\n passed,\n failed,\n skipped,\n hunts: results,\n ...(flaky.length > 0 ? { flaky } : {}),\n ...(clusters.length > 0 ? { clusters } : {})\n },\n resultPath\n };\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport chalk from \"chalk\";\nimport type { CiFailureCluster, CiFlakyHunt, CiHuntResult, CiResult, CiStatus } from \"../types/index.js\";\n\nexport type CiCounts = {\n passed: number;\n failed: number;\n skipped: number;\n};\n\nexport function countCiResults(results: CiHuntResult[]): CiCounts {\n return {\n passed: results.filter((r) => r.status === \"pass\").length,\n failed: results.filter((r) => r.status === \"fail\").length,\n skipped: results.filter((r) => r.status === \"skipped\").length\n };\n}\n\nexport function resolveCiStatus(results: CiHuntResult[]): CiStatus {\n if (results.length === 0) return \"no-hunts\";\n const { failed, passed } = countCiResults(results);\n if (failed > 0) return \"fail\";\n if (passed > 0) return \"pass\";\n return \"all-skipped\";\n}\n\nexport function printCiSummary(\n results: CiHuntResult[],\n totalDurationMs: number,\n flaky: CiFlakyHunt[] = [],\n clusters: CiFailureCluster[] = []\n): void {\n const { passed, failed, skipped } = countCiResults(results);\n\n const lineWidth = 45;\n console.log(`\\n ── CI Summary ${\"─\".repeat(lineWidth - 15)}`);\n\n for (const r of results) {\n const icon = r.status === \"pass\" ? chalk.green(\"✓\") : r.status === \"fail\" ? chalk.red(\"✗\") : chalk.yellow(\"○\");\n const name = r.status === \"fail\" ? chalk.red(r.hunt) : r.status === \"skipped\" ? chalk.yellow(r.hunt) : r.hunt;\n const duration = r.status === \"skipped\" ? \"\" : chalk.gray(`(${r.durationMs}ms)`);\n const pad = \" \".repeat(Math.max(1, 40 - r.hunt.length));\n console.log(` ${icon} ${name}${pad}${duration}`);\n }\n\n console.log(` ${\"─\".repeat(lineWidth)}`);\n\n const parts: string[] = [];\n if (passed > 0) parts.push(chalk.green(`${passed} passed`));\n if (failed > 0) parts.push(chalk.red(`${failed} failed`));\n if (skipped > 0) parts.push(chalk.yellow(`${skipped} skipped`));\n parts.push(chalk.gray(`(${totalDurationMs}ms)`));\n\n console.log(` ${parts.join(\" \")}`);\n\n if (flaky.length > 0) {\n console.log(`\\n ${chalk.yellow(\"Flaky hunts\")} (oscillating pass/fail):`);\n for (const entry of flaky) {\n console.log(` ${chalk.yellow(\"~\")} ${entry.hunt} ${chalk.gray(`(score ${entry.score.toFixed(2)})`)}`);\n }\n }\n\n if (clusters.length > 0) {\n console.log(`\\n ${chalk.red(\"Failure clusters\")} (shared root causes):`);\n for (const cluster of clusters) {\n console.log(` ${chalk.red(\"✗\")} ${cluster.cause} ${chalk.gray(`(${cluster.count} hunts: ${cluster.hunts.join(\", \")})`)}`);\n }\n }\n}\n\nexport function writeCiResult(\n ciRunDir: string,\n results: CiHuntResult[],\n startedAt: string,\n totalDurationMs: number,\n flaky: CiFlakyHunt[] = [],\n clusters: CiFailureCluster[] = []\n): string {\n const { passed, failed, skipped } = countCiResults(results);\n\n const ciResult: CiResult = {\n status: resolveCiStatus(results),\n startedAt,\n durationMs: totalDurationMs,\n totalHunts: results.length,\n passed,\n failed,\n skipped,\n hunts: results,\n ...(flaky.length > 0 ? { flaky } : {}),\n ...(clusters.length > 0 ? { clusters } : {})\n };\n\n fs.mkdirSync(ciRunDir, { recursive: true });\n const filePath = path.join(ciRunDir, \"ci-result.json\");\n fs.writeFileSync(filePath, JSON.stringify(ciResult, null, 2) + \"\\n\");\n return filePath;\n}\n","export type ConcurrencyResult<T> =\n | { status: \"fulfilled\"; value: T }\n | { status: \"rejected\"; reason: unknown };\n\nexport async function runWithConcurrency<T>(\n tasks: Array<() => Promise<T>>,\n concurrency: number\n): Promise<Array<ConcurrencyResult<T>>> {\n const normalizedConcurrency =\n Number.isFinite(concurrency) && concurrency > 0\n ? Math.floor(concurrency)\n : 1;\n const results: Array<ConcurrencyResult<T>> = new Array(tasks.length);\n let nextIndex = 0;\n\n async function worker(): Promise<void> {\n while (nextIndex < tasks.length) {\n const index = nextIndex;\n nextIndex += 1;\n try {\n const value = await tasks[index]();\n results[index] = { status: \"fulfilled\", value };\n } catch (reason) {\n results[index] = { status: \"rejected\", reason };\n }\n }\n }\n\n const workers = Array.from(\n { length: Math.min(normalizedConcurrency, tasks.length) },\n () => worker()\n );\n await Promise.all(workers);\n return results;\n}\n","/**\n * The minimal surface `analyzePage` needs: a way to evaluate a DOM-scraping\n * function in the page. Both a live Playwright page and a {@link SessionDriver}\n * satisfy it (the driver's `evaluate` verb), so the analyzer has no direct\n * Playwright dependency — its DOM scraping runs through the driver.\n */\nexport type DomEvaluator = {\n evaluate(pageFunction: () => unknown): Promise<unknown>;\n};\n\nexport type PageElement = {\n tag: string;\n type?: string;\n selectors: Record<string, string>;\n role?: string;\n label?: string;\n placeholder?: string;\n required: boolean;\n formGroup?: number;\n};\n\nexport type PageForm = {\n index: number;\n action?: string;\n method?: string;\n fieldCount: number;\n};\n\nexport type PageLink = {\n text: string;\n href: string;\n selector: string;\n};\n\nexport type AnalysisResult = {\n url: string;\n title: string;\n elements: PageElement[];\n forms: PageForm[];\n links: PageLink[];\n};\n\ntype RawElement = {\n tag: string;\n type?: string;\n testId?: string;\n ariaLabel?: string;\n role?: string;\n id?: string;\n name?: string;\n label?: string;\n placeholder?: string;\n required: boolean;\n formIndex: number;\n text?: string;\n href?: string;\n};\n\ntype RawForm = {\n index: number;\n action?: string;\n method?: string;\n fieldCount: number;\n};\n\ntype RawAnalysis = {\n title: string;\n url: string;\n elements: RawElement[];\n forms: RawForm[];\n};\n\nexport async function analyzePage(page: DomEvaluator): Promise<AnalysisResult> {\n const raw = await page.evaluate(() => {\n const forms = Array.from(document.querySelectorAll(\"form\"));\n const formData = forms.map((form, index) => ({\n index,\n action: form.getAttribute(\"action\") || undefined,\n method: (form.getAttribute(\"method\") || \"GET\").toUpperCase(),\n fieldCount: form.querySelectorAll(\"input, textarea, select\").length\n }));\n\n function getFormIndex(el: Element): number {\n const form = el.closest(\"form\");\n if (!form) return -1;\n return forms.indexOf(form);\n }\n\n function getLabel(el: Element): string | undefined {\n const id = el.getAttribute(\"id\");\n if (id) {\n const label = document.querySelector(`label[for=\"${id}\"]`);\n if (label) return label.textContent?.trim() || undefined;\n }\n const parentLabel = el.closest(\"label\");\n if (parentLabel) return parentLabel.textContent?.trim() || undefined;\n return undefined;\n }\n\n const selectors = \"input, textarea, select, button, [role=button], a\";\n const rawElements = Array.from(document.querySelectorAll(selectors));\n\n const elements = rawElements\n .filter((el) => {\n if (el.tagName.toLowerCase() === \"input\" && el.getAttribute(\"type\") === \"hidden\") {\n return false;\n }\n return true;\n })\n .map((el) => {\n const tag = el.tagName.toLowerCase();\n const type = el.getAttribute(\"type\") || undefined;\n const testId = el.getAttribute(\"data-testid\") || undefined;\n const ariaLabel = el.getAttribute(\"aria-label\") || undefined;\n const role = el.getAttribute(\"role\") || undefined;\n const id = el.getAttribute(\"id\") || undefined;\n const name = el.getAttribute(\"name\") || undefined;\n const label = getLabel(el);\n const placeholder = el.getAttribute(\"placeholder\") || undefined;\n const required = el.hasAttribute(\"required\");\n const formIndex = getFormIndex(el);\n const text = el.textContent?.trim() || undefined;\n const href = el.getAttribute(\"href\") || undefined;\n\n return {\n tag,\n type: type || undefined,\n testId,\n ariaLabel,\n role,\n id,\n name,\n label,\n placeholder,\n required,\n formIndex,\n text: tag === \"a\" || tag === \"button\" || role === \"button\" ? text : undefined,\n href: tag === \"a\" ? href : undefined\n };\n });\n\n return {\n title: document.title,\n url: window.location.href,\n elements,\n forms: formData\n };\n }) as RawAnalysis;\n\n const elements: PageElement[] = raw.elements\n .filter((el) => el.tag !== \"a\")\n .map((el) => {\n const selectors: Record<string, string> = {};\n if (el.testId) selectors.testId = `[data-testid=\"${el.testId}\"]`;\n if (el.ariaLabel) selectors.ariaLabel = el.ariaLabel;\n if (el.label) selectors.label = el.label;\n if (el.id) selectors.css = `#${el.id}`;\n if (el.name) selectors.name = `[name=\"${el.name}\"]`;\n if (el.placeholder) selectors.placeholder = el.placeholder;\n if (el.text) selectors.text = el.text;\n if (el.role) selectors.role = el.role;\n\n return {\n tag: el.tag,\n ...(el.type ? { type: el.type } : {}),\n selectors,\n ...(el.role ? { role: el.role } : {}),\n ...(el.label ? { label: el.label } : {}),\n ...(el.placeholder ? { placeholder: el.placeholder } : {}),\n required: el.required,\n ...(el.formIndex >= 0 ? { formGroup: el.formIndex } : {})\n };\n });\n\n const links: PageLink[] = raw.elements\n .filter((el) => el.tag === \"a\" && el.href)\n .map((el) => {\n let selector: string;\n if (el.testId) {\n selector = `[data-testid=\"${el.testId}\"]`;\n } else if (el.href) {\n selector = `a[href=\"${el.href}\"]`;\n } else {\n selector = `a`;\n }\n return {\n text: el.text || \"\",\n href: el.href!,\n selector\n };\n });\n\n const forms: PageForm[] = raw.forms;\n\n return {\n url: raw.url,\n title: raw.title,\n elements,\n forms,\n links\n };\n}\n","/**\n * PROWL-055 / ARCH-007 — macOS analyzer.\n *\n * The native analog of {@link analyzePage}: dumps a macOS app's interactive\n * elements with ranked selector candidates so hunt authors don't have to guess\n * accessibility identifiers. It talks to the same `prowl-macdriver` helper the\n * runner uses ({@link MacHelperClient}) — reading the AX tree, the window list,\n * and (read-only) the status-item menu — and shapes the result to mirror the web\n * analyzer's feel.\n *\n * Selector ranking (best → last resort), matching the driver's selector dialect\n * so the emitted selectors are directly usable in hunts:\n * id=<AXIdentifier> (best — the native `data-testid`)\n * label=\"<exact title/desc>\" (exact accessibility label)\n * role=<role>[name=\"<name>\"] (role + accessible name)\n * text=\"<substring>\" (last resort — substring match)\n *\n * Read-only: the ONLY state-changing interaction is opening the status-item menu\n * to read its items (their identifiers are gold) and immediately closing it.\n */\nimport type { MacHelperClient } from \"../browser/mac-driver.js\";\n\n/**\n * The interactive AX roles the analyzer surfaces from the window tree. Menu\n * items are collected separately via the status-item menu; windows are listed\n * as navigable surfaces. Tuned from the roles the helper's `tree`/`openMenu`\n * payloads actually expose for controls.\n */\nexport const INTERACTIVE_ROLES: ReadonlySet<string> = new Set<string>([\n \"AXButton\",\n \"AXTextField\",\n \"AXSecureTextField\",\n \"AXTextArea\",\n \"AXCheckBox\",\n \"AXRadioButton\",\n \"AXPopUpButton\",\n \"AXMenuButton\",\n \"AXLink\",\n \"AXMenuItem\",\n \"AXComboBox\",\n \"AXSlider\",\n \"AXDisclosureTriangle\"\n]);\n\n/** Default AX-tree depth requested from the helper (deeper than the run default). */\nexport const DEFAULT_ANALYZE_TREE_DEPTH = 20;\n\n/** A single element the `axInfo` snapshot describes (tree / menu / window node). */\nexport type MacAxNode = {\n role?: string;\n title?: string;\n description?: string;\n value?: string;\n identifier?: string;\n enabled?: boolean;\n children?: MacAxNode[];\n};\n\n/** An interactive element with ranked selector candidates (best first). */\nexport type MacAnalysisElement = {\n role: string;\n title?: string;\n description?: string;\n value?: string;\n identifier?: string;\n enabled?: boolean;\n /** Where the element was discovered: the app's window tree or the status menu. */\n source: \"window\" | \"menu\";\n /** Ranked selector candidates, best first. Always at least one entry. */\n selectors: string[];\n};\n\n/** A top-level window, exposed as a navigable surface with its best selector. */\nexport type MacAnalysisWindow = {\n title?: string;\n identifier?: string;\n /** Best selector candidate for the window. */\n selector: string;\n};\n\nexport type MacAnalysisResult = {\n /** Bundle id (or the app reference) of the analyzed app. */\n app: string;\n elements: MacAnalysisElement[];\n windows: MacAnalysisWindow[];\n menuItems: MacAnalysisElement[];\n};\n\nfunction str(value: unknown): string | undefined {\n if (typeof value !== \"string\") {\n return undefined;\n }\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n}\n\n/** Coerce a raw helper payload node into a typed {@link MacAxNode}. */\nfunction toNode(raw: unknown): MacAxNode {\n const node = (raw ?? {}) as Record<string, unknown>;\n const children = Array.isArray(node.children) ? node.children.map(toNode) : undefined;\n return {\n role: str(node.role),\n title: str(node.title),\n description: str(node.description),\n value: str(node.value),\n identifier: str(node.identifier),\n enabled: typeof node.enabled === \"boolean\" ? node.enabled : undefined,\n ...(children ? { children } : {})\n };\n}\n\n/** Quote a selector value; the dialect strips surrounding quotes on parse. */\nfunction quote(value: string): string {\n return `\"${value}\"`;\n}\n\n/**\n * Ranked selector candidates for an element, best first. Mirrors the driver's\n * selector dialect (`id=` > `label=` > `role=…[name=\"…\"]` > `text=`). Falls back\n * to a bare `role=<role>` so every element with a role is at least addressable.\n */\nexport function rankMacSelectors(node: MacAxNode): string[] {\n const selectors: string[] = [];\n const identifier = node.identifier;\n const exactLabel = node.title ?? node.description;\n const name = node.title ?? node.description ?? node.value;\n\n if (identifier) {\n selectors.push(`id=${identifier}`);\n }\n if (exactLabel) {\n selectors.push(`label=${quote(exactLabel)}`);\n }\n if (node.role && name) {\n selectors.push(`role=${node.role}[name=${quote(name)}]`);\n }\n if (name) {\n selectors.push(`text=${quote(name)}`);\n }\n if (selectors.length === 0 && node.role) {\n selectors.push(`role=${node.role}`);\n }\n return selectors;\n}\n\nfunction toElement(node: MacAxNode, source: \"window\" | \"menu\"): MacAnalysisElement {\n return {\n role: node.role ?? \"?\",\n ...(node.title ? { title: node.title } : {}),\n ...(node.description ? { description: node.description } : {}),\n ...(node.value ? { value: node.value } : {}),\n ...(node.identifier ? { identifier: node.identifier } : {}),\n ...(node.enabled !== undefined ? { enabled: node.enabled } : {}),\n source,\n selectors: rankMacSelectors(node)\n };\n}\n\n/** Depth-first walk of the AX tree, collecting nodes with an interactive role. */\nfunction collectInteractive(root: MacAxNode): MacAnalysisElement[] {\n const out: MacAnalysisElement[] = [];\n const visit = (node: MacAxNode): void => {\n if (node.role && INTERACTIVE_ROLES.has(node.role)) {\n out.push(toElement(node, \"window\"));\n }\n for (const child of node.children ?? []) {\n visit(child);\n }\n };\n visit(root);\n return out;\n}\n\nfunction toWindow(node: MacAxNode): MacAnalysisWindow {\n const [best] = rankMacSelectors(node);\n return {\n ...(node.title ? { title: node.title } : {}),\n ...(node.identifier ? { identifier: node.identifier } : {}),\n selector: best ?? \"role=AXWindow\"\n };\n}\n\nexport type AnalyzeMacOptions = {\n /** Bundle id / app label to report in the result. */\n app: string;\n /** AX-tree depth to request from the helper. */\n treeDepth?: number;\n /** Timeout (seconds) for opening the status-item menu. */\n menuTimeoutSeconds?: number;\n};\n\n/**\n * Analyze an already-launched macOS app through `client`, returning its\n * interactive elements, windows, and status-menu items with ranked selectors.\n *\n * The caller owns the session lifecycle (launch + guardrails + teardown); this\n * function is read-only apart from opening and immediately closing the\n * status-item menu to read its contents.\n */\nexport async function analyzeMacApp(\n client: MacHelperClient,\n options: AnalyzeMacOptions\n): Promise<MacAnalysisResult> {\n const depth = options.treeDepth ?? DEFAULT_ANALYZE_TREE_DEPTH;\n\n const treeResult = await client.request(\"tree\", { depth });\n const elements = collectInteractive(toNode(treeResult.tree));\n\n const windowsResult = await client.request(\"windows\");\n const rawWindows = Array.isArray(windowsResult.windows) ? windowsResult.windows : [];\n const windows = rawWindows.map((raw) => toWindow(toNode(raw)));\n\n const menuItems = await readStatusMenu(client, options.menuTimeoutSeconds);\n\n return { app: options.app, elements, windows, menuItems };\n}\n\n/**\n * Read the status-item menu contents (read-only): only when a status item\n * exists, open it, snapshot its items, then always close it. Any failure\n * degrades to an empty menu list rather than aborting the whole analysis.\n */\nasync function readStatusMenu(\n client: MacHelperClient,\n menuTimeoutSeconds?: number\n): Promise<MacAnalysisElement[]> {\n let statusItems: unknown[];\n try {\n const status = await client.request(\"statusItems\");\n statusItems = Array.isArray(status.items) ? status.items : [];\n } catch {\n return [];\n }\n if (statusItems.length === 0) {\n return [];\n }\n\n const params = menuTimeoutSeconds !== undefined ? { timeout: menuTimeoutSeconds } : {};\n try {\n const menu = await client.request(\"openMenu\", params);\n const rawItems = Array.isArray(menu.items) ? menu.items : [];\n return rawItems\n .map((raw) => toNode(raw))\n .filter((node) => node.role !== \"AXMenuItem\" || node.title || node.identifier || node.description)\n .map((node) => toElement(node, \"menu\"));\n } catch {\n return [];\n } finally {\n await client.request(\"closeMenu\").catch(() => undefined);\n }\n}\n","/**\n * PROWL-061 — Android analyzer.\n *\n * The Android analog of {@link analyzeMacApp}: dumps a running Android app's\n * interactive elements with ranked selector candidates so hunt authors don't have\n * to guess resource-ids. It reads the on-device UI hierarchy through the same\n * uiautomator2 agent the runner uses (`GET /source`, the standard `uiautomator\n * dump` XML) and shapes the result to mirror the macOS analyzer's feel.\n *\n * Selector ranking (best → last resort), matching the Android driver's selector\n * dialect so the emitted selectors are directly usable in hunts:\n * id=<resource-id> (best — the native `data-testid`; already\n * package-qualified in the dump, e.g.\n * `com.android.settings:id/title`)\n * label=\"<content-desc>\" (exact content-description)\n * role=<class>[name=\"<text>\"] (widget class + visible text, substring)\n * text=\"<text>\" (last resort — visible-text substring)\n *\n * Read-only: this never taps, types, or otherwise mutates the app — it only reads\n * the page source.\n */\nimport {\n ANDROID_MATCH_DIALECT,\n matchNativeTree,\n parseNativeSelector,\n rankNativeSelectors,\n type NativeMatchOptions,\n type NativeNode\n} from \"../selector/native.js\";\nimport { parseXml, type XmlElement } from \"./xml.js\";\n\n/**\n * Widget classes treated as interactive on their own (in addition to any node\n * flagged clickable / long-clickable / checkable / scrollable). Tuned to the\n * common `android.widget` / AndroidX input and control classes; text/containers\n * without an interactive flag are surfaced only when clickable.\n */\nexport const ANDROID_INTERACTIVE_CLASSES: ReadonlySet<string> = new Set<string>([\n \"android.widget.Button\",\n \"android.widget.ImageButton\",\n \"android.widget.EditText\",\n \"android.widget.CheckBox\",\n \"android.widget.RadioButton\",\n \"android.widget.Switch\",\n \"android.widget.ToggleButton\",\n \"android.widget.Spinner\",\n \"android.widget.SeekBar\",\n \"android.widget.RatingBar\",\n \"android.widget.CompoundButton\",\n \"android.widget.AutoCompleteTextView\",\n \"android.widget.MultiAutoCompleteTextView\",\n \"android.widget.CheckedTextView\",\n \"androidx.appcompat.widget.SwitchCompat\",\n \"androidx.appcompat.widget.AppCompatButton\",\n \"androidx.appcompat.widget.AppCompatEditText\"\n]);\n\n/** A single node in the uiautomator hierarchy (`<node>` element attributes). */\nexport type AndroidUiNode = {\n className?: string;\n resourceId?: string;\n contentDesc?: string;\n text?: string;\n package?: string;\n clickable?: boolean;\n longClickable?: boolean;\n checkable?: boolean;\n checked?: boolean;\n scrollable?: boolean;\n focusable?: boolean;\n /** Whether the node currently holds input focus (`:focus`; from the dump's `focused` attr). */\n focused?: boolean;\n enabled?: boolean;\n children: AndroidUiNode[];\n};\n\n/** An interactive element with ranked selector candidates (best first). */\nexport type AndroidAnalysisElement = {\n className: string;\n resourceId?: string;\n contentDesc?: string;\n text?: string;\n clickable?: boolean;\n checkable?: boolean;\n scrollable?: boolean;\n enabled?: boolean;\n /** Ranked selector candidates, best first. Always at least one entry. */\n selectors: string[];\n};\n\nexport type AndroidAnalysisResult = {\n /** Package name of the analyzed app. */\n app: string;\n elements: AndroidAnalysisElement[];\n};\n\nfunction str(value: string | undefined): string | undefined {\n if (value === undefined) {\n return undefined;\n }\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n}\n\nfunction bool(value: string | undefined): boolean | undefined {\n if (value === undefined) {\n return undefined;\n }\n return value === \"true\";\n}\n\n/** Coerce a raw `<node>` {@link XmlElement} into a typed {@link AndroidUiNode}. */\nfunction toAndroidNode(element: XmlElement): AndroidUiNode {\n const a = element.attrs;\n return {\n className: str(a[\"class\"]),\n resourceId: str(a[\"resource-id\"]),\n contentDesc: str(a[\"content-desc\"]),\n text: str(a.text),\n package: str(a.package),\n clickable: bool(a.clickable),\n longClickable: bool(a[\"long-clickable\"]),\n checkable: bool(a.checkable),\n checked: bool(a.checked),\n scrollable: bool(a.scrollable),\n focusable: bool(a.focusable),\n focused: bool(a.focused),\n enabled: bool(a.enabled),\n children: element.children.map(toAndroidNode)\n };\n}\n\n/** Parse a uiautomator2 `/source` XML dump into a tree of {@link AndroidUiNode}. */\nexport function parseAndroidHierarchy(xml: string): AndroidUiNode | null {\n const root = parseXml(xml);\n return root ? toAndroidNode(root) : null;\n}\n\n/** Whether a node is worth surfacing as an interactive element. */\nexport function isAndroidInteractive(node: AndroidUiNode): boolean {\n if (node.clickable || node.longClickable || node.checkable || node.scrollable) {\n return true;\n }\n return node.className !== undefined && ANDROID_INTERACTIVE_CLASSES.has(node.className);\n}\n\n/**\n * Ranked selector candidates for a node, best first, via the shared native\n * selector engine (PROWL-060). The Android attribute mapping is applied here —\n * `id=`←resource-id (already package-qualified in the dump, emitted verbatim),\n * `label=`←content-desc, `role=`←class, name←visible text — then\n * {@link rankNativeSelectors} imposes the shared `id=` > `label=` >\n * `role=…[name]` > `text=` order (with a bare `role=` fallback).\n */\nexport function rankAndroidSelectors(node: AndroidUiNode): string[] {\n return rankNativeSelectors({\n id: node.resourceId,\n label: node.contentDesc,\n role: node.className,\n name: node.text\n });\n}\n\n/**\n * Project an {@link AndroidUiNode} into the neutral {@link NativeNode} the shared\n * matcher compares against: `id`←resource-id, `label`←content-desc, `role`←class,\n * `text=` substring source ← visible text.\n */\nexport function androidNodeToNative(node: AndroidUiNode): NativeNode {\n return {\n ...(node.resourceId !== undefined ? { id: node.resourceId } : {}),\n ...(node.contentDesc !== undefined ? { label: node.contentDesc } : {}),\n ...(node.className !== undefined ? { role: node.className } : {}),\n textValues: node.text !== undefined ? [node.text] : [],\n ...(node.focused !== undefined ? { focused: node.focused } : {})\n };\n}\n\n/**\n * Host-side \"snapshot-then-match\": parse a uiautomator2 `/source` dump and return\n * every node the Prowl `selector` resolves to, in document order, using Android's\n * shared dialect semantics. Read-only and device-free. Exposed for tooling and a\n * future runner/macdriver migration; the runner still matches on-device today.\n */\nexport function matchAndroidSelector(\n xml: string,\n selector: string,\n options: NativeMatchOptions = {}\n): AndroidUiNode[] {\n const parsedSelector = parseNativeSelector(selector);\n const root = parseAndroidHierarchy(xml);\n if (!root) {\n return [];\n }\n return matchNativeTree(\n ANDROID_MATCH_DIALECT,\n parsedSelector,\n root,\n androidNodeToNative,\n (node) => node.children,\n options\n );\n}\n\nfunction toElement(node: AndroidUiNode): AndroidAnalysisElement {\n return {\n className: node.className ?? \"?\",\n ...(node.resourceId ? { resourceId: node.resourceId } : {}),\n ...(node.contentDesc ? { contentDesc: node.contentDesc } : {}),\n ...(node.text ? { text: node.text } : {}),\n ...(node.clickable !== undefined ? { clickable: node.clickable } : {}),\n ...(node.checkable !== undefined ? { checkable: node.checkable } : {}),\n ...(node.scrollable !== undefined ? { scrollable: node.scrollable } : {}),\n ...(node.enabled !== undefined ? { enabled: node.enabled } : {}),\n selectors: rankAndroidSelectors(node)\n };\n}\n\n/** Depth-first walk of the hierarchy, collecting interactive nodes in tree order. */\nfunction collectInteractive(root: AndroidUiNode): AndroidAnalysisElement[] {\n const out: AndroidAnalysisElement[] = [];\n const visit = (node: AndroidUiNode): void => {\n if (isAndroidInteractive(node)) {\n out.push(toElement(node));\n }\n for (const child of node.children) {\n visit(child);\n }\n };\n visit(root);\n return out;\n}\n\n/** The minimal transport the Android analyzer needs: read the UI hierarchy XML. */\nexport interface AndroidUiSource {\n /** Return the current UI hierarchy as uiautomator2 `/source` XML. */\n source(): Promise<string>;\n}\n\nexport type AnalyzeAndroidOptions = {\n /** Package name to report in the result. */\n app: string;\n};\n\n/**\n * Analyze an already-launched Android app through `client`, returning its\n * interactive elements with ranked selectors.\n *\n * The caller owns the session lifecycle (launch + guardrails + teardown); this\n * function is strictly read-only — it only reads the page source.\n */\nexport async function analyzeAndroidApp(\n client: AndroidUiSource,\n options: AnalyzeAndroidOptions\n): Promise<AndroidAnalysisResult> {\n const xml = await client.source();\n const root = parseAndroidHierarchy(xml);\n const elements = root ? collectInteractive(root) : [];\n return { app: options.app, elements };\n}\n","/**\n * PROWL-061 — iOS analyzer.\n *\n * The iOS analog of {@link analyzeMacApp}: dumps a running iOS app's interactive\n * elements (and its windows) with ranked selector candidates so hunt authors\n * don't have to guess accessibility identifiers. It reads the on-simulator UI\n * hierarchy through the same WebDriverAgent the runner uses (`GET /source`, WDA's\n * XML page source of `<XCUIElementType…>` elements) and shapes the result to\n * mirror the macOS analyzer's feel.\n *\n * Selector ranking (best → last resort), matching the iOS driver's selector\n * dialect so the emitted selectors are directly usable in hunts:\n * id=<accessibility id> (best — the native `data-testid`)\n * label=\"<label>\" (exact accessibility label)\n * role=<Type>[name=\"<text>\"] (element type + visible text, substring)\n * text=\"<text>\" (last resort — label/value substring)\n *\n * Identifier caveat: WDA's page source exposes a single `name` attribute that is\n * the element's `accessibilityIdentifier` when one is set, otherwise its label.\n * We therefore rank `id=` only when `name` differs from `label`, so the analyzer\n * does not recommend label-shaped ids. Host-side matching still follows WDA's\n * `accessibility id` strategy and resolves `id=` against `name`.\n *\n * Read-only: this never taps, types, or otherwise mutates the app — it only reads\n * the page source.\n */\nimport {\n IOS_MATCH_DIALECT,\n matchNativeTree,\n parseNativeSelector,\n rankNativeSelectors,\n shortIosType,\n type NativeNode\n} from \"../selector/native.js\";\nimport { parseXml, type XmlElement } from \"./xml.js\";\n\n// Re-export the iOS type-shorthand helper from the shared native selector engine\n// (PROWL-060), its single source of truth, so existing importers of\n// `shortIosType` from this module keep working.\nexport { shortIosType } from \"../selector/native.js\";\n\n/**\n * Element types treated as interactive. Tuned to the common `XCUIElementType…`\n * controls; static text and layout containers are intentionally excluded.\n */\nexport const IOS_INTERACTIVE_TYPES: ReadonlySet<string> = new Set<string>([\n \"XCUIElementTypeButton\",\n \"XCUIElementTypeCell\",\n \"XCUIElementTypeTextField\",\n \"XCUIElementTypeSecureTextField\",\n \"XCUIElementTypeSearchField\",\n \"XCUIElementTypeSwitch\",\n \"XCUIElementTypeToggle\",\n \"XCUIElementTypeLink\",\n \"XCUIElementTypeMenuItem\",\n \"XCUIElementTypeSlider\",\n \"XCUIElementTypeStepper\",\n \"XCUIElementTypeTextView\",\n \"XCUIElementTypePickerWheel\",\n \"XCUIElementTypeTab\",\n \"XCUIElementTypeSegmentedControl\",\n \"XCUIElementTypeCheckBox\",\n \"XCUIElementTypeRadioButton\",\n \"XCUIElementTypeKey\"\n]);\n\n/** The element type that represents a navigable window/screen surface. */\nexport const IOS_WINDOW_TYPE = \"XCUIElementTypeWindow\";\n\n/** A single node in the WDA hierarchy (`<XCUIElementType…>` element attributes). */\nexport type IosUiNode = {\n type?: string;\n name?: string;\n label?: string;\n value?: string;\n enabled?: boolean;\n visible?: boolean;\n children: IosUiNode[];\n};\n\n/** An interactive element with ranked selector candidates (best first). */\nexport type IosAnalysisElement = {\n type: string;\n name?: string;\n label?: string;\n value?: string;\n enabled?: boolean;\n visible?: boolean;\n /** Ranked selector candidates, best first. Always at least one entry. */\n selectors: string[];\n};\n\n/** A top-level window, exposed as a navigable surface with its best selector. */\nexport type IosAnalysisWindow = {\n name?: string;\n label?: string;\n /** Best selector candidate for the window. */\n selector: string;\n};\n\nexport type IosAnalysisResult = {\n /** Bundle id of the analyzed app. */\n app: string;\n elements: IosAnalysisElement[];\n windows: IosAnalysisWindow[];\n};\n\nfunction str(value: string | undefined): string | undefined {\n if (value === undefined) {\n return undefined;\n }\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n}\n\nfunction bool(value: string | undefined): boolean | undefined {\n if (value === undefined) {\n return undefined;\n }\n return value === \"true\" || value === \"1\";\n}\n\n/** Coerce a raw {@link XmlElement} into a typed {@link IosUiNode}. */\nfunction toIosNode(element: XmlElement): IosUiNode {\n const a = element.attrs;\n // WDA carries the element type both as the tag name and a `type` attribute;\n // prefer the attribute and fall back to the tag.\n return {\n type: str(a.type) ?? str(element.tag),\n name: str(a.name),\n label: str(a.label),\n value: str(a.value),\n enabled: bool(a.enabled),\n visible: bool(a.visible),\n children: element.children.map(toIosNode)\n };\n}\n\n/** Parse a WDA `/source` XML dump into a tree of {@link IosUiNode}. */\nexport function parseIosHierarchy(xml: string): IosUiNode | null {\n const root = parseXml(xml);\n return root ? toIosNode(root) : null;\n}\n\n/** Whether `name` looks like a distinct accessibility identifier (not the label). */\nfunction hasAccessibilityId(node: IosUiNode): boolean {\n return node.name !== undefined && node.name !== node.label;\n}\n\n/**\n * Ranked selector candidates for a node, best first, via the shared native\n * selector engine (PROWL-060). The iOS attribute mapping is applied here — `id=`←\n * accessibility id (the `name` attribute, but only when it differs from the label;\n * see the caveat above), `label=`←label, `role=`←the short element type, name←\n * `label ?? value` — then {@link rankNativeSelectors} imposes the shared `id=` >\n * `label=` > `role=…[name]` > `text=` order (with a bare `role=` fallback).\n */\nexport function rankIosSelectors(node: IosUiNode): string[] {\n return rankNativeSelectors({\n id: hasAccessibilityId(node) ? node.name : undefined,\n label: node.label,\n role: node.type ? shortIosType(node.type) : undefined,\n name: node.label ?? node.value\n });\n}\n\n/**\n * Project an {@link IosUiNode} into the neutral {@link NativeNode} the shared\n * matcher compares against: `id`←name (matching WDA's `accessibility id`\n * strategy even when it equals the label), `label`←label, `role`←the full\n * element type (the dialect normalizes a `Button` shorthand against it), and both\n * label and value as `text=` substring sources.\n */\nexport function iosNodeToNative(node: IosUiNode): NativeNode {\n const textValues: string[] = [];\n if (node.label !== undefined) {\n textValues.push(node.label);\n }\n if (node.value !== undefined) {\n textValues.push(node.value);\n }\n return {\n ...(node.name !== undefined ? { id: node.name } : {}),\n ...(node.label !== undefined ? { label: node.label } : {}),\n ...(node.type !== undefined ? { role: node.type } : {}),\n textValues\n };\n}\n\n/**\n * Host-side \"snapshot-then-match\": parse a WebDriverAgent `/source` dump and\n * return every node the Prowl `selector` resolves to, in document order, using\n * iOS's shared dialect semantics. Read-only and device-free. Exposed for tooling\n * and a future runner/macdriver migration; the runner still matches on-device.\n */\nexport function matchIosSelector(xml: string, selector: string): IosUiNode[] {\n const parsedSelector = parseNativeSelector(selector);\n const root = parseIosHierarchy(xml);\n if (!root) {\n return [];\n }\n return matchNativeTree(\n IOS_MATCH_DIALECT,\n parsedSelector,\n root,\n iosNodeToNative,\n (node) => node.children\n );\n}\n\nfunction toElement(node: IosUiNode): IosAnalysisElement {\n return {\n type: node.type ?? \"?\",\n ...(node.name ? { name: node.name } : {}),\n ...(node.label ? { label: node.label } : {}),\n ...(node.value ? { value: node.value } : {}),\n ...(node.enabled !== undefined ? { enabled: node.enabled } : {}),\n ...(node.visible !== undefined ? { visible: node.visible } : {}),\n selectors: rankIosSelectors(node)\n };\n}\n\nfunction toWindow(node: IosUiNode): IosAnalysisWindow {\n const [best] = rankIosSelectors(node);\n return {\n ...(node.name ? { name: node.name } : {}),\n ...(node.label ? { label: node.label } : {}),\n selector: best ?? `role=${shortIosType(IOS_WINDOW_TYPE)}`\n };\n}\n\n/** Whether a node is worth surfacing as an interactive element. */\nexport function isIosInteractive(node: IosUiNode): boolean {\n return node.type !== undefined && IOS_INTERACTIVE_TYPES.has(node.type);\n}\n\n/** Depth-first walk collecting interactive elements and windows in tree order. */\nfunction collect(root: IosUiNode): { elements: IosAnalysisElement[]; windows: IosAnalysisWindow[] } {\n const elements: IosAnalysisElement[] = [];\n const windows: IosAnalysisWindow[] = [];\n const visit = (node: IosUiNode): void => {\n if (node.type === IOS_WINDOW_TYPE) {\n windows.push(toWindow(node));\n }\n if (isIosInteractive(node)) {\n elements.push(toElement(node));\n }\n for (const child of node.children) {\n visit(child);\n }\n };\n visit(root);\n return { elements, windows };\n}\n\n/** The minimal transport the iOS analyzer needs: read the UI hierarchy XML. */\nexport interface IosUiSource {\n /** Return the current UI hierarchy as WebDriverAgent `/source` XML. */\n source(): Promise<string>;\n}\n\nexport type AnalyzeIosOptions = {\n /** Bundle id to report in the result. */\n app: string;\n};\n\n/**\n * Analyze an already-launched iOS app through `client`, returning its interactive\n * elements and windows with ranked selectors.\n *\n * The caller owns the session lifecycle (launch + guardrails + teardown); this\n * function is strictly read-only — it only reads the page source.\n */\nexport async function analyzeIosApp(\n client: IosUiSource,\n options: AnalyzeIosOptions\n): Promise<IosAnalysisResult> {\n const xml = await client.source();\n const root = parseIosHierarchy(xml);\n if (!root) {\n return { app: options.app, elements: [], windows: [] };\n }\n const { elements, windows } = collect(root);\n return { app: options.app, elements, windows };\n}\n","import yaml from \"yaml\";\nimport { launchBrowser, closeBrowser, createPlaywrightDriver } from \"../browser/controller.js\";\nimport { parseBrowserEngine } from \"../browser/engines.js\";\nimport type { AnalysisResult } from \"../analyzer/index.js\";\nimport { analyzePage } from \"../analyzer/index.js\";\nimport { buildGenerationPrompt, extractYamlFromResponse } from \"./prompt.js\";\nimport { generateWithAi, resolveAiConfig } from \"./ai.js\";\nimport type { AiConfig } from \"./ai.js\";\nimport { resolveViewport } from \"../config/loader.js\";\nimport { huntSchema } from \"../config/schema.js\";\n\nexport type GenerateOptions = {\n url?: string;\n analysis?: AnalysisResult;\n intent: string;\n browser?: string;\n viewport?: string;\n aiConfig?: AiConfig;\n};\n\nfunction parseViewportFlag(value: string): string | { width: number; height: number } {\n const match = /^(\\d+)x(\\d+)$/i.exec(value);\n if (match) {\n return { width: Number(match[1]), height: Number(match[2]) };\n }\n return value;\n}\n\nexport async function generateHunt(options: GenerateOptions): Promise<string> {\n let analysis = options.analysis;\n\n if (!analysis && options.url) {\n const engine = parseBrowserEngine(options.browser);\n const viewport = options.viewport\n ? resolveViewport(parseViewportFlag(options.viewport))\n : resolveViewport(undefined);\n const session = await launchBrowser({\n headless: true,\n slowMo: 0,\n timeout: 30000,\n trace: false,\n recordHar: false,\n runDir: process.cwd(),\n engine,\n viewport\n });\n const driver = createPlaywrightDriver(session.page);\n try {\n await driver.goto(options.url, { waitUntil: \"networkidle\" });\n analysis = await analyzePage(driver);\n } finally {\n await closeBrowser(session);\n }\n }\n\n if (!analysis) {\n throw new Error(\"Either --url or piped analysis JSON is required\");\n }\n\n const config = options.aiConfig ?? resolveAiConfig();\n const prompt = buildGenerationPrompt(analysis, options.intent);\n const response = await generateWithAi(prompt, config);\n const yamlStr = extractYamlFromResponse(response);\n\n // Validate generated YAML\n const parsed = yaml.parse(yamlStr);\n huntSchema.parse(parsed);\n\n return yamlStr;\n}\n","import { SUPPORTED_BROWSER_ENGINES, type BrowserEngine } from \"../types/index.js\";\n\nexport function formatSupportedBrowserEngines(): string {\n return SUPPORTED_BROWSER_ENGINES.join(\", \");\n}\n\nexport function parseBrowserEngine(value: string | undefined, fallback: BrowserEngine = \"chromium\"): BrowserEngine {\n if (value === undefined || value.length === 0) {\n return fallback;\n }\n if ((SUPPORTED_BROWSER_ENGINES as readonly string[]).includes(value)) {\n return value as BrowserEngine;\n }\n throw new Error(`Unsupported browser engine \"${value}\". Use ${formatSupportedBrowserEngines()}.`);\n}\n","import type { AnalysisResult } from \"../analyzer/index.js\";\n\nconst STEP_REFERENCE = `\n## Prowl Step Types\n\n### Navigation & Waiting\n- navigate: \"/path\" — navigate to URL (relative to target)\n- wait: \"Text\" — wait for text to appear\n- wait: { for: \"Text\", timeout: 5000 } — with timeout\n- waitForSelector: { selector: \"#el\", timeout: 5000 }\n- waitForUrl: { value: \"/path\", timeout: 5000 }\n- waitForNetworkIdle: { timeout: 5000 }\n\n### Interaction\n- click: \"Button Text\" — click by text (tries role=button first)\n- click: { selector: \"[data-testid=btn]\" } — click by selector\n- fill: { \"Label\": \"value\" } — fill by label/placeholder\n- fill: { selector: \"#input\", value: \"text\" } — fill by selector\n- type: \"text\" — type into focused element\n- press: { selector: \"#input\", key: \"Enter\" }\n- hover: { selector: \"#menu\" }\n- selectOption: { selector: \"select\", value: \"option\" }\n- select: { \"Label\": \"value\" } — select by label\n- setInputFiles: { selector: \"#file\", files: \"path.png\" }\n- onDialog: { action: \"accept\" } — handle browser dialogs\n\n### Assertions\n- assert: { visible: \"Text\" }\n- assert: { notVisible: \"Error\" }\n- assert: { urlIncludes: \"/dashboard\" }\n- assert: { urlEquals: \"https://...\" }\n\n### Scrolling & Screenshots\n- scroll: { direction: \"down\", amount: 500 }\n- scrollTo: { selector: \"#section\" }\n- screenshot: { name: \"step-name\" }\n\n### Script Execution\n- evalScript: \"document.title\" — evaluate JS expression\n- evalScript: { expression: \"expr\", as: \"VAR\" } — capture to variable\n- runScript: { file: \"scripts/setup.js\" }\n\n### Visual Regression\n- assertScreenshot: { name: \"baseline-name\", threshold: 0.1 }\n- assertWithAI: \"The login form should show email and password fields\" — AI checks the screenshot against the claim\n\n### Control Flow\n- if: { visible: \".banner\", then: [steps...] }\n- repeat: { times: 3, steps: [steps...] }\n- repeat: { while: { visible: \".more\" }, maxIterations: 10, steps: [steps...] }\n- runHunt: \"other-hunt\" — run another hunt file\n- mockRoute: { url: \"**/api/data\", response: { status: 200, body: \"{}\" } }\n- unmockRoute: { url: \"**/api/data\" }\n`.trim();\n\nexport function buildGenerationPrompt(analysis: AnalysisResult, intent: string): string {\n return `You are a QA test generator for Prowl. Generate a YAML hunt file that tests the described intent using the page analysis data below.\n\n${STEP_REFERENCE}\n\n## Page Analysis\n\\`\\`\\`json\n${JSON.stringify(analysis, null, 2)}\n\\`\\`\\`\n\n## Test Intent\n${intent}\n\n## Instructions\n1. Output ONLY a valid Prowl YAML hunt between \\`\\`\\`yaml fences\n2. Use shorthand syntax when possible (click: \"Text\", fill: { \"Label\": \"value\" })\n3. Prefer stable selectors: data-testid > aria-label > text > CSS selectors\n4. Include assertions to verify expected outcomes\n5. Add a descriptive name and description\n6. Keep steps focused and minimal — test exactly what the intent describes\n\n\\`\\`\\`yaml\n`;\n}\n\nexport function extractYamlFromResponse(response: string): string {\n const fenceMatch = response.match(/```ya?ml\\n?([\\s\\S]*?)```/);\n if (fenceMatch) {\n return fenceMatch[1].trim();\n }\n return response.trim();\n}\n","/**\n * PROWL-074 / PROWL-052 — download, verify, and install the prebuilt, signed\n * `prowl-macdriver` helper so the macOS target works from a plain\n * `npm i -g prowl-tools` with no Xcode or Swift toolchain.\n *\n * `prowl macdriver install` fetches the pinned version's universal binary from\n * GitHub Releases (raw `fetch`, redirects followed — no SDK), checksum-verifies\n * it against the released `.sha256` sidecar, validates the archive contents,\n * verifies the code signature / Gatekeeper policy, and installs it to\n * `~/.prowl/macdriver/<version>/prowl-macdriver` (mode 0755).\n *\n * Every side effect (network, unzip, signature check, home dir) is injectable so\n * the flow is unit-testable without a real download or a signed artifact.\n */\nimport { execFile } from \"node:child_process\";\nimport { createHash } from \"node:crypto\";\nimport fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { promisify } from \"node:util\";\nimport { HELPER_BINARY, resolveHelperBinary } from \"./mac-helper.js\";\nimport {\n MACDRIVER_VERSION,\n MACDRIVER_SIGNING_AUTHORITY_PREFIX,\n MACDRIVER_SIGNING_IDENTIFIER,\n macdriverAssetName,\n macdriverAssetUrl,\n macdriverChecksumName,\n macdriverInstallRoot,\n macdriverInstalledBinary,\n macdriverReleaseTag,\n macdriverVersionDir,\n validateMacdriverVersion\n} from \"./macdriver-release.js\";\n\nconst execFileAsync = promisify(execFile);\n\n/** Minimal shape of the `fetch` responses this module consumes. */\nexport interface FetchResponseLike {\n ok: boolean;\n status: number;\n arrayBuffer(): Promise<ArrayBuffer>;\n text(): Promise<string>;\n}\n\n/** A `fetch`-like function; the real global `fetch` satisfies it. */\nexport type FetchLike = (\n url: string,\n init?: { redirect?: \"follow\" | \"error\" | \"manual\" }\n) => Promise<FetchResponseLike>;\n\n/** Extracts the binary out of a downloaded zip into `destDir`. */\nexport type Extractor = (zipPath: string, destDir: string) => Promise<void>;\n\n/** Lists the raw path entries in a downloaded zip before extraction. */\nexport type ArchiveLister = (zipPath: string) => Promise<string[]>;\n\n/** Verifies the code signature of an installed binary; throws if invalid. */\nexport type SignatureVerifier = (binaryPath: string) => Promise<void>;\n\nexport interface CommandResult {\n stdout?: string | Buffer;\n stderr?: string | Buffer;\n}\n\nexport type CommandRunner = (file: string, args: string[]) => Promise<CommandResult>;\n\n/** Parse the hex digest out of a `shasum`-style `.sha256` file. */\nexport function parseChecksumFile(text: string): string {\n const token = text.trim().split(/\\s+/, 1)[0] ?? \"\";\n const digest = token.toLowerCase();\n if (!/^[0-9a-f]{64}$/.test(digest)) {\n throw new Error(`Malformed .sha256 checksum file (expected a 64-char hex digest, got: ${text.trim().slice(0, 80)})`);\n }\n return digest;\n}\n\n/** SHA-256 of a buffer, lowercase hex. */\nexport function sha256Hex(bytes: Buffer): string {\n return createHash(\"sha256\").update(bytes).digest(\"hex\");\n}\n\nconst commandRunner: CommandRunner = async (file, args) => execFileAsync(file, args) as Promise<CommandResult>;\n\nfunction commandOutput(result: CommandResult): string {\n return [result.stdout, result.stderr]\n .filter((value): value is string | Buffer => value !== undefined)\n .map((value) => value.toString())\n .join(\"\\n\")\n .trim();\n}\n\nfunction commandErrorDetail(error: unknown): string {\n const err = error as NodeJS.ErrnoException & { stdout?: string | Buffer; stderr?: string | Buffer };\n return [err.stderr, err.stdout, err.message]\n .filter((value): value is string | Buffer => value !== undefined && value !== \"\")\n .map((value) => value.toString())\n .join(\"\\n\")\n .trim();\n}\n\nasync function runRequiredCommand(\n run: CommandRunner,\n binaryPath: string,\n command: string,\n args: string[],\n label: string\n): Promise<CommandResult> {\n try {\n return await run(command, args);\n } catch (error) {\n const detail = commandErrorDetail(error);\n throw new Error(`${label} failed for ${binaryPath}${detail ? `: ${detail}` : \"\"}`);\n }\n}\n\n/** Default archive lister: `zipinfo -1`, used before any extraction happens. */\nexport const zipinfoArchiveLister: ArchiveLister = async (zipPath) => {\n try {\n const { stdout } = await execFileAsync(\"zipinfo\", [\"-1\", zipPath]);\n return stdout\n .toString()\n .split(/\\r?\\n/)\n .filter((entry) => entry.length > 0);\n } catch (error) {\n const detail = commandErrorDetail(error);\n throw new Error(`Failed to inspect release archive ${zipPath} with zipinfo${detail ? `: ${detail}` : \"\"}`);\n }\n};\n\nfunction normalizeArchiveEntryName(entry: string): string {\n if (entry.length === 0 || entry !== entry.trim() || entry.includes(\"\\0\") || entry.includes(\"\\\\\")) {\n throw new Error(`Unsafe path in prowl-macdriver release archive: ${JSON.stringify(entry)}`);\n }\n if (entry.endsWith(\"/\")) {\n throw new Error(`Unexpected directory in prowl-macdriver release archive: ${entry}`);\n }\n if (path.posix.isAbsolute(entry)) {\n throw new Error(`Unsafe absolute path in prowl-macdriver release archive: ${entry}`);\n }\n const parts = entry.split(\"/\");\n if (parts.some((part) => part === \"\" || part === \".\" || part === \"..\")) {\n throw new Error(`Unsafe path in prowl-macdriver release archive: ${entry}`);\n }\n return entry;\n}\n\n/** Validate the zip member list before extraction. */\nexport function validateMacdriverArchiveEntries(entries: string[]): void {\n const normalized = entries.map(normalizeArchiveEntryName);\n if (normalized.length !== 1 || normalized[0] !== HELPER_BINARY) {\n const shown = normalized.length > 0 ? normalized.join(\", \") : \"(empty archive)\";\n throw new Error(\n `Unexpected prowl-macdriver release archive contents: ${shown}. ` +\n `Expected exactly \"${HELPER_BINARY}\" at the archive root.`\n );\n }\n}\n\n/** Default extractor: Apple's `ditto`, which preserves the code signature. */\nexport const dittoExtractor: Extractor = async (zipPath, destDir) => {\n await execFileAsync(\"ditto\", [\"-x\", \"-k\", zipPath, destDir]);\n};\n\nexport interface CodesignDetails {\n identifier: string | null;\n authorities: string[];\n teamIdentifier: string | null;\n}\n\nexport function parseCodesignDetails(text: string): CodesignDetails {\n const details: CodesignDetails = { identifier: null, authorities: [], teamIdentifier: null };\n for (const line of text.split(/\\r?\\n/)) {\n const trimmed = line.trim();\n if (trimmed.startsWith(\"Identifier=\")) {\n details.identifier = trimmed.slice(\"Identifier=\".length);\n } else if (trimmed.startsWith(\"Authority=\")) {\n details.authorities.push(trimmed.slice(\"Authority=\".length));\n } else if (trimmed.startsWith(\"TeamIdentifier=\")) {\n details.teamIdentifier = trimmed.slice(\"TeamIdentifier=\".length);\n }\n }\n return details;\n}\n\nfunction validateCodesignDetails(details: CodesignDetails, binaryPath: string): void {\n if (details.identifier !== MACDRIVER_SIGNING_IDENTIFIER) {\n throw new Error(\n `codesign verification failed for ${binaryPath}: expected identifier ` +\n `\"${MACDRIVER_SIGNING_IDENTIFIER}\", got \"${details.identifier ?? \"missing\"}\"`\n );\n }\n\n const developerIdAuthority = details.authorities.find((authority) =>\n authority.startsWith(`${MACDRIVER_SIGNING_AUTHORITY_PREFIX} (`)\n );\n const authorityTeamId = developerIdAuthority?.match(/\\(([A-Z0-9]{10})\\)$/)?.[1] ?? null;\n if (!developerIdAuthority || !authorityTeamId) {\n const shown = details.authorities.length > 0 ? details.authorities.join(\" / \") : \"missing\";\n throw new Error(\n `codesign verification failed for ${binaryPath}: expected ${MACDRIVER_SIGNING_AUTHORITY_PREFIX} signer, got ${shown}`\n );\n }\n\n if (!details.teamIdentifier) {\n throw new Error(`codesign verification failed for ${binaryPath}: missing TeamIdentifier`);\n }\n if (details.teamIdentifier !== authorityTeamId) {\n throw new Error(\n `codesign verification failed for ${binaryPath}: TeamIdentifier ${details.teamIdentifier} ` +\n `does not match Developer ID authority team ${authorityTeamId}`\n );\n }\n}\n\nexport async function verifyMacdriverSignature(\n binaryPath: string,\n run: CommandRunner = commandRunner\n): Promise<void> {\n await runRequiredCommand(run, binaryPath, \"codesign\", [\"--verify\", \"--strict\", binaryPath], \"codesign verification\");\n const display = await runRequiredCommand(\n run,\n binaryPath,\n \"codesign\",\n [\"--display\", \"--verbose=4\", binaryPath],\n \"codesign detail inspection\"\n );\n validateCodesignDetails(parseCodesignDetails(commandOutput(display)), binaryPath);\n await runRequiredCommand(\n run,\n binaryPath,\n \"spctl\",\n [\"--assess\", \"--type\", \"execute\", \"--verbose=4\", binaryPath],\n \"spctl assessment\"\n );\n}\n\n/**\n * Default signature verifier: fail closed unless `codesign` verifies the\n * signature, the identity matches the release contract, and Gatekeeper accepts\n * the executable through `spctl --assess --type execute`.\n */\nexport const codesignVerifier: SignatureVerifier = async (binaryPath) => {\n await verifyMacdriverSignature(binaryPath);\n};\n\nexport interface DownloadOptions {\n version?: string;\n fetchImpl?: FetchLike;\n}\n\n/**\n * Download the pinned release's universal-binary zip and verify its SHA-256\n * against the released `.sha256` sidecar. Returns the verified zip bytes.\n * Throws a clear, actionable error on a 404 (no release cut yet) or a checksum\n * mismatch.\n */\nexport async function downloadAndVerify(options: DownloadOptions = {}): Promise<Buffer> {\n const version = options.version ?? MACDRIVER_VERSION;\n const fetchImpl = options.fetchImpl ?? (fetch as unknown as FetchLike);\n\n const assetName = macdriverAssetName(version);\n const zipUrl = macdriverAssetUrl(assetName, version);\n const sumUrl = macdriverAssetUrl(macdriverChecksumName(version), version);\n\n const [zipRes, sumRes] = await Promise.all([\n fetchImpl(zipUrl, { redirect: \"follow\" }),\n fetchImpl(sumUrl, { redirect: \"follow\" })\n ]);\n\n if (zipRes.status === 404 || sumRes.status === 404) {\n throw new Error(\n `No published prowl-macdriver release for ${macdriverReleaseTag(version)} yet.\\n` +\n \"The signed binary is cut by the maintainer; until then build from source:\\n\" +\n \" cd macdriver && swift build -c release\"\n );\n }\n if (!zipRes.ok) {\n throw new Error(`Failed to download ${assetName} (HTTP ${zipRes.status}) from ${zipUrl}`);\n }\n if (!sumRes.ok) {\n throw new Error(`Failed to download the checksum (HTTP ${sumRes.status}) from ${sumUrl}`);\n }\n\n const zipBytes = Buffer.from(await zipRes.arrayBuffer());\n const expected = parseChecksumFile(await sumRes.text());\n const actual = sha256Hex(zipBytes);\n if (actual !== expected) {\n throw new Error(\n `Checksum mismatch for ${assetName}.\\n expected: ${expected}\\n actual: ${actual}\\n` +\n \"The download was rejected and discarded; re-run the install, and report it if it repeats.\"\n );\n }\n return zipBytes;\n}\n\nexport interface InstallOptions {\n version?: string;\n force?: boolean;\n homedir?: string;\n fetchImpl?: FetchLike;\n listArchiveEntries?: ArchiveLister;\n extract?: Extractor;\n verifySignature?: SignatureVerifier;\n}\n\nexport interface InstallResult {\n version: string;\n binaryPath: string;\n alreadyInstalled: boolean;\n}\n\n/**\n * Install the pinned helper to `~/.prowl/macdriver/<version>/prowl-macdriver`\n * (0755). Returns `alreadyInstalled: true` (a no-op) when the binary is already\n * present and `force` is not set. The downloaded archive is inspected before\n * extraction, extracted into a temporary staging directory, and moved into place\n * only after the staged helper passes file and signature verification.\n */\nexport async function installMacdriver(options: InstallOptions = {}): Promise<InstallResult> {\n const version = validateMacdriverVersion(options.version ?? MACDRIVER_VERSION);\n const homedir = options.homedir ?? os.homedir();\n const listArchiveEntries = options.listArchiveEntries ?? zipinfoArchiveLister;\n const extract = options.extract ?? dittoExtractor;\n const verifySignature = options.verifySignature ?? codesignVerifier;\n\n const installRoot = macdriverInstallRoot(homedir);\n const versionDir = macdriverVersionDir(version, homedir);\n const binaryPath = macdriverInstalledBinary(version, homedir);\n\n if (!options.force && fs.existsSync(binaryPath)) {\n return { version, binaryPath, alreadyInstalled: true };\n }\n\n const zipBytes = await downloadAndVerify({ version, fetchImpl: options.fetchImpl });\n\n fs.mkdirSync(installRoot, { recursive: true });\n const stagingDir = fs.mkdtempSync(path.join(installRoot, `.tmp-${version}-`));\n const extractDir = path.join(stagingDir, \"extract\");\n const zipPath = path.join(stagingDir, macdriverAssetName(version));\n try {\n fs.mkdirSync(extractDir);\n fs.writeFileSync(zipPath, zipBytes);\n validateMacdriverArchiveEntries(await listArchiveEntries(zipPath));\n await extract(zipPath, extractDir);\n\n const stagedBinaryPath = path.join(extractDir, HELPER_BINARY);\n assertExtractedHelper(extractDir, stagedBinaryPath);\n fs.chmodSync(stagedBinaryPath, 0o755);\n await verifySignature(stagedBinaryPath);\n replaceVersionDir(versionDir, extractDir, installRoot);\n } finally {\n fs.rmSync(stagingDir, { recursive: true, force: true });\n }\n\n return { version, binaryPath, alreadyInstalled: false };\n}\n\nfunction assertExtractedHelper(extractDir: string, binaryPath: string): void {\n const entries = fs.readdirSync(extractDir);\n if (!entries.includes(HELPER_BINARY)) {\n throw new Error(`The release archive did not contain a \"${HELPER_BINARY}\" binary.`);\n }\n if (entries.length !== 1 || entries[0] !== HELPER_BINARY) {\n const shown = entries.length > 0 ? entries.join(\", \") : \"(empty directory)\";\n throw new Error(\n `Unexpected extracted prowl-macdriver archive contents: ${shown}. ` +\n `Expected exactly \"${HELPER_BINARY}\".`\n );\n }\n let stat: fs.Stats | null = null;\n try {\n stat = fs.lstatSync(binaryPath);\n } catch {\n // The existence check below keeps the missing-helper error specific.\n }\n if (!stat?.isFile()) {\n throw new Error(`The release archive \"${HELPER_BINARY}\" entry is not a regular file.`);\n }\n}\n\nfunction replaceVersionDir(versionDir: string, stagedVersionDir: string, installRoot: string): void {\n const backupDir = path.join(installRoot, `.previous-${path.basename(versionDir)}-${process.pid}-${Date.now()}`);\n let backedUp = false;\n try {\n if (fs.existsSync(versionDir)) {\n fs.renameSync(versionDir, backupDir);\n backedUp = true;\n }\n fs.renameSync(stagedVersionDir, versionDir);\n if (backedUp) {\n fs.rmSync(backupDir, { recursive: true, force: true });\n }\n } catch (error) {\n if (backedUp && !fs.existsSync(versionDir) && fs.existsSync(backupDir)) {\n fs.renameSync(backupDir, versionDir);\n }\n throw error;\n }\n}\n\nexport interface InstalledVersion {\n version: string;\n binaryPath: string;\n}\n\nexport interface MacdriverStatus {\n /** The binary Prowl would use now, and how it was found. */\n resolved: { path: string; source: \"env\" | \"user-install\" | \"source-build\" } | null;\n /** The pinned version the CLI targets. */\n pinnedVersion: string;\n /** All versions found under `~/.prowl/macdriver/`. */\n installed: InstalledVersion[];\n /** Version string the resolved binary reports, or null if it couldn't run. */\n probedVersion: string | null;\n}\n\n/** Probe a helper binary's `version` output; null if it can't be run/parsed. */\nexport type VersionProbe = (binaryPath: string) => Promise<string | null>;\n\n/** Default probe: run `<binary> version` and parse the `prowl-macdriver X.Y.Z` line. */\nexport const runVersionProbe: VersionProbe = async (binaryPath) => {\n try {\n const { stdout } = await execFileAsync(binaryPath, [\"version\"], { timeout: 5000 });\n const match = stdout.match(/prowl-macdriver\\s+(\\S+)/);\n return match ? match[1] : stdout.trim() || null;\n } catch {\n return null;\n }\n};\n\nexport interface StatusOptions {\n env?: NodeJS.ProcessEnv;\n homedir?: string;\n probe?: VersionProbe;\n}\n\n/**\n * Gather the state `prowl macdriver status` reports: the resolved binary and\n * how it was found, every installed version, and the resolved binary's probed\n * version. Pure aside from filesystem reads and the (injectable) probe.\n */\nexport async function collectMacdriverStatus(options: StatusOptions = {}): Promise<MacdriverStatus> {\n const env = options.env ?? process.env;\n const homedir = options.homedir ?? os.homedir();\n const probe = options.probe ?? runVersionProbe;\n\n let resolved: MacdriverStatus[\"resolved\"] = null;\n try {\n const resolvedPath = resolveHelperBinary(env, { homedir });\n resolved = { path: resolvedPath, source: classifyResolvedSource(resolvedPath, env, homedir) };\n } catch {\n resolved = null;\n }\n\n const installed: InstalledVersion[] = [];\n const root = macdriverInstallRoot(homedir);\n if (fs.existsSync(root)) {\n for (const entry of fs.readdirSync(root, { withFileTypes: true })) {\n if (!entry.isDirectory()) continue;\n let binaryPath: string;\n try {\n binaryPath = macdriverInstalledBinary(entry.name, homedir);\n } catch {\n continue;\n }\n if (fs.existsSync(binaryPath)) {\n installed.push({ version: entry.name, binaryPath });\n }\n }\n installed.sort((a, b) => a.version.localeCompare(b.version));\n }\n\n const probedVersion = resolved ? await probe(resolved.path) : null;\n\n return { resolved, pinnedVersion: MACDRIVER_VERSION, installed, probedVersion };\n}\n\nfunction classifyResolvedSource(\n resolvedPath: string,\n env: NodeJS.ProcessEnv,\n homedir: string\n): \"env\" | \"user-install\" | \"source-build\" {\n if (env.PROWL_MACDRIVER_BIN && resolvedPath === env.PROWL_MACDRIVER_BIN) {\n return \"env\";\n }\n if (resolvedPath.startsWith(macdriverInstallRoot(homedir) + path.sep)) {\n return \"user-install\";\n }\n return \"source-build\";\n}\n\n/** Static TCC-permission guidance printed after install / in status. */\nexport function tccGuidance(): string {\n return (\n \"macOS permissions: the app that hosts Prowl (your terminal — Terminal, iTerm, VS Code, …)\\n\" +\n \"must be granted, in System Settings → Privacy & Security:\\n\" +\n \" • Accessibility — required to drive the target app\\n\" +\n \" • Screen Recording — required for screenshots / visual baselines\\n\" +\n \"Grant both to the terminal app, not to prowl-macdriver itself, then re-run your hunt.\"\n );\n}\n"],"mappings":"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","names":["path","fs","os","path","path","fs","os","num","quote","fs","fs","spawn","path","execFile","fs","path","path","fs","execFile","WAIT_POLL_INTERVAL_MS","DEFAULT_WAIT_TIMEOUT_MS","delay","execFile","spawn","os","path","W3C_ELEMENT_KEY","path","parseJson","extractWebdriverError","extractElementId","isNoSuchElement","sleep","createRequire","execFile","fs","os","path","createRequire","path","fs","os","execFile","locator","fs","path","fs","path","fs","path","fs","path","path","captureScreenshot","fs","fs","path","fs","path","fs","path","path","fs","delay","fs","path","fs","path","path","fs","path","path","forms","elements","str","toElement","collectInteractive","str","bool","toElement","toWindow","parseViewportFlag","execFile","createHash","fs","os","path","execFile","createHash","path","os","fs"]}
|