@tapflowio/ios-agent 0.29.0 → 0.30.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/AudioCaptureStreamer.d.ts +3 -2
- package/dist/AudioCaptureStreamer.d.ts.map +1 -1
- package/dist/AudioCaptureStreamer.js +16 -10
- package/dist/AudioCaptureStreamer.js.map +1 -1
- package/dist/IOSAgent.d.ts +7 -2
- package/dist/IOSAgent.d.ts.map +1 -1
- package/dist/IOSAgent.js +87 -38
- package/dist/IOSAgent.js.map +1 -1
- package/dist/SimProcessTree.d.ts +3 -1
- package/dist/SimProcessTree.d.ts.map +1 -1
- package/dist/SimProcessTree.js +14 -4
- package/dist/SimProcessTree.js.map +1 -1
- package/dist/SimctlWrapper.d.ts +2 -1
- package/dist/SimctlWrapper.d.ts.map +1 -1
- package/dist/SimctlWrapper.js +10 -2
- package/dist/SimctlWrapper.js.map +1 -1
- package/dist/SimulatorNetwork.d.ts +3 -2
- package/dist/SimulatorNetwork.d.ts.map +1 -1
- package/dist/SimulatorNetwork.js +3 -2
- package/dist/SimulatorNetwork.js.map +1 -1
- package/dist/launchArgs.d.ts +6 -0
- package/dist/launchArgs.d.ts.map +1 -0
- package/dist/launchArgs.js +21 -0
- package/dist/launchArgs.js.map +1 -0
- package/dist/simctl.d.ts +3 -0
- package/dist/simctl.d.ts.map +1 -1
- package/dist/simctl.js +1 -1
- package/dist/simctl.js.map +1 -1
- package/package.json +5 -5
|
@@ -3,9 +3,10 @@ import type { AudioFrame } from '@tapflowio/agent-core';
|
|
|
3
3
|
* Read the simulator's current media volume (`sim_volume`, 0–100) from its audiosettings.plist and
|
|
4
4
|
* return it as a 0–1 gain. The Core Audio process tap captures audio *before* the simulator applies
|
|
5
5
|
* its volume, so the agent multiplies it back in. The plist updates live as the user changes the
|
|
6
|
-
* simulator volume.
|
|
6
|
+
* simulator volume. Resolves 1 (full) when unreadable — a volume-read failure must never mute audio.
|
|
7
|
+
* Asynchronous because it is polled beside `enumerateSimPids`, on the loop that carries the video.
|
|
7
8
|
*/
|
|
8
|
-
export declare function readSimVolume(udid: string): number
|
|
9
|
+
export declare function readSimVolume(udid: string): Promise<number>;
|
|
9
10
|
export declare function parseAudioFrames(buf: Buffer): {
|
|
10
11
|
frames: Buffer[];
|
|
11
12
|
rest: Buffer;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AudioCaptureStreamer.d.ts","sourceRoot":"","sources":["../src/AudioCaptureStreamer.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AASvD
|
|
1
|
+
{"version":3,"file":"AudioCaptureStreamer.d.ts","sourceRoot":"","sources":["../src/AudioCaptureStreamer.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AASvD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAa3D;AAKD,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAShF;AAID,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAKzD;AAED;;;;;;;GAOG;AACH,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,UAAU,CAA2D;IAC7E,OAAO,CAAC,GAAG,CAA0B;IACrC,OAAO,CAAC,IAAI,CAAI;IAChB,OAAO,CAAC,UAAU,CAA0B;IAC5C,OAAO,CAAC,WAAW,CAAwB;IAE3C,yFAAyF;IACzF,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC;IAazB,OAAO,CAAC,YAAY;IAiBpB;;;;;OAKG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI;IAKhC,OAAO,CAAC,SAAS;IAOjB,yEAAyE;IACzE,MAAM,IAAI,cAAc,CAAC,UAAU,CAAC;IAOpC,IAAI,IAAI,IAAI;CAOb"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import net from 'node:net';
|
|
2
2
|
import os from 'node:os';
|
|
3
|
-
import {
|
|
3
|
+
import { execFile } from 'node:child_process';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
5
|
import { createLogger } from '@tapflowio/agent-core';
|
|
6
6
|
const logger = createLogger('ios-agent:audio');
|
|
@@ -11,18 +11,24 @@ const logger = createLogger('ios-agent:audio');
|
|
|
11
11
|
* Read the simulator's current media volume (`sim_volume`, 0–100) from its audiosettings.plist and
|
|
12
12
|
* return it as a 0–1 gain. The Core Audio process tap captures audio *before* the simulator applies
|
|
13
13
|
* its volume, so the agent multiplies it back in. The plist updates live as the user changes the
|
|
14
|
-
* simulator volume.
|
|
14
|
+
* simulator volume. Resolves 1 (full) when unreadable — a volume-read failure must never mute audio.
|
|
15
|
+
* Asynchronous because it is polled beside `enumerateSimPids`, on the loop that carries the video.
|
|
15
16
|
*/
|
|
16
17
|
export function readSimVolume(udid) {
|
|
17
18
|
const plist = join(os.homedir(), 'Library', 'Developer', 'CoreSimulator', 'Devices', udid, 'data', 'var', 'run', 'simulatoraudio', 'audiosettings.plist');
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
19
|
+
return new Promise((resolve) => {
|
|
20
|
+
execFile('/usr/bin/plutil', ['-convert', 'json', '-o', '-', plist], { encoding: 'utf8', timeout: 2_000, killSignal: 'SIGKILL' }, (err, json) => {
|
|
21
|
+
if (err)
|
|
22
|
+
return resolve(1); // plist missing / not yet created → full volume
|
|
23
|
+
try {
|
|
24
|
+
const v = JSON.parse(json).sim_volume;
|
|
25
|
+
resolve(typeof v === 'number' && v >= 0 ? Math.min(1, v / 100) : 1);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
resolve(1);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
});
|
|
26
32
|
}
|
|
27
33
|
// Wire format from the audiotap-helper: length-prefixed PCM frames — [u32 BE len][PCM bytes], where
|
|
28
34
|
// PCM is normalized S16LE / 44100 / Stereo. Mirrors the video helper's length-prefix framing
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AudioCaptureStreamer.js","sourceRoot":"","sources":["../src/AudioCaptureStreamer.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,UAAU,CAAA;AAC1B,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"AudioCaptureStreamer.js","sourceRoot":"","sources":["../src/AudioCaptureStreamer.ts"],"names":[],"mappings":"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"}
|
package/dist/IOSAgent.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Device, DeviceAgent, NetworkControlCapability, NetworkStatePayload, UIElement } from '@tapflowio/agent-core';
|
|
2
|
+
import type { LaunchOptions } from '@tapflowio/protocol';
|
|
2
3
|
export declare const IOS_BUTTON_ALIASES: Record<string, string>;
|
|
3
4
|
import { type SleepBlocker } from '@tapflowio/agent-core/utils';
|
|
4
5
|
import { SimctlWrapper } from './SimctlWrapper.js';
|
|
@@ -108,6 +109,7 @@ export declare class IOSAgent implements DeviceAgent, NetworkControlCapability {
|
|
|
108
109
|
* falling back to nothing would blank a form factor it already had until the next reconnect. */
|
|
109
110
|
private formFactors;
|
|
110
111
|
private resourcesTimer;
|
|
112
|
+
private resourcesInFlight;
|
|
111
113
|
private readonly resources;
|
|
112
114
|
private _stopping;
|
|
113
115
|
private _reconnectTimer;
|
|
@@ -128,6 +130,9 @@ export declare class IOSAgent implements DeviceAgent, NetworkControlCapability {
|
|
|
128
130
|
disconnect(): void;
|
|
129
131
|
/** `loss` is absent when a reconnect attempt failed: there is no close to describe, only a retry. */
|
|
130
132
|
private _scheduleReconnect;
|
|
133
|
+
/** Samples off the event loop, then reports. One sample at a time per socket, so a slow one (bounded by its
|
|
134
|
+
* 1 s timeout) is not stacked by the next tick; keyed by socket, so a reconnect is not skipped behind the
|
|
135
|
+
* old socket's sample. Slots and the clock are read after the sample, when the report leaves. */
|
|
131
136
|
private reportResources;
|
|
132
137
|
private cleanupDeviceState;
|
|
133
138
|
private stopAudioCapture;
|
|
@@ -316,7 +321,7 @@ export declare class IOSAgent implements DeviceAgent, NetworkControlCapability {
|
|
|
316
321
|
*/
|
|
317
322
|
setNetworkOffline(offline: boolean): Promise<NetworkStatePayload>;
|
|
318
323
|
networkState(): Promise<NetworkStatePayload>;
|
|
319
|
-
launchApp(bundleId: string): Promise<void>;
|
|
324
|
+
launchApp(bundleId: string, options?: LaunchOptions): Promise<void>;
|
|
320
325
|
/** The one booted session, or undefined when there is no unambiguous answer. Callers that can
|
|
321
326
|
* reasonably do nothing (input) use this; callers that owe the user a result use
|
|
322
327
|
* `soleDeviceState`, which throws instead. */
|
|
@@ -367,7 +372,7 @@ export declare class IOSAgent implements DeviceAgent, NetworkControlCapability {
|
|
|
367
372
|
private audioEnabled;
|
|
368
373
|
private startAudioCapture;
|
|
369
374
|
private launchWholeSimTap;
|
|
370
|
-
private
|
|
375
|
+
private refreshAudio;
|
|
371
376
|
private pumpAudio;
|
|
372
377
|
screenshot(): Promise<Buffer>;
|
|
373
378
|
stream(): ReadableStream<Buffer>;
|
package/dist/IOSAgent.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IOSAgent.d.ts","sourceRoot":"","sources":["../src/IOSAgent.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAA4C,MAAM,EAAE,WAAW,EAAE,wBAAwB,EAAE,mBAAmB,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;
|
|
1
|
+
{"version":3,"file":"IOSAgent.d.ts","sourceRoot":"","sources":["../src/IOSAgent.ts"],"names":[],"mappings":"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"}
|
package/dist/IOSAgent.js
CHANGED
|
@@ -6,6 +6,7 @@ import { randomUUID } from 'crypto';
|
|
|
6
6
|
import { spawn } from 'child_process';
|
|
7
7
|
import { WebSocket } from 'ws';
|
|
8
8
|
import { createLogger, PlatformError, ValidationError, bootAbandonMessage, BOOT_NO_SESSION_STATE, SHUTDOWN_NO_SESSION_STATE, downloadBuild } from '@tapflowio/agent-core';
|
|
9
|
+
import { appliedKeys, compactLaunchOptions, launchOptionsProblem } from '@tapflowio/protocol/launch';
|
|
9
10
|
const logger = createLogger('ios-agent');
|
|
10
11
|
/** The run reset's pasteboard step, the wait for the clipboard queue included; one step's bound like the others. */
|
|
11
12
|
const RUN_RESET_PASTEBOARD_TIMEOUT_MS = 10_000;
|
|
@@ -16,7 +17,7 @@ const RUN_RESET_PASTEBOARD_TIMEOUT_MS = 10_000;
|
|
|
16
17
|
// actually took is per device and per app, and `network:state.available` carries that — the split the
|
|
17
18
|
// protocol documents. Added last, after the handler and the boot-time arming, because the string on
|
|
18
19
|
// its own is what puts a control on screen.
|
|
19
|
-
const AGENT_CAPABILITIES = ['clipboard', 'full-reset', 'network-control', 'build-download'];
|
|
20
|
+
const AGENT_CAPABILITIES = ['clipboard', 'full-reset', 'network-control', 'build-download', 'launch-options', 'launch-env'];
|
|
20
21
|
// Human prose for each reason. Not in `@tapflowio/protocol`: that package's main entry must stay
|
|
21
22
|
// runtime-free so it erases under `import type` and never reaches the dashboard bundle — a lookup
|
|
22
23
|
// table is a runtime value. The `reason` on the wire is the machine-readable half; this is only what
|
|
@@ -158,6 +159,7 @@ export class IOSAgent {
|
|
|
158
159
|
* falling back to nothing would blank a form factor it already had until the next reconnect. */
|
|
159
160
|
formFactors = new Map();
|
|
160
161
|
resourcesTimer = null;
|
|
162
|
+
resourcesInFlight = null;
|
|
161
163
|
resources = createResourceSampler();
|
|
162
164
|
_stopping = false;
|
|
163
165
|
_reconnectTimer = null;
|
|
@@ -301,8 +303,8 @@ export class IOSAgent {
|
|
|
301
303
|
}
|
|
302
304
|
catch { /* ignore malformed */ }
|
|
303
305
|
});
|
|
304
|
-
this.reportResources();
|
|
305
|
-
this.resourcesTimer = setInterval(() => this.reportResources(), 5000);
|
|
306
|
+
void this.reportResources();
|
|
307
|
+
this.resourcesTimer = setInterval(() => { void this.reportResources(); }, 5000);
|
|
306
308
|
this.lastPingAt = null;
|
|
307
309
|
this.lastSocketError = undefined;
|
|
308
310
|
ws.on('ping', () => { this.lastPingAt = Date.now(); });
|
|
@@ -434,24 +436,41 @@ export class IOSAgent {
|
|
|
434
436
|
});
|
|
435
437
|
}, delay);
|
|
436
438
|
}
|
|
437
|
-
|
|
438
|
-
|
|
439
|
+
/** Samples off the event loop, then reports. One sample at a time per socket, so a slow one (bounded by its
|
|
440
|
+
* 1 s timeout) is not stacked by the next tick; keyed by socket, so a reconnect is not skipped behind the
|
|
441
|
+
* old socket's sample. Slots and the clock are read after the sample, when the report leaves. */
|
|
442
|
+
async reportResources() {
|
|
443
|
+
const ws = this.ws;
|
|
444
|
+
if (!ws || ws.readyState !== WebSocket.OPEN || this.resourcesInFlight === ws)
|
|
439
445
|
return;
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
446
|
+
this.resourcesInFlight = ws;
|
|
447
|
+
try {
|
|
448
|
+
const [{ memUsedMB, memTotalMB }, memoryPressure] = await Promise.all([
|
|
449
|
+
this.resources.getMemoryUsage(),
|
|
450
|
+
this.resources.getMemoryPressure(),
|
|
451
|
+
]);
|
|
452
|
+
// A reconnect while sampling replaced the socket: this report has nowhere to go.
|
|
453
|
+
if (this.ws !== ws || ws.readyState !== WebSocket.OPEN)
|
|
454
|
+
return;
|
|
455
|
+
const bootedCount = Array.from(this.deviceStates.values()).filter((s) => s.streamReader !== null).length;
|
|
456
|
+
const slotsTotal = this.deviceStates.size;
|
|
457
|
+
this.sendOn(ws, {
|
|
458
|
+
type: 'agent:resources',
|
|
459
|
+
resources: {
|
|
460
|
+
cpuPercent: this.resources.getCpuPercent(),
|
|
461
|
+
memUsedMB,
|
|
462
|
+
memTotalMB,
|
|
463
|
+
slotsAvailable: Math.max(0, slotsTotal - bootedCount),
|
|
464
|
+
slotsTotal,
|
|
465
|
+
reportedAt: Date.now(),
|
|
466
|
+
memoryPressure,
|
|
467
|
+
},
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
finally {
|
|
471
|
+
if (this.resourcesInFlight === ws)
|
|
472
|
+
this.resourcesInFlight = null;
|
|
473
|
+
}
|
|
455
474
|
}
|
|
456
475
|
cleanupDeviceState(state) {
|
|
457
476
|
// Drop this device out of the filter rule. The rule lives on the host and is keyed only by udid,
|
|
@@ -1272,7 +1291,7 @@ export class IOSAgent {
|
|
|
1272
1291
|
break;
|
|
1273
1292
|
}
|
|
1274
1293
|
case 'app:launch': {
|
|
1275
|
-
const { bundleId } = msg.payload;
|
|
1294
|
+
const { bundleId, arguments: args, env } = msg.payload;
|
|
1276
1295
|
const sessionId = msg.sessionId;
|
|
1277
1296
|
const { requestId } = msg;
|
|
1278
1297
|
if (typeof requestId !== 'string' || requestId === '') {
|
|
@@ -1287,12 +1306,20 @@ export class IOSAgent {
|
|
|
1287
1306
|
respond({ type: 'app:launch-error', message: 'No booted device' });
|
|
1288
1307
|
break;
|
|
1289
1308
|
}
|
|
1309
|
+
// Checked again here rather than trusted: the relay checks with the same function, but an agent is the last
|
|
1310
|
+
// door before a command line, and a relay of another version may check less.
|
|
1311
|
+
const problem = launchOptionsProblem({ arguments: args, env });
|
|
1312
|
+
if (problem) {
|
|
1313
|
+
respond({ type: 'app:launch-error', message: problem });
|
|
1314
|
+
break;
|
|
1315
|
+
}
|
|
1316
|
+
const options = compactLaunchOptions({ arguments: args, env });
|
|
1290
1317
|
// The target is named BEFORE the launch, and the order is the whole point: dyld reads the
|
|
1291
1318
|
// environment when the process starts, so naming it afterwards arms the next launch and leaves
|
|
1292
1319
|
// this one unhooked while `available` would still say true.
|
|
1293
1320
|
this.network.target(launchState.deviceId, bundleId)
|
|
1294
1321
|
.catch((e) => logger.warn('could not name the network target:', e.message))
|
|
1295
|
-
.then(() => this.simctl.launchApp(launchState.deviceId, bundleId))
|
|
1322
|
+
.then(() => this.simctl.launchApp(launchState.deviceId, bundleId, options))
|
|
1296
1323
|
.then((pid) => {
|
|
1297
1324
|
// **After the launch, with the pid it returned.** The network control measures how long a
|
|
1298
1325
|
// started process has gone without reporting its hooks; opening that window before
|
|
@@ -1303,7 +1330,7 @@ export class IOSAgent {
|
|
|
1303
1330
|
this.lastBundleIds.set(launchState.deviceId, bundleId);
|
|
1304
1331
|
// Audio: the whole-sim tap's poll picks up the launched app process within one interval;
|
|
1305
1332
|
// no per-launch helper needed.
|
|
1306
|
-
respond({ type: 'app:launch-done' });
|
|
1333
|
+
respond(options ? { type: 'app:launch-done', applied: appliedKeys(options) } : { type: 'app:launch-done' });
|
|
1307
1334
|
})
|
|
1308
1335
|
.catch((e) => {
|
|
1309
1336
|
// The toast gets the reason alone; the full simctl output stays in this log.
|
|
@@ -2119,8 +2146,11 @@ export class IOSAgent {
|
|
|
2119
2146
|
async networkState() {
|
|
2120
2147
|
return this.network.state(await this.soleLiveDeviceId());
|
|
2121
2148
|
}
|
|
2122
|
-
async launchApp(bundleId) {
|
|
2123
|
-
|
|
2149
|
+
async launchApp(bundleId, options) {
|
|
2150
|
+
const problem = launchOptionsProblem(options ?? {});
|
|
2151
|
+
if (problem)
|
|
2152
|
+
throw new ValidationError(problem);
|
|
2153
|
+
await this.simctl.launchApp(await this.soleLiveDeviceId(), bundleId, compactLaunchOptions(options));
|
|
2124
2154
|
}
|
|
2125
2155
|
/** The one booted session, or undefined when there is no unambiguous answer. Callers that can
|
|
2126
2156
|
* reasonably do nothing (input) use this; callers that owe the user a result use
|
|
@@ -2228,7 +2258,7 @@ export class IOSAgent {
|
|
|
2228
2258
|
const seq = state.bootSeq;
|
|
2229
2259
|
const streamer = new AudioCaptureStreamer();
|
|
2230
2260
|
streamer.listen()
|
|
2231
|
-
.then((port) => {
|
|
2261
|
+
.then(async (port) => {
|
|
2232
2262
|
// A reboot/shutdown/disconnect may have superseded this boot while listen() was binding —
|
|
2233
2263
|
// discard so we don't leave an orphan helper/poll/server behind the current lifecycle.
|
|
2234
2264
|
if (seq !== state.bootSeq || streamWs.readyState !== WebSocket.OPEN) {
|
|
@@ -2237,21 +2267,37 @@ export class IOSAgent {
|
|
|
2237
2267
|
}
|
|
2238
2268
|
state.audioStreamer = streamer;
|
|
2239
2269
|
state.audioPort = port;
|
|
2240
|
-
|
|
2270
|
+
// Read before launching, so the first frames carry the sim's volume rather than full.
|
|
2271
|
+
const [pids, volume] = await Promise.all([enumerateSimPids(udid), readSimVolume(udid)]);
|
|
2272
|
+
// A stop or reboot while reading retired this capture: `stopAudioCapture` already stopped `streamer`,
|
|
2273
|
+
// and launching the helper now would tap a device nobody is streaming.
|
|
2274
|
+
if (state.audioStreamer !== streamer)
|
|
2275
|
+
return;
|
|
2276
|
+
// Only the control socket's close tears audio down; a stream socket that closed while reading would
|
|
2277
|
+
// leave a helper and a poll running for nothing until the next reboot.
|
|
2278
|
+
if (streamWs.readyState !== WebSocket.OPEN) {
|
|
2279
|
+
this.stopAudioCapture(state);
|
|
2280
|
+
return;
|
|
2281
|
+
}
|
|
2282
|
+
state.audioVolume = volume;
|
|
2241
2283
|
void this.pumpAudio(streamWs, streamer.frames(), state);
|
|
2242
|
-
this.launchWholeSimTap(state,
|
|
2284
|
+
this.launchWholeSimTap(state, pids);
|
|
2285
|
+
// Started after the launch, so the first poll never races the launch's own pid set. One poll at a
|
|
2286
|
+
// time: `ps` is bounded by its timeout, and a slow one is not stacked by the next tick.
|
|
2287
|
+
let polling = false;
|
|
2243
2288
|
state.audioPoll = setInterval(() => {
|
|
2244
|
-
|
|
2245
|
-
|
|
2289
|
+
if (polling)
|
|
2290
|
+
return;
|
|
2291
|
+
polling = true;
|
|
2292
|
+
void this.refreshAudio(state, udid, streamer).finally(() => { polling = false; });
|
|
2246
2293
|
}, AUDIO_POLL_MS);
|
|
2247
2294
|
})
|
|
2248
2295
|
.catch((e) => logger.warn(`audio capture server failed to start: ${e instanceof Error ? e.message : String(e)}`));
|
|
2249
2296
|
}
|
|
2250
2297
|
// Launch the tap helper for the simulator's whole process tree. Swallows errors — audio must never
|
|
2251
2298
|
// break the launch/video path (e.g. helper build fails, or the user hasn't granted the audio
|
|
2252
|
-
// permission). The helper holds the socket open;
|
|
2253
|
-
launchWholeSimTap(state,
|
|
2254
|
-
const pids = enumerateSimPids(udid);
|
|
2299
|
+
// permission). The helper holds the socket open; refreshAudio() pushes later updates to it.
|
|
2300
|
+
launchWholeSimTap(state, pids) {
|
|
2255
2301
|
if (!pids.length) {
|
|
2256
2302
|
logger.warn('no simulator processes to tap (audio idle until first poll)');
|
|
2257
2303
|
return;
|
|
@@ -2268,18 +2314,21 @@ export class IOSAgent {
|
|
|
2268
2314
|
// freshly spawned WebKit WebContent), push the updated set so the helper rebuilds its tap. Dead pids
|
|
2269
2315
|
// need no rebuild — they resolve to no audio object, so the helper just stops mixing them; we still
|
|
2270
2316
|
// refresh the baseline so a later reappearance is detected. Rebuilding only on additions keeps
|
|
2271
|
-
// short-lived daemon churn from causing constant tap teardown (audio glitches).
|
|
2272
|
-
|
|
2273
|
-
|
|
2317
|
+
// short-lived daemon churn from causing constant tap teardown (audio glitches). Also tracks live sim-volume
|
|
2318
|
+
// changes. Both reads are asynchronous, so the capture may have been retired by the time they land; `streamer`
|
|
2319
|
+
// is the capture this poll belongs to.
|
|
2320
|
+
async refreshAudio(state, udid, streamer) {
|
|
2321
|
+
const [pids, volume] = await Promise.all([enumerateSimPids(udid), readSimVolume(udid)]);
|
|
2322
|
+
if (state.audioStreamer !== streamer)
|
|
2274
2323
|
return;
|
|
2275
|
-
|
|
2324
|
+
state.audioVolume = volume;
|
|
2276
2325
|
if (!pids.length)
|
|
2277
2326
|
return;
|
|
2278
2327
|
const prev = state.audioPids;
|
|
2279
2328
|
const hasNew = !prev || pids.some((p) => !prev.has(p));
|
|
2280
2329
|
state.audioPids = new Set(pids);
|
|
2281
2330
|
if (hasNew)
|
|
2282
|
-
|
|
2331
|
+
streamer.updatePids(pids);
|
|
2283
2332
|
}
|
|
2284
2333
|
// Forward captured PCM to the relay on the shared stream socket via the yielding sender (audio must
|
|
2285
2334
|
// never inflate the socket buffer enough to trip video's backpressure). Mirrors android-agent.
|