@tapflowio/ios-agent 0.18.0 → 0.19.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/README.md +1 -1
- package/dist/DeviceChromeLoader.d.ts +2 -45
- package/dist/DeviceChromeLoader.d.ts.map +1 -1
- package/dist/DeviceChromeLoader.js.map +1 -1
- package/dist/IOSAgent.d.ts +65 -0
- package/dist/IOSAgent.d.ts.map +1 -1
- package/dist/IOSAgent.js +471 -109
- package/dist/IOSAgent.js.map +1 -1
- package/dist/KeyboardHelperDaemon.d.ts.map +1 -1
- package/dist/KeyboardHelperDaemon.js +2 -1
- package/dist/KeyboardHelperDaemon.js.map +1 -1
- package/dist/MjpegStreamer.d.ts.map +1 -1
- package/dist/MjpegStreamer.js +9 -1
- package/dist/MjpegStreamer.js.map +1 -1
- package/dist/SimctlWrapper.d.ts +46 -1
- package/dist/SimctlWrapper.d.ts.map +1 -1
- package/dist/SimctlWrapper.js +86 -3
- package/dist/SimctlWrapper.js.map +1 -1
- package/dist/TouchHelper.d.ts +53 -14
- package/dist/TouchHelper.d.ts.map +1 -1
- package/dist/TouchHelper.js +237 -59
- package/dist/TouchHelper.js.map +1 -1
- package/dist/childProcessKill.d.ts +3 -0
- package/dist/childProcessKill.d.ts.map +1 -0
- package/dist/childProcessKill.js +19 -0
- package/dist/childProcessKill.js.map +1 -0
- package/package.json +9 -8
package/dist/TouchHelper.d.ts
CHANGED
|
@@ -1,24 +1,63 @@
|
|
|
1
1
|
export declare class TouchHelper {
|
|
2
2
|
private readonly udid;
|
|
3
3
|
private proc;
|
|
4
|
+
private gestureProc;
|
|
4
5
|
private lastX;
|
|
5
6
|
private lastY;
|
|
7
|
+
private stopped;
|
|
8
|
+
private ready;
|
|
9
|
+
private readyTimer;
|
|
10
|
+
private spawns;
|
|
6
11
|
constructor(udid: string);
|
|
7
12
|
start(): void;
|
|
8
13
|
stop(): void;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
14
|
+
isReady(): boolean;
|
|
15
|
+
private isRunning;
|
|
16
|
+
/**
|
|
17
|
+
* Why a write would be refused right now, for the ack to report.
|
|
18
|
+
*
|
|
19
|
+
* `starting` is the state that had no name: measured 186–247ms after spawn, the pipe is open and
|
|
20
|
+
* frames written to it land nowhere, and a caller that retries a moment later succeeds. Answering
|
|
21
|
+
* "no channel" for it — which is what a bare boolean forced — told the caller to reconnect when it
|
|
22
|
+
* should simply have waited.
|
|
23
|
+
*
|
|
24
|
+
* Safe to read at the ack site rather than inside the write: writes here are synchronous, so
|
|
25
|
+
* nothing can have changed between the two.
|
|
26
|
+
*/
|
|
27
|
+
inputState(): 'ready' | 'starting' | 'unavailable';
|
|
28
|
+
/**
|
|
29
|
+
* Whether a gesture is currently owned by the live process — i.e. whether a continuation frame
|
|
30
|
+
* could land at all.
|
|
31
|
+
*
|
|
32
|
+
* Separate from `inputState()` because the two answer questions decided at different times.
|
|
33
|
+
* Readiness is about now; ownership was decided when the opening frame was written. A gesture whose
|
|
34
|
+
* open was refused (during the start-up window, say) leaves nothing owned, and no amount of waiting
|
|
35
|
+
* makes its terminal frame deliverable — the caller has to open a new gesture. Reading readiness
|
|
36
|
+
* alone at the ack site turned that into "the message is malformed, never retry".
|
|
37
|
+
*/
|
|
38
|
+
ownsGesture(): boolean;
|
|
39
|
+
touchStart(x: number, y: number): boolean;
|
|
40
|
+
touchMove(x: number, y: number): boolean;
|
|
41
|
+
touchEnd(): boolean;
|
|
42
|
+
pressButton(usagePage: number, usage: number): boolean;
|
|
43
|
+
pressButtonDown(usagePage: number, usage: number): boolean;
|
|
44
|
+
pressButtonUp(usagePage: number, usage: number): boolean;
|
|
45
|
+
pressLegacyButton(code: number): boolean;
|
|
46
|
+
pinchStart(x1: number, y1: number, x2: number, y2: number): boolean;
|
|
47
|
+
pinchMove(x1: number, y1: number, x2: number, y2: number): boolean;
|
|
48
|
+
pinchEnd(): boolean;
|
|
49
|
+
sendKey(usage: number, modifiers?: number): boolean;
|
|
50
|
+
private coordFrame;
|
|
51
|
+
private buttonFrame;
|
|
52
|
+
private twoFingerFrame;
|
|
53
|
+
private sendSelfContained;
|
|
54
|
+
private openGesture;
|
|
55
|
+
private continueGesture;
|
|
56
|
+
private send;
|
|
57
|
+
private armReadyDeadline;
|
|
58
|
+
private clearReadyDeadline;
|
|
59
|
+
private respawn;
|
|
60
|
+
private spawnHelper;
|
|
61
|
+
private handleDeath;
|
|
23
62
|
}
|
|
24
63
|
//# sourceMappingURL=TouchHelper.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TouchHelper.d.ts","sourceRoot":"","sources":["../src/TouchHelper.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"TouchHelper.d.ts","sourceRoot":"","sources":["../src/TouchHelper.ts"],"names":[],"mappings":"AAiCA,qBAAa,WAAW;IAYV,OAAO,CAAC,QAAQ,CAAC,IAAI;IAXjC,OAAO,CAAC,IAAI,CAA4B;IAGxC,OAAO,CAAC,WAAW,CAA4B;IAC/C,OAAO,CAAC,KAAK,CAAI;IACjB,OAAO,CAAC,KAAK,CAAI;IACjB,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,UAAU,CAA6C;IAC/D,OAAO,CAAC,MAAM,CAAe;gBAEA,IAAI,EAAE,MAAM;IAEzC,KAAK,IAAI,IAAI;IAKb,IAAI,IAAI,IAAI;IAWZ,OAAO,IAAI,OAAO;IAOlB,OAAO,CAAC,SAAS;IAIjB;;;;;;;;;;OAUG;IACH,UAAU,IAAI,OAAO,GAAG,UAAU,GAAG,aAAa;IAKlD;;;;;;;;;OASG;IACH,WAAW,IAAI,OAAO;IAItB,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO;IAMzC,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO;IAMxC,QAAQ,IAAI,OAAO;IAInB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO;IAOtD,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO;IAQ1D,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO;IAKxD,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAIxC,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO;IAInE,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO;IAIlE,QAAQ,IAAI,OAAO;IAOnB,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,SAAI,GAAG,OAAO;IAW9C,OAAO,CAAC,UAAU;IAQlB,OAAO,CAAC,WAAW;IAQnB,OAAO,CAAC,cAAc;IAYtB,OAAO,CAAC,iBAAiB;IASzB,OAAO,CAAC,WAAW;IAoBnB,OAAO,CAAC,eAAe;IAKvB,OAAO,CAAC,IAAI;IAQZ,OAAO,CAAC,gBAAgB;IAaxB,OAAO,CAAC,kBAAkB;IAI1B,OAAO,CAAC,OAAO;IAQf,OAAO,CAAC,WAAW;IAsDnB,OAAO,CAAC,WAAW;CAMpB"}
|
package/dist/TouchHelper.js
CHANGED
|
@@ -1,100 +1,144 @@
|
|
|
1
1
|
import { spawn } from 'child_process';
|
|
2
2
|
import { join } from 'path';
|
|
3
3
|
import { createLogger } from '@tapflowio/agent-core';
|
|
4
|
+
import { killWithSigkillFallback } from './childProcessKill.js';
|
|
4
5
|
const logger = createLogger('ios-agent:touch-helper');
|
|
5
6
|
const BINARY = join(import.meta.dirname, '..', 'bin', 'touch-helper');
|
|
7
|
+
// At most this many spawns in any window of this length. A rolling window rather than a count
|
|
8
|
+
// of consecutive failures: the helper's start-up is expensive (a subprocess, two dlopens, a
|
|
9
|
+
// device lookup), so "it failed quickly" cannot be told apart from "it failed slowly" without
|
|
10
|
+
// guessing how long start-up takes — and a helper that reliably dies just after that guess would
|
|
11
|
+
// reset a consecutive counter forever and churn processes for the life of the agent. A window
|
|
12
|
+
// bounds it whatever the lifetime, and it self-clears, so a session that briefly could not start
|
|
13
|
+
// a helper is not stuck without input until the device is rebooted.
|
|
14
|
+
const RESPAWN_LIMIT = 3;
|
|
15
|
+
const RESPAWN_WINDOW_MS = 30_000;
|
|
16
|
+
// The helper announces itself on stderr once it holds its HID client and is about to start
|
|
17
|
+
// reading stdin (`touch-helper.swift:281`). Measured against a real simulator: 186–247ms after
|
|
18
|
+
// spawn (n=5), and a gesture written before it lands *nothing* — the frames are buffered by the
|
|
19
|
+
// pipe and then drained in one go, which collapses a swipe into microseconds and iOS reads it as
|
|
20
|
+
// no gesture at all. So "the pipe is open" is not the same as "the device will act on this", and
|
|
21
|
+
// reporting the first as success is the same lie as #482 in a narrower window.
|
|
22
|
+
const READY_MARKER = 'touch-helper ready';
|
|
23
|
+
// A helper that wedges *before* announcing would otherwise never be replaced: it is running, so
|
|
24
|
+
// nothing asks for a new one, and it is not ready, so every input is refused — honestly, but
|
|
25
|
+
// forever. The announcement comes after a device lookup through CoreSimulator, which is where a
|
|
26
|
+
// wedge would sit. Measured start-up is 186–247ms, so this is roughly twenty times the worst case
|
|
27
|
+
// observed rather than a tight bound.
|
|
28
|
+
const READY_DEADLINE_MS = 5_000;
|
|
6
29
|
export class TouchHelper {
|
|
7
30
|
udid;
|
|
8
31
|
proc = null;
|
|
32
|
+
// The process that received the current gesture's opening frame. Mid-gesture frames are only
|
|
33
|
+
// meaningful to that one, so they are checked against it rather than against liveness alone.
|
|
34
|
+
gestureProc = null;
|
|
9
35
|
lastX = 0;
|
|
10
36
|
lastY = 0;
|
|
37
|
+
stopped = false;
|
|
38
|
+
ready = false;
|
|
39
|
+
readyTimer = null;
|
|
40
|
+
spawns = [];
|
|
11
41
|
constructor(udid) {
|
|
12
42
|
this.udid = udid;
|
|
13
43
|
}
|
|
14
44
|
start() {
|
|
15
|
-
|
|
16
|
-
this.
|
|
17
|
-
this.proc.stderr?.on('data', (d) => {
|
|
18
|
-
const msg = d.toString().trim();
|
|
19
|
-
// print all lines, even debug: lines, until we're confident it's working
|
|
20
|
-
logger.error(msg);
|
|
21
|
-
});
|
|
22
|
-
this.proc.on('exit', (code) => {
|
|
23
|
-
logger.error(`exited with code ${code ?? 'null'}`);
|
|
24
|
-
});
|
|
25
|
-
this.proc.on('error', (e) => {
|
|
26
|
-
logger.error(`spawn error: ${e.message}`);
|
|
27
|
-
});
|
|
45
|
+
this.stopped = false;
|
|
46
|
+
this.spawnHelper();
|
|
28
47
|
}
|
|
29
48
|
stop() {
|
|
30
|
-
this.
|
|
49
|
+
this.stopped = true;
|
|
50
|
+
this.clearReadyDeadline();
|
|
51
|
+
killWithSigkillFallback(this.proc);
|
|
31
52
|
this.proc = null;
|
|
53
|
+
this.gestureProc = null;
|
|
54
|
+
}
|
|
55
|
+
// Whether an input written right now actually reaches the device. Two conditions, and they are
|
|
56
|
+
// deliberately not the same question as `isRunning()`: a helper that is still starting up is
|
|
57
|
+
// very much alive and must not be replaced, but a frame written to it is dropped on the floor.
|
|
58
|
+
isReady() {
|
|
59
|
+
return this.isRunning() && this.ready;
|
|
60
|
+
}
|
|
61
|
+
// Whether the helper process exists with an open pipe — the question a *replacement* decision
|
|
62
|
+
// asks. `this.proc` only ever holds a process that execed (`spawnHelper` is the gate), so from
|
|
63
|
+
// here on stdin closing is what death looks like.
|
|
64
|
+
isRunning() {
|
|
65
|
+
return this.proc?.stdin?.writable === true;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Why a write would be refused right now, for the ack to report.
|
|
69
|
+
*
|
|
70
|
+
* `starting` is the state that had no name: measured 186–247ms after spawn, the pipe is open and
|
|
71
|
+
* frames written to it land nowhere, and a caller that retries a moment later succeeds. Answering
|
|
72
|
+
* "no channel" for it — which is what a bare boolean forced — told the caller to reconnect when it
|
|
73
|
+
* should simply have waited.
|
|
74
|
+
*
|
|
75
|
+
* Safe to read at the ack site rather than inside the write: writes here are synchronous, so
|
|
76
|
+
* nothing can have changed between the two.
|
|
77
|
+
*/
|
|
78
|
+
inputState() {
|
|
79
|
+
if (this.isReady())
|
|
80
|
+
return 'ready';
|
|
81
|
+
return this.isRunning() ? 'starting' : 'unavailable';
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Whether a gesture is currently owned by the live process — i.e. whether a continuation frame
|
|
85
|
+
* could land at all.
|
|
86
|
+
*
|
|
87
|
+
* Separate from `inputState()` because the two answer questions decided at different times.
|
|
88
|
+
* Readiness is about now; ownership was decided when the opening frame was written. A gesture whose
|
|
89
|
+
* open was refused (during the start-up window, say) leaves nothing owned, and no amount of waiting
|
|
90
|
+
* makes its terminal frame deliverable — the caller has to open a new gesture. Reading readiness
|
|
91
|
+
* alone at the ack site turned that into "the message is malformed, never retry".
|
|
92
|
+
*/
|
|
93
|
+
ownsGesture() {
|
|
94
|
+
return this.proc !== null && this.proc === this.gestureProc;
|
|
32
95
|
}
|
|
33
96
|
touchStart(x, y) {
|
|
34
97
|
this.lastX = x;
|
|
35
98
|
this.lastY = y;
|
|
36
|
-
this.
|
|
99
|
+
return this.openGesture(this.coordFrame(1, x, y));
|
|
37
100
|
}
|
|
38
101
|
touchMove(x, y) {
|
|
39
102
|
this.lastX = x;
|
|
40
103
|
this.lastY = y;
|
|
41
|
-
this.
|
|
104
|
+
return this.continueGesture(this.coordFrame(2, x, y));
|
|
42
105
|
}
|
|
43
106
|
touchEnd() {
|
|
44
|
-
this.
|
|
107
|
+
return this.continueGesture(this.coordFrame(3, this.lastX, this.lastY));
|
|
45
108
|
}
|
|
46
109
|
pressButton(usagePage, usage) {
|
|
47
|
-
|
|
48
|
-
return;
|
|
49
|
-
const buf = Buffer.allocUnsafe(9);
|
|
50
|
-
buf.writeUInt8(4, 0);
|
|
51
|
-
buf.writeUInt32BE(usagePage, 1);
|
|
52
|
-
buf.writeUInt32BE(usage, 5);
|
|
53
|
-
this.proc.stdin.write(buf);
|
|
110
|
+
return this.sendSelfContained(this.buttonFrame(4, usagePage, usage));
|
|
54
111
|
}
|
|
55
112
|
// Real-time button hold: down + up sent as separate frames so the hold lasts as long as
|
|
56
113
|
// the user holds the button in the dashboard (e.g. Action button long-press). type 4 above
|
|
57
114
|
// remains the fixed short-press path.
|
|
58
|
-
writeButtonOp(type, usagePage, usage) {
|
|
59
|
-
if (!this.proc?.stdin?.writable)
|
|
60
|
-
return;
|
|
61
|
-
const buf = Buffer.allocUnsafe(9);
|
|
62
|
-
buf.writeUInt8(type, 0);
|
|
63
|
-
buf.writeUInt32BE(usagePage, 1);
|
|
64
|
-
buf.writeUInt32BE(usage, 5);
|
|
65
|
-
this.proc.stdin.write(buf);
|
|
66
|
-
}
|
|
67
115
|
pressButtonDown(usagePage, usage) {
|
|
68
|
-
this.
|
|
116
|
+
return this.sendSelfContained(this.buttonFrame(10, usagePage, usage));
|
|
69
117
|
}
|
|
118
|
+
// Self-contained despite being the tail of a pair: the helper keeps no record of which
|
|
119
|
+
// buttons are down (`touch-helper.swift` sends one op per frame), so a fresh process serves
|
|
120
|
+
// this exactly as the old one would have. Refusing it would be worse — a `down` that reached
|
|
121
|
+
// a live helper could then never be released.
|
|
70
122
|
pressButtonUp(usagePage, usage) {
|
|
71
|
-
this.
|
|
123
|
+
return this.sendSelfContained(this.buttonFrame(11, usagePage, usage));
|
|
72
124
|
}
|
|
73
125
|
// Legacy path for home (code=0) and lock (code=1) buttons
|
|
74
126
|
pressLegacyButton(code) {
|
|
75
|
-
|
|
76
|
-
return;
|
|
77
|
-
const buf = Buffer.allocUnsafe(9);
|
|
78
|
-
buf.writeUInt8(5, 0);
|
|
79
|
-
buf.writeUInt32BE(code, 1);
|
|
80
|
-
buf.writeUInt32BE(0, 5);
|
|
81
|
-
this.proc.stdin.write(buf);
|
|
127
|
+
return this.sendSelfContained(this.buttonFrame(5, code, 0));
|
|
82
128
|
}
|
|
83
129
|
pinchStart(x1, y1, x2, y2) {
|
|
84
|
-
this.
|
|
130
|
+
return this.openGesture(this.twoFingerFrame(6, x1, y1, x2, y2));
|
|
85
131
|
}
|
|
86
132
|
pinchMove(x1, y1, x2, y2) {
|
|
87
|
-
this.
|
|
133
|
+
return this.continueGesture(this.twoFingerFrame(7, x1, y1, x2, y2));
|
|
88
134
|
}
|
|
89
135
|
pinchEnd() {
|
|
90
|
-
this.
|
|
136
|
+
return this.continueGesture(this.twoFingerFrame(8, 0, 0, 0, 0));
|
|
91
137
|
}
|
|
92
138
|
// HID keyboard — type 9 frame: [9][modifiers][pad x3][usage:u32BE]
|
|
93
139
|
// modifiers: USB HID modifier bitmap (0x01=LeftCtrl, 0x02=LeftShift, 0x04=LeftAlt, 0x08=LeftMeta, …)
|
|
94
140
|
// usage: keyboard usage code from HID Keyboard/Keypad page (0x07)
|
|
95
141
|
sendKey(usage, modifiers = 0) {
|
|
96
|
-
if (!this.proc?.stdin?.writable)
|
|
97
|
-
return;
|
|
98
142
|
const buf = Buffer.allocUnsafe(9);
|
|
99
143
|
buf.writeUInt8(9, 0);
|
|
100
144
|
buf.writeUInt8(modifiers, 1);
|
|
@@ -102,27 +146,161 @@ export class TouchHelper {
|
|
|
102
146
|
buf.writeUInt8(0, 3);
|
|
103
147
|
buf.writeUInt8(0, 4);
|
|
104
148
|
buf.writeUInt32BE(usage, 5);
|
|
105
|
-
this.
|
|
149
|
+
return this.sendSelfContained(buf);
|
|
106
150
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
151
|
+
coordFrame(type, x, y) {
|
|
152
|
+
const buf = Buffer.allocUnsafe(9);
|
|
153
|
+
buf.writeUInt8(type, 0);
|
|
154
|
+
buf.writeFloatBE(x, 1);
|
|
155
|
+
buf.writeFloatBE(y, 5);
|
|
156
|
+
return buf;
|
|
157
|
+
}
|
|
158
|
+
buttonFrame(type, a, b) {
|
|
159
|
+
const buf = Buffer.allocUnsafe(9);
|
|
160
|
+
buf.writeUInt8(type, 0);
|
|
161
|
+
buf.writeUInt32BE(a, 1);
|
|
162
|
+
buf.writeUInt32BE(b, 5);
|
|
163
|
+
return buf;
|
|
164
|
+
}
|
|
165
|
+
twoFingerFrame(type, x1, y1, x2, y2) {
|
|
110
166
|
const buf = Buffer.allocUnsafe(17);
|
|
111
167
|
buf.writeUInt8(type, 0);
|
|
112
168
|
buf.writeFloatBE(x1, 1);
|
|
113
169
|
buf.writeFloatBE(y1, 5);
|
|
114
170
|
buf.writeFloatBE(x2, 9);
|
|
115
171
|
buf.writeFloatBE(y2, 13);
|
|
116
|
-
|
|
172
|
+
return buf;
|
|
173
|
+
}
|
|
174
|
+
// Frames that carry their whole payload: a dead helper is replaced before the frame goes out,
|
|
175
|
+
// so input recovers rather than staying dead for the rest of the session (#482).
|
|
176
|
+
sendSelfContained(buf) {
|
|
177
|
+
// `isRunning`, not `isReady`: replacing a helper that is merely still starting up would spawn
|
|
178
|
+
// a second one, burn the window budget, and make the wait longer rather than shorter.
|
|
179
|
+
if (!this.isRunning())
|
|
180
|
+
this.respawn();
|
|
181
|
+
return this.send(buf);
|
|
182
|
+
}
|
|
183
|
+
// A gesture's opening frame. Self-contained, and it records which process is serving the
|
|
184
|
+
// gesture so the frames that follow can be held to it.
|
|
185
|
+
openGesture(buf) {
|
|
186
|
+
const sent = this.sendSelfContained(buf);
|
|
187
|
+
// Only a *delivered* open owns the gesture. Recording unconditionally looks equivalent and is
|
|
188
|
+
// not: during the start-up window the open is refused while `this.proc` is a healthy process
|
|
189
|
+
// that becomes ready ~200ms later, so identity would then pass and the rest of the gesture
|
|
190
|
+
// would reach a process that never received the down — a release at (0,0) reported as
|
|
191
|
+
// delivered, which is the exact defect this guard exists to prevent.
|
|
192
|
+
this.gestureProc = sent ? this.proc : null;
|
|
193
|
+
return sent;
|
|
194
|
+
}
|
|
195
|
+
// Everything after the opening frame. These are meaningless to any other process: types 3 and
|
|
196
|
+
// 8 inject coordinate latches the helper accumulated (`lastX/lastY`, `pinchLast*`) — a pinch
|
|
197
|
+
// end's own coordinates are all zero for exactly that reason — and a type 2 or 7 move with no
|
|
198
|
+
// preceding down is not the gesture the tester made.
|
|
199
|
+
//
|
|
200
|
+
// Checking liveness is not enough. Replacement is eager, so by the time a terminal frame
|
|
201
|
+
// arrives after a mid-gesture death there is usually a healthy process to write to, and a
|
|
202
|
+
// fresh one has those latches at zero: the tap would be released at (0,0) and reported as
|
|
203
|
+
// delivered. Identity is what the guard has to be about.
|
|
204
|
+
continueGesture(buf) {
|
|
205
|
+
if (this.proc === null || this.proc !== this.gestureProc)
|
|
206
|
+
return false;
|
|
207
|
+
return this.send(buf);
|
|
117
208
|
}
|
|
118
|
-
|
|
119
|
-
if (!this.
|
|
209
|
+
send(buf) {
|
|
210
|
+
if (!this.isReady())
|
|
211
|
+
return false;
|
|
212
|
+
// A `false` from write() is backpressure: node buffers the frame and flushes it when the
|
|
213
|
+
// pipe drains, so it does not mean the input was dropped.
|
|
214
|
+
this.proc?.stdin?.write(buf);
|
|
215
|
+
return true;
|
|
216
|
+
}
|
|
217
|
+
armReadyDeadline(proc) {
|
|
218
|
+
this.clearReadyDeadline();
|
|
219
|
+
const timer = setTimeout(() => {
|
|
220
|
+
if (this.proc !== proc || this.ready)
|
|
221
|
+
return;
|
|
222
|
+
logger.error(`no readiness announcement within ${READY_DEADLINE_MS}ms — replacing the helper`);
|
|
223
|
+
killWithSigkillFallback(proc);
|
|
224
|
+
// Through the same path as a real death, so the replacement is subject to the same window.
|
|
225
|
+
this.handleDeath(proc);
|
|
226
|
+
}, READY_DEADLINE_MS);
|
|
227
|
+
timer.unref?.();
|
|
228
|
+
this.readyTimer = timer;
|
|
229
|
+
}
|
|
230
|
+
clearReadyDeadline() {
|
|
231
|
+
if (this.readyTimer) {
|
|
232
|
+
clearTimeout(this.readyTimer);
|
|
233
|
+
this.readyTimer = null;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
respawn() {
|
|
237
|
+
if (this.stopped)
|
|
120
238
|
return;
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
this.
|
|
239
|
+
const now = Date.now();
|
|
240
|
+
this.spawns = this.spawns.filter((t) => now - t < RESPAWN_WINDOW_MS);
|
|
241
|
+
if (this.spawns.length >= RESPAWN_LIMIT)
|
|
242
|
+
return;
|
|
243
|
+
this.spawnHelper();
|
|
244
|
+
}
|
|
245
|
+
spawnHelper() {
|
|
246
|
+
const proc = spawn(BINARY, [this.udid], { stdio: ['pipe', 'ignore', 'pipe'] });
|
|
247
|
+
this.spawns.push(Date.now());
|
|
248
|
+
this.ready = false;
|
|
249
|
+
// The one place that decides whether a spawn counts as a helper, and it has to decide
|
|
250
|
+
// synchronously: the frame that triggered this spawn is written immediately after.
|
|
251
|
+
//
|
|
252
|
+
// A pid is the only signal available that early. When the exec fails — binary missing, no
|
|
253
|
+
// execute bit, wrong architecture (#464) — libuv still hands back an open stdin pipe and
|
|
254
|
+
// reports the failure on a LATER tick. Measured against a real spawn of a nonexistent path,
|
|
255
|
+
// `stdin.writable` is `true` on a process that does not exist, and `stdin.write()` returning
|
|
256
|
+
// `false` is indistinguishable from ordinary backpressure on a healthy helper. A pid is
|
|
257
|
+
// assigned only once the exec succeeded.
|
|
258
|
+
if (proc.pid === undefined) {
|
|
259
|
+
proc.on('error', (e) => logger.error(`spawn error: ${e.message}`));
|
|
260
|
+
this.proc = null;
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
this.proc = proc;
|
|
264
|
+
this.armReadyDeadline(proc);
|
|
265
|
+
// Cap what is retained: the marker is short, and a helper that never announces itself can
|
|
266
|
+
// still emit warnings indefinitely.
|
|
267
|
+
let tail = '';
|
|
268
|
+
proc.stderr?.on('data', (d) => {
|
|
269
|
+
const msg = d.toString();
|
|
270
|
+
// Guarded on identity so a superseded process's late announcement cannot mark a newer one
|
|
271
|
+
// ready.
|
|
272
|
+
if (!this.ready && this.proc === proc) {
|
|
273
|
+
const seen = tail + msg;
|
|
274
|
+
if (seen.includes(READY_MARKER)) {
|
|
275
|
+
this.ready = true;
|
|
276
|
+
tail = '';
|
|
277
|
+
this.clearReadyDeadline();
|
|
278
|
+
// Retain only enough to rejoin a marker split across two chunks.
|
|
279
|
+
}
|
|
280
|
+
else
|
|
281
|
+
tail = seen.slice(-READY_MARKER.length);
|
|
282
|
+
}
|
|
283
|
+
// print all lines, even debug: lines, until we're confident it's working
|
|
284
|
+
logger.error(msg.trim());
|
|
285
|
+
});
|
|
286
|
+
proc.on('exit', (code) => {
|
|
287
|
+
logger.error(`exited with code ${code ?? 'null'}`);
|
|
288
|
+
this.handleDeath(proc);
|
|
289
|
+
});
|
|
290
|
+
proc.on('error', (e) => {
|
|
291
|
+
logger.error(`spawn error: ${e.message}`);
|
|
292
|
+
this.handleDeath(proc);
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
// Replace the helper as soon as it dies rather than on the next input. Waiting would make
|
|
296
|
+
// the first tap after a death pay the whole start-up cost (xcode-select, two dlopens, a
|
|
297
|
+
// device lookup), and that tap is the one the tester is watching.
|
|
298
|
+
handleDeath(proc) {
|
|
299
|
+
if (this.proc !== proc)
|
|
300
|
+
return; // superseded by a newer spawn, or stop() already cleared it
|
|
301
|
+
this.clearReadyDeadline();
|
|
302
|
+
this.proc = null;
|
|
303
|
+
this.respawn();
|
|
126
304
|
}
|
|
127
305
|
}
|
|
128
306
|
//# sourceMappingURL=TouchHelper.js.map
|
package/dist/TouchHelper.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TouchHelper.js","sourceRoot":"","sources":["../src/TouchHelper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAqB,MAAM,eAAe,CAAA;AACxD,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAC3B,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;
|
|
1
|
+
{"version":3,"file":"TouchHelper.js","sourceRoot":"","sources":["../src/TouchHelper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAqB,MAAM,eAAe,CAAA;AACxD,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAC3B,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AACpD,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAA;AAE/D,MAAM,MAAM,GAAG,YAAY,CAAC,wBAAwB,CAAC,CAAA;AAErD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,cAAc,CAAC,CAAA;AAErE,8FAA8F;AAC9F,4FAA4F;AAC5F,8FAA8F;AAC9F,iGAAiG;AACjG,8FAA8F;AAC9F,iGAAiG;AACjG,oEAAoE;AACpE,MAAM,aAAa,GAAG,CAAC,CAAA;AACvB,MAAM,iBAAiB,GAAG,MAAM,CAAA;AAEhC,2FAA2F;AAC3F,+FAA+F;AAC/F,gGAAgG;AAChG,iGAAiG;AACjG,iGAAiG;AACjG,+EAA+E;AAC/E,MAAM,YAAY,GAAG,oBAAoB,CAAA;AACzC,gGAAgG;AAChG,6FAA6F;AAC7F,gGAAgG;AAChG,kGAAkG;AAClG,sCAAsC;AACtC,MAAM,iBAAiB,GAAG,KAAK,CAAA;AAE/B,MAAM,OAAO,WAAW;IAYO;IAXrB,IAAI,GAAwB,IAAI,CAAA;IACxC,6FAA6F;IAC7F,6FAA6F;IACrF,WAAW,GAAwB,IAAI,CAAA;IACvC,KAAK,GAAG,CAAC,CAAA;IACT,KAAK,GAAG,CAAC,CAAA;IACT,OAAO,GAAG,KAAK,CAAA;IACf,KAAK,GAAG,KAAK,CAAA;IACb,UAAU,GAAyC,IAAI,CAAA;IACvD,MAAM,GAAa,EAAE,CAAA;IAE7B,YAA6B,IAAY;QAAZ,SAAI,GAAJ,IAAI,CAAQ;IAAG,CAAC;IAE7C,KAAK;QACH,IAAI,CAAC,OAAO,GAAG,KAAK,CAAA;QACpB,IAAI,CAAC,WAAW,EAAE,CAAA;IACpB,CAAC;IAED,IAAI;QACF,IAAI,CAAC,OAAO,GAAG,IAAI,CAAA;QACnB,IAAI,CAAC,kBAAkB,EAAE,CAAA;QACzB,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAClC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAChB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAA;IACzB,CAAC;IAED,+FAA+F;IAC/F,6FAA6F;IAC7F,+FAA+F;IAC/F,OAAO;QACL,OAAO,IAAI,CAAC,SAAS,EAAE,IAAI,IAAI,CAAC,KAAK,CAAA;IACvC,CAAC;IAED,8FAA8F;IAC9F,+FAA+F;IAC/F,kDAAkD;IAC1C,SAAS;QACf,OAAO,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAA;IAC5C,CAAC;IAED;;;;;;;;;;OAUG;IACH,UAAU;QACR,IAAI,IAAI,CAAC,OAAO,EAAE;YAAE,OAAO,OAAO,CAAA;QAClC,OAAO,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,aAAa,CAAA;IACtD,CAAC;IAED;;;;;;;;;OASG;IACH,WAAW;QACT,OAAO,IAAI,CAAC,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,WAAW,CAAA;IAC7D,CAAC;IAED,UAAU,CAAC,CAAS,EAAE,CAAS;QAC7B,IAAI,CAAC,KAAK,GAAG,CAAC,CAAA;QACd,IAAI,CAAC,KAAK,GAAG,CAAC,CAAA;QACd,OAAO,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;IACnD,CAAC;IAED,SAAS,CAAC,CAAS,EAAE,CAAS;QAC5B,IAAI,CAAC,KAAK,GAAG,CAAC,CAAA;QACd,IAAI,CAAC,KAAK,GAAG,CAAC,CAAA;QACd,OAAO,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;IACvD,CAAC;IAED,QAAQ;QACN,OAAO,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAA;IACzE,CAAC;IAED,WAAW,CAAC,SAAiB,EAAE,KAAa;QAC1C,OAAO,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC,CAAA;IACtE,CAAC;IAED,wFAAwF;IACxF,2FAA2F;IAC3F,sCAAsC;IACtC,eAAe,CAAC,SAAiB,EAAE,KAAa;QAC9C,OAAO,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC,CAAA;IACvE,CAAC;IAED,uFAAuF;IACvF,4FAA4F;IAC5F,6FAA6F;IAC7F,8CAA8C;IAC9C,aAAa,CAAC,SAAiB,EAAE,KAAa;QAC5C,OAAO,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC,CAAA;IACvE,CAAC;IAED,0DAA0D;IAC1D,iBAAiB,CAAC,IAAY;QAC5B,OAAO,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAA;IAC7D,CAAC;IAED,UAAU,CAAC,EAAU,EAAE,EAAU,EAAE,EAAU,EAAE,EAAU;QACvD,OAAO,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAA;IACjE,CAAC;IAED,SAAS,CAAC,EAAU,EAAE,EAAU,EAAE,EAAU,EAAE,EAAU;QACtD,OAAO,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAA;IACrE,CAAC;IAED,QAAQ;QACN,OAAO,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;IACjE,CAAC;IAED,mEAAmE;IACnE,qGAAqG;IACrG,kEAAkE;IAClE,OAAO,CAAC,KAAa,EAAE,SAAS,GAAG,CAAC;QAClC,MAAM,GAAG,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAA;QACjC,GAAG,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;QACpB,GAAG,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC,CAAC,CAAA;QAC5B,GAAG,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;QACpB,GAAG,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;QACpB,GAAG,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;QACpB,GAAG,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC,CAAC,CAAA;QAC3B,OAAO,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAA;IACpC,CAAC;IAEO,UAAU,CAAC,IAAY,EAAE,CAAS,EAAE,CAAS;QACnD,MAAM,GAAG,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAA;QACjC,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC,CAAA;QACvB,GAAG,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;QACtB,GAAG,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;QACtB,OAAO,GAAG,CAAA;IACZ,CAAC;IAEO,WAAW,CAAC,IAAY,EAAE,CAAS,EAAE,CAAS;QACpD,MAAM,GAAG,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAA;QACjC,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC,CAAA;QACvB,GAAG,CAAC,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;QACvB,GAAG,CAAC,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;QACvB,OAAO,GAAG,CAAA;IACZ,CAAC;IAEO,cAAc,CAAC,IAAY,EAAE,EAAU,EAAE,EAAU,EAAE,EAAU,EAAE,EAAU;QACjF,MAAM,GAAG,GAAG,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAA;QAClC,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC,CAAA;QACvB,GAAG,CAAC,YAAY,CAAC,EAAE,EAAE,CAAC,CAAC,CAAA;QACvB,GAAG,CAAC,YAAY,CAAC,EAAE,EAAE,CAAC,CAAC,CAAA;QACvB,GAAG,CAAC,YAAY,CAAC,EAAE,EAAE,CAAC,CAAC,CAAA;QACvB,GAAG,CAAC,YAAY,CAAC,EAAE,EAAE,EAAE,CAAC,CAAA;QACxB,OAAO,GAAG,CAAA;IACZ,CAAC;IAED,8FAA8F;IAC9F,iFAAiF;IACzE,iBAAiB,CAAC,GAAW;QACnC,8FAA8F;QAC9F,sFAAsF;QACtF,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE;YAAE,IAAI,CAAC,OAAO,EAAE,CAAA;QACrC,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IACvB,CAAC;IAED,yFAAyF;IACzF,uDAAuD;IAC/C,WAAW,CAAC,GAAW;QAC7B,MAAM,IAAI,GAAG,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAA;QACxC,8FAA8F;QAC9F,6FAA6F;QAC7F,2FAA2F;QAC3F,sFAAsF;QACtF,qEAAqE;QACrE,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAA;QAC1C,OAAO,IAAI,CAAA;IACb,CAAC;IAED,8FAA8F;IAC9F,6FAA6F;IAC7F,8FAA8F;IAC9F,qDAAqD;IACrD,EAAE;IACF,yFAAyF;IACzF,0FAA0F;IAC1F,0FAA0F;IAC1F,yDAAyD;IACjD,eAAe,CAAC,GAAW;QACjC,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,WAAW;YAAE,OAAO,KAAK,CAAA;QACtE,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IACvB,CAAC;IAEO,IAAI,CAAC,GAAW;QACtB,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE;YAAE,OAAO,KAAK,CAAA;QACjC,yFAAyF;QACzF,0DAA0D;QAC1D,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAA;QAC5B,OAAO,IAAI,CAAA;IACb,CAAC;IAEO,gBAAgB,CAAC,IAAkB;QACzC,IAAI,CAAC,kBAAkB,EAAE,CAAA;QACzB,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,KAAK;gBAAE,OAAM;YAC5C,MAAM,CAAC,KAAK,CAAC,oCAAoC,iBAAiB,2BAA2B,CAAC,CAAA;YAC9F,uBAAuB,CAAC,IAAI,CAAC,CAAA;YAC7B,2FAA2F;YAC3F,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAA;QACxB,CAAC,EAAE,iBAAiB,CAAC,CAAA;QACrB,KAAK,CAAC,KAAK,EAAE,EAAE,CAAA;QACf,IAAI,CAAC,UAAU,GAAG,KAAK,CAAA;IACzB,CAAC;IAEO,kBAAkB;QACxB,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YAAC,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAAC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAA;QAAC,CAAC;IAChF,CAAC;IAEO,OAAO;QACb,IAAI,IAAI,CAAC,OAAO;YAAE,OAAM;QACxB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QACtB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,GAAG,iBAAiB,CAAC,CAAA;QACpE,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,aAAa;YAAE,OAAM;QAC/C,IAAI,CAAC,WAAW,EAAE,CAAA;IACpB,CAAC;IAEO,WAAW;QACjB,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,CAAC,CAAA;QAC9E,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAA;QAC5B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;QAElB,sFAAsF;QACtF,mFAAmF;QACnF,EAAE;QACF,0FAA0F;QAC1F,yFAAyF;QACzF,4FAA4F;QAC5F,6FAA6F;QAC7F,wFAAwF;QACxF,yCAAyC;QACzC,IAAI,IAAI,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;YAC3B,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAA;YAClE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;YAChB,OAAM;QACR,CAAC;QAED,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAChB,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAA;QAC3B,0FAA0F;QAC1F,oCAAoC;QACpC,IAAI,IAAI,GAAG,EAAE,CAAA;QACb,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,CAAS,EAAE,EAAE;YACpC,MAAM,GAAG,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAA;YACxB,0FAA0F;YAC1F,SAAS;YACT,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;gBACtC,MAAM,IAAI,GAAG,IAAI,GAAG,GAAG,CAAA;gBACvB,IAAI,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;oBAChC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAA;oBACjB,IAAI,GAAG,EAAE,CAAA;oBACT,IAAI,CAAC,kBAAkB,EAAE,CAAA;oBAC3B,iEAAiE;gBACjE,CAAC;;oBAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,CAAA;YAChD,CAAC;YACD,yEAAyE;YACzE,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAA;QAC1B,CAAC,CAAC,CAAA;QACF,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;YACvB,MAAM,CAAC,KAAK,CAAC,oBAAoB,IAAI,IAAI,MAAM,EAAE,CAAC,CAAA;YAClD,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAA;QACxB,CAAC,CAAC,CAAA;QACF,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE;YACrB,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,OAAO,EAAE,CAAC,CAAA;YACzC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAA;QACxB,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,0FAA0F;IAC1F,wFAAwF;IACxF,kEAAkE;IAC1D,WAAW,CAAC,IAAkB;QACpC,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI;YAAE,OAAM,CAAC,4DAA4D;QAC3F,IAAI,CAAC,kBAAkB,EAAE,CAAA;QACzB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAChB,IAAI,CAAC,OAAO,EAAE,CAAA;IAChB,CAAC;CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"childProcessKill.d.ts","sourceRoot":"","sources":["../src/childProcessKill.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAMjD,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,EAAE,SAAS,SAAO,GAAG,IAAI,CAWzF"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// SIGKILL fallback if SIGTERM did not take — shared by TouchHelper and KeyboardHelperDaemon,
|
|
2
|
+
// which spawn non-detached helper processes (plain kill(), not a process group).
|
|
3
|
+
// ScreenCaptureStreamer / XCUITreeReader use their own variant (different timeout, and
|
|
4
|
+
// XCUITreeReader kills a process group) and are intentionally not folded into this helper.
|
|
5
|
+
export function killWithSigkillFallback(proc, timeoutMs = 1000) {
|
|
6
|
+
if (!proc)
|
|
7
|
+
return;
|
|
8
|
+
// kill() returns false when the signal can't be delivered (e.g. already exited) — don't
|
|
9
|
+
// arm the fallback in that case, or it fires later against a possibly-reused pid: 'exit'
|
|
10
|
+
// already happened, so a once('exit', ...) attached now would never see it and clear it.
|
|
11
|
+
if (!proc.kill('SIGTERM'))
|
|
12
|
+
return;
|
|
13
|
+
const killTimer = setTimeout(() => proc.kill('SIGKILL'), timeoutMs);
|
|
14
|
+
killTimer.unref?.();
|
|
15
|
+
const clear = () => clearTimeout(killTimer);
|
|
16
|
+
proc.once('exit', clear);
|
|
17
|
+
proc.once('error', clear);
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=childProcessKill.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"childProcessKill.js","sourceRoot":"","sources":["../src/childProcessKill.ts"],"names":[],"mappings":"AAEA,6FAA6F;AAC7F,iFAAiF;AACjF,uFAAuF;AACvF,2FAA2F;AAC3F,MAAM,UAAU,uBAAuB,CAAC,IAAyB,EAAE,SAAS,GAAG,IAAI;IACjF,IAAI,CAAC,IAAI;QAAE,OAAM;IACjB,wFAAwF;IACxF,yFAAyF;IACzF,yFAAyF;IACzF,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC;QAAE,OAAM;IACjC,MAAM,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC,CAAA;IACnE,SAAS,CAAC,KAAK,EAAE,EAAE,CAAA;IACnB,MAAM,KAAK,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,SAAS,CAAC,CAAA;IAC3C,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;IACxB,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;AAC3B,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tapflowio/ios-agent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"description": "iOS simulator agent for tapflow — xcrun simctl control and screen streaming",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"tapflow",
|
|
@@ -46,28 +46,29 @@
|
|
|
46
46
|
"LICENSE"
|
|
47
47
|
],
|
|
48
48
|
"engines": {
|
|
49
|
-
"node": ">=
|
|
49
|
+
"node": ">=22.0.0"
|
|
50
50
|
},
|
|
51
51
|
"publishConfig": {
|
|
52
52
|
"access": "public"
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"ws": "^8.21.
|
|
56
|
-
"@tapflowio/
|
|
57
|
-
"@tapflowio/
|
|
55
|
+
"ws": "^8.21.3",
|
|
56
|
+
"@tapflowio/audiotap-helper": "0.3.0",
|
|
57
|
+
"@tapflowio/protocol": "0.19.0",
|
|
58
|
+
"@tapflowio/agent-core": "0.19.0"
|
|
58
59
|
},
|
|
59
60
|
"devDependencies": {
|
|
60
|
-
"@types/node": "^20.
|
|
61
|
+
"@types/node": "^22.20.1",
|
|
61
62
|
"@types/ws": "^8.0.0",
|
|
62
63
|
"typescript": "^5.0.0",
|
|
63
64
|
"vitest": "^4.1.10",
|
|
64
|
-
"@tapflowio/relay": "0.
|
|
65
|
+
"@tapflowio/relay": "0.19.0",
|
|
65
66
|
"@tapflowio/test-utils": "0.0.0"
|
|
66
67
|
},
|
|
67
68
|
"scripts": {
|
|
68
69
|
"postinstall": "node -e \"if (process.platform === 'darwin') { require('fs').readdirSync('bin').forEach(f => require('fs').chmodSync('bin/' + f, 0o755)) }\"",
|
|
69
70
|
"build": "tsc -b",
|
|
70
|
-
"typecheck": "tsc -b",
|
|
71
|
+
"typecheck": "tsc -b && tsc -p src/__tests__/tsconfig.json",
|
|
71
72
|
"test": "vitest run",
|
|
72
73
|
"lint": "eslint src"
|
|
73
74
|
}
|