stim 1.14.0 → 1.16.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.
- package/README.md +2 -0
- package/dist/activity-BxwdD81S.mjs +609 -0
- package/dist/agent-device-usage-output-Dpih1Tfa.mjs +411 -0
- package/dist/{android-d6rGq1pA.mjs → android-B_NumVyQ.mjs} +169 -81
- package/dist/{android-LbPFRWVL.mjs → android-Ift4kVvt.mjs} +517 -413
- package/dist/android-cas-8U4DPqeR.mjs +167 -0
- package/dist/android-cas-compiler.mjs +2 -2
- package/dist/{android-XpvCrV7q.mjs → android-r2rBL8kV.mjs} +2 -2
- package/dist/{app-install-BpgSgkJj.mjs → app-install-CKWdbXIX.mjs} +128 -151
- package/dist/attempt-Bk2ouQVL.mjs +39 -0
- package/dist/budget-DjrURfQn.mjs +2700 -0
- package/dist/{build-plan-Dcr-3ie0.mjs → build-plan-DjeSb2Xl.mjs} +192 -653
- package/dist/build-slots-rUk77xqh.mjs +132 -0
- package/dist/{cache-manifest-C68QQ0ua.mjs → cache-manifest-DnUHBJaU.mjs} +2 -2
- package/dist/cache-manifest.mjs +1 -1
- package/dist/{chrome-BPCiC3ZX.mjs → chrome-BrcVWQyD.mjs} +1 -1
- package/dist/cli-BkYZ_6hy.mjs +51 -0
- package/dist/cli.mjs +6 -42
- package/dist/client-BTY686R6.mjs +1368 -0
- package/dist/collector-run.mjs +9 -195
- package/dist/{command-output-DVcpw6eE.mjs → command-output-DnJMdEY-.mjs} +1 -1
- package/dist/{config-lvc7d3C2.mjs → config-D0DbpZtM.mjs} +28 -29
- package/dist/{created-devices-CllH_8eb.mjs → created-devices-D5owjN4C.mjs} +2 -2
- package/dist/{dependency-state-DsT7aKeo.mjs → dependency-state-B6cMXZab.mjs} +2 -2
- package/dist/{deps-D9IcKqXS.mjs → deps-urbmfXig.mjs} +3 -3
- package/dist/{detached-entry-DJHoca49.mjs → detached-entry-CiTEgMR1.mjs} +1 -1
- package/dist/dev-client-CroAIjjj.mjs +43 -0
- package/dist/{device-CjuuAJjQ.mjs → device-DZQjF7H5.mjs} +8 -7
- package/dist/device-capacity-BHwxNBlu.mjs +136 -0
- package/dist/device-host-worker.d.mts +1 -0
- package/dist/device-host-worker.mjs +964 -0
- package/dist/device-ios-BadmeL2V.mjs +381 -0
- package/dist/{device-lease-5c3WrVRm.mjs → device-lease-D-hLqx1b.mjs} +43 -36
- package/dist/{device-pool-BiyDyPP-.mjs → device-lease-run-CqjShqWW.mjs} +6 -126
- package/dist/device-pool-CUaEWMMm.mjs +125 -0
- package/dist/{device-remote-D87zEzaq.mjs → device-remote-D5WVWqOq.mjs} +69 -163
- package/dist/{doctor-x9pnU9fq.mjs → doctor-Cimf-gGB.mjs} +241 -103
- package/dist/{error-diagnostics-C5HzcwCW.mjs → error-diagnostics-B9L4Ae0x.mjs} +15 -18
- package/dist/{exec-DK8KaVHK.mjs → exec-CaErVv7t.mjs} +5 -1
- package/dist/{gc-ko6mUAYD.mjs → gc-Bqy4bjEu.mjs} +599 -108
- package/dist/{android-cas-iZK3DOCr.mjs → gradle-BsO1MdsH.mjs} +8 -294
- package/dist/{guide-6R0VNFqI.mjs → guide-B_uyKTxW.mjs} +2511 -239
- package/dist/{guide-status-BZrFPIA_.mjs → guide-status-CjEZMAD4.mjs} +3 -3
- package/dist/hosted-client-a-WmqE2L.mjs +197 -0
- package/dist/hosted-ios-CIWelIqf.mjs +220 -0
- package/dist/hosted-logs-D5g041Q3.mjs +187 -0
- package/dist/hosted-macos-BSZyIb3y.mjs +135 -0
- package/dist/idle-DYVymG92.mjs +1099 -0
- package/dist/idle-shutdown-CTsDnAde.mjs +122 -0
- package/dist/in-use-DvBw0Kxo.mjs +130 -0
- package/dist/{ios-BwDPrnYQ.mjs → ios-BaInn3sW.mjs} +201 -10
- package/dist/ios-Cr1h5gnO.mjs +189 -0
- package/dist/{ios-P1f5nm2n.mjs → ios-aq3QoGI3.mjs} +1159 -633
- package/dist/{ios-device-BiOsSVzd.mjs → ios-device-Ee7sDXfp.mjs} +1 -1
- package/dist/{ios-device-DGglU3E_.mjs → ios-device-FfCN_C8p.mjs} +12 -7
- package/dist/ios-state-DS5UpYTH.mjs +48 -0
- package/dist/{launch-verify-Deg4tB3Z.mjs → launch-verify-2DBRizjo.mjs} +5 -5
- package/dist/logs-CHEVK8zD.mjs +371 -0
- package/dist/machines-BIBUdhpj.mjs +265 -0
- package/dist/macos-CSYEAFmF.mjs +473 -0
- package/dist/macos-run.d.mts +5 -0
- package/dist/macos-run.mjs +2 -0
- package/dist/maintenance-run.d.mts +4 -0
- package/dist/maintenance-run.mjs +254 -0
- package/dist/{metro-CcEo8Fl8.mjs → metro-BeK1ZPz1.mjs} +20 -120
- package/dist/metro-gateway-BJ-rxQAB.mjs +84 -0
- package/dist/{named-ports-Blg8IxOh.mjs → named-ports-Bk_YXEmE.mjs} +6 -59
- package/dist/native-run-NaBrRFtQ.mjs +147 -0
- package/dist/{native-runtime-DjBHRg5m.mjs → native-runtime-DBzRnEKS.mjs} +9 -5
- package/dist/{ndjson-CDY9IkVC.mjs → ndjson-PclOIwVY.mjs} +2 -2
- package/dist/offload-worker.d.mts +122 -0
- package/dist/offload-worker.mjs +606 -0
- package/dist/{ownership-DyYDF5ww.mjs → ownership-DiA16AsN.mjs} +3 -2
- package/dist/ownership-hCkPzGmr.mjs +571 -0
- package/dist/{page-BHUzTDFt.mjs → page-QF_Ke10s.mjs} +7 -2
- package/dist/{ports-BguS83ex.mjs → ports-R2k4Knz3.mjs} +3 -3
- package/dist/prebuild-D2fsca_Z.mjs +213 -0
- package/dist/preview-DhVbvPAZ.mjs +318 -0
- package/dist/process-identity-BmRm1qV0.mjs +12 -0
- package/dist/{project-CWlvEaeN.mjs → project-Du91Ilvc.mjs} +74 -19
- package/dist/pull-request-pxnDCQzn.mjs +322 -0
- package/dist/pull-requests.d.mts +120 -0
- package/dist/pull-requests.mjs +2 -0
- package/dist/recordings-BIXOzVnr.mjs +37 -0
- package/dist/{reload-BiniwsSZ.mjs → reload-rM9jV1fz.mjs} +109 -24
- package/dist/{remote-cache-CcR6o6lI.mjs → remote-cache-C8-V13XN.mjs} +5 -5
- package/dist/run-f4vI6EnK.mjs +119 -0
- package/dist/{server-bare-DGfgyJ5J.mjs → server-bare-CpPGmnmO.mjs} +33 -15
- package/dist/server-expo-CVqO2c35.mjs +2 -0
- package/dist/{server-expo-BVuwQ65v.mjs → server-expo-MC0rHlOC.mjs} +8 -12
- package/dist/settings-BBpMcDbD.mjs +25 -0
- package/dist/{settings-B-fsC-Om.mjs → settings-ZkDzkzvB.mjs} +22 -10
- package/dist/settings-oTSkkNrM.mjs +1439 -0
- package/dist/settings.schema.json +845 -97
- package/dist/{simslim-DrDN4WuV.mjs → simslim-B8-7AoTI.mjs} +8 -132
- package/dist/{slot-launch-D5t8EiMf.mjs → slot-launch-B8VrHxn1.mjs} +11 -8
- package/dist/{spawn-claims-Drw_o-Fb.mjs → spawn-claims-D0XfSe8u.mjs} +1 -1
- package/dist/spawn-entry-jw19m8Nl.mjs +17 -0
- package/dist/stage-CzaiwFsI.mjs +143 -0
- package/dist/start-CX8Ig-U2.mjs +2 -0
- package/dist/{start-s4RXp1wy.mjs → start-ClDfwXa4.mjs} +116 -65
- package/dist/state-3DwSQIBK.mjs +41 -0
- package/dist/state-B4PQFXMW.d.mts +12 -0
- package/dist/{state-Dk9RVXI7.mjs → state-C1jB7Q8q.mjs} +70 -8
- package/dist/{state-DHqvsbqC.mjs → state-DcW6vOd_.mjs} +7 -4
- package/dist/stats-CEZ_i5je.mjs +77 -0
- package/dist/{status-BgAuxESg.mjs → status-BuvBFeII.mjs} +118 -20
- package/dist/{status-BsPAnfOI.mjs → status-Cs7VLg9D.mjs} +542 -283
- package/dist/status-DlKvVJBE.mjs +49 -0
- package/dist/stdout-reader-C8yjre1x.mjs +23 -0
- package/dist/stim-desktop-Cnz8CPnl.mjs +63 -0
- package/dist/stim-footprint.swift +32 -0
- package/dist/{stim-installations-CINXy_su.mjs → stim-installations-QMEI32v1.mjs} +1 -1
- package/dist/stop-B0Yxtmiy.mjs +48 -0
- package/dist/{stop-BzreOWJp.mjs → stop-Bffozsac.mjs} +83 -16
- package/dist/{stop-BWIF_6BK.mjs → stop-Dt_dM16A.mjs} +2 -2
- package/dist/stop-cause-CidxIqup.mjs +97 -0
- package/dist/supervisor-run.d.mts +43 -11
- package/dist/supervisor-run.mjs +363 -24
- package/dist/support-CAP8qDUF.mjs +316 -0
- package/dist/support-D4aYJniO.mjs +201 -0
- package/dist/teardown-CL6vDs2Y.mjs +727 -0
- package/dist/{xcode-Cp5KvK9x.mjs → toolchain-DkUflduf.mjs} +364 -14
- package/dist/trigger-Bemu8ytw.mjs +65 -0
- package/dist/trigger-CXts6qt1.mjs +2 -0
- package/dist/warm-progress-CpGv3Wb6.mjs +732 -0
- package/dist/{web-BsQs1WlO.mjs → web-DWUaMp8P.mjs} +37 -21
- package/dist/web-run.mjs +310 -13
- package/dist/workspace-process-lock-BL7Q0HCO.mjs +57 -0
- package/dist/workspace-state-B8-aJ1_5.mjs +371 -0
- package/dist/workspaces-BV4pdHFz.mjs +306 -0
- package/dist/{worktree-UrQWXSeb.mjs → worktree-K-xj9Db0.mjs} +625 -256
- package/dist/worktree-jnG8Iph5.mjs +2 -0
- package/package.json +10 -5
- package/shim/bundle-response.cjs +28 -12
- package/dist/activity-CYVDcrt3.mjs +0 -356
- package/dist/budget-CGDkhvv_.mjs +0 -1745
- package/dist/build-progress-DxKpwnaS.mjs +0 -247
- package/dist/idle-DXtx3RbX.mjs +0 -191
- package/dist/in-use-DlqBDKqY.mjs +0 -249
- package/dist/logs-Civ4gbBH.mjs +0 -486
- package/dist/merge-state-ytFQCeh4.mjs +0 -262
- package/dist/server-expo-DIHDhvGg.mjs +0 -2
- package/dist/settings-D61iNOsB.mjs +0 -629
- package/dist/start-Cetfvy8l.mjs +0 -2
- package/dist/stats-CFpFGlJV.mjs +0 -62
- package/dist/stats-CX1Z8Tca.mjs +0 -368
- package/dist/stim-desktop-CXt8-Z3J.mjs +0 -40
- package/dist/stop-cause-hjGBOZTG.mjs +0 -1231
- package/dist/worktree-BVrHsYZG.mjs +0 -752
- package/dist/worktree-CHWWvm5v.mjs +0 -2
- package/dist/{metro-store-DzeWkfqW.mjs → metro-store-CDDZHtPl.mjs} +1 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { t as RECENT_LAUNCH_MS } from "./status-
|
|
4
|
-
import { t as guideStatus } from "./guide-status-
|
|
5
|
-
import { SETTINGS_SCHEMA_URL } from "@stim-cli/core/state";
|
|
1
|
+
import { t as ANDROID_AVD_CONFIG_HELP } from "./settings-oTSkkNrM.mjs";
|
|
2
|
+
import { o as findProjectRoot$1 } from "./project-Du91Ilvc.mjs";
|
|
3
|
+
import { t as RECENT_LAUNCH_MS } from "./status-BuvBFeII.mjs";
|
|
4
|
+
import { t as guideStatus } from "./guide-status-CjEZMAD4.mjs";
|
|
5
|
+
import { SETTINGS, SETTINGS_SCHEMA_URL } from "@stim-cli/core/state";
|
|
6
6
|
import chalk from "chalk";
|
|
7
7
|
//#endregion
|
|
8
8
|
//#region src/guide/index.ts
|
|
@@ -18,6 +18,10 @@ parse a stable payload.
|
|
|
18
18
|
|
|
19
19
|
TWO WORKFLOWS
|
|
20
20
|
|
|
21
|
+
WORKTREE is the default. Take SINGLE CHECKOUT only when the user asks to work in
|
|
22
|
+
place, or the task depends on the user's uncommitted changes in the current
|
|
23
|
+
checkout; say why.
|
|
24
|
+
|
|
21
25
|
SINGLE CHECKOUT: work in place, on whatever branch the task needs, in one
|
|
22
26
|
directory. start, ios, android, logs, stop, and never a linked worktree. That
|
|
23
27
|
directory is your workspace, and no rule below about keeping the source checkout
|
|
@@ -39,14 +43,28 @@ reach multiple devices. A launch counts a bundle delivery only when it can
|
|
|
39
43
|
attribute it to its own device; read guide lifecycle options for the rule.
|
|
40
44
|
Use stop --slot <name> for one slot, or plain stop for the whole workspace.
|
|
41
45
|
|
|
46
|
+
HOSTED IOS
|
|
47
|
+
|
|
48
|
+
For ios --remote <machine>, read guide lifecycle hosted-ios. Stop before switching
|
|
49
|
+
between a local simulator and a hosting Mac. Hosted Debug requires the local
|
|
50
|
+
Metro supervisor; restart a missing or older one with stim stop; stim start.
|
|
51
|
+
It never falls back here; stop reconciles the recorded host before changing placement.
|
|
52
|
+
For hosted iOS, use agent-device <command> --remote-config <file> from ios.host.agent
|
|
53
|
+
in stim status --json. Open the installed app first; never use the host UDID locally.
|
|
54
|
+
See guide lifecycle hosted-ios for the agent policy and setup.
|
|
55
|
+
|
|
56
|
+
When driving a device yourself with agent-device, set AGENT_DEVICE_STATE_DIR
|
|
57
|
+
from agentDevice.stateDir in stim ios|android|macos --json or status --json.
|
|
58
|
+
Its sessions and logs are then cleaned with the workspace. Do not set
|
|
59
|
+
AGENT_DEVICE_IOS_RUNNER_DERIVED_PATH or AGENT_DEVICE_CLAIMS_DIR; the runner
|
|
60
|
+
build cache and device claims stay shared. Read stim guide logs for examples.
|
|
61
|
+
|
|
42
62
|
NORMAL WORKFLOW
|
|
43
63
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
copies missing ignored paths from the source checkout, including eligible .env
|
|
49
|
-
and local configuration files. It preserves the branch, tracked files, and
|
|
64
|
+
Create a linked worktree with Git and warm its ignored state. If a harness
|
|
65
|
+
already created this linked worktree, run stim worktree warm here instead of
|
|
66
|
+
creating another one. It copies missing ignored paths from the source checkout,
|
|
67
|
+
including eligible .env and local configuration files. It preserves the branch, tracked files, and
|
|
50
68
|
every existing destination entry; existing ignored directories are skipped
|
|
51
69
|
whole, not filled in. Add --refresh to fast-forward the source checkout and
|
|
52
70
|
install what moved there before the copy; it refuses a source checkout with local
|
|
@@ -76,7 +94,8 @@ other apps or other workspaces' devices.
|
|
|
76
94
|
|
|
77
95
|
stim doctor --platform ios # or: --platform android
|
|
78
96
|
|
|
79
|
-
# Skip Git creation if the harness already created this
|
|
97
|
+
# Worktree workflow. Skip Git creation if the harness already created this
|
|
98
|
+
# linked worktree.
|
|
80
99
|
git worktree add -b <branch> <worktree-path> HEAD
|
|
81
100
|
cd <worktree-path>
|
|
82
101
|
stim worktree warm
|
|
@@ -84,6 +103,12 @@ other apps or other workspaces' devices.
|
|
|
84
103
|
stim start
|
|
85
104
|
stim ios # or: stim android, or: stim web
|
|
86
105
|
|
|
106
|
+
When Stim Desktop is installed, worktree warm, start, ios, android and web
|
|
107
|
+
print "Open in Stim Desktop: <link>" on stderr (links.desktop in JSON). The
|
|
108
|
+
first time one prints it in a workspace, tell the user once: "You can watch
|
|
109
|
+
this workspace in Stim Desktop: <link>". Do not repeat it after later
|
|
110
|
+
commands. When no command prints it, do not mention Stim Desktop.
|
|
111
|
+
|
|
87
112
|
For the web target, stim web opens the page in a Stim-owned headless Chrome
|
|
88
113
|
and captures its console and errors in stim logs. It never starts a web
|
|
89
114
|
server: start the dev server first, then run stim web. For Expo web the dev
|
|
@@ -105,6 +130,12 @@ To learn whether the next build is a cache hit and how long it should take,
|
|
|
105
130
|
without building, run stim ios --plan (or stim android --plan); see stim guide
|
|
106
131
|
lifecycle builds.
|
|
107
132
|
|
|
133
|
+
Agents never set STIM_ACCESS_TICKET, run stim-server setup, edit offload.*
|
|
134
|
+
or hosting.* for the user, or approve requests. Read stim guide settings for
|
|
135
|
+
Desktop access tickets and person-owned approval.
|
|
136
|
+
Read stim guide settings to select a build machine. Keep a requested named
|
|
137
|
+
placement when recovering STIM_OFFLOAD_REFUSED: report its reason and remedy.
|
|
138
|
+
|
|
108
139
|
Read stim guide lifecycle concurrency when a build waits on another workspace
|
|
109
140
|
or a build call times out. A native build can outlive a shell timeout; if the
|
|
110
141
|
tool call timed out, retry the same command and follow its printed remedy if
|
|
@@ -128,6 +159,8 @@ waiting times out.
|
|
|
128
159
|
|
|
129
160
|
RULES DURING THE LOOP
|
|
130
161
|
|
|
162
|
+
- Agents never run stim-server setup and never approve access requests.
|
|
163
|
+
A person on the worker approves builds and device hosting (guide settings).
|
|
131
164
|
- Run Stim from the app directory: the one whose package.json depends on
|
|
132
165
|
react-native or expo. Anywhere else -- a monorepo root, a tools package --
|
|
133
166
|
start, ios and android refuse with STIM_NO_PROJECT naming that package.json,
|
|
@@ -197,10 +230,11 @@ the stim-<label> (<model> <runtime>) name. Never point Stim at a user-created
|
|
|
197
230
|
emulator or simulator.
|
|
198
231
|
|
|
199
232
|
worktree remove parks the workspace's simulator or emulator for later adoption.
|
|
200
|
-
|
|
233
|
+
During owned-device teardown, Stim best-effort closes local agent-device sessions
|
|
201
234
|
on that exact device; failures warn and teardown continues (guide cleanup).
|
|
202
|
-
A parked device is Stim-owned: never delete one by hand. gc --delete clears
|
|
203
|
-
entries and keeps failures;
|
|
235
|
+
A parked device is Stim-owned: never delete or erase one by hand. gc --delete clears
|
|
236
|
+
verified entries and keeps failures; gc --cache parked --delete erases them and keeps
|
|
237
|
+
them parked; see guide lifecycle pool. First launch on a
|
|
204
238
|
physical iPhone can need the one-time taps named by the remedy.
|
|
205
239
|
|
|
206
240
|
stim android --device [serial] and stim ios --device [udid] install on a
|
|
@@ -298,8 +332,11 @@ Read the matching guide before acting in these situations:
|
|
|
298
332
|
| Parallel iOS, simulator stall, or memory pressure | stim guide lifecycle simslim |
|
|
299
333
|
| Android boot timeout | stim guide errors STIM_NO_DEVICE |
|
|
300
334
|
| gc or orphaned resources | stim guide cleanup gc |
|
|
335
|
+
| watchman or Gradle daemon memory | stim guide cleanup memory |
|
|
301
336
|
| worktree remove refusal or --force | stim guide errors remove |
|
|
302
337
|
| Cleanup failure or unverified cleanup ownership | stim guide errors teardown |
|
|
338
|
+
| Swift Package macOS development | stim guide macos |
|
|
339
|
+
| macOS app on another Mac (stim macos --remote) | stim guide macos |
|
|
303
340
|
| Unfamiliar state or JSON field | stim guide facts payloads |
|
|
304
341
|
| Refusal without a code | stim guide errors |
|
|
305
342
|
|
|
@@ -328,12 +365,14 @@ FULL TOPIC LIST
|
|
|
328
365
|
stim guide lifecycle release # Release configurations and ...Release variants
|
|
329
366
|
stim guide facts # the --json payloads
|
|
330
367
|
stim guide facts devmenu # the Expo dev menu or Tools button over the app
|
|
368
|
+
stim guide macos # Swift Package Debug apps, logs, local preview, --remote on another Mac
|
|
331
369
|
stim guide web # stim web: owned Chrome, page logs, launched, teardown
|
|
332
370
|
stim guide ports # named ports for web and API servers
|
|
333
371
|
stim guide metro # supervisor, custom Metro, tunnels, and remote devices
|
|
334
372
|
stim guide logs # filters, record shape, and capture limits
|
|
335
373
|
stim guide cleanup # what reclaims a device, and what deletes
|
|
336
374
|
stim guide cleanup collector # an unproven collector pid; why the app on a phone closed
|
|
375
|
+
stim guide cleanup memory # watchman and Gradle daemon memory; gc --cache watchman
|
|
337
376
|
stim guide settings # configuration files and supported keys`
|
|
338
377
|
},
|
|
339
378
|
facts: {
|
|
@@ -352,6 +391,15 @@ stdout for \`--json\`. Every other line goes to stderr, so it is always safe
|
|
|
352
391
|
to pipe. \`logs --json\` is the one exception: it is NDJSON, one record per
|
|
353
392
|
line by design (see \`guide logs\`), not this single-payload contract.
|
|
354
393
|
|
|
394
|
+
status adds archived (newest first, bounded by archive.maxCount) and
|
|
395
|
+
archivedUsage { count, bytes, byKind: { logs, recordings, agentActions, record } }.
|
|
396
|
+
Each archive carries id, projectRoot, project, workspace, worktree facts,
|
|
397
|
+
removedAt, removedBy, lastUsedAt, builds, agents, bytes, expires and version.
|
|
398
|
+
replacedBy is the live environment path when its canonical root matches.
|
|
399
|
+
Older producers may omit both fields.
|
|
400
|
+
Plain status prints Archived: <count> workspace(s), <size> when count is positive;
|
|
401
|
+
status --watch reuses archive reads until archive changes and emits no duplicates.
|
|
402
|
+
|
|
355
403
|
status's unprovisionedWorktrees lists the linked worktrees with no Stim
|
|
356
404
|
environment in every repository with a registered environment, plus the
|
|
357
405
|
repository status runs from. A worktree counts as having an
|
|
@@ -360,9 +408,13 @@ environment when one is registered at it or inside it. Each entry is
|
|
|
360
408
|
directory of a bare repository. status lists worktrees from git's own worktree
|
|
361
409
|
records, and on macOS runs no git in a worktree with no environment under a
|
|
362
410
|
protected folder such as ~/Documents, so listing it raises no privacy prompt.
|
|
363
|
-
\`worktree warm\`
|
|
364
|
-
|
|
365
|
-
|
|
411
|
+
\`worktree warm\` registers the app it prepares, before it copies: the
|
|
412
|
+
nearest React Native or Expo app above where it runs when that is below the
|
|
413
|
+
worktree root; else the one app the source checkout has registered, at the
|
|
414
|
+
same path in this worktree; else the worktree root when it is the app. A warm
|
|
415
|
+
that finds none registers nothing and says so on stderr, and \`start\`, \`ios\`,
|
|
416
|
+
\`android\` and \`doctor\` register the app later. An environment's worktree is the linked worktree it is
|
|
417
|
+
registered at or inside.
|
|
366
418
|
|
|
367
419
|
Each worktree entry, in unprovisionedWorktrees and in an environment's
|
|
368
420
|
worktree, carries git:
|
|
@@ -392,6 +444,46 @@ change, and for at most 60 s, so a file edit, creation or deletion that is
|
|
|
392
444
|
not staged can take up to a minute to show. Plain status prints "git: 2 changed, 1 untracked, ahead 3" under
|
|
393
445
|
each environment, and the same after each worktree with no environment.
|
|
394
446
|
|
|
447
|
+
The paired phone's Work sheet can open changed and untracked files when the
|
|
448
|
+
server advertises workspace-diff. File lists and selected patches load on
|
|
449
|
+
demand, not through status. The read-only preview is capped at 200 files per
|
|
450
|
+
list and 256 KiB per patch response; binary and unavailable files are labeled. Active Git clean/process filters
|
|
451
|
+
(including Git LFS) refuse without running those commands.
|
|
452
|
+
|
|
453
|
+
A worktree entry also carries pullRequest, the GitHub pull request of its
|
|
454
|
+
branch and HEAD, once \`status --watch\` has looked it up:
|
|
455
|
+
|
|
456
|
+
pullRequest { number, url, title, state, checks, reviewDecision,
|
|
457
|
+
checkedAt }, null when GitHub has none for the branch and
|
|
458
|
+
HEAD, absent when never looked up: gh missing or signed
|
|
459
|
+
out, no GitHub remote, or status could not read the
|
|
460
|
+
worktree's git. A later gh failure keeps the last answer.
|
|
461
|
+
state "open", "draft", "merged" or "closed"
|
|
462
|
+
checks { passing, failing, pending } counts of the head commit's
|
|
463
|
+
check runs and commit statuses, or null when it has none
|
|
464
|
+
reviewDecision "approved", "changes-requested", "review-required" or null
|
|
465
|
+
checkedAt when Stim last asked GitHub
|
|
466
|
+
|
|
467
|
+
A worktree entry with git also carries gitChip, the git chip Stim Desktop and
|
|
468
|
+
the phone app show for it:
|
|
469
|
+
|
|
470
|
+
gitChip { parts, ci }
|
|
471
|
+
parts in order: { kind: "arrows", ahead, behind } when either is above
|
|
472
|
+
0; { kind: "changed", count } of changed plus untracked entries;
|
|
473
|
+
{ kind: "merged", into } when mergedInto is set and the pull
|
|
474
|
+
request is not merged; { kind: "no-upstream" } when the branch is
|
|
475
|
+
not merged and upstream is null
|
|
476
|
+
ci "failing" when a check fails, else "pending" when one is pending,
|
|
477
|
+
else "passing" when one passes; null without a pull request or
|
|
478
|
+
checks
|
|
479
|
+
|
|
480
|
+
\`status --watch\` asks GitHub off its refresh path, through the same gh api
|
|
481
|
+
graphql lookup gc uses, one call per repository, for a worktree whose lookup
|
|
482
|
+
is over 5 minutes old or whose branch or HEAD moved. It runs git in the
|
|
483
|
+
repository, never in the worktree. It caches each answer
|
|
484
|
+
under $STIM_HOME/pull-requests, which one-shot status only reads, and never
|
|
485
|
+
warns when gh is unavailable.
|
|
486
|
+
|
|
395
487
|
status's remoteDevices lists each environment's recorded EAS Simulator
|
|
396
488
|
session. The session is billable while it runs, and it makes the environment
|
|
397
489
|
live. status reads only local records; it does not ask EAS whether the session
|
|
@@ -409,10 +501,37 @@ is still running.
|
|
|
409
501
|
Stim created the session, or null
|
|
410
502
|
|
|
411
503
|
Plain status prints one "remote <platform>: EAS session <id> billable" line
|
|
412
|
-
per session, with the preview URL
|
|
504
|
+
per session, with the preview URL.
|
|
505
|
+
|
|
506
|
+
status's physicalDevices lists each physical phone or tablet the environment
|
|
507
|
+
holds an unexpired lease on, from \`ios --device\`, \`android --device\` or
|
|
508
|
+
\`device lock\`, in every slot. A run lease ends with its run, so a phone
|
|
509
|
+
stays listed after the run only while \`device lock\` holds it. A
|
|
510
|
+
simulator or emulator lease is not listed. The field is absent when the
|
|
511
|
+
workspace leases no physical device.
|
|
512
|
+
|
|
513
|
+
physicalDevices [{ platform, slot, id, name, model, owned, physical,
|
|
514
|
+
connection, lease }]
|
|
515
|
+
id the UDID or adb serial
|
|
516
|
+
name the device's own name, else the name the lease recorded
|
|
517
|
+
model devicectl's marketing name ("iPhone 12 Pro"), null when
|
|
518
|
+
unread, or the Android model the lease recorded
|
|
519
|
+
owned always false: Stim uses a physical device, never owns it
|
|
520
|
+
physical always true
|
|
521
|
+
connection "connected" devicectl can reach the phone, or adb lists
|
|
522
|
+
the serial as device
|
|
523
|
+
"disconnected" the tool answered without it
|
|
524
|
+
"unknown" the tool could not be read in time
|
|
525
|
+
status reuses one devicectl or adb listing for 30 s
|
|
526
|
+
under \`status --watch\`
|
|
527
|
+
lease { holder, kind, grantedAt, expiresAt }: holder is the
|
|
528
|
+
workspace path, kind "declared" (device lock) or "run"
|
|
529
|
+
|
|
530
|
+
Plain status prints "ios: Old iPhone (physical, iPhone 12 Pro) connected --
|
|
531
|
+
leased until <time>" for each one.`,
|
|
413
532
|
sections: {
|
|
414
533
|
payloads: {
|
|
415
|
-
summary: "every field of the start, ios, android, web and reload payloads, the error contract, the device rules",
|
|
534
|
+
summary: "every field of the start, ios, android, macos, web and reload payloads, the error contract, the device rules",
|
|
416
535
|
body: () => ` stim start --json
|
|
417
536
|
|
|
418
537
|
port the Metro port RESERVED for this workspace
|
|
@@ -420,7 +539,21 @@ per session, with the preview URL.`,
|
|
|
420
539
|
already answering that Stim did not start
|
|
421
540
|
mode "bare-inproc" | "expo-child" | null (see \`guide metro\`)
|
|
422
541
|
logsDir where the NDJSON timeline is written
|
|
542
|
+
agentDevice { stateDir }: absolute workspace agent-device state path;
|
|
543
|
+
set AGENT_DEVICE_STATE_DIR to it (see guide logs)
|
|
423
544
|
alreadyRunning true when nothing needed starting
|
|
545
|
+
links { desktop }: a stim-desktop://workspace?path=<workspace>
|
|
546
|
+
link that shows this workspace in Stim Desktop. Absent when
|
|
547
|
+
no app on this Mac opens stim-desktop links. Plain output
|
|
548
|
+
prints it on stderr as "Open in Stim Desktop: <link>"; so
|
|
549
|
+
does worktree warm, which has no JSON payload
|
|
550
|
+
|
|
551
|
+
stim macos --json
|
|
552
|
+
|
|
553
|
+
platform "macos"
|
|
554
|
+
product, launchId, build, host? the launch record (see guide macos)
|
|
555
|
+
agentDevice { stateDir }: absolute workspace agent-device state path
|
|
556
|
+
for local or hosted launches with a local workspace
|
|
424
557
|
|
|
425
558
|
stim ios --json
|
|
426
559
|
|
|
@@ -471,7 +604,12 @@ per session, with the preview URL.`,
|
|
|
471
604
|
scheme the explicit shared Xcode scheme selected by --scheme;
|
|
472
605
|
absent for automatic selection; not the app URL scheme
|
|
473
606
|
cacheKey the shared-build-cache key derived from it (the
|
|
474
|
-
configuration is part of it: -release-sim vs
|
|
607
|
+
configuration is part of it: -release-sim vs
|
|
608
|
+
-debug-sim-arm64). A single-architecture simulator build
|
|
609
|
+
ends in its architecture (-arm64 or -x86-64): a local Debug
|
|
610
|
+
build compiles this Mac's, a --remote build the remote
|
|
611
|
+
host's. A local non-Debug simulator build compiles every
|
|
612
|
+
architecture the project lists and has no suffix
|
|
475
613
|
With --eas-profile, fingerprint is computed by EAS CLI using the selected
|
|
476
614
|
profile/environment, and cacheKey identifies the EAS project and build ID
|
|
477
615
|
separately from local native builds. cacheHit is "remote" for the EAS
|
|
@@ -490,6 +628,15 @@ per session, with the preview URL.`,
|
|
|
490
628
|
the device; it is a page, not a deep link.
|
|
491
629
|
cacheSkipped true only when --no-build-cache was passed: "nothing was
|
|
492
630
|
looked up", which is a different fact from "nothing was found"
|
|
631
|
+
buildMachine selected auto, local or machine name, including cache hits
|
|
632
|
+
builtOn here or machine name when a build ran; absent otherwise
|
|
633
|
+
offloadedTo only on an app a build machine compiled: its
|
|
634
|
+
offload.machines entry (see \`guide settings\`). cacheHit is
|
|
635
|
+
false for it. The Android payload carries it too
|
|
636
|
+
offloadFallback only on an app compiled here after the run considered
|
|
637
|
+
offloading it: why it built here -- one reason per
|
|
638
|
+
machine when none took the build, or why the offload
|
|
639
|
+
stopped. The Android payload carries it too
|
|
493
640
|
compilationCache
|
|
494
641
|
Xcode compilation-cache activity for a compiled iOS app:
|
|
495
642
|
{ status: "reported", hits, cacheableTasks, hitRatePercent }
|
|
@@ -561,6 +708,7 @@ per session, with the preview URL.`,
|
|
|
561
708
|
launch with no process id is "unverified", and
|
|
562
709
|
\`stim logs --errors\` has the device log that says why
|
|
563
710
|
logs { dir }
|
|
711
|
+
agentDevice { stateDir }: absolute workspace agent-device state path
|
|
564
712
|
durationMs wall time for the whole run
|
|
565
713
|
|
|
566
714
|
stim android --json
|
|
@@ -588,7 +736,8 @@ per session, with the preview URL.`,
|
|
|
588
736
|
hw.device.name. Null where systemImage is null, and on an
|
|
589
737
|
AVD of the old generic profile, which has no hw.device.name
|
|
590
738
|
fingerprint / cacheKey / cacheHit / cacheSkipped / waitedForBuild /
|
|
591
|
-
appPath / installSkipped / launched
|
|
739
|
+
appPath / installSkipped / launched / buildMachine / builtOn /
|
|
740
|
+
offloadedTo / offloadFallback
|
|
592
741
|
as above -- cacheKey keys on the VARIANT here
|
|
593
742
|
(<fingerprint>-productionrelease-sim). A Debug artifact for
|
|
594
743
|
a proven target ABI also ends in that ABI
|
|
@@ -630,8 +779,10 @@ per session, with the preview URL.`,
|
|
|
630
779
|
through ccache -- ccache absent from PATH, a project that
|
|
631
780
|
sets its own CMake compiler launcher, or a Gradle run whose
|
|
632
781
|
native work was all up to date. None of the three is an
|
|
633
|
-
error, and this field is separate from cacheHit
|
|
782
|
+
error, and this field is separate from cacheHit. On an
|
|
783
|
+
offloaded APK it is the build machine's ccache
|
|
634
784
|
logs the workspace log directory
|
|
785
|
+
agentDevice { stateDir }: absolute workspace agent-device state path
|
|
635
786
|
durationMs wall time for the whole run
|
|
636
787
|
reclaimed present only when the run was over its disk or memory
|
|
637
788
|
budget and reclaimed first (\`start\`, \`ios\` and
|
|
@@ -647,6 +798,8 @@ per session, with the preview URL.`,
|
|
|
647
798
|
{ started: true, reason }
|
|
648
799
|
reason is "not running" or "stopped (idle)" when the
|
|
649
800
|
supervisor had stopped it after metro.idleStopMinutes
|
|
801
|
+
links as in start, with &platform= and, for a named slot, &slot=,
|
|
802
|
+
so Stim Desktop can focus that device
|
|
650
803
|
|
|
651
804
|
stim reload [ios|android|web] --json
|
|
652
805
|
|
|
@@ -659,7 +812,8 @@ per session, with the preview URL.`,
|
|
|
659
812
|
deviceId the exact owned simulator UDID or emulator serial targeted;
|
|
660
813
|
for web, the owned Chrome's DevTools endpoint
|
|
661
814
|
deviceName the owned simulator or AVD name; for web, the Chrome version
|
|
662
|
-
appId the live bundle id or Android package; for web, the
|
|
815
|
+
appId the live bundle id or Android package; for web, the URL the
|
|
816
|
+
owned page was on when Stim sent the reload
|
|
663
817
|
metroPort the workspace's verified Metro port; for web, the reserved
|
|
664
818
|
Metro port or null
|
|
665
819
|
strategy how the reload was addressed.
|
|
@@ -677,6 +831,9 @@ per session, with the preview URL.`,
|
|
|
677
831
|
broadcast. Greater than 1 means several devices are running
|
|
678
832
|
this app on that Metro and the request addressed all of
|
|
679
833
|
them, not only deviceId. Completion is not observed
|
|
834
|
+
reverseRestored the Android serials whose adb reverse for metroPort was
|
|
835
|
+
missing and that Stim re-applied before reloading; [] when
|
|
836
|
+
none was, and always [] for iOS and web
|
|
680
837
|
|
|
681
838
|
stim web --json
|
|
682
839
|
|
|
@@ -692,6 +849,8 @@ per session, with the preview URL.`,
|
|
|
692
849
|
profile the Stim-owned Chrome user data directory
|
|
693
850
|
cdpEndpoint http://127.0.0.1:<port>, the reserved DevTools endpoint, or
|
|
694
851
|
null when not running
|
|
852
|
+
targetId the DevTools target id of the owned page, or null when not
|
|
853
|
+
running
|
|
695
854
|
reused true when the running Chrome navigated again instead of
|
|
696
855
|
starting: same --headed, viewport and certificate options
|
|
697
856
|
launched true | "bundling" | "unverified" (see \`guide web\`)
|
|
@@ -699,6 +858,7 @@ per session, with the preview URL.`,
|
|
|
699
858
|
that does not use {port:metro}
|
|
700
859
|
logs { dir }: web.ndjson holds the page records
|
|
701
860
|
durationMs wall time of the run
|
|
861
|
+
links as in start, with &platform=web
|
|
702
862
|
|
|
703
863
|
stim doctor --json
|
|
704
864
|
|
|
@@ -719,8 +879,42 @@ per session, with the preview URL.`,
|
|
|
719
879
|
null when off; plan lists what the next start, ios or
|
|
720
880
|
android would reclaim, in the \`reclaimed\` shape without
|
|
721
881
|
freedMb, and is empty while under budget
|
|
882
|
+
deviceHosts one { machine, state, dnsName?, deviceId?, requestedAt?, host? }
|
|
883
|
+
per hosting.machines entry. state is "approved", "pending",
|
|
884
|
+
"not-asked", "revoked", "node-changed", "not-on-tailnet",
|
|
885
|
+
"tailscale-off", "unreachable", "invalid",
|
|
886
|
+
"credentials-unavailable" or "busy". Tokens stay private.
|
|
887
|
+
Only --fix asks for access or forgets removed names.
|
|
888
|
+
An approved hosting machine does not imply build approval
|
|
889
|
+
or change ios/android placement; see \`guide settings\`.
|
|
890
|
+
host is { name, screenRecording, accessibility }, present
|
|
891
|
+
only for approved machines whose worker reports it.
|
|
892
|
+
buildMachines one { machine, state, dnsName?, deviceId?, requestedAt?,
|
|
893
|
+
offloadable?, reasons?, problems?, capacity?, host? } per
|
|
894
|
+
offload.machines entry; state is "approved", "pending", "not-asked",
|
|
895
|
+
"revoked" (revoked, or the request lapsed), "node-changed",
|
|
896
|
+
"not-on-tailnet", "tailscale-off", "unreachable" or
|
|
897
|
+
"invalid". host has the same shape and presence rule as in
|
|
898
|
+
deviceHosts. An approved machine also carries offloadable,
|
|
899
|
+
true when it would take this app's builds now (iOS
|
|
900
|
+
simulator unless --platform android, Android emulator when
|
|
901
|
+
--platform android or the app has android/ or uses Expo,
|
|
902
|
+
plus macOS SwiftPM when Package.swift exists and no platform
|
|
903
|
+
is selected on a Mac), and reasons, each reason it would not,
|
|
904
|
+
prefixed "iOS: ", "Android: " or "macOS: " when only one platform
|
|
905
|
+
has it, with a finding
|
|
906
|
+
per reason (code build-machine-<reason>: unreachable,
|
|
907
|
+
checkout, stim-build, arch, xcode, simulator-sdk, macos-sdk,
|
|
908
|
+
cocoapods, bundler, runtime, jdk, android-sdk, ndk,
|
|
909
|
+
build-tools, compile-sdk, disk or busy). problems lists the same
|
|
910
|
+
reasons as { code, reason } with that code. capacity
|
|
911
|
+
is the machine's offer: { running, max, diskFreeBytes,
|
|
912
|
+
minDiskFreeBytes, cpus?, loadPerCore?, builds?, maxBuilds?,
|
|
913
|
+
maxLoadPerCore?, declined? }; an older stim-server omits the
|
|
914
|
+
optional fields
|
|
722
915
|
findings the diagnostic findings; a lower resolved Stim is a
|
|
723
916
|
costs-time finding with a PATH or installation remedy
|
|
917
|
+
offload-candidate is a note after 3+ successful local cold builds in 7 days average over 3 min, with no offload.machines and an online tailnet Mac; open Stim Desktop Settings > Build machines > Add
|
|
724
918
|
|
|
725
919
|
ON FAILURE
|
|
726
920
|
\`start\`, \`ios\`, \`android\` and \`web\` all print the error contract instead,
|
|
@@ -741,7 +935,8 @@ ON FAILURE
|
|
|
741
935
|
lease, Ctrl-C with no build tool running) exits 130 with no payload.
|
|
742
936
|
|
|
743
937
|
\`stop --json\` prints { root, ok, supervisor, collectors, metro, device,
|
|
744
|
-
port, metroTunnel, releasedLeases }.
|
|
938
|
+
port, metroTunnel, releasedLeases, macos? }. macos is { status: stopped | failed,
|
|
939
|
+
reason? } when a macOS app is recorded. When it cannot end the \`ios\` or
|
|
745
940
|
\`android\` run holding the workspace, it prints the error contract with
|
|
746
941
|
root and ok: false instead, plus device.remote when it ended a recorded EAS
|
|
747
942
|
session first, and exits 1:
|
|
@@ -787,16 +982,50 @@ RULES
|
|
|
787
982
|
that is the URL the launcher hands to the check; on the
|
|
788
983
|
outer deep link it does nothing there. Android reads it on
|
|
789
984
|
either.
|
|
790
|
-
ON
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
985
|
+
ON AN OWNED SIMULATOR, Stim preapproves CoreSimulatorBridge
|
|
986
|
+
for the installed bundle id and every URL scheme its Info.plist
|
|
987
|
+
declares, except system and third-party SDK schemes, also for
|
|
988
|
+
apps without expo-dev-client. The "Open in <App>?" alert can
|
|
989
|
+
still appear for an unapproved scheme, on a physical device or
|
|
990
|
+
user-created simulator, or from another opener such as Safari.
|
|
991
|
+
After \`simctl openurl\` or \`agent-device open <url>\`, expect
|
|
992
|
+
it for such schemes; take a snapshot and tap Open.
|
|
993
|
+
Approvals persist until erasure; Stim records them and writes
|
|
994
|
+
only missing ones. Parking drops the record, so adoption
|
|
995
|
+
rewrites them once.
|
|
996
|
+
THE DEV MENU AND ITS BUTTON stay off a simulator two ways,
|
|
997
|
+
which the onboarding flag does NOT cover. Stim's own
|
|
998
|
+
\`simctl launch --initialUrl\` ends in
|
|
999
|
+
\`-EXDevMenuShowsAtLaunch 0 -EXDevMenuShowFloatingActionButton 0\`
|
|
1000
|
+
as trailing argv, the same mechanism the phone launch uses:
|
|
1001
|
+
NSUserDefaults reads the argument domain ahead of the
|
|
1002
|
+
persisted one, so Stim's own launch comes up on the app, not
|
|
1003
|
+
the menu, without writing to the simulator. Stim also writes
|
|
795
1004
|
EXDevMenuShowsAtLaunch=false and
|
|
796
|
-
EXDevMenuShowFloatingActionButton=false
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
1005
|
+
EXDevMenuShowFloatingActionButton=false to the app's
|
|
1006
|
+
persisted defaults, but only on a real install -- one that
|
|
1007
|
+
is not skipped as byte-identical -- or when its own launch
|
|
1008
|
+
falls back to \`simctl openurl\` (no console capture in flight),
|
|
1009
|
+
which carries no launch arguments at all and so writes them
|
|
1010
|
+
itself before opening the URL. Adoption clears the app's
|
|
1011
|
+
data by replacing its defaults with those two keys (one
|
|
1012
|
+
\`defaults import\`), so an install that adoption then skips
|
|
1013
|
+
as byte-identical keeps them too. Those persisted values
|
|
1014
|
+
cover launches Stim did not make: a home-screen tap, an
|
|
1015
|
+
agent relaunch, or a crash restart. The \`__expo_\` deep-link
|
|
1016
|
+
params below last one process and do not persist.
|
|
1017
|
+
When the project's installed expo-dev-launcher (found
|
|
1018
|
+
through expo-dev-client, no network) is 58.0.0 or later
|
|
1019
|
+
(expo/expo#49651), Stim's own deep link also carries
|
|
1020
|
+
\`disableFab=1&disableAutoLaunch=1\` on the outer link and
|
|
1021
|
+
on the project url, plus
|
|
1022
|
+
\`__expo_disable_fab=1&__expo_disable_auto_launch=1\` on the
|
|
1023
|
+
project url, which is the only url \`simctl launch
|
|
1024
|
+
--initialUrl\` hands the launcher and the one 58.0.7 reads
|
|
1025
|
+
the \`__expo_\` spellings from, for that launch only. So
|
|
1026
|
+
device automation opens on the app.
|
|
1027
|
+
The \`install dev client prepared (...)\` line appears
|
|
1028
|
+
only when one of those simulator writes ran. The
|
|
800
1029
|
unverified warning therefore leads with the picker, then
|
|
801
1030
|
prints the openurl
|
|
802
1031
|
retry. ON LOCAL ANDROID the same deep link also carries the
|
|
@@ -808,8 +1037,8 @@ RULES
|
|
|
808
1037
|
Remote Android opens only the URL, so that intent-extra
|
|
809
1038
|
suppression does not apply there.
|
|
810
1039
|
Every Stim deep link also carries an outer \`disableFab=1\`
|
|
811
|
-
query parameter. Versions with expo/expo#49651
|
|
812
|
-
|
|
1040
|
+
query parameter. Versions with expo/expo#49651 read it and
|
|
1041
|
+
hide the button; earlier versions ignore it. Stim does
|
|
813
1042
|
not rewrite expo-dev-menu's private SharedPreferences XML:
|
|
814
1043
|
that internal file is not a supported API, and changing it
|
|
815
1044
|
would persist over the user's own Tools-button setting. The
|
|
@@ -856,7 +1085,7 @@ RULES
|
|
|
856
1085
|
writes and the Local Network grant both survive an
|
|
857
1086
|
UPGRADE install. Android's intent extra prevents the menu's
|
|
858
1087
|
automatic launch; versions with expo/expo#49651 also honor
|
|
859
|
-
the
|
|
1088
|
+
the outer FAB flag in Stim's deep link.
|
|
860
1089
|
The phone's unverified remedy is also ROUTED, not a fixed
|
|
861
1090
|
list. When this launch's device records carry the Local
|
|
862
1091
|
Network path reason, the remedy leads with that evidence and
|
|
@@ -871,7 +1100,7 @@ RULES
|
|
|
871
1100
|
},
|
|
872
1101
|
gc: {
|
|
873
1102
|
summary: "the gc report payload: mode, sections, reasons, failures, results, inventory, and the gc refusals",
|
|
874
|
-
body: () => ` stim gc [--delete] [--older-than <days>] [--cache <name|all|workspaces>]
|
|
1103
|
+
body: () => ` stim gc [--delete] [--older-than <days>] [--cache <name|all|workspaces|recordings|parked>]
|
|
875
1104
|
[--worktrees] [--idle <duration>] --json
|
|
876
1105
|
|
|
877
1106
|
The report the text prints, as one payload. Show the user its sections
|
|
@@ -884,7 +1113,8 @@ RULES
|
|
|
884
1113
|
idle the --idle duration in milliseconds, or null
|
|
885
1114
|
cacheScope the --cache name, or null. When set, devices, project
|
|
886
1115
|
entries and locks were not inspected and their sections
|
|
887
|
-
are empty
|
|
1116
|
+
are empty, except that --cache parked fills
|
|
1117
|
+
parkedSimulators and parkedEmulators
|
|
888
1118
|
olderThan the --older-than days, or null
|
|
889
1119
|
worktreeSweep null without --worktrees; otherwise { olderThan, defaulted }:
|
|
890
1120
|
the idle days a linked worktree needs, and whether that is
|
|
@@ -898,10 +1128,13 @@ RULES
|
|
|
898
1128
|
run. status is "done", "kept" (left alone, detail says
|
|
899
1129
|
why) or "failed" (detail says why and what to retry).
|
|
900
1130
|
kind: device, parkedDevice, idleDevice, deviceRecord,
|
|
901
|
-
workspaceOutputs, workspaceDirectory, project,
|
|
902
|
-
buildSlot, deviceLease, easSession, worktree,
|
|
1131
|
+
workspaceOutputs, recording, workspaceDirectory, project,
|
|
1132
|
+
buildLock, buildSlot, deviceLease, easSession, worktree,
|
|
1133
|
+
cache.
|
|
903
1134
|
label is a device, path or cache name; id is the UDID,
|
|
904
|
-
AVD name or path behind it, or null
|
|
1135
|
+
AVD name or path behind it, or null. Under --cache
|
|
1136
|
+
parked, a "done" parkedDevice was erased and stays
|
|
1137
|
+
parked; otherwise it was deleted
|
|
905
1138
|
inventory null except on a dry run without --cache or --idle. Report
|
|
906
1139
|
only: gc never acts on it, even under --delete.
|
|
907
1140
|
{ devices, runtimes, systemImages, notices }
|
|
@@ -933,6 +1166,10 @@ RULES
|
|
|
933
1166
|
timing out or an AVD or system image folder Stim
|
|
934
1167
|
cannot read. A macOS privacy denial (EPERM) names the
|
|
935
1168
|
Privacy & Security setting to grant
|
|
1169
|
+
maintenance top-level { mode, pressure, actions, blocked,
|
|
1170
|
+
skips, note, invalid? }, the next report-only maintenance plan
|
|
1171
|
+
from live pressure and cached sizes. note says when no
|
|
1172
|
+
pass has run yet. No du runs for this preview.
|
|
936
1173
|
sections one array per report section, in the text order. Every key
|
|
937
1174
|
is present, empty when there is nothing to report:
|
|
938
1175
|
deadProjects { path }
|
|
@@ -955,10 +1192,12 @@ RULES
|
|
|
955
1192
|
recent-activity worktree becomes removable,
|
|
956
1193
|
else null. Without --worktrees, the source
|
|
957
1194
|
checkout and roots outside git are left out
|
|
958
|
-
parkedSimulators { udid, name, model, runtime, parkedAt,
|
|
959
|
-
listed }
|
|
960
|
-
at
|
|
961
|
-
|
|
1195
|
+
parkedSimulators { udid, name, model, runtime, parkedAt, app,
|
|
1196
|
+
bytes, listed } app is the bundle id recorded
|
|
1197
|
+
at park, else null; with --older-than, only
|
|
1198
|
+
those parked at least that long
|
|
1199
|
+
parkedEmulators { name, systemImage, deviceProfile, parkedAt, app,
|
|
1200
|
+
bytes, listed } app is the package name;
|
|
962
1201
|
likewise
|
|
963
1202
|
orphanedDevices { kind, id, name, bytes, directory }
|
|
964
1203
|
unverifiedDevices { kind, id, name, command } stim-* devices this
|
|
@@ -976,6 +1215,10 @@ RULES
|
|
|
976
1215
|
root, and browser profile paths whose
|
|
977
1216
|
directory is gone; --delete forgets the
|
|
978
1217
|
ledger entry only
|
|
1218
|
+
staleStatusCaches { kind: "disk-usage" | "pull-request", path,
|
|
1219
|
+
file, bytes } entries of $STIM_HOME/disk-usage
|
|
1220
|
+
and pull-requests whose path is gone from a
|
|
1221
|
+
mounted volume; --delete removes the file
|
|
979
1222
|
idleDevices { kind, id, name, project, slot, lastActivityAt,
|
|
980
1223
|
idleForMs, buildInProgress } booted owned
|
|
981
1224
|
devices whose status activity is "idle";
|
|
@@ -999,14 +1242,52 @@ RULES
|
|
|
999
1242
|
Metro, client and device logs over twice the
|
|
1000
1243
|
8 MiB cap; willTrim marks the ones it would
|
|
1001
1244
|
trim
|
|
1245
|
+
recordings { dir, projectRoot, bytes, deleteBytes,
|
|
1246
|
+
willDelete, withWorkspace, reason, detail }
|
|
1247
|
+
recordings/ of each workspace; deleteBytes is
|
|
1248
|
+
what --delete would remove; withWorkspace
|
|
1249
|
+
marks those of a gone workspace, removed with
|
|
1250
|
+
its workspace directory
|
|
1002
1251
|
workspaceBuildOutputs { dir, projectRoot, bytes, idleDays, willClear,
|
|
1003
1252
|
reason, detail } derived-data, gradle-build,
|
|
1004
|
-
android-cas
|
|
1253
|
+
android-cas, cache-provider, macos/build,
|
|
1254
|
+
macos/<Product>.app and macos/staging-* of each
|
|
1005
1255
|
workspace; willClear marks the ones --delete
|
|
1006
|
-
would clear
|
|
1256
|
+
would clear; --cache workspaces omits the
|
|
1257
|
+
directories that --delete removes whole (dead
|
|
1258
|
+
projects and orphanedWorkspaces), and --cache
|
|
1259
|
+
all does not
|
|
1260
|
+
agentDevice <AgentDeviceUsage|null> report-only; workspace state
|
|
1261
|
+
goes only with its workspace;
|
|
1262
|
+
null with any --cache scope
|
|
1263
|
+
swiftpmCache <SwiftpmCacheUsage|null> report-only, never deleted;
|
|
1264
|
+
null with any --cache scope
|
|
1007
1265
|
caches { name, dir, source, bytes, note, willEmpty,
|
|
1008
|
-
emptySkipped } alive, not
|
|
1009
|
-
marks the ones --delete
|
|
1266
|
+
emptySkipped, scopedEmpty } alive, not
|
|
1267
|
+
garbage; willEmpty marks the ones --delete
|
|
1268
|
+
would empty. scopedEmpty is { willEmpty,
|
|
1269
|
+
emptySkipped } and says what \`--delete
|
|
1270
|
+
--cache <selector>\` without --older-than
|
|
1271
|
+
would do to the cache; it is present without
|
|
1272
|
+
--cache and null with it, where willEmpty
|
|
1273
|
+
already says so. Caches that share a name
|
|
1274
|
+
carry their directory's last component, as
|
|
1275
|
+
"Metro transform cache: <store>"
|
|
1276
|
+
memory { kind: "watchman" | "gradleDaemon" |
|
|
1277
|
+
"kotlinDaemon", cacheKind: "watchman" |
|
|
1278
|
+
"gradle-daemons", pid, startedAt, bytes,
|
|
1279
|
+
measure: "footprint" | "rss", version,
|
|
1280
|
+
gradleHome, offloadClient, state: "idle" |
|
|
1281
|
+
"busy" | "unknown", reclaimable, reason,
|
|
1282
|
+
detail } without --cache, or with --cache
|
|
1283
|
+
watchman or gradle-daemons; reclaimable marks
|
|
1284
|
+
the ones gc --delete --cache <cacheKind>
|
|
1285
|
+
would stop (\`guide cleanup memory\`)
|
|
1286
|
+
watchmanRoots { path, stale: "missing" | "pruned-worktree" |
|
|
1287
|
+
null, subscriptions, triggers, removable,
|
|
1288
|
+
detail } removable marks the stale roots
|
|
1289
|
+
--cache watchman would watch-del
|
|
1290
|
+
memoryNotices { message } why the memory sweep was skipped
|
|
1010
1291
|
bytes is null when the size is unknown: not measured, or the measurement
|
|
1011
1292
|
failed. idleDays is null when the last use is unknown. listed is false for
|
|
1012
1293
|
a parked device that is no longer on this machine, and null when its
|
|
@@ -1018,6 +1299,9 @@ RULES
|
|
|
1018
1299
|
workspaceBuildOutputs unresolved | in-use | last-use-unknown |
|
|
1019
1300
|
recently-used
|
|
1020
1301
|
workspaceLogs unresolved | in-use | collector
|
|
1302
|
+
recordings unresolved | retained | recently-recorded
|
|
1303
|
+
memory in-use | busy | unknown | build-running |
|
|
1304
|
+
stim-server
|
|
1021
1305
|
linkedWorktrees not-a-worktree | bare-repository |
|
|
1022
1306
|
source-checkout-unknown | source-checkout |
|
|
1023
1307
|
locked | in-use | status-unreadable | dirty |
|
|
@@ -1032,26 +1316,110 @@ RULES
|
|
|
1032
1316
|
|
|
1033
1317
|
- a --cache name that no shared cache carries; the remedy names the
|
|
1034
1318
|
caches on this machine
|
|
1035
|
-
- --cache together with --worktrees or --idle
|
|
1319
|
+
- --cache together with --worktrees or --idle
|
|
1320
|
+
- --cache watchman or gradle-daemons together with --older-than`
|
|
1036
1321
|
},
|
|
1037
1322
|
status: {
|
|
1038
|
-
summary: "the status payload's issues and their codes, build and device activity fields: a running build, its estimate, each platform's last build, who drives each device, whether the app runs on it, and what uses CPU and memory now",
|
|
1323
|
+
summary: "the status payload's lifecycle phase and stage, issues and their codes, build and device activity fields: a running build, its estimate, each platform's last build, who drives each device, whether the app runs on it, and what uses CPU and memory now",
|
|
1039
1324
|
body: () => ` stim status --json
|
|
1040
1325
|
|
|
1326
|
+
logs { dir, errorsSinceMarker }, or null without a log directory
|
|
1327
|
+
agentDevice { stateDir }: absolute workspace agent-device state path on
|
|
1328
|
+
every environment, shared by its slots. Reporting it creates
|
|
1329
|
+
no directory; agent-device creates it when used.
|
|
1330
|
+
|
|
1331
|
+
Each environment carries phase, where the workspace is in its lifecycle:
|
|
1332
|
+
|
|
1333
|
+
phase "warming" stim worktree warm runs in it now
|
|
1334
|
+
"ready" its last warm succeeded and nothing has run there
|
|
1335
|
+
since: no start, ios, android, web or reload, for
|
|
1336
|
+
at most 2 hours
|
|
1337
|
+
"live" live is true: Metro, a device, Chrome or a remote
|
|
1338
|
+
session of it runs
|
|
1339
|
+
"idle" none of these
|
|
1340
|
+
phaseSince when the warm started ("warming") or finished ("ready"); null
|
|
1341
|
+
for "live" and "idle"
|
|
1342
|
+
warmStep "refresh" or "copy", the step a warming workspace is in;
|
|
1343
|
+
absent in other phases
|
|
1344
|
+
platforms ordered supported app platforms: ios, android, macos, web;
|
|
1345
|
+
always present, [] when none are detected. Expo uses its
|
|
1346
|
+
explicit platforms list, otherwise ios and android plus web
|
|
1347
|
+
with react-native-web. Bare apps use ios/*.xcodeproj or
|
|
1348
|
+
*.xcworkspace and android/ Gradle project files. web.url adds
|
|
1349
|
+
web to any app and overrides an Expo list; a bare app has no
|
|
1350
|
+
other route to web. macOS needs Package.swift, macos.product
|
|
1351
|
+
and macos.infoPlist. Detection reads config literals and
|
|
1352
|
+
files, never runs project scripts.
|
|
1353
|
+
recording { enabled }: whether stim-server may record the workspace's
|
|
1354
|
+
device screens for replay, from recording.enabled
|
|
1355
|
+
|
|
1356
|
+
Each environment also carries stage, where the workspace is as Stim
|
|
1357
|
+
Desktop and the phone app show it: { kind, since, platform, closedApps }.
|
|
1358
|
+
The first kind that applies wins:
|
|
1359
|
+
|
|
1360
|
+
kind "building" a build runs; platform names it
|
|
1361
|
+
"warming" phase is "warming" and nothing is live
|
|
1362
|
+
"ready" phase is "ready" and nothing is live
|
|
1363
|
+
"build-failed" the newest run of either platform failed;
|
|
1364
|
+
platform names it
|
|
1365
|
+
"running" live is true or a remote session runs
|
|
1366
|
+
"stopped" none of these
|
|
1367
|
+
since when it began: the build's start, phaseSince, the failed
|
|
1368
|
+
run's finishedAt (else startedAt), the supervisor's start or
|
|
1369
|
+
metro.lastStop.at; null when unknown
|
|
1370
|
+
platform "ios" or "android" for "building" and "build-failed", else null
|
|
1371
|
+
closedApps for "running", [{ platform, slot }] of the devices whose
|
|
1372
|
+
appPresence is "closed", the default slot first, then each
|
|
1373
|
+
slot in order, iOS before Android; empty otherwise
|
|
1374
|
+
|
|
1375
|
+
A warm records warming under its own ownership claim, so a warm that was
|
|
1376
|
+
killed or failed reads as idle, never as warming. Plain \`stim status\`
|
|
1377
|
+
marks the workspace [warming: <step>], [ready] or [idle].
|
|
1378
|
+
|
|
1041
1379
|
Each environment carries issues, the things in that workspace that need
|
|
1042
|
-
the user, and warnings,
|
|
1043
|
-
default slot, then "<message>; run \`<remedy>\`"):
|
|
1380
|
+
the user, and warnings, its error and warning issues as text ("<slot>: "
|
|
1381
|
+
when not the default slot, then "<message>; run \`<remedy>\`"):
|
|
1044
1382
|
|
|
1045
1383
|
issues [{ code, severity, message, remedy, workspace, slot? }]
|
|
1046
1384
|
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1385
|
+
severity "error" Stim cannot verify or safely act on something; a
|
|
1386
|
+
person resolves it (stim guide errors teardown)
|
|
1387
|
+
"warning" something is broken now and needs someone to act:
|
|
1388
|
+
an app cannot reach Metro, a port is held, an
|
|
1389
|
+
expected device vanished
|
|
1390
|
+
"info" a fact the next normal stim command handles by
|
|
1391
|
+
itself, so it needs no action
|
|
1392
|
+
The apps show error and warning and hide info; plain stim
|
|
1393
|
+
status prints info as a dim note with no remedy; warnings
|
|
1394
|
+
excludes info. The info codes: sim-missing, sim-without-metro,
|
|
1395
|
+
avd-missing, and port-not-ours while no supervisor runs.
|
|
1396
|
+
|
|
1397
|
+
code port-not-ours another process answers Metro on this
|
|
1398
|
+
workspace's reserved port. "info" with
|
|
1399
|
+
remedy stim start when no supervisor
|
|
1400
|
+
runs: start reserves a free port.
|
|
1401
|
+
"warning" when this workspace's
|
|
1402
|
+
supervisor runs and cannot serve there
|
|
1403
|
+
(remedy stim stop), or its supervisor is
|
|
1404
|
+
unverified. The message names the holder:
|
|
1405
|
+
"Metro for workspace <name>" when it runs
|
|
1406
|
+
inside another workspace, else "a dev
|
|
1407
|
+
server in <last dirs>"; metro.heldBy
|
|
1408
|
+
carries its pid and cwd
|
|
1409
|
+
sim-missing the recorded simulator no longer exists;
|
|
1410
|
+
always "info", stim ios creates a new one
|
|
1050
1411
|
sim-without-metro the simulator is booted and no Metro
|
|
1051
|
-
serves the workspace
|
|
1412
|
+
serves the workspace; always "info"
|
|
1052
1413
|
avd-serial-changed the owned emulator came back on another
|
|
1053
1414
|
serial, so Metro forwarding is lost
|
|
1054
|
-
avd-missing the recorded AVD no longer exists
|
|
1415
|
+
avd-missing the recorded AVD no longer exists; always
|
|
1416
|
+
"info", stim android creates a new one
|
|
1417
|
+
android-reverse-missing the owned emulator runs this
|
|
1418
|
+
workspace's debug app against its live
|
|
1419
|
+
Metro, but the adb reverse for that Metro
|
|
1420
|
+
port is gone, so the app cannot reach
|
|
1421
|
+
Metro; the remedy stim reload android
|
|
1422
|
+
restores it
|
|
1055
1423
|
avd-not-detected adb does not see the owned emulator while
|
|
1056
1424
|
the workspace expects it: it holds an
|
|
1057
1425
|
unexpired lease on its serial, or it
|
|
@@ -1071,8 +1439,8 @@ RULES
|
|
|
1071
1439
|
browser-orphaned the owned Chrome runs but its supervisor
|
|
1072
1440
|
exited, so page logs are not captured;
|
|
1073
1441
|
stim stop closes it
|
|
1074
|
-
severity "error"
|
|
1075
|
-
|
|
1442
|
+
severity the rule above; "error" is also what makes stop or start
|
|
1443
|
+
refuse until it is resolved
|
|
1076
1444
|
remedy a command to run from workspace, such as "stim android --slot
|
|
1077
1445
|
fold"
|
|
1078
1446
|
slot absent for the default slot
|
|
@@ -1085,20 +1453,45 @@ RULES
|
|
|
1085
1453
|
An environment where \`stim web\` ran carries web, its Stim-owned Chrome:
|
|
1086
1454
|
|
|
1087
1455
|
web { browser, version, running, pid, supervisorPid, url, headless,
|
|
1088
|
-
viewport, profile, cdpEndpoint }
|
|
1456
|
+
viewport, profile, cdpEndpoint, targetId, page?, activity? }
|
|
1089
1457
|
|
|
1090
1458
|
running the browser supervisor and Chrome are both verified live;
|
|
1091
|
-
pid, supervisorPid and
|
|
1459
|
+
pid, supervisorPid, cdpEndpoint and targetId are null when
|
|
1460
|
+
false, and page and activity are absent
|
|
1461
|
+
url the page stim web opened; page.url is the document the page
|
|
1462
|
+
loaded last, and page.route the URL an in-app route change
|
|
1463
|
+
(history API or fragment) moved it to since
|
|
1092
1464
|
cdpEndpoint http://127.0.0.1:<port>, the reserved loopback DevTools
|
|
1093
1465
|
endpoint of that Chrome. Attach Playwright MCP
|
|
1094
1466
|
(--cdp-endpoint) or agent-browser (--cdp <port>) to it; it
|
|
1095
1467
|
reaches only the Stim profile, never your own browser
|
|
1468
|
+
targetId the DevTools target id of the owned page, the one Stim
|
|
1469
|
+
Desktop and the phone stream; other tabs are not the page
|
|
1096
1470
|
profile the Stim-owned user data directory under STIM_HOME
|
|
1471
|
+
page { url, state, error?, route? }: the page's latest load, from
|
|
1472
|
+
the newest page-load marker in web.ndjson; null before the
|
|
1473
|
+
first. route is absent until an in-app route change after
|
|
1474
|
+
that load
|
|
1475
|
+
"loading" the document was requested; no load event yet
|
|
1476
|
+
"loaded" the load event fired
|
|
1477
|
+
"failed" the document failed (connection refused, HTTP
|
|
1478
|
+
error, certificate) or the page crashed; error
|
|
1479
|
+
is the log message
|
|
1480
|
+
activity like a device's activity below. driver.tool names a
|
|
1481
|
+
DevTools client connected to cdpEndpoint (agent-browser,
|
|
1482
|
+
agent-device, argent, playwright, puppeteer,
|
|
1483
|
+
chrome-devtools-mcp, or the executable's name); Stim's own
|
|
1484
|
+
connections (the browser supervisor, stim-server, Stim
|
|
1485
|
+
Desktop, the stim CLI) are not drivers. basis: cdp-client
|
|
1486
|
+
(one lsof of the DevTools port); agent-action (the newest
|
|
1487
|
+
agent input the browser supervisor recorded in web.ndjson)
|
|
1488
|
+
and page-log (the newest other
|
|
1489
|
+
web.ndjson record) for recency
|
|
1097
1490
|
|
|
1098
1491
|
Each booted simulator and detected emulator in environments (and in
|
|
1099
1492
|
slots) carries activity; a shut-down or physical device has none:
|
|
1100
1493
|
|
|
1101
|
-
activity { state, driver?, lastActivityAt?, basis }
|
|
1494
|
+
activity { state, driver?, lastActivityAt?, recent?, basis }
|
|
1102
1495
|
|
|
1103
1496
|
state "driven" a live claim or driver holds the device now
|
|
1104
1497
|
"active" no driver, but activity in the last 10 minutes
|
|
@@ -1106,25 +1499,46 @@ RULES
|
|
|
1106
1499
|
"unknown" a claim or driver check could not be read;
|
|
1107
1500
|
never treated as idle
|
|
1108
1501
|
driver { tool, pid, since } for "driven": agent-device, stim device
|
|
1109
|
-
lock, xcodebuild, idb, maestro, appium, simctl,
|
|
1502
|
+
lock, argent, xcodebuild, idb, maestro, appium, simctl,
|
|
1110
1503
|
uiautomator or instrumentation; pid and since are null when
|
|
1111
1504
|
the claim does not record them
|
|
1112
1505
|
lastActivityAt the newest of this device's app log records, this
|
|
1113
|
-
platform's Metro bundle requests
|
|
1114
|
-
Stim run,
|
|
1506
|
+
platform's Metro bundle requests, the workspace's last
|
|
1507
|
+
Stim run, while agent-device drives it the agent's last
|
|
1508
|
+
recorded action, and now while a stim-server client views
|
|
1509
|
+
it, rounded down to the minute; absent when none is
|
|
1115
1510
|
recorded
|
|
1511
|
+
recent the newest time of each kind of evidence behind
|
|
1512
|
+
lastActivityAt: agent-action, device-log, metro-bundle,
|
|
1513
|
+
workspace-use, supervisor-start, page-log or viewer, each
|
|
1514
|
+
rounded down to the minute. viewer is now while a
|
|
1515
|
+
stim-server client streams the device's frames
|
|
1116
1516
|
basis the evidence behind state, strongest first:
|
|
1117
1517
|
agent-device-claim, agent-device-lease agent-device state,
|
|
1118
1518
|
read only; live only when every recorded process is alive
|
|
1119
|
-
with its recorded start time, so a reused pid is dead
|
|
1519
|
+
with its recorded start time, so a reused pid is dead.
|
|
1520
|
+
A runner lease that declares agent-device's device
|
|
1521
|
+
claims is never a driver: without a claim, the runner
|
|
1522
|
+
is one agent-device kept warm after close or handed
|
|
1523
|
+
off when its daemon exited
|
|
1120
1524
|
device-lock an unexpired \`stim device lock\` lease
|
|
1121
|
-
driver-process a host process naming the UDID or
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1525
|
+
driver-process a host process naming the UDID or
|
|
1526
|
+
serial, other than such a runner
|
|
1527
|
+
instrumentation an on-device uiautomator, androidx.test or
|
|
1528
|
+
argent helper process (one adb shell ps
|
|
1529
|
+
per emulator)
|
|
1530
|
+
device-log, metro-bundle, workspace-use, agent-action,
|
|
1531
|
+
viewer recency
|
|
1125
1532
|
|
|
1126
1533
|
Plain \`stim status\` appends it to each device line: "driven by
|
|
1127
1534
|
agent-device for 12m", "active", "idle 3h", or "activity unknown (...)".
|
|
1535
|
+
|
|
1536
|
+
An owned device that is not booted after its supervisor shut it down for
|
|
1537
|
+
devices.idleShutdownMinutes carries idleShutdown, and plain \`status\`
|
|
1538
|
+
appends "shut down after 30m idle at <at>" to its line; the next \`ios\` or
|
|
1539
|
+
\`android\` run for that slot clears it (\`guide lifecycle budget\`):
|
|
1540
|
+
|
|
1541
|
+
idleShutdown { at, idleMinutes }
|
|
1128
1542
|
\`gc --idle <duration>\` shuts down owned devices idle that long
|
|
1129
1543
|
(\`guide cleanup gc\`).
|
|
1130
1544
|
|
|
@@ -1143,6 +1557,16 @@ RULES
|
|
|
1143
1557
|
app is absent when the device is not owned or no app id is known: the
|
|
1144
1558
|
process was not checked.
|
|
1145
1559
|
|
|
1560
|
+
Each ios and android device record, in every slot, also carries
|
|
1561
|
+
appPresence, whether the device lacks the app:
|
|
1562
|
+
|
|
1563
|
+
appPresence "none" the device runs, its platform's latest run failed
|
|
1564
|
+
and builds carries the platform with no
|
|
1565
|
+
succeeded run
|
|
1566
|
+
"closed" the device runs and app.state is "stopped"
|
|
1567
|
+
null otherwise, including a device that is not running
|
|
1568
|
+
and a physical device
|
|
1569
|
+
|
|
1146
1570
|
This is current process state, read from one host ps (simulator apps are
|
|
1147
1571
|
host processes) and the same adb shell ps as activity, so a status --watch
|
|
1148
1572
|
refresh notices an exit within its 30-second fallback. It is not launch
|
|
@@ -1151,6 +1575,11 @@ RULES
|
|
|
1151
1575
|
status appends "<id> running", "<id> not running" or "<id> process
|
|
1152
1576
|
unknown" to the device line.
|
|
1153
1577
|
|
|
1578
|
+
An environment's metro carries tunnel { provider, url } for a recorded
|
|
1579
|
+
managed tunnel on its reserved port: ngrok, cloudflared or tailscale.
|
|
1580
|
+
Plain status prints the provider and URL, with "tailnet-only" for tailscale.
|
|
1581
|
+
This describes the record, not a reachability check (\`guide metro\`).
|
|
1582
|
+
|
|
1154
1583
|
An environment's metro carries idleStop { reason: "idle", at, idleMinutes }
|
|
1155
1584
|
when its supervisor stopped the dev server for idleness and nothing serves
|
|
1156
1585
|
the port since; plain \`status\` prints "stopped (idle)" (\`guide metro\`).
|
|
@@ -1163,12 +1592,32 @@ RULES
|
|
|
1163
1592
|
{ reason: "vanished", pid, startedAt } the recorded supervisor is gone
|
|
1164
1593
|
and recorded no cause
|
|
1165
1594
|
(\`guide metro\`, WHY IT STOPPED).
|
|
1595
|
+
When another process answers Metro on the port, metro carries
|
|
1596
|
+
heldBy { pid, cwd }, cwd null when its directory could not be read.
|
|
1597
|
+
|
|
1598
|
+
metro also carries bundle, from the bundle requests Stim's middleware
|
|
1599
|
+
records in the workspace's metro log: an app's, and Stim's own prefetch
|
|
1600
|
+
before a launch. It is absent when the log's tail holds none.
|
|
1601
|
+
|
|
1602
|
+
bundle { bundling, platform?, startedAt?, percent?, last? }
|
|
1603
|
+
bundling true while a request is in flight: Metro runs, and the request
|
|
1604
|
+
started under the current dev server less than 10 minutes ago
|
|
1605
|
+
platform with bundling, the in-flight request's "ios" or "android";
|
|
1606
|
+
startedAt when it started
|
|
1607
|
+
percent 0-100, only when Metro reported progress for that request,
|
|
1608
|
+
which it does for clients that accept multipart responses
|
|
1609
|
+
last { platform, status, durationMs, finishedAt }: the newest
|
|
1610
|
+
finished request, status "ok" or "failed", durationMs from the
|
|
1611
|
+
request to the end of the response
|
|
1166
1612
|
|
|
1167
1613
|
Each entry of environments also carries build: null, or the ios or android
|
|
1168
1614
|
run that holds this workspace's native-run.lock:
|
|
1169
1615
|
|
|
1170
1616
|
build { platform, slot, state, phase, startedAt, phaseStartedAt,
|
|
1171
|
-
outcome, expectedMs, expectedPhaseMs,
|
|
1617
|
+
outcome, outcomeKnown, cacheLookupOutcome?, expectedMs, expectedPhaseMs,
|
|
1618
|
+
completedPhaseMs?, basis,
|
|
1619
|
+
plannedPhases, missReason?, missProvisional?, detail?, placement,
|
|
1620
|
+
waitingOn? }
|
|
1172
1621
|
|
|
1173
1622
|
state "running" while the run's own native-run claim is live;
|
|
1174
1623
|
"stale" when that claim was released or its process is
|
|
@@ -1176,30 +1625,102 @@ RULES
|
|
|
1176
1625
|
record; "unknown" when the claim set cannot be resolved.
|
|
1177
1626
|
Never treat a stale record as a build in progress.
|
|
1178
1627
|
phase prepare | cache-lookup | wait | prebuild | pods |
|
|
1179
|
-
compile | install | launch. prepare covers
|
|
1180
|
-
|
|
1181
|
-
workspace's build of the same fingerprint
|
|
1182
|
-
|
|
1628
|
+
compile | device | install | launch. prepare covers
|
|
1629
|
+
settings and Metro; wait is waiting on another
|
|
1630
|
+
workspace's build of the same fingerprint. Creating,
|
|
1631
|
+
adopting or booting the owned simulator or emulator
|
|
1632
|
+
before the cache lookup is prepare time. device starts
|
|
1633
|
+
once the app is ready and covers waiting for the
|
|
1634
|
+
device: its boot, adoption cleanup, or a physical
|
|
1635
|
+
device's lease and connection check. A boot that
|
|
1636
|
+
finishes during the build adds no device time. An
|
|
1637
|
+
--eas-profile run has no cache lookup, so its outcome
|
|
1638
|
+
stays the project's most recent one until install.
|
|
1183
1639
|
startedAt when the run started; phaseStartedAt when its phase did
|
|
1184
|
-
outcome "cold"
|
|
1185
|
-
"hit"
|
|
1186
|
-
|
|
1640
|
+
outcome "cold" after the local/provider lookups resolve a miss;
|
|
1641
|
+
"hit" after a cached artifact is ready to reuse, including
|
|
1642
|
+
a shared-build hit or a recheck after prebuild or pods.
|
|
1643
|
+
A later recheck can replace the first lookup's outcome.
|
|
1644
|
+
Before resolution, the outcome of this project's most
|
|
1645
|
+
recent run.
|
|
1646
|
+
outcomeKnown true once outcome is this run's own, false while it is
|
|
1647
|
+
the project's most recent one
|
|
1648
|
+
cacheLookupOutcome "hit" or "miss" once an actual cache lookup resolves;
|
|
1649
|
+
absent before resolution and on runs that skip lookup,
|
|
1650
|
+
such as --eas-profile
|
|
1187
1651
|
expectedMs the median duration of this project's last successful
|
|
1188
1652
|
runs with that outcome on that platform, or null with
|
|
1189
|
-
no history
|
|
1653
|
+
no history. The run estimates twice: when it starts,
|
|
1654
|
+
from every run with the project's most recent outcome,
|
|
1655
|
+
and once when outcomeKnown turns true. From then on only
|
|
1656
|
+
runs that created, adopted or cold-booted their device
|
|
1657
|
+
the way this run did count; a run that reuses a booted
|
|
1658
|
+
device also counts runs recorded before Stim tagged them
|
|
1659
|
+
until 3 tagged ones exist. Runs that finish meanwhile do
|
|
1660
|
+
not change the estimate.
|
|
1190
1661
|
expectedPhaseMs the median duration of this phase in those runs, or null
|
|
1191
|
-
|
|
1662
|
+
when none of them entered it
|
|
1663
|
+
completedPhaseMs milliseconds spent in each phase the run already left,
|
|
1664
|
+
summing repeated visits. The current visit is excluded;
|
|
1665
|
+
its phase appears only if visited earlier. Absent before
|
|
1666
|
+
any phase completes and on older Stim versions
|
|
1667
|
+
basis how many runs the expectedMs median comes from (at most
|
|
1668
|
+
10 per kind: device-setup runs keep their own 10)
|
|
1669
|
+
plannedPhases [{ phase, expectedMs }] in phase order: the phases at
|
|
1670
|
+
least half of those runs entered, each with its median
|
|
1671
|
+
in them; null with no such run. The current phase can be
|
|
1672
|
+
one it does not list, such as wait. Draw a progress bar
|
|
1673
|
+
from it rather than from builds, which holds only this
|
|
1674
|
+
workspace's runs and includes offloaded ones
|
|
1675
|
+
missReason once the run knows its cache lookup missed, why, in the
|
|
1676
|
+
shape of lastBuilds missReason below
|
|
1677
|
+
missProvisional true while missReason is the first lookup's miss and the
|
|
1678
|
+
run will prebuild or install pods and look the key up
|
|
1679
|
+
again, because those can change the fingerprint. A hit on
|
|
1680
|
+
that second lookup removes missReason and sets outcome to
|
|
1681
|
+
hit; otherwise missReason is replaced with the final one,
|
|
1682
|
+
whose rekeyedBy lists what changed the key, and
|
|
1683
|
+
missProvisional goes away before compile
|
|
1684
|
+
detail during compile, once the build tool printed a line Stim
|
|
1685
|
+
reads: { step, unit, done, total, line, updatedAt }
|
|
1686
|
+
step the tool's step: configure, compile, link, resources,
|
|
1687
|
+
script, dex, package or sign; null before one is known
|
|
1688
|
+
unit "targets" for xcodebuild, "tasks" for Gradle
|
|
1689
|
+
done xcodebuild: targets it finished (a target counts once
|
|
1690
|
+
xcodebuild touches or signs its product, so an
|
|
1691
|
+
incremental build can end below total); Gradle: tasks
|
|
1692
|
+
it reported so far
|
|
1693
|
+
total xcodebuild: targets in its dependency graph; Gradle: null,
|
|
1694
|
+
since its plain output gives no total
|
|
1695
|
+
line the latest compile, link or task line, paths shortened
|
|
1696
|
+
to file names, at most 160 characters
|
|
1697
|
+
updatedAt when the build last wrote it; the run writes it at most
|
|
1698
|
+
every 2 seconds
|
|
1699
|
+
placement where the build runs: "local", or while it is offloaded
|
|
1700
|
+
{ host, phase, startedAt, phaseStartedAt }. host is the
|
|
1701
|
+
offload.machines entry; phase is the step there: sync,
|
|
1702
|
+
deps, prebuild, pods, build (xcodebuild or Gradle) or fetch;
|
|
1703
|
+
startedAt is when the offload started and phaseStartedAt
|
|
1704
|
+
when that step did. Meanwhile phase above follows it as
|
|
1705
|
+
prebuild, pods or compile.
|
|
1706
|
+
waitingOn while phase is "wait" and the holder is known: { path },
|
|
1707
|
+
the workspace whose build of the same artifact this run
|
|
1708
|
+
waits for; absent otherwise. The lock is machine-wide, so
|
|
1709
|
+
path is usually an entry of environments but can name a
|
|
1710
|
+
workspace that is not listed.
|
|
1192
1711
|
|
|
1193
1712
|
Plain \`stim status\` prints the same as one line per workspace:
|
|
1194
1713
|
|
|
1195
1714
|
build: ios compile, 1m10s elapsed -- about 3 min left (median of 4 cold runs)
|
|
1715
|
+
build: ios compile on janics-mac-mini (build, 2m10s), 3m05s elapsed
|
|
1196
1716
|
|
|
1197
1717
|
An environment with a recorded run also carries lastBuilds, each
|
|
1198
1718
|
platform's most recent ios or android run, finished or failed:
|
|
1199
1719
|
|
|
1200
1720
|
lastBuilds { ios?, android? }, each { platform, status, cacheHit,
|
|
1201
1721
|
cacheSkipped, durationMs, fingerprint, startedAt, finishedAt,
|
|
1202
|
-
errorCode?, missReason?,
|
|
1722
|
+
errorCode?, missReason?, buildMachine?, builtOn?, offloadedTo?, offloadFallback?,
|
|
1723
|
+
diagnostics? }
|
|
1203
1724
|
|
|
1204
1725
|
status "ok" or "failed"
|
|
1205
1726
|
cacheHit "local" or "remote" for an app from that cache tier; false
|
|
@@ -1220,10 +1741,43 @@ RULES
|
|
|
1220
1741
|
diagnostics only on a failed run whose compiler reported errors: up to
|
|
1221
1742
|
5 { file, line, column, message }, null where the compiler
|
|
1222
1743
|
gave no position.
|
|
1744
|
+
cause only on a failed run: { key, file, line }, the first
|
|
1745
|
+
diagnostic with a file and a line, keyed "<file>:<line>",
|
|
1746
|
+
else the errorCode (or "failed") with null file and line.
|
|
1747
|
+
Failed runs in a row with the same key failed the same way.
|
|
1748
|
+
Invalid, unlisted or unpaired selections refuse during setup, before cache
|
|
1749
|
+
lookup, and create no lastBuild record or failed-run stats.
|
|
1750
|
+
buildMachine selected auto, local or configured machine entry; always on new build records
|
|
1751
|
+
builtOn here or machine name; absent when no build ran (cache/refusal)
|
|
1752
|
+
offloadedTo only on a run a build machine compiled: its name
|
|
1753
|
+
offloadFallback
|
|
1754
|
+
only on a run that considered offloading and built here: one
|
|
1755
|
+
reason per machine when none took the build, or why the
|
|
1756
|
+
offload stopped
|
|
1223
1757
|
|
|
1224
1758
|
Plain status prints "last build: ios local cache in 12s, android compiled
|
|
1225
1759
|
in 7m02s". To predict the next run instead, see \`guide facts plan\`.
|
|
1226
1760
|
|
|
1761
|
+
Swift Package development carries macos, separate from simulator devices:
|
|
1762
|
+
product, bundle, bundleId, executable, launchId, arguments
|
|
1763
|
+
app?, supervisor? { pid, processToken, startedAtMicros }
|
|
1764
|
+
state running | orphaned | stopped | unverified
|
|
1765
|
+
build { state: running | ok | failed, startedAt,
|
|
1766
|
+
finishedAt?, durationMs?, error?,
|
|
1767
|
+
buildMachine?, builtOn?, errorCode?, offloadedTo?, offloadFallback? }
|
|
1768
|
+
host? { machine, session, appSlot, appAttempt, bundleId,
|
|
1769
|
+
agent } when stim macos --remote placed the app on
|
|
1770
|
+
another Mac; it then has no local app or supervisor
|
|
1771
|
+
hostLaunched? true when that host reported a live app, false when
|
|
1772
|
+
stopped, "unverified" when it could not confirm one
|
|
1773
|
+
A live app without its supervisor is orphaned. An identity the system cannot
|
|
1774
|
+
verify is unverified. For hosted placements only, status asks the host for
|
|
1775
|
+
the session state with about a 3 s timeout per connection and request and
|
|
1776
|
+
a 10 s cache. It reports stopped when the host says the session stopped,
|
|
1777
|
+
for example after a stim-server restart there, and unverified when the
|
|
1778
|
+
host is unreachable or cannot confirm. The placement stays recorded.
|
|
1779
|
+
See stim guide macos for local capture, hosting and cleanup.
|
|
1780
|
+
|
|
1227
1781
|
An environment with a recorded run also carries builds, each platform's
|
|
1228
1782
|
last 10 runs, newest first. Its newest entry that is not "interrupted" is
|
|
1229
1783
|
the run lastBuilds reports, once a run has recorded builds.
|
|
@@ -1243,23 +1797,121 @@ RULES
|
|
|
1243
1797
|
cacheKey the cache key the run looked up or stored under
|
|
1244
1798
|
phases milliseconds spent in each phase the run entered, among
|
|
1245
1799
|
prepare, cache-lookup, wait, prebuild, pods, compile,
|
|
1246
|
-
install and launch
|
|
1800
|
+
device, install and launch; runs recorded before device
|
|
1801
|
+
existed count their device time in prepare and install
|
|
1247
1802
|
|
|
1248
1803
|
Only runs that record a last build are listed: a run that stops before
|
|
1249
1804
|
looking up a build, such as a bad flag, is not. Estimates (expectedMs) come from run statistics, not builds.
|
|
1250
1805
|
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1806
|
+
done and total are counts, not a completion fraction: a target or task
|
|
1807
|
+
can take a second or ten minutes, and cached work finishes instantly.
|
|
1808
|
+
|
|
1809
|
+
Each environment also carries disk, and each owned simulator and owned
|
|
1810
|
+
emulator in ios, android and slots carries disk, once \`status --watch\` has
|
|
1811
|
+
measured them:
|
|
1812
|
+
|
|
1813
|
+
disk (environment) { worktreeBytes, nodeModulesBytes, buildBytes,
|
|
1814
|
+
measuredAt }
|
|
1815
|
+
worktreeBytes the linked worktree, or else the checkout holding the
|
|
1816
|
+
workspace, node_modules included
|
|
1817
|
+
nodeModulesBytes node_modules at that root and at the workspace path;
|
|
1818
|
+
part of worktreeBytes
|
|
1819
|
+
buildBytes Stim's folder for the workspace: Xcode derived data,
|
|
1820
|
+
Gradle outputs and logs
|
|
1821
|
+
measuredAt the oldest of the three measurements; a figure not
|
|
1822
|
+
measured yet is null
|
|
1823
|
+
disk (device) { bytes, measuredAt }: the simulator's data folder
|
|
1824
|
+
under CoreSimulator/Devices, or the AVD's .avd folder
|
|
1825
|
+
|
|
1826
|
+
\`status --watch\` runs one du at a time off its refresh path, and measures
|
|
1827
|
+
a folder at most every 5 minutes while its environment is live and every
|
|
1828
|
+
hour otherwise. It caches each size under $STIM_HOME/disk-usage, which
|
|
1829
|
+
one-shot status only reads, so the fields appear once a watcher, such as
|
|
1830
|
+
stim-server or Stim Desktop, has measured.
|
|
1831
|
+
|
|
1832
|
+
An environment carries agents when coding-agent sessions work in it, most
|
|
1833
|
+
recently active first:
|
|
1834
|
+
|
|
1835
|
+
agents [{ tool, sessionId, title?, cwd, startedAt?, lastActiveAt?,
|
|
1836
|
+
pid?, openUrl?, webUrl? }]
|
|
1837
|
+
tool claude-code | codex
|
|
1838
|
+
title the short name the tool keeps for the session; never a
|
|
1839
|
+
prompt or any conversation content
|
|
1840
|
+
cwd the session's working directory, canonical
|
|
1841
|
+
lastActiveAt the tool's last update of the session, or the last Stim
|
|
1842
|
+
command the session ran here when that is newer
|
|
1843
|
+
openUrl opens the session in the Claude desktop app
|
|
1844
|
+
(claude://code/continue) or the Codex app
|
|
1845
|
+
(codex://threads/<id>) when that app is installed on the
|
|
1846
|
+
Mac; absent for a Claude Code session started in a terminal
|
|
1847
|
+
webUrl https://claude.ai/code/<id> for a Claude Code session with
|
|
1848
|
+
Remote Control connected; it opens the session in a browser
|
|
1849
|
+
or the Claude mobile app
|
|
1850
|
+
|
|
1851
|
+
A session works in an environment when its cwd is the environment's
|
|
1852
|
+
path or inside it (the deepest environment wins), or is the environment's
|
|
1853
|
+
git worktree root. \`status --watch\` looks for sessions at most every 15
|
|
1854
|
+
seconds: Claude Code sessions in ~/.claude/sessions whose process runs,
|
|
1855
|
+
and Codex threads in $CODEX_HOME (~/.codex) updated in the last 30
|
|
1856
|
+
minutes. It caches them in $STIM_HOME/agent-sessions.json, which one-shot
|
|
1857
|
+
status reads and ignores once it is 2 minutes old. Both sources are other
|
|
1858
|
+
tools' internal files; a missing or changed one only drops its sessions.
|
|
1859
|
+
\`start\`, \`ios\`, \`android\`, \`web\`, \`reload\` and \`worktree warm\`
|
|
1860
|
+
also record the CLAUDE_CODE_SESSION_ID or CODEX_THREAD_ID of the shell
|
|
1861
|
+
that ran them, so that session is listed for 30 minutes after the command
|
|
1862
|
+
even when status cannot see it, and for as long as it runs.
|
|
1863
|
+
|
|
1864
|
+
When a watcher stops finding a session it found on its previous look, it
|
|
1865
|
+
records the session in each environment it worked in, under
|
|
1866
|
+
$STIM_HOME/workspaces/<id>/ended-agents.json, for 3 days:
|
|
1867
|
+
|
|
1868
|
+
endedAgents [{ tool, sessionId, title?, cwd, startedAt?, lastActiveAt?,
|
|
1869
|
+
openUrl?, webUrl?, endedAt }], most recently ended first
|
|
1870
|
+
endedAt the last time a watcher found the session running; a Codex
|
|
1871
|
+
thread ends 30 minutes after its last update
|
|
1872
|
+
|
|
1873
|
+
The other fields are as the watcher last found them. A session that runs
|
|
1874
|
+
again is listed in agents only.
|
|
1875
|
+
|
|
1876
|
+
memorySource says how a memory figure was obtained:
|
|
1877
|
+
|
|
1878
|
+
footprint physical footprint, the figure Activity Monitor's Memory
|
|
1879
|
+
column shows, read by a small helper Stim compiles with the
|
|
1880
|
+
Xcode command line tools into $STIM_HOME/helpers on first use
|
|
1881
|
+
rss summed resident set size, for machine owners when the helper
|
|
1882
|
+
cannot be built (no Xcode command line tools, Linux). It
|
|
1883
|
+
overstates a simulator many times over.
|
|
1884
|
+
estimate a fixed amount per booted simulator, detected emulator,
|
|
1885
|
+
running Metro and running Chrome
|
|
1886
|
+
|
|
1887
|
+
An environment's memoryMb is what its own processes use now, the sum of
|
|
1888
|
+
memoryMb over the machine owners whose workspace is that environment,
|
|
1889
|
+
builds included, when machine.memorySource is footprint. Otherwise it is
|
|
1890
|
+
the estimate, with memorySource estimate.
|
|
1891
|
+
|
|
1892
|
+
capacity.committedMb sums memoryMb. The memory budget plans before a boot
|
|
1893
|
+
and always uses the estimate. What is using CPU and memory now is the
|
|
1894
|
+
top-level machine section:
|
|
1895
|
+
|
|
1896
|
+
maintenance { mode, lastChecks: { pressure, size }, pressure, sizes,
|
|
1897
|
+
lastPass, running, recent, plan, invalid?, claim? }
|
|
1898
|
+
Report-only observations; every plan action's kind starts with would-.
|
|
1899
|
+
lastChecks are epoch milliseconds or null. running comes only from a
|
|
1900
|
+
live maintenance/run.claims owner, never from a check stamp.
|
|
1901
|
+
lastPass carries startedAt, durationMs, trigger, mode, freedBytes (0),
|
|
1902
|
+
actions, stopped (0), blocked. recent holds the last 20 action, failure
|
|
1903
|
+
and blocked NDJSON records from maintenance/maintenance.ndjson.
|
|
1904
|
+
invalid names invalid settings; invalid cache caps use their defaults,
|
|
1905
|
+
while invalid maintenance settings disable passes. claim contains an
|
|
1906
|
+
unresolved claim's path and reason, plus removeCommand from the claim
|
|
1907
|
+
protocol; inspect the holder before running that command.
|
|
1908
|
+
|
|
1909
|
+
machine null, or { memorySource, owners: [{ kind, name, workspace,
|
|
1910
|
+
slot?, id, owned, cpuPercent, residentMb, memoryMb,
|
|
1911
|
+
processes }] }
|
|
1912
|
+
|
|
1913
|
+
kind macos the owned Swift Package app and supervisor
|
|
1914
|
+
simulator a booted simulator's launchd_sim tree
|
|
1263
1915
|
emulator an emulator's launcher and qemu tree, by its -avd
|
|
1264
1916
|
metro a workspace's supervisor and Metro trees
|
|
1265
1917
|
build a running ios or android run's process tree,
|
|
@@ -1281,14 +1933,19 @@ RULES
|
|
|
1281
1933
|
residentMb summed resident set size. Pages shared between processes
|
|
1282
1934
|
count once per process, so a simulator reports well above
|
|
1283
1935
|
its physical footprint.
|
|
1936
|
+
memoryMb summed physical footprint when machine.memorySource is
|
|
1937
|
+
footprint; a process the helper cannot read, another user's
|
|
1938
|
+
or one that just exited, counts its resident size. Equals
|
|
1939
|
+
residentMb when memorySource is rss.
|
|
1284
1940
|
|
|
1285
1941
|
Each process counts in exactly one owner, the one whose root process is
|
|
1286
1942
|
its nearest ancestor, so the supervisor a build started counts as Metro,
|
|
1287
1943
|
not as the build, and a Gradle or Kotlin daemon counts in the build that
|
|
1288
1944
|
started it until that build exits, then as shared. Processes with no owner
|
|
1289
1945
|
are left out. machine comes from one host ps, the one status reads for
|
|
1290
|
-
device activity, and
|
|
1291
|
-
live and no build runs: status then
|
|
1946
|
+
device activity, and one run of the footprint helper. It is null when no
|
|
1947
|
+
simulator is booted, no workspace is live and no build runs: status then
|
|
1948
|
+
runs neither. \`status --watch --json\` rereads both every 15 seconds while
|
|
1292
1949
|
machine is not null, with no other subprocess.`
|
|
1293
1950
|
},
|
|
1294
1951
|
plan: {
|
|
@@ -1311,7 +1968,9 @@ RULES
|
|
|
1311
1968
|
outcome "hit" or "cold"; null when the run would refuse
|
|
1312
1969
|
expectedMs the median wall time of this project's last successful runs
|
|
1313
1970
|
with that outcome on that platform, or null with none
|
|
1314
|
-
basis how many runs expectedMs comes from (at most
|
|
1971
|
+
basis how many runs expectedMs comes from (at most 20: runs that
|
|
1972
|
+
set up their device and runs that did not are not told apart
|
|
1973
|
+
before the run)
|
|
1315
1974
|
missReason on a predicted cold build with cache reads on, why the
|
|
1316
1975
|
cache has no app, in the shape of lastBuilds.<platform>
|
|
1317
1976
|
.missReason (\`guide facts status\`), compared with the same
|
|
@@ -1338,14 +1997,61 @@ RULES
|
|
|
1338
1997
|
{ "version": 1,
|
|
1339
1998
|
"project": { "key": "<path>", "ios": <bucket|null>,
|
|
1340
1999
|
"android": <bucket|null> } | null,
|
|
1341
|
-
"machine": { "ios": <bucket|null>, "android": <bucket|null> }
|
|
2000
|
+
"machine": { "ios": <bucket|null>, "android": <bucket|null> },
|
|
2001
|
+
"offload": { "today": { "here", "offloaded", "fellBack" },
|
|
2002
|
+
"machines": { "<machine>": { "today": <day>,
|
|
2003
|
+
"total": <totals> } },
|
|
2004
|
+
"placements": [<placement>, ...] },
|
|
2005
|
+
"agentDevice": <AgentDeviceUsage|null>,
|
|
2006
|
+
"swiftpmCache": <SwiftpmCacheUsage|null> }
|
|
1342
2007
|
|
|
1343
2008
|
\`project\` is null outside a project; a platform with no run yet is null.
|
|
1344
2009
|
A bucket carries runs, failed, hits, misses, coldRuns, coldRunMs, hitRuns,
|
|
1345
2010
|
hitRunMs, timeSavedMs, firstRunAt and lastRunAt, plus lastColdBuildMs and
|
|
1346
|
-
lastPodsMs once the project has compiled or installed pods
|
|
2011
|
+
lastPodsMs once the project has compiled or installed pods, and
|
|
2012
|
+
offloadedRuns, offloadedRunMs and lastOffloadHost once a build machine
|
|
2013
|
+
compiled one. An offloaded run is a miss but not a cold run, so the cold
|
|
2014
|
+
average, time saved and build estimates stay local. Milliseconds are
|
|
1347
2015
|
integers.
|
|
1348
2016
|
|
|
2017
|
+
AGENT-DEVICE DISK USAGE
|
|
2018
|
+
agentDevice: { version: 1, measuredAt, bytes, complete, stateDir,
|
|
2019
|
+
runnerBuilds, workspaces, hosted }
|
|
2020
|
+
bytes sums known state, separate runner builds, workspace and hosted bytes;
|
|
2021
|
+
complete is false when a measurement fails. Individual unknown byte fields
|
|
2022
|
+
are null. Absent directories have zero bytes and present: false.
|
|
2023
|
+
stateDir defaults to ~/.agent-device. AGENT_DEVICE_STATE_DIR overrides it
|
|
2024
|
+
only when it does not overlap a workspace agent-device dir or the hosted dir.
|
|
2025
|
+
stateDir: { dir, present, bytes, sessions: { dir, bytes, count },
|
|
2026
|
+
logs: { dir, bytes }, other: { bytes, largest: [{ name, bytes }] } }
|
|
2027
|
+
runnerBuilds: { dir, present, bytes, sharedBytes, platforms: [
|
|
2028
|
+
{ platform, dir, bytes, entries: [{ name, dir, bytes, lastUsedAt,
|
|
2029
|
+
packageVersion, xcodeBuildVersion, inUse, inUseReason }] }] }
|
|
2030
|
+
inUseReason is lease, lock, unreadable or null. Lease means a live owner or
|
|
2031
|
+
runner matched by start time; lock means a live or unknown lock owner.
|
|
2032
|
+
Unreadable lock owners and unreadable or unknown leases use unreadable.
|
|
2033
|
+
Dead lock owners and leases do not mark entries in use.
|
|
2034
|
+
workspaces: [{ dir, projectRoot, bytes }]; hosted: { dir, bytes, sessions } | null.
|
|
2035
|
+
$STIM_HOME/agent-device-usage.json caches the measurement and resolved roots
|
|
2036
|
+
for 10 minutes. stats and unscoped gc measure; no other command measures this
|
|
2037
|
+
state. Server stats.get reads only the cached value.
|
|
2038
|
+
Stim never trims or deletes the shared runner builds, sessions, logs and other
|
|
2039
|
+
state or the hosted driver dir; a workspace's own agent-device dir goes only with its workspace.
|
|
2040
|
+
|
|
2041
|
+
SWIFTPM CACHE DISK USAGE
|
|
2042
|
+
swiftpmCache: { version: 1, measuredAt, dir, present, bytes, complete } | null.
|
|
2043
|
+
The user-level cache is ~/Library/Caches/org.swift.swiftpm on macOS and
|
|
2044
|
+
org.swift.swiftpm under XDG_CACHE_HOME (default ~/.cache) elsewhere.
|
|
2045
|
+
It is shared by every SwiftPM build on the machine; Stim never deletes it.
|
|
2046
|
+
bytes is null and complete is false if du fails; absent directories have
|
|
2047
|
+
present: false, bytes: 0 and complete: true. Plain output omits absent caches.
|
|
2048
|
+
$STIM_HOME/swiftpm-cache-usage.json caches the measurement for 10 minutes,
|
|
2049
|
+
keyed on the resolved dir. stats and unscoped gc alone run du -sk with a
|
|
2050
|
+
20-second timeout. Server stats.get reads only the cached value. status,
|
|
2051
|
+
start, ios, android and the budget gate never measure it. gc reports it as
|
|
2052
|
+
sections.swiftpmCache, never in caches or actionable; --delete and
|
|
2053
|
+
--older-than leave it untouched, and any --cache scope reports null.
|
|
2054
|
+
|
|
1349
2055
|
HOW A RUN IS COUNTED (\`stats\`)
|
|
1350
2056
|
Every \`ios\` or \`android\` invocation that got as far as computing a
|
|
1351
2057
|
cache key is one run, in this project's bucket and in the machine-wide one.
|
|
@@ -1365,7 +2071,8 @@ HOW A RUN IS COUNTED (\`stats\`)
|
|
|
1365
2071
|
is therefore an ESTIMATE and is printed as one. The file is
|
|
1366
2072
|
$STIM_HOME/stats.json (see \`guide lifecycle builds\`). Beside the
|
|
1367
2073
|
aggregates it keeps the last 10 successful runs per project, platform and
|
|
1368
|
-
outcome (hit or cold)
|
|
2074
|
+
outcome (hit or cold), plus the last 10 that created, adopted or cold-booted
|
|
2075
|
+
their device, that did not wait for another workspace's build: each
|
|
1369
2076
|
one's duration and per-phase durations. \`stim status\` estimates a running
|
|
1370
2077
|
build from them (see \`guide facts status\`); \`stats --json\` does not
|
|
1371
2078
|
print them.
|
|
@@ -1386,7 +2093,30 @@ HOW A RUN IS COUNTED (\`stats\`)
|
|
|
1386
2093
|
a hang. A project with no record yet gets \`(1m00s)\`, the elapsed alone,
|
|
1387
2094
|
and a warm run has no long phase to size. That read takes no lock and
|
|
1388
2095
|
ignores what it cannot read, so nothing about statistics can change a
|
|
1389
|
-
run's outcome
|
|
2096
|
+
run's outcome.
|
|
2097
|
+
|
|
2098
|
+
BUILD PLACEMENT (\`offload\`)
|
|
2099
|
+
Every run that compiles records where it built and why, in a placement: { at, project, platform,
|
|
2100
|
+
decision, reason, machine?, buildMs?, slotWaitMs?, localEstimateMs?, failed? }.
|
|
2101
|
+
slotWaitMs is whole milliseconds waiting for a build slot, present only when positive.
|
|
2102
|
+
decision is "here", "offloaded", or "fell-back" (it tried a build machine
|
|
2103
|
+
and built here). reason is why: the run's \`placement:\` reason, such as
|
|
2104
|
+
"load 0.6/core, 1 of 3 build slots busy here" (auto keeps the build here
|
|
2105
|
+
while this Mac has room) or "offload.mode is off" (which prints no line),
|
|
2106
|
+
or for a fallback the cause, prefixed by the machine it concerns, such as
|
|
2107
|
+
"mini: no less loaded (load 1.2/core there, 0.6/core here)". buildMs is the compile here, or the offloaded build's
|
|
2108
|
+
total (offer, sync, build and fetch); localEstimateMs is this project's
|
|
2109
|
+
last cold build here before the run. A run that hits the cache or waits
|
|
2110
|
+
for another workspace's build compiles nothing and records none.
|
|
2111
|
+
placements lists them newest first: the last 100 from the last 7 days.
|
|
2112
|
+
Per machine, \`total\` keeps { offloaded, offloadedMs, savedMs, fallbacks,
|
|
2113
|
+
lastOffloadAt?, lastFallbackAt? } for good and \`today\` counts { offloaded,
|
|
2114
|
+
offloadedMs, savedMs, fallbacks } from the placements on this Mac's local
|
|
2115
|
+
calendar day. savedMs sums localEstimateMs minus buildMs over offloaded
|
|
2116
|
+
builds that had an estimate, so it is an ESTIMATE and is negative when the
|
|
2117
|
+
machine was slower. \`offload.today\` counts the day's placements by
|
|
2118
|
+
decision. A fallback counts against a machine only when the run knows
|
|
2119
|
+
which one: the machine that failed, or the only one paired.`
|
|
1390
2120
|
}
|
|
1391
2121
|
}
|
|
1392
2122
|
},
|
|
@@ -1410,10 +2140,49 @@ another worktree's bundler.
|
|
|
1410
2140
|
--wait, it does not wait for a device lease.
|
|
1411
2141
|
--remote expose Metro for a remote device
|
|
1412
2142
|
|
|
1413
|
-
Plain \`stim start\` is local and does not create a
|
|
1414
|
-
comes from \`start --remote\`, \`ios.remote\`, or \`android.remote\`. The
|
|
2143
|
+
Plain \`stim start\` is local and does not create a tunnel. Remote intent
|
|
2144
|
+
comes from \`start --remote\`, eas/proxy in \`ios.remote\`, or \`android.remote\`. The
|
|
1415
2145
|
\`metro.tunnel\` setting selects the provider after remote intent exists.
|
|
1416
2146
|
|
|
2147
|
+
HOSTED IOS METRO
|
|
2148
|
+
ios --remote <machine> keeps Metro local and needs no start --remote.
|
|
2149
|
+
The supervisor watches private gateway requests in workspace state, binds
|
|
2150
|
+
only this Mac's Tailscale address and accepts only the pinned hosting peer
|
|
2151
|
+
with a per-session secret. The host's bridge is loopback-only. Port and secret
|
|
2152
|
+
stay in 0600 workspace state and never appear in status, JSON or logs.
|
|
2153
|
+
metro.tunnel and metro.publicUrl are ignored. Supervisor restart recreates
|
|
2154
|
+
the recorded gateway; stop or a stopped hosted session closes it. A recorded
|
|
2155
|
+
hosted session prevents Metro idle stop. Release runs skip the bridge.
|
|
2156
|
+
Hosted Debug runs require the supervisor's private gateway support before
|
|
2157
|
+
reservation. --no-metro-check refuses; restart a missing or older supervisor
|
|
2158
|
+
with stim stop; stim start.
|
|
2159
|
+
Native iOS logs are pulled from the host by stim logs, including --errors;
|
|
2160
|
+
JavaScript logs still arrive through Metro. Stop pulls native logs before
|
|
2161
|
+
deleting the simulator. Read guide lifecycle hosted-ios for placement and cleanup.
|
|
2162
|
+
|
|
2163
|
+
TAILNET-ONLY METRO
|
|
2164
|
+
Install Tailscale and sign in on this machine and the remote device or
|
|
2165
|
+
agent-device daemon's host, then select the mode explicitly:
|
|
2166
|
+
|
|
2167
|
+
stim settings set metro.tunnel tailscale --scope workspace
|
|
2168
|
+
stim start --remote
|
|
2169
|
+
|
|
2170
|
+
The device must be on the same tailnet, so the eas backend refuses it. "auto"
|
|
2171
|
+
never selects tailscale.
|
|
2172
|
+
Stim runs a foreground \`tailscale serve --https=<metro port>
|
|
2173
|
+
http://127.0.0.1:<metro port>\` process, never Funnel. It prints
|
|
2174
|
+
https://<tailnet-name>:<metro port> and \`status\` reports the recorded
|
|
2175
|
+
provider and URL. Stim reads the URL from the child's pipes, without a log
|
|
2176
|
+
file, and skips the public bundle probe for this recorded tunnel: this
|
|
2177
|
+
machine may not reach its own tailnet name.
|
|
2178
|
+
|
|
2179
|
+
\`stop\` and \`worktree remove\` stop only the recorded process after
|
|
2180
|
+
verifying its identity; ending that foreground session removes its route.
|
|
2181
|
+
A route Stim did not create is never touched. If the port already has a
|
|
2182
|
+
serve route, Tailscale refuses and Stim prints Tailscale's error message.
|
|
2183
|
+
\`tailscale serve status --json\` shows foreground sessions under the
|
|
2184
|
+
top-level Foreground key; plain \`tailscale serve status\` does not.
|
|
2185
|
+
|
|
1417
2186
|
BUNDLE WARMUP
|
|
1418
2187
|
After verifying Metro, \`stim ios\` and \`stim android\` prefetch the
|
|
1419
2188
|
platform's development bundle while native work continues. Expo supplies the
|
|
@@ -1441,7 +2210,7 @@ BUNDLE WARMUP
|
|
|
1441
2210
|
REMOTE DEVICE BACKENDS
|
|
1442
2211
|
Metro exposure and device selection are separate:
|
|
1443
2212
|
|
|
1444
|
-
stim start --remote prepare
|
|
2213
|
+
stim start --remote prepare remote Metro
|
|
1445
2214
|
stim ios --remote proxy use an agent-device daemon
|
|
1446
2215
|
stim android --remote eas create an EAS Simulator session
|
|
1447
2216
|
|
|
@@ -1452,6 +2221,12 @@ REMOTE DEVICE BACKENDS
|
|
|
1452
2221
|
requires AGENT_DEVICE_DAEMON_BASE_URL and
|
|
1453
2222
|
AGENT_DEVICE_DAEMON_AUTH_TOKEN. Stim creates no remote session for it.
|
|
1454
2223
|
|
|
2224
|
+
An iOS remote build compiles one simulator architecture, since xcodebuild
|
|
2225
|
+
cannot target the remote simulator itself: the proxy daemon's host
|
|
2226
|
+
architecture from its /health hostArch, and arm64 when the daemon does not
|
|
2227
|
+
report one, cannot be reached within 3 seconds, or runs on EAS, whose hosts
|
|
2228
|
+
are Apple silicon. The cache key ends in that architecture.
|
|
2229
|
+
|
|
1455
2230
|
The EAS backend needs eas-cli 21.6.0 or later and an account with EAS
|
|
1456
2231
|
Simulator access. An EAS session is billable. Stim prepares the app before
|
|
1457
2232
|
it creates or reconnects the session, so a failed build starts no session
|
|
@@ -1466,7 +2241,17 @@ IDEMPOTENT
|
|
|
1466
2241
|
A healthy dev server on the reserved port is a no-op: \`start\` prints the
|
|
1467
2242
|
facts with alreadyRunning: true and starts nothing. A foreign process holding
|
|
1468
2243
|
the reserved port moves the RESERVATION instead, so the project is never
|
|
1469
|
-
stranded on a port it can never use.
|
|
2244
|
+
stranded on a port it can never use. A port pinned with metro.port or
|
|
2245
|
+
STIM_METRO_PORT never moves: \`start\` refuses with STIM_BAD_ARG and names
|
|
2246
|
+
the holder before anything starts or the pin is reserved. This project's
|
|
2247
|
+
own Metro already on the pin is attached to. Changing the pin while this
|
|
2248
|
+
workspace's dev server runs refuses until stim stop; a supervisor that cannot
|
|
2249
|
+
be verified also refuses, until you stop it with the tool that started it.
|
|
2250
|
+
You can also unset or restore the pin. stim web follows
|
|
2251
|
+
the same rules when its page uses Metro.
|
|
2252
|
+
ios and android with --no-metro-check use the pin, then the recorded port,
|
|
2253
|
+
then 8081, without probing, reserving or writing the registry. An invalid
|
|
2254
|
+
pin still refuses with STIM_BAD_ARG. Release builds still skip Metro.
|
|
1470
2255
|
|
|
1471
2256
|
WHAT THE SUPERVISOR IS
|
|
1472
2257
|
One detached process per workspace. There is no machine-wide daemon, nothing
|
|
@@ -1517,7 +2302,10 @@ WHAT THE SUPERVISOR IS
|
|
|
1517
2302
|
connected app alone does not keep the server. It records a
|
|
1518
2303
|
supervisor_idle_stopped line in metro.ndjson and devServerStop in
|
|
1519
2304
|
state.json, so \`status\` shows "stopped (idle)" rather than a crash.
|
|
1520
|
-
Devices stay booted
|
|
2305
|
+
Devices stay booted, unless devices.idleShutdownMinutes is set: then the
|
|
2306
|
+
idle stop first shuts down the workspace's owned devices idle for the
|
|
2307
|
+
shorter of the two (\`guide lifecycle budget\`). The next \`stim start\`
|
|
2308
|
+
starts it again and clears the
|
|
1521
2309
|
record, also when it reuses a dev server that another process started. A
|
|
1522
2310
|
Debug \`ios\` or \`android\` run starts it the same way and reports
|
|
1523
2311
|
devServer.reason "stopped (idle)". The setting
|
|
@@ -1547,6 +2335,23 @@ WHAT THE SUPERVISOR IS
|
|
|
1547
2335
|
supervisor logs supervisor_vanished (level warn) for it, and until then
|
|
1548
2336
|
\`status\` reports metro.lastStop.reason "vanished".
|
|
1549
2337
|
|
|
2338
|
+
WATCHMAN ROOTS: a Metro that uses watchman registers a watch root in the
|
|
2339
|
+
shared watchman daemon and never removes it. Bare React Native Metro uses
|
|
2340
|
+
watchman whenever it is installed; Expo SDK 56 and later use it only when
|
|
2341
|
+
metro.config.js sets resolver.useWatchman to true. When the supervisor stops
|
|
2342
|
+
its dev server, it removes with \`watchman watch-del\` a root that appeared
|
|
2343
|
+
after it started, contains the workspace and carried this Metro's
|
|
2344
|
+
subscription, once no other client subscribes to it or has a trigger on it.
|
|
2345
|
+
It logs watchman_root_removed, or watchman_root_kept with the reason, in
|
|
2346
|
+
metro.ndjson. It never starts the daemon and leaves every other root alone,
|
|
2347
|
+
including those jest registers.
|
|
2348
|
+
A supervisor that dies without stopping leaves its root: remove it with
|
|
2349
|
+
\`watchman watch-del <root>\`. Removing a root stops its recrawls; the
|
|
2350
|
+
daemon's memory footprint shrinks only when the daemon restarts, which drops
|
|
2351
|
+
the watches of every other client. \`stim gc --cache watchman\` reports the
|
|
2352
|
+
daemon's footprint, roots and clients, and with --delete shuts it down only
|
|
2353
|
+
when no client is connected (\`guide cleanup memory\`).
|
|
2354
|
+
|
|
1550
2355
|
ENVIRONMENT: the supervisor -- and through it the dev server, including a
|
|
1551
2356
|
metro.config.js evaluated inside the expo child -- inherits the environment
|
|
1552
2357
|
of the \`start\` call that SPAWNED it. A later \`start\` that finds a healthy
|
|
@@ -1607,7 +2412,14 @@ If Stim is not installed globally, replace stim with npx stim.
|
|
|
1607
2412
|
get <label> allocates on first use and prints only the number to stdout.
|
|
1608
2413
|
Repeated calls reuse it, even while a server is listening. Labels start with
|
|
1609
2414
|
a letter and contain up to 64 letters, digits, underscores, or hyphens.
|
|
1610
|
-
metro is reserved; use stim start and stim stop for managed Metro
|
|
2415
|
+
metro is reserved; use stim start and stim stop for managed Metro, and
|
|
2416
|
+
metro.port or STIM_METRO_PORT to choose its number (guide settings).
|
|
2417
|
+
Changing the pin while this workspace's dev server runs refuses until
|
|
2418
|
+
stim stop; an unverified supervisor also blocks the change until you stop it with the tool that started it. A foreign holder
|
|
2419
|
+
of the pin is refused before anything starts; this project's own Metro on
|
|
2420
|
+
it is attached to. stim web follows these rules when its page uses Metro.
|
|
2421
|
+
ios and android with --no-metro-check use the pin, then the recorded port,
|
|
2422
|
+
then 8081, without probing or reserving; an invalid pin still refuses.
|
|
1611
2423
|
|
|
1612
2424
|
New allocations scan TCP ports 8900-8999. They skip registry reservations
|
|
1613
2425
|
and existing listeners, announcing occupied ports and upward retries on
|
|
@@ -1659,6 +2471,34 @@ instead of allocating a separate label in every worktree.`
|
|
|
1659
2471
|
summary: "Querying the merged NDJSON timeline, and what --errors means",
|
|
1660
2472
|
body: () => `LOGS
|
|
1661
2473
|
|
|
2474
|
+
If Stim is not installed globally, replace stim with npx stim.
|
|
2475
|
+
|
|
2476
|
+
AGENT-DEVICE WORKSPACE STATE
|
|
2477
|
+
|
|
2478
|
+
When you drive a device yourself with agent-device, opt in to the workspace's
|
|
2479
|
+
state directory reported as agentDevice.stateDir in the stim ios, android and
|
|
2480
|
+
macos output and in status. Read it once from status, run from the app root:
|
|
2481
|
+
|
|
2482
|
+
STATE_DIR="$(stim status --json | jq -r '.environments[] | select(.path=="'"$(pwd -P)"'") | .agentDevice.stateDir')"
|
|
2483
|
+
|
|
2484
|
+
Then prefix each agent-device command, so the choice survives shells that do
|
|
2485
|
+
not keep exports:
|
|
2486
|
+
|
|
2487
|
+
AGENT_DEVICE_STATE_DIR="$STATE_DIR" agent-device open <bundle id> --platform ios --session <name>
|
|
2488
|
+
AGENT_DEVICE_STATE_DIR="$STATE_DIR" agent-device snapshot --session <name>
|
|
2489
|
+
|
|
2490
|
+
The path is $STIM_HOME/workspaces/<name>/agent-device. agent-device creates it
|
|
2491
|
+
itself. Sessions (including events, runner logs and request text), logs,
|
|
2492
|
+
metro-sessions, allocations and daemon files move there. The apple-runner build
|
|
2493
|
+
cache and device claims stay shared; do not set
|
|
2494
|
+
AGENT_DEVICE_IOS_RUNNER_DERIVED_PATH or AGENT_DEVICE_CLAIMS_DIR.
|
|
2495
|
+
Each active workspace adds one agent-device daemon, which exits after five
|
|
2496
|
+
idle minutes with no open session. The variable is opt-in per command: forgetting it falls back to
|
|
2497
|
+
~/.agent-device.
|
|
2498
|
+
stim stop closes owned-device sessions there, stim logs --source agent reads
|
|
2499
|
+
them, and stim worktree remove stops the daemon and removes the directory.
|
|
2500
|
+
Hosted/stim-server sessions use their own state directory and need none of this.
|
|
2501
|
+
|
|
1662
2502
|
stim logs [filters]
|
|
1663
2503
|
|
|
1664
2504
|
Reads every *.ndjson file in the global workspace logs directory, merges them into one timeline
|
|
@@ -1688,11 +2528,16 @@ The only exit-1 paths are a malformed query and no project.
|
|
|
1688
2528
|
Device collectors and build records carry their named slot. Use --slot phone
|
|
1689
2529
|
for that slot's timeline; its launch marker cannot hide a sibling's errors.
|
|
1690
2530
|
Untagged legacy records belong to default. Metro/client records are shared and
|
|
1691
|
-
usually untagged
|
|
2531
|
+
usually untagged, so every --slot query includes them, windowed by that slot's
|
|
2532
|
+
own launch and by bundles of the platforms it launched. When another slot's
|
|
2533
|
+
relaunch hides a Metro error from the unfiltered query, --slot <name> for the
|
|
2534
|
+
slot that printed it still shows it.
|
|
1692
2535
|
|
|
1693
2536
|
FLAGS
|
|
1694
|
-
--slot <name> only this slot's records
|
|
1695
|
-
|
|
2537
|
+
--slot <name> only this slot's records plus, once it has launched, the
|
|
2538
|
+
shared untagged Metro and app client records (not the web
|
|
2539
|
+
page's); default also includes untagged legacy records
|
|
2540
|
+
--source <s...> metro, client, device, build, agent, maintenance (one or more), or all.
|
|
1696
2541
|
An unknown value is REJECTED rather than quietly matching
|
|
1697
2542
|
nothing.
|
|
1698
2543
|
--level <l> minimum level: debug, info, warn, error, fatal
|
|
@@ -1705,7 +2550,12 @@ FLAGS
|
|
|
1705
2550
|
owned Chrome's device errors (platform web: failed
|
|
1706
2551
|
requests, browser errors).
|
|
1707
2552
|
Capped at 20 printed records.
|
|
1708
|
-
--follow keep streaming until interrupted (Ctrl+C is exit 0)
|
|
2553
|
+
--follow keep streaming until interrupted (Ctrl+C is exit 0). Closing
|
|
2554
|
+
its stdout also ends it with exit 0; on Linux, so does the
|
|
2555
|
+
exit of the process that started it when stdout is a pipe.
|
|
2556
|
+
It notices within a few seconds even when nothing new
|
|
2557
|
+
arrives, except a closed stdout pipe on Linux whose starter
|
|
2558
|
+
is still running, which it notices at the next record.
|
|
1709
2559
|
--json the raw records, one per line, so stdout is valid NDJSON.
|
|
1710
2560
|
ZERO matches is ZERO bytes on stdout (an empty NDJSON
|
|
1711
2561
|
stream), exit 0 -- parse stdout line by line, never as one
|
|
@@ -1755,9 +2605,19 @@ FLAGS
|
|
|
1755
2605
|
errors are reported -- and nothing else. A failed attempt's own summary
|
|
1756
2606
|
and details land at or after its marker, so they stay reported.
|
|
1757
2607
|
a LAUNCH marker (src build, written before \`ios\` / \`android\` attempts
|
|
1758
|
-
launch) resets EVERYTHING except the web page's records. It
|
|
1759
|
-
tool call so an immediate native crash is not hidden by a
|
|
1760
|
-
after the process died.
|
|
2608
|
+
launch) resets EVERYTHING in its slot except the web page's records. It
|
|
2609
|
+
precedes the tool call so an immediate native crash is not hidden by a
|
|
2610
|
+
marker written after the process died. Metro and client output names no
|
|
2611
|
+
device, so in the unfiltered query the newest launch of any slot resets
|
|
2612
|
+
it, and the newest bundle of any platform resets Metro errors; with
|
|
2613
|
+
--slot <name>, that slot's newest launch and the newest bundle of a
|
|
2614
|
+
platform it launched do. Metro records that name their platform (bare
|
|
2615
|
+
React Native bundle records; the "iOS Bundled" / "Android Bundled" /
|
|
2616
|
+
"RSC(iOS) Bundled" tag Expo prints for bundle markers, so a "DOM
|
|
2617
|
+
Bundled" line does not close a native slot's window) follow only that
|
|
2618
|
+
platform, and a slot query leaves out iOS or Android ones when it never
|
|
2619
|
+
launched that platform. A single slot that runs both iOS and Android still shares one
|
|
2620
|
+
window for untagged Metro errors.
|
|
1761
2621
|
a PAGE-LOAD marker (the web_navigation record \`stim web\` writes for each
|
|
1762
2622
|
load of the page's top-level document) resets only the page's records,
|
|
1763
2623
|
those with platform web. It is written when the load starts, so a
|
|
@@ -1800,9 +2660,12 @@ FLAGS
|
|
|
1800
2660
|
In --follow mode the marker window is dropped -- every error arriving from
|
|
1801
2661
|
then on is by definition after the last marker seen.
|
|
1802
2662
|
|
|
1803
|
-
\`stim status\` reports the same
|
|
1804
|
-
logs.errorsSinceMarker
|
|
1805
|
-
|
|
2663
|
+
\`stim status\` reports the errors of the same query and scope per
|
|
2664
|
+
workspace, as logs.errorsSinceMarker, so the two never disagree about
|
|
2665
|
+
whether this workspace is failing. It counts failures as the Stim Desktop and
|
|
2666
|
+
phone logs list them: a Metro bundle failure is one error, although it is a
|
|
2667
|
+
marker line, the error line and a failed bundle response in the log, so the
|
|
2668
|
+
count can be lower than the records this command prints.
|
|
1806
2669
|
|
|
1807
2670
|
THE RECORD
|
|
1808
2671
|
{ ts, src, level, msg } always. ts is epoch milliseconds; src is one of
|
|
@@ -1822,7 +2685,14 @@ THE RECORD
|
|
|
1822
2685
|
there are none. Stim adds it at query time; it is not in the
|
|
1823
2686
|
log files.
|
|
1824
2687
|
|
|
2688
|
+
Maintenance records use src: maintenance and appear in the plain timeline.
|
|
2689
|
+
stim logs --source maintenance
|
|
2690
|
+
Add --errors to --source maintenance to show only maintenance_failure events;
|
|
2691
|
+
failures before a later launch marker are hidden. Machine passes are reported in
|
|
2692
|
+
status and maintenance/maintenance.ndjson; this command reads workspace logs.
|
|
2693
|
+
|
|
1825
2694
|
WHAT WRITES WHAT
|
|
2695
|
+
maintenance.ndjson workspace maintenance actions, failures and explaining skips
|
|
1826
2696
|
metro.ndjson the bundler, in both supervisor modes
|
|
1827
2697
|
client.ndjson in-app console logs and redboxes -- BARE PROJECTS ONLY.
|
|
1828
2698
|
In expo-child mode everything Expo prints lands in
|
|
@@ -1960,13 +2830,18 @@ WHAT WRITES WHAT
|
|
|
1960
2830
|
archive rather than a stream. Streaming with full fidelity needs
|
|
1961
2831
|
libimobiledevice or pymobiledevice3, which are third-party installs Stim
|
|
1962
2832
|
does not require. See appandflow/stim#179.
|
|
1963
|
-
agent
|
|
1964
|
-
|
|
1965
|
-
|
|
2833
|
+
agent agent input on the owned Chrome page is in
|
|
2834
|
+
web.ndjson (\`guide web\`). For simulators and
|
|
2835
|
+
emulators, no file: each query reads agent-device's
|
|
2836
|
+
session records (~/.agent-device/sessions, or
|
|
2837
|
+
AGENT_DEVICE_STATE_DIR, plus existing workspace
|
|
2838
|
+
agent-device directories) for this workspace's owned
|
|
1966
2839
|
simulators and emulators, read-only. An action is
|
|
1967
2840
|
info (msg is agent-device's summary, e.g. "Tapped
|
|
1968
2841
|
(201, 731)"; event agent_action with command, session,
|
|
1969
|
-
deviceId and details
|
|
2842
|
+
deviceId and details, and startedAt when agent-device
|
|
2843
|
+
logged the command's start: ts is when it finished,
|
|
2844
|
+
after any --settle wait); a failed command is error
|
|
1970
2845
|
(event agent_failed). A plain query includes them;
|
|
1971
2846
|
--errors only with --source agent or all. iOS sessions
|
|
1972
2847
|
match a simulator through their runner.log, Android
|
|
@@ -1997,6 +2872,23 @@ WHAT WRITES WHAT
|
|
|
1997
2872
|
Every refusal listed here carries a stable CODE, whichever command prints it.
|
|
1998
2873
|
Branch on the code, never on the message.`,
|
|
1999
2874
|
sections: {
|
|
2875
|
+
STIM_HOSTING_REFUSED: {
|
|
2876
|
+
summary: "a named hosting Mac refused or could not confirm its iOS session; no local fallback",
|
|
2877
|
+
body: () => `STIM_HOSTING_REFUSED
|
|
2878
|
+
The message names the hosting Mac and its reason: unreachable or changed
|
|
2879
|
+
tailnet node, no installed simulator choice, no capacity, elevated or unknown
|
|
2880
|
+
memory pressure, or an unresolved reservation or delivery. Stim boots nothing
|
|
2881
|
+
locally and never tries another Mac. Check stim-server and Tailscale there,
|
|
2882
|
+
then run stim doctor. Correct --device-type / --runtime when the offer names
|
|
2883
|
+
an unavailable choice. A session that exists stays recorded even if delivery
|
|
2884
|
+
fails: retry stim ios --remote <machine>, or run stim stop to reconcile it.
|
|
2885
|
+
An unreachable stop keeps the placement; rerun stim stop when the host answers.
|
|
2886
|
+
A failed build handoff uses upload instead. If native log queries are unavailable,
|
|
2887
|
+
logs prints a note on stderr and shows copied records. Update an older stim-server
|
|
2888
|
+
on the host to enable iOS handoff and native logs (hello feature hosted-ios-data).
|
|
2889
|
+
A name outside hosting.machines is STIM_BAD_ARG. Missing or pending hosting
|
|
2890
|
+
approval keeps the doctor --fix and stim-server devices grant remedies.`
|
|
2891
|
+
},
|
|
2000
2892
|
STIM_EAS_BUILD_MISSING: {
|
|
2001
2893
|
summary: "no completed EAS development build matches; build only with session authorization",
|
|
2002
2894
|
separator: "--- EAS BUILD CODES (`ios --eas-profile` / `android --eas-profile`) ---",
|
|
@@ -2163,8 +3055,12 @@ Branch on the code, never on the message.`,
|
|
|
2163
3055
|
On a release cache hit Stim regenerates this workspace's JS bundle into a
|
|
2164
3056
|
COPY of the cached artifact before installing it -- \`ios --configuration
|
|
2165
3057
|
Release\` into a copy of the .app, \`android --variant ...Release\` into a
|
|
2166
|
-
copy of the APK.
|
|
2167
|
-
|
|
3058
|
+
copy of the APK. An iOS app that sets React Native's RCTUseAssetCatalog
|
|
3059
|
+
Info.plist key also gets its RNAssets.bundle image catalog recompiled with
|
|
3060
|
+
actool from the new bundle. When any step of that swap fails (reading the
|
|
3061
|
+
app's Info.plist, the bundle command, hermesc, actool, the expo-updates
|
|
3062
|
+
manifest refresh, the re-sign, zipalign, apksigner), the run does NOT
|
|
3063
|
+
install the
|
|
2168
3064
|
cached artifact -- its baked-in JS is the builder's, not yours -- and does
|
|
2169
3065
|
NOT fail: it prints a \`swap failed at <step>: ... --
|
|
2170
3066
|
building fresh instead\` note on stderr and falls back to a full build. If
|
|
@@ -2278,6 +3174,39 @@ Branch on the code, never on the message.`,
|
|
|
2278
3174
|
Legacy inline \`deletionClaim\` fields in config.json have no process
|
|
2279
3175
|
identity and require separate manual inspection; \`stim guide lifecycle pool\`
|
|
2280
3176
|
describes their field-only recovery.`
|
|
3177
|
+
},
|
|
3178
|
+
STIM_MACOS_OWNER_UNVERIFIED: {
|
|
3179
|
+
summary: "the macOS app owner cannot be verified; no signal is sent",
|
|
3180
|
+
body: () => `STIM_MACOS_OWNER_UNVERIFIED
|
|
3181
|
+
The recorded macOS process identity is unavailable or its workspace record
|
|
3182
|
+
is malformed. Stim sends no signal to an owner it cannot verify. Inspect the
|
|
3183
|
+
named process and workspace record before repairing it; do not replace a PID
|
|
3184
|
+
or token with another running app. Retry stim stop once identity inspection
|
|
3185
|
+
works. See stim guide macos.`
|
|
3186
|
+
},
|
|
3187
|
+
STIM_OFFLOAD_REFUSED: {
|
|
3188
|
+
summary: "the selected build machine cannot build this app",
|
|
3189
|
+
body: () => `STIM_OFFLOAD_REFUSED
|
|
3190
|
+
|
|
3191
|
+
A named --build-machine selection is strict. The message names the machine
|
|
3192
|
+
and why it cannot take or finish the build: not configured or paired, approval
|
|
3193
|
+
pending or denied, unreachable or changed pinned identity, incompatible
|
|
3194
|
+
toolchain/runtime/CPU, low disk, busy, sync/build failure, artifact failure,
|
|
3195
|
+
or a checkout that changed while it built. Unsupported build kinds also refuse.
|
|
3196
|
+
No local xcodebuild, Gradle or SwiftPM compile or alternate machine follows a
|
|
3197
|
+
refusal. Prebuild and pod install still run on this Mac before a named offload.
|
|
3198
|
+
Unlisted or unpaired names refuse at setup before consulting the cache, without
|
|
3199
|
+
a build record or failed-run stats. A listed paired name with a cache hit needs
|
|
3200
|
+
no build and does not contact the selected machine.
|
|
3201
|
+
|
|
3202
|
+
Run stim doctor --fix to ask for build access if not paired. Check
|
|
3203
|
+
stim settings get offload.machines. A person on the worker finds the id with
|
|
3204
|
+
stim-server devices and approves this Mac with stim-server devices grant <id> --build,
|
|
3205
|
+
or runs stim-server setup on the worker with its node, ticket and expiry
|
|
3206
|
+
(guide settings). Agents never run setup or approve requests.
|
|
3207
|
+
Rerun with --build-machine auto for normal placement and local fallback, or
|
|
3208
|
+
--build-machine local to keep the build here.
|
|
3209
|
+
`
|
|
2281
3210
|
},
|
|
2282
3211
|
STIM_CLAIM_UNAVAILABLE: {
|
|
2283
3212
|
summary: "a process identity or warm claim store is unavailable, so the protected operation refuses",
|
|
@@ -2755,6 +3684,18 @@ so a Debug run on one is wired to a LAN origin instead of localhost.`,
|
|
|
2755
3684
|
holds one remote session, and Stim will not end the recorded one to make
|
|
2756
3685
|
room -- it may be mid-run for whoever started it. Run \`stim stop\` for this
|
|
2757
3686
|
workspace, then re-run with the platform you want. Nothing was created here.`
|
|
3687
|
+
},
|
|
3688
|
+
STIM_REMOTE_DEVICE_MISMATCH: {
|
|
3689
|
+
summary: "the recorded EAS session runs another model than --device-type asks for; stop, then rerun",
|
|
3690
|
+
body: () => `STIM_REMOTE_DEVICE_MISMATCH
|
|
3691
|
+
\`stim ios --remote eas --device-type <name>\` found this workspace's
|
|
3692
|
+
recorded EAS Simulator session still running another model, or the model
|
|
3693
|
+
EAS chose when no --device-type was given. EAS cannot change a running
|
|
3694
|
+
session's model, and Stim will not end the recorded session to make room --
|
|
3695
|
+
it may be mid-run for whoever started it. Run \`stim stop\` for this
|
|
3696
|
+
workspace, then rerun with the model you want. A recorded session that has
|
|
3697
|
+
already ended is replaced on the requested model instead, and without
|
|
3698
|
+
--device-type a live one is reused as it is. Nothing was created here.`
|
|
2758
3699
|
},
|
|
2759
3700
|
STIM_REMOTE_SESSION_STATE: {
|
|
2760
3701
|
summary: "the EAS session was created but its state could not be recorded, so Stim stopped it",
|
|
@@ -2784,6 +3725,7 @@ so a Debug run on one is wired to a LAN origin instead of localhost.`,
|
|
|
2784
3725
|
The gate that proves a tunnel still reaches THIS workspace's Metro failed --
|
|
2785
3726
|
before a session or a build, whether the tunnel is Expo's own, one Stim
|
|
2786
3727
|
started (metro.tunnel: cloudflared/ngrok/auto), or a named metro.publicUrl.
|
|
3728
|
+
A recorded tailscale tunnel skips this public probe (\`guide metro\`).
|
|
2787
3729
|
The usual cause: the tunnel was built for a port this workspace no longer
|
|
2788
3730
|
holds (a stale one survived a \`stop\`/\`start\` that reserved a different
|
|
2789
3731
|
port), and it now serves ANOTHER workspace's dev server -- healthy, and
|
|
@@ -2848,6 +3790,11 @@ so a Debug run on one is wired to a LAN origin instead of localhost.`,
|
|
|
2848
3790
|
relaunches that app on that device with this workspace's Metro port and loses
|
|
2849
3791
|
in-memory state. Keep the existing --session flag and verify afterward.
|
|
2850
3792
|
|
|
3793
|
+
ADB COULD NOT LIST OR RESTORE THE REVERSE. Before an Android reload Stim
|
|
3794
|
+
checks the emulator's adb reverse for the Metro port. When adb cannot list
|
|
3795
|
+
or re-apply it, nothing was reloaded; the remedy names the adb reverse
|
|
3796
|
+
command to run before reloading again.
|
|
3797
|
+
|
|
2851
3798
|
MORE THAN ONE MATCHING PEER IS NOT A FAILURE. A workspace Metro serves one
|
|
2852
3799
|
app, so several matching peers are that app on several devices. Stim reloads
|
|
2853
3800
|
every one of them and reports the count in the facts as targets.`
|
|
@@ -2971,12 +3918,22 @@ captured" (in metro.ndjson, bare RN)
|
|
|
2971
3918
|
The command refused before doing anything: an unusable --wait value, a known
|
|
2972
3919
|
setting with the wrong type ("Invalid <key> setting <value>. Expected <shape>."
|
|
2973
3920
|
-- \`guide settings\` names the type each key takes), an invalid
|
|
2974
|
-
Metro tunnel setting,
|
|
3921
|
+
Metro tunnel setting, \`stim macos\` with macos.product or macos.infoPlist
|
|
3922
|
+
unset (set both explicitly in .stim.json; see \`stim guide macos\`),
|
|
3923
|
+
\`stim macos --remote eas|proxy|auto\` (name a hosting Mac instead),
|
|
3924
|
+
an invalid android.dataPartitionSizeGb value, an unsafe
|
|
2975
3925
|
android.avdConfig key or fragment, a malformed ios.signingIdentity,
|
|
2976
|
-
ios.signingIdentitySha1 or ios.lanHost value,
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
3926
|
+
ios.signingIdentitySha1 or ios.lanHost value, a metro.port or
|
|
3927
|
+
STIM_METRO_PORT that another workspace reserves or another process holds,
|
|
3928
|
+
a changed pin while this workspace's dev server runs or its supervisor
|
|
3929
|
+
cannot be verified,
|
|
3930
|
+
\`--device\` with an empty
|
|
3931
|
+
serial or UDID, \`--device\` together with \`--remote\`, \`ios --runtime\`
|
|
3932
|
+
on an eas/proxy run (\`--remote\` or ios.remote; that backend picks the
|
|
3933
|
+
iOS version), \`ios --device-type\` on the proxy backend or with an
|
|
3934
|
+
eas-cli older than 22.2.0 on the eas backend, \`android --system-image\` or
|
|
3935
|
+
\`--device-profile\` on a remote run (\`--remote\` or android.remote), a
|
|
3936
|
+
working directory
|
|
2980
3937
|
with no package.json above it, or one whose nearest package.json does not
|
|
2981
3938
|
parse or depends on neither react-native nor expo, so the directory is not
|
|
2982
3939
|
an app (the refusal names that package.json and says which of the two it
|
|
@@ -2996,7 +3953,10 @@ captured" (in metro.ndjson, bare RN)
|
|
|
2996
3953
|
ios.deviceType, ios.runtime, android.systemImage or android.deviceProfile
|
|
2997
3954
|
setting is checked the same way, and the check applies
|
|
2998
3955
|
even when this workspace ALREADY owns a device, so a name that could never
|
|
2999
|
-
create anything is caught rather than left to a later run.
|
|
3956
|
+
create anything is caught rather than left to a later run. When the bad
|
|
3957
|
+
value came from a settings layer rather than a flag, the message also names
|
|
3958
|
+
the layer (workspace, repo, committed, or machine) it is set at, since all
|
|
3959
|
+
four settings are readable from the machine layer too. The
|
|
3000
3960
|
\`pixel_fold\` and \`resizable\` profiles on a system image without
|
|
3001
3961
|
foldable support (SupportPixelFold in its advancedFeatures.ini) refuse the
|
|
3002
3962
|
same way, since the emulator quits on boot; the remedy names an installed
|
|
@@ -3010,6 +3970,12 @@ captured" (in metro.ndjson, bare RN)
|
|
|
3010
3970
|
is on stdout, except \`logs --json\`, whose stdout stays empty NDJSON; the
|
|
3011
3971
|
refusal is on stderr. \`stop\` outside a project refuses rather than
|
|
3012
3972
|
reporting that nothing was running.
|
|
3973
|
+
For a changed Metro pin, run stim stop before retrying, or unset or restore
|
|
3974
|
+
the pin; for an unverified supervisor, stop it with the tool that started it. A foreign holder is refused before anything starts; this project's
|
|
3975
|
+
own Metro on the pin is attached to. stim web follows the same rules when
|
|
3976
|
+
its page uses Metro. ios and android with --no-metro-check use the pin,
|
|
3977
|
+
then the recorded port, then 8081, without probing or reserving; an invalid
|
|
3978
|
+
pin still refuses.
|
|
3013
3979
|
These errors are caught before the port is reserved and before any build or
|
|
3014
3980
|
device work, so nothing was started. The one listing they need
|
|
3015
3981
|
(\`simctl list runtimes\`, the SDK's system-images directory, \`avdmanager
|
|
@@ -3123,6 +4089,16 @@ captured" (in metro.ndjson, bare RN)
|
|
|
3123
4089
|
Matching this workspace's port or directory does not authorize cleanup,
|
|
3124
4090
|
and stop has no override for process ownership.
|
|
3125
4091
|
|
|
4092
|
+
"port <n> is in use by <holder>; stim start will choose a free port" (status)
|
|
4093
|
+
A note, not a problem: no supervisor of this workspace runs, and
|
|
4094
|
+
\`stim start\` reserves a free port when its own is taken. The holder's pid
|
|
4095
|
+
and directory are in \`status --json\` under metro.heldBy.
|
|
4096
|
+
|
|
4097
|
+
"port <n> is in use by <holder>, so this workspace's Metro cannot serve on
|
|
4098
|
+
it" (status)
|
|
4099
|
+
This workspace's supervisor runs but another process answers its port.
|
|
4100
|
+
Run \`stim stop\`, then \`stim start\`, which reserves a free port.
|
|
4101
|
+
|
|
3126
4102
|
"stop refusing to signal supervisor pid <n>: ..." (stop)
|
|
3127
4103
|
The records disagree, the saved OS identity is unavailable, or it records a
|
|
3128
4104
|
port this project did not reserve. If process inspection is denied, retry
|
|
@@ -3176,6 +4152,10 @@ not on any remote" (worktree remove)
|
|
|
3176
4152
|
so per file -- those files die with the worktree either way, and a lockfile
|
|
3177
4153
|
change anyone meant would have been committed. ONE other dirty path and the
|
|
3178
4154
|
whole set is refused, churn included, so this never eats real work.
|
|
4155
|
+
Untracked \`.watchman-cookie-*\` files, which watchman writes into the roots
|
|
4156
|
+
it watches, are not work either: \`worktree remove\` deletes them itself and
|
|
4157
|
+
ignores them when it counts dirty paths. Any other untracked file still
|
|
4158
|
+
refuses.
|
|
3179
4159
|
When it does refuse, the refusal PRINTS THE DIRTY PATHS, and the restore
|
|
3180
4160
|
command under it carries those same paths: run it as printed rather than
|
|
3181
4161
|
reaching for --force.
|
|
@@ -3306,9 +4286,9 @@ not on any remote" (worktree remove)
|
|
|
3306
4286
|
A line such as \`npm warn exec ... will be installed\` is normal when using
|
|
3307
4287
|
the no-install form.
|
|
3308
4288
|
|
|
3309
|
-
"Unsupported engine" or a syntax error before Stim starts
|
|
3310
|
-
Stim requires Node 22.12.0 or later.
|
|
3311
|
-
|
|
4289
|
+
"Unsupported engine" from npm, or a syntax error before Stim starts
|
|
4290
|
+
Stim requires Node 22.12.0 or later. Stim itself refuses an older Node
|
|
4291
|
+
with STIM_NODE_UNSUPPORTED; see that section.
|
|
3312
4292
|
|
|
3313
4293
|
|
|
3314
4294
|
"Found no free Metro port between ..."
|
|
@@ -3391,6 +4371,30 @@ not on any remote" (worktree remove)
|
|
|
3391
4371
|
Expo build-cache provider would each use a different store. Set the named
|
|
3392
4372
|
variable to an absolute path, or unset it to use the default. Metro and the
|
|
3393
4373
|
cache provider, which cannot refuse, ignore a relative value with a warning.`
|
|
4374
|
+
},
|
|
4375
|
+
STIM_NODE_UNSUPPORTED: {
|
|
4376
|
+
summary: "stim or stim-server started on a Node older than 22.12.0, often a project pin",
|
|
4377
|
+
body: () => `STIM_NODE_UNSUPPORTED ("Stim needs Node <floor> or later; this is Node <version> at <path>")
|
|
4378
|
+
stim and stim-server refuse before loading anything else when the Node that
|
|
4379
|
+
runs them is older than their engines floor. Both start through
|
|
4380
|
+
\`#!/usr/bin/env node\`, so the working directory can choose that Node: asdf,
|
|
4381
|
+
mise and Volta's node shim follow the project's .nvmrc, .node-version,
|
|
4382
|
+
.tool-versions or package.json, and nvm and fnm do when a shell hook
|
|
4383
|
+
switches versions on cd. A Stim installed with Volta keeps the Node it was
|
|
4384
|
+
installed with. Run Stim with a supported Node for that one command:
|
|
4385
|
+
|
|
4386
|
+
ASDF_NODEJS_VERSION=<version> stim <command>
|
|
4387
|
+
mise exec node@<version> -- stim <command>
|
|
4388
|
+
volta run --node <version> stim <command>
|
|
4389
|
+
fnm exec --using=<version> stim <command>
|
|
4390
|
+
nvm exec --silent <version> stim <command>
|
|
4391
|
+
|
|
4392
|
+
<version> is 22.12.0 or later. asdf, mise, fnm and nvm keep global packages
|
|
4393
|
+
per Node version, so when Stim was installed with npm under one of them, use
|
|
4394
|
+
the version it was installed with; asdf names it when it prints "No version
|
|
4395
|
+
is set for command stim" instead of running Stim. When that version is
|
|
4396
|
+
older than 22.12.0, first install Stim with npm under a supported version.
|
|
4397
|
+
The tools Stim starts inherit the override.`
|
|
3394
4398
|
}
|
|
3395
4399
|
}
|
|
3396
4400
|
},
|
|
@@ -3481,8 +4485,10 @@ apply. The run then reports \`devServer: { started: true, reason }\` in its
|
|
|
3481
4485
|
JSON, with reason "not running" or "stopped (idle)". When that start fails,
|
|
3482
4486
|
the run refuses with the start's own code (STIM_METRO_TIMEOUT,
|
|
3483
4487
|
STIM_SUPERVISOR_EXITED, ...) before any build work. Release builds and
|
|
3484
|
-
\`--no-metro-check\` neither check nor start the dev server
|
|
3485
|
-
|
|
4488
|
+
\`--no-metro-check\` neither check nor start the dev server; hosted Debug iOS
|
|
4489
|
+
refuses --no-metro-check with STIM_BAD_ARG. \`--remote\`
|
|
4490
|
+
with eas or proxy starts it the way \`stim start --remote\` does. A named
|
|
4491
|
+
hosting Mac keeps Metro local through a private bridge; see hosted-ios. Step 2 remains useful to warm
|
|
3486
4492
|
Metro while other work happens.
|
|
3487
4493
|
|
|
3488
4494
|
Repeat step 3 whenever a NATIVE input changes. A JS-only edit needs nothing --
|
|
@@ -3501,6 +4507,17 @@ ambiguous no-platform request. Every reload goes over this workspace's Metro
|
|
|
3501
4507
|
websocket, on both platforms. It never reopens a development-client URL,
|
|
3502
4508
|
because that restarts the app rather than reloading its JavaScript.
|
|
3503
4509
|
|
|
4510
|
+
An Android reload first lists \`adb reverse\` on each live owned emulator
|
|
4511
|
+
launched against this Metro and re-applies the Metro port's reverse where it is
|
|
4512
|
+
missing. adb ties a reverse to its transport, and a transport that drops and
|
|
4513
|
+
reconnects comes back without it and without any error, which leaves the app
|
|
4514
|
+
cut off from Metro and Fast Refresh. After restoring one, Stim waits up to 8
|
|
4515
|
+
seconds, sending nothing, until the app on every emulator it checked is
|
|
4516
|
+
connected again, then sends the reload once. A dev server that cannot name
|
|
4517
|
+
its clients (bare React Native) gets a fixed 2.5-second wait instead. \`reverseRestored\` in the
|
|
4518
|
+
facts names the serials it restored. \`stim status\` reports the same loss as
|
|
4519
|
+
the android-reverse-missing issue.
|
|
4520
|
+
|
|
3504
4521
|
How the message is addressed depends on the dev server, and \`strategy\` in
|
|
3505
4522
|
the facts reports which you got. Where Metro can name its clients, Stim
|
|
3506
4523
|
addresses every peer matching the platform and reports \`metro-websocket\`. A
|
|
@@ -3546,9 +4563,10 @@ If the app is stuck before loading its first bundle, restart its process with
|
|
|
3546
4563
|
the printed force-stop and launcher commands. Foregrounding the same process
|
|
3547
4564
|
does not restart initialization. Confirm the bundle request and expected UI.
|
|
3548
4565
|
|
|
3549
|
-
Debug Android launches on an owned emulator
|
|
3550
|
-
|
|
3551
|
-
emulators, remote targets, and physical devices keep the
|
|
4566
|
+
Debug Android launches on an owned emulator that the run creates, or whose
|
|
4567
|
+
adoption from the pool it finishes, get up to 60 seconds for bundle loading;
|
|
4568
|
+
the verify phase names that budget. Existing emulators, remote targets, and physical devices keep the
|
|
4569
|
+
20-second budget.
|
|
3552
4570
|
Bundle delivery still requires the usual stability or app readiness check;
|
|
3553
4571
|
an observed fatal error ends verification without waiting for the deadline.
|
|
3554
4572
|
|
|
@@ -3634,6 +4652,166 @@ WAITING FOR A CHANGE
|
|
|
3634
4652
|
still running, which it notices at the next change.
|
|
3635
4653
|
Without --json it reprints the human view on change.`,
|
|
3636
4654
|
sections: {
|
|
4655
|
+
"hosted-ios": {
|
|
4656
|
+
summary: "iOS on a named approved Mac: strict placement, private Metro, status and stop",
|
|
4657
|
+
body: () => `IOS ON A HOSTING MAC
|
|
4658
|
+
|
|
4659
|
+
If Stim is not installed globally, replace stim with npx stim.
|
|
4660
|
+
|
|
4661
|
+
stim settings set hosting.machines '["janics-mac-mini"]'
|
|
4662
|
+
stim doctor --fix
|
|
4663
|
+
# A person there approves: stim-server devices grant <id> --device-host
|
|
4664
|
+
# Or runs stim-server setup on the worker with its client node, ticket and expiry.
|
|
4665
|
+
stim doctor
|
|
4666
|
+
stim ios --remote janics-mac-mini --device-type "iPhone 17 Pro" --runtime "iOS 27.0"
|
|
4667
|
+
stim status --json
|
|
4668
|
+
stim reload ios
|
|
4669
|
+
stim stop
|
|
4670
|
+
|
|
4671
|
+
The same setup serves macos --remote. Approval is separate from build, read
|
|
4672
|
+
and phone control. Set ios.remote to the machine name for a workspace default;
|
|
4673
|
+
no flag or setting runs here. eas and proxy keep their remote backend meanings.
|
|
4674
|
+
auto is accepted but refuses with STIM_BAD_ARG until automatic placement ships.
|
|
4675
|
+
Android --remote <machine> refuses with STIM_BAD_ARG: Android on a paired Mac
|
|
4676
|
+
is not available yet.
|
|
4677
|
+
|
|
4678
|
+
A named Mac is strict: STIM_HOSTING_REFUSED names its reason; nothing boots
|
|
4679
|
+
here or elsewhere. --device cannot use a hosting Mac, and --simulator-app
|
|
4680
|
+
refuses. The host boots headless and ignores this Mac's iosSimulatorApp setting.
|
|
4681
|
+
--slot, --device-type and --runtime select the hosted simulator; --scheme,
|
|
4682
|
+
--configuration and --eas-profile select the app as usual. The build targets
|
|
4683
|
+
the offered simulator architecture, not this Mac's. --build-machine remains
|
|
4684
|
+
independent and selects a compatible build worker for a Debug cache miss.
|
|
4685
|
+
|
|
4686
|
+
Stim builds or fetches before reserving, records the session immediately, then
|
|
4687
|
+
uploads the app on every run. Debug keeps Metro here; its supervisor owns a
|
|
4688
|
+
private gateway bound to this Mac's Tailscale address, pinned to the host peer
|
|
4689
|
+
and protected by a per-session secret. The host exposes only a loopback bridge.
|
|
4690
|
+
No start --remote is needed; metro.tunnel and metro.publicUrl are ignored.
|
|
4691
|
+
Debug requires a running supervisor with private gateway support before reserving
|
|
4692
|
+
a simulator. --no-metro-check refuses with STIM_BAD_ARG for hosted Debug runs.
|
|
4693
|
+
If its supervisor is missing or older, run stim stop; stim start, then retry.
|
|
4694
|
+
Non-Debug runs skip Metro. Launch is unverified until this workspace's Metro
|
|
4695
|
+
provides bundle evidence, or the host proves a live release process.
|
|
4696
|
+
|
|
4697
|
+
A rerun on the same named Mac reattaches and delivers a new app attempt. A
|
|
4698
|
+
stopped or missing session is replaced; an unreachable or unknown owner refuses
|
|
4699
|
+
replacement. Switching between this Mac and a hosting Mac, or to a different
|
|
4700
|
+
hosting Mac, refuses for that slot: run stim stop first.
|
|
4701
|
+
Status adds ios.host { machine, session, selected, device: { name, runtime },
|
|
4702
|
+
agent, state } per slot. A shutdown local simulator is replaced in status; a
|
|
4703
|
+
booted or unknown local simulator stays visible alongside ios.host with a warning.
|
|
4704
|
+
The default slot appears only in ios, never in slots[]. No host UDID or gateway secret enters local device state or
|
|
4705
|
+
status. Ready is normal; stopped has a rerun hint; unknown or unreachable is
|
|
4706
|
+
unverified. A host server restart stops its sessions.
|
|
4707
|
+
|
|
4708
|
+
stop and worktree remove wait for the host to stop, delete exactly its owned
|
|
4709
|
+
simulator without parking, close the gateway and clear placement. stop --json
|
|
4710
|
+
reports each hosted slot under outcomes.device["ios:host:<slot>"], retaining
|
|
4711
|
+
local device outcomes and successful siblings when another slot fails. Revoked or
|
|
4712
|
+
missing sessions clear placement too. An unreachable host retains placement
|
|
4713
|
+
and fails cleanup: rerun stim stop when it answers. Missing or unapproved local
|
|
4714
|
+
credentials require stim doctor to restore access before retrying stop. An
|
|
4715
|
+
unreadable machine or session remains recorded; status warns and stop names the
|
|
4716
|
+
workspace-state key to inspect. Only that slot's ios run refuses; restore its
|
|
4717
|
+
recorded machine and session from the host, then run stim stop for that slot.
|
|
4718
|
+
Reload returns a JSON error with that remedy when no other app can reload;
|
|
4719
|
+
logs remain readable. Unknown selection or agent metadata is normalized
|
|
4720
|
+
without discarding the owner. devices.idleShutdownMinutes
|
|
4721
|
+
does not stop a hosted simulator in this phase; its recorded session keeps Metro
|
|
4722
|
+
from idle stopping. Reruns upload only missing manifest and file digests from a
|
|
4723
|
+
session-scoped store; the host verifies bytes before reuse. When the iOS build
|
|
4724
|
+
ran on the hosting Mac's pinned node, it takes matching files from that build.
|
|
4725
|
+
A refused or timed-out handoff falls back to upload, retrying a still-busy host
|
|
4726
|
+
for at most one minute.
|
|
4727
|
+
|
|
4728
|
+
stim logs and stim logs --errors pull native device records from bounded host
|
|
4729
|
+
queries. Concurrent followers share a collection, throttled per session, without
|
|
4730
|
+
blocking app delivery, viewing or control. Stop limits each log drain to 30 seconds with
|
|
4731
|
+
progress on stderr and a no-progress guard. The host collects a bounded final
|
|
4732
|
+
tail before deletion, including on revocation or server close; stop copies it
|
|
4733
|
+
back afterwards. If the final collection drops a backlog interval and eventually
|
|
4734
|
+
succeeds, a device warning record names that interval. A damaged collection checkpoint is ignored and
|
|
4735
|
+
rebuilt; it never blocks reading collected records. The client waits up to
|
|
4736
|
+
180 seconds for stop. The final collection and worker termination paths fit within
|
|
4737
|
+
that wait; an in-flight handoff copy and closing Metro or view transports are
|
|
4738
|
+
outside those worker bounds.
|
|
4739
|
+
Collected records remain in the session home after deletion;
|
|
4740
|
+
app blobs and materialized bundles are removed. Native queries read persisted
|
|
4741
|
+
entries, overlap by five seconds and de-duplicate; info-level or later-persisted
|
|
4742
|
+
entries may be unavailable. JavaScript logs arrive through Metro.
|
|
4743
|
+
The host advertises hosted-ios-data for iOS handoff and native logs. An older
|
|
4744
|
+
host gets an update note, uses uploads and shows logs already copied here.
|
|
4745
|
+
|
|
4746
|
+
AGENT CONTROL
|
|
4747
|
+
|
|
4748
|
+
On the hosting Mac, set hosting.agentDriver to agent-device. Both Macs need
|
|
4749
|
+
agent-device 0.21.20 or later (the policy and remote-config contract). The host
|
|
4750
|
+
starts one loopback daemon per installed iOS session under a child-aware
|
|
4751
|
+
ownership claim. Its daemon policy pins exactly that simulator UDID and denies
|
|
4752
|
+
shutdown. Stop, close, revocation, reinstall and graceful server close end its
|
|
4753
|
+
daemon and grant. A server crash can leave detached daemon processes and claims;
|
|
4754
|
+
restart alone does not prove they stopped. A daemon exit restarts only that
|
|
4755
|
+
session and rotates its grant; rerun stim ios to reattach and refresh the config.
|
|
4756
|
+
An unresolved proxy or daemon retains its claim and blocks reinstall and native
|
|
4757
|
+
teardown; clear a named claim only after proving its owner and child are gone.
|
|
4758
|
+
|
|
4759
|
+
The client writes a separate 0600 remote-config file for each workspace slot.
|
|
4760
|
+
Status prints agent: <command>; ios.host.agent in --json carries driver,
|
|
4761
|
+
remoteConfig and command, never the token. Use the returned file:
|
|
4762
|
+
|
|
4763
|
+
agent-device open <bundleId> --remote-config <file>
|
|
4764
|
+
agent-device snapshot --remote-config <file>
|
|
4765
|
+
agent-device click <ref> --remote-config <file>
|
|
4766
|
+
agent-device screenshot --remote-config <file>
|
|
4767
|
+
|
|
4768
|
+
The command shape is agent-device <command> --remote-config <file>. Open leases
|
|
4769
|
+
the simulator automatically with ios-instance and the proxy provider, scoped
|
|
4770
|
+
to this session's tenant. Close releases it; Stim still owns the simulator.
|
|
4771
|
+
The config names daemonBaseUrl, daemonAuthToken, tenant, sessionIsolation,
|
|
4772
|
+
runId, clientId, deviceKey, leaseBackend, leaseProvider and platform; it carries
|
|
4773
|
+
no preallocated leaseId. stim stop closes the matching agent-device connection
|
|
4774
|
+
and removes that slot's config after the host confirms stop or revocation.
|
|
4775
|
+
The host stops the session daemon and identity-checked agent-device runners
|
|
4776
|
+
targeting its exact simulator UDID before deleting the simulator, even without close.
|
|
4777
|
+
|
|
4778
|
+
Allowed: devices, open, close, snapshot, diff, wait, find, get, is, click, fill,
|
|
4779
|
+
press, type, focus, scroll, screenshot, longpress, swipe, back, home, orientation,
|
|
4780
|
+
appstate, alert and batch. These inspect or interact with the simulator and
|
|
4781
|
+
installed apps. devices lists only the session's simulator, filtered by the
|
|
4782
|
+
daemon policy. Its device claims are session-local and record / as the workspace;
|
|
4783
|
+
a host-side agent-device using the same simulator is not visible to the hosted daemon.
|
|
4784
|
+
Client device selectors are stripped and requests are pinned to iOS and that
|
|
4785
|
+
UDID; an explicitly named foreign UDID is refused. Selecting another device,
|
|
4786
|
+
boot/shutdown/erase, installs, uninstall, uploads, push, record, logs, network,
|
|
4787
|
+
perf, trace, clipboard and settings are refused. Stim installs; the agent cannot
|
|
4788
|
+
enumerate other host devices or write host files. Ambient client fields (cwd,
|
|
4789
|
+
stateDir, config and remoteConfig) are stripped, including inside batch steps.
|
|
4790
|
+
Host paths and launch inputs (--out, baseline, launchConsole, developerDir,
|
|
4791
|
+
installSource and similar fields) are refused, including inside batch steps.
|
|
4792
|
+
Only agent-device's generated screenshot temp artifact is accepted and returned
|
|
4793
|
+
to the client; arbitrary host screenshot destinations are refused. Runtime
|
|
4794
|
+
hints are dropped and only reporting/artifact metadata passes through.
|
|
4795
|
+
|
|
4796
|
+
If hosting.agentDriver is none, the driver cannot enforce its policy, or the
|
|
4797
|
+
host does not advertise hosted-ios-agent, agent reports
|
|
4798
|
+
{ driver: 'none', setting: 'hosting.agentDriver' }. Update the host and check
|
|
4799
|
+
its setting and agent-device installation. No other simulator becomes reachable.
|
|
4800
|
+
Client hello carries only name and version, not features, so the host cannot gate
|
|
4801
|
+
daemon startup or grant issuance on client support. The client checks the host's
|
|
4802
|
+
hosted-ios-agent feature before using a grant.
|
|
4803
|
+
|
|
4804
|
+
Stim Desktop and the phone app view and control the simulator through this
|
|
4805
|
+
Mac's stim-server relay, with an "on <machine>" label. Turn on Serve to phones
|
|
4806
|
+
in Desktop; pair the phone with this Mac, not the hosting Mac. frames.subscribe
|
|
4807
|
+
and control.begin address workspace, platform ios and slot, including named
|
|
4808
|
+
slots. The relay shares one authenticated host connection with hosted macOS
|
|
4809
|
+
viewers. Stopped sessions show a rerun remedy; unavailable hosts report why
|
|
4810
|
+
frames cannot arrive. Replay, duoFrame and physical targets are refused for
|
|
4811
|
+
hosted subscriptions. Desktop hides local Simulator.app, rotation, hardware
|
|
4812
|
+
buttons and simulator options; touch and text use the relay. The phone can also
|
|
4813
|
+
send the host's supported button, rotation and posture inputs.`
|
|
4814
|
+
},
|
|
3637
4815
|
eas: {
|
|
3638
4816
|
summary: "download a matching EAS development build; explicit profile, costs, cache and miss remedies",
|
|
3639
4817
|
body: () => `EAS DEVELOPMENT BUILDS
|
|
@@ -3822,8 +5000,9 @@ result as proof instead of requiring an unrelated screenshot.`
|
|
|
3822
5000
|
The labels are a closed set, and nothing else is ever printed in that
|
|
3823
5001
|
column:
|
|
3824
5002
|
|
|
5003
|
+
agent-device
|
|
3825
5004
|
branch budget build cache caches carry
|
|
3826
|
-
checkout deps device
|
|
5005
|
+
checkout daemons deps device
|
|
3827
5006
|
devices error failed findings fingerprint gems
|
|
3828
5007
|
install installs ip.txt lan launch lease
|
|
3829
5008
|
lock log
|
|
@@ -3832,7 +5011,7 @@ result as proof instead of requiring an unrelated screenshot.`
|
|
|
3832
5011
|
result
|
|
3833
5012
|
services
|
|
3834
5013
|
setting settings setup state stats stop
|
|
3835
|
-
storage swap verify version workspace
|
|
5014
|
+
storage swap verify version workspace worktrees
|
|
3836
5015
|
|
|
3837
5016
|
\`app\` and \`compilation cache\` join them in the stdout block a successful
|
|
3838
5017
|
run ends with. When a native build runs, its compilation-cache result is
|
|
@@ -3943,6 +5122,16 @@ result as proof instead of requiring an unrelated screenshot.`
|
|
|
3943
5122
|
is never touched: \`Reclaimed the environment; the working tree stays (it
|
|
3944
5123
|
is the source checkout).\`
|
|
3945
5124
|
|
|
5125
|
+
\`gc\` names each slow step on stderr as it starts, in every mode; its
|
|
5126
|
+
report stays on stdout, and with \`--json\` stdout carries only the payload:
|
|
5127
|
+
|
|
5128
|
+
caches measuring 18 shared caches
|
|
5129
|
+
devices listing simulators and emulators
|
|
5130
|
+
worktrees checking 26 projects for merged or idle worktrees
|
|
5131
|
+
daemons watchman pid 49040: checking 12 roots
|
|
5132
|
+
daemons removing stale watchman root /w/app-409
|
|
5133
|
+
daemons waiting up to 10s for 1 Gradle daemon to exit: pid 51234
|
|
5134
|
+
|
|
3946
5135
|
A GAP BETWEEN HEARTBEATS IS NOT A HANG. Stim runs device tools
|
|
3947
5136
|
synchronously, so a long \`simctl\`, \`adb\` or copy call holds the timer
|
|
3948
5137
|
until it returns; the next heartbeat then lands on the grid, which is why an
|
|
@@ -3950,26 +5139,40 @@ result as proof instead of requiring an unrelated screenshot.`
|
|
|
3950
5139
|
killing a run.`
|
|
3951
5140
|
},
|
|
3952
5141
|
pool: {
|
|
3953
|
-
summary: "parked and adopted simulators: what park and adoption clear or keep, the model and runtime match",
|
|
5142
|
+
summary: "parked and adopted simulators and emulators: what park and adoption clear or keep, the model and runtime match, emulator storage, gc erase",
|
|
3954
5143
|
body: () => ` THE SIMULATOR POOL
|
|
3955
5144
|
\`worktree remove\` PARKS this workspace's owned simulator instead of
|
|
3956
5145
|
deleting it, and the next workspace that wants the same model and runtime
|
|
3957
|
-
ADOPTS it. Adoption reuses the simulator
|
|
5146
|
+
ADOPTS it. Adoption reuses the simulator and the app installed on it, but
|
|
5147
|
+
not the previous workspace's state:
|
|
3958
5148
|
|
|
3959
|
-
at park shut down
|
|
3960
|
-
|
|
3961
|
-
|
|
5149
|
+
at park shut down and renamed
|
|
5150
|
+
\`stim-parked (<model> <runtime>) <4 hex>\`; nothing is
|
|
5151
|
+
erased, and the pool record names the installed app's
|
|
5152
|
+
bundle id and build cache key
|
|
3962
5153
|
at adoption renamed for the adopting workspace, then, inside the boot
|
|
3963
5154
|
the run pays anyway, \`simctl privacy reset all\` and
|
|
3964
|
-
\`simctl keychain reset\`; at install,
|
|
3965
|
-
uninstalled
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
|
|
3972
|
-
|
|
5155
|
+
\`simctl keychain reset\`; at install, every other user app
|
|
5156
|
+
is uninstalled, and this workspace's app, when it is
|
|
5157
|
+
installed, has its preferences domain deleted (an
|
|
5158
|
+
expo-dev-client app's is replaced by the two dev-menu-off
|
|
5159
|
+
keys, see \`guide facts devmenu\`) and its data
|
|
5160
|
+
container's Documents, Library, tmp and SystemData emptied
|
|
5161
|
+
|
|
5162
|
+
Adoption always clears the app's data, whichever workspace parked the
|
|
5163
|
+
simulator: a new workspace starts from a fresh app state. The app itself
|
|
5164
|
+
stays installed, so the usual byte comparison decides the install: the same
|
|
5165
|
+
build prints \`install unchanged\` and skips the copy. A parked record's
|
|
5166
|
+
cache key that differs from this run's skips that comparison. App-group
|
|
5167
|
+
containers, the pasteboard, photos, Safari data and Simulator settings are
|
|
5168
|
+
not cleared; \`gc --cache parked --delete\` erases a parked simulator when a
|
|
5169
|
+
clean system image is needed.
|
|
5170
|
+
|
|
5171
|
+
A parked simulator keeps its apps and data, so it can hold gigabytes. It
|
|
5172
|
+
uses the Mac's disk, not a fixed partition, so there is no free-space check
|
|
5173
|
+
on iOS; \`stim gc\` lists each parked simulator's app and size.
|
|
5174
|
+
|
|
5175
|
+
Park takes only a simulator that reports Shutdown. It waits up to 15s for
|
|
3973
5176
|
the shutdown to settle, retries the shutdown once, and waits up to 15s more.
|
|
3974
5177
|
A simulator that is still booted after that is deleted instead, and
|
|
3975
5178
|
\`worktree remove\` prints
|
|
@@ -3996,6 +5199,11 @@ result as proof instead of requiring an unrelated screenshot.`
|
|
|
3996
5199
|
the next attempt, unless it may have left native work running. Deletion also
|
|
3997
5200
|
puts an opaque marker on the pool record so older Stim versions keep it
|
|
3998
5201
|
protected. New versions resolve that marker through the same identity claim.
|
|
5202
|
+
When a marker outlives its owner, Stim cannot tell whether the erase or
|
|
5203
|
+
deletion ran, so adoption treats the device as erased and drops the recorded
|
|
5204
|
+
app, cache key and scheme approvals. A \`gc --cache parked --delete\` erase
|
|
5205
|
+
that fails after the erase command started keeps the device parked without
|
|
5206
|
+
them for the same reason.
|
|
3999
5207
|
A crash during a synchronous device-tool call leaves that work unverifiable; inspect
|
|
4000
5208
|
the old process and its native children before following the claim's removal
|
|
4001
5209
|
remedy. See \`stim guide errors STIM_CLAIM_REFUSED\`.
|
|
@@ -4010,8 +5218,7 @@ result as proof instead of requiring an unrelated screenshot.`
|
|
|
4010
5218
|
|
|
4011
5219
|
device stim-app-412 (iPhone 17 26.5) (9C1F..) adopted (17s)
|
|
4012
5220
|
|
|
4013
|
-
That time includes the
|
|
4014
|
-
runs longer than a plain boot.
|
|
5221
|
+
That time includes the two resets, so it runs longer than a plain boot.
|
|
4015
5222
|
\`stim status\` prints one line while the pool is not empty:
|
|
4016
5223
|
|
|
4017
5224
|
pool: 2 parked iOS simulators (max 3)
|
|
@@ -4019,12 +5226,21 @@ result as proof instead of requiring an unrelated screenshot.`
|
|
|
4019
5226
|
\`stim gc\` reports the pool, and \`stim gc --delete\` empties every entry
|
|
4020
5227
|
it can re-verify:
|
|
4021
5228
|
|
|
4022
|
-
Parked simulators (2,
|
|
4023
|
-
ios stim-parked (iPhone 17 26.5) 9c1f (9C1F..) iPhone 17 26.5 parked 3d ago
|
|
4024
|
-
--delete attempts
|
|
5229
|
+
Parked simulators (2, 4.1G):
|
|
5230
|
+
ios stim-parked (iPhone 17 26.5) 9c1f (9C1F..) iPhone 17 26.5 parked 3d ago com.example.app 2.3G
|
|
5231
|
+
--delete attempts verified deletions and keeps failures.
|
|
5232
|
+
|
|
5233
|
+
\`stim gc --cache parked\` reports only the pool, and with --delete ERASES
|
|
5234
|
+
each parked simulator (\`simctl erase\`) and emulator (user data and
|
|
5235
|
+
snapshots) it can re-verify instead of deleting it. The device stays parked
|
|
5236
|
+
for adoption, with no app recorded; the next adopter installs its app again.
|
|
5237
|
+
\`--cache all\` leaves them alone. Erase takes only a simulator
|
|
5238
|
+
that reports Shutdown and an emulator that is not running and no workspace
|
|
5239
|
+
references, under the same per-device claim as adoption.
|
|
4025
5240
|
|
|
4026
|
-
\`gc --older-than <days>\` reports and deletes
|
|
4027
|
-
least that long; one parked at an unknown
|
|
5241
|
+
\`gc --older-than <days>\` reports and deletes, or with \`--cache parked\`
|
|
5242
|
+
erases, only the devices parked at least that long; one parked at an unknown
|
|
5243
|
+
time stays.
|
|
4028
5244
|
|
|
4029
5245
|
If simulator listing or deletion fails, \`gc --delete\` reports the failure
|
|
4030
5246
|
and keeps that entry. It never turns an unverified absence into a dropped
|
|
@@ -4041,15 +5257,15 @@ result as proof instead of requiring an unrelated screenshot.`
|
|
|
4041
5257
|
\`pool.androidParkedMax\` (default 3) or STIM_POOL_ANDROID_PARKED_MAX.
|
|
4042
5258
|
A redirected STIM_HOME disables parking unless that environment override
|
|
4043
5259
|
is set. Zero disables parking and adoption. \`stop\` keeps the assignment;
|
|
4044
|
-
\`worktree remove\` parks eligible AVDs,
|
|
4045
|
-
|
|
5260
|
+
\`worktree remove\` parks eligible AVDs, \`gc --delete\` empties the pool
|
|
5261
|
+
and \`gc --cache parked --delete\` wipes their user data (with
|
|
5262
|
+
--older-than, only the AVDs parked that long).
|
|
4046
5263
|
|
|
4047
5264
|
Adoption matches the system image, data partition size, and the creation
|
|
4048
5265
|
settings from android.avdConfig / android.avdConfigFile. The AVD keeps its
|
|
4049
|
-
original stim-<label> name. Parking
|
|
4050
|
-
|
|
4051
|
-
|
|
4052
|
-
boot (about 20s) and a fresh install.
|
|
5266
|
+
original stim-<label> name. Parking keeps its user data and Quick Boot
|
|
5267
|
+
snapshot, so the installed app stays and the pool record names its package
|
|
5268
|
+
and build cache key.
|
|
4053
5269
|
Incompatible AVDs stay parked until eviction or GC. AVDs created by older
|
|
4054
5270
|
versions without a recorded creation configuration are deleted at removal.
|
|
4055
5271
|
|
|
@@ -4065,10 +5281,9 @@ result as proof instead of requiring an unrelated screenshot.`
|
|
|
4065
5281
|
manually clearing a claim, and keep the AVD data while its process state
|
|
4066
5282
|
cannot be verified.
|
|
4067
5283
|
|
|
4068
|
-
Android cleanup happens AFTER boot, before install or launch
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
are uninstalled. If ADB goes offline or closes the
|
|
5284
|
+
Android cleanup happens AFTER boot, before install or launch:
|
|
5285
|
+
\`adb shell pm clear\` clears the adopting app's data while retaining its
|
|
5286
|
+
APK, and other third-party apps are uninstalled. If ADB goes offline or closes the
|
|
4072
5287
|
connection, Stim waits for boot readiness and retries cleanup for up to 30
|
|
4073
5288
|
seconds, verifying the same owned AVD before each destructive command.
|
|
4074
5289
|
Failed cleanup blocks launch and remains pending for a retry. The installed APK's SHA-256 must match the
|
|
@@ -4077,10 +5292,25 @@ result as proof instead of requiring an unrelated screenshot.`
|
|
|
4077
5292
|
If the retained APK has a conflicting signer or version, adoption uninstalls
|
|
4078
5293
|
it and retries installation. Adoption stays pending until installation succeeds.
|
|
4079
5294
|
|
|
5295
|
+
An emulator's /data partition is fixed, so an adopted AVD can run out of
|
|
5296
|
+
room. Before installing, adoption reads the free space on /data
|
|
5297
|
+
(\`adb shell df -k /data\`) and needs twice the APK size plus 512 MB. When
|
|
5298
|
+
there is less, it runs \`pm trim-caches\`; when that is not enough, it
|
|
5299
|
+
shuts the emulator down, wipes its user data through centralized teardown,
|
|
5300
|
+
boots it again and installs:
|
|
5301
|
+
|
|
5302
|
+
device stim-app has 97M free on /data after removing other apps and trimming caches; wiping its user data
|
|
5303
|
+
device stim-app (emulator-5556) booted with wiped user data
|
|
5304
|
+
|
|
5305
|
+
An install that fails with INSTALL_FAILED_INSUFFICIENT_STORAGE gets the
|
|
5306
|
+
same steps: trim caches and retry, then wipe and retry. Only an adopting
|
|
5307
|
+
run does this; a workspace's own emulator never loses data to it.
|
|
5308
|
+
|
|
4080
5309
|
The wipe resets apps, accounts, device settings and the shared storage
|
|
4081
5310
|
inside the data partition; the AVD's creation settings and any separate
|
|
4082
5311
|
SD card image remain. Status lists parked Android emulators; GC
|
|
4083
|
-
reports their system image, hardware profile, age and disk
|
|
5312
|
+
reports their system image, hardware profile, age, recorded app and disk
|
|
5313
|
+
size.
|
|
4084
5314
|
`
|
|
4085
5315
|
},
|
|
4086
5316
|
builds: {
|
|
@@ -4134,8 +5364,9 @@ PREDICTING THE NEXT BUILD (--plan)
|
|
|
4134
5364
|
An Android plan reads the ABI from the emulator the slot records, or from
|
|
4135
5365
|
the system image a new one would use. A plan refuses --device, --remote,
|
|
4136
5366
|
--wait, --no-wait, --no-metro-check and --simulator-app with STIM_BAD_ARG.
|
|
4137
|
-
Without --eas-profile it also refuses the
|
|
4138
|
-
|
|
5367
|
+
Without --eas-profile it also refuses the ios.remote and android.remote
|
|
5368
|
+
settings with STIM_BAD_ARG. Without --eas-profile an Android plan also
|
|
5369
|
+
refuses the experimental compiler CAS with STIM_BAD_ARG, and refuses with
|
|
4139
5370
|
STIM_NO_DEVICE when no system image is installed.
|
|
4140
5371
|
|
|
4141
5372
|
IOS SCHEME SELECTION
|
|
@@ -4172,8 +5403,10 @@ AN ARTIFACT THE DEVICE ALREADY HOLDS IS NOT INSTALLED AGAIN
|
|
|
4172
5403
|
install unchanged (emulator-5584 already has this build) (0.4s)
|
|
4173
5404
|
|
|
4174
5405
|
On iOS the install line names the identity proof separately from the Expo
|
|
4175
|
-
dev-client
|
|
4176
|
-
to an install that did not run
|
|
5406
|
+
dev-client simulator writes, so a slow simulator command is never charged
|
|
5407
|
+
to an install that did not run. Those writes run only after a real install
|
|
5408
|
+
or for a scheme approval the device record lacks (\`stim guide facts
|
|
5409
|
+
devmenu\`), so a skipped rerun usually prints no second line:
|
|
4177
5410
|
|
|
4178
5411
|
install unchanged (stim-app already has this build) (0.4s)
|
|
4179
5412
|
install dev client prepared (0.9s)
|
|
@@ -4211,7 +5444,7 @@ $STIM_HOME/workspaces/<project>--<digest>/ (default ~/.stim/workspaces/).
|
|
|
4211
5444
|
The aggregate run counters \`stats\` prints live beside it in
|
|
4212
5445
|
$STIM_HOME/stats.json, one bucket per project and platform plus a machine-wide
|
|
4213
5446
|
one, and the last 10 successful runs per project, platform and cache outcome
|
|
4214
|
-
that size \`status\`'s build estimate. The running build itself is the
|
|
5447
|
+
(plus the last 10 that set up their device) that size \`status\`'s build estimate. The running build itself is the
|
|
4215
5448
|
activeBuild key of the workspace's state.json, cleared when the run exits.
|
|
4216
5449
|
No .gitignore entry is created or required.
|
|
4217
5450
|
Native preparation can change project files: expo prebuild generates native
|
|
@@ -4614,6 +5847,27 @@ OPT-IN CONCURRENCY LIMITS (UNLIMITED BY DEFAULT)
|
|
|
4614
5847
|
4. trim shared cache entries nothing has used for 14 days
|
|
4615
5848
|
(\`gc --delete --older-than 14\` for the caches)
|
|
4616
5849
|
|
|
5850
|
+
IDLE SHUTDOWN (OFF BY DEFAULT): with devices.idleShutdownMinutes set, a
|
|
5851
|
+
workspace's supervisor shuts down that workspace's owned simulators and
|
|
5852
|
+
emulators once they have been idle that long, without waiting for a run
|
|
5853
|
+
to go over budget. Idle is step 1 with that many minutes in place of 10:
|
|
5854
|
+
booted, no driver, no Stim or agent-device lock, no build in progress, and
|
|
5855
|
+
no activity. A stim-server client viewing the device (the phone app's
|
|
5856
|
+
device viewer) counts as activity, here and in step 1. The supervisor
|
|
5857
|
+
checks once a minute after the workspace's last Stim command, in-app log
|
|
5858
|
+
and device log are all that old, rechecks under the workspace's native-run lock, and shuts devices down
|
|
5859
|
+
through the same teardown as \`stop\`: never deleted, and physical
|
|
5860
|
+
devices are never touched. It writes a device_idle_shutdown line to
|
|
5861
|
+
metro.ndjson and records deviceIdleShutdowns in state.json, so \`stim
|
|
5862
|
+
status\` shows "shut down after 30m idle" on the device until the next
|
|
5863
|
+
\`ios\` or \`android\` run boots it again. When metro.idleStopMinutes is
|
|
5864
|
+
shorter, the dev server's idle stop first shuts down the devices idle that
|
|
5865
|
+
long, because no supervisor is left to check afterwards. Nothing checks
|
|
5866
|
+
without a supervisor: release runs, and after \`stim stop\`. The setting
|
|
5867
|
+
is read when the supervisor starts. Stim Desktop's simulator view is not a
|
|
5868
|
+
stim-server client and does not count as a viewer. Turn it on with
|
|
5869
|
+
\`stim settings set devices.idleShutdownMinutes 30 --scope machine\`.
|
|
5870
|
+
|
|
4617
5871
|
Steps 3 and 4 run only for disk. Memory and workspace limits never refuse;
|
|
4618
5872
|
a run still over them prints one \`budget\` warning and continues. The
|
|
4619
5873
|
current workspace is never reclaimed.
|
|
@@ -4632,8 +5886,8 @@ OPT-IN CONCURRENCY LIMITS (UNLIMITED BY DEFAULT)
|
|
|
4632
5886
|
summary: "every flag per command, Android variants and flavors, the per-run simulator model, runtime and system image",
|
|
4633
5887
|
body: () => `THE OPTION SURFACE, IN FULL
|
|
4634
5888
|
start --json --wait <seconds> --remote --reset-cache
|
|
4635
|
-
ios --slot <name> --json --plan --no-metro-check --no-build-cache --scheme <name> --configuration <name> --device-type <name> --runtime <version> --simulator-app <xcode|siniulator|stim-desktop> --device [udid] --wait <seconds> --no-wait --remote <proxy|
|
|
4636
|
-
android --slot <name> --json --plan --no-metro-check --no-build-cache --variant <name> --system-image <id> --device-profile <id> --device [serial] --wait <seconds> --no-wait --remote <proxy|eas>
|
|
5889
|
+
ios --build-machine <auto|local|name> --slot <name> --json --plan --no-metro-check --no-build-cache --scheme <name> --configuration <name> --device-type <name> --runtime <version> --simulator-app <xcode|siniulator|stim-desktop> --device [udid] --wait <seconds> --no-wait --remote <eas|proxy|auto|machine>
|
|
5890
|
+
android --build-machine <auto|local|name> --slot <name> --json --plan --no-metro-check --no-build-cache --variant <name> --system-image <id> --device-profile <id> --device [serial] --wait <seconds> --no-wait --remote <proxy|eas>
|
|
4637
5891
|
reload [ios|android] --json
|
|
4638
5892
|
device lock <ios|android> [id] --slot <name> --for <duration> --wait <seconds> --json;
|
|
4639
5893
|
unlock [ios|android] --slot <name> --json
|
|
@@ -4645,7 +5899,7 @@ OPT-IN CONCURRENCY LIMITS (UNLIMITED BY DEFAULT)
|
|
|
4645
5899
|
set <key> <value> --scope <layer> --json; unset <key> --scope <layer> --json
|
|
4646
5900
|
doctor --json --fix --platform <ios|android>
|
|
4647
5901
|
(--platform keeps shared checks and filters native findings)
|
|
4648
|
-
gc --delete --older-than <days> --cache <name|all|workspaces> --worktrees --json
|
|
5902
|
+
gc --delete --older-than <days> --cache <name|all|workspaces|recordings|parked> --worktrees --json
|
|
4649
5903
|
worktree warm --refresh; remove [path] --force
|
|
4650
5904
|
|
|
4651
5905
|
DEVICE SLOTS
|
|
@@ -4667,7 +5921,7 @@ OPT-IN CONCURRENCY LIMITS (UNLIMITED BY DEFAULT)
|
|
|
4667
5921
|
and prints what it waits for. \`stop --slot\` interrupts a build only for
|
|
4668
5922
|
its own slot or when it stops the workspace's only device (see STOP DURING
|
|
4669
5923
|
A BUILD).
|
|
4670
|
-
Named slots support local devices;
|
|
5924
|
+
Named slots support local devices and hosted iOS simulators; eas/proxy sessions use the default slot.
|
|
4671
5925
|
Names use 1-64 letters, digits, underscores or hyphens, starting with a letter
|
|
4672
5926
|
or digit. Reserved object-property names and web are refused.
|
|
4673
5927
|
|
|
@@ -4707,6 +5961,12 @@ OPT-IN CONCURRENCY LIMITS (UNLIMITED BY DEFAULT)
|
|
|
4707
5961
|
worktrees of the same Git repository; the source checkout must be available.
|
|
4708
5962
|
Running it in the source checkout refuses.
|
|
4709
5963
|
|
|
5964
|
+
Warm registers the app it prepares, so \`stim status\` lists the workspace
|
|
5965
|
+
with phase "warming" while it runs and "ready" once it succeeds, until the
|
|
5966
|
+
first start, ios, android, web or reload there or for 2 hours
|
|
5967
|
+
(\`guide facts status\`). In a monorepo worktree root, that app is the one
|
|
5968
|
+
the source checkout has registered.
|
|
5969
|
+
|
|
4710
5970
|
\`stim doctor\` reports the source checkout's fitness as a seed -- how far
|
|
4711
5971
|
behind its upstream it is, uncommitted tracked changes, an interrupted rebase
|
|
4712
5972
|
or merge, a detached HEAD, a diverged branch, a branch that is not the
|
|
@@ -4787,7 +6047,7 @@ OPT-IN CONCURRENCY LIMITS (UNLIMITED BY DEFAULT)
|
|
|
4787
6047
|
paths eligible under the source checkout's Git ignore rules, including .env
|
|
4788
6048
|
and local configuration. The source's nonempty
|
|
4789
6049
|
.worktreeexclude replaces its resolved worktree.exclude setting. Nested
|
|
4790
|
-
registered worktrees, .DS_Store, .DerivedData, .idea, and
|
|
6050
|
+
registered worktrees, .DS_Store, .DerivedData, .idea, .jj, and
|
|
4791
6051
|
android/build/generated/autolinking caches are excluded, including inside
|
|
4792
6052
|
newly copied directories. Gradle regenerates autolinking
|
|
4793
6053
|
for the destination checkout on its next build. Warm also skips paths
|
|
@@ -4866,11 +6126,23 @@ OPT-IN CONCURRENCY LIMITS (UNLIMITED BY DEFAULT)
|
|
|
4866
6126
|
runtime when \`--runtime\` also resolved, which is what catches a pair like
|
|
4867
6127
|
\`--device-type "iPhone 8" --runtime 26.5\` that each half would pass alone.
|
|
4868
6128
|
\`--runtime\` takes a version (\`26.5\`) or a runtime's full name
|
|
4869
|
-
(\`iOS 26.5\`), exactly; no prefix or suffix matches.
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
device
|
|
4873
|
-
|
|
6129
|
+
(\`iOS 26.5\`), exactly; no prefix or suffix matches. On a remote run
|
|
6130
|
+
(eas/proxy via \`--remote\` or ios.remote) \`--runtime\` refuses with
|
|
6131
|
+
STIM_BAD_ARG, because the remote backend picks the iOS version. So does
|
|
6132
|
+
\`--device-type\` on the proxy backend. The eas backend honors
|
|
6133
|
+
\`--device-type\`: Stim passes it to \`eas simulator:start --device\`,
|
|
6134
|
+
unchecked against local runtimes, and refuses with STIM_BAD_ARG when
|
|
6135
|
+
eas-cli is older than 22.2.0, the first release with that flag. A
|
|
6136
|
+
recorded EAS session still running another model refuses with
|
|
6137
|
+
STIM_REMOTE_DEVICE_MISMATCH instead of being reused; one that already
|
|
6138
|
+
ended is replaced on the requested model. The ios.deviceType and
|
|
6139
|
+
ios.runtime settings do not refuse on an eas/proxy run; it ignores them.
|
|
6140
|
+
A named hosting Mac uses both selectors and its installed choices instead;
|
|
6141
|
+
an unavailable choice is STIM_HOSTING_REFUSED. See hosted-ios.
|
|
6142
|
+
\`android --system-image\` and \`--device-profile\` refuse with
|
|
6143
|
+
STIM_BAD_ARG on every remote run (\`--remote\` or the android.remote
|
|
6144
|
+
setting), and a remote run ignores android.systemImage and
|
|
6145
|
+
android.deviceProfile.
|
|
4874
6146
|
|
|
4875
6147
|
These flags describe a device that does not exist yet. When this workspace
|
|
4876
6148
|
ALREADY owns a simulator and \`--device-type\` names a different model, or
|
|
@@ -4938,11 +6210,25 @@ OPT-IN CONCURRENCY LIMITS (UNLIMITED BY DEFAULT)
|
|
|
4938
6210
|
and the remedy is the cable. Metro is unaffected: a Debug app reaches it
|
|
4939
6211
|
over the LAN either way.
|
|
4940
6212
|
|
|
6213
|
+
WATCHING A LEASED ANDROID PHONE: while this workspace holds the phone's
|
|
6214
|
+
lease, stim-server can stream its screen to paired clients that ask for the
|
|
6215
|
+
physical device, and let one with control tap and type on it. stim-server
|
|
6216
|
+
pushes the scrcpy server to /data/local/tmp for the stream and deletes it
|
|
6217
|
+
when the stream stops; it installs nothing and changes no setting. Control
|
|
6218
|
+
ends when the lease is released or expires, and stim-server never takes a
|
|
6219
|
+
phone's lease itself.
|
|
6220
|
+
|
|
4941
6221
|
\`stop\` releases this workspace's leases and stops its log collectors.
|
|
4942
6222
|
On a physical iPhone that also closes the app, because its collector owns
|
|
4943
6223
|
the devicectl launch session. \`gc --delete\` removes expired lease files;
|
|
4944
6224
|
neither command shuts down the phone or uninstalls the app.
|
|
4945
6225
|
|
|
6226
|
+
WATCHING THE PHONE: while this workspace holds the lease, stim-server
|
|
6227
|
+
streams a USB-cabled iPhone's screen to the phone app, view only; Stim
|
|
6228
|
+
sends a physical iPhone no input. The lease ends with the run, so
|
|
6229
|
+
\`device lock ios <udid>\` keeps it watchable between runs. A Wi-Fi phone
|
|
6230
|
+
has no stream, and a locked phone shows its last frame until unlocked.
|
|
6231
|
+
|
|
4946
6232
|
A device build is LOCAL-TIER ONLY. Its cache key is
|
|
4947
6233
|
\`<fingerprint>-<configuration>-device\`, so a device app can never collide
|
|
4948
6234
|
with the simulator one, and neither the build-cache provider nor the Expo
|
|
@@ -4968,8 +6254,9 @@ OPT-IN CONCURRENCY LIMITS (UNLIMITED BY DEFAULT)
|
|
|
4968
6254
|
this workspace's Metro, and then wires the app to it: an expo-dev-client app
|
|
4969
6255
|
through the deep link, passed to devicectl as \`--payload-url\` and followed
|
|
4970
6256
|
by \`-- -EXDevMenuShowsAtLaunch 0 -EXDevMenuShowFloatingActionButton 0\`,
|
|
4971
|
-
|
|
4972
|
-
bare app by writing \`<addr>:<port>\` into the app bundle's
|
|
6257
|
+
the same launch arguments Stim's own \`simctl launch --initialUrl\` carries on
|
|
6258
|
+
a simulator, and a bare app by writing \`<addr>:<port>\` into the app bundle's
|
|
6259
|
+
ip.txt --
|
|
4973
6260
|
RCTBundleURLProvider's own mechanism, which honours a colon-bearing value
|
|
4974
6261
|
verbatim and never consults the compiled RCT_METRO_PORT. Stim never sets
|
|
4975
6262
|
that define: it would put the reserved port into a compiled input and fork
|
|
@@ -5149,6 +6436,17 @@ THE POOL: WHICH DEVICE AN ID-LESS \`--device\` PICKS
|
|
|
5149
6436
|
android.keystorePassword override it (see \`guide settings\`). The cache
|
|
5150
6437
|
entry itself is never modified.
|
|
5151
6438
|
|
|
6439
|
+
AN EXPO-UPDATES APP GETS A FRESH EMBEDDED UPDATE. expo-updates launches the
|
|
6440
|
+
update with the newest commitTime for the binary's runtime version, and the
|
|
6441
|
+
embedded bundle is one of those updates. The cached artifact's embedded
|
|
6442
|
+
manifest (app.manifest) carries the CACHED build's id and time, so an
|
|
6443
|
+
update the app downloaded after that build would launch instead of the
|
|
6444
|
+
swapped JS. The swap gives the copy's manifest a new id and this run's time,
|
|
6445
|
+
exactly as a full build does, on both platforms. An update published after
|
|
6446
|
+
the swap can still download and launch on a later start, as it would on a
|
|
6447
|
+
fresh build. A manifest the swap cannot rewrite, or one inside a dynamic
|
|
6448
|
+
EXUpdates.framework on iOS, falls back to a full build.
|
|
6449
|
+
|
|
5152
6450
|
Before re-packing, THE ASSET GATE compares CONTENT HASHES of the assets
|
|
5153
6451
|
React Native emits: what this workspace just emitted under --assets-dest
|
|
5154
6452
|
against a manifest of what the cached build emitted, recorded as
|
|
@@ -5193,7 +6491,8 @@ THE POOL: WHICH DEVICE AN ID-LESS \`--device\` PICKS
|
|
|
5193
6491
|
its builder's baked-in JS, a cache hit regenerates THIS workspace's bundle
|
|
5194
6492
|
(the project's own \`expo export:embed\` / \`react-native bundle\`, plus
|
|
5195
6493
|
its own hermesc when Hermes is enabled) into a copy of the artifact,
|
|
5196
|
-
|
|
6494
|
+
refreshes an expo-updates manifest as described above, re-signs it and
|
|
6495
|
+
installs that; any swap failure falls back to a full build
|
|
5197
6496
|
rather than ever installing stale JS. Device logs are still collected, so
|
|
5198
6497
|
\`logs --errors\` answers "does it repro in release/Hermes bytecode".
|
|
5199
6498
|
A run with no \`--device\` installs on the simulator only. \`ios --device\`
|
|
@@ -5294,8 +6593,56 @@ wipe state to upgrade: it records ownership needed for safe teardown. Use the
|
|
|
5294
6593
|
same slot-aware CLI for all commands while named assignments exist; older
|
|
5295
6594
|
versions cannot reliably manage their assignments.
|
|
5296
6595
|
|
|
6596
|
+
ARCHIVE
|
|
6597
|
+
|
|
6598
|
+
Linked-worktree removal and gc dead-project pruning keep history under
|
|
6599
|
+
$STIM_HOME/archive by default. See stim guide cleanup archive.
|
|
6600
|
+
|
|
6601
|
+
MAINTENANCE
|
|
6602
|
+
|
|
6603
|
+
Automatic maintenance is report-only in this release: it measures, plans and
|
|
6604
|
+
logs, and never stops or deletes resources. The default mode is report; it is
|
|
6605
|
+
off in CI and scoped STIM_HOME homes unless STIM_MAINTENANCE is explicit.
|
|
6606
|
+
Commands trigger a detached pass when disk and memory checks (every minute)
|
|
6607
|
+
or directory sizes (hourly) are due. guide, settings and help do not trigger
|
|
6608
|
+
it; gc --delete also skips the hook. status --watch also triggers checks.
|
|
6609
|
+
Size checks defer under high load. Attempts back off for at least one minute.
|
|
6610
|
+
Measured directories are workspace build outputs and Stim's shared native,
|
|
6611
|
+
Metro, ccache, Swift compilation and registered caches. Pressure checks read
|
|
6612
|
+
free disk on the Stim home, projects and worker root volumes. On macOS the
|
|
6613
|
+
memory signal is the sysctl pressure level, with no signal if sysctl fails.
|
|
6614
|
+
Other platforms use os.freemem(); macOS never falls back to it.
|
|
6615
|
+
Memory pressure is recorded only; memory stops are deferred to a later phase.
|
|
6616
|
+
|
|
6617
|
+
stim status last checks, plan and running pass
|
|
6618
|
+
stim status --json maintenance observations and recent records
|
|
6619
|
+
stim logs --source maintenance this workspace's maintenance records
|
|
6620
|
+
stim gc live pressure and cached-size preview
|
|
6621
|
+
stim settings set maintenance.mode off
|
|
6622
|
+
|
|
6623
|
+
The machine log is $STIM_HOME/maintenance/maintenance.ndjson, rotated at
|
|
6624
|
+
maintenance.logMaxMb with the old generation retained for
|
|
6625
|
+
maintenance.logRetentionDays. Child crashes use maintenance/child.log.
|
|
6626
|
+
Actions and explaining skips are logged only when newly planned. A pass is
|
|
6627
|
+
logged after a size check or a change to actions, skips or blocked reasons.
|
|
6628
|
+
maintenance.logChecks enables debug observations; default false.
|
|
6629
|
+
status and doctor report invalid settings and unresolved claims with a removal
|
|
6630
|
+
command to run only after confirming the holder is gone. Invalid cache caps
|
|
6631
|
+
fall back to their own defaults; invalid maintenance settings disable passes.
|
|
6632
|
+
The run claim serializes passes with gc --delete; gc refuses a held claim
|
|
6633
|
+
with the holder and recovery guidance instead of waiting.
|
|
6634
|
+
|
|
5297
6635
|
CLEANUP AND DISK
|
|
5298
6636
|
|
|
6637
|
+
worktree remove stops the agent-device daemon when the workspace's
|
|
6638
|
+
agent-device/daemon.json exists, then removes its state directory with the
|
|
6639
|
+
workspace. If agent-device is missing or the stop fails, it keeps the directory
|
|
6640
|
+
and reports the retry command: agent-device daemon stop --state-dir <dir> --clean.
|
|
6641
|
+
A daemon.json whose process is gone is stale and does not block removal.
|
|
6642
|
+
gc --delete of a finished worktree does the same.
|
|
6643
|
+
stop closes owned-device sessions in that directory without deleting it.
|
|
6644
|
+
See stim guide logs for AGENT_DEVICE_STATE_DIR examples.
|
|
6645
|
+
|
|
5299
6646
|
WHAT RECLAIMS AN OWNED DEVICE
|
|
5300
6647
|
stim worktree remove parks eligible owned simulators and emulators
|
|
5301
6648
|
(\`guide lifecycle pool\`); deletes them when
|
|
@@ -5313,6 +6660,10 @@ WHAT RECLAIMS AN OWNED DEVICE
|
|
|
5313
6660
|
command has used in that long, even though the
|
|
5314
6661
|
project is still on disk, and clears only the
|
|
5315
6662
|
devices parked at least that long
|
|
6663
|
+
stim gc --cache parked --delete
|
|
6664
|
+
ERASES (never deletes) verified parked simulators
|
|
6665
|
+
and wipes parked emulators' user data; they stay
|
|
6666
|
+
parked for adoption (\`guide lifecycle pool\`)
|
|
5316
6667
|
stim gc --delete --worktrees
|
|
5317
6668
|
also runs \`stim worktree remove\` on every clean,
|
|
5318
6669
|
idle, Stim-managed linked worktree
|
|
@@ -5321,25 +6672,36 @@ WHAT RECLAIMS AN OWNED DEVICE
|
|
|
5321
6672
|
shuts DOWN (never deletes) owned devices with no
|
|
5322
6673
|
driver, claim or activity for that long
|
|
5323
6674
|
|
|
6675
|
+
\`gc --delete --cache watchman\` and \`gc --delete --cache gradle-daemons\`
|
|
6676
|
+
stop shared helper processes, not devices (\`guide cleanup memory\`).
|
|
6677
|
+
|
|
5324
6678
|
\`worktree remove\` and \`gc --delete\` are the only two commands that delete;
|
|
5325
6679
|
\`gc --delete\` deletes worktrees only through \`worktree remove\`. \`gc
|
|
5326
6680
|
--delete\` also clears workspace build outputs, trims oversized workspace logs
|
|
5327
6681
|
(\`guide cleanup disk\`) and removes orphaned workspace directories, never a
|
|
5328
|
-
checkout.
|
|
6682
|
+
checkout. Device recordings can also go without either: stim-server prunes
|
|
6683
|
+
them, and turning recording.enabled off deletes them (\`guide cleanup disk\`). \`stim stop\` shuts a device
|
|
5329
6684
|
DOWN and leaves it assigned, which is what makes returning to a branch cost a
|
|
5330
6685
|
boot rather than a create, a provision and a reinstall.
|
|
5331
6686
|
|
|
5332
6687
|
Before shutting down, parking or deleting an owned device, Stim best-effort
|
|
5333
6688
|
closes local agent-device sessions bound to its exact iOS UDID or live Android
|
|
5334
6689
|
serial. An Android session must also name the owned AVD, because the next
|
|
5335
|
-
emulator on a console port reuses its serial.
|
|
6690
|
+
emulator on a console port reuses its serial. For an emulator that is already
|
|
6691
|
+
shut down, Stim closes the sessions that name its AVD on any serial that no
|
|
6692
|
+
connected device now holds. \`stop\` closes a session only
|
|
5336
6693
|
when agent-device's claim on the device names it and was taken inside this
|
|
5337
|
-
workspace
|
|
6694
|
+
workspace. It also closes a session claimed from the workspace's git root
|
|
6695
|
+
itself, such as a worktree root above the app, but only after it has shut that
|
|
6696
|
+
device down and rechecked that the device is still stopped and that no other
|
|
6697
|
+
device holds its UDID or serial. Sessions from another workspace or claim,
|
|
6698
|
+
including sibling directories under the git root, stay open. Stim rechecks
|
|
5338
6699
|
device ownership and uses agent-device's rejecting session target guard;
|
|
5339
6700
|
sessions on other devices stay open. Physical devices are outside
|
|
5340
6701
|
this cleanup. agent-device is optional: a missing binary skips this step, and a
|
|
5341
6702
|
failed or timed-out list/close prints a device line on stderr while teardown
|
|
5342
|
-
continues.
|
|
6703
|
+
continues. Each cleanup pass allows at most 15 seconds of agent-device calls
|
|
6704
|
+
per device; the pass \`stop\` makes after shutdown has its own budget.
|
|
5343
6705
|
A local daemon that cannot use socket transport or a CLI without the target guard
|
|
5344
6706
|
skips cleanup with a warning; no remote daemon is used.
|
|
5345
6707
|
|
|
@@ -5350,9 +6712,77 @@ written by a newer Stim -- costs one dim line on stderr and is otherwise left
|
|
|
5350
6712
|
alone; only the next \`ios\` or \`android\` run moves an unparseable one aside
|
|
5351
6713
|
to stats.json.corrupt-<unix ms> and starts a new one.`,
|
|
5352
6714
|
sections: {
|
|
6715
|
+
archive: {
|
|
6716
|
+
summary: "retained workspace history and explicit archive cleanup",
|
|
6717
|
+
body: () => `ARCHIVE
|
|
6718
|
+
Linked-worktree removal and gc pruning a registered root that is gone keep
|
|
6719
|
+
state, ended agents, the error index, logs, closed recording segments and
|
|
6720
|
+
workspace-local agent-device sessions under $STIM_HOME/archive.
|
|
6721
|
+
Build outputs and open recording segments are deleted with the workspace.
|
|
6722
|
+
Archives have no checkout, devices, ports or running processes. A recreated
|
|
6723
|
+
path starts fresh; status links its earlier archives with replacedBy.
|
|
6724
|
+
Paired clients with read can select archived[].id with archive instead of
|
|
6725
|
+
workspace for server log queries and replay. Archived log subscriptions send
|
|
6726
|
+
retained records, then logs-ended. Archived frame subscriptions need at and
|
|
6727
|
+
cannot be physical or go live. Replay ranges report recording disabled.
|
|
6728
|
+
Reads stay on this Mac.
|
|
6729
|
+
An updated server is required; older servers refuse with bad-request when
|
|
6730
|
+
workspace is omitted. There is no CLI archive log flag.
|
|
6731
|
+
archive.enabled defaults true and accepts every settings layer. STIM_HOME
|
|
6732
|
+
disables archives unless STIM_ARCHIVE_ENABLED is set. Empty stubs and orphaned
|
|
6733
|
+
workspace directories are not archived. A failure prints one stderr line
|
|
6734
|
+
beginning "could not archive:" and removal continues with empty stdout.
|
|
6735
|
+
|
|
6736
|
+
Retention runs on every worktree removal, archived or not, once an archive
|
|
6737
|
+
exists. It deletes records past their age or count limit first, then expires
|
|
6738
|
+
recordings, agent actions and logs by age from removedAt; trims per-workspace
|
|
6739
|
+
logs (oldest rotated generations, then build logs, then oldest other files);
|
|
6740
|
+
and removes the largest kind of the oldest archive while over total caps.
|
|
6741
|
+
Archives with expired artifacts keep their record until the record limit. recording.enabled false at machine scope deletes archived
|
|
6742
|
+
recordings on the next retention pass. Sizes are binary GB/MB; 0 keeps none.
|
|
6743
|
+
See stim guide settings for the nine archive.* keys and overrides.
|
|
6744
|
+
|
|
6745
|
+
stim gc --cache archived list ids, kinds, bytes and expiry
|
|
6746
|
+
stim gc --delete --cache archived delete every archive
|
|
6747
|
+
stim gc --delete --cache archived --older-than 14
|
|
6748
|
+
only removed at least 14 days ago
|
|
6749
|
+
stim gc --delete --cache archived:<id> delete one; ignores --older-than
|
|
6750
|
+
stim gc --delete --cache archived-logs delete logs, keep records
|
|
6751
|
+
stim gc --delete --cache archived-recordings delete recordings, keep records
|
|
6752
|
+
stim gc --delete --cache archived-agent delete agent actions, keep records
|
|
6753
|
+
Per-kind selectors also accept --older-than, measured from removedAt.
|
|
6754
|
+
Normal gc, --cache all, --worktrees and unscoped --older-than exclude archives.
|
|
6755
|
+
Archive scopes report abandoned staging directories and delete only those
|
|
6756
|
+
with no live or unresolved claim. An unresolved claim names its removal command.
|
|
6757
|
+
|
|
6758
|
+
Directories are 0700 and files 0600. No redaction is applied; logs and agent
|
|
6759
|
+
actions can contain secrets. Use archive.enabled false for a sensitive repo.
|
|
6760
|
+
Archived logs and replay are not exposed through CLI or app readers yet.`
|
|
6761
|
+
},
|
|
5353
6762
|
gc: {
|
|
5354
6763
|
summary: "what gc and worktree remove delete, keep and refuse: orphans, stale records, locks, leases, EAS sessions",
|
|
5355
|
-
body: () => `
|
|
6764
|
+
body: () => `AGENT-DEVICE (REPORT ONLY)
|
|
6765
|
+
Unscoped gc reports agent-device runner builds by platform and entry, last use,
|
|
6766
|
+
agent-device and Xcode versions, sessions, logs, other state, workspace state
|
|
6767
|
+
and hosted driver state. Stim never selects this state as a cache.
|
|
6768
|
+
Stim never trims or deletes the shared runner builds, sessions, logs and other
|
|
6769
|
+
state or the hosted driver dir; a workspace's own agent-device dir goes only with its workspace.
|
|
6770
|
+
A live owner or runner lease marks an entry in use. A lock marks it in use only
|
|
6771
|
+
when its owner is live or unknown. Unreadable lock owners and unreadable or unknown
|
|
6772
|
+
leases conservatively mark entries in use. Dead lock owners and leases do not.
|
|
6773
|
+
Use agent-device's own tooling or remove directories yourself to clear unused
|
|
6774
|
+
state. --cache scopes omit this report, including --cache all.
|
|
6775
|
+
|
|
6776
|
+
SWIFTPM CACHE (REPORT ONLY)
|
|
6777
|
+
Unscoped gc also reports the user-level SwiftPM cache after agent-device:
|
|
6778
|
+
~/Library/Caches/org.swift.swiftpm on macOS, or org.swift.swiftpm under
|
|
6779
|
+
XDG_CACHE_HOME (default ~/.cache) elsewhere. It is shared by every SwiftPM
|
|
6780
|
+
build on this machine. Stim reports it and never deletes it. It is never in
|
|
6781
|
+
caches, never changes actionable, and --delete and --older-than never select
|
|
6782
|
+
it. Any --cache scope, including all, omits measurement and reports
|
|
6783
|
+
sections.swiftpmCache: null. Only stats and unscoped gc measure it.
|
|
6784
|
+
|
|
6785
|
+
LINKED WORKTREES
|
|
5356
6786
|
\`stim worktree remove\` works with any linked worktree, warmed or not.
|
|
5357
6787
|
Git registration identifies the worktree; a Stim registry entry is not
|
|
5358
6788
|
required. Before it reclaims anything, the command refuses a worktree git
|
|
@@ -5388,7 +6818,8 @@ SWEEPING FINISHED WORKTREES
|
|
|
5388
6818
|
one that is idle: no recorded use for --older-than days, 7 without it; a
|
|
5389
6819
|
worktree whose last use is unknown is kept. Both need the same clean state;
|
|
5390
6820
|
a worktree is kept when it is the source checkout, bare, locked, in use,
|
|
5391
|
-
dirty (untracked files count; pod install churn
|
|
6821
|
+
dirty (untracked files count; pod install churn and
|
|
6822
|
+
watchman's untracked \`.watchman-cookie-*\` files alone do not), unpushed
|
|
5392
6823
|
(commits no remote-tracking ref or other local branch reaches), or has
|
|
5393
6824
|
initialized submodules. Without --worktrees, the report leaves out the
|
|
5394
6825
|
source checkout and roots outside git.
|
|
@@ -5443,11 +6874,14 @@ SWEEPING FINISHED WORKTREES
|
|
|
5443
6874
|
takes the pull request whose head is HEAD, else one whose head contains
|
|
5444
6875
|
HEAD. A pull request whose head HEAD is past ("PR #12 merged, and HEAD has
|
|
5445
6876
|
commits it does not"), one unrelated to HEAD (an older use of the branch
|
|
5446
|
-
name), and one from a fork
|
|
5447
|
-
|
|
5448
|
-
|
|
5449
|
-
|
|
5450
|
-
|
|
6877
|
+
name), and one from a fork that is not one of the repository's own remotes
|
|
6878
|
+
(a stranger's fork reusing the branch name) do not count; a pull request
|
|
6879
|
+
opened from the user's own fork counts like any other. A merged or closed
|
|
6880
|
+
one makes the worktree finished under the same clean state. Merged, its
|
|
6881
|
+
commits are kept on GitHub, so local commits whose remote branch was
|
|
6882
|
+
deleted do not block removal. Closed, they do: a closed pull request whose
|
|
6883
|
+
remote branch is gone keeps the worktree as unpushed. An open pull request
|
|
6884
|
+
never makes a worktree finished. When gh is
|
|
5451
6885
|
not installed, not signed in, or fails, the JSON pullRequestUnknown field
|
|
5452
6886
|
and the report say why, and gc judges from git alone. A detached HEAD is
|
|
5453
6887
|
not looked up. \`stim worktree remove\` makes the same check when
|
|
@@ -5492,6 +6926,20 @@ ORPHANED WORKSPACE DIRECTORIES
|
|
|
5492
6926
|
it. A directory with a missing or unparseable workspace.json, or a root on
|
|
5493
6927
|
an unmounted volume, is reported under Skipped and never deleted.
|
|
5494
6928
|
|
|
6929
|
+
STALE STATUS CACHE ENTRIES
|
|
6930
|
+
\`stim status\` caches each folder's size under $STIM_HOME/disk-usage and
|
|
6931
|
+
each worktree's pull request under $STIM_HOME/pull-requests. \`worktree
|
|
6932
|
+
remove\` drops the entries of the worktree it removed (its checkout,
|
|
6933
|
+
node_modules and workspace folder, and its pull request), not the size
|
|
6934
|
+
entries of its owned devices' data folders. \`gc\` reports the
|
|
6935
|
+
entries of a folder or worktree that was removed some other way under "Stale
|
|
6936
|
+
status cache entries", and \`gc --delete\` removes them. An entry counts as
|
|
6937
|
+
stale only when the path it records is gone and its volume is mounted (a
|
|
6938
|
+
measured node_modules folder follows its checkout). An entry that records no
|
|
6939
|
+
readable path, one whose path cannot be checked, and one on an unmounted
|
|
6940
|
+
volume are kept and counted under Skipped. \`status\` measures a folder again
|
|
6941
|
+
if it comes back.
|
|
6942
|
+
|
|
5495
6943
|
NAMED SERVER PORTS
|
|
5496
6944
|
worktree remove stops TCP listeners on each named allocation and releases
|
|
5497
6945
|
the ports. gc reports named allocations for missing workspaces; gc --delete
|
|
@@ -5683,9 +7131,65 @@ THE ONE CASE GC WILL NOT REAP
|
|
|
5683
7131
|
is gone, and the unrelated process is never signalled. A missing, malformed,
|
|
5684
7132
|
or unreadable identity leaves the record unverified and kept for a retry.
|
|
5685
7133
|
Wall-clock timestamps and command names are not ownership proof.`
|
|
7134
|
+
},
|
|
7135
|
+
memory: {
|
|
7136
|
+
summary: "watchman and Gradle and Kotlin daemon memory in gc, and when gc --delete --cache watchman or gradle-daemons stops them",
|
|
7137
|
+
body: () => `MEMORY
|
|
7138
|
+
Every \`gc\` without --cache, and \`gc --cache watchman\` or
|
|
7139
|
+
\`gc --cache gradle-daemons\`, reports the long-lived helpers that grow while
|
|
7140
|
+
they run: the shared watchman daemon, Gradle daemons and Kotlin compile
|
|
7141
|
+
daemons. Each line gives the pid, physical footprint (the measure status
|
|
7142
|
+
uses; resident size when the footprint cannot be read), uptime and whether
|
|
7143
|
+
it is idle, and a kept one says why. The reclaimable line totals what
|
|
7144
|
+
the two commands below would free now.
|
|
7145
|
+
|
|
7146
|
+
Only these two commands stop anything, and each acts only on its own kind.
|
|
7147
|
+
Neither a plain \`gc --delete\` nor \`--cache all\` touches them:
|
|
7148
|
+
stim gc --delete --cache watchman
|
|
7149
|
+
runs \`watchman watch-del\` on each stale root
|
|
7150
|
+
with no subscription or trigger, then
|
|
7151
|
+
\`watchman shutdown-server\` only when
|
|
7152
|
+
\`debug-status\` lists no client but gc's own
|
|
7153
|
+
call and no root has a trigger; otherwise
|
|
7154
|
+
the daemon is kept and the report names
|
|
7155
|
+
each client, as the Stim workspace whose
|
|
7156
|
+
dev server it belongs to where it can
|
|
7157
|
+
stim gc --delete --cache gradle-daemons
|
|
7158
|
+
stops each Gradle daemon its own
|
|
7159
|
+
\`gradle --status\` reports IDLE, then each
|
|
7160
|
+
Kotlin compile daemon with no open client
|
|
7161
|
+
connection once every Gradle daemon is idle
|
|
7162
|
+
|
|
7163
|
+
A stale watch root is one whose directory is gone from a mounted volume, or
|
|
7164
|
+
that sits in a linked worktree git pruned while its directory stayed.
|
|
7165
|
+
Watchman drops a root itself when its directory is deleted, so stale roots
|
|
7166
|
+
are rare. Removing one stops its recrawls but does not shrink the daemon;
|
|
7167
|
+
only a restart returns its memory. After a shutdown the next client that
|
|
7168
|
+
needs watchman starts it again and re-watches the roots in its state file.
|
|
7169
|
+
Every watchman call uses --no-spawn, so gc never starts the daemon.
|
|
7170
|
+
Shutting it down under a Metro that uses watchman would break that Metro's
|
|
7171
|
+
file watching, which is why any connected client keeps it.
|
|
7172
|
+
|
|
7173
|
+
gc finds a Gradle daemon's Gradle user home from the daemon log it holds
|
|
7174
|
+
open (lsof), and asks the daemon's own distribution for its status with
|
|
7175
|
+
that home and the daemon's Java. When every daemon that status lists for a
|
|
7176
|
+
home and version is idle, gc runs that distribution's \`gradle --stop\`;
|
|
7177
|
+
otherwise it sends SIGTERM to each idle daemon, after checking the pid still
|
|
7178
|
+
has the same start time. A build that picks a daemon between gc's last
|
|
7179
|
+
check and the stop fails and must be run again, so gc re-checks the build
|
|
7180
|
+
locks and the daemon's status right before each stop. Nothing is stopped while an Android build lock, or a build slot no
|
|
7181
|
+
iOS build holds, is live or unresolved. stim-server stops the daemons of
|
|
7182
|
+
offloaded builds (offload.gradleDaemonIdleMinutes, \`guide settings\`), so
|
|
7183
|
+
while it runs gc keeps those. A daemon whose home, distribution or status cannot be read is
|
|
7184
|
+
kept. Gradle stops an idle daemon itself after 3 hours and Kotlin after 2
|
|
7185
|
+
hours by default.
|
|
7186
|
+
|
|
7187
|
+
--older-than does not apply to these kinds and is refused with
|
|
7188
|
+
STIM_BAD_ARG. With STIM_HOME set, gc skips them: they are machine-global.
|
|
7189
|
+
\`stim doctor\` notes a watchman footprint over 2 GiB.`
|
|
5686
7190
|
},
|
|
5687
7191
|
disk: {
|
|
5688
|
-
summary: "disk usage, workspace build outputs and
|
|
7192
|
+
summary: "disk usage, workspace build outputs, logs and device recordings, AVD and build-log sizes, the data partition, trimming the shared caches",
|
|
5689
7193
|
body: () => `DISK
|
|
5690
7194
|
Logs, state, pidfiles and Xcode DerivedData are under the global workspace
|
|
5691
7195
|
directory, and \`worktree remove\` reclaims them. \`gc --delete\` clears the
|
|
@@ -5695,7 +7199,8 @@ THE ONE CASE GC WILL NOT REAP
|
|
|
5695
7199
|
|
|
5696
7200
|
Android AVDs normally live under ~/.android/avd, and a booted owned AVD can
|
|
5697
7201
|
use several GB. \`worktree remove\` parks the workspace's owned AVD with its
|
|
5698
|
-
user data and snapshots
|
|
7202
|
+
user data and snapshots kept, or deletes it when the pool is off or full;
|
|
7203
|
+
\`gc --cache parked --delete\` wipes a parked AVD's user data and snapshots;
|
|
5699
7204
|
plain \`stop\` only shuts it down for reuse. Stim uses Android's default Quick Boot
|
|
5700
7205
|
unless displayless Linux requires software rendering, where snapshots are
|
|
5701
7206
|
disabled. The first boot and a boot after the emulator, system image, or AVD
|
|
@@ -5707,7 +7212,7 @@ THE ONE CASE GC WILL NOT REAP
|
|
|
5707
7212
|
errors teardown\`).
|
|
5708
7213
|
New owned AVDs default to an 8 GiB data partition, though project settings can
|
|
5709
7214
|
change it. When enabled, Quick Boot keeps one automatic snapshot until the AVD is
|
|
5710
|
-
|
|
7215
|
+
wiped or deleted.
|
|
5711
7216
|
\`gc\` prints the on-disk size beside an orphaned or stale owned Android AVD
|
|
5712
7217
|
when its content directory can be read, and beside an orphaned or stale
|
|
5713
7218
|
owned simulator the data size simctl reports for it.
|
|
@@ -5738,15 +7243,20 @@ setting applies only to a newly created AVD; recreate the environment to adopt
|
|
|
5738
7243
|
a changed value.
|
|
5739
7244
|
|
|
5740
7245
|
WORKSPACE BUILD OUTPUTS
|
|
5741
|
-
Each workspace directory holds derived-data/, gradle-build/, android-cas
|
|
5742
|
-
|
|
5743
|
-
|
|
7246
|
+
Each workspace directory holds derived-data/, gradle-build/, android-cas/,
|
|
7247
|
+
cache-provider/, macos/build, the staged macos/<Product>.app and macos/staging-*
|
|
7248
|
+
leftovers. \`gc\` reports them as one detected cache, "Workspace build
|
|
7249
|
+
outputs", with a per-workspace size, last use and verdict. Plain
|
|
5744
7250
|
\`gc --delete\` clears them for every workspace not in use (see \`guide
|
|
5745
7251
|
cleanup gc\`), before anything else. \`--older-than <days>\` limits that to
|
|
5746
7252
|
workspaces idle at least that long, and keeps one whose last use is
|
|
5747
|
-
unknown. \`--cache workspaces\` acts on them alone
|
|
5748
|
-
|
|
5749
|
-
|
|
7253
|
+
unknown. \`--cache workspaces\` acts on them alone, except in the workspace
|
|
7254
|
+
directories that plain \`gc --delete\` removes whole (dead projects and
|
|
7255
|
+
orphaned directories); \`--cache all\` includes them. macos/ itself,
|
|
7256
|
+
macos/runtime.lock and its claim set, workspace.json, state.json, logs/,
|
|
7257
|
+
other macOS files, locks and device records stay, as does the project's
|
|
7258
|
+
own .build. The workspace keeps its devices and ports. A running, building,
|
|
7259
|
+
unverified or hosted macOS app keeps its workspace untouched.
|
|
5750
7260
|
stim gc --delete --cache workspaces --older-than 7
|
|
5751
7261
|
Last use is the newest of the lastUsedAt that start, ios, android, reload
|
|
5752
7262
|
and worktree warm record in state.json, lastBuild.startedAt,
|
|
@@ -5757,7 +7267,8 @@ WORKSPACE BUILD OUTPUTS
|
|
|
5757
7267
|
The next build of an unchanged app installs from the shared build cache.
|
|
5758
7268
|
After a native change the Xcode compilation cache speeds the rebuild, but on
|
|
5759
7269
|
React Native 0.86 Swift does not use it (explicit modules are off), so that
|
|
5760
|
-
build recompiles Swift.
|
|
7270
|
+
build recompiles Swift. The next \`stim macos\` rebuilds its scratch from
|
|
7271
|
+
nothing (a full Swift build) and restages the app.
|
|
5761
7272
|
|
|
5762
7273
|
WORKSPACE LOGS
|
|
5763
7274
|
\`gc\` reports the size of each workspace's logs/ (the workspaceLogs
|
|
@@ -5772,6 +7283,33 @@ WORKSPACE LOGS
|
|
|
5772
7283
|
stops the collector. Build transcripts keep the whole run and are never
|
|
5773
7284
|
trimmed, and nothing else under logs/ is touched.
|
|
5774
7285
|
|
|
7286
|
+
DEVICE RECORDINGS
|
|
7287
|
+
stim-server records owned simulators, emulators and the Stim-owned Chrome
|
|
7288
|
+
page while an automation tool drives them or a paired client watches them,
|
|
7289
|
+
into recordings/ in the workspace directory (\`guide settings\`). It keeps
|
|
7290
|
+
the last 15 minutes of recorded footage per device; idle time and time after
|
|
7291
|
+
\`stim stop\` age nothing out, so \`stop\` ends recording and the footage
|
|
7292
|
+
stays replayable. Across every workspace of this Stim home it keeps at most
|
|
7293
|
+
1 GiB, deleting the oldest footage first. Recordings are deleted by:
|
|
7294
|
+
|
|
7295
|
+
stim worktree remove that workspace's recordings, with its
|
|
7296
|
+
workspace directory
|
|
7297
|
+
stim gc --delete the recordings of dead or orphaned
|
|
7298
|
+
workspaces, with their workspace directory;
|
|
7299
|
+
a workspace whose project root cannot be
|
|
7300
|
+
proven gone keeps them
|
|
7301
|
+
stim gc --delete --older-than <days>
|
|
7302
|
+
also footage recorded before that many days
|
|
7303
|
+
ago, in every workspace
|
|
7304
|
+
stim gc --delete --cache recordings
|
|
7305
|
+
every workspace's recordings, and nothing
|
|
7306
|
+
else; --cache all includes them
|
|
7307
|
+
recording.enabled false the recordings of each workspace it turns off
|
|
7308
|
+
|
|
7309
|
+
\`gc\` lists them in the recordings section of \`gc --json\` with each
|
|
7310
|
+
workspace's bytes, what a delete would remove (deleteBytes), and why the rest
|
|
7311
|
+
stays.
|
|
7312
|
+
|
|
5775
7313
|
SHARED BUILD CACHES
|
|
5776
7314
|
The caches that make a second workspace fast are alive by design and never
|
|
5777
7315
|
included in a plain \`gc --delete\`. Every \`gc\` run reports them anyway,
|
|
@@ -5819,7 +7357,7 @@ read from, or an unknown key refuses with STIM_BAD_ARG naming the expected
|
|
|
5819
7357
|
shape, before anything is written. Writes to the machine file take its lock
|
|
5820
7358
|
and replace it atomically; a committed write keeps the file's other keys and
|
|
5821
7359
|
indentation. Run \`stim settings\` from the app directory: workspace and
|
|
5822
|
-
committed resolve from the nearest package.json, repo from its Git
|
|
7360
|
+
committed resolve from the nearest package.json or Package.swift, repo from its Git
|
|
5823
7361
|
repository. From a monorepo web package that resolves to its worktree's app
|
|
5824
7362
|
(see stim guide ports), workspace and committed are that app's entry and
|
|
5825
7363
|
.stim.json. worktree.exclude and worktree.defaultBranch are read only from
|
|
@@ -5838,9 +7376,11 @@ carries its dotted key, layers, and environment override under "x-stim".
|
|
|
5838
7376
|
Resolution order, first match wins:
|
|
5839
7377
|
1. workspace ~/.stim/config.json, under this project's entry
|
|
5840
7378
|
2. repo ~/.stim/config.json, under this repo's git common dir
|
|
5841
|
-
3. committed .stim.json beside the app's package.json
|
|
5842
|
-
4. machine ~/.stim/config.json, top-level optimizations
|
|
5843
|
-
android.
|
|
7379
|
+
3. committed .stim.json beside the app's package.json or Package.swift
|
|
7380
|
+
4. machine ~/.stim/config.json, top-level optimizations,
|
|
7381
|
+
ios.deviceType, ios.runtime, android.systemImage,
|
|
7382
|
+
android.deviceProfile and devices.idleShutdownMinutes
|
|
7383
|
+
and archive.enabled
|
|
5844
7384
|
5. Stim default
|
|
5845
7385
|
An environment override, where a setting has one, wins over every layer.
|
|
5846
7386
|
|
|
@@ -5866,19 +7406,31 @@ An app's .stim.json can contain:
|
|
|
5866
7406
|
"android": { "variant": "productionDebug" }
|
|
5867
7407
|
}
|
|
5868
7408
|
|
|
7409
|
+
ARCHIVE SETTINGS
|
|
7410
|
+
${SETTINGS.filter((setting) => setting.key.startsWith("archive.")).map((setting) => ` ${setting.key}: ${setting.description}; default ${setting.default}; layers ${setting.scopes.join(", ")}; ${setting.env}`).join("\n")}
|
|
7411
|
+
Sizes use binary GB/MB. Archive ages and caps accept 0 to keep none.
|
|
7412
|
+
STIM_HOME makes archive.enabled false unless STIM_ARCHIVE_ENABLED is set.
|
|
7413
|
+
See stim guide cleanup archive for retention and explicit deletion.
|
|
7414
|
+
|
|
5869
7415
|
KEYS STIM READS
|
|
5870
7416
|
ios.deviceType e.g. "iPhone 17 Pro" -- the simulator model this
|
|
5871
7417
|
workspace's owned sim is created as, spelled exactly as
|
|
5872
7418
|
\`xcrun simctl list devicetypes\` names it, and one an
|
|
5873
|
-
installed runtime can create.
|
|
5874
|
-
|
|
5875
|
-
|
|
5876
|
-
|
|
7419
|
+
installed runtime can create. Readable from the
|
|
7420
|
+
machine layer too, so one machine can default every
|
|
7421
|
+
workspace to a model; a project layer still wins. The
|
|
7422
|
+
\`--device-type\` flag overrides this per invocation. A
|
|
7423
|
+
name no installed runtime offers is STIM_BAD_ARG and
|
|
7424
|
+
the creatable names are printed, naming the layer the
|
|
7425
|
+
value came from
|
|
5877
7426
|
ios.runtime e.g. "26.2" -- the iOS runtime that sim is created on,
|
|
5878
7427
|
as a version ("26.2") or a runtime's full name
|
|
5879
|
-
("iOS 26.2"); nothing else matches.
|
|
5880
|
-
|
|
5881
|
-
|
|
7428
|
+
("iOS 26.2"); nothing else matches. Readable from the
|
|
7429
|
+
machine layer too, so one machine can default every
|
|
7430
|
+
workspace to a runtime; a project layer still wins.
|
|
7431
|
+
The \`--runtime\` flag overrides this per invocation,
|
|
7432
|
+
and an uninstalled version refuses the same way,
|
|
7433
|
+
naming the layer the value came from
|
|
5882
7434
|
ios.configuration e.g. "Release" -- the Xcode configuration to build
|
|
5883
7435
|
(simulator only). Committing
|
|
5884
7436
|
{ "ios": { "configuration": "Release" } } makes every
|
|
@@ -5886,9 +7438,10 @@ KEYS STIM READS
|
|
|
5886
7438
|
embedded JS, no Metro, cache keyed -release-sim, and
|
|
5887
7439
|
a JS-bundle swap on cache hits. The \`--configuration\`
|
|
5888
7440
|
flag overrides this per invocation. Unset means Debug.
|
|
5889
|
-
ios.remote "proxy"
|
|
5890
|
-
|
|
5891
|
-
|
|
7441
|
+
ios.remote "proxy", "eas", or a named approved Mac from
|
|
7442
|
+
hosting.machines, with the same meaning as --remote.
|
|
7443
|
+
"auto" is accepted but refuses until automatic placement
|
|
7444
|
+
ships. Unset runs here. See lifecycle hosted-ios.
|
|
5892
7445
|
ios.simslimProfile a SimSlim JSON profile under the app directory,
|
|
5893
7446
|
at most 64 KiB. Install the
|
|
5894
7447
|
external tool once with
|
|
@@ -5930,9 +7483,12 @@ KEYS STIM READS
|
|
|
5930
7483
|
interface.
|
|
5931
7484
|
android.systemImage e.g. "system-images;android-36;google_apis;arm64-v8a"
|
|
5932
7485
|
-- the sdkmanager package id the owned AVD is created
|
|
5933
|
-
from.
|
|
5934
|
-
|
|
5935
|
-
|
|
7486
|
+
from. Readable from the machine layer too, so one
|
|
7487
|
+
machine can default every workspace to an image; a
|
|
7488
|
+
project layer still wins. The \`--system-image\` flag
|
|
7489
|
+
overrides this per invocation, and an id this SDK has
|
|
7490
|
+
not installed is STIM_BAD_ARG with the installed ids
|
|
7491
|
+
printed, naming the layer the value came from.
|
|
5936
7492
|
android.deviceProfile e.g. "pixel_fold" -- the avdmanager hardware profile a
|
|
5937
7493
|
new owned AVD is created with, spelled exactly as
|
|
5938
7494
|
\`avdmanager list device -c\` prints it. Defaults to
|
|
@@ -6036,15 +7592,35 @@ ${ANDROID_AVD_CONFIG_HELP.map((line) => ` ${line}`).joi
|
|
|
6036
7592
|
shares this machine and is the only mode that needs no
|
|
6037
7593
|
tunnel. "expo" lets the Expo dev server tunnel itself.
|
|
6038
7594
|
"cloudflared" and "ngrok" name a managed provider
|
|
6039
|
-
explicitly.
|
|
7595
|
+
explicitly. "tailscale" explicitly selects a tailnet-only
|
|
7596
|
+
foreground serve process, never Funnel; "auto" never
|
|
7597
|
+
selects it. The device must share the tailnet. See
|
|
7598
|
+
\`guide metro\`. Any other value is refused as invalid.
|
|
6040
7599
|
metro.ngrokUrl the stable managed ngrok URL. It requires metro.tunnel
|
|
6041
7600
|
"ngrok" and passes --url to ngrok http. Stim owns
|
|
6042
7601
|
this process.
|
|
6043
7602
|
metro.publicUrl an existing tunnel's URL. Takes precedence over
|
|
6044
7603
|
starting one, whatever metro.tunnel says -- Stim
|
|
6045
7604
|
did not create it, so a Metro request through it is
|
|
6046
|
-
still gated
|
|
7605
|
+
still gated through a public bundle probe. Set it
|
|
6047
7606
|
before Expo start so the manifest advertises it.
|
|
7607
|
+
metro.port this workspace's Metro port, 1024-65535;
|
|
7608
|
+
STIM_METRO_PORT overrides it. Stim reserves this
|
|
7609
|
+
number instead of picking one from 8082 up, for a
|
|
7610
|
+
workspace whose tools already expect a port. A
|
|
7611
|
+
committed value suits a single checkout; give each
|
|
7612
|
+
worktree its own in the workspace layer or the
|
|
7613
|
+
environment. start refuses with STIM_BAD_ARG when
|
|
7614
|
+
another workspace reserves it or another process
|
|
7615
|
+
holds it. start, ios, android and web refuse a
|
|
7616
|
+
changed pin while this workspace's dev server runs
|
|
7617
|
+
(run stim stop before retrying) or its supervisor
|
|
7618
|
+
cannot be verified (stop it with the tool that
|
|
7619
|
+
started it). A foreign holder is refused before
|
|
7620
|
+
anything starts; this project's Metro on the pin is
|
|
7621
|
+
attached to. --no-metro-check uses the pin, then the
|
|
7622
|
+
recorded port, then 8081, without probing or reserving;
|
|
7623
|
+
an invalid pin still refuses.
|
|
6048
7624
|
metro.warmupUrl optional object with per-platform bundle URLs:
|
|
6049
7625
|
metro.warmupUrl.ios
|
|
6050
7626
|
metro.warmupUrl.android
|
|
@@ -6072,6 +7648,23 @@ ${ANDROID_AVD_CONFIG_HELP.map((line) => ` ${line}`).joi
|
|
|
6072
7648
|
driven. Read
|
|
6073
7649
|
when \`start\` spawns the supervisor; see
|
|
6074
7650
|
\`guide metro\`.
|
|
7651
|
+
devices.idleShutdownMinutes
|
|
7652
|
+
minutes an owned simulator or emulator stays idle
|
|
7653
|
+
(\`gc --idle\` conditions; an open stim-server viewer
|
|
7654
|
+
counts as activity) before its workspace's supervisor
|
|
7655
|
+
shuts it down, never deletes it. Default 0, never.
|
|
7656
|
+
Machine or project layers. Read when the supervisor
|
|
7657
|
+
starts; see \`guide lifecycle budget\`.
|
|
7658
|
+
macos.product the explicit Swift Package executable product built in
|
|
7659
|
+
Debug by stim macos
|
|
7660
|
+
macos.infoPlist development Info.plist relative to Package.swift,
|
|
7661
|
+
without shared URL schemes or an update feed
|
|
7662
|
+
macos.arguments string array passed directly to the executable;
|
|
7663
|
+
see stim guide macos for the prototype's limits
|
|
7664
|
+
macos.assetCatalog optional .xcassets directory relative to Package.swift,
|
|
7665
|
+
compiled into Contents/Resources before signing
|
|
7666
|
+
macos.resources map of Contents/Resources destinations to source paths
|
|
7667
|
+
relative to Package.swift; see stim guide macos
|
|
6075
7668
|
web.url the page \`stim web\` opens in the owned Chrome, an
|
|
6076
7669
|
http:// or https:// URL. {port:<label>} becomes the
|
|
6077
7670
|
workspace's named port (allocated like \`stim ports
|
|
@@ -6129,7 +7722,7 @@ ${ANDROID_AVD_CONFIG_HELP.map((line) => ` ${line}`).joi
|
|
|
6129
7722
|
them from the environment or the machine layers.
|
|
6130
7723
|
|
|
6131
7724
|
Each setting takes its documented type: string, array of strings, number,
|
|
6132
|
-
boolean, or object.
|
|
7725
|
+
boolean, or object. android.remote, metro.tunnel, web.viewport,
|
|
6133
7726
|
optimizations.android.compilerCache and optimizations.android.pch take only
|
|
6134
7727
|
their listed choices. A value of the wrong
|
|
6135
7728
|
type or outside those choices is refused by name on every command that resolves
|
|
@@ -6185,6 +7778,18 @@ overrides the file:
|
|
|
6185
7778
|
budget is off. A value of the wrong shape refuses start, ios and android with
|
|
6186
7779
|
STIM_BAD_ARG. See \`guide lifecycle budget\` for what each limit reclaims.
|
|
6187
7780
|
|
|
7781
|
+
AUTOMATIC MAINTENANCE IS MACHINE-LEVEL
|
|
7782
|
+
Report-only in this release: it measures and plans, and deletes or stops nothing.
|
|
7783
|
+
STIM_HOME and CI make the mode off unless STIM_MAINTENANCE is set.
|
|
7784
|
+
See \`guide cleanup\` for checks, plans and log paths.
|
|
7785
|
+
|
|
7786
|
+
${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}`}
|
|
7787
|
+
${setting.description}`).join("\n")}
|
|
7788
|
+
|
|
7789
|
+
Maintenance uses the sysctl pressure level on macOS, with no memory signal
|
|
7790
|
+
when sysctl fails. os.freemem() and maintenance.minAvailableMemoryGb apply
|
|
7791
|
+
only on other platforms.
|
|
7792
|
+
|
|
6188
7793
|
THE IOS SIMULATOR APP IS MACHINE-LEVEL
|
|
6189
7794
|
Top-level \`iosSimulatorApp\` in ~/.stim/config.json selects the macOS app that
|
|
6190
7795
|
displays Stim's owned local iOS simulator after boot. It is not a project
|
|
@@ -6251,7 +7856,9 @@ to its app path for every command it starts.
|
|
|
6251
7856
|
THE DEVICE POOL BOUNDS ARE MACHINE-LEVEL TOO
|
|
6252
7857
|
\`pool.iosParkedMax\` caps how many parked simulators \`worktree remove\` may
|
|
6253
7858
|
leave behind for a later workspace to adopt. It is machine-level for the same
|
|
6254
|
-
reason: the disk they sit on is the whole machine's
|
|
7859
|
+
reason: the disk they sit on is the whole machine's. A parked simulator keeps
|
|
7860
|
+
its apps and data, so it can hold gigabytes until \`stim gc --cache parked
|
|
7861
|
+
--delete\` erases it.
|
|
6255
7862
|
|
|
6256
7863
|
{
|
|
6257
7864
|
"pool": { "iosParkedMax": 3 }
|
|
@@ -6278,6 +7885,242 @@ test suites and the end-to-end harness use one -- and a scoped config must not
|
|
|
6278
7885
|
leave simulators on the machine it cannot account for. A redirected home that
|
|
6279
7886
|
wants a pool says so with the variable.
|
|
6280
7887
|
|
|
7888
|
+
DESKTOP ACCESS TICKETS
|
|
7889
|
+
Stim Desktop's Add wizard sets STIM_ACCESS_TICKET for one \`doctor --fix\`
|
|
7890
|
+
run. Agents never set it, never run \`stim-server setup\`, never edit
|
|
7891
|
+
\`offload.*\` or \`hosting.*\` for the user, and never approve requests.
|
|
7892
|
+
Stim sends the ticket (43 base64url characters) only in build and device-host access
|
|
7893
|
+
requests and stores only its SHA-256 ticketHash. With --fix, a still-pending
|
|
7894
|
+
request is replaced when its ticketHash is missing or differs from the current
|
|
7895
|
+
ticket; matching hashes and approved credentials are preserved.
|
|
7896
|
+
Approved doctor entries under buildMachines and deviceHosts include host
|
|
7897
|
+
{ name, screenRecording, accessibility } when the worker reports it.
|
|
7898
|
+
|
|
7899
|
+
HOSTING MACHINES ARE MACHINE-LEVEL
|
|
7900
|
+
\`hosting.machines\` names Macs that may host owned simulator sessions,
|
|
7901
|
+
by MagicDNS name with an optional serve port (default 7443). Name each node
|
|
7902
|
+
and port once. This is separate from build offloading:
|
|
7903
|
+
|
|
7904
|
+
stim settings set hosting.machines '["janics-mac-mini"]'
|
|
7905
|
+
stim doctor --fix
|
|
7906
|
+
|
|
7907
|
+
Only \`doctor --fix\` asks for device-host access. A person on that Mac
|
|
7908
|
+
approves the printed id with \`stim-server devices grant <id> --device-host\`.
|
|
7909
|
+
A person can also run \`stim-server setup\` on the worker: one node, one ticket,
|
|
7910
|
+
one expiry, at most one request per capability, with per-grant y/N in a
|
|
7911
|
+
terminal or \`--yes\` otherwise. Agents never run \`stim-server setup\` or
|
|
7912
|
+
approve requests. Setup never changes TCC or enables Funnel; an SSH-driven
|
|
7913
|
+
run is not offered. Without a terminal or --yes, setup refuses before installing
|
|
7914
|
+
unless every chosen capability already has a matching approval. Desktop reuse
|
|
7915
|
+
requires an existing tailnet route. Ctrl-C or SIGTERM completes the journal,
|
|
7916
|
+
releases the setup claim and exits 1; a typed N also exits 1. Hosting grants
|
|
7917
|
+
include no read, control or build capability.
|
|
7918
|
+
$STIM_HOME/device-host-machines.json stores a private token and pinned tailnet
|
|
7919
|
+
node. Doctor never prints the token; it reports each machine under deviceHosts
|
|
7920
|
+
in JSON. Stim sends tokens only to the pinned node's own tailnet address,
|
|
7921
|
+
with its MagicDNS name for TLS and Host routing. A changed node refuses
|
|
7922
|
+
access, and uncertain replies or unreadable credentials preserve the pin.
|
|
7923
|
+
Invalid hosting settings report an error and preserve every saved credential.
|
|
7924
|
+
\`doctor --fix\` forgets entries removed from hosting.machines; remove a
|
|
7925
|
+
replaced machine, run it, then re-add the name to request a new approval.
|
|
7926
|
+
A definite revoked or lapsed request can be requested again with --fix.
|
|
7927
|
+
An in-progress approval inspection reports busy rather than replacing its
|
|
7928
|
+
pending token. \`stim macos --remote <machine>\` runs a macOS app on an
|
|
7929
|
+
approved machine (stim guide macos); \`ios\` and \`android\` do not yet
|
|
7930
|
+
place sessions on these machines. To view or control a hosted macOS app, a person
|
|
7931
|
+
on that Mac approves Screen & System Audio Recording and Device Control and Data
|
|
7932
|
+
Access (Accessibility on macOS 26 and earlier) for Stim Host, the app
|
|
7933
|
+
\`stim-server service install\` runs the server under.
|
|
7934
|
+
|
|
7935
|
+
On a hosting Mac, \`hosting.agentDriver\` names the tool it starts so a
|
|
7936
|
+
client's coding agent can drive the macOS apps and iOS simulators it hosts for that client.
|
|
7937
|
+
The default, \`none\`, starts nothing. For macOS, \`agent-device\` starts its
|
|
7938
|
+
shared daemon only when that agent-device can lease a single app (its
|
|
7939
|
+
\`macos-app\` lease backend); otherwise agent control reports \`none\` with a
|
|
7940
|
+
notice, and no client is handed the Mac's desktop. STIM_AGENT_DEVICE_BIN in
|
|
7941
|
+
stim-server's environment names an agent-device binary to use instead of
|
|
7942
|
+
~/.local/bin/agent-device. Hosted iOS uses one daemon per simulator, pinned by
|
|
7943
|
+
its UDID policy; both Macs need agent-device 0.21.20 or later. See guide lifecycle
|
|
7944
|
+
hosted-ios. \`doctor\` on that Mac notes an installed hosted app that runs while
|
|
7945
|
+
the setting is \`none\`.
|
|
7946
|
+
|
|
7947
|
+
BUILD MACHINES ARE MACHINE-LEVEL
|
|
7948
|
+
\`offload.machines\` lists the Macs on the tailnet that may build for this one,
|
|
7949
|
+
by MagicDNS name, each optionally with the port of its \`tailscale serve\`
|
|
7950
|
+
route (default 7443):
|
|
7951
|
+
|
|
7952
|
+
{
|
|
7953
|
+
"offload": { "machines": ["janics-mac-mini"] }
|
|
7954
|
+
}
|
|
7955
|
+
|
|
7956
|
+
\`doctor --fix\`, run in any app directory, asks each named Mac for build access and pins its tailnet
|
|
7957
|
+
node in $STIM_HOME/build-machines.json. A person approves the request on that
|
|
7958
|
+
Mac with \`stim-server devices grant <id> --build\`; doctor prints the id.
|
|
7959
|
+
A person can instead run \`stim-server setup\` on the worker with a node,
|
|
7960
|
+
ticket and expiry, under the approval rule above. Build never includes read
|
|
7961
|
+
or control.
|
|
7962
|
+
Stim connects to a named Mac only while its name still belongs to the pinned
|
|
7963
|
+
node and never sends the token to another node. \`doctor\` reports each
|
|
7964
|
+
machine's pairing state and, for an approved machine, asks it for one build
|
|
7965
|
+
offer and lists every reason it would not take this app's iOS build: no
|
|
7966
|
+
answer, another Stim build, CPU, Xcode, simulator SDK or CocoaPods, no
|
|
7967
|
+
Bundler for an app whose Gemfile.lock pins CocoaPods, no iPhone simulator on
|
|
7968
|
+
the runtime \`stim ios\` builds for here, low disk, or busy. With Package.swift
|
|
7969
|
+
and no platform selection on a Mac, it also checks the macOS Xcode and SDK.
|
|
7970
|
+
|
|
7971
|
+
\`offload.machine\` (machine scope, default auto) selects placement for
|
|
7972
|
+
\`ios\`, \`android\` and \`macos\`. \`--build-machine <auto|local|name>\`
|
|
7973
|
+
overrides STIM_OFFLOAD_MACHINE, which overrides the setting. A blank environment
|
|
7974
|
+
value is unset. Trimmed auto/local are case-insensitive. Machine names match
|
|
7975
|
+
configured names case-insensitively, with port 7443 when omitted; reports use
|
|
7976
|
+
the configured entry.
|
|
7977
|
+
|
|
7978
|
+
auto follows offload.mode and keeps its local fallback behavior
|
|
7979
|
+
local builds only on this Mac for this invocation
|
|
7980
|
+
name requires a matching entry in offload.machines, already paired and
|
|
7981
|
+
approved; ignores offload.mode and this Mac's load/slot gating
|
|
7982
|
+
|
|
7983
|
+
A name never prompts for pairing, tries another machine or falls back here.
|
|
7984
|
+
Any refusal or failure is STIM_OFFLOAD_REFUSED with the machine and reason.
|
|
7985
|
+
Device, Release/non-Debug, --remote, Android CAS compiler builds, builds with
|
|
7986
|
+
the build cache off, and an unknown simulator runtime cannot build there and
|
|
7987
|
+
refuse on a cache miss. Invalid values, unlisted names and unpaired names refuse
|
|
7988
|
+
at setup before consulting the cache; they create no build record or failed-run
|
|
7989
|
+
stats. A listed paired name with a cache hit compiles nothing, contacts no worker
|
|
7990
|
+
and records buildMachine with builtOn absent. Build failures retain the selected
|
|
7991
|
+
value; builtOn is here or the configured machine entry when compilation ran.
|
|
7992
|
+
Prebuild and pod install still run on this Mac before a named offload. A refusal
|
|
7993
|
+
starts no local xcodebuild, Gradle or SwiftPM compile.
|
|
7994
|
+
Run stim doctor --fix to ask for build access if not paired. A person on the
|
|
7995
|
+
worker finds the id with stim-server devices and approves it with
|
|
7996
|
+
stim-server devices grant <id> --build, or runs stim-server setup there with
|
|
7997
|
+
the client node, ticket and expiry. Check stim settings get offload.machines,
|
|
7998
|
+
or rerun with --build-machine auto or --build-machine local. Ctrl-C cancels
|
|
7999
|
+
the remote build without a local xcodebuild, Gradle or SwiftPM compile.
|
|
8000
|
+
|
|
8001
|
+
Copyable agent prompt:
|
|
8002
|
+
Run stim ios --build-machine janics-mac-mini. If it refuses, report the
|
|
8003
|
+
STIM_OFFLOAD_REFUSED reason and remedy; do not retry with another placement.
|
|
8004
|
+
|
|
8005
|
+
With offload.machine auto, \`offload.mode\` decides where an iOS simulator Debug build or an Android
|
|
8006
|
+
emulator debug build or a stim macos SwiftPM Debug build compiles:
|
|
8007
|
+
|
|
8008
|
+
auto (default) here while this Mac has capacity: a free
|
|
8009
|
+
\`concurrency.maxBuilds\` slot (always, with no build limit) and a
|
|
8010
|
+
load per core under \`offload.maxLoadPerCore\`. Otherwise on a
|
|
8011
|
+
build machine that accepts the build and is expected to be faster:
|
|
8012
|
+
while every slot here is busy, any machine that accepts; while only
|
|
8013
|
+
the load is high, a machine whose load per core is lower than this
|
|
8014
|
+
Mac's. A machine too old to report its load counts only while every
|
|
8015
|
+
slot here is busy.
|
|
8016
|
+
force on a build machine whenever one accepts it
|
|
8017
|
+
off always here
|
|
8018
|
+
|
|
8019
|
+
Load per core is the 5-minute load average divided by the CPU count; a Mac's
|
|
8020
|
+
native builds are its Stim runs in prebuild, pods or compile on that Mac, not
|
|
8021
|
+
the ones it offloaded.
|
|
8022
|
+
\`offload.maxLoadPerCore\` (default 2) is the load per core at which a Mac
|
|
8023
|
+
counts as saturated, both here and on a build machine.
|
|
8024
|
+
|
|
8025
|
+
STIM_OFFLOAD_MODE overrides it for one command. Device, Release and
|
|
8026
|
+
\`--remote eas|proxy\` builds, Android builds with the Apple Clang CAS compiler cache,
|
|
8027
|
+
and iOS/Android runs with the build cache off, always build here. Hosted iOS
|
|
8028
|
+
Debug builds can use a separate --build-machine, targeting the hosting Mac's
|
|
8029
|
+
architecture and runtime.
|
|
8030
|
+
macOS uses matching Xcode and macOS SDK, with a per-client SwiftPM dependency
|
|
8031
|
+
cache on the worker. It skips JavaScript dependencies, prebuild and pods; see
|
|
8032
|
+
stim guide macos. macOS artifacts are not cached.
|
|
8033
|
+
An offloaded iOS/Android build first runs prebuild (and \`pod install\` for iOS) here,
|
|
8034
|
+
then asks every paired machine what it can build. It takes one whose Stim
|
|
8035
|
+
build matches this Mac and CPU matches the target simulator, with enough disk, that does not decline,
|
|
8036
|
+
preferring the one that already holds this repository, then the least loaded,
|
|
8037
|
+
and moves to the next one in that order when a machine that offered fails the
|
|
8038
|
+
sync or refuses to start the build.
|
|
8039
|
+
For iOS the machine's Xcode and simulator SDK must match, and it needs an
|
|
8040
|
+
iPhone simulator on the target runtime. Its CocoaPods must match too, unless
|
|
8041
|
+
the app's Gemfile.lock pins CocoaPods: both Macs then run that version through
|
|
8042
|
+
bundler, so the machine needs only Bundler on its stim-server PATH and
|
|
8043
|
+
installs the pinned gems itself on the first build. For Android its JDK major
|
|
8044
|
+
version must match, and its Android SDK must hold the NDK, build-tools and
|
|
8045
|
+
compile platform that the project's React Native version names in
|
|
8046
|
+
gradle/libs.versions.toml; Gradle and AGP come from the synced project. When
|
|
8047
|
+
no machine takes the build in auto placement, the run prints one line with each machine's
|
|
8048
|
+
reason, such as \`janics-mac-mini: busy (load at or above 2/core; load
|
|
8049
|
+
8.2/core, 2 builds) -> building here\`, and compiles here. It sends the files
|
|
8050
|
+
\`git ls-files -co --exclude-standard\` lists (the machine keeps only files it
|
|
8051
|
+
lacks, so an unignored secret such as \`.env\` is sent too), builds with
|
|
8052
|
+
Stim's own code there, refuses unless the fingerprint there equals this one,
|
|
8053
|
+
and brings the \`.app\` or APK back. An Android build there uses this Mac's
|
|
8054
|
+
variant, target ABI, Gradle build cache, PCH and compiler cache choices, so
|
|
8055
|
+
the APK matches the ABI-narrowed cache key. Stim checks the archive's sha256
|
|
8056
|
+
and fingerprints this checkout again before it stores the app under the
|
|
8057
|
+
post-mutation key and installs it the usual way, so an APK is still compared
|
|
8058
|
+
with the installed one before an install is skipped.
|
|
8059
|
+
While a machine builds, Stim pings it every 15 seconds and treats a minute of
|
|
8060
|
+
silence as a dropped connection while it waits for the build. When the
|
|
8061
|
+
connection drops without a close, Stim reconnects to the same pinned machine
|
|
8062
|
+
for up to 3 minutes and takes the running build back; the machine keeps it
|
|
8063
|
+
running for 5 minutes without a connection, then cancels it. With auto, a drop while the
|
|
8064
|
+
app is fetched still builds here. Interrupting the run with Ctrl-C closes the
|
|
8065
|
+
connection, which cancels the build there; a run killed another way, or
|
|
8066
|
+
interrupted while it reconnects, leaves it until those 5 minutes pass. With auto, a failure after a machine took the build prints \`offload failed: <reason>
|
|
8067
|
+
-> building here\` and compiles here. The run's lastBuilds entry and
|
|
8068
|
+
\`--json\` facts record the reason as offloadFallback. Offloading holds no
|
|
8069
|
+
local build slot; that fallback takes one. \`stim stats\` keeps where each
|
|
8070
|
+
compiling build ran and why (\`guide facts stats\`).
|
|
8071
|
+
An offloaded app lands only in this Mac's build cache, not in a remote cache
|
|
8072
|
+
provider. A project whose xcodebuild changes its own fingerprinted inputs
|
|
8073
|
+
fails the offload fingerprint check: auto builds here; a named placement refuses.
|
|
8074
|
+
|
|
8075
|
+
On the build machine, stim-server keeps each client's checkouts, dependencies,
|
|
8076
|
+
DerivedData, compilation cache, ccache and Gradle home under
|
|
8077
|
+
\`offload.workerRoot\` (absolute; default $STIM_HOME/build-worker), one area
|
|
8078
|
+
and Stim home per client and repository, and one Gradle home per client. It
|
|
8079
|
+
runs one offloaded build at a time. It declines a build while that volume has
|
|
8080
|
+
less than 10 GB free, while its own native builds and the offloaded one fill
|
|
8081
|
+
its \`concurrency.maxBuilds\`, or while its load per core is at or above its
|
|
8082
|
+
\`offload.maxLoadPerCore\`, and reports the reason in its offer. It boots and
|
|
8083
|
+
installs nothing. While an offloaded build runs there, it holds one of that
|
|
8084
|
+
Mac's \`concurrency.maxBuilds\` slots, so a local Stim build there waits for it
|
|
8085
|
+
and counts it as busy; the slot is freed when the build ends, is cancelled or
|
|
8086
|
+
its process is gone. Android builds there run on the JDK in stim-server's
|
|
8087
|
+
JAVA_HOME (else the macOS default JDK) with the SDK in its ANDROID_HOME (else
|
|
8088
|
+
~/Library/Android/sdk). The client's Gradle daemon stays warm there for
|
|
8089
|
+
\`offload.gradleDaemonIdleMinutes\` (default 30; 0 stops it when each build
|
|
8090
|
+
ends; a new value applies from the next daemon) and never holds a build
|
|
8091
|
+
slot. While none of the client's builds runs, stim-server stops it sooner
|
|
8092
|
+
when a build is cancelled, the client is revoked, or the machine has less
|
|
8093
|
+
than 2 GB of memory available, and deleting the client's directory under the
|
|
8094
|
+
worker root stops it too.
|
|
8095
|
+
\`stim-server service install [--serve]\` on the build machine keeps stim-server
|
|
8096
|
+
running as a login LaunchAgent; \`--path-prepend <dir>\` and \`--env KEY=VALUE\`
|
|
8097
|
+
pin a PATH entry or variable such as a private CocoaPods that stim-server's
|
|
8098
|
+
login-shell environment would otherwise replace.
|
|
8099
|
+
\`stim-server service update --release <version>\` there moves the service to
|
|
8100
|
+
that exact stim-server release from the public npm registry once npm verifies
|
|
8101
|
+
its integrity and registry signatures; \`--from <dir>\` installs the packed
|
|
8102
|
+
packages of a checkout instead. It waits for offloaded builds and hosted
|
|
8103
|
+
sessions to finish, restarts the job, and switches back when the new server
|
|
8104
|
+
exits or does not answer within 90 seconds; \`stim-server service rollback\` returns
|
|
8105
|
+
to the previous server. A client Mac approved for builds or device hosting
|
|
8106
|
+
there can ask for the same update over its tailnet connection: an npm release
|
|
8107
|
+
always, and its own packed build only while \`server.acceptClientBuilds\` is
|
|
8108
|
+
true on that machine (default false).
|
|
8109
|
+
Replacing a loaded service waits up to 45 seconds for its existing listeners to
|
|
8110
|
+
release the port. Uninstall does not wait for the port. If stopping the old job
|
|
8111
|
+
or starting the new one fails, install restores the previous plist and attempts
|
|
8112
|
+
to restart a previously loaded LaunchAgent; if it cannot, it prints the
|
|
8113
|
+
launchctl bootout/bootstrap remedy. A running LaunchAgent does not prove readiness; check \`stim-server service status\`
|
|
8114
|
+
and its reported log. The server listens before it touches the Stim home; a
|
|
8115
|
+
read-only child process reads the Stim home, server and recording directories,
|
|
8116
|
+
and until it returns \`/health\` answers 503 with \`startup.state\` pending.
|
|
8117
|
+
A read that does not return within 10 seconds, plus up to one second to stop
|
|
8118
|
+
the child, leaves the server listening and degraded, with the reason in
|
|
8119
|
+
\`/health\` and \`stim-server service status\`, and no status followers,
|
|
8120
|
+
watchers, native helpers or recorder; it reads again every 30 seconds and
|
|
8121
|
+
serves once a read returns. It preserves claims and recordings; it does not
|
|
8122
|
+
restore an inaccessible volume or grant filesystem access.
|
|
8123
|
+
|
|
6281
8124
|
THE GC WORKTREE GRACE PERIOD IS MACHINE-LEVEL
|
|
6282
8125
|
\`gc.worktreeGraceMinutes\` is how long \`gc --delete\` waits before it removes
|
|
6283
8126
|
a merged or idle linked worktree. The clock starts at the worktree's latest
|
|
@@ -6294,6 +8137,25 @@ which overrides the file. Absent means 120. \`0\` removes a finished worktree at
|
|
|
6294
8137
|
once. A value that is not a whole number 0 or more is warned about by \`gc\`,
|
|
6295
8138
|
which then uses 120. See \`guide cleanup\` for what else keeps a worktree.
|
|
6296
8139
|
|
|
8140
|
+
DEVICE RECORDINGS CAN BE TURNED OFF
|
|
8141
|
+
stim-server records the screens of owned simulators, emulators and the
|
|
8142
|
+
Stim-owned Chrome page while an automation tool drives them or a paired client
|
|
8143
|
+
watches them, so the phone app can scrub back through them. It keeps the last
|
|
8144
|
+
15 minutes of footage per device under $STIM_HOME/workspaces/<id>/recordings/,
|
|
8145
|
+
on this Mac only, and serves it only to paired clients. \`recording.enabled\`
|
|
8146
|
+
turns it off:
|
|
8147
|
+
|
|
8148
|
+
stim settings set recording.enabled false --scope machine
|
|
8149
|
+
|
|
8150
|
+
It is a boolean, true by default, set at machine, repo or workspace scope; the
|
|
8151
|
+
workspace layer wins, then repo, then machine. STIM_RECORDING (true, false, 1
|
|
8152
|
+
or 0) overrides every layer; stim-server reads it from its own environment when
|
|
8153
|
+
it starts. Turning it off takes effect at once: \`stim settings set\` deletes
|
|
8154
|
+
the recordings of every workspace it turns off, and stim-server stops recording
|
|
8155
|
+
them within seconds, as soon as \`stim status\` shows recording.enabled false
|
|
8156
|
+
for them. Turning it back on records from then on. See \`guide cleanup\` for
|
|
8157
|
+
how recordings are removed otherwise.
|
|
8158
|
+
|
|
6297
8159
|
STIM NEEDS NO PROJECT CHANGES TO RUN
|
|
6298
8160
|
Nothing above is required to use Stim. The performance caches that used to
|
|
6299
8161
|
be setup steps are supplied by Stim on the command lines it composes itself:
|
|
@@ -6649,11 +8511,28 @@ platform "web":
|
|
|
6649
8511
|
page document is error whatever its
|
|
6650
8512
|
status; another request's network failure or HTTP 5xx is
|
|
6651
8513
|
error, a 4xx warn, a canceled request debug; browser messages such
|
|
6652
|
-
as CSP violations; the browser's own lifecycle
|
|
8514
|
+
as CSP violations; the browser's own lifecycle; in-app
|
|
8515
|
+
route changes (history API or fragment) as web_route with
|
|
8516
|
+
url, at most four a second
|
|
8517
|
+
src agent input a DevTools client other than Stim's sent to the page
|
|
8518
|
+
while connected, as agent_action with driver (the tool),
|
|
8519
|
+
command click, type, press or scroll, msg (e.g. 'Clicked
|
|
8520
|
+
button "Save"', "Typed 12 characters into input#email"),
|
|
8521
|
+
deviceId (web.targetId) and details. Typed text is never
|
|
8522
|
+
recorded, only its length. Clicks, key presses, text input
|
|
8523
|
+
and wheel scrolls in the top frame are observed; pointer
|
|
8524
|
+
moves and iframes are not, and a typing, key or scroll burst
|
|
8525
|
+
is one record. Input from Stim Desktop's or the phone's Take
|
|
8526
|
+
over is not recorded, nor is other input within 3 seconds of
|
|
8527
|
+
it, nor input while no other client is connected. A person
|
|
8528
|
+
clicking a headed window while a tool is connected counts as
|
|
8529
|
+
that tool, and a script that disconnects within a few hundred
|
|
8530
|
+
milliseconds of its first input goes unrecorded
|
|
6653
8531
|
Expo also prints web console calls on Metro ("Web LOG"), so they can appear
|
|
6654
8532
|
twice: once from the page (client), once from Metro (metro, level info).
|
|
6655
8533
|
Each load of the page's top-level document is a page-load marker: stim web,
|
|
6656
|
-
stim reload, and a reload or navigation the page makes itself.
|
|
8534
|
+
stim reload, and a reload or navigation the page makes itself. A reload's
|
|
8535
|
+
marker carries reload: true. logs --errors
|
|
6657
8536
|
and the status error count report the page's records (platform web) from the
|
|
6658
8537
|
latest load only, the way Chrome DevTools clears its console when the page
|
|
6659
8538
|
navigates. A page load does not hide the native app's errors, and an ios or
|
|
@@ -6669,7 +8548,20 @@ them start their own browser:
|
|
|
6669
8548
|
The port reaches only the Stim profile: Chrome refuses remote debugging on
|
|
6670
8549
|
your default profile, and Stim never attaches to a browser it did not start.
|
|
6671
8550
|
The port is managed like metro: stim ports lists it as web-cdp (managed),
|
|
6672
|
-
and ports get, stop and release refuse it.
|
|
8551
|
+
and ports get, stop and release refuse it. While a tool is connected,
|
|
8552
|
+
status reports it as the browser's driver (web.activity, "driven by
|
|
8553
|
+
playwright"), the way it names agent-device on a simulator, and its clicks,
|
|
8554
|
+
typing and scrolls are agent actions: stim logs --source agent lists them,
|
|
8555
|
+
web.activity.recent["agent-action"] dates the newest, and Stim Desktop and
|
|
8556
|
+
the phone show them in the Web device's agent feed. web.targetId is the
|
|
8557
|
+
owned page; a tab another tool opens is not captured.
|
|
8558
|
+
|
|
8559
|
+
stim status also reports the document the page loaded last and how that
|
|
8560
|
+
load went: web.page { url, state: loading | loaded | failed, error?, route? }.
|
|
8561
|
+
route is the URL the page shows after an in-app route change since that
|
|
8562
|
+
load, and is absent when none moved it. A failed
|
|
8563
|
+
load (the dev server is down, a certificate error, a crash) shows on status's
|
|
8564
|
+
web line.
|
|
6673
8565
|
|
|
6674
8566
|
OWNERSHIP AND CLEANUP
|
|
6675
8567
|
|
|
@@ -6689,10 +8581,390 @@ which would otherwise block the next launch. status reports
|
|
|
6689
8581
|
browser-unverified when an identity cannot be proven; follow stim guide
|
|
6690
8582
|
errors teardown.
|
|
6691
8583
|
|
|
8584
|
+
STIM DESKTOP
|
|
8585
|
+
|
|
8586
|
+
Stim Desktop shows the owned Chrome as a Web tile next to the simulators,
|
|
8587
|
+
with live frames of the page read from its DevTools endpoint, the page's URL,
|
|
8588
|
+
a "Page failed to load" pill and "Driven by" when a tool is attached. Take
|
|
8589
|
+
over sends clicks, scrolls and keys to the page. The tile's buttons open the
|
|
8590
|
+
URL in your own browser (never the Stim profile), run stim reload web, and
|
|
8591
|
+
close the browser with stim stop --slot web.
|
|
8592
|
+
|
|
8593
|
+
THE PHONE APP
|
|
8594
|
+
|
|
8595
|
+
The phone app shows the page as a Web tile in the devices grid and the
|
|
8596
|
+
workspace screen, and opens it in the device viewer as H.264 video through
|
|
8597
|
+
stim-server. With Control on, taps click, drags scroll and the keyboard types;
|
|
8598
|
+
Back goes back in the page's history. Reload in the workspace menu runs stim
|
|
8599
|
+
reload web. While stim-server recorded the page (\`guide settings\`, device
|
|
8600
|
+
recordings), the viewer can scrub back through it, with the agent's actions
|
|
8601
|
+
as markers.
|
|
8602
|
+
|
|
6692
8603
|
LIMITS
|
|
6693
8604
|
|
|
6694
|
-
Chrome and Chromium only.
|
|
6695
|
-
|
|
8605
|
+
Chrome and Chromium only. The owned page is one tab: a page the app opens in
|
|
8606
|
+
a new window is not captured.`
|
|
8607
|
+
},
|
|
8608
|
+
macos: {
|
|
8609
|
+
summary: "Swift Package macOS Debug apps: owned bundle, logs, local window viewing, and --remote on another Mac",
|
|
8610
|
+
body: () => `MACOS: SWIFT PACKAGE DEVELOPMENT PROTOTYPE
|
|
8611
|
+
|
|
8612
|
+
If Stim is not installed globally, replace stim with npx stim.
|
|
8613
|
+
|
|
8614
|
+
ios --remote <machine> shares hosting.machines and its separate device-host
|
|
8615
|
+
approval. Read guide lifecycle hosted-ios for simulator placement and Metro.
|
|
8616
|
+
|
|
8617
|
+
Run from the directory containing Package.swift. This prototype builds one
|
|
8618
|
+
explicit executable product in Debug, creates an isolated development app,
|
|
8619
|
+
and launches it. It does not use package scripts, Metro, simulators, Xcode
|
|
8620
|
+
projects, release builds, provisioning, or store distribution.
|
|
8621
|
+
|
|
8622
|
+
Set macos.product and macos.infoPlist explicitly in .stim.json. Optional
|
|
8623
|
+
macos.arguments is an array of arguments passed directly to the executable:
|
|
8624
|
+
|
|
8625
|
+
{ "macos": { "product": "MyApp", "infoPlist": "Support/Info-Development.plist" } }
|
|
8626
|
+
|
|
8627
|
+
The plist must contain CFBundleIdentifier and CFBundleExecutable matching the
|
|
8628
|
+
product. Use a development plist without shared URL schemes or an update feed.
|
|
8629
|
+
Stim gives the copied bundle a workspace-specific identifier. SwiftPM resource
|
|
8630
|
+
bundles and frameworks in the reported build directory are copied into it;
|
|
8631
|
+
macos.resources maps destinations under Contents/Resources to file or directory
|
|
8632
|
+
sources relative to the Swift Package directory, for example:
|
|
8633
|
+
|
|
8634
|
+
{ "macos": { "resources": { "AppIcon.icns": "Support/AppIcon-Dev.icns" },
|
|
8635
|
+
"assetCatalog": "Support/Assets.xcassets" } }
|
|
8636
|
+
|
|
8637
|
+
macos.assetCatalog selects an optional .xcassets directory compiled by fixed
|
|
8638
|
+
xcrun actool arguments. Copies and the compiled Assets.car are added before
|
|
8639
|
+
ad hoc signing. If actool does not emit Assets.car, staging refuses; set
|
|
8640
|
+
LSMinimumSystemVersion in the plist when Xcode requires a deployment target.
|
|
8641
|
+
Destinations are non-empty relative paths without empty, . or
|
|
8642
|
+
.. segments, at most 1024 characters, and cannot overlap another resource,
|
|
8643
|
+
Assets.car when a catalog is set, or a SwiftPM resource bundle. At most 256
|
|
8644
|
+
resources are allowed. Sources must exist as regular files or directories;
|
|
8645
|
+
their realpaths must stay inside the git repository root, or the Swift Package
|
|
8646
|
+
directory when there is no git root, and a source directory cannot contain
|
|
8647
|
+
symbolic links. Sources can use ../ to reach other files
|
|
8648
|
+
inside that repository. Custom packaging scripts are not run and extra
|
|
8649
|
+
executables, including Stim Desktop's sim-fold helper, are not built.
|
|
8650
|
+
|
|
8651
|
+
stim macos # fixed SwiftPM Debug build, then launch
|
|
8652
|
+
stim macos --json # one launch record; progress goes to stderr
|
|
8653
|
+
stim status --json # environments[].macos and build state
|
|
8654
|
+
stim logs --source build
|
|
8655
|
+
stim logs --errors
|
|
8656
|
+
stim stop # stop this workspace's owned app and supervisor
|
|
8657
|
+
|
|
8658
|
+
Each invocation stops its previous owned app, rebuilds and launches. SwiftPM
|
|
8659
|
+
keeps incremental outputs in the workspace's runtime directory under STIM_HOME.
|
|
8660
|
+
Local stim macos starts the app in the background without activating it or
|
|
8661
|
+
changing focus: it sets STIM_BACKGROUND_LAUNCH=1 in the app's environment, which
|
|
8662
|
+
Stim Desktop honors. An app that activates itself at launch still takes focus.
|
|
8663
|
+
Hosted launches (macos --remote) do not set it.
|
|
8664
|
+
macOS artifacts are not cached. This prototype has no --plan, --slot, or reload command.
|
|
8665
|
+
A failed build records its error and compiler output without launching an app.
|
|
8666
|
+
Runtime stdout and stderr become client records; build output becomes build
|
|
8667
|
+
records, all with platform "macos". Unexpected app exits are error records.
|
|
8668
|
+
Stim runs the app with NSUnbufferedIO=YES, so Swift print output arrives per
|
|
8669
|
+
line instead of when the app exits.
|
|
8670
|
+
|
|
8671
|
+
BUILD OFFLOAD
|
|
8672
|
+
|
|
8673
|
+
--build-machine <auto|local|name> overrides STIM_OFFLOAD_MACHINE and the
|
|
8674
|
+
machine setting offload.machine (default auto). local builds here; a name
|
|
8675
|
+
requires the matching configured and paired build machine, ignoring offload.mode
|
|
8676
|
+
and this Mac's capacity. Any failure is STIM_OFFLOAD_REFUSED without a local xcodebuild, Gradle or
|
|
8677
|
+
SwiftPM compile or another machine. Invalid, unlisted or unpaired selections
|
|
8678
|
+
refuse before stopping the app or changing its build record. See stim guide errors STIM_OFFLOAD_REFUSED.
|
|
8679
|
+
|
|
8680
|
+
With offload.machine auto, offload.mode places these SwiftPM Debug builds:
|
|
8681
|
+
auto builds here while this Mac has capacity, force uses an approved build
|
|
8682
|
+
machine when one accepts, and off always builds here. Configure offload.machines
|
|
8683
|
+
and approve build access as described in stim guide settings. The worker needs
|
|
8684
|
+
matching Stim, CPU architecture, Xcode and macOS SDK, plus network access to
|
|
8685
|
+
fetch package dependencies the first time. It keeps SwiftPM dependencies in a
|
|
8686
|
+
per-client cache and incremental outputs per repository. It runs fixed swift
|
|
8687
|
+
build commands without JavaScript installs, prebuild or pods. It receives only
|
|
8688
|
+
the files git lists (tracked and untracked, not ignored). Resource and catalog
|
|
8689
|
+
sources must be among those files. The client sends resolved sources relative
|
|
8690
|
+
to the repository root; the worker resolves them inside its checkout.
|
|
8691
|
+
|
|
8692
|
+
The client validates the development plist and resource entries before asking a machine. It verifies
|
|
8693
|
+
the returned archive's sha256, bundle identifier, executable, declared resources
|
|
8694
|
+
and ad hoc signature
|
|
8695
|
+
before replacing the owned bundle, then launches locally as usual. With auto, every offload
|
|
8696
|
+
failure falls back to the local build, including force. Failed staging preserves
|
|
8697
|
+
the previous bundle. No failed artifact is promoted or cached. The build record
|
|
8698
|
+
is unchanged by invalid, unlisted or unpaired setup refusals. It
|
|
8699
|
+
carries buildMachine (selected) and builtOn (here or the machine, absent until
|
|
8700
|
+
a build runs), and errorCode on a typed failure. It carries offloadedTo only for a remote build, and offloadFallback when an offload
|
|
8701
|
+
attempt falls back here. Placement and failure reasons are in build logs.
|
|
8702
|
+
|
|
8703
|
+
OWNERSHIP AND LOCAL VIEWING
|
|
8704
|
+
|
|
8705
|
+
The supervisor owns a process-identity claim with the app as its child. Stop
|
|
8706
|
+
signals only verified recorded identities. An unverifiable or malformed owner
|
|
8707
|
+
refuses with STIM_MACOS_OWNER_UNVERIFIED; other apps remain untouched.
|
|
8708
|
+
|
|
8709
|
+
Stim Desktop shows this app in its workspace, with Build and run, Open app (local
|
|
8710
|
+
apps only) and Stop, and a live preview that updates itself while the app runs. Capture and Open app verify the recorded executable, bundle
|
|
8711
|
+
identifier, PID and process start time. Open app rechecks that the captured window
|
|
8712
|
+
is still the app's front window, or raises the pinned one, then activates that
|
|
8713
|
+
owned app for normal native input.
|
|
8714
|
+
The preview follows the app's front standard window, its main window with any
|
|
8715
|
+
attached sheet, as the app opens, switches, closes or resizes windows. It never
|
|
8716
|
+
captures another process's windows, menus or the desktop. Without Device Control
|
|
8717
|
+
and Data Access permission (Accessibility on macOS 26 and earlier) Stim cannot
|
|
8718
|
+
tell which window is in front, so the preview shows only an app whose one window
|
|
8719
|
+
contains the others. The viewer never captures its own process recursively.
|
|
8720
|
+
The captured view is read-only; background mouse/keyboard relay is not included.
|
|
8721
|
+
|
|
8722
|
+
Capture requires existing Screen & System Audio Recording permission (Screen
|
|
8723
|
+
Recording on macOS 14); Open app also requires Device Control and Data Access
|
|
8724
|
+
permission (Accessibility on macOS 26 and earlier). The first native viewer opening
|
|
8725
|
+
shows one Desktop setup screen for both permissions, named for this Mac's macOS
|
|
8726
|
+
version, with statuses, Request permissions, Settings and Check again. Settings
|
|
8727
|
+
opens the matching System Settings > Privacy & Security pane. You approve normal macOS requests; Stim never resets or grants access
|
|
8728
|
+
automatically. Permissions on the app card reopens setup. Builds never prompt.
|
|
8729
|
+
If unavailable, use the normal app window and
|
|
8730
|
+
read the workspace logs.
|
|
8731
|
+
|
|
8732
|
+
PHONE VIEWING AND CONTROL
|
|
8733
|
+
|
|
8734
|
+
A paired phone shows native app and build state in the workspace and home list.
|
|
8735
|
+
Tap the app tile to view the app's front window through stim-server. The server
|
|
8736
|
+
requires read access and its macos-window feature; it rechecks the recorded PID,
|
|
8737
|
+
process start time, executable and bundle before capture and on every frame.
|
|
8738
|
+
It never captures the desktop or another application, and offers no replay.
|
|
8739
|
+
The view follows the app's front standard window like the Desktop preview. A
|
|
8740
|
+
server advertising macos-windows also sends a macos-windows event on the frames
|
|
8741
|
+
subscription with the captured window and the app's windows (id, title and frame
|
|
8742
|
+
in points), after subscribing and whenever they change. Viewing starts only with
|
|
8743
|
+
an open window; after the app closes its last one the view reports a delay until
|
|
8744
|
+
another opens.
|
|
8745
|
+
|
|
8746
|
+
WINDOWS
|
|
8747
|
+
|
|
8748
|
+
The view follows the app's front window by default. A controller can pin it to
|
|
8749
|
+
one of the app's windows instead: with a server advertising macos-window-select,
|
|
8750
|
+
input.window { session, window: <id from macos-windows> } brings that window to
|
|
8751
|
+
the front of the app, and capture and input stay on it while another window
|
|
8752
|
+
comes forward on the Mac. input.window { session, window: null } follows the
|
|
8753
|
+
front window again. The pin also ends when its window closes or minimizes, or
|
|
8754
|
+
when the Control session that set it ends for any reason, including release,
|
|
8755
|
+
takeover, disconnect and the five-minute idle timeout, since viewers without
|
|
8756
|
+
Control cannot unpin; macos-windows reports pinned true while it holds. Choosing
|
|
8757
|
+
a window while the app shows a modal dialog, or one that just closed, is dropped
|
|
8758
|
+
without ending Control. The phone's Control toolbar and Stim Desktop's app card and
|
|
8759
|
+
hosted viewer offer the same choice as a Window menu: Follow front window, or a
|
|
8760
|
+
window by title. Stim Desktop's local preview pins without Control because it
|
|
8761
|
+
only raises the window among the app's own windows. An agent that drives the app
|
|
8762
|
+
through the stim-server protocol sends the same input.window.
|
|
8763
|
+
|
|
8764
|
+
A server advertising macos-window-control also supports the phone's Control
|
|
8765
|
+
mode on a control pairing. Tap/click and drag act on the displayed window;
|
|
8766
|
+
Scroll mode turns a drag into pixel scrolling. The main toolbar offers Keyboard
|
|
8767
|
+
and Scroll. Keyboard types printable ASCII and attaches an extra-key strip with
|
|
8768
|
+
Tab, Escape, Backspace, arrows and labeled Select all, Undo, Save, Copy, Paste,
|
|
8769
|
+
Cut and Find shortcuts. Shift, Control, Option and Command apply to the next
|
|
8770
|
+
supported key, then clear; dismissing the keyboard clears them too.
|
|
8771
|
+
|
|
8772
|
+
A newly built phone client with Keyboard Controller is required; this native
|
|
8773
|
+
change cannot be delivered to an older client by a JavaScript update. Servers
|
|
8774
|
+
advertising macos-keyboard-extended accept modified a-z and 0-9 one key at a
|
|
8775
|
+
time. Older servers retain fixed shortcuts and navigation but cannot receive
|
|
8776
|
+
other modified letters or digits. Multi-character modified input and symbols
|
|
8777
|
+
are not supported; ordinary typing keeps using input.text.
|
|
8778
|
+
|
|
8779
|
+
Shortcuts for a-z and 0-9 use the key that types the character in the Mac's
|
|
8780
|
+
current keyboard layout, using the Command layer when Command is held. Dvorak
|
|
8781
|
+
and Dvorak-QWERTY Command are supported. On Russian and similar layouts
|
|
8782
|
+
(Cyrillic, Greek, Hebrew, Arabic), Latin-letter shortcuts work with Command;
|
|
8783
|
+
Control-only or Option-only letter shortcuts are refused. With Control, the
|
|
8784
|
+
layout's Control table must also yield the requested character or, for letters,
|
|
8785
|
+
its C0 control character; otherwise the shortcut is refused. The Mac's selected
|
|
8786
|
+
input source is read on each key, so switching layouts takes effect on
|
|
8787
|
+
the next key. Letters and digits available only with Shift or Option, or through
|
|
8788
|
+
a dead key (for example digits on AZERTY), are refused with a reason naming the
|
|
8789
|
+
layout, and Control ends. Stim does not add modifiers to reach those characters.
|
|
8790
|
+
Ordinary typing and navigation do not depend on the layout. Symbols such as
|
|
8791
|
+
comma remain unsupported key names.
|
|
8792
|
+
Control posts input to the owned process without activating it or raising its
|
|
8793
|
+
window; only choosing a window to pin raises it among the app's windows. Only when the captured window is not the app's key window (or its
|
|
8794
|
+
attached sheet) does Stim activate the app to deliver input, waiting up to one
|
|
8795
|
+
second for focus. The helper then sends a controlActivated notice, which stim-server
|
|
8796
|
+
logs.
|
|
8797
|
+
Clicks on views that reject the first mouse, such as custom views and SwiftUI
|
|
8798
|
+
onTapGesture regions, do not land while the app is in the background. Use
|
|
8799
|
+
Desktop's Open app to bring the app to the front for those views.
|
|
8800
|
+
Control holds one exclusive server session per owned app, ends on disconnect,
|
|
8801
|
+
revocation, takeover or five minutes without input, and does not take a CLI
|
|
8802
|
+
simulator/device lock. Each action rechecks the exact owned process and that the
|
|
8803
|
+
captured window is still the app's front standard window, or the pinned one. The
|
|
8804
|
+
app's other windows are allowed; input goes to the captured window, and a sheet attached to it takes
|
|
8805
|
+
focus and pointer input. Input that arrives while the view moves to another
|
|
8806
|
+
window is dropped and Control continues. A modal dialog window refuses input. A
|
|
8807
|
+
sheet larger than the captured window is not supported. Existing Device
|
|
8808
|
+
Control and Data Access permission (Accessibility on macOS 26 and earlier) is
|
|
8809
|
+
required. The phone and server never request or reset it. A refusal ends Control
|
|
8810
|
+
with its reason while viewing and logs remain usable. Older servers remain
|
|
8811
|
+
view-only.
|
|
8812
|
+
Native Control uses dynamically resolved private CoreGraphics input SPI in the
|
|
8813
|
+
server helper, outside the phone and Mac App Store app binaries. A missing symbol
|
|
8814
|
+
or incompatible macOS version refuses Control while viewing and logs remain usable.
|
|
8815
|
+
|
|
8816
|
+
The existing capture host needs Screen & System Audio Recording permission (Screen
|
|
8817
|
+
Recording on macOS 14). If capture is denied,
|
|
8818
|
+
open Permissions in Stim Desktop on the host Mac, approve its normal OS requests
|
|
8819
|
+
and reconnect the phone viewer. The Desktop host presents setup on the first
|
|
8820
|
+
native viewer opening, including one initiated by the phone. A server started
|
|
8821
|
+
outside Desktop uses that launching host's permissions; granting this copy of Stim
|
|
8822
|
+
may not apply to it. The server never requests or resets permissions. Status and logs still work. Tap the build card for
|
|
8823
|
+
SwiftPM output or the logs card for native runtime stdout and stderr. Metro is not
|
|
8824
|
+
used.
|
|
8825
|
+
|
|
8826
|
+
ON ANOTHER MAC
|
|
8827
|
+
|
|
8828
|
+
stim macos --remote <machine> builds the Debug app on this Mac and runs it on an
|
|
8829
|
+
approved hosting Mac over the tailnet, without SSH. The machine must be listed
|
|
8830
|
+
in hosting.machines and approved: stim doctor --fix asks it, a person on that
|
|
8831
|
+
Mac runs stim-server devices grant <id> --device-host or stim-server setup
|
|
8832
|
+
with that client node, ticket and expiry (guide settings), and stim doctor then
|
|
8833
|
+
records the approval. Stim connects only to the machine's pinned tailnet node.
|
|
8834
|
+
A refusal or an unreachable host fails the command; it never launches here
|
|
8835
|
+
instead.
|
|
8836
|
+
|
|
8837
|
+
The --remote value is a hosting Mac name from hosting.machines. macOS refuses
|
|
8838
|
+
eas and proxy because it has neither backend. It also refuses auto until
|
|
8839
|
+
automatic placement ships. These reserved names are case-insensitive and
|
|
8840
|
+
trimmed; they refuse with STIM_BAD_ARG before state access or a connection.
|
|
8841
|
+
|
|
8842
|
+
Logs: the host records the hosted app's stdout, stderr and exit like a local run.
|
|
8843
|
+
stim logs, including --errors, --json and --follow, asks the host over the same
|
|
8844
|
+
approved connection, copies the records it has not copied yet into the
|
|
8845
|
+
workspace's logs/macos-host.ndjson and prints them with the local records.
|
|
8846
|
+
stim stop copies the last ones, including the exit record, before it forgets the
|
|
8847
|
+
placement, so the logs stay readable after stop. A host that cannot answer, because
|
|
8848
|
+
it is unreachable or runs a stim-server that predates this, costs one stderr
|
|
8849
|
+
warning (after up to 10 seconds of connecting); stdout still carries the records already copied, so logs --json stays
|
|
8850
|
+
valid NDJSON. The host's unified log is not collected: os.Logger output that is
|
|
8851
|
+
not written to stderr does not appear.
|
|
8852
|
+
|
|
8853
|
+
Phones and Stim Desktop view and control the hosted app through this Mac's
|
|
8854
|
+
stim-server, which relays to the host. Hosted iOS simulator tiles use the same
|
|
8855
|
+
relay and show on <machine>; see guide lifecycle hosted-ios. The phone sends clicks, scrolls,
|
|
8856
|
+
text and keys; Stim Desktop's Control sends clicks, drags and typed text.
|
|
8857
|
+
|
|
8858
|
+
stim macos --remote mini # build here, deliver, launch on mini
|
|
8859
|
+
stim macos --remote mini --json # { platform, product, launchId, build, host }
|
|
8860
|
+
stim status --json # environments[].macos.state and macos.host
|
|
8861
|
+
stim stop # stop the session on mini and confirm it
|
|
8862
|
+
stim worktree remove <path> # also stops it
|
|
8863
|
+
|
|
8864
|
+
The copy keeps the plist's own CFBundleIdentifier. The host runs it as
|
|
8865
|
+
<id>.hosted<slot> from a fixed slot pool, so the bundle id stays the same
|
|
8866
|
+
across rebuilds; whether macOS keeps the permissions the hosted app asks for
|
|
8867
|
+
itself also depends on how the host signs it. Running the
|
|
8868
|
+
command again reuses the session and delivers a new copy. macos.arguments are
|
|
8869
|
+
passed to the hosted launch as plain arguments, without environment injection:
|
|
8870
|
+
at most 32 arguments, 1024 characters each and 8192 characters total. Empty
|
|
8871
|
+
strings are allowed; NUL, CR and LF are refused. Values are stored in the host's
|
|
8872
|
+
app receipt and visible in process listings, so do not put secrets there. Older
|
|
8873
|
+
hosts ignore them and Stim warns to update stim-server on the host.
|
|
8874
|
+
status --json reports the host's applied arguments under
|
|
8875
|
+
environments[].macos.arguments.
|
|
8876
|
+
A workspace has one macOS app: a local stim macos
|
|
8877
|
+
refuses while it runs on a host, and --remote with a different machine refuses
|
|
8878
|
+
until stim stop. When the host cannot be reached or does not confirm the stop,
|
|
8879
|
+
the placement stays recorded; restore the connection and run stim stop again.
|
|
8880
|
+
|
|
8881
|
+
Viewing and controlling the hosted app from a phone needs Screen & System Audio
|
|
8882
|
+
Recording and Device Control and Data Access (Accessibility on macOS 26 and
|
|
8883
|
+
earlier), granted once on the host to the app that runs stim-server, not to the
|
|
8884
|
+
hosted app. stim-server service install runs the server under the signed Stim
|
|
8885
|
+
Host app (dev.stim.host, installed in ~/Applications) and shows
|
|
8886
|
+
macOS's own requests on that Mac's screen; a person there approves them.
|
|
8887
|
+
stim-server service status shows the grants, and stim doctor here reports an
|
|
8888
|
+
approved host that lacks them. A server started by Stim Desktop uses Desktop's
|
|
8889
|
+
grants.
|
|
8890
|
+
|
|
8891
|
+
Hosted delivery carries the staged bundle with declared resources and compiled assets.
|
|
8892
|
+
When offload built it on the host's own tailnet node, the host copies the files
|
|
8893
|
+
from the build it kept for this Mac instead of receiving them again, admitting
|
|
8894
|
+
only bytes that match the bundle's digests here. That needs both the build and
|
|
8895
|
+
the device-host approval for this Mac. Files the host cannot take, an older
|
|
8896
|
+
host, or a build fetched more than 10 minutes earlier are uploaded as before.
|
|
8897
|
+
|
|
8898
|
+
status --json reports macos.host { machine, session, appSlot, appAttempt,
|
|
8899
|
+
bundleId, agent }. For hosted placements only, status asks the host for the
|
|
8900
|
+
session state with about a 3 s timeout per connection and request and a 10 s
|
|
8901
|
+
cache. It reports stopped when the host says the session stopped, for example
|
|
8902
|
+
after a stim-server restart there, and unverified when the host is unreachable
|
|
8903
|
+
or cannot confirm. Run stim macos --remote <machine> to launch a stopped app
|
|
8904
|
+
again, or stim stop to clear or reconcile the placement. The host field stays
|
|
8905
|
+
in status --json for cleanup.
|
|
8906
|
+
While a placement is recorded, gc treats the workspace as in use. agent says
|
|
8907
|
+
how a coding agent drives the app: { driver: "none", setting:
|
|
8908
|
+
"hosting.agentDriver" } until the hosting Mac's owner sets that setting there,
|
|
8909
|
+
or { driver: "agent-device", remoteConfig, command }. remoteConfig is a mode
|
|
8910
|
+
0600 file in the workspace directory that holds the credential; run the command
|
|
8911
|
+
it names and never print the file. stop, worktree remove and gc first run
|
|
8912
|
+
agent-device close and disconnect for the connection that agent-device reports
|
|
8913
|
+
as connected to that remote config (the default or active session), then delete
|
|
8914
|
+
the file, so the next hosted workspace needs no manual disconnect. Any other
|
|
8915
|
+
connection, including one under another session name, stays untouched, and a
|
|
8916
|
+
failure or a missing agent-device is reported without blocking the stop. When
|
|
8917
|
+
the hosted session ends, stim-server removes its agent-device session
|
|
8918
|
+
directories under its own state directory. Start with agent-device open
|
|
8919
|
+
<bundleId> --remote-config <path>, using macos.host.bundleId; the lease allows
|
|
8920
|
+
open, close, snapshot, wait, find, get, is, click, fill, press, type, focus,
|
|
8921
|
+
scroll, screenshot and batch on that app only. The agent-device on this Mac must
|
|
8922
|
+
know the macos-app lease backend. A request outside that command list, or a
|
|
8923
|
+
method the relay does not forward, is refused with an agent-device UNAUTHORIZED
|
|
8924
|
+
error whose details.reason is STIM_AGENT_REQUEST_REFUSED and whose message names
|
|
8925
|
+
the refused command or method; do not retry it.
|
|
8926
|
+
|
|
8927
|
+
Agents that built, launched and drove test copies on another Mac with an SSH
|
|
8928
|
+
script such as mini-desktop.sh use these instead:
|
|
8929
|
+
|
|
8930
|
+
script command stim
|
|
8931
|
+
build stim macos --remote <mac>
|
|
8932
|
+
launch the same command; the host launches it under the
|
|
8933
|
+
session's own home
|
|
8934
|
+
shot <out.png> agent-device screenshot --remote-config <path>
|
|
8935
|
+
click, rclick, key, type agent-device click, press or type --remote-config <path>
|
|
8936
|
+
quit stim stop (verified stop; the host frees the slot
|
|
8937
|
+
and removes the app's defaults)
|
|
8938
|
+
clean stim worktree remove
|
|
8939
|
+
3 concurrent slots concurrency.maxDevices on the host; one bundle id
|
|
8940
|
+
slot per session
|
|
8941
|
+
|
|
8942
|
+
The agent-device rows work once the host's agent field names agent-device.
|
|
8943
|
+
|
|
8944
|
+
DESKTOP DOGFOOD
|
|
8945
|
+
|
|
8946
|
+
This repository's apps/desktop/.stim.json selects the full StimDesktop app,
|
|
8947
|
+
its development plist, asset catalog, fonts, branding and licences. Its sim-fold
|
|
8948
|
+
helper is not built. It monitors the regular Stim home alongside the installed
|
|
8949
|
+
app, with a workspace-specific bundle identifier and separate preferences.
|
|
8950
|
+
Its launch arguments disable automatic cleanup and notification alerts in this development copy.
|
|
8951
|
+
Use Window > SwiftUI Playground for in-memory production screen fixtures.
|
|
8952
|
+
|
|
8953
|
+
cd apps/desktop
|
|
8954
|
+
stim macos
|
|
8955
|
+
stim logs --source build
|
|
8956
|
+
stim stop
|
|
8957
|
+
|
|
8958
|
+
When using an unreleased CLI from this repository, build the packages first and
|
|
8959
|
+
set STIM_BIN to the absolute packages/stim-cli/dist/cli.mjs path before running
|
|
8960
|
+
that executable's macos command. The development app inherits this CLI override;
|
|
8961
|
+
the installed app's CLI preference and running server remain unchanged.
|
|
8962
|
+
|
|
8963
|
+
Ask an agent: "In my Swift Package app, configure the executable product and a
|
|
8964
|
+
development Info.plist for stim macos. Build and show its owned window in Stim
|
|
8965
|
+
Desktop, verify a source edit and readable failed-build logs, then stop only
|
|
8966
|
+
this workspace's app. Do not change permissions or use custom build scripts."
|
|
8967
|
+
`
|
|
6696
8968
|
}
|
|
6697
8969
|
};
|
|
6698
8970
|
//#endregion
|
|
@@ -6774,7 +9046,7 @@ function renderIndex(version) {
|
|
|
6774
9046
|
return lines.join("\n");
|
|
6775
9047
|
}
|
|
6776
9048
|
function guideCommand(program, version, status = (running) => guideStatus({
|
|
6777
|
-
projectRoot: findProjectRoot(process.cwd()),
|
|
9049
|
+
projectRoot: findProjectRoot$1(process.cwd()),
|
|
6778
9050
|
running
|
|
6779
9051
|
})) {
|
|
6780
9052
|
program.command("guide [topic] [section]").description("Print reference documentation for THIS version of Stim (topics: " + topicNames().join(", ") + "). A topic with sections prints its section index; name a section to print only it, e.g. `stim guide errors STIM_NO_METRO` or `stim guide lifecycle builds`. Generated by the binary, so it cannot drift from the installed CLI.").action(async (topic, section) => {
|