stim 1.26.0 → 1.28.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 (172) hide show
  1. package/dist/{acquire-artifact-DVsvSwf7.mjs → acquire-artifact-CKByBnTD.mjs} +2 -2
  2. package/dist/{act-C-BqJEpu.mjs → act-8iswyfRa.mjs} +319 -126
  3. package/dist/{activity-Lnb_EoRY.mjs → activity-DRMg4_mD.mjs} +6 -6
  4. package/dist/{agent-device-usage-output-DbXy5tg8.mjs → agent-device-usage-output-tE90fDdo.mjs} +5 -5
  5. package/dist/{android-CiNQw3kq.mjs → android-CHmSU4B4.mjs} +101 -45
  6. package/dist/{android-DTAcRwBC.mjs → android-CsPs-7Mg.mjs} +23 -18
  7. package/dist/{android-B5P81yI-.mjs → android-D577ApKG.mjs} +37 -15
  8. package/dist/{android-CSbLRD53.mjs → android-YWCbmkVK.mjs} +5 -5
  9. package/dist/{android-cas-BTVhZ0Qs.mjs → android-cas-_aDifWW0.mjs} +4 -4
  10. package/dist/android-cas-compiler.mjs +1 -1
  11. package/dist/api.d.mts +14 -4
  12. package/dist/api.mjs +39 -29
  13. package/dist/{apk-signed-tail-B46U9hXc.mjs → apk-signed-tail-CQbwM0qO.mjs} +2 -2
  14. package/dist/{artifact-CsAP6AGK.mjs → artifact-DYtHjxsn.mjs} +19 -24
  15. package/dist/{artifact-BxREhAaV.mjs → artifact-bFkfD3vf.mjs} +18 -23
  16. package/dist/{artifact-platform-CMRc0M_r.mjs → artifact-platform-BO2Uvk_A.mjs} +1 -1
  17. package/dist/auto-placement-BVqs8AJi.mjs +350 -0
  18. package/dist/{browser-web-rYPyqhTf.mjs → browser-web-BUaLcafY.mjs} +16 -16
  19. package/dist/{build-DPhApsjV.mjs → build-cQURy_KW.mjs} +35 -31
  20. package/dist/{build-cache-DrXNCy3T.mjs → build-cache-CZULpXVl.mjs} +43 -21
  21. package/dist/{build-plan-DrMMdayj.mjs → build-plan-CxoYfxsc.mjs} +7 -6
  22. package/dist/{cache-manifest-BEZ9Bpl1.mjs → cache-manifest-DEjqGGat.mjs} +1 -1
  23. package/dist/cache-manifest.mjs +1 -1
  24. package/dist/{budget-k2mZGvvk.mjs → ccache-Bm_A1xX9.mjs} +562 -1604
  25. package/dist/{cdp-BG3FJCCx.mjs → cdp-C3svvXiE.mjs} +1 -1
  26. package/dist/{chrome-BvV8ssI3.mjs → chrome-DyDWurWG.mjs} +1 -1
  27. package/dist/{cli-BBKFGOFl.mjs → cli-CpbaLDa7.mjs} +24 -23
  28. package/dist/cli.mjs +1 -1
  29. package/dist/{client-D9CaRks-.mjs → client-YxCer6ab.mjs} +84 -65
  30. package/dist/collector-run.d.mts +10 -9
  31. package/dist/collector-run.mjs +42 -27
  32. package/dist/{command-output-BF5AR2dL.mjs → command-output-C8R1KIvi.mjs} +1 -1
  33. package/dist/{config-BktkY_zC.mjs → config-Cn94Jc0g.mjs} +23 -4
  34. package/dist/{dependencies-Dw6wfQdv.mjs → dependencies-DKeMzwXY.mjs} +108 -60
  35. package/dist/{dependency-state-DFB41vfr.mjs → dependency-state-CojA2o0t.mjs} +14 -7
  36. package/dist/{detached-entry-KmieOKgN.mjs → detached-entry-BPlSaCwC.mjs} +1 -1
  37. package/dist/{dev-client-DXR_tPVY.mjs → dev-client-D-BW5uiq.mjs} +2 -2
  38. package/dist/{device-BK-zwyd_.mjs → device-CgXfbcz5.mjs} +25 -24
  39. package/dist/{device-capacity-DCSvUF0l.mjs → device-capacity-Cd8lMSn3.mjs} +175 -22
  40. package/dist/{device-BcYeHSFu.mjs → device-eeK95Lir.mjs} +16 -16
  41. package/dist/device-host-worker.mjs +22 -20
  42. package/dist/{device-ios-BLGfXQeK.mjs → device-ios-Cc9ulihb.mjs} +251 -23
  43. package/dist/{device-lease-run-BmvIl7bI.mjs → device-lease-run-Mtzp4THX.mjs} +5 -5
  44. package/dist/{device-pool-Dluix4H3.mjs → device-pool-eRz6DtCg.mjs} +3 -3
  45. package/dist/{doctor-Bhg1ii70.mjs → doctor-Bz_ouYAW.mjs} +28 -16
  46. package/dist/{doctor-Cp-BhzTS.mjs → doctor-C2QyhejJ.mjs} +1 -1
  47. package/dist/{doctor-fingerprint-D3a04Mhu.mjs → doctor-fingerprint-o7ZI9YLn.mjs} +1 -1
  48. package/dist/doctor-fingerprint.mjs +1 -1
  49. package/dist/{doctor-watchman-BStK05nt.mjs → doctor-watchman-BTW82PDC.mjs} +5 -5
  50. package/dist/{doctor-OnwP94ne.mjs → doctor-xf7ht-5n.mjs} +76 -32
  51. package/dist/{eas-build-Dsj93iM9.mjs → eas-build-Dm6q68QY.mjs} +4 -4
  52. package/dist/eas-fallback-DajNpxf-.mjs +87 -0
  53. package/dist/{eas-session-ledger-B5vygTiD.mjs → eas-session-ledger-CBFo_Uo1.mjs} +37 -8
  54. package/dist/{error-diagnostics-BOOm9Db7.mjs → error-diagnostics-D1QfyJzQ.mjs} +51 -25
  55. package/dist/{exec-VFRpu1iA.mjs → exec-aULwtNly.mjs} +57 -6
  56. package/dist/{gc-ldgr-hXU.mjs → gc-DM5oMm-J.mjs} +161 -45
  57. package/dist/{gc-C_79zvz4.mjs → gc-PLFwb0k4.mjs} +2 -2
  58. package/dist/{guide-D7K0X6KG.mjs → guide-C4I46XAk.mjs} +674 -213
  59. package/dist/{hosted-android-Bk54ej6n.mjs → hosted-android-DlaKLbW0.mjs} +6 -7
  60. package/dist/{hosted-client-fHO8yqzc.mjs → hosted-client-DDRm9hPJ.mjs} +51 -20
  61. package/dist/{hosted-logs-BfbmDM4M.mjs → hosted-logs-bSebOKbz.mjs} +8 -8
  62. package/dist/{hosted-logs-sync-B0pvmOk-.mjs → hosted-logs-sync-B0c53W_c.mjs} +5 -5
  63. package/dist/{hosted-macos-CPl0OZ9h.mjs → hosted-macos-B7-8Pv4H.mjs} +6 -6
  64. package/dist/{hosted-native-CpDB3-PO.mjs → hosted-native-CDe33CSr.mjs} +22 -18
  65. package/dist/{idle-shutdown-FfxL1KdG.mjs → idle-shutdown-CQ0QwU9J.mjs} +7 -8
  66. package/dist/{ios-WxpDJ5T1.mjs → ios-B1DYuOjh.mjs} +27 -11
  67. package/dist/{ios-B7-lToDu.mjs → ios-B1HvafEG.mjs} +8 -8
  68. package/dist/{ios-ENC4sRPA.mjs → ios-CsRVik64.mjs} +7 -1
  69. package/dist/{ios-device-CsyryvpT.mjs → ios-device-B4elVFKi.mjs} +3 -3
  70. package/dist/{ios-device-B6HK951C.mjs → ios-device-CiBA-er9.mjs} +1 -1
  71. package/dist/{ios-project-Ty5pHl9O.mjs → ios-project-CR4mZ7CH.mjs} +22 -9
  72. package/dist/{ios-signing-4c_qXvQm.mjs → ios-signing-BJzvDPfU.mjs} +2 -2
  73. package/dist/{ios-state-Cw7-WQf2.mjs → ios-state-BPHqMDdZ.mjs} +1 -1
  74. package/dist/{ios-ftxdQbJS.mjs → ios-tg4tp3q1.mjs} +94 -37
  75. package/dist/{js-swap-BMhXYVDS.mjs → js-swap-CVpSW1fn.mjs} +8 -7
  76. package/dist/{launch-DOp8RjDz.mjs → launch-DoySmhjz.mjs} +42 -38
  77. package/dist/lightened-DFAJgfIk.mjs +171 -0
  78. package/dist/{logs-s1dRIG1E.mjs → logs-BGsW7iPe.mjs} +46 -21
  79. package/dist/{logs-dQDki6Tt.mjs → logs-DI5ILkYB.mjs} +7 -7
  80. package/dist/{macos-DBBPNnIA.mjs → macos-Bo9st9Di.mjs} +73 -38
  81. package/dist/{macos-B0VEV3Gf.mjs → macos-C12RMJQF.mjs} +1 -1
  82. package/dist/macos-run.mjs +1 -1
  83. package/dist/maintenance-run.mjs +7 -7
  84. package/dist/{metro-CjC10t6E.mjs → metro-BnhXV2Mj.mjs} +4 -4
  85. package/dist/{metro-gateway-B6_PgD0L.mjs → metro-request-DXyKjVDF.mjs} +26 -47
  86. package/dist/{named-ports-BG0j1X4g.mjs → named-ports-Phoi2POD.mjs} +3 -3
  87. package/dist/{native-android-CL2NGiik.mjs → native-android-vxgtLD7w.mjs} +17 -18
  88. package/dist/{native-gradle-artifact-inputs-btSUulme.mjs → native-gradle-artifact-inputs-BN96IVw8.mjs} +5 -5
  89. package/dist/{native-runtime-Dq3RBcON.mjs → native-runtime-DG4Rodzj.mjs} +9 -8
  90. package/dist/{native-source-D-4Dh4JU.mjs → native-source-D1AzWiuP.mjs} +1 -1
  91. package/dist/{native-xcode-inputs-BW7H8JGu.mjs → native-xcode-inputs-BKarCoKg.mjs} +3 -3
  92. package/dist/{native-xcode-ios-CBXJAxfC.mjs → native-xcode-ios-8A-efGa5.mjs} +17 -17
  93. package/dist/{ndjson-CgE_o3Dl.mjs → ndjson-Dy2W4vRa.mjs} +2 -2
  94. package/dist/offload-worker.mjs +23 -24
  95. package/dist/{operation-exec-CUZifp6Z.mjs → operation-exec-Dp4FuIGR.mjs} +1 -1
  96. package/dist/{ownership-BjYvMNX_.mjs → ownership-DJCq48IJ.mjs} +3 -3
  97. package/dist/{page-CzEA_-5_.mjs → page-CQJlcj3x.mjs} +2 -2
  98. package/dist/{phone-reverses-lmj6kcPm.mjs → phone-reverses-BzTa8g1F.mjs} +18 -4
  99. package/dist/{placement-log-DIFPQAXp.mjs → placement-log-Cvhta5ib.mjs} +3 -3
  100. package/dist/{plan-placement-IiiSQhtz.mjs → plan-placement-WkZdccuN.mjs} +36 -10
  101. package/dist/{ports-C2_w1Z0Z.mjs → ports-UV4mJgyC.mjs} +3 -3
  102. package/dist/{prebuild-gmouvmJZ.mjs → prebuild-k8d_tqOq.mjs} +16 -17
  103. package/dist/{project-BUu6Is4k.mjs → project-Sui52ywT.mjs} +17 -17
  104. package/dist/{pull-request-CbNlkFok.mjs → pull-request-BzLmzAIQ.mjs} +2 -2
  105. package/dist/pull-requests.mjs +1 -1
  106. package/dist/{react-native-android-Cgg99REN.mjs → react-native-android-DANlGGQf.mjs} +32 -26
  107. package/dist/{react-native-build-oNwqF5Wb.mjs → react-native-build-jUHnIfZ9.mjs} +9 -9
  108. package/dist/{react-native-doctor-BhA3KUte.mjs → react-native-doctor-DCWRjS7K.mjs} +6 -6
  109. package/dist/{react-native-ios-B-nf4rjN.mjs → react-native-ios-BwMsFMXi.mjs} +23 -17
  110. package/dist/{recordings-BldzVKq0.mjs → recordings-iy0mkm7b.mjs} +1 -1
  111. package/dist/{registration-CN6nlhcD.mjs → registration-BwCe64PH.mjs} +1 -1
  112. package/dist/{reload-C9OduiNc.mjs → reload-Bj7QyuDy.mjs} +1 -1
  113. package/dist/{reload-CCR7KC_l.mjs → reload-c_uEyc_X.mjs} +16 -16
  114. package/dist/{remote-C8S7FlPF.mjs → remote-DFlpAqdV.mjs} +23 -10
  115. package/dist/{result-7F5hEwQJ.mjs → result-BIcXREyC.mjs} +32 -21
  116. package/dist/{run-BqB0v3pt.mjs → run-y2HwyNvg.mjs} +3 -3
  117. package/dist/{selection-BNPBZ6T5.mjs → selection-CXgM_VK_.mjs} +37 -29
  118. package/dist/{server-bare-A75rDIdD.mjs → server-bare-2N4vSRZG.mjs} +2 -2
  119. package/dist/{server-command-CssKFOD7.mjs → server-command-qneHPcRI.mjs} +6 -6
  120. package/dist/{server-expo-eQfOdydb.mjs → server-expo-Ccm9zB0o.mjs} +4 -4
  121. package/dist/server-expo-WulkyAwT.mjs +2 -0
  122. package/dist/{settings-CosW5X-S.mjs → settings-CY5JZlNN.mjs} +375 -62
  123. package/dist/{settings-OaGQF-ri.mjs → settings-Cfd_Q6ys.mjs} +24 -3
  124. package/dist/settings-payload-DwQiEMCn.mjs +257 -0
  125. package/dist/settings-write-CJpjkeAd.mjs +185 -0
  126. package/dist/settings.schema.json +51 -33
  127. package/dist/{simslim-Bax1qcxu.mjs → simslim-DCQwzoPy.mjs} +2 -2
  128. package/dist/{slot-launch-CYCEhfBY.mjs → slot-launch-BpmanOnk.mjs} +6 -6
  129. package/dist/{stage-jKQ7CP_B.mjs → stage-CmugrSG5.mjs} +2 -2
  130. package/dist/{start-C_wefNKB.mjs → start-CHH3zgIU.mjs} +1 -1
  131. package/dist/{start-_BdJ32cZ.mjs → start-DYLk6GXn.mjs} +27 -22
  132. package/dist/{state-C1i2nMOP.mjs → state-B2QJbDwz.mjs} +4 -4
  133. package/dist/{state-C4OdpOTF.mjs → state-BXC1Lfxw.mjs} +2 -2
  134. package/dist/{stats-tEpEEH-u.mjs → stats-DxKqhcGp.mjs} +5 -5
  135. package/dist/{stats-report-n2U5plhk.mjs → stats-report-B86Cv7Uc.mjs} +1 -1
  136. package/dist/{status-CRXz-Gtj.mjs → status-7ULDEJ1e.mjs} +1 -1
  137. package/dist/{status-DQrqodLj.mjs → status-BHCkOZga.mjs} +51 -28
  138. package/dist/{status-B9M7x4CZ.mjs → status-D_Ljc89p.mjs} +1 -1
  139. package/dist/{status-watch-B5wDIH_o.mjs → status-watch-BmsPqYSU.mjs} +18 -7
  140. package/dist/status-worker.mjs +1 -1
  141. package/dist/{stim-desktop-6Dqp-gjX.mjs → stim-desktop-CbWo21kb.mjs} +2 -2
  142. package/dist/{stop-DRqQVqDJ.mjs → stop-D-dGE9gh.mjs} +21 -20
  143. package/dist/{stop-ySNQHXMG.mjs → stop-DQvE-P76.mjs} +10 -6
  144. package/dist/{stop-CmdNhGlC.mjs → stop-b2pouxAO.mjs} +2 -2
  145. package/dist/supervisor-run.mjs +237 -63
  146. package/dist/{support-BGSCBoib.mjs → support-04IhBUTT.mjs} +6 -6
  147. package/dist/{support-CVOCp0Ft.mjs → support-BtlJKjyQ.mjs} +12 -11
  148. package/dist/{swiftpm-artifact-inputs-CfUzt_0Z.mjs → swiftpm-artifact-inputs-Cj2UpmPN.mjs} +4 -4
  149. package/dist/{swiftpm-macos-x6HPpl_2.mjs → swiftpm-macos-BhF2stxY.mjs} +11 -12
  150. package/dist/{toolchain-J4J8MrAr.mjs → toolchain-mLA_km-b.mjs} +321 -15
  151. package/dist/{toolchain-DoavrvIA.mjs → toolchain-qktiV8xc.mjs} +1 -1
  152. package/dist/{trigger-KVaWWNyn.mjs → trigger-DMou-X7j.mjs} +2 -2
  153. package/dist/trigger-qp2ji5wE.mjs +2 -0
  154. package/dist/{warm-progress-DvhTX_eu.mjs → warm-progress-B0Vpuvpl.mjs} +731 -727
  155. package/dist/{web-Bihbf7-u.mjs → web-BYmK0TjL.mjs} +9 -9
  156. package/dist/web-CrVLVQZY.mjs +2 -0
  157. package/dist/web-run.mjs +9 -9
  158. package/dist/windows-report.mjs +1 -1
  159. package/dist/{workspace-state-pUg3_RRA.mjs → workspace-state-kIZrdlSg.mjs} +3 -3
  160. package/dist/{workspaces-g2lhdBXF.mjs → workspaces-CmOcceDN.mjs} +1879 -74
  161. package/dist/{worktree-BKuklFh_.mjs → worktree-2dk5edza.mjs} +933 -534
  162. package/dist/worktree-nliAoHxB.mjs +2 -0
  163. package/package.json +7 -7
  164. package/dist/ccache-Bx5TbxwG.mjs +0 -143
  165. package/dist/eas-fallback-FwkeqqGi.mjs +0 -381
  166. package/dist/hosted-ios-D55QhYaD.mjs +0 -15
  167. package/dist/native-run-BfIDFKLH.mjs +0 -275
  168. package/dist/server-expo-wvcj5tiZ.mjs +0 -2
  169. package/dist/settings-write-QjYfSrjF.mjs +0 -360
  170. package/dist/trigger-DzJu208Q.mjs +0 -2
  171. package/dist/web-BFLueSh-.mjs +0 -2
  172. package/dist/worktree-Bm4cb-nU.mjs +0 -2
@@ -1,39 +1,77 @@
1
- import { D as ownership_claim_exports, E as debugLog, S as process_identity_exports, b as operationState, c as throwIfCancelled, d as workspaceProcessLockError, l as WorkspaceProcessCleanupError, n as isTimeoutError, o as cancellableSleep, s as cancelledError, t as getExecutor, u as withWorkspaceProcessLock, y as isTermination } from "./exec-VFRpu1iA.mjs";
2
- import { D as workspaceId$1, E as workspaceDir, d as writeWorkspaceState, j as workspaceStateFile$1, k as workspaceLogsDir$1, l as withWorkspaceStateLockAsync, o as updateWorkspaceState, r as readWorkspaceState$1, t as clearWorkspaceStateKey, y as sharedBuildCache } from "./workspace-state-pUg3_RRA.mjs";
3
- import { D as withConfigLock$1, F as isOnMountedVolume, G as projectDeviceSlots, H as deviceSlotPlatforms, L as measuredDirectorySizeAsync, M as directorySize, N as directorySizeAsync, O as withConfigLockAsync$1, P as formatBytes, R as volumeRootFor, _ as loadConfig$1, a as clearDevice, d as getConfigDir, o as clearSupervisor, p as getProject } from "./config-BktkY_zC.mjs";
1
+ import { D as ownership_claim_exports, E as debugLog, S as process_identity_exports, b as operationState, c as throwIfCancelled, d as workspaceProcessLockError, l as WorkspaceProcessCleanupError, n as isTimeoutError, o as cancellableSleep, s as cancelledError, t as getExecutor, u as withWorkspaceProcessLock, y as isTermination } from "./exec-aULwtNly.mjs";
2
+ import { D as workspaceId$1, E as workspaceDir, M as withDirLock$1, b as sharedCcache$1, d as writeWorkspaceState, j as workspaceStateFile$1, k as workspaceLogsDir$1, l as withWorkspaceStateLockAsync, r as readWorkspaceState$1, t as clearWorkspaceStateKey, y as sharedBuildCache } from "./workspace-state-kIZrdlSg.mjs";
3
+ import { F as isOnMountedVolume, G as projectDeviceSlots, H as deviceSlotPlatforms, L as measuredDirectorySizeAsync, M as directorySize, N as directorySizeAsync, O as withConfigLockAsync$1, P as formatBytes, R as volumeRootFor, _ as loadConfig$1, a as clearDevice, d as getConfigDir, o as clearSupervisor, p as getProject } from "./config-Cn94Jc0g.mjs";
4
4
  import { n as stripAnsi } from "./process-output-Fpl8PYi8.mjs";
5
- import { a as formatElapsed, l as phaseLine, o as formatLongDuration, s as isAppLaunchError, u as plural } from "./command-output-BF5AR2dL.mjs";
6
- import { a as killOnAbort, n as runOperationFileQuiet, r as childEnvironment, t as runOperationFile } from "./operation-exec-CUZifp6Z.mjs";
7
- import { Y as resolveProjectSettingsAsync, d as androidLayoutSetting, ut as planMetroReach } from "./settings-CosW5X-S.mjs";
8
- import { C as resolvePackageJson, M as readMetroTunnel, N as readPidFile, P as readRemoteSession, i as findProjectRoot$1, j as clearWorkspaceSupervisor, k as clearRemoteSession, s as ownedDeviceLabel, v as detectIsExpo } from "./project-BUu6Is4k.mjs";
9
- import { F as listAvdsAsync, G as ownedAvdSerialResolverAsync, I as listInstalledSystemImages, J as parseAvdSystemImage, V as ownedAvdDirectory, W as ownedAvdSerialResolver, d as androidToolPath, dt as isStimOwnedSim, m as avdIniPaths, mt as dropParked, o as androidHome, q as parseAvdDeviceProfile, ut as isStimOwnedAvd, v as avdStorageRoots, yt as readParked } from "./android-DTAcRwBC.mjs";
10
- import { a as listeningPidsAsync, c as pidExists, d as resolveProjectMetro, h as isMetroRunning, r as killMetroTreeAsync, u as processCwds } from "./metro-CjC10t6E.mjs";
11
- import { r as createNdjsonWriter, s as readNdjsonGenerations$1 } from "./ndjson-CgE_o3Dl.mjs";
12
- import { C as environmentState, a as removeEasSessionClaim, d as parseWatchRoots, f as parseWatchmanClients, i as recordEasSessionClaim, l as parseSubscriberNames, n as easMachineStateRoot$1, o as removeEasSessionClaimAsync, p as parseWatchmanPid, r as readEasSessionLedger$1, s as compareStimVersions, t as assertEasMachineRootWritable, u as parseTriggerCount, w as formatSpace, y as capacity } from "./eas-session-ledger-B5vygTiD.mjs";
13
- import { i as registeredCaches } from "./cache-manifest-BEZ9Bpl1.mjs";
5
+ import { a as formatElapsed, l as phaseLine, o as formatLongDuration, s as isAppLaunchError, u as plural } from "./command-output-C8R1KIvi.mjs";
6
+ import { a as killOnAbort, n as runOperationFileQuiet, r as childEnvironment, t as runOperationFile } from "./operation-exec-Dp4FuIGR.mjs";
7
+ import { Z as resolveProjectSettingsAsync, d as androidLayoutSetting, ft as planMetroReach, m as defaultAndroidLayout } from "./settings-CY5JZlNN.mjs";
8
+ import { C as resolvePackageJson, M as readMetroTunnel, N as readPidFile, P as readRemoteSession, i as findProjectRoot$1, j as clearWorkspaceSupervisor, k as clearRemoteSession, s as ownedDeviceLabel, v as detectIsExpo } from "./project-Sui52ywT.mjs";
9
+ import { F as listAvdsAsync, G as ownedAvdSerialResolverAsync, I as listInstalledSystemImages, J as parseAvdSystemImage, V as ownedAvdDirectory, d as androidToolPath, dt as isStimOwnedSim, m as avdIniPaths, mt as dropParked, o as androidHome, q as parseAvdDeviceProfile, ut as isStimOwnedAvd, v as avdStorageRoots, yt as readParked } from "./android-CsPs-7Mg.mjs";
10
+ import { a as listeningPidsAsync, c as pidExists, d as resolveProjectMetro, h as isMetroRunning, r as killMetroTreeAsync, u as processCwds } from "./metro-BnhXV2Mj.mjs";
11
+ import { c as readNdjsonGenerations$1, i as createNdjsonWriter, r as createNdjsonTail } from "./ndjson-Dy2W4vRa.mjs";
12
+ import { T as formatSpace, a as removeEasSessionClaim, d as parseWatchRoots, f as parseWatchmanClients, i as recordEasSessionClaim, l as parseSubscriberNames, n as easMachineStateRoot$1, o as removeEasSessionClaimAsync, p as parseWatchmanPid, r as readEasSessionLedger$1, s as compareStimVersions, t as assertEasMachineRootWritable, u as parseTriggerCount, w as environmentState, y as capacity } from "./eas-session-ledger-CBFo_Uo1.mjs";
13
+ import { i as registeredCaches, r as register } from "./cache-manifest-DEjqGGat.mjs";
14
+ import { At as mapLimited, B as workspaceBuildInProgress, Dt as createGcResults, Et as GC_OUTPUT, F as collectOwnedDeviceActivityAsync, L as idleShutdownCandidates, Mt as sizeConcurrency, N as collectIdleDevicesAsync, _t as teardownParkedIosSim, bt as measureDu, dt as eraseParkedIosSim, gt as teardownParkedAvd, ht as teardownOwnedIosSim, i as clearWorkspaceOutputs, l as keptByLock, o as collectWorkspaceOutputs, pt as teardownOwnedAvd, ut as eraseParkedAvd, v as withIdleWorkspace, vt as probePublicHttp, wt as listBuildSlots, x as resolveSupervisorTarget, xt as writeUsageCache, yt as canonical$1, z as shutDownIdleDevices } from "./workspaces-CmOcceDN.mjs";
14
15
  import { t as canonicalPath } from "./paths-BsJMoPP2.mjs";
15
- import { c as nativeRunWaitNotice, d as runCancellationSignal, m as claimForEmptying } from "./native-run-BfIDFKLH.mjs";
16
- import { A as teardownOwnedIosSim, B as GC_OUTPUT, E as eraseParkedIosSim, F as measureDu, I as writeUsageCache, M as teardownParkedIosSim, N as probePublicHttp, O as teardownOwnedAvd, P as canonical, R as listBuildSlots, T as eraseParkedAvd, V as createGcResults, a as collectWorkspaceOutputs, g as resolveSupervisorTarget, j as teardownParkedAvd, p as withIdleWorkspace, r as clearWorkspaceOutputs } from "./workspaces-g2lhdBXF.mjs";
17
- import { N as listBuildLocks, b as leaseIsExpired, g as workspaceActivity, i as createActivityReaderAsync, o as envDir, p as readHostProcessTableAsync, r as createActivityReader, s as idleForMs, x as listLeaseFiles } from "./activity-Lnb_EoRY.mjs";
18
- import { c as expoConfigContents } from "./build-cache-DrXNCy3T.mjs";
19
- import { A as uninstallIosApp, O as sanitizeDeviceLabel, U as deviceHoldsApk, W as deviceHoldsBundle, b as parseSimctlList, f as listAllIosSimsAsync, g as listUserApps, p as listIosDeviceTypesAsync, u as iosSimulatorFailureAdvice, y as parseRuntimeVersion } from "./ios-WxpDJ5T1.mjs";
16
+ import { lt as claimForEmptying, ot as runCancellationSignal, rt as nativeRunWaitNotice } from "./toolchain-mLA_km-b.mjs";
17
+ import { N as listBuildLocks, b as leaseIsExpired, g as workspaceActivity, o as envDir, p as readHostProcessTableAsync, x as listLeaseFiles } from "./activity-DRMg4_mD.mjs";
18
+ import { l as expoConfigContents } from "./build-cache-CZULpXVl.mjs";
19
+ import { G as deviceHoldsBundle, O as sanitizeDeviceLabel, W as deviceHoldsApk, b as parseSimctlList, f as listAllIosSimsAsync, g as listUserApps, j as uninstallIosApp, p as listIosDeviceTypesAsync, u as iosSimulatorFailureAdvice, y as parseRuntimeVersion } from "./ios-B1DYuOjh.mjs";
20
20
  import { n as supervisorError } from "./errors-DHm_U5gv.mjs";
21
- import { c as findBinUpward, n as expoBinFromPackage, r as expoBinPath, s as expoTelemetryEnv } from "./server-expo-eQfOdydb.mjs";
22
- import { t as DEV_MENU_LAUNCH_ARGS } from "./ios-device-B6HK951C.mjs";
21
+ import { c as findBinUpward, n as expoBinFromPackage, r as expoBinPath, s as expoTelemetryEnv } from "./server-expo-Ccm9zB0o.mjs";
22
+ import { t as DEV_MENU_LAUNCH_ARGS } from "./ios-device-CiBA-er9.mjs";
23
23
  import { t as resolveMaintenanceSettings } from "./settings-Djl-mpjX.mjs";
24
24
  import { createRequire } from "node:module";
25
- import { randomUUID } from "node:crypto";
25
+ import { createHash, randomUUID } from "node:crypto";
26
26
  import { closeSync, existsSync, lstatSync, mkdirSync, openSync, readFileSync, readSync, readdirSync, realpathSync, renameSync, rmSync, statSync, statfsSync, unlinkSync, writeFileSync } from "node:fs";
27
27
  import { homedir, platform, totalmem } from "node:os";
28
28
  import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
29
29
  import { LOG_ROTATE_BYTES, METRO_NAMED_CACHE_LAYOUT, ambientContext, cacheDirRefusal, compiledHelper, configDir, ensurePrivateDir, rotatedLogPath, withAmbientContext, withDirLock } from "@stim-cli/core";
30
- import { BUILD_HISTORY_KEY, BUILD_HISTORY_LIMIT, BUILD_PHASES, DEV_SERVER_STOP_REQUEST_KEY, LAST_BUILD_KEYS, MACHINE_OWNER_KINDS, MAINTENANCE_LOG_NAME, STATS_VERSION, buildWorkerRoot, decodeStats, gcLogFile, isJsonObject, loadConfig, machineCacheUsageFile, maintenanceNdjsonFile, parseMissReason, readDevServerStop, readDevServerStopRequest, readJsonObject, readMachineCacheUsage, readRunEstimates, readStats as readStats$1, runId, settingDefinition, settingValueError, statsFile, statsProjectKey as statsProjectKey$1, trimCapacityRefusals, trimCapacityWaits, trimSamples, wholePhases, workspaceBuildDetailFile, workspaceLogsDir } from "@stim-cli/core/state";
30
+ import { DEV_SERVER_STOP_REQUEST_KEY, MACHINE_OWNER_KINDS, MAINTENANCE_LOG_NAME, budgetSweepFile, budgetSweepLock, buildWorkerRoot, gcLogFile, isJsonObject, loadConfig, machineCacheUsageFile, maintenanceNdjsonFile, readDevServerStop, readDevServerStopRequest, readJsonObject, readMachineCacheUsage, runId, settingDefinition, settingValueError, workspaceLogsDir } from "@stim-cli/core/state";
31
31
  import { basename as basename$1, dirname as dirname$1, isAbsolute as isAbsolute$1, join as join$1, relative as relative$1, resolve as resolve$1 } from "path";
32
32
  import { fileURLToPath } from "node:url";
33
33
  import chalk from "chalk";
34
34
  import { existsSync as existsSync$1, readFileSync as readFileSync$1, readdirSync as readdirSync$1, realpathSync as realpathSync$1, rmSync as rmSync$1, statSync as statSync$1 } from "fs";
35
35
  import { access, lstat, readFile, readdir, rm, stat, statfs } from "node:fs/promises";
36
36
  import { homedir as homedir$1, tmpdir as tmpdir$1 } from "os";
37
+ //#region src/budget-sweep.ts
38
+ const EMPTY_SWEEP_WINDOW_MS = 3e5;
39
+ function sweepMarker(caches, olderThanDays) {
40
+ const hash = createHash("sha256").update(String(olderThanDays));
41
+ for (const line of caches.map((cache) => `${cache.name}\0${cache.dir}`).toSorted()) hash.update("\n").update(line);
42
+ return hash.digest("hex");
43
+ }
44
+ function canSkipEmptySweep(recorded, now) {
45
+ if (!recorded || recorded.marker !== now.marker) return false;
46
+ const age = now.at - recorded.at;
47
+ return age >= 0 && age < 3e5 && recorded.freeMb - now.freeMb <= 1024;
48
+ }
49
+ function readEmptySweep() {
50
+ const value = readJsonObject(budgetSweepFile());
51
+ if (!value) return null;
52
+ const { at, freeMb, marker } = value;
53
+ return typeof at === "number" && Number.isFinite(at) && typeof freeMb === "number" && Number.isFinite(freeMb) && typeof marker === "string" ? {
54
+ at,
55
+ freeMb,
56
+ marker
57
+ } : null;
58
+ }
59
+ function writeEmptySweep(sweep) {
60
+ const file = budgetSweepFile();
61
+ try {
62
+ mkdirSync(dirname(file), {
63
+ recursive: true,
64
+ mode: 448
65
+ });
66
+ withDirLock$1(budgetSweepLock(), () => {
67
+ if (!sweep) return rmSync(file, { force: true });
68
+ const tmp = `${file}.tmp-${process.pid}`;
69
+ writeFileSync(tmp, JSON.stringify(sweep), { mode: 384 });
70
+ renameSync(tmp, file);
71
+ });
72
+ } catch {}
73
+ }
74
+ //#endregion
37
75
  //#region src/cache/caches.ts
38
76
  function xcodeDerivedDataRoot(env) {
39
77
  return join$1(env.HOME || env.USERPROFILE || homedir$1(), "Library", "Developer", "Xcode", "DerivedData");
@@ -204,17 +242,15 @@ function canonicalCacheDir(dir) {
204
242
  }
205
243
  const CACHE_SIZE_TIMEOUT_MS = 6e4;
206
244
  async function sizeCaches(caches, operation) {
207
- const sized = [];
208
- for (const cache of caches) {
245
+ return mapLimited(caches, sizeConcurrency(operation), async (cache) => {
209
246
  operation?.signal.throwIfAborted();
210
247
  const bytes = cache.bytes ?? await measuredDirectorySizeAsync(cache.dir, {
211
248
  timeoutMs: CACHE_SIZE_TIMEOUT_MS,
212
249
  operation,
213
250
  signal: operation?.signal
214
251
  });
215
- sized.push(Object.assign({}, cache, bytes === null ? { sizeError: "Size unavailable: measurement failed or exceeded 60 seconds." } : { bytes }));
216
- }
217
- return sized;
252
+ return Object.assign({}, cache, bytes === null ? { sizeError: "Size unavailable: measurement failed or exceeded 60 seconds." } : { bytes });
253
+ });
218
254
  }
219
255
  function pruneCache(cache, { olderThanDays, now = Date.now(), protect, evictBytes, byMtime = false } = {}) {
220
256
  const cutoff = now - olderThanDays * 24 * 60 * 60 * 1e3;
@@ -296,18 +332,22 @@ async function pruneCacheAsync(cache, { olderThanDays, now = Date.now(), signal,
296
332
  };
297
333
  const cutoff = now - olderThanDays * 24 * 60 * 60 * 1e3;
298
334
  let entries = cache.files ?? [cache.dir];
335
+ let unlisted = 0;
299
336
  for (let depth = 0; !cache.files && depth < (cache.entriesDepth ?? 1); depth++) {
300
337
  const next = [];
301
338
  for (const parent of entries) {
302
339
  signal?.throwIfAborted();
303
- const names = await readdir(parent).catch(() => []);
340
+ const names = await readdir(parent).catch((error) => {
341
+ if (error.code !== "ENOENT" && error.code !== "ENOTDIR") unlisted++;
342
+ return [];
343
+ });
304
344
  next.push(...names.map((name) => join$1(parent, name)));
305
345
  }
306
346
  entries = next;
307
347
  }
308
348
  let removed = 0;
309
349
  let bytes = 0;
310
- let failed = 0;
350
+ let failed = unlisted;
311
351
  for (const entry of entries) {
312
352
  signal?.throwIfAborted();
313
353
  try {
@@ -2983,9 +3023,32 @@ async function launchedIosAppAfterNoHandle(error, udid, bundleId, exec) {
2983
3023
  }
2984
3024
  async function launchIosApp({ udid, bundleId, metroPort, devClientScheme = null, devMenuParams = false, consolePaths }, { exec = null } = {}) {
2985
3025
  const e = exec || getExecutor();
2986
- const running = await iosAppProcess(udid, bundleId, { exec: e });
2987
- const stateUnknown = running.state === "unknown";
2988
3026
  const launchRestarts = metroPort === null || !devClientScheme || Boolean(consolePaths);
3027
+ const writeJsLocation = async (port) => {
3028
+ try {
3029
+ await e.runFileAsync("xcrun", [
3030
+ "simctl",
3031
+ "spawn",
3032
+ udid,
3033
+ "defaults",
3034
+ "write",
3035
+ bundleId,
3036
+ "RCT_jsLocation",
3037
+ jsLocationValue(port)
3038
+ ], { timeoutMs: 6e4 });
3039
+ return null;
3040
+ } catch (err) {
3041
+ return {
3042
+ failed: true,
3043
+ code: LAUNCH_ERROR,
3044
+ reason: `Could not point ${bundleId} at Metro port ${port} (defaults write RCT_jsLocation): ${await describeIosSimulatorFailure(err, e)}`
3045
+ };
3046
+ }
3047
+ };
3048
+ const runningProbe = iosAppProcess(udid, bundleId, { exec: e });
3049
+ const earlyWrite = metroPort !== null && launchRestarts ? writeJsLocation(metroPort) : null;
3050
+ const running = await runningProbe;
3051
+ const stateUnknown = running.state === "unknown";
2989
3052
  const launchArgs = [
2990
3053
  "simctl",
2991
3054
  "launch",
@@ -3014,24 +3077,8 @@ async function launchIosApp({ udid, bundleId, metroPort, devClientScheme = null,
3014
3077
  restart.restartedPid = running.pid;
3015
3078
  }
3016
3079
  if (metroPort !== null) {
3017
- try {
3018
- await e.runFileAsync("xcrun", [
3019
- "simctl",
3020
- "spawn",
3021
- udid,
3022
- "defaults",
3023
- "write",
3024
- bundleId,
3025
- "RCT_jsLocation",
3026
- jsLocationValue(metroPort)
3027
- ], { timeoutMs: 6e4 });
3028
- } catch (err) {
3029
- return {
3030
- failed: true,
3031
- code: LAUNCH_ERROR,
3032
- reason: `Could not point ${bundleId} at Metro port ${metroPort} (defaults write RCT_jsLocation): ${await describeIosSimulatorFailure(err, e)}`
3033
- };
3034
- }
3080
+ const writeFailure = await (earlyWrite ?? writeJsLocation(metroPort));
3081
+ if (writeFailure) return writeFailure;
3035
3082
  if (devClientScheme) {
3036
3083
  const url = devClientUrl(devClientScheme, metroPort, "localhost", { devMenuParams });
3037
3084
  const viaInitialUrl = Boolean(consolePaths) || stateUnknown;
@@ -3738,6 +3785,43 @@ function jsRunningFields(jsRunningAt) {
3738
3785
  function expoBundleLine(msg) {
3739
3786
  return /\bBundl(?:ing|ed)\b/.test(msg);
3740
3787
  }
3788
+ function createCrashProbe({ firstAt, everyMs, now, run }) {
3789
+ let nextAt = firstAt;
3790
+ let pending = null;
3791
+ let outcome = null;
3792
+ const consume = () => {
3793
+ const done = outcome;
3794
+ if (!done) return null;
3795
+ pending = null;
3796
+ outcome = null;
3797
+ if ("error" in done) throw done.error;
3798
+ return done.result;
3799
+ };
3800
+ return {
3801
+ get pending() {
3802
+ return pending;
3803
+ },
3804
+ poll() {
3805
+ const found = consume();
3806
+ if (found) return found;
3807
+ if (!pending && now() >= nextAt) {
3808
+ nextAt = now() + everyMs;
3809
+ pending = (async () => {
3810
+ try {
3811
+ outcome = { result: await run() };
3812
+ } catch (error) {
3813
+ outcome = { error };
3814
+ }
3815
+ })();
3816
+ }
3817
+ return null;
3818
+ },
3819
+ async settle() {
3820
+ await pending;
3821
+ return consume();
3822
+ }
3823
+ };
3824
+ }
3741
3825
  async function nativeLaunchFailure({ readCrashes, since, platform, processAlive, mode, waitedMs }) {
3742
3826
  if (!readCrashes) return null;
3743
3827
  const crashes = (await readCrashes()).filter((record) => record.event === "native_crash" && after(record, since) && recordCouldBelongToPlatform(record, platform));
@@ -3763,15 +3847,18 @@ function manifestDeadline(records, { startedAt, timeoutMs }) {
3763
3847
  if (open.size > 0) return cap;
3764
3848
  return servedAt === null ? base : Math.min(cap, Math.max(base, servedAt + MANIFEST_GRACE_MS));
3765
3849
  }
3766
- async function verifyLaunch({ slot, appPid, platformShared, logsDir, since, metroPort = null, platform = null, mode = null, requireBundleResponse, timeoutMs = VERIFY_TIMEOUT_MS, stabilityMs = STABILITY_WINDOW_MS, pollMs = VERIFY_POLL_MS, readRecords = null, readDeviceRecords = null, readClientRecords = null, readNativeCrashes = null, processAlive = null, onReadinessPending = null, onActivity, deviceLogsThrough, now = Date.now, sleep = (ms) => new Promise((r) => setTimeout(r, ms)) } = {}) {
3850
+ async function verifyLaunch({ slot, appPid, platformShared, logsDir, since, metroPort = null, platform = null, mode = null, requireBundleResponse, timeoutMs = VERIFY_TIMEOUT_MS, stabilityMs = STABILITY_WINDOW_MS, pollMs = VERIFY_POLL_MS, readRecords = null, readDeviceRecords = null, readClientRecords = null, readNativeCrashes = null, processAlive = null, onReadinessPending = null, onActivity, deviceLogsThrough, now = Date.now, signal, sleep = (ms) => cancellableSleep(ms, {
3851
+ signal,
3852
+ what: "launch readiness check"
3853
+ }) } = {}) {
3767
3854
  const delivered = (record) => deliveryOwner(record, {
3768
3855
  slot,
3769
3856
  appPid,
3770
3857
  platformShared
3771
3858
  }) === "this";
3772
- const read = readRecords || (() => readMetroRecords(logsDir));
3773
- const readDevice = readDeviceRecords || (() => readNdjson(logsDir, "device.ndjson"));
3774
- const readClient = readClientRecords || (() => readNdjson(logsDir, "client.ndjson"));
3859
+ const read = readRecords || tailNdjson(logsDir, "metro.ndjson");
3860
+ const readDevice = readDeviceRecords || tailNdjson(logsDir, "device.ndjson");
3861
+ const readClient = readClientRecords || tailNdjson(logsDir, "client.ndjson");
3775
3862
  const deviceNow = () => {
3776
3863
  const at = now();
3777
3864
  return deviceLogsThrough ? Math.max(Math.min(at, deviceLogsThrough() ?? -Infinity), at - DEVICE_LOG_LAG_LIMIT_MS) : at;
@@ -3786,7 +3873,19 @@ async function verifyLaunch({ slot, appPid, platformShared, logsDir, since, metr
3786
3873
  let deliveryId = null;
3787
3874
  let runtimeLoadingAt = null;
3788
3875
  let runtimeStartedAt = null;
3789
- let nextCrashCheck = startedAt + 1e3;
3876
+ const crashProbe = createCrashProbe({
3877
+ firstAt: startedAt + 1e3,
3878
+ everyMs: 2e3,
3879
+ now,
3880
+ run: () => nativeLaunchFailure({
3881
+ readCrashes: readNativeCrashes,
3882
+ since,
3883
+ platform,
3884
+ processAlive,
3885
+ mode,
3886
+ waitedMs: 0
3887
+ })
3888
+ });
3790
3889
  let bundlingAt = null;
3791
3890
  let reported = null;
3792
3891
  const report = (name, at) => {
@@ -3794,131 +3893,149 @@ async function verifyLaunch({ slot, appPid, platformShared, logsDir, since, metr
3794
3893
  reported = name;
3795
3894
  onActivity?.(name, at);
3796
3895
  };
3797
- while (true) {
3798
- const metroRecords = read().filter((record) => after(record, since));
3799
- const bundleRecords = metroRecords.filter((record) => (!requireBundleResponse || String(record.event).startsWith("bundle_response_")) && delivered(record));
3800
- const deviceRecords = readDevice().filter((record) => after(record, since) && (slot === void 0 || (record.slot ?? "default") === slot));
3801
- const clientRecords = readClient().filter((record) => after(record, since) && !platformShared);
3802
- bundleDeadline = manifestDeadline(metroRecords.filter((record) => String(record.event).startsWith("manifest_request_") && (!platform || record.platform === platform)), {
3803
- startedAt,
3804
- timeoutMs: baseTimeoutMs
3805
- });
3806
- if (now() >= nextCrashCheck) {
3807
- nextCrashCheck = now() + 2e3;
3808
- const crash = await nativeLaunchFailure({
3809
- readCrashes: readNativeCrashes,
3810
- since,
3811
- platform,
3812
- processAlive,
3813
- mode,
3814
- waitedMs: now() - startedAt
3896
+ const loop = async () => {
3897
+ while (true) {
3898
+ const metroRecords = read().filter((record) => after(record, since));
3899
+ const bundleRecords = metroRecords.filter((record) => (!requireBundleResponse || String(record.event).startsWith("bundle_response_")) && delivered(record));
3900
+ const deviceRecords = readDevice().filter((record) => after(record, since) && (slot === void 0 || (record.slot ?? "default") === slot));
3901
+ const clientRecords = readClient().filter((record) => after(record, since) && !platformShared);
3902
+ bundleDeadline = manifestDeadline(metroRecords.filter((record) => String(record.event).startsWith("manifest_request_") && (!platform || record.platform === platform)), {
3903
+ startedAt,
3904
+ timeoutMs: baseTimeoutMs
3815
3905
  });
3816
- if (crash) return crash;
3817
- }
3818
- if (deliveryId === null) {
3819
- const request = bundleRecords.find((record) => record.event === "bundle_response_started" && typeof record.requestId === "string" && isBundleProof(record, since, platform));
3820
- if (request) {
3821
- deliveryId = request.requestId;
3822
- bundlingAt = request;
3823
- proof = null;
3824
- stabilityDeadline = null;
3906
+ const crash = crashProbe.poll();
3907
+ if (crash) return {
3908
+ ...crash,
3909
+ waitedMs: now() - startedAt
3910
+ };
3911
+ if (deliveryId === null) {
3912
+ const request = bundleRecords.find((record) => record.event === "bundle_response_started" && typeof record.requestId === "string" && isBundleProof(record, since, platform));
3913
+ if (request) {
3914
+ deliveryId = request.requestId;
3915
+ bundlingAt = request;
3916
+ proof = null;
3917
+ stabilityDeadline = null;
3918
+ }
3825
3919
  }
3826
- }
3827
- for (const record of bundleRecords) {
3828
- if (isBundleProof(record, since, platform)) activity = record;
3829
- const completed = deliveryId === null ? isBundleReadyProof(record, since, platform) : record.event === "bundle_response_finished" && record.requestId === deliveryId && isBundleProof(record, since, platform);
3830
- if (!proof && completed) {
3831
- proof = record;
3832
- stabilityDeadline = Number(record.ts) + Math.max(0, stabilityMs);
3920
+ for (const record of bundleRecords) {
3921
+ if (isBundleProof(record, since, platform)) activity = record;
3922
+ const completed = deliveryId === null ? isBundleReadyProof(record, since, platform) : record.event === "bundle_response_finished" && record.requestId === deliveryId && isBundleProof(record, since, platform);
3923
+ if (!proof && completed) {
3924
+ proof = record;
3925
+ stabilityDeadline = Number(record.ts) + Math.max(0, stabilityMs);
3926
+ }
3833
3927
  }
3834
- }
3835
- const signals = deviceRecords.filter((record) => Number(record.ts) <= now()).toSorted((a, b) => Number(a.ts) - Number(b.ts));
3836
- if (platform === "android" && runtimeLoadingAt === null) {
3837
- const loading = signals.find((record) => record.src === "device" && record.platform === "android" && /^(?:unknown:)?BridgelessReact\(\d+\)$/.test(String(record.proc)) && /^ReactHost\{\d+\}\.getOrCreateReactInstanceTask\(\): Loading JS Bundle$/.test(String(record.msg)) && (stabilityDeadline === null || Number(record.ts) <= stabilityDeadline));
3838
- if (loading) runtimeLoadingAt = Number(loading.ts);
3839
- }
3840
- if (runtimeLoadingAt !== null && runtimeStartedAt === null) {
3841
- const executing = signals.find((record) => record.src === "device" && record.platform === "android" && Number(record.ts) >= runtimeLoadingAt && (/^ReactNativeJS\(\d+\)$/.test(String(record.proc)) || appReadinessSignal(record, platform) !== null));
3842
- if (executing) runtimeStartedAt = Number(executing.ts);
3843
- }
3844
- const runtimeWaiting = runtimeLoadingAt !== null && runtimeStartedAt === null;
3845
- const completedAt = proof ? Math.max(Number(proof.ts), runtimeStartedAt ?? 0) : null;
3846
- if (completedAt !== null) stabilityDeadline = completedAt + Math.max(0, stabilityMs);
3847
- const jsRunningAt = jsRunningSince(signals, completedAt, runtimeWaiting, platform);
3848
- const windowEnd = settledWindowEnd(stabilityDeadline, jsRunningAt);
3849
- const readinessDeadline = completedAt !== null ? completedAt + APP_READINESS_TIMEOUT_MS : bundleDeadline;
3850
- if (pendingAt === null) {
3851
- const pending = signals.find((record) => appReadinessSignal(record, platform) === "pending" && (runtimeWaiting || stabilityDeadline === null || Number(record.ts) <= stabilityDeadline));
3852
- if (pending) {
3853
- pendingAt = Number(pending.ts);
3854
- onReadinessPending?.();
3928
+ const signals = deviceRecords.filter((record) => Number(record.ts) <= now()).toSorted((a, b) => Number(a.ts) - Number(b.ts));
3929
+ if (platform === "android" && runtimeLoadingAt === null) {
3930
+ const loading = signals.find((record) => record.src === "device" && record.platform === "android" && /^(?:unknown:)?BridgelessReact\(\d+\)$/.test(String(record.proc)) && /^ReactHost\{\d+\}\.getOrCreateReactInstanceTask\(\): Loading JS Bundle$/.test(String(record.msg)) && (stabilityDeadline === null || Number(record.ts) <= stabilityDeadline));
3931
+ if (loading) runtimeLoadingAt = Number(loading.ts);
3855
3932
  }
3856
- }
3857
- report(...launchActivity(pendingAt, proof, bundlingAt));
3858
- const ready = pendingAt !== null && signals.some((record) => Number(record.ts) >= pendingAt && Number(record.ts) <= readinessDeadline && appReadinessSignal(record, platform) === "ready");
3859
- const bundleErrors = bundleRecords.filter((record) => isFatalLaunchError(record, platform) || deliveryId !== null && record.event === "bundle_response_failed" && record.requestId === deliveryId && isBundleProof(record, since, platform));
3860
- if (bundleErrors.length) {
3861
- const alive = processAlive ? await processAlive() : null;
3862
- const fatalRecord = bundleErrors[bundleErrors.length - 1];
3863
- const fatalAt = Number(fatalRecord?.ts ?? since ?? 0);
3864
- return {
3865
- verified: false,
3866
- fatal: true,
3867
- errors: metroRecords.filter((record) => after(record, fatalAt) && recordCouldBelongToPlatform(record, platform) && isLaunchError(record, platform)),
3868
- processAlive: alive,
3869
- record: fatalRecord,
3870
- mode,
3871
- waitedMs: now() - startedAt,
3872
- ...pendingAt !== null ? { readiness: "error" } : {}
3873
- };
3874
- }
3875
- if (proof && windowEnd !== null && (pendingAt !== null || deviceNow() >= windowEnd)) {
3876
- const errorSince = pendingAt === null ? Number(proof.ts ?? since ?? 0) : Number(since ?? 0);
3877
- const errors = [
3878
- ...metroRecords.filter((record) => after(record, errorSince) && recordCouldBelongToPlatform(record, platform)),
3879
- ...deviceRecords.filter((record) => after(record, errorSince) && recordCouldBelongToPlatform(record, platform)),
3880
- ...clientRecords.filter((record) => after(record, errorSince) && recordCouldBelongToPlatform(record, platform))
3881
- ].filter((record) => isLaunchError(record, platform));
3882
- const alive = processAlive ? await processAlive() : null;
3883
- const waitedMs = now() - startedAt;
3884
- if (alive === false) return {
3885
- verified: false,
3886
- fatal: true,
3887
- errors,
3888
- processAlive: alive,
3889
- record: proof,
3890
- mode,
3891
- waitedMs,
3892
- ...pendingAt !== null ? { readiness: "error" } : {}
3893
- };
3894
- const actionableErrors = errors.filter((record) => !isIosConnectionRefusal(record, platform));
3895
- const appErrors = actionableErrors.some(isAppLaunchError);
3896
- if (!runtimeWaiting && pendingAt === null || appErrors || ready || deviceNow() >= readinessDeadline) return {
3897
- verified: true,
3898
- record: proof,
3899
- errors: actionableErrors,
3900
- processAlive: alive,
3933
+ if (runtimeLoadingAt !== null && runtimeStartedAt === null) {
3934
+ const executing = signals.find((record) => record.src === "device" && record.platform === "android" && Number(record.ts) >= runtimeLoadingAt && (/^ReactNativeJS\(\d+\)$/.test(String(record.proc)) || appReadinessSignal(record, platform) !== null));
3935
+ if (executing) runtimeStartedAt = Number(executing.ts);
3936
+ }
3937
+ const runtimeWaiting = runtimeLoadingAt !== null && runtimeStartedAt === null;
3938
+ const completedAt = proof ? Math.max(Number(proof.ts), runtimeStartedAt ?? 0) : null;
3939
+ if (completedAt !== null) stabilityDeadline = completedAt + Math.max(0, stabilityMs);
3940
+ const jsRunningAt = jsRunningSince(signals, completedAt, runtimeWaiting, platform);
3941
+ const windowEnd = settledWindowEnd(stabilityDeadline, jsRunningAt);
3942
+ const readinessDeadline = completedAt !== null ? completedAt + APP_READINESS_TIMEOUT_MS : bundleDeadline;
3943
+ if (pendingAt === null) {
3944
+ const pending = signals.find((record) => appReadinessSignal(record, platform) === "pending" && (runtimeWaiting || stabilityDeadline === null || Number(record.ts) <= stabilityDeadline));
3945
+ if (pending) {
3946
+ pendingAt = Number(pending.ts);
3947
+ onReadinessPending?.();
3948
+ }
3949
+ }
3950
+ report(...launchActivity(pendingAt, proof, bundlingAt));
3951
+ const ready = pendingAt !== null && signals.some((record) => Number(record.ts) >= pendingAt && Number(record.ts) <= readinessDeadline && appReadinessSignal(record, platform) === "ready");
3952
+ const bundleErrors = bundleRecords.filter((record) => isFatalLaunchError(record, platform) || deliveryId !== null && record.event === "bundle_response_failed" && record.requestId === deliveryId && isBundleProof(record, since, platform));
3953
+ if (bundleErrors.length) {
3954
+ const alive = processAlive ? await processAlive() : null;
3955
+ const fatalRecord = bundleErrors[bundleErrors.length - 1];
3956
+ const fatalAt = Number(fatalRecord?.ts ?? since ?? 0);
3957
+ return {
3958
+ verified: false,
3959
+ fatal: true,
3960
+ errors: metroRecords.filter((record) => after(record, fatalAt) && recordCouldBelongToPlatform(record, platform) && isLaunchError(record, platform)),
3961
+ processAlive: alive,
3962
+ record: fatalRecord,
3963
+ mode,
3964
+ waitedMs: now() - startedAt,
3965
+ ...pendingAt !== null ? { readiness: "error" } : {}
3966
+ };
3967
+ }
3968
+ if (proof && windowEnd !== null && (pendingAt !== null || deviceNow() >= windowEnd)) {
3969
+ const errorSince = pendingAt === null ? Number(proof.ts ?? since ?? 0) : Number(since ?? 0);
3970
+ const errors = [
3971
+ ...metroRecords.filter((record) => after(record, errorSince) && recordCouldBelongToPlatform(record, platform)),
3972
+ ...deviceRecords.filter((record) => after(record, errorSince) && recordCouldBelongToPlatform(record, platform)),
3973
+ ...clientRecords.filter((record) => after(record, errorSince) && recordCouldBelongToPlatform(record, platform))
3974
+ ].filter((record) => isLaunchError(record, platform));
3975
+ const alive = processAlive ? await processAlive() : null;
3976
+ const waitedMs = now() - startedAt;
3977
+ if (alive === false) return {
3978
+ verified: false,
3979
+ fatal: true,
3980
+ errors,
3981
+ processAlive: alive,
3982
+ record: proof,
3983
+ mode,
3984
+ waitedMs,
3985
+ ...pendingAt !== null ? { readiness: "error" } : {}
3986
+ };
3987
+ const actionableErrors = errors.filter((record) => !isIosConnectionRefusal(record, platform));
3988
+ const appErrors = actionableErrors.some(isAppLaunchError);
3989
+ if (!runtimeWaiting && pendingAt === null || appErrors || ready || deviceNow() >= readinessDeadline) return {
3990
+ verified: true,
3991
+ record: proof,
3992
+ errors: actionableErrors,
3993
+ processAlive: alive,
3994
+ mode,
3995
+ waitedMs,
3996
+ ...pendingAt !== null ? { readiness: appErrors ? "error" : ready ? "ready" : "timed-out" } : {},
3997
+ ...jsRunningFields(jsRunningAt)
3998
+ };
3999
+ }
4000
+ if ((!proof || runtimeWaiting) && now() >= bundleDeadline) return await bundleTimeoutOutcome({
4001
+ requested: findBundleRequest(deviceRecords, since, metroPort, platform) ?? activity,
4002
+ unattributed: metroRecords.find((record) => record.event === "bundle_response_finished" && deliveryOwner(record, {
4003
+ slot,
4004
+ appPid,
4005
+ platformShared
4006
+ }) === "unknown" && isBundleProof(record, since, platform)) ?? null,
4007
+ processAlive,
4008
+ deviceRecords,
4009
+ platform,
3901
4010
  mode,
3902
- waitedMs,
3903
- ...pendingAt !== null ? { readiness: appErrors ? "error" : ready ? "ready" : "timed-out" } : {},
3904
- ...jsRunningFields(jsRunningAt)
3905
- };
4011
+ waitedMs: now() - startedAt
4012
+ });
4013
+ const nap = sleep(Math.min(pollMs, Math.max(0, (runtimeWaiting ? bundleDeadline : proof && pendingAt !== null ? readinessDeadline : windowEnd ?? bundleDeadline) - deviceNow())));
4014
+ await (crashProbe.pending ? Promise.race([nap, crashProbe.pending]) : nap);
3906
4015
  }
3907
- if ((!proof || runtimeWaiting) && now() >= bundleDeadline) return await bundleTimeoutOutcome({
3908
- requested: findBundleRequest(deviceRecords, since, metroPort, platform) ?? activity,
3909
- unattributed: metroRecords.find((record) => record.event === "bundle_response_finished" && deliveryOwner(record, {
3910
- slot,
3911
- appPid,
3912
- platformShared
3913
- }) === "unknown" && isBundleProof(record, since, platform)) ?? null,
3914
- processAlive,
3915
- deviceRecords,
3916
- platform,
3917
- mode,
3918
- waitedMs: now() - startedAt
3919
- });
3920
- await sleep(Math.min(pollMs, Math.max(0, (runtimeWaiting ? bundleDeadline : proof && pendingAt !== null ? readinessDeadline : windowEnd ?? bundleDeadline) - deviceNow())));
4016
+ };
4017
+ let result;
4018
+ try {
4019
+ result = await loop();
4020
+ } catch (error) {
4021
+ await crashProbe.settle().catch(() => {});
4022
+ throw error;
3921
4023
  }
4024
+ const probed = crashProbe.pending !== null;
4025
+ const late = await crashProbe.settle();
4026
+ if (late) return {
4027
+ ...late,
4028
+ waitedMs: now() - startedAt
4029
+ };
4030
+ if (probed && result.verified && await processAlive?.() === false) return {
4031
+ ...result,
4032
+ verified: false,
4033
+ fatal: true,
4034
+ processAlive: false,
4035
+ waitedMs: now() - startedAt,
4036
+ ...result.readiness ? { readiness: "error" } : {}
4037
+ };
4038
+ return result;
3922
4039
  }
3923
4040
  function launchActivity(pendingAt, proof, bundling) {
3924
4041
  if (pendingAt !== null) return ["waiting-ready", pendingAt];
@@ -4012,6 +4129,9 @@ function readCollectorRecords(logsDir) {
4012
4129
  function readNdjson(logsDir, name) {
4013
4130
  return logsDir ? readNdjsonGenerations$1(join(logsDir, name)) : [];
4014
4131
  }
4132
+ function tailNdjson(logsDir, name) {
4133
+ return logsDir ? createNdjsonTail(join(logsDir, name)) : () => [];
4134
+ }
4015
4135
  function unattributedLaunchLines({ platform, metroPort, slot = "default", siblings }) {
4016
4136
  const others = siblings.length === 1 ? `Slot ${siblings[0]} also runs` : `Slots ${siblings.join(", ")} also run`;
4017
4137
  return [
@@ -6214,11 +6334,11 @@ function directories(dir, notices) {
6214
6334
  }
6215
6335
  function scopedOutside(gradleHome, env) {
6216
6336
  if (!deviceSweepIsScoped(env)) return false;
6217
- const rel = relative(canonical(getConfigDir()), gradleHome);
6337
+ const rel = relative(canonical$1(getConfigDir()), gradleHome);
6218
6338
  return rel === "" || rel.startsWith("..") || isAbsolute(rel);
6219
6339
  }
6220
6340
  function gradleUserHome(env, cwd) {
6221
- return canonical(envDir("GRADLE_USER_HOME", env, cwd) ?? join(env?.HOME || env?.USERPROFILE || homedir(), ".gradle"));
6341
+ return canonical$1(envDir("GRADLE_USER_HOME", env, cwd) ?? join(env?.HOME || env?.USERPROFILE || homedir(), ".gradle"));
6222
6342
  }
6223
6343
  /** Each Gradle version's `caches/<version>` and `wrapper/dists/gradle-<version>-*` directories under `gradleHome`. */
6224
6344
  function gradleVersionDirs(gradleHome, notices) {
@@ -7127,7 +7247,7 @@ async function emptyCache(cache, signal, operation) {
7127
7247
  }
7128
7248
  function reportFailures(cache, failed, { log, record, fail }) {
7129
7249
  if (!failed) return false;
7130
- const message = `${failed} entr${failed === 1 ? "y" : "ies"} in ${cache.dir} could not be removed`;
7250
+ const message = `${failed} entr${failed === 1 ? "y" : "ies"} in ${cache.dir} could not be read or removed`;
7131
7251
  log(chalk.red(`${cache.name}: ${message}`));
7132
7252
  record("cache", "failed", cache.name, {
7133
7253
  id: cache.dir,
@@ -7141,6 +7261,7 @@ async function trimCaches(caches, olderThan, output = GC_OUTPUT, signal, operati
7141
7261
  let cacheBytes = 0;
7142
7262
  let failures = 0;
7143
7263
  const trimmed = [];
7264
+ let unchanged = 0;
7144
7265
  for (const c of caches) {
7145
7266
  signal?.throwIfAborted();
7146
7267
  if (c.machineGlobal) {
@@ -7170,9 +7291,10 @@ async function trimCaches(caches, olderThan, output = GC_OUTPUT, signal, operati
7170
7291
  bytes: r.bytes
7171
7292
  });
7172
7293
  log(chalk.green(`Trimmed ${c.name}: ${r.removed} entr${r.removed === 1 ? "y" : "ies"} (${formatBytes(r.bytes)})`));
7173
- } else if (!r.failed) log(chalk.dim(`${c.name}: nothing older than ${olderThan}d`));
7294
+ } else if (!r.failed) unchanged++;
7174
7295
  if (reportFailures(c, r.failed, output)) failures++;
7175
7296
  }
7297
+ if (unchanged) log(chalk.dim(`${unchanged} cache${unchanged === 1 ? "" : "s"}: nothing older than ${olderThan}d`));
7176
7298
  if (cacheBytes) log(chalk.dim(`Trimmed ${formatBytes(cacheBytes)} of shared cache. The next build that wanted those entries pays to rebuild them.`));
7177
7299
  return {
7178
7300
  trimmed,
@@ -7182,6 +7304,7 @@ async function trimCaches(caches, olderThan, output = GC_OUTPUT, signal, operati
7182
7304
  async function emptyCaches(caches, output = GC_OUTPUT, signal, operation) {
7183
7305
  const { log, record } = output;
7184
7306
  let cacheBytes = 0;
7307
+ let failures = 0;
7185
7308
  for (const c of caches) {
7186
7309
  signal?.throwIfAborted();
7187
7310
  if (!c.willEmpty) {
@@ -7207,1431 +7330,86 @@ async function emptyCaches(caches, output = GC_OUTPUT, signal, operation) {
7207
7330
  });
7208
7331
  log(chalk.green(`Emptied ${c.name}: ${r.removed} entr${r.removed === 1 ? "y" : "ies"} (${formatBytes(r.bytes)})`));
7209
7332
  } else if (!r.failed) log(chalk.dim(`${c.name}: already empty`));
7210
- reportFailures(c, r.failed, output);
7333
+ if (reportFailures(c, r.failed, output)) failures++;
7211
7334
  }
7212
7335
  if (cacheBytes) log(chalk.dim(`Emptied ${formatBytes(cacheBytes)} of shared cache. Every build that wanted any of it now pays to rebuild it.`));
7336
+ return failures;
7213
7337
  }
7214
7338
  //#endregion
7215
- //#region src/engine/stats.ts
7216
- const PLACEMENT_MAX_AGE_MS = 6048e5;
7217
- function emptyStats() {
7218
- return {
7219
- version: STATS_VERSION,
7220
- machine: {},
7221
- projects: {}
7222
- };
7223
- }
7224
- function updateStats(record, run, now) {
7225
- const at = new Date(now).toISOString();
7226
- const durationMs = wholeMs(run.durationMs);
7227
- const phases = {
7228
- coldBuildMs: wholeMs(run.coldBuildMs),
7229
- podsMs: wholeMs(run.podsMs)
7230
- };
7231
- const projects = { ...record.projects };
7232
- const scope = { ...projects[run.projectKey] };
7233
- const machine = { ...record.machine };
7234
- const before = scope[run.platform] ?? null;
7235
- const credit = creditMs(before, run, durationMs);
7236
- scope[run.platform] = applyRun(before, run, {
7237
- at,
7238
- durationMs,
7239
- credit,
7240
- phases
7241
- });
7242
- projects[run.projectKey] = scope;
7243
- machine[run.platform] = applyRun(machine[run.platform] ?? null, run, {
7244
- at,
7245
- durationMs,
7246
- credit,
7247
- phases
7248
- });
7249
- const history = { ...record.history };
7250
- if (run.phases && !run.failed && !run.waitedForBuild && !run.offloadedTo) {
7251
- const outcome = isHit(run.cacheHit) ? "hit" : "cold";
7252
- const project = { ...history[run.projectKey] };
7253
- const lists = { ...project[run.platform] };
7254
- const sample = {
7255
- at,
7256
- durationMs,
7257
- phases: wholePhases(run.phases),
7258
- ...run.deviceSetup === void 0 ? {} : { deviceSetup: run.deviceSetup }
7259
- };
7260
- lists[outcome] = trimSamples([...lists[outcome] ?? [], sample]);
7261
- project[run.platform] = lists;
7262
- history[run.projectKey] = project;
7263
- }
7264
- let placements = record.placements;
7265
- let buildMachines = record.buildMachines;
7266
- if (run.placement) {
7267
- const placement = buildPlacement(run, before, at);
7268
- placements = trimPlacements([...placements ?? [], placement], now);
7269
- if (placement.machine && placement.decision !== "here") buildMachines = {
7270
- ...buildMachines,
7271
- [placement.machine]: applyPlacement(buildMachines?.[placement.machine], placement)
7272
- };
7273
- }
7274
- const capacityRefusals = trimCapacityRefusals(record.capacityRefusals ?? [], now);
7275
- const capacityWaits = trimCapacityWaits(record.capacityWaits ?? [], now);
7276
- return {
7277
- version: STATS_VERSION,
7278
- machine,
7279
- projects,
7280
- ...Object.keys(history).length ? { history } : {},
7281
- ...placements?.length ? { placements } : {},
7282
- ...capacityRefusals.length ? { capacityRefusals } : {},
7283
- ...capacityWaits.length ? { capacityWaits } : {},
7284
- ...buildMachines && Object.keys(buildMachines).length ? { buildMachines } : {}
7285
- };
7286
- }
7287
- function buildPlacement(run, before, at) {
7288
- const placement = run.placement;
7289
- const buildMs = wholeMs(placement.decision === "offloaded" ? placement.buildMs : run.coldBuildMs);
7290
- const localEstimateMs = wholeMs(before?.lastColdBuildMs);
7291
- const slotWaitMs = wholeMs(placement.slotWaitMs);
7292
- const deviceSlotWaitMs = wholeMs(placement.deviceSlotWaitMs);
7293
- return {
7294
- at,
7295
- project: run.projectKey,
7296
- platform: run.platform,
7297
- decision: placement.decision,
7298
- reason: placement.reason,
7299
- ...placement.machine ? { machine: placement.machine } : {},
7300
- ...buildMs > 0 ? { buildMs } : {},
7301
- ...slotWaitMs > 0 ? { slotWaitMs } : {},
7302
- ...deviceSlotWaitMs > 0 ? { deviceSlotWaitMs } : {},
7303
- ...localEstimateMs > 0 ? { localEstimateMs } : {},
7304
- ...run.failed ? { failed: true } : {}
7305
- };
7306
- }
7307
- function applyPlacement(totals, placement) {
7308
- const next = totals ? { ...totals } : {
7309
- offloaded: 0,
7310
- offloadedMs: 0,
7311
- savedMs: 0,
7312
- fallbacks: 0
7313
- };
7314
- if (placement.decision === "fell-back") {
7315
- next.fallbacks += 1;
7316
- next.lastFallbackAt = placement.at;
7317
- return next;
7318
- }
7319
- next.offloaded += 1;
7320
- next.offloadedMs += placement.buildMs ?? 0;
7321
- if (placement.localEstimateMs && placement.buildMs) next.savedMs += placement.localEstimateMs - placement.buildMs;
7322
- next.lastOffloadAt = placement.at;
7323
- return next;
7324
- }
7325
- function trimPlacements(placements, now) {
7326
- return placements.filter((each) => now - Date.parse(each.at) <= PLACEMENT_MAX_AGE_MS).slice(-100);
7327
- }
7328
- function recordRunStats(run, now) {
7329
- return withConfigLock$1(() => {
7330
- const path = statsFile();
7331
- const loaded = loadForUpdate(path, now);
7332
- if (loaded.newerVersion !== null) return {
7333
- recorded: false,
7334
- note: `Run statistics at ${path} are version ${loaded.newerVersion}, which this Stim does not understand, so this run was not recorded.`
7335
- };
7336
- writeStats(path, updateStats(loaded.record, run, now));
7337
- return {
7338
- recorded: true,
7339
- note: loaded.note
7340
- };
7339
+ //#region src/maintenance/log.ts
7340
+ function openRetainedLog(file, settings) {
7341
+ ensurePrivateDir(dirname(file));
7342
+ const rotated = rotatedLogPath(file);
7343
+ withDirLock(`${file}.lock`, () => {
7344
+ try {
7345
+ if (Date.now() - statSync(rotated).mtimeMs > settings.logRetentionDays * 864e5) unlinkSync(rotated);
7346
+ } catch {}
7341
7347
  });
7348
+ return createNdjsonWriter(file, { maxBytes: settings.logMaxMb * 1024 ** 2 });
7342
7349
  }
7343
- function recordCapacityRefusal(event, now) {
7344
- try {
7345
- withConfigLock$1(() => {
7346
- const path = statsFile();
7347
- const loaded = loadForUpdate(path, now, false);
7348
- if (!loaded.record || loaded.newerVersion !== null) return;
7349
- const capacityRefusals = trimCapacityRefusals([...loaded.record.capacityRefusals ?? [], {
7350
- ...event,
7351
- at: new Date(now).toISOString(),
7352
- kind: "device"
7353
- }], now);
7354
- writeStats(path, {
7355
- ...loaded.record,
7356
- capacityRefusals
7357
- });
7358
- });
7359
- } catch {}
7360
- }
7361
- function recordCapacityWait(event, now) {
7362
- try {
7363
- withConfigLock$1(() => {
7364
- const path = statsFile();
7365
- const loaded = loadForUpdate(path, now, false);
7366
- if (!loaded.record || loaded.newerVersion !== null) return;
7367
- const capacityWaits = trimCapacityWaits([...loaded.record.capacityWaits ?? [], {
7368
- platform: event.platform,
7369
- max: event.max,
7370
- workspace: event.workspace,
7371
- ms: wholeMs(event.ms),
7372
- ...event.reclaimed && Number.isInteger(event.reclaimed) && event.reclaimed > 0 ? { reclaimed: event.reclaimed } : {},
7373
- at: new Date(now).toISOString(),
7374
- kind: "device-wait"
7375
- }], now);
7376
- writeStats(path, {
7377
- ...loaded.record,
7378
- capacityWaits
7379
- });
7380
- });
7381
- } catch {}
7382
- }
7383
- function createRunRecorder({ platform, write, now, note, phases, deviceSetup }) {
7384
- let projectKey = null;
7385
- let cacheKey = null;
7386
- let coldBuildMs = 0;
7387
- let podsMs = 0;
7388
- let placement = null;
7389
- let deviceSlotWaitMs = 0;
7390
- let recorded = false;
7350
+ function maintenanceLogger(settings, pass, trigger) {
7351
+ const file = maintenanceNdjsonFile();
7352
+ const writer = openRetainedLog(file, settings);
7391
7353
  return {
7392
- setProject(key) {
7393
- projectKey = key;
7394
- },
7395
- setCacheKey(key) {
7396
- cacheKey = key;
7397
- },
7398
- setBuildMs(ms) {
7399
- coldBuildMs = wholeMs(ms);
7400
- },
7401
- setPodsMs(ms) {
7402
- podsMs = wholeMs(ms);
7403
- },
7404
- addDeviceSlotWaitMs(ms) {
7405
- deviceSlotWaitMs += wholeMs(ms);
7406
- },
7407
- deviceSlotWaitMs() {
7408
- return deviceSlotWaitMs || void 0;
7409
- },
7410
- setPlacement(next) {
7411
- placement = next;
7412
- },
7413
- record({ failed, cacheHit = false, waited = null, durationMs, offloadedTo = null }) {
7414
- if (!projectKey || !cacheKey || recorded) return;
7415
- recorded = true;
7416
- const ran = failed ? {} : phases?.() ?? {};
7417
- try {
7418
- const outcome = write({
7419
- platform,
7420
- projectKey,
7421
- failed,
7422
- cacheHit,
7423
- waitedForBuild: Boolean(waited),
7424
- durationMs,
7425
- ...offloadedTo ? { offloadedTo } : {},
7426
- ...coldBuildMs > 0 ? { coldBuildMs } : {},
7427
- ...podsMs > 0 ? { podsMs } : {},
7428
- ...Object.keys(ran).length ? { phases: ran } : {},
7429
- ...failed || deviceSetup?.() === void 0 ? {} : { deviceSetup: deviceSetup() },
7430
- ...placement ? { placement: {
7431
- ...placement,
7432
- ...deviceSlotWaitMs > 0 ? { deviceSlotWaitMs } : {}
7433
- } } : {}
7434
- }, now());
7435
- if (outcome?.note) note(outcome.note);
7436
- } catch (error) {
7437
- note(`Run statistics could not be recorded: ${error?.message || error}`);
7354
+ write(event, level, msg, fields = {}) {
7355
+ if (level === "debug" && !settings.logChecks) return null;
7356
+ const record = {
7357
+ ...fields,
7358
+ ts: Date.now(),
7359
+ src: "maintenance",
7360
+ level,
7361
+ msg,
7362
+ event,
7363
+ pass,
7364
+ trigger,
7365
+ mode: settings.mode === "on" ? "on" : "report"
7366
+ };
7367
+ if (!writer.write(record)) throw writer.lastError ?? /* @__PURE__ */ new Error(`Could not append ${file}`);
7368
+ if (typeof fields.workspace === "string" && [
7369
+ "maintenance_action",
7370
+ "maintenance_failure",
7371
+ "maintenance_skip"
7372
+ ].includes(event)) {
7373
+ const workspace = createNdjsonWriter(join(workspaceLogsDir(fields.workspace), MAINTENANCE_LOG_NAME), { maxBytes: LOG_ROTATE_BYTES });
7374
+ try {
7375
+ if (!workspace.write(record)) throw workspace.lastError ?? /* @__PURE__ */ new Error("Could not append workspace maintenance log");
7376
+ } finally {
7377
+ workspace.close();
7378
+ }
7438
7379
  }
7439
- }
7380
+ return record;
7381
+ },
7382
+ close: () => writer.close()
7440
7383
  };
7441
7384
  }
7442
- function loadForUpdate(path, now, recoverCorrupt = true) {
7443
- let text;
7444
- try {
7445
- text = readFileSync(path, "utf-8");
7446
- } catch {
7447
- return {
7448
- record: emptyStats(),
7449
- note: null,
7450
- newerVersion: null
7451
- };
7452
- }
7453
- const parsed = decodeStats(text);
7454
- if (!parsed.record && parsed.newerVersion === null) {
7455
- if (!recoverCorrupt) return {
7456
- record: null,
7457
- note: null,
7458
- newerVersion: null
7459
- };
7460
- const aside = `${path}.corrupt-${now}`;
7461
- renameSync(path, aside);
7462
- return {
7463
- record: emptyStats(),
7464
- note: `Run statistics at ${path} could not be read, so they were moved to ${aside} and a new file was started.`,
7465
- newerVersion: null
7466
- };
7467
- }
7468
- if (parsed.newerVersion !== null) return {
7469
- record: emptyStats(),
7470
- note: null,
7471
- newerVersion: parsed.newerVersion
7472
- };
7385
+ //#endregion
7386
+ //#region src/commands/gc/log.ts
7387
+ const VERBS = {
7388
+ done: "Cleared",
7389
+ kept: "Kept",
7390
+ failed: "Failed to clear"
7391
+ };
7392
+ function gcLogRecord(run, result, ts) {
7393
+ const path = [result.id, result.label].find((value) => value !== null && isAbsolute(value)) ?? null;
7394
+ const size = result.bytes ? ` (${formatBytes(result.bytes)})` : "";
7395
+ const why = result.detail ? `: ${result.detail}` : "";
7473
7396
  return {
7474
- record: parsed.record,
7475
- note: null,
7476
- newerVersion: null
7477
- };
7478
- }
7479
- function writeStats(path, record) {
7480
- const tmp = `${path}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
7481
- writeFileSync(tmp, `${JSON.stringify(record)}\n`);
7482
- try {
7483
- renameSync(tmp, path);
7484
- } catch (error) {
7485
- rmSync(tmp, { force: true });
7486
- throw error;
7487
- }
7488
- }
7489
- function applyRun(bucket, run, { at, durationMs, credit, phases }) {
7490
- const next = bucket ? { ...bucket } : {
7491
- runs: 0,
7492
- failed: 0,
7493
- hits: 0,
7494
- misses: 0,
7495
- coldRuns: 0,
7496
- coldRunMs: 0,
7497
- hitRuns: 0,
7498
- hitRunMs: 0,
7499
- timeSavedMs: 0,
7500
- firstRunAt: at,
7501
- lastRunAt: at
7502
- };
7503
- next.runs += 1;
7504
- next.lastRunAt = at;
7505
- if (phases.coldBuildMs > 0) next.lastColdBuildMs = phases.coldBuildMs;
7506
- if (phases.podsMs > 0) next.lastPodsMs = phases.podsMs;
7507
- if (run.failed) {
7508
- next.failed += 1;
7509
- return next;
7510
- }
7511
- if (isHit(run.cacheHit)) {
7512
- next.hits += 1;
7513
- if (!run.waitedForBuild) {
7514
- next.hitRuns += 1;
7515
- next.hitRunMs += durationMs;
7516
- next.timeSavedMs += credit;
7517
- }
7518
- return next;
7519
- }
7520
- next.misses += 1;
7521
- if (run.offloadedTo) {
7522
- next.offloadedRuns = (next.offloadedRuns ?? 0) + 1;
7523
- next.offloadedRunMs = (next.offloadedRunMs ?? 0) + durationMs;
7524
- next.lastOffloadHost = run.offloadedTo;
7525
- return next;
7526
- }
7527
- next.coldRuns += 1;
7528
- next.coldRunMs += durationMs;
7529
- return next;
7530
- }
7531
- function creditMs(bucket, run, durationMs) {
7532
- if (run.failed || !isHit(run.cacheHit) || run.waitedForBuild) return 0;
7533
- if (!bucket || bucket.coldRuns <= 0) return 0;
7534
- return Math.max(0, Math.round(bucket.coldRunMs / bucket.coldRuns) - durationMs);
7535
- }
7536
- function isHit(cacheHit) {
7537
- return cacheHit === "local" || cacheHit === "remote";
7538
- }
7539
- function wholeMs(value) {
7540
- const ms = Number(value);
7541
- return Number.isFinite(ms) && ms > 0 ? Math.round(ms) : 0;
7542
- }
7543
- //#endregion
7544
- //#region src/engine/build-detail.ts
7545
- const LINE_MAX = 160;
7546
- const TARGET_GRAPH = /^note: Target dependency graph \((\d+) targets?\)/;
7547
- const XCODE_ACTION = /^([A-Z][A-Za-z]+)\s(.*)\s\(in target '([^']+)' from project '([^']+)'\)$/;
7548
- const XCODE_CONFIGURE = /^(ComputeTargetDependencyGraph|CreateBuildDescription|CreateBuildRequest|Resolve Package Graph|Prepare packages)$/;
7549
- const GRADLE_TASK = /^> Task (:\S+)/;
7550
- /**
7551
- * xcodebuild actions that end a target's work: a clean build touches each product last, and an incremental build
7552
- * that touches nothing still signs framework products last. Targets that end any other way are not counted.
7553
- */
7554
- const XCODE_TARGET_DONE = /^(Touch|RegisterExecutionPolicyException|CodeSign)$/;
7555
- const GRADLE_CONFIGURE = /^> Configure project /;
7556
- /**
7557
- * SwiftPM prints `[done / total] name` once it plans the build (with U+2009 thin spaces around the slash in Swift 6.4
7558
- * output), `[done/total] Compiling ...` in older toolchains; Swift 6.4 names the target being worked on, and
7559
- * `<product>-product` is the link of a product.
7560
- */
7561
- const SWIFTPM_PROGRESS = /^\[(\d+)\s*\/\s*(\d+)\](?:\s+(.*))?$/;
7562
- const SWIFTPM_CONFIGURE = /^(Fetching|Fetched|Computing version|Computed|Creating working copy|Working copy of)\b|^\[(Pre-planning|Planning|Using on-disk description)/;
7563
- const XCODE_STEPS = [
7564
- [/^(CpResource|CopyPNGFile|CopyStringsFile|CompileAssetCatalog\w*|CompileStoryboard|CompileXIB|LinkStoryboards|ProcessInfoPlistFile)$/, "resources"],
7565
- [/^(Compile\w*|Swift\w*|ScanDependencies|Precompile\w*)$/, "compile"],
7566
- [/^(Ld|Libtool|CreateUniversalBinary|GenerateDSYMFile)$/, "link"],
7567
- [/^PhaseScriptExecution$/, "script"],
7568
- [/^CodeSign$/, "sign"]
7569
- ];
7570
- const GRADLE_STEPS = [
7571
- [/[Dd]ex/, "dex"],
7572
- [/^(createBundle\w*JsAndAssets|generateCodegen\w*)$/, "script"],
7573
- [/(Resources?|Assets|Manifest|ResValues|RFile|JavaRes)$/, "resources"],
7574
- [/^(compile|kapt|ksp|javaPreCompile|buildCMake|configureCMake|externalNativeBuild)/, "compile"],
7575
- [/^(package|assemble|install|zipalign|validateSigning|merge\w*NativeLibs|strip\w*Symbols)/, "package"]
7576
- ];
7577
- function stepOf(name, table) {
7578
- return table.find(([pattern]) => pattern.test(name))?.[1] ?? null;
7579
- }
7580
- function clip(text) {
7581
- return text.length > LINE_MAX ? `${text.slice(0, 157)}...` : text;
7582
- }
7583
- function unescape(token) {
7584
- return token.replace(/\\(.)/g, "$1");
7585
- }
7586
- function baseName(path) {
7587
- return path.split("/").findLast(Boolean) ?? path;
7588
- }
7589
- /**
7590
- * An xcodebuild action line in short form: the action, the file it works on and its target, as in
7591
- * `CompileC sqlite3.c (sqlite3)`. The file is the last absolute path in the arguments, which xcodebuild prints
7592
- * as the source for compile, copy and plist steps and as the product for link and sign steps; a script phase
7593
- * shows its name instead.
7594
- */
7595
- function shortXcodeAction(action, args, target) {
7596
- const tokens = args.match(/(?:\\.|\S)+/g) ?? [];
7597
- const subject = action === "PhaseScriptExecution" ? unescape(tokens[0] ?? "") : baseName(unescape(tokens.findLast((token) => token.startsWith("/")) ?? ""));
7598
- return clip(`${action}${subject ? ` ${subject}` : ""} (${target})`);
7599
- }
7600
- function swiftpmStep(text) {
7601
- return /^Linking\b|-product$/.test(text) ? "link" : "compile";
7602
- }
7603
- /** What one line of xcodebuild, Gradle or SwiftPM output says about the build's progress, or null for any other line. */
7604
- function parseBuildToolLine(msg) {
7605
- const counted = SWIFTPM_PROGRESS.exec(msg);
7606
- if (counted) {
7607
- const text = counted[3]?.trim() ?? "";
7608
- return {
7609
- kind: "count",
7610
- done: Number(counted[1]),
7611
- total: Number(counted[2]),
7612
- step: swiftpmStep(text),
7613
- line: text ? clip(text) : null
7614
- };
7615
- }
7616
- if (SWIFTPM_CONFIGURE.test(msg)) return { kind: "configure" };
7617
- const graph = TARGET_GRAPH.exec(msg);
7618
- if (graph) return {
7619
- kind: "total",
7620
- unit: "targets",
7621
- total: Number(graph[1])
7622
- };
7623
- if (XCODE_CONFIGURE.test(msg) || GRADLE_CONFIGURE.test(msg)) return { kind: "configure" };
7624
- const action = XCODE_ACTION.exec(msg);
7625
- if (action) {
7626
- const [, name, args, target, project] = action;
7627
- const step = stepOf(name, XCODE_STEPS);
7628
- return {
7629
- kind: "unit",
7630
- unit: "targets",
7631
- id: `${project}/${target}`,
7632
- step,
7633
- line: step ? shortXcodeAction(name, args, target) : null,
7634
- finished: XCODE_TARGET_DONE.test(name)
7635
- };
7636
- }
7637
- const task = GRADLE_TASK.exec(msg);
7638
- if (task) {
7639
- const path = task[1];
7640
- return {
7641
- kind: "unit",
7642
- unit: "tasks",
7643
- id: path,
7644
- step: stepOf(path.slice(path.lastIndexOf(":") + 1), GRADLE_STEPS),
7645
- line: clip(`> Task ${path}`),
7646
- finished: false
7647
- };
7648
- }
7649
- return null;
7650
- }
7651
- /**
7652
- * Accumulates a build's tool output into its detail: xcodebuild counts the distinct targets it finished against the
7653
- * total its dependency graph note gives; Gradle counts the task lines it printed and has no total; SwiftPM reports
7654
- * its own `[done / total]` counts, which the parser passes through.
7655
- */
7656
- function createBuildDetailParser() {
7657
- let seen = false;
7658
- let step = null;
7659
- let unit = null;
7660
- let total = null;
7661
- let line = null;
7662
- let tasks = 0;
7663
- let counted = null;
7664
- const targets = /* @__PURE__ */ new Set();
7665
- return {
7666
- push(msg) {
7667
- const parsed = parseBuildToolLine(msg);
7668
- if (!parsed) return false;
7669
- seen = true;
7670
- if (parsed.kind === "total") {
7671
- unit = "targets";
7672
- total = parsed.total;
7673
- return true;
7674
- }
7675
- if (parsed.kind === "configure") {
7676
- step = "configure";
7677
- return true;
7678
- }
7679
- if (parsed.kind === "count") {
7680
- unit = "steps";
7681
- counted = {
7682
- done: parsed.done,
7683
- total: parsed.total
7684
- };
7685
- step = parsed.step;
7686
- if (parsed.line) line = parsed.line;
7687
- return true;
7688
- }
7689
- unit = parsed.unit;
7690
- if (parsed.unit === "tasks") tasks += 1;
7691
- else if (parsed.finished) targets.add(parsed.id);
7692
- if (parsed.step) step = parsed.step;
7693
- if (parsed.line) line = parsed.line;
7694
- return true;
7695
- },
7696
- detail(updatedAt) {
7697
- if (!seen) return null;
7698
- if (unit === "steps" && counted) return {
7699
- step,
7700
- unit,
7701
- done: Math.min(counted.done, counted.total),
7702
- total: counted.total,
7703
- line,
7704
- updatedAt
7705
- };
7706
- const done = unit === "tasks" ? tasks : unit === "targets" ? targets.size : null;
7707
- return {
7708
- step,
7709
- unit,
7710
- done: done !== null && total !== null ? Math.min(done, total) : done,
7711
- total: unit === "tasks" ? null : total,
7712
- line,
7713
- updatedAt
7714
- };
7715
- }
7716
- };
7717
- }
7718
- //#endregion
7719
- //#region src/engine/build-progress.ts
7720
- const ACTIVE_BUILD_KEY = "activeBuild";
7721
- const NO_BUILD_PROGRESS = {
7722
- step: () => {},
7723
- activity: () => {},
7724
- estimate: () => {},
7725
- miss: () => {},
7726
- hit: () => {},
7727
- deviceSetup: () => {},
7728
- deviceSetupKnown: () => void 0,
7729
- place: () => {},
7730
- waitingOn: () => {},
7731
- waitingFor: () => {},
7732
- output: () => {},
7733
- durations: () => ({}),
7734
- phase: () => null,
7735
- steps: () => null,
7736
- clear: () => {}
7737
- };
7738
- /** `writer`, with each record it writes also passed to `progress.output`. */
7739
- function tapBuildLog(writer, progress) {
7740
- if (progress === NO_BUILD_PROGRESS) return writer;
7741
- return {
7742
- get file() {
7743
- return writer.file;
7744
- },
7745
- write(record) {
7746
- progress.output(record);
7747
- return writer.write(record);
7748
- },
7749
- close: () => writer.close(),
7750
- get written() {
7751
- return writer.written;
7752
- },
7753
- get dropped() {
7754
- return writer.dropped;
7755
- },
7756
- get lastError() {
7757
- return writer.lastError;
7758
- }
7759
- };
7760
- }
7761
- function runFailureRecord({ code, message, phase }) {
7762
- return {
7763
- src: "build",
7764
- level: "error",
7765
- event: "run_failed",
7766
- ...code ? { code } : {},
7767
- ...phase ? { phase } : {},
7768
- msg: [code, message].filter(Boolean).join(": ") || "the run failed"
7769
- };
7770
- }
7771
- function thrownRunFailureRecord(error, phase) {
7772
- return runFailureRecord({
7773
- code: typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" ? error.code : void 0,
7774
- message: error instanceof Error ? error.message : String(error),
7775
- phase
7776
- });
7777
- }
7778
- const DETAIL_WRITE_MS = 2e3;
7779
- function toolLine(record) {
7780
- if (!record || typeof record !== "object") return null;
7781
- const { src, level, msg } = record;
7782
- return src === "build" && level === "debug" && typeof msg === "string" ? msg : null;
7783
- }
7784
- function writeBuildDetail(root, claimId, detail) {
7785
- const file = workspaceBuildDetailFile(root);
7786
- const tmp = `${file}.tmp-${process.pid}`;
7787
- mkdirSync(dirname(file), { recursive: true });
7788
- writeFileSync(tmp, JSON.stringify({
7789
- claimId,
7790
- detail
7791
- }));
7792
- renameSync(tmp, file);
7793
- }
7794
- const COLD_PHASES = [
7795
- "prebuild",
7796
- "pods",
7797
- "compile"
7798
- ];
7799
- const HIT_PHASES = ["install", "launch"];
7800
- function settledOutcome(phase, entered) {
7801
- if (COLD_PHASES.includes(phase)) return "cold";
7802
- if (phase === "device") return entered.some((entry) => entry.phase === "cache-lookup") ? "hit" : null;
7803
- return HIT_PHASES.includes(phase) ? "hit" : null;
7804
- }
7805
- function projectHistory(projectKey) {
7806
- try {
7807
- return readStats$1().record?.history?.[projectKey];
7808
- } catch {
7809
- return;
7810
- }
7811
- }
7812
- function startBuildProgress({ root, platform, slot, claim, now = Date.now, note = () => {} }) {
7813
- const startedAt = new Date(now()).toISOString();
7814
- const record = {
7815
- platform,
7816
- slot,
7817
- startedAt,
7818
- phase: "prepare",
7819
- phaseStartedAt: startedAt,
7820
- phases: [{
7821
- phase: "prepare",
7822
- startedAt
7823
- }],
7824
- claim: {
7825
- root: claim.root,
7826
- path: claim.path,
7827
- claimId: claim.claimId,
7828
- pid: claim.owner.pid
7829
- }
7830
- };
7831
- let warned = false;
7832
- const guard = (fn) => {
7833
- try {
7834
- fn();
7835
- } catch (error) {
7836
- if (warned) return;
7837
- warned = true;
7838
- note(`Build progress could not be recorded in the workspace state: ${error?.message || error}`);
7839
- }
7840
- };
7841
- const write = (before = (state) => state) => guard(() => updateWorkspaceState(root, (state) => ({
7842
- ...before(state),
7843
- [ACTIVE_BUILD_KEY]: record
7844
- })));
7845
- write(withInterruptedBuild);
7846
- let projectKey = null;
7847
- const parser = createBuildDetailParser();
7848
- let detailWrittenAt = -Infinity;
7849
- let detailTimer = null;
7850
- const flushDetail = () => {
7851
- detailTimer = null;
7852
- detailWrittenAt = now();
7853
- const detail = parser.detail(new Date(detailWrittenAt).toISOString());
7854
- if (detail) guard(() => writeBuildDetail(root, record.claim.claimId, detail));
7855
- };
7856
- const settle = (outcome) => {
7857
- record.outcome = outcome;
7858
- if (projectKey && platform !== "macos") record.estimate = estimateBuild(projectHistory(projectKey), platform, outcome, record.deviceSetup);
7859
- };
7860
- const waits = /* @__PURE__ */ new Map();
7861
- return {
7862
- step(phase) {
7863
- if (phase === record.phase) return;
7864
- const at = new Date(now()).toISOString();
7865
- record.phase = phase;
7866
- record.phaseStartedAt = at;
7867
- record.phases.push({
7868
- phase,
7869
- startedAt: at
7870
- });
7871
- if (phase !== "wait") delete record.waitingOn;
7872
- delete record.activity;
7873
- const outcome = record.outcome || platform === "macos" ? null : settledOutcome(phase, record.phases);
7874
- if (outcome) settle(outcome);
7875
- write();
7876
- },
7877
- activity(name, at) {
7878
- if ((record.activity?.name ?? null) === name) return;
7879
- if (name) record.activity = {
7880
- name,
7881
- startedAt: new Date(at ?? now()).toISOString()
7882
- };
7883
- else delete record.activity;
7884
- write();
7885
- },
7886
- estimate(key) {
7887
- if (platform === "macos") return;
7888
- projectKey = key;
7889
- record.estimate = estimateBuild(projectHistory(key), platform, record.outcome ?? null, record.outcome ? record.deviceSetup : void 0);
7890
- write();
7891
- },
7892
- miss(reason, provisional = false) {
7893
- record.missReason = reason;
7894
- settle("cold");
7895
- if (provisional) record.missProvisional = true;
7896
- else delete record.missProvisional;
7897
- write();
7898
- },
7899
- hit() {
7900
- delete record.missReason;
7901
- delete record.missProvisional;
7902
- settle("hit");
7903
- write();
7904
- },
7905
- deviceSetup(setup) {
7906
- if (setup === void 0) return;
7907
- record.deviceSetup = setup;
7908
- write();
7909
- },
7910
- deviceSetupKnown() {
7911
- return record.deviceSetup;
7912
- },
7913
- place(remote) {
7914
- const current = record.placement;
7915
- if (!remote) {
7916
- if (!current) return;
7917
- delete record.placement;
7918
- return write();
7919
- }
7920
- if (current?.host === remote.host && current.phase === remote.phase) return;
7921
- const at = new Date(now()).toISOString();
7922
- record.placement = {
7923
- host: remote.host,
7924
- phase: remote.phase,
7925
- startedAt: current?.host === remote.host ? current.startedAt : at,
7926
- phaseStartedAt: at
7927
- };
7928
- write();
7929
- },
7930
- waitingFor(info, kind) {
7931
- if (info) waits.set(info.kind, info);
7932
- else if (kind) waits.delete(kind);
7933
- else waits.clear();
7934
- const first = [...waits.values()].toSorted((a, b) => Date.parse(a.since) - Date.parse(b.since))[0];
7935
- if (first) record.waitingFor = first;
7936
- else delete record.waitingFor;
7937
- write();
7938
- },
7939
- waitingOn(holder) {
7940
- if (holder === null) {
7941
- if (!record.waitingOn) return;
7942
- delete record.waitingOn;
7943
- } else {
7944
- if (record.waitingOn?.path === holder) return;
7945
- record.waitingOn = { path: holder };
7946
- }
7947
- write();
7948
- },
7949
- output(line) {
7950
- const msg = toolLine(line);
7951
- if (msg === null || !parser.push(msg) || detailTimer) return;
7952
- const wait = detailWrittenAt + DETAIL_WRITE_MS - now();
7953
- if (wait <= 0) return flushDetail();
7954
- detailTimer = setTimeout(flushDetail, wait);
7955
- detailTimer.unref?.();
7956
- },
7957
- durations() {
7958
- return phaseDurations(record.phases, now());
7959
- },
7960
- phase() {
7961
- return record.phase;
7962
- },
7963
- steps() {
7964
- const detail = parser.detail("");
7965
- return detail?.unit === "steps" ? detail.total : null;
7966
- },
7967
- clear() {
7968
- if (detailTimer) clearTimeout(detailTimer);
7969
- detailTimer = null;
7970
- guard(() => rmSync(workspaceBuildDetailFile(root), { force: true }));
7971
- guard(() => {
7972
- clearWorkspaceStateKey(root, ACTIVE_BUILD_KEY, (value) => parseActiveBuild(value)?.claim.claimId === record.claim.claimId);
7973
- });
7974
- }
7975
- };
7976
- }
7977
- function phaseDurations(phases, now) {
7978
- const out = {};
7979
- phases.forEach(({ phase, startedAt }, index) => {
7980
- const start = Date.parse(startedAt);
7981
- const next = phases[index + 1];
7982
- const end = next ? Date.parse(next.startedAt) : now;
7983
- if (!Number.isFinite(start) || !Number.isFinite(end)) return;
7984
- out[phase] = (out[phase] ?? 0) + Math.max(0, end - start);
7985
- });
7986
- return out;
7987
- }
7988
- function completedPhaseDurations(phases) {
7989
- if (phases.length < 2) return void 0;
7990
- const durations = phaseDurations(phases.slice(0, -1), Date.parse(phases.at(-1).startedAt));
7991
- return Object.keys(durations).length ? durations : void 0;
7992
- }
7993
- const HISTORY_FIELDS = [
7994
- "devicePlacement",
7995
- "platform",
7996
- "status",
7997
- "configuration",
7998
- "fingerprint",
7999
- "cacheKey",
8000
- "cacheHit",
8001
- "cacheSkipped",
8002
- "durationMs",
8003
- "startedAt",
8004
- "errorCode",
8005
- "missReason",
8006
- "buildMachine",
8007
- "builtOn",
8008
- "offloadedTo",
8009
- "offloadFallback",
8010
- "diagnostics",
8011
- "compileSteps"
8012
- ];
8013
- function withHistoryEntry(state, platform, entry) {
8014
- const saved = state[BUILD_HISTORY_KEY];
8015
- const history = saved && typeof saved === "object" && !Array.isArray(saved) ? saved : {};
8016
- const list = Array.isArray(history[platform]) ? history[platform] : [];
8017
- return {
8018
- ...state,
8019
- [BUILD_HISTORY_KEY]: {
8020
- ...history,
8021
- [platform]: [entry, ...list].slice(0, BUILD_HISTORY_LIMIT)
8022
- }
8023
- };
8024
- }
8025
- function finishedResult(record) {
8026
- if (record.status === "ok") return "succeeded";
8027
- return record.errorCode === "STIM_CANCELLED" ? "cancelled" : "failed";
8028
- }
8029
- function recordFinishedBuild(root, record, { update = updateWorkspaceState, now = Date.now, artifactOnly = false } = {}) {
8030
- const platform = record.platform;
8031
- update(root, (state) => {
8032
- const active = parseActiveBuild(state[ACTIVE_BUILD_KEY]);
8033
- const own = active?.platform === platform ? active : null;
8034
- const entry = {
8035
- ...Object.fromEntries(HISTORY_FIELDS.filter((key) => key in record).map((key) => [key, record[key]])),
8036
- result: finishedResult(record),
8037
- slot: own?.slot ?? "default",
8038
- phases: own ? phaseDurations(own.phases, now()) : {}
8039
- };
8040
- if (artifactOnly || platform === "macos") return withHistoryEntry(state, platform, entry);
8041
- const slot = own?.slot ?? "default";
8042
- const slots = isJsonObject(state.deviceSlots) ? state.deviceSlots : {};
8043
- const savedSlot = isJsonObject(slots[slot]) ? slots[slot] : {};
8044
- const savedDevice = slot === "default" ? state[platform] : savedSlot[platform];
8045
- if (record.devicePlacement || isJsonObject(savedDevice)) {
8046
- const device = isJsonObject(savedDevice) ? { ...savedDevice } : {};
8047
- if (record.devicePlacement) device.devicePlacement = record.devicePlacement;
8048
- else delete device.devicePlacement;
8049
- state = slot === "default" ? {
8050
- ...state,
8051
- [platform]: device
8052
- } : {
8053
- ...state,
8054
- deviceSlots: {
8055
- ...slots,
8056
- [slot]: {
8057
- ...savedSlot,
8058
- [platform]: device
8059
- }
8060
- }
8061
- };
8062
- }
8063
- return withHistoryEntry({
8064
- ...state,
8065
- lastBuild: record,
8066
- [LAST_BUILD_KEYS[platform]]: record
8067
- }, platform, entry);
8068
- });
8069
- }
8070
- /** Adds one phase's duration to the recorded run of `platform` that started at `startedAt`. */
8071
- function recordBuildPhase(root, platform, startedAt, phase, ms) {
8072
- updateWorkspaceState(root, (state) => {
8073
- const saved = state[BUILD_HISTORY_KEY];
8074
- const list = isJsonObject(saved) && Array.isArray(saved[platform]) ? saved[platform] : [];
8075
- const index = list.findIndex((entry) => isJsonObject(entry) && entry.startedAt === startedAt);
8076
- const entry = list[index];
8077
- if (!isJsonObject(entry)) return state;
8078
- const phases = isJsonObject(entry.phases) ? entry.phases : {};
8079
- const next = [...list];
8080
- next[index] = {
8081
- ...entry,
8082
- phases: {
8083
- ...phases,
8084
- [phase]: Math.round(ms)
8085
- }
8086
- };
8087
- return {
8088
- ...state,
8089
- [BUILD_HISTORY_KEY]: {
8090
- ...saved,
8091
- [platform]: next
8092
- }
8093
- };
8094
- });
8095
- }
8096
- function withInterruptedBuild(state) {
8097
- const left = parseActiveBuild(state[ACTIVE_BUILD_KEY]);
8098
- if (!left) return state;
8099
- const last = state[BUILD_HISTORY_KEY]?.[left.platform]?.[0];
8100
- if (Date.parse(String(last?.startedAt)) >= Date.parse(left.startedAt)) return state;
8101
- return withHistoryEntry(state, left.platform, {
8102
- platform: left.platform,
8103
- status: "failed",
8104
- result: "interrupted",
8105
- slot: left.slot,
8106
- configuration: null,
8107
- fingerprint: null,
8108
- cacheKey: null,
8109
- cacheHit: false,
8110
- cacheSkipped: false,
8111
- durationMs: null,
8112
- startedAt: left.startedAt,
8113
- phases: {
8114
- ...phaseDurations(left.phases, Date.parse(left.phaseStartedAt)),
8115
- [left.phase]: 0
8116
- }
8117
- });
8118
- }
8119
- function parseActiveBuild(value) {
8120
- if (!value || typeof value !== "object" || Array.isArray(value)) return null;
8121
- const record = value;
8122
- const claim = record.claim;
8123
- if (record.platform !== "ios" && record.platform !== "android" && record.platform !== "macos") return null;
8124
- if (!isPhase(record.phase)) return null;
8125
- if (typeof record.startedAt !== "string" || typeof record.phaseStartedAt !== "string") return null;
8126
- if (!claim || typeof claim.root !== "string" || typeof claim.claimId !== "string") return null;
8127
- const phases = Array.isArray(record.phases) ? record.phases.filter((entry) => isPhase(entry?.phase) && typeof entry?.startedAt === "string") : [];
8128
- const missReason = parseMissReason(record.missReason);
8129
- const placement = parsePlacement(record.placement);
8130
- const waitingOn = parseWaitingOn(record.waitingOn);
8131
- const waitingFor = parseWaitingFor(record.waitingFor);
8132
- const estimate = parseEstimate(record.estimate);
8133
- const activity = parseActivity(record.activity);
8134
- return {
8135
- platform: record.platform,
8136
- slot: typeof record.slot === "string" ? record.slot : "default",
8137
- startedAt: record.startedAt,
8138
- phase: record.phase,
8139
- phaseStartedAt: record.phaseStartedAt,
8140
- phases,
8141
- ...missReason ? { missReason } : {},
8142
- ...missReason && record.missProvisional === true ? { missProvisional: true } : {},
8143
- ...placement ? { placement } : {},
8144
- ...waitingOn ? { waitingOn } : {},
8145
- ...waitingFor ? { waitingFor } : {},
8146
- ...typeof record.deviceSetup === "boolean" ? { deviceSetup: record.deviceSetup } : {},
8147
- ...record.outcome === "hit" || record.outcome === "cold" ? { outcome: record.outcome } : {},
8148
- ...estimate ? { estimate } : {},
8149
- ...activity ? { activity } : {},
8150
- claim: {
8151
- root: claim.root,
8152
- path: typeof claim.path === "string" ? claim.path : "",
8153
- claimId: claim.claimId,
8154
- pid: typeof claim.pid === "number" ? claim.pid : 0
8155
- }
8156
- };
8157
- }
8158
- function parsePlacement(value) {
8159
- if (!value || typeof value !== "object") return null;
8160
- const { host, phase, startedAt, phaseStartedAt } = value;
8161
- if ([
8162
- host,
8163
- phase,
8164
- startedAt,
8165
- phaseStartedAt
8166
- ].some((field) => typeof field !== "string")) return null;
8167
- return {
8168
- host,
8169
- phase,
8170
- startedAt,
8171
- phaseStartedAt
8172
- };
8173
- }
8174
- function parseWaitingFor(value) {
8175
- if (!value || typeof value !== "object") return null;
8176
- const { kind, inUse, max, since } = value;
8177
- if (kind !== "build-slot" && kind !== "device-slot") return null;
8178
- if (!Number.isInteger(inUse) || inUse < 0 || !Number.isInteger(max) || max <= 0) return null;
8179
- if (typeof since !== "string" || !Number.isFinite(Date.parse(since))) return null;
8180
- return {
8181
- kind,
8182
- inUse,
8183
- max,
8184
- since
8185
- };
8186
- }
8187
- function parseActivity(value) {
8188
- if (!value || typeof value !== "object") return null;
8189
- const { name, startedAt } = value;
8190
- if (name !== "booting" && name !== "bundling" && name !== "waiting-ready") return null;
8191
- return typeof startedAt === "string" ? {
8192
- name,
8193
- startedAt
8194
- } : null;
8195
- }
8196
- function parseWaitingOn(value) {
8197
- const path = value?.path;
8198
- return typeof path === "string" && path ? { path } : null;
8199
- }
8200
- function parseEstimate(value) {
8201
- if (!value || typeof value !== "object") return null;
8202
- const { outcome, expectedMs, basis, phaseMs, planned } = value;
8203
- if (outcome !== null && outcome !== "hit" && outcome !== "cold") return null;
8204
- if (expectedMs !== null && typeof expectedMs !== "number") return null;
8205
- if (typeof basis !== "number" || !Array.isArray(planned) || !phaseMs || typeof phaseMs !== "object") return null;
8206
- const durations = Object.entries(phaseMs).filter((entry) => isPhase(entry[0]) && typeof entry[1] === "number");
8207
- return {
8208
- outcome,
8209
- expectedMs,
8210
- basis,
8211
- phaseMs: Object.fromEntries(durations),
8212
- planned: planned.filter(isPhase)
8213
- };
8214
- }
8215
- function isPhase(value) {
8216
- return BUILD_PHASES.includes(value);
8217
- }
8218
- function activeBuildState(claim, survey = (0, ownership_claim_exports.readClaimSet)(claim.root)) {
8219
- if (survey.live.some((holder) => holder.claimId === claim.claimId)) return "running";
8220
- if (survey.dead.some((holder) => holder.claimId === claim.claimId)) return "stale";
8221
- return survey.unresolved.length ? "unknown" : "stale";
8222
- }
8223
- function median(values) {
8224
- if (!values.length) return null;
8225
- const sorted = values.toSorted((a, b) => a - b);
8226
- const mid = Math.floor(sorted.length / 2);
8227
- return sorted.length % 2 ? sorted[mid] : Math.round((sorted[mid - 1] + sorted[mid]) / 2);
8228
- }
8229
- function estimateBuild(history, platform, known, deviceSetup) {
8230
- const lists = platform === "macos" ? void 0 : history?.[platform];
8231
- const outcome = known ?? latestOutcome(lists);
8232
- const samples = matchDeviceSetup(outcome && lists?.[outcome] || [], deviceSetup);
8233
- const phaseMs = {};
8234
- const planned = [];
8235
- for (const phase of BUILD_PHASES) {
8236
- const durations = samples.flatMap((sample) => phase in sample.phases ? [sample.phases[phase]] : []);
8237
- const ms = median(durations);
8238
- if (ms === null) continue;
8239
- phaseMs[phase] = ms;
8240
- if (durations.length * 2 >= samples.length) planned.push(phase);
8241
- }
8242
- return {
8243
- outcome,
8244
- expectedMs: median(samples.map((sample) => sample.durationMs)),
8245
- basis: samples.length,
8246
- phaseMs,
8247
- planned
8248
- };
8249
- }
8250
- const MIN_TAGGED_SAMPLES = 3;
8251
- function matchDeviceSetup(samples, deviceSetup) {
8252
- if (deviceSetup === void 0) return samples;
8253
- const tagged = samples.filter((sample) => sample.deviceSetup === deviceSetup);
8254
- if (deviceSetup || tagged.length >= MIN_TAGGED_SAMPLES) return tagged;
8255
- return samples.filter((sample) => sample.deviceSetup !== true);
8256
- }
8257
- function latestOutcome(lists) {
8258
- let latest = null;
8259
- for (const outcome of ["hit", "cold"]) {
8260
- const last = lists?.[outcome]?.at(-1);
8261
- const at = last ? Date.parse(last.at) : NaN;
8262
- if (Number.isFinite(at) && (!latest || at > latest.at)) latest = {
8263
- outcome,
8264
- at
8265
- };
8266
- }
8267
- return latest?.outcome ?? null;
8268
- }
8269
- function buildReport(record, { state, history }) {
8270
- const estimate = record.estimate ?? estimateBuild(history, record.platform, record.outcome ?? null, record.outcome ? record.deviceSetup : void 0);
8271
- const plannedPhases = estimate.planned.flatMap((phase) => {
8272
- const expectedMs = estimate.phaseMs[phase];
8273
- return expectedMs === void 0 ? [] : [{
8274
- phase,
8275
- expectedMs
8276
- }];
8277
- });
8278
- const completedPhaseMs = completedPhaseDurations(record.phases);
8279
- return {
8280
- platform: record.platform,
8281
- slot: record.slot,
8282
- state,
8283
- phase: record.phase,
8284
- startedAt: record.startedAt,
8285
- phaseStartedAt: record.phaseStartedAt,
8286
- outcome: estimate.outcome,
8287
- outcomeKnown: record.outcome !== void 0,
8288
- ...record.outcome && record.phases.some(({ phase }) => phase === "cache-lookup") ? { cacheLookupOutcome: record.outcome === "hit" ? "hit" : "miss" } : {},
8289
- expectedMs: estimate.expectedMs,
8290
- expectedPhaseMs: estimate.phaseMs[record.phase] ?? null,
8291
- ...completedPhaseMs ? { completedPhaseMs } : {},
8292
- basis: estimate.basis,
8293
- plannedPhases: plannedPhases.length ? plannedPhases : null,
8294
- ...record.missReason ? { missReason: record.missReason } : {},
8295
- ...record.missReason && record.missProvisional ? { missProvisional: true } : {},
8296
- placement: record.placement ?? "local",
8297
- ...record.waitingOn ? { waitingOn: record.waitingOn } : {},
8298
- ...record.waitingFor ? { waitingFor: record.waitingFor } : {},
8299
- ...record.activity ? { activity: { ...record.activity } } : {}
8300
- };
8301
- }
8302
- /**
8303
- * The run's `bundling` activity with Metro's percent, when the request Metro reports in flight is the one the activity
8304
- * started on: same platform and same start, since both come from the request's `bundle_response_started` record.
8305
- */
8306
- function bundlingActivity(activity, platform, bundle) {
8307
- if (activity?.name !== "bundling" || !bundle?.bundling || bundle.percent === void 0) return activity;
8308
- if (bundle.platform !== platform || bundle.startedAt !== activity.startedAt) return activity;
8309
- return {
8310
- ...activity,
8311
- percent: bundle.percent
8312
- };
8313
- }
8314
- function remainingText(remainingMs) {
8315
- return remainingMs < 6e4 ? "under a minute left" : `about ${Math.ceil(remainingMs / 6e4)} min left`;
8316
- }
8317
- function activityText({ name, percent, startedAt }, now) {
8318
- return `${name === "booting" ? "booting" : name === "waiting-ready" ? "waiting for app ready" : percent === void 0 ? "bundling JS" : `bundling JS ${percent}%`}, ${formatElapsed(Math.max(0, now - Date.parse(startedAt)))}`;
8319
- }
8320
- function buildStatusLine(report, now) {
8321
- const slot = report.slot === "default" ? "" : ` [${report.slot}]`;
8322
- if (report.state === "stale") return `build: stale ${report.platform}${slot} record from ${report.startedAt} (its run is gone; the next run replaces it)`;
8323
- const elapsedMs = Math.max(0, now - Date.parse(report.startedAt));
8324
- const remote = typeof report.placement === "object" ? ` on ${report.placement.host} (${report.placement.phase}, ${formatElapsed(Math.max(0, now - Date.parse(report.placement.phaseStartedAt)))})` : "";
8325
- const waiting = report.waitingOn ? ` on ${report.waitingOn.path}` : "";
8326
- const activity = report.activity ? ` (${activityText(report.activity, now)})` : "";
8327
- const head = `build: ${report.platform}${slot} ${report.phase}${activity}${waiting}${remote}, ${formatElapsed(elapsedMs)} elapsed`;
8328
- if (report.state === "unknown") return `${head} (its native-run claim cannot be resolved, so it may not be running)`;
8329
- if (report.expectedMs === null) return head;
8330
- const basis = `median of ${plural(report.basis, `${report.outcome} run`)}`;
8331
- const remainingMs = report.expectedMs - elapsedMs;
8332
- return remainingMs > 0 ? `${head} -- ${remainingText(remainingMs)} (${basis})` : `${head} (usually ~${formatElapsed(report.expectedMs)}, ${basis})`;
8333
- }
8334
- //#endregion
8335
- //#region src/commands/gc/idle.ts
8336
- function findOwnedDeviceActivity({ config, sims, androidSerial, readActivity, deadProjects = [] }) {
8337
- const booted = new Map(sims.filter((sim) => sim.state === "Booted").map((sim) => [sim.udid, sim.name]));
8338
- const dead = new Set(deadProjects);
8339
- const found = [];
8340
- for (const [project, record] of Object.entries(config?.projects ?? {})) {
8341
- if (dead.has(project)) continue;
8342
- for (const { slot, platforms } of projectDeviceSlots(record)) {
8343
- const ios = platforms.ios;
8344
- if (ios?.owned && ios.deviceUdid && booted.has(ios.deviceUdid)) found.push({
8345
- kind: "ios",
8346
- id: ios.deviceUdid,
8347
- name: booted.get(ios.deviceUdid) ?? ios.deviceUdid,
8348
- project,
8349
- slot,
8350
- activity: readActivity({
8351
- platform: "ios",
8352
- id: ios.deviceUdid,
8353
- slot,
8354
- workspace: project
8355
- })
8356
- });
8357
- const android = platforms.android;
8358
- const serial = android?.owned && android.avdName ? androidSerial(android.avdName) : null;
8359
- if (android?.avdName && serial) found.push({
8360
- kind: "android",
8361
- id: android.avdName,
8362
- name: android.avdName,
8363
- project,
8364
- slot,
8365
- activity: readActivity({
8366
- platform: "android",
8367
- id: serial,
8368
- slot,
8369
- workspace: project
8370
- })
8371
- });
8372
- }
8373
- }
8374
- return found;
8375
- }
8376
- function findIdleDevices({ buildInProgress, now, ...inputs }) {
8377
- return idleDevicesFromActivity(findOwnedDeviceActivity(inputs), buildInProgress, now);
8378
- }
8379
- function idleDevicesFromActivity(devices, buildInProgress, now) {
8380
- return devices.filter((device) => device.activity.state === "idle").map(({ kind, id, name, project, slot, activity }) => ({
8381
- kind,
8382
- id,
8383
- name,
8384
- project,
8385
- slot,
8386
- lastActivityAt: activity.lastActivityAt ?? null,
8387
- idleForMs: idleForMs(activity, now),
8388
- buildInProgress: buildInProgress(project)
8389
- }));
8390
- }
8391
- function idleShutdownCandidates(devices, idleMs) {
8392
- return devices.filter((d) => !d.buildInProgress && d.idleForMs !== null && d.idleForMs >= idleMs);
8393
- }
8394
- function parseIdleDuration(value) {
8395
- const match = /^([1-9][0-9]*)(m|h|d)$/.exec(value.trim());
8396
- if (!match) return null;
8397
- const unit = {
8398
- m: 6e4,
8399
- h: 36e5,
8400
- d: 864e5
8401
- }[match[2]];
8402
- return Number(match[1]) * unit;
8403
- }
8404
- function workspaceBuildInProgress(project) {
8405
- const record = parseActiveBuild(readWorkspaceState$1(project)?.[ACTIVE_BUILD_KEY]);
8406
- if (!record) return false;
8407
- try {
8408
- return activeBuildState(record.claim) !== "stale";
8409
- } catch {
8410
- return true;
8411
- }
8412
- }
8413
- function deviceActivityInputs(config, sims, now, env) {
8414
- const resolve = ownedAvdSerialResolver({ timeoutMs: 5e3 });
8415
- return {
8416
- config,
8417
- sims,
8418
- readActivity: createActivityReader({
8419
- now,
8420
- env
8421
- }),
8422
- androidSerial: (avdName) => {
8423
- try {
8424
- return resolve(avdName).serial ?? null;
8425
- } catch {
8426
- return null;
8427
- }
8428
- }
8429
- };
8430
- }
8431
- function collectOwnedDeviceActivity(config, sims, now = Date.now(), env) {
8432
- return findOwnedDeviceActivity(deviceActivityInputs(config, sims, now, env));
8433
- }
8434
- function collectIdleDevices(config, sims, deadProjects, now = Date.now(), env) {
8435
- return findIdleDevices({
8436
- ...deviceActivityInputs(config, sims, now, env),
8437
- deadProjects,
8438
- now,
8439
- buildInProgress: workspaceBuildInProgress
8440
- });
8441
- }
8442
- async function deviceActivityInputsAsync(config, sims, now, env, deadProjects = [], signal, operation) {
8443
- const resolve = ownedAvdSerialResolverAsync({
8444
- timeoutMs: 5e3,
8445
- signal,
8446
- operation
8447
- });
8448
- const serials = /* @__PURE__ */ new Map();
8449
- for (const [project, record] of Object.entries(config?.projects ?? {})) {
8450
- signal?.throwIfAborted();
8451
- if (deadProjects.includes(project)) continue;
8452
- for (const { platforms } of projectDeviceSlots(record)) {
8453
- const android = platforms.android;
8454
- if (!android?.owned || !android.avdName || serials.has(android.avdName)) continue;
8455
- try {
8456
- serials.set(android.avdName, (await resolve(android.avdName)).serial ?? null);
8457
- } catch (error) {
8458
- if (error instanceof WorkspaceProcessCleanupError) throw error;
8459
- signal?.throwIfAborted();
8460
- serials.set(android.avdName, null);
8461
- }
8462
- }
8463
- }
8464
- return {
8465
- config,
8466
- sims,
8467
- readActivity: createActivityReaderAsync({
8468
- now,
8469
- env,
8470
- signal,
8471
- operation
8472
- }),
8473
- androidSerial: (name) => serials.get(name) ?? null
8474
- };
8475
- }
8476
- async function collectOwnedDeviceActivityAsync(config, sims, now = Date.now(), env, signal, operation) {
8477
- const devices = findOwnedDeviceActivity(await deviceActivityInputsAsync(config, sims, now, env, [], signal, operation));
8478
- return Promise.all(devices.map(async (device) => Object.assign({}, device, { activity: await device.activity })));
8479
- }
8480
- async function collectIdleDevicesAsync(config, sims, deadProjects, now = Date.now(), env, signal, operation) {
8481
- const pending = findOwnedDeviceActivity({
8482
- ...await deviceActivityInputsAsync(config, sims, now, env, deadProjects, signal, operation),
8483
- deadProjects
8484
- });
8485
- return idleDevicesFromActivity(await Promise.all(pending.map(async (device) => Object.assign({}, device, { activity: await device.activity }))), workspaceBuildInProgress, now);
8486
- }
8487
- function idleDeviceLines(devices) {
8488
- if (devices.length === 0) return [];
8489
- return [`Idle owned devices (${devices.length}) - booted with no driver, claim, or recent activity:`, ...devices.map((d) => {
8490
- const slot = d.slot === "default" ? "" : ` [${d.slot}]`;
8491
- const idle = d.idleForMs === null ? "idle for an unknown time" : `idle ${formatLongDuration(d.idleForMs)}`;
8492
- const build = d.buildInProgress ? ", build in progress" : "";
8493
- return ` ${d.kind}${slot} ${d.name} (${d.project}) -- ${idle}${build}`;
8494
- })];
8495
- }
8496
- async function shutDownIdleDevices(devices, idleMs, recollect, { log, record } = GC_OUTPUT, signal, operation) {
8497
- signal?.throwIfAborted();
8498
- const candidates = idleShutdownCandidates(devices, idleMs);
8499
- const shutDown = [];
8500
- if (candidates.length === 0) {
8501
- log(chalk.dim(`No owned device has been idle for ${formatLongDuration(idleMs)} or more.`));
8502
- return {
8503
- failures: 0,
8504
- shutDown
8505
- };
8506
- }
8507
- const fresh = idleShutdownCandidates(await recollect(), idleMs);
8508
- signal?.throwIfAborted();
8509
- let failures = 0;
8510
- for (const device of candidates) {
8511
- signal?.throwIfAborted();
8512
- const what = `${device.kind} ${device.name}`;
8513
- const current = fresh.find((d) => d.kind === device.kind && d.id === device.id && d.project === device.project && d.slot === device.slot);
8514
- if (!current) {
8515
- log(chalk.dim(`Kept ${what}: it is no longer idle for ${formatLongDuration(idleMs)}.`));
8516
- record("idleDevice", "kept", device.name, {
8517
- id: device.id,
8518
- detail: `it is no longer idle for ${formatLongDuration(idleMs)}`
8519
- });
8520
- continue;
8521
- }
8522
- const outcome = device.kind === "ios" ? await teardownOwnedIosSim(device.id, {
8523
- label: device.name,
8524
- operation
8525
- }) : await teardownOwnedAvd(device.id, {
8526
- owner: {
8527
- projectPath: device.project,
8528
- slot: device.slot
8529
- },
8530
- operation
8531
- });
8532
- if (outcome.status === "torn-down") {
8533
- shutDown.push(device);
8534
- log(chalk.green(`Shut down ${what}, idle ${formatLongDuration(current.idleForMs)}`));
8535
- record("idleDevice", "done", device.name, {
8536
- id: device.id,
8537
- detail: `idle ${formatLongDuration(current.idleForMs)}`
8538
- });
8539
- } else if (outcome.status === "missing") log(chalk.dim(`${what} is already gone; nothing to shut down.`));
8540
- else if (outcome.status === "skipped") {
8541
- log(chalk.yellow(`Skipped ${what}: ${outcome.reason}`));
8542
- record("idleDevice", "kept", device.name, {
8543
- id: device.id,
8544
- detail: outcome.reason
8545
- });
8546
- } else {
8547
- failures++;
8548
- log(chalk.red(`Failed to shut down ${what}: ${outcome.reason}`));
8549
- record("idleDevice", "failed", device.name, {
8550
- id: device.id,
8551
- detail: outcome.reason
8552
- });
8553
- }
8554
- }
8555
- return {
8556
- failures,
8557
- shutDown
8558
- };
8559
- }
8560
- //#endregion
8561
- //#region src/maintenance/log.ts
8562
- function openRetainedLog(file, settings) {
8563
- ensurePrivateDir(dirname(file));
8564
- const rotated = rotatedLogPath(file);
8565
- withDirLock(`${file}.lock`, () => {
8566
- try {
8567
- if (Date.now() - statSync(rotated).mtimeMs > settings.logRetentionDays * 864e5) unlinkSync(rotated);
8568
- } catch {}
8569
- });
8570
- return createNdjsonWriter(file, { maxBytes: settings.logMaxMb * 1024 ** 2 });
8571
- }
8572
- function maintenanceLogger(settings, pass, trigger) {
8573
- const file = maintenanceNdjsonFile();
8574
- const writer = openRetainedLog(file, settings);
8575
- return {
8576
- write(event, level, msg, fields = {}) {
8577
- if (level === "debug" && !settings.logChecks) return null;
8578
- const record = {
8579
- ...fields,
8580
- ts: Date.now(),
8581
- src: "maintenance",
8582
- level,
8583
- msg,
8584
- event,
8585
- pass,
8586
- trigger,
8587
- mode: settings.mode === "on" ? "on" : "report"
8588
- };
8589
- if (!writer.write(record)) throw writer.lastError ?? /* @__PURE__ */ new Error(`Could not append ${file}`);
8590
- if (typeof fields.workspace === "string" && [
8591
- "maintenance_action",
8592
- "maintenance_failure",
8593
- "maintenance_skip"
8594
- ].includes(event)) {
8595
- const workspace = createNdjsonWriter(join(workspaceLogsDir(fields.workspace), MAINTENANCE_LOG_NAME), { maxBytes: LOG_ROTATE_BYTES });
8596
- try {
8597
- if (!workspace.write(record)) throw workspace.lastError ?? /* @__PURE__ */ new Error("Could not append workspace maintenance log");
8598
- } finally {
8599
- workspace.close();
8600
- }
8601
- }
8602
- return record;
8603
- },
8604
- close: () => writer.close()
8605
- };
8606
- }
8607
- //#endregion
8608
- //#region src/commands/gc/log.ts
8609
- const VERBS = {
8610
- done: "Cleared",
8611
- kept: "Kept",
8612
- failed: "Failed to clear"
8613
- };
8614
- function gcLogRecord(run, result, ts) {
8615
- const path = [result.id, result.label].find((value) => value !== null && isAbsolute(value)) ?? null;
8616
- const size = result.bytes ? ` (${formatBytes(result.bytes)})` : "";
8617
- const why = result.detail ? `: ${result.detail}` : "";
8618
- return {
8619
- ts,
8620
- src: "gc",
8621
- level: result.status === "failed" ? "error" : "info",
8622
- msg: `${VERBS[result.status]} ${result.kind} ${result.label}${size}${why}`,
8623
- event: "gc_item",
8624
- runId: run.runId ?? runId(),
8625
- trigger: run.trigger,
8626
- ...run.via === void 0 ? {} : { via: run.via },
8627
- ...run.options === void 0 ? {} : { options: run.options },
8628
- kind: result.kind,
8629
- status: result.status,
8630
- label: result.label,
8631
- id: result.id,
8632
- path,
8633
- bytes: result.bytes,
8634
- detail: result.detail
7397
+ ts,
7398
+ src: "gc",
7399
+ level: result.status === "failed" ? "error" : "info",
7400
+ msg: `${VERBS[result.status]} ${result.kind} ${result.label}${size}${why}`,
7401
+ event: "gc_item",
7402
+ runId: run.runId ?? runId(),
7403
+ trigger: run.trigger,
7404
+ ...run.via === void 0 ? {} : { via: run.via },
7405
+ ...run.options === void 0 ? {} : { options: run.options },
7406
+ kind: result.kind,
7407
+ status: result.status,
7408
+ label: result.label,
7409
+ id: result.id,
7410
+ path,
7411
+ bytes: result.bytes,
7412
+ detail: result.detail
8635
7413
  };
8636
7414
  }
8637
7415
  function openGcLog(run) {
@@ -8704,6 +7482,38 @@ function gcOptionsOf(opts) {
8704
7482
  };
8705
7483
  }
8706
7484
  //#endregion
7485
+ //#region src/maintenance/pressure.ts
7486
+ /**
7487
+ * The free-disk floor of each level in MiB. Level 2 has no floor of its own: it runs when level 1 left free disk
7488
+ * under the budget floor, and stops once free disk is back above it. Its urgent threshold only sets the reported level.
7489
+ */
7490
+ function levelFloors(budget, settings) {
7491
+ const floor = Math.max(budget.minFreeDiskMb, budget.hardFloorDiskMb);
7492
+ return {
7493
+ 1: floor,
7494
+ 2: floor,
7495
+ 3: Math.max(settings.criticalFreeDiskGb * 1024, budget.hardFloorDiskMb),
7496
+ urgent: settings.urgentFreeDiskGb * 1024
7497
+ };
7498
+ }
7499
+ function diskPressureLevel(freeMb, budget, settings) {
7500
+ const floors = levelFloors(budget, settings);
7501
+ if (freeMb < floors[3]) return 3;
7502
+ if (freeMb < floors.urgent) return 2;
7503
+ return freeMb < floors[1] ? 1 : 0;
7504
+ }
7505
+ function lowestFree(disk) {
7506
+ return disk.length ? Math.min(...disk.map((volume) => volume.freeMb)) : Infinity;
7507
+ }
7508
+ /** Whether an action planned at a pressure level may still run with this much free disk. */
7509
+ function levelStillShort(action, freeMb, budget, settings) {
7510
+ return action.level === void 0 || freeMb < levelFloors(budget, settings)[action.level];
7511
+ }
7512
+ /** The protection window in milliseconds: protectRecentHours, or maintenance.pressureProtectMinutes for actions of level 2 and 3. */
7513
+ function protectWindowMs(action, settings) {
7514
+ return action.level === void 0 ? settings.protectRecentHours * 36e5 : settings.pressureProtectMinutes * 6e4;
7515
+ }
7516
+ //#endregion
8707
7517
  //#region src/supervisor/stop-cause.ts
8708
7518
  function requestDevServerStop(root, processToken, requester) {
8709
7519
  if (!processToken) return;
@@ -9229,9 +8039,10 @@ async function reclaimWorkspaceOutputs({ root, dryRun, now, note, record, diskRe
9229
8039
  targets: clearable.map((entry) => entry.projectRoot),
9230
8040
  failures: 0
9231
8041
  };
8042
+ const locked = report.workspaces.filter((entry) => keptByLock(entry) && entry.projectRoot !== null && other(entry.projectRoot));
9232
8043
  const targets = [];
9233
8044
  let failures = 0;
9234
- for (const entry of clearable) {
8045
+ for (const entry of [...locked, ...clearable]) {
9235
8046
  const result = await clearWorkspaceOutputs({
9236
8047
  ...report,
9237
8048
  workspaces: [entry]
@@ -9256,13 +8067,26 @@ function cacheLabel(cache) {
9256
8067
  function trimmableCaches(env = process.env, cwd) {
9257
8068
  return planCacheEmptying(discoverCaches(env, cwd), false, env).filter((cache) => cache.prune !== "report-only" && cache.prune !== "atomic" && !cache.machineGlobal);
9258
8069
  }
9259
- async function reclaimStaleCacheEntries({ dryRun, note, record, env, cwd, signal }) {
8070
+ async function reclaimStaleCacheEntries({ dryRun, note, record, env, cwd, now, freeMb, signal }) {
9260
8071
  const caches = trimmableCaches(env, cwd);
9261
8072
  if (dryRun) return {
9262
8073
  targets: caches.map(cacheLabel),
9263
8074
  failures: 0
9264
8075
  };
8076
+ const current = {
8077
+ at: now,
8078
+ freeMb,
8079
+ marker: sweepMarker(caches, 14)
8080
+ };
8081
+ if (canSkipEmptySweep(readEmptySweep(), current)) {
8082
+ note(chalk.dim(phaseLine("budget", `cache trim skipped: the last one, under ${EMPTY_SWEEP_WINDOW_MS / 6e4} minutes ago with the same caches and about the same free disk, found nothing older than 14d`)));
8083
+ return {
8084
+ targets: [],
8085
+ failures: 0
8086
+ };
8087
+ }
9265
8088
  const { trimmed, failures } = await trimCaches(caches, 14, stepOutput(note, record), signal);
8089
+ writeEmptySweep(trimmed.length === 0 && failures === 0 ? current : null);
9266
8090
  return {
9267
8091
  targets: trimmed.map(cacheLabel),
9268
8092
  failures
@@ -9310,23 +8134,22 @@ async function spaceUsers(env = process.env, signal, operation) {
9310
8134
  ["Android emulators", join(home, ".android", "avd")]
9311
8135
  ];
9312
8136
  const measured = /* @__PURE__ */ new Set();
9313
- const users = [];
8137
+ const targets = [];
9314
8138
  for (const [label, path] of candidates) {
9315
8139
  const canonical = canonicalPath(path);
9316
8140
  if (measured.has(canonical) || [...measured].some((dir) => canonical.startsWith(`${dir}${sep}`))) continue;
9317
8141
  measured.add(canonical);
9318
- if (!existsSync(path)) continue;
9319
- const bytes = await measuredDirectorySizeAsync(path, {
9320
- timeoutMs: USAGE_TIMEOUT_MS,
9321
- signal,
9322
- operation
9323
- });
9324
- if (bytes) users.push({
9325
- label: `${label} ${path}`,
9326
- bytes
9327
- });
8142
+ if (existsSync(path)) targets.push([label, path]);
9328
8143
  }
9329
- return users.toSorted((a, b) => b.bytes - a.bytes).map(({ label, bytes }) => `${label}: ${formatBytes(bytes)}`);
8144
+ const sizes = await Promise.all(targets.map(([, path]) => measuredDirectorySizeAsync(path, {
8145
+ timeoutMs: USAGE_TIMEOUT_MS,
8146
+ signal,
8147
+ operation
8148
+ })));
8149
+ return targets.flatMap(([label, path], index) => sizes[index] ? [{
8150
+ label: `${label} ${path}`,
8151
+ bytes: sizes[index]
8152
+ }] : []).toSorted((a, b) => b.bytes - a.bytes).map(({ label, bytes }) => `${label}: ${formatBytes(bytes)}`);
9330
8153
  }
9331
8154
  const DEFAULT_DEPS = {
9332
8155
  volumes: readVolumeSpace,
@@ -9390,6 +8213,7 @@ async function enforceBudget({ root, note, dryRun = false, budget: resolved, env
9390
8213
  note,
9391
8214
  record: gcResults?.record,
9392
8215
  now: deps.now(),
8216
+ freeMb: lowestFree(measure.volumes),
9393
8217
  diskRecovered: async () => budgetShortfalls(budget, {
9394
8218
  volumes: await (deps.volumesAsync?.(paths) ?? deps.volumes(paths)),
9395
8219
  memory: null
@@ -9546,5 +8370,139 @@ function budgetLine(report) {
9546
8370
  const { budget } = report;
9547
8371
  return `${report.volumes.length ? `${report.volumes.map((v) => `${v.freeGb} GB free on ${v.volume}`).join(", ")} (reclaims below ${Math.max(budget.minFreeDiskGb, budget.hardFloorDiskGb)} GB, ${budget.hardFloorDiskGb ? `refuses below ${budget.hardFloorDiskGb} GB` : "never refuses"})` : "disk budget off"}; ${report.memory ? `roughly ${report.memory.committedGb} GB of a ${budget.maxCommittedMemoryGb} GB memory budget committed` : "memory budget off"}`;
9548
8372
  }
8373
+ function ccacheInstallCommand(platform = process.platform) {
8374
+ return platform === "darwin" ? "brew install ccache" : platform === "linux" ? "sudo apt install ccache" : "install ccache";
8375
+ }
8376
+ const CCACHE_SLOPPINESS = "pch_defines,time_macros";
8377
+ const STATS_LOG_FILE = "ccache-stats.log";
8378
+ const CCACHE_UNAVAILABLE = {
8379
+ status: "unavailable",
8380
+ hits: null,
8381
+ misses: null,
8382
+ hitRatePercent: null
8383
+ };
8384
+ const CCACHE_NOT_RUN = {
8385
+ status: "not-run",
8386
+ hits: null,
8387
+ misses: null,
8388
+ hitRatePercent: null
8389
+ };
8390
+ function ccacheStatsLog(root) {
8391
+ return join(workspaceLogsDir$1(root), STATS_LOG_FILE);
8392
+ }
8393
+ function ccacheMaxSize(config = loadConfig$1(), env = process.env) {
8394
+ const { value, error } = machineNumber("caches.ccacheMaxGb", config, env);
8395
+ return error || value === void 0 ? "5G" : `${value}G`;
8396
+ }
8397
+ function ccacheEnvironment({ binary, dir, workspaceRoot, statsLog, maxSize = "5G" }) {
8398
+ return {
8399
+ CMAKE_C_COMPILER_LAUNCHER: binary,
8400
+ CMAKE_CXX_COMPILER_LAUNCHER: binary,
8401
+ CCACHE_DIR: dir,
8402
+ CCACHE_BASEDIR: workspaceRoot,
8403
+ CCACHE_NOHASHDIR: "true",
8404
+ CCACHE_SLOPPINESS,
8405
+ CCACHE_MAXSIZE: maxSize,
8406
+ CCACHE_STATSLOG: statsLog
8407
+ };
8408
+ }
8409
+ function parseCcacheBinary(output) {
8410
+ const first = String(output ?? "").split("\n")[0]?.trim();
8411
+ if (!first || !isAbsolute(first)) return null;
8412
+ return first;
8413
+ }
8414
+ const CMAKE_LAUNCHER = /CMAKE_(C|CXX)_COMPILER_LAUNCHER\b/;
8415
+ function projectCmakeLauncher(source) {
8416
+ if (typeof source !== "string") return null;
8417
+ const match = CMAKE_LAUNCHER.exec(source);
8418
+ return match ? `CMAKE_${match[1]}_COMPILER_LAUNCHER` : null;
8419
+ }
8420
+ const HIT_COUNTERS = /* @__PURE__ */ new Set(["direct_cache_hit", "preprocessed_cache_hit"]);
8421
+ function parseCcacheStatsLog(text) {
8422
+ if (typeof text !== "string") return null;
8423
+ let hits = 0;
8424
+ let misses = 0;
8425
+ for (const raw of text.split("\n")) {
8426
+ const line = raw.trim();
8427
+ if (HIT_COUNTERS.has(line)) hits += 1;
8428
+ else if (line === "cache_miss") misses += 1;
8429
+ }
8430
+ const total = hits + misses;
8431
+ if (total === 0) return null;
8432
+ return {
8433
+ status: "reported",
8434
+ hits,
8435
+ misses,
8436
+ hitRatePercent: Math.round(hits / total * 1e3) / 10
8437
+ };
8438
+ }
8439
+ function readCcacheActivity(statsLog) {
8440
+ let text;
8441
+ try {
8442
+ text = readFileSync(statsLog, "utf-8");
8443
+ } catch {
8444
+ return CCACHE_UNAVAILABLE;
8445
+ }
8446
+ return parseCcacheStatsLog(text) ?? CCACHE_UNAVAILABLE;
8447
+ }
8448
+ function ccacheActivityLine(activity) {
8449
+ if (activity.status === "reported") return `${activity.hits} hits / ${activity.misses} misses (${activity.hitRatePercent}%)`;
8450
+ if (activity.status === "not-run") return "not run; artifact cache supplied the app";
8451
+ return "unavailable; no C++ compile went through ccache";
8452
+ }
8453
+ function lookupCcache() {
8454
+ try {
8455
+ return parseCcacheBinary(getExecutor().findExecutable("ccache"));
8456
+ } catch {
8457
+ return null;
8458
+ }
8459
+ }
8460
+ function readOrNull(file) {
8461
+ try {
8462
+ return readFileSync(file, "utf-8");
8463
+ } catch {
8464
+ return null;
8465
+ }
8466
+ }
8467
+ function canonical(root) {
8468
+ try {
8469
+ return realpathSync(root);
8470
+ } catch {
8471
+ return root;
8472
+ }
8473
+ }
8474
+ function resolveCcache({ root, layout = defaultAndroidLayout(root), dir = sharedCcache$1(), lookup = lookupCcache, env = process.env, onNote = (line) => console.error(line) }) {
8475
+ const binary = lookup();
8476
+ if (!binary) {
8477
+ onNote(chalk.dim(phaseLine("cache", `ccache off (not installed; ${ccacheInstallCommand()})`)));
8478
+ return null;
8479
+ }
8480
+ const { moduleDir } = layout;
8481
+ const declared = projectCmakeLauncher(readOrNull(join(moduleDir, "build.gradle")) ?? readOrNull(join(moduleDir, "build.gradle.kts")));
8482
+ if (declared) {
8483
+ onNote(chalk.dim(phaseLine("cache", `ccache off (${join(relative(root, moduleDir), "build.gradle")} sets ${declared} itself)`)));
8484
+ return null;
8485
+ }
8486
+ const statsLog = ccacheStatsLog(root);
8487
+ register({
8488
+ dir,
8489
+ registeredBy: root,
8490
+ name: "ccache",
8491
+ prune: "atomic",
8492
+ note: "ccache keeps itself under CCACHE_MAXSIZE, so it is emptied whole or not at all"
8493
+ });
8494
+ onNote(chalk.dim(phaseLine("cache", `ccache on (${dir})`)));
8495
+ return {
8496
+ dir,
8497
+ statsLog,
8498
+ env: ccacheEnvironment({
8499
+ binary,
8500
+ dir,
8501
+ workspaceRoot: canonical(root),
8502
+ statsLog,
8503
+ maxSize: ccacheMaxSize(loadConfig$1(), env)
8504
+ })
8505
+ };
8506
+ }
9549
8507
  //#endregion
9550
- export { recordCapacityWait as $, readProjectConfig as $n, LAUNCH_BUNDLING as $t, collectOwnedDeviceActivityAsync as A, launchAndroidApp as An, describeUnverifiableDevices as At, bundlingActivity as B, gateMetroOrigin as Bn, describeError as Bt, gcOptionsOf as C, deviceShellArg as Cn, stopIdleGradleDaemons as Cr, collectDeviceLeases as Ct, collectIdleDevices as D, installConflictKind as Dn, discoverCaches as Dr, deleteParkedSims as Dt, maintenanceLogger as E, installAndroidApp as En, readFootprints as Er, deleteParkedAvds as Et, ACTIVE_BUILD_KEY as F, writeDebugHttpHost as Fn, findStaleProjectDevices as Ft, runFailureRecord as G, UPLOAD_TIMEOUT_MS as Gn, endRecordedSession as Gt, parseActiveBuild as H, processContext as Hn, REMOTE_SESSION_ERROR as Ht, NO_BUILD_PROGRESS as I, pickDevClientScheme as In, unverifiedDeviceCommand as It, thrownRunFailureRecord as J, easAuthNote as Jn, readInstalledEasCliVersionAsync as Jt, startBuildProgress as K, cacheLevel as Kn, ensureMetroReachable as Kt, activeBuildState as L, readBundleSchemes as Ln, withAndroidAvdSizes as Lt, parseIdleDuration as M, launchIosApp as Mn, eraseParkedDevices as Mt, shutDownIdleDevices as N, restartedAppNote as Nn, findOrphanedDevices as Nt, collectIdleDevicesAsync as O, installIosApp as On, pruneCache as Or, deleteProjectDevices as Ot, workspaceBuildInProgress as P, reverseMetroPorts as Pn, findStaleDeviceRecords as Pt, recordCapacityRefusal as Q, providerFromConfig as Qn, withEasProjectLock as Qt, buildReport as R, schemesFromInfoPlist as Rn, collectEasSessionSweep as Rt, withdrawDevServerStopRequest as S, devClientUrl as Sn, scopedMemoryReport as Sr, DEVICE_LIST_TIMEOUT_MS$1 as St, openGcLog as T, hasMetroReverse as Tn, attributeMachineUsage as Tr, collectParkedSims as Tt, recordBuildPhase as U, runProcessOperation as Un, binOnPath as Ut, estimateBuild as V, exitAfterFlush as Vn, withRemoteSessionGcLocks as Vt, recordFinishedBuild as W, RESOLVE_TIMEOUT_MS as Wn, bundleEntryPoint as Wt, readRunEstimates as X, loadProjectProvider as Xn, remoteIosDeps as Xt, createRunRecorder as Y, isEasAuthFailureText as Yn, remoteAndroidDeps as Yt, readStats$1 as Z, ownerFromConfig as Zn, resolveRemoteContext as Zt, devServerStopRecord as _, IOS_DEV_MENU_OFF_DEFAULTS_PLIST as _n, memoryLines as _r, gradleVersionsPending as _t, inspectBudget as a, isBundleProof as an, remoteCommand as ar, includesRecordings as at, requestDevServerStop as b, appProcessFromPid as bn, memoryWorkPending as br, getMachineCacheReport as bt, readVolumeSpace as c, unattributedLaunchLines as cn, easCliSupport as cr, planCacheEmptying as ct, trimmableCaches as d, verifyLaunch as dn, parseArchiveSelector as dr, selectsBeyondCaches as dt, LAUNCH_FATAL as en, resolveEasCliBin as er, recordRunStats as et, stopOwnedMetro as f, verifyReleaseLaunch as fn, GRADLE_DAEMONS_KIND as fr, trimCaches as ft, devServerStopLevel as g, INSTALL_ERROR as gn, memoryCacheKind as gr, gradleVersionLines as gt, describeMetroLastStop as h, DEFAULT_METRO_PORT as hn, inspectWatchmanMemory as hr, deleteGradleVersions as ht, enforceBudget as i, VERIFY_TIMEOUT_MS as in, remoteCliInvocation as ir, includesParkedDevices as it, idleDeviceLines as j, launchAndroidReleaseApp as jn, deviceSweepIsScoped as jt, collectOwnedDeviceActivity as k, iosAppProcess as kn, sizeCaches as kr, describeParkedSims as kt, readVolumeSpaceAsync as l, unverifiedLaunchLines as ln, easCliUpgradeRemedy as lr, previewScopedEmptying as lt, describeDevServerStop as m, ADB_INSTALL_TIMEOUT_MS as mn, collectMemoryReport as mr, deleteGradleVersion as mt, budgetLine as n, MANIFEST_VERIFY_TIMEOUT_MS as nn, uploadRemote as nr, emptyCaches as nt, machineNumber as o, readCollectorRecords as on, MIN_EAS_CLI_DEVICE_VERSION as or, includesWorkspaceOutputs as ot, stopOwnedMetroForReset as p, APP_READINESS_TIMEOUT_MS as pn, WATCHMAN_KIND as pr, collectGradleVersions as pt, tapBuildLog as q, checkEasAuth as qn, ensureRemoteBootOwned as qt, cacheLabel as r, RELEASE_VERIFY_WAIT_MS as rn, bundleInvocation as rr, includesArchives as rt, peekBudget as s, readMetroRecords as sn, MIN_EAS_CLI_SIMULATOR_VERSION as sr, isWorkspacesScope as st, budgetGate as t, LAUNCH_UNVERIFIED as tn, resolveRemote as tr, statsProjectKey$1 as tt, resolveBudget as u, verifyAndroidReleaseLaunch as un, ownedSessionName as ur, selectCaches as ut, logVanishedSupervisor as v, androidAppProcess as vn, memoryScope as vr, includesGradleVersions as vt, maintenanceItem as w, ensureMetroReverse as wn, phase as wr, collectParkedAvds as wt, vanishedSupervisorMessage as x, clearOtherUserApps as xn, reclaimMemory as xr, collectInventory as xt, metroLastStop as y, androidDevClientUrl as yn, memorySweepIsScoped as yr, waitForGradleExit as yt, buildStatusLine as z, readRemoteSimulatorArch as zn, deleteEasSessions as zt };
8508
+ export { gradleVersionLines as $, memoryCacheKind as $n, INSTALL_ERROR as $t, diskPressureLevel as A, checkEasAuth as An, ensureRemoteBootOwned as At, includesArchives as B, bundleInvocation as Bn, RELEASE_VERIFY_WAIT_MS as Bt, devServerStopLevel as C, gateMetroOrigin as Cn, describeError as Ct, requestDevServerStop as D, RESOLVE_TIMEOUT_MS as Dn, bundleEntryPoint as Dt, metroLastStop as E, runProcessOperation as En, binOnPath as Et, gcOptionsOf as F, providerFromConfig as Fn, withEasProjectLock as Ft, planCacheEmptying as G, easCliSupport as Gn, unattributedLaunchLines as Gt, includesRecordings as H, remoteCommand as Hn, isBundleProof as Ht, maintenanceItem as I, readProjectConfig as In, LAUNCH_BUNDLING as It, selectsBeyondCaches as J, parseArchiveSelector as Jn, verifyLaunch as Jt, previewScopedEmptying as K, easCliUpgradeRemedy as Kn, unverifiedLaunchLines as Kt, openGcLog as L, resolveEasCliBin as Ln, LAUNCH_FATAL as Lt, levelStillShort as M, isEasAuthFailureText as Mn, remoteAndroidDeps as Mt, lowestFree as N, loadProjectProvider as Nn, remoteIosDeps as Nt, vanishedSupervisorMessage as O, UPLOAD_TIMEOUT_MS as On, endRecordedSession as Ot, protectWindowMs as P, ownerFromConfig as Pn, resolveRemoteContext as Pt, deleteGradleVersions as Q, inspectWatchmanMemory as Qn, DEFAULT_METRO_PORT as Qt, maintenanceLogger as R, resolveRemote as Rn, LAUNCH_UNVERIFIED as Rt, describeMetroLastStop as S, readRemoteSimulatorArch as Sn, deleteEasSessions as St, logVanishedSupervisor as T, processContext as Tn, REMOTE_SESSION_ERROR as Tt, includesWorkspaceOutputs as U, MIN_EAS_CLI_DEVICE_VERSION as Un, readCollectorRecords as Ut, includesParkedDevices as V, remoteCliInvocation as Vn, VERIFY_TIMEOUT_MS as Vt, isWorkspacesScope as W, MIN_EAS_CLI_SIMULATOR_VERSION as Wn, readMetroRecords as Wt, collectGradleVersions as X, WATCHMAN_KIND as Xn, APP_READINESS_TIMEOUT_MS as Xt, trimCaches as Y, GRADLE_DAEMONS_KIND as Yn, verifyReleaseLaunch as Yt, deleteGradleVersion as Z, collectMemoryReport as Zn, ADB_INSTALL_TIMEOUT_MS as Zt, resolveBudget as _, reverseMetroPorts as _n, findStaleDeviceRecords as _t, projectCmakeLauncher as a, devClientUrl as an, scopedMemoryReport as ar, DEVICE_LIST_TIMEOUT_MS$1 as at, stopOwnedMetroForReset as b, readBundleSchemes as bn, withAndroidAvdSizes as bt, budgetGate as c, hasMetroReverse as cn, attributeMachineUsage as cr, collectParkedSims as ct, enforceBudget as d, installIosApp as dn, pruneCache as dr, deleteProjectDevices as dt, IOS_DEV_MENU_OFF_DEFAULTS_PLIST as en, memoryLines as er, gradleVersionsPending as et, inspectBudget as f, iosAppProcess as fn, sizeCaches as fr, describeParkedSims as ft, readVolumeSpaceAsync as g, restartedAppNote as gn, findOrphanedDevices as gt, readVolumeSpace as h, launchIosApp as hn, eraseParkedDevices as ht, ccacheInstallCommand as i, clearOtherUserApps as in, reclaimMemory as ir, collectInventory as it, levelFloors as j, easAuthNote as jn, readInstalledEasCliVersionAsync as jt, withdrawDevServerStopRequest as k, cacheLevel as kn, ensureMetroReachable as kt, budgetLine as l, installAndroidApp as ln, readFootprints as lr, deleteParkedAvds as lt, peekBudget as m, launchAndroidReleaseApp as mn, deviceSweepIsScoped as mt, CCACHE_UNAVAILABLE as n, androidDevClientUrl as nn, memorySweepIsScoped as nr, waitForGradleExit as nt, readCcacheActivity as o, deviceShellArg as on, stopIdleGradleDaemons as or, collectDeviceLeases as ot, machineNumber as p, launchAndroidApp as pn, describeUnverifiableDevices as pt, selectCaches as q, ownedSessionName as qn, verifyAndroidReleaseLaunch as qt, ccacheActivityLine as r, appProcessFromPid as rn, memoryWorkPending as rr, getMachineCacheReport as rt, resolveCcache as s, ensureMetroReverse as sn, phase as sr, collectParkedAvds as st, CCACHE_NOT_RUN as t, androidAppProcess as tn, memoryScope as tr, includesGradleVersions as tt, cacheLabel as u, installConflictKind as un, discoverCaches as ur, deleteParkedSims as ut, trimmableCaches as v, writeDebugHttpHost as vn, findStaleProjectDevices as vt, devServerStopRecord as w, exitAfterFlush as wn, withRemoteSessionGcLocks as wt, describeDevServerStop as x, schemesFromInfoPlist as xn, collectEasSessionSweep as xt, stopOwnedMetro as y, pickDevClientScheme as yn, unverifiedDeviceCommand as yt, emptyCaches as z, uploadRemote as zn, MANIFEST_VERIFY_TIMEOUT_MS as zt };