stim 1.17.2 → 1.18.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 (150) hide show
  1. package/README.md +50 -1
  2. package/dist/{activity-IRvJY_ru.mjs → activity-BxnkDZDi.mjs} +4 -4
  3. package/dist/{agent-device-usage-output-BK41vnzN.mjs → agent-device-usage-output-CZ66YA4T.mjs} +8 -8
  4. package/dist/android-BKA16DTf.mjs +4 -0
  5. package/dist/{android-B9yaNKwa.mjs → android-BvwuMUJ9.mjs} +192 -98
  6. package/dist/{android-DE4v59Mb.mjs → android-CBwCNicO.mjs} +2 -2
  7. package/dist/{android-B_YNZy61.mjs → android-DC3ZLuw0.mjs} +9 -9
  8. package/dist/{android-cas-ZFpjJBL_.mjs → android-cas-BBFyr9CA.mjs} +5 -5
  9. package/dist/android-cas-compiler.mjs +2 -2
  10. package/dist/api-D0VtFhqk.mjs +152 -0
  11. package/dist/api-run.d.mts +1 -0
  12. package/dist/api-run.mjs +145 -0
  13. package/dist/api.d.mts +212 -0
  14. package/dist/api.mjs +2 -0
  15. package/dist/{app-install-VPkI5WEg.mjs → app-install-D-PUENug.mjs} +3 -3
  16. package/dist/{attempt-Bk2ouQVL.mjs → attempt-DW-mQqM7.mjs} +14 -4
  17. package/dist/{budget-DfcgFdQB.mjs → budget-DIhL2BKx.mjs} +1578 -58
  18. package/dist/build-facts-DcLv2HSd.d.mts +111 -0
  19. package/dist/{cache-manifest-DnUHBJaU.mjs → cache-manifest-BI7ozfmH.mjs} +3 -3
  20. package/dist/cache-manifest.mjs +1 -1
  21. package/dist/{chrome-BrcVWQyD.mjs → chrome-DJnmnS5q.mjs} +1 -1
  22. package/dist/cli-DNJMwADx.mjs +68 -0
  23. package/dist/cli.mjs +1 -1
  24. package/dist/{client-SvBdgObD.mjs → client-DzhapoMB.mjs} +139 -32
  25. package/dist/collector-run.mjs +9 -9
  26. package/dist/{command-output-CDAIFD-8.mjs → command-output-CBtzyIgF.mjs} +2 -2
  27. package/dist/{config-D0DbpZtM.mjs → config-CgYMahz7.mjs} +5 -5
  28. package/dist/{created-devices-CpvzBL25.mjs → created-devices-DKbeWsiK.mjs} +2 -2
  29. package/dist/{dependency-state-CLcBHH2M.mjs → dependency-state-xKoOypBm.mjs} +2 -2
  30. package/dist/{detached-entry-CiTEgMR1.mjs → detached-entry-BLfcgc4O.mjs} +1 -1
  31. package/dist/{dev-client-B-XC4uRH.mjs → dev-client-CLnIH02x.mjs} +2 -2
  32. package/dist/{device-C8IOoJPv.mjs → device-D7oCHGWi.mjs} +8 -8
  33. package/dist/{device-capacity-ZC4XSZ4R.mjs → device-capacity-EUOOYr8T.mjs} +13 -13
  34. package/dist/device-host-worker.mjs +19 -19
  35. package/dist/{device-ios-C2Um-XUR.mjs → device-ios-6MewKKnc.mjs} +13 -13
  36. package/dist/{device-lease-DpoS36_u.mjs → device-lease-Co-EwifR.mjs} +9 -9
  37. package/dist/{device-lease-run-B8YltIH_.mjs → device-lease-run-w1xhCqIW.mjs} +4 -4
  38. package/dist/{device-pool-3o7s5lo4.mjs → device-pool-F5WvRPtD.mjs} +3 -3
  39. package/dist/{device-remote-hFSkpfQu.mjs → device-remote-tHbd1fKy.mjs} +15 -15
  40. package/dist/{doctor-CFv1fCrF.mjs → doctor-B9ckYUoU.mjs} +33 -30
  41. package/dist/{error-diagnostics-C-NMnhrk.mjs → error-diagnostics-BRXOqDzG.mjs} +11 -11
  42. package/dist/{exec-CaErVv7t.mjs → exec-DWhCZDGm.mjs} +65 -2
  43. package/dist/{gc-DEmkg3Xh.mjs → gc-Dh6mXOKC.mjs} +23 -469
  44. package/dist/{gradle-dJpre3Yn.mjs → gradle-C6GKnunQ.mjs} +6 -6
  45. package/dist/{guide-status-Du-ZQ3FD.mjs → guide-status-Bhf9SJXc.mjs} +3 -3
  46. package/dist/{guide-CSmhMTf8.mjs → guide-tm6igAaa.mjs} +290 -53
  47. package/dist/{hosted-android-E1Heoqnv.mjs → hosted-android-Bv285t_t.mjs} +3 -3
  48. package/dist/{hosted-client-Bp4knSwu.mjs → hosted-client-C-rMtppF.mjs} +2 -2
  49. package/dist/{hosted-ios-BnbeYOUD.mjs → hosted-ios-D5mNmtj4.mjs} +1 -1
  50. package/dist/{hosted-logs-OcgIBvCT.mjs → hosted-logs-DN8Iz4mJ.mjs} +9 -9
  51. package/dist/{hosted-macos-DcxKjtfH.mjs → hosted-macos-B-wEjlwG.mjs} +5 -5
  52. package/dist/{hosted-native-1Ebbig4w.mjs → hosted-native-DeeOG8xZ.mjs} +6 -6
  53. package/dist/{idle-shutdown-IGyNV8Cr.mjs → idle-shutdown-jjtxAn5C.mjs} +7 -8
  54. package/dist/{ios-DSfnPfse.mjs → ios-B3SbOWdw.mjs} +19 -7
  55. package/dist/{ios-Cr1h5gnO.mjs → ios-Bvbf0SGY.mjs} +1 -1
  56. package/dist/{ios-BJ4XUSe1.mjs → ios-CPdkoKaa.mjs} +181 -95
  57. package/dist/ios-DNpdygiv.mjs +7 -0
  58. package/dist/{ios-device-Ee7sDXfp.mjs → ios-device-Bn7jWDB1.mjs} +1 -1
  59. package/dist/{ios-device-pAN4t54-.mjs → ios-device-vOaByMhw.mjs} +3 -3
  60. package/dist/{ios-state-8L3lgum6.mjs → ios-state-B2pES6Vs.mjs} +1 -1
  61. package/dist/{launch-verify-lTnWZikR.mjs → launch-verify-hskNcWkZ.mjs} +5 -5
  62. package/dist/{logs-BAaS7gS2.mjs → logs-1Ylc1Ri6.mjs} +13 -13
  63. package/dist/macos-Ck7OBMNY.mjs +2 -0
  64. package/dist/{macos-BIRQS90f.mjs → macos-DF6C7wUU.mjs} +118 -34
  65. package/dist/macos-run.mjs +1 -1
  66. package/dist/maintenance-run.mjs +349 -47
  67. package/dist/{metro-g8NqiJp-.mjs → metro-Dh8T4Ytd.mjs} +5 -5
  68. package/dist/{metro-gateway-C-ijGI2F.mjs → metro-gateway-CEUw9RMH.mjs} +4 -4
  69. package/dist/{metro-store-CDDZHtPl.mjs → metro-store-BAHFcJm6.mjs} +16 -2
  70. package/dist/{named-ports-C8m8EQs2.mjs → named-ports-BFIz1Shz.mjs} +61 -6
  71. package/dist/{native-run-D1Q56rAD.mjs → native-run-B06FkmZx.mjs} +4 -4
  72. package/dist/{native-runtime-BEK171xQ.mjs → native-runtime-D_603JiC.mjs} +6 -6
  73. package/dist/{ndjson-PclOIwVY.mjs → ndjson-LGZAMsqa.mjs} +8 -4
  74. package/dist/offload-worker.d.mts +3 -17
  75. package/dist/offload-worker.mjs +10 -11
  76. package/dist/{ownership-DyRQru0q.mjs → ownership-D6Se7gg_.mjs} +3 -3
  77. package/dist/{page-QF_Ke10s.mjs → page-Bv9s2KwY.mjs} +1 -1
  78. package/dist/placement-log-BcPhRwR4.mjs +84 -0
  79. package/dist/{build-plan-BDsSi09V.mjs → plan-placement-CtxkCO54.mjs} +122 -48
  80. package/dist/{ports-4nYqxPWC.mjs → ports-BLTtfBq-.mjs} +3 -3
  81. package/dist/{prebuild-D7RUMkwN.mjs → prebuild-juCyreaR.mjs} +5 -5
  82. package/dist/preview-D5V07Nrl.mjs +987 -0
  83. package/dist/{rolldown-runtime-BsOwBuO_.mjs → process-identity-Biv3arCO.mjs} +11 -1
  84. package/dist/{project-D47Rh0ly.mjs → project-C3ChRLZy.mjs} +4 -68
  85. package/dist/projects-DD7dBTCj.mjs +92 -0
  86. package/dist/{pull-request-pxnDCQzn.mjs → pull-request-u4vp7zmK.mjs} +1 -1
  87. package/dist/pull-requests.mjs +1 -1
  88. package/dist/{recordings-BNgsD1Tr.mjs → recordings-BAWltsVt.mjs} +1 -1
  89. package/dist/{reload-DcRxj3qG.mjs → reload-DvyWzVjJ.mjs} +15 -15
  90. package/dist/{remote-cache-Ci4DQYYj.mjs → remote-cache-BGg7aGIZ.mjs} +5 -5
  91. package/dist/{run-CkE9y0nc.mjs → run-vhtDgpHO.mjs} +5 -5
  92. package/dist/{server-bare-BkrSxSsZ.mjs → server-bare-B9E7S1aR.mjs} +4 -5
  93. package/dist/server-expo-Db9HBUoa.mjs +2 -0
  94. package/dist/{server-expo-CHTyXD0G.mjs → server-expo-DoBHhuzK.mjs} +5 -6
  95. package/dist/{settings-SR9OmW9h.mjs → settings-Byp2AQ_E.mjs} +6 -5
  96. package/dist/{settings-Cfd-0r2d.mjs → settings-CQyMElEP.mjs} +24 -763
  97. package/dist/{settings-BBpMcDbD.mjs → settings-Djl-mpjX.mjs} +1 -1
  98. package/dist/settings.schema.json +126 -3
  99. package/dist/{simslim-BWNrc3M3.mjs → simslim-EHBQ2R4G.mjs} +7 -7
  100. package/dist/{slot-launch-DmjBmpQV.mjs → slot-launch-C1nqh4mu.mjs} +6 -6
  101. package/dist/{spawn-claims-D0XfSe8u.mjs → spawn-claims-Kqiifsk2.mjs} +1 -1
  102. package/dist/{spawn-entry-jw19m8Nl.mjs → spawn-entry-Dcb7sDUz.mjs} +1 -0
  103. package/dist/{stage-CzaiwFsI.mjs → stage-g_WqWeaE.mjs} +14 -3
  104. package/dist/{start-DUz3mYdb.mjs → start-D-IeOyoK.mjs} +19 -19
  105. package/dist/{start-AlfCPRVU.mjs → start-NTc1Ifqs.mjs} +1 -1
  106. package/dist/{state-PeQiJuIu.mjs → state-BG-aAk4k.mjs} +4 -4
  107. package/dist/{state-B4PQFXMW.d.mts → state-lKh8shBp.d.mts} +2 -3
  108. package/dist/{state-DyaNTgfR.mjs → state-xnqpx3AD.mjs} +2 -2
  109. package/dist/{stats-Bq4fJOdB.mjs → stats-DY6ekz8F.mjs} +5 -5
  110. package/dist/{status-DlKvVJBE.mjs → status-BEeL6IKz.mjs} +6 -4
  111. package/dist/{status-BA2pehwf.mjs → status-DCjZeE-K.mjs} +33 -34
  112. package/dist/{status-CzS4bPO9.mjs → status-Dcr0Qexd.mjs} +5 -5
  113. package/dist/{stim-desktop-Cnz8CPnl.mjs → stim-desktop-Behws_fi.mjs} +1 -1
  114. package/dist/{stim-installations-QMEI32v1.mjs → stim-installations-aR_WsNOA.mjs} +1 -1
  115. package/dist/{stop-BIiM49EY.mjs → stop-BGhKMOuH.mjs} +5 -5
  116. package/dist/{stop-DQIasgKp.mjs → stop-BTjs3xbc.mjs} +17 -18
  117. package/dist/{stop-zF1y04f2.mjs → stop-DIJtD2GL.mjs} +2 -2
  118. package/dist/supervisor-run.d.mts +24 -22
  119. package/dist/supervisor-run.mjs +21 -22
  120. package/dist/{support-CsJNVfOc.mjs → support-BaayeYCf.mjs} +7 -7
  121. package/dist/{support-WvD__tSV.mjs → support-Bwvbrq5-.mjs} +5 -5
  122. package/dist/{toolchain-DNyR3NPq.mjs → toolchain-Bnyd_K3A.mjs} +471 -22
  123. package/dist/{trigger-Bemu8ytw.mjs → trigger-CJMhwXe8.mjs} +5 -5
  124. package/dist/trigger-DWAq9hkw.mjs +2 -0
  125. package/dist/{warm-progress-DF4JRvYI.mjs → warm-progress-CjB-MP6H.mjs} +7 -7
  126. package/dist/web-B6_bFDn7.mjs +2 -0
  127. package/dist/{web-Dl0KSRHf.mjs → web-DTU0uiWj.mjs} +22 -22
  128. package/dist/web-run.mjs +10 -10
  129. package/dist/{workspace-state-DqcCbpE7.mjs → workspace-state-C59dqG-Z.mjs} +3 -3
  130. package/dist/{ownership-BMLqyBgJ.mjs → workspaces-xXItD8Dj.mjs} +577 -23
  131. package/dist/{worktree-j3IcFAI5.mjs → worktree-BJfnU6r5.mjs} +1 -1
  132. package/dist/{worktree-Be_7NLKy.mjs → worktree-BSYlofkq.mjs} +234 -35
  133. package/dist/worktree-CsKYexJM.mjs +753 -0
  134. package/package.json +10 -5
  135. package/dist/build-progress-D0WkjCPd.mjs +0 -1036
  136. package/dist/build-slots-41v_d2x9.mjs +0 -142
  137. package/dist/cli-CYXb29oE.mjs +0 -51
  138. package/dist/deps-iKw0eD4P.mjs +0 -438
  139. package/dist/errors-B6W7FkxQ.mjs +0 -15
  140. package/dist/idle-BXG__1BA.mjs +0 -176
  141. package/dist/in-use-CvGOUn-x.mjs +0 -130
  142. package/dist/preview-B-g9d7kV.mjs +0 -318
  143. package/dist/process-identity-BmRm1qV0.mjs +0 -12
  144. package/dist/server-expo-CeakhI8P.mjs +0 -2
  145. package/dist/state-BKa8wNN5.mjs +0 -148
  146. package/dist/stop-cause-rhmGfwvt.mjs +0 -97
  147. package/dist/trigger-CXts6qt1.mjs +0 -2
  148. package/dist/watchman-APYjXSQa.mjs +0 -48
  149. package/dist/workspace-process-lock-2mNzOlaB.mjs +0 -58
  150. package/dist/workspaces-Bcib97-6.mjs +0 -306
@@ -1,16 +1,193 @@
1
- import { j as formatBytes } from "./config-D0DbpZtM.mjs";
2
- import { t as relaunchWithLogFile } from "./detached-entry-CiTEgMR1.mjs";
3
- import { c as resolveBudget } from "./budget-DfcgFdQB.mjs";
4
- import { r as createNdjsonWriter } from "./ndjson-PclOIwVY.mjs";
5
- import { t as resolveMaintenanceSettings } from "./settings-BBpMcDbD.mjs";
6
- import { r as due, t as capChildLog } from "./attempt-Bk2ouQVL.mjs";
7
- import { a as sizeScanDeferred, i as measureSizes, r as measurePressure, t as plannedMaintenance } from "./preview-B-g9d7kV.mjs";
8
- import { appendFileSync, mkdirSync, renameSync, statSync, unlinkSync, writeFileSync } from "node:fs";
9
- import { basename, join, resolve } from "node:path";
10
- import { LOG_ROTATE_BYTES, rotatedLogPath, withDirLock } from "@stim-cli/core";
11
- import { maintenanceChildLogFile, maintenanceDir, maintenanceNdjsonFile, maintenanceRunClaims, maintenanceStateFile, readMaintenanceState, workspaceLogsDir } from "@stim-cli/core/state";
12
- import { releaseClaim, tryAcquireClaim } from "@stim-cli/core/ownership-claim";
1
+ import { M as isOnMountedVolume, N as listMountedVolumes, _ as loadConfig$1, j as formatBytes } from "./config-CgYMahz7.mjs";
2
+ import { a as unregister, n as readManifest } from "./cache-manifest-BI7ozfmH.mjs";
3
+ import { Qt as pruneCache, Zt as discoverCaches, l as resolveBudget, pt as planCacheEmptying, r as cacheLabel, st as emptyCaches } from "./budget-DIhL2BKx.mjs";
4
+ import { F as takeGcResults, I as canonicalPath, a as collectWorkspaceOutputs, i as collectOrphanedWorkspaces, o as deleteOrphanedWorkspaces, r as clearWorkspaceOutputs } from "./workspaces-xXItD8Dj.mjs";
5
+ import { D as projectMaintenancePinned } from "./settings-CQyMElEP.mjs";
6
+ import { r as createNdjsonWriter } from "./ndjson-LGZAMsqa.mjs";
7
+ import { t as relaunchWithLogFile } from "./detached-entry-BLfcgc4O.mjs";
8
+ import { t as resolveMaintenanceSettings } from "./settings-Djl-mpjX.mjs";
9
+ import { r as due, t as capChildLog } from "./attempt-DW-mQqM7.mjs";
10
+ import { a as recentUse, d as measureDisk, f as measurePressure, i as cacheEntryProtection, m as sizeScanDeferred, p as measureSizes, r as triggeringRoot, s as removeWorktrees, t as plannedMaintenance, u as cacheBlocked } from "./preview-D5V07Nrl.mjs";
11
+ import { appendFileSync, existsSync, mkdirSync, renameSync, statSync, unlinkSync, writeFileSync } from "node:fs";
12
+ import { basename, join, resolve, sep } from "node:path";
13
+ import { MAINTENANCE_LOG_NAME, maintenanceChildLogFile, maintenanceDir, maintenanceNdjsonFile, maintenanceRunClaims, maintenanceStateFile, readMaintenanceState, sharedCompilationCache, workspaceLogsDir } from "@stim-cli/core/state";
13
14
  import { fileURLToPath } from "node:url";
15
+ import { LOG_ROTATE_BYTES, buildCacheRoot, metroCacheRoot, rotatedLogPath, withDirLock } from "@stim-cli/core";
16
+ import { releaseClaim, tryAcquireClaim } from "@stim-cli/core/ownership-claim";
17
+ //#region src/maintenance/act.ts
18
+ const kept = (detail) => ({
19
+ status: "kept",
20
+ bytes: 0,
21
+ detail
22
+ });
23
+ async function quietly(run) {
24
+ const { log, error } = {
25
+ log: console.log,
26
+ error: console.error
27
+ };
28
+ const exitCode = process.exitCode;
29
+ takeGcResults();
30
+ console.log = () => {};
31
+ console.error = () => {};
32
+ try {
33
+ return {
34
+ value: await run(),
35
+ results: takeGcResults()
36
+ };
37
+ } finally {
38
+ console.log = log;
39
+ console.error = error;
40
+ process.exitCode = exitCode;
41
+ takeGcResults();
42
+ }
43
+ }
44
+ function outcomeOf(results, fallbackBytes) {
45
+ const failed = results.find((result) => result.status === "failed");
46
+ if (failed) return {
47
+ status: "failed",
48
+ bytes: 0,
49
+ detail: failed.detail ?? `could not process ${failed.label}`
50
+ };
51
+ const done = results.filter((result) => result.status === "done");
52
+ if (done.length) return {
53
+ status: "done",
54
+ bytes: done.reduce((sum, result) => sum + (result.bytes ?? 0), 0) || fallbackBytes,
55
+ detail: done.map((result) => result.label).join(", ")
56
+ };
57
+ return kept(results.find((result) => result.detail)?.detail ?? "nothing was eligible when the action ran");
58
+ }
59
+ function rootCache(dir) {
60
+ const name = canonicalPath(dir) === canonicalPath(buildCacheRoot()) ? "Build cache" : canonicalPath(dir) === canonicalPath(metroCacheRoot()) ? "Metro transform caches" : null;
61
+ return name ? {
62
+ name,
63
+ dir,
64
+ prune: "entries",
65
+ entriesDepth: name === "Build cache" ? 2 : 3,
66
+ note: ""
67
+ } : null;
68
+ }
69
+ function cacheOf(action) {
70
+ const discovered = discoverCaches();
71
+ if (action.dir) {
72
+ const dir = canonicalPath(action.dir);
73
+ return discovered.find((cache) => canonicalPath(cache.dir) === dir) ?? rootCache(action.dir);
74
+ }
75
+ return discovered.find((cache) => cacheLabel(cache) === action.target) ?? null;
76
+ }
77
+ function trimCache(action, context) {
78
+ const cache = cacheOf(action);
79
+ if (!cache) return kept("it is no longer a cache Stim may trim");
80
+ const [planned] = planCacheEmptying([cache], false);
81
+ if (!planned || planned.machineGlobal) return kept(planned?.machineGlobal ?? "it is no longer a cache Stim may trim");
82
+ const aged = action.olderThanDays !== void 0;
83
+ const result = pruneCache(cache, {
84
+ olderThanDays: Math.max(action.olderThanDays ?? 0, context.settings.protectRecentHours / 24),
85
+ byMtime: true,
86
+ protect: context.protect ??= cacheEntryProtection(),
87
+ ...aged ? {} : { evictBytes: action.bytes }
88
+ });
89
+ if (result.skipped) return kept(result.skipped);
90
+ if (result.failed) return {
91
+ status: "failed",
92
+ bytes: result.bytes,
93
+ detail: `${result.failed} entries could not be removed`
94
+ };
95
+ const protectedNote = result.protectedEntries ? `; ${result.protectedEntries} protected entries kept` : "";
96
+ if (result.removed === 0) return kept(`no unprotected entry was old enough${protectedNote}`);
97
+ return {
98
+ status: "done",
99
+ bytes: result.bytes,
100
+ detail: `${result.removed} entries${protectedNote}`,
101
+ ...!aged && result.bytes < action.bytes ? { shortfall: `${action.target} reached ${result.bytes} of ${action.bytes} bytes${protectedNote}` } : {}
102
+ };
103
+ }
104
+ async function emptyCompilationCache(action, context) {
105
+ if (!measureDisk().some((disk) => disk.freeMb < context.budget.hardFloorDiskMb)) return kept("free disk is above budget.hardFloorDiskGb, and this cache can only be emptied whole");
106
+ const dir = sharedCompilationCache();
107
+ const blocked = cacheBlocked({
108
+ name: "Swift compilation cache",
109
+ dir,
110
+ category: "compilation-cache"
111
+ });
112
+ if (blocked) return kept(blocked);
113
+ const cache = discoverCaches().find((candidate) => canonicalPath(candidate.dir) === canonicalPath(dir));
114
+ if (cache?.prune !== "atomic") return kept("it is not a registered cache Stim empties whole");
115
+ const [planned] = planCacheEmptying([{
116
+ ...cache,
117
+ bytes: action.bytes
118
+ }], true);
119
+ const { results } = await quietly(() => emptyCaches(planned ? [planned] : []));
120
+ return outcomeOf(results, action.bytes);
121
+ }
122
+ async function clearOutputs(action, context) {
123
+ const root = action.workspace;
124
+ if (!root) return kept("the action names no workspace");
125
+ if (root === context.protectedRoot) return kept("the command that started this pass runs in it");
126
+ if (projectMaintenancePinned(root)) return kept("pinned by maintenance.keep");
127
+ const recent = recentUse(root, context.settings);
128
+ if (recent) return kept(recent);
129
+ const olderThan = action.olderThanDays ?? null;
130
+ const now = Date.now();
131
+ const report = collectWorkspaceOutputs({
132
+ olderThan,
133
+ now,
134
+ measure: false
135
+ });
136
+ const entry = report.workspaces.find((candidate) => candidate.projectRoot && canonicalPath(candidate.projectRoot) === root);
137
+ if (!entry) return kept("it has no build outputs any more");
138
+ const { results } = await quietly(() => clearWorkspaceOutputs({
139
+ ...report,
140
+ workspaces: [entry]
141
+ }, {
142
+ olderThan,
143
+ now
144
+ }));
145
+ return outcomeOf(results, action.bytes);
146
+ }
147
+ async function removeWorktree(action, context) {
148
+ const sweep = context.worktreeSweep;
149
+ const worktree = sweep?.worktrees.find((candidate) => candidate.path === action.target);
150
+ if (!sweep || !worktree) return kept("it is not in this pass's worktree check");
151
+ const path = canonicalPath(worktree.path);
152
+ if (context.protectedRoot === path || context.protectedRoot.startsWith(`${path}${sep}`)) return kept("the command that started this pass runs inside it");
153
+ if (worktree.keys.some(projectMaintenancePinned)) return kept("pinned by maintenance.keep");
154
+ const { results } = await quietly(() => removeWorktrees({
155
+ ...sweep,
156
+ worktrees: [worktree]
157
+ }));
158
+ return outcomeOf(results, 0);
159
+ }
160
+ async function removeOrphan(action) {
161
+ const orphan = collectOrphanedWorkspaces(Object.keys(loadConfig$1()?.projects ?? {}), listMountedVolumes()).orphaned.find((candidate) => candidate.dir === action.target);
162
+ if (!orphan) return kept("it is no longer an orphaned workspace directory");
163
+ const { results } = await quietly(() => deleteOrphanedWorkspaces([orphan]));
164
+ return outcomeOf(results, orphan.bytes ?? 0);
165
+ }
166
+ function unregisterCache(action) {
167
+ const dir = action.dir ?? action.target;
168
+ if (existsSync(dir) || !isOnMountedVolume(dir, listMountedVolumes()) || !readManifest().caches.some((c) => c.dir === dir)) return kept("it can no longer be shown to be a stale registration");
169
+ return unregister(dir) ? {
170
+ status: "done",
171
+ bytes: 0,
172
+ detail: dir
173
+ } : kept("it was already unregistered");
174
+ }
175
+ async function executeAction(action, context) {
176
+ switch (action.kind) {
177
+ case "would-clear-outputs": return clearOutputs(action, context);
178
+ case "would-trim-cache": return trimCache(action, context);
179
+ case "would-empty-cache": return emptyCompilationCache(action, context);
180
+ case "would-remove-worktree": return removeWorktree(action, context);
181
+ case "would-remove-orphan": return removeOrphan(action);
182
+ case "would-unregister-cache": return unregisterCache(action);
183
+ default: return kept("automatic maintenance does not stop devices or dev servers yet");
184
+ }
185
+ }
186
+ function diskRecovered(budget) {
187
+ const floor = Math.max(budget.minFreeDiskMb, budget.hardFloorDiskMb);
188
+ return measureDisk().every((disk) => disk.freeMb >= floor);
189
+ }
190
+ //#endregion
14
191
  //#region src/maintenance/log.ts
15
192
  function maintenanceLogger(settings, pass, trigger) {
16
193
  const file = maintenanceNdjsonFile();
@@ -33,7 +210,7 @@ function maintenanceLogger(settings, pass, trigger) {
33
210
  event,
34
211
  pass,
35
212
  trigger,
36
- mode: "report"
213
+ mode: settings.mode === "on" ? "on" : "report"
37
214
  };
38
215
  if (!writer.write(record)) throw writer.lastError ?? /* @__PURE__ */ new Error(`Could not append ${file}`);
39
216
  if (typeof fields.workspace === "string" && [
@@ -41,7 +218,7 @@ function maintenanceLogger(settings, pass, trigger) {
41
218
  "maintenance_failure",
42
219
  "maintenance_skip"
43
220
  ].includes(event)) {
44
- const workspace = createNdjsonWriter(join(workspaceLogsDir(fields.workspace), "maintenance.ndjson"), { maxBytes: LOG_ROTATE_BYTES });
221
+ const workspace = createNdjsonWriter(join(workspaceLogsDir(fields.workspace), MAINTENANCE_LOG_NAME), { maxBytes: LOG_ROTATE_BYTES });
45
222
  try {
46
223
  if (!workspace.write(record)) throw workspace.lastError ?? /* @__PURE__ */ new Error("Could not append workspace maintenance log");
47
224
  } finally {
@@ -70,6 +247,37 @@ function writeState(state) {
70
247
  }
71
248
  });
72
249
  }
250
+ const EXECUTION_ORDER = {
251
+ "would-unregister-cache": 0,
252
+ "would-remove-orphan": 0,
253
+ "would-clear-outputs": 1,
254
+ "would-trim-cache": 2,
255
+ "would-empty-cache": 3,
256
+ "would-remove-worktree": 4
257
+ };
258
+ function shrinkSizes(state, action, outcome) {
259
+ state.sizes = state.sizes.flatMap((size) => {
260
+ if (action.kind === "would-clear-outputs" && size.category === "workspace-outputs" && size.workspace === action.workspace) return [];
261
+ if (action.kind === "would-trim-cache" && action.dir && size.dir === action.dir) return [{
262
+ ...size,
263
+ bytes: Math.max(0, size.bytes - outcome.bytes)
264
+ }];
265
+ if (action.kind === "would-empty-cache" && size.dir === action.target) return [];
266
+ return [size];
267
+ });
268
+ }
269
+ const executedKind = (action) => action.kind.replace(/^would-/, "");
270
+ function describeDone(action) {
271
+ const name = basename(action.workspace ?? action.target);
272
+ switch (action.kind) {
273
+ case "would-clear-outputs": return `Cleared build outputs of ${name}`;
274
+ case "would-trim-cache": return `Trimmed ${action.target}`;
275
+ case "would-empty-cache": return `Emptied ${action.target}`;
276
+ case "would-remove-worktree": return `Removed the worktree ${action.target}`;
277
+ case "would-remove-orphan": return `Removed the orphaned workspace directory ${action.target}`;
278
+ default: return `Unregistered the stale cache ${action.target}`;
279
+ }
280
+ }
73
281
  const actionKey = (action) => JSON.stringify([action.kind, action.target]);
74
282
  const stableText = (text) => text.replace(/\d+(?:\.\d+)?/g, "#");
75
283
  const sameSet = (before, after) => before.size === after.size && [...before].every((key) => after.has(key));
@@ -113,10 +321,11 @@ async function runMaintenance(trigger) {
113
321
  const failures = [];
114
322
  let measuredSizes = false;
115
323
  const budget = resolveBudget().budget;
324
+ const ran = /* @__PURE__ */ new Set();
116
325
  for (const check of checks) {
117
- if (check === "size" && sizeScanDeferred(settings)) {
118
- record("maintenance_skip", "debug", "Size check deferred: host load exceeds maintenance.maxLoadPerCore", {
119
- target: "size",
326
+ if (check !== "pressure" && sizeScanDeferred(settings)) {
327
+ record("maintenance_skip", "debug", `${check} check deferred: host load exceeds maintenance.maxLoadPerCore`, {
328
+ target: check,
120
329
  reason: "host load exceeds maintenance.maxLoadPerCore"
121
330
  });
122
331
  state.deferredAt = {
@@ -127,9 +336,11 @@ async function runMaintenance(trigger) {
127
336
  continue;
128
337
  }
129
338
  state.lastAt[check] = Date.now();
339
+ ran.add(check);
130
340
  delete state.deferredAt?.[check];
131
341
  writeState(state);
132
342
  try {
343
+ if (check === "worktree" || check === "sweep") continue;
133
344
  if (check === "pressure") {
134
345
  state.pressure = measurePressure(settings, state.pressure, state.lastAt[check]);
135
346
  const floor = Math.max(budget.minFreeDiskMb, budget.hardFloorDiskMb);
@@ -175,7 +386,10 @@ async function runMaintenance(trigger) {
175
386
  }
176
387
  let result;
177
388
  try {
178
- result = await plannedMaintenance(state.pressure, state.sizes, settings);
389
+ result = await plannedMaintenance(state.pressure, state.sizes, settings, {
390
+ sweep: ran.has("sweep"),
391
+ worktrees: ran.has("worktree")
392
+ });
179
393
  } catch (error) {
180
394
  const message = error instanceof Error ? error.message : String(error);
181
395
  failures.push(message);
@@ -188,42 +402,130 @@ async function runMaintenance(trigger) {
188
402
  blocked: [],
189
403
  skips: []
190
404
  };
405
+ for (const check of ["sweep", "worktree"]) if (ran.has(check)) delete state.lastAt[check];
406
+ }
407
+ async function executePlan(actions) {
408
+ const context = {
409
+ settings,
410
+ budget,
411
+ protectedRoot: triggeringRoot(),
412
+ worktreeSweep: result.worktreeSweep
413
+ };
414
+ const results = [];
415
+ const ordered = actions.toSorted((a, b) => (EXECUTION_ORDER[a.kind] ?? 9) - (EXECUTION_ORDER[b.kind] ?? 9));
416
+ for (const action of ordered) {
417
+ const began = Date.now();
418
+ let outcome;
419
+ try {
420
+ outcome = action.check === "disk" && diskRecovered(budget) ? {
421
+ status: "kept",
422
+ bytes: 0,
423
+ detail: "free disk recovered before this action"
424
+ } : await executeAction(action, context);
425
+ } catch (error) {
426
+ outcome = {
427
+ status: "failed",
428
+ bytes: 0,
429
+ detail: error instanceof Error ? error.message : String(error)
430
+ };
431
+ }
432
+ results.push({
433
+ action,
434
+ outcome
435
+ });
436
+ const fields = {
437
+ action: {
438
+ kind: executedKind(action),
439
+ target: action.target,
440
+ bytes: outcome.bytes,
441
+ durationMs: Date.now() - began
442
+ },
443
+ ...action.workspace && action.kind !== "would-remove-worktree" ? { workspace: action.workspace } : {}
444
+ };
445
+ if (outcome.status === "done") record("maintenance_action", action.kind === "would-remove-worktree" ? "warn" : "info", `${describeDone(action)} (${formatBytes(outcome.bytes)}): ${action.reason}`, fields);
446
+ else if (outcome.status === "failed") {
447
+ failures.push(`${describeDone(action)} failed: ${outcome.detail}`);
448
+ record("maintenance_failure", "error", `Could not process ${action.target}: ${outcome.detail}`, {
449
+ target: action.target,
450
+ error: outcome.detail,
451
+ ...fields.workspace ? { workspace: fields.workspace } : {}
452
+ });
453
+ } else if (!loggedSkips.has(action.target)) {
454
+ loggedSkips.add(action.target);
455
+ record("maintenance_skip", "info", `Kept ${action.target}: ${outcome.detail}`, {
456
+ target: action.target,
457
+ reason: outcome.detail,
458
+ ...fields.workspace ? { workspace: fields.workspace } : {}
459
+ });
460
+ }
461
+ }
462
+ return results;
191
463
  }
464
+ const retained = state.plan.filter((action) => (action.check === "sweep" || action.check === "worktree") && !ran.has(action.check));
192
465
  const previousActions = new Set(state.plan.map(actionKey));
193
- const nextActions = new Set(result.actions.map(actionKey));
194
466
  const previousSkips = new Set(state.skipKeys ?? []);
195
467
  const nextSkips = new Set(result.skips.map((skip) => skip.target));
196
- const blocked = [...result.blocked, ...failures];
197
- const changed = !state.lastPass || !sameSet(previousActions, nextActions) || !sameSet(previousSkips, nextSkips) || !sameSet(new Set(state.lastPass.blocked.map(stableText)), new Set(blocked.map(stableText)));
198
- for (const action of result.actions) {
199
- if (previousActions.has(actionKey(action))) continue;
200
- previousActions.add(actionKey(action));
201
- record("maintenance_action", "info", `Would ${action.kind === "would-clear-outputs" ? `clear build outputs of ${basename(action.workspace ?? action.target)}` : `${action.kind.slice(6).replaceAll("-", " ")} ${action.target}`} (${formatBytes(action.bytes)}): ${action.reason}`, {
202
- action: {
203
- ...action,
204
- durationMs: 0
205
- },
206
- ...action.workspace ? { workspace: action.workspace } : {}
207
- });
208
- }
468
+ const loggedSkips = new Set(previousSkips);
209
469
  for (const skip of result.skips) {
210
- if (previousSkips.has(skip.target)) continue;
211
- previousSkips.add(skip.target);
470
+ if (loggedSkips.has(skip.target)) continue;
471
+ loggedSkips.add(skip.target);
212
472
  record("maintenance_skip", "info", `Kept ${skip.target}: ${skip.reason}`, skip);
213
473
  }
214
- state.plan = result.actions;
215
- state.skipKeys = [...nextSkips];
216
- state.lastPass = {
217
- startedAt,
218
- durationMs: Date.now() - startedAt,
219
- trigger,
220
- mode: "report",
221
- freedBytes: 0,
222
- actions: result.actions.length,
223
- stopped: 0,
224
- blocked
225
- };
226
- if (measuredSizes || changed) record("maintenance_pass", state.lastPass.blocked.length ? "warn" : "info", `Report-only pass: ${result.actions.length} planned actions${state.lastPass.blocked.length ? `; ${state.lastPass.blocked.join("; ")}` : ""}`, { ...state.lastPass });
474
+ if (settings.mode === "on") {
475
+ const outcomes = await executePlan(result.actions);
476
+ const blockedNow = [
477
+ ...result.blocked,
478
+ ...failures,
479
+ ...outcomes.flatMap(({ outcome }) => outcome.shortfall ? [outcome.shortfall] : [])
480
+ ];
481
+ const done = outcomes.filter(({ outcome }) => outcome.status === "done");
482
+ const freedBytes = done.reduce((sum, { outcome }) => sum + outcome.bytes, 0);
483
+ for (const { action, outcome } of done) shrinkSizes(state, action, outcome);
484
+ state.plan = outcomes.filter(({ outcome }) => outcome.status !== "done").map(({ action }) => action);
485
+ state.skipKeys = [...nextSkips, ...outcomes.filter(({ outcome }) => outcome.status === "kept").map(({ action }) => action.target)];
486
+ const previousBlocked = state.lastPass?.blocked;
487
+ state.lastPass = {
488
+ startedAt,
489
+ durationMs: Date.now() - startedAt,
490
+ trigger,
491
+ mode: "on",
492
+ freedBytes,
493
+ actions: done.length,
494
+ stopped: 0,
495
+ blocked: blockedNow
496
+ };
497
+ const blockedChanged = !sameSet(new Set((previousBlocked ?? []).map(stableText)), new Set(state.lastPass.blocked.map(stableText)));
498
+ if (done.length || measuredSizes || blockedChanged) record("maintenance_pass", state.lastPass.blocked.length ? "warn" : "info", `Pass: ${done.length} actions, ${formatBytes(freedBytes)} freed${state.lastPass.blocked.length ? `; ${state.lastPass.blocked.join("; ")}` : ""}`, { ...state.lastPass });
499
+ } else {
500
+ const blocked = [...result.blocked, ...failures];
501
+ const planned = [...result.actions, ...retained];
502
+ const nextActions = new Set(planned.map(actionKey));
503
+ const changed = !state.lastPass || !sameSet(previousActions, nextActions) || !sameSet(previousSkips, nextSkips) || !sameSet(new Set(state.lastPass.blocked.map(stableText)), new Set(blocked.map(stableText)));
504
+ for (const action of result.actions) {
505
+ if (previousActions.has(actionKey(action))) continue;
506
+ previousActions.add(actionKey(action));
507
+ record("maintenance_action", "info", `Would ${action.kind === "would-clear-outputs" ? `clear build outputs of ${basename(action.workspace ?? action.target)}` : `${action.kind.slice(6).replaceAll("-", " ")} ${action.target}`} (${formatBytes(action.bytes)}): ${action.reason}`, {
508
+ action: {
509
+ ...action,
510
+ durationMs: 0
511
+ },
512
+ ...action.workspace ? { workspace: action.workspace } : {}
513
+ });
514
+ }
515
+ state.plan = planned;
516
+ state.skipKeys = [...nextSkips];
517
+ state.lastPass = {
518
+ startedAt,
519
+ durationMs: Date.now() - startedAt,
520
+ trigger,
521
+ mode: "report",
522
+ freedBytes: 0,
523
+ actions: planned.length,
524
+ stopped: 0,
525
+ blocked
526
+ };
527
+ if (measuredSizes || changed) record("maintenance_pass", state.lastPass.blocked.length ? "warn" : "info", `Report-only pass: ${planned.length} planned actions${state.lastPass.blocked.length ? `; ${state.lastPass.blocked.join("; ")}` : ""}`, { ...state.lastPass });
528
+ }
227
529
  writeState(state);
228
530
  } catch (error) {
229
531
  const message = error instanceof Error ? error.message : String(error);
@@ -1,9 +1,9 @@
1
- import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
- import { M as isOnMountedVolume, N as listMountedVolumes, _ as loadConfig, n as allMetroPorts, r as claimMetroPort, x as releaseMetroPort } from "./config-D0DbpZtM.mjs";
3
- import { t as process_identity_exports } from "./process-identity-BmRm1qV0.mjs";
4
- import { r as readWorkspaceState } from "./workspace-state-DqcCbpE7.mjs";
5
- import { existsSync, readlinkSync, realpathSync } from "fs";
1
+ import { t as process_identity_exports } from "./process-identity-Biv3arCO.mjs";
2
+ import { t as getExecutor } from "./exec-DWhCZDGm.mjs";
3
+ import { M as isOnMountedVolume, N as listMountedVolumes, _ as loadConfig, n as allMetroPorts, r as claimMetroPort, x as releaseMetroPort } from "./config-CgYMahz7.mjs";
4
+ import { r as readWorkspaceState } from "./workspace-state-C59dqG-Z.mjs";
6
5
  import { sep } from "path";
6
+ import { existsSync, readlinkSync, realpathSync } from "fs";
7
7
  import { request } from "http";
8
8
  import { connect } from "net";
9
9
  //#region src/ports.ts
@@ -1,8 +1,8 @@
1
- import { t as process_identity_exports } from "./process-identity-BmRm1qV0.mjs";
2
- import { a as updateWorkspaceState, r as readWorkspaceState$1 } from "./workspace-state-DqcCbpE7.mjs";
3
- import { S as realIo } from "./client-SvBdgObD.mjs";
4
- import { randomBytes, randomUUID } from "node:crypto";
1
+ import { t as process_identity_exports } from "./process-identity-Biv3arCO.mjs";
2
+ import { a as updateWorkspaceState, r as readWorkspaceState$1 } from "./workspace-state-C59dqG-Z.mjs";
3
+ import { w as realIo } from "./client-DzhapoMB.mjs";
5
4
  import { HOSTED_METRO_REQUESTS_KEY, findPeer, hostedMetroGateways, hostedMetroRequests, isJsonObject, parseMachine } from "@stim-cli/core/state";
5
+ import { randomBytes, randomUUID } from "node:crypto";
6
6
  import { isIP } from "node:net";
7
7
  //#region src/device-host/metro-gateway.ts
8
8
  function gatewayAddresses(credential, status) {
@@ -1,7 +1,21 @@
1
1
  import { existsSync, readFileSync } from "node:fs";
2
2
  import { dirname, join, resolve } from "node:path";
3
- import { METRO_NAMED_CACHE_LAYOUT, metroCacheRoot, registerCache, sharedStoreRoot, tagSharedStore } from "@stim-cli/core";
4
3
  import { fileURLToPath } from "node:url";
4
+ import { METRO_NAMED_CACHE_LAYOUT, metroCacheRoot, registerCache, sharedStoreRoot, tagSharedStore } from "@stim-cli/core";
5
+ //#region src/supervisor/errors.ts
6
+ function supervisorError(code, message, remedy) {
7
+ const err = new Error(message);
8
+ err.code = code;
9
+ if (remedy) err.remedy = remedy;
10
+ return err;
11
+ }
12
+ function describeError(err) {
13
+ if (!err) return "unknown error";
14
+ const e = err;
15
+ const message = e.message || String(err);
16
+ return `${e.code ? `${e.code}: ` : ""}${message}${e.remedy ? ` Remedy: ${e.remedy}` : ""}`;
17
+ }
18
+ //#endregion
5
19
  //#region src/supervisor/metro-store.ts
6
20
  function metroStoreName(root) {
7
21
  try {
@@ -89,4 +103,4 @@ function expoMetroStoreEnv({ root, storeRoot, adapterPath, existingOverride }) {
89
103
  };
90
104
  }
91
105
  //#endregion
92
- export { metroStoreRoot as a, metroStoreConfirmedRoot as i, expoMetroConfigPath as n, registerMetroStore as o, expoMetroStoreEnv as r, appendCacheStore as t };
106
+ export { metroStoreRoot as a, supervisorError as c, metroStoreConfirmedRoot as i, expoMetroConfigPath as n, registerMetroStore as o, expoMetroStoreEnv as r, describeError as s, appendCacheStore as t };
@@ -1,9 +1,64 @@
1
- import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
- import { C as saveConfig, D as withConfigLock, _ as loadConfig, c as ensureConfig, d as getConfigDir, p as getProject } from "./config-D0DbpZtM.mjs";
3
- import { t as process_identity_exports } from "./process-identity-BmRm1qV0.mjs";
4
- import { f as isPortFree, i as listeningPids, u as signalProcessTree } from "./metro-g8NqiJp-.mjs";
5
- import { t as withWorkspaceProcessLock } from "./workspace-process-lock-2mNzOlaB.mjs";
1
+ import { n as ownership_claim_exports, t as process_identity_exports } from "./process-identity-Biv3arCO.mjs";
2
+ import { t as getExecutor } from "./exec-DWhCZDGm.mjs";
3
+ import { C as saveConfig, D as withConfigLock, _ as loadConfig, c as ensureConfig, d as getConfigDir, p as getProject } from "./config-CgYMahz7.mjs";
4
+ import { f as isPortFree, i as listeningPids, u as signalProcessTree } from "./metro-Dh8T4Ytd.mjs";
5
+ import { n as declareSpawnsOn, o as stopDeclaringSpawnsOn } from "./spawn-claims-Kqiifsk2.mjs";
6
6
  import { realpathSync } from "node:fs";
7
+ import { join } from "node:path";
8
+ import { setTimeout } from "node:timers/promises";
9
+ //#region src/engine/workspace-process-lock.ts
10
+ const DEFAULT_WAIT_MS = 6e4;
11
+ const POLL_MS = 25;
12
+ function workspaceProcessLockError(err) {
13
+ const code = err?.code;
14
+ if (code === "STIM_LOCK_REFUSED") return "refused";
15
+ if (code === "STIM_LOCK_TIMEOUT") return "timeout";
16
+ return null;
17
+ }
18
+ function workspaceProcessLockPath(root, name, external) {
19
+ return external ? join(root, `${name}.lock`) : join(root, ".stim", `${name}.lock`);
20
+ }
21
+ async function withWorkspaceProcessLock(root, name, fn, { now = Date.now, signal, sleep = (ms) => setTimeout(ms, void 0, { signal }), waitMs = DEFAULT_WAIT_MS, ownerPurpose, details = {}, onHeld, rejectOwnerPurposes = [], external = false, declareSpawns = false } = {}) {
22
+ const path = workspaceProcessLockPath(root, name, external);
23
+ const deadline = now() + waitMs;
24
+ for (;;) {
25
+ signal?.throwIfAborted();
26
+ const attempt = (0, ownership_claim_exports.tryAcquireClaim)({
27
+ root: path,
28
+ mode: "exclusive",
29
+ label: `${name} lock`,
30
+ details: ownerPurpose ? {
31
+ ...details,
32
+ purpose: ownerPurpose
33
+ } : details
34
+ });
35
+ if (attempt.pending) (0, ownership_claim_exports.releaseClaim)(attempt.pending);
36
+ if (attempt.acquired) {
37
+ if (declareSpawns) declareSpawnsOn(attempt.acquired);
38
+ try {
39
+ return await fn(attempt.acquired);
40
+ } finally {
41
+ stopDeclaringSpawnsOn(attempt.acquired);
42
+ (0, ownership_claim_exports.releaseClaim)(attempt.acquired);
43
+ }
44
+ }
45
+ const holder = attempt.held ?? attempt.waitingFor?.[0];
46
+ const purpose = holder?.details.purpose;
47
+ if (typeof purpose === "string" && rejectOwnerPurposes.includes(purpose)) {
48
+ const error = /* @__PURE__ */ new Error(`The ${name} lock at ${path} is held for ${purpose}.`);
49
+ error.code = "STIM_LOCK_REFUSED";
50
+ throw error;
51
+ }
52
+ if (holder && onHeld) await onHeld(holder);
53
+ if (now() >= deadline) {
54
+ const error = /* @__PURE__ */ new Error(`Timed out waiting for the ${name} lock at ${path}.`);
55
+ error.code = "STIM_LOCK_TIMEOUT";
56
+ throw error;
57
+ }
58
+ await sleep(POLL_MS);
59
+ }
60
+ }
61
+ //#endregion
7
62
  //#region src/named-ports.ts
8
63
  const FIRST_PORT = 8900;
9
64
  const LAST_PORT = 8999;
@@ -165,4 +220,4 @@ async function clearNamedPorts(projectPath, { label, stop = false, dryRun = fals
165
220
  });
166
221
  }
167
222
  //#endregion
168
- export { reserveBrowserPort as i, getNamedPort as n, releaseBrowserPort as r, clearNamedPorts as t };
223
+ export { withWorkspaceProcessLock as a, reserveBrowserPort as i, getNamedPort as n, workspaceProcessLockError as o, releaseBrowserPort as r, workspaceProcessLockPath as s, clearNamedPorts as t };
@@ -1,7 +1,7 @@
1
- import { a as formatElapsed } from "./command-output-CDAIFD-8.mjs";
2
- import { a as updateWorkspaceState, r as readWorkspaceState, t as clearWorkspaceStateKey, v as workspaceDir } from "./workspace-state-DqcCbpE7.mjs";
3
- import { i as signalDeclaredSpawns, r as refuseDeclaredSpawns, t as allowDeclaredSpawns } from "./spawn-claims-D0XfSe8u.mjs";
4
- import { t as withWorkspaceProcessLock } from "./workspace-process-lock-2mNzOlaB.mjs";
1
+ import { a as formatElapsed } from "./command-output-CBtzyIgF.mjs";
2
+ import { a as updateWorkspaceState, r as readWorkspaceState, t as clearWorkspaceStateKey, v as workspaceDir } from "./workspace-state-C59dqG-Z.mjs";
3
+ import { i as signalDeclaredSpawns, r as refuseDeclaredSpawns, t as allowDeclaredSpawns } from "./spawn-claims-Kqiifsk2.mjs";
4
+ import { a as withWorkspaceProcessLock } from "./named-ports-BFIz1Shz.mjs";
5
5
  //#region src/engine/native-run.ts
6
6
  const NATIVE_RUN_LOCK = "native-run";
7
7
  const NATIVE_RUN_WAIT_MS = 18e5;
@@ -1,9 +1,9 @@
1
- import { l as phaseLine } from "./command-output-CDAIFD-8.mjs";
2
- import { x as metroPortSetting } from "./settings-Cfd-0r2d.mjs";
3
- import { l as resolveProjectMetro } from "./metro-g8NqiJp-.mjs";
4
- import { r as readWorkspaceState$1, u as ensureWorkspaceStorage } from "./workspace-state-DqcCbpE7.mjs";
5
- import { r as LAUNCH_UNVERIFIED, t as LAUNCH_BUNDLING } from "./launch-verify-lTnWZikR.mjs";
6
- import { c as startDevServer } from "./start-DUz3mYdb.mjs";
1
+ import { l as phaseLine } from "./command-output-CBtzyIgF.mjs";
2
+ import { l as resolveProjectMetro } from "./metro-Dh8T4Ytd.mjs";
3
+ import { r as readWorkspaceState$1, u as ensureWorkspaceStorage } from "./workspace-state-C59dqG-Z.mjs";
4
+ import { x as metroPortSetting } from "./settings-CQyMElEP.mjs";
5
+ import { r as LAUNCH_UNVERIFIED, t as LAUNCH_BUNDLING } from "./launch-verify-hskNcWkZ.mjs";
6
+ import { c as startDevServer } from "./start-D-IeOyoK.mjs";
7
7
  import { readIdleStop } from "@stim-cli/core/state";
8
8
  import chalk from "chalk";
9
9
  //#region src/commands/native-runtime.ts
@@ -1,7 +1,7 @@
1
- import { closeSync, mkdirSync, openSync, writeSync } from "fs";
2
1
  import { dirname } from "path";
2
+ import { LEVELS, SOURCES, levelRank, parseNdjsonLine, readNdjsonGenerations as readNdjsonGenerations$1, runId } from "@stim-cli/core/state";
3
+ import { closeSync, mkdirSync, openSync, writeSync } from "fs";
3
4
  import { rotateLog } from "@stim-cli/core";
4
- import { LEVELS, SOURCES, levelRank, parseNdjsonLine, readNdjsonGenerations as readNdjsonGenerations$1 } from "@stim-cli/core/state";
5
5
  //#region src/ndjson.ts
6
6
  const ROTATE_CHECK_MS = 1e3;
7
7
  function formatNdjsonLine(record) {
@@ -104,9 +104,13 @@ function createNdjsonWriter(file, { truncate = false, fields = {}, maxBytes, now
104
104
  }
105
105
  function stamp(record) {
106
106
  const base = record && typeof record === "object" && !Array.isArray(record) ? record : { msg: String(record) };
107
- if (typeof base.ts === "number" && Number.isFinite(base.ts)) return base;
108
- return {
107
+ const withRun = typeof base.runId === "string" ? base : {
109
108
  ...base,
109
+ runId: runId()
110
+ };
111
+ if (typeof base.ts === "number" && Number.isFinite(base.ts)) return withRun;
112
+ return {
113
+ ...withRun,
110
114
  ts: Date.now()
111
115
  };
112
116
  }