stim 1.23.1 → 1.24.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 (169) hide show
  1. package/README.md +3 -2
  2. package/dist/{acquire-artifact-BzYRDgaP.mjs → acquire-artifact-CEs02U2o.mjs} +2 -2
  3. package/dist/{act-vxiMwdf5.mjs → act-Cyvm8xYc.mjs} +40 -25
  4. package/dist/{activity-BpWUPgUm.mjs → activity-Dq3ZcdD8.mjs} +6 -6
  5. package/dist/{agent-device-usage-output-CUlLsbRt.mjs → agent-device-usage-output-CccomeE0.mjs} +5 -5
  6. package/dist/{android-C7sVmyI9.mjs → android-882m4hxX.mjs} +140 -46
  7. package/dist/{android-DtfRGLcc.mjs → android-BZppyPe_.mjs} +66 -46
  8. package/dist/{android-DR5lZ2Az.mjs → android-CX5IcRuf.mjs} +13 -3
  9. package/dist/{android-CLi6DSiS.mjs → android-DcztblCT.mjs} +5 -5
  10. package/dist/{android-cas-ngferVq2.mjs → android-cas-BtCOrNqb.mjs} +3 -3
  11. package/dist/android-cas-compiler.mjs +1 -1
  12. package/dist/api.d.mts +21 -4
  13. package/dist/api.mjs +32 -31
  14. package/dist/{artifact-Xvgibgzq.mjs → artifact-BR5VNArK.mjs} +17 -16
  15. package/dist/{artifact-DBI3DALs.mjs → artifact-Bdt_n9Jc.mjs} +17 -16
  16. package/dist/{artifact-platform-CXcmX_j3.mjs → artifact-platform-CMRc0M_r.mjs} +3 -3
  17. package/dist/{browser-web-BJN9ZHxB.mjs → browser-web-ycg9qWB8.mjs} +16 -17
  18. package/dist/{budget-3ivAK99e.mjs → budget-dPGuhNai.mjs} +101 -40
  19. package/dist/{build-Dv9s8Fz-.mjs → build-CaF574eS.mjs} +31 -28
  20. package/dist/{build-cache-Dv7RN200.mjs → build-cache-HFUQ9eLz.mjs} +152 -19
  21. package/dist/{build-plan-BmbdHQbr.mjs → build-plan-JsDUq4w1.mjs} +8 -7
  22. package/dist/{cache-manifest-DSeQjt-Q.mjs → cache-manifest-BPXRONJz.mjs} +1 -1
  23. package/dist/cache-manifest.mjs +1 -1
  24. package/dist/{ccache-CH2EWGPB.mjs → ccache-CCn1S6qG.mjs} +6 -6
  25. package/dist/{cdp-3fwICjzi.mjs → cdp-BG3FJCCx.mjs} +1 -1
  26. package/dist/{chrome-Bbw689-6.mjs → chrome-BvV8ssI3.mjs} +1 -1
  27. package/dist/{cli-BkK6Wr5i.mjs → cli-xqAidP7O.mjs} +20 -20
  28. package/dist/cli.mjs +1 -1
  29. package/dist/{client-B4MMzfo_.mjs → client-18-ZowoT.mjs} +254 -28
  30. package/dist/collector-run.mjs +6 -6
  31. package/dist/{command-output-YKGWivpG.mjs → command-output-DxC0k2rk.mjs} +3 -2
  32. package/dist/{config-DlrsduwG.mjs → config-BZhXY15H.mjs} +3 -3
  33. package/dist/{created-devices-x9rN6bhs.mjs → created-devices-D2Aca-xu.mjs} +6 -2
  34. package/dist/{dependency-state-1I9KZT1Y.mjs → dependency-state-C56ll9hY.mjs} +2 -2
  35. package/dist/{detached-entry-BwNEtIrP.mjs → detached-entry-KmieOKgN.mjs} +1 -1
  36. package/dist/{dev-client-BFxiDpkf.mjs → dev-client-w_fok0Ek.mjs} +3 -3
  37. package/dist/{device-B7ljwccX.mjs → device-4RUugw5X.mjs} +16 -16
  38. package/dist/{device-CiNYxqp6.mjs → device-BktRGdjS.mjs} +40 -44
  39. package/dist/{device-capacity-DH0QsZYZ.mjs → device-capacity-Ic-kdmEV.mjs} +20 -16
  40. package/dist/device-host-worker.mjs +17 -17
  41. package/dist/{device-ios-CbVDtDIy.mjs → device-ios-DzpSlZyd.mjs} +23 -52
  42. package/dist/{device-lease-run-DXPSwdbb.mjs → device-lease-run-DUtkbqBQ.mjs} +5 -5
  43. package/dist/{device-pool-B4iOXIrD.mjs → device-pool-CgBHk401.mjs} +3 -3
  44. package/dist/{doctor-DRu-8ld6.mjs → doctor-BZ6oxNum.mjs} +1 -1
  45. package/dist/{doctor-HUC1igzw.mjs → doctor-C33AzVfR.mjs} +28 -28
  46. package/dist/{doctor-C6t4Maue.mjs → doctor-DZgpqR6J.mjs} +14 -14
  47. package/dist/{doctor-fingerprint-DZLoYFVb.mjs → doctor-fingerprint-zlujA1Gs.mjs} +1 -1
  48. package/dist/doctor-fingerprint.mjs +1 -1
  49. package/dist/{doctor-watchman-iGBpC6KM.mjs → doctor-watchman-B453A231.mjs} +5 -5
  50. package/dist/{eas-build-DMWSSvxq.mjs → eas-build-CxKUNHsb.mjs} +4 -4
  51. package/dist/{eas-fallback-vKlrqxgE.mjs → eas-fallback-LMxk9Kw0.mjs} +14 -14
  52. package/dist/{eas-session-ledger-BQF9Yyy9.mjs → eas-session-ledger-Ck6YXPAO.mjs} +12 -8
  53. package/dist/{error-diagnostics-DYsjJIHz.mjs → error-diagnostics-BkKCZq4P.mjs} +17 -42
  54. package/dist/{exec-DEoAhcze.mjs → exec-VFRpu1iA.mjs} +116 -28
  55. package/dist/{gc-dDE4QPVu.mjs → gc-DDWEAj0w.mjs} +2 -2
  56. package/dist/{gc-BW8E1uSC.mjs → gc-yVKpAOKM.mjs} +205 -28
  57. package/dist/{guide-DYYEO3Wz.mjs → guide-Bggftwc5.mjs} +222 -59
  58. package/dist/{hosted-android-xcfgCgSF.mjs → hosted-android-D4dweBof.mjs} +4 -4
  59. package/dist/{hosted-client-Bt-tndxV.mjs → hosted-client-BBTVe3X6.mjs} +4 -4
  60. package/dist/{hosted-ios-y19jCYFF.mjs → hosted-ios-Sn0djzg8.mjs} +1 -1
  61. package/dist/{hosted-logs-DzJKzKlj.mjs → hosted-logs-CYIZ5ubJ.mjs} +11 -6
  62. package/dist/{hosted-macos-BJusKvqL.mjs → hosted-macos-CXjsUNBR.mjs} +7 -7
  63. package/dist/{hosted-native-BmQC6U_x.mjs → hosted-native-Doxbfzem.mjs} +58 -11
  64. package/dist/{idle-shutdown-CJmUOV5x.mjs → idle-shutdown-BInjLKqm.mjs} +10 -10
  65. package/dist/{ios-CWjqfSm_.mjs → ios-B1FXRCMX.mjs} +71 -44
  66. package/dist/{ios-DTOYZGMJ.mjs → ios-BItP6lkn.mjs} +8 -8
  67. package/dist/{ios-hcBpnEGk.mjs → ios-CVUFFZ6C.mjs} +70 -13
  68. package/dist/{ios-xtlRHaeB.mjs → ios-ENC4sRPA.mjs} +1 -1
  69. package/dist/{ios-device-BXZ0c7Kb.mjs → ios-device-B6HK951C.mjs} +1 -1
  70. package/dist/{ios-device-DFGuwF4U.mjs → ios-device-BI38NFFH.mjs} +3 -3
  71. package/dist/{ios-project-CSZYI1iu.mjs → ios-project-KYqeJURj.mjs} +5 -5
  72. package/dist/{ios-signing-BtAffm0A.mjs → ios-signing-4c_qXvQm.mjs} +2 -2
  73. package/dist/{ios-state-Dtw5K2aI.mjs → ios-state-DlElquYD.mjs} +15 -7
  74. package/dist/{js-swap-BSBHldmm.mjs → js-swap-BvrQvXHY.mjs} +7 -6
  75. package/dist/{launch-CP2wDCVd.mjs → launch-BVlyQw3T.mjs} +51 -49
  76. package/dist/{launch-D-zq8SHG.mjs → launch-SfzGH_s9.mjs} +48 -29
  77. package/dist/{logs-pNvA2hHK.mjs → logs-DXE32kyb.mjs} +16 -9
  78. package/dist/{logs-By5aw3g9.mjs → logs-ovlU7m7N.mjs} +8 -6
  79. package/dist/{macos-CPMahOC2.mjs → macos-B3BveDrU.mjs} +1 -1
  80. package/dist/{macos-C67FZ2ft.mjs → macos-CCfhb-jT.mjs} +49 -42
  81. package/dist/macos-run.mjs +1 -1
  82. package/dist/maintenance-run.mjs +7 -7
  83. package/dist/{metro-DiXtA3Ty.mjs → metro-Bu8LBfzS.mjs} +4 -4
  84. package/dist/{metro-gateway-CKZqKqx7.mjs → metro-gateway-DtsOy-0P.mjs} +3 -4
  85. package/dist/{named-ports-DkEzOfzO.mjs → named-ports-BWDuU5oQ.mjs} +3 -3
  86. package/dist/{native-android-_MWSs3PP.mjs → native-android-96BBizbq.mjs} +17 -17
  87. package/dist/{native-gradle-artifact-inputs-DHFnmrVf.mjs → native-gradle-artifact-inputs-BcLYBZil.mjs} +5 -5
  88. package/dist/{native-run-Dv88i0aU.mjs → native-run-BmH7U8pr.mjs} +6 -6
  89. package/dist/{native-runtime-DBRfY_PN.mjs → native-runtime-DgtJkzIa.mjs} +10 -9
  90. package/dist/{native-source-Y0yLhFyQ.mjs → native-source-D-4Dh4JU.mjs} +7 -4
  91. package/dist/{native-xcode-inputs-CqViUh3r.mjs → native-xcode-inputs-irplbQb0.mjs} +2 -2
  92. package/dist/{native-xcode-ios-vFgqL_yS.mjs → native-xcode-ios-BY-6hh6M.mjs} +18 -18
  93. package/dist/offload-worker.d.mts +7 -2
  94. package/dist/offload-worker.mjs +75 -41
  95. package/dist/{operation-exec-BW0em61G.mjs → operation-exec-CUZifp6Z.mjs} +2 -25
  96. package/dist/{ownership-C5fo5Edb.mjs → ownership-DxlZGDcV.mjs} +3 -3
  97. package/dist/{page-DBL2PiUU.mjs → page-CzEA_-5_.mjs} +2 -3
  98. package/dist/phone-reverses-BdsdesVF.mjs +57 -0
  99. package/dist/{placement-log-BnGgJRRZ.mjs → placement-log-DUvqqsB-.mjs} +1 -1
  100. package/dist/{plan-placement-cr0fEU7b.mjs → plan-placement-4axc0Hs7.mjs} +3 -3
  101. package/dist/{ports-DpXjKkSV.mjs → ports-D-E_OiAO.mjs} +3 -3
  102. package/dist/{prebuild-BligW0QS.mjs → prebuild-DmVCf5xb.mjs} +14 -9
  103. package/dist/{project-DhCtQWWU.mjs → project-Dz-hWkb-.mjs} +21 -17
  104. package/dist/{pull-request-W1IuLXDt.mjs → pull-request-CbNlkFok.mjs} +2 -2
  105. package/dist/pull-requests.mjs +1 -1
  106. package/dist/{react-native-android-ZlhQfhvq.mjs → react-native-android-DOinxgwB.mjs} +75 -32
  107. package/dist/{react-native-build-CelbTO_y.mjs → react-native-build-AuCu-aAP.mjs} +22 -19
  108. package/dist/{react-native-doctor-LTIaP7lv.mjs → react-native-doctor-2ivJUmwZ.mjs} +10 -9
  109. package/dist/{react-native-ios-C5W0SA6f.mjs → react-native-ios-D5zTIuH8.mjs} +38 -22
  110. package/dist/{recordings-CQmfzAh7.mjs → recordings-CdPwYwvs.mjs} +1 -1
  111. package/dist/{registration-BcRgF6Ox.mjs → registration-CN6nlhcD.mjs} +1 -1
  112. package/dist/{reload-COJWTbae.mjs → reload-C1TTVesC.mjs} +144 -42
  113. package/dist/{reload-QcwnYLYV.mjs → reload-Ck9RJeSq.mjs} +1 -1
  114. package/dist/{remote-DR8qUERM.mjs → remote-DEZ7AOVq.mjs} +7 -7
  115. package/dist/{result-utnBea7x.mjs → result-DqBrb12U.mjs} +40 -17
  116. package/dist/{run-DidHghD1.mjs → run-BIwoU44q.mjs} +2 -2
  117. package/dist/{selection-DSMbWEX2.mjs → selection-BLtvaqxc.mjs} +11 -5
  118. package/dist/{server-bare-CafzuyFB.mjs → server-bare-Z1ToVLPn.mjs} +2 -2
  119. package/dist/{server-command-B9o92sLM.mjs → server-command-C0pmjNdG.mjs} +5 -5
  120. package/dist/server-expo-CEntXzRw.mjs +2 -0
  121. package/dist/{server-expo-BXrWxNYh.mjs → server-expo-Tt_hLRpy.mjs} +8 -5
  122. package/dist/{settings-C7O8A6ZI.mjs → settings-RKwY33wv.mjs} +114 -19
  123. package/dist/{settings-PfJx8uK1.mjs → settings-kkt0vP4w.mjs} +1 -1
  124. package/dist/{settings-write-Chuy7Cte.mjs → settings-write--gj6MACV.mjs} +7 -7
  125. package/dist/settings.schema.json +13 -0
  126. package/dist/{simslim-BNckWpXi.mjs → simslim-CdWKT3vA.mjs} +2 -2
  127. package/dist/{slot-launch-B7AuoWJv.mjs → slot-launch-A1BgotaM.mjs} +6 -6
  128. package/dist/{stage-C4u17aES.mjs → stage-ERTMh88n.mjs} +2 -3
  129. package/dist/{start-CU8mCPC7.mjs → start-BKGrEhzG.mjs} +75 -35
  130. package/dist/{start-BL_BsiWt.mjs → start-Bmg_qmHi.mjs} +1 -1
  131. package/dist/{state--smjFSTL.mjs → state-B1ig0Kqy.mjs} +4 -4
  132. package/dist/{state-08I3hw3_.mjs → state-BfAltYJP.mjs} +2 -2
  133. package/dist/{stats-CO6sK0Nh.mjs → stats-B-ckuiUg.mjs} +5 -5
  134. package/dist/{stats-report-BzaPrRm6.mjs → stats-report-n2U5plhk.mjs} +1 -1
  135. package/dist/{status-_WPHzVft.mjs → status-DfFp6a76.mjs} +24 -24
  136. package/dist/{status-BRROykC8.mjs → status-Fect2w9R.mjs} +1 -1
  137. package/dist/{status-CrJutl6-.mjs → status-Pw-807f0.mjs} +1 -1
  138. package/dist/{status-watch-BHFasOZN.mjs → status-watch-XC7F-AMf.mjs} +5 -5
  139. package/dist/status-worker.mjs +1 -1
  140. package/dist/stim-clonefile.c +18 -0
  141. package/dist/{stim-desktop-D5OGpYZM.mjs → stim-desktop-DS0mzR8U.mjs} +2 -2
  142. package/dist/{stop-iuFHjUST.mjs → stop-BFu7X_Do.mjs} +2 -2
  143. package/dist/{stop-BJ06ipTZ.mjs → stop-CL1kDHds.mjs} +55 -21
  144. package/dist/{stop-CR5689oB.mjs → stop-DhclRwOO.mjs} +5 -5
  145. package/dist/supervisor-run.mjs +16 -16
  146. package/dist/{support-TWuQlHQo.mjs → support-B1W-hmC2.mjs} +8 -8
  147. package/dist/{support-CbXPbUMJ.mjs → support-B3tzVhKn.mjs} +6 -6
  148. package/dist/{swiftpm-artifact-inputs-DFcGsXDP.mjs → swiftpm-artifact-inputs-BWYPj9Sn.mjs} +4 -4
  149. package/dist/{swiftpm-macos-DbwGCpZg.mjs → swiftpm-macos-BVCP5mO4.mjs} +14 -13
  150. package/dist/{toolchain-XkA2PfJZ.mjs → toolchain-BjgKdoEf.mjs} +1 -1
  151. package/dist/{toolchain-B9hCFCQw.mjs → toolchain-D3bmRMia.mjs} +57 -36
  152. package/dist/trigger-BLsHgAGR.mjs +2 -0
  153. package/dist/{trigger-Cbyc41d_.mjs → trigger-CBMe2YLH.mjs} +2 -2
  154. package/dist/{warm-progress-CA3TgT3-.mjs → warm-progress-ol0ZypEu.mjs} +6 -6
  155. package/dist/{web-DRF_oFXk.mjs → web-BH7peScT.mjs} +12 -10
  156. package/dist/web-BdOFlTJr.mjs +2 -0
  157. package/dist/web-run.mjs +8 -9
  158. package/dist/windows-report.mjs +12 -1
  159. package/dist/windows-report.ps1 +1 -1
  160. package/dist/{workspace-state-U08dEzhG.mjs → workspace-state-CeBpIZgr.mjs} +11 -4
  161. package/dist/{workspaces-e41xsjKr.mjs → workspaces-CSV2Orf4.mjs} +36 -32
  162. package/dist/{worktree-BEwxVwKF.mjs → worktree-DEDUzO_L.mjs} +1 -1
  163. package/dist/{worktree-B4Nj3EGS.mjs → worktree-Dw8kfacb.mjs} +324 -99
  164. package/dist/{xcode-Rq744mBF.mjs → xcode-DHu41qQ4.mjs} +41 -27
  165. package/package.json +7 -7
  166. package/dist/native-gradle-inputs-Dnh84Zbd.mjs +0 -211
  167. package/dist/server-expo-Ddr8cG_c.mjs +0 -2
  168. package/dist/trigger-yb_nFzPF.mjs +0 -2
  169. package/dist/web-kDE9Myoy.mjs +0 -2
package/README.md CHANGED
@@ -188,8 +188,9 @@ refuses with `STIM_CLAIM_UNAVAILABLE` instead of a cancellation or timeout
188
188
  error. Child cleanup errors name the launcher PID; unverifiable processes and descendants
189
189
  are left untouched. On POSIX, a launcher that exited before identity capture can only
190
190
  finish successfully once its process group is proven gone.
191
- After a termination request, cleanup waits at most one second for a child to
192
- close. Overall cleanup has a 35 second budget, including temporary-worktree
191
+ After a termination request, cleanup waits for a child to close, then for its
192
+ process group to exit: each wait is 5 seconds times the host's load per CPU,
193
+ from 5 to 15 seconds. Overall cleanup has a 35 second budget, including temporary-worktree
193
194
  removal; unfinished cleanup returns `STIM_CLAIM_UNAVAILABLE` and leaves
194
195
  unresolved resources in place. Credential refresh waits asynchronously and
195
196
  checks cancellation before writing.
@@ -1,5 +1,5 @@
1
- import { r as WorkspaceProcessCleanupError } from "./exec-DEoAhcze.mjs";
2
- import { I as waitForSharedBuild } from "./activity-BpWUPgUm.mjs";
1
+ import { l as WorkspaceProcessCleanupError } from "./exec-VFRpu1iA.mjs";
2
+ import { I as waitForSharedBuild } from "./activity-Dq3ZcdD8.mjs";
3
3
  import { rmSync } from "node:fs";
4
4
  //#region src/engine/artifact-lifecycle.ts
5
5
  async function runArtifactLifecycle(phases) {
@@ -1,23 +1,23 @@
1
- import { n as isTimeoutError, r as WorkspaceProcessCleanupError, t as getExecutor } from "./exec-DEoAhcze.mjs";
1
+ import { l as WorkspaceProcessCleanupError, n as isTimeoutError, t as getExecutor } from "./exec-VFRpu1iA.mjs";
2
2
  import { g as workspaceStateFile$1, m as workspaceLogsDir$1 } from "./paths-DQtTgsBf.mjs";
3
- import { F as isOnMountedVolume, G as projectDeviceSlots, I as listMountedVolumes, L as measuredDirectorySizeAsync, N as directorySizeAsync, P as formatBytes, _ as loadConfig$1, p as getProject } from "./config-DlrsduwG.mjs";
4
- import { o as formatLongDuration, u as plural } from "./command-output-YKGWivpG.mjs";
5
- import { c as gitQuiet, l as gitQuietAsync, n as runOperationFileQuiet, r as childEnvironment } from "./operation-exec-BW0em61G.mjs";
6
- import { r as readWorkspaceState$1, u as workspaceLastUsed } from "./workspace-state-U08dEzhG.mjs";
7
- import { E as maintenanceKeepSetting, Et as listWorktreesAsync, Ft as resolveFullRefAsync, It as resolveSourceCheckoutAsync, J as resolveProjectSettingsAsync, Rt as sourceCheckoutOf, St as hasUncommittedWorkAsync, Tt as listWorktrees, Wt as removeTemporaryEntryAsync, bt as hasPopulatedSubmodulesAsync, gt as dirtyPathsAsync, q as resolveProjectSettings, zt as unpushedCommitsAsync } from "./settings-C7O8A6ZI.mjs";
8
- import { i as findProjectRoot$1 } from "./project-DhCtQWWU.mjs";
9
- import { a as unregister, n as readManifest } from "./cache-manifest-DSeQjt-Q.mjs";
10
- import { A as workspaceBuildInProgress, E as collectOwnedDeviceActivityAsync, St as deleteProjectDevices, T as collectOwnedDeviceActivity, Tt as deviceSweepIsScoped, X as emptyCaches, _r as phase, br as discoverCaches, bt as deleteParkedAvds, ct as deleteGradleVersion, d as trimmableCaches, f as stopOwnedMetro, fr as memorySweepIsScoped, gr as stopIdleGradleDaemons, gt as DEVICE_LIST_TIMEOUT_MS, i as enforceBudget, kt as findStaleProjectDevices, nt as planCacheEmptying, o as machineNumber, pt as waitForGradleExit, r as cacheLabel, sr as collectMemoryReport, st as collectGradleVersions, u as resolveBudget, vt as collectParkedAvds, xr as pruneCache, xt as deleteParkedSims, yt as collectParkedSims } from "./budget-3ivAK99e.mjs";
3
+ import { F as isOnMountedVolume, G as projectDeviceSlots, I as listMountedVolumes, L as measuredDirectorySizeAsync, N as directorySizeAsync, P as formatBytes, _ as loadConfig$1, p as getProject } from "./config-BZhXY15H.mjs";
4
+ import { o as formatLongDuration, u as plural } from "./command-output-DxC0k2rk.mjs";
5
+ import { c as gitQuiet, l as gitQuietAsync, n as runOperationFileQuiet, r as childEnvironment } from "./operation-exec-CUZifp6Z.mjs";
6
+ import { r as readWorkspaceState$1, u as workspaceLastUsed } from "./workspace-state-CeBpIZgr.mjs";
7
+ import { E as maintenanceKeepSetting, Et as listWorktreesAsync, Ft as resolveFullRefAsync, It as resolveSourceCheckoutAsync, J as resolveProjectSettingsAsync, Rt as sourceCheckoutOf, St as hasUncommittedWorkAsync, Tt as listWorktrees, Wt as removeTemporaryEntryAsync, bt as hasPopulatedSubmodulesAsync, gt as dirtyPathsAsync, q as resolveProjectSettings, zt as unpushedCommitsAsync } from "./settings-RKwY33wv.mjs";
8
+ import { i as findProjectRoot$1 } from "./project-Dz-hWkb-.mjs";
9
+ import { F as listAvdsAsync, G as ownedAvdSerialResolverAsync, P as listAvds, V as ownedAvdDirectory, yt as readParked } from "./android-882m4hxX.mjs";
10
+ import { a as unregister, n as readManifest } from "./cache-manifest-BPXRONJz.mjs";
11
+ import { A as workspaceBuildInProgress, Cr as pruneCache, Ct as deleteParkedSims, Dt as deviceSweepIsScoped, E as collectOwnedDeviceActivityAsync, Q as emptyCaches, Sr as discoverCaches, St as deleteParkedAvds, T as collectOwnedDeviceActivity, bt as collectParkedAvds, d as trimmableCaches, f as stopOwnedMetro, ht as waitForGradleExit, i as enforceBudget, it as planCacheEmptying, jt as findStaleProjectDevices, lr as collectMemoryReport, lt as collectGradleVersions, mr as memorySweepIsScoped, o as machineNumber, r as cacheLabel, u as resolveBudget, ut as deleteGradleVersion, vr as stopIdleGradleDaemons, vt as DEVICE_LIST_TIMEOUT_MS, wt as deleteProjectDevices, xt as collectParkedSims, yr as phase } from "./budget-dPGuhNai.mjs";
11
12
  import { t as canonicalPath } from "./paths-BsJMoPP2.mjs";
12
- import { V as takeGcResults, a as collectWorkspaceOutputs, c as listWorkspaceDirs, f as withIdleWorkspace, i as collectOrphanedWorkspaces, m as workspaceInUseAsync, o as deleteOrphanedWorkspaces, p as workspaceInUse, r as clearWorkspaceOutputs, s as isInsideWorkspaces, z as GC_OUTPUT } from "./workspaces-e41xsjKr.mjs";
13
- import { N as listBuildLocks, b as leaseIsExpired, g as workspaceActivity, x as listLeaseFiles } from "./activity-BpWUPgUm.mjs";
14
- import { F as listAvdsAsync, G as ownedAvdSerialResolverAsync, P as listAvds, V as ownedAvdDirectory, vt as readParked } from "./android-C7sVmyI9.mjs";
15
- import { d as listAllIosSims, f as listAllIosSimsAsync } from "./ios-hcBpnEGk.mjs";
13
+ import { B as GC_OUTPUT, H as takeGcResults, a as collectWorkspaceOutputs, c as listWorkspaceDirs, d as workspaceDirRemoved, h as workspaceInUseAsync, i as collectOrphanedWorkspaces, m as workspaceInUse, o as deleteOrphanedWorkspaces, p as withIdleWorkspace, r as clearWorkspaceOutputs, s as isInsideWorkspaces } from "./workspaces-CSV2Orf4.mjs";
14
+ import { N as listBuildLocks, b as leaseIsExpired, g as workspaceActivity, x as listLeaseFiles } from "./activity-Dq3ZcdD8.mjs";
15
+ import { d as listAllIosSims, f as listAllIosSimsAsync } from "./ios-CVUFFZ6C.mjs";
16
16
  import { s as metroStoreRoot } from "./errors-ccaPc4ox.mjs";
17
17
  import { t as resolveMaintenanceSettings } from "./settings-Djl-mpjX.mjs";
18
- import { A as levelFloors, C as measureDisk, E as measurePressureAsync, M as lowestFree, N as protectWindowMs, S as cacheBlocked, T as measurePressure, a as matchWorktreeEntry, c as reclaimKeys, i as excludeWatchmanCookies, k as diskPressureLevel, m as removeWorktreeTarget, o as nestedWorktrees, r as excludePodChurn, s as porcelainPath, w as measureDiskAsync } from "./worktree-B4Nj3EGS.mjs";
19
- import { c as FETCH_FRESH_MS, d as localDefaultBranchAsync, l as fetchDefaultBranchAsync, o as recordPullRequestLookup, p as mergeStateAsync, u as lastFetchedAtAsync } from "./warm-progress-CA3TgT3-.mjs";
20
- import { n as describePullRequest, r as endedPullRequest, s as pullRequestLookups } from "./pull-request-W1IuLXDt.mjs";
18
+ import { A as measurePressureAsync, D as measureDisk, E as cacheBlocked, I as lowestFree, L as protectWindowMs, N as diskPressureLevel, O as measureDiskAsync, P as levelFloors, a as matchWorktreeEntry, c as reclaimKeys, i as excludeWatchmanCookies, k as measurePressure, m as removeWorktreeTarget, o as nestedWorktrees, r as excludePodChurn, s as porcelainPath } from "./worktree-Dw8kfacb.mjs";
19
+ import { c as FETCH_FRESH_MS, d as localDefaultBranchAsync, l as fetchDefaultBranchAsync, o as recordPullRequestLookup, p as mergeStateAsync, u as lastFetchedAtAsync } from "./warm-progress-ol0ZypEu.mjs";
20
+ import { n as describePullRequest, r as endedPullRequest, s as pullRequestLookups } from "./pull-request-CbNlkFok.mjs";
21
21
  import { readClaimSet } from "@stim-cli/core/ownership-claim";
22
22
  import { existsSync, lstatSync, readdirSync, realpathSync, rmSync, statSync } from "node:fs";
23
23
  import { tmpdir } from "node:os";
@@ -1700,20 +1700,33 @@ const STIM_PREFIXES = [
1700
1700
  const STAGING_PREFIX = ".stim-delete-";
1701
1701
  const TRACE_PATTERN = /^instruments.*\.ktrace$/;
1702
1702
  const TRACE_PROCESSES = /* @__PURE__ */ new Set(["xctrace", "Instruments"]);
1703
+ const SWIFTPM_PATTERN = /^TemporaryDirectory\.[A-Za-z0-9]{6}$/;
1704
+ const SWIFTPM_PROCESSES = /* @__PURE__ */ new Set([
1705
+ "swift",
1706
+ "swift-build",
1707
+ "swift-frontend",
1708
+ "swift-package",
1709
+ "swift-run",
1710
+ "swift-test",
1711
+ "xcodebuild"
1712
+ ]);
1703
1713
  const REMEDIES = {
1704
1714
  stim: "stim gc --delete removes them once they are idle for 24 hours",
1705
1715
  xctrace: "stim gc --delete removes them once they are idle for 24 hours and no xctrace or Instruments process runs; export a recording you need first",
1716
+ swiftpm: "stim gc --delete removes them once they are idle for 24 hours and no swift or xcodebuild process runs",
1706
1717
  jest: "Jest owns this cache; clear it with `npx jest --clearCache`"
1707
1718
  };
1708
1719
  const LABELS = {
1709
1720
  stim: "Stim temp directories",
1710
1721
  xctrace: "xctrace recordings (instruments*.ktrace)",
1722
+ swiftpm: "SwiftPM temp directories (TemporaryDirectory.*)",
1711
1723
  jest: "Jest cache"
1712
1724
  };
1713
1725
  function kindOf(name, stat, jestName) {
1714
1726
  const unstaged = name.startsWith(STAGING_PREFIX) ? name.slice(13) : name;
1715
1727
  if (stat.isDirectory() && STIM_PREFIXES.some((prefix) => unstaged.startsWith(prefix))) return "stim";
1716
1728
  if ((stat.isFile() || stat.isDirectory()) && TRACE_PATTERN.test(name)) return "xctrace";
1729
+ if (stat.isDirectory() && SWIFTPM_PATTERN.test(name)) return "swiftpm";
1717
1730
  if (stat.isDirectory() && name === jestName) return "jest";
1718
1731
  return null;
1719
1732
  }
@@ -1812,8 +1825,8 @@ function temporaryRoot(env) {
1812
1825
  }
1813
1826
  /**
1814
1827
  * Recognized leftovers directly inside the user's TMPDIR, never following a symlink: Stim's own temp directories,
1815
- * xctrace recordings and Jest's cache. Only the first two are ever deleted, and only once nothing in them changed for
1816
- * 24 hours. Null with STIM_HOME set, because TMPDIR is shared by every Stim home, and for the real TMPDIR a test
1828
+ * xctrace recordings, SwiftPM's `TemporaryDirectory.*` and Jest's cache. All but Jest's are deleted, and only once
1829
+ * nothing in them changed for 24 hours. Null with STIM_HOME set, because TMPDIR is shared by every Stim home, and for the real TMPDIR a test
1817
1830
  * process recorded in STIM_TEST_GUARD_REAL_TMPDIR.
1818
1831
  */
1819
1832
  async function collectTempLeftovers({ now = Date.now(), env = process.env, signal, operation } = {}) {
@@ -1833,16 +1846,16 @@ async function collectTempLeftovers({ now = Date.now(), env = process.env, signa
1833
1846
  const registeredProjects = () => Object.keys(loadConfig$1()?.projects ?? {}).flatMap((key) => [key, canonicalKey(key)]);
1834
1847
  let projects = registeredProjects();
1835
1848
  let executables;
1836
- const traceBusy = async () => {
1849
+ const busy = async (processes) => {
1837
1850
  if (executables === void 0) executables = await runningExecutables(operation, signal);
1838
1851
  const listed = executables;
1839
1852
  if (listed === null) return "the running processes could not be listed";
1840
- const running = [...TRACE_PROCESSES].filter((name) => listed.has(name));
1853
+ const running = [...processes].filter((name) => listed.has(name));
1841
1854
  return running.length ? `${running.join(" and ")} is running` : null;
1842
1855
  };
1843
1856
  const judge = async (path, stat, kind, identity = path) => {
1844
1857
  const scan = await walk(path, stat, signal);
1845
- const kept = now - scan.newest < MIN_AGE_MS ? `modified within the last ${TEMP_LEFTOVER_MIN_AGE_HOURS}h` : kind === "stim" ? stimKept(identity, scan, projects) : scan.unreadable ?? await traceBusy();
1858
+ const kept = now - scan.newest < MIN_AGE_MS ? `modified within the last ${TEMP_LEFTOVER_MIN_AGE_HOURS}h` : kind === "stim" ? stimKept(identity, scan, projects) : scan.unreadable ?? await busy(kind === "xctrace" ? TRACE_PROCESSES : SWIFTPM_PROCESSES);
1846
1859
  return {
1847
1860
  bytes: scan.bytes,
1848
1861
  kept
@@ -1851,7 +1864,7 @@ async function collectTempLeftovers({ now = Date.now(), env = process.env, signa
1851
1864
  const recheck = async (staged, original, kind) => {
1852
1865
  const stat = await lstatOrError(staged);
1853
1866
  if (!stat || "error" in stat || stat.isSymbolicLink()) return "it changed while it was being deleted";
1854
- if (kind === "xctrace") executables = void 0;
1867
+ if (kind !== "stim") executables = void 0;
1855
1868
  else projects = registeredProjects();
1856
1869
  return (await judge(staged, stat, kind, original)).kept;
1857
1870
  };
@@ -1896,6 +1909,7 @@ function summarize(report) {
1896
1909
  return [
1897
1910
  "stim",
1898
1911
  "xctrace",
1912
+ "swiftpm",
1899
1913
  "jest"
1900
1914
  ].flatMap((kind) => {
1901
1915
  const entries = report.entries.filter((entry) => entry.kind === kind);
@@ -1950,7 +1964,7 @@ async function deleteTempLeftovers(options = {}, output = GC_OUTPUT) {
1950
1964
  try {
1951
1965
  if (entry.kind === "jest") continue;
1952
1966
  const name = basename(entry.path);
1953
- const staged = entry.kind === "xctrace" || name.startsWith(STAGING_PREFIX) ? entry.path : join(report.root, `${STAGING_PREFIX}${name}`);
1967
+ const staged = entry.kind !== "stim" || name.startsWith(STAGING_PREFIX) ? entry.path : join(report.root, `${STAGING_PREFIX}${name}`);
1954
1968
  const before = staged === entry.path ? null : await report.recheck?.(entry.path, entry.path, entry.kind) ?? null;
1955
1969
  if (before) {
1956
1970
  output.record("tempLeftover", "kept", entry.path, {
@@ -2062,7 +2076,7 @@ async function planLevel2({ settings, sizes, protectedRoot, pinned, now, operati
2062
2076
  kind: "would-delete-temp-leftovers",
2063
2077
  target: leftovers.root,
2064
2078
  bytes: deletable.reduce((sum, entry) => sum + entry.bytes, 0),
2065
- reason: "disk pressure level 2, Stim temp directories and xctrace recordings unmodified for 24 hours",
2079
+ reason: "disk pressure level 2, Stim temp directories, xctrace recordings and SwiftPM temp directories unmodified for 24 hours",
2066
2080
  level: 2
2067
2081
  });
2068
2082
  const options = {
@@ -2242,7 +2256,7 @@ async function planPressureLevels(level, context) {
2242
2256
  function triggeringRoot(operation) {
2243
2257
  return canonicalPath(findProjectRoot$1(operation?.cwd ?? process.cwd()) ?? join(configDir(), "maintenance", "no-project"));
2244
2258
  }
2245
- async function plannedMaintenance(pressure, sizes, settings, { devices = true, sweep = false, worktrees = false, refresh = true, operation } = {}) {
2259
+ async function plannedMaintenance(pressure, recordedSizes, settings, { devices = true, sweep = false, worktrees = false, refresh = true, operation } = {}) {
2246
2260
  const env = operation?.env ?? process.env;
2247
2261
  const resolved = resolveBudget({ env });
2248
2262
  if (resolved.error) throw new Error(resolved.error);
@@ -2272,6 +2286,7 @@ async function plannedMaintenance(pressure, sizes, settings, { devices = true, s
2272
2286
  signal: operation?.signal,
2273
2287
  operation
2274
2288
  });
2289
+ const sizes = recordedSizes.filter((size) => size.category !== "workspace-outputs" || !workspaceDirRemoved(size.dir));
2275
2290
  const currentSizes = sizes.map((size) => {
2276
2291
  if (size.category === "compilation-cache") return {
2277
2292
  ...size,
@@ -1,10 +1,10 @@
1
- import { c as declareSpawnsOn, f as stopDeclaringSpawnsOn, g as process_identity_exports, t as getExecutor, x as ownership_claim_exports } from "./exec-DEoAhcze.mjs";
1
+ import { D as ownership_claim_exports, S as process_identity_exports, m as declareSpawnsOn, t as getExecutor, v as stopDeclaringSpawnsOn } from "./exec-VFRpu1iA.mjs";
2
2
  import { m as workspaceLogsDir$1, v as withDirLock$1, y as withDirLockAsync$1 } from "./paths-DQtTgsBf.mjs";
3
- import { U as parseDeviceSlotKey, V as deviceSlotKey, d as getConfigDir, q as validateDeviceSlot } from "./config-DlrsduwG.mjs";
4
- import { a as formatElapsed, i as formatDuration, m as shortHash } from "./command-output-YKGWivpG.mjs";
5
- import { n as runOperationFileQuiet } from "./operation-exec-BW0em61G.mjs";
6
- import { c as withWorkspaceStateLock, d as writeWorkspaceState, l as withWorkspaceStateLockAsync, n as clearWorkspaceStateKeys, r as readWorkspaceState$1 } from "./workspace-state-U08dEzhG.mjs";
7
- import { h as resolveBuild } from "./build-cache-Dv7RN200.mjs";
3
+ import { U as parseDeviceSlotKey, V as deviceSlotKey, d as getConfigDir, q as validateDeviceSlot } from "./config-BZhXY15H.mjs";
4
+ import { a as formatElapsed, i as formatDuration, m as shortHash } from "./command-output-DxC0k2rk.mjs";
5
+ import { n as runOperationFileQuiet } from "./operation-exec-CUZifp6Z.mjs";
6
+ import { c as withWorkspaceStateLock, d as writeWorkspaceState, l as withWorkspaceStateLockAsync, n as clearWorkspaceStateKeys, r as readWorkspaceState$1 } from "./workspace-state-CeBpIZgr.mjs";
7
+ import { y as resolveBuild } from "./build-cache-HFUQ9eLz.mjs";
8
8
  import { randomBytes } from "node:crypto";
9
9
  import { closeSync, fstatSync, mkdirSync, openSync, readFileSync, readSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
10
10
  import { homedir } from "node:os";
@@ -1,8 +1,8 @@
1
- import { g as process_identity_exports } from "./exec-DEoAhcze.mjs";
2
- import { P as formatBytes } from "./config-DlrsduwG.mjs";
3
- import { o as formatLongDuration } from "./command-output-YKGWivpG.mjs";
4
- import { F as writeUsageCache, N as canonical, P as measureDu, c as listWorkspaceDirs } from "./workspaces-e41xsjKr.mjs";
5
- import { c as parseAgentDeviceRecord, o as envDir, u as processMatches } from "./activity-BpWUPgUm.mjs";
1
+ import { S as process_identity_exports } from "./exec-VFRpu1iA.mjs";
2
+ import { P as formatBytes } from "./config-BZhXY15H.mjs";
3
+ import { o as formatLongDuration } from "./command-output-DxC0k2rk.mjs";
4
+ import { F as measureDu, I as writeUsageCache, P as canonical, c as listWorkspaceDirs } from "./workspaces-CSV2Orf4.mjs";
5
+ import { c as parseAgentDeviceRecord, o as envDir, u as processMatches } from "./activity-Dq3ZcdD8.mjs";
6
6
  import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
7
7
  import { homedir } from "node:os";
8
8
  import { isAbsolute, join, relative, sep } from "node:path";
@@ -1,10 +1,10 @@
1
- import { c as declareSpawnsOn, f as stopDeclaringSpawnsOn, g as process_identity_exports, r as WorkspaceProcessCleanupError, t as getExecutor, x as ownership_claim_exports } from "./exec-DEoAhcze.mjs";
2
- import { C as saveConfig, D as withConfigLock$1, G as projectDeviceSlots, H as deviceSlotPlatforms, K as removeSlotDevice, O as withConfigLockAsync$1, _ as loadConfig$1, c as ensureConfig, d as getConfigDir, f as getConfigPath$1, z as assignSlotDevice } from "./config-DlrsduwG.mjs";
3
- import { n as runOperationFileQuiet, t as runOperationFile, u as cancellableSleep } from "./operation-exec-BW0em61G.mjs";
4
- import { c as androidDataPartitionSizeBytes } from "./settings-C7O8A6ZI.mjs";
5
- import { c as pidExists, p as signalProcessTreeAsync } from "./metro-DiXtA3Ty.mjs";
6
- import { i as recordCreatedDevice, r as readCreatedDevices$1, t as forgetCreatedDevice } from "./created-devices-x9rN6bhs.mjs";
7
- import { i as stimDesktopInstalledAsync, n as settingDefault, t as STIM_DESKTOP_OPEN_OPTIONS } from "./stim-desktop-D5OGpYZM.mjs";
1
+ import { D as ownership_claim_exports, S as process_identity_exports, l as WorkspaceProcessCleanupError, m as declareSpawnsOn, o as cancellableSleep, t as getExecutor, v as stopDeclaringSpawnsOn } from "./exec-VFRpu1iA.mjs";
2
+ import { C as saveConfig, D as withConfigLock$1, G as projectDeviceSlots, H as deviceSlotPlatforms, K as removeSlotDevice, O as withConfigLockAsync$1, _ as loadConfig$1, c as ensureConfig, d as getConfigDir, f as getConfigPath$1, z as assignSlotDevice } from "./config-BZhXY15H.mjs";
3
+ import { n as runOperationFileQuiet, t as runOperationFile } from "./operation-exec-CUZifp6Z.mjs";
4
+ import { c as androidDataPartitionSizeBytes } from "./settings-RKwY33wv.mjs";
5
+ import { i as stimDesktopInstalledAsync, n as settingDefault, t as STIM_DESKTOP_OPEN_OPTIONS } from "./stim-desktop-DS0mzR8U.mjs";
6
+ import { a as recordCreatedDeviceAsync, n as forgetCreatedDeviceAsync, r as readCreatedDevices$1 } from "./created-devices-D2Aca-xu.mjs";
7
+ import { c as pidExists, p as signalProcessTreeAsync } from "./metro-Bu8LBfzS.mjs";
8
8
  import { isDeepStrictEqual } from "node:util";
9
9
  import { join } from "node:path";
10
10
  import { ambientEnv, ambientWrite } from "@stim-cli/core";
@@ -13,6 +13,27 @@ import { dirname as dirname$1, isAbsolute as isAbsolute$1, join as join$1, resol
13
13
  import { existsSync, lstatSync, mkdirSync, openSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "fs";
14
14
  import { rm } from "node:fs/promises";
15
15
  import { homedir, tmpdir } from "os";
16
+ //#region src/devices/android-emulator-viewer.ts
17
+ async function configuredAndroidEmulatorApp(platform = process.platform, env) {
18
+ let value = loadConfig$1()?.androidEmulatorApp;
19
+ if (value === void 0) {
20
+ const installed = await stimDesktopInstalledAsync(platform, env);
21
+ value = settingDefault(settingDefinition("androidEmulatorApp"), () => installed).value;
22
+ }
23
+ if (ANDROID_EMULATOR_APPS.includes(value)) return value;
24
+ const error = /* @__PURE__ */ new Error("Invalid androidEmulatorApp in machine config. Use \"emulator\" or \"stim-desktop\".");
25
+ Object.assign(error, { code: "STIM_BAD_ARG" });
26
+ throw error;
27
+ }
28
+ async function openEmulatorInStimDesktop(serial) {
29
+ await getExecutor().runFileQuietAsync("open", [
30
+ "-g",
31
+ "-a",
32
+ "Stim",
33
+ `stim-desktop://open?serial=${serial}`
34
+ ], STIM_DESKTOP_OPEN_OPTIONS);
35
+ }
36
+ //#endregion
16
37
  //#region src/devices/sim-pool.ts
17
38
  const POOL_SETTING_REMEDY = "Run `stim guide settings` for the device pool bounds and where it can be set.";
18
39
  function parkedMaxSetting(platform, options = {}) {
@@ -89,11 +110,11 @@ function parkSim({ platform, projectPath, slot = "default", record, max }) {
89
110
  return evicted;
90
111
  });
91
112
  }
92
- function adoptParked({ platform, projectPath, slot = "default", udid, device }) {
113
+ async function adoptParked({ platform, projectPath, slot = "default", udid, device }) {
93
114
  const claim = claimParkedOperation(platform, udid);
94
115
  if (!claim) return null;
95
116
  try {
96
- return withConfigLock$1(() => {
117
+ return await withConfigLockAsync$1(() => {
97
118
  const cfg = ensureConfig();
98
119
  const records = readParked(platform, { config: cfg });
99
120
  const taken = records.find((r) => r.udid === udid);
@@ -243,27 +264,6 @@ function isStimOwnedAvd(name) {
243
264
  return name.startsWith("stim-") && isStimOwnedDevice("android", name);
244
265
  }
245
266
  //#endregion
246
- //#region src/devices/android-emulator-viewer.ts
247
- async function configuredAndroidEmulatorApp(platform = process.platform, env) {
248
- let value = loadConfig$1()?.androidEmulatorApp;
249
- if (value === void 0) {
250
- const installed = await stimDesktopInstalledAsync(platform, env);
251
- value = settingDefault(settingDefinition("androidEmulatorApp"), () => installed).value;
252
- }
253
- if (ANDROID_EMULATOR_APPS.includes(value)) return value;
254
- const error = /* @__PURE__ */ new Error("Invalid androidEmulatorApp in machine config. Use \"emulator\" or \"stim-desktop\".");
255
- Object.assign(error, { code: "STIM_BAD_ARG" });
256
- throw error;
257
- }
258
- async function openEmulatorInStimDesktop(serial) {
259
- await getExecutor().runFileQuietAsync("open", [
260
- "-g",
261
- "-a",
262
- "Stim",
263
- `stim-desktop://open?serial=${serial}`
264
- ], STIM_DESKTOP_OPEN_OPTIONS);
265
- }
266
- //#endregion
267
267
  //#region src/devices/android.ts
268
268
  const ANDROID_ABIS = /* @__PURE__ */ new Set([
269
269
  "armeabi-v7a",
@@ -410,7 +410,7 @@ async function createOwnedAvd(label, { systemImage, deviceProfile = DEFAULT_AVD_
410
410
  throw new Error(`No ${arch} Android system image is installed. Install one, e.g.: sdkmanager "system-images;android-36;google_apis;${arch}"`);
411
411
  }
412
412
  const avdName = ownedAvdName(label);
413
- recordCreatedDevice("android", avdName);
413
+ await recordCreatedDeviceAsync("android", avdName);
414
414
  const tool = androidToolPath("avdmanager");
415
415
  const args = [
416
416
  "create",
@@ -542,7 +542,7 @@ async function deleteAvd(avdName, operation) {
542
542
  cwd: operation?.cwd,
543
543
  ...root ? { env: { ANDROID_AVD_HOME: root } } : {}
544
544
  });
545
- forgetCreatedDevice("android", avdName);
545
+ await forgetCreatedDeviceAsync("android", avdName);
546
546
  }
547
547
  function parseAvdList(text) {
548
548
  return text.split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("INFO") && !l.startsWith("WARNING"));
@@ -964,7 +964,10 @@ async function resetAdoptedAvd(avdName, serial, keepPackage) {
964
964
  let unavailableTarget = false;
965
965
  const commandTimeout = (timeoutMs) => recoveryDeadline === void 0 ? timeoutMs : Math.max(1, Math.min(timeoutMs, recoveryDeadline - Date.now()));
966
966
  const assertTarget = async () => {
967
- const resolved = await resolveOwnedAvdSerialAsync(avdName, { timeoutMs: commandTimeout(3e4) });
967
+ const resolved = await resolveOwnedAvdSerialAsync(avdName, {
968
+ timeoutMs: commandTimeout(3e4),
969
+ preferredSerial: serial
970
+ });
968
971
  unavailableTarget = Boolean(resolved.notRunning);
969
972
  if (resolved.serial !== serial) throw new Error(`AVD ${avdName} is no longer running on ${serial}.`);
970
973
  };
@@ -1283,6 +1286,44 @@ async function shutdownAndroidEmulator(serial, timeoutMs = ANDROID_EMULATOR_SHUT
1283
1286
  "kill"
1284
1287
  ], Math.max(1, timeoutMs - (Date.now() - started)));
1285
1288
  }
1289
+ function reverseDevicePortsTo(listing, hostPorts) {
1290
+ return String(listing ?? "").split("\n").flatMap((line) => {
1291
+ const [, device, host] = line.trim().split(/\s+/);
1292
+ return device?.startsWith("tcp:") && hostPorts.some((port) => host === `tcp:${port}`) ? [device] : [];
1293
+ });
1294
+ }
1295
+ /**
1296
+ * Removes the reverses that point an owned emulator at `metroPort`, verified by the emulator's own
1297
+ * console. The adb server connects a reverse stream from the device to the host port synchronously
1298
+ * on its single event thread (packages/modules/adb services.cpp `service_to_fd`), and on Windows a
1299
+ * refused loopback connect takes seconds, so an app retrying a stopped Metro stalls every adb call.
1300
+ * https://github.com/appandflow/stim/issues/4101
1301
+ */
1302
+ async function removeOwnedEmulatorMetroReverses(avdName, serial, metroPort, operation) {
1303
+ if (await getAvdNameForSerialAsync(serial, { operation }) !== avdName) return;
1304
+ await removeMetroReverses(serial, [metroPort], operation);
1305
+ }
1306
+ async function removeMetroReverses(serial, hostPorts, operation) {
1307
+ const adb = androidToolPath("adb", process.platform, operation?.env);
1308
+ const options = {
1309
+ timeoutMs: ADB_PROP_TIMEOUT_MS,
1310
+ killSignal: "SIGKILL",
1311
+ cwd: androidToolCwd() ?? operation?.cwd
1312
+ };
1313
+ const listing = await runOperationFileQuiet(operation, getExecutor(), adb, [
1314
+ "-s",
1315
+ serial,
1316
+ "reverse",
1317
+ "--list"
1318
+ ], options);
1319
+ for (const devicePort of reverseDevicePortsTo(listing, hostPorts)) await runOperationFileQuiet(operation, getExecutor(), adb, [
1320
+ "-s",
1321
+ serial,
1322
+ "reverse",
1323
+ "--remove",
1324
+ devicePort
1325
+ ], options);
1326
+ }
1286
1327
  function avdProcessLockPaths(avdDirectory, platform) {
1287
1328
  return ["hardware-qemu.ini.lock", "userdata-qemu.img.lock"].map((name) => join$1(avdDirectory, name, ...platform === "win32" ? ["pid"] : []));
1288
1329
  }
@@ -1557,11 +1598,20 @@ async function getAvdNameForSerialAsync(serial, { timeoutMs = EMU_AVD_NAME_TIMEO
1557
1598
  if (!out) return null;
1558
1599
  return out.split("\n")[0]?.trim() || null;
1559
1600
  }
1560
- /** `resolveOwnedAvdSerial` without blocking the event loop, with the same reads and the same timeout budget. */
1601
+ /**
1602
+ * `resolveOwnedAvdSerial` without blocking the event loop, with the same reads and the same timeout budget.
1603
+ * `preferredSerial` is the serial this workspace recorded for the AVD: it is read first, and the other
1604
+ * emulators are read only when it does not run the AVD.
1605
+ */
1561
1606
  async function resolveOwnedAvdSerialAsync(avdName, opts = {}) {
1562
- return ownedAvdSerialResolverAsync(opts)(avdName);
1607
+ return ownedAvdSerialResolverAsync(opts)(avdName, opts.preferredSerial);
1563
1608
  }
1564
- /** Shares bounded asynchronous AVD, adb and emulator-name reads across the requested AVDs. */
1609
+ /**
1610
+ * Shares bounded asynchronous AVD, adb and emulator-name reads across the requested AVDs. Emulators are
1611
+ * read concurrently within each tier (the preferred serial, then healthy ones, then unhealthy ones), and
1612
+ * the reads still pending are cancelled once one runs the requested AVD, so a wedged foreign emulator
1613
+ * neither adds up across serials nor delays the match.
1614
+ */
1565
1615
  function ownedAvdSerialResolverAsync({ timeoutMs, signal, operation } = {}) {
1566
1616
  const started = Date.now();
1567
1617
  const remaining = () => ({
@@ -1571,23 +1621,67 @@ function ownedAvdSerialResolverAsync({ timeoutMs, signal, operation } = {}) {
1571
1621
  });
1572
1622
  const avds = once(() => listAvdsAsync(remaining()));
1573
1623
  const adb = once(() => listAdbDevicesAsync(remaining()));
1574
- const names = /* @__PURE__ */ new Map();
1624
+ const reads = /* @__PURE__ */ new Map();
1575
1625
  const avdNameOf = (serial) => {
1576
- let name = names.get(serial);
1577
- if (!name) {
1578
- name = getAvdNameForSerialAsync(serial, remaining());
1579
- names.set(serial, name);
1626
+ let read = reads.get(serial);
1627
+ if (!read) {
1628
+ const own = new AbortController();
1629
+ const options = remaining();
1630
+ const entry = {
1631
+ name: getAvdNameForSerialAsync(serial, {
1632
+ ...options,
1633
+ signal: options.signal ? AbortSignal.any([options.signal, own.signal]) : own.signal
1634
+ }),
1635
+ settled: false,
1636
+ cancel: () => {
1637
+ if (entry.settled) return;
1638
+ own.abort();
1639
+ reads.delete(serial);
1640
+ }
1641
+ };
1642
+ const settle = () => {
1643
+ entry.settled = true;
1644
+ };
1645
+ entry.name.then(settle, settle);
1646
+ reads.set(serial, entry);
1647
+ read = entry;
1580
1648
  }
1581
- return name;
1649
+ return read;
1582
1650
  };
1583
- return async (avdName) => {
1651
+ const firstRunning = async (serials, avdName) => {
1652
+ try {
1653
+ return await new Promise((settle, reject) => {
1654
+ let pending = serials.length;
1655
+ if (pending === 0) return settle(void 0);
1656
+ for (const serial of serials) avdNameOf(serial).name.then((name) => {
1657
+ if (name === avdName) settle(serial);
1658
+ else if (--pending === 0) settle(void 0);
1659
+ return name;
1660
+ }, reject);
1661
+ });
1662
+ } finally {
1663
+ for (const serial of serials) reads.get(serial)?.cancel();
1664
+ }
1665
+ };
1666
+ return async (avdName, preferredSerial) => {
1584
1667
  signal?.throwIfAborted();
1585
1668
  if (!(await avds()).includes(avdName)) return { missing: true };
1586
1669
  if (!isStimOwnedAvd(avdName)) return { notOwned: true };
1587
1670
  const devices = await adb();
1588
1671
  signal?.throwIfAborted();
1589
- const candidates = [...devices.emulators, ...devices.unhealthy.filter((entry) => entry.kind === "emulator" && entry.consolePort !== void 0)];
1590
- for (const candidate of candidates) if (await avdNameOf(candidate.serial) === avdName) return { serial: candidate.serial };
1672
+ const healthy = devices.emulators.map((entry) => entry.serial);
1673
+ const unhealthy = devices.unhealthy.filter((entry) => entry.kind === "emulator" && entry.consolePort !== void 0).map((entry) => entry.serial);
1674
+ const preferred = preferredSerial && [...healthy, ...unhealthy].includes(preferredSerial) ? preferredSerial : null;
1675
+ const tiers = [
1676
+ ...preferred ? [[preferred]] : [],
1677
+ healthy.filter((serial) => serial !== preferred),
1678
+ unhealthy.filter((serial) => serial !== preferred)
1679
+ ];
1680
+ for (const tier of tiers) {
1681
+ const serial = await firstRunning(tier, avdName);
1682
+ signal?.throwIfAborted();
1683
+ if (serial) return { serial };
1684
+ }
1591
1685
  return { notRunning: true };
1592
1686
  };
1593
1687
  }
@@ -1631,4 +1725,4 @@ function ownedAvdSerialResolver({ timeoutMs } = {}) {
1631
1725
  };
1632
1726
  }
1633
1727
  //#endregion
1634
- export { resetAdoptedAvd as $, listAdbDevices as A, ownedAvdDeviceProfile as B, emulatorDiskSpaceRemedy as C, getAvdNameForSerial as D, findBuildTool as E, listAvdsAsync as F, ownedAvdSerialResolverAsync as G, ownedAvdMatchesConfiguration as H, listInstalledSystemImages as I, parseAvdSystemImage as J, ownedAvdSystemImage as K, listOrphanedAvdDirectories as L, listAvdDeviceProfiles as M, listAvdDeviceProfilesAsync as N, getAvdNameForSerialAsync as O, listAvds as P, profileNeedsFoldFeature as Q, memoizeEmulatorProbe as R, deleteAvd as S, extractEmulatorFailure as T, ownedAvdName as U, ownedAvdDirectory as V, ownedAvdSerialResolver as W, pickDefaultSystemImage as X, physicalDeviceModel as Y, probeEmulatorSerial as Z, avdPoolConfiguration as _, parkedMaxSetting as _t, androidDeviceAbi as a, waitForAndroidEmulatorShutdown as at, configureNewOwnedAvd as b, selectParked as bt, androidPoolNoCandidatesRefusal as c, configuredAndroidEmulatorApp as ct, androidToolPath as d, POOL_SETTING_REMEDY as dt, resolveOwnedAvdSerialAsync as et, assertOwnedAvdStopped as f, adoptParked as ft, avdPathExists as g, parkSim as gt, avdNameAbsent as h, isLegacyDeletionClaim as ht, androidDataFreeBytes as i, trimAndroidCaches as it, listAdbDevicesAsync as j, hostSystemImageArch as k, androidSystemImageAbi as l, isStimOwnedAvd as lt, avdIniPaths as m, eraseParkedAfter as mt, OWNED_AVD_CONFIG_DEFAULTS as n, shutdownAndroidEmulator as nt, androidHome as o, waitForBoot as ot, assertOwnedAvdStoppedAsync as p, dropParked as pt, parseAvdDeviceProfile as q, androidBuildToolName as r, systemImageSupportsFold as rt, androidPoolCandidates as s, wipeAvdUserData as st, DEFAULT_AVD_DEVICE_PROFILE as t, resolvePhysicalDevice as tt, androidToolCwd as u, isStimOwnedSim as ut, avdStorageRoots as v, readParked as vt, emulatorFailureRemedy as w, createOwnedAvd as x, bootAndroidEmulator as y, removeParkedAfter as yt, nextConsolePort as z };
1728
+ export { removeMetroReverses as $, listAdbDevices as A, ownedAvdDeviceProfile as B, emulatorDiskSpaceRemedy as C, getAvdNameForSerial as D, findBuildTool as E, listAvdsAsync as F, ownedAvdSerialResolverAsync as G, ownedAvdMatchesConfiguration as H, listInstalledSystemImages as I, parseAvdSystemImage as J, ownedAvdSystemImage as K, listOrphanedAvdDirectories as L, listAvdDeviceProfiles as M, listAvdDeviceProfilesAsync as N, getAvdNameForSerialAsync as O, listAvds as P, profileNeedsFoldFeature as Q, memoizeEmulatorProbe as R, deleteAvd as S, configuredAndroidEmulatorApp as St, extractEmulatorFailure as T, ownedAvdName as U, ownedAvdDirectory as V, ownedAvdSerialResolver as W, pickDefaultSystemImage as X, physicalDeviceModel as Y, probeEmulatorSerial as Z, avdPoolConfiguration as _, parkSim as _t, androidDeviceAbi as a, systemImageSupportsFold as at, configureNewOwnedAvd as b, removeParkedAfter as bt, androidPoolNoCandidatesRefusal as c, waitForBoot as ct, androidToolPath as d, isStimOwnedSim as dt, removeOwnedEmulatorMetroReverses as et, assertOwnedAvdStopped as f, POOL_SETTING_REMEDY as ft, avdPathExists as g, isLegacyDeletionClaim as gt, avdNameAbsent as h, eraseParkedAfter as ht, androidDataFreeBytes as i, shutdownAndroidEmulator as it, listAdbDevicesAsync as j, hostSystemImageArch as k, androidSystemImageAbi as l, wipeAvdUserData as lt, avdIniPaths as m, dropParked as mt, OWNED_AVD_CONFIG_DEFAULTS as n, resolveOwnedAvdSerialAsync as nt, androidHome as o, trimAndroidCaches as ot, assertOwnedAvdStoppedAsync as p, adoptParked as pt, parseAvdDeviceProfile as q, androidBuildToolName as r, resolvePhysicalDevice as rt, androidPoolCandidates as s, waitForAndroidEmulatorShutdown as st, DEFAULT_AVD_DEVICE_PROFILE as t, resetAdoptedAvd as tt, androidToolCwd as u, isStimOwnedAvd as ut, avdStorageRoots as v, parkedMaxSetting as vt, emulatorFailureRemedy as w, createOwnedAvd as x, selectParked as xt, bootAndroidEmulator as y, readParked as yt, nextConsolePort as z };