Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 15 additions & 8 deletions packages/platform-apple/src/snapshot-observability.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import type { PlatformRuntimeHost } from '@agent-device/contracts/platform-runti
import type { DeviceInfo } from '@agent-device/kernel/device';
import type { SimulatorSnapshotSource } from './snapshot-source-facade.ts';
import {
isSimulatorTargetDiscoveryPending,
resolveSimulatorTargetJoiningDiscovery,
type SimulatorSnapshotTarget,
type SimulatorSnapshotTargetResolver,
} from './snapshot-target.ts';
Expand Down Expand Up @@ -111,12 +111,19 @@ async function resolveLaunchedTarget(
appBundleId: string,
signal: AbortSignal,
): Promise<SimulatorSnapshotTarget | undefined> {
for (;;) {
try {
return await resolveTarget(device, appBundleId, signal);
} catch (error) {
signal.throwIfAborted();
if (!isSimulatorTargetDiscoveryPending(error)) return undefined;
}
try {
return await resolveSimulatorTargetJoiningDiscovery(
resolveTarget,
device,
appBundleId,
signal,
() => {
signal.throwIfAborted();
return true;
},
);
} catch {
signal.throwIfAborted();
return undefined;
}
}
19 changes: 9 additions & 10 deletions packages/platform-apple/src/snapshot-route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import {
} from './snapshot-observability.ts';
import {
createSimulatorSnapshotTargetResolver,
isSimulatorTargetDiscoveryPending,
resolveSimulatorTargetJoiningDiscovery,
type SimulatorSnapshotTarget,
type SimulatorSnapshotTargetResolver,
} from './snapshot-target.ts';
Expand Down Expand Up @@ -236,15 +236,14 @@ async function resolveTargetForObservation(
signal: AbortSignal,
): Promise<SimulatorSnapshotTarget> {
const appBundleId = input.options!.appBundleId!;
for (;;) {
try {
return await resolveTarget(device, appBundleId, signal);
} catch (error) {
if (!isSimulatorTargetDiscoveryPending(error)) throw error;
const execution = { requestId: input.execution?.requestId };
if (await host.appleApplications.hasLiveRunnerSession(device, execution)) throw error;
}
}
const execution = { requestId: input.execution?.requestId };
return await resolveSimulatorTargetJoiningDiscovery(
resolveTarget,
device,
appBundleId,
signal,
async () => !(await host.appleApplications.hasLiveRunnerSession(device, execution)),
);
}

function isEligible(device: DeviceInfo, input: CaptureSnapshotInput): boolean {
Expand Down
26 changes: 25 additions & 1 deletion packages/platform-apple/src/snapshot-target.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,34 @@ export function createSimulatorSnapshotTargetResolver(): SimulatorSnapshotTarget
* Whether a resolver failure only says the discovery is still running. The discovery keeps going
* under its own deadline, so asking again joins it rather than starting another.
*/
export function isSimulatorTargetDiscoveryPending(error: unknown): boolean {
function isSimulatorTargetDiscoveryPending(error: unknown): boolean {
return error instanceof AppError && error.details?.reason === TARGET_DISCOVERY_PENDING;
}

/**
* Resolves `resolveTarget`, re-asking it for as long as the only thing standing in the way is a
* discovery still in flight: the discovery's own deadline, or `resolveTarget` itself observing the
* request signal, is what eventually turns that into a real answer or a non-pending failure, which
* this always rethrows at once. `keepWaiting` is the one thing callers differ on: whether riding
* the discovery out is still worth it before the next wait slice.
*/
export async function resolveSimulatorTargetJoiningDiscovery(
resolveTarget: SimulatorSnapshotTargetResolver,
device: DeviceInfo,
appBundleId: string,
signal: AbortSignal,
keepWaiting: () => Promise<boolean> | boolean,
): Promise<SimulatorSnapshotTarget> {
for (;;) {
try {
return await resolveTarget(device, appBundleId, signal);
} catch (error) {
if (!isSimulatorTargetDiscoveryPending(error)) throw error;
if (!(await keepWaiting())) throw error;
}
}
}

async function resolveSimulatorSnapshotTarget(
device: DeviceInfo,
appBundleId: string,
Expand Down
Loading