@e2edev/agent-device 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +131 -0
  3. package/dist/backend.d.ts +16 -0
  4. package/dist/backend.d.ts.map +1 -0
  5. package/dist/backend.js +66 -0
  6. package/dist/backend.js.map +1 -0
  7. package/dist/device.d.ts +69 -0
  8. package/dist/device.d.ts.map +1 -0
  9. package/dist/device.js +77 -0
  10. package/dist/device.js.map +1 -0
  11. package/dist/errors.d.ts +25 -0
  12. package/dist/errors.d.ts.map +1 -0
  13. package/dist/errors.js +72 -0
  14. package/dist/errors.js.map +1 -0
  15. package/dist/index.d.ts +18 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +14 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/locate.d.ts +14 -0
  20. package/dist/locate.d.ts.map +1 -0
  21. package/dist/locate.js +100 -0
  22. package/dist/locate.js.map +1 -0
  23. package/dist/nodes.d.ts +82 -0
  24. package/dist/nodes.d.ts.map +1 -0
  25. package/dist/nodes.js +295 -0
  26. package/dist/nodes.js.map +1 -0
  27. package/dist/png.d.ts +22 -0
  28. package/dist/png.d.ts.map +1 -0
  29. package/dist/png.js +172 -0
  30. package/dist/png.js.map +1 -0
  31. package/dist/selector.d.ts +17 -0
  32. package/dist/selector.d.ts.map +1 -0
  33. package/dist/selector.js +84 -0
  34. package/dist/selector.js.map +1 -0
  35. package/dist/support.d.ts +59 -0
  36. package/dist/support.d.ts.map +1 -0
  37. package/dist/support.js +105 -0
  38. package/dist/support.js.map +1 -0
  39. package/dist/surface.d.ts +146 -0
  40. package/dist/surface.d.ts.map +1 -0
  41. package/dist/surface.js +485 -0
  42. package/dist/surface.js.map +1 -0
  43. package/dist/tools.d.ts +24 -0
  44. package/dist/tools.d.ts.map +1 -0
  45. package/dist/tools.js +110 -0
  46. package/dist/tools.js.map +1 -0
  47. package/package.json +80 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 TesterArmy, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,131 @@
1
+ # @e2edev/agent-device
2
+
3
+ The mobile backend for [`e2e`](https://www.npmjs.com/package/e2e), built on
4
+ [agent-device](https://github.com/callstack/agent-device): iOS simulators and
5
+ Android emulators through the same `@e2edev/e2e/backend` contract the browser backend
6
+ implements. A test written against `screen`, `expect`, `app`, and `agent` runs
7
+ on a device target unchanged; nothing in `e2e` core knows this package exists.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ npm install --save-dev @e2edev/e2e @e2edev/agent-device
13
+ ```
14
+
15
+ `agent-device` needs Xcode with an iOS simulator runtime, or the Android SDK
16
+ with an emulator. Run `npx agent-device doctor` once before handing the target
17
+ to the runner.
18
+
19
+ ```ts title="e2e.config.ts"
20
+ import { defineConfig } from '@e2edev/e2e';
21
+ import { createAgent } from '@e2edev/e2e/agent';
22
+ import { agentDevice } from '@e2edev/agent-device';
23
+ import { agentDeviceTools } from '@e2edev/agent-device/tools';
24
+
25
+ const iphone = agentDevice({ platform: 'ios', app: 'Settings' });
26
+ const pixel = agentDevice({ platform: 'android', app: 'com.android.settings' });
27
+
28
+ export default defineConfig({
29
+ targets: [
30
+ { name: 'iphone', platform: 'ios', backend: iphone },
31
+ { name: 'pixel', platform: 'android', backend: pixel },
32
+ ],
33
+ workers: 1,
34
+ agent: { executor: createAgent({ tools: agentDeviceTools(iphone, pixel) }) },
35
+ });
36
+ ```
37
+
38
+ A test written against `agent`, `app`, `screen`, and `device` runs on both
39
+ targets unchanged. Only a check that names a platform label (`General` on iOS,
40
+ `Network & internet` on Android) needs `platforms: ['ios']` or
41
+ `platforms: ['android']` on the test.
42
+
43
+ Options:
44
+
45
+ | Option | Meaning |
46
+ | --- | --- |
47
+ | `platform` | `'ios'` or `'android'`. |
48
+ | `app` | Bundle id, package, or display name opened fresh at the start of every attempt. Also unlocks `app.restart()` and `app.clearState()`. |
49
+ | `device` | Simulator or emulator name or id; a booted one is picked otherwise. |
50
+ | `session` | agent-device session name; defaults to `e2e-<target name>`. One run per session at a time. |
51
+ | `snapshot` | `'full'` (default, includes static text) or `'interactive'` (actionable nodes only). |
52
+
53
+ ## What the backend declares
54
+
55
+ - **Observation**: the accessibility tree, projected onto the role vocabulary
56
+ (iOS `Button` becomes `button`, `TextField` becomes `textbox`, `Cell` becomes
57
+ `listitem`; Android `android.widget.TextView` becomes `text`, `EditText`
58
+ becomes `textbox`, `Switch` becomes `switch`), with rects, a viewport, and
59
+ pixels on request with every secure field painted over. Element identifiers
60
+ (`ABOUT`, `android:id/title`) surface as the configured test id attribute.
61
+ - **Actions**: tap, double tap, long press, fill, clear, check/uncheck (when
62
+ the tree exposes the checked state; Android switches do not), focus on
63
+ editable fields, `Enter`, single-character keys, swipe within a node, drag.
64
+ `selectOption`, `setInputFiles`, focus on a control, and other keys fail
65
+ with `UNSUPPORTED_CAPABILITY`.
66
+ - **Location**: every `screen` query, plus agent-device selectors through
67
+ `device.locator('role=NavigationBar id=General')`.
68
+ - **Viewport swipe**, `app.back()`, `app.restart()`, `app.clearState()`, and
69
+ redacted screenshots under the attempt artifact directory: the bounds of
70
+ every secure field are painted black before the file is kept, and a
71
+ screenshot that cannot be redacted is not written. No `state` capability: a
72
+ simulator has no portable session snapshot.
73
+
74
+ ## Trace cache
75
+
76
+ The runner caches `agent.act` steps by their location anchor, and a device has
77
+ no address bar. This backend reports one anyway: `app://device/<bundle
78
+ id>/<screen title>`, with the title read off the navigation bar on iOS and the
79
+ collapsing toolbar on Android. The cache compares pathnames, so the app
80
+ identity is part of the path and two apps with a "General" screen never share
81
+ an anchor. A step recorded on `/com.apple.preferences/General` replays only
82
+ when that app is on that screen again, and a flow that stays within tap, type,
83
+ and scroll replays with zero model calls. Pin `app` so every attempt starts on the same screen, and
84
+ prefer the grammar over the `open_app` tool inside a step: a tool call is a
85
+ replay gap.
86
+
87
+ ## The `device` fixture
88
+
89
+ Deterministic device management, recorded as `device.<method>` steps. Import
90
+ `test` from this package to have it typed.
91
+
92
+ ```ts
93
+ import { test } from '@e2edev/agent-device';
94
+ import { expect } from '@e2edev/e2e';
95
+
96
+ test('shows the version offline in dark mode', async ({ agent, device, screen }) => {
97
+ await device.setAppearance('dark');
98
+ await device.setNetwork('offline');
99
+ await agent.act('go to General, then About');
100
+ await expect(screen.getByRole('button', { name: /^iOS Version/ })).toBeVisible();
101
+ await expect(device.locator('role=NavigationBar id=About')).toBeVisible();
102
+ });
103
+ ```
104
+
105
+ Methods: `setNetwork`, `setAirplaneMode`, `setPermission`, `setLocation`,
106
+ `clearLocation`, `setAppearance`, `setOrientation`, `setBiometrics`,
107
+ `enrollBiometrics`, `openApp`, `closeApp`, `foregroundApp`, `home`, `back`,
108
+ `alert`, `dismissKeyboard`, `clipboard`, `setClipboard`, and the `locator`
109
+ accessor.
110
+
111
+ ## Agent tools
112
+
113
+ `@e2edev/agent-device/tools` exports `agentDeviceTools(...backends)`:
114
+ `open_app`, `swipe` (free-form, in logical pixels), `type_text` (into the
115
+ focused field, for editors that hide it from the tree), `alert`, and
116
+ `screenshot` (the model sees the image). Pass every device backend the config
117
+ declares: tool names are fixed, so two packs cannot be merged, and the pack
118
+ dispatches each call to the backend whose attempt is running. Tools are scoped
119
+ to the platforms of those backends, so a suite that mixes web and device
120
+ targets can hand the pack to one `createAgent`.
121
+
122
+ ## Secrets
123
+
124
+ Secret fills need an origin the runner can check, and an `app://` location has
125
+ an opaque origin that no allowlist can name, so `type_secret` is denied on a
126
+ device target before any plaintext reaches the device. Fill credentials
127
+ through a deterministic `screen` action in a setup step instead.
128
+
129
+ ## Documentation
130
+
131
+ Full documentation lives at [e2e.dev](https://e2e.dev).
@@ -0,0 +1,16 @@
1
+ /**
2
+ * The agent-device backend for e2e (RFC0002): a mobile body built with the
3
+ * public `defineBackend`, validated by the same rules and graded by the same
4
+ * capabilities as any other backend. Core imports nothing from here; this
5
+ * package imports the contract from `@e2edev/e2e/backend` and contributes the
6
+ * `device` fixture the way the browser backend contributes `web`.
7
+ */
8
+ import { type BackendHandle } from '@e2edev/e2e/backend';
9
+ import { AgentDeviceSurface, type AgentDeviceOptions } from './surface.ts';
10
+ /** Assembles the manifest for one surface. Exported for tests that script the client. */
11
+ export declare function buildBackend(surface: AgentDeviceSurface): BackendHandle;
12
+ /** Creates one agent-device backend: one device session per worker, one fresh app launch per attempt. */
13
+ export declare function agentDevice(options: AgentDeviceOptions): BackendHandle;
14
+ /** The surface behind a handle this package created; undefined for any other backend. */
15
+ export declare function surfaceOf(backend: BackendHandle): AgentDeviceSurface | undefined;
16
+ //# sourceMappingURL=backend.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAAiB,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAExE,OAAO,EAAE,kBAAkB,EAAE,KAAK,kBAAkB,EAAsB,MAAM,cAAc,CAAC;AAI/F,yFAAyF;AACzF,wBAAgB,YAAY,CAAC,OAAO,EAAE,kBAAkB,GAAG,aAAa,CAgCvE;AAED,yGAAyG;AACzG,wBAAgB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,aAAa,CAGtE;AAED,yFAAyF;AACzF,wBAAgB,SAAS,CAAC,OAAO,EAAE,aAAa,GAAG,kBAAkB,GAAG,SAAS,CAEhF"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * The agent-device backend for e2e (RFC0002): a mobile body built with the
3
+ * public `defineBackend`, validated by the same rules and graded by the same
4
+ * capabilities as any other backend. Core imports nothing from here; this
5
+ * package imports the contract from `@e2edev/e2e/backend` and contributes the
6
+ * `device` fixture the way the browser backend contributes `web`.
7
+ */
8
+ import { createRequire } from 'node:module';
9
+ import { createAgentDeviceClient } from 'agent-device';
10
+ import { defineBackend } from '@e2edev/e2e/backend';
11
+ import { createDeviceFixture } from './device.js';
12
+ import { AgentDeviceSurface } from './surface.js';
13
+ const surfaces = new WeakMap();
14
+ /** Assembles the manifest for one surface. Exported for tests that script the client. */
15
+ export function buildBackend(surface) {
16
+ const handle = defineBackend({
17
+ name: 'agent-device',
18
+ version: ownVersion(),
19
+ spiVersion: 1,
20
+ init: (info) => surface.init(info),
21
+ startAttempt: (context) => surface.startAttempt(context),
22
+ endAttempt: (context) => surface.endAttempt(context),
23
+ dispose: (context) => surface.dispose(context),
24
+ observe: (operation, options) => surface.observe(operation, options),
25
+ locate: (expression, operation) => surface.locate(expression, operation),
26
+ perform: (ref, action, operation) => surface.perform(ref, action, operation),
27
+ swipe: (direction, momentum, operation) => surface.swipe(direction, momentum, operation),
28
+ app: {
29
+ back: (operation) => surface.back(operation),
30
+ ...(surface.managesApp
31
+ ? {
32
+ restart: (operation) => surface.restart(operation),
33
+ clearState: (operation) => surface.clearState(operation),
34
+ }
35
+ : {}),
36
+ },
37
+ artifacts: {
38
+ screenshot: (label, operation) => surface.screenshot(label, operation),
39
+ },
40
+ url: (operation) => surface.url(operation),
41
+ fixtures: {
42
+ device: (context) => createDeviceFixture(surface, context),
43
+ },
44
+ });
45
+ surfaces.set(handle, surface);
46
+ return handle;
47
+ }
48
+ /** Creates one agent-device backend: one device session per worker, one fresh app launch per attempt. */
49
+ export function agentDevice(options) {
50
+ const factory = (session) => createAgentDeviceClient({ session });
51
+ return buildBackend(new AgentDeviceSurface(options, factory));
52
+ }
53
+ /** The surface behind a handle this package created; undefined for any other backend. */
54
+ export function surfaceOf(backend) {
55
+ return surfaces.get(backend);
56
+ }
57
+ /** This package's published version, read through require resolution. */
58
+ function ownVersion() {
59
+ try {
60
+ return createRequire(import.meta.url)('../package.json').version;
61
+ }
62
+ catch {
63
+ return 'unknown';
64
+ }
65
+ }
66
+ //# sourceMappingURL=backend.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backend.js","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,EAAE,aAAa,EAAsB,MAAM,qBAAqB,CAAC;AACxE,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAA+C,MAAM,cAAc,CAAC;AAE/F,MAAM,QAAQ,GAAG,IAAI,OAAO,EAAqC,CAAC;AAElE,yFAAyF;AACzF,MAAM,UAAU,YAAY,CAAC,OAA2B;IACtD,MAAM,MAAM,GAAG,aAAa,CAAC;QAC3B,IAAI,EAAE,cAAc;QACpB,OAAO,EAAE,UAAU,EAAE;QACrB,UAAU,EAAE,CAAC;QACb,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;QAClC,YAAY,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC;QACxD,UAAU,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC;QACpD,OAAO,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC;QAC9C,OAAO,EAAE,CAAC,SAAS,EAAE,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,SAAS,EAAE,OAAO,CAAC;QACpE,MAAM,EAAE,CAAC,UAAU,EAAE,SAAS,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,SAAS,CAAC;QACxE,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,CAAC;QAC5E,KAAK,EAAE,CAAC,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,EAAE,QAAQ,EAAE,SAAS,CAAC;QACxF,GAAG,EAAE;YACH,IAAI,EAAE,CAAC,SAAS,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC;YAC5C,GAAG,CAAC,OAAO,CAAC,UAAU;gBACpB,CAAC,CAAC;oBACE,OAAO,EAAE,CAAC,SAAS,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC;oBAClD,UAAU,EAAE,CAAC,SAAS,EAAE,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC;iBACzD;gBACH,CAAC,CAAC,EAAE,CAAC;SACR;QACD,SAAS,EAAE;YACT,UAAU,EAAE,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,KAAK,EAAE,SAAS,CAAC;SACvE;QACD,GAAG,EAAE,CAAC,SAAS,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC;QAC1C,QAAQ,EAAE;YACR,MAAM,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC;SAC3D;KACF,CAAC,CAAC;IACH,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,yGAAyG;AACzG,MAAM,UAAU,WAAW,CAAC,OAA2B;IACrD,MAAM,OAAO,GAAkB,CAAC,OAAO,EAAE,EAAE,CAAC,uBAAuB,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;IACjF,OAAO,YAAY,CAAC,IAAI,kBAAkB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;AAChE,CAAC;AAED,yFAAyF;AACzF,MAAM,UAAU,SAAS,CAAC,OAAsB;IAC9C,OAAO,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AAC/B,CAAC;AAED,yEAAyE;AACzE,SAAS,UAAU;IACjB,IAAI,CAAC;QACH,OAAQ,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,iBAAiB,CAAyB,CAAC,OAAO,CAAC;IAC5F,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * The `device` fixture: deterministic device management this backend
3
+ * contributes. Not an agent tool; a test calls these directly to arrange or
4
+ * assert device state, and the harness records each call as a
5
+ * `device.<method>` step bounded by the action timeout.
6
+ */
7
+ import type { BackendFixtureContext, Locator } from '@e2edev/e2e/backend';
8
+ import type { AgentDeviceSurface } from './surface.ts';
9
+ /** Permissions agent-device can grant, deny, or reset on an open app. */
10
+ export type DevicePermission = 'camera' | 'microphone' | 'photos' | 'contacts' | 'notifications' | 'calendar' | 'location' | 'reminders' | 'motion' | 'siri' | 'media-library';
11
+ export type DeviceOrientation = 'portrait' | 'portrait-upside-down' | 'landscape-left' | 'landscape-right';
12
+ export type BiometricSensor = 'faceid' | 'touchid' | 'fingerprint';
13
+ export interface ForegroundApp {
14
+ readonly name: string;
15
+ readonly bundleId?: string;
16
+ }
17
+ /** Deterministic device management exposed to tests as `device`. */
18
+ export interface Device {
19
+ /**
20
+ * A locator from an agent-device selector (`'id=SW_VERSION_SPECIFIER'`,
21
+ * `'role=StaticText label="26.2"'`), for nodes the closed `screen` query
22
+ * vocabulary cannot name. Same polling and strictness as any locator.
23
+ */
24
+ locator(selector: string): Locator;
25
+ /** Toggles the device's Wi-Fi. */
26
+ setNetwork(state: 'online' | 'offline'): Promise<void>;
27
+ /** Toggles airplane mode. */
28
+ setAirplaneMode(enabled: boolean): Promise<void>;
29
+ /** Grants, denies, or resets one permission for the open app. */
30
+ setPermission(permission: DevicePermission, state: 'grant' | 'deny' | 'reset'): Promise<void>;
31
+ /** Sets the simulated location. */
32
+ setLocation(coordinates: {
33
+ latitude: number;
34
+ longitude: number;
35
+ }): Promise<void>;
36
+ /** Turns simulated location off. */
37
+ clearLocation(): Promise<void>;
38
+ /** Sets the system appearance. */
39
+ setAppearance(mode: 'light' | 'dark'): Promise<void>;
40
+ /** Rotates the device. */
41
+ setOrientation(orientation: DeviceOrientation): Promise<void>;
42
+ /** Simulates one biometric attempt on the open app. */
43
+ setBiometrics(sensor: BiometricSensor, result: 'match' | 'nonmatch'): Promise<void>;
44
+ /** Enrolls or unenrolls the simulator's Face ID or Touch ID. */
45
+ enrollBiometrics(sensor: 'faceid' | 'touchid', enrolled: boolean): Promise<void>;
46
+ /** Brings an app to the foreground; `relaunch` restarts it fresh. */
47
+ openApp(app: string, options?: {
48
+ relaunch?: boolean;
49
+ }): Promise<void>;
50
+ /** Closes the session's current app. */
51
+ closeApp(): Promise<void>;
52
+ /** The app currently in the foreground of the session. */
53
+ foregroundApp(): Promise<ForegroundApp>;
54
+ /** Sends the device to its home screen. */
55
+ home(): Promise<void>;
56
+ /** Navigates back once (in-app back). */
57
+ back(): Promise<void>;
58
+ /** Accepts or dismisses the visible system alert. */
59
+ alert(action: 'accept' | 'dismiss'): Promise<void>;
60
+ /** Dismisses the soft keyboard. */
61
+ dismissKeyboard(): Promise<void>;
62
+ /** Reads the clipboard. */
63
+ clipboard(): Promise<string>;
64
+ /** Writes the clipboard. */
65
+ setClipboard(text: string): Promise<void>;
66
+ }
67
+ /** Builds the device fixture for one attempt. */
68
+ export declare function createDeviceFixture(surface: AgentDeviceSurface, context: BackendFixtureContext): Device;
69
+ //# sourceMappingURL=device.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device.d.ts","sourceRoot":"","sources":["../src/device.ts"],"names":[],"mappings":"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"}
package/dist/device.js ADDED
@@ -0,0 +1,77 @@
1
+ /**
2
+ * The `device` fixture: deterministic device management this backend
3
+ * contributes. Not an agent tool; a test calls these directly to arrange or
4
+ * assert device state, and the harness records each call as a
5
+ * `device.<method>` step bounded by the action timeout.
6
+ */
7
+ /** Builds the device fixture for one attempt. */
8
+ export function createDeviceFixture(surface, context) {
9
+ const signal = context.signal;
10
+ return {
11
+ locator: (selector) => context.locator({ kind: 'selector', selector }),
12
+ async setNetwork(state) {
13
+ await surface.command('device.setNetwork', (client) => client.settings.update({ setting: 'wifi', state: state === 'offline' ? 'off' : 'on' }), signal);
14
+ },
15
+ async setAirplaneMode(enabled) {
16
+ await surface.command('device.setAirplaneMode', (client) => client.settings.update({ setting: 'airplane', state: enabled ? 'on' : 'off' }), signal);
17
+ },
18
+ async setPermission(permission, state) {
19
+ await surface.command('device.setPermission', (client) => client.settings.update({ setting: 'permission', permission, state }), signal);
20
+ },
21
+ async setLocation({ latitude, longitude }) {
22
+ await surface.command('device.setLocation', (client) => client.settings.update({ setting: 'location', state: 'set', latitude, longitude }), signal);
23
+ },
24
+ async clearLocation() {
25
+ await surface.command('device.clearLocation', (client) => client.settings.update({ setting: 'location', state: 'off' }), signal);
26
+ },
27
+ async setAppearance(mode) {
28
+ await surface.command('device.setAppearance', (client) => client.settings.update({ setting: 'appearance', state: mode }), signal);
29
+ },
30
+ async setOrientation(orientation) {
31
+ await surface.command('device.setOrientation', (client) => client.command.orientation({ orientation }), signal);
32
+ },
33
+ async setBiometrics(sensor, result) {
34
+ await surface.command('device.setBiometrics', (client) => sensor === 'fingerprint'
35
+ ? client.settings.update({ setting: 'fingerprint', state: result })
36
+ : client.settings.update({ setting: sensor, state: result }), signal);
37
+ },
38
+ async enrollBiometrics(sensor, enrolled) {
39
+ await surface.command('device.enrollBiometrics', (client) => client.settings.update({ setting: sensor, state: enrolled ? 'enroll' : 'unenroll' }), signal);
40
+ },
41
+ async openApp(app, options) {
42
+ await surface.openApp(app, options?.relaunch === true, signal);
43
+ },
44
+ async closeApp() {
45
+ await surface.command('device.closeApp', (client) => client.apps.close({}), signal);
46
+ },
47
+ async foregroundApp() {
48
+ const state = await surface.command('device.foregroundApp', (client) => client.command.appState({}), signal);
49
+ if ('package' in state)
50
+ return { name: state.package, bundleId: state.package };
51
+ return {
52
+ name: state.appName,
53
+ ...(state.appBundleId === undefined ? {} : { bundleId: state.appBundleId }),
54
+ };
55
+ },
56
+ async home() {
57
+ await surface.command('device.home', (client) => client.command.home({}), signal);
58
+ },
59
+ async back() {
60
+ await surface.command('device.back', (client) => client.command.back({ settle: true }), signal);
61
+ },
62
+ async alert(action) {
63
+ await surface.command('device.alert', (client) => client.command.alert({ action }), signal);
64
+ },
65
+ async dismissKeyboard() {
66
+ await surface.command('device.dismissKeyboard', (client) => client.command.keyboard({ action: 'dismiss' }), signal);
67
+ },
68
+ async clipboard() {
69
+ const result = await surface.command('device.clipboard', (client) => client.command.clipboard({ action: 'read' }), signal);
70
+ return result.action === 'read' ? result.text : '';
71
+ },
72
+ async setClipboard(text) {
73
+ await surface.command('device.setClipboard', (client) => client.command.clipboard({ action: 'write', text }), signal);
74
+ },
75
+ };
76
+ }
77
+ //# sourceMappingURL=device.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device.js","sourceRoot":"","sources":["../src/device.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA0EH,iDAAiD;AACjD,MAAM,UAAU,mBAAmB,CAAC,OAA2B,EAAE,OAA8B;IAC7F,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAC9B,OAAO;QACL,OAAO,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC;QACtE,KAAK,CAAC,UAAU,CAAC,KAAK;YACpB,MAAM,OAAO,CAAC,OAAO,CACnB,mBAAmB,EACnB,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,EAClG,MAAM,CACP,CAAC;QACJ,CAAC;QACD,KAAK,CAAC,eAAe,CAAC,OAAO;YAC3B,MAAM,OAAO,CAAC,OAAO,CACnB,wBAAwB,EACxB,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,EAC1F,MAAM,CACP,CAAC;QACJ,CAAC;QACD,KAAK,CAAC,aAAa,CAAC,UAAU,EAAE,KAAK;YACnC,MAAM,OAAO,CAAC,OAAO,CACnB,sBAAsB,EACtB,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,EAChF,MAAM,CACP,CAAC;QACJ,CAAC;QACD,KAAK,CAAC,WAAW,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE;YACvC,MAAM,OAAO,CAAC,OAAO,CACnB,oBAAoB,EACpB,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,EAC9F,MAAM,CACP,CAAC;QACJ,CAAC;QACD,KAAK,CAAC,aAAa;YACjB,MAAM,OAAO,CAAC,OAAO,CACnB,sBAAsB,EACtB,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,EACzE,MAAM,CACP,CAAC;QACJ,CAAC;QACD,KAAK,CAAC,aAAa,CAAC,IAAI;YACtB,MAAM,OAAO,CAAC,OAAO,CACnB,sBAAsB,EACtB,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,EAC1E,MAAM,CACP,CAAC;QACJ,CAAC;QACD,KAAK,CAAC,cAAc,CAAC,WAAW;YAC9B,MAAM,OAAO,CAAC,OAAO,CAAC,uBAAuB,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC;QAClH,CAAC;QACD,KAAK,CAAC,aAAa,CAAC,MAAM,EAAE,MAAM;YAChC,MAAM,OAAO,CAAC,OAAO,CACnB,sBAAsB,EACtB,CAAC,MAAM,EAAE,EAAE,CACT,MAAM,KAAK,aAAa;gBACtB,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;gBACnE,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,EAChE,MAAM,CACP,CAAC;QACJ,CAAC;QACD,KAAK,CAAC,gBAAgB,CAAC,MAAM,EAAE,QAAQ;YACrC,MAAM,OAAO,CAAC,OAAO,CACnB,yBAAyB,EACzB,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,EAChG,MAAM,CACP,CAAC;QACJ,CAAC;QACD,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO;YACxB,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,EAAE,QAAQ,KAAK,IAAI,EAAE,MAAM,CAAC,CAAC;QACjE,CAAC;QACD,KAAK,CAAC,QAAQ;YACZ,MAAM,OAAO,CAAC,OAAO,CAAC,iBAAiB,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC;QACtF,CAAC;QACD,KAAK,CAAC,aAAa;YACjB,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,sBAAsB,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC;YAC7G,IAAI,SAAS,IAAI,KAAK;gBAAE,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;YAChF,OAAO;gBACL,IAAI,EAAE,KAAK,CAAC,OAAO;gBACnB,GAAG,CAAC,KAAK,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,WAAW,EAAE,CAAC;aAC5E,CAAC;QACJ,CAAC;QACD,KAAK,CAAC,IAAI;YACR,MAAM,OAAO,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC;QACpF,CAAC;QACD,KAAK,CAAC,IAAI;YACR,MAAM,OAAO,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC;QAClG,CAAC;QACD,KAAK,CAAC,KAAK,CAAC,MAAM;YAChB,MAAM,OAAO,CAAC,OAAO,CAAC,cAAc,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC;QAC9F,CAAC;QACD,KAAK,CAAC,eAAe;YACnB,MAAM,OAAO,CAAC,OAAO,CAAC,wBAAwB,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC;QACtH,CAAC;QACD,KAAK,CAAC,SAAS;YACb,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,OAAO,CAClC,kBAAkB,EAClB,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,EACxD,MAAM,CACP,CAAC;YACF,OAAO,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;QACrD,CAAC;QACD,KAAK,CAAC,YAAY,CAAC,IAAI;YACrB,MAAM,OAAO,CAAC,OAAO,CAAC,qBAAqB,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC;QACxH,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Translation of agent-device failures onto the backend error contract.
3
+ *
4
+ * | agent-device failure | Code |
5
+ * | ----------------------------------------------------------- | ---------------------------- |
6
+ * | already a BackendError / runner error | passed through untouched |
7
+ * | AbortError | CANCELLED |
8
+ * | no active session / session not found | INVALID_STATE |
9
+ * | UNSUPPORTED_OPERATION, NOT_IMPLEMENTED, UNSUPPORTED_PLATFORM | UNSUPPORTED_CAPABILITY |
10
+ * | "not supported on this device" under any code | UNSUPPORTED_CAPABILITY |
11
+ * | a ref the daemon no longer knows (action paths only) | NODE_STALE (retryable) |
12
+ * | timed out | OPERATION_TIMEOUT |
13
+ * | anything else | BACKEND_FAILURE |
14
+ */
15
+ /** A failure's message in agent-device's normalized form. */
16
+ export declare function message(cause: unknown): string;
17
+ /** Translates an unexpected agent-device error at the contract boundary. */
18
+ export declare function translateError(cause: unknown, operation: string): Error;
19
+ /**
20
+ * Like translateError, but a ref the daemon no longer binds becomes retryable
21
+ * `NODE_STALE`: nothing was dispatched, so the harness may re-observe and
22
+ * re-resolve the node instead of failing the action.
23
+ */
24
+ export declare function staleOr(cause: unknown, operation: string): Error;
25
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAiBH,6DAA6D;AAC7D,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAE9C;AASD,4EAA4E;AAC5E,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,KAAK,CAcvE;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,KAAK,CAOhE"}
package/dist/errors.js ADDED
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Translation of agent-device failures onto the backend error contract.
3
+ *
4
+ * | agent-device failure | Code |
5
+ * | ----------------------------------------------------------- | ---------------------------- |
6
+ * | already a BackendError / runner error | passed through untouched |
7
+ * | AbortError | CANCELLED |
8
+ * | no active session / session not found | INVALID_STATE |
9
+ * | UNSUPPORTED_OPERATION, NOT_IMPLEMENTED, UNSUPPORTED_PLATFORM | UNSUPPORTED_CAPABILITY |
10
+ * | "not supported on this device" under any code | UNSUPPORTED_CAPABILITY |
11
+ * | a ref the daemon no longer knows (action paths only) | NODE_STALE (retryable) |
12
+ * | timed out | OPERATION_TIMEOUT |
13
+ * | anything else | BACKEND_FAILURE |
14
+ */
15
+ import { normalizeAgentDeviceError } from 'agent-device';
16
+ import { BackendError, ConfigurationError, InfrastructureError, TestError } from '@e2edev/e2e/backend';
17
+ import { cancelled } from './support.js';
18
+ /**
19
+ * True for an error that already carries its classification: a `BackendError`
20
+ * from any module copy, or a runner error. Those cross the boundary untouched;
21
+ * re-wrapping one would turn a `POLICY_DENIED` into an infrastructure failure.
22
+ */
23
+ function isClassified(cause) {
24
+ if (cause instanceof BackendError || cause instanceof TestError)
25
+ return true;
26
+ if (cause instanceof ConfigurationError || cause instanceof InfrastructureError)
27
+ return true;
28
+ return cause instanceof Error && cause.name === 'BackendError';
29
+ }
30
+ /** A failure's message in agent-device's normalized form. */
31
+ export function message(cause) {
32
+ return normalizeAgentDeviceError(cause).message;
33
+ }
34
+ const NO_SESSION_PATTERN = /no active (?:app )?session|session\b.*\bnot found|open an app first|no app (?:is )?open/i;
35
+ const UNSUPPORTED_CODES = new Set(['UNSUPPORTED_OPERATION', 'NOT_IMPLEMENTED', 'UNSUPPORTED_PLATFORM']);
36
+ const UNSUPPORTED_PATTERN = /\b(?:is )?not supported\b|\bunsupported\b/i;
37
+ const TIMEOUT_PATTERN = /\btimed?\s?out\b/i;
38
+ const STALE_PATTERN = /\bref\b.*\b(?:not found|unknown|stale|no longer|expired|invalid|missing)|\b(?:not found|unknown|stale|no longer|expired|invalid|missing)\b.*\bref\b|refs?generation/i;
39
+ /** Translates an unexpected agent-device error at the contract boundary. */
40
+ export function translateError(cause, operation) {
41
+ if (isClassified(cause))
42
+ return cause;
43
+ if (cause instanceof Error && cause.name === 'AbortError')
44
+ return cancelled(`${operation} cancelled`);
45
+ const normalized = normalizeAgentDeviceError(cause);
46
+ const text = `${operation} failed: ${normalized.message}`;
47
+ const options = { retryable: false, cause };
48
+ if (normalized.code === 'SESSION_NOT_FOUND' || NO_SESSION_PATTERN.test(normalized.message)) {
49
+ return new BackendError('INVALID_STATE', text, options);
50
+ }
51
+ if (UNSUPPORTED_CODES.has(normalized.code) || UNSUPPORTED_PATTERN.test(normalized.message)) {
52
+ return new BackendError('UNSUPPORTED_CAPABILITY', text, options);
53
+ }
54
+ if (TIMEOUT_PATTERN.test(normalized.message))
55
+ return new BackendError('OPERATION_TIMEOUT', text, options);
56
+ return new BackendError('BACKEND_FAILURE', text, options);
57
+ }
58
+ /**
59
+ * Like translateError, but a ref the daemon no longer binds becomes retryable
60
+ * `NODE_STALE`: nothing was dispatched, so the harness may re-observe and
61
+ * re-resolve the node instead of failing the action.
62
+ */
63
+ export function staleOr(cause, operation) {
64
+ if (isClassified(cause))
65
+ return cause;
66
+ const normalized = normalizeAgentDeviceError(cause);
67
+ if (STALE_PATTERN.test(normalized.message)) {
68
+ return new BackendError('NODE_STALE', `${operation}: ${normalized.message}`, { retryable: true, cause });
69
+ }
70
+ return translateError(cause, operation);
71
+ }
72
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * `@e2edev/agent-device` public surface: the `agentDevice()` backend factory,
3
+ * the `device` fixture types, and a `test` typed with that fixture. `expect`
4
+ * and `credentials` still come from `e2e`; the agent-side tool pack lives on
5
+ * the `@e2edev/agent-device/tools` subpath so this entry never loads the AI SDK.
6
+ */
7
+ import type { Device } from './device.ts';
8
+ export { agentDevice } from './backend.ts';
9
+ export type { AgentDeviceOptions, AgentDevicePlatform } from './surface.ts';
10
+ export type { BiometricSensor, Device, DeviceOrientation, DevicePermission, ForegroundApp } from './device.ts';
11
+ /**
12
+ * `test` typed with this backend's contributed `device` fixture. The same
13
+ * runtime `test` as `e2e`'s; only the fixture types differ.
14
+ */
15
+ export declare const test: import("@e2edev/e2e").TestAPI<import("@e2edev/e2e").TestFixtures & {
16
+ device: Device;
17
+ }>;
18
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,YAAY,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAC5E,YAAY,EAAE,eAAe,EAAE,MAAM,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE/G;;;GAGG;AACH,eAAO,MAAM,IAAI;YAAyB,MAAM;EAAK,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,14 @@
1
+ /**
2
+ * `@e2edev/agent-device` public surface: the `agentDevice()` backend factory,
3
+ * the `device` fixture types, and a `test` typed with that fixture. `expect`
4
+ * and `credentials` still come from `e2e`; the agent-side tool pack lives on
5
+ * the `@e2edev/agent-device/tools` subpath so this entry never loads the AI SDK.
6
+ */
7
+ import { test as base } from '@e2edev/e2e';
8
+ export { agentDevice } from './backend.js';
9
+ /**
10
+ * `test` typed with this backend's contributed `device` fixture. The same
11
+ * runtime `test` as `e2e`'s; only the fixture types differ.
12
+ */
13
+ export const test = base.extend();
14
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,IAAI,IAAI,IAAI,EAAE,MAAM,aAAa,CAAC;AAG3C,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAI3C;;;GAGG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,EAAsB,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * LocatorExpression evaluation over one projected snapshot: the location
3
+ * capability's query language answered against the accessibility tree the
4
+ * device just reported. One immediate pass, every match in document order;
5
+ * polling, strictness, and staleness stay with the runner.
6
+ */
7
+ import { type LocatorExpression } from '@e2edev/e2e/backend';
8
+ import { type ProjectedNode } from './nodes.ts';
9
+ export interface LocateOptions {
10
+ readonly testIdAttribute: string;
11
+ }
12
+ /** Resolves one expression to the nodes it currently matches. */
13
+ export declare function resolveExpression(expression: LocatorExpression, index: readonly ProjectedNode[], options: LocateOptions): ProjectedNode[];
14
+ //# sourceMappingURL=locate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"locate.d.ts","sourceRoot":"","sources":["../src/locate.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAA6B,KAAK,iBAAiB,EAAsB,MAAM,qBAAqB,CAAC;AAC5G,OAAO,EAAY,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAG1D,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;CAClC;AAED,iEAAiE;AACjE,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,iBAAiB,EAC7B,KAAK,EAAE,SAAS,aAAa,EAAE,EAC/B,OAAO,EAAE,aAAa,GACrB,aAAa,EAAE,CAgCjB"}