From 9a8519f139f1a78ff484aaa17d8455c0ff2f4f88 Mon Sep 17 00:00:00 2001 From: Gabe Debes Date: Tue, 20 Oct 2026 13:10:49 -0700 Subject: [PATCH 1/3] [build-tools] Observe Argent Stop requests during startup Co-Authored-By: Claude Opus 5.5 --- .changeset/argent-stop-during-startup.md | 5 + .../src/__tests__/utils/startupWatch.ts | 22 +++ ...tArgentRemoteSession-orchestration.test.ts | 77 ++++++++- .../functions/startArgentRemoteSession.ts | 157 +++++++++--------- 4 files changed, 184 insertions(+), 77 deletions(-) create mode 100644 .changeset/argent-stop-during-startup.md create mode 100644 packages/build-tools/src/__tests__/utils/startupWatch.ts diff --git a/.changeset/argent-stop-during-startup.md b/.changeset/argent-stop-during-startup.md new file mode 100644 index 0000000000..3d1dccd74e --- /dev/null +++ b/.changeset/argent-stop-during-startup.md @@ -0,0 +1,5 @@ +--- +'@expo/build-tools': patch +--- + +Stop an Argent session that is still starting up when it is stopped. diff --git a/packages/build-tools/src/__tests__/utils/startupWatch.ts b/packages/build-tools/src/__tests__/utils/startupWatch.ts new file mode 100644 index 0000000000..1a7dbf1f03 --- /dev/null +++ b/packages/build-tools/src/__tests__/utils/startupWatch.ts @@ -0,0 +1,22 @@ +import { type watchDeviceRunSessionStartup } from '../../steps/utils/deviceRunSessionStartup'; +import { DeviceRunSessionStoppedError } from '../../steps/utils/remoteDeviceRunSession'; + +/** + * A `watchDeviceRunSessionStartup` stand-in that never polls. A test calls `stop` on the + * returned watch (`jest.mocked(watchDeviceRunSessionStartup).mock.results[0].value`) to act like + * a Stop request during startup. + */ +export function createStartupWatchMock({ + startup, +}: Parameters[0]): ReturnType< + typeof watchDeviceRunSessionStartup +> & { stop: () => void } { + const stopped = new DeviceRunSessionStoppedError('Stopped during startup.'); + return { + stopped, + stop: () => startup.abort(stopped), + isStopped: () => startup.signal.reason === stopped, + completeAsync: jest.fn(async () => startup.signal.throwIfAborted()), + finishAsync: jest.fn(async () => {}), + }; +} diff --git a/packages/build-tools/src/steps/functions/__tests__/startArgentRemoteSession-orchestration.test.ts b/packages/build-tools/src/steps/functions/__tests__/startArgentRemoteSession-orchestration.test.ts index d85cae8091..6ced314bba 100644 --- a/packages/build-tools/src/steps/functions/__tests__/startArgentRemoteSession-orchestration.test.ts +++ b/packages/build-tools/src/steps/functions/__tests__/startArgentRemoteSession-orchestration.test.ts @@ -24,6 +24,8 @@ import { uploadRemoteSessionConfigAsync, waitForDeviceRunSessionStoppedAsync, } from '../../utils/remoteDeviceRunSession'; +import { watchDeviceRunSessionStartup } from '../../utils/deviceRunSessionStartup'; +import { createStartupWatchMock } from '../../../__tests__/utils/startupWatch'; import { createStartArgentRemoteSessionBuildFunction } from '../startArgentRemoteSession'; import { readIosApplicationIdentifierAsync } from '../../utils/iosAppArtifact'; @@ -80,6 +82,9 @@ jest.mock('../../utils/remoteDeviceRunSession', () => ({ uploadRemoteSessionConfigAsync: jest.fn(), waitForDeviceRunSessionStoppedAsync: jest.fn(), })); +jest.mock('../../utils/deviceRunSessionStartup', () => ({ + watchDeviceRunSessionStartup: jest.fn(), +})); const TEST_HOME = path.join(os.tmpdir(), 'eas-argent-orchestration-home'); const ARGENT_STATE_DIR = path.join(TEST_HOME, '.argent'); @@ -130,6 +135,7 @@ describe('createStartArgentRemoteSessionBuildFunction orchestration', () => { }); jest.mocked(uploadRemoteSessionConfigAsync).mockResolvedValue(undefined); jest.mocked(waitForDeviceRunSessionStoppedAsync).mockResolvedValue(undefined); + jest.mocked(watchDeviceRunSessionStartup).mockImplementation(createStartupWatchMock); // The (real) tool-server state wait reads this file from the redirected ~/.argent. await fs.promises.mkdir(ARGENT_STATE_DIR, { recursive: true }); @@ -143,6 +149,73 @@ describe('createStartArgentRemoteSessionBuildFunction orchestration', () => { await fs.promises.rm(TEST_HOME, { recursive: true, force: true }); }); + it.each([ + [BuildRuntimePlatform.DARWIN, false], + [BuildRuntimePlatform.LINUX, false], + [BuildRuntimePlatform.DARWIN, true], + ])( + 'handles Stop during %s setup, keeping teardown failure=%s visible', + async (runtimePlatform, teardownFails) => { + let rejectSetup!: (error: Error) => void; + const setup = Object.assign( + new Promise((_resolve, reject) => { + rejectSetup = reject; + }), + { child: { pid: 12345 } } + ); + jest.mocked(spawn).mockReturnValueOnce(setup as unknown as ReturnType); + jest + .mocked(killProcessGroup) + .mockImplementationOnce(() => rejectSetup(new Error('setup killed'))); + const teardownError = new Error('host teardown failed'); + if (teardownFails) { + mockPreviewStopAsync.mockRejectedValueOnce(teardownError); + } + const buildFunction = createStartArgentRemoteSessionBuildFunction({} as CustomBuildContext); + const running = Promise.resolve( + buildFunction.fn!( + { + logger: { info: jest.fn(), warn: jest.fn() }, + global: { runtimePlatform }, + } as unknown as BuildStepContext, + { + inputs: { + package_version: { value: undefined }, + max_idle_time_minutes: { value: undefined }, + boot_simulator: { + value: runtimePlatform === BuildRuntimePlatform.DARWIN ? true : undefined, + }, + }, + outputs: {}, + env: {}, + } as never + ) + ); + running.catch(() => {}); + try { + for (let i = 0; i < 20 && !jest.mocked(spawn).mock.calls.length; i++) { + await new Promise(resolve => setImmediate(resolve)); + } + expect(spawn).toHaveBeenCalledTimes(1); + const watch = jest.mocked(watchDeviceRunSessionStartup).mock.results[0].value; + watch.stop(); + if (teardownFails) { + await expect(running).rejects.toBe(teardownError); + } else { + await expect(running).resolves.toBeUndefined(); + } + expect(killProcessGroup).toHaveBeenCalledTimes(1); + expect(spawnDetached).not.toHaveBeenCalled(); + expect(uploadRemoteSessionConfigAsync).not.toHaveBeenCalled(); + expect(waitForDeviceRunSessionStoppedAsync).not.toHaveBeenCalled(); + expect(watch.finishAsync).toHaveBeenCalled(); + } finally { + rejectSetup(new Error('test cleanup')); + await running.catch(() => {}); + } + } + ); + it('reports an early exit with output and stops Argent before opening tunnels', async () => { const stopError = new Error('drain timed out'); const stopServer = jest.fn().mockRejectedValue(stopError); @@ -892,7 +965,7 @@ describe('createStartArgentRemoteSessionBuildFunction orchestration', () => { }); it.each([BuildRuntimePlatform.DARWIN, BuildRuntimePlatform.LINUX])( - 'keeps %s prepared setup commands outside the new cancellation path', + 'makes %s prepared setup commands cancellable', async runtimePlatform => { const buildFunction = createStartArgentRemoteSessionBuildFunction({} as CustomBuildContext); await buildFunction.fn!( @@ -911,7 +984,7 @@ describe('createStartArgentRemoteSessionBuildFunction orchestration', () => { ); expect(spawn).toHaveBeenCalledTimes(3); for (const [, , options] of jest.mocked(spawn).mock.calls) { - expect(options).not.toHaveProperty('detached'); + expect(options).toHaveProperty('detached', true); expect(options).not.toHaveProperty('signal'); } expect(killProcessGroup).not.toHaveBeenCalled(); diff --git a/packages/build-tools/src/steps/functions/startArgentRemoteSession.ts b/packages/build-tools/src/steps/functions/startArgentRemoteSession.ts index 742d20eb69..5930a8f6b1 100644 --- a/packages/build-tools/src/steps/functions/startArgentRemoteSession.ts +++ b/packages/build-tools/src/steps/functions/startArgentRemoteSession.ts @@ -57,6 +57,8 @@ import { startNgrokTunnelAsync, waitForDeviceRunSessionStoppedAsync, } from '../utils/remoteDeviceRunSession'; +import { watchDeviceRunSessionStartup } from '../utils/deviceRunSessionStartup'; +import { createStartupTasks } from '../utils/startupTasks'; const ARGENT_PACKAGE_NAME = '@swmansion/argent'; // 0.16.0 is the first version that exposes the tool-server event log flag; keeping the floor @@ -132,48 +134,13 @@ export function createStartArgentRemoteSessionBuildFunction( warnIfArgentPackageVersionCannotBeVerified({ packageVersion, logger }); const versionSpec = packageVersion ?? 'latest'; const { runtimePlatform } = global; - if (runtimePlatform === BuildRuntimePlatform.DARWIN) { - await selectXcodeDeveloperDirectoryAsync({ env, logger }); - } - const { launch, iosStartup } = await resolveIosSessionStartupAsync({ - runtimePlatform, - bootSimulator: inputs.boot_simulator?.value as boolean | undefined, - deviceIdentifier: inputs.device_identifier?.value as string | undefined, - installAppPath: inputs.install_app_path?.value as string | undefined, - launchAppIdentifier: inputs.launch_app_identifier?.value, - launchArgs: inputs.launch_args?.value, - openUrl: inputs.open_url?.value, - env, + const startup = createStartupTasks(logger, signal); + const startupWatch = watchDeviceRunSessionStartup({ + ctx, + deviceRunSessionId, logger, - signal, + startup, }); - const { networkCapture, networkCaptureFields } = parseNetworkCaptureInputs( - { - networkCapture: inputs.network_capture?.value, - networkCaptureFields: inputs.network_capture_fields?.value, - }, - { runtimePlatform } - ); - logger.info( - `Starting argent remote session (version: ${versionSpec}, runtime: ${runtimePlatform}).` - ); - - // Start the potentially slow installation while Argent is being prepared. - // On Linux expo-device-hub calls this again and awaits the same in-flight - // setup before launching. On macOS this remains non-blocking, so only a - // recording started in the first moments may miss ffmpeg. - // Never rejects, so `void` is safe. - void ensureFfmpegInstalledOnceAsync({ runtimePlatform, env, logger }); - - const packageManager = resolveConfiguredPackageManager(env, PackageManager.BUN); - const argentExec = (args: string[]): { command: string; args: string[] } => - resolvePackageExec(packageManager, args); - - const startupAbortController = new AbortController(); - const startupSignal = signal - ? AbortSignal.any([signal, startupAbortController.signal]) - : startupAbortController.signal; - const setupSignal = iosStartup ? startupSignal : undefined; const artifactPollAbortController = new AbortController(); let artifactPollingPromise: Promise | undefined; let eventCollection: Awaited> | undefined; @@ -182,30 +149,69 @@ export function createStartArgentRemoteSessionBuildFunction( let hostStartup: Promise> | undefined; let sessionHost: DeviceSessionHost | undefined; let sessionFailed = false; - const startHost = () => - startDeviceSessionHostAsync(ctx, { - ...iosStartup, - separateLogPhase: true, + try { + startup.signal.throwIfAborted(); + if (runtimePlatform === BuildRuntimePlatform.DARWIN) { + await selectXcodeDeveloperDirectoryAsync({ env, logger }); + } + const { launch, iosStartup } = await resolveIosSessionStartupAsync({ runtimePlatform, + bootSimulator: inputs.boot_simulator?.value as boolean | undefined, + deviceIdentifier: inputs.device_identifier?.value as string | undefined, + installAppPath: inputs.install_app_path?.value as string | undefined, + launchAppIdentifier: inputs.launch_app_identifier?.value, + launchArgs: inputs.launch_args?.value, + openUrl: inputs.open_url?.value, env, logger, - disableShare: inputs.public_preview?.value === false, - privatePreviewUrl: inputs.private_preview_url?.value as string | undefined, - frameAncestors: parseFrameAncestorsInput(inputs.frame_ancestors?.value), - corsOrigins: parseCorsOriginsInput(inputs.cors_origins?.value), - timeoutMs: STARTUP_TIMEOUT_MS, - signal: startupSignal, - onStartupError: iosStartup ? error => startupAbortController.abort(error) : undefined, - ...launch, - networkCapture, - networkCaptureFields, - }).catch(error => { - if (iosStartup) { - startupAbortController.abort(error); - } - throw error; + signal: startup.signal, }); - try { + startup.signal.throwIfAborted(); + const { networkCapture, networkCaptureFields } = parseNetworkCaptureInputs( + { + networkCapture: inputs.network_capture?.value, + networkCaptureFields: inputs.network_capture_fields?.value, + }, + { runtimePlatform } + ); + logger.info( + `Starting argent remote session (version: ${versionSpec}, runtime: ${runtimePlatform}).` + ); + + // Start the potentially slow installation while Argent is being prepared. + // On Linux expo-device-hub calls this again and awaits the same in-flight + // setup before launching. On macOS this remains non-blocking, so only a + // recording started in the first moments may miss ffmpeg. + // Never rejects, so `void` is safe. + void ensureFfmpegInstalledOnceAsync({ runtimePlatform, env, logger }); + + const packageManager = resolveConfiguredPackageManager(env, PackageManager.BUN); + const argentExec = (args: string[]): { command: string; args: string[] } => + resolvePackageExec(packageManager, args); + + const startHost = () => + startDeviceSessionHostAsync(ctx, { + ...iosStartup, + separateLogPhase: true, + runtimePlatform, + env, + logger, + disableShare: inputs.public_preview?.value === false, + privatePreviewUrl: inputs.private_preview_url?.value as string | undefined, + frameAncestors: parseFrameAncestorsInput(inputs.frame_ancestors?.value), + corsOrigins: parseCorsOriginsInput(inputs.cors_origins?.value), + timeoutMs: STARTUP_TIMEOUT_MS, + signal: startup.signal, + onStartupError: iosStartup ? error => startup.abort(error) : undefined, + ...launch, + networkCapture, + networkCaptureFields, + }).catch(error => { + if (iosStartup) { + startup.abort(error); + } + throw error; + }); if (iosStartup) { hostStartup = asyncResult(startHost()); } @@ -219,7 +225,7 @@ export function createStartArgentRemoteSessionBuildFunction( enableArtifacts.command, enableArtifacts.args, { env, logger }, - setupSignal + startup.signal ); logger.info('Enabling the Argent tool-server event log flag.'); @@ -232,7 +238,7 @@ export function createStartArgentRemoteSessionBuildFunction( enableEventLog.command, enableEventLog.args, { env, logger }, - setupSignal + startup.signal ); logger.info('Disabling the Argent video watermark flag.'); @@ -247,17 +253,17 @@ export function createStartArgentRemoteSessionBuildFunction( disableVideoWatermark.command, disableVideoWatermark.args, { env, logger }, - setupSignal + startup.signal ); } catch (err) { - setupSignal?.throwIfAborted(); + startup.signal.throwIfAborted(); logger.warn( { err }, 'Could not disable the Argent video watermark, so recorded videos may include it. Continuing the session.' ); } - setupSignal?.throwIfAborted(); + startup.signal.throwIfAborted(); const startServer = argentExec([ `${ARGENT_PACKAGE_NAME}@${versionSpec}`, 'server', @@ -296,7 +302,7 @@ export function createStartArgentRemoteSessionBuildFunction( ancestorPid: argentServer.pid, timeoutMs: STARTUP_TIMEOUT_MS, getExitError: argentServer.getExitError, - signal: setupSignal, + signal: startup.signal, }); toolServerPort = toolServerState.port; toolServerToken = toolServerState.token; @@ -314,7 +320,7 @@ export function createStartArgentRemoteSessionBuildFunction( if (hostStartup) { sessionHost = (await hostStartup).enforceValue(); } - setupSignal?.throwIfAborted(); + startup.signal.throwIfAborted(); logger.info(`Argent tool-server is listening on port ${toolServerPort}.`); const artifactPollSignal = signal ? AbortSignal.any([signal, artifactPollAbortController.signal]) @@ -363,7 +369,7 @@ export function createStartArgentRemoteSessionBuildFunction( await uploadRemoteSessionConfigWithLocalEgressAsync({ env, - signal, + signal: startup.signal, ctx, deviceRunSessionId, remoteConfig: { @@ -376,6 +382,7 @@ export function createStartArgentRemoteSessionBuildFunction( logger, }); + await startupWatch.completeAsync(); await waitForDeviceRunSessionStoppedAsync({ ctx, deviceRunSessionId, @@ -392,10 +399,14 @@ export function createStartArgentRemoteSessionBuildFunction( : undefined, }); } catch (error) { + if (startupWatch.isStopped()) { + return; + } sessionFailed = true; - throw setupSignal?.aborted ? setupSignal.reason : error; + throw startup.signal.aborted ? startup.signal.reason : error; } finally { - startupAbortController.abort(new Error('Argent session ended.')); + startup.abort(new Error('Argent session ended.')); + await startupWatch.finishAsync(); await finishRemoteSessionAsync({ logger, sessionFailed, @@ -495,12 +506,8 @@ async function runArgentSetupCommandAsync( command: string, args: string[], options: SpawnOptions, - signal?: AbortSignal + signal: AbortSignal ): Promise { - if (!signal) { - await spawn(command, args, options); - return; - } signal.throwIfAborted(); const task = spawn(command, args, { ...options, detached: true }); const abort = (): void => killProcessGroup(task.child, 'SIGKILL'); From d0505350bfe69bd9d6bc92fc2955f185776ebb29 Mon Sep 17 00:00:00 2001 From: Gabe Debes Date: Thu, 29 Oct 2026 12:43:57 -0700 Subject: [PATCH 2/3] [build-tools] Check Stop before opening the Argent preview --- ...tArgentRemoteSession-orchestration.test.ts | 55 +++++++++++++++++++ .../functions/startArgentRemoteSession.ts | 1 + 2 files changed, 56 insertions(+) diff --git a/packages/build-tools/src/steps/functions/__tests__/startArgentRemoteSession-orchestration.test.ts b/packages/build-tools/src/steps/functions/__tests__/startArgentRemoteSession-orchestration.test.ts index 6ced314bba..05779c5ff8 100644 --- a/packages/build-tools/src/steps/functions/__tests__/startArgentRemoteSession-orchestration.test.ts +++ b/packages/build-tools/src/steps/functions/__tests__/startArgentRemoteSession-orchestration.test.ts @@ -216,6 +216,61 @@ describe('createStartArgentRemoteSessionBuildFunction orchestration', () => { } ); + it('does not open the preview after Stop while the tools tunnel is opening', async () => { + let tunnelEntered!: () => void; + const entered = new Promise(resolve => { + tunnelEntered = resolve; + }); + let finishTunnel!: () => void; + const pendingTunnel = new Promise(resolve => { + finishTunnel = resolve; + }); + jest.mocked(startNgrokTunnelAsync).mockImplementationOnce(async () => { + tunnelEntered(); + await pendingTunnel; + return { + url: 'https://argent.example.com', + subdomainId: 'argent', + stopAsync: mockTunnelStopAsync, + }; + }); + const openPreviewAsync = jest.fn(); + jest.mocked(startDeviceSessionHostAsync).mockResolvedValueOnce({ + openPreviewAsync, + finishAsync: mockPreviewStopAsync, + getLastPreviewActivityAt: jest.fn(), + }); + const running = Promise.resolve( + createStartArgentRemoteSessionBuildFunction({} as CustomBuildContext).fn!( + { + logger: { info: jest.fn(), warn: jest.fn() }, + global: { runtimePlatform: BuildRuntimePlatform.DARWIN }, + } as unknown as BuildStepContext, + { + inputs: { + package_version: { value: undefined }, + max_idle_time_minutes: { value: undefined }, + boot_simulator: { value: true }, + }, + outputs: {}, + env: {}, + } as never + ) + ); + running.catch(() => {}); + try { + await entered; + jest.mocked(watchDeviceRunSessionStartup).mock.results[0].value.stop(); + } finally { + finishTunnel(); + } + await expect(running).resolves.toBeUndefined(); + expect(openPreviewAsync).not.toHaveBeenCalled(); + expect(uploadRemoteSessionConfigAsync).not.toHaveBeenCalled(); + expect(mockTunnelStopAsync).toHaveBeenCalledTimes(1); + expect(mockPreviewStopAsync).toHaveBeenCalledTimes(1); + }); + it('reports an early exit with output and stops Argent before opening tunnels', async () => { const stopError = new Error('drain timed out'); const stopServer = jest.fn().mockRejectedValue(stopError); diff --git a/packages/build-tools/src/steps/functions/startArgentRemoteSession.ts b/packages/build-tools/src/steps/functions/startArgentRemoteSession.ts index 5930a8f6b1..a0ea0fc7ff 100644 --- a/packages/build-tools/src/steps/functions/startArgentRemoteSession.ts +++ b/packages/build-tools/src/steps/functions/startArgentRemoteSession.ts @@ -357,6 +357,7 @@ export function createStartArgentRemoteSessionBuildFunction( logger.info(launchDescription); } sessionHost ??= (await (hostStartup ??= asyncResult(startHost()))).enforceValue(); + startup.signal.throwIfAborted(); const webPreview = await sessionHost.openPreviewAsync({ baseDomain: ngrokTunnelDomain, authtoken: ngrokAuthtoken, From 81b9de5ee1a7516277b2d5feaff856ddf3a7ac55 Mon Sep 17 00:00:00 2001 From: Gabe Debes Date: Thu, 29 Oct 2026 13:03:17 -0700 Subject: [PATCH 3/3] [build-tools] Preserve prepared Argent host failures before Stop --- ...tArgentRemoteSession-orchestration.test.ts | 48 +++++++++++++++++++ .../functions/startArgentRemoteSession.ts | 2 +- 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/packages/build-tools/src/steps/functions/__tests__/startArgentRemoteSession-orchestration.test.ts b/packages/build-tools/src/steps/functions/__tests__/startArgentRemoteSession-orchestration.test.ts index 05779c5ff8..880bb1c014 100644 --- a/packages/build-tools/src/steps/functions/__tests__/startArgentRemoteSession-orchestration.test.ts +++ b/packages/build-tools/src/steps/functions/__tests__/startArgentRemoteSession-orchestration.test.ts @@ -271,6 +271,54 @@ describe('createStartArgentRemoteSessionBuildFunction orchestration', () => { expect(mockPreviewStopAsync).toHaveBeenCalledTimes(1); }); + it.each([BuildRuntimePlatform.DARWIN, BuildRuntimePlatform.LINUX])( + 'preserves an earlier prepared %s host failure when Stop arrives during cleanup', + async runtimePlatform => { + const failure = new Error('Host became ready without a session token'); + let cleanupEntered!: () => void; + const entered = new Promise(resolve => { + cleanupEntered = resolve; + }); + let finishCleanup!: () => void; + const cleanup = new Promise(resolve => { + finishCleanup = resolve; + }); + jest.mocked(startDeviceSessionHostAsync).mockImplementationOnce(async (_ctx, options) => { + options.onStartupError?.(failure); + cleanupEntered(); + await cleanup; + throw failure; + }); + const running = Promise.resolve( + createStartArgentRemoteSessionBuildFunction({} as CustomBuildContext).fn!( + { + logger: { info: jest.fn(), warn: jest.fn() }, + global: { runtimePlatform }, + } as unknown as BuildStepContext, + { + inputs: { + package_version: { value: undefined }, + max_idle_time_minutes: { value: undefined }, + }, + outputs: {}, + env: {}, + } as never + ) + ); + running.catch(() => {}); + try { + await entered; + jest.mocked(watchDeviceRunSessionStartup).mock.results[0].value.stop(); + } finally { + finishCleanup(); + } + await expect(running).rejects.toBe(failure); + expect(uploadRemoteSessionConfigAsync).not.toHaveBeenCalled(); + expect(waitForDeviceRunSessionStoppedAsync).not.toHaveBeenCalled(); + expect(mockTunnelStopAsync).toHaveBeenCalledTimes(1); + } + ); + it('reports an early exit with output and stops Argent before opening tunnels', async () => { const stopError = new Error('drain timed out'); const stopServer = jest.fn().mockRejectedValue(stopError); diff --git a/packages/build-tools/src/steps/functions/startArgentRemoteSession.ts b/packages/build-tools/src/steps/functions/startArgentRemoteSession.ts index a0ea0fc7ff..1857ca8256 100644 --- a/packages/build-tools/src/steps/functions/startArgentRemoteSession.ts +++ b/packages/build-tools/src/steps/functions/startArgentRemoteSession.ts @@ -202,7 +202,7 @@ export function createStartArgentRemoteSessionBuildFunction( corsOrigins: parseCorsOriginsInput(inputs.cors_origins?.value), timeoutMs: STARTUP_TIMEOUT_MS, signal: startup.signal, - onStartupError: iosStartup ? error => startup.abort(error) : undefined, + onStartupError: error => startup.abort(error), ...launch, networkCapture, networkCaptureFields,