Skip to content

Commit 369e095

Browse files
committed
refactor: move runtime resource mechanics out of daemon
1 parent 852c0f7 commit 369e095

6 files changed

Lines changed: 119 additions & 42 deletions

src/daemon/device-ready.ts

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { isIosFamily, type DeviceInfo } from '@agent-device/kernel/device';
2-
import { resolveIosPhysicalDeviceControl } from '../platforms/apple/core/physical-device-control.ts';
1+
import type { DeviceInfo } from '@agent-device/kernel/device';
2+
import { ensureLocalPlatformDeviceReady } from '../platform-runtime-device-ready.ts';
33
import { isActiveProviderDevice } from '../provider-device-runtime.ts';
44
import { createTtlMemo } from '../utils/ttl-memo.ts';
55

@@ -27,26 +27,8 @@ export async function ensureDeviceReady(
2727
readyCache.delete(cacheKey);
2828
}
2929

30-
if (isIosFamily(device)) {
31-
if (device.kind === 'simulator') {
32-
const { ensureBootedSimulator } = await import('../platforms/apple/core/simulator.ts');
33-
await ensureBootedSimulator(device, {
34-
deviceHub: options.deviceHub,
35-
focusExisting: options.focusExisting,
36-
onColdBootStart: options.onIosSimulatorColdBootStart,
37-
});
38-
markDeviceReady(cacheKey);
39-
return;
40-
}
41-
if (device.kind === 'device') {
42-
await resolveIosPhysicalDeviceControl(device).ensureReady(device);
43-
markDeviceReady(cacheKey);
44-
return;
45-
}
46-
}
47-
if (device.platform === 'android') {
48-
const { waitForAndroidBoot } = await import('../platforms/android/emulator-lifecycle.ts');
49-
await waitForAndroidBoot(device.id);
30+
const handled = await ensureLocalPlatformDeviceReady(device, options);
31+
if (handled) {
5032
markDeviceReady(cacheKey);
5133
}
5234
}

src/daemon/server/daemon-runtime.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ import {
3636
withDiagnosticsScope,
3737
} from '../../utils/diagnostics.ts';
3838
import { isEnvTruthy } from '../../utils/retry.ts';
39-
import { resetAndroidSnapshotHelperSessions } from '../../platforms/android/snapshot-helper.ts';
4039
import {
4140
acquireDaemonLock,
4241
parseIntegerEnv,
@@ -55,9 +54,11 @@ import {
5554
} from './transport.ts';
5655
import { prewarmPngWorker, terminatePngWorker } from '../../utils/png-worker-client.ts';
5756
import { sleep } from '../../utils/timeouts.ts';
58-
import { setRunnerLeaseOwnerStateDir } from '../../platforms/apple/core/runner-owner-state.ts';
59-
import { cleanupManagedAgentBrowserOrphans } from '../../platforms/web/agent-browser-lifecycle.ts';
60-
import { getManagedAgentBrowserStatus } from '../../platforms/web/agent-browser-tool.ts';
57+
import { configureAppleRunnerLeaseOwnerStateDir } from '../../platform-runtime-apple-runner-owner.ts';
58+
import {
59+
cleanupManagedWebRuntimeOrphans,
60+
resetAndroidSnapshotHelperRuntime,
61+
} from '../../platform-runtime-resource-cleanup.ts';
6162
import { openWebSessionNames } from '../web-session-names.ts';
6263
import {
6364
recoverAppLogResourcesAfterDaemonLock,
@@ -247,7 +248,7 @@ export async function startDaemonRuntime(
247248
const { baseDir, infoPath, lockPath, logPath, sessionsDir } = daemonPaths;
248249
const daemonServerMode = resolveDaemonServerMode(env.AGENT_DEVICE_DAEMON_SERVER_MODE);
249250
const retainArtifacts = isEnvTruthy(env.AGENT_DEVICE_RETAIN_ARTIFACTS);
250-
setRunnerLeaseOwnerStateDir(baseDir);
251+
await configureAppleRunnerLeaseOwnerStateDir(baseDir);
251252

252253
const sessionStore = new SessionStore(sessionsDir);
253254
const ownedProcessRecords = createOwnedProcessRecordStore({
@@ -479,7 +480,7 @@ export async function startDaemonRuntime(
479480
};
480481
if (!acquireDaemonLock(baseDir, lockPath, lockData)) {
481482
stderr.write('Daemon lock is held by another process; exiting.\n');
482-
setRunnerLeaseOwnerStateDir(undefined);
483+
await configureAppleRunnerLeaseOwnerStateDir(undefined);
483484
exit(0);
484485
return null;
485486
}
@@ -560,7 +561,7 @@ export async function startDaemonRuntime(
560561
closeServersBestEffort(servers);
561562
removeInfo(infoPath);
562563
releaseDaemonLock(lockPath);
563-
setRunnerLeaseOwnerStateDir(undefined);
564+
await configureAppleRunnerLeaseOwnerStateDir(undefined);
564565
exit(1);
565566
return null;
566567
}
@@ -587,7 +588,7 @@ export async function startDaemonRuntime(
587588
expiredProviderLeaseReleaser.beginShutdown();
588589
await teardownDaemonSessions();
589590
try {
590-
await resetAndroidSnapshotHelperSessions();
591+
await resetAndroidSnapshotHelperRuntime();
591592
} catch (error) {
592593
emitDiagnostic({
593594
level: 'warn',
@@ -625,7 +626,7 @@ export async function startDaemonRuntime(
625626
]);
626627
removeInfo(infoPath);
627628
releaseDaemonLock(lockPath);
628-
setRunnerLeaseOwnerStateDir(undefined);
629+
await configureAppleRunnerLeaseOwnerStateDir(undefined);
629630
exit(shutdownOptions.exitCode ?? 0);
630631
};
631632

@@ -693,10 +694,9 @@ export async function cleanupWebBrowserOrphansForDaemonStartup(params: {
693694
sessionStore: SessionStore;
694695
ownedProcessRecords?: OwnedProcessRecordStore;
695696
}): Promise<void> {
696-
const status = getManagedAgentBrowserStatus({ stateDir: params.stateDir });
697-
if (!status.installed) return;
698697
try {
699-
await cleanupManagedAgentBrowserOrphans(status, 'daemon-startup', {
698+
await cleanupManagedWebRuntimeOrphans({
699+
stateDir: params.stateDir,
700700
openWebSessionNames: openWebSessionNames(params.sessionStore),
701701
...(params.ownedProcessRecords === undefined
702702
? {}

src/daemon/session-teardown.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ import { finishLiveScreenRecording } from './screen-recording-session-resource.t
99
import { finishLiveAudioProbe } from './audio-probe-session-resource.ts';
1010
import { finishLivePerfCapture } from './perf-capture-session-resource.ts';
1111
import { openWebSessionNames } from './web-session-names.ts';
12+
import {
13+
closeManagedWebRuntimeSession,
14+
stopAndroidSnapshotHelperRuntimeForDevice,
15+
} from '../platform-runtime-resource-cleanup.ts';
1216

1317
// Android cleanup helpers and the web managed-browser provider stay behind dynamic imports: every
1418
// teardown caller pays this module's graph, while the helpers only matter when the corresponding
@@ -43,9 +47,7 @@ export async function stopSessionPerfCapture(params: {
4347

4448
export async function stopSessionAndroidSnapshotHelper(session: SessionState): Promise<void> {
4549
if (session.device.platform !== 'android') return;
46-
const { stopAndroidSnapshotHelperSessionForDevice } =
47-
await import('../platforms/android/snapshot-helper.ts');
48-
await stopAndroidSnapshotHelperSessionForDevice(session.device);
50+
await stopAndroidSnapshotHelperRuntimeForDevice(session.device);
4951
}
5052

5153
// Single source of truth for "is this a web session", shared with `shouldDispatchPlatformClose`
@@ -68,13 +70,11 @@ async function stopSessionWebBrowser(params: {
6870
}): Promise<void> {
6971
const { session, sessionName, sessionStore } = params;
7072
if (!isWebSession(session)) return;
71-
const { createAgentBrowserWebProvider } =
72-
await import('../platforms/web/agent-browser-provider.ts');
73-
await createAgentBrowserWebProvider({
74-
session: sessionName,
73+
await closeManagedWebRuntimeSession({
74+
sessionName,
7575
stateDir: sessionStore.resolveDaemonStateDir(),
7676
openWebSessionNames: () => openWebSessionNames(sessionStore),
77-
}).close();
77+
});
7878
}
7979

8080
type SessionCleanupStep = { step: string; run: () => Promise<void> };
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/** Root-composed daemon ownership input consumed by the Apple runner host. */
2+
export async function configureAppleRunnerLeaseOwnerStateDir(
3+
stateDir: string | undefined,
4+
): Promise<void> {
5+
const { setRunnerLeaseOwnerStateDir } =
6+
await import('./platforms/apple/core/runner-owner-state.ts');
7+
setRunnerLeaseOwnerStateDir(stateDir);
8+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { isIosFamily, type DeviceInfo } from '@agent-device/kernel/device';
2+
3+
export type LocalPlatformDeviceReadyOptions = {
4+
deviceHub?: boolean;
5+
focusExisting?: boolean;
6+
onIosSimulatorColdBootStart?: (device: DeviceInfo) => void;
7+
};
8+
9+
/**
10+
* Runs concrete local-platform readiness mechanics from the root composition layer.
11+
* Returns false when the platform has no readiness preparation step.
12+
*/
13+
export async function ensureLocalPlatformDeviceReady(
14+
device: DeviceInfo,
15+
options: LocalPlatformDeviceReadyOptions = {},
16+
): Promise<boolean> {
17+
if (isIosFamily(device)) {
18+
if (device.kind === 'simulator') {
19+
const { ensureBootedSimulator } = await import('./platforms/apple/core/simulator.ts');
20+
await ensureBootedSimulator(device, {
21+
deviceHub: options.deviceHub,
22+
focusExisting: options.focusExisting,
23+
onColdBootStart: options.onIosSimulatorColdBootStart,
24+
});
25+
return true;
26+
}
27+
if (device.kind === 'device') {
28+
const { resolveIosPhysicalDeviceControl } =
29+
await import('./platforms/apple/core/physical-device-control.ts');
30+
await resolveIosPhysicalDeviceControl(device).ensureReady(device);
31+
return true;
32+
}
33+
}
34+
if (device.platform === 'android') {
35+
const { waitForAndroidBoot } = await import('./platforms/android/emulator-lifecycle.ts');
36+
await waitForAndroidBoot(device.id);
37+
return true;
38+
}
39+
return false;
40+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import type { DeviceInfo } from '@agent-device/kernel/device';
2+
import type { OwnedProcessRecordStore } from './utils/owned-process-record.ts';
3+
4+
/** Focused durable-resource cleanup composed above daemon policy and concrete platforms. */
5+
export async function resetAndroidSnapshotHelperRuntime(): Promise<void> {
6+
const { resetAndroidSnapshotHelperSessions } =
7+
await import('./platforms/android/snapshot-helper.ts');
8+
await resetAndroidSnapshotHelperSessions();
9+
}
10+
11+
export async function stopAndroidSnapshotHelperRuntimeForDevice(device: DeviceInfo): Promise<void> {
12+
const { stopAndroidSnapshotHelperSessionForDevice } =
13+
await import('./platforms/android/snapshot-helper.ts');
14+
await stopAndroidSnapshotHelperSessionForDevice(device);
15+
}
16+
17+
export async function cleanupManagedWebRuntimeOrphans(params: {
18+
stateDir: string;
19+
openWebSessionNames: readonly string[];
20+
ownedProcessRecords?: OwnedProcessRecordStore;
21+
}): Promise<void> {
22+
const { getManagedAgentBrowserStatus } = await import('./platforms/web/agent-browser-tool.ts');
23+
const status = getManagedAgentBrowserStatus({ stateDir: params.stateDir });
24+
if (!status.installed) return;
25+
const { cleanupManagedAgentBrowserOrphans } =
26+
await import('./platforms/web/agent-browser-lifecycle.ts');
27+
await cleanupManagedAgentBrowserOrphans(status, 'daemon-startup', {
28+
openWebSessionNames: params.openWebSessionNames,
29+
...(params.ownedProcessRecords === undefined
30+
? {}
31+
: { ownedProcessRecords: params.ownedProcessRecords }),
32+
});
33+
}
34+
35+
export async function closeManagedWebRuntimeSession(params: {
36+
sessionName: string;
37+
stateDir: string;
38+
openWebSessionNames: () => readonly string[];
39+
}): Promise<void> {
40+
const { createAgentBrowserWebProvider } =
41+
await import('./platforms/web/agent-browser-provider.ts');
42+
await createAgentBrowserWebProvider({
43+
session: params.sessionName,
44+
stateDir: params.stateDir,
45+
openWebSessionNames: params.openWebSessionNames,
46+
}).close();
47+
}

0 commit comments

Comments
 (0)