stim 1.17.1 → 1.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/README.md +50 -1
  2. package/dist/{activity-IRvJY_ru.mjs → activity-BxnkDZDi.mjs} +4 -4
  3. package/dist/{agent-device-usage-output-BK41vnzN.mjs → agent-device-usage-output-CZ66YA4T.mjs} +8 -8
  4. package/dist/android-BKA16DTf.mjs +4 -0
  5. package/dist/{android-B6rDWrTm.mjs → android-BvwuMUJ9.mjs} +192 -98
  6. package/dist/{android-DE4v59Mb.mjs → android-CBwCNicO.mjs} +2 -2
  7. package/dist/{android-B_YNZy61.mjs → android-DC3ZLuw0.mjs} +9 -9
  8. package/dist/{android-cas-ZFpjJBL_.mjs → android-cas-BBFyr9CA.mjs} +5 -5
  9. package/dist/android-cas-compiler.mjs +2 -2
  10. package/dist/api-D0VtFhqk.mjs +152 -0
  11. package/dist/api-run.d.mts +1 -0
  12. package/dist/api-run.mjs +145 -0
  13. package/dist/api.d.mts +212 -0
  14. package/dist/api.mjs +2 -0
  15. package/dist/{app-install-VPkI5WEg.mjs → app-install-D-PUENug.mjs} +3 -3
  16. package/dist/{attempt-Bk2ouQVL.mjs → attempt-DW-mQqM7.mjs} +14 -4
  17. package/dist/{budget-CUDz870h.mjs → budget-DIhL2BKx.mjs} +1581 -61
  18. package/dist/build-facts-DcLv2HSd.d.mts +111 -0
  19. package/dist/{cache-manifest-DnUHBJaU.mjs → cache-manifest-BI7ozfmH.mjs} +3 -3
  20. package/dist/cache-manifest.mjs +1 -1
  21. package/dist/{chrome-BrcVWQyD.mjs → chrome-DJnmnS5q.mjs} +1 -1
  22. package/dist/cli-DNJMwADx.mjs +68 -0
  23. package/dist/cli.mjs +1 -1
  24. package/dist/{client-CxNotugx.mjs → client-DzhapoMB.mjs} +184 -49
  25. package/dist/collector-run.mjs +9 -9
  26. package/dist/{command-output-CDAIFD-8.mjs → command-output-CBtzyIgF.mjs} +2 -2
  27. package/dist/{config-D0DbpZtM.mjs → config-CgYMahz7.mjs} +5 -5
  28. package/dist/{created-devices-CpvzBL25.mjs → created-devices-DKbeWsiK.mjs} +2 -2
  29. package/dist/{dependency-state-CLcBHH2M.mjs → dependency-state-xKoOypBm.mjs} +2 -2
  30. package/dist/{detached-entry-CiTEgMR1.mjs → detached-entry-BLfcgc4O.mjs} +1 -1
  31. package/dist/{dev-client-B-XC4uRH.mjs → dev-client-CLnIH02x.mjs} +2 -2
  32. package/dist/{device-C8IOoJPv.mjs → device-D7oCHGWi.mjs} +8 -8
  33. package/dist/{device-capacity-ZC4XSZ4R.mjs → device-capacity-EUOOYr8T.mjs} +13 -13
  34. package/dist/device-host-worker.mjs +19 -19
  35. package/dist/{device-ios-C2Um-XUR.mjs → device-ios-6MewKKnc.mjs} +13 -13
  36. package/dist/{device-lease-DpoS36_u.mjs → device-lease-Co-EwifR.mjs} +9 -9
  37. package/dist/{device-lease-run-B8YltIH_.mjs → device-lease-run-w1xhCqIW.mjs} +4 -4
  38. package/dist/{device-pool-3o7s5lo4.mjs → device-pool-F5WvRPtD.mjs} +3 -3
  39. package/dist/{device-remote-hFSkpfQu.mjs → device-remote-tHbd1fKy.mjs} +15 -15
  40. package/dist/{doctor-BF_435OC.mjs → doctor-B9ckYUoU.mjs} +33 -30
  41. package/dist/{error-diagnostics-C-NMnhrk.mjs → error-diagnostics-BRXOqDzG.mjs} +11 -11
  42. package/dist/{exec-CaErVv7t.mjs → exec-DWhCZDGm.mjs} +65 -2
  43. package/dist/{gc-BsWKa_-d.mjs → gc-Dh6mXOKC.mjs} +23 -469
  44. package/dist/{gradle-bsBRy6i9.mjs → gradle-C6GKnunQ.mjs} +6 -6
  45. package/dist/{guide-status-Du-ZQ3FD.mjs → guide-status-Bhf9SJXc.mjs} +3 -3
  46. package/dist/{guide-DC3n6-NH.mjs → guide-tm6igAaa.mjs} +314 -72
  47. package/dist/{hosted-android-DgU31wY3.mjs → hosted-android-Bv285t_t.mjs} +3 -3
  48. package/dist/{hosted-client-CdSoH-G6.mjs → hosted-client-C-rMtppF.mjs} +2 -2
  49. package/dist/{hosted-ios-BTaVj4Jb.mjs → hosted-ios-D5mNmtj4.mjs} +1 -1
  50. package/dist/{hosted-logs-SxfBvSvc.mjs → hosted-logs-DN8Iz4mJ.mjs} +9 -9
  51. package/dist/{hosted-macos-CT9E3meL.mjs → hosted-macos-B-wEjlwG.mjs} +5 -5
  52. package/dist/{hosted-native-CZLOrMrN.mjs → hosted-native-DeeOG8xZ.mjs} +6 -6
  53. package/dist/{idle-shutdown-IGyNV8Cr.mjs → idle-shutdown-jjtxAn5C.mjs} +7 -8
  54. package/dist/{ios-DSfnPfse.mjs → ios-B3SbOWdw.mjs} +19 -7
  55. package/dist/{ios-Cr1h5gnO.mjs → ios-Bvbf0SGY.mjs} +1 -1
  56. package/dist/{ios-C7JuT1bI.mjs → ios-CPdkoKaa.mjs} +181 -95
  57. package/dist/ios-DNpdygiv.mjs +7 -0
  58. package/dist/{ios-device-Ee7sDXfp.mjs → ios-device-Bn7jWDB1.mjs} +1 -1
  59. package/dist/{ios-device-pAN4t54-.mjs → ios-device-vOaByMhw.mjs} +3 -3
  60. package/dist/{ios-state-8L3lgum6.mjs → ios-state-B2pES6Vs.mjs} +1 -1
  61. package/dist/{launch-verify-lTnWZikR.mjs → launch-verify-hskNcWkZ.mjs} +5 -5
  62. package/dist/{logs-DNxKcok9.mjs → logs-1Ylc1Ri6.mjs} +13 -13
  63. package/dist/macos-Ck7OBMNY.mjs +2 -0
  64. package/dist/{macos-BEs8cwKx.mjs → macos-DF6C7wUU.mjs} +118 -34
  65. package/dist/macos-run.mjs +1 -1
  66. package/dist/maintenance-run.mjs +349 -47
  67. package/dist/{metro-g8NqiJp-.mjs → metro-Dh8T4Ytd.mjs} +5 -5
  68. package/dist/{metro-gateway-CHqK0p-a.mjs → metro-gateway-CEUw9RMH.mjs} +4 -4
  69. package/dist/{metro-store-CDDZHtPl.mjs → metro-store-BAHFcJm6.mjs} +16 -2
  70. package/dist/{named-ports-C8m8EQs2.mjs → named-ports-BFIz1Shz.mjs} +61 -6
  71. package/dist/{native-run-D1Q56rAD.mjs → native-run-B06FkmZx.mjs} +4 -4
  72. package/dist/{native-runtime-YhaSxP4v.mjs → native-runtime-D_603JiC.mjs} +6 -6
  73. package/dist/{ndjson-PclOIwVY.mjs → ndjson-LGZAMsqa.mjs} +8 -4
  74. package/dist/offload-worker.d.mts +3 -17
  75. package/dist/offload-worker.mjs +10 -11
  76. package/dist/{ownership-DyRQru0q.mjs → ownership-D6Se7gg_.mjs} +3 -3
  77. package/dist/{page-QF_Ke10s.mjs → page-Bv9s2KwY.mjs} +1 -1
  78. package/dist/placement-log-BcPhRwR4.mjs +84 -0
  79. package/dist/{build-plan-BJSXzsCL.mjs → plan-placement-CtxkCO54.mjs} +122 -48
  80. package/dist/{ports-4nYqxPWC.mjs → ports-BLTtfBq-.mjs} +3 -3
  81. package/dist/{prebuild-D7RUMkwN.mjs → prebuild-juCyreaR.mjs} +5 -5
  82. package/dist/preview-D5V07Nrl.mjs +987 -0
  83. package/dist/{rolldown-runtime-BsOwBuO_.mjs → process-identity-Biv3arCO.mjs} +11 -1
  84. package/dist/{project-D47Rh0ly.mjs → project-C3ChRLZy.mjs} +4 -68
  85. package/dist/projects-DD7dBTCj.mjs +92 -0
  86. package/dist/{pull-request-pxnDCQzn.mjs → pull-request-u4vp7zmK.mjs} +1 -1
  87. package/dist/pull-requests.mjs +1 -1
  88. package/dist/{recordings-BNgsD1Tr.mjs → recordings-BAWltsVt.mjs} +1 -1
  89. package/dist/{reload-byOjE3P4.mjs → reload-DvyWzVjJ.mjs} +15 -15
  90. package/dist/{remote-cache-Ci4DQYYj.mjs → remote-cache-BGg7aGIZ.mjs} +5 -5
  91. package/dist/{run-CkE9y0nc.mjs → run-vhtDgpHO.mjs} +5 -5
  92. package/dist/{server-bare-BkrSxSsZ.mjs → server-bare-B9E7S1aR.mjs} +4 -5
  93. package/dist/server-expo-Db9HBUoa.mjs +2 -0
  94. package/dist/{server-expo-CHTyXD0G.mjs → server-expo-DoBHhuzK.mjs} +5 -6
  95. package/dist/{settings-SR9OmW9h.mjs → settings-Byp2AQ_E.mjs} +6 -5
  96. package/dist/{settings-Cfd-0r2d.mjs → settings-CQyMElEP.mjs} +24 -763
  97. package/dist/{settings-BBpMcDbD.mjs → settings-Djl-mpjX.mjs} +1 -1
  98. package/dist/settings.schema.json +128 -5
  99. package/dist/{simslim-BWNrc3M3.mjs → simslim-EHBQ2R4G.mjs} +7 -7
  100. package/dist/{slot-launch-DmjBmpQV.mjs → slot-launch-C1nqh4mu.mjs} +6 -6
  101. package/dist/{spawn-claims-D0XfSe8u.mjs → spawn-claims-Kqiifsk2.mjs} +1 -1
  102. package/dist/{spawn-entry-jw19m8Nl.mjs → spawn-entry-Dcb7sDUz.mjs} +1 -0
  103. package/dist/{stage-CzaiwFsI.mjs → stage-g_WqWeaE.mjs} +14 -3
  104. package/dist/{start-BHIersSm.mjs → start-D-IeOyoK.mjs} +19 -19
  105. package/dist/{start-BEP-XmEA.mjs → start-NTc1Ifqs.mjs} +1 -1
  106. package/dist/{state-PeQiJuIu.mjs → state-BG-aAk4k.mjs} +4 -4
  107. package/dist/{state-B4PQFXMW.d.mts → state-lKh8shBp.d.mts} +2 -3
  108. package/dist/{state-DyaNTgfR.mjs → state-xnqpx3AD.mjs} +2 -2
  109. package/dist/{stats-Bq4fJOdB.mjs → stats-DY6ekz8F.mjs} +5 -5
  110. package/dist/{status-DlKvVJBE.mjs → status-BEeL6IKz.mjs} +6 -4
  111. package/dist/{status-CqeWbIWW.mjs → status-DCjZeE-K.mjs} +34 -35
  112. package/dist/{status-CzS4bPO9.mjs → status-Dcr0Qexd.mjs} +5 -5
  113. package/dist/{stim-desktop-Cnz8CPnl.mjs → stim-desktop-Behws_fi.mjs} +1 -1
  114. package/dist/{stim-installations-QMEI32v1.mjs → stim-installations-aR_WsNOA.mjs} +1 -1
  115. package/dist/{stop-D03prQnW.mjs → stop-BGhKMOuH.mjs} +5 -5
  116. package/dist/{stop-Cj01je8D.mjs → stop-BTjs3xbc.mjs} +17 -18
  117. package/dist/{stop-DOxwkH4j.mjs → stop-DIJtD2GL.mjs} +2 -2
  118. package/dist/supervisor-run.d.mts +24 -22
  119. package/dist/supervisor-run.mjs +21 -22
  120. package/dist/{support-BE-tbOP9.mjs → support-BaayeYCf.mjs} +7 -7
  121. package/dist/{support-WvD__tSV.mjs → support-Bwvbrq5-.mjs} +5 -5
  122. package/dist/{toolchain-CdrxgUeA.mjs → toolchain-Bnyd_K3A.mjs} +480 -27
  123. package/dist/{trigger-Bemu8ytw.mjs → trigger-CJMhwXe8.mjs} +5 -5
  124. package/dist/trigger-DWAq9hkw.mjs +2 -0
  125. package/dist/{warm-progress-DF4JRvYI.mjs → warm-progress-CjB-MP6H.mjs} +7 -7
  126. package/dist/web-B6_bFDn7.mjs +2 -0
  127. package/dist/{web-DddUdJzr.mjs → web-DTU0uiWj.mjs} +22 -22
  128. package/dist/web-run.mjs +10 -10
  129. package/dist/{workspace-state-DqcCbpE7.mjs → workspace-state-C59dqG-Z.mjs} +3 -3
  130. package/dist/{ownership-BMLqyBgJ.mjs → workspaces-xXItD8Dj.mjs} +577 -23
  131. package/dist/{worktree-CsAjSmEo.mjs → worktree-BJfnU6r5.mjs} +1 -1
  132. package/dist/{worktree-AK_m-d_m.mjs → worktree-BSYlofkq.mjs} +234 -35
  133. package/dist/worktree-CsKYexJM.mjs +753 -0
  134. package/package.json +10 -5
  135. package/dist/build-progress-D0WkjCPd.mjs +0 -1036
  136. package/dist/build-slots-41v_d2x9.mjs +0 -142
  137. package/dist/cli-BLQqDGlX.mjs +0 -51
  138. package/dist/deps-CVxlsRFn.mjs +0 -438
  139. package/dist/errors-B6W7FkxQ.mjs +0 -15
  140. package/dist/idle-BXG__1BA.mjs +0 -176
  141. package/dist/in-use-CvGOUn-x.mjs +0 -130
  142. package/dist/preview-C_JjkEy4.mjs +0 -318
  143. package/dist/process-identity-BmRm1qV0.mjs +0 -12
  144. package/dist/server-expo-CeakhI8P.mjs +0 -2
  145. package/dist/state-BKa8wNN5.mjs +0 -148
  146. package/dist/stop-cause-rhmGfwvt.mjs +0 -97
  147. package/dist/trigger-CXts6qt1.mjs +0 -2
  148. package/dist/watchman-APYjXSQa.mjs +0 -48
  149. package/dist/workspace-process-lock-2mNzOlaB.mjs +0 -58
  150. package/dist/workspaces-Bcib97-6.mjs +0 -306
@@ -1,7 +1,7 @@
1
- import { t as ANDROID_AVD_CONFIG_HELP } from "./settings-Cfd-0r2d.mjs";
2
- import { s as findProjectRoot$1 } from "./project-D47Rh0ly.mjs";
3
- import { t as RECENT_LAUNCH_MS } from "./status-CzS4bPO9.mjs";
4
- import { t as guideStatus } from "./guide-status-Du-ZQ3FD.mjs";
1
+ import { s as findProjectRoot$1 } from "./project-C3ChRLZy.mjs";
2
+ import { t as RECENT_LAUNCH_MS } from "./status-Dcr0Qexd.mjs";
3
+ import { t as ANDROID_AVD_CONFIG_HELP } from "./settings-CQyMElEP.mjs";
4
+ import { t as guideStatus } from "./guide-status-Bhf9SJXc.mjs";
5
5
  import { SETTINGS, SETTINGS_SCHEMA_URL } from "@stim-cli/core/state";
6
6
  import chalk from "chalk";
7
7
  //#region src/guide/agent.ts
@@ -293,6 +293,11 @@ Ask the user before these actions:
293
293
  nothing else.
294
294
  - gc --delete --worktrees, because it runs worktree remove on every clean,
295
295
  idle linked worktree Stim manages, across projects.
296
+ - settings set maintenance.mode on in a home where it is off or report, because
297
+ it lets Stim itself clear build outputs, trim caches and remove finished
298
+ worktrees later, with no command from you. It is on by default outside CI
299
+ and scoped STIM_HOME homes. maintenance.keep true pins a workspace against
300
+ that; maintenance.mode report or off stops it.
296
301
  - stop when the workspace owns an EAS session, because it irreversibly ends
297
302
  that remote session. For a local device, stop shuts it down but does not
298
303
  delete it. An explicit stop shuts down a Stim-owned simulator even when
@@ -382,6 +387,7 @@ FULL TOPIC LIST
382
387
  stim guide lifecycle options # every flag, Android variants, --device-type, --system-image, --device-profile
383
388
  stim guide lifecycle devices # ios --device and android --device on a physical phone
384
389
  stim guide lifecycle release # Release configurations and ...Release variants
390
+ stim guide api # typed run, stop, diagnostics and cancellation from the main package
385
391
  stim guide facts # the --json payloads
386
392
  stim guide facts devmenu # the Expo dev menu or Tools button over the app
387
393
  stim guide macos # Swift Package Debug apps, logs, local preview, --remote on another Mac
@@ -397,6 +403,64 @@ FULL TOPIC LIST
397
403
  stim guide settings # configuration files and supported keys`
398
404
  };
399
405
  //#endregion
406
+ //#region src/guide/api.ts
407
+ var api_default = {
408
+ summary: "Typed lifecycle API: createStim, run, stop, diagnostics, and cancellation",
409
+ body: () => `PROGRAMMATIC API
410
+
411
+ Install stim as a project dependency: npm install --save-dev stim.
412
+ Import { createStim, StimError } from 'stim'. Node 22.12 or later is required.
413
+
414
+ const stim = createStim({ projectRoot: process.cwd() });
415
+ try {
416
+ const result = await stim.run({ platform: 'ios' });
417
+ } finally {
418
+ try {
419
+ const diagnostics = await stim.diagnostics({ errors: true });
420
+ } finally {
421
+ const cleanup = await stim.stop();
422
+ if (!cleanup.ok) throw new Error(cleanup.summary);
423
+ }
424
+ }
425
+
426
+ createStim takes projectRoot, optional absolute home and buildCache paths,
427
+ and onProgress({ stream, message }). message is an output chunk. Without
428
+ onProgress, operations write nothing to the caller's stdout or stderr. Each
429
+ operation uses a bundled worker that calls the same lifecycle operations as
430
+ the CLI; it never parses CLI arguments or changes the caller's cwd/environment.
431
+
432
+ run takes platform plus its options:
433
+ ios: configuration, scheme, deviceType, runtime
434
+ android: variant, systemImage, deviceProfile
435
+ macos: remoteBuild
436
+ web: headed (default false)
437
+ iOS and Android also accept slot, metroCheck, buildCache, and remoteBuild.
438
+ All methods accept signal. run builds, installs and launches; build-only is
439
+ not available. Web requires a running server, just like stim web.
440
+
441
+ run returns { platform, facts }. iOS facts include udid; Android includes
442
+ serial; both include bundleId, appPath, metroPort, cacheHit and launched.
443
+ macOS facts include bundle, bundleId, executable, pid, build and launched.
444
+ Web facts contain the owned browser's existing launch and page facts.
445
+ Do not interpret 'bundling' or 'unverified' as proven application readiness.
446
+
447
+ stop({ slot? }) returns { ok, outcomes, summary }. It acts on the workspace,
448
+ including resources from earlier runs. Use a dedicated workspace in CI.
449
+ Cancellation waits for the operation worker to exit; stop with a fresh signal
450
+ after cancellation or partial failure. Always inspect cleanup.ok.
451
+
452
+ diagnostics({ tail: 200, errors: false }) returns { directory, records } from
453
+ the local timeline, even before a successful run. tail: 0 returns paths only.
454
+ It does not fetch remote logs or capture new crashes. Stim failures reject with
455
+ StimError carrying code, message, remedy and details (including the log path).
456
+ An exception thrown by onProgress cancels its worker and propagates unchanged.
457
+
458
+ Normal ownership, device creation, cache locks and coordination remain active.
459
+ All concurrent artifact-cache writers must share the same coordinating home.
460
+ Do not point independent Stim homes at a concurrently writable shared cache.
461
+ `
462
+ };
463
+ //#endregion
400
464
  //#region src/guide/facts.ts
401
465
  const facts = {
402
466
  summary: "The --json payloads: `start`, `ios`, `android`, `web`, `ios|android --plan`, `reload`, `stop`, `status`, `doctor`, `device lock`/`unlock`, `gc`, and the error contract",
@@ -910,9 +974,9 @@ leased until <time>" for each one.`,
910
974
  null when off; plan lists what the next start, ios or
911
975
  android would reclaim, in the \`reclaimed\` shape without
912
976
  freedMb, and is empty while under budget
913
- deviceHosts one { machine, state, dnsName?, deviceId?, requestedAt?, host? }
914
- per remote.machines entry. state is "approved", "pending",
915
- "not-asked", "revoked", "node-changed", "not-on-tailnet",
977
+ deviceHosts one { machine, state, dnsName?, deviceId?, requestedAt?, expiresAt?,
978
+ host? } per remote.machines entry. state is "approved",
979
+ "pending", "not-asked", "revoked", "lapsed", "node-changed", "not-on-tailnet",
916
980
  "tailscale-off", "unreachable", "invalid",
917
981
  "credentials-unavailable" or "busy". Tokens stay private.
918
982
  Only --fix asks for access or forgets removed names.
@@ -920,13 +984,17 @@ leased until <time>" for each one.`,
920
984
  build approval; see \`guide settings\`.
921
985
  host is { name, screenRecording, accessibility }, present
922
986
  only for approved machines whose worker reports it.
987
+ expiresAt is when a pending request lapses; "lapsed" is
988
+ a pending request whose expiry passed. --fix asks again.
923
989
  remoteMachines one { machine, state, dnsName?, deviceId?, requestedAt?,
924
- offloadable?, reasons?, problems?, capacity?, host? } per
990
+ expiresAt?, offloadable?, reasons?, problems?, capacity?, host? } per
925
991
  remote.machines entry, for its build approval; state is "approved", "pending", "not-asked",
926
- "revoked" (revoked, or the request lapsed), "node-changed",
992
+ "revoked" (revoked or denied), "lapsed" (the request lapsed
993
+ before approval), "node-changed",
927
994
  "not-on-tailnet", "tailscale-off", "unreachable" or
928
995
  "invalid". host has the same shape and presence rule as in
929
- deviceHosts. An approved machine also carries offloadable,
996
+ deviceHosts, as do expiresAt (a pending request's lapse
997
+ time) and "lapsed" (--fix asks again). An approved machine also carries offloadable,
930
998
  true when it would take this app's builds now (iOS
931
999
  simulator unless --platform android, Android emulator when
932
1000
  --platform android or the app has android/ or uses Expo,
@@ -941,7 +1009,7 @@ leased until <time>" for each one.`,
941
1009
  reasons as { code, reason } with that code. capacity
942
1010
  is the machine's offer: { running, max, diskFreeBytes,
943
1011
  minDiskFreeBytes, cpus?, loadPerCore?, builds?, maxBuilds?,
944
- maxLoadPerCore?, declined? }; an older stim-server omits the
1012
+ maxLoadPerCore?, memoryUsedBytes?, memoryTotalBytes?, declined? }; an older stim-server omits the
945
1013
  optional fields
946
1014
  findings the diagnostic findings; a lower resolved Stim is a
947
1015
  costs-time finding with a PATH or installation remedy
@@ -1199,7 +1267,7 @@ RULES
1199
1267
  cannot read. A macOS privacy denial (EPERM) names the
1200
1268
  Privacy & Security setting to grant
1201
1269
  maintenance top-level { mode, pressure, actions, blocked,
1202
- skips, note, invalid? }, the next report-only maintenance plan
1270
+ skips, note, invalid? }, the next maintenance plan
1203
1271
  from live pressure and cached sizes. note says when no
1204
1272
  pass has run yet. No du runs for this preview.
1205
1273
  sections one array per report section, in the text order. Every key
@@ -1374,8 +1442,8 @@ RULES
1374
1442
  "ready" its last warm succeeded and nothing has run there
1375
1443
  since: no start, ios, android, web or reload, for
1376
1444
  at most 2 hours
1377
- "live" live is true: Metro, a device, Chrome or a remote
1378
- session of it runs
1445
+ "live" the workspace is active (live is true): Metro, a
1446
+ device, Chrome or a remote session of it runs
1379
1447
  "idle" none of these
1380
1448
  phaseSince when the warm started ("warming") or finished ("ready"); null
1381
1449
  for "live" and "idle"
@@ -1403,8 +1471,8 @@ RULES
1403
1471
  The first kind that applies wins:
1404
1472
 
1405
1473
  kind "building" a build runs; platform names it
1406
- "warming" phase is "warming" and nothing is live
1407
- "ready" phase is "ready" and nothing is live
1474
+ "warming" phase is "warming" and nothing is active
1475
+ "ready" phase is "ready" and nothing is active
1408
1476
  "build-failed" the newest run of either platform failed;
1409
1477
  platform names it
1410
1478
  "running" live is true or a remote session runs
@@ -1688,7 +1756,8 @@ RULES
1688
1756
  with detail), install (staging the bundle, or fetching
1689
1757
  it from a build machine) and launch. It has no cache
1690
1758
  lookup: outcome is null, plannedPhases is null, and
1691
- builds.macos holds its finished runs and their phases.
1759
+ builds.macos holds its finished runs, their phases and
1760
+ compileSteps.
1692
1761
  startedAt when the run started; phaseStartedAt when its phase did
1693
1762
  outcome "cold" after the local/provider lookups resolve a miss;
1694
1763
  "hit" after a cached artifact is ready to reuse, including
@@ -1884,7 +1953,7 @@ RULES
1884
1953
  under CoreSimulator/Devices, or the AVD's .avd folder
1885
1954
 
1886
1955
  \`status --watch\` runs one du at a time off its refresh path, and measures
1887
- a folder at most every 5 minutes while its environment is live and every
1956
+ a folder at most every 5 minutes while its environment is active and every
1888
1957
  hour otherwise. It caches each size under $STIM_HOME/disk-usage, which
1889
1958
  one-shot status only reads, so the fields appear once a watcher, such as
1890
1959
  stim-server or Stim Desktop, has measured.
@@ -1953,13 +2022,15 @@ RULES
1953
2022
  and always uses the estimate. What is using CPU and memory now is the
1954
2023
  top-level machine section:
1955
2024
 
1956
- maintenance { mode, lastChecks: { pressure, size }, pressure, sizes,
2025
+ maintenance { mode, lastChecks: { pressure, size, worktree, sweep }, pressure, sizes,
1957
2026
  lastPass, running, recent, plan, invalid?, claim? }
1958
- Report-only observations; every plan action's kind starts with would-.
2027
+ mode is off, report or on. Plan actions start with would-; the records
2028
+ of actions taken drop the prefix (clear-outputs, trim-cache, empty-cache,
2029
+ remove-worktree, remove-orphan, unregister-cache).
1959
2030
  lastChecks are epoch milliseconds or null. running comes only from a
1960
2031
  live maintenance/run.claims owner, never from a check stamp.
1961
- lastPass carries startedAt, durationMs, trigger, mode, freedBytes (0),
1962
- actions, stopped (0), blocked. recent holds the last 20 action, failure
2032
+ lastPass carries startedAt, durationMs, trigger, mode, freedBytes
2033
+ (0 in report mode), actions, stopped (0), blocked. recent holds the last 20 action, failure
1963
2034
  and blocked NDJSON records from maintenance/maintenance.ndjson.
1964
2035
  invalid names invalid settings; invalid cache caps use their defaults,
1965
2036
  while invalid maintenance settings disable passes. claim contains an
@@ -2004,7 +2075,7 @@ RULES
2004
2075
  started it until that build exits, then as shared. Processes with no owner
2005
2076
  are left out. machine comes from one host ps, the one status reads for
2006
2077
  device activity, and one run of the footprint helper. It is null when no
2007
- simulator is booted, no workspace is live and no build runs: status then
2078
+ simulator is booted, no workspace is active and no build runs: status then
2008
2079
  runs neither. \`status --watch --json\` rereads both every 15 seconds while
2009
2080
  machine is not null, with no other subprocess.`
2010
2081
  },
@@ -2014,7 +2085,7 @@ RULES
2014
2085
 
2015
2086
  { platform, slot?, fingerprint, cacheKey, cacheHit, provider,
2016
2087
  cacheSkipped, prebuild, outcome, expectedMs, basis, missReason?,
2017
- refusal? }
2088
+ placement?, refusal? }
2018
2089
 
2019
2090
  fingerprint the fingerprint the run would look up first; with
2020
2091
  --eas-profile, the one EAS CLI computes
@@ -2040,6 +2111,9 @@ RULES
2040
2111
  plan does not, so changes compare the fingerprint before that
2041
2112
  prebuild; changeCount 0 then means those inputs match the
2042
2113
  baseline. rekeyedBy is empty.
2114
+ placement with ios.remote or android.remote set to auto or a Mac,
2115
+ where the plan assumes the device runs: "on <machine>" or
2116
+ "this Mac; auto may use <machines>"; absent otherwise
2043
2117
  refusal { code, message, remedy } when the run would refuse:
2044
2118
  STIM_PREBUILD_FAILED for a tracked native dir the fingerprint
2045
2119
  leaves out, STIM_EAS_BUILD_MISSING for an EAS miss
@@ -2642,7 +2716,7 @@ FLAGS
2642
2716
  --slot <name> only this slot's records plus, once it has launched, the
2643
2717
  shared untagged Metro and app client records (not the web
2644
2718
  page's); default also includes untagged legacy records
2645
- --source <s...> metro, client, device, build, agent, maintenance (one or more), or all.
2719
+ --source <s...> metro, client, device, build, agent, maintenance, placement (one or more), or all.
2646
2720
  An unknown value is REJECTED rather than quietly matching
2647
2721
  nothing.
2648
2722
  --level <l> minimum level: debug, info, warn, error, fatal
@@ -2779,6 +2853,12 @@ THE RECORD
2779
2853
  event the producer's own event name (bundle_build_done, client_log, ...)
2780
2854
  stack frames of { file, line, column, fn }, passed through as reported
2781
2855
  marker true on the records that close an error window
2856
+ runId the id of the stim invocation that wrote the record: STIM_RUN_ID
2857
+ when set to a valid id (letters, digits, . _ -, at most 64),
2858
+ else generated per run. Processes a command starts, such as the
2859
+ Metro supervisor and the collectors, keep the id of the command
2860
+ that started them. Debug records and the hello to stim-server
2861
+ carry it too, and stim-server puts it on its log lines.
2782
2862
  deviceTs Android logcat's original epoch milliseconds; ts is aligned to
2783
2863
  host time using a bounded clock query at each collector attachment
2784
2864
  clockOffsetMs the offset added to deviceTs; absent if the query failed.
@@ -2796,6 +2876,59 @@ Add --errors to --source maintenance to show only maintenance_failure events;
2796
2876
  failures before a later launch marker are hidden. Machine passes are reported in
2797
2877
  status and maintenance/maintenance.ndjson; this command reads workspace logs.
2798
2878
 
2879
+ REMOTE REQUEST RECORDS
2880
+ A request to another Mac that fails is a warn record with src: build in the
2881
+ run's build log: remote_connect_failed { host, capability, ms, timeoutMs, msg,
2882
+ code } (could not connect or say hello, e.g. "no reply in time" when the Mac
2883
+ did not answer hello) and remote_request_failed { method, ms, code, msg }.
2884
+ Timings of requests that succeed are debug records (below).
2885
+ stim-server writes to its service log (~/Library/Logs/Stim/<label>.log), with
2886
+ no tokens or tickets: "request failed method=<m> client=<device id> run=<runId>
2887
+ ms=<n> error=<code>" for an error reply (once a minute per client, method and
2888
+ code) and "host_connect ... error=<reason_with_underscores>" when its connection to a hosting
2889
+ Mac fails. With debug.logs on or STIM_DEBUG=1 for the server it logs every
2890
+ request instead: "debug request method=<m> client=<id> run=<runId> ms=<n>
2891
+ slow=true error=<code> whois=<ms> probe=<ms>" (slow means 1000 ms or more;
2892
+ whois and probe are the hello's Tailscale identity lookup and its wait for
2893
+ the host permission probe; a method name that is not a plain dotted lowercase
2894
+ name is logged as unknown)
2895
+ and "debug host_connect host=<mac> ms=<n> connectMs=<n> helloMs=<n>" (reused=true instead of the two
2896
+ timings when an open connection was shared), also as
2897
+ records in STIM_HOME/logs/debug/server.ndjson. Search the run id in both logs.
2898
+
2899
+ DEBUG LOGS
2900
+ With debug.logs on or STIM_DEBUG=1 (stim guide settings), the CLI also appends
2901
+ debug records to STIM_HOME/logs/debug/cli.ndjson, outside any workspace, so
2902
+ stim logs does not show them. Read them with jq:
2903
+ jq -c 'select(.event=="exec" and .ms>1000)' ~/.stim/logs/debug/cli.ndjson
2904
+ Events: run_start, run_end { exit, ms }, exec { program, ms, ok, exit? }, and
2905
+ remote_connect and remote_request { ms, ok, code? } for requests to another
2906
+ Mac. They carry no arguments, tokens or tickets.
2907
+
2908
+ Placement records use src: placement and sit in the run's build log
2909
+ (build-*.ndjson), so a new run replaces the previous run's. A run writes one
2910
+ record per decision it makes (a build that compiles and has a remote Mac to
2911
+ consider, or ios/android --remote auto), with event build_placement,
2912
+ device_placement or placement_fallback. A cache hit, a project with no paired
2913
+ remote Mac and a named --remote target write none:
2914
+ stim logs --source placement
2915
+ stim logs --source placement --json
2916
+ Fields: kind (build or device), platform, settings [{ key, value, from }] with
2917
+ from flag, env, setting or default (remote.build and remote.buildMode for a
2918
+ build, ios.remote or android.remote for a device), candidates [{ machine, code,
2919
+ msg, detail? }], choice { machine, code, msg } with machine local when the run
2920
+ stays on this Mac, and fallback { code, msg, machine? } when a remote Mac that
2921
+ was meant to take the run did not (level warn). The msg is the same text the
2922
+ placement: and build: phase lines print.
2923
+ Candidate codes: accepted, unreachable, busy, disk, load, version-mismatch
2924
+ (detail lists the toolchain parts: xcode, arch, jdk, ...), no-matching-device,
2925
+ declined, memory, no-capacity. Choice codes for a build: placed, named,
2926
+ forced, this-mac-busy, this-mac-free, mode-off, local-selected, no-remote-mac,
2927
+ unsupported; fallback codes: no-remote-mac-took-it, offload-failed, fallback.
2928
+ Choice codes for a device: placed, sticky, this-mac-free, no-remote-mac,
2929
+ device-count-unknown, no-host-admits. Machine names appear in these records;
2930
+ they stay in the local logs. These records carry no tokens.
2931
+
2799
2932
  WHAT WRITES WHAT
2800
2933
  maintenance.ndjson workspace maintenance actions, failures and explaining skips
2801
2934
  metro.ndjson the bundler, in both supervisor modes
@@ -3001,6 +3134,9 @@ Branch on the code, never on the message.`,
3001
3134
  an unavailable choice. A session that exists stays recorded even if delivery
3002
3135
  fails: retry stim ios|android --remote <machine>, or run stim stop to reconcile it.
3003
3136
  An unreachable stop keeps the placement; rerun stim stop when the host answers.
3137
+ To see why a host was slow or refused: stim logs --source placement (hosts checked and
3138
+ reason codes), stim logs --source build (remote_connect_failed, remote_request_failed),
3139
+ and the host's stim-server service log, searched for the run id (stim guide logs).
3004
3140
  A failed build handoff uses upload instead. If native log queries are unavailable,
3005
3141
  logs prints a note on stderr and shows copied records. Update an older stim-server
3006
3142
  on the host to enable handoff and native logs (hello features hosted-ios-data
@@ -4393,7 +4529,12 @@ not on any remote" (worktree remove)
4393
4529
  Warm copied ignored Pods from the source checkout, but their Manifest.lock
4394
4530
  differs from the tracked Podfile.lock in this worktree. Warm does not change
4395
4531
  tracked files. Run the printed pod-install command before building directly.
4396
- \`stim ios\` detects a mismatch and runs \`pod install\` for you.
4532
+ \`stim ios\` detects a mismatch and runs \`pod install\` for you. A mismatch
4533
+ limited to checksums of podspecs that embed the source checkout's path is
4534
+ resolved by warm itself ("carry moved <dir>/Pods to this checkout's path")
4535
+ and does not print this line. When the locks match but Pods still name the
4536
+ source path and warm cannot move it, it prints "removed <dir>/Pods/Manifest.lock
4537
+ so pod install runs" and \`stim ios\` runs \`pod install\`.
4397
4538
 
4398
4539
  "carry carried <dir>/Pods but there is no <dir>/Podfile.lock"
4399
4540
  Warm copied Pods but the destination has no Podfile.lock. Follow the printed
@@ -4519,6 +4660,22 @@ not on any remote" (worktree remove)
4519
4660
  Expo build-cache provider would each use a different store. Set the named
4520
4661
  variable to an absolute path, or unset it to use the default. Metro and the
4521
4662
  cache provider, which cannot refuse, ignore a relative value with a warning.`
4663
+ },
4664
+ STIM_WORKER_FAILED: {
4665
+ summary: "a programmatic API worker exited without returning an operation result",
4666
+ body: () => `STIM_WORKER_FAILED (programmatic API)
4667
+ The worker process ended without a structured result. StimError.details
4668
+ includes its final stderr output. Inspect that output and call diagnostics()
4669
+ for existing workspace logs, then stop() with a fresh signal to clean up
4670
+ any resources created before the failure. See stim guide api.`
4671
+ },
4672
+ STIM_RUN_FAILED: {
4673
+ summary: "a programmatic operation failed without a more specific Stim code",
4674
+ body: () => `STIM_RUN_FAILED (programmatic API)
4675
+ The underlying operation threw an error without a specific code. Its
4676
+ message and workspace log path are preserved in StimError. Read diagnostics()
4677
+ and call stop() in cleanup, including after a partial run. If the error is
4678
+ unexpected, report its message and logs at github.com/appandflow/stim/issues.`
4522
4679
  },
4523
4680
  STIM_NODE_UNSUPPORTED: {
4524
4681
  summary: "stim or stim-server started on a Node older than 22.12.0, often a project pin",
@@ -4775,7 +4932,7 @@ STOP DURING A BUILD
4775
4932
 
4776
4933
  CAPACITY
4777
4934
  A booted iOS sim is roughly 1-2 GB of RAM, an Android emulator 2-3 GB. On a
4778
- 16 GB machine plan for 2-3 live environments. Nothing enforces this;
4935
+ 16 GB machine plan for 2-3 active environments. Nothing enforces this;
4779
4936
  \`stim status\` is how you check -- it reports every workspace on the
4780
4937
  machine, not just this one.
4781
4938
 
@@ -4982,6 +5139,8 @@ offload resolves later, after the device architecture is known.
4982
5139
  When no host admits, auto runs here and may wait in the existing FIFO device
4983
5140
  slot queue; --no-wait and --wait 0 refuse with STIM_AT_CAPACITY. The placement
4984
5141
  line explains the decision and the skipped hosts. JSON progress goes to stderr.
5142
+ The same decision, with a reason code per host, is a src: placement record in
5143
+ stim logs (stim guide logs).
4985
5144
  A recorded hosted session wins over load; a live local owned slot stays here.
4986
5145
  A stopped recorded session places again; unreachable or unknown sessions refuse.
4987
5146
 
@@ -5732,8 +5891,16 @@ PREDICTING THE NEXT BUILD (--plan)
5732
5891
  An Android plan reads the ABI from the emulator the slot records, or from
5733
5892
  the system image a new one would use. A plan refuses --device, --remote,
5734
5893
  --wait, --no-wait, --no-metro-check and --simulator-app with STIM_BAD_ARG.
5735
- Without --eas-profile it also refuses the ios.remote and android.remote
5736
- settings with STIM_BAD_ARG. Without --eas-profile an Android plan also
5894
+ With ios.remote or android.remote set to a Mac name, a plan reads the
5895
+ architecture or ABI from that Mac's device offer, the question a run asks
5896
+ before it reserves anything. With auto it repeats the placement decision a
5897
+ run would make now and reports where in the payload's placement field, for
5898
+ example "this Mac; auto may use janics-mac-mini". When the plan stays on
5899
+ this Mac and a listed Mac would build for another architecture, placement
5900
+ says so, since that Mac's key differs. The plan only predicts the key: a
5901
+ run reads the architecture of the device it actually gets. A Mac that does
5902
+ not answer, or ios.remote or android.remote set to eas or proxy, refuses
5903
+ with STIM_BAD_ARG. Without --eas-profile an Android plan also
5737
5904
  refuses the experimental compiler CAS with STIM_BAD_ARG, and refuses with
5738
5905
  STIM_NO_DEVICE when no system image is installed.
5739
5906
 
@@ -6204,7 +6371,7 @@ OPT-IN CONCURRENCY LIMITS (UNLIMITED BY DEFAULT)
6204
6371
  and the command to remove that exact claim.
6205
6372
  See \`guide errors STIM_AT_CAPACITY\`.
6206
6373
 
6207
- \`stim doctor\` prints one note echoing the caps and the current live count,
6374
+ \`stim doctor\` prints one note echoing the caps and the current active count,
6208
6375
  but ONLY when a cap is set. \`stim gc\` reports stale build slots the way it
6209
6376
  reports stale build locks, and \`gc --delete\` clears them. Set the caps
6210
6377
  with \`stim settings set concurrency.maxBuilds 2\`, by editing
@@ -6220,7 +6387,7 @@ OPT-IN CONCURRENCY LIMITS (UNLIMITED BY DEFAULT)
6220
6387
  $STIM_HOME. Default 20. Below it, Stim reclaims.
6221
6388
  budget.hardFloorDiskGb default 5. Still below it after reclaiming,
6222
6389
  the run refuses with STIM_LOW_DISK.
6223
- budget.maxCommittedMemoryGb the rough memory of live environments, the
6390
+ budget.maxCommittedMemoryGb the rough memory of active environments, the
6224
6391
  figure \`stim status\` prints (a booted
6225
6392
  simulator 1.5 GB, an emulator 2.5 GB, a dev
6226
6393
  server 0.7 GB). Default 60% of physical memory.
@@ -6489,6 +6656,24 @@ OPT-IN CONCURRENCY LIMITS (UNLIMITED BY DEFAULT)
6489
6656
  overlapping a nested destination worktree or below a symlink ancestor.
6490
6657
  Tracked .idea settings come from Git and stay untouched by warm.
6491
6658
 
6659
+ A carried ios/Pods still names the source checkout's path in its generated
6660
+ files and in the checksum of a podspec that embeds that path (the
6661
+ precompiled ExpoModulesCore). When ios/Podfile.lock and the carried
6662
+ ios/Pods/Manifest.lock differ only by those checksums, warm rewrites the
6663
+ source path to the worktree's path in Pods/Target Support Files,
6664
+ Pods.xcodeproj, Local Podspecs and absolute symlinks, then makes
6665
+ Manifest.lock equal Podfile.lock, so the first \`stim ios\` skips
6666
+ \`pod install\`. Any other difference, a missing podspec, or a podspec
6667
+ that does not embed the source path, any other file in Pods that names
6668
+ the source path, or carried node_modules that do not match the worktree's
6669
+ lockfile leaves Pods as copied, and \`pod install\` runs. Warm does not edit Podfile.lock.
6670
+
6671
+ When the two locks already match but the copied Pods still name the source
6672
+ path, warm applies the same rewrite and scan so builds do not read the
6673
+ source checkout's files (entitlements, Podfile.properties.json). If it
6674
+ cannot (stale node_modules, a leftover path, an error), warm deletes
6675
+ ios/Pods/Manifest.lock so \`stim ios\` runs \`pod install\`.
6676
+
6492
6677
  Other generated state stays eligible: .gradle, .cxx, *.tsbuildinfo, build
6493
6678
  directories, and embedded JavaScript need project-specific decisions about
6494
6679
  regeneration. Native intermediates can record the source checkout's paths;
@@ -7038,35 +7223,66 @@ $STIM_HOME/archive by default. See stim guide cleanup archive.
7038
7223
 
7039
7224
  MAINTENANCE
7040
7225
 
7041
- Automatic maintenance is report-only in this release: it measures, plans and
7042
- logs, and never stops or deletes resources. The default mode is report; it is
7043
- off in CI and scoped STIM_HOME homes unless STIM_MAINTENANCE is explicit.
7044
- Commands trigger a detached pass when disk and memory checks (every minute)
7045
- or directory sizes (hourly) are due. guide, settings and help do not trigger
7226
+ Automatic maintenance has three modes. on (the default) runs the disk actions
7227
+ below through gc's own removal code and logs each one. report measures, plans
7228
+ and logs, and never deletes anything. off disables it. It is off in CI and
7229
+ scoped STIM_HOME homes unless STIM_MAINTENANCE is explicit, so a scratch home
7230
+ or CI run never deletes on its own.
7231
+ Commands trigger a detached pass when a check is due: disk and memory pressure
7232
+ (every minute), directory sizes (hourly), finished worktrees (every 15
7233
+ minutes) and the age sweep (daily). guide, settings and help do not trigger
7046
7234
  it; gc --delete also skips the hook. status --watch also triggers checks.
7047
- Size checks defer under high load. Attempts back off for at least one minute.
7048
- Measured directories are workspace build outputs and Stim's shared native,
7049
- Metro, ccache, Swift compilation and registered caches. Pressure checks read
7050
- free disk on the Stim home, projects and worker root volumes. On macOS the
7051
- memory signal is the sysctl pressure level, with no signal if sysctl fails.
7052
- Other platforms use os.freemem(); macOS never falls back to it.
7053
- Memory pressure is recorded only; memory stops are deferred to a later phase.
7235
+ Size, worktree and sweep checks defer under high load. Attempts back off for at
7236
+ least one minute. Measured directories are workspace build outputs and Stim's
7237
+ shared native, Metro, ccache, Swift compilation and registered caches.
7238
+ Pressure checks read free disk on the Stim home, projects and worker root
7239
+ volumes. On macOS the memory signal is the sysctl pressure level, with no
7240
+ signal if sysctl fails. Other platforms use os.freemem(); macOS never falls
7241
+ back to it. Memory pressure is recorded only; stopping devices, dev servers
7242
+ and helpers is not automatic.
7243
+
7244
+ In on mode a pass runs, cheapest to rebuild first:
7245
+ 1. orphaned workspace directories whose project is gone, and registered
7246
+ caches whose directory no longer exists
7247
+ 2. build outputs of idle workspaces, over maintenance.workspaceOutputsMaxGb,
7248
+ below the free-disk floor, or unused for maintenance.olderThanDays
7249
+ 3. build-cache and Metro entries, least recently used first, down to
7250
+ maintenance.capTargetPercent of caches.buildCacheMaxGb or
7251
+ caches.metroCacheMaxGb, and entries unused for olderThanDays
7252
+ 4. the Swift compilation cache, emptied whole, only below
7253
+ budget.hardFloorDiskGb and with no build lock or slot held
7254
+ 5. linked worktrees whose branch or pull request finished (gc's finished
7255
+ worktree rules and gc.worktreeGraceMinutes; maintenance.removeFinishedWorktrees)
7256
+ Disk-driven steps stop once free space is back above the floor. Kept for
7257
+ every pass: a workspace that is in use, holds a lock, was used within
7258
+ maintenance.protectRecentHours, is pinned with maintenance.keep, or is the
7259
+ workspace of the command that started the pass; cache entries used within
7260
+ protectRecentHours, named by a project's last builds, a parked device or a
7261
+ live build lock, or in a Metro store with a running or unverifiable dev
7262
+ server. Anything the pass cannot verify is kept. A pass never shuts down idle devices or dev servers or stops watchman; removing a
7263
+ finished worktree or orphaned directory does tear down that workspace's own
7264
+ dev server, owned devices and Chrome profile, as gc --delete does. ccache evicts by itself under
7265
+ caches.ccacheMaxGb.
7054
7266
 
7055
7267
  stim status last checks, plan and running pass
7056
7268
  stim status --json maintenance observations and recent records
7057
7269
  stim logs --source maintenance this workspace's maintenance records
7058
7270
  stim gc live pressure and cached-size preview
7059
- stim settings set maintenance.mode off
7271
+ stim settings set maintenance.mode off turn automatic cleanup off
7272
+ stim settings set maintenance.mode report plan and log, delete nothing
7060
7273
 
7061
7274
  The machine log is $STIM_HOME/maintenance/maintenance.ndjson, rotated at
7062
7275
  maintenance.logMaxMb with the old generation retained for
7063
7276
  maintenance.logRetentionDays. Child crashes use maintenance/child.log.
7064
- Actions and explaining skips are logged only when newly planned. A pass is
7065
- logged after a size check or a change to actions, skips or blocked reasons.
7277
+ In report mode actions and explaining skips are logged only when newly
7278
+ planned; in on mode every action taken, failure and explaining skip is logged
7279
+ with its bytes, and a pass record carries the bytes freed. Removal of a
7280
+ worktree or orphaned directory is logged to the machine log only.
7066
7281
  maintenance.logChecks enables debug observations; default false.
7067
7282
  status and doctor report invalid settings and unresolved claims with a removal
7068
7283
  command to run only after confirming the holder is gone. Invalid cache caps
7069
7284
  fall back to their own defaults; invalid maintenance settings disable passes.
7285
+ Pin a workspace with \`stim settings set maintenance.keep true\` in its project.
7070
7286
  The run claim serializes passes with gc --delete; gc refuses a held claim
7071
7287
  with the holder and recovery guidance instead of waiting.
7072
7288
 
@@ -8263,12 +8479,26 @@ overrides the file:
8263
8479
  budget is off. A value of the wrong shape refuses start, ios and android with
8264
8480
  STIM_BAD_ARG. See \`guide lifecycle budget\` for what each limit reclaims.
8265
8481
 
8482
+ DEBUG LOGS ARE MACHINE-LEVEL AND OFF BY DEFAULT
8483
+ debug.logs STIM_DEBUG default false. STIM_DEBUG=1 (or 0) overrides the setting
8484
+ for one command. While on, the CLI writes debug records to
8485
+ STIM_HOME/logs/debug/cli.ndjson (run_start, run_end, exec with the program name,
8486
+ duration and exit status but never arguments, remote_connect and
8487
+ remote_request with durations and codes); stim-server writes server.ndjson there
8488
+ and logs every request with its timings to its service log. The file rotates at about 8 MiB and keeps one previous
8489
+ generation, and nothing is sent anywhere. Keys named like a secret are redacted.
8490
+ stim settings set debug.logs true --scope machine
8491
+ See \`guide logs\` for reading the files.
8492
+
8266
8493
  AUTOMATIC MAINTENANCE IS MACHINE-LEVEL
8267
- Report-only in this release: it measures and plans, and deletes or stops nothing.
8494
+ maintenance.mode on (the default) removes what gc would under the caps and
8495
+ floors below; report measures and plans and deletes nothing; off disables it.
8268
8496
  STIM_HOME and CI make the mode off unless STIM_MAINTENANCE is set.
8497
+ maintenance.keep is a project setting: pin a workspace so a pass never clears
8498
+ its build outputs or removes its worktree.
8269
8499
  See \`guide cleanup\` for checks, plans and log paths.
8270
8500
 
8271
- ${SETTINGS.filter((setting) => setting.key.startsWith("maintenance.") || /^caches\..*MaxGb$/.test(setting.key)).map((setting) => ` ${setting.key} ${setting.env} ${setting.default === void 0 ? "unset" : `default ${setting.default}`}
8501
+ ${SETTINGS.filter((setting) => setting.key.startsWith("maintenance.") || /^caches\..*MaxGb$/.test(setting.key)).map((setting) => ` ${setting.key} ${setting.env ?? "project setting"} ${setting.default === void 0 ? "unset" : `default ${setting.default}`}
8272
8502
  ${setting.description}`).join("\n")}
8273
8503
 
8274
8504
  Maintenance uses the sysctl pressure level on macOS, with no memory signal
@@ -8414,7 +8644,7 @@ DEVICE HOSTING
8414
8644
  Only \`doctor --fix\` asks for device-host access. A person on that Mac
8415
8645
  approves the printed id with \`stim-server devices grant <id> --device-host\`.
8416
8646
  A person can also run \`stim-server setup\` on the worker: one node, one ticket,
8417
- one expiry, at most one request per capability, with per-grant y/N in a
8647
+ one expiry, at most one request per capability, with a per-grant Y/n question (Enter approves) in a
8418
8648
  terminal or \`--yes\` otherwise. Agents never run \`stim-server setup\` or
8419
8649
  approve requests. Setup never changes TCC or enables Funnel; an SSH-driven
8420
8650
  run is not offered. Without a terminal or --yes, setup refuses before installing
@@ -8422,9 +8652,9 @@ unless every chosen capability already has a matching approval. Desktop reuse
8422
8652
  requires an existing tailnet route. Ctrl-C or SIGTERM completes the journal,
8423
8653
  releases the setup claim and exits 1; a typed N also exits 1. Hosting grants
8424
8654
  include no read, control or build capability.
8425
- On a remote Mac that hosts, Stim Desktop > Settings > Phones > Hosted here lists the
8426
- simulators, emulators and apps approved Macs run there, below Device hosting
8427
- approvals. Stop asks for confirmation, ends the session and deletes or parks
8655
+ On a remote Mac that hosts, Stim Desktop > Settings > Remote Macs > Running here lists the
8656
+ simulators, emulators and apps approved Macs run there, below Macs using this
8657
+ Mac. Stop asks for confirmation, ends the session and deletes or parks
8428
8658
  its device on that Mac. Parked sessions remain listed without Stop. The list
8429
8659
  refreshes every five seconds and stays hidden when the local server does not
8430
8660
  support it.
@@ -8559,10 +8789,12 @@ preferring the one that already holds this repository, then the least loaded,
8559
8789
  and moves to the next one in that order when a machine that offered fails the
8560
8790
  sync or refuses to start the build.
8561
8791
  For iOS the machine's Xcode and simulator SDK must match, and it needs an
8562
- iPhone simulator on the target runtime. Its CocoaPods must match too, unless
8792
+ iPhone simulator on the target runtime. Its project-selected CocoaPods must match too, unless
8563
8793
  the app's Gemfile.lock pins CocoaPods: both Macs then run that version through
8564
8794
  bundler, so the machine needs only Bundler on its stim-server PATH and
8565
- installs the pinned gems itself on the first build. For Android its JDK major
8795
+ installs the pinned gems itself on the first build. The comparison selects
8796
+ the app's .ruby-version when installed, with pod install's UTF-8 locale defaults.
8797
+ For Android its JDK major
8566
8798
  version must match, and its Android SDK must hold the NDK, build-tools and
8567
8799
  compile platform that the project's React Native version names in
8568
8800
  gradle/libs.versions.toml; Gradle and AGP come from the synced project. When
@@ -8621,7 +8853,8 @@ login-shell environment would otherwise replace.
8621
8853
  \`stim-server service update --release <version>\` there moves the service to
8622
8854
  that exact stim-server release from the public npm registry once npm verifies
8623
8855
  its integrity and registry signatures; \`--from <dir>\` installs the packed
8624
- packages of a checkout instead. It waits for offloaded builds and hosted
8856
+ packages of a checkout instead, with the same signature check on each npm
8857
+ dependency but none for Stim's own packages. It waits for offloaded builds and hosted
8625
8858
  sessions to finish, restarts the job, and switches back when the new server
8626
8859
  exits or does not answer within 90 seconds; \`stim-server service rollback\` returns
8627
8860
  to the previous server. A client Mac approved for builds or device hosting
@@ -9152,7 +9385,14 @@ macos.arguments is an array of arguments passed directly to the executable:
9152
9385
 
9153
9386
  The plist must contain CFBundleIdentifier and CFBundleExecutable matching the
9154
9387
  product. Use a development plist without shared URL schemes or an update feed.
9155
- Stim gives the copied bundle a workspace-specific identifier. SwiftPM resource
9388
+ Stim gives the copied bundle a workspace-specific identifier and name: it sets
9389
+ CFBundleDisplayName and CFBundleName on the copy to "<product> \u00b7 <label>",
9390
+ where label is the owned-device label (worktree and app directory names, cleaned
9391
+ to letters, digits, . _ -; at most 24 characters, cut with an ellipsis). The Dock,
9392
+ Cmd-Tab and lsappinfo show it, so several runs tell apart; the process name in
9393
+ System Events stays the executable. The project plist and the app's window titles
9394
+ are untouched. The name is displayName in the launch payload and status. Hosted
9395
+ (--remote) and offloaded builds get the same name. SwiftPM resource
9156
9396
  bundles and frameworks in the reported build directory are copied into it;
9157
9397
  macos.resources maps destinations under Contents/Resources to file or directory
9158
9398
  sources relative to the Swift Package directory, for example:
@@ -9194,8 +9434,9 @@ platform "macos": phase prepare, compile, install, then launch, and during
9194
9434
  compile detail.unit "steps" with SwiftPM's [done / total] counts (fetching and
9195
9435
  planning are detail.step "configure"). It has no cache lookup, so outcome and
9196
9436
  plannedPhases are null; finished runs and their phase times are in
9197
- environments[].builds.macos. An offloaded build reports the worker's steps the
9198
- same way.
9437
+ environments[].builds.macos, where phases includes launch once the launch step finishes
9438
+ and compileSteps is SwiftPM's step total. An offloaded build reports the worker's
9439
+ steps the same way.
9199
9440
  Runtime stdout and stderr become client records; build output becomes build
9200
9441
  records, all with platform "macos". Unexpected app exits are error records.
9201
9442
  Stim runs the app with NSUnbufferedIO=YES, so Swift print output arrives per
@@ -9649,7 +9890,7 @@ const TUTORIAL_RESTART_PROMPT = "Restart the Stim tutorial.";
9649
9890
  const TUTORIAL_STEPS = [
9650
9891
  {
9651
9892
  id: "begin",
9652
- title: "Create the tutorial",
9893
+ title: "Create the Tutorial",
9653
9894
  who: "agent",
9654
9895
  optional: false,
9655
9896
  prompt: TUTORIAL_PROMPTS.begin,
@@ -9678,7 +9919,7 @@ const TUTORIAL_STEPS = [
9678
9919
  },
9679
9920
  {
9680
9921
  id: "sidebar",
9681
- title: "Workspace in sidebar",
9922
+ title: "Workspace in Sidebar",
9682
9923
  who: "you",
9683
9924
  optional: false,
9684
9925
  prompt: null,
@@ -9687,7 +9928,7 @@ const TUTORIAL_STEPS = [
9687
9928
  },
9688
9929
  {
9689
9930
  id: "build",
9690
- title: "First iOS build",
9931
+ title: "First iOS Build",
9691
9932
  who: "you",
9692
9933
  optional: false,
9693
9934
  prompt: null,
@@ -9705,7 +9946,7 @@ const TUTORIAL_STEPS = [
9705
9946
  },
9706
9947
  {
9707
9948
  id: "rebuild",
9708
- title: "Rebuild from cache",
9949
+ title: "Rebuild from Cache",
9709
9950
  who: "agent",
9710
9951
  optional: false,
9711
9952
  prompt: TUTORIAL_PROMPTS.rebuild,
@@ -9718,7 +9959,7 @@ const TUTORIAL_STEPS = [
9718
9959
  },
9719
9960
  {
9720
9961
  id: "device",
9721
- title: "Live view and control",
9962
+ title: "Live View and Control",
9722
9963
  who: "you",
9723
9964
  optional: false,
9724
9965
  prompt: null,
@@ -9727,7 +9968,7 @@ const TUTORIAL_STEPS = [
9727
9968
  },
9728
9969
  {
9729
9970
  id: "logs",
9730
- title: "App logs",
9971
+ title: "App Logs",
9731
9972
  who: "you",
9732
9973
  optional: false,
9733
9974
  prompt: null,
@@ -9736,7 +9977,7 @@ const TUTORIAL_STEPS = [
9736
9977
  },
9737
9978
  {
9738
9979
  id: "agent",
9739
- title: "Agent actions and replay",
9980
+ title: "Agent Actions and Replay",
9740
9981
  who: "agent",
9741
9982
  optional: false,
9742
9983
  prompt: TUTORIAL_PROMPTS.agent,
@@ -9775,7 +10016,7 @@ const TUTORIAL_STEPS = [
9775
10016
  },
9776
10017
  {
9777
10018
  id: "phone",
9778
- title: "Watch on your phone",
10019
+ title: "Watch on Your Phone",
9779
10020
  who: "you",
9780
10021
  optional: true,
9781
10022
  prompt: null,
@@ -9784,7 +10025,7 @@ const TUTORIAL_STEPS = [
9784
10025
  },
9785
10026
  {
9786
10027
  id: "machine",
9787
- title: "Build on another Mac",
10028
+ title: "Build on Another Mac",
9788
10029
  who: "both",
9789
10030
  optional: true,
9790
10031
  prompt: TUTORIAL_PROMPTS.machine,
@@ -9793,7 +10034,7 @@ const TUTORIAL_STEPS = [
9793
10034
  },
9794
10035
  {
9795
10036
  id: "finish",
9796
- title: "Finish and archive",
10037
+ title: "Finish and Archive",
9797
10038
  who: "agent",
9798
10039
  optional: false,
9799
10040
  prompt: TUTORIAL_PROMPTS.finish,
@@ -9819,6 +10060,7 @@ function commands(id) {
9819
10060
  //#region src/guide/index.ts
9820
10061
  const TOPICS = {
9821
10062
  agent: agent_default,
10063
+ api: api_default,
9822
10064
  facts,
9823
10065
  metro: metro_default,
9824
10066
  ports: ports_default,
@@ -9966,7 +10208,7 @@ Remove the target-v1 evidence and dismiss-overlay lines before replay: replaying
9966
10208
  them can fail with REPLAY_DIVERGENCE on the recorded button identity.
9967
10209
  The scripts and screenshot stay in the tour worktree and are git-ignored.
9968
10210
 
9969
- PAUSE: end the turn. Point at Agent actions, or the agent log records just
10211
+ PAUSE: end the turn. Point at Agent Actions, or the agent log records just
9970
10212
  printed. Expect another error-button line after replay. When screen recording
9971
10213
  is enabled, the user can scrub Replay in Desktop. Give the next prompt:
9972
10214
  "${TUTORIAL_PROMPTS.refresh}".`
@@ -10063,7 +10305,7 @@ first failure. Read stderr and do not continue to later commands. Follow stim gu
10063
10305
  safety. Reuse only this version's tutorial app, never overwrite another folder.
10064
10306
  The creation block skips writes on reuse and checks the repository root.
10065
10307
 
10066
- Replace {base} and {tour} with absolute paths; {base} must end in stim-tutorial. For Agent actions, replace
10308
+ Replace {base} and {tour} with absolute paths; {base} must end in stim-tutorial. For Agent Actions, replace
10067
10309
  {stateDir} with agentDevice.stateDir from stim ios or stim status --json;
10068
10310
  when read -r iosUdid waits, type this workspace's ios.udid from that status.
10069
10311
  For the optional machine step,
@@ -10072,9 +10314,9 @@ replace {machine} with a machine you have already approved, or skip it.
10072
10314
  Look at the sidebar during warm and Build during the first build (about four
10073
10315
  minutes on a cold cache). Compare cacheHit and missReason on rebuild. At Live
10074
10316
  view and control, open the device viewer and tap Log an error; without Desktop
10075
- use the simulator. At App logs, try Crash me or Slow request if wanted. A JS
10317
+ use the simulator. At App Logs, try Crash me or Slow request if wanted. A JS
10076
10318
  crash shows a red box; the slow request is a local timer, not network capture.
10077
- At Watch on your phone, optionally open an already paired Stim phone to see
10319
+ At Watch on Your Phone, optionally open an already paired Stim phone to see
10078
10320
  the tour workspace; phone setup and machine approval stay with you.
10079
10321
  Use stim status, stim logs --errors, and stim stats without Desktop.
10080
10322