@tapflowio/ios-agent 0.29.0 → 0.31.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 (71) hide show
  1. package/bin/guest-helper +0 -0
  2. package/bin/screencapture-helper +0 -0
  3. package/dist/AudioCaptureStreamer.d.ts +3 -2
  4. package/dist/AudioCaptureStreamer.d.ts.map +1 -1
  5. package/dist/AudioCaptureStreamer.js +16 -10
  6. package/dist/AudioCaptureStreamer.js.map +1 -1
  7. package/dist/DeviceChromeLoader.d.ts +28 -1
  8. package/dist/DeviceChromeLoader.d.ts.map +1 -1
  9. package/dist/DeviceChromeLoader.js +118 -37
  10. package/dist/DeviceChromeLoader.js.map +1 -1
  11. package/dist/FoldableInput.d.ts +58 -0
  12. package/dist/FoldableInput.d.ts.map +1 -0
  13. package/dist/FoldableInput.js +166 -0
  14. package/dist/FoldableInput.js.map +1 -0
  15. package/dist/GuestHelper.d.ts +109 -0
  16. package/dist/GuestHelper.d.ts.map +1 -0
  17. package/dist/GuestHelper.js +437 -0
  18. package/dist/GuestHelper.js.map +1 -0
  19. package/dist/IOSAgent.d.ts +126 -4
  20. package/dist/IOSAgent.d.ts.map +1 -1
  21. package/dist/IOSAgent.js +673 -62
  22. package/dist/IOSAgent.js.map +1 -1
  23. package/dist/InputChannel.d.ts +38 -0
  24. package/dist/InputChannel.d.ts.map +1 -0
  25. package/dist/InputChannel.js +7 -0
  26. package/dist/InputChannel.js.map +1 -0
  27. package/dist/PanelState.d.ts +72 -0
  28. package/dist/PanelState.d.ts.map +1 -0
  29. package/dist/PanelState.js +192 -0
  30. package/dist/PanelState.js.map +1 -0
  31. package/dist/PhysicalOrientation.d.ts +58 -0
  32. package/dist/PhysicalOrientation.d.ts.map +1 -0
  33. package/dist/PhysicalOrientation.js +155 -0
  34. package/dist/PhysicalOrientation.js.map +1 -0
  35. package/dist/ScreenCaptureStreamer.d.ts +12 -1
  36. package/dist/ScreenCaptureStreamer.d.ts.map +1 -1
  37. package/dist/ScreenCaptureStreamer.js +28 -3
  38. package/dist/ScreenCaptureStreamer.js.map +1 -1
  39. package/dist/SimProcessTree.d.ts +3 -1
  40. package/dist/SimProcessTree.d.ts.map +1 -1
  41. package/dist/SimProcessTree.js +14 -4
  42. package/dist/SimProcessTree.js.map +1 -1
  43. package/dist/SimctlWrapper.d.ts +20 -2
  44. package/dist/SimctlWrapper.d.ts.map +1 -1
  45. package/dist/SimctlWrapper.js +53 -3
  46. package/dist/SimctlWrapper.js.map +1 -1
  47. package/dist/SimulatorNetwork.d.ts +3 -2
  48. package/dist/SimulatorNetwork.d.ts.map +1 -1
  49. package/dist/SimulatorNetwork.js +3 -2
  50. package/dist/SimulatorNetwork.js.map +1 -1
  51. package/dist/TouchHelper.d.ts +3 -2
  52. package/dist/TouchHelper.d.ts.map +1 -1
  53. package/dist/TouchHelper.js +4 -6
  54. package/dist/TouchHelper.js.map +1 -1
  55. package/dist/TypePanels.d.ts +33 -0
  56. package/dist/TypePanels.d.ts.map +1 -0
  57. package/dist/TypePanels.js +68 -0
  58. package/dist/TypePanels.js.map +1 -0
  59. package/dist/launchArgs.d.ts +6 -0
  60. package/dist/launchArgs.d.ts.map +1 -0
  61. package/dist/launchArgs.js +21 -0
  62. package/dist/launchArgs.js.map +1 -0
  63. package/dist/panels.d.ts +52 -0
  64. package/dist/panels.d.ts.map +1 -0
  65. package/dist/panels.js +68 -0
  66. package/dist/panels.js.map +1 -0
  67. package/dist/simctl.d.ts +3 -0
  68. package/dist/simctl.d.ts.map +1 -1
  69. package/dist/simctl.js +1 -1
  70. package/dist/simctl.js.map +1 -1
  71. package/package.json +6 -5
@@ -0,0 +1,58 @@
1
+ import { type InputChannel } from './InputChannel.js';
2
+ import type { GuestHelper } from './GuestHelper.js';
3
+ /**
4
+ * A gesture that starts within this fraction of any screen edge carries `FromEdgeTip`, which is what makes
5
+ * SpringBoard treat a swipe as a system gesture (home, app switcher). Measured on the Duo's cover: a home
6
+ * flick works from 7% of the bottom edge and not from 12%, because the system checks the start point
7
+ * itself — so the flag on a touch it does not consider an edge changes nothing, and a tap that carries it
8
+ * stays a tap. The threshold can therefore be generous. It is the same for all four edges, which also makes
9
+ * it independent of how the panel's UI is rotated inside its buffer.
10
+ */
11
+ export declare const EDGE_FRACTION = 0.08;
12
+ export interface FoldableInputDeps {
13
+ /** The device's guest helper, owned by IOSAgent outside `DeviceState`, or null if there is none. */
14
+ guest: () => GuestHelper | null;
15
+ /** The display UUID of the panel the device is showing, or null when it is not known yet. */
16
+ litScreen: () => string | null;
17
+ minContactMs?: number;
18
+ }
19
+ /**
20
+ * `InputChannel` for a device with more than one integrated panel, through `guest-helper` inside the
21
+ * simulator. Coordinates arrive normalized to the lit panel's framebuffer — the same space TouchHelper
22
+ * gets — and go to that panel's virtual touchscreen as they are: measured on the unfolded panel, whose UI
23
+ * is turned inside a portrait buffer, buffer coordinates land where they point.
24
+ */
25
+ export declare class FoldableInput implements InputChannel {
26
+ private readonly deps;
27
+ private gesture;
28
+ private touchStartedAt;
29
+ private stopped;
30
+ private heldEnd;
31
+ constructor(deps: FoldableInputDeps);
32
+ start(): void;
33
+ stop(): void;
34
+ inputState(): 'ready' | 'starting' | 'unavailable';
35
+ ownsGesture(): boolean;
36
+ touchStart(x: number, y: number): boolean;
37
+ touchMove(x: number, y: number): boolean;
38
+ touchEnd(): boolean;
39
+ /** TouchHelper's minimum contact, for the same reason (a client sends down and up back to back). */
40
+ touchEndAfterMinContact(): Promise<boolean>;
41
+ pinchStart(x1: number, y1: number, x2: number, y2: number): boolean;
42
+ pinchMove(x1: number, y1: number, x2: number, y2: number): boolean;
43
+ pinchEnd(): boolean;
44
+ sendKey(usage: number, modifiers?: number): boolean;
45
+ pressButton(usagePage: number, usage: number): boolean;
46
+ pressButtonDown(usagePage: number, usage: number): boolean;
47
+ pressButtonUp(usagePage: number, usage: number): boolean;
48
+ pressLegacyButton(code: number): boolean;
49
+ /** The panel a new gesture goes to, fixed for the gesture: a fold mid-drag finishes the drag where it began. */
50
+ private open;
51
+ /**
52
+ * A key or button, acked on the write like TouchHelper's: the helper plays it and answers later, and
53
+ * waiting on that would change what an ack means. A refusal it reports afterwards is logged.
54
+ */
55
+ private fire;
56
+ private releaseHeldEnd;
57
+ }
58
+ //# sourceMappingURL=FoldableInput.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FoldableInput.d.ts","sourceRoot":"","sources":["../src/FoldableInput.ts"],"names":[],"mappings":"AACA,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAA;AACrE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAInD;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,OAAO,CAAA;AAiBjC,MAAM,WAAW,iBAAiB;IAChC,oGAAoG;IACpG,KAAK,EAAE,MAAM,WAAW,GAAG,IAAI,CAAA;IAC/B,6FAA6F;IAC7F,SAAS,EAAE,MAAM,MAAM,GAAG,IAAI,CAAA;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED;;;;;GAKG;AACH,qBAAa,aAAc,YAAW,YAAY;IAMpC,OAAO,CAAC,QAAQ,CAAC,IAAI;IALjC,OAAO,CAAC,OAAO,CAAuB;IACtC,OAAO,CAAC,cAAc,CAAI;IAC1B,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,OAAO,CAA0F;gBAE5E,IAAI,EAAE,iBAAiB;IAEpD,KAAK,IAAI,IAAI;IAIb,IAAI,IAAI,IAAI;IAMZ,UAAU,IAAI,OAAO,GAAG,UAAU,GAAG,aAAa;IAQlD,WAAW,IAAI,OAAO;IAItB,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO;IAOzC,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO;IAOxC,QAAQ,IAAI,OAAO;IAOnB,oGAAoG;IACpG,uBAAuB,IAAI,OAAO,CAAC,OAAO,CAAC;IAa3C,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO;IAOnE,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO;IAOlE,QAAQ,IAAI,OAAO;IAQnB,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,SAAI,GAAG,OAAO;IAO9C,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO;IAItD,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO;IAI1D,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO;IAIxD,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAKxC,gHAAgH;IAChH,OAAO,CAAC,IAAI;IASZ;;;OAGG;IACH,OAAO,CAAC,IAAI;IAOZ,OAAO,CAAC,cAAc;CAOvB"}
@@ -0,0 +1,166 @@
1
+ import { createLogger } from '@tapflowio/agent-core';
2
+ import { MIN_CONTACT_MS } from './InputChannel.js';
3
+ const logger = createLogger('ios-agent:foldable-input');
4
+ /**
5
+ * A gesture that starts within this fraction of any screen edge carries `FromEdgeTip`, which is what makes
6
+ * SpringBoard treat a swipe as a system gesture (home, app switcher). Measured on the Duo's cover: a home
7
+ * flick works from 7% of the bottom edge and not from 12%, because the system checks the start point
8
+ * itself — so the flag on a touch it does not consider an edge changes nothing, and a tap that carries it
9
+ * stays a tap. The threshold can therefore be generous. It is the same for all four edges, which also makes
10
+ * it independent of how the panel's UI is rotated inside its buffer.
11
+ */
12
+ export const EDGE_FRACTION = 0.08;
13
+ // The keyboard page's modifier usages, in the order the HID modifier bitmap numbers them.
14
+ const MODIFIER_USAGES = [0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7]; // LCtrl LShift LAlt LMeta RCtrl RShift RAlt RMeta
15
+ const KEYBOARD_PAGE = 0x07;
16
+ const KEY_HOLD_MS = 100; // a keyboard key: the same as a tap's contact, which iOS reads as a press
17
+ const BUTTON_HOLD_MS = 250; // a hardware button: DeviceHub holds its buttons a quarter second
18
+ // Measured on the Duo: consumer-page Menu goes home, AC Home (0x223) does nothing.
19
+ const HOME = [0x0c, 0x40];
20
+ const POWER = [0x0c, 0x30];
21
+ /**
22
+ * `InputChannel` for a device with more than one integrated panel, through `guest-helper` inside the
23
+ * simulator. Coordinates arrive normalized to the lit panel's framebuffer — the same space TouchHelper
24
+ * gets — and go to that panel's virtual touchscreen as they are: measured on the unfolded panel, whose UI
25
+ * is turned inside a portrait buffer, buffer coordinates land where they point.
26
+ */
27
+ export class FoldableInput {
28
+ deps;
29
+ gesture = null;
30
+ touchStartedAt = 0;
31
+ stopped = false;
32
+ heldEnd = null;
33
+ constructor(deps) {
34
+ this.deps = deps;
35
+ }
36
+ start() {
37
+ this.stopped = false;
38
+ }
39
+ stop() {
40
+ this.releaseHeldEnd(false);
41
+ this.stopped = true;
42
+ this.gesture = null;
43
+ }
44
+ inputState() {
45
+ if (this.stopped)
46
+ return 'unavailable';
47
+ const state = this.deps.guest()?.inputState() ?? 'unavailable';
48
+ // Ready to write, but not yet told which panel is lit (the first devicectl reading after a boot or an
49
+ // agent restart): a caller should wait a moment, which is what `starting` tells it.
50
+ return state === 'ready' && this.deps.litScreen() === null ? 'starting' : state;
51
+ }
52
+ ownsGesture() {
53
+ return this.gesture !== null && (this.deps.guest()?.ownsGesture() ?? false);
54
+ }
55
+ touchStart(x, y) {
56
+ if (this.heldEnd)
57
+ this.releaseHeldEnd(this.touchEnd());
58
+ const opened = this.open([x, y], nearEdge(x, y));
59
+ this.touchStartedAt = Date.now();
60
+ return opened !== null && this.deps.guest().touch(opened.screen, 'd', x, y, opened.edge);
61
+ }
62
+ touchMove(x, y) {
63
+ const g = this.gesture;
64
+ if (!g)
65
+ return false;
66
+ g.points = [x, y];
67
+ return this.deps.guest()?.touch(g.screen, 'm', x, y, g.edge) ?? false;
68
+ }
69
+ touchEnd() {
70
+ const g = this.gesture;
71
+ if (!g)
72
+ return false;
73
+ this.gesture = null;
74
+ return this.deps.guest()?.touch(g.screen, 'u', g.points[0], g.points[1], g.edge) ?? false;
75
+ }
76
+ /** TouchHelper's minimum contact, for the same reason (a client sends down and up back to back). */
77
+ touchEndAfterMinContact() {
78
+ const wait = this.touchStartedAt + (this.deps.minContactMs ?? MIN_CONTACT_MS) - Date.now();
79
+ if (wait <= 0 || this.stopped)
80
+ return Promise.resolve(this.touchEnd());
81
+ if (this.heldEnd)
82
+ this.releaseHeldEnd(this.touchEnd());
83
+ return new Promise((resolve) => {
84
+ const timer = setTimeout(() => {
85
+ this.heldEnd = null;
86
+ resolve(this.touchEnd());
87
+ }, wait);
88
+ this.heldEnd = { timer, resolve };
89
+ });
90
+ }
91
+ pinchStart(x1, y1, x2, y2) {
92
+ if (this.heldEnd)
93
+ this.releaseHeldEnd(this.touchEnd());
94
+ // The pinch command carries no edge flag: a two-finger touch is not a system swipe.
95
+ const opened = this.open([x1, y1, x2, y2], false);
96
+ return opened !== null && this.deps.guest().pinch(opened.screen, 'd', x1, y1, x2, y2);
97
+ }
98
+ pinchMove(x1, y1, x2, y2) {
99
+ const g = this.gesture;
100
+ if (!g)
101
+ return false;
102
+ g.points = [x1, y1, x2, y2];
103
+ return this.deps.guest()?.pinch(g.screen, 'm', x1, y1, x2, y2) ?? false;
104
+ }
105
+ pinchEnd() {
106
+ const g = this.gesture;
107
+ if (!g)
108
+ return false;
109
+ this.gesture = null;
110
+ const [x1, y1, x2, y2] = g.points;
111
+ return this.deps.guest()?.pinch(g.screen, 'u', x1, y1, x2, y2) ?? false;
112
+ }
113
+ sendKey(usage, modifiers = 0) {
114
+ const mods = MODIFIER_USAGES
115
+ .filter((_, bit) => modifiers & (1 << bit))
116
+ .map((u) => [KEYBOARD_PAGE, u]);
117
+ return this.fire((g) => g.key(KEYBOARD_PAGE, usage, KEY_HOLD_MS, mods), 'key');
118
+ }
119
+ pressButton(usagePage, usage) {
120
+ return this.fire((g) => g.key(usagePage, usage, BUTTON_HOLD_MS), 'button');
121
+ }
122
+ pressButtonDown(usagePage, usage) {
123
+ return this.fire((g) => g.keyDown(usagePage, usage), 'button down');
124
+ }
125
+ pressButtonUp(usagePage, usage) {
126
+ return this.fire((g) => g.keyUp(usagePage, usage), 'button up');
127
+ }
128
+ pressLegacyButton(code) {
129
+ const [page, usage] = code === 0 ? HOME : POWER;
130
+ return this.fire((g) => g.key(page, usage, BUTTON_HOLD_MS), code === 0 ? 'home' : 'lock');
131
+ }
132
+ /** The panel a new gesture goes to, fixed for the gesture: a fold mid-drag finishes the drag where it began. */
133
+ open(points, edge) {
134
+ this.gesture = null;
135
+ const guest = this.deps.guest();
136
+ const screen = this.deps.litScreen();
137
+ if (this.stopped || !guest || guest.inputState() !== 'ready' || !screen)
138
+ return null;
139
+ this.gesture = { screen, edge, points };
140
+ return this.gesture;
141
+ }
142
+ /**
143
+ * A key or button, acked on the write like TouchHelper's: the helper plays it and answers later, and
144
+ * waiting on that would change what an ack means. A refusal it reports afterwards is logged.
145
+ */
146
+ fire(send, what) {
147
+ const guest = this.deps.guest();
148
+ if (this.stopped || !guest || guest.inputState() !== 'ready')
149
+ return false;
150
+ void send(guest).then((ok) => { if (!ok)
151
+ logger.warn(`guest-helper did not take the ${what}`); });
152
+ return true;
153
+ }
154
+ releaseHeldEnd(sent) {
155
+ const held = this.heldEnd;
156
+ if (!held)
157
+ return;
158
+ this.heldEnd = null;
159
+ clearTimeout(held.timer);
160
+ held.resolve(sent);
161
+ }
162
+ }
163
+ function nearEdge(x, y) {
164
+ return Math.min(x, 1 - x, y, 1 - y) < EDGE_FRACTION;
165
+ }
166
+ //# sourceMappingURL=FoldableInput.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FoldableInput.js","sourceRoot":"","sources":["../src/FoldableInput.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,109 @@
1
+ import { type ChildProcess } from 'child_process';
2
+ export declare const GUEST_HELPER_BINARY: string;
3
+ export type TouchPhase = 'd' | 'm' | 'u';
4
+ /** DeviceHub's own orientation labels. On the unfolded panel `landscape-left` shows the UI upright and
5
+ * `pud` shows it landscape with its bottom at the buffer's left edge (measured) — they are not the
6
+ * public names' meaning, which is why nothing outside the agent should pick one. */
7
+ export type GuestOrientation = 'portrait' | 'pud' | 'landscape-left' | 'landscape-right';
8
+ /** The process-level operations, injectable for tests — the same seam `XCUITreeReader` uses. */
9
+ export interface GuestHelperNative {
10
+ spawn(udid: string, args: string[]): ChildProcess;
11
+ /** Guest helpers for this udid still running from an earlier agent or an earlier spawn. */
12
+ findOrphans(udid: string): Promise<number[]>;
13
+ killPid(pid: number, signal: NodeJS.Signals): void;
14
+ /**
15
+ * Why a multi-panel device cannot be used on this Mac before anything is booted, or null if it can: the
16
+ * helper binary is missing, or there is no devicectl to say which panel is lit. Asked at connect, so such a
17
+ * device is left out of the list rather than offered and refused after a 3 GB boot.
18
+ */
19
+ unavailableReason(): Promise<string | null>;
20
+ }
21
+ export declare const defaultGuestHelperNative: GuestHelperNative;
22
+ export interface GuestHelperOptions {
23
+ /** Called when the helper stops serving this device: for good (the runtime refused it — `fatal` — or it
24
+ * could not be exec'd), or until the respawn window clears. The owner hides posture. */
25
+ onUnavailable?: (reason: string) => void;
26
+ /** Called when a helper is ready again after `onUnavailable` — the owner shows posture again. */
27
+ onAvailable?: () => void;
28
+ }
29
+ /**
30
+ * The host side of `guest-helper`: one per multi-panel device, resident for the device's life.
31
+ *
32
+ * Its lifecycle follows TouchHelper's rules — readiness gates writes, a gesture belongs to the process
33
+ * that took its opening frame, deaths are replaced eagerly within a rolling budget — with three
34
+ * differences the guest process forces:
35
+ * - the process the agent spawns is `simctl spawn`, not the helper; the helper's own pid arrives on
36
+ * stdout and is killed separately, because the two do not die together;
37
+ * - earlier helpers for the same device are found with `pgrep` and killed before every spawn;
38
+ * - `fatal` is final: it means the runtime refused the private API, and spawning again would only
39
+ * hear the same answer.
40
+ *
41
+ * **Running out of the respawn budget is not final**, which is TouchHelper's rule too ("it self-clears,
42
+ * so a session that briefly could not start a helper is not left without input until the device is
43
+ * rebooted"). A first version made it final, and a Mac short of memory turned one slow minute into a
44
+ * device with no input for the rest of its boot. The helper waits for the window to clear and launches
45
+ * again, answering `starting` meanwhile.
46
+ */
47
+ export declare class GuestHelper {
48
+ private readonly udid;
49
+ private readonly touchscreens;
50
+ private readonly native;
51
+ private readonly options;
52
+ private proc;
53
+ private guestPid;
54
+ private gestureProc;
55
+ private ready;
56
+ private stopped;
57
+ private unavailable;
58
+ private fatalReason;
59
+ private spawns;
60
+ private pending;
61
+ private readyTimer;
62
+ private readyWaiters;
63
+ private generation;
64
+ private cooldown;
65
+ private outage;
66
+ private launching;
67
+ /**
68
+ * `touchscreens` is asked on every launch, not once: display UUIDs change on every boot, and a respawn
69
+ * after the device rebooted under the agent must register the new ones.
70
+ */
71
+ constructor(udid: string, touchscreens: () => Promise<string[]>, native?: GuestHelperNative, options?: GuestHelperOptions);
72
+ start(): Promise<void>;
73
+ stop(): void;
74
+ inputState(): 'ready' | 'starting' | 'unavailable';
75
+ /** Resolves true once the helper is ready, false if it becomes unavailable or is stopped first. */
76
+ whenReady(): Promise<boolean>;
77
+ /** Why the helper cannot serve — for good, or for now — or null. */
78
+ unavailableReason(): string | null;
79
+ ownsGesture(): boolean;
80
+ /** One finger on the panel whose display UUID is `screen`. Acked on the write, like Indigo: the helper
81
+ * answers a touch only when it fails, and a drag is too many lines a second to wait on. */
82
+ touch(screen: string, phase: TouchPhase, x: number, y: number, edge: boolean): boolean;
83
+ pinch(screen: string, phase: TouchPhase, x1: number, y1: number, x2: number, y2: number): boolean;
84
+ hinge(fromDeg: number, toDeg: number, durationMs: number): Promise<boolean>;
85
+ orient(value: GuestOrientation): Promise<boolean>;
86
+ key(usagePage: number, usage: number, holdMs: number, modifiers?: Array<[number, number]>): Promise<boolean>;
87
+ /** Half of a held press — a long-press lasts as long as the tester holds it, like TouchHelper's
88
+ * `pressButtonDown`/`Up`. */
89
+ keyDown(usagePage: number, usage: number): Promise<boolean>;
90
+ keyUp(usagePage: number, usage: number): Promise<boolean>;
91
+ private gestureFrame;
92
+ private command;
93
+ private write;
94
+ private launch;
95
+ private onLine;
96
+ private handleDeath;
97
+ /** Out of service until the respawn window has room again, then launch. */
98
+ private retryLater;
99
+ private noteOutage;
100
+ /** For good: the runtime refused the helper, or it could not be exec'd at all. */
101
+ private becomeUnavailable;
102
+ private armReadyDeadline;
103
+ private clearReadyDeadline;
104
+ private failPending;
105
+ private settleReadyWaiters;
106
+ /** `simctl spawn` was started detached, so it leads its own process group. */
107
+ private signalProcess;
108
+ }
109
+ //# sourceMappingURL=GuestHelper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GuestHelper.d.ts","sourceRoot":"","sources":["../src/GuestHelper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,YAAY,EAAE,MAAM,eAAe,CAAA;AAWlE,eAAO,MAAM,mBAAmB,QAAyD,CAAA;AAyBzF,MAAM,MAAM,UAAU,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAA;AACxC;;qFAEqF;AACrF,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG,KAAK,GAAG,gBAAgB,GAAG,iBAAiB,CAAA;AAExF,gGAAgG;AAChG,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,YAAY,CAAA;IACjD,2FAA2F;IAC3F,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;IAC5C,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAA;IAClD;;;;OAIG;IACH,iBAAiB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;CAC5C;AAED,eAAO,MAAM,wBAAwB,EAAE,iBA0CtC,CAAA;AAOD,MAAM,WAAW,kBAAkB;IACjC;6FACyF;IACzF,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IACxC,iGAAiG;IACjG,WAAW,CAAC,EAAE,MAAM,IAAI,CAAA;CACzB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,WAAW;IA0BpB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO;IA5B1B,OAAO,CAAC,IAAI,CAA4B;IACxC,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,WAAW,CAA4B;IAC/C,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,WAAW,CAAsB;IACzC,OAAO,CAAC,WAAW,CAAsB;IACzC,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,UAAU,CAA6C;IAC/D,OAAO,CAAC,YAAY,CAAmC;IACvD,OAAO,CAAC,UAAU,CAAI;IAEtB,OAAO,CAAC,QAAQ,CAA6C;IAE7D,OAAO,CAAC,MAAM,CAAsB;IAGpC,OAAO,CAAC,SAAS,CAAQ;IAEzB;;;OAGG;gBAEgB,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC,EACrC,MAAM,GAAE,iBAA4C,EACpD,OAAO,GAAE,kBAAuB;IAG7C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAK5B,IAAI,IAAI,IAAI;IA0BZ,UAAU,IAAI,OAAO,GAAG,UAAU,GAAG,aAAa;IAMlD,mGAAmG;IACnG,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;IAM7B,oEAAoE;IACpE,iBAAiB,IAAI,MAAM,GAAG,IAAI;IAIlC,WAAW,IAAI,OAAO;IAItB;gGAC4F;IAC5F,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO;IAItF,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO;IAIjG,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAI3E,MAAM,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;IAIjD,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,GAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKhH;kCAC8B;IAC9B,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAI3D,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIzD,OAAO,CAAC,YAAY;IAYpB,OAAO,CAAC,OAAO;IAoBf,OAAO,CAAC,KAAK;YAMC,MAAM;IAwDpB,OAAO,CAAC,MAAM;IA0Bd,OAAO,CAAC,WAAW;IAiBnB,2EAA2E;IAC3E,OAAO,CAAC,UAAU;IAgBlB,OAAO,CAAC,UAAU;IAMlB,kFAAkF;IAClF,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,gBAAgB;IAYxB,OAAO,CAAC,kBAAkB;IAI1B,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,kBAAkB;IAI1B,8EAA8E;IAC9E,OAAO,CAAC,aAAa;CAItB"}