stim 1.17.2 → 1.19.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 (162) hide show
  1. package/README.md +50 -1
  2. package/dist/{activity-IRvJY_ru.mjs → activity-BSw0x84R.mjs} +4 -4
  3. package/dist/{agent-device-usage-output-BK41vnzN.mjs → agent-device-usage-output-Bjxu_VmX.mjs} +8 -8
  4. package/dist/{android-B9yaNKwa.mjs → android-BXc6BmEi.mjs} +658 -404
  5. package/dist/{android-B_YNZy61.mjs → android-BdQC6zs1.mjs} +9 -9
  6. package/dist/android-BvV1gGdN.mjs +4 -0
  7. package/dist/{android-DE4v59Mb.mjs → android-DE5g_5gv.mjs} +2 -2
  8. package/dist/{android-cas-ZFpjJBL_.mjs → android-cas-C87geAg-.mjs} +5 -5
  9. package/dist/android-cas-compiler.mjs +2 -2
  10. package/dist/api-DIf_s86s.mjs +150 -0
  11. package/dist/api-run.d.mts +1 -0
  12. package/dist/api-run.mjs +145 -0
  13. package/dist/api.d.mts +218 -0
  14. package/dist/api.mjs +2 -0
  15. package/dist/{app-install-VPkI5WEg.mjs → app-install-CFI4ptUL.mjs} +33 -13
  16. package/dist/{attempt-Bk2ouQVL.mjs → attempt-DW-mQqM7.mjs} +14 -4
  17. package/dist/{web-Dl0KSRHf.mjs → browser-web-CDhqQxhv.mjs} +138 -176
  18. package/dist/{budget-DfcgFdQB.mjs → budget-Dz-kyXLv.mjs} +1391 -58
  19. package/dist/build-facts-B87vo-Im.d.mts +142 -0
  20. package/dist/{cache-manifest-DnUHBJaU.mjs → cache-manifest-DiwKSyv9.mjs} +3 -3
  21. package/dist/cache-manifest.mjs +1 -1
  22. package/dist/{chrome-BrcVWQyD.mjs → chrome-BTUOo8p2.mjs} +1 -1
  23. package/dist/cli-C9Zvgbdc.mjs +68 -0
  24. package/dist/cli.mjs +1 -1
  25. package/dist/{client-SvBdgObD.mjs → client-CuUo8Nvg.mjs} +381 -49
  26. package/dist/collector-run.d.mts +6 -0
  27. package/dist/collector-run.mjs +9 -9
  28. package/dist/{command-output-CDAIFD-8.mjs → command-output-Bt8ygHE7.mjs} +2 -2
  29. package/dist/{config-D0DbpZtM.mjs → config-DC5jEtd1.mjs} +9 -5
  30. package/dist/{created-devices-CpvzBL25.mjs → created-devices-D0s26Hx5.mjs} +2 -2
  31. package/dist/{dependency-state-CLcBHH2M.mjs → dependency-state-DKaS1OM9.mjs} +2 -2
  32. package/dist/{detached-entry-CiTEgMR1.mjs → detached-entry-pB_IPfV4.mjs} +1 -1
  33. package/dist/{dev-client-B-XC4uRH.mjs → dev-client-CDY_zAmD.mjs} +2 -2
  34. package/dist/{device-C8IOoJPv.mjs → device-BHw0WlHl.mjs} +8 -8
  35. package/dist/{device-capacity-ZC4XSZ4R.mjs → device-capacity-BoN1jQkO.mjs} +25 -14
  36. package/dist/device-host-worker.mjs +19 -19
  37. package/dist/{gradle-dJpre3Yn.mjs → device-ios-B2ruohE-.mjs} +661 -107
  38. package/dist/{device-lease-DpoS36_u.mjs → device-lease-Cu7PtQpZ.mjs} +9 -9
  39. package/dist/{device-lease-run-B8YltIH_.mjs → device-lease-run-D38SnZtt.mjs} +4 -4
  40. package/dist/{device-pool-3o7s5lo4.mjs → device-pool-CP4rAsHx.mjs} +3 -3
  41. package/dist/{device-remote-hFSkpfQu.mjs → device-remote-B5GiUifk.mjs} +15 -16
  42. package/dist/{doctor-CFv1fCrF.mjs → doctor-PRJH94v6.mjs} +61 -34
  43. package/dist/doctor-watchman-wZhyTdp4.mjs +259 -0
  44. package/dist/{error-diagnostics-C-NMnhrk.mjs → error-diagnostics--v5HGOU0.mjs} +11 -11
  45. package/dist/{exec-CaErVv7t.mjs → exec-i8GDPwq9.mjs} +93 -6
  46. package/dist/{gc-DEmkg3Xh.mjs → gc-CyHMcKcn.mjs} +25 -471
  47. package/dist/{guide-CSmhMTf8.mjs → guide-dRaQJJk9.mjs} +681 -507
  48. package/dist/{hosted-android-E1Heoqnv.mjs → hosted-android-Bs7fytj7.mjs} +3 -3
  49. package/dist/{hosted-client-Bp4knSwu.mjs → hosted-client-C5G4h_OJ.mjs} +2 -2
  50. package/dist/{hosted-ios-BnbeYOUD.mjs → hosted-ios-Bik035eL.mjs} +1 -1
  51. package/dist/{hosted-logs-OcgIBvCT.mjs → hosted-logs-CI9Az9a4.mjs} +9 -9
  52. package/dist/{hosted-macos-DcxKjtfH.mjs → hosted-macos-DABm3ScO.mjs} +5 -5
  53. package/dist/{hosted-native-1Ebbig4w.mjs → hosted-native-DbV2aUVw.mjs} +13 -9
  54. package/dist/{idle-shutdown-IGyNV8Cr.mjs → idle-shutdown-DYiMBn34.mjs} +7 -8
  55. package/dist/{ios-DSfnPfse.mjs → ios-BWbHFZVz.mjs} +19 -7
  56. package/dist/{ios-Cr1h5gnO.mjs → ios-DCrkgXEM.mjs} +1 -1
  57. package/dist/ios-DYUr9LGc.mjs +8 -0
  58. package/dist/ios-ITYno9rO.mjs +2241 -0
  59. package/dist/{ios-device-pAN4t54-.mjs → ios-device-CJ_ZOGky.mjs} +3 -3
  60. package/dist/{ios-device-Ee7sDXfp.mjs → ios-device-CrW8aMb5.mjs} +1 -1
  61. package/dist/{ios-state-8L3lgum6.mjs → ios-state-By0XuLTQ.mjs} +1 -1
  62. package/dist/{launch-verify-lTnWZikR.mjs → launch-verify-DCveSE6d.mjs} +5 -5
  63. package/dist/{logs-BAaS7gS2.mjs → logs-Bao1HOMy.mjs} +13 -13
  64. package/dist/{macos-BIRQS90f.mjs → macos-ciyBdTY5.mjs} +194 -132
  65. package/dist/macos-nIOATJmR.mjs +2 -0
  66. package/dist/macos-run.mjs +1 -1
  67. package/dist/maintenance-run.mjs +349 -47
  68. package/dist/manifest-CX0d1IkZ.mjs +148 -0
  69. package/dist/{metro-g8NqiJp-.mjs → metro-DI2AjPF_.mjs} +196 -43
  70. package/dist/{metro-gateway-C-ijGI2F.mjs → metro-gateway-BxpJLIwh.mjs} +4 -4
  71. package/dist/{metro-store-CDDZHtPl.mjs → metro-store-BAHFcJm6.mjs} +16 -2
  72. package/dist/{build-plan-BDsSi09V.mjs → miss-reason-CjQCOKqq.mjs} +207 -249
  73. package/dist/{named-ports-C8m8EQs2.mjs → named-ports-B4iWH4E-.mjs} +70 -8
  74. package/dist/{native-runtime-BEK171xQ.mjs → native-runtime-BGxsbfny.mjs} +6 -6
  75. package/dist/{ndjson-PclOIwVY.mjs → ndjson-LGZAMsqa.mjs} +8 -4
  76. package/dist/offload-worker.d.mts +3 -45
  77. package/dist/offload-worker.mjs +11 -11
  78. package/dist/{ownership-DyRQru0q.mjs → ownership-EB0ySdGK.mjs} +3 -3
  79. package/dist/{page-QF_Ke10s.mjs → page-Bv9s2KwY.mjs} +1 -1
  80. package/dist/placement-log-Dxe34rJm.mjs +88 -0
  81. package/dist/plan-placement-BVY59qa8.mjs +290 -0
  82. package/dist/{ports-4nYqxPWC.mjs → ports-ByPc0_97.mjs} +3 -3
  83. package/dist/{prebuild-D7RUMkwN.mjs → prebuild-BKJ3wJI1.mjs} +5 -5
  84. package/dist/preview-BbSottmO.mjs +986 -0
  85. package/dist/{rolldown-runtime-BsOwBuO_.mjs → process-identity-Biv3arCO.mjs} +11 -1
  86. package/dist/project-B5eQ1USs.mjs +612 -0
  87. package/dist/{pull-request-pxnDCQzn.mjs → pull-request-IHNdMnG8.mjs} +1 -1
  88. package/dist/pull-requests.mjs +1 -1
  89. package/dist/react-native-ios-BMh2i25I.mjs +2 -0
  90. package/dist/react-native-ios-CSpIxexZ.mjs +2083 -0
  91. package/dist/{recordings-BNgsD1Tr.mjs → recordings-Bv8McZN4.mjs} +1 -1
  92. package/dist/{reload-DcRxj3qG.mjs → reload-rAPdqeRR.mjs} +14 -15
  93. package/dist/{remote-cache-Ci4DQYYj.mjs → remote-cache-N3hAEaAL.mjs} +5 -5
  94. package/dist/{run-CkE9y0nc.mjs → run-lAkle1-c.mjs} +5 -5
  95. package/dist/{server-bare-BkrSxSsZ.mjs → server-bare-CkIjnegq.mjs} +5 -6
  96. package/dist/server-expo-DGsdUGX2.mjs +2 -0
  97. package/dist/{server-expo-CHTyXD0G.mjs → server-expo-n3tUi3Em.mjs} +5 -6
  98. package/dist/{settings-SR9OmW9h.mjs → settings-2am7TSxp.mjs} +5 -5
  99. package/dist/{settings-Cfd-0r2d.mjs → settings-CxgngUZk.mjs} +116 -103
  100. package/dist/{settings-BBpMcDbD.mjs → settings-Djl-mpjX.mjs} +1 -1
  101. package/dist/settings.schema.json +198 -9
  102. package/dist/{simslim-BWNrc3M3.mjs → simslim-D9LkJxIO.mjs} +7 -7
  103. package/dist/{slot-launch-DmjBmpQV.mjs → slot-launch-HV_KtPIw.mjs} +6 -6
  104. package/dist/{spawn-claims-D0XfSe8u.mjs → spawn-claims-Kqiifsk2.mjs} +1 -1
  105. package/dist/{spawn-entry-jw19m8Nl.mjs → spawn-entry-Dcb7sDUz.mjs} +1 -0
  106. package/dist/{stage-CzaiwFsI.mjs → stage-BgaZJVZY.mjs} +14 -3
  107. package/dist/{start-AlfCPRVU.mjs → start-D8NbxNBb.mjs} +1 -1
  108. package/dist/{start-DUz3mYdb.mjs → start-DtxthMfI.mjs} +34 -27
  109. package/dist/{state-PeQiJuIu.mjs → state-D2KtnALG.mjs} +4 -4
  110. package/dist/{state-DyaNTgfR.mjs → state-Tp4uImDS.mjs} +5 -3
  111. package/dist/{state-B4PQFXMW.d.mts → state-lKh8shBp.d.mts} +2 -3
  112. package/dist/{stats-Bq4fJOdB.mjs → stats-8EJ5ivFT.mjs} +6 -6
  113. package/dist/{status-DlKvVJBE.mjs → status-BEeL6IKz.mjs} +6 -4
  114. package/dist/{status-BA2pehwf.mjs → status-BOyp0CUl.mjs} +31 -34
  115. package/dist/{stim-desktop-Cnz8CPnl.mjs → stim-desktop-C65jk_q2.mjs} +1 -1
  116. package/dist/{stim-installations-QMEI32v1.mjs → stim-installations-DbmUjsLv.mjs} +1 -1
  117. package/dist/stop-D0UIpZuh.mjs +72 -0
  118. package/dist/{stop-zF1y04f2.mjs → stop-DRipAg-e.mjs} +2 -2
  119. package/dist/{stop-DQIasgKp.mjs → stop-zDlknFoz.mjs} +79 -33
  120. package/dist/supervisor-run.d.mts +24 -22
  121. package/dist/supervisor-run.mjs +21 -22
  122. package/dist/{support-CsJNVfOc.mjs → support-CNqYkV0y.mjs} +7 -7
  123. package/dist/{support-WvD__tSV.mjs → support-DsNaFg6D.mjs} +5 -5
  124. package/dist/swiftpm-macos-aJzPAj1t.mjs +119 -0
  125. package/dist/{toolchain-DNyR3NPq.mjs → toolchain-C3xSoyUl.mjs} +475 -269
  126. package/dist/{trigger-Bemu8ytw.mjs → trigger-CAhmG-4s.mjs} +6 -6
  127. package/dist/trigger-CoalsMTV.mjs +2 -0
  128. package/dist/{warm-progress-DF4JRvYI.mjs → warm-progress-pFLB2e6P.mjs} +135 -38
  129. package/dist/{status-CzS4bPO9.mjs → watchman-BL1IdOtq.mjs} +75 -7
  130. package/dist/web-DFpFeZu5.mjs +2 -0
  131. package/dist/web-DbbrPUn0.mjs +127 -0
  132. package/dist/web-run.mjs +10 -10
  133. package/dist/{workspace-state-DqcCbpE7.mjs → workspace-state-CR9X86xJ.mjs} +3 -3
  134. package/dist/{ownership-BMLqyBgJ.mjs → workspaces-Djb2gfi9.mjs} +661 -23
  135. package/dist/{worktree-Be_7NLKy.mjs → worktree-BL4M1rhB.mjs} +252 -50
  136. package/dist/{worktree-j3IcFAI5.mjs → worktree-DV-A0LNL.mjs} +1 -1
  137. package/package.json +10 -5
  138. package/shim/bundle-response.cjs +2 -1
  139. package/shim/bundle-response.d.cts +1 -1
  140. package/shim/expo-metro-config.cjs +1 -1
  141. package/dist/build-progress-D0WkjCPd.mjs +0 -1036
  142. package/dist/build-slots-41v_d2x9.mjs +0 -142
  143. package/dist/cli-CYXb29oE.mjs +0 -51
  144. package/dist/deps-iKw0eD4P.mjs +0 -438
  145. package/dist/device-ios-C2Um-XUR.mjs +0 -412
  146. package/dist/errors-B6W7FkxQ.mjs +0 -15
  147. package/dist/guide-status-Du-ZQ3FD.mjs +0 -125
  148. package/dist/idle-BXG__1BA.mjs +0 -176
  149. package/dist/in-use-CvGOUn-x.mjs +0 -130
  150. package/dist/ios-BJ4XUSe1.mjs +0 -3867
  151. package/dist/native-run-D1Q56rAD.mjs +0 -158
  152. package/dist/preview-B-g9d7kV.mjs +0 -318
  153. package/dist/process-identity-BmRm1qV0.mjs +0 -12
  154. package/dist/project-D47Rh0ly.mjs +0 -276
  155. package/dist/server-expo-CeakhI8P.mjs +0 -2
  156. package/dist/state-BKa8wNN5.mjs +0 -148
  157. package/dist/stop-BIiM49EY.mjs +0 -48
  158. package/dist/stop-cause-rhmGfwvt.mjs +0 -97
  159. package/dist/trigger-CXts6qt1.mjs +0 -2
  160. package/dist/watchman-APYjXSQa.mjs +0 -48
  161. package/dist/workspace-process-lock-2mNzOlaB.mjs +0 -58
  162. 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-DC5jEtd1.mjs";
2
+ import { D as projectMaintenancePinned } from "./settings-CxgngUZk.mjs";
3
+ import { a as unregister, n as readManifest } from "./cache-manifest-DiwKSyv9.mjs";
4
+ import { Nt as discoverCaches, Pt as pruneCache, l as resolveBudget, pt as planCacheEmptying, r as cacheLabel, st as emptyCaches } from "./budget-Dz-kyXLv.mjs";
5
+ import { I as takeGcResults, L as canonicalPath, a as collectWorkspaceOutputs, i as collectOrphanedWorkspaces, o as deleteOrphanedWorkspaces, r as clearWorkspaceOutputs } from "./workspaces-Djb2gfi9.mjs";
6
+ import { r as createNdjsonWriter } from "./ndjson-LGZAMsqa.mjs";
7
+ import { t as relaunchWithLogFile } from "./detached-entry-pB_IPfV4.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-BbSottmO.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);
@@ -0,0 +1,148 @@
1
+ import { t as getExecutor } from "./exec-i8GDPwq9.mjs";
2
+ import { l as phaseLine } from "./command-output-Bt8ygHE7.mjs";
3
+ import { _ as loadConfig } from "./config-DC5jEtd1.mjs";
4
+ import { f as sharedCcache, x as workspaceLogsDir } from "./workspace-state-CR9X86xJ.mjs";
5
+ import { r as register } from "./cache-manifest-DiwKSyv9.mjs";
6
+ import { o as machineNumber } from "./budget-Dz-kyXLv.mjs";
7
+ import { readFileSync, realpathSync } from "node:fs";
8
+ import { isAbsolute, join } from "node:path";
9
+ import chalk from "chalk";
10
+ import { createHash } from "node:crypto";
11
+ const CCACHE_SLOPPINESS = "pch_defines,time_macros";
12
+ const STATS_LOG_FILE = "ccache-stats.log";
13
+ const CCACHE_UNAVAILABLE = {
14
+ status: "unavailable",
15
+ hits: null,
16
+ misses: null,
17
+ hitRatePercent: null
18
+ };
19
+ const CCACHE_NOT_RUN = {
20
+ status: "not-run",
21
+ hits: null,
22
+ misses: null,
23
+ hitRatePercent: null
24
+ };
25
+ function ccacheStatsLog(root) {
26
+ return join(workspaceLogsDir(root), STATS_LOG_FILE);
27
+ }
28
+ function ccacheMaxSize(config = loadConfig(), env = process.env) {
29
+ const { value, error } = machineNumber("caches.ccacheMaxGb", config, env);
30
+ return error || value === void 0 ? "5G" : `${value}G`;
31
+ }
32
+ function ccacheEnvironment({ binary, dir, workspaceRoot, statsLog, maxSize = "5G" }) {
33
+ return {
34
+ CMAKE_C_COMPILER_LAUNCHER: binary,
35
+ CMAKE_CXX_COMPILER_LAUNCHER: binary,
36
+ CCACHE_DIR: dir,
37
+ CCACHE_BASEDIR: workspaceRoot,
38
+ CCACHE_NOHASHDIR: "true",
39
+ CCACHE_SLOPPINESS,
40
+ CCACHE_MAXSIZE: maxSize,
41
+ CCACHE_STATSLOG: statsLog
42
+ };
43
+ }
44
+ function parseCcacheBinary(output) {
45
+ const first = String(output ?? "").split("\n")[0]?.trim();
46
+ if (!first || !isAbsolute(first)) return null;
47
+ return first;
48
+ }
49
+ const CMAKE_LAUNCHER = /CMAKE_(C|CXX)_COMPILER_LAUNCHER\b/;
50
+ function projectCmakeLauncher(source) {
51
+ if (typeof source !== "string") return null;
52
+ const match = CMAKE_LAUNCHER.exec(source);
53
+ return match ? `CMAKE_${match[1]}_COMPILER_LAUNCHER` : null;
54
+ }
55
+ const HIT_COUNTERS = /* @__PURE__ */ new Set(["direct_cache_hit", "preprocessed_cache_hit"]);
56
+ function parseCcacheStatsLog(text) {
57
+ if (typeof text !== "string") return null;
58
+ let hits = 0;
59
+ let misses = 0;
60
+ for (const raw of text.split("\n")) {
61
+ const line = raw.trim();
62
+ if (HIT_COUNTERS.has(line)) hits += 1;
63
+ else if (line === "cache_miss") misses += 1;
64
+ }
65
+ const total = hits + misses;
66
+ if (total === 0) return null;
67
+ return {
68
+ status: "reported",
69
+ hits,
70
+ misses,
71
+ hitRatePercent: Math.round(hits / total * 1e3) / 10
72
+ };
73
+ }
74
+ function readCcacheActivity(statsLog) {
75
+ let text;
76
+ try {
77
+ text = readFileSync(statsLog, "utf-8");
78
+ } catch {
79
+ return CCACHE_UNAVAILABLE;
80
+ }
81
+ return parseCcacheStatsLog(text) ?? CCACHE_UNAVAILABLE;
82
+ }
83
+ function ccacheActivityLine(activity) {
84
+ if (activity.status === "reported") return `${activity.hits} hits / ${activity.misses} misses (${activity.hitRatePercent}%)`;
85
+ if (activity.status === "not-run") return "not run; artifact cache supplied the app";
86
+ return "unavailable; no C++ compile went through ccache";
87
+ }
88
+ function lookupCcache() {
89
+ try {
90
+ return parseCcacheBinary(getExecutor().findExecutable("ccache"));
91
+ } catch {
92
+ return null;
93
+ }
94
+ }
95
+ function readOrNull(file) {
96
+ try {
97
+ return readFileSync(file, "utf-8");
98
+ } catch {
99
+ return null;
100
+ }
101
+ }
102
+ function canonical(root) {
103
+ try {
104
+ return realpathSync(root);
105
+ } catch {
106
+ return root;
107
+ }
108
+ }
109
+ function resolveCcache({ root, dir = sharedCcache(), lookup = lookupCcache, onNote = (line) => console.error(line) }) {
110
+ const binary = lookup();
111
+ if (!binary) {
112
+ onNote(chalk.dim(phaseLine("cache", "ccache off (not installed; brew install ccache)")));
113
+ return null;
114
+ }
115
+ const declared = projectCmakeLauncher(readOrNull(join(root, "android", "app", "build.gradle")));
116
+ if (declared) {
117
+ onNote(chalk.dim(phaseLine("cache", `ccache off (android/app/build.gradle sets ${declared} itself)`)));
118
+ return null;
119
+ }
120
+ const statsLog = ccacheStatsLog(root);
121
+ register({
122
+ dir,
123
+ name: "ccache",
124
+ prune: "atomic",
125
+ note: "ccache keeps itself under CCACHE_MAXSIZE, so it is emptied whole or not at all"
126
+ });
127
+ onNote(chalk.dim(phaseLine("cache", `ccache on (${dir})`)));
128
+ return {
129
+ dir,
130
+ statsLog,
131
+ env: ccacheEnvironment({
132
+ binary,
133
+ dir,
134
+ workspaceRoot: canonical(root),
135
+ statsLog,
136
+ maxSize: ccacheMaxSize()
137
+ })
138
+ };
139
+ }
140
+ //#endregion
141
+ //#region src/offload/manifest.ts
142
+ function manifestDigest(entries) {
143
+ const hash = createHash("sha256");
144
+ for (const entry of entries.toSorted((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0)) hash.update(`${entry.path}\0${entry.kind}\0${entry.sha256}\n`);
145
+ return hash.digest("hex");
146
+ }
147
+ //#endregion
148
+ export { projectCmakeLauncher as a, ccacheActivityLine as i, CCACHE_NOT_RUN as n, readCcacheActivity as o, CCACHE_UNAVAILABLE as r, resolveCcache as s, manifestDigest as t };