stim 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/README.md +7 -0
  2. package/dist/{android-CC5bUer7.mjs → android-BW8i_YxU.mjs} +1 -1
  3. package/dist/{android-DnZlT3aU.mjs → android-Bcmev2s2.mjs} +86 -31
  4. package/dist/{android-xmyGBNy5.mjs → android-K63_0UfF.mjs} +77 -15
  5. package/dist/{android-cas-DGlTStWu.mjs → android-cas-D6HPZ6UQ.mjs} +6 -6
  6. package/dist/android-cas-compiler.mjs +1 -1
  7. package/dist/{app-install-oZIQRbdD.mjs → app-install-NFxUahnH.mjs} +25 -17
  8. package/dist/{build-lock-vE45ZzJZ.mjs → build-lock-BsVoLDaq.mjs} +6 -6
  9. package/dist/{build-slots-CgacA9zQ.mjs → build-slots-D0b6sJyo.mjs} +3 -3
  10. package/dist/{cache-manifest-oeH18ERr.mjs → cache-manifest-Cu4_Znw9.mjs} +1 -1
  11. package/dist/cache-manifest.mjs +1 -1
  12. package/dist/cli.mjs +13 -13
  13. package/dist/collector-run.d.mts +1 -0
  14. package/dist/collector-run.mjs +6 -6
  15. package/dist/{config-D7uu8Gbq.mjs → config-CMtIaTk9.mjs} +1 -1
  16. package/dist/{deps-64wVnpZc.mjs → deps-BLBsTNDS.mjs} +2 -2
  17. package/dist/{dev-client-Bvky1hya.mjs → dev-client-RpvXxCXV.mjs} +72 -9
  18. package/dist/{device-DJKv5Qej.mjs → device-CoCPmazS.mjs} +5 -5
  19. package/dist/{device-lease-D-G3SgVe.mjs → device-lease-Dpik41RY.mjs} +3 -3
  20. package/dist/{device-pool-BlHXnYYs.mjs → device-pool-B5pEVa3t.mjs} +3 -3
  21. package/dist/{device-remote-CCgkG82J.mjs → device-remote-E81KAdXf.mjs} +8 -8
  22. package/dist/{doctor-DlVxTtMq.mjs → doctor-CqPeMsSm.mjs} +19 -26
  23. package/dist/{doctor-BRF9aAJ3.mjs → doctor-D_CX7ib5.mjs} +10 -10
  24. package/dist/{error-diagnostics-B5IJwv3-.mjs → error-diagnostics-Dw8Gn3yw.mjs} +8 -8
  25. package/dist/{exec-bsN9MJXb.mjs → exec-CyylIdq9.mjs} +4 -2
  26. package/dist/{gc-DhXdgSdt.mjs → gc-B7KkDinF.mjs} +39 -25
  27. package/dist/{guide-DrhrDBo5.mjs → guide-BS2Xc24o.mjs} +220 -36
  28. package/dist/{ios-CK1Vrk6Q.mjs → ios-B7KLgIp5.mjs} +38 -26
  29. package/dist/{ios-BZxKPuqP.mjs → ios-D1h1Di39.mjs} +79 -10
  30. package/dist/{ios-device-CYbuRNXR.mjs → ios-device-D3pzTKQE.mjs} +1 -1
  31. package/dist/{ios-device-CdSm0GDw.mjs → ios-device-xNt0Lg6v.mjs} +2 -2
  32. package/dist/{logs-Dxoy16Qv.mjs → logs-DcX9AvRF.mjs} +3 -3
  33. package/dist/{metro-B01H3EuD.mjs → metro-COslRK_F.mjs} +3 -3
  34. package/dist/{ownership-D8HmSv2E.mjs → ownership-CGPart6T.mjs} +3 -3
  35. package/dist/{ownership-B9QIdQOw.mjs → ownership-CYOyqxUo.mjs} +4 -4
  36. package/dist/{ownership-claim-CeJLEpjD.mjs → ownership-claim-7zRxxTfZ.mjs} +1 -1
  37. package/dist/{project-COj6nzzh.mjs → project-BBTVABUz.mjs} +2 -2
  38. package/dist/{reclaim-BPvN2HlP.mjs → reclaim-C5Jmuahx.mjs} +9 -8
  39. package/dist/{reload-DYZEBbZy.mjs → reload-Z8nKifAk.mjs} +7 -7
  40. package/dist/{remote-cache-DOi5-zLj.mjs → remote-cache-DDmJ1Roq.mjs} +73 -13
  41. package/dist/{server-bare-BYHqEeyw.mjs → server-bare-DEk37Mz2.mjs} +7 -2
  42. package/dist/{server-expo-BEMr_m6p.mjs → server-expo-C3ktAwUG.mjs} +4 -4
  43. package/dist/server-expo-Drd7ksHu.mjs +2 -0
  44. package/dist/{settings-BSwrY4FZ.mjs → settings-DwU1_U_q.mjs} +26 -5
  45. package/dist/{start-ByCylYtB.mjs → start-BrY4EuIg.mjs} +10 -10
  46. package/dist/{state-IYEY-zi8.mjs → state-CZ-ER-8d.mjs} +1 -1
  47. package/dist/{stats-wqxPm7Tv.mjs → stats-BCWEVLrM.mjs} +1 -1
  48. package/dist/{stats-DZ4Q5J-7.mjs → stats-BsF15Bti.mjs} +3 -3
  49. package/dist/{status-Dia0XTqG.mjs → status-DFI1Hfpg.mjs} +46 -16
  50. package/dist/{stop-D6o5zAdm.mjs → stop-B7DR4awK.mjs} +2 -2
  51. package/dist/{stop-BApyaF21.mjs → stop-Btg770vV.mjs} +9 -9
  52. package/dist/supervisor-run.mjs +4 -4
  53. package/dist/{workspace-process-lock-CxOO9-pf.mjs → workspace-process-lock-BKfdys1q.mjs} +1 -1
  54. package/dist/{worktree-DxEmUI_n.mjs → worktree-BPTbI-LN.mjs} +16 -16
  55. package/dist/{worktree-Dx3ZC5lS.mjs → worktree-C79xx_kM.mjs} +7 -3
  56. package/dist/{xcode-CDB6fytw.mjs → xcode-DeKCribl.mjs} +190 -63
  57. package/package.json +5 -5
  58. package/shim/bundle-response.cjs +5 -0
  59. package/dist/server-expo-rb0k-0w7.mjs +0 -2
package/README.md CHANGED
@@ -58,6 +58,13 @@ one native platform is in scope; shared project checks still run. Doctor also
58
58
  prints the running CLI version and the `stim` installation resolved from PATH,
59
59
  and flags a resolved installation that is older than another available one.
60
60
 
61
+ For parallel iOS work, SimSlim is recommended as an optional way to reduce
62
+ simulator background services and memory use. Run `stim guide lifecycle simslim`
63
+ to review the service tradeoffs and configure a profile. Doctor recommends the
64
+ setup without installing or enabling it. When a simulator cannot start
65
+ processes, Stim bounds the wait and reports observed host memory pressure when
66
+ available; free memory before retrying. A timeout alone does not prove OOM.
67
+
61
68
  For stale Android CMake launcher findings, stop native builds and run
62
69
  `stim doctor --fix --platform android` in the affected checkout. It clears
63
70
  affected ignored, untracked generated `.cxx` configurations in the app and
@@ -1,4 +1,4 @@
1
- import { t as getExecutor } from "./exec-bsN9MJXb.mjs";
1
+ import { t as getExecutor } from "./exec-CyylIdq9.mjs";
2
2
  //#region src/collector/android.ts
3
3
  const LEVEL_BY_LETTER = {
4
4
  V: "debug",
@@ -1,29 +1,29 @@
1
- import { t as getExecutor } from "./exec-bsN9MJXb.mjs";
2
- import { C as withConfigLock, S as upsertProject, f as getProject, g as loadConfig, l as getConcurrencyLimits, y as saveConfig } from "./config-D7uu8Gbq.mjs";
1
+ import { t as getExecutor } from "./exec-CyylIdq9.mjs";
2
+ import { C as withConfigLock, S as upsertProject, b as setDevice, f as getProject, g as loadConfig, l as getConcurrencyLimits, y as saveConfig } from "./config-CMtIaTk9.mjs";
3
3
  import { r as createNdjsonWriter } from "./ndjson-DcAtEx_K.mjs";
4
- import { D as workspaceLogsDir, T as workspaceDir, _ as writeWorkspaceState, g as writeWorkspaceLaunch, n as MODE_EXPO, p as readWorkspaceState, t as MODE_BARE, v as emulatorLogFile } from "./state-IYEY-zi8.mjs";
5
- import { C as removeTemporaryEntry, S as makeTemporaryDirectory, g as repoRoot, s as gitCommonDir } from "./worktree-Dx3ZC5lS.mjs";
6
- import { a as findProjectRoot, c as projectShortcut, i as detectIsExpo, n as detectAndroidPackage, r as detectBundleId, t as appProjectProblem } from "./project-COj6nzzh.mjs";
4
+ import { D as workspaceLogsDir, T as workspaceDir, _ as writeWorkspaceState, g as writeWorkspaceLaunch, n as MODE_EXPO, p as readWorkspaceState, t as MODE_BARE, v as emulatorLogFile } from "./state-CZ-ER-8d.mjs";
5
+ import { C as removeTemporaryEntry, S as makeTemporaryDirectory, g as repoRoot, s as gitCommonDir } from "./worktree-C79xx_kM.mjs";
6
+ import { a as findProjectRoot, c as projectShortcut, i as detectIsExpo, n as detectAndroidPackage, r as detectBundleId, t as appProjectProblem } from "./project-BBTVABUz.mjs";
7
7
  import { r as waitForChild, t as createLineReader } from "./process-output-C7IlIzd7.mjs";
8
- import { i as resolveProjectMetro, r as pidExists } from "./metro-B01H3EuD.mjs";
9
- import { n as verifyCollectorOwnership, r as readCollectors } from "./ownership-D8HmSv2E.mjs";
10
- import { a as easAuthNote, c as loadProjectProvider, d as resolveEasCliBin, f as resolveRemote, i as checkEasAuth, n as UPLOAD_TIMEOUT_MS, o as exitAfterFlush, p as uploadRemote, r as cacheLevel, s as isEasAuthFailureText, t as RESOLVE_TIMEOUT_MS, x as parkedMaxSetting } from "./remote-cache-DOi5-zLj.mjs";
11
- import { A as settingFile, B as compilerCacheFallbackMessage, E as remoteDeviceSettingError, F as unknownSettingKeys, H as resolveOptimizations, L as detectProviders, M as settingShapeErrors, O as resolveCacheProviderConfig, P as tunnelModeSetting, T as remoteAndroidSetting, V as optimizationBuildProfile, a as SETTING_SHAPE_REMEDY, d as cacheProviderSettingError, k as resolveSettings, r as REMOTE_DEVICE_BACKENDS, s as androidAvdConfigSettingError, u as androidDataPartitionSizeGbSettingError, w as publicUrlSetting, z as artifactCachePolicy } from "./settings-BSwrY4FZ.mjs";
8
+ import { i as resolveProjectMetro, r as pidExists } from "./metro-COslRK_F.mjs";
9
+ import { n as verifyCollectorOwnership, r as readCollectors } from "./ownership-CGPart6T.mjs";
10
+ import { a as easAuthNote, c as loadProjectProvider, d as resolveEasCliBin, f as resolveRemote, i as checkEasAuth, n as UPLOAD_TIMEOUT_MS, o as exitAfterFlush, p as uploadRemote, r as cacheLevel, s as isEasAuthFailureText, t as RESOLVE_TIMEOUT_MS, x as parkedMaxSetting } from "./remote-cache-DDmJ1Roq.mjs";
11
+ import { A as resolveSettings, B as artifactCachePolicy, D as remoteDeviceSettingError, E as remoteAndroidSetting, F as tunnelModeSetting, H as optimizationBuildProfile, I as unknownSettingKeys, N as settingShapeErrors, R as detectProviders, T as publicUrlSetting, U as resolveOptimizations, V as compilerCacheFallbackMessage, a as SETTING_SHAPE_REMEDY, d as cacheProviderSettingError, j as settingFile, k as resolveCacheProviderConfig, r as REMOTE_DEVICE_BACKENDS, s as androidAvdConfigSettingError, u as androidDataPartitionSizeGbSettingError, x as metroWarmupUrlSetting } from "./settings-DwU1_U_q.mjs";
12
12
  import { _ as launchErrorPreview, c as launchErrorReport, g as stepTimer, h as stepClock, i as formatDuration, l as phaseLine, n as appReadinessMessage, p as shortHash } from "./command-output-8wHMEde0.mjs";
13
- import { D as verifyAndroidReleaseLaunch, E as unverifiedLaunchLines, O as verifyLaunch, S as launchAndroidReleaseApp, _ as installConflictKind, c as RELEASE_VERIFY_WAIT_MS, d as androidDevClientUrl, g as installAndroidApp, h as deviceShellArg, i as LAUNCH_BUNDLING, n as DEFAULT_METRO_PORT, o as LAUNCH_FATAL, s as LAUNCH_UNVERIFIED, t as ADB_INSTALL_TIMEOUT_MS, u as androidAppProcess, x as launchAndroidApp } from "./app-install-oZIQRbdD.mjs";
14
- import { C as untrackedNativeFiles, D as readAssetManifest, E as compareAssetManifests, S as untrackedMissLine, T as captureAssetManifest, _ as providerUploadOutcome, b as storeBuild, c as buildCacheKey, d as filesystemBuildCapability, f as fingerprintDiffRecord, g as providerDownloadPath, h as prepareProviderDownloadDir, i as releaseBuildLock, l as describeFingerprintMiss, m as fingerprintProject, n as acquireBuildLock, o as takeoverLine, p as fingerprintDiffSuffix, s as waitForBuild, t as WAIT_CEILING_MS, v as refingerprintAfterMutation, w as assetDiffReason, x as storedAssetManifest, y as resolveBuild } from "./build-lock-vE45ZzJZ.mjs";
15
- import { r as claimFailure } from "./ownership-claim-CeJLEpjD.mjs";
16
- import { a as lostLine, c as releaseLeaseOnSignal, i as leaseExpiryText, l as runLease, n as DEBUG_VERIFY_STEP_MS, o as lostRefusal, r as acquireRunLease, s as parseDeviceWait, t as selectFromPool, u as waitFlagConflict } from "./device-pool-BlHXnYYs.mjs";
17
- import { r as releaseBuildSlot, t as acquireBuildSlot } from "./build-slots-CgacA9zQ.mjs";
18
- import { c as ensureWorkspaceStorageSafely, d as noMetroMessage, f as noMetroRemedy, i as detectEntryFile, n as pickDevClientScheme, o as needsPrebuild, p as resolveMetroWithRetry, s as runPrebuild, t as devClientScheme, u as launchOutcomeRecord } from "./dev-client-Bvky1hya.mjs";
19
- import { a as statsProjectKey, i as recordRunStats, n as readRunEstimates, t as createRunRecorder } from "./stats-wqxPm7Tv.mjs";
20
- import { E as physicalDeviceModel, O as probeEmulatorSerial, _ as listAdbDevices, a as androidSystemImageAbi, b as memoizeEmulatorProbe, f as emulatorDiskSpaceRemedy, h as findBuildTool, i as androidPoolNoCandidatesRefusal, j as resolvePhysicalDevice, k as resetAdoptedAvd, m as extractEmulatorFailure, n as androidHome, p as emulatorFailureRemedy, r as androidPoolCandidates, t as androidDeviceAbi, y as listInstalledSystemImages } from "./android-xmyGBNy5.mjs";
21
- import { S as unknownAndroidSystemImageRefusal, _ as checkDeviceCapacity, b as ensureOwnedDevice, f as startBuildHeartbeat, p as tailLines, r as HEARTBEAT_INTERVAL_MS, y as ensureBooted } from "./xcode-CDB6fytw.mjs";
22
- import { a as ensureMetroReachable, b as ownedSessionName, l as resolveRemoteContext, n as binOnPath, o as ensureRemoteBootOwned, s as remoteAndroidDeps } from "./device-remote-CCgkG82J.mjs";
23
- import { t as cleanLine } from "./server-expo-BEMr_m6p.mjs";
24
- import { a as buildAndroid, c as CCACHE_NOT_RUN, f as resolveCcache, l as CCACHE_UNAVAILABLE, n as resolveAndroidCas, o as productFlavorRefusal, p as formatDiagnostic, r as resolveAndroidCompilerCache, s as readProductFlavors, u as ccacheActivityLine } from "./android-cas-DGlTStWu.mjs";
13
+ import { D as verifyAndroidReleaseLaunch, E as unverifiedLaunchLines, O as verifyLaunch, S as launchAndroidReleaseApp, _ as installConflictKind, c as RELEASE_VERIFY_WAIT_MS, d as androidDevClientUrl, g as installAndroidApp, h as deviceShellArg, i as LAUNCH_BUNDLING, l as VERIFY_TIMEOUT_MS, n as DEFAULT_METRO_PORT, o as LAUNCH_FATAL, s as LAUNCH_UNVERIFIED, t as ADB_INSTALL_TIMEOUT_MS, u as androidAppProcess, x as launchAndroidApp } from "./app-install-NFxUahnH.mjs";
14
+ import { C as untrackedNativeFiles, D as readAssetManifest, E as compareAssetManifests, S as untrackedMissLine, T as captureAssetManifest, _ as providerUploadOutcome, b as storeBuild, c as buildCacheKey, d as filesystemBuildCapability, f as fingerprintDiffRecord, g as providerDownloadPath, h as prepareProviderDownloadDir, i as releaseBuildLock, l as describeFingerprintMiss, m as fingerprintProject, n as acquireBuildLock, o as takeoverLine, p as fingerprintDiffSuffix, s as waitForBuild, t as WAIT_CEILING_MS, v as refingerprintAfterMutation, w as assetDiffReason, x as storedAssetManifest, y as resolveBuild } from "./build-lock-BsVoLDaq.mjs";
15
+ import { r as claimFailure } from "./ownership-claim-7zRxxTfZ.mjs";
16
+ import { a as lostLine, c as releaseLeaseOnSignal, i as leaseExpiryText, l as runLease, n as DEBUG_VERIFY_STEP_MS, o as lostRefusal, r as acquireRunLease, s as parseDeviceWait, t as selectFromPool, u as waitFlagConflict } from "./device-pool-B5pEVa3t.mjs";
17
+ import { r as releaseBuildSlot, t as acquireBuildSlot } from "./build-slots-D0b6sJyo.mjs";
18
+ import { c as ensureWorkspaceStorageSafely, d as noMetroMessage, f as noMetroRemedy, h as warmMetro, i as detectEntryFile, n as pickDevClientScheme, o as needsPrebuild, p as resolveMetroWithRetry, s as runPrebuild, t as devClientScheme, u as launchOutcomeRecord } from "./dev-client-RpvXxCXV.mjs";
19
+ import { a as statsProjectKey, i as recordRunStats, n as readRunEstimates, t as createRunRecorder } from "./stats-BCWEVLrM.mjs";
20
+ import { C as memoizeEmulatorProbe, L as waitForBoot, M as resetAdoptedAvd, N as resolveOwnedAvdSerial, P as resolvePhysicalDevice, _ as findBuildTool, a as androidSystemImageAbi, g as extractEmulatorFailure, h as emulatorFailureRemedy, i as androidPoolNoCandidatesRefusal, j as probeEmulatorSerial, k as physicalDeviceModel, m as emulatorDiskSpaceRemedy, n as androidHome, r as androidPoolCandidates, t as androidDeviceAbi, x as listInstalledSystemImages, y as listAdbDevices } from "./android-K63_0UfF.mjs";
21
+ import { C as unknownAndroidSystemImageRefusal, _ as AvdRecoveryError, b as ensureBooted, f as startBuildHeartbeat, p as tailLines, r as HEARTBEAT_INTERVAL_MS, v as checkDeviceCapacity, x as ensureOwnedDevice } from "./xcode-DeKCribl.mjs";
22
+ import { a as ensureMetroReachable, b as ownedSessionName, l as resolveRemoteContext, n as binOnPath, o as ensureRemoteBootOwned, s as remoteAndroidDeps } from "./device-remote-E81KAdXf.mjs";
23
+ import { t as cleanLine } from "./server-expo-C3ktAwUG.mjs";
24
+ import { a as buildAndroid, c as CCACHE_NOT_RUN, f as resolveCcache, l as CCACHE_UNAVAILABLE, n as resolveAndroidCas, o as productFlavorRefusal, p as formatDiagnostic, r as resolveAndroidCompilerCache, s as readProductFlavors, u as ccacheActivityLine } from "./android-cas-D6HPZ6UQ.mjs";
25
25
  import { t as spawnEntry } from "./spawn-entry-nCE6m770.mjs";
26
- import { a as printNativeCrashReport, r as captureNativeCrashes, t as errorDiagnostics } from "./error-diagnostics-B5IJwv3-.mjs";
26
+ import { a as printNativeCrashReport, r as captureNativeCrashes, t as errorDiagnostics } from "./error-diagnostics-Dw8Gn3yw.mjs";
27
27
  import { existsSync, mkdirSync, openSync, readFileSync, readdirSync, rmSync } from "node:fs";
28
28
  import { InvalidArgumentError } from "commander";
29
29
  import { basename, dirname, join, relative } from "node:path";
@@ -840,7 +840,7 @@ async function startCollector({ root, serial, packageName, spawn, kill, alive =
840
840
  }
841
841
  //#endregion
842
842
  //#region src/commands/android/launch.ts
843
- async function verifyAndroidRun({ root, release, remoteRelease, remoteDevice, verifyReleaseLaunched, verifyLaunched, serial, androidPackage, variant, metroCheck, logsDir, launchedAt, metroPort, isExpo, physical, scheme, component = null, phase }) {
843
+ async function verifyAndroidRun({ root, release, remoteRelease, remoteDevice, verifyReleaseLaunched, verifyLaunched, serial, androidPackage, variant, metroCheck, logsDir, launchedAt, metroPort, isExpo, physical, device, scheme, component = null, phase }) {
844
844
  const readNativeCrashes = () => remoteDevice ? [] : captureNativeCrashes({
845
845
  root,
846
846
  platform: "android",
@@ -872,7 +872,12 @@ async function verifyAndroidRun({ root, release, remoteRelease, remoteDevice, ve
872
872
  phase("", chalk.yellow("A release process exited before readiness. Run `stim logs --errors` for captured crash reports, or `stim logs --source device` for the full device output."));
873
873
  return { state: LAUNCH_FATAL };
874
874
  }
875
+ const newEmulator = Boolean(device.created && device.owned && !remoteDevice);
876
+ const timeoutMs = newEmulator ? 6e4 : VERIFY_TIMEOUT_MS;
877
+ if (metroCheck && newEmulator) phase("verify", "waiting up to 60s for bundle load (new emulator)");
875
878
  const verification = metroCheck ? await verifyLaunched({
879
+ timeoutMs,
880
+ requireBundleResponse: true,
876
881
  onReadinessPending: () => phase("readiness", "waiting for app readiness (up to 30s after bundle load)"),
877
882
  logsDir,
878
883
  since: launchedAt,
@@ -947,7 +952,29 @@ async function verifyAndroidRun({ root, release, remoteRelease, remoteDevice, ve
947
952
  })) phase("", chalk.yellow(line));
948
953
  return { state: LAUNCH_UNVERIFIED };
949
954
  }
950
- async function finishAndroidRun({ lease, releaseLease, root, json, metroCheck, useBuildCache, variant, release, isExpo, metroPort, logsDir, emuLog, device, physical, remoteDevice, bootPromise, bootDuration, apkPath, androidPackage: initialPackage, swapDir, record, waitedForBuild, ccache, uploadPending, providerUpload, providerName, remote, abandonedRemote: remoteWasAbandoned, started, startedAt, writer, phase, fail, readApkPackage, install, launch, launchRelease, resolveDevClientScheme, verifyLaunched, verifyReleaseLaunched, spawn, kill, pidAlive, verifyCollector, writeLaunch, writeState, now, out, emit, recordRun }) {
955
+ async function resolveInstallSerial({ root, device, physical, remoteDevice, serial, resolveAvdSerial, waitForDeviceBoot, phase }) {
956
+ if (physical || remoteDevice || !device.owned || !device.avdName) return serial;
957
+ const resolved = resolveAvdSerial(device.avdName, { timeoutMs: 5e3 });
958
+ if (!resolved.serial) throw new Error(`Could not verify a running owned AVD ${device.avdName} before installation.`);
959
+ if (resolved.serial !== serial) {
960
+ phase("device", `${device.avdName} changed serial (${serial} -> ${resolved.serial}); checking readiness before installation`);
961
+ const ready = await waitForDeviceBoot(resolved.serial, 6e4, { commandTimeoutMs: 5e3 });
962
+ if (!ready.ok) throw new Error(`Emulator ${resolved.serial} never reported boot completion. Diagnostic: ${JSON.stringify(ready.diagnostic)}`);
963
+ if (resolveAvdSerial(device.avdName, { timeoutMs: 5e3 }).serial !== resolved.serial) throw new Error(`Could not verify AVD ${device.avdName} still runs on ${resolved.serial} after checking readiness.`);
964
+ phase("device", `reopen agent-device on ${resolved.serial}`);
965
+ }
966
+ const consolePort = Number(resolved.serial.replace(/^emulator-/, ""));
967
+ withConfigLock(() => {
968
+ const current = loadConfig()?.projects?.[root]?.platforms?.android;
969
+ if (!current?.owned || current.avdName !== device.avdName) throw new Error("The owned emulator assignment changed before installation.");
970
+ if (current.consolePort !== consolePort) setDevice(root, PLATFORM, {
971
+ ...current,
972
+ consolePort
973
+ });
974
+ });
975
+ return resolved.serial;
976
+ }
977
+ async function finishAndroidRun({ lease, releaseLease, root, json, metroCheck, useBuildCache, variant, release, isExpo, metroPort, logsDir, emuLog, device, physical, remoteDevice, bootPromise, resolveAvdSerial, waitForDeviceBoot, bootDuration, apkPath, androidPackage: initialPackage, swapDir, record, waitedForBuild, ccache, uploadPending, providerUpload, providerName, remote, abandonedRemote: remoteWasAbandoned, started, startedAt, writer, phase, fail, readApkPackage, install, launch, launchRelease, resolveDevClientScheme, verifyLaunched, verifyReleaseLaunched, spawn, kill, pidAlive, verifyCollector, writeLaunch, writeState, now, out, emit, recordRun }) {
951
978
  let androidPackage = initialPackage;
952
979
  let leaseWarned = false;
953
980
  const raiseLeaseFor = (boundMs, beforeInstall) => {
@@ -976,7 +1003,21 @@ async function finishAndroidRun({ lease, releaseLease, root, json, metroCheck, u
976
1003
  logPath: diag.logPath ? displayPath(root, diag.logPath) : null
977
1004
  });
978
1005
  }
979
- const serial = booted.serial;
1006
+ let serial;
1007
+ try {
1008
+ serial = await resolveInstallSerial({
1009
+ root,
1010
+ device,
1011
+ physical,
1012
+ remoteDevice,
1013
+ serial: booted.serial,
1014
+ resolveAvdSerial,
1015
+ waitForDeviceBoot,
1016
+ phase
1017
+ });
1018
+ } catch (error) {
1019
+ return fail(NO_DEVICE, error instanceof Error ? error.message : String(error), "Run `stim status` to inspect the owned AVD, then retry `stim android`; the APK was not installed.");
1020
+ }
980
1021
  phase("device", physical ? `${device.deviceName || serial} (${serial}) connected, not owned by Stim` : `${device.avdName || serial} (${serial}) ${device.adopted || device.adoptionPending ? "adopted" : "booted"} ${bootDuration()}`);
981
1022
  const packageFromApk = readApkPackage(apkPath);
982
1023
  if (packageFromApk && androidPackage && packageFromApk !== androidPackage) phase("install", chalk.dim(`applicationId ${packageFromApk} (from the APK; project files say ${androidPackage})`));
@@ -1159,6 +1200,7 @@ async function finishAndroidRun({ lease, releaseLease, root, json, metroCheck, u
1159
1200
  metroPort,
1160
1201
  isExpo,
1161
1202
  physical,
1203
+ device,
1162
1204
  scheme,
1163
1205
  component: launched.component ?? null,
1164
1206
  phase
@@ -1263,7 +1305,7 @@ function registerAndroid(program) {
1263
1305
  })).ok) process.exit(1);
1264
1306
  });
1265
1307
  }
1266
- function resolveRunAndroidOptions({ root, json = false, remoteDevice: commandRemoteBackend = null, resolveSettingsFor = resolveSettings, resolveRemoteDeviceContext = resolveRemoteContext, remoteDeviceDeps: makeRemoteDeviceDeps = remoteAndroidDeps, resolveEasBin = resolveEasCliBin, ensureMetroReachable: ensureRemoteMetro = ensureMetroReachable, ensureRemoteBootOwned: ensureRemoteOwned = ensureRemoteBootOwned, detectRemoteProviders = detectProviders, metroCheck = true, useBuildCache = true, variant: variantFlag = null, systemImage: systemImageFlag = null, device: deviceFlag = null, wait: waitFlag = void 0, waitConflict = false, acquireLease = acquireRunLease, makeRunLease = runLease, selectPool = selectFromPool, onLeaseSignal = releaseLeaseOnSignal, listDevices = listAdbDevices, deviceModel = physicalDeviceModel, deviceAbi = androidDeviceAbi, isEmulatorDevice = probeEmulatorSerial, readApkPackage = (apkPath) => apkPackage(dumpApkManifest(apkPath)), getLimits = getConcurrencyLimits, checkCapacity = checkDeviceCapacity, acquireSlot = acquireBuildSlot, releaseSlot = releaseBuildSlot, ensureDevice = ensureOwnedDevice, listSystemImages = listInstalledSystemImages, ensureDeviceBooted = ensureBooted, resolveMetro = resolveProjectMetro, resolveMetroRetrying = resolveMetroWithRetry, readState = readWorkspaceState, pidAlive = pidExists, verifyCollector = verifyCollectorOwnership, verifyLaunched = verifyLaunch, ensureStorage = ensureWorkspaceStorageSafely, fingerprint = fingerprintProject, untracked = untrackedNativeFiles, resolveCached = resolveBuild, storeCached = storeBuild, storedAssets = storedAssetManifest, captureAssets = captureAssetManifest, acquireLock = acquireBuildLock, releaseLock = releaseBuildLock, waitForBuild: waitForBuild$1 = waitForBuild, loadProvider = loadProjectProvider, easAuth = checkEasAuth, resolveRemoteBuild = resolveRemote, uploadRemoteBuild = uploadRemote, resolveCacheProvider = resolveCacheProviderConfig, loadCacheProviderModule = loadCacheProvider, needsPrebuildFor = needsPrebuild, prebuild = runPrebuild, build = buildAndroid, ccacheFor = resolveCcache, install = installAndroidApp, launch = launchAndroidApp, launchRelease = launchAndroidReleaseApp, verifyReleaseLaunched = verifyAndroidReleaseLaunch, swapApk = swapApkBundle, resolveDevClientScheme = androidDevClientScheme, spawn = (cmd, args, opts) => getExecutor().spawn(cmd, args, opts), kill = (pid, signal) => process.kill(pid, signal), createWriter = createNdjsonWriter, writeLaunch = writeWorkspaceLaunch, writeState = writeWorkspaceState, recordStats = recordRunStats, readEstimates = readRunEstimates, now = Date.now, out = (line) => console.error(line), emit = (line) => console.log(line) } = {}) {
1308
+ function resolveRunAndroidOptions({ root, json = false, remoteDevice: commandRemoteBackend = null, resolveSettingsFor = resolveSettings, resolveRemoteDeviceContext = resolveRemoteContext, remoteDeviceDeps: makeRemoteDeviceDeps = remoteAndroidDeps, resolveEasBin = resolveEasCliBin, ensureMetroReachable: ensureRemoteMetro = ensureMetroReachable, ensureRemoteBootOwned: ensureRemoteOwned = ensureRemoteBootOwned, detectRemoteProviders = detectProviders, metroCheck = true, useBuildCache = true, variant: variantFlag = null, systemImage: systemImageFlag = null, device: deviceFlag = null, wait: waitFlag = void 0, waitConflict = false, acquireLease = acquireRunLease, makeRunLease = runLease, selectPool = selectFromPool, onLeaseSignal = releaseLeaseOnSignal, listDevices = listAdbDevices, deviceModel = physicalDeviceModel, deviceAbi = androidDeviceAbi, isEmulatorDevice = probeEmulatorSerial, readApkPackage = (apkPath) => apkPackage(dumpApkManifest(apkPath)), getLimits = getConcurrencyLimits, checkCapacity = checkDeviceCapacity, acquireSlot = acquireBuildSlot, releaseSlot = releaseBuildSlot, ensureDevice = ensureOwnedDevice, listSystemImages = listInstalledSystemImages, ensureDeviceBooted = ensureBooted, resolveAvdSerial = resolveOwnedAvdSerial, waitForDeviceBoot = waitForBoot, resolveMetro = resolveProjectMetro, warmMetro: prewarmMetro = warmMetro, resolveMetroRetrying = resolveMetroWithRetry, readState = readWorkspaceState, pidAlive = pidExists, verifyCollector = verifyCollectorOwnership, verifyLaunched = verifyLaunch, ensureStorage = ensureWorkspaceStorageSafely, fingerprint = fingerprintProject, untracked = untrackedNativeFiles, resolveCached = resolveBuild, storeCached = storeBuild, storedAssets = storedAssetManifest, captureAssets = captureAssetManifest, acquireLock = acquireBuildLock, releaseLock = releaseBuildLock, waitForBuild: waitForBuild$1 = waitForBuild, loadProvider = loadProjectProvider, easAuth = checkEasAuth, resolveRemoteBuild = resolveRemote, uploadRemoteBuild = uploadRemote, resolveCacheProvider = resolveCacheProviderConfig, loadCacheProviderModule = loadCacheProvider, needsPrebuildFor = needsPrebuild, prebuild = runPrebuild, build = buildAndroid, ccacheFor = resolveCcache, install = installAndroidApp, launch = launchAndroidApp, launchRelease = launchAndroidReleaseApp, verifyReleaseLaunched = verifyAndroidReleaseLaunch, swapApk = swapApkBundle, resolveDevClientScheme = androidDevClientScheme, spawn = (cmd, args, opts) => getExecutor().spawn(cmd, args, opts), kill = (pid, signal) => process.kill(pid, signal), createWriter = createNdjsonWriter, writeLaunch = writeWorkspaceLaunch, writeState = writeWorkspaceState, recordStats = recordRunStats, readEstimates = readRunEstimates, now = Date.now, out = (line) => console.error(line), emit = (line) => console.log(line) } = {}) {
1267
1309
  return {
1268
1310
  root,
1269
1311
  json,
@@ -1298,7 +1340,10 @@ function resolveRunAndroidOptions({ root, json = false, remoteDevice: commandRem
1298
1340
  ensureDevice,
1299
1341
  listSystemImages,
1300
1342
  ensureDeviceBooted,
1343
+ resolveAvdSerial,
1344
+ waitForDeviceBoot,
1301
1345
  resolveMetro,
1346
+ prewarmMetro,
1302
1347
  resolveMetroRetrying,
1303
1348
  readState,
1304
1349
  pidAlive,
@@ -1343,7 +1388,7 @@ function resolveRunAndroidOptions({ root, json = false, remoteDevice: commandRem
1343
1388
  };
1344
1389
  }
1345
1390
  async function runAndroid(options = {}) {
1346
- let { root, json, commandRemoteBackend, resolveSettingsFor, resolveRemoteDeviceContext, makeRemoteDeviceDeps, resolveEasBin, ensureRemoteMetro, ensureRemoteOwned, detectRemoteProviders, metroCheck, useBuildCache: requestedBuildCache, variantFlag, systemImageFlag, deviceFlag, waitFlag, waitConflict, acquireLease, makeRunLease, selectPool, onLeaseSignal, listDevices, deviceModel, deviceAbi, isEmulatorDevice, readApkPackage, getLimits, checkCapacity, acquireSlot, releaseSlot, ensureDevice, listSystemImages, ensureDeviceBooted, resolveMetro, resolveMetroRetrying, readState, pidAlive, verifyCollector, verifyLaunched, ensureStorage, fingerprint, untracked, resolveCached, storeCached, storedAssets, captureAssets, acquireLock, releaseLock, waitForBuild, loadProvider, easAuth, resolveRemoteBuild, uploadRemoteBuild, resolveCacheProvider, loadCacheProviderModule, needsPrebuildFor, prebuild, build, ccacheFor, install, launch, launchRelease, verifyReleaseLaunched, swapApk, resolveDevClientScheme, spawn, kill, createWriter, writeLaunch, writeState, recordStats, readEstimates, now, out, emit } = resolveRunAndroidOptions(options);
1391
+ let { root, json, commandRemoteBackend, resolveSettingsFor, resolveRemoteDeviceContext, makeRemoteDeviceDeps, resolveEasBin, ensureRemoteMetro, ensureRemoteOwned, detectRemoteProviders, metroCheck, useBuildCache: requestedBuildCache, variantFlag, systemImageFlag, deviceFlag, waitFlag, waitConflict, acquireLease, makeRunLease, selectPool, onLeaseSignal, listDevices, deviceModel, deviceAbi, isEmulatorDevice, readApkPackage, getLimits, checkCapacity, acquireSlot, releaseSlot, ensureDevice, listSystemImages, ensureDeviceBooted, resolveAvdSerial, waitForDeviceBoot, resolveMetro, prewarmMetro, resolveMetroRetrying, readState, pidAlive, verifyCollector, verifyLaunched, ensureStorage, fingerprint, untracked, resolveCached, storeCached, storedAssets, captureAssets, acquireLock, releaseLock, waitForBuild, loadProvider, easAuth, resolveRemoteBuild, uploadRemoteBuild, resolveCacheProvider, loadCacheProviderModule, needsPrebuildFor, prebuild, build, ccacheFor, install, launch, launchRelease, verifyReleaseLaunched, swapApk, resolveDevClientScheme, spawn, kill, createWriter, writeLaunch, writeState, recordStats, readEstimates, now, out, emit } = resolveRunAndroidOptions(options);
1347
1392
  const started = now();
1348
1393
  const startedAt = new Date(started).toISOString();
1349
1394
  const projectProblem = appProjectProblem(root);
@@ -1673,7 +1718,8 @@ async function runAndroid(options = {}) {
1673
1718
  const diag = noDeviceDiagnostic({
1674
1719
  reason: `Could not ensure an owned Android emulator: ${err?.message || err}`,
1675
1720
  logFile: emuLog,
1676
- remedy: "Check that JAVA_HOME and ANDROID_HOME are set correctly, and that an arm64 system image is installed (`sdkmanager \"system-images;android-36;google_apis;arm64-v8a\"`)."
1721
+ localEmulator: !(err instanceof AvdRecoveryError),
1722
+ remedy: err instanceof AvdRecoveryError ? err.remedy : "Check that JAVA_HOME and ANDROID_HOME are set correctly, and that an arm64 system image is installed (`sdkmanager \"system-images;android-36;google_apis;arm64-v8a\"`)."
1677
1723
  });
1678
1724
  return fail(NO_DEVICE, diag.message, diag.remedy, {
1679
1725
  lines: diag.lines,
@@ -1825,6 +1871,13 @@ async function runAndroid(options = {}) {
1825
1871
  return true;
1826
1872
  }
1827
1873
  const runFromFingerprint = async () => {
1874
+ if (metroCheck && metroPort !== null && optimizations.metroWarmup) prewarmMetro({
1875
+ port: metroPort,
1876
+ platform: "android",
1877
+ isExpo,
1878
+ appId: androidPackage,
1879
+ bundleUrl: metroWarmupUrlSetting(settings, "android")
1880
+ });
1828
1881
  if (!await resolveInitialFingerprint()) return phaseFailure;
1829
1882
  let remote = null;
1830
1883
  let abandonedRemote = false;
@@ -1910,7 +1963,7 @@ async function runAndroid(options = {}) {
1910
1963
  releasedWait = null;
1911
1964
  const holder = attempt.held;
1912
1965
  const who = holder.projectRoot || "another workspace";
1913
- phase("build", `${who} is already building ${shortHash(hash)} (pid ${holder.pid})${holder.logFile ? ` -- tail ${holder.logFile}` : ""}`);
1966
+ phase("build", `${who} is already building ${shortHash(hash)} (pid ${holder.pid})${holder.logFile ? ` -- tail ${holder.logFile}` : ""} -- stim guide lifecycle concurrency`);
1914
1967
  let waited = null;
1915
1968
  try {
1916
1969
  const ceilingMs = WAIT_CEILING_MS - (now() - waitStarted);
@@ -1942,7 +1995,7 @@ async function runAndroid(options = {}) {
1942
1995
  pid: holder.pid,
1943
1996
  ms: waited.waitedMs
1944
1997
  };
1945
- phase("build", `waited ${formatDuration(waited.waitedMs)} for ${who}'s build -> installed from cache`);
1998
+ phase("build", `waited ${formatDuration(waited.waitedMs)} for ${who}'s build -> installed from cache -- stim guide lifecycle concurrency`);
1946
1999
  } else if (waited?.lockReleased) {
1947
2000
  failedHolder = void 0;
1948
2001
  releasedWait = {
@@ -2049,7 +2102,7 @@ async function runAndroid(options = {}) {
2049
2102
  phase("cache", `hit ${shortHash(storeHash)} (post-prebuild key)`);
2050
2103
  if (releasedWait) {
2051
2104
  waitedForBuild = releasedWait.facts;
2052
- phase("build", `waited ${formatDuration(waitedForBuild.ms)} for ${releasedWait.who}'s build -> installed from cache`);
2105
+ phase("build", `waited ${formatDuration(waitedForBuild.ms)} for ${releasedWait.who}'s build -> installed from cache -- stim guide lifecycle concurrency`);
2053
2106
  }
2054
2107
  }
2055
2108
  }
@@ -2223,6 +2276,8 @@ async function runAndroid(options = {}) {
2223
2276
  physical,
2224
2277
  remoteDevice,
2225
2278
  bootPromise,
2279
+ resolveAvdSerial,
2280
+ waitForDeviceBoot,
2226
2281
  bootDuration: () => bootDuration,
2227
2282
  apkPath,
2228
2283
  androidPackage,
@@ -1,7 +1,7 @@
1
- import { t as getExecutor } from "./exec-bsN9MJXb.mjs";
2
- import { r as pidExists } from "./metro-B01H3EuD.mjs";
3
- import { c as androidDataPartitionSizeBytes } from "./settings-BSwrY4FZ.mjs";
4
- import { existsSync, mkdirSync, openSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "fs";
1
+ import { t as getExecutor } from "./exec-CyylIdq9.mjs";
2
+ import { r as pidExists } from "./metro-COslRK_F.mjs";
3
+ import { c as androidDataPartitionSizeBytes } from "./settings-DwU1_U_q.mjs";
4
+ import { existsSync, lstatSync, mkdirSync, openSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "fs";
5
5
  import { homedir } from "os";
6
6
  import { dirname, isAbsolute, join, resolve } from "path";
7
7
  //#region src/sim/android.ts
@@ -11,6 +11,7 @@ const ANDROID_ABIS = /* @__PURE__ */ new Set([
11
11
  "x86",
12
12
  "x86_64"
13
13
  ]);
14
+ const DEFAULT_AVD_DEVICE = "pixel_6";
14
15
  function androidHome() {
15
16
  return process.env.ANDROID_HOME || process.env.ANDROID_SDK_ROOT || join(homedir(), "Library", "Android", "sdk");
16
17
  }
@@ -115,7 +116,7 @@ function createOwnedAvd(label, { systemImage } = {}) {
115
116
  throw new Error(`No ${arch} Android system image is installed. Install one, e.g.: sdkmanager "system-images;android-36;google_apis;${arch}"`);
116
117
  }
117
118
  const avdName = ownedAvdName(label);
118
- getExecutor().run(`echo no | ${androidTool("avdmanager")} create avd -n "${avdName}" -k "${pick.pkg}"`);
119
+ getExecutor().run(`echo no | ${androidTool("avdmanager")} create avd -n "${avdName}" -k "${pick.pkg}" --device "${DEFAULT_AVD_DEVICE}"`);
119
120
  return {
120
121
  avdName,
121
122
  systemImage: pick.pkg
@@ -334,6 +335,71 @@ function parseAvdRootIni(contents) {
334
335
  relativePath
335
336
  };
336
337
  }
338
+ function avdIniPaths(root, ini) {
339
+ const parsed = parseAvdRootIni(ini);
340
+ return [parsed.relativePath && !isAbsolute(parsed.relativePath) ? resolve(dirname(root), parsed.relativePath) : null, parsed.path && isAbsolute(parsed.path) ? parsed.path : null].filter((candidate) => candidate !== null);
341
+ }
342
+ function avdStorageRoots() {
343
+ const env = process.env;
344
+ return [...new Set([
345
+ env.ANDROID_AVD_HOME,
346
+ env.ANDROID_EMULATOR_HOME ? join(env.ANDROID_EMULATOR_HOME, "avd") : null,
347
+ env.ANDROID_USER_HOME ? join(env.ANDROID_USER_HOME, "avd") : null,
348
+ env.ANDROID_SDK_HOME ? join(env.ANDROID_SDK_HOME, ".android", "avd") : null,
349
+ env.ANDROID_SDK_HOME ? join(env.ANDROID_SDK_HOME, "avd") : null,
350
+ join(homedir(), ".android", "avd")
351
+ ].filter((value) => Boolean(value)))];
352
+ }
353
+ function avdPathExists(path) {
354
+ try {
355
+ lstatSync(path);
356
+ return true;
357
+ } catch (error) {
358
+ if (error.code === "ENOENT") return false;
359
+ throw error;
360
+ }
361
+ }
362
+ function listOrphanedAvdDirectories(avdName) {
363
+ const roots = avdStorageRoots();
364
+ const stores = roots.filter(avdPathExists).map((root) => ({
365
+ root,
366
+ canonicalRoot: realpathSync(root),
367
+ names: readdirSync(root)
368
+ }));
369
+ const registered = /* @__PURE__ */ new Set();
370
+ for (const { root, canonicalRoot, names } of stores) for (const name of names.filter((entry) => entry.endsWith(".ini"))) {
371
+ const path = join(root, name);
372
+ const ini = readFileSync(path, "utf8");
373
+ const targets = /* @__PURE__ */ new Set([...avdIniPaths(root, ini), ...avdIniPaths(canonicalRoot, ini)]);
374
+ if (!targets.size) throw new Error(`Cannot verify AVD registration at ${path}; its data was kept.`);
375
+ for (const target of targets) try {
376
+ registered.add(realpathSync(target));
377
+ } catch (error) {
378
+ const code = error.code;
379
+ if (code !== "ENOENT" && code !== "ENOTDIR") throw error;
380
+ }
381
+ }
382
+ const orphaned = [];
383
+ for (const { canonicalRoot, names: entries } of stores) {
384
+ const names = avdName ? [`${avdName}.avd`] : entries;
385
+ for (const entry of names) {
386
+ if (!/^stim-[A-Za-z0-9._-]+\.avd$/.test(entry)) continue;
387
+ const name = entry.slice(0, -4);
388
+ if (roots.some((candidate) => avdPathExists(join(candidate, `${name}.ini`)))) continue;
389
+ const directory = join(canonicalRoot, entry);
390
+ if (registered.has(directory) || !avdPathExists(directory)) continue;
391
+ const stat = lstatSync(directory);
392
+ if (!stat.isDirectory() || stat.isSymbolicLink() || realpathSync(directory) !== directory) throw new Error(`Cannot verify AVD data at ${directory}; it was kept.`);
393
+ if (!orphaned.some((candidate) => candidate.directory === directory)) orphaned.push({
394
+ name,
395
+ directory,
396
+ dev: stat.dev,
397
+ ino: stat.ino
398
+ });
399
+ }
400
+ }
401
+ return orphaned;
402
+ }
337
403
  function ownedAvdDirectory(avdName, { env = process.env, home = homedir(), readFile = (path) => readFileSync(path, "utf8"), realpath = realpathSync, isDirectory = (path) => statSync(path).isDirectory() } = {}) {
338
404
  if (!/^stim-[A-Za-z0-9._-]+$/.test(avdName)) return null;
339
405
  const roots = [
@@ -350,15 +416,10 @@ function ownedAvdDirectory(avdName, { env = process.env, home = homedir(), readF
350
416
  if (code === "ENOENT" || code === "ENOTDIR") continue;
351
417
  return null;
352
418
  }
353
- const parsed = parseAvdRootIni(ini);
354
- const candidates = [parsed.relativePath && !isAbsolute(parsed.relativePath) ? resolve(dirname(root), parsed.relativePath) : null, parsed.path && isAbsolute(parsed.path) ? parsed.path : null];
355
- for (const candidate of candidates) {
356
- if (!candidate) continue;
357
- try {
358
- const canonical = realpath(candidate);
359
- if (isDirectory(canonical)) return canonical;
360
- } catch {}
361
- }
419
+ for (const candidate of avdIniPaths(root, ini)) try {
420
+ const canonical = realpath(candidate);
421
+ if (isDirectory(canonical)) return canonical;
422
+ } catch {}
362
423
  return null;
363
424
  }
364
425
  return null;
@@ -415,6 +476,7 @@ function configureNewOwnedAvd(avdName, { dataPartitionSizeGb, avdConfig = {} },
415
476
  function avdPoolConfiguration(dataPartitionSizeGb, avdConfig) {
416
477
  return JSON.stringify(Object.entries({
417
478
  ...avdConfig,
479
+ "hw.device.name": DEFAULT_AVD_DEVICE,
418
480
  "disk.dataPartition.size": String(androidDataPartitionSizeBytes(dataPartitionSizeGb))
419
481
  }).toSorted(([a], [b]) => a.localeCompare(b)));
420
482
  }
@@ -662,4 +724,4 @@ function resolveOwnedAvdSerial(avdName, { timeoutMs } = {}) {
662
724
  return { notRunning: true };
663
725
  }
664
726
  //#endregion
665
- export { resolveOwnedAvdSerial as A, ownedAvdMatchesConfiguration as C, pickDefaultSystemImage as D, physicalDeviceModel as E, shutdownAndroidEmulator as M, waitForAndroidEmulatorShutdown as N, probeEmulatorSerial as O, waitForBoot as P, ownedAvdDirectory as S, ownedAvdSystemImage as T, listAdbDevices as _, androidSystemImageAbi as a, memoizeEmulatorProbe as b, bootAndroidEmulator as c, deleteAvd as d, emulatorDiskSpaceRemedy as f, getAvdNameForSerial as g, findBuildTool as h, androidPoolNoCandidatesRefusal as i, resolvePhysicalDevice as j, resetAdoptedAvd as k, configureNewOwnedAvd as l, extractEmulatorFailure as m, androidHome as n, assertOwnedAvdStopped as o, emulatorFailureRemedy as p, androidPoolCandidates as r, avdPoolConfiguration as s, androidDeviceAbi as t, createOwnedAvd as u, listAvds as v, ownedAvdName as w, nextConsolePort as x, listInstalledSystemImages as y };
727
+ export { pickDefaultSystemImage as A, memoizeEmulatorProbe as C, ownedAvdName as D, ownedAvdMatchesConfiguration as E, shutdownAndroidEmulator as F, waitForAndroidEmulatorShutdown as I, waitForBoot as L, resetAdoptedAvd as M, resolveOwnedAvdSerial as N, ownedAvdSystemImage as O, resolvePhysicalDevice as P, listOrphanedAvdDirectories as S, ownedAvdDirectory as T, findBuildTool as _, androidSystemImageAbi as a, listAvds as b, avdPoolConfiguration as c, configureNewOwnedAvd as d, createOwnedAvd as f, extractEmulatorFailure as g, emulatorFailureRemedy as h, androidPoolNoCandidatesRefusal as i, probeEmulatorSerial as j, physicalDeviceModel as k, avdStorageRoots as l, emulatorDiskSpaceRemedy as m, androidHome as n, assertOwnedAvdStopped as o, deleteAvd as p, androidPoolCandidates as r, avdPathExists as s, androidDeviceAbi as t, bootAndroidEmulator as u, getAvdNameForSerial as v, nextConsolePort as w, listInstalledSystemImages as x, listAdbDevices as y };
@@ -1,11 +1,11 @@
1
- import { t as getExecutor } from "./exec-bsN9MJXb.mjs";
2
- import { k as withDirLock, u as getConfigDir } from "./config-D7uu8Gbq.mjs";
3
- import { r as register } from "./cache-manifest-oeH18ERr.mjs";
4
- import { D as workspaceLogsDir, x as sharedCcache, y as ensureWorkspaceStorage } from "./state-IYEY-zi8.mjs";
1
+ import { t as getExecutor } from "./exec-CyylIdq9.mjs";
2
+ import { k as withDirLock, u as getConfigDir } from "./config-CMtIaTk9.mjs";
3
+ import { r as register } from "./cache-manifest-Cu4_Znw9.mjs";
4
+ import { D as workspaceLogsDir, x as sharedCcache, y as ensureWorkspaceStorage } from "./state-CZ-ER-8d.mjs";
5
5
  import { n as stripAnsi, r as waitForChild, t as createLineReader } from "./process-output-C7IlIzd7.mjs";
6
6
  import { l as phaseLine } from "./command-output-8wHMEde0.mjs";
7
- import { n as androidHome } from "./android-xmyGBNy5.mjs";
8
- import { f as startBuildHeartbeat, r as HEARTBEAT_INTERVAL_MS } from "./xcode-CDB6fytw.mjs";
7
+ import { n as androidHome } from "./android-K63_0UfF.mjs";
8
+ import { f as startBuildHeartbeat, r as HEARTBEAT_INTERVAL_MS } from "./xcode-DeKCribl.mjs";
9
9
  import { accessSync, constants, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, symlinkSync, writeFileSync } from "node:fs";
10
10
  import { createHash, randomUUID } from "node:crypto";
11
11
  import { dirname, isAbsolute, join, relative } from "node:path";
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { t as getExecutor } from "./exec-bsN9MJXb.mjs";
2
+ import { t as getExecutor } from "./exec-CyylIdq9.mjs";
3
3
  import { appendFileSync, existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
4
4
  import { createHash, randomUUID } from "node:crypto";
5
5
  import { join } from "node:path";
@@ -1,8 +1,8 @@
1
- import { t as getExecutor } from "./exec-bsN9MJXb.mjs";
1
+ import { t as getExecutor } from "./exec-CyylIdq9.mjs";
2
2
  import { o as parseNdjsonText } from "./ndjson-DcAtEx_K.mjs";
3
- import { t as DEV_MENU_LAUNCH_ARGS } from "./ios-device-CYbuRNXR.mjs";
3
+ import { t as DEV_MENU_LAUNCH_ARGS } from "./ios-device-D3pzTKQE.mjs";
4
4
  import { s as isAppLaunchError } from "./command-output-8wHMEde0.mjs";
5
- import { C as uninstallIosApp, f as listUserApps } from "./ios-BZxKPuqP.mjs";
5
+ import { l as iosSimulatorFailureAdvice, p as listUserApps, w as uninstallIosApp } from "./ios-D1h1Di39.mjs";
6
6
  import { closeSync, lstatSync, openSync, readFileSync, readSync, readdirSync } from "node:fs";
7
7
  import { createHash } from "node:crypto";
8
8
  import { join } from "node:path";
@@ -157,6 +157,10 @@ const IOS_DEV_MENU_OFF_KEYS = ["EXDevMenuShowsAtLaunch", "EXDevMenuShowFloatingA
157
157
  const DEV_CLIENT_ONBOARDING_QUERY = "disableOnboarding=1";
158
158
  const DEV_CLIENT_DISABLE_FAB_QUERY = "disableFab=1";
159
159
  const ANDROID_DISABLE_AUTO_LAUNCH_EXTRA = "EXDevMenuDisableAutoLaunch";
160
+ function describeIosSimulatorFailure(error, exec) {
161
+ const detail = describe(error);
162
+ return error?.code === "ETIMEDOUT" ? `${detail}. ${iosSimulatorFailureAdvice(exec)}` : detail;
163
+ }
160
164
  function installIosApp({ udid, appPath, bundleId = null, devClientScheme = null, proveInstalled = true }, { exec = null, now = null } = {}) {
161
165
  const e = exec || getExecutor();
162
166
  const artifactStartedAt = now?.();
@@ -171,12 +175,15 @@ function installIosApp({ udid, appPath, bundleId = null, devClientScheme = null,
171
175
  "install",
172
176
  udid,
173
177
  appPath
174
- ]);
178
+ ], {
179
+ timeoutMs: 3e5,
180
+ killSignal: "SIGKILL"
181
+ });
175
182
  } catch (err) {
176
183
  return {
177
184
  failed: true,
178
185
  code: INSTALL_ERROR,
179
- reason: `simctl install failed for ${appPath}: ${describe(err)}`
186
+ reason: `simctl install failed for ${appPath}: ${describeIosSimulatorFailure(err, e)}`
180
187
  };
181
188
  }
182
189
  const artifactFinishedAt = now?.();
@@ -193,7 +200,7 @@ function installIosApp({ udid, appPath, bundleId = null, devClientScheme = null,
193
200
  key,
194
201
  "-bool",
195
202
  "false"
196
- ]);
203
+ ], { timeoutMs: 6e4 });
197
204
  for (const key of iosSchemeApprovalKeys(bundleId, devClientScheme)) e.runFile("xcrun", [
198
205
  "simctl",
199
206
  "spawn",
@@ -204,12 +211,12 @@ function installIosApp({ udid, appPath, bundleId = null, devClientScheme = null,
204
211
  key,
205
212
  "-string",
206
213
  bundleId
207
- ]);
214
+ ], { timeoutMs: 6e4 });
208
215
  } catch (err) {
209
216
  return {
210
217
  failed: true,
211
218
  code: INSTALL_ERROR,
212
- reason: `Installed ${bundleId}, but could not prepare the dev client: ${describe(err)}`
219
+ reason: `Installed ${bundleId}, but could not prepare the dev client: ${describeIosSimulatorFailure(err, e)}`
213
220
  };
214
221
  }
215
222
  const devClientPreparationDurationMs = now && preparationStartedAt !== void 0 ? now() - preparationStartedAt : void 0;
@@ -318,12 +325,12 @@ function launchIosApp({ udid, bundleId, metroPort, devClientScheme = null, conso
318
325
  bundleId,
319
326
  "RCT_jsLocation",
320
327
  jsLocationValue(metroPort)
321
- ]);
328
+ ], { timeoutMs: 6e4 });
322
329
  } catch (err) {
323
330
  return {
324
331
  failed: true,
325
332
  code: LAUNCH_ERROR,
326
- reason: `Could not point ${bundleId} at Metro port ${metroPort} (defaults write RCT_jsLocation): ${describe(err)}`
333
+ reason: `Could not point ${bundleId} at Metro port ${metroPort} (defaults write RCT_jsLocation): ${describeIosSimulatorFailure(err, e)}`
327
334
  };
328
335
  }
329
336
  if (devClientScheme) {
@@ -349,7 +356,7 @@ function launchIosApp({ udid, bundleId, metroPort, devClientScheme = null, conso
349
356
  "openurl",
350
357
  udid,
351
358
  url
352
- ]);
359
+ ], { timeoutMs: 6e4 });
353
360
  return {
354
361
  ok: true,
355
362
  mode: "openurl",
@@ -360,7 +367,7 @@ function launchIosApp({ udid, bundleId, metroPort, devClientScheme = null, conso
360
367
  return {
361
368
  failed: true,
362
369
  code: LAUNCH_ERROR,
363
- reason: `simctl openurl ${url} failed: ${describe(err)}`
370
+ reason: `simctl openurl ${url} failed: ${describeIosSimulatorFailure(err, e)}`
364
371
  };
365
372
  }
366
373
  }
@@ -377,7 +384,7 @@ function launchIosApp({ udid, bundleId, metroPort, devClientScheme = null, conso
377
384
  return {
378
385
  failed: true,
379
386
  code: LAUNCH_ERROR,
380
- reason: `simctl launch ${bundleId} failed: ${describe(err)}`
387
+ reason: `simctl launch ${bundleId} failed: ${describeIosSimulatorFailure(err, e)}`
381
388
  };
382
389
  }
383
390
  }
@@ -893,7 +900,7 @@ function nativeLaunchFailure({ readCrashes, since, platform, processAlive, mode,
893
900
  waitedMs
894
901
  } : null;
895
902
  }
896
- async function verifyLaunch({ logsDir, since, metroPort = null, platform = null, mode = null, timeoutMs = VERIFY_TIMEOUT_MS, stabilityMs = STABILITY_WINDOW_MS, pollMs = VERIFY_POLL_MS, readRecords = null, readDeviceRecords = null, readClientRecords = null, readNativeCrashes = null, processAlive = null, onReadinessPending = null, now = Date.now, sleep = (ms) => new Promise((r) => setTimeout(r, ms)) } = {}) {
903
+ async function verifyLaunch({ logsDir, since, metroPort = null, platform = null, mode = null, requireBundleResponse, timeoutMs = VERIFY_TIMEOUT_MS, stabilityMs = STABILITY_WINDOW_MS, pollMs = VERIFY_POLL_MS, readRecords = null, readDeviceRecords = null, readClientRecords = null, readNativeCrashes = null, processAlive = null, onReadinessPending = null, now = Date.now, sleep = (ms) => new Promise((r) => setTimeout(r, ms)) } = {}) {
897
904
  const read = readRecords || (() => readMetroRecords(logsDir));
898
905
  const readDevice = readDeviceRecords || (() => readNdjson(logsDir, "device.ndjson"));
899
906
  const readClient = readClientRecords || (() => readNdjson(logsDir, "client.ndjson"));
@@ -909,6 +916,7 @@ async function verifyLaunch({ logsDir, since, metroPort = null, platform = null,
909
916
  let nextCrashCheck = startedAt + 1e3;
910
917
  while (true) {
911
918
  const metroRecords = read().filter((record) => after(record, since));
919
+ const bundleRecords = metroRecords.filter((record) => !requireBundleResponse || String(record.event).startsWith("bundle_response_"));
912
920
  const deviceRecords = readDevice().filter((record) => after(record, since));
913
921
  const clientRecords = readClient().filter((record) => after(record, since));
914
922
  if (now() >= nextCrashCheck) {
@@ -924,14 +932,14 @@ async function verifyLaunch({ logsDir, since, metroPort = null, platform = null,
924
932
  if (crash) return crash;
925
933
  }
926
934
  if (deliveryId === null) {
927
- const request = metroRecords.find((record) => record.event === "bundle_response_started" && typeof record.requestId === "string" && isBundleProof(record, since, platform));
935
+ const request = bundleRecords.find((record) => record.event === "bundle_response_started" && typeof record.requestId === "string" && isBundleProof(record, since, platform));
928
936
  if (request) {
929
937
  deliveryId = request.requestId;
930
938
  proof = null;
931
939
  stabilityDeadline = null;
932
940
  }
933
941
  }
934
- for (const record of metroRecords) {
942
+ for (const record of bundleRecords) {
935
943
  if (isBundleProof(record, since, platform)) activity = record;
936
944
  const completed = deliveryId === null ? isBundleReadyProof(record, since, platform) : record.event === "bundle_response_finished" && record.requestId === deliveryId && isBundleProof(record, since, platform);
937
945
  if (!proof && completed) {
@@ -960,7 +968,7 @@ async function verifyLaunch({ logsDir, since, metroPort = null, platform = null,
960
968
  }
961
969
  }
962
970
  const ready = pendingAt !== null && signals.some((record) => Number(record.ts) >= pendingAt && Number(record.ts) <= readinessDeadline && appReadinessSignal(record, platform) === "ready");
963
- const bundleErrors = metroRecords.filter((record) => isFatalLaunchError(record, platform) || deliveryId !== null && record.event === "bundle_response_failed" && record.requestId === deliveryId && isBundleProof(record, since, platform));
971
+ const bundleErrors = bundleRecords.filter((record) => isFatalLaunchError(record, platform) || deliveryId !== null && record.event === "bundle_response_failed" && record.requestId === deliveryId && isBundleProof(record, since, platform));
964
972
  if (bundleErrors.length) {
965
973
  const alive = processAlive ? processAlive() : null;
966
974
  const fatalRecord = bundleErrors[bundleErrors.length - 1];
@@ -1,9 +1,9 @@
1
- import { t as getExecutor } from "./exec-bsN9MJXb.mjs";
2
- import { u as getConfigDir } from "./config-D7uu8Gbq.mjs";
3
- import { r as register } from "./cache-manifest-oeH18ERr.mjs";
4
- import { b as sharedBuildCache } from "./state-IYEY-zi8.mjs";
1
+ import { t as getExecutor } from "./exec-CyylIdq9.mjs";
2
+ import { u as getConfigDir } from "./config-CMtIaTk9.mjs";
3
+ import { r as register } from "./cache-manifest-Cu4_Znw9.mjs";
4
+ import { b as sharedBuildCache } from "./state-CZ-ER-8d.mjs";
5
5
  import { a as formatElapsed } from "./command-output-8wHMEde0.mjs";
6
- import { d as readClaimSet, f as releaseClaim, h as tryAcquireClaim, i as claimRemoveCommand, n as ClaimRefusedError } from "./ownership-claim-CeJLEpjD.mjs";
6
+ import { d as readClaimSet, f as releaseClaim, h as tryAcquireClaim, i as claimRemoveCommand, n as ClaimRefusedError } from "./ownership-claim-7zRxxTfZ.mjs";
7
7
  import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
8
8
  import { createHash } from "node:crypto";
9
9
  import { join } from "node:path";
@@ -578,7 +578,7 @@ function listBuildLocks() {
578
578
  function waitingLine({ projectRoot, pid, elapsedMs, logFile }) {
579
579
  const where = projectRoot || "another workspace";
580
580
  const tail = logFile ? ` -- tail ${logFile}` : "";
581
- return `${"build".padEnd(11)} waiting on ${where} (pid ${pid ?? "?"}, ${formatElapsed(elapsedMs)} elapsed)${tail}`;
581
+ return `${"build".padEnd(11)} waiting on ${where} (pid ${pid ?? "?"}, ${formatElapsed(elapsedMs)} elapsed)${tail} -- stim guide lifecycle concurrency`;
582
582
  }
583
583
  function takeoverLine({ projectRoot, pid, logFile, startedAt = null, now = Date.now }) {
584
584
  const where = projectRoot || "another workspace";
@@ -1,6 +1,6 @@
1
- import { u as getConfigDir } from "./config-D7uu8Gbq.mjs";
1
+ import { u as getConfigDir } from "./config-CMtIaTk9.mjs";
2
2
  import { a as formatElapsed } from "./command-output-8wHMEde0.mjs";
3
- import { d as readClaimSet, f as releaseClaim, h as tryAcquireClaim, s as isClaimRefusal } from "./ownership-claim-CeJLEpjD.mjs";
3
+ import { d as readClaimSet, f as releaseClaim, h as tryAcquireClaim, s as isClaimRefusal } from "./ownership-claim-7zRxxTfZ.mjs";
4
4
  import { readdirSync, statSync } from "node:fs";
5
5
  import { join } from "node:path";
6
6
  //#region src/engine/build-slots.ts
@@ -77,7 +77,7 @@ function tryAcquireBuildSlot({ max, root = null, logFile = null }) {
77
77
  return null;
78
78
  }
79
79
  function slotWaitingLine({ max, elapsedMs }) {
80
- return `${"build".padEnd(11)} waiting for a build slot (all ${max} in use, ${formatElapsed(elapsedMs)} elapsed)`;
80
+ return `${"build".padEnd(11)} waiting for a build slot (all ${max} in use, ${formatElapsed(elapsedMs)} elapsed) -- stim guide lifecycle concurrency`;
81
81
  }
82
82
  async function acquireBuildSlot({ max, root = null, logFile = null, now = Date.now, out = () => {}, sleep = sleepAsync, intervalMs = SLOT_POLL_MS, progressMs = SLOT_PROGRESS_MS, ceilingMs = SLOT_CEILING_MS }) {
83
83
  if (!max || max <= 0) return {