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
24 changes: 24 additions & 0 deletions packages/contracts/src/application-lifecycle-interaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -380,3 +380,27 @@ export async function invokeApplicationClose(
}
await interactor.close(app);
}

/**
* The lifecycle binding of an owner whose interactor drives open and close directly: resolve the
* local interactor for the device, then dispatch through {@link bindDirectApplicationLifecycle}.
*/
export function bindLocalDirectApplicationLifecycle(
params: Readonly<{
owner: string;
openTargetIdentity: DirectOpenTargetIdentity;
host: Readonly<{ resolve: LocalApplicationLifecycleInteractorResolver }>;
device: DeviceInfo;
signal: AbortSignal;
}>,
): ApplicationLifecycleRuntimeOperations {
return bindDirectApplicationLifecycle({
owner: params.owner,
openTargetIdentity: params.openTargetIdentity,
binding: bindLocalApplicationLifecycleInteractor({
device: params.device,
signal: params.signal,
resolveInteractor: params.host.resolve,
}),
});
}
8 changes: 4 additions & 4 deletions packages/contracts/src/local-interactor-operation-set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ import { bindLocalTypeTextInteractor } from './type-text-runtime.ts';
/**
* The whole local interactor-backed operation set a pointer-driving family binds: the uniform
* one-fact-one-bind leaves the interactor catalog walks, plus the four that carry extra input of their own — gesture and touch read the
* fact map, and touch needs a pause clock. Android and Linux each held a byte-identical copy of
* this list; it is one list now, so neither family can drift from the other by forgetting a
* member. Capture stays out: an owner's snapshot mechanics are its own (Linux captures a surface
* through its host, Android drives the interactor), so there is no shared reading to make.
* fact map, and touch needs a pause clock. Android, Linux, and HarmonyOS bind this one list, so
* no pointer-driving family can drift from another by forgetting a member. Capture stays out: an
* owner's snapshot mechanics are its own (Linux captures a surface through its host, Android
* drives the interactor), so there is no shared reading to make.
*/
export function bindLocalInteractorOperationSet(
params: Readonly<{
Expand Down
23 changes: 3 additions & 20 deletions packages/platform-apple/src/runner/runner-session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
buildSimctlArgsForDevice,
runXcrun,
} from './host.ts';
import type { ExecResult } from '@agent-device/host-kit/command';
import { isApplePlatform, type DeviceInfo } from '@agent-device/kernel/device';
import type { RunnerLogicalLeaseContext } from '@agent-device/contracts/runner-lease-context';
import type { AppleRunnerLifecycleOptions } from './runner-provider.ts';
Expand Down Expand Up @@ -521,21 +520,18 @@ async function cleanupStaleSimulatorRunnerBundles(device: DeviceInfo): Promise<v

await Promise.allSettled(
IOS_RUNNER_CONTAINER_BUNDLE_IDS.map(async (bundleId) => {
const result = await uninstallStaleSimulatorRunnerBundle(device, bundleId);
if (!result || isBenignSimulatorRunnerUninstallResult(result)) {
return;
}
// Best-effort cleanup only; xcodebuild may still be able to install.
await uninstallStaleSimulatorRunnerBundle(device, bundleId);
}),
);
}

async function uninstallStaleSimulatorRunnerBundle(
device: DeviceInfo,
bundleId: string,
): Promise<ExecResult | undefined> {
): Promise<void> {
try {
return await runXcrun(buildSimctlArgsForDevice(device, ['uninstall', device.id, bundleId]), {
await runXcrun(buildSimctlArgsForDevice(device, ['uninstall', device.id, bundleId]), {
allowFailure: true,
timeoutMs: RUNNER_STALE_BUNDLE_UNINSTALL_TIMEOUT_MS,
});
Expand All @@ -550,22 +546,9 @@ async function uninstallStaleSimulatorRunnerBundle(
error: error instanceof Error ? error.message : String(error),
},
});
return undefined;
}
}

function isBenignSimulatorRunnerUninstallResult(result: ExecResult): boolean {
if (result.exitCode === 0) return true;
const output = `${result.stdout}\n${result.stderr}`.toLowerCase();
return (
output.includes('not installed') ||
output.includes('found nothing') ||
output.includes('no such file') ||
output.includes('invalid device') ||
output.includes('could not find')
);
}

/**
* The one reader of what is registered for a device: the session's state plus the one fact the
* session cannot know itself — whether its runner process is still there. `null` means nothing is
Expand Down
30 changes: 0 additions & 30 deletions packages/platform-harmonyos/src/lifecycle.ts

This file was deleted.

86 changes: 14 additions & 72 deletions packages/platform-harmonyos/src/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,11 @@ import {
} from '@agent-device/contracts/application-lifecycle-runtime';
import { backRuntimeOperationFacts } from '@agent-device/contracts/back-runtime';
import { elementTextRuntimeOperationFacts } from '@agent-device/contracts/element-text-runtime';
import {
bindLocalFocusInteractor,
focusRuntimeOperationFacts,
} from '@agent-device/contracts/focus-runtime';
import { focusRuntimeOperationFacts } from '@agent-device/contracts/focus-runtime';
import { TARGET_AUTHORED_DRAG_UNSUPPORTED_HINT } from '@agent-device/contracts/gesture-admission';
import {
bindLocalGestureInteractor,
gestureRuntimeOperationFacts,
} from '@agent-device/contracts/gesture-runtime';
import {
bindLocalScrollInteractor,
scrollRuntimeOperationFacts,
} from '@agent-device/contracts/scroll-runtime';
import { bindAdmittedLocalInteractorOperations } from '@agent-device/contracts/interactor-operation-catalog';
import { gestureRuntimeOperationFacts } from '@agent-device/contracts/gesture-runtime';
import { scrollRuntimeOperationFacts } from '@agent-device/contracts/scroll-runtime';
import { bindLocalInteractorOperationSet } from '@agent-device/contracts/local-interactor-operation-set';
import { alertRuntimeOperationFacts } from '@agent-device/contracts/alert-runtime';
import { appEventRuntimeOperationFacts } from '@agent-device/contracts/app-event-runtime';
import { settingsRuntimeOperationFacts } from '@agent-device/contracts/settings-runtime';
Expand All @@ -38,24 +29,15 @@ import {
unavailableFact,
whenAdmitted,
} from '@agent-device/contracts/platform-runtime';
import {
bindLocalScreenshotInteractor,
screenshotRuntimeOperationFacts,
} from '@agent-device/contracts/screenshot-runtime';
import { screenshotRuntimeOperationFacts } from '@agent-device/contracts/screenshot-runtime';
import { selectorObservationRuntimeOperationFacts } from '@agent-device/contracts/selector-observation-runtime';
import {
bindLocalSnapshotInteractor,
snapshotRuntimeOperationFacts,
} from '@agent-device/contracts/snapshot-runtime';
import { tvRemoteRuntimeOperationFacts } from '@agent-device/contracts/tv-remote-runtime';
import {
bindLocalTypeTextInteractor,
typeTextRuntimeOperationFacts,
} from '@agent-device/contracts/type-text-runtime';
import {
bindLocalTouchInteractor,
touchRuntimeOperationFacts,
} from '@agent-device/contracts/touch-runtime';
import { typeTextRuntimeOperationFacts } from '@agent-device/contracts/type-text-runtime';
import { touchRuntimeOperationFacts } from '@agent-device/contracts/touch-runtime';
import { viewportRuntimeOperationFacts } from '@agent-device/contracts/viewport-runtime';
import type { DeviceInfo } from '@agent-device/kernel/device';
import { createHarmonyAppLogRuntime } from './logs/runtime.ts';
Expand All @@ -65,7 +47,7 @@ import {
harmonyScreenRecordingFacts,
} from './recording/runtime.ts';
import { readHarmonyAppState } from './app-state.ts';
import { bindHarmonyApplicationLifecycle } from './lifecycle.ts';
import { bindLocalDirectApplicationLifecycle } from '@agent-device/contracts/application-lifecycle-interaction';
import {
createHarmonyAppDeploymentOperations,
harmonyAppDeploymentFacts,
Expand Down Expand Up @@ -347,62 +329,20 @@ export function createHarmonyPlatformRuntime(host: PlatformRuntimeHost): Platfor
resolveInteractor: host.localInteractors.resolve,
})
: {}),
...(facts.operations.captureScreenshot.available
? bindLocalScreenshotInteractor({
device: request.device,
signal: request.scope.signal,
resolveInteractor: host.localInteractors.resolve,
})
: {}),
...(facts.operations.focusPoint.available
? bindLocalFocusInteractor({
device: request.device,
signal: request.scope.signal,
resolveInteractor: host.localInteractors.resolve,
})
: {}),
...(facts.operations.typeText.available
? bindLocalTypeTextInteractor({
device: request.device,
signal: request.scope.signal,
resolveInteractor: host.localInteractors.resolve,
})
: {}),
...bindLocalGestureInteractor({
device: request.device,
signal: request.scope.signal,
facts: facts.operations,
resolveInteractor: host.localInteractors.resolve,
}),
...(facts.operations.scrollDirection.available
? bindLocalScrollInteractor({
device: request.device,
signal: request.scope.signal,
resolveInteractor: host.localInteractors.resolve,
})
: {}),
...bindAdmittedLocalInteractorOperations({
...bindLocalInteractorOperationSet({
device: request.device,
signal: request.scope.signal,
resolveInteractor: host.localInteractors.resolve,
facts: facts.operations,
pause: async (milliseconds) =>
await host.clock.sleep(milliseconds, request.scope.signal),
}),
...whenAdmitted(facts.operations.perfFrames, () =>
createHarmonyPerfOperations({
resolveHost: () => host.perf.harmony,
device: request.device,
}),
),
...whenAdmitted(facts.operations.tapPoint, () =>
bindLocalTouchInteractor({
device: request.device,
signal: request.scope.signal,
resolveInteractor: host.localInteractors.resolve,
facts: facts.operations,
pause: async (milliseconds) =>
await host.clock.sleep(milliseconds, request.scope.signal),
}),
),
listApps: async (input: { device: DeviceInfo; filter: 'all' | 'user-installed' }) => {
request.scope.signal.throwIfAborted();
const { listHarmonyApps } = await import('./app-lifecycle.ts');
Expand All @@ -411,7 +351,9 @@ export function createHarmonyPlatformRuntime(host: PlatformRuntimeHost): Platfor
).map((app) => ({ id: app.package, name: app.name }));
},
...availableApplicationLifecycleOperations(
bindHarmonyApplicationLifecycle({
bindLocalDirectApplicationLifecycle({
owner: 'HarmonyOS',
openTargetIdentity: 'bundle-id',
host: host.localInteractors,
device: request.device,
signal: request.scope.signal,
Expand Down
30 changes: 0 additions & 30 deletions packages/platform-linux/src/lifecycle.ts

This file was deleted.

6 changes: 4 additions & 2 deletions packages/platform-linux/src/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import { typeTextRuntimeOperationFacts } from '@agent-device/contracts/type-text
import { touchRuntimeOperationFacts } from '@agent-device/contracts/touch-runtime';
import type { DeviceInfo } from '@agent-device/kernel/device';
import { AppError } from '@agent-device/kernel/errors';
import { bindLinuxApplicationLifecycle } from './lifecycle.ts';
import { bindLocalDirectApplicationLifecycle } from '@agent-device/contracts/application-lifecycle-interaction';

const supported = Object.freeze({ available: true } as const);
const linuxOwner = localRuntimeOwner('linux');
Expand Down Expand Up @@ -140,7 +140,9 @@ export function createLinuxPlatformRuntime(host: PlatformRuntimeHost): PlatformR
throw new AppError('UNSUPPORTED_PLATFORM', 'Linux runtime cannot bind this device');
}
const facts = linuxFacts(request.device);
const lifecycle = bindLinuxApplicationLifecycle({
const lifecycle = bindLocalDirectApplicationLifecycle({
owner: 'Linux',
openTargetIdentity: 'app-name',
host: host.localInteractors,
device: request.device,
signal: request.scope.signal,
Expand Down
30 changes: 0 additions & 30 deletions packages/platform-vega/src/lifecycle.ts

This file was deleted.

6 changes: 4 additions & 2 deletions packages/platform-vega/src/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { gestureRuntimeOperationFacts } from '@agent-device/contracts/gesture-ru
import { tvRemoteRuntimeOperationFacts } from '@agent-device/contracts/tv-remote-runtime';
import type { DeviceInfo } from '@agent-device/kernel/device';
import { AppError } from '@agent-device/kernel/errors';
import { bindVegaApplicationLifecycle } from './lifecycle.ts';
import { bindLocalDirectApplicationLifecycle } from '@agent-device/contracts/application-lifecycle-interaction';

const vegaOwner = localRuntimeOwner('vega');
const lifecycleAvailable = Object.freeze({ available: true } as const);
Expand Down Expand Up @@ -63,7 +63,9 @@ export function createVegaPlatformRuntime(host: PlatformRuntimeHost): PlatformRu
throw new AppError('UNSUPPORTED_PLATFORM', 'Vega runtime cannot bind this device');
}
const facts = vegaFacts(request.device);
const lifecycle = bindVegaApplicationLifecycle({
const lifecycle = bindLocalDirectApplicationLifecycle({
owner: 'Vega',
openTargetIdentity: 'app-name',
host: host.localInteractors,
device: request.device,
signal: request.scope.signal,
Expand Down
Loading
Loading