Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import fs from 'node:fs';
import path from 'node:path';
import { test } from 'vitest';
import type { DeviceInfo } from '@agent-device/kernel/device';
import { IOS_DEVICECTL_MIN_TIMEOUT_SECONDS } from '../config.ts';
import {
launchCoreDeviceApp,
parseIosDeviceDetailsPayload,
Expand Down Expand Up @@ -486,6 +487,44 @@ test('the CoreDevice backend publishes the device report', async () => {
assert.equal(readiness.developerDiskImage, 'available');
});

test.each([
[250, '5'],
[4999, '5'],
[5000, '5'],
[5001, '6'],
[10000, '10'],
])(
'CoreDevice tunnel lookup keeps a %i ms host budget with CLI timeout %s',
async (budget, cliTimeout) => {
let observedCliTimeout: string | undefined;
let observedHostTimeout: number | undefined;
const tunnel = await withAppleToolProvider(
createLocalAppleToolProvider({
runCommand: async (_cmd, args, options) => {
observedCliTimeout = args[args.indexOf('--timeout') + 1];
observedHostTimeout = options?.timeoutMs;
if (Number(observedCliTimeout) < IOS_DEVICECTL_MIN_TIMEOUT_SECONDS) {
return {
exitCode: 64,
stdout: '',
stderr: "Error: Please specify a 'timeout' value between 5 and 9223372036854775807",
};
}
const outputPath = jsonOutputPath(args);
if (outputPath) fs.writeFileSync(outputPath, DEVICE_INFO_DETAILS_TEXT);
return { exitCode: 0, stdout: '', stderr: '' };
},
}),
async () =>
await resolveIosPhysicalDeviceControl(IOS_DEVICE).resolveTunnel(IOS_DEVICE, budget),
);

assert.deepEqual(tunnel, { tunnelIp: 'fd00:0000:0000::1' });
assert.equal(observedCliTimeout, cliTimeout);
assert.equal(observedHostTimeout, budget);
},
);

/**
* A `deviceProperties` payload together with the context it was read in. Both default to the state a
* developer disk image answer requires — tunnel up, phone booted — because #2683 treats that context
Expand Down
8 changes: 5 additions & 3 deletions packages/platform-apple/src/core/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ export const IOS_SIMCTL_LIST_TIMEOUT_MS = 60_000;
export const IOS_APP_LAUNCH_TIMEOUT_MS = 60_000;

export const IOS_DEVICECTL_TIMEOUT_MS = 20_000;
/** The smallest `--timeout` in seconds devicectl accepts; smaller values exit 64 before querying. */
export const IOS_DEVICECTL_MIN_TIMEOUT_SECONDS = 5;

export const IOS_DEVICE_INSTALL_TIMEOUT_MS = 120_000;

Expand All @@ -20,10 +22,10 @@ export const IOS_SIMULATOR_SCREENSHOT_TIMEOUT_MS = 20_000;
// request-level daemon reset. Measured probe cost is ~0.2s.
export const IOS_APPLE_DISPLAY_PROBE_TIMEOUT_MS = 5_000;
/**
* The smallest `--timeout` devicectl accepts. The hinge-angle stream never ends on its own, so
* one read costs exactly this long and the exec deadline below only guards a wedged CoreDevice.
* The hinge-angle stream never ends on its own, so one read costs the devicectl minimum
* and the exec deadline below only guards a wedged CoreDevice.
*/
export const IOS_HINGE_ANGLE_STREAM_SECONDS = 5;
export const IOS_HINGE_ANGLE_STREAM_SECONDS = IOS_DEVICECTL_MIN_TIMEOUT_SECONDS;
export const IOS_HINGE_ANGLE_TIMEOUT_MS = 20_000;
/** How many hinge reads a dispatched hinge request gets to reach its pose before the pose is refused. */
export const IOS_FOLD_POSE_SETTLE_ATTEMPTS = 4;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { DeviceInfo } from '@agent-device/kernel/device';
import { AppError } from '@agent-device/kernel/errors';
import { execFailureDetails, type ExecResult } from '@agent-device/host-kit/command';
import { IOS_DEVICECTL_MIN_TIMEOUT_SECONDS } from './config.ts';
import {
IOS_DEVICE_DEVELOPER_DISK_IMAGE_HINT,
IOS_DEVICE_DEVELOPER_MODE_OFF_HINT,
Expand Down Expand Up @@ -177,7 +178,8 @@ async function runCoreDeviceDetails(
commandTimeoutBufferMs = 0,
signal?: AbortSignal,
): Promise<CoreDeviceDetailsProbe> {
const timeoutSeconds = Math.max(1, Math.ceil(timeoutMs / 1000));
// devicectl rejects sub-minimum values; shorter budgets use the host deadline alone.
const timeoutSeconds = Math.max(IOS_DEVICECTL_MIN_TIMEOUT_SECONDS, Math.ceil(timeoutMs / 1000));
const outcome = await runIosDevicectlJsonRequest({
jsonPrefix: 'agent-device-coredevice-info',
args: [
Expand Down
Loading