@phone-use/sdk 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.
- package/LICENSE +202 -0
- package/README.md +103 -0
- package/dist/backend-Cbr2tIN-.d.mts +316 -0
- package/dist/device-BzPnHvQy.mjs +284 -0
- package/dist/device-BzPnHvQy.mjs.map +1 -0
- package/dist/index.d.mts +689 -0
- package/dist/index.mjs +1848 -0
- package/dist/index.mjs.map +1 -0
- package/dist/testing.d.mts +127 -0
- package/dist/testing.mjs +188 -0
- package/dist/testing.mjs.map +1 -0
- package/package.json +44 -0
- package/src/actions.ts +545 -0
- package/src/backend.ts +185 -0
- package/src/backends/agent-device.ts +242 -0
- package/src/backends/ios.ts +262 -0
- package/src/config.ts +43 -0
- package/src/device.ts +98 -0
- package/src/errors.ts +177 -0
- package/src/exec.ts +48 -0
- package/src/index.ts +86 -0
- package/src/lifecycle.ts +349 -0
- package/src/observe.ts +1093 -0
- package/src/secrets.ts +67 -0
- package/src/testing.ts +239 -0
package/src/config.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Device configuration: a discriminated union, never a stringly-typed
|
|
3
|
+
// capability blob (docs/19 §Backend contract, learning from Appium's leaks).
|
|
4
|
+
// Field names verified against agent-device 0.19.3's AgentDeviceSelectionOptions
|
|
5
|
+
// and AgentDeviceClientConfig. Plain types — no zod in the SDK public API
|
|
6
|
+
// (docs/20 risk 6).
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
|
|
9
|
+
/** Fields shared by every platform's device config (session/daemon pinning). */
|
|
10
|
+
export type CommonDeviceConfig = {
|
|
11
|
+
/** agent-device session name pinning. */
|
|
12
|
+
session?: string | undefined;
|
|
13
|
+
/** Remote daemon endpoint (API surface reserved; local-only this cycle). */
|
|
14
|
+
daemonBaseUrl?: string | undefined;
|
|
15
|
+
daemonAuthToken?: string | undefined;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/** iOS device selection: pin by udid, name match, or custom device set. */
|
|
19
|
+
export type IosDeviceConfig = CommonDeviceConfig & {
|
|
20
|
+
platform: 'ios';
|
|
21
|
+
/** Pin a specific simulator/device by udid. */
|
|
22
|
+
udid?: string | undefined;
|
|
23
|
+
/** Name match, e.g. "iPhone 16". */
|
|
24
|
+
device?: string | undefined;
|
|
25
|
+
/** Custom simulator device set directory. */
|
|
26
|
+
simulatorDeviceSet?: string | undefined;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/** Android device selection: pin by adb serial or name match. */
|
|
30
|
+
export type AndroidDeviceConfig = CommonDeviceConfig & {
|
|
31
|
+
platform: 'android';
|
|
32
|
+
/** Pin a specific device/emulator by adb serial. */
|
|
33
|
+
serial?: string | undefined;
|
|
34
|
+
/** Name match. */
|
|
35
|
+
device?: string | undefined;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Device configuration: a discriminated union on `platform`, never a
|
|
40
|
+
* stringly-typed capability blob (docs/19 §Backend contract, learning from
|
|
41
|
+
* Appium's leaks). Plain types — no zod in the SDK public API.
|
|
42
|
+
*/
|
|
43
|
+
export type DeviceConfig = IosDeviceConfig | AndroidDeviceConfig;
|
package/src/device.ts
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Shared device types — the SDK's own vocabulary, structurally compatible with
|
|
3
|
+
// agent-device's snapshot nodes but never importing its types (docs/20 item 2:
|
|
4
|
+
// no backend type in the public API). Every optional is `| undefined` so
|
|
5
|
+
// narrower backend types assign structurally under exactOptionalPropertyTypes.
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
|
|
8
|
+
/** Element geometry in screen points (same space as screenshot pixels at @1x). */
|
|
9
|
+
export type Rect = { x: number; y: number; width: number; height: number };
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* One accessibility-tree node as reported by a backend snapshot. The SDK's own
|
|
13
|
+
* vocabulary — structurally compatible with agent-device's nodes but never
|
|
14
|
+
* importing its types (docs/20 item 2: no backend type in the public API).
|
|
15
|
+
*/
|
|
16
|
+
export type SnapshotNode = {
|
|
17
|
+
ref?: string | undefined;
|
|
18
|
+
type?: string | undefined;
|
|
19
|
+
role?: string | undefined;
|
|
20
|
+
label?: string | undefined;
|
|
21
|
+
value?: string | undefined;
|
|
22
|
+
identifier?: string | undefined;
|
|
23
|
+
enabled?: boolean | undefined;
|
|
24
|
+
selected?: boolean | undefined;
|
|
25
|
+
focused?: boolean | undefined;
|
|
26
|
+
interactionBlocked?: string | undefined;
|
|
27
|
+
rect?: Rect | undefined;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/** One backend snapshot: the node list plus the frontmost app when known. */
|
|
31
|
+
export type Snapshot = {
|
|
32
|
+
nodes: SnapshotNode[];
|
|
33
|
+
appName?: string | undefined;
|
|
34
|
+
appBundleId?: string | undefined;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/** Scroll gesture direction. */
|
|
38
|
+
export type ScrollDirection = 'up' | 'down' | 'left' | 'right';
|
|
39
|
+
|
|
40
|
+
/** A press target: an element ref from a snapshot, or raw screen coordinates. */
|
|
41
|
+
export type PressTarget = { ref: string } | { x: number; y: number };
|
|
42
|
+
|
|
43
|
+
/** What to do with a system alert: read it, accept it, or dismiss it. */
|
|
44
|
+
export type AlertAction = 'get' | 'accept' | 'dismiss';
|
|
45
|
+
|
|
46
|
+
/** Raw result of a backend's system-alert command. */
|
|
47
|
+
export type BackendAlertResult = {
|
|
48
|
+
alert?:
|
|
49
|
+
| { title?: string | undefined; message?: string | undefined; buttons?: string[] | undefined }
|
|
50
|
+
| null
|
|
51
|
+
| undefined;
|
|
52
|
+
handled?: boolean | undefined;
|
|
53
|
+
button?: string | undefined;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/** What a backend reports after opening an app or URL. */
|
|
57
|
+
export type OpenAppResult = { appName?: string | undefined; appBundleId?: string | undefined };
|
|
58
|
+
|
|
59
|
+
/** A backend feature a caller can query via `backend.capabilities`. */
|
|
60
|
+
export type Capability =
|
|
61
|
+
| 'snapshot'
|
|
62
|
+
| 'screenshot'
|
|
63
|
+
| 'press'
|
|
64
|
+
| 'longPress'
|
|
65
|
+
| 'fill'
|
|
66
|
+
| 'type'
|
|
67
|
+
| 'key'
|
|
68
|
+
| 'scroll'
|
|
69
|
+
| 'pan'
|
|
70
|
+
| 'waitForText'
|
|
71
|
+
| 'alert'
|
|
72
|
+
| 'home'
|
|
73
|
+
| 'back'
|
|
74
|
+
| 'openApp'
|
|
75
|
+
| 'openUrl'
|
|
76
|
+
| 'listApps'
|
|
77
|
+
| 'closeSession';
|
|
78
|
+
|
|
79
|
+
/** Every capability — what a full backend (agent-device iOS) declares. */
|
|
80
|
+
export const ALL_CAPABILITIES: readonly Capability[] = [
|
|
81
|
+
'snapshot',
|
|
82
|
+
'screenshot',
|
|
83
|
+
'press',
|
|
84
|
+
'longPress',
|
|
85
|
+
'fill',
|
|
86
|
+
'type',
|
|
87
|
+
'key',
|
|
88
|
+
'scroll',
|
|
89
|
+
'pan',
|
|
90
|
+
'waitForText',
|
|
91
|
+
'alert',
|
|
92
|
+
'home',
|
|
93
|
+
'back',
|
|
94
|
+
'openApp',
|
|
95
|
+
'openUrl',
|
|
96
|
+
'listApps',
|
|
97
|
+
'closeSession',
|
|
98
|
+
];
|
package/src/errors.ts
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { isAgentDeviceError } from 'agent-device';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Stable machine-readable error codes carried by every {@link PhoneUseError}.
|
|
5
|
+
*
|
|
6
|
+
* Codes are deliberately string-identical to agent-device's where a concept
|
|
7
|
+
* maps 1:1 (DEVICE_NOT_FOUND, SESSION_NOT_FOUND, ...) so existing harness
|
|
8
|
+
* checks like `(e as {code?: string}).code === 'SESSION_NOT_FOUND'` keep
|
|
9
|
+
* working unchanged across the normalization boundary.
|
|
10
|
+
*/
|
|
11
|
+
export type PhoneUseErrorCode =
|
|
12
|
+
| 'DEVICE_NOT_FOUND'
|
|
13
|
+
| 'DEVICE_IN_USE'
|
|
14
|
+
| 'SESSION_NOT_FOUND'
|
|
15
|
+
| 'TIMEOUT'
|
|
16
|
+
| 'ACTION_FAILED'
|
|
17
|
+
| 'UNSUPPORTED_CAPABILITY'
|
|
18
|
+
| 'BACKEND_NOT_FOUND'
|
|
19
|
+
| 'ABORTED'
|
|
20
|
+
| 'UNKNOWN';
|
|
21
|
+
|
|
22
|
+
/** Structured, code-specific context attached to a {@link PhoneUseError}. */
|
|
23
|
+
export type PhoneUseErrorDetails = Record<string, unknown> & {
|
|
24
|
+
/** Preserved from agent-device details.hint — describeError renders it. */
|
|
25
|
+
hint?: string | undefined;
|
|
26
|
+
/** The raw backend code when we collapse to ACTION_FAILED. */
|
|
27
|
+
backendCode?: string | undefined;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Root of the typed error tree (docs/19 §Artifacts, errors; docs/20 item 2).
|
|
32
|
+
* Every backend method rejects only with PhoneUseError subclasses; agent-device
|
|
33
|
+
* (or any transport) errors are normalized at the boundary by
|
|
34
|
+
* {@link toPhoneUseError} so no backend type ever reaches the public API. Each
|
|
35
|
+
* error carries a stable `code` and an explicit `retryable` flag — the field
|
|
36
|
+
* agent loops need to decide retry-vs-replan.
|
|
37
|
+
*/
|
|
38
|
+
export class PhoneUseError extends Error {
|
|
39
|
+
/** Stable machine-readable code — the field to branch on. */
|
|
40
|
+
readonly code: PhoneUseErrorCode;
|
|
41
|
+
/** Whether retrying the same call can plausibly succeed. */
|
|
42
|
+
readonly retryable: boolean;
|
|
43
|
+
/** Optional structured context (hint, raw backend code, ...). */
|
|
44
|
+
readonly details?: PhoneUseErrorDetails | undefined;
|
|
45
|
+
|
|
46
|
+
constructor(
|
|
47
|
+
message: string,
|
|
48
|
+
opts: {
|
|
49
|
+
code: PhoneUseErrorCode;
|
|
50
|
+
retryable: boolean;
|
|
51
|
+
details?: PhoneUseErrorDetails | undefined;
|
|
52
|
+
cause?: unknown;
|
|
53
|
+
},
|
|
54
|
+
) {
|
|
55
|
+
super(message, opts.cause === undefined ? undefined : { cause: opts.cause });
|
|
56
|
+
this.name = new.target.name;
|
|
57
|
+
this.code = opts.code;
|
|
58
|
+
this.retryable = opts.retryable;
|
|
59
|
+
this.details = opts.details;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** No device matched the selection (`DEVICE_NOT_FOUND`, not retryable). */
|
|
64
|
+
export class DeviceNotFoundError extends PhoneUseError {
|
|
65
|
+
constructor(message: string, opts: { details?: PhoneUseErrorDetails | undefined; cause?: unknown } = {}) {
|
|
66
|
+
super(message, { code: 'DEVICE_NOT_FOUND', retryable: false, ...opts });
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** The device is held by another session (`DEVICE_IN_USE`, retryable). */
|
|
71
|
+
export class DeviceInUseError extends PhoneUseError {
|
|
72
|
+
constructor(message: string, opts: { details?: PhoneUseErrorDetails | undefined; cause?: unknown } = {}) {
|
|
73
|
+
super(message, { code: 'DEVICE_IN_USE', retryable: true, ...opts });
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** No live session — the device/session was closed (`SESSION_NOT_FOUND`, not retryable). */
|
|
78
|
+
export class SessionNotFoundError extends PhoneUseError {
|
|
79
|
+
constructor(message: string, opts: { details?: PhoneUseErrorDetails | undefined; cause?: unknown } = {}) {
|
|
80
|
+
super(message, { code: 'SESSION_NOT_FOUND', retryable: false, ...opts });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** A deadline elapsed before the operation completed (`TIMEOUT`, retryable). */
|
|
85
|
+
export class TimeoutError extends PhoneUseError {
|
|
86
|
+
constructor(message: string, opts: { details?: PhoneUseErrorDetails | undefined; cause?: unknown } = {}) {
|
|
87
|
+
super(message, { code: 'TIMEOUT', retryable: true, ...opts });
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** A device action failed to execute (`ACTION_FAILED`, retryable). */
|
|
92
|
+
export class ActionFailedError extends PhoneUseError {
|
|
93
|
+
constructor(message: string, opts: { details?: PhoneUseErrorDetails | undefined; cause?: unknown } = {}) {
|
|
94
|
+
super(message, { code: 'ACTION_FAILED', retryable: true, ...opts });
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** The backend does not implement the capability (`UNSUPPORTED_CAPABILITY`, not retryable). */
|
|
99
|
+
export class UnsupportedCapabilityError extends PhoneUseError {
|
|
100
|
+
/** The backend that rejected the call. */
|
|
101
|
+
readonly backend: string;
|
|
102
|
+
/** The missing capability. */
|
|
103
|
+
readonly capability: string;
|
|
104
|
+
|
|
105
|
+
constructor(opts: { backend: string; capability: string; cause?: unknown }) {
|
|
106
|
+
super(`backend "${opts.backend}" does not support "${opts.capability}"`, {
|
|
107
|
+
code: 'UNSUPPORTED_CAPABILITY',
|
|
108
|
+
retryable: false,
|
|
109
|
+
details: { backend: opts.backend, capability: opts.capability },
|
|
110
|
+
...(opts.cause === undefined ? {} : { cause: opts.cause }),
|
|
111
|
+
});
|
|
112
|
+
this.backend = opts.backend;
|
|
113
|
+
this.capability = opts.capability;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* The caller's AbortSignal fired (`ABORTED`, not retryable). Abort is caller
|
|
119
|
+
* control flow, not a device outcome — never retried.
|
|
120
|
+
*/
|
|
121
|
+
export class AbortedError extends PhoneUseError {
|
|
122
|
+
constructor(message = 'aborted by caller', opts: { cause?: unknown } = {}) {
|
|
123
|
+
super(message, { code: 'ABORTED', retryable: false, ...opts });
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const TIMEOUT_RE = /\btime[d ]?\s?out\b|\btimeout\b/i;
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Boundary normalizer: ANY thrown value → PhoneUseError. agent-device AppError
|
|
131
|
+
* codes map per the table below; an existing PhoneUseError passes through
|
|
132
|
+
* untouched; unknown values wrap as UNKNOWN (not retryable). The original
|
|
133
|
+
* error always rides on `cause`; details (including hint) are preserved.
|
|
134
|
+
*/
|
|
135
|
+
export function toPhoneUseError(
|
|
136
|
+
err: unknown,
|
|
137
|
+
ctx: { backend?: string; capability?: string } = {},
|
|
138
|
+
): PhoneUseError {
|
|
139
|
+
if (err instanceof PhoneUseError) return err;
|
|
140
|
+
|
|
141
|
+
if (isAgentDeviceError(err)) {
|
|
142
|
+
const e = err as Error & { code?: string; details?: Record<string, unknown> };
|
|
143
|
+
const message = e.message || String(e.code ?? 'agent-device error');
|
|
144
|
+
const details: PhoneUseErrorDetails = { ...(e.details ?? {}) };
|
|
145
|
+
const opts = { details, cause: err };
|
|
146
|
+
// Timeout is a shape, not a code, in agent-device — heuristic on message.
|
|
147
|
+
// Flagged in docs/20: Mac verification is the arbiter for this mapping.
|
|
148
|
+
if (TIMEOUT_RE.test(message)) return new TimeoutError(message, opts);
|
|
149
|
+
switch (e.code) {
|
|
150
|
+
case 'DEVICE_NOT_FOUND':
|
|
151
|
+
return new DeviceNotFoundError(message, opts);
|
|
152
|
+
case 'DEVICE_IN_USE':
|
|
153
|
+
return new DeviceInUseError(message, opts);
|
|
154
|
+
case 'SESSION_NOT_FOUND':
|
|
155
|
+
return new SessionNotFoundError(message, opts);
|
|
156
|
+
case 'UNSUPPORTED_PLATFORM':
|
|
157
|
+
case 'UNSUPPORTED_OPERATION':
|
|
158
|
+
case 'NOT_IMPLEMENTED':
|
|
159
|
+
return new UnsupportedCapabilityError({
|
|
160
|
+
backend: ctx.backend ?? 'unknown',
|
|
161
|
+
capability: ctx.capability ?? String(e.code),
|
|
162
|
+
cause: err,
|
|
163
|
+
});
|
|
164
|
+
default:
|
|
165
|
+
return new ActionFailedError(message, {
|
|
166
|
+
details: { ...details, backendCode: e.code === undefined ? undefined : String(e.code) },
|
|
167
|
+
cause: err,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (err instanceof Error) {
|
|
173
|
+
if (TIMEOUT_RE.test(err.message)) return new TimeoutError(err.message, { cause: err });
|
|
174
|
+
return new PhoneUseError(err.message, { code: 'UNKNOWN', retryable: false, cause: err });
|
|
175
|
+
}
|
|
176
|
+
return new PhoneUseError(String(err), { code: 'UNKNOWN', retryable: false, cause: err });
|
|
177
|
+
}
|
package/src/exec.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { promisify } from 'node:util';
|
|
3
|
+
|
|
4
|
+
/** Per-call options an {@link ExecRunner} accepts. */
|
|
5
|
+
export type ExecOptions = {
|
|
6
|
+
/** Kill the process after this many ms (rejects in the killed shape). */
|
|
7
|
+
timeoutMs?: number | undefined;
|
|
8
|
+
/** Replacement environment for the child process. */
|
|
9
|
+
env?: Record<string, string> | undefined;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/** What an {@link ExecRunner} resolves with on exit 0. */
|
|
13
|
+
export type ExecResult = { stdout: string; stderr: string };
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The process-execution seam (docs/19: dependency-inject the spawn/exec runner
|
|
17
|
+
* so unit tests run without the real binaries). Runners are dumb: resolve on
|
|
18
|
+
* exit 0, reject with the execFile error shape otherwise — error normalization
|
|
19
|
+
* to PhoneUseError happens at the call site, once.
|
|
20
|
+
*/
|
|
21
|
+
export type ExecRunner = (file: string, args: string[], opts?: ExecOptions) => Promise<ExecResult>;
|
|
22
|
+
|
|
23
|
+
const pExecFile = promisify(execFile);
|
|
24
|
+
|
|
25
|
+
export const defaultExecRunner: ExecRunner = async (file, args, opts) => {
|
|
26
|
+
const { stdout, stderr } = await pExecFile(file, args, {
|
|
27
|
+
encoding: 'utf8',
|
|
28
|
+
// simctl `list devices -j` on a runtime-rich Mac can exceed the 1 MiB
|
|
29
|
+
// default and fail spuriously — a failure the VPS test tier can never see.
|
|
30
|
+
maxBuffer: 16 * 1024 * 1024,
|
|
31
|
+
...(opts?.timeoutMs === undefined ? {} : { timeout: opts.timeoutMs }),
|
|
32
|
+
...(opts?.env === undefined ? {} : { env: opts.env }),
|
|
33
|
+
});
|
|
34
|
+
return { stdout, stderr };
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/** The execFile rejection shape runners produce (structural, for call sites). */
|
|
38
|
+
export type ExecError = Error & {
|
|
39
|
+
code?: number | string | undefined;
|
|
40
|
+
stdout?: string | undefined;
|
|
41
|
+
stderr?: string | undefined;
|
|
42
|
+
killed?: boolean | undefined;
|
|
43
|
+
signal?: string | undefined;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export function isExecError(err: unknown): err is ExecError {
|
|
47
|
+
return err instanceof Error && ('code' in err || 'killed' in err || 'stderr' in err);
|
|
48
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @phone-use/sdk — the device runtime SDK: engine-as-object lifecycle
|
|
3
|
+
* (ios.launch/connect → Device), Device backends, config, errors, capabilities
|
|
4
|
+
* (docs/20-runtime-sdk-v1-plan.md items 2-3; action verbs land in item 4).
|
|
5
|
+
*
|
|
6
|
+
* The test double (FakeBackend) lives on the "@phone-use/sdk/testing" subpath,
|
|
7
|
+
* deliberately not re-exported here.
|
|
8
|
+
*/
|
|
9
|
+
/** The published package version (kept in sync with package.json by the release flow). */
|
|
10
|
+
export const VERSION = '0.1.0';
|
|
11
|
+
|
|
12
|
+
export {
|
|
13
|
+
type Action,
|
|
14
|
+
type ActionResult,
|
|
15
|
+
type ActionVerb,
|
|
16
|
+
type ActOptions,
|
|
17
|
+
buildObserveResult,
|
|
18
|
+
type CompiledSkill,
|
|
19
|
+
type ElementQuery,
|
|
20
|
+
executeAction,
|
|
21
|
+
type ObservedElement,
|
|
22
|
+
type ObserveResult,
|
|
23
|
+
toActions,
|
|
24
|
+
} from './actions.ts';
|
|
25
|
+
export {
|
|
26
|
+
type BackendFactory,
|
|
27
|
+
BaseDeviceBackend,
|
|
28
|
+
type DeviceBackend,
|
|
29
|
+
getBackendFactory,
|
|
30
|
+
listBackends,
|
|
31
|
+
registerBackend,
|
|
32
|
+
} from './backend.ts';
|
|
33
|
+
export { createAgentDeviceBackend } from './backends/agent-device.ts';
|
|
34
|
+
export { type IosConnectOptions, type IosLaunchOptions, ios } from './backends/ios.ts';
|
|
35
|
+
export type { AndroidDeviceConfig, CommonDeviceConfig, DeviceConfig, IosDeviceConfig } from './config.ts';
|
|
36
|
+
export type {
|
|
37
|
+
AlertAction,
|
|
38
|
+
BackendAlertResult,
|
|
39
|
+
Capability,
|
|
40
|
+
OpenAppResult,
|
|
41
|
+
PressTarget,
|
|
42
|
+
Rect,
|
|
43
|
+
ScrollDirection,
|
|
44
|
+
Snapshot,
|
|
45
|
+
SnapshotNode,
|
|
46
|
+
} from './device.ts';
|
|
47
|
+
export { ALL_CAPABILITIES } from './device.ts';
|
|
48
|
+
export {
|
|
49
|
+
AbortedError,
|
|
50
|
+
ActionFailedError,
|
|
51
|
+
DeviceInUseError,
|
|
52
|
+
DeviceNotFoundError,
|
|
53
|
+
PhoneUseError,
|
|
54
|
+
type PhoneUseErrorCode,
|
|
55
|
+
type PhoneUseErrorDetails,
|
|
56
|
+
SessionNotFoundError,
|
|
57
|
+
TimeoutError,
|
|
58
|
+
toPhoneUseError,
|
|
59
|
+
UnsupportedCapabilityError,
|
|
60
|
+
} from './errors.ts';
|
|
61
|
+
export {
|
|
62
|
+
type CreateDeviceHandleOptions,
|
|
63
|
+
createDeviceHandle,
|
|
64
|
+
type Device,
|
|
65
|
+
type DevicePlatform,
|
|
66
|
+
type DeviceStatus,
|
|
67
|
+
} from './lifecycle.ts';
|
|
68
|
+
export type {
|
|
69
|
+
ActionEvidence,
|
|
70
|
+
AlertOutcome,
|
|
71
|
+
Observation,
|
|
72
|
+
RenderState,
|
|
73
|
+
Resolution,
|
|
74
|
+
ResolveOpts,
|
|
75
|
+
UiElement,
|
|
76
|
+
} from './observe.ts';
|
|
77
|
+
export { DeviceCore, describeError, labelMatches, matchInElements } from './observe.ts';
|
|
78
|
+
export { SecretStore } from './secrets.ts';
|
|
79
|
+
|
|
80
|
+
// Built-in backend registration — explicit, here, so importing the barrel
|
|
81
|
+
// registers it (documented; sideEffects:false refers to bundler tree-shaking
|
|
82
|
+
// of the *published* dist, where the barrel is the entry).
|
|
83
|
+
import { registerBackend as _register } from './backend.ts';
|
|
84
|
+
import { createAgentDeviceBackend as _createAd } from './backends/agent-device.ts';
|
|
85
|
+
|
|
86
|
+
_register('agent-device', _createAd);
|