prowl-tools 0.1.3 → 0.1.5
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/LICENSE +1 -1
- package/NOTICE +11 -1
- package/README.md +390 -3
- package/dist/chunk-2KD2XCTH.js +6202 -0
- package/dist/chunk-2KD2XCTH.js.map +1 -0
- package/dist/{chunk-NXXGJOBG.js → chunk-ITOSUJCN.js} +74 -13
- package/dist/chunk-ITOSUJCN.js.map +1 -0
- package/dist/index.cjs +4249 -1116
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +184 -78
- package/dist/index.js.map +1 -1
- package/dist/lib.cjs +3988 -816
- package/dist/lib.cjs.map +1 -1
- package/dist/lib.d.cts +1052 -17
- package/dist/lib.d.ts +1052 -17
- package/dist/lib.js +152 -4
- package/dist/{loader-5RDNTJHH.js → loader-FCXPARP7.js} +2 -2
- package/package.json +6 -2
- package/dist/chunk-NXXGJOBG.js.map +0 -1
- package/dist/chunk-T7YLXF6X.js +0 -3158
- package/dist/chunk-T7YLXF6X.js.map +0 -1
- /package/dist/{loader-5RDNTJHH.js.map → loader-FCXPARP7.js.map} +0 -0
package/dist/lib.d.ts
CHANGED
|
@@ -1,16 +1,63 @@
|
|
|
1
|
-
import { Page } from 'playwright';
|
|
2
1
|
import { z } from 'zod';
|
|
3
2
|
|
|
4
|
-
|
|
3
|
+
declare const SUPPORTED_BROWSER_ENGINES: readonly ["chromium", "firefox", "webkit"];
|
|
4
|
+
type BrowserEngine = (typeof SUPPORTED_BROWSER_ENGINES)[number];
|
|
5
5
|
type BrowserChannel = "chromium" | "chrome" | "chrome-beta" | "chrome-canary" | "chrome-dev" | "msedge" | "msedge-beta" | "msedge-canary" | "msedge-dev";
|
|
6
6
|
type Viewport = {
|
|
7
7
|
width: number;
|
|
8
8
|
height: number;
|
|
9
9
|
};
|
|
10
|
+
/** Web execution target (default): drives a browser at `url`. */
|
|
11
|
+
type WebTarget = {
|
|
12
|
+
type: "web";
|
|
13
|
+
url: string;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* macOS native execution target (experimental, PROWL-048). `app` is a bundle
|
|
17
|
+
* identifier (e.g. `com.example.App`) or an absolute path to a `.app` bundle.
|
|
18
|
+
* App-path guardrails are matched against the path, bundle name, and
|
|
19
|
+
* `CFBundleIdentifier` from `Contents/Info.plist` when it is readable.
|
|
20
|
+
*/
|
|
21
|
+
type MacosTarget = {
|
|
22
|
+
type: "macos";
|
|
23
|
+
app: string;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Android native execution target (experimental, PROWL-058). `app` is an Android
|
|
27
|
+
* package name (e.g. `com.example.app`) or an absolute/relative path to an `.apk`
|
|
28
|
+
* file, which Prowl installs before launch. Optional `deviceSerial` selects one of
|
|
29
|
+
* several attached devices/emulators; `coldStart` opts into a deterministic
|
|
30
|
+
* `pm clear` before launch (default off). Android guardrails match package IDs
|
|
31
|
+
* or canonical full APK paths; APK package IDs are resolved before install.
|
|
32
|
+
*/
|
|
33
|
+
type AndroidTarget = {
|
|
34
|
+
type: "android";
|
|
35
|
+
app: string;
|
|
36
|
+
/** adb device serial to target when more than one device is attached. */
|
|
37
|
+
deviceSerial?: string;
|
|
38
|
+
/** Run `pm clear <package>` before launch for a deterministic cold start (default off). */
|
|
39
|
+
coldStart?: boolean;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* iOS simulator native execution target (experimental, PROWL-059). `app` is an
|
|
43
|
+
* iOS bundle identifier (e.g. `com.example.App`) or an absolute/relative path to
|
|
44
|
+
* a built `.app` bundle, which Prowl installs onto the simulator before launch.
|
|
45
|
+
* Optional `udid` selects a specific booted simulator; `coldStart` opts into an
|
|
46
|
+
* uninstall+reinstall before launch (default off, and only possible with a `.app`
|
|
47
|
+
* path). Real devices are out of scope (PROWL-062) — simulators only. iOS
|
|
48
|
+
* guardrails match bundle ids, the bundle name, or the canonical `.app` path.
|
|
49
|
+
*/
|
|
50
|
+
type IosTarget = {
|
|
51
|
+
type: "ios";
|
|
52
|
+
app: string;
|
|
53
|
+
/** UDID of the booted simulator to target when more than one is booted. */
|
|
54
|
+
udid?: string;
|
|
55
|
+
/** Uninstall+reinstall the app before launch for a deterministic cold start (default off). */
|
|
56
|
+
coldStart?: boolean;
|
|
57
|
+
};
|
|
58
|
+
type Target = WebTarget | MacosTarget | AndroidTarget | IosTarget;
|
|
10
59
|
type Config = {
|
|
11
|
-
target:
|
|
12
|
-
url: string;
|
|
13
|
-
};
|
|
60
|
+
target: Target;
|
|
14
61
|
browser: {
|
|
15
62
|
headless: boolean;
|
|
16
63
|
slowMo: number;
|
|
@@ -34,6 +81,8 @@ type Config = {
|
|
|
34
81
|
guardrails: {
|
|
35
82
|
maxSteps: number;
|
|
36
83
|
allowedDomains: string[];
|
|
84
|
+
/** Native scope analog of `allowedDomains`: allowed bundle/package IDs or canonical app artifact paths. */
|
|
85
|
+
allowedApps: string[];
|
|
37
86
|
forbiddenSelectors: string[];
|
|
38
87
|
selfHealing: boolean;
|
|
39
88
|
};
|
|
@@ -339,6 +388,801 @@ type CiResult = {
|
|
|
339
388
|
clusters?: CiFailureCluster[];
|
|
340
389
|
};
|
|
341
390
|
|
|
391
|
+
/**
|
|
392
|
+
* PROWL-047 / ARCH-001 — Session driver abstraction.
|
|
393
|
+
*
|
|
394
|
+
* `SessionDriver` is the narrow, engine-agnostic surface the step runner drives.
|
|
395
|
+
* Today the only implementation is the Playwright driver (`playwright-driver.ts`),
|
|
396
|
+
* but factoring the runner against this interface is the prerequisite for
|
|
397
|
+
* non-browser execution targets (macOS native, Electron, mobile — see PROWL-048).
|
|
398
|
+
*
|
|
399
|
+
* The interface intentionally covers only the verbs `src/runner/steps.ts`
|
|
400
|
+
* actually uses. Selector strings are the driver's own dialect: the driver
|
|
401
|
+
* supplies `parseTextSelector` so the guardrail policy can reason about them
|
|
402
|
+
* without knowing the engine.
|
|
403
|
+
*/
|
|
404
|
+
/** Coarse capability groups a step handler can require of a driver. */
|
|
405
|
+
type DriverCapability = "navigate" | "query" | "interact" | "wait" | "screenshot" | "evaluate" | "response" | "route" | "dialog" | "files" | "download";
|
|
406
|
+
/** How a dialog opened by the page should be answered. */
|
|
407
|
+
type DialogAction = "accept" | "dismiss";
|
|
408
|
+
/** Options accepted when navigating; mirrors the subset of Playwright's `goto`. */
|
|
409
|
+
type NavigateOptions = {
|
|
410
|
+
waitUntil?: "load" | "domcontentloaded" | "networkidle" | "commit";
|
|
411
|
+
};
|
|
412
|
+
/** A network response as the driver exposes it (engine-neutral). */
|
|
413
|
+
type DriverResponse = {
|
|
414
|
+
url(): string;
|
|
415
|
+
status(): number;
|
|
416
|
+
/** Header names MUST be lowercased by the driver; consumers look up lowercase keys. */
|
|
417
|
+
headers(): Record<string, string>;
|
|
418
|
+
};
|
|
419
|
+
/** A route intercepted via `route()`; the handler fulfills it with a canned response. */
|
|
420
|
+
type DriverRoute = {
|
|
421
|
+
fulfill(response: {
|
|
422
|
+
status: number;
|
|
423
|
+
contentType?: string;
|
|
424
|
+
body?: string;
|
|
425
|
+
}): Promise<void>;
|
|
426
|
+
};
|
|
427
|
+
/** A file download surfaced by `waitForDownloadEvent()`. */
|
|
428
|
+
type DriverDownload = {
|
|
429
|
+
suggestedFilename(): string;
|
|
430
|
+
saveAs(path: string): Promise<void>;
|
|
431
|
+
};
|
|
432
|
+
/**
|
|
433
|
+
* The operations the step runner performs against a live session. Each method
|
|
434
|
+
* corresponds to one Playwright Page/Locator call in the legacy runner, so the
|
|
435
|
+
* Playwright implementation is a faithful pass-through and behaviour is
|
|
436
|
+
* unchanged.
|
|
437
|
+
*/
|
|
438
|
+
interface SessionDriver {
|
|
439
|
+
/** Capabilities this driver supports; handlers declare what they need. */
|
|
440
|
+
readonly capabilities: ReadonlySet<DriverCapability>;
|
|
441
|
+
goto(url: string, options?: NavigateOptions): Promise<void>;
|
|
442
|
+
currentUrl(): string;
|
|
443
|
+
count(selector: string): Promise<number>;
|
|
444
|
+
textContent(selector: string): Promise<string | null>;
|
|
445
|
+
click(selector: string): Promise<void>;
|
|
446
|
+
clickFirst(selector: string): Promise<void>;
|
|
447
|
+
fill(selector: string, value: string): Promise<void>;
|
|
448
|
+
fillFirst(selector: string, value: string): Promise<void>;
|
|
449
|
+
press(selector: string, key: string): Promise<void>;
|
|
450
|
+
selectOption(selector: string, value: string): Promise<void>;
|
|
451
|
+
selectOptionFirst(selector: string, value: string): Promise<void>;
|
|
452
|
+
hover(selector: string): Promise<void>;
|
|
453
|
+
scrollIntoView(selector: string): Promise<void>;
|
|
454
|
+
setInputFiles(selector: string, files: string | string[]): Promise<void>;
|
|
455
|
+
countByRole(role: string, name: string): Promise<number>;
|
|
456
|
+
clickFirstByRole(role: string, name: string): Promise<void>;
|
|
457
|
+
countByLabel(label: string): Promise<number>;
|
|
458
|
+
fillFirstByLabel(label: string, value: string): Promise<void>;
|
|
459
|
+
selectOptionFirstByLabel(label: string, value: string): Promise<void>;
|
|
460
|
+
waitForSelector(selector: string, options?: {
|
|
461
|
+
timeout?: number;
|
|
462
|
+
}): Promise<void>;
|
|
463
|
+
waitForUrl(predicate: (url: string) => boolean, options?: {
|
|
464
|
+
timeout?: number;
|
|
465
|
+
}): Promise<void>;
|
|
466
|
+
waitForNetworkIdle(options?: {
|
|
467
|
+
timeout?: number;
|
|
468
|
+
}): Promise<void>;
|
|
469
|
+
evaluate<R = unknown, A = unknown>(pageFunction: string | ((arg: A) => R | Promise<R>), arg?: A): Promise<R>;
|
|
470
|
+
screenshot(options: {
|
|
471
|
+
path: string;
|
|
472
|
+
fullPage?: boolean;
|
|
473
|
+
}): Promise<void>;
|
|
474
|
+
onResponse(handler: (response: DriverResponse) => void): void;
|
|
475
|
+
route(url: string, handler: (route: DriverRoute) => void | Promise<void>): Promise<void>;
|
|
476
|
+
unroute(url: string): Promise<void>;
|
|
477
|
+
onDialog(action: DialogAction): void;
|
|
478
|
+
waitForDownloadEvent(options?: {
|
|
479
|
+
timeout?: number;
|
|
480
|
+
}): Promise<DriverDownload>;
|
|
481
|
+
/**
|
|
482
|
+
* Driver-supplied selector parser: returns the literal text a text-engine
|
|
483
|
+
* selector matches (e.g. `text="Delete"` → `Delete`), or null for any other
|
|
484
|
+
* selector. The guardrail policy uses this to interpret forbidden patterns
|
|
485
|
+
* without hard-coding the engine's selector dialect.
|
|
486
|
+
*/
|
|
487
|
+
parseTextSelector(selector: string): string | null;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* PROWL-048 / ARCH-002 — macOS native implementation of {@link SessionDriver}.
|
|
492
|
+
*
|
|
493
|
+
* `MacDriver` drives a native macOS app through the `prowl-macdriver` Swift
|
|
494
|
+
* helper (Accessibility / AXUIElement) over a long-lived JSON-over-stdio
|
|
495
|
+
* transport ({@link MacHelperClient}). It implements only the portable subset of
|
|
496
|
+
* the driver surface — its `capabilities` set is honest: `query`, `interact`,
|
|
497
|
+
* `wait`, `screenshot`. Web-only verbs (navigation, network, dialogs, files,
|
|
498
|
+
* downloads, script evaluation) are unsupported stubs; the runner never reaches
|
|
499
|
+
* them because both the target step-compatibility check and the runtime
|
|
500
|
+
* capability gate reject web-only steps for the macOS target.
|
|
501
|
+
*
|
|
502
|
+
* Selector dialect (parsed here, so the Swift helper only matches attributes):
|
|
503
|
+
* id=openSettings → accessibility identifier
|
|
504
|
+
* role=button[name="Save"] → AX role + accessible name
|
|
505
|
+
* label="Email" → exact accessibility label
|
|
506
|
+
* text="Save" | Save → title/description/value substring
|
|
507
|
+
* statusItem → open the app's menu bar status-item menu
|
|
508
|
+
* menu=Preferences… → open the status-item menu and click an item
|
|
509
|
+
*/
|
|
510
|
+
|
|
511
|
+
/** A structured accessibility query understood by the Swift helper. */
|
|
512
|
+
type MacQuery = {
|
|
513
|
+
by: "id";
|
|
514
|
+
value: string;
|
|
515
|
+
} | {
|
|
516
|
+
by: "role";
|
|
517
|
+
role: string;
|
|
518
|
+
name?: string;
|
|
519
|
+
} | {
|
|
520
|
+
by: "text";
|
|
521
|
+
value: string;
|
|
522
|
+
} | {
|
|
523
|
+
by: "label";
|
|
524
|
+
value: string;
|
|
525
|
+
} | {
|
|
526
|
+
by: "focused";
|
|
527
|
+
};
|
|
528
|
+
/** The transport MacDriver talks to: one request → one response, matched by id. */
|
|
529
|
+
interface MacHelperClient {
|
|
530
|
+
/** Send a command; resolve with its `result` payload or reject with its error. */
|
|
531
|
+
request(cmd: string, params?: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
532
|
+
/** Shut the helper down. */
|
|
533
|
+
close(): Promise<void>;
|
|
534
|
+
}
|
|
535
|
+
type MacDriverOptions = {
|
|
536
|
+
/** Bundle id / app label, used only for the informational `currentUrl()` value. */
|
|
537
|
+
appLabel?: string;
|
|
538
|
+
};
|
|
539
|
+
/** Parse a Prowl selector string into a {@link MacQuery}. Bare text matches by text. */
|
|
540
|
+
declare function parseMacSelector(selector: string): MacQuery;
|
|
541
|
+
/** Wrap a live {@link MacHelperClient} as a {@link SessionDriver}. */
|
|
542
|
+
declare function createMacDriver(client: MacHelperClient, options?: MacDriverOptions): SessionDriver;
|
|
543
|
+
|
|
544
|
+
declare function macdriverBuildInstructions(): string;
|
|
545
|
+
/**
|
|
546
|
+
* Resolve the helper binary path. Order: `PROWL_MACDRIVER_BIN`, then the
|
|
547
|
+
* release then debug build under `macdriver/.build/`. Throws with build
|
|
548
|
+
* instructions when none is found.
|
|
549
|
+
*/
|
|
550
|
+
declare function resolveHelperBinary(env?: NodeJS.ProcessEnv): string;
|
|
551
|
+
/** Default per-request deadline for the helper transport. */
|
|
552
|
+
declare const DEFAULT_REQUEST_TIMEOUT_MS = 30000;
|
|
553
|
+
type SpawnMacHelperOptions = {
|
|
554
|
+
/** Per-request deadline; a request that gets no response by then rejects. */
|
|
555
|
+
requestTimeoutMs?: number;
|
|
556
|
+
};
|
|
557
|
+
/** A {@link MacHelperClient} backed by a spawned `prowl-macdriver serve` process. */
|
|
558
|
+
declare class SpawnMacHelperClient implements MacHelperClient {
|
|
559
|
+
private readonly child;
|
|
560
|
+
private readonly pending;
|
|
561
|
+
private readonly requestTimeoutMs;
|
|
562
|
+
private stdoutBuffer;
|
|
563
|
+
private stderrBuffer;
|
|
564
|
+
private nextId;
|
|
565
|
+
private closed;
|
|
566
|
+
private terminalError;
|
|
567
|
+
constructor(binaryPath: string, options?: SpawnMacHelperOptions);
|
|
568
|
+
private onStdout;
|
|
569
|
+
private dispatch;
|
|
570
|
+
private failAll;
|
|
571
|
+
private recordTerminalFailure;
|
|
572
|
+
/** Number of in-flight requests awaiting a response (for teardown/tests). */
|
|
573
|
+
get pendingCount(): number;
|
|
574
|
+
request(cmd: string, params?: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
575
|
+
close(): Promise<void>;
|
|
576
|
+
}
|
|
577
|
+
type MacSession = {
|
|
578
|
+
client: MacHelperClient;
|
|
579
|
+
driver: SessionDriver;
|
|
580
|
+
bundleId: string;
|
|
581
|
+
};
|
|
582
|
+
type LaunchMacOptions = {
|
|
583
|
+
/** Bundle id or absolute `.app` path. */
|
|
584
|
+
app: string;
|
|
585
|
+
timeoutMs?: number;
|
|
586
|
+
/** Inject a helper client (tests / a prebuilt binary); defaults to spawning the helper. */
|
|
587
|
+
clientFactory?: () => MacHelperClient;
|
|
588
|
+
};
|
|
589
|
+
/** Launch/attach the target app through the helper and build a {@link MacDriver}. */
|
|
590
|
+
declare function launchMacSession(options: LaunchMacOptions): Promise<MacSession>;
|
|
591
|
+
/** Quit the target app (best effort) and shut the helper down. */
|
|
592
|
+
declare function closeMacSession(session: MacSession): Promise<void>;
|
|
593
|
+
|
|
594
|
+
/** A structured query the {@link AndroidAgentClient} resolves against the device. */
|
|
595
|
+
type AndroidQuery = {
|
|
596
|
+
by: "id";
|
|
597
|
+
value: string;
|
|
598
|
+
} | {
|
|
599
|
+
by: "accessibilityId";
|
|
600
|
+
value: string;
|
|
601
|
+
} | {
|
|
602
|
+
by: "role";
|
|
603
|
+
role: string;
|
|
604
|
+
name?: string;
|
|
605
|
+
} | {
|
|
606
|
+
by: "text";
|
|
607
|
+
value: string;
|
|
608
|
+
} | {
|
|
609
|
+
by: "focused";
|
|
610
|
+
};
|
|
611
|
+
/**
|
|
612
|
+
* A locator in the uiautomator2 server's native wire shape. The raw on-device
|
|
613
|
+
* server does NOT accept W3C `{using, value}` — that translation normally lives
|
|
614
|
+
* in Appium's driver layer, which we bypass — it requires `{strategy, selector}`
|
|
615
|
+
* (plus a `context` field, empty for a root-scoped search). Device-verified
|
|
616
|
+
* against appium-uiautomator2-server 10.6.2 (2026-08-19).
|
|
617
|
+
*/
|
|
618
|
+
type AndroidLocator = {
|
|
619
|
+
strategy: string;
|
|
620
|
+
selector: string;
|
|
621
|
+
context: string;
|
|
622
|
+
};
|
|
623
|
+
/**
|
|
624
|
+
* The semantic transport `AndroidDriver` talks to: element lookups return opaque
|
|
625
|
+
* element ids, and interactions take those ids. The HTTP/UiAutomator2
|
|
626
|
+
* implementation lives in {@link ./android-agent.js}; tests fake this interface.
|
|
627
|
+
*/
|
|
628
|
+
interface AndroidAgentClient {
|
|
629
|
+
/** Resolve the first element matching `query`, or null when none match. */
|
|
630
|
+
findElement(query: AndroidQuery): Promise<string | null>;
|
|
631
|
+
/** Resolve every element id matching `query` (empty when none match). */
|
|
632
|
+
findElements(query: AndroidQuery): Promise<string[]>;
|
|
633
|
+
click(elementId: string): Promise<void>;
|
|
634
|
+
/** Replace an element's text (unicode-safe; W3C `element/value`). */
|
|
635
|
+
setValue(elementId: string, text: string): Promise<void>;
|
|
636
|
+
getText(elementId: string): Promise<string | null>;
|
|
637
|
+
/** Dispatch a global key event by Android key code (goes to the focused view). */
|
|
638
|
+
pressKeyCode(keyCode: number): Promise<void>;
|
|
639
|
+
/** Capture the current screen as PNG bytes. */
|
|
640
|
+
screenshotPng(): Promise<Buffer>;
|
|
641
|
+
close(): Promise<void>;
|
|
642
|
+
}
|
|
643
|
+
type AndroidDriverOptions = {
|
|
644
|
+
/** Package name, used only for the informational `currentUrl()` value. */
|
|
645
|
+
appLabel?: string;
|
|
646
|
+
};
|
|
647
|
+
/**
|
|
648
|
+
* Android key names accepted by the `press` step, mapped to KeyEvent key codes.
|
|
649
|
+
* Names are matched case-insensitively.
|
|
650
|
+
*/
|
|
651
|
+
declare const ANDROID_KEYCODES: Readonly<Record<string, number>>;
|
|
652
|
+
/** Escape a string for embedding inside a `new UiSelector()...("...")` argument. */
|
|
653
|
+
declare function escapeUiSelectorArg(value: string): string;
|
|
654
|
+
/** Parse a Prowl selector string into an {@link AndroidQuery}. Bare text matches by text. */
|
|
655
|
+
declare function parseAndroidSelector(selector: string): AndroidQuery;
|
|
656
|
+
/**
|
|
657
|
+
* Translate an {@link AndroidQuery} into the uiautomator2 server's native
|
|
658
|
+
* locator shape. `id`/`accessibility id` are native strategies;
|
|
659
|
+
* text/role(+name)/focused compose a `-android uiautomator` `UiSelector`.
|
|
660
|
+
* `appPackage` qualifies bare `id=` names ({@link qualifyResourceId}).
|
|
661
|
+
*/
|
|
662
|
+
declare function androidQueryToLocator(query: AndroidQuery, options?: {
|
|
663
|
+
appPackage?: string;
|
|
664
|
+
}): AndroidLocator;
|
|
665
|
+
/** The literal text a `text=` selector matches, else null (mirrors the web driver). */
|
|
666
|
+
declare function unwrapAndroidTextSelector(selector: string): string | null;
|
|
667
|
+
/** Wrap a live {@link AndroidAgentClient} as a {@link SessionDriver}. */
|
|
668
|
+
declare function createAndroidDriver(client: AndroidAgentClient, options?: AndroidDriverOptions): SessionDriver;
|
|
669
|
+
|
|
670
|
+
/** Result of one adb invocation. */
|
|
671
|
+
type AdbResult = {
|
|
672
|
+
stdout: string;
|
|
673
|
+
stderr: string;
|
|
674
|
+
code: number;
|
|
675
|
+
};
|
|
676
|
+
/** Runs one adb command to completion and resolves with its captured output. */
|
|
677
|
+
type AdbRunner = (args: string[], options?: {
|
|
678
|
+
timeoutMs?: number;
|
|
679
|
+
}) => Promise<AdbResult>;
|
|
680
|
+
/** A handle to a spawned long-running adb process (the instrumentation server). */
|
|
681
|
+
type AdbProcessHandle = {
|
|
682
|
+
kill(): void;
|
|
683
|
+
};
|
|
684
|
+
/** Spawns a long-running adb command (e.g. `am instrument -w`) in the background. */
|
|
685
|
+
type AdbSpawner = (args: string[]) => AdbProcessHandle;
|
|
686
|
+
/** One row of `adb devices -l`. */
|
|
687
|
+
type AdbDevice = {
|
|
688
|
+
serial: string;
|
|
689
|
+
state: string;
|
|
690
|
+
description: Record<string, string>;
|
|
691
|
+
};
|
|
692
|
+
/**
|
|
693
|
+
* Parse `adb devices -l` output into structured rows. The header line
|
|
694
|
+
* (`List of devices attached`) and blank lines are skipped.
|
|
695
|
+
*/
|
|
696
|
+
declare function parseAdbDevices(stdout: string): AdbDevice[];
|
|
697
|
+
/** Devices that are fully booted and usable (`device` state). */
|
|
698
|
+
declare function bootedDevices(devices: AdbDevice[]): AdbDevice[];
|
|
699
|
+
/**
|
|
700
|
+
* Choose which device to drive. With `requested` set it must be attached and
|
|
701
|
+
* booted. Otherwise exactly one booted device is required; zero or many raise an
|
|
702
|
+
* actionable error listing what was found.
|
|
703
|
+
*/
|
|
704
|
+
declare function selectDeviceSerial(devices: AdbDevice[], requested?: string): string;
|
|
705
|
+
/** List attached devices via `adb devices -l`. */
|
|
706
|
+
declare function listDevices(runner: AdbRunner): Promise<AdbDevice[]>;
|
|
707
|
+
/**
|
|
708
|
+
* Parse the local port `adb forward tcp:0 tcp:<remote>` prints (dynamic port
|
|
709
|
+
* allocation). adb echoes the chosen local port on stdout.
|
|
710
|
+
*/
|
|
711
|
+
declare function parseForwardPort(stdout: string): number;
|
|
712
|
+
/** Parse the package name out of `aapt dump badging <apk>` output. */
|
|
713
|
+
declare function parseAaptPackage(stdout: string): string | null;
|
|
714
|
+
|
|
715
|
+
/** The remote port the uiautomator2 server listens on inside the device. */
|
|
716
|
+
declare const UIA2_REMOTE_PORT = 6790;
|
|
717
|
+
/** Locations of the two prebuilt agent APKs. */
|
|
718
|
+
type AgentApks = {
|
|
719
|
+
serverApk: string;
|
|
720
|
+
testApk: string;
|
|
721
|
+
};
|
|
722
|
+
/** Establishes a live {@link AndroidAgentClient} against a forwarded local port. */
|
|
723
|
+
type AgentConnector = (options: {
|
|
724
|
+
host: string;
|
|
725
|
+
port: number;
|
|
726
|
+
requestTimeoutMs: number;
|
|
727
|
+
readyDeadlineMs: number;
|
|
728
|
+
/** Target app's package name; qualifies bare `id=` selectors on the device. */
|
|
729
|
+
appPackage?: string;
|
|
730
|
+
}) => Promise<AndroidAgentClient>;
|
|
731
|
+
/** Resolve a package name from an `.apk` file, or null when it can't be determined. */
|
|
732
|
+
type AaptResolver = (apkPath: string) => Promise<string | null>;
|
|
733
|
+
/**
|
|
734
|
+
* Resolve the two agent APKs shipped inside `appium-uiautomator2-server`. They
|
|
735
|
+
* live under the package's `apks/` folder; the server APK is version-stamped.
|
|
736
|
+
*/
|
|
737
|
+
declare function resolveAgentApks(requireFn?: NodeRequire): AgentApks;
|
|
738
|
+
/** Default connector: builds the HTTP transport, waits for readiness, opens a session. */
|
|
739
|
+
declare const defaultAgentConnector: AgentConnector;
|
|
740
|
+
type AndroidSession = {
|
|
741
|
+
client: AndroidAgentClient;
|
|
742
|
+
driver: SessionDriver;
|
|
743
|
+
/** The resolved package name being driven. */
|
|
744
|
+
package: string;
|
|
745
|
+
/** The adb serial of the device being driven. */
|
|
746
|
+
serial: string;
|
|
747
|
+
/** Tear down the session, agent, port forward, and app (best effort). */
|
|
748
|
+
teardown(): Promise<void>;
|
|
749
|
+
};
|
|
750
|
+
type LaunchAndroidOptions = {
|
|
751
|
+
/** Package name or `.apk` path. */
|
|
752
|
+
app: string;
|
|
753
|
+
/** adb serial when more than one device is attached. */
|
|
754
|
+
deviceSerial?: string;
|
|
755
|
+
/** `pm clear` the package before launch for a deterministic cold start. */
|
|
756
|
+
coldStart?: boolean;
|
|
757
|
+
timeoutMs?: number;
|
|
758
|
+
runner?: AdbRunner;
|
|
759
|
+
spawner?: AdbSpawner;
|
|
760
|
+
agentConnector?: AgentConnector;
|
|
761
|
+
apks?: AgentApks;
|
|
762
|
+
aaptResolver?: AaptResolver;
|
|
763
|
+
/** Optional app scope guardrail from config.guardrails.allowedApps. */
|
|
764
|
+
allowedApps?: string[];
|
|
765
|
+
};
|
|
766
|
+
/**
|
|
767
|
+
* Preflight, install, launch, and attach the uiautomator2 agent, returning a
|
|
768
|
+
* live {@link AndroidSession}. Actionable errors cover each failure mode: adb
|
|
769
|
+
* missing / no booted device (device selection), agent unreachable (readiness).
|
|
770
|
+
*/
|
|
771
|
+
declare function launchAndroidSession(options: LaunchAndroidOptions): Promise<AndroidSession>;
|
|
772
|
+
/** Tear down an Android session (agent session, instrumentation, forward, app). */
|
|
773
|
+
declare function closeAndroidSession(session: AndroidSession): Promise<void>;
|
|
774
|
+
|
|
775
|
+
/**
|
|
776
|
+
* PROWL-058 / ARCH-009 — HTTP/JSON transport for the on-device uiautomator2 agent.
|
|
777
|
+
*
|
|
778
|
+
* `appium-uiautomator2-server` exposes W3C-WebDriver-shaped endpoints over plain
|
|
779
|
+
* HTTP. This module speaks them with the global `fetch` (no heavy WebDriver SDK,
|
|
780
|
+
* per the `ai.ts` ethos), mirroring the mac-helper client's ergonomics: a
|
|
781
|
+
* per-request deadline via `AbortController`, and cleanup on
|
|
782
|
+
* every path. It exposes the semantic {@link AndroidAgentClient} the driver
|
|
783
|
+
* consumes; tests fake either the `fetch` implementation or the client itself.
|
|
784
|
+
*/
|
|
785
|
+
|
|
786
|
+
/** The subset of `fetch` this module uses; overridable in tests. */
|
|
787
|
+
type FetchLike$1 = (url: string, init: RequestInit) => Promise<Response>;
|
|
788
|
+
/** Default per-request deadline for the agent transport. */
|
|
789
|
+
declare const DEFAULT_AGENT_REQUEST_TIMEOUT_MS = 30000;
|
|
790
|
+
type Uia2TransportOptions = {
|
|
791
|
+
/** Base URL including the `/wd/hub` prefix, e.g. `http://127.0.0.1:6790/wd/hub`. */
|
|
792
|
+
baseUrl: string;
|
|
793
|
+
requestTimeoutMs?: number;
|
|
794
|
+
fetchImpl?: FetchLike$1;
|
|
795
|
+
};
|
|
796
|
+
/** An HTTP-level failure from the agent, carrying the status and parsed body. */
|
|
797
|
+
declare class Uia2HttpError extends Error {
|
|
798
|
+
readonly status: number;
|
|
799
|
+
readonly webdriverError?: string | undefined;
|
|
800
|
+
constructor(message: string, status: number, webdriverError?: string | undefined);
|
|
801
|
+
}
|
|
802
|
+
/** Low-level request/response transport with a per-request deadline. */
|
|
803
|
+
declare class Uia2Transport {
|
|
804
|
+
private readonly baseUrl;
|
|
805
|
+
private readonly requestTimeoutMs;
|
|
806
|
+
private readonly fetchImpl;
|
|
807
|
+
constructor(options: Uia2TransportOptions);
|
|
808
|
+
/**
|
|
809
|
+
* Send one request and return the parsed `value` field. Rejects with a
|
|
810
|
+
* {@link Uia2HttpError} on a non-2xx response, or a timeout error when the
|
|
811
|
+
* per-request deadline elapses.
|
|
812
|
+
*/
|
|
813
|
+
request(method: string, path: string, body?: unknown, timeoutMs?: number): Promise<unknown>;
|
|
814
|
+
}
|
|
815
|
+
/** Extract an element id from a W3C element-reference object, or null. */
|
|
816
|
+
declare function extractElementId(value: unknown): string | null;
|
|
817
|
+
/**
|
|
818
|
+
* Create a uiautomator2 session and return its id. Uses the W3C `capabilities`
|
|
819
|
+
* envelope; the server ignores the empty match set and starts a default session.
|
|
820
|
+
*/
|
|
821
|
+
declare function createUia2Session(transport: Uia2Transport): Promise<string>;
|
|
822
|
+
/** Poll `GET /status` until the agent reports ready or the deadline elapses. */
|
|
823
|
+
declare function waitForAgentReady(transport: Uia2Transport, options?: {
|
|
824
|
+
deadlineMs: number;
|
|
825
|
+
intervalMs?: number;
|
|
826
|
+
}): Promise<void>;
|
|
827
|
+
/**
|
|
828
|
+
* Build the semantic {@link AndroidAgentClient} over a live session. `close`
|
|
829
|
+
* deletes the session (best effort); the transport itself is stateless.
|
|
830
|
+
*/
|
|
831
|
+
declare function createUia2AgentClient(transport: Uia2Transport, sessionId: string, options?: {
|
|
832
|
+
appPackage?: string;
|
|
833
|
+
}): AndroidAgentClient;
|
|
834
|
+
|
|
835
|
+
/**
|
|
836
|
+
* PROWL-059 / ARCH-010 — iOS simulator implementation of {@link SessionDriver}.
|
|
837
|
+
*
|
|
838
|
+
* `IosDriver` drives a native iOS app through a prebuilt WebDriverAgent (WDA)
|
|
839
|
+
* runner over its W3C-shaped HTTP/JSON API ({@link IosAgentClient}). Like the
|
|
840
|
+
* macOS and Android drivers it implements only the portable subset of the driver
|
|
841
|
+
* surface — its `capabilities` set is honest: `query`, `interact`, `wait`,
|
|
842
|
+
* `screenshot`. Web-only verbs (navigation, network, dialogs, files, downloads,
|
|
843
|
+
* script evaluation) are unsupported stubs; the runner never reaches them because
|
|
844
|
+
* both the target step-compatibility check and the runtime capability gate reject
|
|
845
|
+
* web-only steps for native targets.
|
|
846
|
+
*
|
|
847
|
+
* Screenshots are captured via `simctl` (injected as `captureScreenshot`), not
|
|
848
|
+
* WDA, so artifacts still work even if the agent wedges.
|
|
849
|
+
*
|
|
850
|
+
* Selector dialect (parsed here into an {@link IosQuery}; the agent then matches
|
|
851
|
+
* on the simulator). Semantics mirror the macOS/Android drivers so `id=`/`label=`/
|
|
852
|
+
* `text=`/`role=` mean the same thing across native targets (PROWL-060 will unify
|
|
853
|
+
* the engines later):
|
|
854
|
+
* id=save → accessibility id (accessibilityIdentifier / name)
|
|
855
|
+
* label="Submit" → predicate `label == "Submit"` (exact)
|
|
856
|
+
* role=XCUIElementTypeButton → element class (shorthand `Button` is accepted too)
|
|
857
|
+
* role=Button[name="Save"] → class + visible text (label/value substring)
|
|
858
|
+
* text="Save" | Save → label/value substring
|
|
859
|
+
* :focus → predicate `hasKeyboardFocus == 1`
|
|
860
|
+
*/
|
|
861
|
+
|
|
862
|
+
/** A structured query the {@link IosAgentClient} resolves against the simulator. */
|
|
863
|
+
type IosQuery = {
|
|
864
|
+
by: "accessibilityId";
|
|
865
|
+
value: string;
|
|
866
|
+
} | {
|
|
867
|
+
by: "label";
|
|
868
|
+
value: string;
|
|
869
|
+
} | {
|
|
870
|
+
by: "role";
|
|
871
|
+
role: string;
|
|
872
|
+
name?: string;
|
|
873
|
+
} | {
|
|
874
|
+
by: "text";
|
|
875
|
+
value: string;
|
|
876
|
+
} | {
|
|
877
|
+
by: "focused";
|
|
878
|
+
};
|
|
879
|
+
/** A WDA locator strategy ({@code using}) + its value, as the agent expects. */
|
|
880
|
+
type IosLocator = {
|
|
881
|
+
using: string;
|
|
882
|
+
value: string;
|
|
883
|
+
};
|
|
884
|
+
/**
|
|
885
|
+
* The semantic transport `IosDriver` talks to: element lookups return opaque
|
|
886
|
+
* element ids, and interactions take those ids. The HTTP/WDA implementation lives
|
|
887
|
+
* in {@link ./ios-agent.js}; tests fake this interface.
|
|
888
|
+
*/
|
|
889
|
+
interface IosAgentClient {
|
|
890
|
+
/** Resolve the first element matching `query`, or null when none match. */
|
|
891
|
+
findElement(query: IosQuery): Promise<string | null>;
|
|
892
|
+
/** Resolve every element id matching `query` (empty when none match). */
|
|
893
|
+
findElements(query: IosQuery): Promise<string[]>;
|
|
894
|
+
click(elementId: string): Promise<void>;
|
|
895
|
+
/** Replace an element's text (W3C `element/value`). */
|
|
896
|
+
setValue(elementId: string, text: string): Promise<void>;
|
|
897
|
+
getText(elementId: string): Promise<string | null>;
|
|
898
|
+
/** Send raw key sequences to the focused element (WDA `/wda/keys`). */
|
|
899
|
+
sendKeys(keys: string[]): Promise<void>;
|
|
900
|
+
/** Return to the springboard home screen (WDA `/wda/homescreen`). */
|
|
901
|
+
homescreen(): Promise<void>;
|
|
902
|
+
close(): Promise<void>;
|
|
903
|
+
}
|
|
904
|
+
type IosDriverOptions = {
|
|
905
|
+
/** Bundle id, used only for the informational `currentUrl()` value. */
|
|
906
|
+
appLabel?: string;
|
|
907
|
+
/** Capture a PNG screenshot to `path` (injected simctl capture). */
|
|
908
|
+
captureScreenshot: (path: string) => Promise<void>;
|
|
909
|
+
};
|
|
910
|
+
/**
|
|
911
|
+
* Key names the `press` step supports on iOS, sorted for error messages. `enter`/
|
|
912
|
+
* `return` send a newline, `delete`/`backspace` send a backspace (both via WDA's
|
|
913
|
+
* key endpoint), and `home` returns to the springboard.
|
|
914
|
+
*/
|
|
915
|
+
declare const IOS_PRESS_KEYS: readonly string[];
|
|
916
|
+
/** Escape a string for embedding inside a double-quoted NSPredicate string literal. */
|
|
917
|
+
declare function escapePredicateArg(value: string): string;
|
|
918
|
+
/** Prefix `XCUIElementType` onto a role shorthand (e.g. `Button`) when missing. */
|
|
919
|
+
declare function normalizeXcuiClassName(role: string): string;
|
|
920
|
+
/** Parse a Prowl selector string into an {@link IosQuery}. Bare text matches by text. */
|
|
921
|
+
declare function parseIosSelector(selector: string): IosQuery;
|
|
922
|
+
/**
|
|
923
|
+
* Translate an {@link IosQuery} into a WDA locator strategy. `id` uses the native
|
|
924
|
+
* `accessibility id` strategy and bare roles use `class name`; everything else
|
|
925
|
+
* composes an NSPredicate string (WDA's `predicate string` strategy).
|
|
926
|
+
*/
|
|
927
|
+
declare function iosQueryToLocator(query: IosQuery): IosLocator;
|
|
928
|
+
/** The literal text a `text=` selector matches, else null (mirrors the web driver). */
|
|
929
|
+
declare function unwrapIosTextSelector(selector: string): string | null;
|
|
930
|
+
/** Wrap a live {@link IosAgentClient} as a {@link SessionDriver}. */
|
|
931
|
+
declare function createIosDriver(client: IosAgentClient, options: IosDriverOptions): SessionDriver;
|
|
932
|
+
|
|
933
|
+
/** Result of one `xcrun` invocation. */
|
|
934
|
+
type SimctlResult = {
|
|
935
|
+
stdout: string;
|
|
936
|
+
stderr: string;
|
|
937
|
+
code: number;
|
|
938
|
+
};
|
|
939
|
+
/** Runs one `xcrun <args>` command to completion and resolves with its output. */
|
|
940
|
+
type SimctlRunner = (args: string[], options?: {
|
|
941
|
+
timeoutMs?: number;
|
|
942
|
+
env?: NodeJS.ProcessEnv;
|
|
943
|
+
}) => Promise<SimctlResult>;
|
|
944
|
+
/** One simulator device from `simctl list devices --json`. */
|
|
945
|
+
type SimDevice = {
|
|
946
|
+
udid: string;
|
|
947
|
+
name: string;
|
|
948
|
+
state: string;
|
|
949
|
+
runtime: string;
|
|
950
|
+
isAvailable: boolean;
|
|
951
|
+
};
|
|
952
|
+
type SimulatorReservation = {
|
|
953
|
+
udid: string;
|
|
954
|
+
release(): Promise<void>;
|
|
955
|
+
};
|
|
956
|
+
type ReserveSimulatorOptions = {
|
|
957
|
+
/** Override the lock directory root; intended for tests. */
|
|
958
|
+
lockRoot?: string;
|
|
959
|
+
};
|
|
960
|
+
declare const DEFAULT_SIMULATOR_LOCK_ROOT: string;
|
|
961
|
+
/**
|
|
962
|
+
* Reserve a simulator UDID across processes. The lock is held until `release` is
|
|
963
|
+
* called, preventing one session's teardown from terminating another session's
|
|
964
|
+
* WDA runner or target app on the same simulator.
|
|
965
|
+
*/
|
|
966
|
+
declare function reserveSimulatorUdid(udid: string, options?: ReserveSimulatorOptions): Promise<SimulatorReservation>;
|
|
967
|
+
/**
|
|
968
|
+
* Allocate a free local TCP port by binding to :0 and releasing it. The returned
|
|
969
|
+
* port is advisory after release; callers that hand it to another process must
|
|
970
|
+
* handle a later bind/readiness failure.
|
|
971
|
+
*/
|
|
972
|
+
declare function findFreePort(): Promise<number>;
|
|
973
|
+
/**
|
|
974
|
+
* Parse `simctl list devices --json` into a flat device list. The JSON maps
|
|
975
|
+
* runtime identifiers to arrays of devices; the runtime is folded into each row.
|
|
976
|
+
*/
|
|
977
|
+
declare function parseSimctlDevices(json: string): SimDevice[];
|
|
978
|
+
/** Simulators that are currently booted. */
|
|
979
|
+
declare function bootedSimulators(devices: SimDevice[]): SimDevice[];
|
|
980
|
+
/**
|
|
981
|
+
* Choose which simulator to drive. With `requested` set it must exist and be
|
|
982
|
+
* booted. Otherwise exactly one booted simulator is required; zero or many raise
|
|
983
|
+
* an actionable error listing what was found.
|
|
984
|
+
*/
|
|
985
|
+
declare function selectSimulatorUdid(devices: SimDevice[], requested?: string): string;
|
|
986
|
+
/** List all simulators via `simctl list devices --json`. */
|
|
987
|
+
declare function listSimulators(runner: SimctlRunner): Promise<SimDevice[]>;
|
|
988
|
+
/** Parse the `x.y[.z]` version out of `xcodebuild -version` output. */
|
|
989
|
+
declare function parseXcodeVersion(stdout: string): string | null;
|
|
990
|
+
|
|
991
|
+
/** Bundle id of the prebuilt WebDriverAgent runner (its xctest host app). */
|
|
992
|
+
declare const WDA_RUNNER_BUNDLE_ID = "com.facebook.WebDriverAgentRunner.xctrunner";
|
|
993
|
+
/** The runner `.app` produced by `xcodebuild build-for-testing`. */
|
|
994
|
+
declare const WDA_RUNNER_APP_NAME = "WebDriverAgentRunner-Runner.app";
|
|
995
|
+
/** Establishes a live {@link IosAgentClient} against a running WDA HTTP server. */
|
|
996
|
+
type IosAgentConnector = (options: {
|
|
997
|
+
host: string;
|
|
998
|
+
port: number;
|
|
999
|
+
bundleId: string;
|
|
1000
|
+
requestTimeoutMs: number;
|
|
1001
|
+
readyDeadlineMs: number;
|
|
1002
|
+
}) => Promise<IosAgentClient>;
|
|
1003
|
+
/** Default connector: builds the HTTP transport, waits for readiness, opens a session. */
|
|
1004
|
+
declare const defaultIosAgentConnector: IosAgentConnector;
|
|
1005
|
+
/** Resolve the WDA Xcode project bundled inside `appium-webdriveragent`. */
|
|
1006
|
+
declare function resolveWdaProject(requireFn?: NodeRequire): {
|
|
1007
|
+
projectPath: string;
|
|
1008
|
+
version: string;
|
|
1009
|
+
};
|
|
1010
|
+
/** Cache directory for a built WDA runner, keyed on the WDA + Xcode versions. */
|
|
1011
|
+
declare function wdaCacheDir(wdaVersion: string, xcode: string, homeDir?: string): string;
|
|
1012
|
+
type ResolveWdaRunnerOptions = {
|
|
1013
|
+
runner?: SimctlRunner;
|
|
1014
|
+
env?: NodeJS.ProcessEnv;
|
|
1015
|
+
homeDir?: string;
|
|
1016
|
+
requireFn?: NodeRequire;
|
|
1017
|
+
/** One-line progress notice sink (defaults to stderr). */
|
|
1018
|
+
logger?: (message: string) => void;
|
|
1019
|
+
};
|
|
1020
|
+
/**
|
|
1021
|
+
* Resolve a WebDriverAgent runner `.app`. Order: (a) the `PROWL_WDA_RUNNER` env
|
|
1022
|
+
* override; (b) a previously built runner in the version-keyed cache; (c) a
|
|
1023
|
+
* one-time `xcodebuild build-for-testing` that populates the cache. Simulators
|
|
1024
|
+
* need no code signing. Throws actionable errors when Xcode is missing or the
|
|
1025
|
+
* build fails.
|
|
1026
|
+
*/
|
|
1027
|
+
declare function resolveWdaRunner(options?: ResolveWdaRunnerOptions): Promise<string>;
|
|
1028
|
+
type IosSession = {
|
|
1029
|
+
client: IosAgentClient;
|
|
1030
|
+
driver: SessionDriver;
|
|
1031
|
+
/** The resolved bundle id being driven. */
|
|
1032
|
+
bundleId: string;
|
|
1033
|
+
/** The UDID of the simulator being driven. */
|
|
1034
|
+
udid: string;
|
|
1035
|
+
/** Tear down the session, WDA runner, and target app (best effort). */
|
|
1036
|
+
teardown(): Promise<void>;
|
|
1037
|
+
};
|
|
1038
|
+
type LaunchIosOptions = {
|
|
1039
|
+
/** Bundle id or `.app` path. */
|
|
1040
|
+
app: string;
|
|
1041
|
+
/** Simulator UDID when more than one is booted. */
|
|
1042
|
+
udid?: string;
|
|
1043
|
+
/** Uninstall+reinstall the app before launch (requires a `.app` path). */
|
|
1044
|
+
coldStart?: boolean;
|
|
1045
|
+
timeoutMs?: number;
|
|
1046
|
+
runner?: SimctlRunner;
|
|
1047
|
+
portAllocator?: () => Promise<number>;
|
|
1048
|
+
agentConnector?: IosAgentConnector;
|
|
1049
|
+
/** Skip WDA build/resolution by supplying the runner app path directly. */
|
|
1050
|
+
wdaRunnerApp?: string;
|
|
1051
|
+
/** Optional app scope guardrail from config.guardrails.allowedApps. */
|
|
1052
|
+
allowedApps?: string[];
|
|
1053
|
+
/** Override the simulator lock root; intended for tests. */
|
|
1054
|
+
simulatorLockRoot?: string;
|
|
1055
|
+
logger?: (message: string) => void;
|
|
1056
|
+
};
|
|
1057
|
+
/**
|
|
1058
|
+
* Preflight, install, launch, and attach WDA, returning a live {@link IosSession}.
|
|
1059
|
+
* Actionable errors cover each failure mode: Xcode/simctl missing, no booted
|
|
1060
|
+
* simulator (device selection), WDA build failures, agent unreachable (readiness).
|
|
1061
|
+
*/
|
|
1062
|
+
declare function launchIosSession(options: LaunchIosOptions): Promise<IosSession>;
|
|
1063
|
+
/** Tear down an iOS session (agent session, WDA runner, target app). */
|
|
1064
|
+
declare function closeIosSession(session: IosSession): Promise<void>;
|
|
1065
|
+
|
|
1066
|
+
/**
|
|
1067
|
+
* PROWL-059 / ARCH-010 — HTTP/JSON transport for the on-simulator WebDriverAgent.
|
|
1068
|
+
*
|
|
1069
|
+
* WebDriverAgent (WDA) exposes W3C-WebDriver-shaped endpoints over plain HTTP. On
|
|
1070
|
+
* a simulator it is reachable directly at `http://127.0.0.1:<port>` (no tunnel),
|
|
1071
|
+
* so this module speaks it with the global `fetch` (no heavy WebDriver SDK, per
|
|
1072
|
+
* the `ai.ts` ethos), mirroring the Android agent's ergonomics: a per-request
|
|
1073
|
+
* deadline via `AbortController`, unref'd timers, and W3C element-key extraction.
|
|
1074
|
+
* It exposes the semantic {@link IosAgentClient} the driver consumes; tests fake
|
|
1075
|
+
* either the `fetch` implementation or the client itself.
|
|
1076
|
+
*/
|
|
1077
|
+
|
|
1078
|
+
/** The subset of `fetch` this module uses; overridable in tests. */
|
|
1079
|
+
type FetchLike = (url: string, init: RequestInit) => Promise<Response>;
|
|
1080
|
+
/** Default per-request deadline for the agent transport. */
|
|
1081
|
+
declare const DEFAULT_WDA_REQUEST_TIMEOUT_MS = 30000;
|
|
1082
|
+
type WdaTransportOptions = {
|
|
1083
|
+
/** Base URL, e.g. `http://127.0.0.1:8100`. */
|
|
1084
|
+
baseUrl: string;
|
|
1085
|
+
requestTimeoutMs?: number;
|
|
1086
|
+
fetchImpl?: FetchLike;
|
|
1087
|
+
};
|
|
1088
|
+
/** An HTTP-level failure from WDA, carrying the status and parsed body. */
|
|
1089
|
+
declare class WdaHttpError extends Error {
|
|
1090
|
+
readonly status: number;
|
|
1091
|
+
readonly webdriverError?: string | undefined;
|
|
1092
|
+
constructor(message: string, status: number, webdriverError?: string | undefined);
|
|
1093
|
+
}
|
|
1094
|
+
/** Low-level request/response transport with a per-request deadline. */
|
|
1095
|
+
declare class WdaTransport {
|
|
1096
|
+
private readonly baseUrl;
|
|
1097
|
+
private readonly requestTimeoutMs;
|
|
1098
|
+
private readonly fetchImpl;
|
|
1099
|
+
constructor(options: WdaTransportOptions);
|
|
1100
|
+
/**
|
|
1101
|
+
* Send one request and return the full parsed JSON body. Rejects with a
|
|
1102
|
+
* {@link WdaHttpError} on a non-2xx response, or a timeout error when the
|
|
1103
|
+
* per-request deadline elapses.
|
|
1104
|
+
*/
|
|
1105
|
+
requestFull(method: string, path: string, body?: unknown, timeoutMs?: number): Promise<unknown>;
|
|
1106
|
+
/** Like {@link requestFull} but returns just the `value` field. */
|
|
1107
|
+
request(method: string, path: string, body?: unknown, timeoutMs?: number): Promise<unknown>;
|
|
1108
|
+
}
|
|
1109
|
+
/**
|
|
1110
|
+
* Create a WDA session bound to `bundleId` and return its id. WDA activates the
|
|
1111
|
+
* app under test named in `alwaysMatch.bundleId`. The session id may arrive at
|
|
1112
|
+
* the envelope root or inside `value`, so both are checked.
|
|
1113
|
+
*/
|
|
1114
|
+
declare function createWdaSession(transport: WdaTransport, bundleId: string): Promise<string>;
|
|
1115
|
+
/** Poll `GET /status` until WDA reports ready or the deadline elapses. */
|
|
1116
|
+
declare function waitForWdaReady(transport: WdaTransport, options?: {
|
|
1117
|
+
deadlineMs: number;
|
|
1118
|
+
intervalMs?: number;
|
|
1119
|
+
}): Promise<void>;
|
|
1120
|
+
/**
|
|
1121
|
+
* Build the semantic {@link IosAgentClient} over a live session. `close` deletes
|
|
1122
|
+
* the session (best effort); the transport itself is stateless.
|
|
1123
|
+
*/
|
|
1124
|
+
declare function createWdaAgentClient(transport: WdaTransport, sessionId: string): IosAgentClient;
|
|
1125
|
+
|
|
1126
|
+
/**
|
|
1127
|
+
* PROWL-048 / ARCH-002 — target ⇄ step compatibility.
|
|
1128
|
+
*
|
|
1129
|
+
* The runtime capability gate in the step runner already rejects steps a driver
|
|
1130
|
+
* cannot honor. This adds a friendlier, earlier validation-time rejection: when
|
|
1131
|
+
* the selected target is macOS, hunts using web-only steps fail fast with a
|
|
1132
|
+
* clear message before anything launches.
|
|
1133
|
+
*/
|
|
1134
|
+
|
|
1135
|
+
/**
|
|
1136
|
+
* Step types that only make sense on the web target. Everything else
|
|
1137
|
+
* (`click`, `fill`, `type`, `press`, `wait`, `assert visible/notVisible`,
|
|
1138
|
+
* `screenshot`, `assertScreenshot`, `repeat`, `runHunt`, `if`, `copyText`,
|
|
1139
|
+
* `hover`, `scrollTo`, `waitForSelector`) is portable across targets.
|
|
1140
|
+
*/
|
|
1141
|
+
declare const WEB_ONLY_STEP_TYPES: ReadonlySet<string>;
|
|
1142
|
+
/** The web-only reason a step is unsupported on a non-web target, or null if portable. */
|
|
1143
|
+
declare function webOnlyReason(step: Step): string | null;
|
|
1144
|
+
/**
|
|
1145
|
+
* Throw if any step in `steps` (recursing into `if`/`repeat` bodies) is not
|
|
1146
|
+
* supported by `target`. `runHunt` references are validated when the referenced
|
|
1147
|
+
* hunt itself runs. No-op for the web target; every native target (macOS,
|
|
1148
|
+
* Android, iOS) rejects the same web-only step vocabulary.
|
|
1149
|
+
*/
|
|
1150
|
+
declare function assertStepsSupportedByTarget(steps: Step[], target: Target["type"]): void;
|
|
1151
|
+
/** Read the bundle id from an iOS `.app` (root `Info.plist`), or null. */
|
|
1152
|
+
declare function readIosBundleIdentifier(appPath: string): string | null;
|
|
1153
|
+
/**
|
|
1154
|
+
* Accepted identities for an iOS target app. A bare bundle id matches itself; a
|
|
1155
|
+
* `.app` path is authorized by its path, bundle name, or the `CFBundleIdentifier`
|
|
1156
|
+
* read from the bundle's root `Info.plist`.
|
|
1157
|
+
*/
|
|
1158
|
+
declare function iosAppAllowedIdentities(app: string): string[];
|
|
1159
|
+
/**
|
|
1160
|
+
* Native scope guardrail: if `allowedApps` is non-empty it must include an
|
|
1161
|
+
* accepted identity for the target app: bundle id, app bundle name, or `.app`
|
|
1162
|
+
* path. An empty list means the scope is unset and the target app is implicitly
|
|
1163
|
+
* allowed — mirroring how allowedDomains auto-includes the web target's host.
|
|
1164
|
+
*/
|
|
1165
|
+
declare function assertTargetAppAllowed(allowedApps: string[], app: string): void;
|
|
1166
|
+
/**
|
|
1167
|
+
* iOS scope guardrail (PROWL-059): mirrors {@link assertTargetAppAllowed} but
|
|
1168
|
+
* resolves identities via {@link iosAppAllowedIdentities} (bundle id or `.app`
|
|
1169
|
+
* path with the id read from the bundle's root `Info.plist`).
|
|
1170
|
+
*/
|
|
1171
|
+
declare function assertIosAppAllowed(allowedApps: string[], app: string): void;
|
|
1172
|
+
/**
|
|
1173
|
+
* Accepted identities for an Android target app. A bare package name matches
|
|
1174
|
+
* itself. An `.apk` path is authorized only by its canonical full path; pass the
|
|
1175
|
+
* resolved package name when validating an APK after `aapt` resolution so
|
|
1176
|
+
* package-ID allowlists can authorize it before install.
|
|
1177
|
+
*/
|
|
1178
|
+
declare function androidAppAllowedIdentities(app: string, resolvedPackage?: string): string[];
|
|
1179
|
+
/**
|
|
1180
|
+
* Android scope guardrail (PROWL-058): mirrors {@link assertTargetAppAllowed} but
|
|
1181
|
+
* resolves identities via {@link androidAppAllowedIdentities} (package name or
|
|
1182
|
+
* canonical APK path) rather than macOS bundle identities.
|
|
1183
|
+
*/
|
|
1184
|
+
declare function assertAndroidAppAllowed(allowedApps: string[], app: string, resolvedPackage?: string): void;
|
|
1185
|
+
|
|
342
1186
|
type StepCallback = (result: StepResult, step: Step, index: number) => void;
|
|
343
1187
|
|
|
344
1188
|
type RunOptions = {
|
|
@@ -353,6 +1197,12 @@ type RunOptions = {
|
|
|
353
1197
|
channel?: BrowserChannel;
|
|
354
1198
|
viewport?: string;
|
|
355
1199
|
junit?: boolean;
|
|
1200
|
+
/** Inject a macOS helper client (tests / a prebuilt binary); defaults to spawning the helper. */
|
|
1201
|
+
macClientFactory?: () => MacHelperClient;
|
|
1202
|
+
/** Inject an Android session factory (tests); defaults to {@link launchAndroidSession}. */
|
|
1203
|
+
androidSessionFactory?: (options: LaunchAndroidOptions) => Promise<AndroidSession>;
|
|
1204
|
+
/** Inject an iOS session factory (tests); defaults to {@link launchIosSession}. */
|
|
1205
|
+
iosSessionFactory?: (options: LaunchIosOptions) => Promise<IosSession>;
|
|
356
1206
|
};
|
|
357
1207
|
declare function runHunt(options: RunOptions): Promise<{
|
|
358
1208
|
result: RunResult;
|
|
@@ -468,6 +1318,16 @@ type RankFlakyOptions = {
|
|
|
468
1318
|
*/
|
|
469
1319
|
declare function rankFlaky(configDir: string, options?: RankFlakyOptions): FlakyScore[];
|
|
470
1320
|
|
|
1321
|
+
/**
|
|
1322
|
+
* The minimal probe surface healing needs: count matches for a candidate
|
|
1323
|
+
* selector. A live Playwright page and a {@link SessionDriver}-backed adapter
|
|
1324
|
+
* both satisfy it, so healing carries no Playwright dependency.
|
|
1325
|
+
*/
|
|
1326
|
+
type SelectorProbe = {
|
|
1327
|
+
locator(selector: string): {
|
|
1328
|
+
count(): Promise<number>;
|
|
1329
|
+
};
|
|
1330
|
+
};
|
|
471
1331
|
/**
|
|
472
1332
|
* Self-healing selectors (PROWL-023). When an explicit selector matches nothing,
|
|
473
1333
|
* derive the human "intent" from the selector and try alternative strategies —
|
|
@@ -508,7 +1368,7 @@ declare function buildHealCandidates(selector: string): Array<{
|
|
|
508
1368
|
* resolves to exactly one element; otherwise null. Counting is delegated so this
|
|
509
1369
|
* is unit-testable with a fake page.
|
|
510
1370
|
*/
|
|
511
|
-
declare function healSelector(
|
|
1371
|
+
declare function healSelector(probe: SelectorProbe, selector: string, options: {
|
|
512
1372
|
enabled: boolean;
|
|
513
1373
|
}): Promise<HealResult | null>;
|
|
514
1374
|
|
|
@@ -541,13 +1401,55 @@ declare function loadHuntMeta(huntName: string, configDir: string): {
|
|
|
541
1401
|
declare function listHunts(configDir: string): string[];
|
|
542
1402
|
|
|
543
1403
|
declare const configSchema: z.ZodObject<{
|
|
544
|
-
target: z.ZodObject<{
|
|
1404
|
+
target: z.ZodUnion<[z.ZodObject<{
|
|
1405
|
+
type: z.ZodLiteral<"macos">;
|
|
1406
|
+
app: z.ZodString;
|
|
1407
|
+
}, "strict", z.ZodTypeAny, {
|
|
1408
|
+
app: string;
|
|
1409
|
+
type: "macos";
|
|
1410
|
+
}, {
|
|
1411
|
+
app: string;
|
|
1412
|
+
type: "macos";
|
|
1413
|
+
}>, z.ZodObject<{
|
|
1414
|
+
type: z.ZodLiteral<"android">;
|
|
1415
|
+
app: z.ZodString;
|
|
1416
|
+
deviceSerial: z.ZodOptional<z.ZodString>;
|
|
1417
|
+
coldStart: z.ZodOptional<z.ZodBoolean>;
|
|
1418
|
+
}, "strict", z.ZodTypeAny, {
|
|
1419
|
+
app: string;
|
|
1420
|
+
type: "android";
|
|
1421
|
+
deviceSerial?: string | undefined;
|
|
1422
|
+
coldStart?: boolean | undefined;
|
|
1423
|
+
}, {
|
|
1424
|
+
app: string;
|
|
1425
|
+
type: "android";
|
|
1426
|
+
deviceSerial?: string | undefined;
|
|
1427
|
+
coldStart?: boolean | undefined;
|
|
1428
|
+
}>, z.ZodObject<{
|
|
1429
|
+
type: z.ZodLiteral<"ios">;
|
|
1430
|
+
app: z.ZodString;
|
|
1431
|
+
udid: z.ZodOptional<z.ZodString>;
|
|
1432
|
+
coldStart: z.ZodOptional<z.ZodBoolean>;
|
|
1433
|
+
}, "strict", z.ZodTypeAny, {
|
|
1434
|
+
app: string;
|
|
1435
|
+
type: "ios";
|
|
1436
|
+
udid?: string | undefined;
|
|
1437
|
+
coldStart?: boolean | undefined;
|
|
1438
|
+
}, {
|
|
1439
|
+
app: string;
|
|
1440
|
+
type: "ios";
|
|
1441
|
+
udid?: string | undefined;
|
|
1442
|
+
coldStart?: boolean | undefined;
|
|
1443
|
+
}>, z.ZodObject<{
|
|
1444
|
+
type: z.ZodOptional<z.ZodLiteral<"web">>;
|
|
545
1445
|
url: z.ZodString;
|
|
546
|
-
}, "
|
|
1446
|
+
}, "strict", z.ZodTypeAny, {
|
|
547
1447
|
url: string;
|
|
1448
|
+
type?: "web" | undefined;
|
|
548
1449
|
}, {
|
|
549
1450
|
url: string;
|
|
550
|
-
|
|
1451
|
+
type?: "web" | undefined;
|
|
1452
|
+
}>]>;
|
|
551
1453
|
browser: z.ZodOptional<z.ZodObject<{
|
|
552
1454
|
headless: z.ZodOptional<z.ZodBoolean>;
|
|
553
1455
|
slowMo: z.ZodOptional<z.ZodNumber>;
|
|
@@ -565,9 +1467,9 @@ declare const configSchema: z.ZodObject<{
|
|
|
565
1467
|
height: number;
|
|
566
1468
|
}>]>>;
|
|
567
1469
|
}, "strip", z.ZodTypeAny, {
|
|
1470
|
+
timeout?: number | undefined;
|
|
568
1471
|
headless?: boolean | undefined;
|
|
569
1472
|
slowMo?: number | undefined;
|
|
570
|
-
timeout?: number | undefined;
|
|
571
1473
|
engine?: "chromium" | "firefox" | "webkit" | undefined;
|
|
572
1474
|
channel?: "chromium" | "chrome" | "chrome-beta" | "chrome-canary" | "chrome-dev" | "msedge" | "msedge-beta" | "msedge-canary" | "msedge-dev" | undefined;
|
|
573
1475
|
viewport?: "mobile" | "tablet" | "desktop" | {
|
|
@@ -575,9 +1477,9 @@ declare const configSchema: z.ZodObject<{
|
|
|
575
1477
|
height: number;
|
|
576
1478
|
} | undefined;
|
|
577
1479
|
}, {
|
|
1480
|
+
timeout?: number | undefined;
|
|
578
1481
|
headless?: boolean | undefined;
|
|
579
1482
|
slowMo?: number | undefined;
|
|
580
|
-
timeout?: number | undefined;
|
|
581
1483
|
engine?: "chromium" | "firefox" | "webkit" | undefined;
|
|
582
1484
|
channel?: "chromium" | "chrome" | "chrome-beta" | "chrome-canary" | "chrome-dev" | "msedge" | "msedge-beta" | "msedge-canary" | "msedge-dev" | undefined;
|
|
583
1485
|
viewport?: "mobile" | "tablet" | "desktop" | {
|
|
@@ -620,16 +1522,19 @@ declare const configSchema: z.ZodObject<{
|
|
|
620
1522
|
guardrails: z.ZodOptional<z.ZodObject<{
|
|
621
1523
|
maxSteps: z.ZodOptional<z.ZodNumber>;
|
|
622
1524
|
allowedDomains: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
1525
|
+
allowedApps: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
623
1526
|
forbiddenSelectors: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
624
1527
|
selfHealing: z.ZodOptional<z.ZodBoolean>;
|
|
625
1528
|
}, "strip", z.ZodTypeAny, {
|
|
626
1529
|
maxSteps?: number | undefined;
|
|
627
1530
|
allowedDomains?: string[] | undefined;
|
|
1531
|
+
allowedApps?: string[] | undefined;
|
|
628
1532
|
forbiddenSelectors?: string[] | undefined;
|
|
629
1533
|
selfHealing?: boolean | undefined;
|
|
630
1534
|
}, {
|
|
631
1535
|
maxSteps?: number | undefined;
|
|
632
1536
|
allowedDomains?: string[] | undefined;
|
|
1537
|
+
allowedApps?: string[] | undefined;
|
|
633
1538
|
forbiddenSelectors?: string[] | undefined;
|
|
634
1539
|
selfHealing?: boolean | undefined;
|
|
635
1540
|
}>>;
|
|
@@ -677,11 +1582,25 @@ declare const configSchema: z.ZodObject<{
|
|
|
677
1582
|
}, "strict", z.ZodTypeAny, {
|
|
678
1583
|
target: {
|
|
679
1584
|
url: string;
|
|
1585
|
+
type?: "web" | undefined;
|
|
1586
|
+
} | {
|
|
1587
|
+
app: string;
|
|
1588
|
+
type: "macos";
|
|
1589
|
+
} | {
|
|
1590
|
+
app: string;
|
|
1591
|
+
type: "android";
|
|
1592
|
+
deviceSerial?: string | undefined;
|
|
1593
|
+
coldStart?: boolean | undefined;
|
|
1594
|
+
} | {
|
|
1595
|
+
app: string;
|
|
1596
|
+
type: "ios";
|
|
1597
|
+
udid?: string | undefined;
|
|
1598
|
+
coldStart?: boolean | undefined;
|
|
680
1599
|
};
|
|
681
1600
|
browser?: {
|
|
1601
|
+
timeout?: number | undefined;
|
|
682
1602
|
headless?: boolean | undefined;
|
|
683
1603
|
slowMo?: number | undefined;
|
|
684
|
-
timeout?: number | undefined;
|
|
685
1604
|
engine?: "chromium" | "firefox" | "webkit" | undefined;
|
|
686
1605
|
channel?: "chromium" | "chrome" | "chrome-beta" | "chrome-canary" | "chrome-dev" | "msedge" | "msedge-beta" | "msedge-canary" | "msedge-dev" | undefined;
|
|
687
1606
|
viewport?: "mobile" | "tablet" | "desktop" | {
|
|
@@ -704,6 +1623,7 @@ declare const configSchema: z.ZodObject<{
|
|
|
704
1623
|
guardrails?: {
|
|
705
1624
|
maxSteps?: number | undefined;
|
|
706
1625
|
allowedDomains?: string[] | undefined;
|
|
1626
|
+
allowedApps?: string[] | undefined;
|
|
707
1627
|
forbiddenSelectors?: string[] | undefined;
|
|
708
1628
|
selfHealing?: boolean | undefined;
|
|
709
1629
|
} | undefined;
|
|
@@ -727,11 +1647,25 @@ declare const configSchema: z.ZodObject<{
|
|
|
727
1647
|
}, {
|
|
728
1648
|
target: {
|
|
729
1649
|
url: string;
|
|
1650
|
+
type?: "web" | undefined;
|
|
1651
|
+
} | {
|
|
1652
|
+
app: string;
|
|
1653
|
+
type: "macos";
|
|
1654
|
+
} | {
|
|
1655
|
+
app: string;
|
|
1656
|
+
type: "android";
|
|
1657
|
+
deviceSerial?: string | undefined;
|
|
1658
|
+
coldStart?: boolean | undefined;
|
|
1659
|
+
} | {
|
|
1660
|
+
app: string;
|
|
1661
|
+
type: "ios";
|
|
1662
|
+
udid?: string | undefined;
|
|
1663
|
+
coldStart?: boolean | undefined;
|
|
730
1664
|
};
|
|
731
1665
|
browser?: {
|
|
1666
|
+
timeout?: number | undefined;
|
|
732
1667
|
headless?: boolean | undefined;
|
|
733
1668
|
slowMo?: number | undefined;
|
|
734
|
-
timeout?: number | undefined;
|
|
735
1669
|
engine?: "chromium" | "firefox" | "webkit" | undefined;
|
|
736
1670
|
channel?: "chromium" | "chrome" | "chrome-beta" | "chrome-canary" | "chrome-dev" | "msedge" | "msedge-beta" | "msedge-canary" | "msedge-dev" | undefined;
|
|
737
1671
|
viewport?: "mobile" | "tablet" | "desktop" | {
|
|
@@ -754,6 +1688,7 @@ declare const configSchema: z.ZodObject<{
|
|
|
754
1688
|
guardrails?: {
|
|
755
1689
|
maxSteps?: number | undefined;
|
|
756
1690
|
allowedDomains?: string[] | undefined;
|
|
1691
|
+
allowedApps?: string[] | undefined;
|
|
757
1692
|
forbiddenSelectors?: string[] | undefined;
|
|
758
1693
|
selfHealing?: boolean | undefined;
|
|
759
1694
|
} | undefined;
|
|
@@ -831,6 +1766,7 @@ declare const huntSchema: z.ZodObject<{
|
|
|
831
1766
|
}>>;
|
|
832
1767
|
}, "strict", z.ZodTypeAny, {
|
|
833
1768
|
steps: Step[];
|
|
1769
|
+
name?: string | undefined;
|
|
834
1770
|
assertions?: ({
|
|
835
1771
|
selectorExists: string;
|
|
836
1772
|
} | {
|
|
@@ -844,7 +1780,6 @@ declare const huntSchema: z.ZodObject<{
|
|
|
844
1780
|
} | {
|
|
845
1781
|
noNetworkErrors: boolean;
|
|
846
1782
|
})[] | undefined;
|
|
847
|
-
name?: string | undefined;
|
|
848
1783
|
vars?: Record<string, string> | undefined;
|
|
849
1784
|
description?: string | undefined;
|
|
850
1785
|
tags?: string[] | undefined;
|
|
@@ -854,6 +1789,7 @@ declare const huntSchema: z.ZodObject<{
|
|
|
854
1789
|
} | undefined;
|
|
855
1790
|
}, {
|
|
856
1791
|
steps: Step[];
|
|
1792
|
+
name?: string | undefined;
|
|
857
1793
|
assertions?: ({
|
|
858
1794
|
selectorExists: string;
|
|
859
1795
|
} | {
|
|
@@ -867,7 +1803,6 @@ declare const huntSchema: z.ZodObject<{
|
|
|
867
1803
|
} | {
|
|
868
1804
|
noNetworkErrors: boolean;
|
|
869
1805
|
})[] | undefined;
|
|
870
|
-
name?: string | undefined;
|
|
871
1806
|
vars?: Record<string, string> | undefined;
|
|
872
1807
|
description?: string | undefined;
|
|
873
1808
|
tags?: string[] | undefined;
|
|
@@ -885,6 +1820,15 @@ type InterpolatedHunt = {
|
|
|
885
1820
|
};
|
|
886
1821
|
declare function interpolateHunt(hunt: Hunt, env: NodeJS.ProcessEnv, randomVars?: Record<string, string>): InterpolatedHunt;
|
|
887
1822
|
|
|
1823
|
+
/**
|
|
1824
|
+
* The minimal surface `analyzePage` needs: a way to evaluate a DOM-scraping
|
|
1825
|
+
* function in the page. Both a live Playwright page and a {@link SessionDriver}
|
|
1826
|
+
* satisfy it (the driver's `evaluate` verb), so the analyzer has no direct
|
|
1827
|
+
* Playwright dependency — its DOM scraping runs through the driver.
|
|
1828
|
+
*/
|
|
1829
|
+
type DomEvaluator = {
|
|
1830
|
+
evaluate(pageFunction: () => unknown): Promise<unknown>;
|
|
1831
|
+
};
|
|
888
1832
|
type PageElement = {
|
|
889
1833
|
tag: string;
|
|
890
1834
|
type?: string;
|
|
@@ -913,7 +1857,98 @@ type AnalysisResult = {
|
|
|
913
1857
|
forms: PageForm[];
|
|
914
1858
|
links: PageLink[];
|
|
915
1859
|
};
|
|
916
|
-
declare function analyzePage(page:
|
|
1860
|
+
declare function analyzePage(page: DomEvaluator): Promise<AnalysisResult>;
|
|
1861
|
+
|
|
1862
|
+
/**
|
|
1863
|
+
* PROWL-055 / ARCH-007 — macOS analyzer.
|
|
1864
|
+
*
|
|
1865
|
+
* The native analog of {@link analyzePage}: dumps a macOS app's interactive
|
|
1866
|
+
* elements with ranked selector candidates so hunt authors don't have to guess
|
|
1867
|
+
* accessibility identifiers. It talks to the same `prowl-macdriver` helper the
|
|
1868
|
+
* runner uses ({@link MacHelperClient}) — reading the AX tree, the window list,
|
|
1869
|
+
* and (read-only) the status-item menu — and shapes the result to mirror the web
|
|
1870
|
+
* analyzer's feel.
|
|
1871
|
+
*
|
|
1872
|
+
* Selector ranking (best → last resort), matching the driver's selector dialect
|
|
1873
|
+
* so the emitted selectors are directly usable in hunts:
|
|
1874
|
+
* id=<AXIdentifier> (best — the native `data-testid`)
|
|
1875
|
+
* label="<exact title/desc>" (exact accessibility label)
|
|
1876
|
+
* role=<role>[name="<name>"] (role + accessible name)
|
|
1877
|
+
* text="<substring>" (last resort — substring match)
|
|
1878
|
+
*
|
|
1879
|
+
* Read-only: the ONLY state-changing interaction is opening the status-item menu
|
|
1880
|
+
* to read its items (their identifiers are gold) and immediately closing it.
|
|
1881
|
+
*/
|
|
1882
|
+
|
|
1883
|
+
/**
|
|
1884
|
+
* The interactive AX roles the analyzer surfaces from the window tree. Menu
|
|
1885
|
+
* items are collected separately via the status-item menu; windows are listed
|
|
1886
|
+
* as navigable surfaces. Tuned from the roles the helper's `tree`/`openMenu`
|
|
1887
|
+
* payloads actually expose for controls.
|
|
1888
|
+
*/
|
|
1889
|
+
declare const INTERACTIVE_ROLES: ReadonlySet<string>;
|
|
1890
|
+
/** Default AX-tree depth requested from the helper (deeper than the run default). */
|
|
1891
|
+
declare const DEFAULT_ANALYZE_TREE_DEPTH = 20;
|
|
1892
|
+
/** A single element the `axInfo` snapshot describes (tree / menu / window node). */
|
|
1893
|
+
type MacAxNode = {
|
|
1894
|
+
role?: string;
|
|
1895
|
+
title?: string;
|
|
1896
|
+
description?: string;
|
|
1897
|
+
value?: string;
|
|
1898
|
+
identifier?: string;
|
|
1899
|
+
enabled?: boolean;
|
|
1900
|
+
children?: MacAxNode[];
|
|
1901
|
+
};
|
|
1902
|
+
/** An interactive element with ranked selector candidates (best first). */
|
|
1903
|
+
type MacAnalysisElement = {
|
|
1904
|
+
role: string;
|
|
1905
|
+
title?: string;
|
|
1906
|
+
description?: string;
|
|
1907
|
+
value?: string;
|
|
1908
|
+
identifier?: string;
|
|
1909
|
+
enabled?: boolean;
|
|
1910
|
+
/** Where the element was discovered: the app's window tree or the status menu. */
|
|
1911
|
+
source: "window" | "menu";
|
|
1912
|
+
/** Ranked selector candidates, best first. Always at least one entry. */
|
|
1913
|
+
selectors: string[];
|
|
1914
|
+
};
|
|
1915
|
+
/** A top-level window, exposed as a navigable surface with its best selector. */
|
|
1916
|
+
type MacAnalysisWindow = {
|
|
1917
|
+
title?: string;
|
|
1918
|
+
identifier?: string;
|
|
1919
|
+
/** Best selector candidate for the window. */
|
|
1920
|
+
selector: string;
|
|
1921
|
+
};
|
|
1922
|
+
type MacAnalysisResult = {
|
|
1923
|
+
/** Bundle id (or the app reference) of the analyzed app. */
|
|
1924
|
+
app: string;
|
|
1925
|
+
elements: MacAnalysisElement[];
|
|
1926
|
+
windows: MacAnalysisWindow[];
|
|
1927
|
+
menuItems: MacAnalysisElement[];
|
|
1928
|
+
};
|
|
1929
|
+
/**
|
|
1930
|
+
* Ranked selector candidates for an element, best first. Mirrors the driver's
|
|
1931
|
+
* selector dialect (`id=` > `label=` > `role=…[name="…"]` > `text=`). Falls back
|
|
1932
|
+
* to a bare `role=<role>` so every element with a role is at least addressable.
|
|
1933
|
+
*/
|
|
1934
|
+
declare function rankMacSelectors(node: MacAxNode): string[];
|
|
1935
|
+
type AnalyzeMacOptions = {
|
|
1936
|
+
/** Bundle id / app label to report in the result. */
|
|
1937
|
+
app: string;
|
|
1938
|
+
/** AX-tree depth to request from the helper. */
|
|
1939
|
+
treeDepth?: number;
|
|
1940
|
+
/** Timeout (seconds) for opening the status-item menu. */
|
|
1941
|
+
menuTimeoutSeconds?: number;
|
|
1942
|
+
};
|
|
1943
|
+
/**
|
|
1944
|
+
* Analyze an already-launched macOS app through `client`, returning its
|
|
1945
|
+
* interactive elements, windows, and status-menu items with ranked selectors.
|
|
1946
|
+
*
|
|
1947
|
+
* The caller owns the session lifecycle (launch + guardrails + teardown); this
|
|
1948
|
+
* function is read-only apart from opening and immediately closing the
|
|
1949
|
+
* status-item menu to read its contents.
|
|
1950
|
+
*/
|
|
1951
|
+
declare function analyzeMacApp(client: MacHelperClient, options: AnalyzeMacOptions): Promise<MacAnalysisResult>;
|
|
917
1952
|
|
|
918
1953
|
type AiProvider = "anthropic" | "openai";
|
|
919
1954
|
type AiConfig = {
|
|
@@ -932,4 +1967,4 @@ type GenerateOptions = {
|
|
|
932
1967
|
};
|
|
933
1968
|
declare function generateHunt(options: GenerateOptions): Promise<string>;
|
|
934
1969
|
|
|
935
|
-
export { type AnalysisResult, type AssertScreenshotStep, type Assertion, type AssertionResult, type BrowserChannel, type BrowserEngine, type BugFailure, type BugLogSummary, type CiFailureCluster, type CiFlakyHunt, type CiHuntResult, type CiResult, type CiStatus, type Config, DEFAULT_FLAKY_THRESHOLD, type EvalScriptStep, type FailureCluster, type FlakyScore, type GenerateOptions, type HealResult, type HistoryEntry, type HistoryFile, type Hunt, type IfStep, type MockRouteStep, type PageElement, type PageForm, type PageLink, type RankFlakyOptions, type ReliabilityConfig, type RepeatStep, type RunArtifacts, type RunOptions, type RunResult, type RunScriptStep, type RunSuiteHooks, type RunSuiteOptions, type RunSuiteResult, type Step, type StepResult, type UnmockRouteStep, type UpdateBacklogOptions, type Viewport, analyzePage, buildHealCandidates, clusterFailures, computeFlakeScore, configSchema, extractFailures, extractSelectorIntent, generateHunt, healSelector, huntSchema, interpolateHunt, listHunts, loadConfig, loadHunt, loadHuntMeta, loadHuntTags, rankFlaky, readHistory, readHuntHistory, runHunt, runSuite, stepSchema, updateBacklogFromSuite };
|
|
1970
|
+
export { ANDROID_KEYCODES, type AdbDevice, type AdbResult, type AdbRunner, type AdbSpawner, type AgentApks, type AgentConnector, type AnalysisResult, type AnalyzeMacOptions, type AndroidAgentClient, type AndroidDriverOptions, type AndroidLocator, type AndroidQuery, type AndroidSession, type AndroidTarget, type AssertScreenshotStep, type Assertion, type AssertionResult, type BrowserChannel, type BrowserEngine, type BugFailure, type BugLogSummary, type CiFailureCluster, type CiFlakyHunt, type CiHuntResult, type CiResult, type CiStatus, type Config, DEFAULT_AGENT_REQUEST_TIMEOUT_MS, DEFAULT_ANALYZE_TREE_DEPTH, DEFAULT_FLAKY_THRESHOLD, DEFAULT_REQUEST_TIMEOUT_MS, DEFAULT_SIMULATOR_LOCK_ROOT, DEFAULT_WDA_REQUEST_TIMEOUT_MS, type EvalScriptStep, type FailureCluster, type FetchLike$1 as FetchLike, type FlakyScore, type GenerateOptions, type HealResult, type HistoryEntry, type HistoryFile, type Hunt, INTERACTIVE_ROLES, IOS_PRESS_KEYS, type IfStep, type IosAgentClient, type IosAgentConnector, type IosDriverOptions, type IosLocator, type IosQuery, type IosSession, type IosTarget, type LaunchAndroidOptions, type LaunchIosOptions, type LaunchMacOptions, type MacAnalysisElement, type MacAnalysisResult, type MacAnalysisWindow, type MacAxNode, type MacDriverOptions, type MacHelperClient, type MacQuery, type MacSession, type MacosTarget, type MockRouteStep, type PageElement, type PageForm, type PageLink, type RankFlakyOptions, type ReliabilityConfig, type RepeatStep, type ReserveSimulatorOptions, type ResolveWdaRunnerOptions, type RunArtifacts, type RunOptions, type RunResult, type RunScriptStep, type RunSuiteHooks, type RunSuiteOptions, type RunSuiteResult, type SimDevice, type SimctlResult, type SimctlRunner, type SimulatorReservation, SpawnMacHelperClient, type SpawnMacHelperOptions, type Step, type StepResult, type Target, UIA2_REMOTE_PORT, Uia2HttpError, Uia2Transport, type Uia2TransportOptions, type UnmockRouteStep, type UpdateBacklogOptions, type Viewport, WDA_RUNNER_APP_NAME, WDA_RUNNER_BUNDLE_ID, WEB_ONLY_STEP_TYPES, WdaHttpError, WdaTransport, type WdaTransportOptions, type WebTarget, analyzeMacApp, analyzePage, androidAppAllowedIdentities, androidQueryToLocator, assertAndroidAppAllowed, assertIosAppAllowed, assertStepsSupportedByTarget, assertTargetAppAllowed, bootedDevices, bootedSimulators, buildHealCandidates, closeAndroidSession, closeIosSession, closeMacSession, clusterFailures, computeFlakeScore, configSchema, createAndroidDriver, createIosDriver, createMacDriver, createUia2AgentClient, createUia2Session, createWdaAgentClient, createWdaSession, defaultAgentConnector, defaultIosAgentConnector, escapePredicateArg, escapeUiSelectorArg, extractElementId, extractFailures, extractSelectorIntent, findFreePort, generateHunt, healSelector, huntSchema, interpolateHunt, iosAppAllowedIdentities, iosQueryToLocator, launchAndroidSession, launchIosSession, launchMacSession, listDevices, listHunts, listSimulators, loadConfig, loadHunt, loadHuntMeta, loadHuntTags, macdriverBuildInstructions, normalizeXcuiClassName, parseAaptPackage, parseAdbDevices, parseAndroidSelector, parseForwardPort, parseIosSelector, parseMacSelector, parseSimctlDevices, parseXcodeVersion, rankFlaky, rankMacSelectors, readHistory, readHuntHistory, readIosBundleIdentifier, reserveSimulatorUdid, resolveAgentApks, resolveHelperBinary, resolveWdaProject, resolveWdaRunner, runHunt, runSuite, selectDeviceSerial, selectSimulatorUdid, stepSchema, unwrapAndroidTextSelector, unwrapIosTextSelector, updateBacklogFromSuite, waitForAgentReady, waitForWdaReady, wdaCacheDir, webOnlyReason };
|