stim 1.28.0 → 1.30.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 (155) hide show
  1. package/dist/{acquire-artifact-CKByBnTD.mjs → acquire-artifact-CrZ4rEbI.mjs} +1 -1
  2. package/dist/{act-8iswyfRa.mjs → act-BaKjDWGw.mjs} +479 -29
  3. package/dist/{activity-DRMg4_mD.mjs → activity-BTaUf0tc.mjs} +6 -5
  4. package/dist/{agent-device-usage-output-tE90fDdo.mjs → agent-device-usage-output-DYBpO17K.mjs} +4 -4
  5. package/dist/{android-YWCbmkVK.mjs → android-BCYLwmH6.mjs} +5 -5
  6. package/dist/{android-D577ApKG.mjs → android-BEQUDpOg.mjs} +1 -1
  7. package/dist/{android-CHmSU4B4.mjs → android-BWqdDK6R.mjs} +44 -42
  8. package/dist/{android-CsPs-7Mg.mjs → android-DdKG6lyD.mjs} +44 -35
  9. package/dist/{android-cas-_aDifWW0.mjs → android-cas-BH6K12__.mjs} +4 -4
  10. package/dist/{android-project-DUGMbq28.d.mts → android-project-CCEQyYJK.d.mts} +6 -2
  11. package/dist/api.d.mts +7 -4
  12. package/dist/api.mjs +27 -27
  13. package/dist/{apk-signed-tail-CQbwM0qO.mjs → apk-signed-tail-B8kw4a8K.mjs} +2 -2
  14. package/dist/{artifact-DYtHjxsn.mjs → artifact-DTHXcmQ3.mjs} +12 -12
  15. package/dist/{artifact-bFkfD3vf.mjs → artifact-M6W2l-xh.mjs} +12 -12
  16. package/dist/{auto-placement-BVqs8AJi.mjs → auto-placement-CZaWDmcY.mjs} +10 -10
  17. package/dist/{browser-web-BUaLcafY.mjs → browser-web-JQ7lFej3.mjs} +12 -12
  18. package/dist/{build-cQURy_KW.mjs → build-C2ZWoCmg.mjs} +23 -23
  19. package/dist/{build-cache-CZULpXVl.mjs → build-cache-DlC3owap.mjs} +4 -4
  20. package/dist/{build-plan-CxoYfxsc.mjs → build-plan-J3iRR3gI.mjs} +6 -6
  21. package/dist/{cache-manifest-DEjqGGat.mjs → cache-manifest-evF_zJ9U.mjs} +1 -1
  22. package/dist/cache-manifest.mjs +1 -1
  23. package/dist/{ccache-Bm_A1xX9.mjs → ccache-B8gY50-h.mjs} +18 -18
  24. package/dist/{cli-CpbaLDa7.mjs → cli-xRPXJPtA.mjs} +19 -19
  25. package/dist/cli.mjs +1 -1
  26. package/dist/{client-YxCer6ab.mjs → client-DrHykA24.mjs} +5 -5
  27. package/dist/collector-run.mjs +5 -5
  28. package/dist/{command-output-C8R1KIvi.mjs → command-output-BBW8eWPL.mjs} +1 -1
  29. package/dist/{config-Cn94Jc0g.mjs → config-AbUjIBOQ.mjs} +121 -19
  30. package/dist/{dependencies-DKeMzwXY.mjs → dependencies-vUh9mp5z.mjs} +39 -38
  31. package/dist/{dependency-state-CojA2o0t.mjs → dependency-state-CPKAKCZ8.mjs} +1 -1
  32. package/dist/{dev-client-D-BW5uiq.mjs → dev-client-CASgAOu7.mjs} +2 -2
  33. package/dist/device-ChnO7bR9.mjs +761 -0
  34. package/dist/device-CsIdRXfm.mjs +175 -0
  35. package/dist/{device-CgXfbcz5.mjs → device-android-CcdQqQWg.mjs} +28 -195
  36. package/dist/{device-capacity-Cd8lMSn3.mjs → device-capacity-C5C_jOQd.mjs} +11 -11
  37. package/dist/device-host-worker.mjs +44 -35
  38. package/dist/{device-ios-Cc9ulihb.mjs → device-ios-DbmtPIiw.mjs} +28 -18
  39. package/dist/{device-lease-run-Mtzp4THX.mjs → device-lease-run-Do6zYA7y.mjs} +4 -4
  40. package/dist/{device-pool-eRz6DtCg.mjs → device-pool-CpoYiCGO.mjs} +3 -3
  41. package/dist/{device-eeK95Lir.mjs → device-provision-B5Ms94qY.mjs} +11 -302
  42. package/dist/{doctor-xf7ht-5n.mjs → doctor-CJSB5q-s.mjs} +20 -20
  43. package/dist/{doctor-Bz_ouYAW.mjs → doctor-CLE4AZEI.mjs} +20 -15
  44. package/dist/{doctor-C2QyhejJ.mjs → doctor-CpfbYh3Q.mjs} +1 -1
  45. package/dist/doctor-fingerprint.mjs +1 -1
  46. package/dist/{doctor-watchman-BTW82PDC.mjs → doctor-watchman-D1IyMle9.mjs} +4 -4
  47. package/dist/{eas-build-Dm6q68QY.mjs → eas-build-DIDOHw2P.mjs} +2 -2
  48. package/dist/{eas-fallback-DajNpxf-.mjs → eas-fallback-DVS9oBSc.mjs} +4 -4
  49. package/dist/{eas-session-ledger-CBFo_Uo1.mjs → eas-session-ledger-CW3l-inB.mjs} +5 -5
  50. package/dist/{error-diagnostics-D1QfyJzQ.mjs → error-diagnostics-XJ9eWdtq.mjs} +8 -8
  51. package/dist/{gc-PLFwb0k4.mjs → gc-BVIcxCxE.mjs} +2 -2
  52. package/dist/{gc-DM5oMm-J.mjs → gc-Co7PE36D.mjs} +80 -18
  53. package/dist/{guide-C4I46XAk.mjs → guide-DkepBgHT.mjs} +235 -34
  54. package/dist/{hosted-android-DlaKLbW0.mjs → hosted-android-Du7NxufJ.mjs} +3 -3
  55. package/dist/{hosted-client-DDRm9hPJ.mjs → hosted-client-Cect92SZ.mjs} +3 -3
  56. package/dist/{hosted-logs-bSebOKbz.mjs → hosted-logs-Opcjv1Lr.mjs} +17 -8
  57. package/dist/{hosted-logs-sync-B0c53W_c.mjs → hosted-logs-sync-wsI7N1Hv.mjs} +7 -11
  58. package/dist/{hosted-macos-B7-8Pv4H.mjs → hosted-macos-BP9Kucat.mjs} +5 -5
  59. package/dist/{hosted-native-CDe33CSr.mjs → hosted-native-DmdLSmvu.mjs} +9 -8
  60. package/dist/{idle-shutdown-CQ0QwU9J.mjs → idle-shutdown-BcMN_cLm.mjs} +7 -7
  61. package/dist/{ios-B1HvafEG.mjs → ios-BFkBHuRT.mjs} +8 -8
  62. package/dist/{ios-tg4tp3q1.mjs → ios-CG0IsLtt.mjs} +29 -29
  63. package/dist/{ios-B1DYuOjh.mjs → ios-CXnoVO_z.mjs} +4 -4
  64. package/dist/{ios-device-B4elVFKi.mjs → ios-device-ClSfuu-_.mjs} +1 -1
  65. package/dist/{ios-project-CR4mZ7CH.mjs → ios-project-BM9TbTE7.mjs} +7 -7
  66. package/dist/{ios-state-BPHqMDdZ.mjs → ios-state-Ce_dystM.mjs} +1 -1
  67. package/dist/{js-swap-CVpSW1fn.mjs → js-swap-9Rx-5Nqz.mjs} +4 -4
  68. package/dist/{launch-DoySmhjz.mjs → launch-DvJV1Mx4.mjs} +19 -19
  69. package/dist/{lightened-DFAJgfIk.mjs → lightened-23pqe7LS.mjs} +4 -4
  70. package/dist/{logs-BGsW7iPe.mjs → logs-DJgZlf2K.mjs} +9 -8
  71. package/dist/{logs-DI5ILkYB.mjs → logs-Pkeh3wZk.mjs} +7 -6
  72. package/dist/{macos-Bo9st9Di.mjs → macos-DPvPQadc.mjs} +20 -20
  73. package/dist/{macos-C12RMJQF.mjs → macos-Du07NsOB.mjs} +1 -1
  74. package/dist/macos-run.mjs +1 -1
  75. package/dist/maintenance-run.mjs +13 -9
  76. package/dist/{metro-BnhXV2Mj.mjs → metro-BMQHczcf.mjs} +2 -2
  77. package/dist/{metro-request-DXyKjVDF.mjs → metro-request-BtF7ox53.mjs} +1 -1
  78. package/dist/{named-ports-Phoi2POD.mjs → named-ports-DcktE2Pw.mjs} +2 -2
  79. package/dist/{native-android-vxgtLD7w.mjs → native-android-CBOM1_gB.mjs} +16 -16
  80. package/dist/{native-gradle-artifact-inputs-BN96IVw8.mjs → native-gradle-artifact-inputs-D_iAfPuS.mjs} +3 -3
  81. package/dist/{native-runtime-DG4Rodzj.mjs → native-runtime-D4cTwU0Z.mjs} +6 -6
  82. package/dist/{native-xcode-inputs-BKarCoKg.mjs → native-xcode-inputs-BF-ogSnG.mjs} +2 -2
  83. package/dist/{native-xcode-ios-8A-efGa5.mjs → native-xcode-ios-DwJBfMP6.mjs} +15 -15
  84. package/dist/{ndjson-Dy2W4vRa.mjs → ndjson-CrjIsGzw.mjs} +2 -2
  85. package/dist/offload-worker.d.mts +1 -1
  86. package/dist/offload-worker.mjs +22 -22
  87. package/dist/{ownership-DJCq48IJ.mjs → ownership-Dtz9G5Gl.mjs} +2 -2
  88. package/dist/{phone-reverses-BzTa8g1F.mjs → phone-reverses-CvhhkGMH.mjs} +4 -4
  89. package/dist/{placement-log-Cvhta5ib.mjs → placement-log-CRR_TE1r.mjs} +1 -1
  90. package/dist/{plan-placement-WkZdccuN.mjs → plan-placement-CdLc-pTb.mjs} +6 -6
  91. package/dist/{ports-UV4mJgyC.mjs → ports-g4N_cfyg.mjs} +3 -3
  92. package/dist/{prebuild-k8d_tqOq.mjs → prebuild-DBJMhwSS.mjs} +9 -9
  93. package/dist/{project-Sui52ywT.mjs → project-DPQjnpcI.mjs} +15 -15
  94. package/dist/{react-native-android-DANlGGQf.mjs → react-native-android-idMWOwkX.mjs} +24 -24
  95. package/dist/{react-native-build-jUHnIfZ9.mjs → react-native-build-BSoh4HkH.mjs} +7 -7
  96. package/dist/{react-native-doctor-DCWRjS7K.mjs → react-native-doctor-CYibY8-4.mjs} +9 -8
  97. package/dist/{react-native-ios-BwMsFMXi.mjs → react-native-ios-13KIIOOy.mjs} +16 -15
  98. package/dist/{recordings-iy0mkm7b.mjs → recordings-CHMaF73C.mjs} +1 -1
  99. package/dist/{reload-Bj7QyuDy.mjs → reload-B9DZOjcV.mjs} +1 -1
  100. package/dist/{reload-c_uEyc_X.mjs → reload-BUQI3dSu.mjs} +14 -14
  101. package/dist/{remote-DFlpAqdV.mjs → remote-C-RGHhAP.mjs} +8 -8
  102. package/dist/{result-BIcXREyC.mjs → result-W4HCum4A.mjs} +16 -16
  103. package/dist/{run-y2HwyNvg.mjs → run-BQAmrohh.mjs} +2 -2
  104. package/dist/{selection-CXgM_VK_.mjs → selection-PCMT97XD.mjs} +105 -62
  105. package/dist/{server-bare-2N4vSRZG.mjs → server-bare-CEQdB_F4.mjs} +37 -1
  106. package/dist/{server-command-qneHPcRI.mjs → server-command-oqi3TDy7.mjs} +5 -5
  107. package/dist/server-expo-BUFAq2In.mjs +2 -0
  108. package/dist/{server-expo-Ccm9zB0o.mjs → server-expo-C1K07shx.mjs} +8 -5
  109. package/dist/{settings-Cfd_Q6ys.mjs → settings-CKlMOmhj.mjs} +2 -2
  110. package/dist/{settings-CY5JZlNN.mjs → settings-D776ygML.mjs} +1 -1
  111. package/dist/{settings-Djl-mpjX.mjs → settings-D9LtQaUw.mjs} +2 -1
  112. package/dist/{settings-payload-DwQiEMCn.mjs → settings-payload-CE-NZ6Lq.mjs} +4 -4
  113. package/dist/{settings-write-CJpjkeAd.mjs → settings-write-D7Xwa_-M.mjs} +5 -5
  114. package/dist/settings.schema.json +58 -9
  115. package/dist/{simslim-DCQwzoPy.mjs → simslim-CbpHERcv.mjs} +1 -1
  116. package/dist/{slot-launch-BpmanOnk.mjs → slot-launch-CeIK1iUI.mjs} +6 -6
  117. package/dist/{stage-CmugrSG5.mjs → stage-Blqe6bWb.mjs} +1 -1
  118. package/dist/{start-DYLk6GXn.mjs → start-B7J5a9nS.mjs} +11 -11
  119. package/dist/{start-CHH3zgIU.mjs → start-CvP2pJjc.mjs} +1 -1
  120. package/dist/{state-BXC1Lfxw.mjs → state-C2AUjjhg.mjs} +1 -1
  121. package/dist/{state-B2QJbDwz.mjs → state-D5ZxwCGJ.mjs} +2 -2
  122. package/dist/{stats-DxKqhcGp.mjs → stats-Uo7l8OAk.mjs} +4 -4
  123. package/dist/{status-7ULDEJ1e.mjs → status-CK7ZoEhV.mjs} +2 -2
  124. package/dist/{status-D_Ljc89p.mjs → status-CqJI1TgG.mjs} +1 -1
  125. package/dist/{status-BHCkOZga.mjs → status-_rWXFSO7.mjs} +24 -24
  126. package/dist/{status-watch-BmsPqYSU.mjs → status-watch-CnSyXUmj.mjs} +4 -4
  127. package/dist/status-worker.mjs +1 -1
  128. package/dist/{stim-desktop-CbWo21kb.mjs → stim-desktop-CYXLFpFS.mjs} +1 -1
  129. package/dist/{stop-b2pouxAO.mjs → stop-Bogo9oO2.mjs} +2 -2
  130. package/dist/{stop-DQvE-P76.mjs → stop-CMVfaA-6.mjs} +4 -4
  131. package/dist/{stop-D-dGE9gh.mjs → stop-CnF9ixYe.mjs} +20 -19
  132. package/dist/supervisor-run.d.mts +1 -0
  133. package/dist/supervisor-run.mjs +23 -19
  134. package/dist/{support-04IhBUTT.mjs → support-BGqpYMV8.mjs} +15 -11
  135. package/dist/{support-BtlJKjyQ.mjs → support-DUevTPur.mjs} +9 -9
  136. package/dist/{swiftpm-artifact-inputs-Cj2UpmPN.mjs → swiftpm-artifact-inputs-BcJk6-3u.mjs} +1 -1
  137. package/dist/{swiftpm-macos-BhF2stxY.mjs → swiftpm-macos-DBrmoocG.mjs} +8 -8
  138. package/dist/toolchain-Cw9VVHOY.mjs +2 -0
  139. package/dist/{toolchain-mLA_km-b.mjs → toolchain-DLdoyxIt.mjs} +19 -11
  140. package/dist/{trigger-DMou-X7j.mjs → trigger-CfnOfpDd.mjs} +1 -1
  141. package/dist/trigger-DBZ-GBrl.mjs +2 -0
  142. package/dist/{warm-progress-B0Vpuvpl.mjs → warm-progress-DQ2wQZKp.mjs} +3 -3
  143. package/dist/web-CI_qCRPw.mjs +2 -0
  144. package/dist/{web-BYmK0TjL.mjs → web-g9U7L1sR.mjs} +8 -8
  145. package/dist/web-run.mjs +5 -5
  146. package/dist/{workspace-state-kIZrdlSg.mjs → workspace-state-DGwkPXwq.mjs} +2 -2
  147. package/dist/{workspaces-CmOcceDN.mjs → workspaces-7_RWoi6c.mjs} +11 -11
  148. package/dist/{worktree-nliAoHxB.mjs → worktree-DEet9H3p.mjs} +1 -1
  149. package/dist/{worktree-2dk5edza.mjs → worktree-qKJ3x07S.mjs} +25 -25
  150. package/package.json +7 -7
  151. package/shim/bundle-response.cjs +37 -0
  152. package/dist/server-expo-WulkyAwT.mjs +0 -2
  153. package/dist/toolchain-qktiV8xc.mjs +0 -2
  154. package/dist/trigger-qp2ji5wE.mjs +0 -2
  155. package/dist/web-CrVLVQZY.mjs +0 -2
@@ -1,5 +1,5 @@
1
1
  import { l as WorkspaceProcessCleanupError } from "./exec-aULwtNly.mjs";
2
- import { I as waitForSharedBuild } from "./activity-DRMg4_mD.mjs";
2
+ import { I as waitForSharedBuild } from "./activity-BTaUf0tc.mjs";
3
3
  import { rmSync } from "node:fs";
4
4
  //#region src/engine/artifact-lifecycle.ts
5
5
  async function runArtifactLifecycle(phases) {
@@ -1,30 +1,33 @@
1
- import { l as WorkspaceProcessCleanupError, n as isTimeoutError, t as getExecutor } from "./exec-aULwtNly.mjs";
2
- import { j as workspaceStateFile$1, k as workspaceLogsDir$1, r as readWorkspaceState$1, u as workspaceLastUsed } from "./workspace-state-kIZrdlSg.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-Cn94Jc0g.mjs";
4
- import { o as formatLongDuration, u as plural } from "./command-output-C8R1KIvi.mjs";
1
+ import { S as process_identity_exports, d as workspaceProcessLockError, l as WorkspaceProcessCleanupError, n as isTimeoutError, t as getExecutor, u as withWorkspaceProcessLock } from "./exec-aULwtNly.mjs";
2
+ import { E as workspaceDir, j as workspaceStateFile$1, k as workspaceLogsDir$1, r as readWorkspaceState$1, u as workspaceLastUsed, w as workspaceAgentDeviceDir } from "./workspace-state-DGwkPXwq.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-AbUjIBOQ.mjs";
4
+ import { o as formatLongDuration, u as plural } from "./command-output-BBW8eWPL.mjs";
5
5
  import { c as gitQuiet, l as gitQuietAsync, n as runOperationFileQuiet, r as childEnvironment } from "./operation-exec-Dp4FuIGR.mjs";
6
- import { At as listWorktrees, Et as hasUncommittedWorkAsync, Ft as matchWorktreeEntry, Kt as sourceCheckoutOf, O as maintenanceKeepSetting, Pt as lockReason, Ut as resolveFullRefAsync, Wt as resolveSourceCheckoutAsync, X as resolveProjectSettings, Xt as worktreeLockAsync, Z as resolveProjectSettingsAsync, bt as dirtyPathsAsync, h as deviceIdleShutdownMinutesSetting, jt as listWorktreesAsync, mt as LOCKED_REASON, qt as unpushedCommitsAsync, sn as removeTemporaryEntryAsync, wt as hasPopulatedSubmodulesAsync } from "./settings-CY5JZlNN.mjs";
7
- import { i as findProjectRoot$1 } from "./project-Sui52ywT.mjs";
8
- import { F as listAvdsAsync, G as ownedAvdSerialResolverAsync, P as listAvds, V as ownedAvdDirectory, yt as readParked } from "./android-CsPs-7Mg.mjs";
9
- import { A as diskPressureLevel, G as planCacheEmptying, N as lowestFree, P as protectWindowMs, X as collectGradleVersions, Z as deleteGradleVersion, Zn as collectMemoryReport, _ as resolveBudget, at as DEVICE_LIST_TIMEOUT_MS, ct as collectParkedSims, d as enforceBudget, dr as pruneCache, dt as deleteProjectDevices, j as levelFloors, lt as deleteParkedAvds, mt as deviceSweepIsScoped, nr as memorySweepIsScoped, nt as waitForGradleExit, or as stopIdleGradleDaemons, p as machineNumber, sr as phase, st as collectParkedAvds, u as cacheLabel, ur as discoverCaches, ut as deleteParkedSims, v as trimmableCaches, vt as findStaleProjectDevices, y as stopOwnedMetro, z as emptyCaches } from "./ccache-Bm_A1xX9.mjs";
10
- import { a as unregister, n as readManifest } from "./cache-manifest-DEjqGGat.mjs";
11
- import { B as workspaceBuildInProgress, Et as GC_OUTPUT, F as collectOwnedDeviceActivityAsync, Ot as takeGcResults, P as collectOwnedDeviceActivity, a as collectOrphanedWorkspaces, b as workspaceInUseAsync, c as isInsideWorkspaces, d as recentNativeUseReason, g as ARTIFACT_DIRS, h as workspaceDirRemoved, i as clearWorkspaceOutputs, jt as readConcurrency, kt as eachLimited, l as keptByLock, m as workspaceBuildReasons, o as collectWorkspaceOutputs, s as deleteOrphanedWorkspaces, t as LIGHTEN_AFTER_MS, u as listWorkspaceDirs, v as withIdleWorkspace, y as workspaceInUse } from "./workspaces-CmOcceDN.mjs";
6
+ import { At as listWorktrees, Et as hasUncommittedWorkAsync, Ft as matchWorktreeEntry, Kt as sourceCheckoutOf, O as maintenanceKeepSetting, Pt as lockReason, Ut as resolveFullRefAsync, Wt as resolveSourceCheckoutAsync, X as resolveProjectSettings, Xt as worktreeLockAsync, Z as resolveProjectSettingsAsync, bt as dirtyPathsAsync, h as deviceIdleShutdownMinutesSetting, it as settingValueAt, jt as listWorktreesAsync, mt as LOCKED_REASON, qt as unpushedCommitsAsync, sn as removeTemporaryEntryAsync, wt as hasPopulatedSubmodulesAsync } from "./settings-D776ygML.mjs";
7
+ import { P as readRemoteSession, i as findProjectRoot$1 } from "./project-DPQjnpcI.mjs";
8
+ import { F as listAvds, G as ownedAvdSerialResolverAsync, I as listAvdsAsync, M as listAdbDevicesAsync, O as getAvdNameForSerial, V as ownedAvdDirectory, j as listAdbDevices, k as getAvdNameForSerialAsync, yt as readParked } from "./android-DdKG6lyD.mjs";
9
+ import { c as pidExists } from "./metro-BMQHczcf.mjs";
10
+ import { A as diskPressureLevel, G as planCacheEmptying, N as lowestFree, P as protectWindowMs, X as collectGradleVersions, Z as deleteGradleVersion, Zn as collectMemoryReport, _ as resolveBudget, at as DEVICE_LIST_TIMEOUT_MS, ct as collectParkedSims, d as enforceBudget, dr as pruneCache, dt as deleteProjectDevices, j as levelFloors, lt as deleteParkedAvds, mt as deviceSweepIsScoped, nr as memorySweepIsScoped, nt as waitForGradleExit, or as stopIdleGradleDaemons, p as machineNumber, sr as phase, st as collectParkedAvds, u as cacheLabel, ur as discoverCaches, ut as deleteParkedSims, v as trimmableCaches, vt as findStaleProjectDevices, y as stopOwnedMetro, z as emptyCaches } from "./ccache-B8gY50-h.mjs";
11
+ import { o as readWebRecord, u as webFacts } from "./state-D5ZxwCGJ.mjs";
12
+ import { a as unregister, n as readManifest } from "./cache-manifest-evF_zJ9U.mjs";
13
+ import { B as workspaceBuildInProgress, Et as GC_OUTPUT, F as collectOwnedDeviceActivityAsync, Ot as takeGcResults, P as collectOwnedDeviceActivity, a as collectOrphanedWorkspaces, b as workspaceInUseAsync, c as isInsideWorkspaces, d as recentNativeUseReason, g as ARTIFACT_DIRS, h as workspaceDirRemoved, i as clearWorkspaceOutputs, jt as readConcurrency, kt as eachLimited, l as keptByLock, m as workspaceBuildReasons, o as collectWorkspaceOutputs, s as deleteOrphanedWorkspaces, t as LIGHTEN_AFTER_MS, u as listWorkspaceDirs, v as withIdleWorkspace, x as resolveSupervisorTarget, y as workspaceInUse } from "./workspaces-7_RWoi6c.mjs";
12
14
  import { t as canonicalPath } from "./paths-BsJMoPP2.mjs";
13
- import { N as listBuildLocks, _ as workspaceDeviceLastUseAt, b as leaseIsExpired, g as workspaceActivity, x as listLeaseFiles } from "./activity-DRMg4_mD.mjs";
14
- import { d as listAllIosSims, f as listAllIosSimsAsync } from "./ios-B1DYuOjh.mjs";
15
+ import { N as listBuildLocks, _ as workspaceDeviceLastUseAt, b as leaseIsExpired, g as workspaceActivity, x as listLeaseFiles } from "./activity-BTaUf0tc.mjs";
16
+ import { d as listAllIosSims, f as listAllIosSimsAsync } from "./ios-CXnoVO_z.mjs";
15
17
  import { s as metroStoreRoot } from "./errors-DHm_U5gv.mjs";
16
- import { t as resolveMaintenanceSettings } from "./settings-Djl-mpjX.mjs";
17
- import { D as stimCliUseOf, E as stimCliEntries, M as worktreeArchiveLimits, T as serviceRunningFrom, _ as cacheBlocked, a as nestedWorktrees, b as measurePressure, i as excludeWatchmanCookies, o as porcelainPath, p as removeWorktreeTarget, r as excludePodChurn, s as reclaimKeys, v as measureDisk, w as uncommittedActivityOf, x as measurePressureAsync, y as measureDiskAsync } from "./worktree-2dk5edza.mjs";
18
+ import { t as resolveMaintenanceSettings } from "./settings-D9LtQaUw.mjs";
19
+ import { o as runStop } from "./stop-CnF9ixYe.mjs";
20
+ import { D as stimCliUseOf, E as stimCliEntries, M as worktreeArchiveLimits, T as serviceRunningFrom, _ as cacheBlocked, a as nestedWorktrees, b as measurePressure, i as excludeWatchmanCookies, o as porcelainPath, p as removeWorktreeTarget, r as excludePodChurn, s as reclaimKeys, v as measureDisk, w as uncommittedActivityOf, x as measurePressureAsync, y as measureDiskAsync } from "./worktree-qKJ3x07S.mjs";
18
21
  import { n as describePullRequest, r as endedPullRequest, s as pullRequestLookups } from "./pull-request-BzLmzAIQ.mjs";
19
- import { a as lastFetchedAtAsync, c as mergeStateAsync, f as recordPullRequestLookup, i as fetchDefaultBranchAsync, o as localDefaultBranchAsync, r as FETCH_FRESH_MS } from "./warm-progress-B0Vpuvpl.mjs";
20
- import { a as restoreProblem, r as recordLightened, t as finishLightened } from "./lightened-DFAJgfIk.mjs";
21
- import { i as writeIdleShutdownEvent, n as dueIdleDevices, r as shutDownIdleDevices } from "./idle-shutdown-CQ0QwU9J.mjs";
22
+ import { a as lastFetchedAtAsync, c as mergeStateAsync, f as recordPullRequestLookup, i as fetchDefaultBranchAsync, o as localDefaultBranchAsync, r as FETCH_FRESH_MS } from "./warm-progress-DQ2wQZKp.mjs";
23
+ import { a as restoreProblem, r as recordLightened, t as finishLightened } from "./lightened-23pqe7LS.mjs";
24
+ import { i as writeIdleShutdownEvent, n as dueIdleDevices, r as shutDownIdleDevices } from "./idle-shutdown-BcMN_cLm.mjs";
22
25
  import { readClaimSet } from "@stim-cli/core/ownership-claim";
23
- import { existsSync, lstatSync, readdirSync, readlinkSync, realpathSync, rmSync, statSync } from "node:fs";
26
+ import { existsSync, lstatSync, readFileSync, readdirSync, readlinkSync, realpathSync, rmSync, statSync } from "node:fs";
24
27
  import { tmpdir } from "node:os";
25
28
  import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
26
- import { buildCacheRoot, configDir, metroCacheRoot } from "@stim-cli/core";
27
- import { LAST_BUILD_KEYS, MAINTENANCE_LOG_NAME, lastNativeUseFrom, loadConfig, readBuildHistory, readMaintenanceState, readPullRequestCache, settingDefinition, sharedCompilationCache } from "@stim-cli/core/state";
29
+ import { buildCacheRoot, configDir, metroCacheRoot, rotatedLogPath } from "@stim-cli/core";
30
+ import { LAST_BUILD_KEYS, MAINTENANCE_LOG_NAME, coerceSettingText, deviceViewersDir, lastNativeUseFrom, loadConfig, parseNdjsonText, readBuildHistory, readJsonObject, readMaintenanceState, readPullRequestCache, settingDefinition, settingValueError, sharedCompilationCache } from "@stim-cli/core/state";
28
31
  import { isAbsolute as isAbsolute$1, join as join$1 } from "path";
29
32
  import chalk from "chalk";
30
33
  import { existsSync as existsSync$1, readdirSync as readdirSync$1, statSync as statSync$1 } from "fs";
@@ -1486,6 +1489,432 @@ async function planWorktrees({ protectedRoot, pinned, now, level, graceMs, sizes
1486
1489
  };
1487
1490
  }
1488
1491
  //#endregion
1492
+ //#region src/maintenance/idle-stop.ts
1493
+ const HOUR_MS = 36e5;
1494
+ const SETTING = "maintenance.idleStopHours";
1495
+ const METRO_REQUESTS = /* @__PURE__ */ new Set([
1496
+ "bundle_response_started",
1497
+ "bundle_response_progress",
1498
+ "bundle_response_finished",
1499
+ "bundle_response_failed",
1500
+ "bundle_build_started",
1501
+ "bundle_build_done",
1502
+ "bundle_build_failed",
1503
+ "manifest_request_started",
1504
+ "manifest_request_finished",
1505
+ "manifest_request_failed",
1506
+ "hmr_update"
1507
+ ]);
1508
+ const STIM_SESSION = "stim-teardown-inventory";
1509
+ const AGENT_DEVICE_DRIVERS = /* @__PURE__ */ new Set(["agent-device-claim", "agent-device-lease"]);
1510
+ function decideIdleStop(evidence, { idleMs, now }) {
1511
+ if (!(idleMs > 0)) return {
1512
+ stop: false,
1513
+ reason: `${SETTING} is 0`
1514
+ };
1515
+ const last = evidence.activity.filter(({ at }) => Number.isFinite(at)).reduce((best, entry) => !best || entry.at > best.at ? entry : best, null);
1516
+ const idle = last ? now - last.at : NaN;
1517
+ if (last && idle < idleMs) return {
1518
+ stop: false,
1519
+ active: true,
1520
+ reason: `its last ${last.basis} was ${formatLongDuration(Math.max(0, idle))} ago, within ${SETTING}`
1521
+ };
1522
+ if (evidence.keep.length) return {
1523
+ stop: false,
1524
+ reason: evidence.keep.join("; ")
1525
+ };
1526
+ if (!last) return {
1527
+ stop: false,
1528
+ reason: "its last activity is unknown"
1529
+ };
1530
+ return {
1531
+ stop: true,
1532
+ idleMs: idle
1533
+ };
1534
+ }
1535
+ async function idleStopHours(root, env, operation) {
1536
+ const setting = settingDefinition(SETTING);
1537
+ if (!setting?.env) return { invalid: `${SETTING} is not in the settings registry` };
1538
+ const raw = env[setting.env];
1539
+ const fromEnv = raw !== void 0 && raw !== "";
1540
+ let value;
1541
+ if (fromEnv) value = coerceSettingText(setting, raw);
1542
+ else try {
1543
+ const resolved = operation ? await resolveProjectSettingsAsync(root, {
1544
+ env,
1545
+ signal: operation.signal,
1546
+ declareFor: operation
1547
+ }) : resolveProjectSettings(root);
1548
+ value = settingValueAt(resolved.settings, SETTING);
1549
+ } catch (error) {
1550
+ rethrowCleanup(error, operation?.signal);
1551
+ return { invalid: `${SETTING} cannot be resolved: ${error instanceof Error ? error.message : String(error)}` };
1552
+ }
1553
+ if (value === void 0) return { hours: Number(setting.default) };
1554
+ const error = settingValueError(setting, value);
1555
+ return error || typeof value !== "number" ? { invalid: `${fromEnv ? setting.env : SETTING} is invalid: expected ${error ?? "a number"}` } : { hours: value };
1556
+ }
1557
+ function deviceKeepReason(device, platform) {
1558
+ const { activity } = device;
1559
+ const what = `its ${device.kind === "ios" ? "simulator" : "emulator"} ${device.name}`;
1560
+ if (activity.state === "driven" && !activity.basis.every((basis) => AGENT_DEVICE_DRIVERS.has(basis))) return `${what} is driven by ${activity.driver?.tool ?? "an unknown tool"}`;
1561
+ if (activity.basis.includes("agent-device-claim") && !activity.recent?.["agent-action"]) return `${what} has a live agent-device claim whose session activity cannot be found`;
1562
+ const probed = activity.state === "driven" ? activity.unknown ?? [] : activity.state === "unknown" ? activity.basis : [];
1563
+ const unknown = platform === "win32" ? probed.filter((basis) => basis !== "driver-process") : probed;
1564
+ if (unknown.length) return `${what} has unknown activity (${unknown.join(", ")})`;
1565
+ return null;
1566
+ }
1567
+ function recentAt(activity, basis) {
1568
+ return Date.parse(activity.recent?.[basis] ?? "");
1569
+ }
1570
+ const missing = (error) => error instanceof Error && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR");
1571
+ const message = (error) => error instanceof Error ? error.message : String(error);
1572
+ function agentSessionsActedAt(root) {
1573
+ const dir = join(workspaceAgentDeviceDir(root), "sessions");
1574
+ let names;
1575
+ try {
1576
+ names = readdirSync(dir);
1577
+ } catch (error) {
1578
+ return missing(error) ? NaN : `its agent-device sessions cannot be read (${message(error)})`;
1579
+ }
1580
+ let latest = NaN;
1581
+ for (const name of names) {
1582
+ if (name === STIM_SESSION) continue;
1583
+ try {
1584
+ const at = statSync(join(dir, name, "events.ndjson")).mtimeMs;
1585
+ if (!(at <= latest)) latest = at;
1586
+ } catch (error) {
1587
+ if (!missing(error)) return `its agent-device session ${name} cannot be read (${message(error)})`;
1588
+ }
1589
+ }
1590
+ return latest;
1591
+ }
1592
+ function metroRequestAt(root, since, serving) {
1593
+ const file = join(workspaceLogsDir$1(root), "metro.ndjson");
1594
+ const rotated = rotatedLogPath(file);
1595
+ const records = [];
1596
+ for (const path of [rotated, file]) try {
1597
+ records.push(...parseNdjsonText(readFileSync(path, "utf8")));
1598
+ } catch (error) {
1599
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") continue;
1600
+ return `its Metro log cannot be read (${error instanceof Error ? error.message : String(error)})`;
1601
+ }
1602
+ let latest = NaN;
1603
+ let earliest = Infinity;
1604
+ for (const record of records) {
1605
+ if (typeof record.ts !== "number") continue;
1606
+ earliest = Math.min(earliest, record.ts);
1607
+ if (typeof record.event === "string" && METRO_REQUESTS.has(record.event) && !(record.ts <= latest)) latest = record.ts;
1608
+ }
1609
+ if (!Number.isFinite(latest) && earliest > since && existsSync(rotated)) return `its rotated Metro log does not reach back ${SETTING}`;
1610
+ if (serving && earliest === Infinity) return "its dev server runs but its Metro log is missing or empty";
1611
+ return latest;
1612
+ }
1613
+ function unreadableViewers() {
1614
+ const dir = deviceViewersDir();
1615
+ let names;
1616
+ try {
1617
+ names = readdirSync(dir).filter((name) => name.endsWith(".json"));
1618
+ } catch (error) {
1619
+ return missing(error) ? null : `the stim-server viewer records cannot be read (${message(error)})`;
1620
+ }
1621
+ for (const name of names) {
1622
+ const record = readJsonObject(join(dir, name));
1623
+ if (!record) return `the stim-server viewer record ${name} cannot be read`;
1624
+ if ((0, process_identity_exports.inspectProcessIdentity)(record) === "unknown") return `the stim-server behind the viewer record ${name} cannot be verified`;
1625
+ }
1626
+ return null;
1627
+ }
1628
+ async function keepReasons(root, devices, { now, platform, locked, operation }) {
1629
+ const checks = {
1630
+ devServer: false,
1631
+ nativeRun: !locked
1632
+ };
1633
+ const keep = operation ? await workspaceInUseAsync(root, checks, operation.signal, operation) : workspaceInUse(root, checks);
1634
+ if (workspaceBuildInProgress(root)) keep.push("a build is in progress");
1635
+ if (readRemoteSession(root)) keep.push("it has a remote device session");
1636
+ const holder = canonicalPath(root);
1637
+ for (const { lease, path } of listLeaseFiles()) if (!lease) keep.push(`the device lease ${path} cannot be read`);
1638
+ else if (!leaseIsExpired(lease, now) && canonicalPath(lease.holder) === holder) keep.push(`it holds the ${lease.platform} device lease on ${lease.deviceName ?? lease.id}`);
1639
+ const browser = webFacts(readWebRecord(root));
1640
+ if (browser && browser.status !== "stopped") keep.push(`its owned Chrome is ${browser.status}`);
1641
+ for (const device of devices) {
1642
+ const reason = deviceKeepReason(device, platform);
1643
+ if (reason) keep.push(reason);
1644
+ }
1645
+ const viewers = devices.length ? unreadableViewers() : null;
1646
+ if (viewers) keep.push(viewers);
1647
+ return keep;
1648
+ }
1649
+ async function idleStopEvidence(root, devices, { now, idleMs, platform = process.platform, locked = false, serving = false, operation }) {
1650
+ const state = readWorkspaceState$1(root);
1651
+ if (!state && existsSync(workspaceStateFile$1(root))) return {
1652
+ activity: [],
1653
+ keep: ["its state.json cannot be read"]
1654
+ };
1655
+ const sessions = agentSessionsActedAt(root);
1656
+ if (typeof sessions === "string") return {
1657
+ activity: [],
1658
+ keep: [sessions]
1659
+ };
1660
+ const build = state?.lastBuild;
1661
+ const builtAt = Date.parse(String(build?.startedAt ?? "")) + (typeof build?.durationMs === "number" ? build.durationMs : 0);
1662
+ const activity = [
1663
+ {
1664
+ basis: "stim command",
1665
+ at: Date.parse(String(state?.lastUsedAt ?? ""))
1666
+ },
1667
+ {
1668
+ basis: "native run",
1669
+ at: builtAt
1670
+ },
1671
+ {
1672
+ basis: "agent-device command",
1673
+ at: typeof sessions === "number" ? sessions : NaN
1674
+ },
1675
+ ...devices.flatMap(({ activity: device }) => [{
1676
+ basis: "agent-device command",
1677
+ at: recentAt(device, "agent-action")
1678
+ }, {
1679
+ basis: "live view",
1680
+ at: recentAt(device, "viewer")
1681
+ }])
1682
+ ].filter(({ at }) => Number.isFinite(at));
1683
+ if (activity.some(({ at }) => now - at < idleMs)) return {
1684
+ activity,
1685
+ keep: []
1686
+ };
1687
+ const keep = await keepReasons(root, devices, {
1688
+ now,
1689
+ platform,
1690
+ locked,
1691
+ operation
1692
+ });
1693
+ if (keep.length) return {
1694
+ activity,
1695
+ keep
1696
+ };
1697
+ const metro = metroRequestAt(root, now - idleMs, serving);
1698
+ if (typeof metro === "string") return {
1699
+ activity,
1700
+ keep: [metro]
1701
+ };
1702
+ if (Number.isFinite(metro)) activity.push({
1703
+ basis: "Metro request",
1704
+ at: metro
1705
+ });
1706
+ return {
1707
+ activity,
1708
+ keep
1709
+ };
1710
+ }
1711
+ function rethrowCleanup(error, signal) {
1712
+ if (error instanceof WorkspaceProcessCleanupError || error instanceof Error && "reportCleanup" in error) throw error;
1713
+ signal?.throwIfAborted();
1714
+ }
1715
+ async function unseenEmulatorError(unseen, operation) {
1716
+ try {
1717
+ const signal = operation?.signal;
1718
+ const adb = operation ? await listAdbDevicesAsync({
1719
+ timeoutMs: 5e3,
1720
+ signal,
1721
+ operation
1722
+ }) : listAdbDevices({ timeoutMs: 5e3 });
1723
+ for (const { serial } of [...adb.emulators, ...adb.unhealthy.filter((entry) => entry.kind === "emulator")]) {
1724
+ const name = operation ? await getAvdNameForSerialAsync(serial, {
1725
+ timeoutMs: 5e3,
1726
+ signal,
1727
+ operation
1728
+ }) : getAvdNameForSerial(serial, { timeoutMs: 5e3 });
1729
+ if (name === null) return `its emulators cannot be identified to prove them idle (${serial} gave no AVD name)`;
1730
+ if (unseen.includes(name)) return `its emulator ${name} runs as ${serial}, but its activity could not be read`;
1731
+ }
1732
+ return null;
1733
+ } catch (error) {
1734
+ rethrowCleanup(error, operation?.signal);
1735
+ return `its emulators cannot be listed to prove them idle (${error instanceof Error ? error.message : String(error)})`;
1736
+ }
1737
+ }
1738
+ function survivingServer(recorded) {
1739
+ const pid = recorded?.serverPid;
1740
+ if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0) return null;
1741
+ const token = recorded?.serverProcessToken;
1742
+ return (typeof token === "string" ? ["same", "unknown"].includes((0, process_identity_exports.inspectProcessIdentity)({
1743
+ pid,
1744
+ processToken: token
1745
+ })) : pidExists(pid)) ? `its dev server (pid ${pid}) may still run after its supervisor exited; stim stop ends it` : null;
1746
+ }
1747
+ async function candidate(path, env, devices, operation) {
1748
+ const signal = operation?.signal;
1749
+ const project = loadConfig$1()?.projects?.[path];
1750
+ if (!project) return null;
1751
+ const supervisor = resolveSupervisorTarget({
1752
+ state: readWorkspaceState$1(path)?.supervisor,
1753
+ record: project.supervisor,
1754
+ reservedPort: project.metroPort
1755
+ });
1756
+ const slots = projectDeviceSlots(project);
1757
+ const ownsIos = slots.some(({ platforms }) => platforms.ios?.owned === true);
1758
+ const owns = ownsIos || slots.some(({ platforms }) => platforms.android?.owned === true);
1759
+ if (!owns && (supervisor.status === "none" || supervisor.status === "stale")) return null;
1760
+ const setting = await idleStopHours(path, env, operation);
1761
+ if ("invalid" in setting) return { skip: setting.invalid };
1762
+ if (setting.hours === 0) return null;
1763
+ let booted = [];
1764
+ if (owns) try {
1765
+ booted = (await devices(ownsIos)).filter((device) => device.project === path);
1766
+ } catch (error) {
1767
+ rethrowCleanup(error, signal);
1768
+ return supervisor.status === "none" || supervisor.status === "stale" ? null : { skip: "its owned devices cannot be listed to prove them idle" };
1769
+ }
1770
+ if (supervisor.status === "unverified") return { skip: `its dev server supervisor cannot be verified: ${supervisor.reason ?? "unknown identity"}` };
1771
+ if (supervisor.status === "ours" && readWorkspaceState$1(path)?.supervisor?.mode === "command-child") return { skip: "its dev server runs metro.command, whose bundle and HMR requests Stim cannot see" };
1772
+ if (supervisor.status === "ours" && readWorkspaceState$1(path)?.supervisor?.hmrUpdates !== true) return { skip: "its dev server does not report HMR updates to Stim: an Expo SDK before 54, or a server an older Stim started, which stim stop and stim start restart" };
1773
+ const avdNames = slots.flatMap(({ platforms }) => platforms.android?.owned && platforms.android.avdName ? [platforms.android.avdName] : []);
1774
+ if (supervisor.status !== "ours" && booted.length === 0) return null;
1775
+ const orphan = supervisor.status === "stale" ? survivingServer(readWorkspaceState$1(path)?.supervisor) : null;
1776
+ if (orphan) return { skip: orphan };
1777
+ const seen = new Set(booted.flatMap((device) => device.kind === "android" ? [device.id] : []));
1778
+ const unseen = avdNames.filter((name) => !seen.has(name));
1779
+ const emulators = unseen.length ? await unseenEmulatorError(unseen, operation) : null;
1780
+ if (emulators) return { skip: emulators };
1781
+ return {
1782
+ path,
1783
+ hours: setting.hours,
1784
+ serving: supervisor.status === "ours",
1785
+ devices: booted
1786
+ };
1787
+ }
1788
+ /**
1789
+ * Plans stopping each workspace, other than `protectedRoot`, whose dev server runs or whose owned simulator or
1790
+ * emulator is booted, once it has had no real activity for its maintenance.idleStopHours.
1791
+ */
1792
+ async function planIdleStop({ protectedRoot, now, devices, env = process.env, platform = process.platform, operation }) {
1793
+ const plan = {
1794
+ actions: [],
1795
+ blocked: [],
1796
+ skips: []
1797
+ };
1798
+ if (deviceSweepIsScoped(env)) return plan;
1799
+ for (const path of Object.keys(loadConfig$1()?.projects ?? {})) {
1800
+ const root = canonicalPath(path);
1801
+ if (root === protectedRoot) continue;
1802
+ operation?.signal.throwIfAborted();
1803
+ const found = await candidate(path, env, devices, operation);
1804
+ if (!found) continue;
1805
+ if ("skip" in found) {
1806
+ plan.skips.push({
1807
+ target: root,
1808
+ reason: found.skip,
1809
+ workspace: root
1810
+ });
1811
+ continue;
1812
+ }
1813
+ const idleMs = found.hours * HOUR_MS;
1814
+ const decision = decideIdleStop(await idleStopEvidence(path, found.devices, {
1815
+ now,
1816
+ idleMs,
1817
+ platform,
1818
+ serving: found.serving,
1819
+ operation
1820
+ }), {
1821
+ idleMs,
1822
+ now
1823
+ });
1824
+ if (!decision.stop) {
1825
+ if (!decision.active) plan.skips.push({
1826
+ target: root,
1827
+ reason: decision.reason,
1828
+ workspace: root
1829
+ });
1830
+ continue;
1831
+ }
1832
+ plan.actions.push({
1833
+ kind: "would-stop-workspace",
1834
+ check: "idle",
1835
+ target: root,
1836
+ workspace: root,
1837
+ bytes: 0,
1838
+ reason: `no real activity for ${formatLongDuration(decision.idleMs)}, over ${SETTING} ${found.hours}`
1839
+ });
1840
+ }
1841
+ return plan;
1842
+ }
1843
+ const kept$1 = (detail) => ({
1844
+ status: "kept",
1845
+ bytes: 0,
1846
+ detail
1847
+ });
1848
+ /**
1849
+ * Stops a planned idle workspace the way `stim stop` does, under its native-run and metro-start locks, after
1850
+ * deciding again that it is still idle. Physical-device leases stay.
1851
+ */
1852
+ async function stopIdleWorkspace(action, { protectedRoot, devices, operation, env = operation?.env ?? process.env, by = "automatic maintenance" }) {
1853
+ const root = action.workspace ?? action.target;
1854
+ if (root === protectedRoot) return kept$1("the command that started this pass runs in it");
1855
+ if (deviceSweepIsScoped(env)) return kept$1("STIM_HOME scopes this config, and devices are machine-wide");
1856
+ const path = Object.keys(loadConfig$1()?.projects ?? {}).find((entry) => canonicalPath(entry) === root) ?? root;
1857
+ const stillIdle = async () => {
1858
+ const now = Date.now();
1859
+ const found = await candidate(path, env, devices(), operation);
1860
+ if (!found) return "nothing of it runs any more";
1861
+ if ("skip" in found) return found.skip;
1862
+ const idleMs = found.hours * HOUR_MS;
1863
+ const decision = decideIdleStop(await idleStopEvidence(path, found.devices, {
1864
+ now,
1865
+ idleMs,
1866
+ locked: true,
1867
+ serving: found.serving,
1868
+ operation
1869
+ }), {
1870
+ idleMs,
1871
+ now
1872
+ });
1873
+ return decision.stop ? null : decision.reason;
1874
+ };
1875
+ const signal = operation?.signal;
1876
+ const run = await withIdleWorkspace(path, async () => {
1877
+ try {
1878
+ return await withWorkspaceProcessLock(workspaceDir(path), "metro-start", async () => {
1879
+ const active = await stillIdle();
1880
+ if (active) return kept$1(active);
1881
+ const stopped = await runStop({
1882
+ root: path,
1883
+ operation,
1884
+ report: () => {},
1885
+ requester: { by: `${by} (${action.reason})` },
1886
+ releaseLeases: () => []
1887
+ });
1888
+ return stopped.ok ? {
1889
+ status: "done",
1890
+ bytes: 0,
1891
+ detail: stopped.summary
1892
+ } : {
1893
+ status: "failed",
1894
+ bytes: 0,
1895
+ detail: stopped.summary
1896
+ };
1897
+ }, {
1898
+ external: true,
1899
+ waitMs: 0,
1900
+ ownerPurpose: "idle stop",
1901
+ signal,
1902
+ declareSpawnsFor: operation
1903
+ });
1904
+ } catch (error) {
1905
+ if (error instanceof WorkspaceProcessCleanupError || error instanceof Error && "reportCleanup" in error) throw error;
1906
+ signal?.throwIfAborted();
1907
+ if (workspaceProcessLockError(error)) return kept$1("a stim start holds its metro-start lock");
1908
+ throw error;
1909
+ }
1910
+ }, {
1911
+ purpose: "automatic maintenance",
1912
+ devServer: false,
1913
+ operation
1914
+ });
1915
+ return run.ran ? run.value : kept$1(`in use: ${run.reasons.join("; ")}`);
1916
+ }
1917
+ //#endregion
1489
1918
  //#region src/workspace/running-from.ts
1490
1919
  const LIST_TIMEOUT_MS = 1e4;
1491
1920
  function parsePs(text) {
@@ -2518,7 +2947,7 @@ async function planPressureLevels(level, context) {
2518
2947
  function triggeringRoot(operation) {
2519
2948
  return canonicalPath(findProjectRoot$1(operation?.cwd ?? process.cwd()) ?? join(configDir(), "maintenance", "no-project"));
2520
2949
  }
2521
- async function plannedMaintenance(pressure, recordedSizes, settings, { devices = true, sweep = false, worktrees = false, idle = false, refresh = true, operation } = {}) {
2950
+ async function plannedMaintenance(pressure, recordedSizes, settings, { devices = true, sweep = false, worktrees = false, idle = false, refresh = true, background = false, operation } = {}) {
2522
2951
  const env = operation?.env ?? process.env;
2523
2952
  const resolved = resolveBudget({ env });
2524
2953
  if (resolved.error) throw new Error(resolved.error);
@@ -2594,7 +3023,7 @@ async function plannedMaintenance(pressure, recordedSizes, settings, { devices =
2594
3023
  recentlyUsed: (workspace) => recentUse(workspace, settings)
2595
3024
  });
2596
3025
  const merge = (extra) => {
2597
- for (const action of extra.actions) if (!result.actions.some((entry) => entry.kind === action.kind && entry.target === action.target && (entry.level === action.level || action.kind === "would-delete-devices"))) result.actions.push(action);
3026
+ for (const action of extra.actions) if (!result.actions.some((entry) => entry.kind === action.kind && entry.target === action.target && (entry.level === action.level || action.kind === "would-delete-devices") && (action.check !== "idle" || entry.check === "idle"))) result.actions.push(action);
2598
3027
  result.blocked.push(...extra.blocked);
2599
3028
  result.skips.push(...extra.skips);
2600
3029
  };
@@ -2621,11 +3050,27 @@ async function plannedMaintenance(pressure, recordedSizes, settings, { devices =
2621
3050
  merge(planned);
2622
3051
  result.worktreeSweep = planned.sweep;
2623
3052
  }
2624
- if (idle && devices) merge(planIdleShutdown({
2625
- protectedRoot: root,
2626
- now: Date.now(),
2627
- env
2628
- }));
3053
+ if (idle && devices) {
3054
+ merge(planIdleShutdown({
3055
+ protectedRoot: root,
3056
+ now: Date.now(),
3057
+ env
3058
+ }));
3059
+ const now = Date.now();
3060
+ try {
3061
+ merge(await planIdleStop({
3062
+ protectedRoot: background ? "" : root,
3063
+ now,
3064
+ env,
3065
+ operation,
3066
+ devices: operation ? cachedDeviceActivityAsync(now, env, operation.signal, operation) : cachedDeviceActivity(now)
3067
+ }));
3068
+ } catch (error) {
3069
+ if (error instanceof WorkspaceProcessCleanupError || error instanceof Error && "reportCleanup" in error) throw error;
3070
+ operation?.signal.throwIfAborted();
3071
+ result.blocked.push(`idle stop could not be planned: ${error instanceof Error ? error.message : String(error)}`);
3072
+ }
3073
+ }
2629
3074
  if (pressure) {
2630
3075
  const pressured = await planPressureLevels(diskPressureLevel(lowestFree(pressure.disk), budget, settings), {
2631
3076
  settings,
@@ -3114,7 +3559,12 @@ async function executeAction(action, context) {
3114
3559
  case "would-delete-parked-device": return deleteParkedDevice(action);
3115
3560
  case "would-delete-devices": return deleteDevices(action, context);
3116
3561
  case "would-shutdown-device": return action.check === "disk" ? kept("automatic maintenance does not stop devices or dev servers for disk") : shutDownIdleDevice(action);
3117
- case "would-stop-workspace": return action.level === 3 ? stopWorkspace(action, context) : kept("automatic maintenance stops dev servers only at disk pressure level 3");
3562
+ case "would-stop-workspace":
3563
+ if (action.level === 3) return stopWorkspace(action, context);
3564
+ return action.check === "idle" ? stopIdleWorkspace(action, {
3565
+ protectedRoot: context.background ? "" : context.protectedRoot,
3566
+ devices: () => cachedDeviceActivity(Date.now())
3567
+ }) : kept("automatic maintenance stops dev servers only at disk pressure level 3 or after maintenance.idleStopHours");
3118
3568
  case "would-clear-build-dirs":
3119
3569
  case "would-delete-dependencies": return clearArtifacts(action, context);
3120
3570
  case "would-stop-gradle-daemons": return stopGradleDaemonsAction(action);
@@ -3128,4 +3578,4 @@ function diskRecovered(budget) {
3128
3578
  return measureDisk().every((disk) => disk.freeMb >= floor);
3129
3579
  }
3130
3580
  //#endregion
3131
- export { worktreeRemovalReason as C, worktreePullRequestNote as S, ownedDeviceUsage as _, previewMaintenance as a, collectWorktreeSweep as b, deleteTempLeftovers as c, appRoots as d, artifactWorktrees as f, measureOwnedDevices as g, finishedWorkspaces as h, plannedMaintenance as i, tempLeftoverLines as l, collectArtifactsAsync as m, diskRecovered as n, triggeringRoot as o, clearArtifact as p, executeAction as r, collectTempLeftovers as s, deleteIdleWorkspaceDevices as t, tempLeftoverSection as u, maintenancePinned as v, removeWorktrees as x, candidateRootsAsync as y };
3581
+ export { candidateRootsAsync as C, worktreeRemovalReason as D, worktreePullRequestNote as E, maintenancePinned as S, removeWorktrees as T, finishedWorkspaces as _, previewMaintenance as a, cachedDeviceActivity as b, deleteTempLeftovers as c, appRoots as d, artifactWorktrees as f, stopIdleWorkspace as g, planIdleStop as h, plannedMaintenance as i, tempLeftoverLines as l, collectArtifactsAsync as m, diskRecovered as n, triggeringRoot as o, clearArtifact as p, executeAction as r, collectTempLeftovers as s, deleteIdleWorkspaceDevices as t, tempLeftoverSection as u, measureOwnedDevices as v, collectWorktreeSweep as w, cachedDeviceActivityAsync as x, ownedDeviceUsage as y };
@@ -1,9 +1,9 @@
1
1
  import { D as ownership_claim_exports, S as process_identity_exports, m as declareSpawnsOn, t as getExecutor, v as stopDeclaringSpawnsOn } from "./exec-aULwtNly.mjs";
2
- import { M as withDirLock$1, N as withDirLockAsync$1, c as withWorkspaceStateLock, d as writeWorkspaceState, k as workspaceLogsDir$1, l as withWorkspaceStateLockAsync, n as clearWorkspaceStateKeys, r as readWorkspaceState$1 } from "./workspace-state-kIZrdlSg.mjs";
3
- import { U as parseDeviceSlotKey, V as deviceSlotKey, d as getConfigDir, q as validateDeviceSlot } from "./config-Cn94Jc0g.mjs";
4
- import { a as formatElapsed, i as formatDuration, m as shortHash } from "./command-output-C8R1KIvi.mjs";
2
+ import { M as withDirLock$1, N as withDirLockAsync$1, c as withWorkspaceStateLock, d as writeWorkspaceState, k as workspaceLogsDir$1, l as withWorkspaceStateLockAsync, n as clearWorkspaceStateKeys, r as readWorkspaceState$1 } from "./workspace-state-DGwkPXwq.mjs";
3
+ import { U as parseDeviceSlotKey, V as deviceSlotKey, d as getConfigDir, q as validateDeviceSlot } from "./config-AbUjIBOQ.mjs";
4
+ import { a as formatElapsed, i as formatDuration, m as shortHash } from "./command-output-BBW8eWPL.mjs";
5
5
  import { n as runOperationFileQuiet } from "./operation-exec-Dp4FuIGR.mjs";
6
- import { C as resolveBuild } from "./build-cache-CZULpXVl.mjs";
6
+ import { C as resolveBuild } from "./build-cache-DlC3owap.mjs";
7
7
  import { randomBytes } from "node:crypto";
8
8
  import { closeSync, fstatSync, mkdirSync, openSync, readFileSync, readSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
9
9
  import { homedir } from "node:os";
@@ -814,7 +814,8 @@ function classifyActivity(evidence, now) {
814
814
  state: "driven",
815
815
  driver,
816
816
  ...lastActivityAt,
817
- basis: [...new Set(evidence.drivers.map((entry) => entry.basis))]
817
+ basis: [...new Set(evidence.drivers.map((entry) => entry.basis))],
818
+ ...evidence.unknown.length ? { unknown: [...new Set(evidence.unknown)] } : {}
818
819
  };
819
820
  }
820
821
  if (evidence.unknown.length) return {
@@ -1,8 +1,8 @@
1
1
  import { S as process_identity_exports } from "./exec-aULwtNly.mjs";
2
- import { P as formatBytes } from "./config-Cn94Jc0g.mjs";
3
- import { o as formatLongDuration } from "./command-output-C8R1KIvi.mjs";
4
- import { bt as measureDu, u as listWorkspaceDirs, xt as writeUsageCache, yt as canonical } from "./workspaces-CmOcceDN.mjs";
5
- import { c as parseAgentDeviceRecord, o as envDir, u as processMatches } from "./activity-DRMg4_mD.mjs";
2
+ import { P as formatBytes } from "./config-AbUjIBOQ.mjs";
3
+ import { o as formatLongDuration } from "./command-output-BBW8eWPL.mjs";
4
+ import { bt as measureDu, u as listWorkspaceDirs, xt as writeUsageCache, yt as canonical } from "./workspaces-7_RWoi6c.mjs";
5
+ import { c as parseAgentDeviceRecord, o as envDir, u as processMatches } from "./activity-BTaUf0tc.mjs";
6
6
  import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
7
7
  import { homedir } from "node:os";
8
8
  import { isAbsolute, join, relative, sep } from "node:path";
@@ -1,6 +1,6 @@
1
- import { i as formatDuration, l as phaseLine, m as shortHash } from "./command-output-C8R1KIvi.mjs";
2
- import { E as resolveVariant, T as resolveSystemImage, _ as findAapt, b as noDeviceDiagnostic, d as androidVariantSetting, f as apkDevClientFacts, g as dumpApkManifest, h as displayPath, i as NO_FINGERPRINT, p as apkPackage, r as NO_DEVICE, s as androidDevClientScheme, u as androidSystemImageSetting, x as parseXmltree, y as isReleaseVariant } from "./support-04IhBUTT.mjs";
3
- import { r as lastBuildRecord, t as androidFacts } from "./result-BIcXREyC.mjs";
4
- import { i as runAndroidOperation, n as registerAndroid, r as runAndroid, t as androidCommand } from "./android-CHmSU4B4.mjs";
5
- import { a as collectorLogFile, o as killPreviousCollector, s as startCollector } from "./launch-DoySmhjz.mjs";
1
+ import { i as formatDuration, l as phaseLine, m as shortHash } from "./command-output-BBW8eWPL.mjs";
2
+ import { E as resolveVariant, T as resolveSystemImage, _ as findAapt, b as noDeviceDiagnostic, d as androidVariantSetting, f as apkDevClientFacts, g as dumpApkManifest, h as displayPath, i as NO_FINGERPRINT, p as apkPackage, r as NO_DEVICE, s as androidDevClientScheme, u as androidSystemImageSetting, x as parseXmltree, y as isReleaseVariant } from "./support-BGqpYMV8.mjs";
3
+ import { r as lastBuildRecord, t as androidFacts } from "./result-W4HCum4A.mjs";
4
+ import { i as runAndroidOperation, n as registerAndroid, r as runAndroid, t as androidCommand } from "./android-BWqdDK6R.mjs";
5
+ import { a as collectorLogFile, o as killPreviousCollector, s as startCollector } from "./launch-DvJV1Mx4.mjs";
6
6
  export { NO_DEVICE, NO_FINGERPRINT, androidDevClientScheme, androidFacts, androidSystemImageSetting, androidVariantSetting, apkDevClientFacts, apkPackage, collectorLogFile, androidCommand as default, displayPath, dumpApkManifest, findAapt, formatDuration, isReleaseVariant, killPreviousCollector, lastBuildRecord, noDeviceDiagnostic, parseXmltree, phaseLine, registerAndroid, resolveSystemImage, resolveVariant, runAndroid, runAndroidOperation, shortHash, startCollector };
@@ -1,5 +1,5 @@
1
1
  import { t as getExecutor } from "./exec-aULwtNly.mjs";
2
- import { u as androidToolCwd } from "./android-CsPs-7Mg.mjs";
2
+ import { u as androidToolCwd } from "./android-DdKG6lyD.mjs";
3
3
  //#region src/collector/android.ts
4
4
  const LEVEL_BY_LETTER = {
5
5
  V: "debug",