@phone-use/sdk 0.1.1 → 0.3.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/dist/index.d.mts +133 -2
- package/dist/index.mjs +278 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/backends/cloud-sandbox.ts +202 -0
- package/src/backends/device-runner.ts +205 -0
- package/src/index.ts +10 -1
package/dist/index.d.mts
CHANGED
|
@@ -477,6 +477,137 @@ declare function executeAction(core: DeviceCore, action: Action, opts?: ActOptio
|
|
|
477
477
|
*/
|
|
478
478
|
declare function createAgentDeviceBackend(config?: DeviceConfig): DeviceBackend;
|
|
479
479
|
//#endregion
|
|
480
|
+
//#region src/backends/cloud-sandbox.d.ts
|
|
481
|
+
type SandboxRpcMethod = 'snapshot' | 'screenshot' | 'press' | 'longPress' | 'fill' | 'typeText' | 'pressKey' | 'scroll' | 'pan' | 'installApp' | 'waitForText' | 'systemAlert' | 'home' | 'back' | 'openApp' | 'listApps' | 'closeSession';
|
|
482
|
+
type SandboxRpcRequest = {
|
|
483
|
+
method: SandboxRpcMethod;
|
|
484
|
+
args: unknown[];
|
|
485
|
+
};
|
|
486
|
+
type SandboxRpcSuccess = {
|
|
487
|
+
ok: true;
|
|
488
|
+
result: unknown;
|
|
489
|
+
};
|
|
490
|
+
type SandboxRpcFailure = {
|
|
491
|
+
ok: false;
|
|
492
|
+
error: {
|
|
493
|
+
message: string;
|
|
494
|
+
code?: string | undefined;
|
|
495
|
+
retryable?: boolean | undefined;
|
|
496
|
+
};
|
|
497
|
+
};
|
|
498
|
+
type SandboxRpcResponse = SandboxRpcSuccess | SandboxRpcFailure;
|
|
499
|
+
type CloudSandboxBackendOptions = {
|
|
500
|
+
endpoint: string;
|
|
501
|
+
token: string;
|
|
502
|
+
capabilities?: Capability[] | undefined;
|
|
503
|
+
fetch?: typeof globalThis.fetch | undefined;
|
|
504
|
+
};
|
|
505
|
+
declare class CloudSandboxBackend extends BaseDeviceBackend {
|
|
506
|
+
private readonly endpoint;
|
|
507
|
+
private readonly token;
|
|
508
|
+
private readonly fetchImpl;
|
|
509
|
+
constructor(opts: CloudSandboxBackendOptions);
|
|
510
|
+
private rpc;
|
|
511
|
+
snapshot(opts?: {
|
|
512
|
+
interactiveOnly?: boolean;
|
|
513
|
+
depth?: number;
|
|
514
|
+
}): Promise<Snapshot>;
|
|
515
|
+
screenshot(opts: {
|
|
516
|
+
path: string;
|
|
517
|
+
overlayRefs?: boolean;
|
|
518
|
+
}): Promise<{
|
|
519
|
+
path: string;
|
|
520
|
+
}>;
|
|
521
|
+
press(target: PressTarget): Promise<void>;
|
|
522
|
+
longPress(ref: string, durationMs?: number): Promise<void>;
|
|
523
|
+
fill(ref: string, text: string): Promise<void>;
|
|
524
|
+
typeText(text: string): Promise<void>;
|
|
525
|
+
pressKey(key: 'return'): Promise<void>;
|
|
526
|
+
scroll(direction: ScrollDirection): Promise<void>;
|
|
527
|
+
pan(x: number, y: number, dx: number, dy: number, durationMs?: number): Promise<void>;
|
|
528
|
+
waitForText(text: string, timeoutMs?: number): Promise<void>;
|
|
529
|
+
systemAlert(action: AlertAction): Promise<BackendAlertResult>;
|
|
530
|
+
home(): Promise<void>;
|
|
531
|
+
back(): Promise<void>;
|
|
532
|
+
openApp(opts: {
|
|
533
|
+
app?: string;
|
|
534
|
+
url?: string;
|
|
535
|
+
relaunch?: boolean;
|
|
536
|
+
}): Promise<OpenAppResult>;
|
|
537
|
+
listApps(): Promise<string[]>;
|
|
538
|
+
closeSession(): Promise<void>;
|
|
539
|
+
/** Upload a zipped .app bundle (base64) and install it on the sandbox device. */
|
|
540
|
+
installApp(base64Zip: string): Promise<{
|
|
541
|
+
installed?: string;
|
|
542
|
+
}>;
|
|
543
|
+
}
|
|
544
|
+
/**
|
|
545
|
+
* Build a CloudSandboxBackend from explicit options or the environment:
|
|
546
|
+
* PHONE_USE_SANDBOX_URL + PHONE_USE_SANDBOX_TOKEN (printed by
|
|
547
|
+
* `phone-use sandbox env <id>`).
|
|
548
|
+
*/
|
|
549
|
+
declare function createCloudSandboxBackend(config?: Partial<CloudSandboxBackendOptions>): CloudSandboxBackend;
|
|
550
|
+
//#endregion
|
|
551
|
+
//#region src/backends/device-runner.d.ts
|
|
552
|
+
/**
|
|
553
|
+
* A device-runner is addressed purely by URL, so it takes no DeviceConfig
|
|
554
|
+
* platform/udid selection — only where to reach the runner and how to auth.
|
|
555
|
+
*/
|
|
556
|
+
type DeviceRunnerConfig = {
|
|
557
|
+
endpoint?: string | undefined;
|
|
558
|
+
token?: string | undefined;
|
|
559
|
+
timeoutMs?: number | undefined;
|
|
560
|
+
};
|
|
561
|
+
/**
|
|
562
|
+
* Backend that speaks to an on-device runner: an XCTest-hosted JSON-RPC server
|
|
563
|
+
* running ON the iPhone itself, which holds the automation privileges iOS
|
|
564
|
+
* denies to ordinary apps.
|
|
565
|
+
*
|
|
566
|
+
* The endpoint is just a URL, so the same backend serves every topology:
|
|
567
|
+
* - `http://127.0.0.1:45678` — port-forwarded from a paired host
|
|
568
|
+
* - `http://<phone-ip>:45678` — straight over the LAN / tailnet
|
|
569
|
+
* - `https://relay.example/d/<id>` — the runner dials out to a cloud relay,
|
|
570
|
+
* which is what lets an agent anywhere drive the phone with no inbound
|
|
571
|
+
* ports and no Mac in the loop.
|
|
572
|
+
*
|
|
573
|
+
* The wire format matches the shape proven by rounak/PhoneAgent: newline-free
|
|
574
|
+
* JSON request/response over HTTP POST, one method per call.
|
|
575
|
+
*/
|
|
576
|
+
declare class DeviceRunnerBackend extends BaseDeviceBackend {
|
|
577
|
+
#private;
|
|
578
|
+
constructor(config?: DeviceRunnerConfig);
|
|
579
|
+
snapshot(opts?: {
|
|
580
|
+
interactiveOnly?: boolean | undefined;
|
|
581
|
+
depth?: number | undefined;
|
|
582
|
+
}): Promise<Snapshot>;
|
|
583
|
+
screenshot(opts: {
|
|
584
|
+
path: string;
|
|
585
|
+
overlayRefs?: boolean | undefined;
|
|
586
|
+
}): Promise<{
|
|
587
|
+
path: string;
|
|
588
|
+
}>;
|
|
589
|
+
press(target: PressTarget): Promise<void>;
|
|
590
|
+
fill(ref: string, text: string): Promise<void>;
|
|
591
|
+
typeText(text: string): Promise<void>;
|
|
592
|
+
scroll(direction: ScrollDirection): Promise<void>;
|
|
593
|
+
pan(x: number, y: number, dx: number, dy: number, durationMs?: number): Promise<void>;
|
|
594
|
+
home(): Promise<void>;
|
|
595
|
+
openApp(opts: {
|
|
596
|
+
app?: string | undefined;
|
|
597
|
+
url?: string | undefined;
|
|
598
|
+
relaunch?: boolean | undefined;
|
|
599
|
+
}): Promise<OpenAppResult>;
|
|
600
|
+
systemAlert(action: AlertAction): Promise<BackendAlertResult>;
|
|
601
|
+
closeSession(): Promise<void>;
|
|
602
|
+
/** Liveness probe used by `phone-use doctor` and the relay health check. */
|
|
603
|
+
ping(): Promise<{
|
|
604
|
+
ok: boolean;
|
|
605
|
+
ios?: string;
|
|
606
|
+
device?: string;
|
|
607
|
+
}>;
|
|
608
|
+
}
|
|
609
|
+
declare const createDeviceRunnerBackend: (config?: DeviceRunnerConfig) => DeviceRunnerBackend;
|
|
610
|
+
//#endregion
|
|
480
611
|
//#region src/lifecycle.d.ts
|
|
481
612
|
/** The platform a {@link Device} runs on. */
|
|
482
613
|
type DevicePlatform = 'ios' | 'android';
|
|
@@ -683,7 +814,7 @@ declare const ios: {
|
|
|
683
814
|
* deliberately not re-exported here.
|
|
684
815
|
*/
|
|
685
816
|
/** The published package version (kept in sync with package.json by the release flow). */
|
|
686
|
-
declare const VERSION = "0.
|
|
817
|
+
declare const VERSION = "0.3.0";
|
|
687
818
|
//#endregion
|
|
688
|
-
export { ALL_CAPABILITIES, AbortedError, type ActOptions, type Action, type ActionEvidence, ActionFailedError, type ActionResult, type ActionVerb, type AlertAction, type AlertOutcome, type AndroidDeviceConfig, type BackendAlertResult, type BackendFactory, BaseDeviceBackend, type Capability, type CommonDeviceConfig, type CompiledSkill, type CreateDeviceHandleOptions, type Device, type DeviceBackend, type DeviceConfig, DeviceCore, DeviceInUseError, DeviceNotFoundError, type DevicePlatform, type DeviceStatus, type ElementQuery, type IosConnectOptions, type IosDeviceConfig, type IosLaunchOptions, type Observation, type ObserveResult, type ObservedElement, type OpenAppResult, PhoneUseError, type PhoneUseErrorCode, type PhoneUseErrorDetails, type PressTarget, type Rect, type RenderState, type Resolution, type ResolveOpts, type ScrollDirection, SecretStore, SessionNotFoundError, type Snapshot, type SnapshotNode, TimeoutError, type UiElement, UnsupportedCapabilityError, VERSION, buildObserveResult, createAgentDeviceBackend, createDeviceHandle, describeError, executeAction, getBackendFactory, ios, labelMatches, listBackends, matchInElements, registerBackend, toActions, toPhoneUseError };
|
|
819
|
+
export { ALL_CAPABILITIES, AbortedError, type ActOptions, type Action, type ActionEvidence, ActionFailedError, type ActionResult, type ActionVerb, type AlertAction, type AlertOutcome, type AndroidDeviceConfig, type BackendAlertResult, type BackendFactory, BaseDeviceBackend, type Capability, CloudSandboxBackend, type CloudSandboxBackendOptions, type CommonDeviceConfig, type CompiledSkill, type CreateDeviceHandleOptions, type Device, type DeviceBackend, type DeviceConfig, DeviceCore, DeviceInUseError, DeviceNotFoundError, type DevicePlatform, DeviceRunnerBackend, type DeviceStatus, type ElementQuery, type IosConnectOptions, type IosDeviceConfig, type IosLaunchOptions, type Observation, type ObserveResult, type ObservedElement, type OpenAppResult, PhoneUseError, type PhoneUseErrorCode, type PhoneUseErrorDetails, type PressTarget, type Rect, type RenderState, type Resolution, type ResolveOpts, type SandboxRpcMethod, type SandboxRpcRequest, type SandboxRpcResponse, type ScrollDirection, SecretStore, SessionNotFoundError, type Snapshot, type SnapshotNode, TimeoutError, type UiElement, UnsupportedCapabilityError, VERSION, buildObserveResult, createAgentDeviceBackend, createCloudSandboxBackend, createDeviceHandle, createDeviceRunnerBackend, describeError, executeAction, getBackendFactory, ios, labelMatches, listBackends, matchInElements, registerBackend, toActions, toPhoneUseError };
|
|
689
820
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { a as registerBackend, c as DeviceInUseError, d as SessionNotFoundError, f as TimeoutError, i as listBackends, l as DeviceNotFoundError, m as toPhoneUseError, n as BaseDeviceBackend, o as AbortedError, p as UnsupportedCapabilityError, r as getBackendFactory, s as ActionFailedError, t as ALL_CAPABILITIES, u as PhoneUseError } from "./device-Xsy_LPUF.mjs";
|
|
2
2
|
import { createAgentDeviceClient } from "agent-device";
|
|
3
|
+
import { writeFile } from "node:fs/promises";
|
|
3
4
|
import { execFile } from "node:child_process";
|
|
4
5
|
import { promisify } from "node:util";
|
|
5
6
|
//#region src/observe.ts
|
|
@@ -1453,6 +1454,281 @@ function createAgentDeviceBackend(config) {
|
|
|
1453
1454
|
return new AgentDeviceBackend(config);
|
|
1454
1455
|
}
|
|
1455
1456
|
//#endregion
|
|
1457
|
+
//#region src/backends/cloud-sandbox.ts
|
|
1458
|
+
var CloudSandboxBackend = class extends BaseDeviceBackend {
|
|
1459
|
+
endpoint;
|
|
1460
|
+
token;
|
|
1461
|
+
fetchImpl;
|
|
1462
|
+
constructor(opts) {
|
|
1463
|
+
super("phone-use-cloud", opts.capabilities ?? ALL_CAPABILITIES);
|
|
1464
|
+
this.endpoint = opts.endpoint.replace(/\/+$/, "");
|
|
1465
|
+
this.token = opts.token;
|
|
1466
|
+
this.fetchImpl = opts.fetch ?? globalThis.fetch;
|
|
1467
|
+
}
|
|
1468
|
+
async rpc(method, ...args) {
|
|
1469
|
+
const response = await this.fetchImpl(`${this.endpoint}/rpc`, {
|
|
1470
|
+
method: "POST",
|
|
1471
|
+
headers: {
|
|
1472
|
+
authorization: `Bearer ${this.token}`,
|
|
1473
|
+
"content-type": "application/json"
|
|
1474
|
+
},
|
|
1475
|
+
body: JSON.stringify({
|
|
1476
|
+
method,
|
|
1477
|
+
args
|
|
1478
|
+
})
|
|
1479
|
+
});
|
|
1480
|
+
const body = await response.json().catch(() => ({}));
|
|
1481
|
+
if (!response.ok || !("ok" in body)) throw new PhoneUseError(("error" in body && typeof body.error === "string" ? body.error : void 0) ?? ("message" in body ? body.message : void 0) ?? `HTTP ${response.status}`, {
|
|
1482
|
+
code: "UNKNOWN",
|
|
1483
|
+
retryable: false
|
|
1484
|
+
});
|
|
1485
|
+
if (!body.ok) throw new PhoneUseError(body.error.message, {
|
|
1486
|
+
code: normalizeErrorCode(body.error.code),
|
|
1487
|
+
retryable: body.error.retryable ?? false
|
|
1488
|
+
});
|
|
1489
|
+
return body.result;
|
|
1490
|
+
}
|
|
1491
|
+
snapshot(opts) {
|
|
1492
|
+
return this.rpc("snapshot", opts);
|
|
1493
|
+
}
|
|
1494
|
+
async screenshot(opts) {
|
|
1495
|
+
const result = await this.rpc("screenshot", { overlayRefs: opts.overlayRefs });
|
|
1496
|
+
await writeFile(opts.path, Buffer.from(result.base64, "base64"));
|
|
1497
|
+
return { path: opts.path };
|
|
1498
|
+
}
|
|
1499
|
+
press(target) {
|
|
1500
|
+
return this.rpc("press", target);
|
|
1501
|
+
}
|
|
1502
|
+
longPress(ref, durationMs) {
|
|
1503
|
+
return this.rpc("longPress", ref, durationMs);
|
|
1504
|
+
}
|
|
1505
|
+
fill(ref, text) {
|
|
1506
|
+
return this.rpc("fill", ref, text);
|
|
1507
|
+
}
|
|
1508
|
+
typeText(text) {
|
|
1509
|
+
return this.rpc("typeText", text);
|
|
1510
|
+
}
|
|
1511
|
+
pressKey(key) {
|
|
1512
|
+
return this.rpc("pressKey", key);
|
|
1513
|
+
}
|
|
1514
|
+
scroll(direction) {
|
|
1515
|
+
return this.rpc("scroll", direction);
|
|
1516
|
+
}
|
|
1517
|
+
pan(x, y, dx, dy, durationMs) {
|
|
1518
|
+
return this.rpc("pan", x, y, dx, dy, durationMs);
|
|
1519
|
+
}
|
|
1520
|
+
waitForText(text, timeoutMs) {
|
|
1521
|
+
return this.rpc("waitForText", text, timeoutMs);
|
|
1522
|
+
}
|
|
1523
|
+
systemAlert(action) {
|
|
1524
|
+
return this.rpc("systemAlert", action);
|
|
1525
|
+
}
|
|
1526
|
+
home() {
|
|
1527
|
+
return this.rpc("home");
|
|
1528
|
+
}
|
|
1529
|
+
back() {
|
|
1530
|
+
return this.rpc("back");
|
|
1531
|
+
}
|
|
1532
|
+
openApp(opts) {
|
|
1533
|
+
return this.rpc("openApp", opts);
|
|
1534
|
+
}
|
|
1535
|
+
listApps() {
|
|
1536
|
+
return this.rpc("listApps");
|
|
1537
|
+
}
|
|
1538
|
+
closeSession() {
|
|
1539
|
+
return this.rpc("closeSession");
|
|
1540
|
+
}
|
|
1541
|
+
/** Upload a zipped .app bundle (base64) and install it on the sandbox device. */
|
|
1542
|
+
installApp(base64Zip) {
|
|
1543
|
+
return this.rpc("installApp", base64Zip);
|
|
1544
|
+
}
|
|
1545
|
+
};
|
|
1546
|
+
/**
|
|
1547
|
+
* Build a CloudSandboxBackend from explicit options or the environment:
|
|
1548
|
+
* PHONE_USE_SANDBOX_URL + PHONE_USE_SANDBOX_TOKEN (printed by
|
|
1549
|
+
* `phone-use sandbox env <id>`).
|
|
1550
|
+
*/
|
|
1551
|
+
function createCloudSandboxBackend(config) {
|
|
1552
|
+
const endpoint = config?.endpoint ?? process.env.PHONE_USE_SANDBOX_URL;
|
|
1553
|
+
const token = config?.token ?? process.env.PHONE_USE_SANDBOX_TOKEN;
|
|
1554
|
+
if (!endpoint || !token) throw new PhoneUseError("cloud sandbox backend needs PHONE_USE_SANDBOX_URL and PHONE_USE_SANDBOX_TOKEN (see `phone-use sandbox env <id>`)", {
|
|
1555
|
+
code: "BACKEND_NOT_FOUND",
|
|
1556
|
+
retryable: false
|
|
1557
|
+
});
|
|
1558
|
+
return new CloudSandboxBackend({
|
|
1559
|
+
...config,
|
|
1560
|
+
endpoint,
|
|
1561
|
+
token
|
|
1562
|
+
});
|
|
1563
|
+
}
|
|
1564
|
+
function normalizeErrorCode(code) {
|
|
1565
|
+
return (/* @__PURE__ */ new Set([
|
|
1566
|
+
"DEVICE_NOT_FOUND",
|
|
1567
|
+
"DEVICE_IN_USE",
|
|
1568
|
+
"SESSION_NOT_FOUND",
|
|
1569
|
+
"TIMEOUT",
|
|
1570
|
+
"ACTION_FAILED",
|
|
1571
|
+
"UNSUPPORTED_CAPABILITY",
|
|
1572
|
+
"BACKEND_NOT_FOUND",
|
|
1573
|
+
"ABORTED",
|
|
1574
|
+
"UNKNOWN"
|
|
1575
|
+
])).has(code ?? "") ? code : "UNKNOWN";
|
|
1576
|
+
}
|
|
1577
|
+
//#endregion
|
|
1578
|
+
//#region src/backends/device-runner.ts
|
|
1579
|
+
const RUNNER_CAPABILITIES = [
|
|
1580
|
+
"snapshot",
|
|
1581
|
+
"screenshot",
|
|
1582
|
+
"press",
|
|
1583
|
+
"fill",
|
|
1584
|
+
"type",
|
|
1585
|
+
"scroll",
|
|
1586
|
+
"pan",
|
|
1587
|
+
"openApp",
|
|
1588
|
+
"home"
|
|
1589
|
+
];
|
|
1590
|
+
/**
|
|
1591
|
+
* Backend that speaks to an on-device runner: an XCTest-hosted JSON-RPC server
|
|
1592
|
+
* running ON the iPhone itself, which holds the automation privileges iOS
|
|
1593
|
+
* denies to ordinary apps.
|
|
1594
|
+
*
|
|
1595
|
+
* The endpoint is just a URL, so the same backend serves every topology:
|
|
1596
|
+
* - `http://127.0.0.1:45678` — port-forwarded from a paired host
|
|
1597
|
+
* - `http://<phone-ip>:45678` — straight over the LAN / tailnet
|
|
1598
|
+
* - `https://relay.example/d/<id>` — the runner dials out to a cloud relay,
|
|
1599
|
+
* which is what lets an agent anywhere drive the phone with no inbound
|
|
1600
|
+
* ports and no Mac in the loop.
|
|
1601
|
+
*
|
|
1602
|
+
* The wire format matches the shape proven by rounak/PhoneAgent: newline-free
|
|
1603
|
+
* JSON request/response over HTTP POST, one method per call.
|
|
1604
|
+
*/
|
|
1605
|
+
var DeviceRunnerBackend = class extends BaseDeviceBackend {
|
|
1606
|
+
#endpoint;
|
|
1607
|
+
#token;
|
|
1608
|
+
#timeoutMs;
|
|
1609
|
+
constructor(config) {
|
|
1610
|
+
super("device-runner", RUNNER_CAPABILITIES);
|
|
1611
|
+
const endpoint = config?.endpoint ?? process.env.PHONE_USE_RUNNER_URL ?? "http://127.0.0.1:45678";
|
|
1612
|
+
this.#endpoint = endpoint.replace(/\/+$/, "");
|
|
1613
|
+
this.#token = config?.token ?? process.env.PHONE_USE_RUNNER_TOKEN;
|
|
1614
|
+
this.#timeoutMs = config?.timeoutMs ?? 3e4;
|
|
1615
|
+
}
|
|
1616
|
+
async #rpc(method, params = {}) {
|
|
1617
|
+
const controller = new AbortController();
|
|
1618
|
+
const timer = setTimeout(() => controller.abort(), this.#timeoutMs);
|
|
1619
|
+
let res;
|
|
1620
|
+
try {
|
|
1621
|
+
res = await fetch(this.#endpoint, {
|
|
1622
|
+
method: "POST",
|
|
1623
|
+
headers: {
|
|
1624
|
+
"content-type": "application/json",
|
|
1625
|
+
...this.#token ? { authorization: `Bearer ${this.#token}` } : {}
|
|
1626
|
+
},
|
|
1627
|
+
body: JSON.stringify({
|
|
1628
|
+
jsonrpc: "2.0",
|
|
1629
|
+
id: Date.now(),
|
|
1630
|
+
method,
|
|
1631
|
+
params
|
|
1632
|
+
}),
|
|
1633
|
+
signal: controller.signal
|
|
1634
|
+
});
|
|
1635
|
+
} catch (cause) {
|
|
1636
|
+
if (controller.signal.aborted) throw new TimeoutError(`runner did not answer ${method} within ${this.#timeoutMs}ms`);
|
|
1637
|
+
throw new DeviceNotFoundError(`cannot reach the on-device runner at ${this.#endpoint} — is it activated on the phone?`, { cause });
|
|
1638
|
+
} finally {
|
|
1639
|
+
clearTimeout(timer);
|
|
1640
|
+
}
|
|
1641
|
+
if (!res.ok) throw new ActionFailedError(`runner returned HTTP ${res.status} for ${method}`);
|
|
1642
|
+
const body = await res.json();
|
|
1643
|
+
if (body.error) throw new ActionFailedError(body.error.message ?? `runner rejected ${method}`);
|
|
1644
|
+
return body.result;
|
|
1645
|
+
}
|
|
1646
|
+
async snapshot(opts) {
|
|
1647
|
+
const wire = await this.#rpc("get_tree", {
|
|
1648
|
+
interactiveOnly: opts?.interactiveOnly ?? false,
|
|
1649
|
+
depth: opts?.depth
|
|
1650
|
+
});
|
|
1651
|
+
return {
|
|
1652
|
+
appBundleId: wire.app,
|
|
1653
|
+
appName: wire.app,
|
|
1654
|
+
nodes: (wire.elements ?? []).map((e) => ({
|
|
1655
|
+
ref: e.ref,
|
|
1656
|
+
role: e.role,
|
|
1657
|
+
type: e.role,
|
|
1658
|
+
label: e.label,
|
|
1659
|
+
value: e.value,
|
|
1660
|
+
enabled: e.enabled,
|
|
1661
|
+
rect: e.rect ? {
|
|
1662
|
+
x: e.rect.x,
|
|
1663
|
+
y: e.rect.y,
|
|
1664
|
+
width: e.rect.w,
|
|
1665
|
+
height: e.rect.h
|
|
1666
|
+
} : void 0
|
|
1667
|
+
}))
|
|
1668
|
+
};
|
|
1669
|
+
}
|
|
1670
|
+
async screenshot(opts) {
|
|
1671
|
+
const { base64 } = await this.#rpc("get_screen_image", { overlayRefs: opts.overlayRefs ?? false });
|
|
1672
|
+
const { writeFile } = await import("node:fs/promises");
|
|
1673
|
+
await writeFile(opts.path, Buffer.from(base64, "base64"));
|
|
1674
|
+
return { path: opts.path };
|
|
1675
|
+
}
|
|
1676
|
+
async press(target) {
|
|
1677
|
+
if ("ref" in target) {
|
|
1678
|
+
await this.#rpc("tap_element", { ref: target.ref });
|
|
1679
|
+
return;
|
|
1680
|
+
}
|
|
1681
|
+
await this.#rpc("tap", {
|
|
1682
|
+
x: target.x,
|
|
1683
|
+
y: target.y
|
|
1684
|
+
});
|
|
1685
|
+
}
|
|
1686
|
+
async fill(ref, text) {
|
|
1687
|
+
await this.#rpc("enter_text", {
|
|
1688
|
+
ref,
|
|
1689
|
+
text,
|
|
1690
|
+
replace: true
|
|
1691
|
+
});
|
|
1692
|
+
}
|
|
1693
|
+
async typeText(text) {
|
|
1694
|
+
await this.#rpc("enter_text", {
|
|
1695
|
+
text,
|
|
1696
|
+
replace: false
|
|
1697
|
+
});
|
|
1698
|
+
}
|
|
1699
|
+
async scroll(direction) {
|
|
1700
|
+
await this.#rpc("scroll", { direction });
|
|
1701
|
+
}
|
|
1702
|
+
async pan(x, y, dx, dy, durationMs = 300) {
|
|
1703
|
+
await this.#rpc("swipe", {
|
|
1704
|
+
x,
|
|
1705
|
+
y,
|
|
1706
|
+
dx,
|
|
1707
|
+
dy,
|
|
1708
|
+
durationMs
|
|
1709
|
+
});
|
|
1710
|
+
}
|
|
1711
|
+
async home() {
|
|
1712
|
+
await this.#rpc("home");
|
|
1713
|
+
}
|
|
1714
|
+
async openApp(opts) {
|
|
1715
|
+
return this.#rpc("open_app", {
|
|
1716
|
+
app: opts.app,
|
|
1717
|
+
url: opts.url,
|
|
1718
|
+
relaunch: opts.relaunch ?? false
|
|
1719
|
+
});
|
|
1720
|
+
}
|
|
1721
|
+
async systemAlert(action) {
|
|
1722
|
+
return this.#rpc("alert", { action });
|
|
1723
|
+
}
|
|
1724
|
+
async closeSession() {}
|
|
1725
|
+
/** Liveness probe used by `phone-use doctor` and the relay health check. */
|
|
1726
|
+
async ping() {
|
|
1727
|
+
return this.#rpc("get_context");
|
|
1728
|
+
}
|
|
1729
|
+
};
|
|
1730
|
+
const createDeviceRunnerBackend = (config) => new DeviceRunnerBackend(config);
|
|
1731
|
+
//#endregion
|
|
1456
1732
|
//#region src/exec.ts
|
|
1457
1733
|
const pExecFile = promisify(execFile);
|
|
1458
1734
|
const defaultExecRunner = async (file, args, opts) => {
|
|
@@ -1840,9 +2116,9 @@ const ios = {
|
|
|
1840
2116
|
* deliberately not re-exported here.
|
|
1841
2117
|
*/
|
|
1842
2118
|
/** The published package version (kept in sync with package.json by the release flow). */
|
|
1843
|
-
const VERSION = "0.
|
|
2119
|
+
const VERSION = "0.3.0";
|
|
1844
2120
|
registerBackend("agent-device", createAgentDeviceBackend);
|
|
1845
2121
|
//#endregion
|
|
1846
|
-
export { ALL_CAPABILITIES, AbortedError, ActionFailedError, BaseDeviceBackend, DeviceCore, DeviceInUseError, DeviceNotFoundError, PhoneUseError, SecretStore, SessionNotFoundError, TimeoutError, UnsupportedCapabilityError, VERSION, buildObserveResult, createAgentDeviceBackend, createDeviceHandle, describeError, executeAction, getBackendFactory, ios, labelMatches, listBackends, matchInElements, registerBackend, toActions, toPhoneUseError };
|
|
2122
|
+
export { ALL_CAPABILITIES, AbortedError, ActionFailedError, BaseDeviceBackend, CloudSandboxBackend, DeviceCore, DeviceInUseError, DeviceNotFoundError, DeviceRunnerBackend, PhoneUseError, SecretStore, SessionNotFoundError, TimeoutError, UnsupportedCapabilityError, VERSION, buildObserveResult, createAgentDeviceBackend, createCloudSandboxBackend, createDeviceHandle, createDeviceRunnerBackend, describeError, executeAction, getBackendFactory, ios, labelMatches, listBackends, matchInElements, registerBackend, toActions, toPhoneUseError };
|
|
1847
2123
|
|
|
1848
2124
|
//# sourceMappingURL=index.mjs.map
|