stim 1.34.0 → 1.35.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 (118) hide show
  1. package/dist/{acquire-artifact-B0SWoVNt.mjs → acquire-artifact-0E_iw7Rf.mjs} +1 -1
  2. package/dist/{act-7f7wIvCQ.mjs → act-CyxFP8Vj.mjs} +9 -10
  3. package/dist/{activity-DkS-Ir6V.mjs → activity-BQpJpAjA.mjs} +2 -2
  4. package/dist/{agent-device-usage-output-BVdgKbfz.mjs → agent-device-usage-output-BoOC3ZF9.mjs} +3 -3
  5. package/dist/{android-BU4uBX8I.mjs → android-CjcJE7uA.mjs} +5 -5
  6. package/dist/{android-D7qjT2fq.mjs → android-YRb1UzSy.mjs} +27 -29
  7. package/dist/api.mjs +21 -21
  8. package/dist/{artifact-CNnTZs3R.mjs → artifact-2NIRz6SC.mjs} +19 -15
  9. package/dist/{artifact-DmRtKFXW.mjs → artifact-BG4h_WTC.mjs} +11 -11
  10. package/dist/{auto-placement-DA65VQH7.mjs → auto-placement-CBdWa87P.mjs} +4 -5
  11. package/dist/{browser-web-Dn_hgKZC.mjs → browser-web-CNlRugJv.mjs} +5 -5
  12. package/dist/{build-D7SuVoRX.mjs → build-CNE3Wo5-.mjs} +15 -16
  13. package/dist/{build-cache-Cqj4FGnX.mjs → build-cache-_fkewiwH.mjs} +6 -1
  14. package/dist/{build-plan-Ct1FiA3E.mjs → build-plan-BSgR9yKo.mjs} +5 -5
  15. package/dist/{cli-DjdvQG5U.mjs → cli-DK24VzHi.mjs} +17 -17
  16. package/dist/cli.mjs +1 -1
  17. package/dist/{client-kU37gURU.mjs → client-Dy0eJJtz.mjs} +8 -7
  18. package/dist/{command-output-DjExb_tZ.mjs → command-output-CGxrQ3Bd.mjs} +1 -1
  19. package/dist/{dependencies-CZ7c8XX_.mjs → dependencies-IVl2Ga2D.mjs} +27 -30
  20. package/dist/{dev-client-D3wOllNd.mjs → dev-client-DDApoVvd.mjs} +2 -2
  21. package/dist/{device-android-ChfF5I9A.mjs → device-android-C-biM49_.mjs} +3 -3
  22. package/dist/{ccache-ChBMXlBq.mjs → device-capacity-C0PiTyRb.mjs} +1004 -21
  23. package/dist/device-host-worker.mjs +6 -7
  24. package/dist/{device-CWPFAqs0.mjs → device-mOFYiHiw.mjs} +8 -8
  25. package/dist/{device-DH5yf6TU.mjs → device-p_cp8IzW.mjs} +6 -6
  26. package/dist/{device-pool-EZziQ0uS.mjs → device-pool-nsMvt2X2.mjs} +2 -2
  27. package/dist/{device-provision-CfpEy-Vm.mjs → device-provision-BYYJuRKZ.mjs} +7 -7
  28. package/dist/{doctor-CGBsS_68.mjs → doctor-BBAb8NLe.mjs} +1 -1
  29. package/dist/{doctor-B5uEZiKp.mjs → doctor-BptE4PaK.mjs} +10 -11
  30. package/dist/{doctor-dwRboaXx.mjs → doctor-RosbBfTV.mjs} +11 -12
  31. package/dist/doctor-fingerprint.mjs +1 -1
  32. package/dist/{doctor-watchman-r7M9UPi0.mjs → doctor-watchman-CKOnCINh.mjs} +2 -2
  33. package/dist/{eas-build-BpHIt_M3.mjs → eas-build-BxJdzQnF.mjs} +2 -2
  34. package/dist/{eas-fallback-CNPcjq3K.mjs → eas-fallback-DU2WDF8r.mjs} +2 -2
  35. package/dist/{eas-session-ledger-CXwagcQ7.mjs → eas-session-ledger-DCUfh6m7.mjs} +1 -1
  36. package/dist/{error-diagnostics-Ym2mjmJ0.mjs → error-diagnostics-D6wlK-yX.mjs} +3 -3
  37. package/dist/{gc-C7YxKNci.mjs → gc-BUkonUfz.mjs} +10 -10
  38. package/dist/{gc-B464w_a5.mjs → gc-CvMEytXz.mjs} +2 -2
  39. package/dist/{guide-BFW-cB7R.mjs → guide-BWozWG7D.mjs} +21 -13
  40. package/dist/{hosted-android-B7yfqXEy.mjs → hosted-android-y6K3DKQT.mjs} +2 -2
  41. package/dist/{hosted-client-CQuzZwAu.mjs → hosted-client-Bb89P6an.mjs} +2 -2
  42. package/dist/{hosted-logs-DfYCqrrR.mjs → hosted-logs-DYPsnWMF.mjs} +2 -2
  43. package/dist/{hosted-logs-sync-Dlpa2mQF.mjs → hosted-logs-sync-DfUJ2GpF.mjs} +4 -4
  44. package/dist/{hosted-macos-JKefapEm.mjs → hosted-macos-DsEzYVY_.mjs} +4 -4
  45. package/dist/{hosted-native-B7OFt5gB.mjs → hosted-native-C9xQYYFb.mjs} +4 -4
  46. package/dist/{ios-sUPbWqEh.mjs → ios-BCKxjpRp.mjs} +8 -8
  47. package/dist/{ios-DSBs9c6d.mjs → ios-FhMkDfNC.mjs} +19 -21
  48. package/dist/{ios-device-Drplrgzo.mjs → ios-device-DMOnaH5n.mjs} +1 -1
  49. package/dist/{ios-project-DoTQYghg.mjs → ios-project-PXBDoaii.mjs} +5 -5
  50. package/dist/{ios-signing-R34vbrNU.mjs → ios-signing-CCFNjjWU.mjs} +1 -1
  51. package/dist/{js-swap-DrokhOfo.mjs → js-swap-07ikLRJJ.mjs} +3 -3
  52. package/dist/{launch-Cy-MWEZc.mjs → launch-CTq5gBim.mjs} +13 -13
  53. package/dist/{lightened-ALwQcQtV.mjs → lightened-DikgC3-S.mjs} +2 -2
  54. package/dist/{logs-DuED-01Z.mjs → logs-DwI3Rkpf.mjs} +5 -5
  55. package/dist/{logs-DWT7jGpF.mjs → logs-YDMfEohb.mjs} +3 -3
  56. package/dist/{macos-CtBtA_hW.mjs → macos-BoNM-l6f.mjs} +14 -14
  57. package/dist/{macos-kWGcfLzx.mjs → macos-C_e9f5-5.mjs} +1 -1
  58. package/dist/maintenance-run.mjs +3 -3
  59. package/dist/{native-android-tISP2EVp.mjs → native-android-Mv4YTrVN.mjs} +11 -11
  60. package/dist/{native-gradle-artifact-inputs-DVoeZkrw.mjs → native-gradle-artifact-inputs-C_TU6wPq.mjs} +2 -2
  61. package/dist/{native-runtime-CNgxeOCG.mjs → native-runtime-oDqT4UES.mjs} +3 -3
  62. package/dist/{native-xcode-inputs-DGzkhpu4.mjs → native-xcode-inputs-DsozghTX.mjs} +1 -1
  63. package/dist/{native-xcode-ios-B9nsfId2.mjs → native-xcode-ios-UXnH9NRY.mjs} +13 -13
  64. package/dist/offload-worker.mjs +15 -15
  65. package/dist/{phone-reverses-Bo0EzJ-P.mjs → phone-reverses-B9WyCBKQ.mjs} +1 -1
  66. package/dist/{placement-log-BsGvKLtU.mjs → placement-log-D0DZLcJk.mjs} +1 -1
  67. package/dist/{plan-placement-Am0IjuxB.mjs → plan-placement-DqmLrWoX.mjs} +4 -4
  68. package/dist/{ports-CNzg1LYD.mjs → ports-C1_1Qgdy.mjs} +1 -1
  69. package/dist/{prebuild-BfJII57G.mjs → prebuild-BptrXmh_.mjs} +6 -6
  70. package/dist/{project-DU3jjnBE.mjs → project-Dqqj3IkW.mjs} +11 -11
  71. package/dist/{react-native-android-3dZiJv-4.mjs → react-native-android-Bsm_o4A8.mjs} +18 -18
  72. package/dist/{react-native-build-CuvNPt-q.mjs → react-native-build-BTUcE926.mjs} +4 -4
  73. package/dist/{react-native-doctor-a35BwMUO.mjs → react-native-doctor-CfsCTKZt.mjs} +4 -4
  74. package/dist/{react-native-ios-CqnReM1t.mjs → react-native-ios-_cK2pjZn.mjs} +24 -19
  75. package/dist/{reload-YPnvXGlB.mjs → reload-CXgce4Yo.mjs} +1 -1
  76. package/dist/{reload-CC3sNo10.mjs → reload-DsTMtMDU.mjs} +6 -6
  77. package/dist/{remote-BfaIHnhM.mjs → remote-BsmYHUxH.mjs} +6 -6
  78. package/dist/{result-H-AxY7Ud.mjs → result-DK6nTO-M.mjs} +9 -10
  79. package/dist/{server-command-Bn_gOObU.mjs → server-command-Cu2w-UjB.mjs} +2 -2
  80. package/dist/{server-expo-CUJ2fuv1.mjs → server-expo-CjQwxvIi.mjs} +1 -1
  81. package/dist/server-expo-DbSypdNl.mjs +2 -0
  82. package/dist/{settings-DoWHJopy.mjs → settings-DcmVICCn.mjs} +2 -2
  83. package/dist/{settings-payload-Bxw3hid5.mjs → settings-payload-BoiFMiFq.mjs} +1 -1
  84. package/dist/{settings-write-C6d7EC54.mjs → settings-write-B3LzEVwf.mjs} +2 -2
  85. package/dist/{slot-launch-kq0-7w9C.mjs → slot-launch-DsQmPdlg.mjs} +2 -2
  86. package/dist/{stage-CBfMTUK3.mjs → stage-Dqk0qvaI.mjs} +1 -1
  87. package/dist/{start-WTMumMHi.mjs → start-DIFiT2P5.mjs} +5 -5
  88. package/dist/{start-Diu8q6_U.mjs → start-GkRBR1e3.mjs} +1 -1
  89. package/dist/{stats-D4cW247z.mjs → stats-Dz08IopP.mjs} +3 -3
  90. package/dist/{status-CEwxo-PS.mjs → status-DhKv_e8a.mjs} +11 -12
  91. package/dist/{status-B-oeWS0Z.mjs → status-Dost__Vn.mjs} +1 -1
  92. package/dist/{status-watch-D63R5nSI.mjs → status-watch-UecLnPQD.mjs} +1 -1
  93. package/dist/status-worker.mjs +1 -1
  94. package/dist/{stop-HL2RvhrE.mjs → stop-CcZISot5.mjs} +2 -2
  95. package/dist/{stop-Dm9ON6Os.mjs → stop-QFOqgICJ.mjs} +2 -2
  96. package/dist/{stop-vX4w3_8V.mjs → stop-vRe6TQk5.mjs} +9 -9
  97. package/dist/supervisor-run.mjs +7 -8
  98. package/dist/{support-BNe1_Hg7.mjs → support-CNqJDBrH.mjs} +3 -3
  99. package/dist/support-DW575Dnx.mjs +2050 -0
  100. package/dist/{swiftpm-artifact-inputs-uq5oV7xR.mjs → swiftpm-artifact-inputs-DQTJTczX.mjs} +1 -1
  101. package/dist/{swiftpm-macos-ClF203_2.mjs → swiftpm-macos-BKczZ0k9.mjs} +4 -4
  102. package/dist/{toolchain-CECahBDa.mjs → toolchain-BDlugtoh.mjs} +1 -1
  103. package/dist/{toolchain-CYonNjd_.mjs → toolchain-DRQyCvlp.mjs} +2 -2
  104. package/dist/web-D1m1LKEz.mjs +2 -0
  105. package/dist/{web-BdaMBr2O.mjs → web-TDWjHUT3.mjs} +3 -3
  106. package/dist/web-run.mjs +1 -1
  107. package/dist/{workspaces-B9v5V7Es.mjs → workspaces-CY3KSdC0.mjs} +2 -2
  108. package/dist/{worktree-C4MBOMz7.mjs → worktree-D6myrQ2H.mjs} +1 -1
  109. package/dist/{worktree-C3N86KpI.mjs → worktree-fKKe8j5M.mjs} +10 -10
  110. package/package.json +7 -7
  111. package/dist/device-capacity-CHC3dKWg.mjs +0 -867
  112. package/dist/device-ios-B3-Ah--R.mjs +0 -716
  113. package/dist/device-lease-run-i3FNX4OE.mjs +0 -282
  114. package/dist/idle-shutdown-DPD8oV36.mjs +0 -142
  115. package/dist/selection-CH-MUVGD.mjs +0 -791
  116. package/dist/server-expo-B59N16uT.mjs +0 -2
  117. package/dist/support-ivsfDj0v.mjs +0 -287
  118. package/dist/web-CIpHpGp8.mjs +0 -2
@@ -1,24 +1,24 @@
1
1
  import { C as operationState, D as ownership_claim_exports, E as debugLog, S as isTermination, T as process_identity_exports, d as throwIfCancelled, f as WorkspaceProcessCleanupError, l as cancellableSleep, m as workspaceProcessLockError, n as isTimeoutError, p as withWorkspaceProcessLock, t as getExecutor, u as cancelledError } from "./exec-CGuDzt40.mjs";
2
- import { D as workspaceId$1, E as workspaceDir, M as withDirLock$1, b as sharedCcache$1, d as writeWorkspaceState, j as workspaceStateFile$1, k as workspaceLogsDir$1, l as withWorkspaceStateLockAsync, r as readWorkspaceState$1, t as clearWorkspaceStateKey, y as sharedBuildCache } from "./workspace-state-BdZ-QgmB.mjs";
2
+ import { D as workspaceId$1, E as workspaceDir, M as withDirLock$1, b as sharedCcache$1, d as writeWorkspaceState, j as workspaceStateFile$1, k as workspaceLogsDir$1, l as withWorkspaceStateLockAsync, r as readWorkspaceState$1, s as updateWorkspaceStateAsync, t as clearWorkspaceStateKey, y as sharedBuildCache } from "./workspace-state-BdZ-QgmB.mjs";
3
3
  import { n as stripAnsi } from "./process-output-Fpl8PYi8.mjs";
4
- import { a as formatElapsed, l as phaseLine, o as formatLongDuration, s as isAppLaunchError, u as plural } from "./command-output-DjExb_tZ.mjs";
5
- import { a as clearDevice, at as isOnMountedVolume, ct as volumeRootFor, d as getConfigDir, ft as deviceSlotPlatforms, ht as projectDeviceSlots, k as withConfigLockAsync$1, m as getProject, nt as directorySizeAsync, o as clearSupervisor, rt as formatBytes, st as measuredDirectorySizeAsync, tt as directorySize, v as loadConfig$1 } from "./config-BW0fAtoK.mjs";
4
+ import { a as formatElapsed, l as phaseLine, o as formatLongDuration, s as isAppLaunchError, u as plural } from "./command-output-CGxrQ3Bd.mjs";
5
+ import { a as clearDevice, at as isOnMountedVolume, ct as volumeRootFor, d as getConfigDir, dt as deviceSlotKey, ft as deviceSlotPlatforms, ht as projectDeviceSlots, k as withConfigLockAsync$1, m as getProject, nt as directorySizeAsync, o as clearSupervisor, p as getPlatformDeviceLimits, rt as formatBytes, st as measuredDirectorySizeAsync, tt as directorySize, u as getConcurrencyLimits, v as loadConfig$1 } from "./config-BW0fAtoK.mjs";
6
6
  import { a as killOnAbort, n as runOperationFileQuiet, r as childEnvironment, t as runOperationFile } from "./operation-exec-DKlBcKAL.mjs";
7
- import { Z as resolveProjectSettingsAsync, d as androidLayoutSetting, ft as planMetroReach, m as defaultAndroidLayout } from "./settings-B5IBDfTx.mjs";
8
- import { C as resolvePackageJson, M as readMetroTunnel, N as readPidFile, P as readRemoteSession, i as findProjectRoot$1, j as clearWorkspaceSupervisor, k as clearRemoteSession, s as ownedDeviceLabel, v as detectIsExpo } from "./project-DU3jjnBE.mjs";
9
- import { R as listAvdsAsync, U as ownedAvdDirectory, X as parseAvdSystemImage, Y as parseAvdDeviceProfile, d as androidToolPath, ft as isStimOwnedAvd, gt as dropParked, m as avdIniPaths, o as androidHome, pt as isStimOwnedSim, q as ownedAvdSerialResolverAsync, xt as readParked, y as avdStorageRoots, z as listInstalledSystemImages } from "./android-Iq0M2t2d.mjs";
7
+ import { Z as resolveProjectSettingsAsync, d as androidLayoutSetting, ft as planMetroReach, g as deviceReclaimIdleMinutesSetting, m as defaultAndroidLayout } from "./settings-B5IBDfTx.mjs";
8
+ import { C as resolvePackageJson, M as readMetroTunnel, N as readPidFile, P as readRemoteSession, i as findProjectRoot$1, j as clearWorkspaceSupervisor, k as clearRemoteSession, s as ownedDeviceLabel, v as detectIsExpo } from "./project-Dqqj3IkW.mjs";
9
+ import { N as listAdbDevices, P as listAdbDevicesAsync, R as listAvdsAsync, U as ownedAvdDirectory, X as parseAvdSystemImage, Y as parseAvdDeviceProfile, d as androidToolPath, ft as isStimOwnedAvd, gt as dropParked, m as avdIniPaths, o as androidHome, pt as isStimOwnedSim, q as ownedAvdSerialResolverAsync, xt as readParked, y as avdStorageRoots, z as listInstalledSystemImages } from "./android-Iq0M2t2d.mjs";
10
10
  import { a as listeningPidsAsync, c as pidExists, d as resolveProjectMetro, h as isMetroRunning, r as killMetroTreeAsync, u as processCwds } from "./metro-B5PVz0Wv.mjs";
11
11
  import { c as readNdjsonGenerations$1, i as createNdjsonWriter, r as createNdjsonTail } from "./ndjson-CrjIsGzw.mjs";
12
- import { T as formatSpace, a as removeEasSessionClaim, d as parseWatchRoots, f as parseWatchmanClients, i as recordEasSessionClaim, l as parseSubscriberNames, n as easMachineStateRoot$1, o as removeEasSessionClaimAsync, p as parseWatchmanPid, r as readEasSessionLedger$1, s as compareStimVersions, t as assertEasMachineRootWritable, u as parseTriggerCount, w as environmentState, y as capacity } from "./eas-session-ledger-CXwagcQ7.mjs";
12
+ import { T as formatSpace, a as removeEasSessionClaim, d as parseWatchRoots, f as parseWatchmanClients, i as recordEasSessionClaim, l as parseSubscriberNames, n as easMachineStateRoot$1, o as removeEasSessionClaimAsync, p as parseWatchmanPid, r as readEasSessionLedger$1, s as compareStimVersions, t as assertEasMachineRootWritable, u as parseTriggerCount, w as environmentState, y as capacity } from "./eas-session-ledger-DCUfh6m7.mjs";
13
13
  import { i as registeredCaches, r as register } from "./cache-manifest-8JX9S1QJ.mjs";
14
- import { At as mapLimited, B as workspaceBuildInProgress, Dt as createGcResults, Et as GC_OUTPUT, F as collectOwnedDeviceActivityAsync, L as idleShutdownCandidates, Mt as sizeConcurrency, N as collectIdleDevicesAsync, _t as teardownParkedIosSim, bt as measureDu, dt as eraseParkedIosSim, gt as teardownParkedAvd, ht as teardownOwnedIosSim, i as clearWorkspaceOutputs, l as keptByLock, o as collectWorkspaceOutputs, pt as teardownOwnedAvd, ut as eraseParkedAvd, v as withIdleWorkspace, vt as probePublicHttp, wt as listBuildSlots, x as resolveSupervisorTarget, xt as writeUsageCache, yt as canonical$1, z as shutDownIdleDevices } from "./workspaces-B9v5V7Es.mjs";
14
+ import { At as mapLimited, B as workspaceBuildInProgress, Dt as createGcResults, Et as GC_OUTPUT, F as collectOwnedDeviceActivityAsync, L as idleShutdownCandidates, Mt as sizeConcurrency, N as collectIdleDevicesAsync, P as collectOwnedDeviceActivity, _t as teardownParkedIosSim, bt as measureDu, dt as eraseParkedIosSim, gt as teardownParkedAvd, ht as teardownOwnedIosSim, i as clearWorkspaceOutputs, l as keptByLock, o as collectWorkspaceOutputs, ot as recordCapacityRefusal, pt as teardownOwnedAvd, st as recordCapacityWait, ut as eraseParkedAvd, v as withIdleWorkspace, vt as probePublicHttp, wt as listBuildSlots, x as resolveSupervisorTarget, xt as writeUsageCache, yt as canonical$1, z as shutDownIdleDevices$1 } from "./workspaces-CY3KSdC0.mjs";
15
15
  import { t as canonicalPath } from "./paths-BsJMoPP2.mjs";
16
- import { $ as readBundleSchemes, Z as approvableSchemes, _t as runCancellationSignal, bt as claimForEmptying, mt as nativeRunWaitNotice } from "./toolchain-CYonNjd_.mjs";
17
- import { N as listBuildLocks, b as leaseIsExpired, g as workspaceActivity, o as envDir, p as readHostProcessTableAsync, x as listLeaseFiles } from "./activity-DkS-Ir6V.mjs";
18
- import { u as expoConfigContents } from "./build-cache-Cqj4FGnX.mjs";
19
- import { G as deviceHoldsBundle, O as sanitizeDeviceLabel, W as deviceHoldsApk, b as parseSimctlList, f as listAllIosSimsAsync, g as listUserApps, j as uninstallIosApp, p as listIosDeviceTypesAsync, u as iosSimulatorFailureAdvice, y as parseRuntimeVersion } from "./ios-BdZdNsAj.mjs";
16
+ import { $ as readBundleSchemes, Z as approvableSchemes, _t as runCancellationSignal, bt as claimForEmptying, mt as nativeRunWaitNotice } from "./toolchain-DRQyCvlp.mjs";
17
+ import { N as listBuildLocks, _ as workspaceDeviceLastUseAt, b as leaseIsExpired, g as workspaceActivity, o as envDir, p as readHostProcessTableAsync, x as listLeaseFiles } from "./activity-BQpJpAjA.mjs";
18
+ import { u as expoConfigContents } from "./build-cache-_fkewiwH.mjs";
19
+ import { G as deviceHoldsBundle, O as sanitizeDeviceLabel, W as deviceHoldsApk, b as parseSimctlList, d as listAllIosSims, f as listAllIosSimsAsync, g as listUserApps, j as uninstallIosApp, l as iosRuntimeMatches, p as listIosDeviceTypesAsync, u as iosSimulatorFailureAdvice, y as parseRuntimeVersion } from "./ios-BdZdNsAj.mjs";
20
20
  import { n as supervisorError } from "./errors-DHm_U5gv.mjs";
21
- import { c as findBinUpward, n as expoBinFromPackage, r as expoBinPath, s as expoTelemetryEnv } from "./server-expo-CUJ2fuv1.mjs";
21
+ import { c as findBinUpward, n as expoBinFromPackage, r as expoBinPath, s as expoTelemetryEnv } from "./server-expo-CjQwxvIi.mjs";
22
22
  import { t as DEV_MENU_LAUNCH_ARGS } from "./ios-device-DLdEDWJj.mjs";
23
23
  import { t as resolveMaintenanceSettings } from "./settings-D9LtQaUw.mjs";
24
24
  import { createRequire } from "node:module";
@@ -26,12 +26,13 @@ import { createHash, randomUUID } from "node:crypto";
26
26
  import { closeSync, existsSync, lstatSync, mkdirSync, openSync, readFileSync, readSync, readdirSync, realpathSync, renameSync, rmSync, statSync, statfsSync, unlinkSync, writeFileSync } from "node:fs";
27
27
  import { homedir, platform, totalmem } from "node:os";
28
28
  import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
29
- import { LOG_ROTATE_BYTES, METRO_NAMED_CACHE_LAYOUT, ambientContext, cacheDirRefusal, compiledHelper, configDir, ensurePrivateDir, rotatedLogPath, withAmbientContext, withDirLock } from "@stim-cli/core";
30
- import { DEV_SERVER_STOP_REQUEST_KEY, MACHINE_OWNER_KINDS, MAINTENANCE_LOG_NAME, budgetSweepFile, budgetSweepLock, buildWorkerRoot, gcLogFile, isJsonObject, loadConfig, machineCacheUsageFile, maintenanceNdjsonFile, readCreatedDevices, readDevServerStop, readDevServerStopRequest, readJsonObject, readMachineCacheUsage, runId, settingDefinition, settingValueError, workspaceLogsDir } from "@stim-cli/core/state";
29
+ import { LOG_ROTATE_BYTES, METRO_NAMED_CACHE_LAYOUT, ambientContext, cacheDirRefusal, compiledHelper, configDir, ensurePrivateDir, rotatedLogPath, withAmbientContext, withDirLock, workspaceId } from "@stim-cli/core";
30
+ import { DEVICE_IDLE_SHUTDOWN_KEY, DEVICE_RECLAIM_REASON, DEV_SERVER_STOP_REQUEST_KEY, MACHINE_OWNER_KINDS, MAINTENANCE_LOG_NAME, automaticMachineEnabled, budgetSweepFile, budgetSweepLock, buildWorkerRoot, gcLogFile, isJsonObject, loadConfig, machineCacheUsageFile, maintenanceNdjsonFile, readCreatedDevices, readDevServerStop, readDevServerStopRequest, readDeviceIdleShutdowns, readJsonObject, readMachineCacheUsage, runId, settingDefinition, settingValueError, workspaceLogsDir } from "@stim-cli/core/state";
31
31
  import { basename as basename$1, dirname as dirname$1, isAbsolute as isAbsolute$1, join as join$1, relative as relative$1, resolve as resolve$1 } from "path";
32
32
  import chalk from "chalk";
33
33
  import { existsSync as existsSync$1, readFileSync as readFileSync$1, readdirSync as readdirSync$1, realpathSync as realpathSync$1, rmSync as rmSync$1, statSync as statSync$1 } from "fs";
34
34
  import { fileURLToPath } from "node:url";
35
+ import { setTimeout as setTimeout$1 } from "node:timers/promises";
35
36
  import { access, lstat, readFile, readdir, rm, stat, statfs } from "node:fs/promises";
36
37
  import { homedir as homedir$1, tmpdir as tmpdir$1 } from "os";
37
38
  //#region src/budget-sweep.ts
@@ -7775,7 +7776,7 @@ function estimateMemoryUse({ config, sims, androidSerial, supervisorRunning }) {
7775
7776
  liveWorkspaces: liveCount
7776
7777
  };
7777
7778
  }
7778
- async function listSims(signal, operation) {
7779
+ async function listSims$1(signal, operation) {
7779
7780
  try {
7780
7781
  return await listAllIosSimsAsync({
7781
7782
  timeoutMs: DEVICE_LIST_TIMEOUT_MS,
@@ -7811,7 +7812,7 @@ async function readMemoryUse(simSnapshot, signal, operation) {
7811
7812
  operation
7812
7813
  });
7813
7814
  const config = loadConfig$1();
7814
- const sims = simSnapshot && !signal ? await snapshotSims(simSnapshot) : await listSims(signal, operation);
7815
+ const sims = simSnapshot && !signal ? await snapshotSims(simSnapshot) : await listSims$1(signal, operation);
7815
7816
  signal?.throwIfAborted();
7816
7817
  const serials = /* @__PURE__ */ new Map();
7817
7818
  for (const record of Object.values(config?.projects ?? {})) for (const { platforms } of projectDeviceSlots(record)) {
@@ -7869,7 +7870,7 @@ async function reclaimIdleDevices({ root, dryRun, note, record, now, env, signal
7869
7870
  const other = isOtherWorkspace(root);
7870
7871
  const collect = async (at) => {
7871
7872
  signal?.throwIfAborted();
7872
- const devices = await collectIdleDevicesAsync(loadConfig$1(), await listSims(signal, operation) ?? [], [], at, env, signal, operation);
7873
+ const devices = await collectIdleDevicesAsync(loadConfig$1(), await listSims$1(signal, operation) ?? [], [], at, env, signal, operation);
7873
7874
  signal?.throwIfAborted();
7874
7875
  return idleShutdownCandidates(devices.filter((device) => other(device.project)), 0);
7875
7876
  };
@@ -7878,7 +7879,7 @@ async function reclaimIdleDevices({ root, dryRun, note, record, now, env, signal
7878
7879
  targets: idle.map(deviceLabel),
7879
7880
  failures: 0
7880
7881
  };
7881
- const { failures, shutDown } = await shutDownIdleDevices(idle, 0, () => collect(Date.now()), stepOutput(note, record), signal, operation);
7882
+ const { failures, shutDown } = await shutDownIdleDevices$1(idle, 0, () => collect(Date.now()), stepOutput(note, record), signal, operation);
7882
7883
  return {
7883
7884
  targets: shutDown.map(deviceLabel),
7884
7885
  failures
@@ -7903,7 +7904,7 @@ async function reclaimIdleDevServers({ root, dryRun, note, record, now, env, sig
7903
7904
  targets: [],
7904
7905
  failures: 0
7905
7906
  };
7906
- const sims = await listSims(signal, operation);
7907
+ const sims = await listSims$1(signal, operation);
7907
7908
  signal?.throwIfAborted();
7908
7909
  if (sims === null) {
7909
7910
  note(chalk.dim(phaseLine("budget", "dev servers left alone: simulators could not be listed to prove them idle")));
@@ -8473,4 +8474,986 @@ function resolveCcache({ root, layout = defaultAndroidLayout(root), dir = shared
8473
8474
  };
8474
8475
  }
8475
8476
  //#endregion
8476
- export { gradleVersionLines as $, memorySweepIsScoped as $n, INSTALL_ERROR as $t, diskPressureLevel as A, loadProjectProvider as An, ensureRemoteBootOwned as At, includesArchives as B, MIN_EAS_CLI_DEVICE_VERSION as Bn, RELEASE_VERIFY_WAIT_MS as Bt, devServerStopLevel as C, runProcessOperation as Cn, describeError as Ct, requestDevServerStop as D, checkEasAuth as Dn, bundleEntryPoint as Dt, metroLastStop as E, cacheLevel as En, binOnPath as Et, gcOptionsOf as F, resolveRemote as Fn, withEasProjectLock as Ft, planCacheEmptying as G, parseArchiveSelector as Gn, unattributedLaunchLines as Gt, includesRecordings as H, easCliSupport as Hn, isBundleProof as Ht, maintenanceItem as I, uploadRemote as In, LAUNCH_BUNDLING as It, selectsBeyondCaches as J, collectMemoryReport as Jn, verifyLaunch as Jt, previewScopedEmptying as K, GRADLE_DAEMONS_KIND as Kn, unverifiedLaunchLines as Kt, openGcLog as L, bundleInvocation as Ln, LAUNCH_FATAL as Lt, levelStillShort as M, providerFromConfig as Mn, remoteAndroidDeps as Mt, lowestFree as N, readProjectConfig as Nn, remoteIosDeps as Nt, vanishedSupervisorMessage as O, easAuthNote as On, endRecordedSession as Ot, protectWindowMs as P, resolveEasCliBin as Pn, resolveRemoteContext as Pt, deleteGradleVersions as Q, memoryScope as Qn, DEFAULT_METRO_PORT as Qt, maintenanceLogger as R, remoteCliInvocation as Rn, LAUNCH_UNVERIFIED as Rt, describeMetroLastStop as S, processContext as Sn, deleteEasSessions as St, logVanishedSupervisor as T, UPLOAD_TIMEOUT_MS as Tn, REMOTE_SESSION_ERROR as Tt, includesWorkspaceOutputs as U, easCliUpgradeRemedy as Un, readCollectorRecords as Ut, includesParkedDevices as V, MIN_EAS_CLI_SIMULATOR_VERSION as Vn, VERIFY_TIMEOUT_MS as Vt, isWorkspacesScope as W, ownedSessionName as Wn, readMetroRecords as Wt, collectGradleVersions as X, memoryCacheKind as Xn, APP_READINESS_TIMEOUT_MS as Xt, trimCaches as Y, inspectWatchmanMemory as Yn, verifyReleaseLaunch as Yt, deleteGradleVersion as Z, memoryLines as Zn, ADB_INSTALL_TIMEOUT_MS as Zt, resolveBudget as _, reverseMetroPorts as _n, findStaleDeviceRecords as _t, projectCmakeLauncher as a, devClientUrl as an, attributeMachineUsage as ar, DEVICE_LIST_TIMEOUT_MS$1 as at, stopOwnedMetroForReset as b, gateMetroOrigin as bn, withAndroidAvdSizes as bt, budgetGate as c, hasMetroReverse as cn, pruneCache as cr, collectParkedSims as ct, enforceBudget as d, installIosApp as dn, deleteProjectDevices as dt, IOS_DEV_MENU_OFF_DEFAULTS_PLIST as en, memoryWorkPending as er, gradleVersionsPending as et, inspectBudget as f, iosAppProcess as fn, describeParkedSims as ft, readVolumeSpaceAsync as g, restartedAppNote as gn, findOrphanedDevices as gt, readVolumeSpace as h, launchIosApp as hn, eraseParkedDevices as ht, ccacheInstallCommand as i, clearOtherUserApps as in, phase as ir, collectInventory as it, levelFloors as j, ownerFromConfig as jn, readInstalledEasCliVersionAsync as jt, withdrawDevServerStopRequest as k, isEasAuthFailureText as kn, ensureMetroReachable as kt, budgetLine as l, installAndroidApp as ln, sizeCaches as lr, deleteParkedAvds as lt, peekBudget as m, launchAndroidReleaseApp as mn, deviceSweepIsScoped as mt, CCACHE_UNAVAILABLE as n, androidDevClientUrl as nn, scopedMemoryReport as nr, waitForGradleExit as nt, readCcacheActivity as o, deviceShellArg as on, readFootprints as or, collectDeviceLeases as ot, machineNumber as p, launchAndroidApp as pn, describeUnverifiableDevices as pt, selectCaches as q, WATCHMAN_KIND as qn, verifyAndroidReleaseLaunch as qt, ccacheActivityLine as r, appProcessFromPid as rn, stopIdleGradleDaemons as rr, getMachineCacheReport as rt, resolveCcache as s, ensureMetroReverse as sn, discoverCaches as sr, collectParkedAvds as st, CCACHE_NOT_RUN as t, androidAppProcess as tn, reclaimMemory as tr, includesGradleVersions as tt, cacheLabel as u, installConflictKind as un, deleteParkedSims as ut, trimmableCaches as v, writeDebugHttpHost as vn, findStaleProjectDevices as vt, devServerStopRecord as w, RESOLVE_TIMEOUT_MS as wn, withRemoteSessionGcLocks as wt, describeDevServerStop as x, exitAfterFlush as xn, collectEasSessionSweep as xt, stopOwnedMetro as y, readRemoteSimulatorArch as yn, unverifiedDeviceCommand as yt, emptyCaches as z, remoteCommand as zn, MANIFEST_VERIFY_TIMEOUT_MS as zt };
8477
+ //#region src/workspace/display-label.ts
8478
+ function baseLabel({ root, label }) {
8479
+ return label || basename(root);
8480
+ }
8481
+ function pathSegments(root) {
8482
+ return root.split(/[\\/]+/).filter(Boolean).toReversed();
8483
+ }
8484
+ function workspaceLabels(workspaces) {
8485
+ const labels = /* @__PURE__ */ new Map();
8486
+ const groups = /* @__PURE__ */ new Map();
8487
+ for (const workspace of workspaces) {
8488
+ const base = baseLabel(workspace);
8489
+ const group = groups.get(base) ?? [];
8490
+ if (!group.some(({ root }) => root === workspace.root)) group.push(workspace);
8491
+ groups.set(base, group);
8492
+ }
8493
+ for (const [base, group] of groups) {
8494
+ const [only] = group;
8495
+ if (group.length === 1 && only) {
8496
+ labels.set(only.root, base);
8497
+ continue;
8498
+ }
8499
+ const segments = group.map(({ root }) => pathSegments(root));
8500
+ const longest = Math.max(...segments.map((parts) => parts.length));
8501
+ let depth = 0;
8502
+ while (depth < longest && segments.every((parts) => parts[depth] === segments[0]?.[depth])) depth++;
8503
+ const suffixes = segments.map((parts) => parts[depth]);
8504
+ const distinct = new Set(suffixes).size === group.length;
8505
+ group.forEach(({ root }, index) => {
8506
+ const suffix = distinct ? suffixes[index] : root;
8507
+ labels.set(root, suffix ? `${base} (${suffix})` : base);
8508
+ });
8509
+ }
8510
+ return labels;
8511
+ }
8512
+ function workspaceDisplayLabel(root, config) {
8513
+ const registered = Object.entries(config?.projects ?? {}).map(([path, project]) => ({
8514
+ root: path,
8515
+ label: project.label
8516
+ }));
8517
+ const own = registered.find((entry) => entry.root === root);
8518
+ return workspaceLabels([...registered, own ?? { root }]).get(root) ?? basename(root);
8519
+ }
8520
+ //#endregion
8521
+ //#region src/devices/idle-shutdown.ts
8522
+ const SIM_LIST_TIMEOUT_MS = 1e4;
8523
+ const MINUTE_MS = 6e4;
8524
+ /**
8525
+ * How long a booted owned device has been idle, when that is at least `idleMs` and nothing keeps it in use: no
8526
+ * driver, Stim or agent-device lock, or unreadable activity evidence, no build in the workspace, and no activity
8527
+ * (including an open stim-server viewer) for `idleMs`. Null keeps the device running.
8528
+ */
8529
+ function idleShutdownDueMs(activity, { idleMs, now, buildInProgress, platform = process.platform }) {
8530
+ if (buildInProgress || activity.state === "driven") return null;
8531
+ const unknown = platform === "win32" ? activity.basis.filter((basis) => basis !== "driver-process") : activity.basis;
8532
+ if (activity.state === "unknown" && unknown.length > 0) return null;
8533
+ const last = Date.parse(activity.lastActivityAt ?? "");
8534
+ if (!Number.isFinite(last)) return null;
8535
+ const idle = now - last;
8536
+ return idle >= idleMs ? idle : null;
8537
+ }
8538
+ function listSims() {
8539
+ try {
8540
+ return listAllIosSims({ timeoutMs: SIM_LIST_TIMEOUT_MS });
8541
+ } catch {
8542
+ return [];
8543
+ }
8544
+ }
8545
+ /** Booted owned devices idle for `idleMs`, in one workspace or all registered workspaces when root is absent. */
8546
+ function dueIdleDevices(root, idleMs, now = Date.now()) {
8547
+ const config = loadConfig$1();
8548
+ if (!config) return [];
8549
+ const self = root === void 0 ? void 0 : canonicalPath(root);
8550
+ const projects = Object.fromEntries(Object.entries(config.projects).filter(([path]) => {
8551
+ if (self !== void 0 && canonicalPath(path) !== self) return false;
8552
+ const usedAt = workspaceDeviceLastUseAt(path);
8553
+ return !Number.isFinite(usedAt) || now - usedAt >= idleMs;
8554
+ }));
8555
+ if (!Object.keys(projects).length) return [];
8556
+ const builds = new Map(Object.keys(projects).map((path) => [path, workspaceBuildInProgress(path)]));
8557
+ return collectOwnedDeviceActivity({
8558
+ ...config,
8559
+ projects
8560
+ }, listSims(), now).flatMap((device) => {
8561
+ const idleForMs = idleShutdownDueMs(device.activity, {
8562
+ idleMs,
8563
+ now,
8564
+ buildInProgress: builds.get(device.project) ?? true
8565
+ });
8566
+ return idleForMs === null ? [] : [{
8567
+ device,
8568
+ idleForMs
8569
+ }];
8570
+ });
8571
+ }
8572
+ /** Appends an idle shutdown record to the workspace's metro.ndjson, where the supervisor writes its own. */
8573
+ function writeIdleShutdownEvent(root, event) {
8574
+ const writer = createNdjsonWriter(join(workspaceLogsDir$1(root), "metro.ndjson"), { maxBytes: LOG_ROTATE_BYTES });
8575
+ writer.write({
8576
+ src: "metro",
8577
+ ...event
8578
+ });
8579
+ writer.close();
8580
+ }
8581
+ /**
8582
+ * Shuts down, through centralized teardown, each of the workspace's owned devices that is still idle for `idleMs`,
8583
+ * and records why. The caller holds the workspace's native-run lock, so no `ios` or `android` run is booting or
8584
+ * installing on them.
8585
+ */
8586
+ async function shutDownIdleDevices(root, idleMs, log, now = Date.now(), { only, reason } = {}) {
8587
+ let shutDown = 0;
8588
+ for (const { device, idleForMs } of dueIdleDevices(root, idleMs, now)) {
8589
+ if (only && (device.kind !== only.kind || device.id !== only.id || device.project !== only.project || device.slot !== only.slot)) continue;
8590
+ const what = `${device.kind === "ios" ? "simulator" : "emulator"} ${device.name}`;
8591
+ const outcome = device.kind === "ios" ? await teardownOwnedIosSim(device.id, {
8592
+ label: device.name,
8593
+ workspace: root
8594
+ }) : await teardownOwnedAvd(device.id, {
8595
+ owner: {
8596
+ projectPath: device.project,
8597
+ slot: device.slot
8598
+ },
8599
+ workspace: root
8600
+ });
8601
+ if (outcome.status === "torn-down") {
8602
+ shutDown++;
8603
+ const idleMinutes = Math.floor(idleForMs / MINUTE_MS);
8604
+ try {
8605
+ await updateWorkspaceStateAsync(root, (state) => ({
8606
+ ...state,
8607
+ [DEVICE_IDLE_SHUTDOWN_KEY]: {
8608
+ ...readDeviceIdleShutdowns(state),
8609
+ [deviceSlotKey(device.kind, device.slot)]: {
8610
+ at: new Date(now).toISOString(),
8611
+ idleMinutes,
8612
+ ...reason ? { reason } : {}
8613
+ }
8614
+ }
8615
+ }));
8616
+ } catch (error) {
8617
+ log({
8618
+ level: "warn",
8619
+ event: "device_idle_shutdown_failed",
8620
+ msg: `shut down idle ${what} but could not record it: ${error?.message || error}`
8621
+ });
8622
+ }
8623
+ log({
8624
+ level: "info",
8625
+ event: "device_idle_shutdown",
8626
+ msg: `shut down ${what}, idle ${formatLongDuration(idleForMs)}${reason === DEVICE_RECLAIM_REASON ? ", reclaimed for a waiting run" : ""}`
8627
+ });
8628
+ } else if (outcome.status !== "missing") log({
8629
+ level: "warn",
8630
+ event: "device_idle_shutdown_failed",
8631
+ msg: `could not shut down idle ${what}: ${outcome.reason}`,
8632
+ ...outcome.status === "skipped" ? { skipped: true } : {}
8633
+ });
8634
+ }
8635
+ return shutDown;
8636
+ }
8637
+ /** Forgets the slot's idle shutdown once a run boots its device again. */
8638
+ async function clearDeviceIdleShutdown(root, platform, slot = "default") {
8639
+ const key = deviceSlotKey(platform, slot);
8640
+ if (!(key in readDeviceIdleShutdowns(readWorkspaceState$1(root)))) return;
8641
+ await updateWorkspaceStateAsync(root, (state) => {
8642
+ const { [key]: _cleared, ...rest } = readDeviceIdleShutdowns(state);
8643
+ const { [DEVICE_IDLE_SHUTDOWN_KEY]: _all, ...others } = state;
8644
+ return Object.keys(rest).length ? {
8645
+ ...others,
8646
+ [DEVICE_IDLE_SHUTDOWN_KEY]: rest
8647
+ } : others;
8648
+ });
8649
+ }
8650
+ //#endregion
8651
+ //#region src/devices/queue-reclaim.ts
8652
+ async function reclaimIdleDevice(root, idleMs, out, now, skipped, platform) {
8653
+ try {
8654
+ const self = canonicalPath(root);
8655
+ const candidate = dueIdleDevices(void 0, idleMs, now()).filter(({ device }) => {
8656
+ try {
8657
+ return realpathSync(device.project) !== self;
8658
+ } catch {
8659
+ return false;
8660
+ }
8661
+ }).filter(({ device }) => !platform || device.kind === platform).toSorted((a, b) => b.idleForMs - a.idleForMs).find(({ device }) => !skipped.has(`${device.kind}:${device.id}`));
8662
+ if (!candidate) return 0;
8663
+ const { device, idleForMs } = candidate;
8664
+ const label = workspaceDisplayLabel(device.project, loadConfig$1());
8665
+ const log = (event) => {
8666
+ writeIdleShutdownEvent(device.project, event);
8667
+ if (event.level === "warn") out(`${"device".padEnd(11)} ${event.msg}`);
8668
+ };
8669
+ skipped.add(`${device.kind}:${device.id}`);
8670
+ const run = await withIdleWorkspace(device.project, () => shutDownIdleDevices(device.project, idleMs, log, now(), {
8671
+ only: device,
8672
+ reason: DEVICE_RECLAIM_REASON
8673
+ }), {
8674
+ purpose: "device queue reclaim",
8675
+ supervisor: false,
8676
+ managedLocks: false
8677
+ });
8678
+ if (!run.ran) {
8679
+ log({
8680
+ level: "warn",
8681
+ event: "device_idle_shutdown_failed",
8682
+ msg: `could not reclaim ${device.name}: ${run.reasons.join("; ")}`
8683
+ });
8684
+ return 0;
8685
+ }
8686
+ if (!run.value) return 0;
8687
+ skipped.delete(`${device.kind}:${device.id}`);
8688
+ out(`${"device".padEnd(11)} reclaimed ${device.kind === "ios" ? "iOS simulator" : "Android emulator"} ${device.name} (workspace ${label}, idle ${Math.floor(idleForMs / 6e4)}m) for this waiting run`);
8689
+ return run.value;
8690
+ } catch (error) {
8691
+ out(`${"device".padEnd(11)} could not reclaim an idle device: ${error?.message || error}`);
8692
+ return 0;
8693
+ }
8694
+ }
8695
+ //#endregion
8696
+ //#region src/engine/device-capacity.ts
8697
+ const LIVE_SIM_STATES = /* @__PURE__ */ new Set(["Booted", "Booting"]);
8698
+ const ADMISSION_LOCK = "device-admission";
8699
+ const ADMISSION_LOCK_WAIT_MS = 3e5;
8700
+ const LOCK_QUIET_MS = 5e3;
8701
+ const LOCK_PROGRESS_MS = 3e4;
8702
+ const INVENTORY_REFRESH_MS = 1e4;
8703
+ const LISTING_TIMEOUT_MS = 3e4;
8704
+ const NO_ADB = {
8705
+ emulators: [],
8706
+ physical: [],
8707
+ unhealthy: []
8708
+ };
8709
+ var DeviceAdmissionRefusal = class extends Error {
8710
+ code;
8711
+ remedy;
8712
+ constructor(refusal) {
8713
+ super(refusal.message);
8714
+ this.code = refusal.code;
8715
+ this.remedy = refusal.remedy;
8716
+ }
8717
+ };
8718
+ var DeviceCountUnavailable = class extends Error {};
8719
+ function deviceKey(platform, key) {
8720
+ return `${platform}:${key}`;
8721
+ }
8722
+ function bootingDevicesRoot() {
8723
+ return join(getConfigDir(), "device-boots");
8724
+ }
8725
+ function liveEmulatorPorts(adb) {
8726
+ return [...adb.emulators, ...adb.unhealthy.filter((entry) => entry.kind === "emulator")];
8727
+ }
8728
+ function readBootingDevices() {
8729
+ const survey = (0, ownership_claim_exports.readClaimSet)(bootingDevicesRoot());
8730
+ const unresolved = survey.unresolved[0];
8731
+ if (unresolved) throw new ownership_claim_exports.ClaimRefusedError({
8732
+ claimPath: unresolved.path,
8733
+ root: bootingDevicesRoot(),
8734
+ reason: unresolved.reason,
8735
+ label: "device boot"
8736
+ });
8737
+ const booting = [];
8738
+ for (const holder of survey.live) {
8739
+ const { platform, key } = holder.details;
8740
+ if (typeof platform === "string" && typeof key === "string") booting.push({
8741
+ platform,
8742
+ key,
8743
+ displayName: typeof holder.details.displayName === "string" ? holder.details.displayName : void 0
8744
+ });
8745
+ }
8746
+ return booting;
8747
+ }
8748
+ function listSimsForCount() {
8749
+ return process.platform === "darwin" ? listAllIosSims({ timeoutMs: LISTING_TIMEOUT_MS }) : [];
8750
+ }
8751
+ function listAdbForCount() {
8752
+ return listAdbDevices({ timeoutMs: LISTING_TIMEOUT_MS });
8753
+ }
8754
+ function toolAbsent(error) {
8755
+ const { code, status, stderr } = error ?? {};
8756
+ return code === "ENOENT" || status === 127 || /unable to find utility|invalid active developer path|Xcode license/.test(String(stderr ?? ""));
8757
+ }
8758
+ function recordsOwnedEmulator(config) {
8759
+ return Object.values(config?.projects || {}).some((project) => projectDeviceSlots(project).some(({ platforms }) => platforms.android?.owned && typeof platforms.android.consolePort === "number"));
8760
+ }
8761
+ function liveOwnedDeviceKeys({ sims = [], adbEmulators = [], config = null, booting = [], exclude = [] }) {
8762
+ const keys = /* @__PURE__ */ new Set();
8763
+ for (const sim of sims) {
8764
+ if (!sim?.name?.startsWith("stim-")) continue;
8765
+ if (exclude.some((device) => device.platform === "ios" && device.key === sim.udid)) continue;
8766
+ if (LIVE_SIM_STATES.has(sim.state)) keys.add(deviceKey("ios", sim.udid));
8767
+ }
8768
+ const livePorts = new Set(adbEmulators.map((e) => e.consolePort));
8769
+ for (const proj of Object.values(config?.projects || {})) for (const { platforms } of projectDeviceSlots(proj)) {
8770
+ const android = platforms.android;
8771
+ if (android?.owned && android.avdName && typeof android.consolePort === "number" && livePorts.has(android.consolePort)) keys.add(deviceKey("android", android.avdName));
8772
+ }
8773
+ for (const device of booting) if (!exclude.some((excluded) => excluded.platform === device.platform && excluded.key === device.key)) keys.add(deviceKey(device.platform, device.key));
8774
+ return keys;
8775
+ }
8776
+ function inventoryKeys(inventory, exclude = []) {
8777
+ return liveOwnedDeviceKeys({
8778
+ ...inventory,
8779
+ adbEmulators: liveEmulatorPorts(inventory.adb),
8780
+ exclude
8781
+ });
8782
+ }
8783
+ const PLATFORM_CAPS = {
8784
+ ios: {
8785
+ setting: "concurrency.maxIosDevices",
8786
+ noun: "iOS simulator"
8787
+ },
8788
+ android: {
8789
+ setting: "concurrency.maxAndroidDevices",
8790
+ noun: "Android emulator"
8791
+ }
8792
+ };
8793
+ function isCappedPlatform(platform) {
8794
+ return platform === "ios" || platform === "android";
8795
+ }
8796
+ function platformCount(keys, platform) {
8797
+ return [...keys].filter((key) => key.startsWith(`${platform}:`)).length;
8798
+ }
8799
+ /** The caps that apply to a boot on `platform` given the booted `keys`, the platform's own cap first. */
8800
+ function deviceLimits(keys, platform, max, platformMax) {
8801
+ const limits = [];
8802
+ if (platformMax > 0 && isCappedPlatform(platform)) limits.push({
8803
+ ...PLATFORM_CAPS[platform],
8804
+ count: platformCount(keys, platform),
8805
+ max: platformMax,
8806
+ platform
8807
+ });
8808
+ if (max > 0) limits.push({
8809
+ setting: "concurrency.maxDevices",
8810
+ noun: "device",
8811
+ count: keys.size,
8812
+ max
8813
+ });
8814
+ return limits;
8815
+ }
8816
+ /** Whether a queued waiter is held back by its own platform cap, so it does not hold up the runs behind it. */
8817
+ function platformWalled(keys, waiter, platformMax) {
8818
+ const platform = String(waiter.details.platform);
8819
+ if (!isCappedPlatform(platform)) return false;
8820
+ const cap = platformMax[platform] ?? 0;
8821
+ return cap > 0 && platformCount(keys, platform) >= cap;
8822
+ }
8823
+ /** The first cap with no room left, or the first cap when all have room (for a run waiting behind another). */
8824
+ function bindingLimit(limits) {
8825
+ return limits.find((limit) => limit.count >= limit.max) ?? limits[0];
8826
+ }
8827
+ function atCapacityRefusal({ count, max, setting, noun }) {
8828
+ return {
8829
+ code: "STIM_AT_CAPACITY",
8830
+ message: count < max ? `${count}/${max} Stim ${noun}s are in use, but another run is ahead in the device slot queue.` : `${count} Stim ${noun}(s) are already booted and ${setting} is ${max}, so booting another would exceed the cap.`,
8831
+ remedy: `stop an environment (stim stop) or raise ${setting}`
8832
+ };
8833
+ }
8834
+ function uncountedRefusal(error) {
8835
+ return {
8836
+ code: "STIM_NO_DEVICE",
8837
+ message: `A device cap (concurrency.maxDevices, concurrency.maxIosDevices or concurrency.maxAndroidDevices) is set, and Stim could not count the booted devices: ${error?.message || error}`,
8838
+ remedy: `Listing devices times out when the machine is overloaded: retry once the load falls. Otherwise run \`stim doctor\` to check the simulator and adb toolchains, and check that ${join(getConfigDir(), "config.json")} and ${bootingDevicesRoot()} are readable.`
8839
+ };
8840
+ }
8841
+ function listed(source, empty, absent = () => false) {
8842
+ try {
8843
+ return (typeof source === "function" ? source() : source) ?? empty;
8844
+ } catch (error) {
8845
+ if (absent(error)) return empty;
8846
+ throw new DeviceCountUnavailable("", { cause: error });
8847
+ }
8848
+ }
8849
+ async function listedAwait(read, empty, absent = () => false) {
8850
+ try {
8851
+ return await read() ?? empty;
8852
+ } catch (error) {
8853
+ if (absent(error)) return empty;
8854
+ throw new DeviceCountUnavailable("", { cause: error });
8855
+ }
8856
+ }
8857
+ async function readInventoryAsync({ sims = () => process.platform === "darwin" ? listAllIosSimsAsync({ timeoutMs: LISTING_TIMEOUT_MS }) : [], adb = () => listAdbDevicesAsync({ timeoutMs: LISTING_TIMEOUT_MS }), config = loadConfig$1, booting = readBootingDevices }) {
8858
+ try {
8859
+ const recorded = await listedAwait(() => typeof config === "function" ? config() : config, null);
8860
+ return {
8861
+ booting: await listedAwait(() => typeof booting === "function" ? booting() : booting, []),
8862
+ sims: await listedAwait(() => typeof sims === "function" ? sims() : sims, [], toolAbsent),
8863
+ adb: recordsOwnedEmulator(recorded) ? await listedAwait(() => typeof adb === "function" ? adb() : adb, NO_ADB, toolAbsent) : NO_ADB,
8864
+ config: recorded
8865
+ };
8866
+ } catch (error) {
8867
+ if (error instanceof DeviceCountUnavailable) {
8868
+ if ((0, ownership_claim_exports.isClaimRefusal)(error.cause)) throw error.cause;
8869
+ return uncountedRefusal(error.cause);
8870
+ }
8871
+ throw error;
8872
+ }
8873
+ }
8874
+ function readInventory({ sims = listSimsForCount, adb = listAdbForCount, config = loadConfig$1, booting = readBootingDevices }) {
8875
+ try {
8876
+ const recorded = listed(config, null);
8877
+ return {
8878
+ booting: listed(booting, []),
8879
+ sims: listed(sims, [], toolAbsent),
8880
+ adb: recordsOwnedEmulator(recorded) ? listed(adb, NO_ADB, toolAbsent) : NO_ADB,
8881
+ config: recorded
8882
+ };
8883
+ } catch (error) {
8884
+ if (error instanceof DeviceCountUnavailable) {
8885
+ if ((0, ownership_claim_exports.isClaimRefusal)(error.cause)) throw error.cause;
8886
+ return uncountedRefusal(error.cause);
8887
+ }
8888
+ throw error;
8889
+ }
8890
+ }
8891
+ /**
8892
+ * Owned simulators that are booted or booting, owned emulators adb lists in any state, and devices another
8893
+ * run is booting under `concurrency.maxDevices`, each counted once unless excluded; when a listing fails, why the count is unknown.
8894
+ */
8895
+ function countLiveOwnedDevices(sources = {}, { exclude = [] } = {}) {
8896
+ const counts = countLiveOwnedDevicesByPlatform(sources, { exclude });
8897
+ return "unknown" in counts ? counts : counts.ios + counts.android;
8898
+ }
8899
+ /** The counts of {@link countLiveOwnedDevices} split by platform. */
8900
+ function countLiveOwnedDevicesByPlatform(sources = {}, { exclude = [] } = {}) {
8901
+ try {
8902
+ const inventory = readInventory(sources);
8903
+ if ("code" in inventory) return { unknown: inventory.message };
8904
+ const keys = inventoryKeys(inventory, exclude);
8905
+ return {
8906
+ ios: platformCount(keys, "ios"),
8907
+ android: platformCount(keys, "android")
8908
+ };
8909
+ } catch (error) {
8910
+ if ((0, ownership_claim_exports.isClaimRefusal)(error)) return { unknown: error.message };
8911
+ throw error;
8912
+ }
8913
+ }
8914
+ async function listedAsync(source, empty) {
8915
+ try {
8916
+ return await source();
8917
+ } catch (error) {
8918
+ if (toolAbsent(error)) return empty;
8919
+ throw new DeviceCountUnavailable("", { cause: error });
8920
+ }
8921
+ }
8922
+ async function countLiveOwnedDevicesFrom(sources, { exclude = [] } = {}) {
8923
+ try {
8924
+ const inventory = await readInventoryAsync(sources);
8925
+ return "code" in inventory ? { unknown: inventory.message } : inventoryKeys(inventory, exclude).size;
8926
+ } catch (error) {
8927
+ if ((0, ownership_claim_exports.isClaimRefusal)(error)) return { unknown: error.message };
8928
+ throw error;
8929
+ }
8930
+ }
8931
+ async function countLiveOwnedDevicesAsync(operation) {
8932
+ try {
8933
+ operation.signal.throwIfAborted();
8934
+ const config = loadConfig$1();
8935
+ const booting = readBootingDevices();
8936
+ const [sims, adb] = await Promise.all([process.platform === "darwin" ? listedAsync(() => listAllIosSimsAsync({ timeoutMs: LISTING_TIMEOUT_MS }), []) : [], recordsOwnedEmulator(config) ? listedAsync(() => listAdbDevicesAsync({ timeoutMs: LISTING_TIMEOUT_MS }), NO_ADB) : NO_ADB]);
8937
+ operation.signal.throwIfAborted();
8938
+ return countLiveOwnedDevices({
8939
+ config,
8940
+ booting,
8941
+ sims,
8942
+ adb
8943
+ });
8944
+ } catch (error) {
8945
+ operation.signal.throwIfAborted();
8946
+ if ((0, ownership_claim_exports.isClaimRefusal)(error)) return { unknown: error.message };
8947
+ if (error instanceof DeviceCountUnavailable) return { unknown: uncountedRefusal(error.cause).message };
8948
+ throw error;
8949
+ }
8950
+ }
8951
+ function workspaceHasLiveDevice({ platform, project, slot = "default", sims = [], adbEmulators = [] } = {}) {
8952
+ if (!platform) return false;
8953
+ const record = deviceSlotPlatforms(project, slot)?.[platform];
8954
+ if (!record) return false;
8955
+ if (platform === "ios") return sims.some((s) => s.udid === record.deviceUdid && LIVE_SIM_STATES.has(s.state));
8956
+ return typeof record.consolePort === "number" && adbEmulators.some((e) => e.consolePort === record.consolePort);
8957
+ }
8958
+ function deviceCapacityRefusal({ platform, project, slot = "default", max, platformMax, sims = [], adb = null, config = null, booting = [] } = {}) {
8959
+ if (!(max && max > 0) && !(platformMax && platformMax > 0)) return null;
8960
+ const adbEmulators = liveEmulatorPorts(adb ?? NO_ADB);
8961
+ if (workspaceHasLiveDevice({
8962
+ platform,
8963
+ project,
8964
+ slot,
8965
+ sims,
8966
+ adbEmulators
8967
+ })) return null;
8968
+ const full = deviceLimits(liveOwnedDeviceKeys({
8969
+ sims,
8970
+ adbEmulators,
8971
+ config,
8972
+ booting
8973
+ }), platform ?? "", max ?? 0, platformMax ?? 0).find((limit) => limit.count >= limit.max);
8974
+ return full ? {
8975
+ ...atCapacityRefusal(full),
8976
+ max: full.max
8977
+ } : null;
8978
+ }
8979
+ /**
8980
+ * The early `concurrency.maxDevices` check, before Metro starts or a device is created. It is advisory, so an
8981
+ * unknown count passes: `withDeviceBootAdmission` makes the binding decision when the boot starts.
8982
+ */
8983
+ async function checkDeviceCapacity({ platform, project, slot = "default", max, platformMax = platform && isCappedPlatform(platform) ? getPlatformDeviceLimits()[platform] : 0, ...sources } = {}) {
8984
+ if (!(max && max > 0) && !(platformMax && platformMax > 0)) return null;
8985
+ let inventory;
8986
+ try {
8987
+ inventory = await readInventoryAsync(sources);
8988
+ } catch (error) {
8989
+ if ((0, ownership_claim_exports.isClaimRefusal)(error)) return null;
8990
+ throw error;
8991
+ }
8992
+ if ("code" in inventory) return null;
8993
+ return deviceCapacityRefusal({
8994
+ platform,
8995
+ project,
8996
+ slot,
8997
+ max,
8998
+ platformMax,
8999
+ ...inventory
9000
+ });
9001
+ }
9002
+ async function peekDeviceSlots({ platform, project, slot = "default", max = getConcurrencyLimits().maxDevices, platformMax = getPlatformDeviceLimits()[platform], sources = {} }) {
9003
+ try {
9004
+ const inventory = await readInventoryAsync(sources);
9005
+ if ("code" in inventory) return {
9006
+ count: null,
9007
+ max,
9008
+ queued: 0,
9009
+ localLive: false
9010
+ };
9011
+ const owned = deviceSlotPlatforms(project, slot)?.[platform]?.owned === true;
9012
+ const keys = inventoryKeys(inventory);
9013
+ const limits = deviceLimits(keys, platform, max, platformMax);
9014
+ const tightest = limits.length ? limits.reduce((a, b) => b.max - b.count < a.max - a.count ? b : a) : void 0;
9015
+ return {
9016
+ count: tightest?.count ?? keys.size,
9017
+ max: tightest?.max ?? max,
9018
+ queued: liveWaiters().filter((waiter) => !platformWalled(keys, waiter, {
9019
+ ...getPlatformDeviceLimits(),
9020
+ [platform]: platformMax
9021
+ })).length,
9022
+ localLive: owned && workspaceHasLiveDevice({
9023
+ platform,
9024
+ project,
9025
+ slot,
9026
+ sims: inventory.sims,
9027
+ adbEmulators: liveEmulatorPorts(inventory.adb)
9028
+ }),
9029
+ ...tightest?.platform ? { limit: tightest.setting } : {}
9030
+ };
9031
+ } catch (error) {
9032
+ if (!(0, ownership_claim_exports.isClaimRefusal)(error)) throw error;
9033
+ return {
9034
+ count: null,
9035
+ max,
9036
+ queued: 0,
9037
+ localLive: false
9038
+ };
9039
+ }
9040
+ }
9041
+ const DEFAULT_DEVICE_SLOT_WAIT_MS = 6e5;
9042
+ function deviceSlotWaitingLine({ count, max, holders, elapsedMs, queue, setting }) {
9043
+ return `${"device".padEnd(11)} waiting for a device slot (${count}/${max} in use${setting ? ` by ${setting}` : ""}${holders.length ? `: ${holders.join(", ")}` : ""}), ${formatElapsed(elapsedMs)} elapsed -- stim guide lifecycle concurrency${queue ? `; ${queue}` : ""}`;
9044
+ }
9045
+ function unrecordedHolder(label) {
9046
+ return {
9047
+ label,
9048
+ workspace: null,
9049
+ slot: null
9050
+ };
9051
+ }
9052
+ function slotHolders(inventory, keys) {
9053
+ const owners = /* @__PURE__ */ new Map();
9054
+ const projects = Object.entries(inventory.config?.projects ?? {});
9055
+ const labels = workspaceLabels(projects.map(([root, project]) => ({
9056
+ root,
9057
+ label: project.label
9058
+ })));
9059
+ for (const [root, project] of projects) {
9060
+ const label = labels.get(root) ?? basename(root);
9061
+ for (const { slot, platforms } of projectDeviceSlots(project)) {
9062
+ if (platforms.ios?.deviceUdid) owners.set(deviceKey("ios", platforms.ios.deviceUdid), {
9063
+ label,
9064
+ workspace: root,
9065
+ slot
9066
+ });
9067
+ if (platforms.android?.avdName) owners.set(deviceKey("android", platforms.android.avdName), {
9068
+ label,
9069
+ workspace: root,
9070
+ slot
9071
+ });
9072
+ }
9073
+ }
9074
+ for (const sim of inventory.sims) {
9075
+ const key = deviceKey("ios", sim.udid);
9076
+ if (!owners.has(key)) owners.set(key, unrecordedHolder(sim.name));
9077
+ }
9078
+ const booting = /* @__PURE__ */ new Set();
9079
+ for (const device of inventory.booting) {
9080
+ const key = deviceKey(device.platform, device.key);
9081
+ booting.add(key);
9082
+ if (!owners.has(key)) owners.set(key, unrecordedHolder(device.displayName ?? device.key));
9083
+ }
9084
+ return [...keys].map((key) => {
9085
+ const { label, workspace, slot } = owners.get(key) ?? unrecordedHolder(key.slice(key.indexOf(":") + 1));
9086
+ const platform = key.startsWith("android:") ? "android" : "ios";
9087
+ return booting.has(key) ? {
9088
+ platform,
9089
+ label,
9090
+ workspace,
9091
+ slot,
9092
+ booting: true
9093
+ } : {
9094
+ platform,
9095
+ label,
9096
+ workspace,
9097
+ slot
9098
+ };
9099
+ }).toSorted((a, b) => a.label.localeCompare(b.label) || a.platform.localeCompare(b.platform));
9100
+ }
9101
+ function deviceHolders(inventory, keys) {
9102
+ return [...new Set(slotHolders(inventory, keys).map((holder) => holder.label))].toSorted();
9103
+ }
9104
+ /**
9105
+ * `concurrency.maxDevices` usage for status (the owned devices the cap counts and who holds each) and the usage of each
9106
+ * per-platform cap that is set, from one inventory read. `sims` is the listing status already read; an unknown count
9107
+ * reports why instead of failing.
9108
+ */
9109
+ async function deviceSlotUsages(max, platformMax, sources = {}) {
9110
+ const capped = [];
9111
+ for (const platform of ["ios", "android"]) {
9112
+ const cap = platformMax[platform] ?? 0;
9113
+ if (cap > 0) capped.push([platform, cap]);
9114
+ }
9115
+ const platformDevices = (count) => capped.length ? { platformDevices: Object.fromEntries(capped.map(([platform, cap]) => [platform, {
9116
+ max: cap,
9117
+ ...count(platform)
9118
+ }])) } : {};
9119
+ try {
9120
+ const inventory = await readInventoryAsync(sources);
9121
+ if ("code" in inventory) return {
9122
+ ...max > 0 ? { devices: {
9123
+ max,
9124
+ inUse: null,
9125
+ unknown: inventory.message,
9126
+ holders: []
9127
+ } } : {},
9128
+ ...platformDevices(() => ({
9129
+ inUse: null,
9130
+ unknown: inventory.message
9131
+ }))
9132
+ };
9133
+ const keys = inventoryKeys(inventory);
9134
+ return {
9135
+ ...max > 0 ? { devices: {
9136
+ max,
9137
+ inUse: keys.size,
9138
+ holders: slotHolders(inventory, keys)
9139
+ } } : {},
9140
+ ...platformDevices((platform) => ({ inUse: platformCount(keys, platform) }))
9141
+ };
9142
+ } catch (error) {
9143
+ if (!(0, ownership_claim_exports.isClaimRefusal)(error)) throw error;
9144
+ return {
9145
+ ...max > 0 ? { devices: {
9146
+ max,
9147
+ inUse: null,
9148
+ unknown: error.message,
9149
+ holders: []
9150
+ } } : {},
9151
+ ...platformDevices(() => ({
9152
+ inUse: null,
9153
+ unknown: error.message
9154
+ }))
9155
+ };
9156
+ }
9157
+ }
9158
+ function liveWaiters() {
9159
+ const root = join(getConfigDir(), "device-waits");
9160
+ const survey = (0, ownership_claim_exports.inspectClaimSet)(root, { label: "device wait" });
9161
+ if (survey.exclusive) throw new ownership_claim_exports.ClaimRefusedError({
9162
+ root,
9163
+ claimPath: survey.exclusive.path,
9164
+ reason: "the wait queue is held exclusively",
9165
+ label: "device wait"
9166
+ });
9167
+ return survey.shared.toSorted((a, b) => (Number(a.details.sequence) || 0) - (Number(b.details.sequence) || 0) || a.startedAt.localeCompare(b.startedAt) || a.claimId.localeCompare(b.claimId));
9168
+ }
9169
+ function takeWaitTicket(device, waiters) {
9170
+ const root = join(getConfigDir(), "device-waits");
9171
+ const attempt = (0, ownership_claim_exports.tryAcquireClaim)({
9172
+ root,
9173
+ mode: "shared",
9174
+ label: "device wait",
9175
+ details: {
9176
+ ...device,
9177
+ sequence: Math.max(0, ...waiters.map((holder) => Number(holder.details.sequence) || 0)) + 1
9178
+ }
9179
+ });
9180
+ if (attempt.acquired) return attempt.acquired;
9181
+ throw new ownership_claim_exports.ClaimRefusedError({
9182
+ root,
9183
+ claimPath: attempt.held?.path ?? root,
9184
+ reason: "the wait queue is held exclusively",
9185
+ label: "device wait"
9186
+ });
9187
+ }
9188
+ function takeBootMarker(device) {
9189
+ const attempt = (0, ownership_claim_exports.tryAcquireClaim)({
9190
+ root: bootingDevicesRoot(),
9191
+ mode: "shared",
9192
+ label: "device boot",
9193
+ details: { ...device }
9194
+ });
9195
+ if (attempt.acquired) return attempt.acquired;
9196
+ throw new DeviceAdmissionRefusal({
9197
+ code: "STIM_NO_DEVICE",
9198
+ message: `Another process holds ${bootingDevicesRoot()} exclusively, so this boot cannot be counted toward the device caps.`,
9199
+ remedy: "Retry once that process finishes."
9200
+ });
9201
+ }
9202
+ async function admissionTransaction(action, lockWaitMs, out, signal) {
9203
+ const started = Date.now();
9204
+ let lastLine = null;
9205
+ const onHeld = () => {
9206
+ const elapsed = Date.now() - started;
9207
+ if (elapsed < LOCK_QUIET_MS || lastLine !== null && Date.now() - lastLine < LOCK_PROGRESS_MS) return;
9208
+ lastLine = Date.now();
9209
+ out(phaseLine("device", `waiting for other runs to finish counting booted devices (${formatElapsed(elapsed)})`));
9210
+ };
9211
+ try {
9212
+ return await withWorkspaceProcessLock(getConfigDir(), ADMISSION_LOCK, action, {
9213
+ external: true,
9214
+ waitMs: lockWaitMs,
9215
+ onHeld,
9216
+ signal
9217
+ });
9218
+ } catch (error) {
9219
+ if (workspaceProcessLockError(error) !== "timeout") throw error;
9220
+ throw new DeviceAdmissionRefusal({
9221
+ code: "STIM_NO_DEVICE",
9222
+ message: `Waited ${formatElapsed(lockWaitMs)} for other runs to finish counting booted devices for the device caps.`,
9223
+ remedy: "Listing devices is slow, which usually means the machine is overloaded. Retry once the load falls."
9224
+ });
9225
+ }
9226
+ }
9227
+ /** Boots an owned device under the Stim home's cap, reserving capacity before releasing admission. */
9228
+ async function withDeviceBootAdmission(device, boot, { root, max = getConcurrencyLimits().maxDevices, platformMax = getPlatformDeviceLimits(), sources = {}, lockWaitMs = ADMISSION_LOCK_WAIT_MS, out = () => {}, waitMs = DEFAULT_DEVICE_SLOT_WAIT_MS, noWait = false, automatic = false, displayName = workspaceDisplayLabel(root, loadConfig$1()), reclaimIdleMinutes = deviceReclaimIdleMinutesSetting({}), now = Date.now, signal, sleep = (ms) => setTimeout$1(ms, void 0, { signal }), waitingFor = () => {}, onWait = () => {}, onBootStart = () => {} }) {
9229
+ const requireMembership = () => {
9230
+ if (automatic && !automaticMachineEnabled("device", "local")) throw new DeviceAdmissionRefusal({
9231
+ code: "STIM_HOSTING_REFUSED",
9232
+ message: "local is disabled in this requester's automatic device pool.",
9233
+ remedy: "Enable local in remote.devicePoolDisabled or select a machine explicitly."
9234
+ });
9235
+ };
9236
+ signal?.throwIfAborted();
9237
+ requireMembership();
9238
+ const ownCap = isCappedPlatform(device.platform) ? platformMax[device.platform] ?? 0 : 0;
9239
+ if (!(max > 0) && !(ownCap > 0)) {
9240
+ onBootStart();
9241
+ return boot();
9242
+ }
9243
+ const workspace = workspaceId(root);
9244
+ device = {
9245
+ ...device,
9246
+ workspace,
9247
+ displayName
9248
+ };
9249
+ let ticket;
9250
+ let marker;
9251
+ let started;
9252
+ let lastLine = -Infinity;
9253
+ let visibleWait = false;
9254
+ let lastInventory;
9255
+ let lastTurn = false;
9256
+ let binding;
9257
+ let lastListed = -Infinity;
9258
+ let reclaimed = 0;
9259
+ let lastReclaim = -Infinity;
9260
+ const skippedReclaims = /* @__PURE__ */ new Set();
9261
+ try {
9262
+ try {
9263
+ for (;;) {
9264
+ const result = await admissionTransaction(async () => {
9265
+ signal?.throwIfAborted();
9266
+ requireMembership();
9267
+ const waiters = liveWaiters();
9268
+ const first = waiters[0];
9269
+ const mine = waiters.findIndex((holder) => holder.claimId === ticket?.claimId);
9270
+ const ahead = mine < 0 ? waiters : waiters.slice(0, mine);
9271
+ const admit = () => {
9272
+ const admitted = takeBootMarker(device);
9273
+ if ((0, ownership_claim_exports.releaseClaim)(ticket)) ticket = void 0;
9274
+ return { marker: admitted };
9275
+ };
9276
+ const wait = ({ limit, holders }, turn) => {
9277
+ const ticketId = ticket?.claimId;
9278
+ const position = ticketId ? waiters.findIndex((holder) => holder.claimId === ticketId) + 1 : waiters.length + 1;
9279
+ const headName = first?.details.displayName ?? displayName;
9280
+ const queue = `queue position ${position}${position > 1 ? " behind" : ", head"} ${headName}; queue: ${join(getConfigDir(), "device-waits")}`;
9281
+ if (noWait || waitMs === 0) throw new DeviceAdmissionRefusal(atCapacityRefusal(limit));
9282
+ if (started !== void 0 && now() - started >= waitMs) throw new DeviceAdmissionRefusal({
9283
+ code: "STIM_AT_CAPACITY",
9284
+ message: `Waited ${formatElapsed(now() - started)} for a device slot; ${limit.count}/${limit.max} Stim ${limit.noun}s are in use (${limit.setting}); ${queue}.`,
9285
+ remedy: `Stop an environment (stim stop), retry with a longer --wait <seconds>, or raise ${limit.setting}.`
9286
+ });
9287
+ if (!ticket) {
9288
+ ticket = takeWaitTicket(device, waiters);
9289
+ started = now();
9290
+ }
9291
+ const firstOfPlatform = ahead.every((holder) => holder.details.platform !== device.platform);
9292
+ return {
9293
+ limit,
9294
+ holders,
9295
+ queue,
9296
+ reclaim: turn && firstOfPlatform && limit.count >= limit.max
9297
+ };
9298
+ };
9299
+ if (ticket && lastInventory && ahead.length > 0 && !lastTurn && now() - lastListed < INVENTORY_REFRESH_MS) {
9300
+ const booting = sources.booting ?? readBootingDevices;
9301
+ const own = deviceKey(device.platform, device.key);
9302
+ if ((typeof booting === "function" ? booting() : booting).some((entry) => deviceKey(entry.platform, entry.key) === own)) return admit();
9303
+ return wait(lastInventory, false);
9304
+ }
9305
+ const inventory = await readInventoryAsync(sources);
9306
+ if ("code" in inventory) throw new DeviceAdmissionRefusal(inventory);
9307
+ const keys = inventoryKeys(inventory);
9308
+ const own = deviceKey(device.platform, device.key);
9309
+ const ownBooting = inventory.booting.some((entry) => deviceKey(entry.platform, entry.key) === own);
9310
+ if (device.platform === "ios" && keys.has(own) || ownBooting) return admit();
9311
+ keys.delete(own);
9312
+ const limits = deviceLimits(keys, device.platform, max, ownCap);
9313
+ const turn = ahead.every((waiter) => platformWalled(keys, waiter, platformMax));
9314
+ if (turn && limits.every((limit) => limit.count < limit.max)) return admit();
9315
+ lastListed = now();
9316
+ lastTurn = turn;
9317
+ const limit = bindingLimit(limits);
9318
+ if (!limit) return admit();
9319
+ binding = limit;
9320
+ lastInventory = {
9321
+ limit,
9322
+ holders: deviceHolders(inventory, limit.platform ? new Set([...keys].filter((key) => key.startsWith(`${limit.platform}:`))) : keys)
9323
+ };
9324
+ return wait(lastInventory, turn);
9325
+ }, lockWaitMs, out, signal);
9326
+ if ("marker" in result) {
9327
+ marker = result.marker;
9328
+ signal?.throwIfAborted();
9329
+ break;
9330
+ }
9331
+ signal?.throwIfAborted();
9332
+ const elapsedMs = now() - started;
9333
+ visibleWait = true;
9334
+ waitingFor({
9335
+ kind: "device-slot",
9336
+ inUse: result.limit.count,
9337
+ max: result.limit.max,
9338
+ since: new Date(started).toISOString()
9339
+ });
9340
+ if (now() - lastLine >= 1e4) {
9341
+ lastLine = now();
9342
+ out(deviceSlotWaitingLine({
9343
+ count: result.limit.count,
9344
+ max: result.limit.max,
9345
+ holders: result.holders,
9346
+ elapsedMs,
9347
+ queue: result.queue,
9348
+ ...result.limit.platform ? { setting: result.limit.setting } : {}
9349
+ }));
9350
+ }
9351
+ signal?.throwIfAborted();
9352
+ if (result.reclaim && now() - lastReclaim >= 15e3) {
9353
+ lastReclaim = now();
9354
+ try {
9355
+ if (reclaimIdleMinutes > 0) reclaimed += await reclaimIdleDevice(root, reclaimIdleMinutes * 6e4, out, now, skippedReclaims, result.limit.platform);
9356
+ } catch (error) {
9357
+ out(phaseLine("device", `could not reclaim an idle device: ${error?.message || error}`));
9358
+ }
9359
+ }
9360
+ await sleep(Math.min(2e3, Math.max(0, waitMs - elapsedMs)));
9361
+ }
9362
+ } catch (error) {
9363
+ if (error instanceof DeviceAdmissionRefusal && error.code === "STIM_AT_CAPACITY") {
9364
+ const at = now();
9365
+ await withConfigLockAsync$1(() => recordCapacityRefusal({
9366
+ platform: device.platform,
9367
+ max: binding?.max ?? max,
9368
+ workspace
9369
+ }, at)).catch(() => {});
9370
+ }
9371
+ throw error;
9372
+ } finally {
9373
+ (0, ownership_claim_exports.releaseClaim)(ticket);
9374
+ if (visibleWait) waitingFor(null);
9375
+ if (started !== void 0) {
9376
+ const ms = Math.max(0, Math.round(now() - started));
9377
+ const at = now();
9378
+ await withConfigLockAsync$1(() => recordCapacityWait({
9379
+ platform: device.platform,
9380
+ ms,
9381
+ max: binding?.max ?? max,
9382
+ workspace,
9383
+ ...reclaimed ? { reclaimed } : {}
9384
+ }, at)).catch(() => {});
9385
+ onWait(ms);
9386
+ }
9387
+ }
9388
+ requireMembership();
9389
+ onBootStart();
9390
+ return await boot();
9391
+ } finally {
9392
+ (0, ownership_claim_exports.releaseClaim)(marker);
9393
+ }
9394
+ }
9395
+ function deviceTypeMismatch(recordedTypeId, requestedName, deviceTypes) {
9396
+ if (!requestedName || !recordedTypeId) return null;
9397
+ const wanted = (deviceTypes || []).find((d) => d.name === requestedName);
9398
+ if (!wanted) return null;
9399
+ if (wanted.identifier === recordedTypeId) return null;
9400
+ const recorded = (deviceTypes || []).find((d) => d.identifier === recordedTypeId);
9401
+ return `this project's sim is ${recorded ? recorded.name : recordedTypeId}, but --device-type asked for ${requestedName}`;
9402
+ }
9403
+ function runtimeMismatch(recordedRuntimeId, requested, runtimes) {
9404
+ if (!requested || !recordedRuntimeId) return null;
9405
+ const wanted = runtimes.find((r) => iosRuntimeMatches(r, requested));
9406
+ if (!wanted || wanted.identifier === recordedRuntimeId) return null;
9407
+ return `this project's sim runs iOS ${parseRuntimeVersion(recordedRuntimeId)}, but --runtime asked for ${requested}`;
9408
+ }
9409
+ function installedNames(names) {
9410
+ const unique = [...new Set(names.filter((n) => typeof n === "string" && n !== ""))];
9411
+ return unique.length > 0 ? unique.join(", ") : "none";
9412
+ }
9413
+ /** Appends which settings layer supplied an unresolved device selector, so the user knows where to fix it. */
9414
+ function layerNote(refusal, key, flag, origin) {
9415
+ if (flag !== void 0 && flag !== null) return refusal;
9416
+ if (!origin) return refusal;
9417
+ return {
9418
+ message: `${refusal.message} ${key} is set at the ${origin} layer.`,
9419
+ remedy: `${refusal.remedy} Fix it with \`stim settings set ${key} <value> --scope ${origin}\` or \`stim settings unset ${key} --scope ${origin}\`.`
9420
+ };
9421
+ }
9422
+ function unknownIosRuntimeRefusal(requested, runtimes) {
9423
+ if (!requested) return null;
9424
+ if ((runtimes || []).some((r) => iosRuntimeMatches(r, requested))) return null;
9425
+ return {
9426
+ message: `No installed simulator runtime matches "${requested}". Installed runtimes: ${installedNames((runtimes || []).map((r) => r.version))}.`,
9427
+ remedy: "Pass `--runtime` (or set ios.runtime) a version printed above (\"26.5\") or a runtime's full name (\"iOS 26.5\"); nothing else matches. Install more runtimes through Xcode."
9428
+ };
9429
+ }
9430
+ function creatableIosDeviceTypeNames(runtimes, runtime) {
9431
+ return (runtime ? (runtimes || []).filter((r) => iosRuntimeMatches(r, runtime)) : runtimes || []).flatMap((r) => (r.supportedDeviceTypes || []).map((d) => d.name));
9432
+ }
9433
+ function unknownIosDeviceTypeRefusal(requested, runtimes, runtime) {
9434
+ if (!requested) return null;
9435
+ const creatable = creatableIosDeviceTypeNames(runtimes, runtime);
9436
+ if (creatable.includes(requested)) return null;
9437
+ return {
9438
+ message: `No device type named "${requested}" can be created on ${runtime ? `runtime ${runtime}` : "any installed simulator runtime"}. ${runtime ? `Device types runtime ${runtime} supports` : "Device types the installed runtimes support"}: ${installedNames(creatable)}.`,
9439
+ remedy: "Pass `--device-type` (or set ios.deviceType) one of the names printed above, exactly as `xcrun simctl list devicetypes` spells it. `xcrun simctl list devicetypes` also lists watchOS, tvOS and visionOS models, which no iOS runtime can create. Install more models through Xcode."
9440
+ };
9441
+ }
9442
+ function unknownAndroidSystemImageRefusal(requested, images) {
9443
+ if (!requested) return null;
9444
+ if ((images || []).some((i) => i.pkg === requested)) return null;
9445
+ return {
9446
+ message: `No installed Android system image is named "${requested}". Installed system images: ${installedNames((images || []).map((i) => i.pkg))}.`,
9447
+ remedy: "Pass `--system-image` (or set android.systemImage) to one of the package ids printed above."
9448
+ };
9449
+ }
9450
+ function unknownAndroidDeviceProfileRefusal(requested, profiles) {
9451
+ if (!requested) return null;
9452
+ if (profiles.includes(requested)) return null;
9453
+ return {
9454
+ message: `No Android hardware profile is named "${requested}". Profiles avdmanager offers: ${installedNames(profiles)}.`,
9455
+ remedy: "Pass `--device-profile` (or set android.deviceProfile) to one of the ids printed above, exactly as `avdmanager list device -c` spells it, e.g. \"pixel_fold\" or \"pixel_tablet\"."
9456
+ };
9457
+ }
9458
+ //#endregion
9459
+ export { levelStillShort as $, providerFromConfig as $n, remoteAndroidDeps as $t, budgetLine as A, installAndroidApp as An, sizeCaches as Ar, deleteParkedAvds as At, stopOwnedMetro as B, readRemoteSimulatorArch as Bn, unverifiedDeviceCommand as Bt, CCACHE_UNAVAILABLE as C, androidDevClientUrl as Cn, scopedMemoryReport as Cr, waitForGradleExit as Ct, readCcacheActivity as D, deviceShellArg as Dn, readFootprints as Dr, collectDeviceLeases as Dt, projectCmakeLauncher as E, devClientUrl as En, attributeMachineUsage as Er, DEVICE_LIST_TIMEOUT_MS$1 as Et, peekBudget as F, launchAndroidReleaseApp as Fn, deviceSweepIsScoped as Ft, devServerStopRecord as G, RESOLVE_TIMEOUT_MS as Gn, withRemoteSessionGcLocks as Gt, describeDevServerStop as H, exitAfterFlush as Hn, collectEasSessionSweep as Ht, readVolumeSpace as I, launchIosApp as In, eraseParkedDevices as It, requestDevServerStop as J, checkEasAuth as Jn, bundleEntryPoint as Jt, logVanishedSupervisor as K, UPLOAD_TIMEOUT_MS as Kn, REMOTE_SESSION_ERROR as Kt, readVolumeSpaceAsync as L, restartedAppNote as Ln, findOrphanedDevices as Lt, enforceBudget as M, installIosApp as Mn, deleteProjectDevices as Mt, inspectBudget as N, iosAppProcess as Nn, describeParkedSims as Nt, resolveCcache as O, ensureMetroReverse as On, discoverCaches as Or, collectParkedAvds as Ot, machineNumber as P, launchAndroidApp as Pn, describeUnverifiableDevices as Pt, levelFloors as Q, ownerFromConfig as Qn, readInstalledEasCliVersionAsync as Qt, resolveBudget as R, reverseMetroPorts as Rn, findStaleDeviceRecords as Rt, CCACHE_NOT_RUN as S, androidAppProcess as Sn, reclaimMemory as Sr, includesGradleVersions as St, ccacheInstallCommand as T, clearOtherUserApps as Tn, phase as Tr, collectInventory as Tt, describeMetroLastStop as U, processContext as Un, deleteEasSessions as Ut, stopOwnedMetroForReset as V, gateMetroOrigin as Vn, withAndroidAvdSizes as Vt, devServerStopLevel as W, runProcessOperation as Wn, describeError as Wt, withdrawDevServerStopRequest as X, isEasAuthFailureText as Xn, ensureMetroReachable as Xt, vanishedSupervisorMessage as Y, easAuthNote as Yn, endRecordedSession as Yt, diskPressureLevel as Z, loadProjectProvider as Zn, ensureRemoteBootOwned as Zt, withDeviceBootAdmission as _, APP_READINESS_TIMEOUT_MS as _n, memoryCacheKind as _r, collectGradleVersions as _t, countLiveOwnedDevicesAsync as a, LAUNCH_UNVERIFIED as an, remoteCliInvocation as ar, maintenanceLogger as at, shutDownIdleDevices as b, INSTALL_ERROR as bn, memorySweepIsScoped as br, gradleVersionLines as bt, deviceSlotUsages as c, VERIFY_TIMEOUT_MS as cn, MIN_EAS_CLI_SIMULATOR_VERSION as cr, includesParkedDevices as ct, peekDeviceSlots as d, readMetroRecords as dn, ownedSessionName as dr, isWorkspacesScope as dt, remoteIosDeps as en, readProjectConfig as er, lowestFree as et, runtimeMismatch as f, unattributedLaunchLines as fn, parseArchiveSelector as fr, planCacheEmptying as ft, unknownIosRuntimeRefusal as g, verifyReleaseLaunch as gn, inspectWatchmanMemory as gr, trimCaches as gt, unknownIosDeviceTypeRefusal as h, verifyLaunch as hn, collectMemoryReport as hr, selectsBeyondCaches as ht, countLiveOwnedDevices as i, LAUNCH_FATAL as in, bundleInvocation as ir, openGcLog as it, cacheLabel as j, installConflictKind as jn, deleteParkedSims as jt, budgetGate as k, hasMetroReverse as kn, pruneCache as kr, collectParkedSims as kt, deviceTypeMismatch as l, isBundleProof as ln, easCliSupport as lr, includesRecordings as lt, unknownAndroidSystemImageRefusal as m, verifyAndroidReleaseLaunch as mn, WATCHMAN_KIND as mr, selectCaches as mt, DeviceAdmissionRefusal as n, withEasProjectLock as nn, resolveRemote as nr, gcOptionsOf as nt, countLiveOwnedDevicesByPlatform as o, MANIFEST_VERIFY_TIMEOUT_MS as on, remoteCommand as or, emptyCaches as ot, unknownAndroidDeviceProfileRefusal as p, unverifiedLaunchLines as pn, GRADLE_DAEMONS_KIND as pr, previewScopedEmptying as pt, metroLastStop as q, cacheLevel as qn, binOnPath as qt, checkDeviceCapacity as r, LAUNCH_BUNDLING as rn, uploadRemote as rr, maintenanceItem as rt, countLiveOwnedDevicesFrom as s, RELEASE_VERIFY_WAIT_MS as sn, MIN_EAS_CLI_DEVICE_VERSION as sr, includesArchives as st, DEFAULT_DEVICE_SLOT_WAIT_MS as t, resolveRemoteContext as tn, resolveEasCliBin as tr, protectWindowMs as tt, layerNote as u, readCollectorRecords as un, easCliUpgradeRemedy as ur, includesWorkspaceOutputs as ut, clearDeviceIdleShutdown as v, ADB_INSTALL_TIMEOUT_MS as vn, memoryLines as vr, deleteGradleVersion as vt, ccacheActivityLine as w, appProcessFromPid as wn, stopIdleGradleDaemons as wr, getMachineCacheReport as wt, writeIdleShutdownEvent as x, IOS_DEV_MENU_OFF_DEFAULTS_PLIST as xn, memoryWorkPending as xr, gradleVersionsPending as xt, dueIdleDevices as y, DEFAULT_METRO_PORT as yn, memoryScope as yr, deleteGradleVersions as yt, trimmableCaches as z, writeDebugHttpHost as zn, findStaleProjectDevices as zt };