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,7 +1,7 @@
1
- import { f as ownership_claim_exports, t as getExecutor } from "./exec-_UbtCQcG.mjs";
2
- import { Gt as childEnvironment, en as throwIfCancelled, qt as killOnAbort } from "./settings-CsJDEXgK.mjs";
3
- import { Zn as remoteCliInvocation, nr as easCliUpgradeRemedy, qn as resolveEasCliBin, tr as easCliSupport } from "./budget-Bs6SxxzO.mjs";
4
- import { M as acquireBuildLock, P as releaseBuildLock } from "./activity-BzVfHSrY.mjs";
1
+ import { t as getExecutor, x as ownership_claim_exports } from "./exec-DEoAhcze.mjs";
2
+ import { a as killOnAbort, f as throwIfCancelled, r as childEnvironment } from "./operation-exec-BW0em61G.mjs";
3
+ import { Zn as remoteCliInvocation, nr as easCliUpgradeRemedy, qn as resolveEasCliBin, tr as easCliSupport } from "./budget-Cvwn54qV.mjs";
4
+ import { M as acquireBuildLock, P as releaseBuildLock } from "./activity-CMLmzp7o.mjs";
5
5
  import { createHash } from "node:crypto";
6
6
  import { existsSync, realpathSync, statSync } from "node:fs";
7
7
  //#region src/engine/eas-build.ts
@@ -1,14 +1,15 @@
1
- import { t as getExecutor } from "./exec-_UbtCQcG.mjs";
2
- import { _ as loadConfig$1, p as getProject } from "./config-CYcBF478.mjs";
3
- import { Gt as childEnvironment, ct as detectProviders, en as throwIfCancelled, lt as planMetroReach } from "./settings-CsJDEXgK.mjs";
4
- import { M as readMetroTunnel, P as readRemoteSession } from "./project-BZNjmW_8.mjs";
5
- import { $n as MIN_EAS_CLI_DEVICE_VERSION, Hn as isEasAuthFailureText, Ht as readInstalledEasCliVersionAsync, Qn as remoteCommand, er as MIN_EAS_CLI_SIMULATOR_VERSION, qn as resolveEasCliBin, s as peekBudget, tr as easCliSupport } from "./budget-Bs6SxxzO.mjs";
6
- import { P as readHostMemoryPressure } from "./ios-CpByZy12.mjs";
7
- import { d as hostingMachines, o as resolveBuildMachine, r as namedBuildMachine } from "./selection-CSl9i5qU.mjs";
8
- import { c as call, l as connectHost, p as requireHostedAppMode } from "./hosted-client-DB_gBPfY.mjs";
9
- import { r as readHostedNative, s as writeHostedNative } from "./ios-state-DvFX-K_l.mjs";
10
- import { n as prepareHostedNative } from "./hosted-native-Ksyjgzq-.mjs";
11
- import { c as peekDeviceSlots } from "./device-capacity-CMlqEcvw.mjs";
1
+ import { t as getExecutor } from "./exec-DEoAhcze.mjs";
2
+ import { _ as loadConfig$1, p as getProject } from "./config-DlrsduwG.mjs";
3
+ import { f as throwIfCancelled, r as childEnvironment } from "./operation-exec-BW0em61G.mjs";
4
+ import { ct as detectProviders, lt as planMetroReach } from "./settings-C7O8A6ZI.mjs";
5
+ import { M as readMetroTunnel, P as readRemoteSession } from "./project-wM_XFTvE.mjs";
6
+ import { $n as MIN_EAS_CLI_DEVICE_VERSION, Hn as isEasAuthFailureText, Ht as readInstalledEasCliVersionAsync, Qn as remoteCommand, er as MIN_EAS_CLI_SIMULATOR_VERSION, qn as resolveEasCliBin, s as peekBudget, tr as easCliSupport } from "./budget-Cvwn54qV.mjs";
7
+ import { F as readHostMemoryPressureAsync } from "./ios-hcBpnEGk.mjs";
8
+ import { d as hostingMachines, o as resolveBuildMachine, r as namedBuildMachine } from "./selection-DSMbWEX2.mjs";
9
+ import { c as call, l as connectHost, p as requireHostedAppMode } from "./hosted-client-DZAPrcro.mjs";
10
+ import { r as readHostedNative, s as writeHostedNative } from "./ios-state-Dtw5K2aI.mjs";
11
+ import { n as prepareHostedNative } from "./hosted-native-BdzE668-.mjs";
12
+ import { c as peekDeviceSlots } from "./device-capacity-DK5_EwRS.mjs";
12
13
  import { ambientEnv } from "@stim-cli/core";
13
14
  import { automaticMachineEnabled, isJsonObject, machineCapacity, parseHostedChoice, parseHostedNativeOffer } from "@stim-cli/core/state";
14
15
  //#region src/device-host/placement.ts
@@ -122,16 +123,20 @@ function decideDevicePlacement(inputs) {
122
123
  }
123
124
  //#endregion
124
125
  //#region src/device-host/auto-placement.ts
125
- async function probeHost(machine, platform, selectors, appMode) {
126
+ async function probeHost(machine, platform, selectors, appMode, signal) {
126
127
  let host;
127
128
  const deadline = Date.now() + 3e3;
129
+ const cancel = () => host?.connection.close();
128
130
  try {
129
- host = await connectHost(machine, 3e3, true);
131
+ host = await connectHost(machine, 3e3, true, signal);
132
+ signal?.addEventListener("abort", cancel, { once: true });
133
+ signal?.throwIfAborted();
130
134
  requireHostedAppMode(host, platform, appMode);
131
135
  const offer = await call(host, "device-host.offer", {
132
136
  platform,
133
137
  ...selectors
134
138
  }, Math.max(1, deadline - Date.now()));
139
+ signal?.throwIfAborted();
135
140
  const resources = isJsonObject(offer.resources) ? offer.resources : {};
136
141
  const parsed = parseHostedNativeOffer({
137
142
  ...offer,
@@ -165,11 +170,13 @@ async function probeHost(machine, platform, selectors, appMode) {
165
170
  } } : {}
166
171
  };
167
172
  } catch (error) {
173
+ signal?.throwIfAborted();
168
174
  return { probe: {
169
175
  machine,
170
176
  failure: error instanceof Error ? error.message : String(error)
171
177
  } };
172
178
  } finally {
179
+ signal?.removeEventListener("abort", cancel);
173
180
  host?.connection.close();
174
181
  }
175
182
  }
@@ -212,11 +219,11 @@ function viaEas(code, reason, skipped) {
212
219
  skipped
213
220
  };
214
221
  }
215
- async function automaticDevicePlacement({ root, slot, platform, selectors, appMode, buildMachine, noWait, eas }, { machines = hostingMachines, read = readHostedNative, write = writeHostedNative, resume = prepareHostedNative, peek = () => peekDeviceSlots({
222
+ async function automaticDevicePlacement({ root, slot, platform, selectors, appMode, buildMachine, noWait, eas, signal }, { machines = () => hostingMachines(void 0, signal), read = readHostedNative, write = writeHostedNative, resume = prepareHostedNative, peek = () => peekDeviceSlots({
216
223
  platform,
217
224
  project: getProject(root),
218
225
  slot
219
- }), capacity = machineCapacity, memory = readHostMemoryPressure, probe = probeHost, budget = (projectRoot) => peekBudget(projectRoot, {}, ambientEnv()) } = {}) {
226
+ }), capacity = machineCapacity, memory = () => readHostMemoryPressureAsync(void 0, void 0, signal), probe = probeHost, budget = (projectRoot) => peekBudget(projectRoot, {}, ambientEnv(), signal) } = {}) {
220
227
  const recorded = read(root, platform, slot)[slot];
221
228
  if (recorded) {
222
229
  const target = await resume(recorded.machine, selectors, recorded, platform, true, appMode);
@@ -238,7 +245,8 @@ async function automaticDevicePlacement({ root, slot, platform, selectors, appMo
238
245
  ...local("sticky", decideDevicePlacement({ sticky: { local: true } }).reason),
239
246
  sticky: true
240
247
  };
241
- const configured = machines();
248
+ const configured = await machines();
249
+ signal?.throwIfAborted();
242
250
  if (configured === null) throw Object.assign(/* @__PURE__ */ new Error("remote.machines is invalid. Run stim guide settings and correct it."), { code: "STIM_HOSTING_REFUSED" });
243
251
  const config = loadConfig$1();
244
252
  const entries = configured.filter((machine) => automaticMachineEnabled("device", machine, config));
@@ -249,10 +257,11 @@ async function automaticDevicePlacement({ root, slot, platform, selectors, appMo
249
257
  localEnabled: automaticMachineEnabled("device", "local", config),
250
258
  loadPerCore: load.loadPerCore,
251
259
  maxLoadPerCore: load.maxLoadPerCore,
252
- memoryPressure: memory(),
260
+ memoryPressure: await memory(),
253
261
  devices,
254
262
  budgetRefusal: await budget(root)
255
263
  };
264
+ signal?.throwIfAborted();
256
265
  const inputs = (offers) => ({
257
266
  platform,
258
267
  here,
@@ -265,7 +274,7 @@ async function automaticDevicePlacement({ root, slot, platform, selectors, appMo
265
274
  failure: "not probed"
266
275
  }));
267
276
  const early = decideDevicePlacement(inputs(notProbed));
268
- const probes = early.kind === "local" && early.skipped.length === 0 ? [] : await Promise.all(entries.map((machine) => probe(machine, platform, selectors, appMode)));
277
+ const probes = early.kind === "local" && early.skipped.length === 0 ? [] : await Promise.all(entries.map((machine) => probe(machine, platform, selectors, appMode, signal)));
269
278
  const offers = probes.length ? probes.map((each) => each.probe) : notProbed;
270
279
  let decision = decideDevicePlacement(inputs(offers));
271
280
  if ((decision.kind === "local" || decision.kind === "refused") && decision.atCapacity && eas) decision = decideDevicePlacement({
@@ -1,9 +1,9 @@
1
- import { m as reportSignal, t as getExecutor } from "./exec-_UbtCQcG.mjs";
1
+ import { _ as reportSignal, t as getExecutor } from "./exec-DEoAhcze.mjs";
2
2
  import { l as workspaceAgentDeviceDir, v as withDirLock$1, y as withDirLockAsync$1 } from "./paths-DQtTgsBf.mjs";
3
- import { H as deviceSlotKey, K as projectDeviceSlots } from "./config-CYcBF478.mjs";
4
- import { a as formatElapsed, o as formatLongDuration, r as clockTime, u as plural } from "./command-output-B1UrjZHL.mjs";
5
- import { n as isInsideProject } from "./metro-DEXpIhHC.mjs";
6
- import { r as cdpEndpoint } from "./state-C4ZtUisW.mjs";
3
+ import { G as projectDeviceSlots, V as deviceSlotKey } from "./config-DlrsduwG.mjs";
4
+ import { a as formatElapsed, o as formatLongDuration, r as clockTime, u as plural } from "./command-output-gwpm28fu.mjs";
5
+ import { n as isInsideProject } from "./metro-DiXtA3Ty.mjs";
6
+ import { r as cdpEndpoint } from "./state--smjFSTL.mjs";
7
7
  import { accessSync, constants, existsSync, mkdirSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
8
8
  import { delimiter, dirname, join, resolve } from "node:path";
9
9
  import { workspaceId } from "@stim-cli/core";
@@ -713,10 +713,11 @@ function removeEasSessionClaim(sessionId, root = easMachineStateRoot(), when = (
713
713
  });
714
714
  }
715
715
  /** Removes a matching EAS session claim after asynchronously acquiring the ownership ledger lock. */
716
- async function removeEasSessionClaimAsync(sessionId, root = easMachineStateRoot(), when = () => true) {
716
+ async function removeEasSessionClaimAsync(sessionId, root = easMachineStateRoot(), when = () => true, signal) {
717
+ signal?.throwIfAborted();
717
718
  if (!existsSync(easSessionLedgerFile(root))) return false;
718
719
  assertEasMachineRootWritable(root);
719
- return withDirLockAsync$1(easSessionLedgerLock(root), () => removeEasSessionClaim(sessionId, root, when));
720
+ return withDirLockAsync$1(easSessionLedgerLock(root), () => removeEasSessionClaim(sessionId, root, when), { signal });
720
721
  }
721
722
  //#endregion
722
723
  export { remoteDeviceState as A, environmentState as C, parseDfFree as D, metroReverseTargets as E, tightVolumes as M, unprovisionedWorktrees as N, poolLine as O, withWebFacts as P, diskLine as S, gitSummaryText as T, addReverseMissingIssue as _, removeEasSessionClaim as a, deviceLeaseLines as b, inspectStimVersions as c, parseWatchRoots as d, parseWatchmanClients as f, activityLabel as g, RECENT_LAUNCH_MS as h, recordEasSessionClaim as i, statusActivity as j, remoteDeviceLine as k, parseSubscriberNames as l, runWatchman as m, easMachineStateRoot$1 as n, removeEasSessionClaimAsync as o, parseWatchmanPid as p, readEasSessionLedger$1 as r, compareStimVersions as s, assertEasMachineRootWritable as t, parseTriggerCount as u, applyMachineMemory as v, formatSpace as w, deviceLeaseStates as x, capacity as y };
@@ -1,14 +1,14 @@
1
- import { t as getExecutor } from "./exec-_UbtCQcG.mjs";
1
+ import { t as getExecutor } from "./exec-DEoAhcze.mjs";
2
2
  import { v as withDirLock$1 } from "./paths-DQtTgsBf.mjs";
3
- import { H as deviceSlotKey, U as deviceSlotPlatforms, p as getProject } from "./config-CYcBF478.mjs";
4
- import { v as launchErrorPreview } from "./command-output-B1UrjZHL.mjs";
5
- import { r as readWorkspaceState$1 } from "./workspace-state-Cjuigeeu.mjs";
6
- import { I as readWorkspaceLaunches } from "./project-BZNjmW_8.mjs";
7
- import { f as resolveProjectMetro } from "./metro-DEXpIhHC.mjs";
8
- import { S as parseLease, v as deviceLeasePath, y as fileLeaseIo } from "./activity-BzVfHSrY.mjs";
9
- import { o as androidHome, u as androidToolCwd } from "./android-DUYkNF4u.mjs";
10
- import { r as deviceConsoleLevel } from "./ios-device-CXGP1bsD.mjs";
11
- import { n as parseLogcatLine } from "./android-BEJ8Jpl7.mjs";
3
+ import { H as deviceSlotPlatforms, V as deviceSlotKey, p as getProject } from "./config-DlrsduwG.mjs";
4
+ import { v as launchErrorPreview } from "./command-output-gwpm28fu.mjs";
5
+ import { r as readWorkspaceState$1 } from "./workspace-state-U08dEzhG.mjs";
6
+ import { I as readWorkspaceLaunches } from "./project-wM_XFTvE.mjs";
7
+ import { d as resolveProjectMetro } from "./metro-DiXtA3Ty.mjs";
8
+ import { S as parseLease, v as deviceLeasePath, y as fileLeaseIo } from "./activity-CMLmzp7o.mjs";
9
+ import { o as androidHome, u as androidToolCwd } from "./android-C7sVmyI9.mjs";
10
+ import { r as deviceConsoleLevel } from "./ios-device-BXZ0c7Kb.mjs";
11
+ import { n as parseLogcatLine } from "./android-DR5lZ2Az.mjs";
12
12
  import { createHash, randomUUID } from "node:crypto";
13
13
  import { existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
14
14
  import { homedir } from "node:os";
@@ -1,7 +1,8 @@
1
1
  import { createRequire } from "node:module";
2
+ import { join } from "node:path";
2
3
  import { ambientContext, ambientEnv } from "@stim-cli/core";
3
4
  import { createDebugLog, runId } from "@stim-cli/core/state";
4
- import { basename, delimiter, resolve } from "path";
5
+ import { basename as basename$1, delimiter as delimiter$1, resolve as resolve$1 } from "path";
5
6
  import { execSync } from "child_process";
6
7
  import spawn from "cross-spawn";
7
8
  import which from "which";
@@ -35,6 +36,11 @@ var __copyProps = (to, from, except, desc) => {
35
36
  var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
36
37
  var __require = /* #__PURE__ */ (() => createRequire(import.meta.url))();
37
38
  //#endregion
39
+ //#region src/ownership-claim.ts
40
+ var ownership_claim_exports = /* @__PURE__ */ __exportAll({});
41
+ import * as import__stim_cli_core_ownership_claim from "@stim-cli/core/ownership-claim";
42
+ __reExport(ownership_claim_exports, import__stim_cli_core_ownership_claim);
43
+ //#endregion
38
44
  //#region src/debug-log.ts
39
45
  const debugLog = createDebugLog("cli", { base: () => ({ runId: runId() }) });
40
46
  //#endregion
@@ -43,21 +49,22 @@ const reports = /* @__PURE__ */ new WeakMap();
43
49
  function reportSignal(context) {
44
50
  return context && reports.has(context) ? context.signal : void 0;
45
51
  }
46
- function trackReportResource(context, closed, cancel, signal = context?.signal) {
52
+ function trackReportResource(context, closed, cancel, signal = context?.signal, cleanupFailure) {
47
53
  const state = context && reports.get(context);
48
54
  if (!context || !state || !signal) return;
49
55
  const { pending } = state;
50
56
  const abort = () => {
51
57
  const cancelled = Promise.resolve().then(cancel);
52
- pending.add(cancelled);
58
+ pending.set(cancelled, cleanupFailure);
53
59
  cancelled.then(() => pending.delete(cancelled), (error) => {
54
60
  state.failure ??= error;
55
61
  pending.delete(cancelled);
56
62
  pending.delete(closed);
57
63
  signal.removeEventListener("abort", abort);
64
+ state.abort(error);
58
65
  });
59
66
  };
60
- pending.add(closed);
67
+ pending.set(closed, cleanupFailure);
61
68
  const settled = () => {
62
69
  signal.removeEventListener("abort", abort);
63
70
  pending.delete(closed);
@@ -65,11 +72,12 @@ function trackReportResource(context, closed, cancel, signal = context?.signal)
65
72
  closed.then(settled, (error) => {
66
73
  state.failure ??= error;
67
74
  settled();
75
+ state.abort(error);
68
76
  });
69
77
  signal.addEventListener("abort", abort, { once: true });
70
78
  if (signal.aborted) abort();
71
79
  }
72
- async function runReport(context, work) {
80
+ async function runReport(context, work, timeoutMs = 6e4) {
73
81
  const controller = new AbortController();
74
82
  const signal = AbortSignal.any([context.signal, controller.signal]);
75
83
  const operation = {
@@ -79,14 +87,17 @@ async function runReport(context, work) {
79
87
  if (!signal.aborted) context.output(stream, text);
80
88
  })
81
89
  };
82
- const pending = /* @__PURE__ */ new Set();
83
- const state = { pending };
90
+ const pending = /* @__PURE__ */ new Map();
91
+ const state = {
92
+ pending,
93
+ abort: (reason) => controller.abort(reason)
94
+ };
84
95
  reports.set(operation, state);
85
- const deadline = performance.now() + 6e4;
96
+ const deadline = performance.now() + timeoutMs;
86
97
  const expire = () => {
87
- controller.abort(Object.assign(/* @__PURE__ */ new Error("The report exceeded its 60 second budget."), { code: "STIM_REPORT_TIMEOUT" }));
98
+ controller.abort(Object.assign(/* @__PURE__ */ new Error(`The report exceeded its ${timeoutMs / 1e3} second budget.`), { code: "STIM_REPORT_TIMEOUT" }));
88
99
  };
89
- const timer = setTimeout(expire, 6e4);
100
+ const timer = setTimeout(expire, timeoutMs);
90
101
  let rejectCancellation;
91
102
  const cancelled = new Promise((_, reject) => {
92
103
  rejectCancellation = reject;
@@ -109,7 +120,7 @@ async function runReport(context, work) {
109
120
  controller.abort();
110
121
  const cleanupDeadline = performance.now() + 35e3;
111
122
  const expireCleanup = () => {
112
- state.failure ??= Object.assign(/* @__PURE__ */ new Error("Report cleanup did not finish within 35 seconds."), {
123
+ state.failure ??= [...pending.values()].find((failure) => failure !== void 0) ?? Object.assign(/* @__PURE__ */ new Error("Report cleanup did not finish within 35 seconds."), {
113
124
  code: "STIM_CLAIM_UNAVAILABLE",
114
125
  reportCleanup: true
115
126
  });
@@ -117,7 +128,7 @@ async function runReport(context, work) {
117
128
  let cleanupTimer;
118
129
  try {
119
130
  await Promise.race([(async () => {
120
- while (pending.size) await Promise.allSettled(pending);
131
+ while (pending.size) await Promise.allSettled(pending.keys());
121
132
  })(), new Promise((resolve) => {
122
133
  cleanupTimer = setTimeout(() => {
123
134
  expireCleanup();
@@ -139,11 +150,6 @@ var process_identity_exports = /* @__PURE__ */ __exportAll({});
139
150
  import * as import__stim_cli_core_process_identity from "@stim-cli/core/process-identity";
140
151
  __reExport(process_identity_exports, import__stim_cli_core_process_identity);
141
152
  //#endregion
142
- //#region src/ownership-claim.ts
143
- var ownership_claim_exports = /* @__PURE__ */ __exportAll({});
144
- import * as import__stim_cli_core_ownership_claim from "@stim-cli/core/ownership-claim";
145
- __reExport(ownership_claim_exports, import__stim_cli_core_ownership_claim);
146
- //#endregion
147
153
  //#region src/engine/operation-state.ts
148
154
  const states = /* @__PURE__ */ new WeakMap();
149
155
  let detached;
@@ -209,7 +215,8 @@ function stopDeclaringSpawnsOn(context, claim) {
209
215
  * the caller's process group, so a terminal interrupt still reaches it; the claim follows the child
210
216
  * itself. A claim that cannot be marked stops the spawn rather than letting it run unprotected. A claim
211
217
  * records one child, so declared spawns must not overlap. `until: 'close'` also holds the declaration
212
- * while a launcher's child retains its stdio pipes after the launcher exits.
218
+ * while a launcher's child retains its stdio pipes after the launcher exits. A promise holds the declaration until
219
+ * cleanup resolves; a rejection keeps the child record for recovery.
213
220
  */
214
221
  function spawnDeclared(context, spawn, { until = "exit" } = {}) {
215
222
  const { declaring, running, refusal } = operationState(context);
@@ -235,7 +242,6 @@ function spawnDeclared(context, spawn, { until = "exit" } = {}) {
235
242
  return child;
236
243
  }
237
244
  running.add(child);
238
- child.once(until, () => running.delete(child));
239
245
  const captured = (0, process_identity_exports.captureProcessIdentity)(pid);
240
246
  if (captured.ok) for (const claim of claims) try {
241
247
  (0, ownership_claim_exports.setClaimChild)(claim, {
@@ -243,7 +249,12 @@ function spawnDeclared(context, spawn, { until = "exit" } = {}) {
243
249
  processToken: captured.token
244
250
  });
245
251
  } catch {}
246
- child.once(until, forget);
252
+ const settled = () => {
253
+ running.delete(child);
254
+ forget();
255
+ };
256
+ if (typeof until === "string") child.once(until, settled);
257
+ else until.then(settled, () => {});
247
258
  return child;
248
259
  }
249
260
  /** Refuse every later `spawnDeclared` of `context`'s operation with `reason` until `allowDeclaredSpawns`. */
@@ -268,14 +279,81 @@ function signalDeclaredSpawns(context, signal) {
268
279
  return signalled;
269
280
  }
270
281
  //#endregion
282
+ //#region src/engine/workspace-process-lock.ts
283
+ const DEFAULT_WAIT_MS = 6e4;
284
+ const POLL_MS = 25;
285
+ var WorkspaceProcessCleanupError = class extends Error {
286
+ code = "STIM_CLAIM_UNAVAILABLE";
287
+ claims;
288
+ remedy;
289
+ constructor(claims, message, remedy) {
290
+ super(`${message} Its ownership claims remain held at ${claims.map((claim) => claim.path).join(", ")}.`);
291
+ this.claims = claims;
292
+ this.remedy = `${remedy} After verifying no child remains, remove only these claims: ${claims.map((claim) => (0, ownership_claim_exports.claimRemoveCommand)(claim.path)).join("; ")}`;
293
+ }
294
+ };
295
+ function workspaceProcessLockError(err) {
296
+ const code = err?.code;
297
+ if (code === "STIM_LOCK_REFUSED") return "refused";
298
+ if (code === "STIM_LOCK_TIMEOUT") return "timeout";
299
+ return null;
300
+ }
301
+ function workspaceProcessLockPath(root, name, external) {
302
+ return external ? join(root, `${name}.lock`) : join(root, ".stim", `${name}.lock`);
303
+ }
304
+ async function withWorkspaceProcessLock(root, name, fn, { now = Date.now, signal, sleep = (ms) => setTimeout$1(ms, void 0, { signal }), waitMs = DEFAULT_WAIT_MS, ownerPurpose, details = {}, onHeld, rejectOwnerPurposes = [], external = false, declareSpawnsFor } = {}) {
305
+ const path = workspaceProcessLockPath(root, name, external);
306
+ const deadline = now() + waitMs;
307
+ for (;;) {
308
+ signal?.throwIfAborted();
309
+ const attempt = (0, ownership_claim_exports.tryAcquireClaim)({
310
+ root: path,
311
+ mode: "exclusive",
312
+ label: `${name} lock`,
313
+ details: ownerPurpose ? {
314
+ ...details,
315
+ purpose: ownerPurpose
316
+ } : details
317
+ });
318
+ if (attempt.pending) (0, ownership_claim_exports.releaseClaim)(attempt.pending);
319
+ if (attempt.acquired) {
320
+ if (declareSpawnsFor) declareSpawnsOn(declareSpawnsFor, attempt.acquired);
321
+ let retained = false;
322
+ try {
323
+ return await fn(attempt.acquired);
324
+ } catch (cause) {
325
+ retained = cause instanceof WorkspaceProcessCleanupError && cause.claims.includes(attempt.acquired);
326
+ throw cause;
327
+ } finally {
328
+ if (declareSpawnsFor) stopDeclaringSpawnsOn(declareSpawnsFor, attempt.acquired);
329
+ if (!retained) (0, ownership_claim_exports.releaseClaim)(attempt.acquired);
330
+ }
331
+ }
332
+ const holder = attempt.held ?? attempt.waitingFor?.[0];
333
+ const purpose = holder?.details.purpose;
334
+ if (typeof purpose === "string" && rejectOwnerPurposes.includes(purpose)) {
335
+ const error = /* @__PURE__ */ new Error(`The ${name} lock at ${path} is held for ${purpose}.`);
336
+ error.code = "STIM_LOCK_REFUSED";
337
+ throw error;
338
+ }
339
+ if (holder && onHeld) await onHeld(holder);
340
+ if (now() >= deadline) {
341
+ const error = /* @__PURE__ */ new Error(`Timed out waiting for the ${name} lock at ${path}.`);
342
+ error.code = "STIM_LOCK_TIMEOUT";
343
+ throw error;
344
+ }
345
+ await sleep(POLL_MS);
346
+ }
347
+ }
348
+ //#endregion
271
349
  //#region src/exec.ts
272
350
  const MAX_BUFFER = 67108864;
273
- function spawnReportProbe(file, args, opts) {
274
- if (process.platform !== "win32" || !reportSignal(ambientContext()) || !executableIn(file, opts.env ?? ambientEnv(), opts.cwd ?? process.cwd())) return spawn(file, args, opts);
351
+ function spawnReportProbe(file, args, opts, containProcessTree = false) {
352
+ if (process.platform !== "win32" || !containProcessTree && !reportSignal(ambientContext()) || !executableIn(file, opts.env ?? ambientEnv(), opts.cwd ?? process.cwd())) return spawn(file, args, opts);
275
353
  const env = opts.env ?? ambientEnv();
276
354
  const systemRoot = process.env.SystemRoot ?? process.env.SYSTEMROOT;
277
355
  if (!systemRoot) throw new Error("SystemRoot is required to contain Windows report probes.");
278
- return spawn(resolve(systemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe"), [
356
+ return spawn(resolve$1(systemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe"), [
279
357
  "-NoLogo",
280
358
  "-NoProfile",
281
359
  "-NonInteractive",
@@ -313,8 +391,7 @@ function reportChild(child, signal, failed) {
313
391
  child.once("close", () => {
314
392
  ended = true;
315
393
  finishNativeClose();
316
- if (report) cancel().then(resolve, reject);
317
- else resolve();
394
+ cancel().then(resolve, reject);
318
395
  });
319
396
  });
320
397
  const pid = child.pid;
@@ -334,15 +411,15 @@ function reportChild(child, signal, failed) {
334
411
  return "unknown";
335
412
  };
336
413
  const refuse = (message) => {
337
- const error = Object.assign(new Error(message), {
414
+ const failure = Object.assign(new Error(message), {
338
415
  code: "STIM_CLAIM_UNAVAILABLE",
339
416
  reportCleanup: true
340
417
  });
418
+ const error = failed?.(failure) ?? failure;
341
419
  child.stdin?.destroy();
342
420
  child.stdout?.destroy();
343
421
  child.stderr?.destroy();
344
422
  child.unref();
345
- failed?.(error);
346
423
  rejectClosed(error);
347
424
  throw error;
348
425
  };
@@ -394,6 +471,11 @@ function reportChild(child, signal, failed) {
394
471
  clearTimeout(timer);
395
472
  if (!ended) refuse(`Report child ${pid} did not close within the cleanup budget.`);
396
473
  }
474
+ if (pid !== void 0 && process.platform !== "win32") {
475
+ const deadline = performance.now() + 1e3;
476
+ while ((0, ownership_claim_exports.processGroupAlive)(pid) && performance.now() < deadline) await setTimeout$1(25);
477
+ if ((0, ownership_claim_exports.processGroupAlive)(pid)) refuse(`Report child ${pid} still has descendants after cleanup.`);
478
+ }
397
479
  })();
398
480
  if (report) trackReportResource(context, closed, cancel, signal);
399
481
  else {
@@ -406,7 +488,10 @@ function reportChild(child, signal, failed) {
406
488
  closed.then(settled, settled);
407
489
  if (callerSignal.aborted) abort();
408
490
  }
409
- return cancel;
491
+ return {
492
+ cancel,
493
+ closed
494
+ };
410
495
  }
411
496
  function budgetMs(timeoutMs) {
412
497
  if (timeoutMs === "unbounded") return void 0;
@@ -431,10 +516,10 @@ function executableIn(file, env, cwd) {
431
516
  const value = (name) => env[name] ?? (windows ? Object.entries(env).find(([key, field]) => field !== void 0 && key.toUpperCase() === name)?.[1] : void 0);
432
517
  const directory = typeof cwd === "string" ? cwd : fileURLToPath(cwd);
433
518
  const path = file.includes("/") || windows && file.includes("\\");
434
- const directories = path ? [""] : (value("PATH") ?? "").split(delimiter);
519
+ const directories = path ? [""] : (value("PATH") ?? "").split(delimiter$1);
435
520
  if (windows && !path) directories.unshift(directory);
436
521
  for (const entry of directories) {
437
- const candidate = resolve(directory, entry.replace(/^"(.*)"$/, "$1"), file);
522
+ const candidate = resolve$1(directory, entry.replace(/^"(.*)"$/, "$1"), file);
438
523
  const found = which.sync(candidate, {
439
524
  nothrow: true,
440
525
  path: "",
@@ -447,7 +532,7 @@ function executableIn(file, env, cwd) {
447
532
  function refuseUnsafeBatchArguments(file, args, env = ambientEnv(), cwd = ambientContext()?.cwd ?? process.cwd()) {
448
533
  if (process.platform !== "win32") return;
449
534
  const target = executableIn(file, env, cwd) ?? file;
450
- if (isUnsafeBatchSpawn(target, args)) throw new Error(`Refusing to run ${basename(target)}: an argument contains a character cmd.exe could misinterpret (a double quote or a line break).`);
535
+ if (isUnsafeBatchSpawn(target, args)) throw new Error(`Refusing to run ${basename$1(target)}: an argument contains a character cmd.exe could misinterpret (a double quote or a line break).`);
451
536
  }
452
537
  function redactError(error, values) {
453
538
  const secrets = values?.filter(Boolean) ?? [];
@@ -524,7 +609,7 @@ const defaultExecutor = {
524
609
  if (detachedSilent) return "";
525
610
  return untrimmed ? String(result.stdout) : String(result.stdout).trim();
526
611
  },
527
- runFileAsync(file, args = [], { timeoutMs, killSignal, cwd, env, omitEnv, input, untrimmed, rejectStderr, onSpawn, declareFor, detachedSilent, redact, signal: callSignal }) {
612
+ runFileAsync(file, args = [], { timeoutMs, killSignal, cwd, env, omitEnv, input, untrimmed, rejectStderr, onSpawn, declareFor, detachedSilent, containProcessTree, cleanupOnExit, redact, signal: callSignal }) {
528
613
  const command = [file, ...args].join(" ");
529
614
  return new Promise((resolve, reject) => {
530
615
  const report = callSignal ?? reportSignal(ambientContext());
@@ -550,20 +635,38 @@ const defaultExecutor = {
550
635
  opts.env = childEnv;
551
636
  }
552
637
  refuseUnsafeBatchArguments(file, args, opts.env, opts.cwd);
553
- const start = () => spawnReportProbe(file, args, opts);
554
- const child = declareFor ? spawnDeclared(declareFor, start) : start();
638
+ const claims = declareFor ? [...operationState(declareFor).declaring] : [];
639
+ let clearDeclaration;
640
+ let retainDeclaration;
641
+ const declaration = claims.length > 0 && report ? new Promise((resolveDeclaration, rejectDeclaration) => {
642
+ clearDeclaration = resolveDeclaration;
643
+ retainDeclaration = rejectDeclaration;
644
+ }) : void 0;
645
+ const start = () => spawnReportProbe(file, args, opts, containProcessTree || Boolean(report));
646
+ const child = declareFor ? spawnDeclared(declareFor, start, declaration ? { until: declaration } : {}) : start();
647
+ let lifetime;
555
648
  let cancel;
556
649
  let timer;
557
650
  try {
558
- cancel = reportChild(child, report, (error) => {
651
+ lifetime = reportChild(child, report, (error) => {
652
+ const failure = claims.length ? Object.assign(new WorkspaceProcessCleanupError(claims, error.message, "Verify the child and its descendants have stopped."), { reportCleanup: true }) : error;
559
653
  clearTimeout(timer);
560
- reject(error);
654
+ return failure;
655
+ });
656
+ cancel = lifetime?.cancel;
657
+ if (cleanupOnExit && cancel) child.once("exit", () => {
658
+ clearTimeout(timer);
659
+ cancel?.().catch(reject);
561
660
  });
661
+ lifetime?.closed.catch(reject);
662
+ if (declaration) lifetime?.closed.then(clearDeclaration, retainDeclaration);
562
663
  onSpawn?.(child);
563
664
  } catch (error) {
564
- if (cancel) cancel().catch(reject);
565
- else if (!report) child.kill("SIGKILL");
566
- reject(error);
665
+ if (cancel) cancel().then(() => lifetime?.closed).then(() => reject(error), reject);
666
+ else {
667
+ if (!report) child.kill("SIGKILL");
668
+ reject(error);
669
+ }
567
670
  return;
568
671
  }
569
672
  if (input !== void 0) {
@@ -610,8 +713,9 @@ const defaultExecutor = {
610
713
  clearTimeout(timer);
611
714
  reject(redactError(error, redact));
612
715
  });
613
- child.on("close", (status, signal) => {
716
+ const finish = async (status, signal) => {
614
717
  clearTimeout(timer);
718
+ await lifetime?.closed;
615
719
  const discardOutput = (timedOut || overflowed || report?.aborted) && redact?.some(Boolean);
616
720
  const out = discardOutput ? "" : Buffer.concat(stdout).toString("utf-8");
617
721
  const err = discardOutput ? "" : Buffer.concat(stderr).toString("utf-8");
@@ -626,6 +730,9 @@ const defaultExecutor = {
626
730
  else if (overflowed) reject(redactError(Object.assign(/* @__PURE__ */ new Error(`Command output exceeded ${MAX_BUFFER} bytes: ${command}`), result, { code: "ENOBUFS" }), redact));
627
731
  else if (status !== 0 || rejectStderr && err.trim()) reject(redactError(Object.assign(/* @__PURE__ */ new Error(`Command failed: ${command}${err ? `\n${err}` : ""}`), result), redact));
628
732
  else resolve(detachedSilent ? "" : untrimmed ? out : out.trim());
733
+ };
734
+ child.on("close", (status, signal) => {
735
+ finish(status, signal).catch(reject);
629
736
  });
630
737
  });
631
738
  },
@@ -660,16 +767,17 @@ const defaultExecutor = {
660
767
  } : opts;
661
768
  if (report && process.platform !== "win32") childOpts.detached = true;
662
769
  refuseUnsafeBatchArguments(cmd, args, childOpts.env ?? ambientEnv(), childOpts.cwd);
770
+ const claims = report ? [...operationState(context).declaring] : [];
663
771
  const child = spawnReportProbe(cmd, args, childOpts);
664
- reportChild(child);
772
+ reportChild(child, void 0, (error) => claims.length ? Object.assign(new WorkspaceProcessCleanupError(claims, error.message, "Verify the child and its descendants have stopped."), { reportCleanup: true }) : error);
665
773
  return child;
666
774
  },
667
- findExecutable(name) {
668
- return executableIn(name, ambientEnv(), ambientContext()?.cwd ?? process.cwd());
775
+ findExecutable(name, options = {}) {
776
+ return executableIn(name, options.env ?? ambientEnv(), options.cwd ?? ambientContext()?.cwd ?? process.cwd());
669
777
  }
670
778
  };
671
779
  /** The program a call runs, without its arguments: those can carry tokens. */
672
- const programOf = (command) => basename(command.trim().split(/\s+/)[0] ?? "");
780
+ const programOf = (command) => basename$1(command.trim().split(/\s+/)[0] ?? "");
673
781
  function outcomeOf(error) {
674
782
  const { status, signal, code } = error ?? {};
675
783
  return {
@@ -713,11 +821,11 @@ function timedSync(fn, program) {
713
821
  let active = {
714
822
  ...defaultExecutor,
715
823
  run: timedSync((cmd, opts) => defaultExecutor.run(cmd, opts), programOf),
716
- runFile: timedSync((file, args, opts) => defaultExecutor.runFile(file, args, opts), (file) => basename(file)),
824
+ runFile: timedSync((file, args, opts) => defaultExecutor.runFile(file, args, opts), (file) => basename$1(file)),
717
825
  async runFileAsync(file, args, opts) {
718
826
  if (!debugLog.enabled()) return defaultExecutor.runFileAsync(file, args, opts);
719
827
  const id = ++executionId;
720
- const name = basename(file);
828
+ const name = basename$1(file);
721
829
  const started = performance.now();
722
830
  debugLog.log("exec.start", {
723
831
  program: name,
@@ -747,4 +855,4 @@ function getExecutor() {
747
855
  return active;
748
856
  }
749
857
  //#endregion
750
- export { debugLog as _, refuseDeclaredSpawns as a, stopDeclaringSpawnsOn as c, withDrainingCancellation as d, ownership_claim_exports as f, trackReportResource as g, runReport as h, declareSpawnsOn as i, isTermination as l, reportSignal as m, isTimeoutError as n, signalDeclaredSpawns as o, process_identity_exports as p, allowDeclaredSpawns as r, spawnDeclared as s, getExecutor as t, operationState as u, __commonJSMin as v, __require as y };
858
+ export { __require as C, __commonJSMin as S, reportSignal as _, workspaceProcessLockError as a, debugLog as b, declareSpawnsOn as c, spawnDeclared as d, stopDeclaringSpawnsOn as f, process_identity_exports as g, withDrainingCancellation as h, withWorkspaceProcessLock as i, refuseDeclaredSpawns as l, operationState as m, isTimeoutError as n, workspaceProcessLockPath as o, isTermination as p, WorkspaceProcessCleanupError as r, allowDeclaredSpawns as s, getExecutor as t, signalDeclaredSpawns as u, runReport as v, ownership_claim_exports as x, trackReportResource as y };
@@ -15,6 +15,12 @@ interface NdjsonWriter {
15
15
  readonly dropped: number;
16
16
  readonly lastError: Error | null;
17
17
  }
18
+ declare function createNdjsonWriter(file: string, { truncate, fields, maxBytes, now }?: {
19
+ truncate?: boolean;
20
+ fields?: Record<string, unknown>;
21
+ maxBytes?: number;
22
+ now?: () => number;
23
+ }): NdjsonWriter;
18
24
  //#endregion
19
25
  //#region src/exec.d.ts
20
26
  interface ExecOptions {
@@ -26,7 +32,7 @@ interface ExecOptions {
26
32
  cwd?: string;
27
33
  env?: Record<string, string>;
28
34
  omitEnv?: readonly string[];
29
- /** Text written to the child's stdin; `runFile` only. */
35
+ /** Text written to the child's stdin. */
30
36
  input?: string;
31
37
  /** `runFileAsync` only: called with the child right after it starts; a throw kills the child and rejects the call. */
32
38
  onSpawn?: (child: ChildProcess) => void;
@@ -40,6 +46,10 @@ interface ExecOptions {
40
46
  rejectStderr?: boolean;
41
47
  /** `runFile` and `runFileAsync`: run in its own session with all stdio ignored and return ''; the child reports through a file, so a background process it leaves behind cannot hold a pipe open. */
42
48
  detachedSilent?: boolean;
49
+ /** `runFileAsync` only: put the Windows child tree in a kill-on-close Job Object. */
50
+ containProcessTree?: boolean;
51
+ /** `runFileAsync` with a cancellation signal: begin verified tree cleanup at leader exit, before inherited pipes close. */
52
+ cleanupOnExit?: boolean;
43
53
  /**
44
54
  * Exact values replaced with `***` in every string field of a thrown error.
45
55
  * Async calls that time out or overflow discard captured output when any nonempty redaction value is supplied.
@@ -58,8 +68,11 @@ interface Executor {
58
68
  spawn(cmd: string, args?: readonly string[], opts?: SpawnOptions): ChildProcess;
59
69
  /** The cancellation signal of the current bounded report, including while a child is queued. */
60
70
  reportSignal?(): AbortSignal | undefined;
61
- /** Absolute path of `name` on PATH, or null. Windows resolves PATH through PATHEXT. */
62
- findExecutable(name: string): string | null;
71
+ /** Absolute path of `name` on PATH, or null. Options override the operation's cwd/environment; Windows uses PATHEXT. */
72
+ findExecutable(name: string, options?: {
73
+ cwd?: string;
74
+ env?: NodeJS.ProcessEnv;
75
+ }): string | null;
63
76
  }
64
77
  //#endregion
65
- export { NdjsonWriter as n, Executor as t };
78
+ export { NdjsonWriter as n, createNdjsonWriter as r, Executor as t };