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,8 +1,8 @@
1
- import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
- import { n as windowsLauncherArgs } from "./detached-entry-CiTEgMR1.mjs";
3
- import { t as resolveMaintenanceSettings } from "./settings-BBpMcDbD.mjs";
4
- import { n as stampAttempt, r as due, t as capChildLog } from "./attempt-Bk2ouQVL.mjs";
5
- import { t as spawnEntry } from "./spawn-entry-jw19m8Nl.mjs";
1
+ import { t as getExecutor } from "./exec-i8GDPwq9.mjs";
2
+ import { t as spawnEntry } from "./spawn-entry-Dcb7sDUz.mjs";
3
+ import { n as windowsLauncherArgs } from "./detached-entry-pB_IPfV4.mjs";
4
+ import { t as resolveMaintenanceSettings } from "./settings-Djl-mpjX.mjs";
5
+ import { n as stampAttempt, r as due, t as capChildLog } from "./attempt-DW-mQqM7.mjs";
6
6
  import { closeSync, openSync } from "node:fs";
7
7
  import { maintenanceChildLogFile, maintenanceRunClaims, readMaintenanceAttempt, readMaintenanceState } from "@stim-cli/core/state";
8
8
  import { readClaimSet } from "@stim-cli/core/ownership-claim";
@@ -14,7 +14,7 @@ function triggerMaintenance(trigger, { argv = process.argv.slice(2), platform =
14
14
  "guide",
15
15
  "settings",
16
16
  "help"
17
- ].includes(argv[0] ?? "") || argv[0] === "gc" && argv.includes("--delete") || argv.some((arg) => [
17
+ ].includes(argv[0] ?? "") || argv.includes("--plan") || argv[0] === "gc" && argv.includes("--delete") || argv.some((arg) => [
18
18
  "--help",
19
19
  "-h",
20
20
  "--version",
@@ -0,0 +1,2 @@
1
+ import { t as triggerMaintenance } from "./trigger-CAhmG-4s.mjs";
2
+ export { triggerMaintenance };
@@ -1,14 +1,16 @@
1
- import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
- import { n as ownership_claim_exports } from "./process-identity-BmRm1qV0.mjs";
3
- import { o as pullRequestLookups } from "./pull-request-pxnDCQzn.mjs";
4
- import { at as gitCommonDirOnDisk } from "./settings-Cfd-0r2d.mjs";
5
- import { D as discoverAgentSessions, E as attributeAgentSessions, T as agentKey, a as updateWorkspaceState, u as ensureWorkspaceStorage, v as workspaceDir, w as withDirLock } from "./workspace-state-DqcCbpE7.mjs";
6
- import { I as ownedAvdDirectory } from "./android-B_YNZy61.mjs";
7
- import { randomUUID } from "node:crypto";
8
- import { existsSync, mkdirSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
9
- import { basename, dirname, join } from "node:path";
10
- import { ENDED_AGENT_RETENTION_MS, READY_PHASE_MS, WARM_KEY, agentSessionsCacheFile, diskUsageCacheFile, pullRequestCacheFile, readAgentSessionsCache, readDiskUsage, readEndedAgentSessions, readPullRequestCache, readWarmRecord, readWorkspaceAgent, readWorkspaceState, workspaceEndedAgentsFile, workspaceEndedAgentsLock } from "@stim-cli/core/state";
1
+ import { n as ownership_claim_exports } from "./process-identity-Biv3arCO.mjs";
2
+ import { n as debugLog, t as getExecutor } from "./exec-i8GDPwq9.mjs";
3
+ import { D as discoverAgentSessions, E as attributeAgentSessions, T as agentKey, a as updateWorkspaceState, u as ensureWorkspaceStorage, v as workspaceDir, w as withDirLock } from "./workspace-state-CR9X86xJ.mjs";
4
+ import { ct as linkedWorktreesOnDisk, rt as gitCommonDirOnDisk } from "./settings-CxgngUZk.mjs";
5
+ import { I as ownedAvdDirectory } from "./android-BdQC6zs1.mjs";
6
+ import { o as pullRequestLookups } from "./pull-request-IHNdMnG8.mjs";
7
+ import { existsSync, mkdirSync, readdirSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
8
+ import { basename, dirname, join, sep } from "node:path";
9
+ import { ENDED_AGENT_RETENTION_MS, READY_PHASE_MS, WARM_KEY, agentSessionsCacheFile, diskUsageCacheFile, diskUsageFailureFile, diskUsageWalkClaims, pullRequestCacheFile, readAgentSessionsCache, readDiskUsage, readDiskUsageFailure, readEndedAgentSessions, readPullRequestCache, readWarmRecord, readWorkspaceAgent, readWorkspaceState, workspaceEndedAgentsFile, workspaceEndedAgentsLock } from "@stim-cli/core/state";
11
10
  import { homedir } from "node:os";
11
+ import { clearClaimChild, markClaimChildPending, releaseClaim, setClaimChild, tryAcquireClaim } from "@stim-cli/core/ownership-claim";
12
+ import { captureProcessIdentity } from "@stim-cli/core/process-identity";
13
+ import { randomUUID } from "node:crypto";
12
14
  //#region src/workspace/merge-state.ts
13
15
  const FETCH_TIMEOUT_MS = 3e4;
14
16
  const FETCH_FRESH_MS = 6e5;
@@ -282,6 +284,7 @@ function mergeState(path, { ref, name }, { timeoutMs = GIT_TIMEOUT_MS$1, maxComm
282
284
  const LIVE_DISK_MAX_AGE_MS = 3e5;
283
285
  const IDLE_DISK_MAX_AGE_MS = 36e5;
284
286
  const DU_TIMEOUT_MS = 12e4;
287
+ const DU_NICENESS = "19";
285
288
  const PULL_REQUEST_MAX_AGE_MS = 3e5;
286
289
  const PULL_REQUEST_CHECK_MS = 6e4;
287
290
  const GIT_TIMEOUT_MS = 5e3;
@@ -301,8 +304,11 @@ function forgetStatusMeasures(worktree, workspaces) {
301
304
  const roots = [.../* @__PURE__ */ new Set([worktree, ...workspaces])];
302
305
  const files = [pullRequestCacheFile(worktree), ...roots.flatMap((root) => [
303
306
  diskUsageCacheFile(root),
307
+ diskUsageFailureFile(root),
304
308
  diskUsageCacheFile(join(root, "node_modules")),
305
- diskUsageCacheFile(workspaceDir(root))
309
+ diskUsageFailureFile(join(root, "node_modules")),
310
+ diskUsageCacheFile(workspaceDir(root)),
311
+ diskUsageFailureFile(workspaceDir(root))
306
312
  ])];
307
313
  for (const file of files) try {
308
314
  rmSync(file, { force: true });
@@ -459,18 +465,63 @@ function applyStatusMeasures(states, worktrees) {
459
465
  if (cached && cached.branch === worktree.branch) worktree.pullRequest = cached.pullRequest;
460
466
  }
461
467
  }
462
- /** Kilobytes from `du -sk` output, or null when it printed none. */
468
+ /** Kilobytes from `du -sk` output, summed over its lines, or null when it printed none. */
463
469
  function parseDuKilobytes(output) {
464
- const kb = Number(/^(\d+)\s/.exec(output)?.[1]);
465
- return Number.isFinite(kb) ? kb : null;
470
+ const sizes = [...output.matchAll(/^(\d+)\s/gm)].map((match) => Number(match[1]));
471
+ return sizes.length ? sizes.reduce((sum, kb) => sum + kb, 0) : null;
472
+ }
473
+ /** The folders inside a checkout that another measurement owns: its linked worktrees and Stim's workspace folder. */
474
+ function nestedFolders(state) {
475
+ const common = gitCommonDirOnDisk(state.path);
476
+ return [workspaceDir(state.path), ...common ? linkedWorktreesOnDisk(common).map((entry) => entry.path) : []];
477
+ }
478
+ /** The entries to hand to `du` so it sums `root` without descending into `nested`; `[root]` when none is inside it. */
479
+ function walkTargets(root, nested) {
480
+ const top = canonicalPath(root);
481
+ const skip = new Set(nested.map(canonicalPath));
482
+ const inside = [...skip].filter((path) => path.startsWith(top + sep));
483
+ if (!inside.length) return [root];
484
+ const targets = [];
485
+ const visit = (dir) => {
486
+ let names;
487
+ try {
488
+ names = readdirSync(dir);
489
+ } catch {
490
+ targets.push(dir);
491
+ return;
492
+ }
493
+ for (const name of names) {
494
+ const path = join(dir, name);
495
+ if (skip.has(path)) continue;
496
+ if (inside.some((nestedPath) => nestedPath.startsWith(path + sep))) visit(path);
497
+ else targets.push(path);
498
+ }
499
+ };
500
+ visit(top);
501
+ return targets;
466
502
  }
467
503
  function isStale(measure, maxAgeMs, now) {
468
504
  return !measure || now - Date.parse(measure.measuredAt) >= maxAgeMs;
469
505
  }
506
+ function takeWalkClaim() {
507
+ try {
508
+ const attempt = tryAcquireClaim({
509
+ root: diskUsageWalkClaims(),
510
+ mode: "exclusive",
511
+ label: "disk usage walk"
512
+ });
513
+ if (attempt.pending) releaseClaim(attempt.pending);
514
+ return attempt.acquired ?? null;
515
+ } catch (error) {
516
+ debugLog.log("disk-walk-claim", { error: String(error?.message ?? error) });
517
+ return null;
518
+ }
519
+ }
470
520
  /**
471
521
  * Keeps the disk use, pull request and agent session caches fresh for `status --watch`, off its refresh path: one
472
522
  * agent session discovery at most every 15 seconds, which status ignores once it is over 2 minutes old, one `du -sk` at
473
- * a time, live environments first, each folder at most every 5 minutes while its environment is live and every hour otherwise, and one `gh api
523
+ * a time on the whole machine (an ownership claim; a failed walk is recorded so other watchers do not repeat it), at low
524
+ * priority, live environments first, a checkout without the linked worktrees nested in it, each folder at most every 5 minutes while its environment is live and every hour otherwise, and one `gh api
474
525
  * graphql` per repository for worktrees whose lookup is over 5 minutes old or whose branch or HEAD moved. It
475
526
  * runs git only in the repository, never in a worktree, and only for worktrees whose git state status read. It rechecks
476
527
  * a folder's cache right before measuring, so two watchers rarely measure the same folder. A session the previous
@@ -485,35 +536,77 @@ function createStatusMeasurer({ updated, now = Date.now }) {
485
536
  let checkedAt = -Infinity;
486
537
  let discovering = false;
487
538
  let discoveredAt = -Infinity;
488
- const failedAt = /* @__PURE__ */ new Map();
489
- async function du(path) {
539
+ async function du(targets, claim) {
540
+ const hold = (child) => {
541
+ if (child.pid === void 0) return;
542
+ const captured = captureProcessIdentity(child.pid);
543
+ if (!captured.ok) throw new Error(`Cannot record the du process: ${captured.reason}`);
544
+ setClaimChild(claim, {
545
+ pid: child.pid,
546
+ processToken: captured.token
547
+ });
548
+ };
490
549
  try {
491
- return parseDuKilobytes(await exec.runFileAsync("du", ["-sk", path], { timeoutMs: DU_TIMEOUT_MS }));
550
+ markClaimChildPending(claim);
551
+ return parseDuKilobytes(await exec.runFileAsync("nice", [
552
+ "-n",
553
+ DU_NICENESS,
554
+ "du",
555
+ "-sk",
556
+ ...targets
557
+ ], {
558
+ timeoutMs: DU_TIMEOUT_MS,
559
+ onSpawn: hold
560
+ }));
492
561
  } catch (error) {
493
562
  const { code, status, stdout } = error;
494
563
  if (code === "ENOENT") duMissing = true;
495
564
  return status === 1 && typeof stdout === "string" ? parseDuKilobytes(stdout) : null;
565
+ } finally {
566
+ clearClaimChild(claim);
496
567
  }
497
568
  }
569
+ function due(path, maxAgeMs) {
570
+ const failedAt = readDiskUsageFailure(path);
571
+ return isStale(readDiskUsage(path), maxAgeMs, now()) && (!failedAt || now() - Date.parse(failedAt) >= maxAgeMs);
572
+ }
498
573
  async function measure(folders) {
499
- for (const { path, maxAgeMs } of folders) {
500
- if (duMissing || !isStale(readDiskUsage(path), maxAgeMs, now())) continue;
501
- if (now() - (failedAt.get(path) ?? -Infinity) < maxAgeMs) continue;
502
- let bytes = 0;
503
- if (existsSync(path)) {
504
- const kb = await du(path);
505
- if (kb === null) {
506
- failedAt.set(path, now());
507
- continue;
508
- }
509
- bytes = kb * 1024;
510
- } else if (!basename(path).startsWith("node_modules")) continue;
511
- writeCacheFile(diskUsageCacheFile(path), {
574
+ for (const { path, maxAgeMs, nested } of folders) {
575
+ if (duMissing || !due(path, maxAgeMs)) continue;
576
+ const stamp = () => ({
512
577
  path,
513
- bytes,
514
578
  measuredAt: new Date(now()).toISOString()
515
579
  });
516
- updated();
580
+ if (!existsSync(path)) {
581
+ if (!basename(path).startsWith("node_modules")) continue;
582
+ writeCacheFile(diskUsageCacheFile(path), {
583
+ ...stamp(),
584
+ bytes: 0
585
+ });
586
+ updated();
587
+ continue;
588
+ }
589
+ const claim = takeWalkClaim();
590
+ if (!claim) return;
591
+ try {
592
+ if (!due(path, maxAgeMs)) continue;
593
+ const targets = walkTargets(path, nested?.() ?? []);
594
+ const kb = targets.length ? await du(targets, claim) : 0;
595
+ if (kb === null) {
596
+ writeCacheFile(diskUsageFailureFile(path), {
597
+ path,
598
+ failedAt: new Date(now()).toISOString()
599
+ });
600
+ continue;
601
+ }
602
+ writeCacheFile(diskUsageCacheFile(path), {
603
+ ...stamp(),
604
+ bytes: kb * 1024
605
+ });
606
+ updated();
607
+ } finally {
608
+ releaseClaim(claim);
609
+ }
517
610
  }
518
611
  }
519
612
  async function branchHead(repository, branch) {
@@ -600,13 +693,17 @@ function createStatusMeasurer({ updated, now = Date.now }) {
600
693
  ...nodeModules,
601
694
  build,
602
695
  ...deviceFolders(state).map((folder) => folder.path)
603
- ]) folders.set(path, Math.min(folders.get(path) ?? Infinity, maxAgeMs));
696
+ ]) {
697
+ const known = folders.get(path);
698
+ folders.set(path, {
699
+ path,
700
+ maxAgeMs: Math.min(known?.maxAgeMs ?? Infinity, maxAgeMs),
701
+ nested: known?.nested ?? (path === worktree ? () => nestedFolders(state) : void 0)
702
+ });
703
+ }
604
704
  }
605
705
  measuring = true;
606
- measure([...folders].map(([path, maxAgeMs]) => ({
607
- path,
608
- maxAgeMs
609
- }))).catch(() => {}).finally(() => {
706
+ measure([...folders.values()]).catch(() => {}).finally(() => {
610
707
  measuring = false;
611
708
  });
612
709
  }
@@ -1,8 +1,9 @@
1
- import { H as projectDeviceSlots, R as deviceSlotKey } from "./config-D0DbpZtM.mjs";
2
- import { a as formatElapsed, o as formatLongDuration, r as clockTime, u as plural } from "./command-output-CDAIFD-8.mjs";
3
- import { n as isInsideProject } from "./metro-g8NqiJp-.mjs";
4
- import { g as workspaceAgentDeviceDir } from "./workspace-state-DqcCbpE7.mjs";
5
- import { r as cdpEndpoint } from "./state-PeQiJuIu.mjs";
1
+ import { t as getExecutor } from "./exec-i8GDPwq9.mjs";
2
+ import { a as formatElapsed, o as formatLongDuration, r as clockTime, u as plural } from "./command-output-Bt8ygHE7.mjs";
3
+ import { H as projectDeviceSlots, R as deviceSlotKey } from "./config-DC5jEtd1.mjs";
4
+ import { g as workspaceAgentDeviceDir } from "./workspace-state-CR9X86xJ.mjs";
5
+ import { n as isInsideProject } from "./metro-DI2AjPF_.mjs";
6
+ import { r as cdpEndpoint } from "./state-D2KtnALG.mjs";
6
7
  import { basename, sep } from "path";
7
8
  import { ACTIVITY_RECENCY_BASES } from "@stim-cli/core/state";
8
9
  //#region src/status.ts
@@ -153,7 +154,10 @@ function environmentState(project, { simsByUdid = {}, metro = null, worktrees =
153
154
  startedAt: supervisor.startedAt ?? null,
154
155
  healthy: Boolean(supervisor.healthy)
155
156
  } : null,
156
- agentDevice: { stateDir: workspaceAgentDeviceDir(project.__path) },
157
+ agentDevice: {
158
+ stateDir: workspaceAgentDeviceDir(project.__path),
159
+ installed: agentDeviceInstalled()
160
+ },
157
161
  logs: logs ? {
158
162
  dir: logs.dir,
159
163
  errorsSinceMarker: logs.errorsSinceMarker ?? 0
@@ -432,5 +436,69 @@ function deviceLeaseLines(states, now) {
432
436
  }
433
437
  return lines;
434
438
  }
439
+ const AGENT_DEVICE_CACHE_MS = 3e4;
440
+ let agentDeviceCache;
441
+ function agentDeviceInstalled() {
442
+ const now = Date.now();
443
+ const age = agentDeviceCache ? now - agentDeviceCache.at : -1;
444
+ if (agentDeviceCache && age >= 0 && age < AGENT_DEVICE_CACHE_MS) return agentDeviceCache.installed;
445
+ let installed = false;
446
+ try {
447
+ installed = Boolean(getExecutor().findExecutable("agent-device"));
448
+ } catch {
449
+ installed = false;
450
+ }
451
+ agentDeviceCache = {
452
+ at: now,
453
+ installed
454
+ };
455
+ return installed;
456
+ }
457
+ //#endregion
458
+ //#region src/watchman.ts
459
+ /** Runs one watchman command with `--no-spawn`, so Stim never starts a daemon, and parses its JSON answer. */
460
+ const runWatchman = async (args, timeoutMs) => JSON.parse(await getExecutor().runFileAsync("watchman", ["--no-spawn", ...args], { timeoutMs }));
461
+ function parseWatchRoots(payload) {
462
+ const roots = payload?.roots;
463
+ if (!Array.isArray(roots)) return null;
464
+ return roots.filter((root) => typeof root === "string");
465
+ }
466
+ function parseSubscriberNames(payload) {
467
+ if (!payload || typeof payload !== "object" || "error" in payload) return null;
468
+ const subscribers = payload.subscribers;
469
+ if (subscribers === void 0) return [];
470
+ if (!Array.isArray(subscribers)) return null;
471
+ const names = [];
472
+ for (const subscriber of subscribers) {
473
+ const name = subscriber?.info?.name;
474
+ if (typeof name !== "string") return null;
475
+ names.push(name);
476
+ }
477
+ return names;
478
+ }
479
+ function parseTriggerCount(payload) {
480
+ const triggers = payload?.triggers;
481
+ return Array.isArray(triggers) ? triggers.length : null;
482
+ }
483
+ function parseWatchmanPid(payload) {
484
+ const pid = payload?.pid;
485
+ return typeof pid === "number" && Number.isInteger(pid) && pid > 0 ? pid : null;
486
+ }
487
+ /** The connected clients in a `debug-status` answer, or null when the answer does not list them. */
488
+ function parseWatchmanClients(payload) {
489
+ if (!payload || typeof payload !== "object" || "error" in payload) return null;
490
+ const clients = payload.clients;
491
+ if (!Array.isArray(clients)) return null;
492
+ const parsed = [];
493
+ for (const client of clients) {
494
+ const peer = client?.peer;
495
+ if (typeof peer?.pid !== "number") return null;
496
+ parsed.push({
497
+ pid: peer.pid,
498
+ name: typeof peer.name === "string" ? peer.name : null
499
+ });
500
+ }
501
+ return parsed;
502
+ }
435
503
  //#endregion
436
- export { statusActivity as _, capacity as a, withWebFacts as b, diskLine as c, gitSummaryText as d, metroReverseTargets as f, remoteDeviceState as g, remoteDeviceLine as h, applyMachineMemory as i, environmentState as l, poolLine as m, activityLabel as n, deviceLeaseLines as o, parseDfFree as p, addReverseMissingIssue as r, deviceLeaseStates as s, RECENT_LAUNCH_MS as t, formatSpace as u, tightVolumes as v, unprovisionedWorktrees as y };
504
+ export { statusActivity as C, withWebFacts as E, remoteDeviceState as S, unprovisionedWorktrees as T, gitSummaryText as _, parseWatchmanPid as a, poolLine as b, activityLabel as c, capacity as d, deviceLeaseLines as f, formatSpace as g, environmentState as h, parseWatchmanClients as i, addReverseMissingIssue as l, diskLine as m, parseTriggerCount as n, runWatchman as o, deviceLeaseStates as p, parseWatchRoots as r, RECENT_LAUNCH_MS as s, parseSubscriberNames as t, applyMachineMemory as u, metroReverseTargets as v, tightVolumes as w, remoteDeviceLine as x, parseDfFree as y };
@@ -0,0 +1,2 @@
1
+ import { n as webCommand, t as runWeb } from "./web-DbbrPUn0.mjs";
2
+ export { webCommand as default, runWeb };
@@ -0,0 +1,127 @@
1
+ import { f as refuseNoProject, l as phaseLine } from "./command-output-Bt8ygHE7.mjs";
2
+ import { E as upsertProject, p as getProject } from "./config-DC5jEtd1.mjs";
3
+ import { i as recordWorkspaceUse, x as workspaceLogsDir } from "./workspace-state-CR9X86xJ.mjs";
4
+ import { d as projectRegistry, n as findCommandWorkspace } from "./project-B5eQ1USs.mjs";
5
+ import { B as settingShapeErrors, I as resolveSettings, i as SETTING_SHAPE_REMEDY, mt as repoRoot, nt as gitCommonDir } from "./settings-CxgngUZk.mjs";
6
+ import { o as readWebRecord, r as cdpEndpoint } from "./state-D2KtnALG.mjs";
7
+ import { a as workspaceLinks, i as workspaceLinkLine } from "./stim-desktop-C65jk_q2.mjs";
8
+ import { n as ensureWorkspaceStorageSafely } from "./native-runtime-BGxsbfny.mjs";
9
+ import { t as liveWebRecord } from "./page-Bv9s2KwY.mjs";
10
+ import chalk from "chalk";
11
+ //#region src/commands/web.ts
12
+ const printNote = (line) => console.error(line);
13
+ async function runWeb({ root, headed, note }) {
14
+ const startedAt = Date.now();
15
+ await ensureWorkspaceStorageSafely(root, { note });
16
+ if (!getProject(root)) upsertProject(root, {});
17
+ const settings = resolveSettings({
18
+ projectPath: root,
19
+ gitCommonDir: gitCommonDir(root),
20
+ repoRoot: repoRoot(root)
21
+ });
22
+ const [shapeError] = settingShapeErrors(settings);
23
+ if (shapeError) return {
24
+ ok: false,
25
+ error: {
26
+ code: "STIM_BAD_ARG",
27
+ message: shapeError,
28
+ remedy: SETTING_SHAPE_REMEDY
29
+ }
30
+ };
31
+ const selected = projectRegistry.selectWeb(root);
32
+ if ("problem" in selected) {
33
+ const { message, remedy } = selected.problem;
34
+ return {
35
+ ok: false,
36
+ error: {
37
+ code: "STIM_NO_PROJECT",
38
+ message,
39
+ remedy
40
+ }
41
+ };
42
+ }
43
+ const runtime = (await selected.load()).runtime({
44
+ settings,
45
+ headed,
46
+ note
47
+ });
48
+ const preparation = await runtime.prepare();
49
+ if (!preparation.ok) return {
50
+ ok: false,
51
+ error: {
52
+ ...preparation.error,
53
+ remedy: preparation.error.remedy ?? null
54
+ }
55
+ };
56
+ const { prepared } = preparation;
57
+ const { url } = prepared;
58
+ const launch = await runtime.launch(prepared, {
59
+ root,
60
+ note
61
+ });
62
+ if (!launch.ok) return launch;
63
+ const { verdict, remedy } = await runtime.verify(prepared, { since: launch.since });
64
+ const live = liveWebRecord(readWebRecord(root));
65
+ const record = live ?? launch.record;
66
+ return {
67
+ ok: true,
68
+ remedy,
69
+ facts: {
70
+ platform: "web",
71
+ browser: "chrome",
72
+ version: record.version ?? null,
73
+ running: live !== null,
74
+ pid: live?.chromeProcess?.pid ?? null,
75
+ supervisorPid: live?.pid ?? null,
76
+ url,
77
+ headless: record.headless,
78
+ viewport: record.viewport,
79
+ profile: record.profile,
80
+ cdpEndpoint: live ? cdpEndpoint(live.cdpPort) : null,
81
+ targetId: live?.targetId ?? null,
82
+ reused: launch.reused,
83
+ launched: verdict.launched,
84
+ metroPort: prepared.metroPort,
85
+ logs: { dir: workspaceLogsDir(root) },
86
+ durationMs: Date.now() - startedAt
87
+ }
88
+ };
89
+ }
90
+ function webCommand(program) {
91
+ program.command("web").description("Open this workspace's page in a Stim-owned headless Chrome and capture its console, errors and failed requests in stim logs").option("--headed", "Show the Chrome window instead of running headless").option("--json", "Print the result as one JSON object; progress goes to stderr").action(async (opts) => {
92
+ const json = Boolean(opts.json);
93
+ const root = findCommandWorkspace(process.cwd());
94
+ if (!root) {
95
+ refuseNoProject({ json });
96
+ return;
97
+ }
98
+ recordWorkspaceUse(root);
99
+ const result = await runWeb({
100
+ root,
101
+ headed: Boolean(opts.headed),
102
+ note: printNote
103
+ });
104
+ if (!result.ok) {
105
+ printNote(chalk.red(phaseLine("error", `${result.error.code}: ${result.error.message}`)));
106
+ if (result.error.remedy) printNote(phaseLine("remedy", result.error.remedy));
107
+ if (json) console.log(JSON.stringify(result.error));
108
+ process.exitCode = 1;
109
+ return;
110
+ }
111
+ const { facts, remedy } = result;
112
+ if (remedy) printNote(chalk.yellow(phaseLine("launch", remedy)));
113
+ const links = workspaceLinks(root, { platform: "web" });
114
+ if (json) {
115
+ console.log(JSON.stringify(links ? {
116
+ ...facts,
117
+ links
118
+ } : facts));
119
+ return;
120
+ }
121
+ const launched = facts.launched === true ? chalk.green("loaded") : chalk.yellow(facts.launched === "bundling" ? "bundling" : "unverified");
122
+ console.log(facts.running ? `${facts.url} ${launched} in ${facts.version ?? "Chrome"} (pid ${facts.pid}, ${facts.headless ? "headless" : "headed"}, ${facts.viewport}). DevTools: ${facts.cdpEndpoint}. Logs: stim logs --errors.` : `${facts.url} ${launched}, and the owned Chrome is no longer running. Run stim logs --errors, then stim web again.`);
123
+ if (links) console.error(chalk.dim(workspaceLinkLine(links)));
124
+ });
125
+ }
126
+ //#endregion
127
+ export { webCommand as n, runWeb as t };
package/dist/web-run.mjs CHANGED
@@ -1,18 +1,18 @@
1
- import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
- import { n as ownership_claim_exports, t as process_identity_exports } from "./process-identity-BmRm1qV0.mjs";
3
- import { t as relaunchWithLogFile } from "./detached-entry-CiTEgMR1.mjs";
4
- import { u as signalProcessTree } from "./metro-g8NqiJp-.mjs";
5
- import { _ as removeSingletonFiles, c as webClaimRoot, d as webLogFile, f as webProfileDir, g as liveProfileHolder, h as chromeProcessState, i as clearWebRecord, l as webDir, m as writeWebRecord, n as browserLogFile, s as updateWebRecord } from "./state-PeQiJuIu.mjs";
6
- import { c as probeWebDrivers } from "./activity-IRvJY_ru.mjs";
7
- import { r as recordCreatedDevice } from "./created-devices-CpvzBL25.mjs";
8
- import { r as createNdjsonWriter } from "./ndjson-PclOIwVY.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-i8GDPwq9.mjs";
3
+ import { d as signalProcessTree } from "./metro-DI2AjPF_.mjs";
4
+ import { _ as removeSingletonFiles, c as webClaimRoot, d as webLogFile, f as webProfileDir, g as liveProfileHolder, h as chromeProcessState, i as clearWebRecord, l as webDir, m as writeWebRecord, n as browserLogFile, s as updateWebRecord } from "./state-D2KtnALG.mjs";
5
+ import { c as probeWebDrivers } from "./activity-BSw0x84R.mjs";
6
+ import { r as recordCreatedDevice } from "./created-devices-D0s26Hx5.mjs";
7
+ import { r as createNdjsonWriter } from "./ndjson-LGZAMsqa.mjs";
8
+ import { t as relaunchWithLogFile } from "./detached-entry-pB_IPfV4.mjs";
9
9
  import { t as connectOwnedBrowser } from "./cdp-Dq_ypAlJ.mjs";
10
- import { i as chromeArgs, n as DESKTOP_PAGE, r as PHONE_SCREEN } from "./chrome-BrcVWQyD.mjs";
10
+ import { i as chromeArgs, n as DESKTOP_PAGE, r as PHONE_SCREEN } from "./chrome-BTUOo8p2.mjs";
11
11
  import { existsSync, mkdirSync, openSync, realpathSync } from "node:fs";
12
12
  import { isAbsolute, resolve } from "node:path";
13
- import { LOG_ROTATE_BYTES } from "@stim-cli/core";
14
13
  import { WEB_VIEWPORTS } from "@stim-cli/core/state";
15
14
  import { fileURLToPath } from "node:url";
15
+ import { LOG_ROTATE_BYTES } from "@stim-cli/core";
16
16
  //#region src/web/events.ts
17
17
  const MAX_FRAMES = 20;
18
18
  const CONSOLE_LEVELS = {
@@ -1,11 +1,11 @@
1
- import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
- import { t as process_identity_exports } from "./process-identity-BmRm1qV0.mjs";
1
+ import { t as process_identity_exports } from "./process-identity-Biv3arCO.mjs";
2
+ import { t as getExecutor } from "./exec-i8GDPwq9.mjs";
3
3
  import { existsSync, mkdirSync, readFileSync, realpathSync, renameSync, rmSync, writeFileSync } from "node:fs";
4
4
  import { dirname, isAbsolute, join, relative } from "node:path";
5
5
  import { join as join$1, resolve as resolve$1 } from "path";
6
- import { buildCacheRoot as sharedBuildCache, withDirLock as withDirLock$1, workspaceId as workspaceId$1, workspaceName as workspaceName$1, workspaceStateDir as workspaceDir } from "@stim-cli/core";
7
6
  import { WORKSPACE_AGENT_KEY, emulatorLogFile, readJsonObject, readWorkspaceState, readWorkspaceState as readWorkspaceState$1, sharedCcache as sharedCcache$1, sharedCompilationCache as sharedCompilationCache$1, supervisorLogFile, supervisorPidFile, workspaceAgentDeviceDir, workspaceDerivedData, workspaceLastUsed, workspaceLogErrorIndex, workspaceLogsDir as workspaceLogsDir$1, workspaceMetadataFile, workspaceStateFile, workspaceStateLock } from "@stim-cli/core/state";
8
7
  import { homedir } from "node:os";
8
+ import { buildCacheRoot as sharedBuildCache, withDirLock as withDirLock$1, workspaceId as workspaceId$1, workspaceName as workspaceName$1, workspaceStateDir as workspaceDir } from "@stim-cli/core";
9
9
  import { readFile, readdir } from "node:fs/promises";
10
10
  //#region src/agent-sessions.ts
11
11
  const TITLE_MAX_LENGTH = 120;