stim 1.21.0 → 1.22.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 (184) hide show
  1. package/README.md +60 -7
  2. package/dist/{acquire-artifact-CD4lbiWE.mjs → acquire-artifact-Co2k4f7O.mjs} +21 -3
  3. package/dist/{act-DHAw2A2S.mjs → act-DsuqEkiM.mjs} +298 -164
  4. package/dist/{activity-BzVfHSrY.mjs → activity-CMLmzp7o.mjs} +33 -12
  5. package/dist/{agent-device-usage-output-zh1vhbLp.mjs → agent-device-usage-output-DUFaTMdd.mjs} +7 -7
  6. package/dist/{android-BLgbNqox.mjs → android-ByRMUoiJ.mjs} +66 -52
  7. package/dist/{android-DUYkNF4u.mjs → android-C7sVmyI9.mjs} +160 -59
  8. package/dist/{android-CV_GCELZ.mjs → android-DGkNqa_c.mjs} +5 -5
  9. package/dist/{android-BEJ8Jpl7.mjs → android-DR5lZ2Az.mjs} +2 -2
  10. package/dist/android-cas-compiler.mjs +1 -1
  11. package/dist/{android-cas-Ct5b1xCK.mjs → android-cas-ngferVq2.mjs} +3 -3
  12. package/dist/{client-B7QVOlCr.d.mts → android-project-DVWyCbWU.d.mts} +145 -7
  13. package/dist/api.d.mts +132 -118
  14. package/dist/api.mjs +239 -42
  15. package/dist/{artifact-B8T3S5gi.mjs → artifact-8EY8SAPw.mjs} +40 -35
  16. package/dist/{artifact-DAuZ-FNU.mjs → artifact-DJPy518Y.mjs} +69 -40
  17. package/dist/artifact-platform-CXcmX_j3.mjs +27 -0
  18. package/dist/{browser-web-QQzVjz3O.mjs → browser-web-Bcv99PBM.mjs} +187 -59
  19. package/dist/{budget-Bs6SxxzO.mjs → budget-Cvwn54qV.mjs} +830 -699
  20. package/dist/{build-CoPzQRNw.mjs → build-CabC3G23.mjs} +34 -27
  21. package/dist/{build-cache-DmyR9Zzc.mjs → build-cache-Dv7RN200.mjs} +4 -4
  22. package/dist/{build-plan-CaFskB7_.mjs → build-plan-DUC8Ka9y.mjs} +74 -14
  23. package/dist/{cache-manifest-DkkqguoK.mjs → cache-manifest-DSeQjt-Q.mjs} +1 -1
  24. package/dist/cache-manifest.mjs +1 -1
  25. package/dist/{ccache-BJRZ9pG3.mjs → ccache-Btwc36-I.mjs} +6 -6
  26. package/dist/{cdp-CWmIUMbw.mjs → cdp-3fwICjzi.mjs} +1 -1
  27. package/dist/{chrome-B6tfCta1.mjs → chrome-Bbw689-6.mjs} +1 -1
  28. package/dist/{cli-0DYf8QhQ.mjs → cli-CHZa9Lvk.mjs} +20 -20
  29. package/dist/cli.mjs +1 -1
  30. package/dist/{client-UXD70ZbM.mjs → client-OyRoEuSr.mjs} +218 -47
  31. package/dist/collector-run.d.mts +1 -1
  32. package/dist/collector-run.mjs +6 -6
  33. package/dist/{command-output-B1UrjZHL.mjs → command-output-gwpm28fu.mjs} +1 -1
  34. package/dist/{config-CYcBF478.mjs → config-DlrsduwG.mjs} +17 -11
  35. package/dist/{created-devices-CqeBlrEn.mjs → created-devices-x9rN6bhs.mjs} +13 -3
  36. package/dist/{dependency-state-BbKQW83h.mjs → dependency-state-1I9KZT1Y.mjs} +2 -2
  37. package/dist/{detached-entry-DFRyG1_o.mjs → detached-entry-BwNEtIrP.mjs} +1 -1
  38. package/dist/{dev-client-UOeESKm6.mjs → dev-client-BSDR5h3F.mjs} +2 -2
  39. package/dist/{device-ExJq8Ei7.mjs → device-B-L3Fbxy.mjs} +71 -14
  40. package/dist/{device-DCxH7_By.mjs → device-DklWZLqb.mjs} +16 -16
  41. package/dist/{device-capacity-CMlqEcvw.mjs → device-capacity-DK5_EwRS.mjs} +9 -10
  42. package/dist/device-host-worker.mjs +25 -32
  43. package/dist/{device-ios-DRUeKaFV.mjs → device-ios-DoB6KFCO.mjs} +39 -16
  44. package/dist/{device-lease-run-C8raGFoP.mjs → device-lease-run-xJPQ_QQG.mjs} +5 -5
  45. package/dist/{device-pool-DMRpVX9a.mjs → device-pool-BMkdsefq.mjs} +3 -3
  46. package/dist/{doctor-DTvaDOda.mjs → doctor-Bo7BXLux.mjs} +24 -37
  47. package/dist/{doctor-Dhqebm0q.mjs → doctor-D5_EzwfA.mjs} +19 -16
  48. package/dist/doctor-fingerprint-DZLoYFVb.mjs +16 -0
  49. package/dist/doctor-fingerprint.mjs +1 -1
  50. package/dist/{doctor-BLHCYxzi.mjs → doctor-thjWAV0z.mjs} +1 -1
  51. package/dist/{doctor-watchman-Cv1F10Kj.mjs → doctor-watchman-N2wDotwQ.mjs} +5 -5
  52. package/dist/{eas-build-BomfRnFT.mjs → eas-build-CwknaQYj.mjs} +4 -4
  53. package/dist/{eas-fallback-DJwZpy0c.mjs → eas-fallback-CSgAulr5.mjs} +27 -18
  54. package/dist/{eas-session-ledger-op6A54yi.mjs → eas-session-ledger-DlY_serQ.mjs} +8 -7
  55. package/dist/{error-diagnostics-Bf0rZvG9.mjs → error-diagnostics-DBWO2rxE.mjs} +10 -10
  56. package/dist/{exec-_UbtCQcG.mjs → exec-DEoAhcze.mjs} +155 -47
  57. package/dist/{exec-H20xSg2T.d.mts → exec-DnYc4HGH.d.mts} +17 -4
  58. package/dist/{gc-_-dCXdyL.mjs → gc-CXxN-4SE.mjs} +587 -313
  59. package/dist/gc-DnNDQyaP.mjs +3 -0
  60. package/dist/{guide-Yqwt2bd6.mjs → guide-Br0LEfKy.mjs} +239 -43
  61. package/dist/{hosted-android-Bw-honsd.mjs → hosted-android-B-9f-jkd.mjs} +6 -6
  62. package/dist/{hosted-client-DB_gBPfY.mjs → hosted-client-DZAPrcro.mjs} +4 -4
  63. package/dist/{hosted-ios-CUvOf4Vr.mjs → hosted-ios-CsCS21hn.mjs} +1 -1
  64. package/dist/{hosted-logs-DL_P5AdN.mjs → hosted-logs-FIRx9X2f.mjs} +5 -5
  65. package/dist/{hosted-macos-QI141yKk.mjs → hosted-macos-_awYSpfv.mjs} +21 -12
  66. package/dist/{hosted-native-Ksyjgzq-.mjs → hosted-native-BdzE668-.mjs} +27 -14
  67. package/dist/{idle-BVeRGroT.d.mts → idle-C4cZ757K.d.mts} +3 -3
  68. package/dist/{idle-shutdown-DaKmZi0z.mjs → idle-shutdown-DDg3SdvY.mjs} +7 -7
  69. package/dist/ios-CDvlmUs7.d.mts +24 -0
  70. package/dist/{ios-CQ68hG9s.mjs → ios-Ds1clbIt.mjs} +8 -8
  71. package/dist/{ios-device-CwFMehUS.mjs → ios-device--h_-8ENa.mjs} +3 -3
  72. package/dist/{ios-device-CXGP1bsD.mjs → ios-device-BXZ0c7Kb.mjs} +1 -1
  73. package/dist/{ios-CpByZy12.mjs → ios-hcBpnEGk.mjs} +81 -34
  74. package/dist/{ios-ubFsBzwq.mjs → ios-pm4nL6By.mjs} +59 -50
  75. package/dist/{ios-project-WuwwBdvW.mjs → ios-project-CZhKODF3.mjs} +5 -5
  76. package/dist/{ios-signing-DTTIChGs.mjs → ios-signing-BtAffm0A.mjs} +2 -2
  77. package/dist/{ios-state-DvFX-K_l.mjs → ios-state-Dtw5K2aI.mjs} +1 -1
  78. package/dist/{ios-qNJt4dFm.mjs → ios-xtlRHaeB.mjs} +1 -1
  79. package/dist/{js-swap-Cc1dttps.mjs → js-swap-Bwhh4S7V.mjs} +7 -29
  80. package/dist/{launch-Zq2mOSDy.mjs → launch-BjxemNpK.mjs} +40 -39
  81. package/dist/{launch-CD1yVc7U.mjs → launch-CCzXlc8r.mjs} +16 -16
  82. package/dist/{logs-BS00Anxf.mjs → logs-BfwwadsZ.mjs} +151 -24
  83. package/dist/{logs-CMm05HxN.mjs → logs-jTS_lHSo.mjs} +10 -17
  84. package/dist/{macos-B65--pPd.mjs → macos-DVlSSyUg.mjs} +1 -1
  85. package/dist/{macos-C-0qJ6zq.mjs → macos-NfV2D7dO.mjs} +143 -46
  86. package/dist/macos-run.mjs +1 -1
  87. package/dist/maintenance-run.mjs +8 -8
  88. package/dist/{metro-DEXpIhHC.mjs → metro-DiXtA3Ty.mjs} +59 -37
  89. package/dist/{metro-gateway-BMJNlj3E.mjs → metro-gateway-CKZqKqx7.mjs} +120 -22
  90. package/dist/{named-ports-BWTzt6mu.mjs → named-ports-DkEzOfzO.mjs} +40 -33
  91. package/dist/{native-android-JACyjTZs.mjs → native-android-CqgnOgsT.mjs} +43 -242
  92. package/dist/native-gradle-artifact-inputs-DHFnmrVf.mjs +270 -0
  93. package/dist/{native-gradle-inputs-Bep9JcSF.mjs → native-gradle-inputs-Dnh84Zbd.mjs} +3 -129
  94. package/dist/{native-run-DY_RLx3C.mjs → native-run-NwUuxbkg.mjs} +4 -5
  95. package/dist/{native-runtime-Dg1Q7tfF.mjs → native-runtime-B7KIafpM.mjs} +8 -7
  96. package/dist/native-source-Y0yLhFyQ.mjs +133 -0
  97. package/dist/{native-xcode-inputs-CB2iQeWq.mjs → native-xcode-inputs-D_VOfFL7.mjs} +2 -3
  98. package/dist/{native-xcode-ios-CXX39F7o.mjs → native-xcode-ios-BYDOHWLN.mjs} +27 -32
  99. package/dist/offload-worker.d.mts +65 -2
  100. package/dist/offload-worker.mjs +305 -73
  101. package/dist/operation-exec-BW0em61G.mjs +171 -0
  102. package/dist/{ownership-DEmM5vhh.mjs → ownership-C5fo5Edb.mjs} +3 -3
  103. package/dist/{page-CPaHeYqj.mjs → page-DBL2PiUU.mjs} +8 -4
  104. package/dist/{placement-log-BqGbTa61.mjs → placement-log-BnGgJRRZ.mjs} +1 -1
  105. package/dist/{plan-placement-Bwj-IjY1.mjs → plan-placement-DIsMtN-d.mjs} +12 -8
  106. package/dist/{ports-D-MmCTdv.mjs → ports-Cy1RkmuQ.mjs} +3 -3
  107. package/dist/{prebuild-uD_ipCxF.mjs → prebuild-D4JoEAEs.mjs} +49 -14
  108. package/dist/{project-BZNjmW_8.mjs → project-wM_XFTvE.mjs} +16 -16
  109. package/dist/{pull-request-Lfetn8Kr.mjs → pull-request-W1IuLXDt.mjs} +102 -20
  110. package/dist/{pull-request-zjCSAryH.d.mts → pull-request-eODyIqZF.d.mts} +6 -2
  111. package/dist/pull-requests.d.mts +2 -2
  112. package/dist/pull-requests.mjs +2 -2
  113. package/dist/{react-native-android-DuzH0cn3.mjs → react-native-android-6OZ7MSM7.mjs} +71 -48
  114. package/dist/{react-native-build-rhk4smwQ.mjs → react-native-build-Dvd8hC21.mjs} +8 -8
  115. package/dist/{react-native-doctor-CQpuIMso.mjs → react-native-doctor-DsoSF1Y9.mjs} +7 -7
  116. package/dist/{react-native-ios-Bhq1rJeV.mjs → react-native-ios-4u1Rn-GR.mjs} +40 -26
  117. package/dist/{recordings-DYteQSu4.mjs → recordings-CQmfzAh7.mjs} +1 -1
  118. package/dist/registration-BcRgF6Ox.mjs +51 -0
  119. package/dist/{reload-CmTRFJaX.mjs → reload-BzkYabB-.mjs} +16 -15
  120. package/dist/{reload-DzjSl5P_.mjs → reload-D7AVlb8N.mjs} +1 -1
  121. package/dist/{remote-ByYkXdGD.mjs → remote-DjGDmlTZ.mjs} +18 -15
  122. package/dist/{result-DNiS-tTP.mjs → result-D2Ykz_iD.mjs} +24 -23
  123. package/dist/{run-D23HmJMs.mjs → run-DidHghD1.mjs} +3 -2
  124. package/dist/{selection-CSl9i5qU.mjs → selection-DSMbWEX2.mjs} +6 -5
  125. package/dist/{server-bare-CxTrvDg8.mjs → server-bare-CafzuyFB.mjs} +2 -2
  126. package/dist/{server-command-CtzloH3v.mjs → server-command-C6QjeupS.mjs} +5 -5
  127. package/dist/{server-expo-DT_F_uVI.mjs → server-expo-CrjB29pq.mjs} +7 -5
  128. package/dist/server-expo-D58c9L0M.mjs +2 -0
  129. package/dist/{settings-CsJDEXgK.mjs → settings-C7O8A6ZI.mjs} +223 -243
  130. package/dist/{settings-T1gtc3bf.mjs → settings-DpR-1Wnn.mjs} +1 -1
  131. package/dist/{settings-write-HyVZ8eQ8.mjs → settings-write-CzD60rOD.mjs} +7 -6
  132. package/dist/settings.schema.json +13 -0
  133. package/dist/{simslim-Cgk17SXV.mjs → simslim-BNckWpXi.mjs} +2 -2
  134. package/dist/{slot-launch-DF8VfT64.mjs → slot-launch-CbfmNgM6.mjs} +6 -6
  135. package/dist/{stage-BIS0ydsV.mjs → stage-Bps5oius.mjs} +21 -4
  136. package/dist/{start-RChynRDp.mjs → start-Br-oxqMD.mjs} +1 -1
  137. package/dist/{start-7lpH7hmN.mjs → start-EojN4WFz.mjs} +622 -393
  138. package/dist/{state-C4ZtUisW.mjs → state--smjFSTL.mjs} +22 -7
  139. package/dist/{state-XMziexv2.mjs → state-08I3hw3_.mjs} +2 -2
  140. package/dist/{stats-53T7OTb9.mjs → stats-D3GerlFW.mjs} +5 -5
  141. package/dist/{stats-report-BFfMpWWF.mjs → stats-report-BzaPrRm6.mjs} +1 -1
  142. package/dist/{status-DqEUSSxs.mjs → status-BRROykC8.mjs} +2 -2
  143. package/dist/{status-CSi6tlRq.mjs → status-EvrK72L2.mjs} +1 -1
  144. package/dist/{status-BL7jVyAB.mjs → status-WBDajew2.mjs} +43 -29
  145. package/dist/{status-watch-GDCfqpTE.mjs → status-watch-Bg_u-pwR.mjs} +48 -16
  146. package/dist/status-worker.mjs +1 -1
  147. package/dist/{stim-desktop-C12we7lz.mjs → stim-desktop-D5OGpYZM.mjs} +2 -2
  148. package/dist/{stop-Bk_1qiV6.mjs → stop-DTZ9uTw3.mjs} +2 -2
  149. package/dist/{stop-D7h6jAXQ.mjs → stop-Dky10AEJ.mjs} +31 -22
  150. package/dist/{stop-CPzlM1L-.mjs → stop-Mv6W9FAk.mjs} +15 -16
  151. package/dist/supervisor-run.d.mts +3 -3
  152. package/dist/supervisor-run.mjs +28 -36
  153. package/dist/{support-Dlu37g6H.mjs → support-CNioewsd.mjs} +10 -10
  154. package/dist/{support-67vraa1Q.mjs → support-yUiOjnVY.mjs} +8 -8
  155. package/dist/swiftpm-artifact-inputs-DFcGsXDP.mjs +153 -0
  156. package/dist/swiftpm-macos-BIak-Rxv.mjs +532 -0
  157. package/dist/toolchain-BY8X8HXx.mjs +2 -0
  158. package/dist/{toolchain-Hp9sCpK4.mjs → toolchain-DpJMHq0n.mjs} +9 -6
  159. package/dist/{trigger-BAahxUPq.mjs → trigger-Cbyc41d_.mjs} +3 -3
  160. package/dist/trigger-yb_nFzPF.mjs +2 -0
  161. package/dist/{warm-progress-BHX5r0Ei.mjs → warm-progress-CA3TgT3-.mjs} +29 -16
  162. package/dist/web-CZ07gd77.mjs +2 -0
  163. package/dist/{web-CJmIj6AI.mjs → web-DKx6yoFA.mjs} +11 -10
  164. package/dist/web-run.mjs +11 -9
  165. package/dist/windows-report.mjs +1 -1
  166. package/dist/{workspace-state-Cjuigeeu.mjs → workspace-state-U08dEzhG.mjs} +3 -3
  167. package/dist/{workspaces-BccMFmGQ.mjs → workspaces-CpANmBlq.mjs} +594 -266
  168. package/dist/{worktree-DQ6Zi2Mv.mjs → worktree-CHecZ4Oq.mjs} +850 -551
  169. package/dist/worktree-ZqZVjD9K.mjs +2 -0
  170. package/dist/{xcode-Zlx3qjEs.mjs → xcode-BXn9-xYx.mjs} +5 -5
  171. package/package.json +7 -7
  172. package/shim/android-audio/Main.java +24 -0
  173. package/shim/android-audio/README.md +17 -0
  174. package/shim/android-audio.jar +0 -0
  175. package/dist/gc-C-WwL0yi.mjs +0 -3
  176. package/dist/git-C3iTsJaZ.mjs +0 -73
  177. package/dist/server-expo-C-90EhcG.mjs +0 -2
  178. package/dist/settings-DkdmlelE.d.mts +0 -15
  179. package/dist/swiftpm-macos-p8WZbxKU.mjs +0 -130
  180. package/dist/toolchain-DhKwp9iI.mjs +0 -2
  181. package/dist/trigger-Bf3uDcpv.mjs +0 -2
  182. package/dist/web-DknQETPs.mjs +0 -2
  183. package/dist/workspace-process-lock-1_1OoVW5.mjs +0 -57
  184. package/dist/worktree-CK2MpJW5.mjs +0 -2
@@ -1,30 +1,30 @@
1
- import { n as isTimeoutError, t as getExecutor } from "./exec-_UbtCQcG.mjs";
1
+ import { n as isTimeoutError, r as WorkspaceProcessCleanupError, t as getExecutor } from "./exec-DEoAhcze.mjs";
2
2
  import { g as workspaceStateFile$1, m as workspaceLogsDir$1 } from "./paths-DQtTgsBf.mjs";
3
- import { F as isOnMountedVolume, I as listMountedVolumes, K as projectDeviceSlots, N as directorySizeAsync, P as formatBytes, R as measuredDirectorySizeAsync, _ as loadConfig$1, p as getProject } from "./config-CYcBF478.mjs";
4
- import { o as formatLongDuration, u as plural } from "./command-output-B1UrjZHL.mjs";
5
- import { r as readWorkspaceState$1, u as workspaceLastUsed } from "./workspace-state-Cjuigeeu.mjs";
6
- import { E as maintenanceKeepSetting, J as resolveProjectSettingsAsync, Lt as resolveFullRef, Ot as listWorktrees, Rt as resolveFullRefAsync, St as hasPopulatedSubmodulesAsync, Tt as hasUncommittedWorkAsync, Ut as unpushedCommitsAsync, Vt as sourceCheckoutOf, Xt as removeTemporaryEntry, _t as dirtyPathsAsync, kt as listWorktreesAsync, q as resolveProjectSettings, zt as resolveSourceCheckoutAsync } from "./settings-CsJDEXgK.mjs";
7
- import { i as gitQuietAsync, r as gitQuiet } from "./git-C3iTsJaZ.mjs";
8
- import { i as findProjectRoot$1 } from "./project-BZNjmW_8.mjs";
9
- import { a as unregister, n as readManifest } from "./cache-manifest-DkkqguoK.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-Bs6SxxzO.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-gwpm28fu.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-wM_XFTvE.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-Cvwn54qV.mjs";
11
11
  import { t as canonicalPath } from "./paths-BsJMoPP2.mjs";
12
- import { B as takeGcResults, a as collectWorkspaceOutputs, c as listWorkspaceDirs, f as withIdleWorkspace, i as collectOrphanedWorkspaces, o as deleteOrphanedWorkspaces, p as workspaceInUse, r as clearWorkspaceOutputs, s as isInsideWorkspaces, z as recordGcResult } from "./workspaces-BccMFmGQ.mjs";
13
- import { N as listBuildLocks, b as leaseIsExpired, g as workspaceActivity, x as listLeaseFiles } from "./activity-BzVfHSrY.mjs";
14
- import { H as ownedAvdSerialResolver, M as listAvds, U as ownedAvdSerialResolverAsync, gt as readParked, z as ownedAvdDirectory } from "./android-DUYkNF4u.mjs";
15
- import { d as listAllIosSims, f as listAllIosSimsAsync } from "./ios-CpByZy12.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-CpANmBlq.mjs";
13
+ import { N as listBuildLocks, b as leaseIsExpired, g as workspaceActivity, x as listLeaseFiles } from "./activity-CMLmzp7o.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";
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 lowestFree, C as measurePressure, D as diskPressureLevel, O as levelFloors, S as measureDisk, a as matchWorktreeEntry, c as reclaimKeys, i as excludeWatchmanCookies, j as protectWindowMs, m as removeWorktreeTarget, o as nestedWorktrees, r as excludePodChurn, s as porcelainPath, w as measurePressureAsync, x as cacheBlocked } from "./worktree-DQ6Zi2Mv.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-BHX5r0Ei.mjs";
20
- import { n as describePullRequest, o as pullRequestLookups, r as endedPullRequest } from "./pull-request-Lfetn8Kr.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-CHecZ4Oq.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";
21
+ import { readClaimSet } from "@stim-cli/core/ownership-claim";
21
22
  import { existsSync, lstatSync, readdirSync, realpathSync, rmSync, statSync } from "node:fs";
22
23
  import { tmpdir } from "node:os";
23
24
  import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
24
25
  import { buildCacheRoot, configDir, metroCacheRoot } from "@stim-cli/core";
25
26
  import { LAST_BUILD_KEYS, MAINTENANCE_LOG_NAME, loadConfig, readBuildHistory, readMaintenanceState, readPullRequestCache, settingDefinition, sharedCompilationCache } from "@stim-cli/core/state";
26
27
  import { isAbsolute as isAbsolute$1, join as join$1 } from "path";
27
- import { readClaimSet } from "@stim-cli/core/ownership-claim";
28
28
  import chalk from "chalk";
29
29
  import { existsSync as existsSync$1, readdirSync as readdirSync$1, statSync as statSync$1 } from "fs";
30
30
  import { lstat, readdir, realpath, rename, stat } from "node:fs/promises";
@@ -175,22 +175,9 @@ function idleDaysOf(keys, now) {
175
175
  const last = lastUsedOf(keys);
176
176
  return Number.isFinite(last) ? Math.max(0, Math.floor((now - last) / DAY_MS)) : null;
177
177
  }
178
- function deviceReads() {
179
- let sims;
180
- let resolver;
181
- return {
182
- sims: () => {
183
- if (sims === void 0) try {
184
- sims = listAllIosSims({ timeoutMs: DEVICE_LIST_TIMEOUT_MS });
185
- } catch {
186
- sims = null;
187
- }
188
- return sims;
189
- },
190
- avdSerial: (avdName) => (resolver ??= ownedAvdSerialResolver({ timeoutMs: DEVICE_LIST_TIMEOUT_MS }))(avdName)
191
- };
192
- }
193
- async function reportDeviceReads(keys) {
178
+ async function reportDeviceReads(keys, operation) {
179
+ const signal = operation?.signal;
180
+ signal?.throwIfAborted();
194
181
  const slots = keys.flatMap((key) => {
195
182
  try {
196
183
  return projectDeviceSlots(getProject(key));
@@ -204,18 +191,34 @@ async function reportDeviceReads(keys) {
204
191
  }));
205
192
  let sims = null;
206
193
  if (requestedSims.size) try {
207
- sims = await listAllIosSimsAsync({ timeoutMs: DEVICE_LIST_TIMEOUT_MS });
208
- } catch {}
194
+ sims = await listAllIosSimsAsync({
195
+ timeoutMs: DEVICE_LIST_TIMEOUT_MS,
196
+ operation,
197
+ signal
198
+ });
199
+ } catch (error) {
200
+ if (error instanceof WorkspaceProcessCleanupError) throw error;
201
+ signal?.throwIfAborted();
202
+ }
209
203
  const names = new Set(slots.flatMap(({ platforms }) => {
210
204
  const android = platforms.android;
211
205
  return android?.owned && android.avdName ? [android.avdName] : [];
212
206
  }));
213
207
  const avds = /* @__PURE__ */ new Map();
214
- const resolver = ownedAvdSerialResolverAsync({ timeoutMs: DEVICE_LIST_TIMEOUT_MS });
215
- for (const name of names) try {
216
- avds.set(name, { value: await resolver(name) });
217
- } catch (error) {
218
- avds.set(name, { error });
208
+ const resolver = ownedAvdSerialResolverAsync({
209
+ timeoutMs: DEVICE_LIST_TIMEOUT_MS,
210
+ operation,
211
+ signal
212
+ });
213
+ for (const name of names) {
214
+ signal?.throwIfAborted();
215
+ try {
216
+ avds.set(name, { value: await resolver(name) });
217
+ } catch (error) {
218
+ if (error instanceof WorkspaceProcessCleanupError) throw error;
219
+ signal?.throwIfAborted();
220
+ avds.set(name, { error });
221
+ }
219
222
  }
220
223
  return {
221
224
  sims: (udid) => requestedSims.has(udid) || sims?.some((sim) => sim.udid === udid) ? sims : null,
@@ -227,8 +230,10 @@ async function reportDeviceReads(keys) {
227
230
  }
228
231
  };
229
232
  }
230
- function inUseOf(keys, checks, devices = deviceReads()) {
231
- return [.../* @__PURE__ */ new Set([...keys.flatMap((key) => workspaceInUse(key, checks)), ...deviceUseOf(keys, devices)])];
233
+ async function inUseOf(keys, checks, devices, signal, operation) {
234
+ const reasons = [];
235
+ for (const key of keys) reasons.push(...await workspaceInUseAsync(key, checks, signal, operation));
236
+ return [.../* @__PURE__ */ new Set([...reasons, ...deviceUseOf(keys, devices)])];
232
237
  }
233
238
  function deviceUseOf(keys, devices) {
234
239
  const reasons = [];
@@ -297,12 +302,18 @@ function worktreeActivityOf(path, keys) {
297
302
  ])?.trim();
298
303
  return activityFromGitDir(gitDir, keys);
299
304
  }
300
- async function worktreeActivityOfAsync(path, keys) {
305
+ async function worktreeActivityOfAsync(path, keys, operation) {
301
306
  const gitDir = (await gitQuietAsync(path, [
302
307
  "rev-parse",
303
308
  "--path-format=absolute",
304
309
  "--git-dir"
305
- ], { killSignal: "SIGKILL" }))?.trim();
310
+ ], {
311
+ ...operation ? childEnvironment(operation.env) : {},
312
+ signal: operation?.signal,
313
+ declareFor: operation,
314
+ cwd: operation?.cwd,
315
+ killSignal: "SIGKILL"
316
+ }))?.trim();
306
317
  return activityFromGitDir(gitDir, keys);
307
318
  }
308
319
  function activityFromGitDir(gitDir, keys) {
@@ -352,10 +363,14 @@ async function unlessTimedOut(read, unknown) {
352
363
  throw error;
353
364
  }
354
365
  }
355
- async function porcelainOf(path, gitAnswered) {
366
+ async function porcelainOf(path, gitAnswered, operation) {
356
367
  if (gitAnswered === null) return null;
357
368
  if (!gitAnswered) return [];
358
- const lines = await unlessTimedOut(() => dirtyPathsAsync(path, { limit: Infinity }), null);
369
+ const lines = await unlessTimedOut(() => dirtyPathsAsync(path, { limit: Infinity }, {
370
+ env: operation?.env,
371
+ signal: operation?.signal,
372
+ declareFor: operation
373
+ }), null);
359
374
  return lines?.length ? lines : null;
360
375
  }
361
376
  function candidateRoots() {
@@ -367,13 +382,20 @@ function candidateRoots() {
367
382
  ...linkedWorktreesOf(registered)
368
383
  ])].filter((root) => existsSync$1(root)).toSorted();
369
384
  }
370
- async function candidateRootsAsync() {
385
+ async function candidateRootsAsync(operation) {
386
+ operation?.signal.throwIfAborted();
387
+ const options = {
388
+ env: operation?.env,
389
+ signal: operation?.signal,
390
+ declareFor: operation
391
+ };
371
392
  const registered = Object.keys(loadConfig$1()?.projects ?? {}).filter(isAbsolute$1);
372
393
  const recorded = listWorkspaceDirs().flatMap((entry) => entry.projectRoot ? [entry.projectRoot] : []);
373
394
  const linked = /* @__PURE__ */ new Set();
374
395
  for (const root of registered) {
396
+ operation?.signal.throwIfAborted();
375
397
  if (!existsSync$1(root)) continue;
376
- const { entries, ...source } = await resolveSourceCheckoutAsync(root);
398
+ const { entries, ...source } = await resolveSourceCheckoutAsync(root, options);
377
399
  if ("refusal" in source) continue;
378
400
  for (const entry of entries) if (!entry.bare && !entry.prunable && entry.path !== source.path) linked.add(entry.path);
379
401
  }
@@ -394,8 +416,8 @@ function linkedWorktreesOf(roots) {
394
416
  }
395
417
  return [...linked];
396
418
  }
397
- async function staleFetch(repo, now, freshMs) {
398
- const at = await lastFetchedAtAsync(repo);
419
+ async function staleFetch(repo, now, freshMs, execution) {
420
+ const at = await lastFetchedAtAsync(repo, execution);
399
421
  if (at !== null && now - at < freshMs) return null;
400
422
  return at === null ? "the repository has no recorded fetch" : `the repository was last fetched ${formatLongDuration(Math.max(0, now - at))} ago`;
401
423
  }
@@ -412,18 +434,27 @@ function notRefreshedNote(fetch, facts) {
412
434
  const stale = [fetch, lookup && "unavailable" in lookup && lookup.unavailable === PULL_REQUEST_NOT_CHECKED ? "its pull request was not checked" : null].filter(Boolean);
413
435
  return stale.length ? `merge state not refreshed: ${stale.join(", ")}; the next maintenance worktree check, which a stim command starts when it is due, or \`stim gc --delete\` checks again` : null;
414
436
  }
415
- async function checkMergeStates(pending, idle, grace, noOwnCommitsIdleMs, refresh, freshMs) {
437
+ async function checkMergeStates(pending, idle, grace, noOwnCommitsIdleMs, refresh, freshMs, operation) {
416
438
  const repos = /* @__PURE__ */ new Map();
439
+ const execution = {
440
+ ...operation ? childEnvironment(operation.env) : {},
441
+ signal: operation?.signal,
442
+ declareFor: operation,
443
+ cwd: operation?.cwd,
444
+ killSignal: "SIGKILL"
445
+ };
417
446
  for (const entry of pending) repos.set(entry.repo, [...repos.get(entry.repo) ?? [], entry]);
418
447
  for (const [repo, entries] of repos) {
419
- const target = refresh ? await fetchDefaultBranchAsync(repo, grace.now) : await localDefaultBranchAsync(repo);
420
- const fetch = refresh ? null : await staleFetch(repo, grace.now, freshMs);
448
+ operation?.signal.throwIfAborted();
449
+ const target = refresh ? await fetchDefaultBranchAsync(repo, grace.now, execution) : await localDefaultBranchAsync(repo, execution);
450
+ const fetch = refresh ? null : await staleFetch(repo, grace.now, freshMs, execution);
421
451
  for (const { candidate, facts } of entries) {
452
+ operation?.signal.throwIfAborted();
422
453
  const merge = "error" in target ? {
423
454
  merged: false,
424
455
  unknown: true,
425
456
  detail: `merge state unknown: ${target.error}`
426
- } : await mergeStateAsync(candidate.path, target);
457
+ } : await mergeStateAsync(candidate.path, target, execution);
427
458
  const verdict = worktreeSkipReason({
428
459
  ...facts,
429
460
  merge
@@ -442,8 +473,8 @@ async function checkMergeStates(pending, idle, grace, noOwnCommitsIdleMs, refres
442
473
  * Classifies each linked worktree of a registered project or its repository. A merged one is removable; with `idle`, so is one unused for
443
474
  * `olderThan` days; with `removesNoOwnCommits`, so is one with no commits of its own beyond the default branch whose
444
475
  * last activity is older than the grace period, and never younger than 5 minutes. Device use is read last, in one pass against one simulator list and one AVD resolver, so
445
- * the resolver's time budget covers only device reads. Pull requests are asked once per repository, all
446
- * repositories at once. Merge state is checked only where it decides the verdict, after one fetch per repository.
476
+ * the resolver's time budget covers only device reads. Pull requests are asked once per repository, sequentially
477
+ * with an operation and concurrently otherwise. Merge state is checked only where it decides the verdict, after one fetch per repository.
447
478
  * Without `refresh`, it runs no fetch and no `gh`: merge state comes from the existing remote-tracking ref and pull
448
479
  * requests from the cache `status --watch` writes, and a kept verdict that fresher state could change says so.
449
480
  */
@@ -456,11 +487,18 @@ async function collectWorktreeSweep({ idle, olderThan, now, graceMs, removesNoOw
456
487
  const freshMs = refresh ? 0 : reportFreshMs(operation);
457
488
  const noOwnCommitsIdleMs = removesNoOwnCommits ? Math.max(grace.ms, NO_OWN_COMMITS_PRESSURE_FLOOR_MS) : null;
458
489
  const groups = /* @__PURE__ */ new Map();
490
+ operation?.signal.throwIfAborted();
491
+ const read = {
492
+ env: operation?.env,
493
+ signal: operation?.signal,
494
+ declareFor: operation
495
+ };
459
496
  const outside = [];
460
- const candidates = await candidateRootsAsync();
497
+ const candidates = await candidateRootsAsync(operation);
461
498
  if (candidates.length) phase("worktrees", `checking ${plural(candidates.length, "project")} for merged or idle worktrees`, operation);
462
499
  for (const root of candidates) {
463
- const entry = matchWorktreeEntry(await listWorktreesAsync(root), root);
500
+ operation?.signal.throwIfAborted();
501
+ const entry = matchWorktreeEntry(await listWorktreesAsync(root, read), root);
464
502
  if (!entry) {
465
503
  outside.push({
466
504
  path: root,
@@ -480,22 +518,23 @@ async function collectWorktreeSweep({ idle, olderThan, now, graceMs, removesNoOw
480
518
  }
481
519
  const inspected = [];
482
520
  for (const [path, roots] of groups) {
483
- const { entries, ...source } = await resolveSourceCheckoutAsync(path);
521
+ operation?.signal.throwIfAborted();
522
+ const { entries, ...source } = await resolveSourceCheckoutAsync(path, read);
484
523
  const entry = matchWorktreeEntry(entries, path);
485
524
  const keys = [.../* @__PURE__ */ new Set([...roots, ...reclaimKeys(path)])];
486
525
  const idleDays = idleDaysOf(keys, now);
487
526
  const linked = !("refusal" in source) && entry !== null && source.path !== entry.path;
488
- const activity = linked && (grace.ms > 0 || days !== null || removesNoOwnCommits) ? await worktreeActivityOfAsync(path, keys) : null;
489
- const gitAnswered = linked ? await unlessTimedOut(() => hasUncommittedWorkAsync(path), null) : null;
490
- const head = linked ? await resolveFullRefAsync(path, "HEAD") : null;
527
+ const activity = linked && (grace.ms > 0 || days !== null || removesNoOwnCommits) ? await worktreeActivityOfAsync(path, keys, operation) : null;
528
+ const gitAnswered = linked ? await unlessTimedOut(() => hasUncommittedWorkAsync(path, read), null) : null;
529
+ const head = linked ? await resolveFullRefAsync(path, "HEAD", read) : null;
491
530
  const facts = {
492
531
  source: "refusal" in source ? source : linked ? "linked" : "source",
493
532
  bare: Boolean(entry?.bare),
494
533
  locked: Boolean(entry?.locked),
495
534
  nested: entry ? nestedWorktrees(entries, entry.path) : [],
496
- porcelain: await porcelainOf(path, gitAnswered),
497
- unpushed: linked ? await unlessTimedOut(() => unpushedCommitsAsync(path), null) : null,
498
- submodules: linked ? await unlessTimedOut(() => hasPopulatedSubmodulesAsync(path), null) : false,
535
+ porcelain: await porcelainOf(path, gitAnswered, operation),
536
+ unpushed: linked ? await unlessTimedOut(() => unpushedCommitsAsync(path, read), null) : null,
537
+ submodules: linked ? await unlessTimedOut(() => hasPopulatedSubmodulesAsync(path, read), null) : false,
499
538
  inUse: [],
500
539
  idleDays,
501
540
  merge: null,
@@ -521,10 +560,10 @@ async function collectWorktreeSweep({ idle, olderThan, now, graceMs, removesNoOw
521
560
  });
522
561
  }
523
562
  const linked = inspected.filter(({ facts }) => facts.source === "linked");
524
- for (const { candidate, facts } of linked) facts.inUse = candidate.keys.flatMap((key) => workspaceInUse(key, { managedLocks: true }));
525
- const devices = await reportDeviceReads(linked.flatMap(({ candidate }) => candidate.keys));
563
+ for (const { candidate, facts } of linked) for (const key of candidate.keys) facts.inUse.push(...await workspaceInUseAsync(key, { managedLocks: true }, operation?.signal, operation));
564
+ const devices = await reportDeviceReads(linked.flatMap(({ candidate }) => candidate.keys), operation);
526
565
  for (const { candidate, facts } of linked) facts.inUse = [.../* @__PURE__ */ new Set([...facts.inUse, ...deviceUseOf(candidate.keys, devices)])];
527
- if (refresh) await lookUpPullRequests(inspected);
566
+ if (refresh) await lookUpPullRequests(inspected, operation);
528
567
  else readCachedPullRequests(inspected, now, freshMs);
529
568
  const worktrees = [];
530
569
  const pending = [];
@@ -543,7 +582,7 @@ async function collectWorktreeSweep({ idle, olderThan, now, graceMs, removesNoOw
543
582
  });
544
583
  worktrees.push(candidate);
545
584
  }
546
- await checkMergeStates(pending, days, grace, noOwnCommitsIdleMs, refresh, freshMs);
585
+ await checkMergeStates(pending, days, grace, noOwnCommitsIdleMs, refresh, freshMs, operation);
547
586
  const listed = [...worktrees, ...outside].filter((w) => idle || w.skipCode !== "not-a-worktree" && w.skipCode !== "source-checkout");
548
587
  return {
549
588
  idle: days === null ? null : {
@@ -556,7 +595,7 @@ async function collectWorktreeSweep({ idle, olderThan, now, graceMs, removesNoOw
556
595
  worktrees: listed
557
596
  };
558
597
  }
559
- async function lookUpPullRequests(inspected) {
598
+ async function lookUpPullRequests(inspected, operation) {
560
599
  const repos = /* @__PURE__ */ new Map();
561
600
  for (const { candidate, facts, repo, branch } of inspected) {
562
601
  if (repo === null || branch === null || candidate.head === null) continue;
@@ -570,11 +609,12 @@ async function lookUpPullRequests(inspected) {
570
609
  query
571
610
  }]);
572
611
  }
573
- const lookup = pullRequestLookups();
574
- await Promise.all([...repos].map(async ([repo, entries]) => {
612
+ const lookup = pullRequestLookups({ operation });
613
+ const probes = [...repos].map(([repo, entries]) => async () => {
575
614
  const answers = await lookup(repo, entries.map(({ query }) => query));
576
615
  const at = (/* @__PURE__ */ new Date()).toISOString();
577
616
  entries.forEach(({ facts, query }, i) => {
617
+ operation?.signal.throwIfAborted();
578
618
  const answer = answers[i] ?? null;
579
619
  facts.pullRequest = answer;
580
620
  if (answer && "pullRequest" in answer) recordPullRequestLookup({
@@ -583,7 +623,12 @@ async function lookUpPullRequests(inspected) {
583
623
  head: query.head
584
624
  }, answer.pullRequest, at);
585
625
  });
586
- }));
626
+ });
627
+ if (operation) for (const probe of probes) {
628
+ operation.signal.throwIfAborted();
629
+ await probe();
630
+ }
631
+ else await Promise.all(probes.map((probe) => probe()));
587
632
  }
588
633
  function readCachedPullRequests(inspected, now, freshMs) {
589
634
  let gh;
@@ -613,33 +658,43 @@ function cachedPullRequest(cached, branch, head, now, freshMs) {
613
658
  endedAt: open ? null : endedAt ?? null
614
659
  } };
615
660
  }
616
- function noOwnCommitsRecheck(sweep, path, keys, now) {
661
+ async function noOwnCommitsRecheck(sweep, path, keys, now, operation) {
617
662
  if (!sweep.idle) {
618
663
  if (sweep.noOwnCommitsIdleMs === null) return "it has no commits of its own and is not idle";
619
- return noOwnCommitsActivityBlocker(worktreeActivityOf(path, keys), sweep.noOwnCommitsIdleMs, now)?.text ?? null;
664
+ return noOwnCommitsActivityBlocker(await worktreeActivityOfAsync(path, keys, operation), sweep.noOwnCommitsIdleMs, now)?.text ?? null;
620
665
  }
621
666
  const idleDays = idleDaysOf(keys, now);
622
667
  if (idleDays !== null) return idleDays < sweep.idle.olderThan ? `used ${idleDays}d ago since gc checked it` : null;
623
- const activity = worktreeActivityOf(path, keys);
668
+ const activity = await worktreeActivityOfAsync(path, keys, operation);
624
669
  if (!activity) return "its last activity could not be read";
625
670
  const days = activityIdleDays(activity, now);
626
671
  return days < sweep.idle.olderThan ? `active ${days}d ago since gc checked it: ${activity.basis}` : null;
627
672
  }
628
- async function removeWorktrees(sweep, { now = Date.now() } = {}) {
673
+ async function removeWorktrees(sweep, { now = Date.now(), operation } = {}, output = GC_OUTPUT) {
629
674
  let failures = 0;
630
675
  for (const candidate of sweep.worktrees) {
676
+ operation?.signal.throwIfAborted();
631
677
  if (candidate.skipped) continue;
632
678
  let kept = [];
633
- const guard = (lockedKeys) => {
679
+ const guard = async (lockedKeys) => {
634
680
  const keys = [.../* @__PURE__ */ new Set([...candidate.keys, ...lockedKeys])];
635
681
  const unlocked = keys.filter((key) => !lockedKeys.includes(key));
636
- const reasons = [...inUseOf(lockedKeys, { managedLocks: false }), ...inUseOf(unlocked, { managedLocks: true })].map((r) => `in use: ${r}`);
682
+ const devices = await reportDeviceReads(keys, operation);
683
+ const reasons = [...await inUseOf(lockedKeys, { managedLocks: false }, devices, operation?.signal, operation), ...await inUseOf(unlocked, { managedLocks: true }, devices, operation?.signal, operation)].map((r) => `in use: ${r}`);
637
684
  const mergedHead = candidate.merge?.merged ? candidate.merge.head : endedPullRequest(candidate.pullRequest) ? candidate.head : null;
638
685
  if (mergedHead) {
639
- if (resolveFullRef(candidate.path, "HEAD") !== mergedHead) reasons.push("its HEAD moved since gc checked it");
686
+ if (await resolveFullRefAsync(candidate.path, "HEAD", {
687
+ env: operation?.env,
688
+ signal: operation?.signal,
689
+ declareFor: operation
690
+ }) !== mergedHead) reasons.push("its HEAD moved since gc checked it");
640
691
  } else if (hasNoOwnCommits(candidate.merge)) {
641
- if (resolveFullRef(candidate.path, "HEAD") !== candidate.head) reasons.push("its HEAD moved since gc checked it");
642
- const recent = noOwnCommitsRecheck(sweep, candidate.path, keys, Date.now());
692
+ if (await resolveFullRefAsync(candidate.path, "HEAD", {
693
+ env: operation?.env,
694
+ signal: operation?.signal,
695
+ declareFor: operation
696
+ }) !== candidate.head) reasons.push("its HEAD moved since gc checked it");
697
+ const recent = await noOwnCommitsRecheck(sweep, candidate.path, keys, Date.now(), operation);
643
698
  if (recent) reasons.push(recent);
644
699
  } else {
645
700
  const idleDays = idleDaysOf(keys, now);
@@ -649,7 +704,7 @@ async function removeWorktrees(sweep, { now = Date.now() } = {}) {
649
704
  const recent = graceBlocker({
650
705
  merge: candidate.merge,
651
706
  pullRequest: candidate.pullRequest,
652
- activity: worktreeActivityOf(candidate.path, keys)
707
+ activity: await worktreeActivityOfAsync(candidate.path, keys, operation)
653
708
  }, {
654
709
  ms: sweep.graceMs,
655
710
  now: Date.now()
@@ -669,22 +724,26 @@ async function removeWorktrees(sweep, { now = Date.now() } = {}) {
669
724
  linkedOnly: true,
670
725
  guard,
671
726
  mergedHead,
672
- expectedHead
727
+ expectedHead,
728
+ output,
729
+ operation
673
730
  });
674
731
  } catch (thrown) {
732
+ if (thrown instanceof WorkspaceProcessCleanupError || thrown instanceof Error && "reportCleanup" in thrown) throw thrown;
733
+ operation?.signal.throwIfAborted();
675
734
  error = thrown?.message || String(thrown);
676
- console.error(chalk.red(`Could not remove ${candidate.path}: ${error}`));
735
+ output.error(chalk.red(`Could not remove ${candidate.path}: ${error}`));
677
736
  }
678
737
  if (removed) {
679
- console.log(chalk.green(`Removed the worktree ${candidate.path} (${worktreeRemovalReason(candidate, sweep)})`));
680
- recordGcResult("worktree", "done", candidate.path, { detail: worktreeRemovalReason(candidate, sweep) });
738
+ output.log(chalk.green(`Removed the worktree ${candidate.path} (${worktreeRemovalReason(candidate, sweep)})`));
739
+ output.record("worktree", "done", candidate.path, { detail: worktreeRemovalReason(candidate, sweep) });
681
740
  } else if (kept.length) {
682
- console.log(chalk.yellow(`Kept the worktree ${candidate.path}: ${kept.join("; ")}`));
683
- recordGcResult("worktree", "kept", candidate.path, { detail: kept.join("; ") });
741
+ output.log(chalk.yellow(`Kept the worktree ${candidate.path}: ${kept.join("; ")}`));
742
+ output.record("worktree", "kept", candidate.path, { detail: kept.join("; ") });
684
743
  } else {
685
744
  failures++;
686
- console.error(chalk.yellow(`Kept the worktree ${candidate.path}; see the lines above for why.`));
687
- recordGcResult("worktree", "failed", candidate.path, { detail: error ?? "stim worktree remove did not remove it; see the details" });
745
+ output.error(chalk.yellow(`Kept the worktree ${candidate.path}; see the lines above for why.`));
746
+ output.record("worktree", "failed", candidate.path, { detail: error ?? "stim worktree remove did not remove it; see the details" });
688
747
  }
689
748
  }
690
749
  return failures;
@@ -874,9 +933,11 @@ async function maintenancePinnedAsync(root, operation) {
874
933
  try {
875
934
  return maintenanceKeepSetting((await resolveProjectSettingsAsync(root, {
876
935
  env: operation.env,
877
- signal: operation.signal
936
+ signal: operation.signal,
937
+ declareFor: operation
878
938
  })).settings);
879
- } catch {
939
+ } catch (error) {
940
+ if (error instanceof WorkspaceProcessCleanupError || error instanceof Error && "reportCleanup" in error) throw error;
880
941
  operation.signal.throwIfAborted();
881
942
  return true;
882
943
  }
@@ -909,14 +970,19 @@ function cachedDeviceActivity(now) {
909
970
  return answer.devices;
910
971
  };
911
972
  }
912
- function cachedDeviceActivityAsync(now, env) {
973
+ function cachedDeviceActivityAsync(now, env, signal, operation) {
913
974
  const answers = /* @__PURE__ */ new Map();
914
975
  return (withSims) => {
915
976
  let answer = answers.get(withSims);
916
977
  if (!answer) {
917
978
  answer = (async () => {
918
- const sims = withSims ? await listAllIosSimsAsync({ timeoutMs: DEVICE_LIST_TIMEOUT_MS }) : [];
919
- return collectOwnedDeviceActivityAsync(loadConfig$1(), sims, now, env);
979
+ signal?.throwIfAborted();
980
+ const sims = withSims ? await listAllIosSimsAsync({
981
+ timeoutMs: DEVICE_LIST_TIMEOUT_MS,
982
+ signal,
983
+ operation
984
+ }) : [];
985
+ return collectOwnedDeviceActivityAsync(loadConfig$1(), sims, now, env, signal, operation);
920
986
  })();
921
987
  answers.set(withSims, answer);
922
988
  }
@@ -955,12 +1021,15 @@ function workspaceActiveReason(root, now, windowMs, devices = (withSims) => read
955
1021
  }
956
1022
  return step.value;
957
1023
  }
958
- async function workspaceActiveReasonAsync(root, now, windowMs, devices) {
1024
+ async function workspaceActiveReasonAsync(root, now, windowMs, devices, signal) {
959
1025
  const checks = workspaceActiveChecks(root, now, windowMs);
960
1026
  let step = checks.next();
961
1027
  while (!step.done) try {
1028
+ signal?.throwIfAborted();
962
1029
  step = checks.next(await devices(step.value));
963
1030
  } catch (error) {
1031
+ if (error instanceof WorkspaceProcessCleanupError || error instanceof Error && "reportCleanup" in error) throw error;
1032
+ signal?.throwIfAborted();
964
1033
  step = checks.throw(error);
965
1034
  }
966
1035
  return step.value;
@@ -1105,13 +1174,13 @@ function planDeviceDeletion(options) {
1105
1174
  while (!step.done) step = checks.next(workspaceActiveReason(step.value, options.now, options.windowMs, activity));
1106
1175
  return step.value;
1107
1176
  }
1108
- async function planDeviceDeletionAsync(options, signal) {
1109
- const activity = cachedDeviceActivityAsync(options.now, options.env ?? process.env);
1177
+ async function planDeviceDeletionAsync(options, signal, operation) {
1178
+ const activity = cachedDeviceActivityAsync(options.now, options.env ?? process.env, signal, operation);
1110
1179
  const checks = deviceDeletionChecks(options);
1111
1180
  let step = checks.next();
1112
1181
  while (!step.done) {
1113
1182
  signal.throwIfAborted();
1114
- step = checks.next(await workspaceActiveReasonAsync(step.value, options.now, options.windowMs, activity));
1183
+ step = checks.next(await workspaceActiveReasonAsync(step.value, options.now, options.windowMs, activity, signal));
1115
1184
  }
1116
1185
  return step.value;
1117
1186
  }
@@ -1151,11 +1220,14 @@ async function planSweep({ settings, sizes, protectedRoot, pinned, now, devices
1151
1220
  const skips = [];
1152
1221
  const blocked = [];
1153
1222
  const days = settings.olderThanDays;
1154
- for (const entry of collectWorkspaceOutputs({
1223
+ const outputs = await collectWorkspaceOutputs({
1155
1224
  olderThan: days,
1156
1225
  now,
1157
- measure: false
1158
- }).workspaces) {
1226
+ measure: false,
1227
+ signal: operation?.signal,
1228
+ operation
1229
+ });
1230
+ for (const entry of outputs.workspaces) {
1159
1231
  const root = entry.projectRoot === null ? null : canonicalPath(entry.projectRoot);
1160
1232
  if (root === null || !entry.willClear || root === protectedRoot) continue;
1161
1233
  if (pinned(root)) {
@@ -1187,7 +1259,10 @@ async function planSweep({ settings, sizes, protectedRoot, pinned, now, devices
1187
1259
  check: "sweep"
1188
1260
  });
1189
1261
  try {
1190
- const orphans = collectOrphanedWorkspaces(Object.keys(loadConfig$1()?.projects ?? {}), listMountedVolumes(), { measure: false });
1262
+ const orphans = await collectOrphanedWorkspaces(Object.keys(loadConfig$1()?.projects ?? {}), listMountedVolumes(), {
1263
+ measure: false,
1264
+ operation
1265
+ });
1191
1266
  for (const orphan of orphans.orphaned) actions.push({
1192
1267
  kind: "would-remove-orphan",
1193
1268
  target: orphan.dir,
@@ -1200,6 +1275,8 @@ async function planSweep({ settings, sizes, protectedRoot, pinned, now, devices
1200
1275
  reason: kept.reason
1201
1276
  });
1202
1277
  } catch (error) {
1278
+ if (error instanceof WorkspaceProcessCleanupError || error instanceof Error && "reportCleanup" in error) throw error;
1279
+ operation?.signal.throwIfAborted();
1203
1280
  blocked.push(`Could not look for orphaned workspace directories: ${error instanceof Error ? error.message : error}`);
1204
1281
  }
1205
1282
  if (devices && settings.deviceIdleDays > 0) {
@@ -1217,7 +1294,7 @@ async function planSweep({ settings, sizes, protectedRoot, pinned, now, devices
1217
1294
  },
1218
1295
  env: operation?.env
1219
1296
  };
1220
- const reaped = operation ? await planDeviceDeletionAsync(options, operation.signal) : planDeviceDeletion(options);
1297
+ const reaped = operation ? await planDeviceDeletionAsync(options, operation.signal, operation) : planDeviceDeletion(options);
1221
1298
  actions.push(...reaped.actions);
1222
1299
  skips.push(...reaped.skips);
1223
1300
  }
@@ -1285,7 +1362,7 @@ async function planWorktrees({ protectedRoot, pinned, now, level, graceMs, sizes
1285
1362
  extra: { check: "worktree" },
1286
1363
  env: operation?.env
1287
1364
  };
1288
- const devices = operation ? await planDeviceDeletionAsync(options, operation.signal) : planDeviceDeletion(options);
1365
+ const devices = operation ? await planDeviceDeletionAsync(options, operation.signal, operation) : planDeviceDeletion(options);
1289
1366
  actions.push(...devices.actions);
1290
1367
  skips.push(...devices.skips);
1291
1368
  }
@@ -1343,11 +1420,15 @@ function artifactWorktree(root, entries, source = sourceCheckoutOf(entries)) {
1343
1420
  keys
1344
1421
  };
1345
1422
  }
1346
- async function artifactWorktreesAsync(signal) {
1423
+ async function artifactWorktreesAsync(signal, operation) {
1347
1424
  const found = /* @__PURE__ */ new Map();
1348
- for (const root of await candidateRootsAsync()) {
1425
+ for (const root of await candidateRootsAsync(operation)) {
1349
1426
  signal.throwIfAborted();
1350
- const { entries, ...source } = await resolveSourceCheckoutAsync(root);
1427
+ const { entries, ...source } = await resolveSourceCheckoutAsync(root, {
1428
+ env: operation?.env,
1429
+ signal,
1430
+ declareFor: operation
1431
+ });
1351
1432
  const entry = artifactWorktree(root, entries, source);
1352
1433
  if (entry) found.set(entry.path, entry);
1353
1434
  }
@@ -1440,10 +1521,10 @@ function artifactGuard(candidate, kind, pinned, locked) {
1440
1521
  if (inUse.length) return `in use: ${inUse.join("; ")}`;
1441
1522
  return null;
1442
1523
  }
1443
- async function collectArtifactsAsync(kind, options, signal) {
1524
+ async function collectArtifactsAsync(kind, options, signal, operation) {
1444
1525
  const candidates = [];
1445
1526
  const skips = [];
1446
- for (const worktree of await artifactWorktreesAsync(signal)) {
1527
+ for (const worktree of await artifactWorktreesAsync(signal, operation)) {
1447
1528
  if (kind === "dependencies" && !worktree.linked) continue;
1448
1529
  const root = canonicalPath(worktree.path);
1449
1530
  const protectedHere = options.protectedRoot === root || options.protectedRoot.startsWith(`${root}${sep}`);
@@ -1479,11 +1560,17 @@ async function collectArtifactsAsync(kind, options, signal) {
1479
1560
  "ls-files",
1480
1561
  "--",
1481
1562
  relative(worktree.path, path)
1482
- ]);
1563
+ ], {
1564
+ ...operation ? childEnvironment(operation.env) : {},
1565
+ cwd: operation?.cwd,
1566
+ signal,
1567
+ declareFor: operation,
1568
+ killSignal: "SIGKILL"
1569
+ });
1483
1570
  const untracked = tracked !== null && !tracked.trim();
1484
1571
  reason = keptReason(candidate, kind, options, {
1485
1572
  tracked,
1486
- activity: untracked ? await worktreeActivityOfAsync(worktree.path, worktree.keys) : null,
1573
+ activity: untracked ? await worktreeActivityOfAsync(worktree.path, worktree.keys, operation) : null,
1487
1574
  written: untracked ? await newestWriteAsync(path, SCAN_DEPTH[kind], signal) : null
1488
1575
  });
1489
1576
  }
@@ -1691,17 +1778,18 @@ function inside(parent, path) {
1691
1778
  return rel === "" || rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute(rel);
1692
1779
  }
1693
1780
  /** Basenames of running executables, or null when the process list cannot be read. */
1694
- async function runningExecutables() {
1695
- try {
1696
- const out = await getExecutor().runFileAsync("ps", [
1697
- "-axww",
1698
- "-o",
1699
- "comm="
1700
- ], { timeoutMs: 1e4 });
1701
- return new Set(out.split("\n").map((line) => line.trim()).filter(Boolean).map((line) => basename(line)));
1702
- } catch {
1703
- return null;
1704
- }
1781
+ async function runningExecutables(operation, signal) {
1782
+ const out = await runOperationFileQuiet(operation, getExecutor(), "ps", [
1783
+ "-axww",
1784
+ "-o",
1785
+ "comm="
1786
+ ], {
1787
+ timeoutMs: 1e4,
1788
+ cwd: operation?.cwd,
1789
+ signal
1790
+ });
1791
+ if (out === null) return null;
1792
+ return new Set(out.split("\n").map((line) => line.trim()).filter(Boolean).map((line) => basename(line)));
1705
1793
  }
1706
1794
  function stimKept(identity, scan, projects) {
1707
1795
  const registered = projects.find((root) => inside(identity, root) || inside(root, identity));
@@ -1728,7 +1816,7 @@ function temporaryRoot(env) {
1728
1816
  * 24 hours. Null with STIM_HOME set, because TMPDIR is shared by every Stim home, and for the real TMPDIR a test
1729
1817
  * process recorded in STIM_TEST_GUARD_REAL_TMPDIR.
1730
1818
  */
1731
- async function collectTempLeftovers({ now = Date.now(), env = process.env, signal } = {}) {
1819
+ async function collectTempLeftovers({ now = Date.now(), env = process.env, signal, operation } = {}) {
1732
1820
  if (deviceSweepIsScoped(env)) return null;
1733
1821
  let root;
1734
1822
  let names;
@@ -1746,7 +1834,7 @@ async function collectTempLeftovers({ now = Date.now(), env = process.env, signa
1746
1834
  let projects = registeredProjects();
1747
1835
  let executables;
1748
1836
  const traceBusy = async () => {
1749
- if (executables === void 0) executables = await runningExecutables();
1837
+ if (executables === void 0) executables = await runningExecutables(operation, signal);
1750
1838
  const listed = executables;
1751
1839
  if (listed === null) return "the running processes could not be listed";
1752
1840
  const running = [...TRACE_PROCESSES].filter((name) => listed.has(name));
@@ -1775,7 +1863,11 @@ async function collectTempLeftovers({ now = Date.now(), env = process.env, signa
1775
1863
  const kind = kindOf(name, stat, jestName);
1776
1864
  if (!kind) continue;
1777
1865
  if (kind === "jest") {
1778
- const bytes = await directorySizeAsync(path, { timeoutMs: 1e4 });
1866
+ const bytes = await directorySizeAsync(path, {
1867
+ timeoutMs: 1e4,
1868
+ signal,
1869
+ operation
1870
+ });
1779
1871
  entries.push({
1780
1872
  kind,
1781
1873
  path,
@@ -1845,7 +1937,7 @@ function tempLeftoverSection(report) {
1845
1937
  } : null;
1846
1938
  }
1847
1939
  /** Deletes what a fresh `collectTempLeftovers` marks for deletion. Returns the bytes freed and the failures. */
1848
- async function deleteTempLeftovers(options = {}) {
1940
+ async function deleteTempLeftovers(options = {}, output = GC_OUTPUT) {
1849
1941
  const report = await collectTempLeftovers(options);
1850
1942
  const done = {
1851
1943
  count: 0,
@@ -1861,7 +1953,7 @@ async function deleteTempLeftovers(options = {}) {
1861
1953
  const staged = entry.kind === "xctrace" || name.startsWith(STAGING_PREFIX) ? entry.path : join(report.root, `${STAGING_PREFIX}${name}`);
1862
1954
  const before = staged === entry.path ? null : await report.recheck?.(entry.path, entry.path, entry.kind) ?? null;
1863
1955
  if (before) {
1864
- recordGcResult("tempLeftover", "kept", entry.path, {
1956
+ output.record("tempLeftover", "kept", entry.path, {
1865
1957
  bytes: entry.bytes,
1866
1958
  detail: before
1867
1959
  });
@@ -1873,6 +1965,7 @@ async function deleteTempLeftovers(options = {}) {
1873
1965
  const original = name.startsWith(STAGING_PREFIX) ? join(report.root, name.slice(13)) : entry.path;
1874
1966
  kept = await report.recheck?.(staged, original, entry.kind) ?? null;
1875
1967
  } catch (error) {
1968
+ if (error instanceof WorkspaceProcessCleanupError || error instanceof Error && "reportCleanup" in error) throw error;
1876
1969
  kept = `it could not be checked again: ${errorCode(error)}`;
1877
1970
  if (options.signal?.aborted) {
1878
1971
  if (staged !== entry.path) await rename(staged, entry.path).catch(() => {});
@@ -1885,32 +1978,33 @@ async function deleteTempLeftovers(options = {}) {
1885
1978
  if (staged !== entry.path) await rename(staged, entry.path);
1886
1979
  } catch (error) {
1887
1980
  detail = `${kept}; it stays at ${staged} because it could not be renamed back: ${errorCode(error)}`;
1888
- console.log(chalk.yellow(`Kept ${entry.path} at ${staged}: ${detail}`));
1981
+ output.log(chalk.yellow(`Kept ${entry.path} at ${staged}: ${detail}`));
1889
1982
  }
1890
- recordGcResult("tempLeftover", "kept", entry.path, {
1983
+ output.record("tempLeftover", "kept", entry.path, {
1891
1984
  bytes: entry.bytes,
1892
1985
  detail
1893
1986
  });
1894
1987
  continue;
1895
1988
  }
1896
- removeTemporaryEntry(staged);
1989
+ await removeTemporaryEntryAsync(staged, options.signal);
1897
1990
  done.count++;
1898
1991
  done.bytes += entry.bytes;
1899
1992
  } catch (error) {
1993
+ if (error instanceof WorkspaceProcessCleanupError || error instanceof Error && "reportCleanup" in error) throw error;
1900
1994
  if (options.signal?.aborted) throw error;
1901
1995
  if (errorCode(error) === "ENOENT") continue;
1902
1996
  done.failures++;
1903
1997
  const detail = error instanceof Error ? error.message : String(error);
1904
- console.log(chalk.red(`Could not delete ${entry.path}: ${detail}`));
1905
- recordGcResult("tempLeftover", "failed", entry.path, {
1998
+ output.log(chalk.red(`Could not delete ${entry.path}: ${detail}`));
1999
+ output.record("tempLeftover", "failed", entry.path, {
1906
2000
  bytes: entry.bytes,
1907
2001
  detail
1908
2002
  });
1909
2003
  }
1910
2004
  }
1911
2005
  if (done.count) {
1912
- console.log(chalk.green(`Deleted ${plural(done.count, "TMPDIR leftover")} (${formatBytes(done.bytes)}) in ${report.root}`));
1913
- recordGcResult("tempLeftover", "done", report.root, {
2006
+ output.log(chalk.green(`Deleted ${plural(done.count, "TMPDIR leftover")} (${formatBytes(done.bytes)}) in ${report.root}`));
2007
+ output.record("tempLeftover", "done", report.root, {
1914
2008
  bytes: done.bytes,
1915
2009
  detail: `${plural(done.count, "entry", "entries")} deleted`
1916
2010
  });
@@ -1924,11 +2018,13 @@ async function planLevel2({ settings, sizes, protectedRoot, pinned, now, operati
1924
2018
  const actions = [];
1925
2019
  const skips = [];
1926
2020
  const reason = `disk pressure level 2, idle for ${settings.pressureProtectMinutes} minutes`;
1927
- const outputs = collectWorkspaceOutputs({
2021
+ const outputs = (await collectWorkspaceOutputs({
1928
2022
  olderThan: null,
1929
2023
  now,
1930
- measure: false
1931
- }).workspaces.flatMap((entry) => {
2024
+ measure: false,
2025
+ signal: operation?.signal,
2026
+ operation
2027
+ })).workspaces.flatMap((entry) => {
1932
2028
  const root = entry.projectRoot === null ? null : canonicalPath(entry.projectRoot);
1933
2029
  if (root === null || !entry.willClear || root === protectedRoot || pinned(root)) return [];
1934
2030
  const used = workspaceLastUsed(root);
@@ -1958,7 +2054,8 @@ async function planLevel2({ settings, sizes, protectedRoot, pinned, now, operati
1958
2054
  const leftovers = await collectTempLeftovers({
1959
2055
  now,
1960
2056
  env: operation?.env,
1961
- signal: operation?.signal
2057
+ signal: operation?.signal,
2058
+ operation
1962
2059
  });
1963
2060
  const deletable = leftovers?.entries.filter((entry) => entry.willDelete) ?? [];
1964
2061
  if (leftovers && deletable.length) actions.push({
@@ -1974,7 +2071,7 @@ async function planLevel2({ settings, sizes, protectedRoot, pinned, now, operati
1974
2071
  protectedRoot,
1975
2072
  pinned
1976
2073
  };
1977
- const build = operation ? await collectArtifactsAsync("build", options, operation.signal) : collectArtifacts("build", options);
2074
+ const build = operation ? await collectArtifactsAsync("build", options, operation.signal, operation) : collectArtifacts("build", options);
1978
2075
  for (const candidate of build.candidates) actions.push({
1979
2076
  kind: "would-clear-build-dirs",
1980
2077
  target: candidate.path,
@@ -1994,7 +2091,7 @@ async function planLevel2({ settings, sizes, protectedRoot, pinned, now, operati
1994
2091
  extra: { level: 2 },
1995
2092
  env: operation?.env
1996
2093
  };
1997
- const devices = operation ? await planDeviceDeletionAsync(deviceOptions, operation.signal) : planDeviceDeletion(deviceOptions);
2094
+ const devices = operation ? await planDeviceDeletionAsync(deviceOptions, operation.signal, operation) : planDeviceDeletion(deviceOptions);
1998
2095
  actions.push(...devices.actions);
1999
2096
  skips.push(...devices.skips);
2000
2097
  return {
@@ -2049,7 +2146,7 @@ async function planLevel3({ settings, sizes, protectedRoot, pinned, now, refresh
2049
2146
  }) : null;
2050
2147
  actions.push(...worktrees?.actions ?? []);
2051
2148
  skips.push(...worktrees?.skips ?? []);
2052
- const activity = operation ? cachedDeviceActivityAsync(now, operation.env) : null;
2149
+ const activity = operation ? cachedDeviceActivityAsync(now, operation.env, operation.signal, operation) : null;
2053
2150
  for (const root of Object.keys(loadConfig$1()?.projects ?? {}).map(canonicalPath)) {
2054
2151
  if (root === protectedRoot || pinned(root)) continue;
2055
2152
  if (workspaceInUse(root, {
@@ -2057,7 +2154,7 @@ async function planLevel3({ settings, sizes, protectedRoot, pinned, now, refresh
2057
2154
  hosted: false,
2058
2155
  nativeRun: false
2059
2156
  }).length === 0) continue;
2060
- const active = activity ? await workspaceActiveReasonAsync(root, now, windowMs, activity) : workspaceActiveReason(root, now, windowMs);
2157
+ const active = activity ? await workspaceActiveReasonAsync(root, now, windowMs, activity, operation?.signal) : workspaceActiveReason(root, now, windowMs);
2061
2158
  if (active) skips.push({
2062
2159
  target: root,
2063
2160
  reason: active,
@@ -2078,7 +2175,7 @@ async function planLevel3({ settings, sizes, protectedRoot, pinned, now, refresh
2078
2175
  protectedRoot,
2079
2176
  pinned
2080
2177
  };
2081
- const dependencies = operation ? await collectArtifactsAsync("dependencies", artifactOptions, operation.signal) : collectArtifacts("dependencies", artifactOptions);
2178
+ const dependencies = operation ? await collectArtifactsAsync("dependencies", artifactOptions, operation.signal, operation) : collectArtifacts("dependencies", artifactOptions);
2082
2179
  for (const candidate of dependencies.candidates) actions.push({
2083
2180
  kind: "would-delete-dependencies",
2084
2181
  target: candidate.path,
@@ -2163,13 +2260,17 @@ async function plannedMaintenance(pressure, sizes, settings, { devices = true, s
2163
2260
  dryRun: true,
2164
2261
  budget,
2165
2262
  env,
2166
- cwd: operation?.cwd
2263
+ cwd: operation?.cwd,
2264
+ signal: operation?.signal,
2265
+ operation
2167
2266
  }, { volumes: () => pressure.disk }) : null;
2168
2267
  if (outcome && outcome.status !== "ok") throw new Error(outcome.refusal.message);
2169
- const outputs = collectWorkspaceOutputs({
2268
+ const outputs = await collectWorkspaceOutputs({
2170
2269
  olderThan: null,
2171
2270
  now: Date.now(),
2172
- measure: false
2271
+ measure: false,
2272
+ signal: operation?.signal,
2273
+ operation
2173
2274
  });
2174
2275
  const currentSizes = sizes.map((size) => {
2175
2276
  if (size.category === "compilation-cache") return {
@@ -2182,14 +2283,18 @@ async function plannedMaintenance(pressure, sizes, settings, { devices = true, s
2182
2283
  });
2183
2284
  const pins = /* @__PURE__ */ new Map();
2184
2285
  if (operation) {
2185
- const candidates = await candidateRootsAsync();
2286
+ const candidates = await candidateRootsAsync(operation);
2186
2287
  const roots = /* @__PURE__ */ new Set([
2187
2288
  ...Object.keys(loadConfig()?.projects ?? {}),
2188
2289
  ...candidates,
2189
2290
  ...sizes.flatMap((size) => size.workspace ? [size.workspace] : []),
2190
2291
  ...outputs.workspaces.flatMap((entry) => entry.projectRoot ? [entry.projectRoot] : [])
2191
2292
  ]);
2192
- for (const path of candidates) for (const entry of await listWorktreesAsync(path)) roots.add(entry.path);
2293
+ for (const path of candidates) for (const entry of await listWorktreesAsync(path, {
2294
+ env: operation.env,
2295
+ signal: operation.signal,
2296
+ declareFor: operation
2297
+ })) roots.add(entry.path);
2193
2298
  for (const path of Array.from(roots, canonicalPath)) roots.add(path);
2194
2299
  for (const path of roots) pins.set(path, await maintenancePinnedAsync(path, operation));
2195
2300
  }
@@ -2294,6 +2399,8 @@ async function previewMaintenance({ devices = true, refresh = true, operation }
2294
2399
  note: settings.invalid ?? (state === null ? "no pass has run yet; sizes have not been measured" : null)
2295
2400
  };
2296
2401
  } catch (error) {
2402
+ if (error instanceof WorkspaceProcessCleanupError || error instanceof Error && "reportCleanup" in error) throw error;
2403
+ operation?.signal.throwIfAborted();
2297
2404
  return {
2298
2405
  mode: settings?.mode ?? "off",
2299
2406
  ...settings?.invalid ? { invalid: settings.invalid } : {},
@@ -2307,8 +2414,8 @@ async function previewMaintenance({ devices = true, refresh = true, operation }
2307
2414
  }
2308
2415
  //#endregion
2309
2416
  //#region src/maintenance/act.ts
2310
- function freedSince(beforeMb) {
2311
- const freed = lowestFree(measureDisk()) - beforeMb;
2417
+ function freedSince(beforeMb, disk = measureDisk()) {
2418
+ const freed = lowestFree(disk) - beforeMb;
2312
2419
  return Number.isFinite(freed) && freed > 0 ? Math.round(freed * 1024 * 1024) : 0;
2313
2420
  }
2314
2421
  const kept = (detail) => ({
@@ -2470,8 +2577,9 @@ async function deleteParkedDevice(action) {
2470
2577
  const { results } = await quietly(async () => await deleteParkedSims(sims) + await deleteParkedAvds(avds));
2471
2578
  return outcomeOf(results, freedSince(before));
2472
2579
  }
2473
- async function deleteOwnedDevicesOf(root, { noneIsKept = false } = {}) {
2474
- if (deviceSweepIsScoped()) return null;
2580
+ async function deleteOwnedDevicesOf(root, { noneIsKept = false, operation, output = GC_OUTPUT } = {}) {
2581
+ operation?.signal.throwIfAborted();
2582
+ if (deviceSweepIsScoped(operation?.env ?? process.env)) return null;
2475
2583
  const config = loadConfig$1();
2476
2584
  const project = config?.projects?.[root];
2477
2585
  if (!config || !project) return null;
@@ -2479,9 +2587,20 @@ async function deleteOwnedDevicesOf(root, { noneIsKept = false } = {}) {
2479
2587
  let sims = [];
2480
2588
  let avds = [];
2481
2589
  try {
2482
- if (owns("ios")) sims = listAllIosSims({ timeoutMs: DEVICE_LIST_TIMEOUT_MS });
2483
- if (owns("android")) avds = listAvds();
2590
+ if (owns("ios")) sims = await listAllIosSimsAsync({
2591
+ timeoutMs: DEVICE_LIST_TIMEOUT_MS,
2592
+ operation,
2593
+ signal: operation?.signal
2594
+ });
2595
+ operation?.signal.throwIfAborted();
2596
+ if (owns("android")) avds = await listAvdsAsync({
2597
+ operation,
2598
+ signal: operation?.signal
2599
+ });
2600
+ operation?.signal.throwIfAborted();
2484
2601
  } catch (error) {
2602
+ if (error instanceof WorkspaceProcessCleanupError || error instanceof Error && "reportCleanup" in error) throw error;
2603
+ operation?.signal.throwIfAborted();
2485
2604
  return {
2486
2605
  status: "failed",
2487
2606
  bytes: 0,
@@ -2499,7 +2618,7 @@ async function deleteOwnedDevicesOf(root, { noneIsKept = false } = {}) {
2499
2618
  lastTouched: () => 0
2500
2619
  });
2501
2620
  if (noneIsKept && devices.length === 0) return kept("it owns no listed devices any more");
2502
- const failures = await deleteProjectDevices([], devices, []);
2621
+ const failures = await deleteProjectDevices([], devices, [], output, operation);
2503
2622
  return failures ? {
2504
2623
  status: "failed",
2505
2624
  bytes: 0,
@@ -2541,16 +2660,31 @@ async function stopWorkspace(action, context) {
2541
2660
  * Deletes the owned simulators and emulators of a workspace that stays idle for `windowMs`, under its native-run
2542
2661
  * claim, without stopping anything: a running dev server, lease, lock or driver keeps every device.
2543
2662
  */
2544
- async function deleteIdleWorkspaceDevices(root, { windowMs, protectedRoot, purpose }) {
2663
+ async function deleteIdleWorkspaceDevices(root, { windowMs, protectedRoot, purpose, operation, output }) {
2664
+ operation?.signal.throwIfAborted();
2545
2665
  if (root === protectedRoot) return kept("the command that started this pass runs in it");
2546
- if (maintenancePinned(root)) return kept("pinned by maintenance.keep");
2547
- if (deviceSweepIsScoped()) return kept("STIM_HOME scopes this config, and devices are machine-wide");
2548
- const active = workspaceActiveReason(root, Date.now(), windowMs);
2666
+ if (operation ? await maintenancePinnedAsync(root, operation) : maintenancePinned(root)) return kept("pinned by maintenance.keep");
2667
+ if (deviceSweepIsScoped(operation?.env ?? process.env)) return kept("STIM_HOME scopes this config, and devices are machine-wide");
2668
+ const activeReason = async () => {
2669
+ const now = Date.now();
2670
+ if (!operation) return workspaceActiveReason(root, now, windowMs);
2671
+ const reason = await workspaceActiveReasonAsync(root, now, windowMs, cachedDeviceActivityAsync(now, operation.env, operation.signal, operation), operation.signal);
2672
+ operation.signal.throwIfAborted();
2673
+ return reason;
2674
+ };
2675
+ const active = await activeReason();
2549
2676
  if (active) return kept(active);
2550
- const before = lowestFree(measureDisk());
2551
- const run = await withIdleWorkspace(root, async () => workspaceActiveReason(root, Date.now(), windowMs) ? kept("it became active") : deleteOwnedDevicesOf(root, { noneIsKept: true }), { purpose });
2677
+ const before = lowestFree(operation ? await measureDiskAsync(operation) : measureDisk());
2678
+ const run = await withIdleWorkspace(root, async () => await activeReason() ? kept("it became active") : deleteOwnedDevicesOf(root, {
2679
+ noneIsKept: true,
2680
+ operation,
2681
+ output
2682
+ }), {
2683
+ purpose,
2684
+ operation
2685
+ });
2552
2686
  if (!run.ran) return kept(`in use: ${run.reasons.join("; ")}`);
2553
- const bytes = freedSince(before);
2687
+ const bytes = operation ? freedSince(before, await measureDiskAsync(operation)) : freedSince(before);
2554
2688
  return run.value ? {
2555
2689
  ...run.value,
2556
2690
  bytes