stim 1.15.0 → 1.17.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 (123) hide show
  1. package/dist/{activity-B2-kb7k0.mjs → activity-IRvJY_ru.mjs} +49 -16
  2. package/dist/agent-device-usage-output-BK41vnzN.mjs +411 -0
  3. package/dist/{android-Cu_L4RpL.mjs → android--GorydsK.mjs} +692 -321
  4. package/dist/{android-Db2eEMaf.mjs → android-B_YNZy61.mjs} +25 -60
  5. package/dist/{android-CYmq0jNi.mjs → android-DE4v59Mb.mjs} +12 -3
  6. package/dist/{android-cas-PnKfAdB2.mjs → android-cas-ZFpjJBL_.mjs} +6 -5
  7. package/dist/{app-install-CAAplXAt.mjs → app-install-VPkI5WEg.mjs} +52 -8
  8. package/dist/attempt-Bk2ouQVL.mjs +39 -0
  9. package/dist/{budget-CMiqghig.mjs → budget-DM984mnd.mjs} +372 -366
  10. package/dist/{build-plan-aeGMFX7e.mjs → build-plan-CmQCoyT-.mjs} +346 -88
  11. package/dist/{build-slots-Df1ApSuH.mjs → build-slots-41v_d2x9.mjs} +36 -21
  12. package/dist/{cli-0cX5s4NM.mjs → cli-Daxug5-K.mjs} +23 -17
  13. package/dist/cli.mjs +1 -1
  14. package/dist/collector-run.mjs +5 -192
  15. package/dist/{command-output-CliHHn7a.mjs → command-output-CDAIFD-8.mjs} +3 -3
  16. package/dist/{created-devices-BwVb3liH.mjs → created-devices-CpvzBL25.mjs} +1 -1
  17. package/dist/{dependency-state-BmsjKZ2H.mjs → dependency-state-CLcBHH2M.mjs} +1 -1
  18. package/dist/{deps-BFlKbOIp.mjs → deps-DNWaQs4V.mjs} +2 -2
  19. package/dist/{dev-client-CjzLoNs0.mjs → dev-client-B-XC4uRH.mjs} +3 -40
  20. package/dist/{device-BZ1IyS5A.mjs → device-C8IOoJPv.mjs} +7 -6
  21. package/dist/device-capacity-D0GWHnTM.mjs +580 -0
  22. package/dist/device-host-worker.mjs +587 -110
  23. package/dist/{device-ios-DMQBUPus.mjs → device-ios-Bc7vbGwl.mjs} +75 -44
  24. package/dist/{device-lease-W5Pi1q38.mjs → device-lease-DpoS36_u.mjs} +2 -2
  25. package/dist/{device-pool-B2r38YjS.mjs → device-lease-run-B8YltIH_.mjs} +5 -125
  26. package/dist/device-pool-3o7s5lo4.mjs +125 -0
  27. package/dist/{device-remote-9ulPsdhY.mjs → device-remote-hFSkpfQu.mjs} +13 -13
  28. package/dist/{doctor-BcE5xsBn.mjs → doctor-Cnk9d7Hm.mjs} +86 -59
  29. package/dist/{error-diagnostics-Cb0P1WLS.mjs → error-diagnostics-C-NMnhrk.mjs} +11 -15
  30. package/dist/{gc-BuL1Genr.mjs → gc-DxCL2A46.mjs} +478 -33
  31. package/dist/{gradle-CmdOBq1d.mjs → gradle-D7-L2Th3.mjs} +4 -4
  32. package/dist/{guide-cCSGRFd3.mjs → guide-DjrTQGik.mjs} +2198 -566
  33. package/dist/{guide-status-CIoWK6mG.mjs → guide-status-Du-ZQ3FD.mjs} +1 -1
  34. package/dist/hosted-android-DwLS4Mse.mjs +32 -0
  35. package/dist/hosted-client-wk1W_CNW.mjs +196 -0
  36. package/dist/hosted-ios-aMo7Cljf.mjs +15 -0
  37. package/dist/hosted-logs-DqYmWtXc.mjs +187 -0
  38. package/dist/hosted-macos-dlHXEeF5.mjs +135 -0
  39. package/dist/hosted-native-C9xJ9rpA.mjs +255 -0
  40. package/dist/{build-progress-C2Jv1AtA.mjs → idle-B2m-Ovod.mjs} +300 -9
  41. package/dist/{idle-shutdown-B5LPaNyY.mjs → idle-shutdown-_3oOV1V_.mjs} +25 -17
  42. package/dist/{in-use-CndPMilN.mjs → in-use-CvGOUn-x.mjs} +30 -10
  43. package/dist/ios-Cr1h5gnO.mjs +189 -0
  44. package/dist/{ios-Chob8ny7.mjs → ios-DE6duQoz.mjs} +819 -460
  45. package/dist/{ios-3CtCRhwr.mjs → ios-DSfnPfse.mjs} +4 -4
  46. package/dist/{ios-device-DERmMmk-.mjs → ios-device-pAN4t54-.mjs} +1 -1
  47. package/dist/ios-state-8L3lgum6.mjs +89 -0
  48. package/dist/{launch-verify-CLlofxRt.mjs → launch-verify-lTnWZikR.mjs} +3 -3
  49. package/dist/logs-DUoKlDoS.mjs +384 -0
  50. package/dist/{client-C2IS36Qf.mjs → machines-C2XiNwh9.mjs} +330 -37
  51. package/dist/{macos-BwtbUtTN.mjs → macos-32po3kUq.mjs} +23 -18
  52. package/dist/macos-run.mjs +1 -1
  53. package/dist/maintenance-run.d.mts +4 -0
  54. package/dist/maintenance-run.mjs +254 -0
  55. package/dist/{metro-Di8EN2Tb.mjs → metro-g8NqiJp-.mjs} +1 -1
  56. package/dist/metro-gateway-DmslUM30.mjs +103 -0
  57. package/dist/{named-ports-Dj_XBNaL.mjs → named-ports-C8m8EQs2.mjs} +2 -2
  58. package/dist/{native-run-DFDoIRbY.mjs → native-run-D1Q56rAD.mjs} +17 -6
  59. package/dist/{native-runtime-i6OkVg-w.mjs → native-runtime-DhJI3IVQ.mjs} +6 -6
  60. package/dist/{ndjson-CDY9IkVC.mjs → ndjson-PclOIwVY.mjs} +2 -2
  61. package/dist/offload-worker.d.mts +1 -0
  62. package/dist/offload-worker.mjs +13 -8
  63. package/dist/{teardown-BYuQafiA.mjs → ownership-BMLqyBgJ.mjs} +639 -53
  64. package/dist/{ownership-BkYxUtuj.mjs → ownership-DyRQru0q.mjs} +2 -2
  65. package/dist/{ports-DddwttKM.mjs → ports-4nYqxPWC.mjs} +2 -2
  66. package/dist/{prebuild-BvxDlD6C.mjs → prebuild-D7RUMkwN.mjs} +4 -4
  67. package/dist/preview-CxPRIEht.mjs +318 -0
  68. package/dist/{project-CV3tDFHl.mjs → project-D47Rh0ly.mjs} +72 -3
  69. package/dist/{recordings-GEXp4iBT.mjs → recordings-BNgsD1Tr.mjs} +1 -1
  70. package/dist/{reload-ChPiwvot.mjs → reload-qZei6iWn.mjs} +77 -13
  71. package/dist/{remote-cache-CO2elm_b.mjs → remote-cache-Ci4DQYYj.mjs} +4 -4
  72. package/dist/{run-B-xqRrsg.mjs → run-CkE9y0nc.mjs} +2 -2
  73. package/dist/{server-bare-X7PUgizL.mjs → server-bare-BkrSxSsZ.mjs} +1 -1
  74. package/dist/{server-expo-DhtX6bom.mjs → server-expo-CHTyXD0G.mjs} +4 -4
  75. package/dist/server-expo-CeakhI8P.mjs +2 -0
  76. package/dist/settings-BBpMcDbD.mjs +25 -0
  77. package/dist/settings-Cfd-0r2d.mjs +1440 -0
  78. package/dist/{settings-DbL6uhH8.mjs → settings-SR9OmW9h.mjs} +10 -4
  79. package/dist/settings.schema.json +472 -50
  80. package/dist/{simslim-Ciluxlq4.mjs → simslim-BWNrc3M3.mjs} +3 -3
  81. package/dist/{slot-launch-BY0BrOLh.mjs → slot-launch-DmjBmpQV.mjs} +10 -7
  82. package/dist/{spawn-entry-DncpJvWL.mjs → spawn-entry-jw19m8Nl.mjs} +1 -0
  83. package/dist/{start-CPO4wRg7.mjs → start-BopZcr4n.mjs} +1 -1
  84. package/dist/{start-CUD9nasA.mjs → start-uee1wWjY.mjs} +21 -17
  85. package/dist/{state-CaeaHbDA.mjs → state-BKa8wNN5.mjs} +1 -1
  86. package/dist/{state-BxCgeTQu.mjs → state-DyaNTgfR.mjs} +2 -2
  87. package/dist/{state-DFm77wRn.mjs → state-PeQiJuIu.mjs} +5 -5
  88. package/dist/{stats-DBLLFmKd.mjs → stats-Bq4fJOdB.mjs} +19 -5
  89. package/dist/{status-l84Gbvp2.mjs → status-CzS4bPO9.mjs} +5 -3
  90. package/dist/status-DlKvVJBE.mjs +49 -0
  91. package/dist/{status-DYXxNOT0.mjs → status-dXX2JX1C.mjs} +244 -50
  92. package/dist/{stop-CceYh1yG.mjs → stop-BCwvvs7F.mjs} +2 -2
  93. package/dist/{stop-Bad61z_2.mjs → stop-BjizrQwv.mjs} +83 -21
  94. package/dist/{stop-Bvf1MflS.mjs → stop-Cr1d8DSE.mjs} +4 -4
  95. package/dist/stop-cause-rhmGfwvt.mjs +97 -0
  96. package/dist/supervisor-run.d.mts +7 -4
  97. package/dist/supervisor-run.mjs +156 -27
  98. package/dist/{support-WLOzVKUv.mjs → support-BJPgYCu-.mjs} +61 -11
  99. package/dist/{support-CDQbDkL5.mjs → support-F9-sr0Oi.mjs} +6 -5
  100. package/dist/{toolchain-DmeYjXD9.mjs → toolchain-CNIBTIOv.mjs} +11 -11
  101. package/dist/trigger-Bemu8ytw.mjs +65 -0
  102. package/dist/trigger-CXts6qt1.mjs +2 -0
  103. package/dist/{warm-progress-DVHLN4uC.mjs → warm-progress-DF4JRvYI.mjs} +274 -5
  104. package/dist/watchman-APYjXSQa.mjs +48 -0
  105. package/dist/{web-k6w8-HnE.mjs → web-exQtGkoc.mjs} +14 -16
  106. package/dist/web-run.mjs +5 -5
  107. package/dist/{workspace-process-lock-BL7Q0HCO.mjs → workspace-process-lock-2mNzOlaB.mjs} +3 -2
  108. package/dist/{workspace-state-CL2V2Ons.mjs → workspace-state-DqcCbpE7.mjs} +34 -34
  109. package/dist/workspaces-Bcib97-6.mjs +306 -0
  110. package/dist/{worktree-BT_uULFx.mjs → worktree-Dc98cE6_.mjs} +1 -1
  111. package/dist/{worktree-qXKzADSC.mjs → worktree-ZpZR3S1k.mjs} +601 -33
  112. package/package.json +5 -5
  113. package/dist/device-capacity-DMuKC7nx.mjs +0 -136
  114. package/dist/hosted-macos-DwowNU7W.mjs +0 -447
  115. package/dist/logs-h9h6MUvE.mjs +0 -568
  116. package/dist/machine-usage-D_YYMzfR.mjs +0 -283
  117. package/dist/machines-CYtOCqcj.mjs +0 -259
  118. package/dist/merge-state-BnxLNQiM.mjs +0 -272
  119. package/dist/server-expo-KJeD109I.mjs +0 -2
  120. package/dist/settings-CduVcoxj.mjs +0 -680
  121. package/dist/stop-cause-CZ_Mr-v9.mjs +0 -665
  122. package/dist/watchman-Bx0wceoF.mjs +0 -238
  123. package/dist/worktree-C0c5Udwa.mjs +0 -753
@@ -1,80 +1,35 @@
1
1
  import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
- import { A as directorySize, D as withConfigLock$1, F as volumeRootFor, H as projectDeviceSlots, M as isOnMountedVolume, N as listMountedVolumes, P as measuredDirectorySize, _ as loadConfig$1, a as clearDevice, d as getConfigDir, g as isPathPrefix, j as formatBytes, o as clearSupervisor, p as getProject, z as deviceSlotPlatforms } from "./config-D0DbpZtM.mjs";
2
+ import { A as directorySize, D as withConfigLock$1, F as volumeRootFor, H as projectDeviceSlots, M as isOnMountedVolume, P as measuredDirectorySize, _ as loadConfig$1, a as clearDevice, d as getConfigDir, j as formatBytes, o as clearSupervisor, p as getProject, z as deviceSlotPlatforms } from "./config-D0DbpZtM.mjs";
3
3
  import { t as process_identity_exports } from "./process-identity-BmRm1qV0.mjs";
4
4
  import { i as registeredCaches } from "./cache-manifest-DnUHBJaU.mjs";
5
- import { b as workspaceDir, m as sharedBuildCache, r as readWorkspaceState$1, s as workspaceLastUsed, w as workspaceName$1 } from "./workspace-state-CL2V2Ons.mjs";
5
+ import { l as phaseLine, o as formatLongDuration, u as plural } from "./command-output-CDAIFD-8.mjs";
6
+ import { l as resolveProjectMetro, r as killMetroTree } from "./metro-g8NqiJp-.mjs";
7
+ import { d as sharedBuildCache, r as readWorkspaceState$1, v as workspaceDir } from "./workspace-state-DqcCbpE7.mjs";
8
+ import { a as capacity, l as environmentState, u as formatSpace } from "./status-CzS4bPO9.mjs";
9
+ import { i as recordGcResult, n as withIdleWorkspace, o as canonicalPath } from "./in-use-CvGOUn-x.mjs";
10
+ import { i as createDeviceProcessTables, p as workspaceActivity } from "./activity-IRvJY_ru.mjs";
11
+ import { h as listBuildLocks, i as listLeaseFiles, r as leaseIsExpired } from "./device-lease-DpoS36_u.mjs";
12
+ import { n as listBuildSlots } from "./build-slots-41v_d2x9.mjs";
13
+ import { l as readPidFile, s as clearWorkspaceSupervisor } from "./state-BKa8wNN5.mjs";
14
+ import { a as parseWatchmanPid, i as parseWatchmanClients, n as parseTriggerCount, r as parseWatchRoots, t as parseSubscriberNames } from "./watchman-APYjXSQa.mjs";
15
+ import { A as listAvds, I as ownedAvdDirectory, at as isStimOwnedSim, ct as dropParked, it as isStimOwnedAvd, pt as readParked, z as ownedAvdSerialResolver } from "./android-B_YNZy61.mjs";
16
+ import { _ as parseRuntimeVersion, f as listIosDeviceTypes, u as listAllIosSims } from "./ios-DSfnPfse.mjs";
17
+ import { _ as teardownParkedAvd, d as eraseParkedAvd, f as eraseParkedIosSim, g as teardownOwnedIosSim, m as teardownOwnedAvd, t as resolveSupervisorTarget, v as teardownParkedIosSim } from "./ownership-BMLqyBgJ.mjs";
18
+ import { n as workspaceProcessLockError, t as withWorkspaceProcessLock } from "./workspace-process-lock-2mNzOlaB.mjs";
19
+ import { i as idleShutdownCandidates, n as collectOwnedDeviceActivity, o as shutDownIdleDevices, s as workspaceBuildInProgress, t as collectIdleDevices } from "./idle-B2m-Ovod.mjs";
20
+ import { a as collectWorkspaceOutputs, r as clearWorkspaceOutputs } from "./workspaces-Bcib97-6.mjs";
6
21
  import { n as supervisorError } from "./errors-B6W7FkxQ.mjs";
7
- import { l as readPidFile, s as clearWorkspaceSupervisor } from "./state-CaeaHbDA.mjs";
8
- import { n as workspaceProcessLockError, t as withWorkspaceProcessLock } from "./workspace-process-lock-BL7Q0HCO.mjs";
9
- import { l as workspaceActivity, r as createDeviceProcessTables } from "./activity-B2-kb7k0.mjs";
10
- import { l as phaseLine, o as formatLongDuration, u as plural } from "./command-output-CliHHn7a.mjs";
11
- import { h as listBuildLocks, i as listLeaseFiles, r as leaseIsExpired } from "./device-lease-W5Pi1q38.mjs";
12
- import { A as listAvds, I as ownedAvdDirectory, ft as readParked, it as isStimOwnedSim, rt as isStimOwnedAvd, st as dropParked, z as ownedAvdSerialResolver } from "./android-Db2eEMaf.mjs";
13
- import { l as resolveProjectMetro, r as killMetroTree } from "./metro-Di8EN2Tb.mjs";
14
- import { a as teardownOwnedBrowser, c as teardownParkedIosSim, i as teardownOwnedAvd, n as eraseParkedIosSim, o as teardownOwnedIosSim, s as teardownParkedAvd, t as eraseParkedAvd } from "./teardown-BYuQafiA.mjs";
15
- import { _ as parseRuntimeVersion, f as listIosDeviceTypes, u as listAllIosSims } from "./ios-3CtCRhwr.mjs";
16
- import { a as parseWatchmanPid, c as collectOwnedDeviceActivity, f as shutDownIdleDevices, i as parseWatchmanClients, m as recordGcResult, n as parseTriggerCount, p as workspaceBuildInProgress, r as parseWatchRoots, s as collectIdleDevices, t as parseSubscriberNames, u as idleShutdownCandidates } from "./watchman-Bx0wceoF.mjs";
17
- import { i as canonicalPath, n as withIdleWorkspace, r as workspaceInUse, t as emptyWorkspaceDir } from "./in-use-CndPMilN.mjs";
18
- import { n as listBuildSlots } from "./build-slots-Df1ApSuH.mjs";
19
- import { l as withdrawDevServerStopRequest, s as requestDevServerStop, u as resolveSupervisorTarget } from "./stop-cause-CZ_Mr-v9.mjs";
20
- import { a as capacity, l as environmentState, u as formatSpace } from "./status-l84Gbvp2.mjs";
21
- import { r as readFootprints, t as STIM_SERVER_COMMAND } from "./machine-usage-D_YYMzfR.mjs";
22
+ import { l as withdrawDevServerStopRequest, s as requestDevServerStop } from "./stop-cause-rhmGfwvt.mjs";
22
23
  import { existsSync, readFileSync, statSync, statfsSync } from "node:fs";
23
- import { dirname, isAbsolute, join, resolve, sep } from "node:path";
24
- import { existsSync as existsSync$1, lstatSync as lstatSync$1, readdirSync as readdirSync$1, realpathSync as realpathSync$1, rmSync as rmSync$1, statSync as statSync$1 } from "fs";
24
+ import { basename, dirname, isAbsolute, join, resolve, sep } from "node:path";
25
+ import { existsSync as existsSync$1, readdirSync as readdirSync$1, realpathSync as realpathSync$1, rmSync as rmSync$1, statSync as statSync$1 } from "fs";
25
26
  import { homedir, tmpdir } from "os";
26
27
  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";
27
- import { METRO_NAMED_CACHE_LAYOUT } from "@stim-cli/core";
28
- import { buildWorkerRoot, isJsonObject, loadConfig, readJsonFile, settingDefinition, settingValueError } from "@stim-cli/core/state";
28
+ import { METRO_NAMED_CACHE_LAYOUT, compiledHelper, configDir } from "@stim-cli/core";
29
+ import { MACHINE_OWNER_KINDS, buildWorkerRoot, loadConfig, settingDefinition, settingValueError } from "@stim-cli/core/state";
30
+ import { fileURLToPath } from "node:url";
29
31
  import { homedir as homedir$1, platform, totalmem as totalmem$1 } from "node:os";
30
32
  import chalk from "chalk";
31
- //#region src/supervisor/cache-reset.ts
32
- async function stopOwnedMetro(root, requester) {
33
- const project = getProject(root);
34
- const port = project?.metroPort;
35
- const target = resolveSupervisorTarget({
36
- state: readWorkspaceState$1(root)?.supervisor,
37
- record: project?.supervisor,
38
- reservedPort: port
39
- });
40
- if (target.status === "unverified") return {
41
- status: "unverified",
42
- reason: target.reason ?? "unknown identity"
43
- };
44
- if (port && target.status !== "ours" && !(await resolveProjectMetro(port, root)).missing) return {
45
- status: "external",
46
- reason: `a dev server Stim did not start answers on port ${port}`
47
- };
48
- if (target.status !== "ours") return { status: "not-running" };
49
- const expected = {
50
- pid: target.pid,
51
- processToken: target.processToken
52
- };
53
- requestDevServerStop(root, target.processToken, requester);
54
- const signalled = killMetroTree(target.pid, target.processToken);
55
- if (!signalled) withdrawDevServerStopRequest(root, target.processToken);
56
- if (!signalled || !await (0, process_identity_exports.waitForProcessExit)(expected, 1e4)) return {
57
- status: "stuck",
58
- reason: `supervisor pid ${target.pid} did not exit`
59
- };
60
- if (port && !(await resolveProjectMetro(port, root)).missing) return {
61
- status: "port-occupied",
62
- reason: `port ${port} is still occupied after stopping Metro`
63
- };
64
- clearWorkspaceSupervisor(root, expected);
65
- clearSupervisor(root, expected);
66
- return { status: "stopped" };
67
- }
68
- async function stopOwnedMetroForReset(root) {
69
- const stopped = await stopOwnedMetro(root, { by: "stim start --reset-cache" });
70
- switch (stopped.status) {
71
- case "unverified": throw supervisorError("STIM_SUPERVISOR_EXITED", `Cannot reset Metro: ${stopped.reason}.`, "Stop it with the tool that started it, then retry. Stim leaves unverified processes alone.");
72
- case "external": throw supervisorError("STIM_SUPERVISOR_EXITED", "Cannot reset an externally started dev server.", "Stop it with the tool that started it, then retry `stim start --reset-cache`.");
73
- case "stuck": throw supervisorError("STIM_SUPERVISOR_EXITED", "The owned Metro supervisor could not be stopped for a cache reset.", "Inspect `stim status` and `stim logs`, then retry. No cache reset was recorded.");
74
- case "port-occupied": throw supervisorError("STIM_SUPERVISOR_EXITED", `Port ${getProject(root)?.metroPort} is still occupied after stopping Metro.`, "Inspect the listener before retrying. Stim does not terminate unrecorded processes.");
75
- }
76
- }
77
- //#endregion
78
33
  //#region src/cache/caches.ts
79
34
  function xcodeDerivedDataRoot() {
80
35
  return join$1(homedir(), "Library", "Developer", "Xcode", "DerivedData");
@@ -309,6 +264,283 @@ function entriesAtDepth(dir, depth) {
309
264
  return level;
310
265
  }
311
266
  //#endregion
267
+ //#region src/footprint.ts
268
+ const SOURCE = "stim-footprint.swift";
269
+ const COMPILE_TIMEOUT_MS = 12e4;
270
+ const READ_TIMEOUT_MS = 5e3;
271
+ function helperSource() {
272
+ const here = fileURLToPath(import.meta.url);
273
+ if (basename(dirname(here)) === "src") return fileURLToPath(new URL(`../helper/${SOURCE}`, import.meta.url));
274
+ return join(dirname(here), SOURCE);
275
+ }
276
+ async function buildHelper() {
277
+ const exec = getExecutor();
278
+ if (!exec.runFileQuiet("xcode-select", ["-p"], { timeoutMs: READ_TIMEOUT_MS })) return null;
279
+ const source = helperSource();
280
+ try {
281
+ return await compiledHelper({
282
+ dir: join(configDir(), "helpers"),
283
+ name: "stim-footprint",
284
+ inputs: [source],
285
+ version: "",
286
+ compile: async (output) => {
287
+ await exec.runFileAsync("xcrun", [
288
+ "swiftc",
289
+ "-O",
290
+ "-swift-version",
291
+ "5",
292
+ "-o",
293
+ output,
294
+ source
295
+ ], { timeoutMs: COMPILE_TIMEOUT_MS });
296
+ }
297
+ });
298
+ } catch {
299
+ return null;
300
+ }
301
+ }
302
+ const RETRY_BACKOFF_INITIAL_MS = 1e4;
303
+ const RETRY_BACKOFF_MAX_MS = 6e5;
304
+ let built = null;
305
+ let failure = null;
306
+ /** The `stim-footprint` helper, compiled on first use. A success is kept for the process's life; a failure retries after a doubling backoff instead of being pinned forever. */
307
+ function footprintHelper() {
308
+ const home = configDir();
309
+ if (built?.home === home) return built.promise;
310
+ if (failure?.home === home && Date.now() < failure.nextRetryAt) return Promise.resolve(null);
311
+ const promise = buildHelper().then((helper) => {
312
+ if (helper === null) {
313
+ const backoffMs = failure?.home === home ? Math.min(failure.backoffMs * 2, RETRY_BACKOFF_MAX_MS) : RETRY_BACKOFF_INITIAL_MS;
314
+ failure = {
315
+ home,
316
+ nextRetryAt: Date.now() + backoffMs,
317
+ backoffMs
318
+ };
319
+ if (built?.promise === promise) built = null;
320
+ return null;
321
+ }
322
+ return helper;
323
+ });
324
+ built = {
325
+ home,
326
+ promise
327
+ };
328
+ return promise;
329
+ }
330
+ /** Parses the helper's `<pid> <bytes>` lines into footprint bytes by pid. */
331
+ function parseFootprints(output) {
332
+ const footprints = /* @__PURE__ */ new Map();
333
+ for (const line of output.split("\n")) {
334
+ const match = /^(\d+) (\d+)$/.exec(line.trim());
335
+ if (match) footprints.set(Number(match[1]), Number(match[2]));
336
+ }
337
+ return footprints;
338
+ }
339
+ /** Every readable process's physical footprint in bytes, or null when the helper is unavailable or fails. */
340
+ async function readFootprints() {
341
+ const helper = await footprintHelper();
342
+ const output = helper ? getExecutor().runFileQuiet(helper, [], { timeoutMs: READ_TIMEOUT_MS }) : null;
343
+ return output === null ? null : parseFootprints(output);
344
+ }
345
+ //#endregion
346
+ //#region src/machine-usage.ts
347
+ const SHARED = [
348
+ {
349
+ name: "CoreSimulator services",
350
+ pattern: /\/CoreSimulator\.framework\/|\/Simulator\.app\/Contents\/MacOS\//
351
+ },
352
+ {
353
+ name: "adb server",
354
+ pattern: /(^|\/)adb\s.*\bfork-server\b/
355
+ },
356
+ {
357
+ name: "Android emulator services",
358
+ pattern: /\/emulator\/netsimd(\s|$)/
359
+ },
360
+ {
361
+ name: "Gradle daemon",
362
+ pattern: /\borg\.gradle\.launcher\.daemon\.bootstrap\.GradleDaemon\b/
363
+ },
364
+ {
365
+ name: "Kotlin daemon",
366
+ pattern: /\bKotlinCompileDaemon\b/
367
+ },
368
+ {
369
+ name: "Watchman",
370
+ pattern: /(^|\/)watchman(\s|$)/
371
+ }
372
+ ];
373
+ const STIM_SERVER_COMMAND = /(^|[\s/])stim-server(\.mjs)?(\s|$)/;
374
+ const LAUNCHD_SIM = /^launchd_sim\s.*\/Devices\/([^/\s]+)\//;
375
+ const MAX_DEPTH = 64;
376
+ const EMULATOR = /(?:^|\/)(?:qemu-system-[^\s/]+|emulator)(?:\s.*)?\s(?:-avd\s+|@)(\S+)/;
377
+ function emulatorAvd(command) {
378
+ return EMULATOR.exec(command)?.[1] ?? null;
379
+ }
380
+ function workspaceOwner(kind, env, name, id) {
381
+ return {
382
+ kind,
383
+ name,
384
+ workspace: env.path,
385
+ id,
386
+ owned: true
387
+ };
388
+ }
389
+ function rank(owner) {
390
+ return MACHINE_OWNER_KINDS.indexOf(owner.kind);
391
+ }
392
+ /**
393
+ * Attributes every process in `processes` to at most one owner: the owner of its nearest ancestor (itself included)
394
+ * that is a root. Roots are each booted simulator's `launchd_sim`, each emulator launcher and qemu process, and each
395
+ * workspace's verified supervisor and Metro, running build and Chrome. Only a process under none of those falls back
396
+ * to stim-server or a machine-wide service in `SHARED`, so a `simctl` a workspace's log collector runs stays with the
397
+ * workspace. An emulator counts in the workspace that records its AVD whenever its process runs, including while adb
398
+ * does not list it. A process with no root ancestor is left out. `footprints` holds physical footprint bytes by pid,
399
+ * or is null when they could not be read, and the owners' `memoryMb` then sums resident sizes. A process missing from
400
+ * `footprints`, another user's or one that exited between the two reads, counts its resident size.
401
+ */
402
+ function attributeMachineUsage({ processes, footprints, environments, roots, simNames }) {
403
+ const owners = /* @__PURE__ */ new Map();
404
+ const rootOf = /* @__PURE__ */ new Map();
405
+ const fallbackOf = /* @__PURE__ */ new Map();
406
+ const claim = (pid, key, owner, into = rootOf) => {
407
+ if (!owners.has(key)) owners.set(key, owner);
408
+ into.set(pid, key);
409
+ };
410
+ const sims = /* @__PURE__ */ new Map();
411
+ const emulators = /* @__PURE__ */ new Map();
412
+ for (const env of environments) for (const device of [{
413
+ slot: "default",
414
+ ios: env.ios,
415
+ android: env.android
416
+ }, ...env.slots ?? []]) {
417
+ const slot = device.slot === "default" ? {} : { slot: device.slot };
418
+ if (device.ios?.state === "Booted") sims.set(device.ios.udid.toUpperCase(), {
419
+ kind: "simulator",
420
+ name: device.ios.name ?? device.ios.udid,
421
+ workspace: env.path,
422
+ ...slot,
423
+ id: device.ios.udid,
424
+ owned: device.ios.owned
425
+ });
426
+ if (device.android?.name && !device.android.physical) emulators.set(device.android.name, {
427
+ kind: "emulator",
428
+ name: device.android.name,
429
+ workspace: env.path,
430
+ ...slot,
431
+ id: device.android.name,
432
+ owned: device.android.owned
433
+ });
434
+ }
435
+ for (const p of processes) {
436
+ const shared = SHARED.find(({ pattern }) => pattern.test(p.command));
437
+ if (shared) {
438
+ const owner = {
439
+ kind: "shared",
440
+ name: shared.name,
441
+ workspace: null,
442
+ id: null,
443
+ owned: false
444
+ };
445
+ claim(p.pid, `shared:${shared.name}`, owner, fallbackOf);
446
+ continue;
447
+ }
448
+ if (STIM_SERVER_COMMAND.test(p.command)) {
449
+ claim(p.pid, "server", {
450
+ kind: "server",
451
+ name: "stim-server",
452
+ workspace: null,
453
+ id: null,
454
+ owned: false
455
+ }, fallbackOf);
456
+ continue;
457
+ }
458
+ const udid = LAUNCHD_SIM.exec(p.command)?.[1]?.toUpperCase();
459
+ if (udid) {
460
+ const owner = sims.get(udid) ?? {
461
+ kind: "simulator",
462
+ name: simNames[udid] ?? udid,
463
+ workspace: null,
464
+ id: udid,
465
+ owned: false
466
+ };
467
+ claim(p.pid, `simulator:${udid}`, owner);
468
+ continue;
469
+ }
470
+ const avd = emulatorAvd(p.command);
471
+ if (avd) {
472
+ const owner = emulators.get(avd) ?? {
473
+ kind: "emulator",
474
+ name: avd,
475
+ workspace: null,
476
+ id: avd,
477
+ owned: false
478
+ };
479
+ claim(p.pid, `emulator:${avd}`, owner);
480
+ }
481
+ }
482
+ const present = new Set(processes.map((p) => p.pid));
483
+ const byPath = new Map(environments.map((env) => [env.path, env]));
484
+ for (const root of roots) {
485
+ const env = byPath.get(root.path);
486
+ if (!env) continue;
487
+ const metroPids = [root.supervisorPid, env.metro?.running ? env.metro.pid : null];
488
+ for (const pid of metroPids) if (pid && present.has(pid)) {
489
+ const port = env.metro?.port ?? null;
490
+ claim(pid, `metro:${env.path}`, workspaceOwner("metro", env, port ? `Metro :${port}` : "Metro", port ? String(port) : null));
491
+ }
492
+ if (root.build && present.has(root.build.pid)) {
493
+ const label = root.build.platform === "ios" ? "iOS build" : "Android build";
494
+ claim(root.build.pid, `build:${env.path}`, workspaceOwner("build", env, label, root.build.platform));
495
+ }
496
+ for (const pid of root.macosPids ?? []) if (present.has(pid)) claim(pid, `macos:${env.path}`, workspaceOwner("macos", env, env.macos?.product ?? "macOS app", null));
497
+ for (const pid of root.browserPids) if (present.has(pid)) claim(pid, `browser:${env.path}`, workspaceOwner("browser", env, "Chrome", null));
498
+ }
499
+ const parentOf = new Map(processes.map((p) => [p.pid, p.ppid]));
500
+ const totals = /* @__PURE__ */ new Map();
501
+ for (const p of processes) {
502
+ let pid = p.pid;
503
+ let key;
504
+ let fallback;
505
+ for (let depth = 0; pid !== void 0 && pid > 1 && depth < MAX_DEPTH; depth++) {
506
+ key = rootOf.get(pid);
507
+ if (key) break;
508
+ fallback ??= fallbackOf.get(pid);
509
+ pid = parentOf.get(pid);
510
+ }
511
+ key ??= fallback;
512
+ if (!key) continue;
513
+ const total = totals.get(key) ?? {
514
+ rssKb: 0,
515
+ memoryKb: 0,
516
+ cpu: 0,
517
+ processes: 0
518
+ };
519
+ total.rssKb += p.rssKb;
520
+ const footprint = footprints?.get(p.pid);
521
+ total.memoryKb += footprint === void 0 ? p.rssKb : footprint / 1024;
522
+ total.cpu += p.cpuPercent;
523
+ total.processes += 1;
524
+ totals.set(key, total);
525
+ }
526
+ const list = [];
527
+ for (const [key, owner] of owners) {
528
+ const total = totals.get(key);
529
+ if (total) list.push({
530
+ ...owner,
531
+ cpuPercent: Math.round(total.cpu),
532
+ residentMb: Math.round(total.rssKb / 1024),
533
+ memoryMb: Math.round(total.memoryKb / 1024),
534
+ processes: total.processes
535
+ });
536
+ }
537
+ list.sort((x, y) => rank(x) - rank(y) || (x.workspace ?? "~").localeCompare(y.workspace ?? "~") || x.name.localeCompare(y.name));
538
+ return {
539
+ memorySource: footprints ? "footprint" : "rss",
540
+ owners: list
541
+ };
542
+ }
543
+ //#endregion
312
544
  //#region src/commands/gc/progress.ts
313
545
  function phase(label, text) {
314
546
  console.error(chalk.dim(phaseLine(label, text)));
@@ -1187,7 +1419,7 @@ async function inspectWatchmanMemory() {
1187
1419
  }
1188
1420
  //#endregion
1189
1421
  //#region src/commands/gc/caches.ts
1190
- const DAY_MS$2 = 864e5;
1422
+ const DAY_MS$1 = 864e5;
1191
1423
  function cacheSweepIsScoped() {
1192
1424
  return Boolean(process.env.STIM_HOME);
1193
1425
  }
@@ -1207,6 +1439,14 @@ const EVERY_CACHE = "all";
1207
1439
  const WORKSPACE_OUTPUTS = "workspaces";
1208
1440
  const RECORDINGS = "recordings";
1209
1441
  const PARKED_DEVICES = "parked";
1442
+ function includesArchives(name) {
1443
+ const wanted = name?.trim().toLowerCase();
1444
+ return Boolean(wanted && (wanted === "archived" || wanted.startsWith("archived:") || [
1445
+ "archived-logs",
1446
+ "archived-recordings",
1447
+ "archived-agent"
1448
+ ].includes(wanted)));
1449
+ }
1210
1450
  function includesParkedDevices(name) {
1211
1451
  return name?.trim().toLowerCase() === PARKED_DEVICES;
1212
1452
  }
@@ -1223,13 +1463,13 @@ function includesWorkspaceOutputs(name) {
1223
1463
  }
1224
1464
  /** Whether a --cache name selects something besides shared caches: recordings, parked devices or a memory kind. */
1225
1465
  function selectsBeyondCaches(name) {
1226
- return includesRecordings(name) || includesParkedDevices(name) || memoryCacheKind(name) !== null;
1466
+ return includesArchives(name) || includesRecordings(name) || includesParkedDevices(name) || memoryCacheKind(name) !== null;
1227
1467
  }
1228
1468
  function selectCaches(caches, name) {
1229
1469
  if (!name) return caches;
1230
1470
  const wanted = name.trim().toLowerCase();
1231
1471
  if (wanted === EVERY_CACHE) return caches;
1232
- if (wanted === WORKSPACE_OUTPUTS || wanted === RECORDINGS || wanted === PARKED_DEVICES || memoryCacheKind(wanted)) return [];
1472
+ if (includesArchives(wanted) || wanted === WORKSPACE_OUTPUTS || wanted === RECORDINGS || wanted === PARKED_DEVICES || memoryCacheKind(wanted)) return [];
1233
1473
  return caches.filter((c) => c.name.toLowerCase().includes(wanted) || c.dir.toLowerCase().includes(wanted));
1234
1474
  }
1235
1475
  function emptyVerdict(c) {
@@ -1276,7 +1516,7 @@ function emptyCache(cache) {
1276
1516
  };
1277
1517
  if (cache.prune !== "atomic") return pruneCache(cache, {
1278
1518
  olderThanDays: 0,
1279
- now: Date.now() + DAY_MS$2
1519
+ now: Date.now() + DAY_MS$1
1280
1520
  });
1281
1521
  let names;
1282
1522
  try {
@@ -1383,7 +1623,7 @@ function emptyCaches(caches) {
1383
1623
  //#region src/commands/gc/devices.ts
1384
1624
  const DEVICE_LIST_TIMEOUT_MS$1 = 3e4;
1385
1625
  const AVD_SIZE_TIMEOUT_MS = 5e3;
1386
- const DAY_MS$1 = 864e5;
1626
+ const DAY_MS = 864e5;
1387
1627
  function describeKept(ref) {
1388
1628
  return ref.mounted ? `referenced by ${ref.path}` : `referenced by ${ref.path} (volume not mounted; kept just in case)`;
1389
1629
  }
@@ -1475,7 +1715,7 @@ function unverifiedDeviceCommand(device) {
1475
1715
  }
1476
1716
  function findStaleProjectDevices({ config, sims = [], avds = [], olderThanDays, now = Date.now(), lastTouched, deadProjects = [] }) {
1477
1717
  if (!Number.isFinite(olderThanDays) || typeof lastTouched !== "function") return [];
1478
- const cutoff = now - olderThanDays * DAY_MS$1;
1718
+ const cutoff = now - olderThanDays * DAY_MS;
1479
1719
  const dead = new Set(deadProjects);
1480
1720
  const liveSims = new Map(sims.filter((s) => s?.name?.startsWith("stim-")).map((s) => [s.udid, s]));
1481
1721
  const liveAvds = new Set(avds.filter((a) => typeof a === "string" && a.startsWith("stim-")));
@@ -1484,7 +1724,7 @@ function findStaleProjectDevices({ config, sims = [], avds = [], olderThanDays,
1484
1724
  if (dead.has(path) || !isAbsolute$1(path)) continue;
1485
1725
  const touched = lastTouched(path);
1486
1726
  if (!Number.isFinite(touched) || touched >= cutoff) continue;
1487
- const idleDays = Math.floor((now - touched) / DAY_MS$1);
1727
+ const idleDays = Math.floor((now - touched) / DAY_MS);
1488
1728
  for (const { slot, platforms } of projectDeviceSlots(proj)) {
1489
1729
  const ios = platforms.ios;
1490
1730
  const sim = ios?.owned && ios.deviceUdid ? liveSims.get(ios.deviceUdid) : void 0;
@@ -1577,7 +1817,7 @@ function describeParkedSims(records, sims, deviceTypes, { simsChecked = true } =
1577
1817
  }
1578
1818
  function parkedAtLeast(records, { olderThanDays = null, now = Date.now() }) {
1579
1819
  if (olderThanDays === null) return [...records];
1580
- const cutoff = now - olderThanDays * DAY_MS$1;
1820
+ const cutoff = now - olderThanDays * DAY_MS;
1581
1821
  return records.filter((record) => Date.parse(record.parkedAt) <= cutoff);
1582
1822
  }
1583
1823
  function collectParkedSims(deps, age = {}) {
@@ -1851,301 +2091,51 @@ function deleteProjectDevices(orphanedDevices, staleDevices, staleDeviceRecords)
1851
2091
  return deleteFailures;
1852
2092
  }
1853
2093
  //#endregion
1854
- //#region src/commands/gc/workspaces.ts
1855
- function workspacesRoot() {
1856
- return join$1(getConfigDir(), "workspaces");
1857
- }
1858
- function isInsideWorkspaces(dir) {
1859
- const rel = relative$1(canonicalPath(workspacesRoot()), canonicalPath(dir));
1860
- return rel !== "" && !rel.startsWith("..") && !isAbsolute$1(rel);
1861
- }
1862
- function readRecordedRoot(dir, name) {
1863
- let parsed;
1864
- try {
1865
- parsed = readJsonFile(join$1(dir, "workspace.json"));
1866
- } catch (error) {
1867
- return {
1868
- projectRoot: null,
1869
- problem: error?.code === "ENOENT" ? "it has no workspace.json" : "its workspace.json does not parse"
1870
- };
1871
- }
1872
- const projectRoot = isJsonObject(parsed) ? parsed.projectRoot : void 0;
1873
- if (typeof projectRoot !== "string" || !isAbsolute$1(projectRoot)) return {
1874
- projectRoot: null,
1875
- problem: "its workspace.json records no absolute project root"
1876
- };
1877
- if (workspaceName$1(projectRoot) !== name) return {
1878
- projectRoot: null,
1879
- problem: `its workspace.json records ${projectRoot}, whose workspace directory has another name`
1880
- };
1881
- return {
1882
- projectRoot,
1883
- problem: null
2094
+ //#region src/supervisor/cache-reset.ts
2095
+ async function stopOwnedMetro(root, requester) {
2096
+ const project = getProject(root);
2097
+ const port = project?.metroPort;
2098
+ const target = resolveSupervisorTarget({
2099
+ state: readWorkspaceState$1(root)?.supervisor,
2100
+ record: project?.supervisor,
2101
+ reservedPort: port
2102
+ });
2103
+ if (target.status === "unverified") return {
2104
+ status: "unverified",
2105
+ reason: target.reason ?? "unknown identity"
1884
2106
  };
1885
- }
1886
- function listWorkspaceDirs() {
1887
- const root = workspacesRoot();
1888
- let names;
1889
- try {
1890
- names = readdirSync$1(root).toSorted();
1891
- } catch {
1892
- return [];
1893
- }
1894
- const entries = [];
1895
- for (const name of names) {
1896
- const dir = join$1(root, name);
1897
- try {
1898
- if (!statSync$1(dir).isDirectory()) continue;
1899
- } catch {
1900
- continue;
1901
- }
1902
- entries.push({
1903
- dir,
1904
- ...readRecordedRoot(dir, name)
1905
- });
1906
- }
1907
- return entries;
1908
- }
1909
- function classifyWorkspaceDirs(entries, { registryKeys, exists, isMounted, inUse }) {
1910
- const orphaned = [];
1911
- const skipped = [];
1912
- for (const { dir, projectRoot, problem } of entries) {
1913
- if (projectRoot === null) {
1914
- skipped.push({
1915
- dir,
1916
- reason: `workspace directory not resolved: ${problem ?? "unknown project root"}`
1917
- });
1918
- continue;
1919
- }
1920
- const present = exists(projectRoot);
1921
- if (present === null) {
1922
- skipped.push({
1923
- dir,
1924
- reason: `cannot tell whether its project root ${projectRoot} still exists`
1925
- });
1926
- continue;
1927
- }
1928
- if (present) continue;
1929
- if (registryKeys.some((key) => isPathPrefix(projectRoot, key))) continue;
1930
- if (!isMounted(projectRoot)) {
1931
- skipped.push({
1932
- dir,
1933
- reason: `the volume of its project root ${projectRoot} is not mounted`
1934
- });
1935
- continue;
1936
- }
1937
- const reasons = inUse(projectRoot);
1938
- if (reasons.length) {
1939
- skipped.push({
1940
- dir,
1941
- reason: `its project root ${projectRoot} is gone, but ${reasons.join("; ")}`
1942
- });
1943
- continue;
1944
- }
1945
- orphaned.push({
1946
- dir,
1947
- projectRoot
1948
- });
1949
- }
1950
- return {
1951
- orphaned,
1952
- skipped
2107
+ if (port && target.status !== "ours" && !(await resolveProjectMetro(port, root)).missing) return {
2108
+ status: "external",
2109
+ reason: `a dev server Stim did not start answers on port ${port}`
1953
2110
  };
1954
- }
1955
- function rootPresence(path) {
1956
- try {
1957
- lstatSync$1(path);
1958
- return true;
1959
- } catch (error) {
1960
- const code = error?.code;
1961
- return code === "ENOENT" || code === "ENOTDIR" ? false : null;
1962
- }
1963
- }
1964
- function collectOrphanedWorkspaces(registryKeys, mountedVolumes, { measure = true } = {}) {
1965
- const classified = classifyWorkspaceDirs(listWorkspaceDirs(), {
1966
- registryKeys,
1967
- exists: rootPresence,
1968
- isMounted: (path) => isOnMountedVolume(path, mountedVolumes),
1969
- inUse: (root) => workspaceInUse(root)
1970
- });
1971
- return {
1972
- orphaned: measure ? classified.orphaned.map((entry) => Object.assign({}, entry, { bytes: sizeOf(entry.dir) })) : classified.orphaned,
1973
- skipped: classified.skipped
2111
+ if (target.status !== "ours") return { status: "not-running" };
2112
+ const expected = {
2113
+ pid: target.pid,
2114
+ processToken: target.processToken
1974
2115
  };
1975
- }
1976
- const REBUILD_COST = "The next build of an unchanged app installs from the shared build cache; after a native change the compilation cache speeds the rebuild, but on React Native 0.86 Swift does not use it (explicit modules are off), so that build recompiles Swift. The next stim macos in a cleared workspace is a full Swift build.";
1977
- const WORKSPACE_OUTPUT_DIRS = [
1978
- "derived-data",
1979
- "gradle-build",
1980
- "android-cas",
1981
- "cache-provider"
1982
- ];
1983
- const DAY_MS = 864e5;
1984
- function planWorkspaceOutputs(entries, { olderThan, now }) {
1985
- return entries.map(({ dir, projectRoot, problem, bytes, lastUsed, inUse }) => {
1986
- const idleDays = Number.isFinite(lastUsed) ? Math.max(0, Math.floor((now - lastUsed) / DAY_MS)) : null;
1987
- const [keptCode, keptReason] = projectRoot === null ? ["unresolved", `workspace directory not resolved: ${problem ?? "unknown project root"}`] : inUse.length ? ["in-use", `in use: ${inUse.join("; ")}`] : olderThan === null ? [null, null] : idleDays === null ? ["last-use-unknown", "its last use is unknown"] : idleDays < olderThan ? ["recently-used", `used ${idleDays}d ago, within --older-than ${olderThan}`] : [null, null];
1988
- return {
1989
- dir,
1990
- projectRoot,
1991
- bytes,
1992
- idleDays,
1993
- willClear: keptCode === null,
1994
- keptCode,
1995
- keptReason
1996
- };
1997
- });
1998
- }
1999
- const SIZE_TIMEOUT_MS = 6e4;
2000
- function sizeOf(dir) {
2001
- return measuredDirectorySize(dir, { timeoutMs: SIZE_TIMEOUT_MS });
2002
- }
2003
- function outputPaths(dir) {
2004
- const paths = WORKSPACE_OUTPUT_DIRS.map((name) => join$1(dir, name)).filter((path) => existsSync$1(path));
2005
- const macos = join$1(dir, "macos");
2006
- try {
2007
- if (!lstatSync$1(macos).isDirectory()) return paths;
2008
- for (const entry of readdirSync$1(macos, { withFileTypes: true })) if (entry.name === "build" || entry.name.endsWith(".app") || entry.name.startsWith("staging-") && entry.isDirectory()) paths.push(join$1(macos, entry.name));
2009
- } catch {}
2010
- return paths;
2011
- }
2012
- function outputBytes(paths) {
2013
- let total = 0;
2014
- for (const path of paths) {
2015
- const size = sizeOf(path);
2016
- if (size === null) return null;
2017
- total += size;
2018
- }
2019
- return total;
2020
- }
2021
- function collectWorkspaceOutputs({ olderThan, now, exclude = [], measure = true }) {
2022
- const mountedVolumes = listMountedVolumes();
2023
- const entries = listWorkspaceDirs().filter((entry) => !exclude.includes(entry.dir)).filter((entry) => entry.projectRoot === null || isOnMountedVolume(entry.projectRoot, mountedVolumes)).map((entry) => Object.assign({}, entry, { paths: outputPaths(entry.dir) })).filter((entry) => entry.paths.length > 0).map(({ paths, ...entry }) => Object.assign({}, entry, { bytes: measure ? outputBytes(paths) : null })).map((entry) => Object.assign({}, entry, {
2024
- lastUsed: entry.projectRoot === null ? NaN : workspaceLastUsed(entry.projectRoot),
2025
- inUse: entry.projectRoot === null ? [] : workspaceInUse(entry.projectRoot)
2026
- }));
2027
- return {
2028
- root: workspacesRoot(),
2029
- workspaces: planWorkspaceOutputs(entries, {
2030
- olderThan,
2031
- now
2032
- })
2116
+ requestDevServerStop(root, target.processToken, requester);
2117
+ const signalled = killMetroTree(target.pid, target.processToken);
2118
+ if (!signalled) withdrawDevServerStopRequest(root, target.processToken);
2119
+ if (!signalled || !await (0, process_identity_exports.waitForProcessExit)(expected, 1e4)) return {
2120
+ status: "stuck",
2121
+ reason: `supervisor pid ${target.pid} did not exit`
2033
2122
  };
2034
- }
2035
- async function clearWorkspaceOutputs(report, { olderThan, now = Date.now() }) {
2036
- let failures = 0;
2037
- let cleared = 0;
2038
- const clearedRoots = [];
2039
- for (const entry of report.workspaces) {
2040
- const root = entry.projectRoot;
2041
- if (!entry.willClear || root === null) {
2042
- if (entry.keptReason) {
2043
- console.log(chalk.dim(`Kept the build outputs of ${root ?? entry.dir}: ${entry.keptReason}`));
2044
- recordGcResult("workspaceOutputs", "kept", root ?? entry.dir, {
2045
- bytes: entry.bytes,
2046
- detail: entry.keptReason
2047
- });
2048
- }
2049
- continue;
2050
- }
2051
- let run;
2052
- try {
2053
- run = await withIdleWorkspace(root, () => {
2054
- const [again] = planWorkspaceOutputs([{
2055
- ...entry,
2056
- problem: null,
2057
- lastUsed: workspaceLastUsed(root),
2058
- inUse: []
2059
- }], {
2060
- olderThan,
2061
- now
2062
- });
2063
- if (again?.keptReason) return again.keptReason;
2064
- for (const path of outputPaths(entry.dir)) rmSync$1(path, {
2065
- recursive: true,
2066
- force: true
2067
- });
2068
- return null;
2069
- }, { purpose: "gc" });
2070
- } catch (error) {
2071
- failures++;
2072
- console.log(chalk.red(`Could not clear the build outputs of ${root}: ${error?.message || String(error)}`));
2073
- recordGcResult("workspaceOutputs", "failed", root, {
2074
- bytes: entry.bytes,
2075
- detail: error?.message || String(error)
2076
- });
2077
- continue;
2078
- }
2079
- const kept = run.ran ? run.value : `in use: ${run.reasons.join("; ")}`;
2080
- if (kept) {
2081
- console.log(chalk.yellow(`Kept the build outputs of ${root}: ${kept}`));
2082
- recordGcResult("workspaceOutputs", "kept", root, {
2083
- bytes: entry.bytes,
2084
- detail: kept
2085
- });
2086
- continue;
2087
- }
2088
- cleared += entry.bytes ?? 0;
2089
- clearedRoots.push(root);
2090
- console.log(chalk.green(`Cleared the build outputs of ${root} (${sizeText(entry.bytes)})`));
2091
- recordGcResult("workspaceOutputs", "done", root, { bytes: entry.bytes });
2092
- }
2093
- if (cleared) console.log(chalk.dim(`Cleared ${formatBytes(cleared)} of workspace build outputs. ${REBUILD_COST}`));
2094
- return {
2095
- failures,
2096
- cleared: clearedRoots
2123
+ if (port && !(await resolveProjectMetro(port, root)).missing) return {
2124
+ status: "port-occupied",
2125
+ reason: `port ${port} is still occupied after stopping Metro`
2097
2126
  };
2127
+ clearWorkspaceSupervisor(root, expected);
2128
+ clearSupervisor(root, expected);
2129
+ return { status: "stopped" };
2098
2130
  }
2099
- function stillOrphaned({ dir, projectRoot }) {
2100
- if (readRecordedRoot(dir, basename$1(dir)).projectRoot !== projectRoot || rootPresence(projectRoot) !== false || !isOnMountedVolume(projectRoot)) return false;
2101
- return !Object.keys(loadConfig$1()?.projects ?? {}).some((key) => isPathPrefix(projectRoot, key));
2102
- }
2103
- async function deleteOrphanedWorkspaces(orphaned) {
2104
- let failures = 0;
2105
- for (const entry of orphaned) {
2106
- if (!existsSync$1(entry.dir)) continue;
2107
- let run;
2108
- try {
2109
- run = await withIdleWorkspace(entry.projectRoot, async () => {
2110
- if (!stillOrphaned(entry)) return false;
2111
- const browser = await teardownOwnedBrowser(entry.projectRoot, { deleteProfile: true });
2112
- if (browser.status === "failed" || browser.status === "skipped") throw new Error(`its owned Chrome profile was kept: ${browser.reason}`);
2113
- emptyWorkspaceDir(entry.dir);
2114
- return true;
2115
- }, { purpose: "gc" });
2116
- } catch (error) {
2117
- failures++;
2118
- console.log(chalk.red(`Could not remove ${entry.dir}: ${error?.message || error}`));
2119
- recordGcResult("workspaceDirectory", "failed", entry.dir, {
2120
- bytes: entry.bytes ?? null,
2121
- detail: error?.message || String(error)
2122
- });
2123
- continue;
2124
- }
2125
- if (!run.ran) {
2126
- console.log(chalk.yellow(`Kept ${entry.dir}: ${run.reasons.join("; ")}`));
2127
- recordGcResult("workspaceDirectory", "kept", entry.dir, {
2128
- bytes: entry.bytes ?? null,
2129
- detail: run.reasons.join("; ")
2130
- });
2131
- continue;
2132
- }
2133
- if (!run.value) {
2134
- console.log(chalk.yellow(`Kept ${entry.dir}: it can no longer be confirmed orphaned.`));
2135
- recordGcResult("workspaceDirectory", "kept", entry.dir, {
2136
- bytes: entry.bytes ?? null,
2137
- detail: "it can no longer be confirmed orphaned"
2138
- });
2139
- continue;
2140
- }
2141
- const size = entry.bytes === void 0 ? "" : ` (${sizeText(entry.bytes)})`;
2142
- console.log(chalk.green(`Removed the orphaned workspace directory ${entry.dir}${size}`));
2143
- recordGcResult("workspaceDirectory", "done", entry.dir, { bytes: entry.bytes ?? null });
2131
+ async function stopOwnedMetroForReset(root) {
2132
+ const stopped = await stopOwnedMetro(root, { by: "stim start --reset-cache" });
2133
+ switch (stopped.status) {
2134
+ case "unverified": throw supervisorError("STIM_SUPERVISOR_EXITED", `Cannot reset Metro: ${stopped.reason}.`, "Stop it with the tool that started it, then retry. Stim leaves unverified processes alone.");
2135
+ case "external": throw supervisorError("STIM_SUPERVISOR_EXITED", "Cannot reset an externally started dev server.", "Stop it with the tool that started it, then retry `stim start --reset-cache`.");
2136
+ case "stuck": throw supervisorError("STIM_SUPERVISOR_EXITED", "The owned Metro supervisor could not be stopped for a cache reset.", "Inspect `stim status` and `stim logs`, then retry. No cache reset was recorded.");
2137
+ case "port-occupied": throw supervisorError("STIM_SUPERVISOR_EXITED", `Port ${getProject(root)?.metroPort} is still occupied after stopping Metro.`, "Inspect the listener before retrying. Stim does not terminate unrecorded processes.");
2144
2138
  }
2145
- return failures;
2146
- }
2147
- function sizeText(bytes) {
2148
- return bytes === null ? "size unknown" : formatBytes(bytes);
2149
2139
  }
2150
2140
  //#endregion
2151
2141
  //#region src/budget.ts
@@ -2178,7 +2168,7 @@ function machineNumber(key, config, env) {
2178
2168
  error: null
2179
2169
  };
2180
2170
  }
2181
- if (env.STIM_HOME && setting.scopedHomeValue !== void 0) return {
2171
+ if (env.STIM_HOME && typeof setting.scopedHomeValue === "number") return {
2182
2172
  value: setting.scopedHomeValue,
2183
2173
  error: null
2184
2174
  };
@@ -2624,6 +2614,22 @@ async function enforceBudget({ root, note, dryRun = false, budget: resolved }, o
2624
2614
  short
2625
2615
  };
2626
2616
  }
2617
+ async function peekBudget(root, overrides = {}) {
2618
+ const outcome = await enforceBudget({
2619
+ root,
2620
+ note: () => {},
2621
+ dryRun: true
2622
+ }, {
2623
+ ...overrides,
2624
+ step: async () => ({
2625
+ targets: [],
2626
+ failures: 0
2627
+ })
2628
+ });
2629
+ if (outcome.status !== "ok") return outcome.refusal.message;
2630
+ const shortfall = describeShortfall(outcome.budget, outcome.measure, outcome.short);
2631
+ return shortfall.length ? shortfall.join("; ") : null;
2632
+ }
2627
2633
  async function budgetGate(args, overrides = {}) {
2628
2634
  const outcome = await enforceBudget(args, overrides);
2629
2635
  return {
@@ -2707,4 +2713,4 @@ function budgetLine(report) {
2707
2713
  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"}`;
2708
2714
  }
2709
2715
  //#endregion
2710
- export { discoverCaches as $, withAndroidAvdSizes as A, trimCaches as B, describeUnverifiableDevices as C, findStaleDeviceRecords as D, findOrphanedDevices as E, isWorkspacesScope as F, memoryCacheKind as G, WATCHMAN_KIND as H, planCacheEmptying as I, memorySweepIsScoped as J, memoryLines as K, previewScopedEmptying as L, includesParkedDevices as M, includesRecordings as N, findStaleProjectDevices as O, includesWorkspaceOutputs as P, phase as Q, selectCaches as R, describeParkedSims as S, eraseParkedDevices as T, collectMemoryReport as U, GRADLE_DAEMONS_KIND as V, inspectWatchmanMemory as W, reclaimMemory as X, memoryWorkPending as Y, scopedMemoryReport as Z, collectParkedAvds as _, REBUILD_COST as a, deleteParkedSims as b, collectOrphanedWorkspaces as c, isInsideWorkspaces as d, sizeCaches as et, listWorkspaceDirs as f, collectDeviceLeases as g, DEVICE_LIST_TIMEOUT_MS$1 as h, machineNumber as i, emptyCaches as j, unverifiedDeviceCommand as k, collectWorkspaceOutputs as l, sizeText as m, budgetLine as n, WORKSPACE_OUTPUT_DIRS as o, rootPresence as p, memoryScope as q, inspectBudget as r, clearWorkspaceOutputs as s, budgetGate as t, stopOwnedMetroForReset as tt, deleteOrphanedWorkspaces as u, collectParkedSims as v, deviceSweepIsScoped as w, deleteProjectDevices as x, deleteParkedAvds as y, selectsBeyondCaches as z };
2716
+ export { includesWorkspaceOutputs as A, inspectWatchmanMemory as B, findStaleProjectDevices as C, includesArchives as D, emptyCaches as E, selectsBeyondCaches as F, memoryWorkPending as G, memoryLines as H, trimCaches as I, phase as J, reclaimMemory as K, GRADLE_DAEMONS_KIND as L, planCacheEmptying as M, previewScopedEmptying as N, includesParkedDevices as O, selectCaches as P, sizeCaches as Q, WATCHMAN_KIND as R, findStaleDeviceRecords as S, withAndroidAvdSizes as T, memoryScope as U, memoryCacheKind as V, memorySweepIsScoped as W, readFootprints as X, attributeMachineUsage as Y, discoverCaches as Z, describeParkedSims as _, machineNumber as a, eraseParkedDevices as b, resolveBudget as c, collectDeviceLeases as d, collectParkedAvds as f, deleteProjectDevices as g, deleteParkedSims as h, inspectBudget as i, isWorkspacesScope as j, includesRecordings as k, stopOwnedMetroForReset as l, deleteParkedAvds as m, budgetLine as n, peekBudget as o, collectParkedSims as p, scopedMemoryReport as q, enforceBudget as r, readVolumeSpace as s, budgetGate as t, DEVICE_LIST_TIMEOUT_MS$1 as u, describeUnverifiableDevices as v, unverifiedDeviceCommand as w, findOrphanedDevices as x, deviceSweepIsScoped as y, collectMemoryReport as z };