@qawolf/run-globals-android 1.0.0 → 1.2.1

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.
@@ -0,0 +1,23 @@
1
+ import { type RunCommandOptions } from "./runCommand.js";
2
+ /**
3
+ * Run an `adb` command and return its combined stdout/stderr.
4
+ *
5
+ * Pass the arguments either as a single string for convenience, or as an array.
6
+ * A string is split on whitespace and supports quoting a single value that
7
+ * contains spaces; it does not go through a shell. When building arguments
8
+ * programmatically, prefer the array form so no quoting is needed.
9
+ *
10
+ * @example
11
+ * // adb shell input keyevent 26
12
+ * await adb("shell input keyevent 26");
13
+ *
14
+ * @example
15
+ * // a path with spaces, quoted in a string...
16
+ * await adb("pull '/sdcard/Recordings/My recording 1.m4a' /tmp/out.m4a");
17
+ *
18
+ * @example
19
+ * // ...or as a single array element, with no quoting to reason about
20
+ * await adb(["pull", "/sdcard/Recordings/My recording 1.m4a", destination]);
21
+ */
22
+ export declare function adb(command: string | string[], runCommandOptions?: Omit<RunCommandOptions, "args">): Promise<string>;
23
+ //# sourceMappingURL=adb.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adb.d.ts","sourceRoot":"","sources":["../../src/emulator/adb.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,iBAAiB,EAAc,MAAM,iBAAiB,CAAC;AAuBrE;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,GAAG,CACvB,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,EAC1B,iBAAiB,CAAC,EAAE,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,GAClD,OAAO,CAAC,MAAM,CAAC,CAIjB"}
@@ -0,0 +1,43 @@
1
+ import { runCommand } from "./runCommand.js";
2
+ /**
3
+ * Split an `adb` command string into arguments, honoring single or double
4
+ * quotes around a value that contains spaces (for example a file path).
5
+ *
6
+ * This is a deliberately small tokenizer, not a shell: it does not expand
7
+ * variables, run pipes, or handle escaped or mid-token quotes. For anything
8
+ * beyond quoting a value with spaces, pass the arguments as an array instead.
9
+ */
10
+ function splitCommand(command) {
11
+ const tokens = command.match(/'[^']*'|"[^"]*"|\S+/g) ?? [];
12
+ return tokens.map((token) => {
13
+ const isQuoted = token.length >= 2 &&
14
+ ((token.startsWith("'") && token.endsWith("'")) ||
15
+ (token.startsWith('"') && token.endsWith('"')));
16
+ return isQuoted ? token.slice(1, -1) : token;
17
+ });
18
+ }
19
+ /**
20
+ * Run an `adb` command and return its combined stdout/stderr.
21
+ *
22
+ * Pass the arguments either as a single string for convenience, or as an array.
23
+ * A string is split on whitespace and supports quoting a single value that
24
+ * contains spaces; it does not go through a shell. When building arguments
25
+ * programmatically, prefer the array form so no quoting is needed.
26
+ *
27
+ * @example
28
+ * // adb shell input keyevent 26
29
+ * await adb("shell input keyevent 26");
30
+ *
31
+ * @example
32
+ * // a path with spaces, quoted in a string...
33
+ * await adb("pull '/sdcard/Recordings/My recording 1.m4a' /tmp/out.m4a");
34
+ *
35
+ * @example
36
+ * // ...or as a single array element, with no quoting to reason about
37
+ * await adb(["pull", "/sdcard/Recordings/My recording 1.m4a", destination]);
38
+ */
39
+ export async function adb(command, runCommandOptions) {
40
+ const args = typeof command === "string" ? splitCommand(command) : command;
41
+ return await runCommand("adb", { args, ...runCommandOptions });
42
+ }
43
+ //# sourceMappingURL=adb.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adb.js","sourceRoot":"","sources":["../../src/emulator/adb.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0B,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAErE;;;;;;;GAOG;AACH,SAAS,YAAY,CAAC,OAAe;IACnC,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,sBAAsB,CAAC,IAAI,EAAE,CAAC;IAE3D,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QAC1B,MAAM,QAAQ,GACZ,KAAK,CAAC,MAAM,IAAI,CAAC;YACjB,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;gBAC7C,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAEpD,OAAO,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IAC/C,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,KAAK,UAAU,GAAG,CACvB,OAA0B,EAC1B,iBAAmD;IAEnD,MAAM,IAAI,GAAG,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;IAE3E,OAAO,MAAM,UAAU,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,GAAG,iBAAiB,EAAE,CAAC,CAAC;AACjE,CAAC"}
@@ -0,0 +1,40 @@
1
+ import { z } from "zod";
2
+ import { type RunCommandOptions } from "./runCommand.js";
3
+ declare const captureAudioOutputOptions: z.ZodObject<{
4
+ delaySeconds: z.ZodOptional<z.ZodNumber>;
5
+ durationSeconds: z.ZodNumber;
6
+ outputPath: z.ZodString;
7
+ }, z.core.$strip>;
8
+ export type CaptureAudioOutputOptions = z.input<typeof captureAudioOutputOptions>;
9
+ /**
10
+ * Capture the emulator's audio output to a file on the runner host.
11
+ *
12
+ * The mirror of `passAudioAsMicrophoneInput`: where injection plays audio into
13
+ * the emulator's microphone with `ffplay`, this records what the emulator plays
14
+ * out of its speaker. The emulator's output sink exposes a PulseAudio monitor
15
+ * source (`virtual_speaker.monitor`), and `ffmpeg` reads from it on the runner
16
+ * host over the existing pulse tunnel (`PULSE_SERVER`). This wraps the recording
17
+ * flags so flow authors do not have to know them.
18
+ *
19
+ * Recording from a live source has no natural end, so `durationSeconds` is
20
+ * required.
21
+ *
22
+ * Note: `virtual_mic` is mastered on `virtual_speaker.monitor`, so audio
23
+ * injected via `passAudioAsMicrophoneInput` also passes through the sink. A
24
+ * recording taken while injecting concurrently will therefore include the
25
+ * injected audio; pure output capture (no simultaneous injection) is clean.
26
+ *
27
+ * @param options recording options
28
+ * @param runCommandOptions optional options forwarded to runCommand
29
+ * @returns the path the recording was written to (the given `outputPath`)
30
+ * @throws if the command failed
31
+ *
32
+ * @example
33
+ * await captureAudioOutput({
34
+ * outputPath: `${process.env.TEAM_STORAGE_DIR}/output.wav`,
35
+ * durationSeconds: 5,
36
+ * });
37
+ */
38
+ export declare function captureAudioOutput(options: CaptureAudioOutputOptions, runCommandOptions?: Omit<RunCommandOptions, "args">): Promise<string>;
39
+ export {};
40
+ //# sourceMappingURL=captureAudioOutput.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"captureAudioOutput.d.ts","sourceRoot":"","sources":["../../src/emulator/captureAudioOutput.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,KAAK,iBAAiB,EAAc,MAAM,iBAAiB,CAAC;AAErE,QAAA,MAAM,yBAAyB;;;;iBAO7B,CAAC;AAEH,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAC7C,OAAO,yBAAyB,CACjC,CAAC;AAUF;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,yBAAyB,EAClC,iBAAiB,CAAC,EAAE,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,GAClD,OAAO,CAAC,MAAM,CAAC,CAiCjB"}
@@ -0,0 +1,73 @@
1
+ import ms from "ms";
2
+ import { z } from "zod";
3
+ import { runCommand } from "./runCommand.js";
4
+ const captureAudioOutputOptions = z.object({
5
+ /** Delay in seconds before recording starts. */
6
+ delaySeconds: z.number().nonnegative().optional(),
7
+ /** How long to record, in seconds. */
8
+ durationSeconds: z.number().positive(),
9
+ /** File path on the runner host to write the recording to. */
10
+ outputPath: z.string().min(1),
11
+ });
12
+ /**
13
+ * ffmpeg records for `durationSeconds`, and `runCommand` kills it at its
14
+ * timeout (1 minute by default). When the requested duration would exceed that
15
+ * default, we raise the timeout to cover it plus this margin (startup/teardown
16
+ * slack).
17
+ */
18
+ const timeoutMarginSeconds = 30;
19
+ /**
20
+ * Capture the emulator's audio output to a file on the runner host.
21
+ *
22
+ * The mirror of `passAudioAsMicrophoneInput`: where injection plays audio into
23
+ * the emulator's microphone with `ffplay`, this records what the emulator plays
24
+ * out of its speaker. The emulator's output sink exposes a PulseAudio monitor
25
+ * source (`virtual_speaker.monitor`), and `ffmpeg` reads from it on the runner
26
+ * host over the existing pulse tunnel (`PULSE_SERVER`). This wraps the recording
27
+ * flags so flow authors do not have to know them.
28
+ *
29
+ * Recording from a live source has no natural end, so `durationSeconds` is
30
+ * required.
31
+ *
32
+ * Note: `virtual_mic` is mastered on `virtual_speaker.monitor`, so audio
33
+ * injected via `passAudioAsMicrophoneInput` also passes through the sink. A
34
+ * recording taken while injecting concurrently will therefore include the
35
+ * injected audio; pure output capture (no simultaneous injection) is clean.
36
+ *
37
+ * @param options recording options
38
+ * @param runCommandOptions optional options forwarded to runCommand
39
+ * @returns the path the recording was written to (the given `outputPath`)
40
+ * @throws if the command failed
41
+ *
42
+ * @example
43
+ * await captureAudioOutput({
44
+ * outputPath: `${process.env.TEAM_STORAGE_DIR}/output.wav`,
45
+ * durationSeconds: 5,
46
+ * });
47
+ */
48
+ export async function captureAudioOutput(options, runCommandOptions) {
49
+ const { delaySeconds, durationSeconds, outputPath } = captureAudioOutputOptions.parse(options);
50
+ if (delaySeconds)
51
+ await new Promise((resolve) => setTimeout(resolve, delaySeconds * 1000));
52
+ const args = [
53
+ "-hide_banner",
54
+ "-loglevel",
55
+ "error",
56
+ "-f",
57
+ "pulse",
58
+ "-i",
59
+ "virtual_speaker.monitor",
60
+ "-t",
61
+ String(durationSeconds),
62
+ "-y",
63
+ outputPath,
64
+ ];
65
+ const timeout = Math.max(ms("1m"), (durationSeconds + timeoutMarginSeconds) * 1000);
66
+ await runCommand("ffmpeg", {
67
+ args,
68
+ timeout,
69
+ ...runCommandOptions,
70
+ });
71
+ return outputPath;
72
+ }
73
+ //# sourceMappingURL=captureAudioOutput.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"captureAudioOutput.js","sourceRoot":"","sources":["../../src/emulator/captureAudioOutput.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAA0B,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAErE,MAAM,yBAAyB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,gDAAgD;IAChD,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE;IACjD,sCAAsC;IACtC,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACtC,8DAA8D;IAC9D,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CAC9B,CAAC,CAAC;AAMH;;;;;GAKG;AACH,MAAM,oBAAoB,GAAG,EAAE,CAAC;AAEhC;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,OAAkC,EAClC,iBAAmD;IAEnD,MAAM,EAAE,YAAY,EAAE,eAAe,EAAE,UAAU,EAAE,GACjD,yBAAyB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAE3C,IAAI,YAAY;QACd,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAAC,CAAC,CAAC;IAE3E,MAAM,IAAI,GAAG;QACX,cAAc;QACd,WAAW;QACX,OAAO;QACP,IAAI;QACJ,OAAO;QACP,IAAI;QACJ,yBAAyB;QACzB,IAAI;QACJ,MAAM,CAAC,eAAe,CAAC;QACvB,IAAI;QACJ,UAAU;KACX,CAAC;IAEF,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CACtB,EAAE,CAAC,IAAI,CAAC,EACR,CAAC,eAAe,GAAG,oBAAoB,CAAC,GAAG,IAAI,CAChD,CAAC;IAEF,MAAM,UAAU,CAAC,QAAQ,EAAE;QACzB,IAAI;QACJ,OAAO;QACP,GAAG,iBAAiB;KACrB,CAAC,CAAC;IAEH,OAAO,UAAU,CAAC;AACpB,CAAC"}
@@ -1,3 +1,6 @@
1
+ export { adb } from "./adb.js";
2
+ export { type CaptureAudioOutputOptions, captureAudioOutput, } from "./captureAudioOutput.js";
3
+ export { type PassAudioAsMicrophoneInputOptions, passAudioAsMicrophoneInput, } from "./passAudioAsMicrophoneInput.js";
1
4
  export { type PlayAutomationOptions, playAutomation, } from "./playAutomation.js";
2
5
  export { type GeoLocationOptions, setGeoLocation } from "./setGeoLocation.js";
3
6
  export { type ProxyOptions, clearProxy, setProxy } from "./setProxy.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/emulator/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,qBAAqB,EAC1B,cAAc,GACf,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,KAAK,kBAAkB,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC9E,OAAO,EAAE,KAAK,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACxE,OAAO,EACL,KAAK,wBAAwB,EAC7B,oBAAoB,GACrB,MAAM,2BAA2B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/emulator/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC/B,OAAO,EACL,KAAK,yBAAyB,EAC9B,kBAAkB,GACnB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,KAAK,iCAAiC,EACtC,0BAA0B,GAC3B,MAAM,iCAAiC,CAAC;AACzC,OAAO,EACL,KAAK,qBAAqB,EAC1B,cAAc,GACf,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,KAAK,kBAAkB,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC9E,OAAO,EAAE,KAAK,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACxE,OAAO,EACL,KAAK,wBAAwB,EAC7B,oBAAoB,GACrB,MAAM,2BAA2B,CAAC"}
@@ -1,3 +1,6 @@
1
+ export { adb } from "./adb.js";
2
+ export { captureAudioOutput, } from "./captureAudioOutput.js";
3
+ export { passAudioAsMicrophoneInput, } from "./passAudioAsMicrophoneInput.js";
1
4
  export { playAutomation, } from "./playAutomation.js";
2
5
  export { setGeoLocation } from "./setGeoLocation.js";
3
6
  export { clearProxy, setProxy } from "./setProxy.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/emulator/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,cAAc,GACf,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAA2B,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC9E,OAAO,EAAqB,UAAU,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACxE,OAAO,EAEL,oBAAoB,GACrB,MAAM,2BAA2B,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/emulator/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC/B,OAAO,EAEL,kBAAkB,GACnB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAEL,0BAA0B,GAC3B,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAEL,cAAc,GACf,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAA2B,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC9E,OAAO,EAAqB,UAAU,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACxE,OAAO,EAEL,oBAAoB,GACrB,MAAM,2BAA2B,CAAC"}
@@ -0,0 +1,37 @@
1
+ import { z } from "zod";
2
+ import { type RunCommandOptions } from "./runCommand.js";
3
+ declare const passAudioAsMicrophoneInputOptions: z.ZodObject<{
4
+ data: z.ZodString;
5
+ delaySeconds: z.ZodOptional<z.ZodNumber>;
6
+ durationSeconds: z.ZodOptional<z.ZodNumber>;
7
+ }, z.core.$strip>;
8
+ export type PassAudioAsMicrophoneInputOptions = z.input<typeof passAudioAsMicrophoneInputOptions>;
9
+ /**
10
+ * Pass an audio file to the emulator as microphone input.
11
+ *
12
+ * The emulator routes host audio into its microphone, and `ffplay` performs the
13
+ * playback on the runner host. This wraps the playback flags so flow authors do
14
+ * not have to know them, and enables the emulator's host-microphone toggle
15
+ * first, so a flow never has to.
16
+ *
17
+ * **Playback is synchronous, so flow order matters.** This resolves only once
18
+ * `ffplay` has finished, which means the app has to be recording *before* it is
19
+ * called. Awaiting it first plays the whole file into a microphone nobody is
20
+ * listening to, and the recording that follows captures silence. `delaySeconds`
21
+ * does not work around that either, since it is awaited inline as well. Start
22
+ * the app recording first, then call this; or hold the promise and await it
23
+ * after the app has started.
24
+ *
25
+ * @param options playback options
26
+ * @param runCommandOptions optional options forwarded to runCommand
27
+ * @throws if the command failed
28
+ *
29
+ * @example
30
+ * await passAudioAsMicrophoneInput({ data: `${process.env.TEAM_STORAGE_DIR}/audio.mp3` });
31
+ *
32
+ * @example
33
+ * await passAudioAsMicrophoneInput({ data: `${process.env.TEAM_STORAGE_DIR}/audio.mp3`, durationSeconds: 10 });
34
+ */
35
+ export declare function passAudioAsMicrophoneInput(options: PassAudioAsMicrophoneInputOptions, runCommandOptions?: Omit<RunCommandOptions, "args">): Promise<string>;
36
+ export {};
37
+ //# sourceMappingURL=passAudioAsMicrophoneInput.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"passAudioAsMicrophoneInput.d.ts","sourceRoot":"","sources":["../../src/emulator/passAudioAsMicrophoneInput.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EAAE,KAAK,iBAAiB,EAAc,MAAM,iBAAiB,CAAC;AAErE,QAAA,MAAM,iCAAiC;;;;iBAWrC,CAAC;AAEH,MAAM,MAAM,iCAAiC,GAAG,CAAC,CAAC,KAAK,CACrD,OAAO,iCAAiC,CACzC,CAAC;AAUF;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAQH,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,iCAAiC,EAC1C,iBAAiB,CAAC,EAAE,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,GAClD,OAAO,CAAC,MAAM,CAAC,CAgCjB"}
@@ -0,0 +1,83 @@
1
+ import ms from "ms";
2
+ import { z } from "zod";
3
+ import { adb } from "./adb.js";
4
+ import { runCommand } from "./runCommand.js";
5
+ const passAudioAsMicrophoneInputOptions = z.object({
6
+ /** File path on the runner host, or URL, for the audio to play. */
7
+ data: z.string().min(1),
8
+ /** Delay in seconds before playback starts. */
9
+ delaySeconds: z.number().nonnegative().optional(),
10
+ /**
11
+ * Maximum playback duration in seconds (`ffplay -t`). Omit to play the whole
12
+ * file, but note playback is still bounded by the command timeout (1 minute
13
+ * by default — pass a larger `runCommandOptions.timeout` for longer audio).
14
+ */
15
+ durationSeconds: z.number().positive().optional(),
16
+ });
17
+ /**
18
+ * ffplay blocks until playback finishes, and `runCommand` kills it at its
19
+ * timeout (1 minute by default). For audio longer than that default, the
20
+ * command would be cut off mid-playback, so when a duration is requested we
21
+ * raise the timeout to cover it plus this margin (network/startup slack).
22
+ */
23
+ const timeoutMarginSeconds = 30;
24
+ /**
25
+ * Pass an audio file to the emulator as microphone input.
26
+ *
27
+ * The emulator routes host audio into its microphone, and `ffplay` performs the
28
+ * playback on the runner host. This wraps the playback flags so flow authors do
29
+ * not have to know them, and enables the emulator's host-microphone toggle
30
+ * first, so a flow never has to.
31
+ *
32
+ * **Playback is synchronous, so flow order matters.** This resolves only once
33
+ * `ffplay` has finished, which means the app has to be recording *before* it is
34
+ * called. Awaiting it first plays the whole file into a microphone nobody is
35
+ * listening to, and the recording that follows captures silence. `delaySeconds`
36
+ * does not work around that either, since it is awaited inline as well. Start
37
+ * the app recording first, then call this; or hold the promise and await it
38
+ * after the app has started.
39
+ *
40
+ * @param options playback options
41
+ * @param runCommandOptions optional options forwarded to runCommand
42
+ * @throws if the command failed
43
+ *
44
+ * @example
45
+ * await passAudioAsMicrophoneInput({ data: `${process.env.TEAM_STORAGE_DIR}/audio.mp3` });
46
+ *
47
+ * @example
48
+ * await passAudioAsMicrophoneInput({ data: `${process.env.TEAM_STORAGE_DIR}/audio.mp3`, durationSeconds: 10 });
49
+ */
50
+ // Named `passAudioAsMicrophoneInput`, not `injectAudio`: the name describes what
51
+ // actually happens (the emulator receives the audio as microphone input, it is
52
+ // not played back on a speaker), and it avoids the iOS `injectAudio` clash —
53
+ // `@qawolf/flows/ios` exports its own `injectAudio`, and the generated
54
+ // editor-type bundles flatten both platforms' declarations into one file, where
55
+ // a shared name would be renamed to `injectAudio_2` and leak into iOS author
56
+ // autocomplete.
57
+ export async function passAudioAsMicrophoneInput(options, runCommandOptions) {
58
+ const { data, delaySeconds, durationSeconds } = passAudioAsMicrophoneInputOptions.parse(options);
59
+ // Without this the guest records silence: `-allow-host-audio` only lets QEMU open the
60
+ // record stream, and this toggle is what forwards it to the guest. It defaults to off
61
+ // and resets on every emulator start, so set it every time. The command is idempotent.
62
+ await adb("emu avd hostmicon");
63
+ if (delaySeconds)
64
+ await new Promise((resolve) => setTimeout(resolve, delaySeconds * 1000));
65
+ const args = [
66
+ "-nodisp",
67
+ "-autoexit",
68
+ "-hide_banner",
69
+ "-loglevel",
70
+ "error",
71
+ ...(durationSeconds !== undefined ? ["-t", String(durationSeconds)] : []),
72
+ data,
73
+ ];
74
+ const timeout = durationSeconds !== undefined
75
+ ? Math.max(ms("1m"), (durationSeconds + timeoutMarginSeconds) * 1000)
76
+ : ms("1m");
77
+ return await runCommand("ffplay", {
78
+ args,
79
+ timeout,
80
+ ...runCommandOptions,
81
+ });
82
+ }
83
+ //# sourceMappingURL=passAudioAsMicrophoneInput.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"passAudioAsMicrophoneInput.js","sourceRoot":"","sources":["../../src/emulator/passAudioAsMicrophoneInput.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC/B,OAAO,EAA0B,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAErE,MAAM,iCAAiC,GAAG,CAAC,CAAC,MAAM,CAAC;IACjD,mEAAmE;IACnE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACvB,+CAA+C;IAC/C,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE;IACjD;;;;OAIG;IACH,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;CAClD,CAAC,CAAC;AAMH;;;;;GAKG;AACH,MAAM,oBAAoB,GAAG,EAAE,CAAC;AAEhC;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,iFAAiF;AACjF,+EAA+E;AAC/E,6EAA6E;AAC7E,uEAAuE;AACvE,gFAAgF;AAChF,6EAA6E;AAC7E,gBAAgB;AAChB,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,OAA0C,EAC1C,iBAAmD;IAEnD,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,eAAe,EAAE,GAC3C,iCAAiC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAEnD,sFAAsF;IACtF,sFAAsF;IACtF,uFAAuF;IACvF,MAAM,GAAG,CAAC,mBAAmB,CAAC,CAAC;IAE/B,IAAI,YAAY;QACd,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAAC,CAAC,CAAC;IAE3E,MAAM,IAAI,GAAG;QACX,SAAS;QACT,WAAW;QACX,cAAc;QACd,WAAW;QACX,OAAO;QACP,GAAG,CAAC,eAAe,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACzE,IAAI;KACL,CAAC;IAEF,MAAM,OAAO,GACX,eAAe,KAAK,SAAS;QAC3B,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,eAAe,GAAG,oBAAoB,CAAC,GAAG,IAAI,CAAC;QACrE,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;IAEf,OAAO,MAAM,UAAU,CAAC,QAAQ,EAAE;QAChC,IAAI;QACJ,OAAO;QACP,GAAG,iBAAiB;KACrB,CAAC,CAAC;AACL,CAAC"}
@@ -4,12 +4,7 @@ import { type RunCommandOptions } from "./runCommand.js";
4
4
  * Options for playing back an automation macro.
5
5
  */
6
6
  declare const PlayAutomationOptions: z.ZodUnion<readonly [z.ZodObject<{
7
- macro: z.ZodEnum<{
8
- Reset_position: "Reset_position";
9
- Track_horizontal_plane: "Track_horizontal_plane";
10
- Track_vertical_plane: "Track_vertical_plane";
11
- Walk_to_image_room: "Walk_to_image_room";
12
- }>;
7
+ macro: z.ZodLiteral<"Reset_position" | "Track_horizontal_plane" | "Track_vertical_plane" | "Walk_to_image_room">;
13
8
  overrideMacroPath: z.ZodOptional<z.ZodString>;
14
9
  }, z.core.$strip>, z.ZodObject<{
15
10
  file: z.ZodString;
@@ -1 +1 @@
1
- {"version":3,"file":"playAutomation.d.ts","sourceRoot":"","sources":["../../src/emulator/playAutomation.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,KAAK,iBAAiB,EAAc,MAAM,iBAAiB,CAAC;AAErE;;GAEG;AACH,QAAA,MAAM,qBAAqB;;;;;;;;;;mBAgBzB,CAAC;AAEH,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAE1E;;;;;;;;;;;;GAYG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,qBAAqB,EAC9B,iBAAiB,CAAC,EAAE,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,GAClD,OAAO,CAAC,MAAM,CAAC,CAgBjB"}
1
+ {"version":3,"file":"playAutomation.d.ts","sourceRoot":"","sources":["../../src/emulator/playAutomation.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,KAAK,iBAAiB,EAAc,MAAM,iBAAiB,CAAC;AAErE;;GAEG;AACH,QAAA,MAAM,qBAAqB;;;;;mBAgBzB,CAAC;AAEH,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAE1E;;;;;;;;;;;;GAYG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,qBAAqB,EAC9B,iBAAiB,CAAC,EAAE,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,GAClD,OAAO,CAAC,MAAM,CAAC,CAgBjB"}
@@ -7,7 +7,7 @@ import { runCommand } from "./runCommand.js";
7
7
  const PlayAutomationOptions = z.union([
8
8
  z.object({
9
9
  /** Built-in macro to play */
10
- macro: z.enum([
10
+ macro: z.literal([
11
11
  "Reset_position",
12
12
  "Track_horizontal_plane",
13
13
  "Track_vertical_plane",
@@ -1 +1 @@
1
- {"version":3,"file":"playAutomation.js","sourceRoot":"","sources":["../../src/emulator/playAutomation.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAA0B,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAErE;;GAEG;AACH,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC;IACpC,CAAC,CAAC,MAAM,CAAC;QACP,6BAA6B;QAC7B,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC;YACZ,gBAAgB;YAChB,wBAAwB;YACxB,sBAAsB;YACtB,oBAAoB;SACrB,CAAC;QACF,oEAAoE;QACpE,iBAAiB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;KACzC,CAAC;IACF,CAAC,CAAC,MAAM,CAAC;QACP,0CAA0C;QAC1C,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;KACjB,CAAC;CACH,CAAC,CAAC;AAIH;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,OAA8B,EAC9B,iBAAmD;IAEnD,MAAM,aAAa,GAAG,qBAAqB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAE3D,MAAM,SAAS,GACb,CAAC,mBAAmB,IAAI,aAAa,IAAI,aAAa,CAAC,iBAAiB,CAAC;QACzE,wCAAwC,CAAC;IAE3C,MAAM,IAAI,GACR,OAAO,IAAI,aAAa;QACtB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,KAAK,CAAC;QAC3C,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC;IAEzB,OAAO,MAAM,UAAU,CAAC,KAAK,EAAE;QAC7B,IAAI,EAAE,CAAC,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,IAAI,CAAC;QACzC,GAAG,iBAAiB;KACrB,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"playAutomation.js","sourceRoot":"","sources":["../../src/emulator/playAutomation.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAA0B,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAErE;;GAEG;AACH,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC;IACpC,CAAC,CAAC,MAAM,CAAC;QACP,6BAA6B;QAC7B,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC;YACf,gBAAgB;YAChB,wBAAwB;YACxB,sBAAsB;YACtB,oBAAoB;SACrB,CAAC;QACF,oEAAoE;QACpE,iBAAiB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;KACzC,CAAC;IACF,CAAC,CAAC,MAAM,CAAC;QACP,0CAA0C;QAC1C,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;KACjB,CAAC;CACH,CAAC,CAAC;AAIH;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,OAA8B,EAC9B,iBAAmD;IAEnD,MAAM,aAAa,GAAG,qBAAqB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAE3D,MAAM,SAAS,GACb,CAAC,mBAAmB,IAAI,aAAa,IAAI,aAAa,CAAC,iBAAiB,CAAC;QACzE,wCAAwC,CAAC;IAE3C,MAAM,IAAI,GACR,OAAO,IAAI,aAAa;QACtB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,KAAK,CAAC;QAC3C,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC;IAEzB,OAAO,MAAM,UAAU,CAAC,KAAK,EAAE;QAC7B,IAAI,EAAE,CAAC,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,IAAI,CAAC;QACzC,GAAG,iBAAiB;KACrB,CAAC,CAAC;AACL,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"runCommand.d.ts","sourceRoot":"","sources":["../../src/emulator/runCommand.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,iBAAiB;;;;;;iBAM5B,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAElE,wBAAsB,UAAU,CAC9B,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,MAAM,CAAC,CAuCjB"}
1
+ {"version":3,"file":"runCommand.d.ts","sourceRoot":"","sources":["../../src/emulator/runCommand.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,iBAAiB;;;;;;iBAM5B,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAElE,wBAAsB,UAAU,CAC9B,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,MAAM,CAAC,CAyCjB"}
@@ -40,6 +40,8 @@ export async function runCommand(command, options = {}) {
40
40
  // We can't be totally sure, but SIGTERM usually means the command timed out
41
41
  if (signal === "SIGTERM")
42
42
  throw Error("Command timed out");
43
+ if (all === undefined)
44
+ throw Error("Expected combined output from execa");
43
45
  return all;
44
46
  }
45
47
  //# sourceMappingURL=runCommand.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"runCommand.js","sourceRoot":"","sources":["../../src/emulator/runCommand.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAC9B,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAChC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;IACrC,iBAAiB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACxC,SAAS,EAAE,CAAC,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE;IACzD,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC/B,CAAC,CAAC;AAIH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,OAAe,EACf,UAA6B,EAAE;IAE/B,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACpC,IAAI,CAAC,OAAO;QAAE,MAAM,KAAK,CAAC,sCAAsC,CAAC,CAAC;IAElE,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACpC,IAAI,CAAC,OAAO;QAAE,MAAM,KAAK,CAAC,sCAAsC,CAAC,CAAC;IAElE,MAAM,aAAa,GAAG,iBAAiB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAEvD,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,OAAO,EAAE,aAAa,CAAC,IAAI,EAAE;QAC/D,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,iBAAiB,IAAI,EAAE,CAAC;QAE9D,gEAAgE;QAChE,iDAAiD;QACjD,GAAG,EAAE;YACH,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC;YACzB,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC;YACzB,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC;YACjC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC;YAC7B,IAAI,EACF,OAAO;gBACP,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,aAAa,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAChE,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;SAC1C;QAED,4BAA4B;QAC5B,GAAG,EAAE,IAAI;QAET,uDAAuD;QACvD,SAAS,EAAE,KAAK;QAEhB,yEAAyE;QACzE,OAAO,EAAE,aAAa,CAAC,OAAO,IAAI,EAAE,CAAC,IAAI,CAAC;KAC3C,CAAC,CAAC;IAEH,4EAA4E;IAC5E,IAAI,MAAM,KAAK,SAAS;QAAE,MAAM,KAAK,CAAC,mBAAmB,CAAC,CAAC;IAE3D,OAAO,GAAG,CAAC;AACb,CAAC"}
1
+ {"version":3,"file":"runCommand.js","sourceRoot":"","sources":["../../src/emulator/runCommand.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAC9B,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAChC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;IACrC,iBAAiB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACxC,SAAS,EAAE,CAAC,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE;IACzD,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC/B,CAAC,CAAC;AAIH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,OAAe,EACf,UAA6B,EAAE;IAE/B,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACpC,IAAI,CAAC,OAAO;QAAE,MAAM,KAAK,CAAC,sCAAsC,CAAC,CAAC;IAElE,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACpC,IAAI,CAAC,OAAO;QAAE,MAAM,KAAK,CAAC,sCAAsC,CAAC,CAAC;IAElE,MAAM,aAAa,GAAG,iBAAiB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAEvD,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,OAAO,EAAE,aAAa,CAAC,IAAI,EAAE;QAC/D,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,iBAAiB,IAAI,EAAE,CAAC;QAE9D,gEAAgE;QAChE,iDAAiD;QACjD,GAAG,EAAE;YACH,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC;YACzB,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC;YACzB,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC;YACjC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC;YAC7B,IAAI,EACF,OAAO;gBACP,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,aAAa,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAChE,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;SAC1C;QAED,4BAA4B;QAC5B,GAAG,EAAE,IAAI;QAET,uDAAuD;QACvD,SAAS,EAAE,KAAK;QAEhB,yEAAyE;QACzE,OAAO,EAAE,aAAa,CAAC,OAAO,IAAI,EAAE,CAAC,IAAI,CAAC;KAC3C,CAAC,CAAC;IAEH,4EAA4E;IAC5E,IAAI,MAAM,KAAK,SAAS;QAAE,MAAM,KAAK,CAAC,mBAAmB,CAAC,CAAC;IAE3D,IAAI,GAAG,KAAK,SAAS;QAAE,MAAM,KAAK,CAAC,qCAAqC,CAAC,CAAC;IAE1E,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -2,10 +2,7 @@ import { z } from "zod";
2
2
  import { type RunCommandOptions } from "./runCommand.js";
3
3
  declare const VirtualSceneImageOptions: z.ZodObject<{
4
4
  image: z.ZodDefault<z.ZodString>;
5
- location: z.ZodEnum<{
6
- wall: "wall";
7
- table: "table";
8
- }>;
5
+ location: z.ZodLiteral<"wall" | "table">;
9
6
  }, z.core.$strip>;
10
7
  export type VirtualSceneImageOptions = z.input<typeof VirtualSceneImageOptions>;
11
8
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"setVirtualSceneImage.d.ts","sourceRoot":"","sources":["../../src/emulator/setVirtualSceneImage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,KAAK,iBAAiB,EAAc,MAAM,iBAAiB,CAAC;AAGrE,QAAA,MAAM,wBAAwB;;;;;;iBAK5B,CAAC;AAEH,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAEhF;;;;;;;;;;;GAWG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,wBAAwB,EACjC,iBAAiB,CAAC,EAAE,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,GAClD,OAAO,CAAC,MAAM,CAAC,CAgBjB"}
1
+ {"version":3,"file":"setVirtualSceneImage.d.ts","sourceRoot":"","sources":["../../src/emulator/setVirtualSceneImage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,KAAK,iBAAiB,EAAc,MAAM,iBAAiB,CAAC;AAGrE,QAAA,MAAM,wBAAwB;;;iBAK5B,CAAC;AAEH,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAEhF;;;;;;;;;;;GAWG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,wBAAwB,EACjC,iBAAiB,CAAC,EAAE,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,GAClD,OAAO,CAAC,MAAM,CAAC,CAgBjB"}
@@ -5,7 +5,7 @@ const VirtualSceneImageOptions = z.object({
5
5
  /** Optional absolute path to image file. Leave empty to reset to default. */
6
6
  image: z.string().default(""),
7
7
  /** Virtual scene location to update the image. */
8
- location: z.enum(["wall", "table"]),
8
+ location: z.literal(["wall", "table"]),
9
9
  });
10
10
  /**
11
11
  * Set or reset the virtual scene image.
@@ -1 +1 @@
1
- {"version":3,"file":"setVirtualSceneImage.js","sourceRoot":"","sources":["../../src/emulator/setVirtualSceneImage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAA0B,UAAU,EAAE,MAAM,iBAAiB,CAAC;AACrE,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAE3E,MAAM,wBAAwB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,6EAA6E;IAC7E,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;IAC7B,kDAAkD;IAClD,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC,CAAC,CAAC;AAIH;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,OAAiC,EACjC,iBAAmD;IAEnD,MAAM,aAAa,GAAG,wBAAwB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC9D,IAAI,SAAS,GAAG,aAAa,CAAC,KAAK,CAAC;IAEpC,qFAAqF;IACrF,gFAAgF;IAChF,oFAAoF;IACpF,OAAO,CAAC,GAAG,CACT,yDAAyD,SAAS,EAAE,CACrE,CAAC;IACF,SAAS,GAAG,MAAM,yBAAyB,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IAEjE,OAAO,MAAM,UAAU,CAAC,KAAK,EAAE;QAC7B,IAAI,EAAE,CAAC,KAAK,EAAE,oBAAoB,EAAE,aAAa,CAAC,QAAQ,EAAE,SAAS,CAAC;QACtE,GAAG,iBAAiB;KACrB,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"setVirtualSceneImage.js","sourceRoot":"","sources":["../../src/emulator/setVirtualSceneImage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAA0B,UAAU,EAAE,MAAM,iBAAiB,CAAC;AACrE,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAE3E,MAAM,wBAAwB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,6EAA6E;IAC7E,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;IAC7B,kDAAkD;IAClD,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACvC,CAAC,CAAC;AAIH;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,OAAiC,EACjC,iBAAmD;IAEnD,MAAM,aAAa,GAAG,wBAAwB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC9D,IAAI,SAAS,GAAG,aAAa,CAAC,KAAK,CAAC;IAEpC,qFAAqF;IACrF,gFAAgF;IAChF,oFAAoF;IACpF,OAAO,CAAC,GAAG,CACT,yDAAyD,SAAS,EAAE,CACrE,CAAC;IACF,SAAS,GAAG,MAAM,yBAAyB,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IAEjE,OAAO,MAAM,UAAU,CAAC,KAAK,EAAE;QAC7B,IAAI,EAAE,CAAC,KAAK,EAAE,oBAAoB,EAAE,aAAa,CAAC,QAAQ,EAAE,SAAS,CAAC;QACtE,GAAG,iBAAiB;KACrB,CAAC,CAAC;AACL,CAAC"}
@@ -12,14 +12,99 @@ declare const RunCommandOptions: z.ZodObject<{
12
12
  timeout: z.ZodOptional<z.ZodNumber>;
13
13
  }, z.core.$strip>;
14
14
  type RunCommandOptions = z.input<typeof RunCommandOptions>;
15
+ /**
16
+ * Run an `adb` command and return its combined stdout/stderr.
17
+ *
18
+ * Pass the arguments either as a single string for convenience, or as an array.
19
+ * A string is split on whitespace and supports quoting a single value that
20
+ * contains spaces; it does not go through a shell. When building arguments
21
+ * programmatically, prefer the array form so no quoting is needed.
22
+ *
23
+ * @example
24
+ * // adb shell input keyevent 26
25
+ * await adb("shell input keyevent 26");
26
+ *
27
+ * @example
28
+ * // a path with spaces, quoted in a string...
29
+ * await adb("pull '/sdcard/Recordings/My recording 1.m4a' /tmp/out.m4a");
30
+ *
31
+ * @example
32
+ * // ...or as a single array element, with no quoting to reason about
33
+ * await adb(["pull", "/sdcard/Recordings/My recording 1.m4a", destination]);
34
+ */
35
+ export declare function adb(command: string | string[], runCommandOptions?: Omit<RunCommandOptions, "args">): Promise<string>;
36
+ declare const captureAudioOutputOptions: z.ZodObject<{
37
+ delaySeconds: z.ZodOptional<z.ZodNumber>;
38
+ durationSeconds: z.ZodNumber;
39
+ outputPath: z.ZodString;
40
+ }, z.core.$strip>;
41
+ export type CaptureAudioOutputOptions = z.input<typeof captureAudioOutputOptions>;
42
+ /**
43
+ * Capture the emulator's audio output to a file on the runner host.
44
+ *
45
+ * The mirror of `passAudioAsMicrophoneInput`: where injection plays audio into
46
+ * the emulator's microphone with `ffplay`, this records what the emulator plays
47
+ * out of its speaker. The emulator's output sink exposes a PulseAudio monitor
48
+ * source (`virtual_speaker.monitor`), and `ffmpeg` reads from it on the runner
49
+ * host over the existing pulse tunnel (`PULSE_SERVER`). This wraps the recording
50
+ * flags so flow authors do not have to know them.
51
+ *
52
+ * Recording from a live source has no natural end, so `durationSeconds` is
53
+ * required.
54
+ *
55
+ * Note: `virtual_mic` is mastered on `virtual_speaker.monitor`, so audio
56
+ * injected via `passAudioAsMicrophoneInput` also passes through the sink. A
57
+ * recording taken while injecting concurrently will therefore include the
58
+ * injected audio; pure output capture (no simultaneous injection) is clean.
59
+ *
60
+ * @param options recording options
61
+ * @param runCommandOptions optional options forwarded to runCommand
62
+ * @returns the path the recording was written to (the given `outputPath`)
63
+ * @throws if the command failed
64
+ *
65
+ * @example
66
+ * await captureAudioOutput({
67
+ * outputPath: `${process.env.TEAM_STORAGE_DIR}/output.wav`,
68
+ * durationSeconds: 5,
69
+ * });
70
+ */
71
+ export declare function captureAudioOutput(options: CaptureAudioOutputOptions, runCommandOptions?: Omit<RunCommandOptions, "args">): Promise<string>;
72
+ declare const passAudioAsMicrophoneInputOptions: z.ZodObject<{
73
+ data: z.ZodString;
74
+ delaySeconds: z.ZodOptional<z.ZodNumber>;
75
+ durationSeconds: z.ZodOptional<z.ZodNumber>;
76
+ }, z.core.$strip>;
77
+ export type PassAudioAsMicrophoneInputOptions = z.input<typeof passAudioAsMicrophoneInputOptions>;
78
+ /**
79
+ * Pass an audio file to the emulator as microphone input.
80
+ *
81
+ * The emulator routes host audio into its microphone, and `ffplay` performs the
82
+ * playback on the runner host. This wraps the playback flags so flow authors do
83
+ * not have to know them, and enables the emulator's host-microphone toggle
84
+ * first, so a flow never has to.
85
+ *
86
+ * **Playback is synchronous, so flow order matters.** This resolves only once
87
+ * `ffplay` has finished, which means the app has to be recording *before* it is
88
+ * called. Awaiting it first plays the whole file into a microphone nobody is
89
+ * listening to, and the recording that follows captures silence. `delaySeconds`
90
+ * does not work around that either, since it is awaited inline as well. Start
91
+ * the app recording first, then call this; or hold the promise and await it
92
+ * after the app has started.
93
+ *
94
+ * @param options playback options
95
+ * @param runCommandOptions optional options forwarded to runCommand
96
+ * @throws if the command failed
97
+ *
98
+ * @example
99
+ * await passAudioAsMicrophoneInput({ data: `${process.env.TEAM_STORAGE_DIR}/audio.mp3` });
100
+ *
101
+ * @example
102
+ * await passAudioAsMicrophoneInput({ data: `${process.env.TEAM_STORAGE_DIR}/audio.mp3`, durationSeconds: 10 });
103
+ */
104
+ export declare function passAudioAsMicrophoneInput(options: PassAudioAsMicrophoneInputOptions, runCommandOptions?: Omit<RunCommandOptions, "args">): Promise<string>;
15
105
  declare const PlayAutomationOptions: z.ZodUnion<readonly [
16
106
  z.ZodObject<{
17
- macro: z.ZodEnum<{
18
- Reset_position: "Reset_position";
19
- Track_horizontal_plane: "Track_horizontal_plane";
20
- Track_vertical_plane: "Track_vertical_plane";
21
- Walk_to_image_room: "Walk_to_image_room";
22
- }>;
107
+ macro: z.ZodLiteral<"Reset_position" | "Track_horizontal_plane" | "Track_vertical_plane" | "Walk_to_image_room">;
23
108
  overrideMacroPath: z.ZodOptional<z.ZodString>;
24
109
  }, z.core.$strip>,
25
110
  z.ZodObject<{
@@ -92,10 +177,7 @@ export declare function setProxy(options: ProxyOptions$1, runCommandOptions?: Om
92
177
  export declare function clearProxy(runCommandOptions?: Omit<RunCommandOptions, "args">): Promise<string>;
93
178
  declare const VirtualSceneImageOptions: z.ZodObject<{
94
179
  image: z.ZodDefault<z.ZodString>;
95
- location: z.ZodEnum<{
96
- wall: "wall";
97
- table: "table";
98
- }>;
180
+ location: z.ZodLiteral<"wall" | "table">;
99
181
  }, z.core.$strip>;
100
182
  type VirtualSceneImageOptions$1 = z.input<typeof VirtualSceneImageOptions>;
101
183
  /**
@@ -111,64 +193,6 @@ type VirtualSceneImageOptions$1 = z.input<typeof VirtualSceneImageOptions>;
111
193
  * @throws if the command failed
112
194
  */
113
195
  export declare function setVirtualSceneImage(options: VirtualSceneImageOptions$1, runCommandOptions?: Omit<RunCommandOptions, "args">): Promise<string>;
114
- declare module "enzyme-shallow-equal" {
115
- export default function shallowEqual(objA: unknown, objB: unknown): boolean;
116
- }
117
- declare module "http-compression" {
118
- import type { IncomingMessage, ServerResponse } from "node:http";
119
- type Options = {
120
- brotli?: boolean;
121
- gzip?: boolean;
122
- level?: number;
123
- mimes?: RegExp;
124
- threshold?: number;
125
- };
126
- type Handler = (req: IncomingMessage, res: ServerResponse) => void;
127
- export default function compression(options?: Options): Handler;
128
- }
129
- declare module "parse-prometheus-text-format" {
130
- export default function parsePrometheusTextFormat(text: string): {
131
- metrics: {
132
- value: string;
133
- }[];
134
- name: string;
135
- }[];
136
- }
137
- declare module "react" {
138
- namespace JSX {
139
- // The entire namespace is deprecated in React, but VS Code doe not
140
- // show the deprecation annotation on the namespace, so we add them
141
- // on individual namespace members.
142
- /**
143
- * @deprecated Usually you want ReactNode from react instead. If you really think you need JSX.Element, explicitly import it from react.
144
- */
145
- interface Element extends JSX.Element {
146
- // Prevent this from being an empty interface,
147
- // which is then collapsed and does not pass
148
- // the @deprecated annotation when using.
149
- _dummy?: unknown;
150
- }
151
- }
152
- }
153
- type DefaultEventsMap = {
154
- [event: string]: (...args: unknown[]) => void;
155
- };
156
- type EventsMap = {
157
- [event: string]: unknown;
158
- };
159
- declare module "socket.io-client" {
160
- interface Socket<ListenEvents extends EventsMap = DefaultEventsMap, EmitEvents extends EventsMap = ListenEvents> extends BaseSocket<ListenEvents, EmitEvents> {
161
- timeout(ms: number): Socket<ListenEvents, EmitEvents> & {
162
- /**
163
- * disallow emit, since it is not correctly typed
164
- * after this change.
165
- *
166
- * use emitWithAck instead.
167
- */
168
- emit: never;
169
- };
170
- }
171
- }
172
196
 
173
197
  export {
174
198
  GeoLocationOptions$1 as GeoLocationOptions,
package/package.json CHANGED
@@ -1,10 +1,15 @@
1
1
  {
2
2
  "name": "@qawolf/run-globals-android",
3
- "version": "1.0.0",
3
+ "version": "1.2.1",
4
4
  "private": false,
5
5
  "license": "MIT",
6
- "sideEffects": false,
6
+ "files": [
7
+ "/dist",
8
+ "/LICENSE",
9
+ "/README.md"
10
+ ],
7
11
  "type": "module",
12
+ "sideEffects": false,
8
13
  "exports": {
9
14
  ".": {
10
15
  "types": "./dist/index.d.ts",
@@ -15,39 +20,29 @@
15
20
  "import": "./dist/emulator/index.js"
16
21
  }
17
22
  },
18
- "files": [
19
- "/dist",
20
- "/LICENSE",
21
- "/README.md"
22
- ],
23
23
  "scripts": {
24
24
  "build": "rm -rf ./dist && tsc -p tsconfig.build.json && npm run build:dts",
25
25
  "build:dts": "dts-bundle-generator --config dts.config.json",
26
- "lint:ci": "npm run lint:non-fixable; NON_FIXABLE_EXIT=$?; npm run lint:fixable && exit $NON_FIXABLE_EXIT",
27
- "lint:dev": "npm run lint:fixable",
28
- "lint:fixable": "eslint . --ext cjs,cts,js,jsx,mjs,ts,tsx --fix --quiet && prettier --write .",
29
- "lint:non-fixable": "dpdm --exit-code circular:1 --no-tree --no-warning --transform --include='.*' --exclude='/(dist|node_modules)/' '**/*'",
26
+ "lint:ci": "npm run lint:first-half; FIRST_HALF_EXIT=$?; npm run lint:second-half && exit $FIRST_HALF_EXIT",
27
+ "lint:dev": "npm run lint:first-half",
28
+ "lint:first-half": "oxlint . --fix --quiet --report-unused-disable-directives --type-aware && oxfmt --write .",
29
+ "lint:second-half": "dpdm --exit-code circular:1 --no-tree --no-warning --transform --include='.*' --exclude='/(dist|node_modules)/' '**/*'",
30
30
  "prepublishOnly": "nx run run-globals-android:build",
31
31
  "test": "NODE_OPTIONS=\"--experimental-vm-modules\" NODE_NO_WARNINGS=1 jest",
32
32
  "test:watch": "npm run test -- --watch",
33
33
  "tsc:check": "tsc"
34
34
  },
35
35
  "dependencies": {
36
- "execa": "^9.3.1",
36
+ "execa": "^9.6.1",
37
37
  "ms": "^3.0.0-canary.202508261828",
38
38
  "tslib": "^2.5.3",
39
39
  "zod": "^4.1.11"
40
40
  },
41
41
  "devDependencies": {
42
42
  "dts-bundle-generator": "^9.5.1",
43
- "typescript": "^5.9.2"
43
+ "typescript": "^5.9.3"
44
44
  },
45
45
  "engines": {
46
- "node": ">=22.22.0 <25"
47
- },
48
- "nx": {
49
- "targets": {
50
- "npm:publish": {}
51
- }
46
+ "node": ">=20.19.0 <25"
52
47
  }
53
48
  }
package/dist/typings.d.ts DELETED
@@ -1,62 +0,0 @@
1
- // Generated by dts-bundle-generator v9.5.1
2
-
3
- declare module "enzyme-shallow-equal" {
4
- export default function shallowEqual(objA: unknown, objB: unknown): boolean;
5
- }
6
- declare module "http-compression" {
7
- import type { IncomingMessage, ServerResponse } from "node:http";
8
- type Options = {
9
- brotli?: boolean;
10
- gzip?: boolean;
11
- level?: number;
12
- mimes?: RegExp;
13
- threshold?: number;
14
- };
15
- type Handler = (req: IncomingMessage, res: ServerResponse) => void;
16
- export default function compression(options?: Options): Handler;
17
- }
18
- declare module "parse-prometheus-text-format" {
19
- export default function parsePrometheusTextFormat(text: string): {
20
- metrics: {
21
- value: string;
22
- }[];
23
- name: string;
24
- }[];
25
- }
26
- declare module "react" {
27
- namespace JSX {
28
- // The entire namespace is deprecated in React, but VS Code doe not
29
- // show the deprecation annotation on the namespace, so we add them
30
- // on individual namespace members.
31
- /**
32
- * @deprecated Usually you want ReactNode from react instead. If you really think you need JSX.Element, explicitly import it from react.
33
- */
34
- interface Element extends JSX.Element {
35
- // Prevent this from being an empty interface,
36
- // which is then collapsed and does not pass
37
- // the @deprecated annotation when using.
38
- _dummy?: unknown;
39
- }
40
- }
41
- }
42
- type DefaultEventsMap = {
43
- [event: string]: (...args: unknown[]) => void;
44
- };
45
- type EventsMap = {
46
- [event: string]: unknown;
47
- };
48
- declare module "socket.io-client" {
49
- interface Socket<ListenEvents extends EventsMap = DefaultEventsMap, EmitEvents extends EventsMap = ListenEvents> extends BaseSocket<ListenEvents, EmitEvents> {
50
- timeout(ms: number): Socket<ListenEvents, EmitEvents> & {
51
- /**
52
- * disallow emit, since it is not correctly typed
53
- * after this change.
54
- *
55
- * use emitWithAck instead.
56
- */
57
- emit: never;
58
- };
59
- }
60
- }
61
-
62
- export {};