stim 1.13.0 → 1.15.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-DfVKZTEB.mjs → activity-B2-kb7k0.mjs} +281 -41
- package/dist/{android-BHfAu2mf.mjs → android-CYmq0jNi.mjs} +2 -2
- package/dist/{android-CD8pr8Q2.mjs → android-Cu_L4RpL.mjs} +524 -412
- package/dist/{android-qx_31wJo.mjs → android-Db2eEMaf.mjs} +152 -65
- package/dist/android-cas-PnKfAdB2.mjs +167 -0
- package/dist/android-cas-compiler.mjs +2 -2
- package/dist/{app-install-CDFY2nUL.mjs → app-install-CAAplXAt.mjs} +162 -173
- package/dist/{budget-E5o91NR1.mjs → budget-CMiqghig.mjs} +1048 -77
- package/dist/{build-plan-BTa93o_r.mjs → build-plan-aeGMFX7e.mjs} +193 -650
- package/dist/build-progress-C2Jv1AtA.mjs +872 -0
- package/dist/build-slots-Df1ApSuH.mjs +127 -0
- package/dist/{cache-manifest-DWPF4RKJ.mjs → cache-manifest-DnUHBJaU.mjs} +2 -2
- package/dist/cache-manifest.mjs +1 -1
- package/dist/{chrome-B5hWgQJS.mjs → chrome-BrcVWQyD.mjs} +4 -4
- package/dist/cli-0cX5s4NM.mjs +45 -0
- package/dist/cli.mjs +6 -42
- package/dist/client-C2IS36Qf.mjs +1333 -0
- package/dist/collector-run.mjs +8 -7
- package/dist/{command-output-BIL7rlX7.mjs → command-output-CliHHn7a.mjs} +1 -1
- package/dist/{config-0jCcMM0E.mjs → config-D0DbpZtM.mjs} +29 -29
- package/dist/{created-devices-Cd558h31.mjs → created-devices-BwVb3liH.mjs} +2 -2
- package/dist/{dependency-state-DNN0yfx6.mjs → dependency-state-BmsjKZ2H.mjs} +2 -2
- package/dist/{deps-2Wo81np9.mjs → deps-BFlKbOIp.mjs} +3 -3
- package/dist/{detached-entry-DJHoca49.mjs → detached-entry-CiTEgMR1.mjs} +1 -1
- package/dist/dev-client-CjzLoNs0.mjs +80 -0
- package/dist/{device-Cf7vl-so.mjs → device-BZ1IyS5A.mjs} +7 -7
- package/dist/device-capacity-DMuKC7nx.mjs +136 -0
- package/dist/device-host-worker.d.mts +1 -0
- package/dist/device-host-worker.mjs +769 -0
- package/dist/device-ios-DMQBUPus.mjs +381 -0
- package/dist/{device-lease-CL9dZf73.mjs → device-lease-W5Pi1q38.mjs} +43 -36
- package/dist/{device-pool-MuoiyRcu.mjs → device-pool-B2r38YjS.mjs} +4 -4
- package/dist/{device-remote-DT9ngZiD.mjs → device-remote-9ulPsdhY.mjs} +68 -162
- package/dist/{doctor-D2iysNNH.mjs → doctor-BcE5xsBn.mjs} +191 -99
- package/dist/{error-diagnostics-BoiOhck8.mjs → error-diagnostics-Cb0P1WLS.mjs} +12 -11
- package/dist/{exec-DK8KaVHK.mjs → exec-CaErVv7t.mjs} +5 -1
- package/dist/{gc-eA-PRBX8.mjs → gc-BuL1Genr.mjs} +430 -102
- package/dist/{android-cas-B9Kv9aIy.mjs → gradle-CmdOBq1d.mjs} +8 -294
- package/dist/{guide-BfZ0OmiP.mjs → guide-cCSGRFd3.mjs} +2052 -240
- package/dist/{guide-status-BaMYu55M.mjs → guide-status-CIoWK6mG.mjs} +3 -3
- package/dist/hosted-macos-DwowNU7W.mjs +447 -0
- package/dist/idle-shutdown-B5LPaNyY.mjs +122 -0
- package/dist/in-use-CndPMilN.mjs +110 -0
- package/dist/{ios-CmEzZiOj.mjs → ios-3CtCRhwr.mjs} +201 -10
- package/dist/{ios-CcfCeZ19.mjs → ios-Chob8ny7.mjs} +560 -259
- package/dist/{ios-device-BW-t_Lkv.mjs → ios-device-DERmMmk-.mjs} +12 -7
- package/dist/{ios-device-BiOsSVzd.mjs → ios-device-Ee7sDXfp.mjs} +1 -1
- package/dist/{launch-verify-CuJaBpEa.mjs → launch-verify-CLlofxRt.mjs} +42 -11
- package/dist/{logs-DcSRbC2t.mjs → logs-h9h6MUvE.mjs} +95 -13
- package/dist/machine-usage-D_YYMzfR.mjs +283 -0
- package/dist/machines-CYtOCqcj.mjs +259 -0
- package/dist/macos-BwtbUtTN.mjs +468 -0
- package/dist/macos-run.d.mts +5 -0
- package/dist/macos-run.mjs +2 -0
- package/dist/{merge-state-ytFQCeh4.mjs → merge-state-BnxLNQiM.mjs} +22 -12
- package/dist/{metro-BPeY1YMh.mjs → metro-Di8EN2Tb.mjs} +20 -120
- package/dist/{named-ports-n7tnJsI8.mjs → named-ports-Dj_XBNaL.mjs} +8 -64
- package/dist/native-run-DFDoIRbY.mjs +147 -0
- package/dist/{native-runtime-CiiFHt0e.mjs → native-runtime-i6OkVg-w.mjs} +11 -6
- package/dist/offload-worker.d.mts +121 -0
- package/dist/offload-worker.mjs +601 -0
- package/dist/{ownership-Bz2kETAS.mjs → ownership-BkYxUtuj.mjs} +3 -2
- package/dist/{page-BTu7I60K.mjs → page-QF_Ke10s.mjs} +7 -2
- package/dist/{ports-C5UutqpJ.mjs → ports-DddwttKM.mjs} +3 -3
- package/dist/prebuild-BvxDlD6C.mjs +213 -0
- package/dist/process-identity-BmRm1qV0.mjs +12 -0
- package/dist/{project-DGeclhvO.mjs → project-CV3tDFHl.mjs} +9 -18
- 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-GEXp4iBT.mjs +37 -0
- package/dist/{reload-C-cV5yy_.mjs → reload-ChPiwvot.mjs} +70 -22
- package/dist/{remote-cache--aehr4Pr.mjs → remote-cache-CO2elm_b.mjs} +4 -4
- package/dist/run-B-xqRrsg.mjs +119 -0
- package/dist/{server-bare-Bq8BpSIo.mjs → server-bare-X7PUgizL.mjs} +60 -13
- package/dist/{server-expo-DUCg52-K.mjs → server-expo-DhtX6bom.mjs} +6 -10
- package/dist/server-expo-KJeD109I.mjs +2 -0
- package/dist/{settings-xB0Frkg6.mjs → settings-CduVcoxj.mjs} +77 -26
- package/dist/{settings-ssVBToDR.mjs → settings-DbL6uhH8.mjs} +16 -10
- package/dist/settings.schema.json +357 -2
- package/dist/{simslim-Bbg9UWrt.mjs → simslim-Ciluxlq4.mjs} +8 -132
- package/dist/slot-launch-BY0BrOLh.mjs +65 -0
- package/dist/{spawn-claims-PT8aQpCa.mjs → spawn-claims-D0XfSe8u.mjs} +1 -1
- package/dist/spawn-entry-DncpJvWL.mjs +16 -0
- package/dist/stage-CzaiwFsI.mjs +143 -0
- package/dist/start-CPO4wRg7.mjs +2 -0
- package/dist/{start-BZhItkiL.mjs → start-CUD9nasA.mjs} +108 -61
- package/dist/state-B4PQFXMW.d.mts +12 -0
- package/dist/state-BxCgeTQu.mjs +41 -0
- package/dist/{state-DwAU2_-D.mjs → state-CaeaHbDA.mjs} +7 -4
- package/dist/{state-l1pYwOre.mjs → state-DFm77wRn.mjs} +67 -5
- package/dist/stats-DBLLFmKd.mjs +66 -0
- package/dist/{status-TJlL0HWo.mjs → status-DYXxNOT0.mjs} +425 -260
- package/dist/{status-sszP4XKU.mjs → status-l84Gbvp2.mjs} +122 -20
- 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-BZ2qMiBP.mjs → stop-Bad61z_2.mjs} +128 -19
- package/dist/stop-Bvf1MflS.mjs +48 -0
- package/dist/{stop-DskDKNTU.mjs → stop-CceYh1yG.mjs} +2 -2
- package/dist/stop-cause-CZ_Mr-v9.mjs +665 -0
- package/dist/supervisor-run.d.mts +44 -12
- package/dist/supervisor-run.mjs +276 -33
- package/dist/support-CDQbDkL5.mjs +315 -0
- package/dist/support-WLOzVKUv.mjs +176 -0
- package/dist/teardown-BYuQafiA.mjs +718 -0
- package/dist/{xcode-LM6A8cNZ.mjs → toolchain-DmeYjXD9.mjs} +363 -13
- package/dist/warm-progress-DVHLN4uC.mjs +463 -0
- package/dist/{idle-DxLi5OkN.mjs → watchman-Bx0wceoF.mjs} +55 -8
- package/dist/{web-sWXd5iek.mjs → web-k6w8-HnE.mjs} +39 -22
- package/dist/web-run.mjs +326 -13
- package/dist/workspace-process-lock-BL7Q0HCO.mjs +57 -0
- package/dist/workspace-state-CL2V2Ons.mjs +371 -0
- package/dist/worktree-BT_uULFx.mjs +2 -0
- package/dist/{worktree-DeBQSVtR.mjs → worktree-C0c5Udwa.mjs} +55 -54
- package/dist/{worktree-D4Do_XiQ.mjs → worktree-qXKzADSC.mjs} +161 -255
- package/package.json +10 -5
- package/shim/bundle-response.cjs +55 -10
- package/shim/bundle-response.d.cts +1 -0
- package/shim/expo-metro-config.cjs +10 -2
- package/dist/build-progress-l7d-X-ZP.mjs +0 -247
- package/dist/in-use-wYEaKQM9.mjs +0 -249
- package/dist/ownership-PUP4Dwai.mjs +0 -1138
- package/dist/server-expo-BeYwP_Xy.mjs +0 -2
- package/dist/slot-launch-BIWEW-wI.mjs +0 -19
- package/dist/start-Rw2GD3nV.mjs +0 -2
- package/dist/stats-CaSwmsSZ.mjs +0 -62
- package/dist/stats-D8rUTPHd.mjs +0 -368
- package/dist/stim-desktop-CXt8-Z3J.mjs +0 -40
- package/dist/worktree-3cBa-bQt.mjs +0 -2
- package/dist/{metro-store-DzeWkfqW.mjs → metro-store-CDDZHtPl.mjs} +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { a as findProjectRoot } from "./project-
|
|
2
|
-
import { t as ANDROID_AVD_CONFIG_HELP } from "./settings-
|
|
3
|
-
import { t as RECENT_LAUNCH_MS } from "./status-
|
|
4
|
-
import { t as guideStatus } from "./guide-status-
|
|
1
|
+
import { a as findProjectRoot$1 } from "./project-CV3tDFHl.mjs";
|
|
2
|
+
import { t as ANDROID_AVD_CONFIG_HELP } from "./settings-CduVcoxj.mjs";
|
|
3
|
+
import { t as RECENT_LAUNCH_MS } from "./status-l84Gbvp2.mjs";
|
|
4
|
+
import { t as guideStatus } from "./guide-status-CIoWK6mG.mjs";
|
|
5
5
|
import { SETTINGS_SCHEMA_URL } from "@stim-cli/core/state";
|
|
6
6
|
import chalk from "chalk";
|
|
7
7
|
//#endregion
|
|
@@ -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
|
|
@@ -34,18 +38,17 @@ MULTIPLE DEVICES
|
|
|
34
38
|
Use ios/android --slot <name> to retain multiple devices in one workspace,
|
|
35
39
|
including several of the same model. Reuse the same slot name on subsequent
|
|
36
40
|
runs. Read guide lifecycle options for the complete slot workflow. Use the
|
|
37
|
-
reported device ID for UI interaction. All slots share Metro
|
|
38
|
-
multiple devices
|
|
41
|
+
reported device ID for UI interaction. All slots share Metro and reload can
|
|
42
|
+
reach multiple devices. A launch counts a bundle delivery only when it can
|
|
43
|
+
attribute it to its own device; read guide lifecycle options for the rule.
|
|
39
44
|
Use stop --slot <name> for one slot, or plain stop for the whole workspace.
|
|
40
45
|
|
|
41
46
|
NORMAL WORKFLOW
|
|
42
47
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
copies missing ignored paths from the source checkout, including eligible .env
|
|
48
|
-
and local configuration files. It preserves the branch, tracked files, and
|
|
48
|
+
Create a linked worktree with Git and warm its ignored state. If a harness
|
|
49
|
+
already created this linked worktree, run stim worktree warm here instead of
|
|
50
|
+
creating another one. It copies missing ignored paths from the source checkout,
|
|
51
|
+
including eligible .env and local configuration files. It preserves the branch, tracked files, and
|
|
49
52
|
every existing destination entry; existing ignored directories are skipped
|
|
50
53
|
whole, not filled in. Add --refresh to fast-forward the source checkout and
|
|
51
54
|
install what moved there before the copy; it refuses a source checkout with local
|
|
@@ -75,7 +78,8 @@ other apps or other workspaces' devices.
|
|
|
75
78
|
|
|
76
79
|
stim doctor --platform ios # or: --platform android
|
|
77
80
|
|
|
78
|
-
# Skip Git creation if the harness already created this
|
|
81
|
+
# Worktree workflow. Skip Git creation if the harness already created this
|
|
82
|
+
# linked worktree.
|
|
79
83
|
git worktree add -b <branch> <worktree-path> HEAD
|
|
80
84
|
cd <worktree-path>
|
|
81
85
|
stim worktree warm
|
|
@@ -83,6 +87,12 @@ other apps or other workspaces' devices.
|
|
|
83
87
|
stim start
|
|
84
88
|
stim ios # or: stim android, or: stim web
|
|
85
89
|
|
|
90
|
+
When Stim Desktop is installed, worktree warm, start, ios, android and web
|
|
91
|
+
print "Open in Stim Desktop: <link>" on stderr (links.desktop in JSON). The
|
|
92
|
+
first time one prints it in a workspace, tell the user once: "You can watch
|
|
93
|
+
this workspace in Stim Desktop: <link>". Do not repeat it after later
|
|
94
|
+
commands. When no command prints it, do not mention Stim Desktop.
|
|
95
|
+
|
|
86
96
|
For the web target, stim web opens the page in a Stim-owned headless Chrome
|
|
87
97
|
and captures its console and errors in stim logs. It never starts a web
|
|
88
98
|
server: start the dev server first, then run stim web. For Expo web the dev
|
|
@@ -104,6 +114,9 @@ To learn whether the next build is a cache hit and how long it should take,
|
|
|
104
114
|
without building, run stim ios --plan (or stim android --plan); see stim guide
|
|
105
115
|
lifecycle builds.
|
|
106
116
|
|
|
117
|
+
Read stim guide settings to select a build machine. Keep a requested named
|
|
118
|
+
placement when recovering STIM_OFFLOAD_REFUSED: report its reason and remedy.
|
|
119
|
+
|
|
107
120
|
Read stim guide lifecycle concurrency when a build waits on another workspace
|
|
108
121
|
or a build call times out. A native build can outlive a shell timeout; if the
|
|
109
122
|
tool call timed out, retry the same command and follow its printed remedy if
|
|
@@ -160,10 +173,11 @@ RULES DURING THE LOOP
|
|
|
160
173
|
the error screen remains, follow the printed reload remedy instead of
|
|
161
174
|
running ios or android again. If launch says FATAL because the app process exited,
|
|
162
175
|
fix the crash and run the platform command again; Metro cannot restart it.
|
|
163
|
-
- ios and android install the app, launch it, and check readiness.
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
176
|
+
- ios and android install the app, launch it, and check readiness. A running
|
|
177
|
+
copy of the app is stopped and launched fresh, so in-app state does not
|
|
178
|
+
survive the run. Trust the exact device, app, Metro, and launch facts in the
|
|
179
|
+
final summary. Use the full reported device ID. Never assume a simulator
|
|
180
|
+
named booted belongs to this workspace.
|
|
167
181
|
- After each ios or android run, give the user one compact result: exact device,
|
|
168
182
|
app id, launch state, cache result, total duration, and whether stim logs
|
|
169
183
|
--errors passed. Include a remedy only when action remains. Do not repeat the
|
|
@@ -195,10 +209,11 @@ the stim-<label> (<model> <runtime>) name. Never point Stim at a user-created
|
|
|
195
209
|
emulator or simulator.
|
|
196
210
|
|
|
197
211
|
worktree remove parks the workspace's simulator or emulator for later adoption.
|
|
198
|
-
|
|
212
|
+
During owned-device teardown, Stim best-effort closes local agent-device sessions
|
|
199
213
|
on that exact device; failures warn and teardown continues (guide cleanup).
|
|
200
|
-
A parked device is Stim-owned: never delete one by hand. gc --delete clears
|
|
201
|
-
entries and keeps failures;
|
|
214
|
+
A parked device is Stim-owned: never delete or erase one by hand. gc --delete clears
|
|
215
|
+
verified entries and keeps failures; gc --cache parked --delete erases them and keeps
|
|
216
|
+
them parked; see guide lifecycle pool. First launch on a
|
|
202
217
|
physical iPhone can need the one-time taps named by the remedy.
|
|
203
218
|
|
|
204
219
|
stim android --device [serial] and stim ios --device [udid] install on a
|
|
@@ -283,6 +298,7 @@ Read the matching guide before acting in these situations:
|
|
|
283
298
|
| Web app in an owned Chrome (stim web) | stim guide web |
|
|
284
299
|
| Web or API server ports | stim guide ports |
|
|
285
300
|
| Remote device, custom Metro, or tunnel | stim guide metro |
|
|
301
|
+
| Metro stopped and no one ran stim stop | stim guide metro |
|
|
286
302
|
| Cache miss, bypass, or fingerprint exclusions | stim guide lifecycle builds |
|
|
287
303
|
| Capacity limits | stim guide lifecycle concurrency |
|
|
288
304
|
| budget lines, reclaimed field, or low disk | stim guide lifecycle budget |
|
|
@@ -295,8 +311,11 @@ Read the matching guide before acting in these situations:
|
|
|
295
311
|
| Parallel iOS, simulator stall, or memory pressure | stim guide lifecycle simslim |
|
|
296
312
|
| Android boot timeout | stim guide errors STIM_NO_DEVICE |
|
|
297
313
|
| gc or orphaned resources | stim guide cleanup gc |
|
|
314
|
+
| watchman or Gradle daemon memory | stim guide cleanup memory |
|
|
298
315
|
| worktree remove refusal or --force | stim guide errors remove |
|
|
299
316
|
| Cleanup failure or unverified cleanup ownership | stim guide errors teardown |
|
|
317
|
+
| Swift Package macOS development | stim guide macos |
|
|
318
|
+
| macOS app on another Mac (stim macos --remote) | stim guide macos |
|
|
300
319
|
| Unfamiliar state or JSON field | stim guide facts payloads |
|
|
301
320
|
| Refusal without a code | stim guide errors |
|
|
302
321
|
|
|
@@ -325,12 +344,14 @@ FULL TOPIC LIST
|
|
|
325
344
|
stim guide lifecycle release # Release configurations and ...Release variants
|
|
326
345
|
stim guide facts # the --json payloads
|
|
327
346
|
stim guide facts devmenu # the Expo dev menu or Tools button over the app
|
|
347
|
+
stim guide macos # Swift Package Debug apps, logs, local preview, --remote on another Mac
|
|
328
348
|
stim guide web # stim web: owned Chrome, page logs, launched, teardown
|
|
329
349
|
stim guide ports # named ports for web and API servers
|
|
330
350
|
stim guide metro # supervisor, custom Metro, tunnels, and remote devices
|
|
331
351
|
stim guide logs # filters, record shape, and capture limits
|
|
332
352
|
stim guide cleanup # what reclaims a device, and what deletes
|
|
333
353
|
stim guide cleanup collector # an unproven collector pid; why the app on a phone closed
|
|
354
|
+
stim guide cleanup memory # watchman and Gradle daemon memory; gc --cache watchman
|
|
334
355
|
stim guide settings # configuration files and supported keys`
|
|
335
356
|
},
|
|
336
357
|
facts: {
|
|
@@ -357,9 +378,13 @@ environment when one is registered at it or inside it. Each entry is
|
|
|
357
378
|
directory of a bare repository. status lists worktrees from git's own worktree
|
|
358
379
|
records, and on macOS runs no git in a worktree with no environment under a
|
|
359
380
|
protected folder such as ~/Documents, so listing it raises no privacy prompt.
|
|
360
|
-
\`worktree warm\`
|
|
361
|
-
|
|
362
|
-
|
|
381
|
+
\`worktree warm\` registers the app it prepares, before it copies: the
|
|
382
|
+
nearest React Native or Expo app above where it runs when that is below the
|
|
383
|
+
worktree root; else the one app the source checkout has registered, at the
|
|
384
|
+
same path in this worktree; else the worktree root when it is the app. A warm
|
|
385
|
+
that finds none registers nothing and says so on stderr, and \`start\`, \`ios\`,
|
|
386
|
+
\`android\` and \`doctor\` register the app later. An environment's worktree is the linked worktree it is
|
|
387
|
+
registered at or inside.
|
|
363
388
|
|
|
364
389
|
Each worktree entry, in unprovisionedWorktrees and in an environment's
|
|
365
390
|
worktree, carries git:
|
|
@@ -389,6 +414,46 @@ change, and for at most 60 s, so a file edit, creation or deletion that is
|
|
|
389
414
|
not staged can take up to a minute to show. Plain status prints "git: 2 changed, 1 untracked, ahead 3" under
|
|
390
415
|
each environment, and the same after each worktree with no environment.
|
|
391
416
|
|
|
417
|
+
The paired phone's Work sheet can open changed and untracked files when the
|
|
418
|
+
server advertises workspace-diff. File lists and selected patches load on
|
|
419
|
+
demand, not through status. The read-only preview is capped at 200 files per
|
|
420
|
+
list and 256 KiB per patch response; binary and unavailable files are labeled. Active Git clean/process filters
|
|
421
|
+
(including Git LFS) refuse without running those commands.
|
|
422
|
+
|
|
423
|
+
A worktree entry also carries pullRequest, the GitHub pull request of its
|
|
424
|
+
branch and HEAD, once \`status --watch\` has looked it up:
|
|
425
|
+
|
|
426
|
+
pullRequest { number, url, title, state, checks, reviewDecision,
|
|
427
|
+
checkedAt }, null when GitHub has none for the branch and
|
|
428
|
+
HEAD, absent when never looked up: gh missing or signed
|
|
429
|
+
out, no GitHub remote, or status could not read the
|
|
430
|
+
worktree's git. A later gh failure keeps the last answer.
|
|
431
|
+
state "open", "draft", "merged" or "closed"
|
|
432
|
+
checks { passing, failing, pending } counts of the head commit's
|
|
433
|
+
check runs and commit statuses, or null when it has none
|
|
434
|
+
reviewDecision "approved", "changes-requested", "review-required" or null
|
|
435
|
+
checkedAt when Stim last asked GitHub
|
|
436
|
+
|
|
437
|
+
A worktree entry with git also carries gitChip, the git chip Stim Desktop and
|
|
438
|
+
the phone app show for it:
|
|
439
|
+
|
|
440
|
+
gitChip { parts, ci }
|
|
441
|
+
parts in order: { kind: "arrows", ahead, behind } when either is above
|
|
442
|
+
0; { kind: "changed", count } of changed plus untracked entries;
|
|
443
|
+
{ kind: "merged", into } when mergedInto is set and the pull
|
|
444
|
+
request is not merged; { kind: "no-upstream" } when the branch is
|
|
445
|
+
not merged and upstream is null
|
|
446
|
+
ci "failing" when a check fails, else "pending" when one is pending,
|
|
447
|
+
else "passing" when one passes; null without a pull request or
|
|
448
|
+
checks
|
|
449
|
+
|
|
450
|
+
\`status --watch\` asks GitHub off its refresh path, through the same gh api
|
|
451
|
+
graphql lookup gc uses, one call per repository, for a worktree whose lookup
|
|
452
|
+
is over 5 minutes old or whose branch or HEAD moved. It runs git in the
|
|
453
|
+
repository, never in the worktree. It caches each answer
|
|
454
|
+
under $STIM_HOME/pull-requests, which one-shot status only reads, and never
|
|
455
|
+
warns when gh is unavailable.
|
|
456
|
+
|
|
392
457
|
status's remoteDevices lists each environment's recorded EAS Simulator
|
|
393
458
|
session. The session is billable while it runs, and it makes the environment
|
|
394
459
|
live. status reads only local records; it does not ask EAS whether the session
|
|
@@ -406,7 +471,34 @@ is still running.
|
|
|
406
471
|
Stim created the session, or null
|
|
407
472
|
|
|
408
473
|
Plain status prints one "remote <platform>: EAS session <id> billable" line
|
|
409
|
-
per session, with the preview URL
|
|
474
|
+
per session, with the preview URL.
|
|
475
|
+
|
|
476
|
+
status's physicalDevices lists each physical phone or tablet the environment
|
|
477
|
+
holds an unexpired lease on, from \`ios --device\`, \`android --device\` or
|
|
478
|
+
\`device lock\`, in every slot. A run lease ends with its run, so a phone
|
|
479
|
+
stays listed after the run only while \`device lock\` holds it. A
|
|
480
|
+
simulator or emulator lease is not listed. The field is absent when the
|
|
481
|
+
workspace leases no physical device.
|
|
482
|
+
|
|
483
|
+
physicalDevices [{ platform, slot, id, name, model, owned, physical,
|
|
484
|
+
connection, lease }]
|
|
485
|
+
id the UDID or adb serial
|
|
486
|
+
name the device's own name, else the name the lease recorded
|
|
487
|
+
model devicectl's marketing name ("iPhone 12 Pro"), null when
|
|
488
|
+
unread, or the Android model the lease recorded
|
|
489
|
+
owned always false: Stim uses a physical device, never owns it
|
|
490
|
+
physical always true
|
|
491
|
+
connection "connected" devicectl can reach the phone, or adb lists
|
|
492
|
+
the serial as device
|
|
493
|
+
"disconnected" the tool answered without it
|
|
494
|
+
"unknown" the tool could not be read in time
|
|
495
|
+
status reuses one devicectl or adb listing for 30 s
|
|
496
|
+
under \`status --watch\`
|
|
497
|
+
lease { holder, kind, grantedAt, expiresAt }: holder is the
|
|
498
|
+
workspace path, kind "declared" (device lock) or "run"
|
|
499
|
+
|
|
500
|
+
Plain status prints "ios: Old iPhone (physical, iPhone 12 Pro) connected --
|
|
501
|
+
leased until <time>" for each one.`,
|
|
410
502
|
sections: {
|
|
411
503
|
payloads: {
|
|
412
504
|
summary: "every field of the start, ios, android, web and reload payloads, the error contract, the device rules",
|
|
@@ -418,6 +510,11 @@ per session, with the preview URL.`,
|
|
|
418
510
|
mode "bare-inproc" | "expo-child" | null (see \`guide metro\`)
|
|
419
511
|
logsDir where the NDJSON timeline is written
|
|
420
512
|
alreadyRunning true when nothing needed starting
|
|
513
|
+
links { desktop }: a stim-desktop://workspace?path=<workspace>
|
|
514
|
+
link that shows this workspace in Stim Desktop. Absent when
|
|
515
|
+
no app on this Mac opens stim-desktop links. Plain output
|
|
516
|
+
prints it on stderr as "Open in Stim Desktop: <link>"; so
|
|
517
|
+
does worktree warm, which has no JSON payload
|
|
421
518
|
|
|
422
519
|
stim ios --json
|
|
423
520
|
|
|
@@ -468,7 +565,12 @@ per session, with the preview URL.`,
|
|
|
468
565
|
scheme the explicit shared Xcode scheme selected by --scheme;
|
|
469
566
|
absent for automatic selection; not the app URL scheme
|
|
470
567
|
cacheKey the shared-build-cache key derived from it (the
|
|
471
|
-
configuration is part of it: -release-sim vs
|
|
568
|
+
configuration is part of it: -release-sim vs
|
|
569
|
+
-debug-sim-arm64). A single-architecture simulator build
|
|
570
|
+
ends in its architecture (-arm64 or -x86-64): a local Debug
|
|
571
|
+
build compiles this Mac's, a --remote build the remote
|
|
572
|
+
host's. A local non-Debug simulator build compiles every
|
|
573
|
+
architecture the project lists and has no suffix
|
|
472
574
|
With --eas-profile, fingerprint is computed by EAS CLI using the selected
|
|
473
575
|
profile/environment, and cacheKey identifies the EAS project and build ID
|
|
474
576
|
separately from local native builds. cacheHit is "remote" for the EAS
|
|
@@ -487,6 +589,15 @@ per session, with the preview URL.`,
|
|
|
487
589
|
the device; it is a page, not a deep link.
|
|
488
590
|
cacheSkipped true only when --no-build-cache was passed: "nothing was
|
|
489
591
|
looked up", which is a different fact from "nothing was found"
|
|
592
|
+
buildMachine selected auto, local or machine name, including cache hits
|
|
593
|
+
builtOn here or machine name when a build ran; absent otherwise
|
|
594
|
+
offloadedTo only on an app a build machine compiled: its
|
|
595
|
+
offload.machines entry (see \`guide settings\`). cacheHit is
|
|
596
|
+
false for it. The Android payload carries it too
|
|
597
|
+
offloadFallback only on an app compiled here after the run considered
|
|
598
|
+
offloading it: why it built here -- one reason per
|
|
599
|
+
machine when none took the build, or why the offload
|
|
600
|
+
stopped. The Android payload carries it too
|
|
490
601
|
compilationCache
|
|
491
602
|
Xcode compilation-cache activity for a compiled iOS app:
|
|
492
603
|
{ status: "reported", hits, cacheableTasks, hitRatePercent }
|
|
@@ -585,7 +696,8 @@ per session, with the preview URL.`,
|
|
|
585
696
|
hw.device.name. Null where systemImage is null, and on an
|
|
586
697
|
AVD of the old generic profile, which has no hw.device.name
|
|
587
698
|
fingerprint / cacheKey / cacheHit / cacheSkipped / waitedForBuild /
|
|
588
|
-
appPath / installSkipped / launched
|
|
699
|
+
appPath / installSkipped / launched / buildMachine / builtOn /
|
|
700
|
+
offloadedTo / offloadFallback
|
|
589
701
|
as above -- cacheKey keys on the VARIANT here
|
|
590
702
|
(<fingerprint>-productionrelease-sim). A Debug artifact for
|
|
591
703
|
a proven target ABI also ends in that ABI
|
|
@@ -627,7 +739,8 @@ per session, with the preview URL.`,
|
|
|
627
739
|
through ccache -- ccache absent from PATH, a project that
|
|
628
740
|
sets its own CMake compiler launcher, or a Gradle run whose
|
|
629
741
|
native work was all up to date. None of the three is an
|
|
630
|
-
error, and this field is separate from cacheHit
|
|
742
|
+
error, and this field is separate from cacheHit. On an
|
|
743
|
+
offloaded APK it is the build machine's ccache
|
|
631
744
|
logs the workspace log directory
|
|
632
745
|
durationMs wall time for the whole run
|
|
633
746
|
reclaimed present only when the run was over its disk or memory
|
|
@@ -644,6 +757,8 @@ per session, with the preview URL.`,
|
|
|
644
757
|
{ started: true, reason }
|
|
645
758
|
reason is "not running" or "stopped (idle)" when the
|
|
646
759
|
supervisor had stopped it after metro.idleStopMinutes
|
|
760
|
+
links as in start, with &platform= and, for a named slot, &slot=,
|
|
761
|
+
so Stim Desktop can focus that device
|
|
647
762
|
|
|
648
763
|
stim reload [ios|android|web] --json
|
|
649
764
|
|
|
@@ -656,7 +771,8 @@ per session, with the preview URL.`,
|
|
|
656
771
|
deviceId the exact owned simulator UDID or emulator serial targeted;
|
|
657
772
|
for web, the owned Chrome's DevTools endpoint
|
|
658
773
|
deviceName the owned simulator or AVD name; for web, the Chrome version
|
|
659
|
-
appId the live bundle id or Android package; for web, the
|
|
774
|
+
appId the live bundle id or Android package; for web, the URL the
|
|
775
|
+
owned page was on when Stim sent the reload
|
|
660
776
|
metroPort the workspace's verified Metro port; for web, the reserved
|
|
661
777
|
Metro port or null
|
|
662
778
|
strategy how the reload was addressed.
|
|
@@ -674,6 +790,9 @@ per session, with the preview URL.`,
|
|
|
674
790
|
broadcast. Greater than 1 means several devices are running
|
|
675
791
|
this app on that Metro and the request addressed all of
|
|
676
792
|
them, not only deviceId. Completion is not observed
|
|
793
|
+
reverseRestored the Android serials whose adb reverse for metroPort was
|
|
794
|
+
missing and that Stim re-applied before reloading; [] when
|
|
795
|
+
none was, and always [] for iOS and web
|
|
677
796
|
|
|
678
797
|
stim web --json
|
|
679
798
|
|
|
@@ -689,6 +808,8 @@ per session, with the preview URL.`,
|
|
|
689
808
|
profile the Stim-owned Chrome user data directory
|
|
690
809
|
cdpEndpoint http://127.0.0.1:<port>, the reserved DevTools endpoint, or
|
|
691
810
|
null when not running
|
|
811
|
+
targetId the DevTools target id of the owned page, or null when not
|
|
812
|
+
running
|
|
692
813
|
reused true when the running Chrome navigated again instead of
|
|
693
814
|
starting: same --headed, viewport and certificate options
|
|
694
815
|
launched true | "bundling" | "unverified" (see \`guide web\`)
|
|
@@ -696,6 +817,7 @@ per session, with the preview URL.`,
|
|
|
696
817
|
that does not use {port:metro}
|
|
697
818
|
logs { dir }: web.ndjson holds the page records
|
|
698
819
|
durationMs wall time of the run
|
|
820
|
+
links as in start, with &platform=web
|
|
699
821
|
|
|
700
822
|
stim doctor --json
|
|
701
823
|
|
|
@@ -716,6 +838,36 @@ per session, with the preview URL.`,
|
|
|
716
838
|
null when off; plan lists what the next start, ios or
|
|
717
839
|
android would reclaim, in the \`reclaimed\` shape without
|
|
718
840
|
freedMb, and is empty while under budget
|
|
841
|
+
deviceHosts one { machine, state, dnsName?, deviceId?, requestedAt? }
|
|
842
|
+
per hosting.machines entry. state is "approved", "pending",
|
|
843
|
+
"not-asked", "revoked", "node-changed", "not-on-tailnet",
|
|
844
|
+
"tailscale-off", "unreachable", "invalid",
|
|
845
|
+
"credentials-unavailable" or "busy". Tokens stay private.
|
|
846
|
+
Only --fix asks for access or forgets removed names.
|
|
847
|
+
An approved hosting machine does not imply build approval
|
|
848
|
+
or change ios/android placement; see \`guide settings\`.
|
|
849
|
+
buildMachines one { machine, state, dnsName?, deviceId?, requestedAt?,
|
|
850
|
+
offloadable?, reasons?, problems?, capacity? } per
|
|
851
|
+
offload.machines entry; state is "approved", "pending", "not-asked",
|
|
852
|
+
"revoked" (revoked, or the request lapsed), "node-changed",
|
|
853
|
+
"not-on-tailnet", "tailscale-off", "unreachable" or
|
|
854
|
+
"invalid". An approved machine also carries offloadable,
|
|
855
|
+
true when it would take this app's builds now (iOS
|
|
856
|
+
simulator unless --platform android, Android emulator when
|
|
857
|
+
--platform android or the app has android/ or uses Expo,
|
|
858
|
+
plus macOS SwiftPM when Package.swift exists and no platform
|
|
859
|
+
is selected on a Mac), and reasons, each reason it would not,
|
|
860
|
+
prefixed "iOS: ", "Android: " or "macOS: " when only one platform
|
|
861
|
+
has it, with a finding
|
|
862
|
+
per reason (code build-machine-<reason>: unreachable,
|
|
863
|
+
checkout, stim-build, arch, xcode, simulator-sdk, macos-sdk,
|
|
864
|
+
cocoapods, bundler, runtime, jdk, android-sdk, ndk,
|
|
865
|
+
build-tools, compile-sdk, disk or busy). problems lists the same
|
|
866
|
+
reasons as { code, reason } with that code. capacity
|
|
867
|
+
is the machine's offer: { running, max, diskFreeBytes,
|
|
868
|
+
minDiskFreeBytes, cpus?, loadPerCore?, builds?, maxBuilds?,
|
|
869
|
+
maxLoadPerCore?, declined? }; an older stim-server omits the
|
|
870
|
+
optional fields
|
|
719
871
|
findings the diagnostic findings; a lower resolved Stim is a
|
|
720
872
|
costs-time finding with a PATH or installation remedy
|
|
721
873
|
|
|
@@ -738,7 +890,8 @@ ON FAILURE
|
|
|
738
890
|
lease, Ctrl-C with no build tool running) exits 130 with no payload.
|
|
739
891
|
|
|
740
892
|
\`stop --json\` prints { root, ok, supervisor, collectors, metro, device,
|
|
741
|
-
port, metroTunnel, releasedLeases }.
|
|
893
|
+
port, metroTunnel, releasedLeases, macos? }. macos is { status: stopped | failed,
|
|
894
|
+
reason? } when a macOS app is recorded. When it cannot end the \`ios\` or
|
|
742
895
|
\`android\` run holding the workspace, it prints the error contract with
|
|
743
896
|
root and ok: false instead, plus device.remote when it ended a recorded EAS
|
|
744
897
|
session first, and exits 1:
|
|
@@ -788,12 +941,44 @@ RULES
|
|
|
788
941
|
preapproves CoreSimulatorBridge for exactly the installed
|
|
789
942
|
bundle id and discovered scheme on its owned simulator. That
|
|
790
943
|
suppresses iOS's first-launch confirmation;
|
|
791
|
-
unrelated schemes remain unapproved.
|
|
944
|
+
unrelated schemes remain unapproved. The approvals persist
|
|
945
|
+
until the simulator is erased, so Stim records the ones it
|
|
946
|
+
wrote on the workspace's device record and writes only the
|
|
947
|
+
ones missing from it. Parking drops the record, so an
|
|
948
|
+
adopted simulator gets them again once.
|
|
949
|
+
THE DEV MENU AND ITS BUTTON stay off a simulator two ways,
|
|
950
|
+
which the onboarding flag does NOT cover. Stim's own
|
|
951
|
+
\`simctl launch --initialUrl\` ends in
|
|
952
|
+
\`-EXDevMenuShowsAtLaunch 0 -EXDevMenuShowFloatingActionButton 0\`
|
|
953
|
+
as trailing argv, the same mechanism the phone launch uses:
|
|
954
|
+
NSUserDefaults reads the argument domain ahead of the
|
|
955
|
+
persisted one, so Stim's own launch comes up on the app, not
|
|
956
|
+
the menu, without writing to the simulator. Stim also writes
|
|
792
957
|
EXDevMenuShowsAtLaunch=false and
|
|
793
|
-
EXDevMenuShowFloatingActionButton=false
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
958
|
+
EXDevMenuShowFloatingActionButton=false to the app's
|
|
959
|
+
persisted defaults, but only on a real install -- one that
|
|
960
|
+
is not skipped as byte-identical -- or when its own launch
|
|
961
|
+
falls back to \`simctl openurl\` (no console capture in flight),
|
|
962
|
+
which carries no launch arguments at all and so writes them
|
|
963
|
+
itself before opening the URL. Adoption clears the app's
|
|
964
|
+
data by replacing its defaults with those two keys (one
|
|
965
|
+
\`defaults import\`), so an install that adoption then skips
|
|
966
|
+
as byte-identical keeps them too. Those persisted values
|
|
967
|
+
cover launches Stim did not make: a home-screen tap, an
|
|
968
|
+
agent relaunch, or a crash restart. The \`__expo_\` deep-link
|
|
969
|
+
params below last one process and do not persist.
|
|
970
|
+
When the project's installed expo-dev-launcher (found
|
|
971
|
+
through expo-dev-client, no network) is 58.0.0 or later
|
|
972
|
+
(expo/expo#49651), Stim's own deep link also carries
|
|
973
|
+
\`disableFab=1&disableAutoLaunch=1\` on the outer link and
|
|
974
|
+
on the project url, plus
|
|
975
|
+
\`__expo_disable_fab=1&__expo_disable_auto_launch=1\` on the
|
|
976
|
+
project url, which is the only url \`simctl launch
|
|
977
|
+
--initialUrl\` hands the launcher and the one 58.0.7 reads
|
|
978
|
+
the \`__expo_\` spellings from, for that launch only. So
|
|
979
|
+
device automation opens on the app.
|
|
980
|
+
The \`install dev client prepared (...)\` line appears
|
|
981
|
+
only when one of those simulator writes ran. The
|
|
797
982
|
unverified warning therefore leads with the picker, then
|
|
798
983
|
prints the openurl
|
|
799
984
|
retry. ON LOCAL ANDROID the same deep link also carries the
|
|
@@ -805,8 +990,8 @@ RULES
|
|
|
805
990
|
Remote Android opens only the URL, so that intent-extra
|
|
806
991
|
suppression does not apply there.
|
|
807
992
|
Every Stim deep link also carries an outer \`disableFab=1\`
|
|
808
|
-
query parameter. Versions with expo/expo#49651
|
|
809
|
-
|
|
993
|
+
query parameter. Versions with expo/expo#49651 read it and
|
|
994
|
+
hide the button; earlier versions ignore it. Stim does
|
|
810
995
|
not rewrite expo-dev-menu's private SharedPreferences XML:
|
|
811
996
|
that internal file is not a supported API, and changing it
|
|
812
997
|
would persist over the user's own Tools-button setting. The
|
|
@@ -853,7 +1038,7 @@ RULES
|
|
|
853
1038
|
writes and the Local Network grant both survive an
|
|
854
1039
|
UPGRADE install. Android's intent extra prevents the menu's
|
|
855
1040
|
automatic launch; versions with expo/expo#49651 also honor
|
|
856
|
-
the
|
|
1041
|
+
the outer FAB flag in Stim's deep link.
|
|
857
1042
|
The phone's unverified remedy is also ROUTED, not a fixed
|
|
858
1043
|
list. When this launch's device records carry the Local
|
|
859
1044
|
Network path reason, the remedy leads with that evidence and
|
|
@@ -868,7 +1053,7 @@ RULES
|
|
|
868
1053
|
},
|
|
869
1054
|
gc: {
|
|
870
1055
|
summary: "the gc report payload: mode, sections, reasons, failures, results, inventory, and the gc refusals",
|
|
871
|
-
body: () => ` stim gc [--delete] [--older-than <days>] [--cache <name|all|workspaces>]
|
|
1056
|
+
body: () => ` stim gc [--delete] [--older-than <days>] [--cache <name|all|workspaces|recordings|parked>]
|
|
872
1057
|
[--worktrees] [--idle <duration>] --json
|
|
873
1058
|
|
|
874
1059
|
The report the text prints, as one payload. Show the user its sections
|
|
@@ -881,7 +1066,8 @@ RULES
|
|
|
881
1066
|
idle the --idle duration in milliseconds, or null
|
|
882
1067
|
cacheScope the --cache name, or null. When set, devices, project
|
|
883
1068
|
entries and locks were not inspected and their sections
|
|
884
|
-
are empty
|
|
1069
|
+
are empty, except that --cache parked fills
|
|
1070
|
+
parkedSimulators and parkedEmulators
|
|
885
1071
|
olderThan the --older-than days, or null
|
|
886
1072
|
worktreeSweep null without --worktrees; otherwise { olderThan, defaulted }:
|
|
887
1073
|
the idle days a linked worktree needs, and whether that is
|
|
@@ -895,10 +1081,13 @@ RULES
|
|
|
895
1081
|
run. status is "done", "kept" (left alone, detail says
|
|
896
1082
|
why) or "failed" (detail says why and what to retry).
|
|
897
1083
|
kind: device, parkedDevice, idleDevice, deviceRecord,
|
|
898
|
-
workspaceOutputs, workspaceDirectory, project,
|
|
899
|
-
buildSlot, deviceLease, easSession, worktree,
|
|
1084
|
+
workspaceOutputs, recording, workspaceDirectory, project,
|
|
1085
|
+
buildLock, buildSlot, deviceLease, easSession, worktree,
|
|
1086
|
+
cache.
|
|
900
1087
|
label is a device, path or cache name; id is the UDID,
|
|
901
|
-
AVD name or path behind it, or null
|
|
1088
|
+
AVD name or path behind it, or null. Under --cache
|
|
1089
|
+
parked, a "done" parkedDevice was erased and stays
|
|
1090
|
+
parked; otherwise it was deleted
|
|
902
1091
|
inventory null except on a dry run without --cache or --idle. Report
|
|
903
1092
|
only: gc never acts on it, even under --delete.
|
|
904
1093
|
{ devices, runtimes, systemImages, notices }
|
|
@@ -952,10 +1141,12 @@ RULES
|
|
|
952
1141
|
recent-activity worktree becomes removable,
|
|
953
1142
|
else null. Without --worktrees, the source
|
|
954
1143
|
checkout and roots outside git are left out
|
|
955
|
-
parkedSimulators { udid, name, model, runtime, parkedAt,
|
|
956
|
-
listed }
|
|
957
|
-
at
|
|
958
|
-
|
|
1144
|
+
parkedSimulators { udid, name, model, runtime, parkedAt, app,
|
|
1145
|
+
bytes, listed } app is the bundle id recorded
|
|
1146
|
+
at park, else null; with --older-than, only
|
|
1147
|
+
those parked at least that long
|
|
1148
|
+
parkedEmulators { name, systemImage, deviceProfile, parkedAt, app,
|
|
1149
|
+
bytes, listed } app is the package name;
|
|
959
1150
|
likewise
|
|
960
1151
|
orphanedDevices { kind, id, name, bytes, directory }
|
|
961
1152
|
unverifiedDevices { kind, id, name, command } stim-* devices this
|
|
@@ -973,6 +1164,10 @@ RULES
|
|
|
973
1164
|
root, and browser profile paths whose
|
|
974
1165
|
directory is gone; --delete forgets the
|
|
975
1166
|
ledger entry only
|
|
1167
|
+
staleStatusCaches { kind: "disk-usage" | "pull-request", path,
|
|
1168
|
+
file, bytes } entries of $STIM_HOME/disk-usage
|
|
1169
|
+
and pull-requests whose path is gone from a
|
|
1170
|
+
mounted volume; --delete removes the file
|
|
976
1171
|
idleDevices { kind, id, name, project, slot, lastActivityAt,
|
|
977
1172
|
idleForMs, buildInProgress } booted owned
|
|
978
1173
|
devices whose status activity is "idle";
|
|
@@ -996,14 +1191,47 @@ RULES
|
|
|
996
1191
|
Metro, client and device logs over twice the
|
|
997
1192
|
8 MiB cap; willTrim marks the ones it would
|
|
998
1193
|
trim
|
|
1194
|
+
recordings { dir, projectRoot, bytes, deleteBytes,
|
|
1195
|
+
willDelete, withWorkspace, reason, detail }
|
|
1196
|
+
recordings/ of each workspace; deleteBytes is
|
|
1197
|
+
what --delete would remove; withWorkspace
|
|
1198
|
+
marks those of a gone workspace, removed with
|
|
1199
|
+
its workspace directory
|
|
999
1200
|
workspaceBuildOutputs { dir, projectRoot, bytes, idleDays, willClear,
|
|
1000
1201
|
reason, detail } derived-data, gradle-build,
|
|
1001
|
-
android-cas
|
|
1202
|
+
android-cas, cache-provider, macos/build,
|
|
1203
|
+
macos/<Product>.app and macos/staging-* of each
|
|
1002
1204
|
workspace; willClear marks the ones --delete
|
|
1003
|
-
would clear
|
|
1205
|
+
would clear; --cache workspaces omits the
|
|
1206
|
+
directories that --delete removes whole (dead
|
|
1207
|
+
projects and orphanedWorkspaces), and --cache
|
|
1208
|
+
all does not
|
|
1004
1209
|
caches { name, dir, source, bytes, note, willEmpty,
|
|
1005
|
-
emptySkipped } alive, not
|
|
1006
|
-
marks the ones --delete
|
|
1210
|
+
emptySkipped, scopedEmpty } alive, not
|
|
1211
|
+
garbage; willEmpty marks the ones --delete
|
|
1212
|
+
would empty. scopedEmpty is { willEmpty,
|
|
1213
|
+
emptySkipped } and says what \`--delete
|
|
1214
|
+
--cache <selector>\` without --older-than
|
|
1215
|
+
would do to the cache; it is present without
|
|
1216
|
+
--cache and null with it, where willEmpty
|
|
1217
|
+
already says so. Caches that share a name
|
|
1218
|
+
carry their directory's last component, as
|
|
1219
|
+
"Metro transform cache: <store>"
|
|
1220
|
+
memory { kind: "watchman" | "gradleDaemon" |
|
|
1221
|
+
"kotlinDaemon", cacheKind: "watchman" |
|
|
1222
|
+
"gradle-daemons", pid, startedAt, bytes,
|
|
1223
|
+
measure: "footprint" | "rss", version,
|
|
1224
|
+
gradleHome, offloadClient, state: "idle" |
|
|
1225
|
+
"busy" | "unknown", reclaimable, reason,
|
|
1226
|
+
detail } without --cache, or with --cache
|
|
1227
|
+
watchman or gradle-daemons; reclaimable marks
|
|
1228
|
+
the ones gc --delete --cache <cacheKind>
|
|
1229
|
+
would stop (\`guide cleanup memory\`)
|
|
1230
|
+
watchmanRoots { path, stale: "missing" | "pruned-worktree" |
|
|
1231
|
+
null, subscriptions, triggers, removable,
|
|
1232
|
+
detail } removable marks the stale roots
|
|
1233
|
+
--cache watchman would watch-del
|
|
1234
|
+
memoryNotices { message } why the memory sweep was skipped
|
|
1007
1235
|
bytes is null when the size is unknown: not measured, or the measurement
|
|
1008
1236
|
failed. idleDays is null when the last use is unknown. listed is false for
|
|
1009
1237
|
a parked device that is no longer on this machine, and null when its
|
|
@@ -1015,6 +1243,9 @@ RULES
|
|
|
1015
1243
|
workspaceBuildOutputs unresolved | in-use | last-use-unknown |
|
|
1016
1244
|
recently-used
|
|
1017
1245
|
workspaceLogs unresolved | in-use | collector
|
|
1246
|
+
recordings unresolved | retained | recently-recorded
|
|
1247
|
+
memory in-use | busy | unknown | build-running |
|
|
1248
|
+
stim-server
|
|
1018
1249
|
linkedWorktrees not-a-worktree | bare-repository |
|
|
1019
1250
|
source-checkout-unknown | source-checkout |
|
|
1020
1251
|
locked | in-use | status-unreadable | dirty |
|
|
@@ -1029,26 +1260,96 @@ RULES
|
|
|
1029
1260
|
|
|
1030
1261
|
- a --cache name that no shared cache carries; the remedy names the
|
|
1031
1262
|
caches on this machine
|
|
1032
|
-
- --cache together with --worktrees or --idle
|
|
1263
|
+
- --cache together with --worktrees or --idle
|
|
1264
|
+
- --cache watchman or gradle-daemons together with --older-than`
|
|
1033
1265
|
},
|
|
1034
1266
|
status: {
|
|
1035
|
-
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",
|
|
1267
|
+
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",
|
|
1036
1268
|
body: () => ` stim status --json
|
|
1037
1269
|
|
|
1270
|
+
Each environment carries phase, where the workspace is in its lifecycle:
|
|
1271
|
+
|
|
1272
|
+
phase "warming" stim worktree warm runs in it now
|
|
1273
|
+
"ready" its last warm succeeded and nothing has run there
|
|
1274
|
+
since: no start, ios, android, web or reload, for
|
|
1275
|
+
at most 2 hours
|
|
1276
|
+
"live" live is true: Metro, a device, Chrome or a remote
|
|
1277
|
+
session of it runs
|
|
1278
|
+
"idle" none of these
|
|
1279
|
+
phaseSince when the warm started ("warming") or finished ("ready"); null
|
|
1280
|
+
for "live" and "idle"
|
|
1281
|
+
warmStep "refresh" or "copy", the step a warming workspace is in;
|
|
1282
|
+
absent in other phases
|
|
1283
|
+
recording { enabled }: whether stim-server may record the workspace's
|
|
1284
|
+
device screens for replay, from recording.enabled
|
|
1285
|
+
|
|
1286
|
+
Each environment also carries stage, where the workspace is as Stim
|
|
1287
|
+
Desktop and the phone app show it: { kind, since, platform, closedApps }.
|
|
1288
|
+
The first kind that applies wins:
|
|
1289
|
+
|
|
1290
|
+
kind "building" a build runs; platform names it
|
|
1291
|
+
"warming" phase is "warming" and nothing is live
|
|
1292
|
+
"ready" phase is "ready" and nothing is live
|
|
1293
|
+
"build-failed" the newest run of either platform failed;
|
|
1294
|
+
platform names it
|
|
1295
|
+
"running" live is true or a remote session runs
|
|
1296
|
+
"stopped" none of these
|
|
1297
|
+
since when it began: the build's start, phaseSince, the failed
|
|
1298
|
+
run's finishedAt (else startedAt), the supervisor's start or
|
|
1299
|
+
metro.lastStop.at; null when unknown
|
|
1300
|
+
platform "ios" or "android" for "building" and "build-failed", else null
|
|
1301
|
+
closedApps for "running", [{ platform, slot }] of the devices whose
|
|
1302
|
+
appPresence is "closed", the default slot first, then each
|
|
1303
|
+
slot in order, iOS before Android; empty otherwise
|
|
1304
|
+
|
|
1305
|
+
A warm records warming under its own ownership claim, so a warm that was
|
|
1306
|
+
killed or failed reads as idle, never as warming. Plain \`stim status\`
|
|
1307
|
+
marks the workspace [warming: <step>], [ready] or [idle].
|
|
1308
|
+
|
|
1038
1309
|
Each environment carries issues, the things in that workspace that need
|
|
1039
|
-
the user, and warnings,
|
|
1040
|
-
default slot, then "<message>; run \`<remedy>\`"):
|
|
1310
|
+
the user, and warnings, its error and warning issues as text ("<slot>: "
|
|
1311
|
+
when not the default slot, then "<message>; run \`<remedy>\`"):
|
|
1041
1312
|
|
|
1042
1313
|
issues [{ code, severity, message, remedy, workspace, slot? }]
|
|
1043
1314
|
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1315
|
+
severity "error" Stim cannot verify or safely act on something; a
|
|
1316
|
+
person resolves it (stim guide errors teardown)
|
|
1317
|
+
"warning" something is broken now and needs someone to act:
|
|
1318
|
+
an app cannot reach Metro, a port is held, an
|
|
1319
|
+
expected device vanished
|
|
1320
|
+
"info" a fact the next normal stim command handles by
|
|
1321
|
+
itself, so it needs no action
|
|
1322
|
+
The apps show error and warning and hide info; plain stim
|
|
1323
|
+
status prints info as a dim note with no remedy; warnings
|
|
1324
|
+
excludes info. The info codes: sim-missing, sim-without-metro,
|
|
1325
|
+
avd-missing, and port-not-ours while no supervisor runs.
|
|
1326
|
+
|
|
1327
|
+
code port-not-ours another process answers Metro on this
|
|
1328
|
+
workspace's reserved port. "info" with
|
|
1329
|
+
remedy stim start when no supervisor
|
|
1330
|
+
runs: start reserves a free port.
|
|
1331
|
+
"warning" when this workspace's
|
|
1332
|
+
supervisor runs and cannot serve there
|
|
1333
|
+
(remedy stim stop), or its supervisor is
|
|
1334
|
+
unverified. The message names the holder:
|
|
1335
|
+
"Metro for workspace <name>" when it runs
|
|
1336
|
+
inside another workspace, else "a dev
|
|
1337
|
+
server in <last dirs>"; metro.heldBy
|
|
1338
|
+
carries its pid and cwd
|
|
1339
|
+
sim-missing the recorded simulator no longer exists;
|
|
1340
|
+
always "info", stim ios creates a new one
|
|
1047
1341
|
sim-without-metro the simulator is booted and no Metro
|
|
1048
|
-
serves the workspace
|
|
1342
|
+
serves the workspace; always "info"
|
|
1049
1343
|
avd-serial-changed the owned emulator came back on another
|
|
1050
1344
|
serial, so Metro forwarding is lost
|
|
1051
|
-
avd-missing the recorded AVD no longer exists
|
|
1345
|
+
avd-missing the recorded AVD no longer exists; always
|
|
1346
|
+
"info", stim android creates a new one
|
|
1347
|
+
android-reverse-missing the owned emulator runs this
|
|
1348
|
+
workspace's debug app against its live
|
|
1349
|
+
Metro, but the adb reverse for that Metro
|
|
1350
|
+
port is gone, so the app cannot reach
|
|
1351
|
+
Metro; the remedy stim reload android
|
|
1352
|
+
restores it
|
|
1052
1353
|
avd-not-detected adb does not see the owned emulator while
|
|
1053
1354
|
the workspace expects it: it holds an
|
|
1054
1355
|
unexpired lease on its serial, or it
|
|
@@ -1068,8 +1369,8 @@ RULES
|
|
|
1068
1369
|
browser-orphaned the owned Chrome runs but its supervisor
|
|
1069
1370
|
exited, so page logs are not captured;
|
|
1070
1371
|
stim stop closes it
|
|
1071
|
-
severity "error"
|
|
1072
|
-
|
|
1372
|
+
severity the rule above; "error" is also what makes stop or start
|
|
1373
|
+
refuse until it is resolved
|
|
1073
1374
|
remedy a command to run from workspace, such as "stim android --slot
|
|
1074
1375
|
fold"
|
|
1075
1376
|
slot absent for the default slot
|
|
@@ -1082,20 +1383,45 @@ RULES
|
|
|
1082
1383
|
An environment where \`stim web\` ran carries web, its Stim-owned Chrome:
|
|
1083
1384
|
|
|
1084
1385
|
web { browser, version, running, pid, supervisorPid, url, headless,
|
|
1085
|
-
viewport, profile, cdpEndpoint }
|
|
1386
|
+
viewport, profile, cdpEndpoint, targetId, page?, activity? }
|
|
1086
1387
|
|
|
1087
1388
|
running the browser supervisor and Chrome are both verified live;
|
|
1088
|
-
pid, supervisorPid and
|
|
1389
|
+
pid, supervisorPid, cdpEndpoint and targetId are null when
|
|
1390
|
+
false, and page and activity are absent
|
|
1391
|
+
url the page stim web opened; page.url is the document the page
|
|
1392
|
+
loaded last, and page.route the URL an in-app route change
|
|
1393
|
+
(history API or fragment) moved it to since
|
|
1089
1394
|
cdpEndpoint http://127.0.0.1:<port>, the reserved loopback DevTools
|
|
1090
1395
|
endpoint of that Chrome. Attach Playwright MCP
|
|
1091
1396
|
(--cdp-endpoint) or agent-browser (--cdp <port>) to it; it
|
|
1092
1397
|
reaches only the Stim profile, never your own browser
|
|
1398
|
+
targetId the DevTools target id of the owned page, the one Stim
|
|
1399
|
+
Desktop and the phone stream; other tabs are not the page
|
|
1093
1400
|
profile the Stim-owned user data directory under STIM_HOME
|
|
1401
|
+
page { url, state, error?, route? }: the page's latest load, from
|
|
1402
|
+
the newest page-load marker in web.ndjson; null before the
|
|
1403
|
+
first. route is absent until an in-app route change after
|
|
1404
|
+
that load
|
|
1405
|
+
"loading" the document was requested; no load event yet
|
|
1406
|
+
"loaded" the load event fired
|
|
1407
|
+
"failed" the document failed (connection refused, HTTP
|
|
1408
|
+
error, certificate) or the page crashed; error
|
|
1409
|
+
is the log message
|
|
1410
|
+
activity like a device's activity below. driver.tool names a
|
|
1411
|
+
DevTools client connected to cdpEndpoint (agent-browser,
|
|
1412
|
+
agent-device, argent, playwright, puppeteer,
|
|
1413
|
+
chrome-devtools-mcp, or the executable's name); Stim's own
|
|
1414
|
+
connections (the browser supervisor, stim-server, Stim
|
|
1415
|
+
Desktop, the stim CLI) are not drivers. basis: cdp-client
|
|
1416
|
+
(one lsof of the DevTools port); agent-action (the newest
|
|
1417
|
+
agent input the browser supervisor recorded in web.ndjson)
|
|
1418
|
+
and page-log (the newest other
|
|
1419
|
+
web.ndjson record) for recency
|
|
1094
1420
|
|
|
1095
1421
|
Each booted simulator and detected emulator in environments (and in
|
|
1096
1422
|
slots) carries activity; a shut-down or physical device has none:
|
|
1097
1423
|
|
|
1098
|
-
activity { state, driver?, lastActivityAt?, basis }
|
|
1424
|
+
activity { state, driver?, lastActivityAt?, recent?, basis }
|
|
1099
1425
|
|
|
1100
1426
|
state "driven" a live claim or driver holds the device now
|
|
1101
1427
|
"active" no driver, but activity in the last 10 minutes
|
|
@@ -1103,25 +1429,46 @@ RULES
|
|
|
1103
1429
|
"unknown" a claim or driver check could not be read;
|
|
1104
1430
|
never treated as idle
|
|
1105
1431
|
driver { tool, pid, since } for "driven": agent-device, stim device
|
|
1106
|
-
lock, xcodebuild, idb, maestro, appium, simctl,
|
|
1432
|
+
lock, argent, xcodebuild, idb, maestro, appium, simctl,
|
|
1107
1433
|
uiautomator or instrumentation; pid and since are null when
|
|
1108
1434
|
the claim does not record them
|
|
1109
1435
|
lastActivityAt the newest of this device's app log records, this
|
|
1110
|
-
platform's Metro bundle requests
|
|
1111
|
-
Stim run,
|
|
1436
|
+
platform's Metro bundle requests, the workspace's last
|
|
1437
|
+
Stim run, while agent-device drives it the agent's last
|
|
1438
|
+
recorded action, and now while a stim-server client views
|
|
1439
|
+
it, rounded down to the minute; absent when none is
|
|
1112
1440
|
recorded
|
|
1441
|
+
recent the newest time of each kind of evidence behind
|
|
1442
|
+
lastActivityAt: agent-action, device-log, metro-bundle,
|
|
1443
|
+
workspace-use, supervisor-start, page-log or viewer, each
|
|
1444
|
+
rounded down to the minute. viewer is now while a
|
|
1445
|
+
stim-server client streams the device's frames
|
|
1113
1446
|
basis the evidence behind state, strongest first:
|
|
1114
1447
|
agent-device-claim, agent-device-lease agent-device state,
|
|
1115
1448
|
read only; live only when every recorded process is alive
|
|
1116
|
-
with its recorded start time, so a reused pid is dead
|
|
1449
|
+
with its recorded start time, so a reused pid is dead.
|
|
1450
|
+
A runner lease that declares agent-device's device
|
|
1451
|
+
claims is never a driver: without a claim, the runner
|
|
1452
|
+
is one agent-device kept warm after close or handed
|
|
1453
|
+
off when its daemon exited
|
|
1117
1454
|
device-lock an unexpired \`stim device lock\` lease
|
|
1118
|
-
driver-process a host process naming the UDID or
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1455
|
+
driver-process a host process naming the UDID or
|
|
1456
|
+
serial, other than such a runner
|
|
1457
|
+
instrumentation an on-device uiautomator, androidx.test or
|
|
1458
|
+
argent helper process (one adb shell ps
|
|
1459
|
+
per emulator)
|
|
1460
|
+
device-log, metro-bundle, workspace-use, agent-action,
|
|
1461
|
+
viewer recency
|
|
1122
1462
|
|
|
1123
1463
|
Plain \`stim status\` appends it to each device line: "driven by
|
|
1124
1464
|
agent-device for 12m", "active", "idle 3h", or "activity unknown (...)".
|
|
1465
|
+
|
|
1466
|
+
An owned device that is not booted after its supervisor shut it down for
|
|
1467
|
+
devices.idleShutdownMinutes carries idleShutdown, and plain \`status\`
|
|
1468
|
+
appends "shut down after 30m idle at <at>" to its line; the next \`ios\` or
|
|
1469
|
+
\`android\` run for that slot clears it (\`guide lifecycle budget\`):
|
|
1470
|
+
|
|
1471
|
+
idleShutdown { at, idleMinutes }
|
|
1125
1472
|
\`gc --idle <duration>\` shuts down owned devices idle that long
|
|
1126
1473
|
(\`guide cleanup gc\`).
|
|
1127
1474
|
|
|
@@ -1140,6 +1487,16 @@ RULES
|
|
|
1140
1487
|
app is absent when the device is not owned or no app id is known: the
|
|
1141
1488
|
process was not checked.
|
|
1142
1489
|
|
|
1490
|
+
Each ios and android device record, in every slot, also carries
|
|
1491
|
+
appPresence, whether the device lacks the app:
|
|
1492
|
+
|
|
1493
|
+
appPresence "none" the device runs, its platform's latest run failed
|
|
1494
|
+
and builds carries the platform with no
|
|
1495
|
+
succeeded run
|
|
1496
|
+
"closed" the device runs and app.state is "stopped"
|
|
1497
|
+
null otherwise, including a device that is not running
|
|
1498
|
+
and a physical device
|
|
1499
|
+
|
|
1143
1500
|
This is current process state, read from one host ps (simulator apps are
|
|
1144
1501
|
host processes) and the same adb shell ps as activity, so a status --watch
|
|
1145
1502
|
refresh notices an exit within its 30-second fallback. It is not launch
|
|
@@ -1148,15 +1505,48 @@ RULES
|
|
|
1148
1505
|
status appends "<id> running", "<id> not running" or "<id> process
|
|
1149
1506
|
unknown" to the device line.
|
|
1150
1507
|
|
|
1508
|
+
An environment's metro carries tunnel { provider, url } for a recorded
|
|
1509
|
+
managed tunnel on its reserved port: ngrok, cloudflared or tailscale.
|
|
1510
|
+
Plain status prints the provider and URL, with "tailnet-only" for tailscale.
|
|
1511
|
+
This describes the record, not a reachability check (\`guide metro\`).
|
|
1512
|
+
|
|
1151
1513
|
An environment's metro carries idleStop { reason: "idle", at, idleMinutes }
|
|
1152
1514
|
when its supervisor stopped the dev server for idleness and nothing serves
|
|
1153
1515
|
the port since; plain \`status\` prints "stopped (idle)" (\`guide metro\`).
|
|
1516
|
+
Whenever nothing serves the port, metro also carries lastStop when the cause
|
|
1517
|
+
is known, one of:
|
|
1518
|
+
{ reason: "idle", at, idleMinutes }
|
|
1519
|
+
{ reason: "requested", at, by, byPid, byWorkspace? } a Stim command
|
|
1520
|
+
{ reason: "signal", at, signal } a signal no Stim command sent
|
|
1521
|
+
{ reason: "server-exited", at, mode, code, signal }
|
|
1522
|
+
{ reason: "vanished", pid, startedAt } the recorded supervisor is gone
|
|
1523
|
+
and recorded no cause
|
|
1524
|
+
(\`guide metro\`, WHY IT STOPPED).
|
|
1525
|
+
When another process answers Metro on the port, metro carries
|
|
1526
|
+
heldBy { pid, cwd }, cwd null when its directory could not be read.
|
|
1527
|
+
|
|
1528
|
+
metro also carries bundle, from the bundle requests Stim's middleware
|
|
1529
|
+
records in the workspace's metro log: an app's, and Stim's own prefetch
|
|
1530
|
+
before a launch. It is absent when the log's tail holds none.
|
|
1531
|
+
|
|
1532
|
+
bundle { bundling, platform?, startedAt?, percent?, last? }
|
|
1533
|
+
bundling true while a request is in flight: Metro runs, and the request
|
|
1534
|
+
started under the current dev server less than 10 minutes ago
|
|
1535
|
+
platform with bundling, the in-flight request's "ios" or "android";
|
|
1536
|
+
startedAt when it started
|
|
1537
|
+
percent 0-100, only when Metro reported progress for that request,
|
|
1538
|
+
which it does for clients that accept multipart responses
|
|
1539
|
+
last { platform, status, durationMs, finishedAt }: the newest
|
|
1540
|
+
finished request, status "ok" or "failed", durationMs from the
|
|
1541
|
+
request to the end of the response
|
|
1154
1542
|
|
|
1155
1543
|
Each entry of environments also carries build: null, or the ios or android
|
|
1156
1544
|
run that holds this workspace's native-run.lock:
|
|
1157
1545
|
|
|
1158
1546
|
build { platform, slot, state, phase, startedAt, phaseStartedAt,
|
|
1159
|
-
outcome, expectedMs, expectedPhaseMs, basis
|
|
1547
|
+
outcome, outcomeKnown, cacheLookupOutcome?, expectedMs, expectedPhaseMs, basis,
|
|
1548
|
+
plannedPhases, missReason?, missProvisional?, detail?, placement,
|
|
1549
|
+
waitingOn? }
|
|
1160
1550
|
|
|
1161
1551
|
state "running" while the run's own native-run claim is live;
|
|
1162
1552
|
"stale" when that claim was released or its process is
|
|
@@ -1164,30 +1554,98 @@ RULES
|
|
|
1164
1554
|
record; "unknown" when the claim set cannot be resolved.
|
|
1165
1555
|
Never treat a stale record as a build in progress.
|
|
1166
1556
|
phase prepare | cache-lookup | wait | prebuild | pods |
|
|
1167
|
-
compile | install | launch. prepare covers
|
|
1168
|
-
|
|
1169
|
-
workspace's build of the same fingerprint
|
|
1170
|
-
|
|
1557
|
+
compile | device | install | launch. prepare covers
|
|
1558
|
+
settings and Metro; wait is waiting on another
|
|
1559
|
+
workspace's build of the same fingerprint. Creating,
|
|
1560
|
+
adopting or booting the owned simulator or emulator
|
|
1561
|
+
before the cache lookup is prepare time. device starts
|
|
1562
|
+
once the app is ready and covers waiting for the
|
|
1563
|
+
device: its boot, adoption cleanup, or a physical
|
|
1564
|
+
device's lease and connection check. A boot that
|
|
1565
|
+
finishes during the build adds no device time. An
|
|
1566
|
+
--eas-profile run has no cache lookup, so its outcome
|
|
1567
|
+
stays the project's most recent one until install.
|
|
1171
1568
|
startedAt when the run started; phaseStartedAt when its phase did
|
|
1172
|
-
outcome "cold"
|
|
1173
|
-
"hit"
|
|
1174
|
-
|
|
1569
|
+
outcome "cold" after the local/provider lookups resolve a miss;
|
|
1570
|
+
"hit" after a cached artifact is ready to reuse, including
|
|
1571
|
+
a shared-build hit or a recheck after prebuild or pods.
|
|
1572
|
+
A later recheck can replace the first lookup's outcome.
|
|
1573
|
+
Before resolution, the outcome of this project's most
|
|
1574
|
+
recent run.
|
|
1575
|
+
outcomeKnown true once outcome is this run's own, false while it is
|
|
1576
|
+
the project's most recent one
|
|
1577
|
+
cacheLookupOutcome "hit" or "miss" once an actual cache lookup resolves;
|
|
1578
|
+
absent before resolution and on runs that skip lookup,
|
|
1579
|
+
such as --eas-profile
|
|
1175
1580
|
expectedMs the median duration of this project's last successful
|
|
1176
1581
|
runs with that outcome on that platform, or null with
|
|
1177
|
-
no history
|
|
1582
|
+
no history. The run estimates twice: when it starts,
|
|
1583
|
+
from every run with the project's most recent outcome,
|
|
1584
|
+
and once when outcomeKnown turns true. From then on only
|
|
1585
|
+
runs that created, adopted or cold-booted their device
|
|
1586
|
+
the way this run did count; a run that reuses a booted
|
|
1587
|
+
device also counts runs recorded before Stim tagged them
|
|
1588
|
+
until 3 tagged ones exist. Runs that finish meanwhile do
|
|
1589
|
+
not change the estimate.
|
|
1178
1590
|
expectedPhaseMs the median duration of this phase in those runs, or null
|
|
1179
|
-
|
|
1591
|
+
when none of them entered it
|
|
1592
|
+
basis how many runs the expectedMs median comes from (at most
|
|
1593
|
+
10 per kind: device-setup runs keep their own 10)
|
|
1594
|
+
plannedPhases [{ phase, expectedMs }] in phase order: the phases at
|
|
1595
|
+
least half of those runs entered, each with its median
|
|
1596
|
+
in them; null with no such run. The current phase can be
|
|
1597
|
+
one it does not list, such as wait. Draw a progress bar
|
|
1598
|
+
from it rather than from builds, which holds only this
|
|
1599
|
+
workspace's runs and includes offloaded ones
|
|
1600
|
+
missReason once the run knows its cache lookup missed, why, in the
|
|
1601
|
+
shape of lastBuilds missReason below
|
|
1602
|
+
missProvisional true while missReason is the first lookup's miss and the
|
|
1603
|
+
run will prebuild or install pods and look the key up
|
|
1604
|
+
again, because those can change the fingerprint. A hit on
|
|
1605
|
+
that second lookup removes missReason and sets outcome to
|
|
1606
|
+
hit; otherwise missReason is replaced with the final one,
|
|
1607
|
+
whose rekeyedBy lists what changed the key, and
|
|
1608
|
+
missProvisional goes away before compile
|
|
1609
|
+
detail during compile, once the build tool printed a line Stim
|
|
1610
|
+
reads: { step, unit, done, total, line, updatedAt }
|
|
1611
|
+
step the tool's step: configure, compile, link, resources,
|
|
1612
|
+
script, dex, package or sign; null before one is known
|
|
1613
|
+
unit "targets" for xcodebuild, "tasks" for Gradle
|
|
1614
|
+
done xcodebuild: targets it finished (a target counts once
|
|
1615
|
+
xcodebuild touches or signs its product, so an
|
|
1616
|
+
incremental build can end below total); Gradle: tasks
|
|
1617
|
+
it reported so far
|
|
1618
|
+
total xcodebuild: targets in its dependency graph; Gradle: null,
|
|
1619
|
+
since its plain output gives no total
|
|
1620
|
+
line the latest compile, link or task line, paths shortened
|
|
1621
|
+
to file names, at most 160 characters
|
|
1622
|
+
updatedAt when the build last wrote it; the run writes it at most
|
|
1623
|
+
every 2 seconds
|
|
1624
|
+
placement where the build runs: "local", or while it is offloaded
|
|
1625
|
+
{ host, phase, startedAt, phaseStartedAt }. host is the
|
|
1626
|
+
offload.machines entry; phase is the step there: sync,
|
|
1627
|
+
deps, prebuild, pods, build (xcodebuild or Gradle) or fetch;
|
|
1628
|
+
startedAt is when the offload started and phaseStartedAt
|
|
1629
|
+
when that step did. Meanwhile phase above follows it as
|
|
1630
|
+
prebuild, pods or compile.
|
|
1631
|
+
waitingOn while phase is "wait" and the holder is known: { path },
|
|
1632
|
+
the workspace whose build of the same artifact this run
|
|
1633
|
+
waits for; absent otherwise. The lock is machine-wide, so
|
|
1634
|
+
path is usually an entry of environments but can name a
|
|
1635
|
+
workspace that is not listed.
|
|
1180
1636
|
|
|
1181
1637
|
Plain \`stim status\` prints the same as one line per workspace:
|
|
1182
1638
|
|
|
1183
1639
|
build: ios compile, 1m10s elapsed -- about 3 min left (median of 4 cold runs)
|
|
1640
|
+
build: ios compile on janics-mac-mini (build, 2m10s), 3m05s elapsed
|
|
1184
1641
|
|
|
1185
1642
|
An environment with a recorded run also carries lastBuilds, each
|
|
1186
1643
|
platform's most recent ios or android run, finished or failed:
|
|
1187
1644
|
|
|
1188
1645
|
lastBuilds { ios?, android? }, each { platform, status, cacheHit,
|
|
1189
1646
|
cacheSkipped, durationMs, fingerprint, startedAt, finishedAt,
|
|
1190
|
-
errorCode?, missReason?,
|
|
1647
|
+
errorCode?, missReason?, buildMachine?, builtOn?, offloadedTo?, offloadFallback?,
|
|
1648
|
+
diagnostics? }
|
|
1191
1649
|
|
|
1192
1650
|
status "ok" or "failed"
|
|
1193
1651
|
cacheHit "local" or "remote" for an app from that cache tier; false
|
|
@@ -1208,10 +1666,43 @@ RULES
|
|
|
1208
1666
|
diagnostics only on a failed run whose compiler reported errors: up to
|
|
1209
1667
|
5 { file, line, column, message }, null where the compiler
|
|
1210
1668
|
gave no position.
|
|
1669
|
+
cause only on a failed run: { key, file, line }, the first
|
|
1670
|
+
diagnostic with a file and a line, keyed "<file>:<line>",
|
|
1671
|
+
else the errorCode (or "failed") with null file and line.
|
|
1672
|
+
Failed runs in a row with the same key failed the same way.
|
|
1673
|
+
Invalid, unlisted or unpaired selections refuse during setup, before cache
|
|
1674
|
+
lookup, and create no lastBuild record or failed-run stats.
|
|
1675
|
+
buildMachine selected auto, local or configured machine entry; always on new build records
|
|
1676
|
+
builtOn here or machine name; absent when no build ran (cache/refusal)
|
|
1677
|
+
offloadedTo only on a run a build machine compiled: its name
|
|
1678
|
+
offloadFallback
|
|
1679
|
+
only on a run that considered offloading and built here: one
|
|
1680
|
+
reason per machine when none took the build, or why the
|
|
1681
|
+
offload stopped
|
|
1211
1682
|
|
|
1212
1683
|
Plain status prints "last build: ios local cache in 12s, android compiled
|
|
1213
1684
|
in 7m02s". To predict the next run instead, see \`guide facts plan\`.
|
|
1214
1685
|
|
|
1686
|
+
Swift Package development carries macos, separate from simulator devices:
|
|
1687
|
+
product, bundle, bundleId, executable, launchId, arguments
|
|
1688
|
+
app?, supervisor? { pid, processToken, startedAtMicros }
|
|
1689
|
+
state running | orphaned | stopped | unverified
|
|
1690
|
+
build { state: running | ok | failed, startedAt,
|
|
1691
|
+
finishedAt?, durationMs?, error?,
|
|
1692
|
+
buildMachine?, builtOn?, errorCode?, offloadedTo?, offloadFallback? }
|
|
1693
|
+
host? { machine, session, appSlot, appAttempt, bundleId,
|
|
1694
|
+
agent } when stim macos --remote placed the app on
|
|
1695
|
+
another Mac; it then has no local app or supervisor
|
|
1696
|
+
hostLaunched? true when that host reported a live app, false when
|
|
1697
|
+
stopped, "unverified" when it could not confirm one
|
|
1698
|
+
A live app without its supervisor is orphaned. An identity the system cannot
|
|
1699
|
+
verify is unverified. For hosted placements only, status asks the host for
|
|
1700
|
+
the session state with about a 3 s timeout per connection and request and
|
|
1701
|
+
a 10 s cache. It reports stopped when the host says the session stopped,
|
|
1702
|
+
for example after a stim-server restart there, and unverified when the
|
|
1703
|
+
host is unreachable or cannot confirm. The placement stays recorded.
|
|
1704
|
+
See stim guide macos for local capture, hosting and cleanup.
|
|
1705
|
+
|
|
1215
1706
|
An environment with a recorded run also carries builds, each platform's
|
|
1216
1707
|
last 10 runs, newest first. Its newest entry that is not "interrupted" is
|
|
1217
1708
|
the run lastBuilds reports, once a run has recorded builds.
|
|
@@ -1231,23 +1722,108 @@ RULES
|
|
|
1231
1722
|
cacheKey the cache key the run looked up or stored under
|
|
1232
1723
|
phases milliseconds spent in each phase the run entered, among
|
|
1233
1724
|
prepare, cache-lookup, wait, prebuild, pods, compile,
|
|
1234
|
-
install and launch
|
|
1725
|
+
device, install and launch; runs recorded before device
|
|
1726
|
+
existed count their device time in prepare and install
|
|
1235
1727
|
|
|
1236
1728
|
Only runs that record a last build are listed: a run that stops before
|
|
1237
1729
|
looking up a build, such as a bad flag, is not. Estimates (expectedMs) come from run statistics, not builds.
|
|
1238
1730
|
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1731
|
+
done and total are counts, not a completion fraction: a target or task
|
|
1732
|
+
can take a second or ten minutes, and cached work finishes instantly.
|
|
1733
|
+
|
|
1734
|
+
Each environment also carries disk, and each owned simulator and owned
|
|
1735
|
+
emulator in ios, android and slots carries disk, once \`status --watch\` has
|
|
1736
|
+
measured them:
|
|
1737
|
+
|
|
1738
|
+
disk (environment) { worktreeBytes, nodeModulesBytes, buildBytes,
|
|
1739
|
+
measuredAt }
|
|
1740
|
+
worktreeBytes the linked worktree, or else the checkout holding the
|
|
1741
|
+
workspace, node_modules included
|
|
1742
|
+
nodeModulesBytes node_modules at that root and at the workspace path;
|
|
1743
|
+
part of worktreeBytes
|
|
1744
|
+
buildBytes Stim's folder for the workspace: Xcode derived data,
|
|
1745
|
+
Gradle outputs and logs
|
|
1746
|
+
measuredAt the oldest of the three measurements; a figure not
|
|
1747
|
+
measured yet is null
|
|
1748
|
+
disk (device) { bytes, measuredAt }: the simulator's data folder
|
|
1749
|
+
under CoreSimulator/Devices, or the AVD's .avd folder
|
|
1750
|
+
|
|
1751
|
+
\`status --watch\` runs one du at a time off its refresh path, and measures
|
|
1752
|
+
a folder at most every 5 minutes while its environment is live and every
|
|
1753
|
+
hour otherwise. It caches each size under $STIM_HOME/disk-usage, which
|
|
1754
|
+
one-shot status only reads, so the fields appear once a watcher, such as
|
|
1755
|
+
stim-server or Stim Desktop, has measured.
|
|
1756
|
+
|
|
1757
|
+
An environment carries agents when coding-agent sessions work in it, most
|
|
1758
|
+
recently active first:
|
|
1759
|
+
|
|
1760
|
+
agents [{ tool, sessionId, title?, cwd, startedAt?, lastActiveAt?,
|
|
1761
|
+
pid?, openUrl?, webUrl? }]
|
|
1762
|
+
tool claude-code | codex
|
|
1763
|
+
title the short name the tool keeps for the session; never a
|
|
1764
|
+
prompt or any conversation content
|
|
1765
|
+
cwd the session's working directory, canonical
|
|
1766
|
+
lastActiveAt the tool's last update of the session, or the last Stim
|
|
1767
|
+
command the session ran here when that is newer
|
|
1768
|
+
openUrl opens the session in the Claude desktop app
|
|
1769
|
+
(claude://code/continue) or the Codex app
|
|
1770
|
+
(codex://threads/<id>) when that app is installed on the
|
|
1771
|
+
Mac; absent for a Claude Code session started in a terminal
|
|
1772
|
+
webUrl https://claude.ai/code/<id> for a Claude Code session with
|
|
1773
|
+
Remote Control connected; it opens the session in a browser
|
|
1774
|
+
or the Claude mobile app
|
|
1775
|
+
|
|
1776
|
+
A session works in an environment when its cwd is the environment's
|
|
1777
|
+
path or inside it (the deepest environment wins), or is the environment's
|
|
1778
|
+
git worktree root. \`status --watch\` looks for sessions at most every 15
|
|
1779
|
+
seconds: Claude Code sessions in ~/.claude/sessions whose process runs,
|
|
1780
|
+
and Codex threads in $CODEX_HOME (~/.codex) updated in the last 30
|
|
1781
|
+
minutes. It caches them in $STIM_HOME/agent-sessions.json, which one-shot
|
|
1782
|
+
status reads and ignores once it is 2 minutes old. Both sources are other
|
|
1783
|
+
tools' internal files; a missing or changed one only drops its sessions.
|
|
1784
|
+
\`start\`, \`ios\`, \`android\`, \`web\`, \`reload\` and \`worktree warm\`
|
|
1785
|
+
also record the CLAUDE_CODE_SESSION_ID or CODEX_THREAD_ID of the shell
|
|
1786
|
+
that ran them, so that session is listed for 30 minutes after the command
|
|
1787
|
+
even when status cannot see it, and for as long as it runs.
|
|
1788
|
+
|
|
1789
|
+
When a watcher stops finding a session it found on its previous look, it
|
|
1790
|
+
records the session in each environment it worked in, under
|
|
1791
|
+
$STIM_HOME/workspaces/<id>/ended-agents.json, for 3 days:
|
|
1792
|
+
|
|
1793
|
+
endedAgents [{ tool, sessionId, title?, cwd, startedAt?, lastActiveAt?,
|
|
1794
|
+
openUrl?, webUrl?, endedAt }], most recently ended first
|
|
1795
|
+
endedAt the last time a watcher found the session running; a Codex
|
|
1796
|
+
thread ends 30 minutes after its last update
|
|
1797
|
+
|
|
1798
|
+
The other fields are as the watcher last found them. A session that runs
|
|
1799
|
+
again is listed in agents only.
|
|
1800
|
+
|
|
1801
|
+
memorySource says how a memory figure was obtained:
|
|
1802
|
+
|
|
1803
|
+
footprint physical footprint, the figure Activity Monitor's Memory
|
|
1804
|
+
column shows, read by a small helper Stim compiles with the
|
|
1805
|
+
Xcode command line tools into $STIM_HOME/helpers on first use
|
|
1806
|
+
rss summed resident set size, for machine owners when the helper
|
|
1807
|
+
cannot be built (no Xcode command line tools, Linux). It
|
|
1808
|
+
overstates a simulator many times over.
|
|
1809
|
+
estimate a fixed amount per booted simulator, detected emulator,
|
|
1810
|
+
running Metro and running Chrome
|
|
1811
|
+
|
|
1812
|
+
An environment's memoryMb is what its own processes use now, the sum of
|
|
1813
|
+
memoryMb over the machine owners whose workspace is that environment,
|
|
1814
|
+
builds included, when machine.memorySource is footprint. Otherwise it is
|
|
1815
|
+
the estimate, with memorySource estimate.
|
|
1816
|
+
|
|
1817
|
+
capacity.committedMb sums memoryMb. The memory budget plans before a boot
|
|
1818
|
+
and always uses the estimate. What is using CPU and memory now is the
|
|
1819
|
+
top-level machine section:
|
|
1820
|
+
|
|
1821
|
+
machine null, or { memorySource, owners: [{ kind, name, workspace,
|
|
1822
|
+
slot?, id, owned, cpuPercent, residentMb, memoryMb,
|
|
1823
|
+
processes }] }
|
|
1824
|
+
|
|
1825
|
+
kind macos the owned Swift Package app and supervisor
|
|
1826
|
+
simulator a booted simulator's launchd_sim tree
|
|
1251
1827
|
emulator an emulator's launcher and qemu tree, by its -avd
|
|
1252
1828
|
metro a workspace's supervisor and Metro trees
|
|
1253
1829
|
build a running ios or android run's process tree,
|
|
@@ -1269,14 +1845,19 @@ RULES
|
|
|
1269
1845
|
residentMb summed resident set size. Pages shared between processes
|
|
1270
1846
|
count once per process, so a simulator reports well above
|
|
1271
1847
|
its physical footprint.
|
|
1848
|
+
memoryMb summed physical footprint when machine.memorySource is
|
|
1849
|
+
footprint; a process the helper cannot read, another user's
|
|
1850
|
+
or one that just exited, counts its resident size. Equals
|
|
1851
|
+
residentMb when memorySource is rss.
|
|
1272
1852
|
|
|
1273
1853
|
Each process counts in exactly one owner, the one whose root process is
|
|
1274
1854
|
its nearest ancestor, so the supervisor a build started counts as Metro,
|
|
1275
1855
|
not as the build, and a Gradle or Kotlin daemon counts in the build that
|
|
1276
1856
|
started it until that build exits, then as shared. Processes with no owner
|
|
1277
1857
|
are left out. machine comes from one host ps, the one status reads for
|
|
1278
|
-
device activity, and
|
|
1279
|
-
live and no build runs: status then
|
|
1858
|
+
device activity, and one run of the footprint helper. It is null when no
|
|
1859
|
+
simulator is booted, no workspace is live and no build runs: status then
|
|
1860
|
+
runs neither. \`status --watch --json\` rereads both every 15 seconds while
|
|
1280
1861
|
machine is not null, with no other subprocess.`
|
|
1281
1862
|
},
|
|
1282
1863
|
plan: {
|
|
@@ -1299,7 +1880,9 @@ RULES
|
|
|
1299
1880
|
outcome "hit" or "cold"; null when the run would refuse
|
|
1300
1881
|
expectedMs the median wall time of this project's last successful runs
|
|
1301
1882
|
with that outcome on that platform, or null with none
|
|
1302
|
-
basis how many runs expectedMs comes from (at most
|
|
1883
|
+
basis how many runs expectedMs comes from (at most 20: runs that
|
|
1884
|
+
set up their device and runs that did not are not told apart
|
|
1885
|
+
before the run)
|
|
1303
1886
|
missReason on a predicted cold build with cache reads on, why the
|
|
1304
1887
|
cache has no app, in the shape of lastBuilds.<platform>
|
|
1305
1888
|
.missReason (\`guide facts status\`), compared with the same
|
|
@@ -1326,12 +1909,19 @@ RULES
|
|
|
1326
1909
|
{ "version": 1,
|
|
1327
1910
|
"project": { "key": "<path>", "ios": <bucket|null>,
|
|
1328
1911
|
"android": <bucket|null> } | null,
|
|
1329
|
-
"machine": { "ios": <bucket|null>, "android": <bucket|null> }
|
|
1912
|
+
"machine": { "ios": <bucket|null>, "android": <bucket|null> },
|
|
1913
|
+
"offload": { "today": { "here", "offloaded", "fellBack" },
|
|
1914
|
+
"machines": { "<machine>": { "today": <day>,
|
|
1915
|
+
"total": <totals> } },
|
|
1916
|
+
"placements": [<placement>, ...] } }
|
|
1330
1917
|
|
|
1331
1918
|
\`project\` is null outside a project; a platform with no run yet is null.
|
|
1332
1919
|
A bucket carries runs, failed, hits, misses, coldRuns, coldRunMs, hitRuns,
|
|
1333
1920
|
hitRunMs, timeSavedMs, firstRunAt and lastRunAt, plus lastColdBuildMs and
|
|
1334
|
-
lastPodsMs once the project has compiled or installed pods
|
|
1921
|
+
lastPodsMs once the project has compiled or installed pods, and
|
|
1922
|
+
offloadedRuns, offloadedRunMs and lastOffloadHost once a build machine
|
|
1923
|
+
compiled one. An offloaded run is a miss but not a cold run, so the cold
|
|
1924
|
+
average, time saved and build estimates stay local. Milliseconds are
|
|
1335
1925
|
integers.
|
|
1336
1926
|
|
|
1337
1927
|
HOW A RUN IS COUNTED (\`stats\`)
|
|
@@ -1353,7 +1943,8 @@ HOW A RUN IS COUNTED (\`stats\`)
|
|
|
1353
1943
|
is therefore an ESTIMATE and is printed as one. The file is
|
|
1354
1944
|
$STIM_HOME/stats.json (see \`guide lifecycle builds\`). Beside the
|
|
1355
1945
|
aggregates it keeps the last 10 successful runs per project, platform and
|
|
1356
|
-
outcome (hit or cold)
|
|
1946
|
+
outcome (hit or cold), plus the last 10 that created, adopted or cold-booted
|
|
1947
|
+
their device, that did not wait for another workspace's build: each
|
|
1357
1948
|
one's duration and per-phase durations. \`stim status\` estimates a running
|
|
1358
1949
|
build from them (see \`guide facts status\`); \`stats --json\` does not
|
|
1359
1950
|
print them.
|
|
@@ -1374,7 +1965,30 @@ HOW A RUN IS COUNTED (\`stats\`)
|
|
|
1374
1965
|
a hang. A project with no record yet gets \`(1m00s)\`, the elapsed alone,
|
|
1375
1966
|
and a warm run has no long phase to size. That read takes no lock and
|
|
1376
1967
|
ignores what it cannot read, so nothing about statistics can change a
|
|
1377
|
-
run's outcome
|
|
1968
|
+
run's outcome.
|
|
1969
|
+
|
|
1970
|
+
BUILD PLACEMENT (\`offload\`)
|
|
1971
|
+
While at least one build machine is paired, every run that compiles
|
|
1972
|
+
records where it built and why, in a placement: { at, project, platform,
|
|
1973
|
+
decision, reason, machine?, buildMs?, localEstimateMs?, failed? }.
|
|
1974
|
+
decision is "here", "offloaded", or "fell-back" (it tried a build machine
|
|
1975
|
+
and built here). reason is why: the run's \`placement:\` reason, such as
|
|
1976
|
+
"load 0.6/core, 1 of 3 build slots busy here" (auto keeps the build here
|
|
1977
|
+
while this Mac has room) or "offload.mode is off" (which prints no line),
|
|
1978
|
+
or for a fallback the cause, prefixed by the machine it concerns, such as
|
|
1979
|
+
"mini: no less loaded (load 1.2/core there, 0.6/core here)". buildMs is the compile here, or the offloaded build's
|
|
1980
|
+
total (offer, sync, build and fetch); localEstimateMs is this project's
|
|
1981
|
+
last cold build here before the run. A run that hits the cache or waits
|
|
1982
|
+
for another workspace's build compiles nothing and records none.
|
|
1983
|
+
placements lists them newest first: the last 100 from the last 7 days.
|
|
1984
|
+
Per machine, \`total\` keeps { offloaded, offloadedMs, savedMs, fallbacks,
|
|
1985
|
+
lastOffloadAt?, lastFallbackAt? } for good and \`today\` counts { offloaded,
|
|
1986
|
+
offloadedMs, savedMs, fallbacks } from the placements on this Mac's local
|
|
1987
|
+
calendar day. savedMs sums localEstimateMs minus buildMs over offloaded
|
|
1988
|
+
builds that had an estimate, so it is an ESTIMATE and is negative when the
|
|
1989
|
+
machine was slower. \`offload.today\` counts the day's placements by
|
|
1990
|
+
decision. A fallback counts against a machine only when the run knows
|
|
1991
|
+
which one: the machine that failed, or the only one paired.`
|
|
1378
1992
|
}
|
|
1379
1993
|
}
|
|
1380
1994
|
},
|
|
@@ -1398,10 +2012,33 @@ another worktree's bundler.
|
|
|
1398
2012
|
--wait, it does not wait for a device lease.
|
|
1399
2013
|
--remote expose Metro for a remote device
|
|
1400
2014
|
|
|
1401
|
-
Plain \`stim start\` is local and does not create a
|
|
2015
|
+
Plain \`stim start\` is local and does not create a tunnel. Remote intent
|
|
1402
2016
|
comes from \`start --remote\`, \`ios.remote\`, or \`android.remote\`. The
|
|
1403
2017
|
\`metro.tunnel\` setting selects the provider after remote intent exists.
|
|
1404
2018
|
|
|
2019
|
+
TAILNET-ONLY METRO
|
|
2020
|
+
Install Tailscale and sign in on this machine and the remote device or
|
|
2021
|
+
agent-device daemon's host, then select the mode explicitly:
|
|
2022
|
+
|
|
2023
|
+
stim settings set metro.tunnel tailscale --scope workspace
|
|
2024
|
+
stim start --remote
|
|
2025
|
+
|
|
2026
|
+
The device must be on the same tailnet, so the eas backend refuses it. "auto"
|
|
2027
|
+
never selects tailscale.
|
|
2028
|
+
Stim runs a foreground \`tailscale serve --https=<metro port>
|
|
2029
|
+
http://127.0.0.1:<metro port>\` process, never Funnel. It prints
|
|
2030
|
+
https://<tailnet-name>:<metro port> and \`status\` reports the recorded
|
|
2031
|
+
provider and URL. Stim reads the URL from the child's pipes, without a log
|
|
2032
|
+
file, and skips the public bundle probe for this recorded tunnel: this
|
|
2033
|
+
machine may not reach its own tailnet name.
|
|
2034
|
+
|
|
2035
|
+
\`stop\` and \`worktree remove\` stop only the recorded process after
|
|
2036
|
+
verifying its identity; ending that foreground session removes its route.
|
|
2037
|
+
A route Stim did not create is never touched. If the port already has a
|
|
2038
|
+
serve route, Tailscale refuses and Stim prints Tailscale's error message.
|
|
2039
|
+
\`tailscale serve status --json\` shows foreground sessions under the
|
|
2040
|
+
top-level Foreground key; plain \`tailscale serve status\` does not.
|
|
2041
|
+
|
|
1405
2042
|
BUNDLE WARMUP
|
|
1406
2043
|
After verifying Metro, \`stim ios\` and \`stim android\` prefetch the
|
|
1407
2044
|
platform's development bundle while native work continues. Expo supplies the
|
|
@@ -1429,7 +2066,7 @@ BUNDLE WARMUP
|
|
|
1429
2066
|
REMOTE DEVICE BACKENDS
|
|
1430
2067
|
Metro exposure and device selection are separate:
|
|
1431
2068
|
|
|
1432
|
-
stim start --remote prepare
|
|
2069
|
+
stim start --remote prepare remote Metro
|
|
1433
2070
|
stim ios --remote proxy use an agent-device daemon
|
|
1434
2071
|
stim android --remote eas create an EAS Simulator session
|
|
1435
2072
|
|
|
@@ -1440,6 +2077,12 @@ REMOTE DEVICE BACKENDS
|
|
|
1440
2077
|
requires AGENT_DEVICE_DAEMON_BASE_URL and
|
|
1441
2078
|
AGENT_DEVICE_DAEMON_AUTH_TOKEN. Stim creates no remote session for it.
|
|
1442
2079
|
|
|
2080
|
+
An iOS remote build compiles one simulator architecture, since xcodebuild
|
|
2081
|
+
cannot target the remote simulator itself: the proxy daemon's host
|
|
2082
|
+
architecture from its /health hostArch, and arm64 when the daemon does not
|
|
2083
|
+
report one, cannot be reached within 3 seconds, or runs on EAS, whose hosts
|
|
2084
|
+
are Apple silicon. The cache key ends in that architecture.
|
|
2085
|
+
|
|
1443
2086
|
The EAS backend needs eas-cli 21.6.0 or later and an account with EAS
|
|
1444
2087
|
Simulator access. An EAS session is billable. Stim prepares the app before
|
|
1445
2088
|
it creates or reconnects the session, so a failed build starts no session
|
|
@@ -1454,7 +2097,17 @@ IDEMPOTENT
|
|
|
1454
2097
|
A healthy dev server on the reserved port is a no-op: \`start\` prints the
|
|
1455
2098
|
facts with alreadyRunning: true and starts nothing. A foreign process holding
|
|
1456
2099
|
the reserved port moves the RESERVATION instead, so the project is never
|
|
1457
|
-
stranded on a port it can never use.
|
|
2100
|
+
stranded on a port it can never use. A port pinned with metro.port or
|
|
2101
|
+
STIM_METRO_PORT never moves: \`start\` refuses with STIM_BAD_ARG and names
|
|
2102
|
+
the holder before anything starts or the pin is reserved. This project's
|
|
2103
|
+
own Metro already on the pin is attached to. Changing the pin while this
|
|
2104
|
+
workspace's dev server runs refuses until stim stop; a supervisor that cannot
|
|
2105
|
+
be verified also refuses, until you stop it with the tool that started it.
|
|
2106
|
+
You can also unset or restore the pin. stim web follows
|
|
2107
|
+
the same rules when its page uses Metro.
|
|
2108
|
+
ios and android with --no-metro-check use the pin, then the recorded port,
|
|
2109
|
+
then 8081, without probing, reserving or writing the registry. An invalid
|
|
2110
|
+
pin still refuses with STIM_BAD_ARG. Release builds still skip Metro.
|
|
1458
2111
|
|
|
1459
2112
|
WHAT THE SUPERVISOR IS
|
|
1460
2113
|
One detached process per workspace. There is no machine-wide daemon, nothing
|
|
@@ -1505,13 +2158,56 @@ WHAT THE SUPERVISOR IS
|
|
|
1505
2158
|
connected app alone does not keep the server. It records a
|
|
1506
2159
|
supervisor_idle_stopped line in metro.ndjson and devServerStop in
|
|
1507
2160
|
state.json, so \`status\` shows "stopped (idle)" rather than a crash.
|
|
1508
|
-
Devices stay booted
|
|
2161
|
+
Devices stay booted, unless devices.idleShutdownMinutes is set: then the
|
|
2162
|
+
idle stop first shuts down the workspace's owned devices idle for the
|
|
2163
|
+
shorter of the two (\`guide lifecycle budget\`). The next \`stim start\`
|
|
2164
|
+
starts it again and clears the
|
|
1509
2165
|
record, also when it reuses a dev server that another process started. A
|
|
1510
2166
|
Debug \`ios\` or \`android\` run starts it the same way and reports
|
|
1511
2167
|
devServer.reason "stopped (idle)". The setting
|
|
1512
2168
|
is read when \`start\` spawns the supervisor; a value that is not a whole
|
|
1513
2169
|
number of 0 or more is refused, as \`stim settings set\` refuses it.
|
|
1514
2170
|
|
|
2171
|
+
WHY IT STOPPED: a supervisor that stops its dev server writes a
|
|
2172
|
+
supervisor_stopped line (supervisor_idle_stopped for an idle stop) whose
|
|
2173
|
+
stop field and message give the cause, and records the same cause as
|
|
2174
|
+
devServerStop in state.json. A dev server that failed to start is
|
|
2175
|
+
supervisor_failed instead. Plain \`status\` prints it after "not running"
|
|
2176
|
+
while the workspace keeps its port (\`stim stop\` releases the port, so its
|
|
2177
|
+
own stops show only in the log).
|
|
2178
|
+
requested a Stim command stopped it and says which: stim stop,
|
|
2179
|
+
stim worktree remove, stim gc --delete, stim start
|
|
2180
|
+
--reset-cache, or budget reclaim (with the workspace whose
|
|
2181
|
+
ios, android or start run reclaimed it)
|
|
2182
|
+
signal a SIGTERM or SIGINT with no Stim stop request recorded,
|
|
2183
|
+
so it likely came from outside Stim (level warn)
|
|
2184
|
+
server-exited the dev server exited on its own (level error). Expo CLI
|
|
2185
|
+
also exits 0 when it gets SIGTERM, so "exit code 0" with
|
|
2186
|
+
no request usually means something outside Stim signalled
|
|
2187
|
+
the Expo process
|
|
2188
|
+
idle metro.idleStopMinutes, above
|
|
2189
|
+
A supervisor killed with SIGKILL, or one that crashed, cannot write
|
|
2190
|
+
anything; neither can one whose state file was not writable. The next
|
|
2191
|
+
supervisor logs supervisor_vanished (level warn) for it, and until then
|
|
2192
|
+
\`status\` reports metro.lastStop.reason "vanished".
|
|
2193
|
+
|
|
2194
|
+
WATCHMAN ROOTS: a Metro that uses watchman registers a watch root in the
|
|
2195
|
+
shared watchman daemon and never removes it. Bare React Native Metro uses
|
|
2196
|
+
watchman whenever it is installed; Expo SDK 56 and later use it only when
|
|
2197
|
+
metro.config.js sets resolver.useWatchman to true. When the supervisor stops
|
|
2198
|
+
its dev server, it removes with \`watchman watch-del\` a root that appeared
|
|
2199
|
+
after it started, contains the workspace and carried this Metro's
|
|
2200
|
+
subscription, once no other client subscribes to it or has a trigger on it.
|
|
2201
|
+
It logs watchman_root_removed, or watchman_root_kept with the reason, in
|
|
2202
|
+
metro.ndjson. It never starts the daemon and leaves every other root alone,
|
|
2203
|
+
including those jest registers.
|
|
2204
|
+
A supervisor that dies without stopping leaves its root: remove it with
|
|
2205
|
+
\`watchman watch-del <root>\`. Removing a root stops its recrawls; the
|
|
2206
|
+
daemon's memory footprint shrinks only when the daemon restarts, which drops
|
|
2207
|
+
the watches of every other client. \`stim gc --cache watchman\` reports the
|
|
2208
|
+
daemon's footprint, roots and clients, and with --delete shuts it down only
|
|
2209
|
+
when no client is connected (\`guide cleanup memory\`).
|
|
2210
|
+
|
|
1515
2211
|
ENVIRONMENT: the supervisor -- and through it the dev server, including a
|
|
1516
2212
|
metro.config.js evaluated inside the expo child -- inherits the environment
|
|
1517
2213
|
of the \`start\` call that SPAWNED it. A later \`start\` that finds a healthy
|
|
@@ -1572,7 +2268,14 @@ If Stim is not installed globally, replace stim with npx stim.
|
|
|
1572
2268
|
get <label> allocates on first use and prints only the number to stdout.
|
|
1573
2269
|
Repeated calls reuse it, even while a server is listening. Labels start with
|
|
1574
2270
|
a letter and contain up to 64 letters, digits, underscores, or hyphens.
|
|
1575
|
-
metro is reserved; use stim start and stim stop for managed Metro
|
|
2271
|
+
metro is reserved; use stim start and stim stop for managed Metro, and
|
|
2272
|
+
metro.port or STIM_METRO_PORT to choose its number (guide settings).
|
|
2273
|
+
Changing the pin while this workspace's dev server runs refuses until
|
|
2274
|
+
stim stop; an unverified supervisor also blocks the change until you stop it with the tool that started it. A foreign holder
|
|
2275
|
+
of the pin is refused before anything starts; this project's own Metro on
|
|
2276
|
+
it is attached to. stim web follows these rules when its page uses Metro.
|
|
2277
|
+
ios and android with --no-metro-check use the pin, then the recorded port,
|
|
2278
|
+
then 8081, without probing or reserving; an invalid pin still refuses.
|
|
1576
2279
|
|
|
1577
2280
|
New allocations scan TCP ports 8900-8999. They skip registry reservations
|
|
1578
2281
|
and existing listeners, announcing occupied ports and upward retries on
|
|
@@ -1653,10 +2356,15 @@ The only exit-1 paths are a malformed query and no project.
|
|
|
1653
2356
|
Device collectors and build records carry their named slot. Use --slot phone
|
|
1654
2357
|
for that slot's timeline; its launch marker cannot hide a sibling's errors.
|
|
1655
2358
|
Untagged legacy records belong to default. Metro/client records are shared and
|
|
1656
|
-
usually untagged
|
|
2359
|
+
usually untagged, so every --slot query includes them, windowed by that slot's
|
|
2360
|
+
own launch and by bundles of the platforms it launched. When another slot's
|
|
2361
|
+
relaunch hides a Metro error from the unfiltered query, --slot <name> for the
|
|
2362
|
+
slot that printed it still shows it.
|
|
1657
2363
|
|
|
1658
2364
|
FLAGS
|
|
1659
|
-
--slot <name> only this slot's records
|
|
2365
|
+
--slot <name> only this slot's records plus, once it has launched, the
|
|
2366
|
+
shared untagged Metro and app client records (not the web
|
|
2367
|
+
page's); default also includes untagged legacy records
|
|
1660
2368
|
--source <s...> metro, client, device, build, agent (one or more), or all.
|
|
1661
2369
|
An unknown value is REJECTED rather than quietly matching
|
|
1662
2370
|
nothing.
|
|
@@ -1670,7 +2378,12 @@ FLAGS
|
|
|
1670
2378
|
owned Chrome's device errors (platform web: failed
|
|
1671
2379
|
requests, browser errors).
|
|
1672
2380
|
Capped at 20 printed records.
|
|
1673
|
-
--follow keep streaming until interrupted (Ctrl+C is exit 0)
|
|
2381
|
+
--follow keep streaming until interrupted (Ctrl+C is exit 0). Closing
|
|
2382
|
+
its stdout also ends it with exit 0; on Linux, so does the
|
|
2383
|
+
exit of the process that started it when stdout is a pipe.
|
|
2384
|
+
It notices within a few seconds even when nothing new
|
|
2385
|
+
arrives, except a closed stdout pipe on Linux whose starter
|
|
2386
|
+
is still running, which it notices at the next record.
|
|
1674
2387
|
--json the raw records, one per line, so stdout is valid NDJSON.
|
|
1675
2388
|
ZERO matches is ZERO bytes on stdout (an empty NDJSON
|
|
1676
2389
|
stream), exit 0 -- parse stdout line by line, never as one
|
|
@@ -1720,9 +2433,19 @@ FLAGS
|
|
|
1720
2433
|
errors are reported -- and nothing else. A failed attempt's own summary
|
|
1721
2434
|
and details land at or after its marker, so they stay reported.
|
|
1722
2435
|
a LAUNCH marker (src build, written before \`ios\` / \`android\` attempts
|
|
1723
|
-
launch) resets EVERYTHING except the web page's records. It
|
|
1724
|
-
tool call so an immediate native crash is not hidden by a
|
|
1725
|
-
after the process died.
|
|
2436
|
+
launch) resets EVERYTHING in its slot except the web page's records. It
|
|
2437
|
+
precedes the tool call so an immediate native crash is not hidden by a
|
|
2438
|
+
marker written after the process died. Metro and client output names no
|
|
2439
|
+
device, so in the unfiltered query the newest launch of any slot resets
|
|
2440
|
+
it, and the newest bundle of any platform resets Metro errors; with
|
|
2441
|
+
--slot <name>, that slot's newest launch and the newest bundle of a
|
|
2442
|
+
platform it launched do. Metro records that name their platform (bare
|
|
2443
|
+
React Native bundle records; the "iOS Bundled" / "Android Bundled" /
|
|
2444
|
+
"RSC(iOS) Bundled" tag Expo prints for bundle markers, so a "DOM
|
|
2445
|
+
Bundled" line does not close a native slot's window) follow only that
|
|
2446
|
+
platform, and a slot query leaves out iOS or Android ones when it never
|
|
2447
|
+
launched that platform. A single slot that runs both iOS and Android still shares one
|
|
2448
|
+
window for untagged Metro errors.
|
|
1726
2449
|
a PAGE-LOAD marker (the web_navigation record \`stim web\` writes for each
|
|
1727
2450
|
load of the page's top-level document) resets only the page's records,
|
|
1728
2451
|
those with platform web. It is written when the load starts, so a
|
|
@@ -1765,9 +2488,12 @@ FLAGS
|
|
|
1765
2488
|
In --follow mode the marker window is dropped -- every error arriving from
|
|
1766
2489
|
then on is by definition after the last marker seen.
|
|
1767
2490
|
|
|
1768
|
-
\`stim status\` reports the same
|
|
1769
|
-
logs.errorsSinceMarker
|
|
1770
|
-
|
|
2491
|
+
\`stim status\` reports the errors of the same query and scope per
|
|
2492
|
+
workspace, as logs.errorsSinceMarker, so the two never disagree about
|
|
2493
|
+
whether this workspace is failing. It counts failures as the Stim Desktop and
|
|
2494
|
+
phone logs list them: a Metro bundle failure is one error, although it is a
|
|
2495
|
+
marker line, the error line and a failed bundle response in the log, so the
|
|
2496
|
+
count can be lower than the records this command prints.
|
|
1771
2497
|
|
|
1772
2498
|
THE RECORD
|
|
1773
2499
|
{ ts, src, level, msg } always. ts is epoch milliseconds; src is one of
|
|
@@ -1876,9 +2602,9 @@ WHAT WRITES WHAT
|
|
|
1876
2602
|
console-only; missing reports or symbols are not proof
|
|
1877
2603
|
of a healthy app. No symbol downloads are performed.
|
|
1878
2604
|
Android may keep a crashed Java PID alive behind its
|
|
1879
|
-
crash dialog.
|
|
1880
|
-
|
|
1881
|
-
|
|
2605
|
+
crash dialog. Fix the crash, then rerun stim android,
|
|
2606
|
+
which force-stops that process before launching. Metro
|
|
2607
|
+
reload alone cannot recover it.
|
|
1882
2608
|
|
|
1883
2609
|
The connection refusal \`TCP Conn ... Failed :
|
|
1884
2610
|
error 0:61 [61]\` (61 is ECONNREFUSED) is not even
|
|
@@ -1925,13 +2651,17 @@ WHAT WRITES WHAT
|
|
|
1925
2651
|
archive rather than a stream. Streaming with full fidelity needs
|
|
1926
2652
|
libimobiledevice or pymobiledevice3, which are third-party installs Stim
|
|
1927
2653
|
does not require. See appandflow/stim#179.
|
|
1928
|
-
agent
|
|
1929
|
-
|
|
2654
|
+
agent agent input on the owned Chrome page is in
|
|
2655
|
+
web.ndjson (\`guide web\`). For simulators and
|
|
2656
|
+
emulators, no file: each query reads agent-device's
|
|
2657
|
+
session records (~/.agent-device/sessions, or
|
|
1930
2658
|
AGENT_DEVICE_STATE_DIR) for this workspace's owned
|
|
1931
2659
|
simulators and emulators, read-only. An action is
|
|
1932
2660
|
info (msg is agent-device's summary, e.g. "Tapped
|
|
1933
2661
|
(201, 731)"; event agent_action with command, session,
|
|
1934
|
-
deviceId and details
|
|
2662
|
+
deviceId and details, and startedAt when agent-device
|
|
2663
|
+
logged the command's start: ts is when it finished,
|
|
2664
|
+
after any --settle wait); a failed command is error
|
|
1935
2665
|
(event agent_failed). A plain query includes them;
|
|
1936
2666
|
--errors only with --source agent or all. iOS sessions
|
|
1937
2667
|
match a simulator through their runner.log, Android
|
|
@@ -2128,8 +2858,12 @@ Branch on the code, never on the message.`,
|
|
|
2128
2858
|
On a release cache hit Stim regenerates this workspace's JS bundle into a
|
|
2129
2859
|
COPY of the cached artifact before installing it -- \`ios --configuration
|
|
2130
2860
|
Release\` into a copy of the .app, \`android --variant ...Release\` into a
|
|
2131
|
-
copy of the APK.
|
|
2132
|
-
|
|
2861
|
+
copy of the APK. An iOS app that sets React Native's RCTUseAssetCatalog
|
|
2862
|
+
Info.plist key also gets its RNAssets.bundle image catalog recompiled with
|
|
2863
|
+
actool from the new bundle. When any step of that swap fails (reading the
|
|
2864
|
+
app's Info.plist, the bundle command, hermesc, actool, the expo-updates
|
|
2865
|
+
manifest refresh, the re-sign, zipalign, apksigner), the run does NOT
|
|
2866
|
+
install the
|
|
2133
2867
|
cached artifact -- its baked-in JS is the builder's, not yours -- and does
|
|
2134
2868
|
NOT fail: it prints a \`swap failed at <step>: ... --
|
|
2135
2869
|
building fresh instead\` note on stderr and falls back to a full build. If
|
|
@@ -2243,6 +2977,37 @@ Branch on the code, never on the message.`,
|
|
|
2243
2977
|
Legacy inline \`deletionClaim\` fields in config.json have no process
|
|
2244
2978
|
identity and require separate manual inspection; \`stim guide lifecycle pool\`
|
|
2245
2979
|
describes their field-only recovery.`
|
|
2980
|
+
},
|
|
2981
|
+
STIM_MACOS_OWNER_UNVERIFIED: {
|
|
2982
|
+
summary: "the macOS app owner cannot be verified; no signal is sent",
|
|
2983
|
+
body: () => `STIM_MACOS_OWNER_UNVERIFIED
|
|
2984
|
+
The recorded macOS process identity is unavailable or its workspace record
|
|
2985
|
+
is malformed. Stim sends no signal to an owner it cannot verify. Inspect the
|
|
2986
|
+
named process and workspace record before repairing it; do not replace a PID
|
|
2987
|
+
or token with another running app. Retry stim stop once identity inspection
|
|
2988
|
+
works. See stim guide macos.`
|
|
2989
|
+
},
|
|
2990
|
+
STIM_OFFLOAD_REFUSED: {
|
|
2991
|
+
summary: "the selected build machine cannot build this app",
|
|
2992
|
+
body: () => `STIM_OFFLOAD_REFUSED
|
|
2993
|
+
|
|
2994
|
+
A named --build-machine selection is strict. The message names the machine
|
|
2995
|
+
and why it cannot take or finish the build: not configured or paired, approval
|
|
2996
|
+
pending or denied, unreachable or changed pinned identity, incompatible
|
|
2997
|
+
toolchain/runtime/CPU, low disk, busy, sync/build failure, artifact failure,
|
|
2998
|
+
or a checkout that changed while it built. Unsupported build kinds also refuse.
|
|
2999
|
+
No local xcodebuild, Gradle or SwiftPM compile or alternate machine follows a
|
|
3000
|
+
refusal. Prebuild and pod install still run on this Mac before a named offload.
|
|
3001
|
+
Unlisted or unpaired names refuse at setup before consulting the cache, without
|
|
3002
|
+
a build record or failed-run stats. A listed paired name with a cache hit needs
|
|
3003
|
+
no build and does not contact the selected machine.
|
|
3004
|
+
|
|
3005
|
+
Run stim doctor --fix to ask for build access if not paired. Check
|
|
3006
|
+
stim settings get offload.machines. A person on the worker finds the id with
|
|
3007
|
+
stim-server devices and approves this Mac with stim-server devices grant <id> --build.
|
|
3008
|
+
Rerun with --build-machine auto for normal placement and local fallback, or
|
|
3009
|
+
--build-machine local to keep the build here.
|
|
3010
|
+
`
|
|
2246
3011
|
},
|
|
2247
3012
|
STIM_CLAIM_UNAVAILABLE: {
|
|
2248
3013
|
summary: "a process identity or warm claim store is unavailable, so the protected operation refuses",
|
|
@@ -2574,13 +3339,14 @@ so a Debug run on one is wired to a LAN origin instead of localhost.`,
|
|
|
2574
3339
|
"this project's sim is X, but --device-type asked for Y"
|
|
2575
3340
|
The project already owns a simulator of a different model, and Stim will
|
|
2576
3341
|
not silently boot a different one. Reap it (\`worktree remove\`, or
|
|
2577
|
-
\`gc --delete\`) and run \`stim ios\` again to create the requested model
|
|
2578
|
-
|
|
3342
|
+
\`gc --delete\`) and run \`stim ios\` again to create the requested model,
|
|
3343
|
+
which loses the old sim's app state, or pass \`--slot <name>\` to create it
|
|
3344
|
+
beside the current one. The \`remedy\` field in \`--json\` names the same
|
|
3345
|
+
choices.
|
|
2579
3346
|
|
|
2580
3347
|
"this project's sim runs iOS X, but --runtime asked for Y"
|
|
2581
3348
|
The same refusal for an explicit \`--runtime\` that names another installed
|
|
2582
|
-
iOS version than the project's sim runs
|
|
2583
|
-
\`--slot <name>\` to create one on the requested runtime beside it. The
|
|
3349
|
+
iOS version than the project's sim runs, with the same remedy. The
|
|
2584
3350
|
ios.runtime setting alone never refuses; it applies at creation.
|
|
2585
3351
|
|
|
2586
3352
|
"this project's emulator uses device profile X, but Y was requested"
|
|
@@ -2719,6 +3485,18 @@ so a Debug run on one is wired to a LAN origin instead of localhost.`,
|
|
|
2719
3485
|
holds one remote session, and Stim will not end the recorded one to make
|
|
2720
3486
|
room -- it may be mid-run for whoever started it. Run \`stim stop\` for this
|
|
2721
3487
|
workspace, then re-run with the platform you want. Nothing was created here.`
|
|
3488
|
+
},
|
|
3489
|
+
STIM_REMOTE_DEVICE_MISMATCH: {
|
|
3490
|
+
summary: "the recorded EAS session runs another model than --device-type asks for; stop, then rerun",
|
|
3491
|
+
body: () => `STIM_REMOTE_DEVICE_MISMATCH
|
|
3492
|
+
\`stim ios --remote eas --device-type <name>\` found this workspace's
|
|
3493
|
+
recorded EAS Simulator session still running another model, or the model
|
|
3494
|
+
EAS chose when no --device-type was given. EAS cannot change a running
|
|
3495
|
+
session's model, and Stim will not end the recorded session to make room --
|
|
3496
|
+
it may be mid-run for whoever started it. Run \`stim stop\` for this
|
|
3497
|
+
workspace, then rerun with the model you want. A recorded session that has
|
|
3498
|
+
already ended is replaced on the requested model instead, and without
|
|
3499
|
+
--device-type a live one is reused as it is. Nothing was created here.`
|
|
2722
3500
|
},
|
|
2723
3501
|
STIM_REMOTE_SESSION_STATE: {
|
|
2724
3502
|
summary: "the EAS session was created but its state could not be recorded, so Stim stopped it",
|
|
@@ -2748,6 +3526,7 @@ so a Debug run on one is wired to a LAN origin instead of localhost.`,
|
|
|
2748
3526
|
The gate that proves a tunnel still reaches THIS workspace's Metro failed --
|
|
2749
3527
|
before a session or a build, whether the tunnel is Expo's own, one Stim
|
|
2750
3528
|
started (metro.tunnel: cloudflared/ngrok/auto), or a named metro.publicUrl.
|
|
3529
|
+
A recorded tailscale tunnel skips this public probe (\`guide metro\`).
|
|
2751
3530
|
The usual cause: the tunnel was built for a port this workspace no longer
|
|
2752
3531
|
holds (a stale one survived a \`stop\`/\`start\` that reserved a different
|
|
2753
3532
|
port), and it now serves ANOTHER workspace's dev server -- healthy, and
|
|
@@ -2812,6 +3591,11 @@ so a Debug run on one is wired to a LAN origin instead of localhost.`,
|
|
|
2812
3591
|
relaunches that app on that device with this workspace's Metro port and loses
|
|
2813
3592
|
in-memory state. Keep the existing --session flag and verify afterward.
|
|
2814
3593
|
|
|
3594
|
+
ADB COULD NOT LIST OR RESTORE THE REVERSE. Before an Android reload Stim
|
|
3595
|
+
checks the emulator's adb reverse for the Metro port. When adb cannot list
|
|
3596
|
+
or re-apply it, nothing was reloaded; the remedy names the adb reverse
|
|
3597
|
+
command to run before reloading again.
|
|
3598
|
+
|
|
2815
3599
|
MORE THAN ONE MATCHING PEER IS NOT A FAILURE. A workspace Metro serves one
|
|
2816
3600
|
app, so several matching peers are that app on several devices. Stim reloads
|
|
2817
3601
|
every one of them and reports the count in the facts as targets.`
|
|
@@ -2935,10 +3719,22 @@ captured" (in metro.ndjson, bare RN)
|
|
|
2935
3719
|
The command refused before doing anything: an unusable --wait value, a known
|
|
2936
3720
|
setting with the wrong type ("Invalid <key> setting <value>. Expected <shape>."
|
|
2937
3721
|
-- \`guide settings\` names the type each key takes), an invalid
|
|
2938
|
-
Metro tunnel setting,
|
|
3722
|
+
Metro tunnel setting, \`stim macos\` with macos.product or macos.infoPlist
|
|
3723
|
+
unset (set both explicitly in .stim.json; see \`stim guide macos\`),
|
|
3724
|
+
\`stim macos --remote eas|proxy|auto\` (name a hosting Mac instead),
|
|
3725
|
+
an invalid android.dataPartitionSizeGb value, an unsafe
|
|
2939
3726
|
android.avdConfig key or fragment, a malformed ios.signingIdentity,
|
|
2940
|
-
ios.signingIdentitySha1 or ios.lanHost value,
|
|
2941
|
-
|
|
3727
|
+
ios.signingIdentitySha1 or ios.lanHost value, a metro.port or
|
|
3728
|
+
STIM_METRO_PORT that another workspace reserves or another process holds,
|
|
3729
|
+
a changed pin while this workspace's dev server runs or its supervisor
|
|
3730
|
+
cannot be verified,
|
|
3731
|
+
\`--device\` with an empty
|
|
3732
|
+
serial or UDID, \`--device\` together with \`--remote\`, \`ios --runtime\`
|
|
3733
|
+
on a remote run (\`--remote\` or ios.remote; the remote backend picks the
|
|
3734
|
+
iOS version), \`ios --device-type\` on the proxy backend or with an
|
|
3735
|
+
eas-cli older than 22.2.0 on the eas backend, \`android --system-image\` or
|
|
3736
|
+
\`--device-profile\` on a remote run (\`--remote\` or android.remote), a
|
|
3737
|
+
working directory
|
|
2942
3738
|
with no package.json above it, or one whose nearest package.json does not
|
|
2943
3739
|
parse or depends on neither react-native nor expo, so the directory is not
|
|
2944
3740
|
an app (the refusal names that package.json and says which of the two it
|
|
@@ -2958,7 +3754,10 @@ captured" (in metro.ndjson, bare RN)
|
|
|
2958
3754
|
ios.deviceType, ios.runtime, android.systemImage or android.deviceProfile
|
|
2959
3755
|
setting is checked the same way, and the check applies
|
|
2960
3756
|
even when this workspace ALREADY owns a device, so a name that could never
|
|
2961
|
-
create anything is caught rather than left to a later run.
|
|
3757
|
+
create anything is caught rather than left to a later run. When the bad
|
|
3758
|
+
value came from a settings layer rather than a flag, the message also names
|
|
3759
|
+
the layer (workspace, repo, committed, or machine) it is set at, since all
|
|
3760
|
+
four settings are readable from the machine layer too. The
|
|
2962
3761
|
\`pixel_fold\` and \`resizable\` profiles on a system image without
|
|
2963
3762
|
foldable support (SupportPixelFold in its advancedFeatures.ini) refuse the
|
|
2964
3763
|
same way, since the emulator quits on boot; the remedy names an installed
|
|
@@ -2972,6 +3771,12 @@ captured" (in metro.ndjson, bare RN)
|
|
|
2972
3771
|
is on stdout, except \`logs --json\`, whose stdout stays empty NDJSON; the
|
|
2973
3772
|
refusal is on stderr. \`stop\` outside a project refuses rather than
|
|
2974
3773
|
reporting that nothing was running.
|
|
3774
|
+
For a changed Metro pin, run stim stop before retrying, or unset or restore
|
|
3775
|
+
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
|
|
3776
|
+
own Metro on the pin is attached to. stim web follows the same rules when
|
|
3777
|
+
its page uses Metro. ios and android with --no-metro-check use the pin,
|
|
3778
|
+
then the recorded port, then 8081, without probing or reserving; an invalid
|
|
3779
|
+
pin still refuses.
|
|
2975
3780
|
These errors are caught before the port is reserved and before any build or
|
|
2976
3781
|
device work, so nothing was started. The one listing they need
|
|
2977
3782
|
(\`simctl list runtimes\`, the SDK's system-images directory, \`avdmanager
|
|
@@ -3085,6 +3890,16 @@ captured" (in metro.ndjson, bare RN)
|
|
|
3085
3890
|
Matching this workspace's port or directory does not authorize cleanup,
|
|
3086
3891
|
and stop has no override for process ownership.
|
|
3087
3892
|
|
|
3893
|
+
"port <n> is in use by <holder>; stim start will choose a free port" (status)
|
|
3894
|
+
A note, not a problem: no supervisor of this workspace runs, and
|
|
3895
|
+
\`stim start\` reserves a free port when its own is taken. The holder's pid
|
|
3896
|
+
and directory are in \`status --json\` under metro.heldBy.
|
|
3897
|
+
|
|
3898
|
+
"port <n> is in use by <holder>, so this workspace's Metro cannot serve on
|
|
3899
|
+
it" (status)
|
|
3900
|
+
This workspace's supervisor runs but another process answers its port.
|
|
3901
|
+
Run \`stim stop\`, then \`stim start\`, which reserves a free port.
|
|
3902
|
+
|
|
3088
3903
|
"stop refusing to signal supervisor pid <n>: ..." (stop)
|
|
3089
3904
|
The records disagree, the saved OS identity is unavailable, or it records a
|
|
3090
3905
|
port this project did not reserve. If process inspection is denied, retry
|
|
@@ -3138,6 +3953,10 @@ not on any remote" (worktree remove)
|
|
|
3138
3953
|
so per file -- those files die with the worktree either way, and a lockfile
|
|
3139
3954
|
change anyone meant would have been committed. ONE other dirty path and the
|
|
3140
3955
|
whole set is refused, churn included, so this never eats real work.
|
|
3956
|
+
Untracked \`.watchman-cookie-*\` files, which watchman writes into the roots
|
|
3957
|
+
it watches, are not work either: \`worktree remove\` deletes them itself and
|
|
3958
|
+
ignores them when it counts dirty paths. Any other untracked file still
|
|
3959
|
+
refuses.
|
|
3141
3960
|
When it does refuse, the refusal PRINTS THE DIRTY PATHS, and the restore
|
|
3142
3961
|
command under it carries those same paths: run it as printed rather than
|
|
3143
3962
|
reaching for --force.
|
|
@@ -3268,9 +4087,9 @@ not on any remote" (worktree remove)
|
|
|
3268
4087
|
A line such as \`npm warn exec ... will be installed\` is normal when using
|
|
3269
4088
|
the no-install form.
|
|
3270
4089
|
|
|
3271
|
-
"Unsupported engine" or a syntax error before Stim starts
|
|
3272
|
-
Stim requires Node 22.12.0 or later.
|
|
3273
|
-
|
|
4090
|
+
"Unsupported engine" from npm, or a syntax error before Stim starts
|
|
4091
|
+
Stim requires Node 22.12.0 or later. Stim itself refuses an older Node
|
|
4092
|
+
with STIM_NODE_UNSUPPORTED; see that section.
|
|
3274
4093
|
|
|
3275
4094
|
|
|
3276
4095
|
"Found no free Metro port between ..."
|
|
@@ -3353,6 +4172,30 @@ not on any remote" (worktree remove)
|
|
|
3353
4172
|
Expo build-cache provider would each use a different store. Set the named
|
|
3354
4173
|
variable to an absolute path, or unset it to use the default. Metro and the
|
|
3355
4174
|
cache provider, which cannot refuse, ignore a relative value with a warning.`
|
|
4175
|
+
},
|
|
4176
|
+
STIM_NODE_UNSUPPORTED: {
|
|
4177
|
+
summary: "stim or stim-server started on a Node older than 22.12.0, often a project pin",
|
|
4178
|
+
body: () => `STIM_NODE_UNSUPPORTED ("Stim needs Node <floor> or later; this is Node <version> at <path>")
|
|
4179
|
+
stim and stim-server refuse before loading anything else when the Node that
|
|
4180
|
+
runs them is older than their engines floor. Both start through
|
|
4181
|
+
\`#!/usr/bin/env node\`, so the working directory can choose that Node: asdf,
|
|
4182
|
+
mise and Volta's node shim follow the project's .nvmrc, .node-version,
|
|
4183
|
+
.tool-versions or package.json, and nvm and fnm do when a shell hook
|
|
4184
|
+
switches versions on cd. A Stim installed with Volta keeps the Node it was
|
|
4185
|
+
installed with. Run Stim with a supported Node for that one command:
|
|
4186
|
+
|
|
4187
|
+
ASDF_NODEJS_VERSION=<version> stim <command>
|
|
4188
|
+
mise exec node@<version> -- stim <command>
|
|
4189
|
+
volta run --node <version> stim <command>
|
|
4190
|
+
fnm exec --using=<version> stim <command>
|
|
4191
|
+
nvm exec --silent <version> stim <command>
|
|
4192
|
+
|
|
4193
|
+
<version> is 22.12.0 or later. asdf, mise, fnm and nvm keep global packages
|
|
4194
|
+
per Node version, so when Stim was installed with npm under one of them, use
|
|
4195
|
+
the version it was installed with; asdf names it when it prints "No version
|
|
4196
|
+
is set for command stim" instead of running Stim. When that version is
|
|
4197
|
+
older than 22.12.0, first install Stim with npm under a supported version.
|
|
4198
|
+
The tools Stim starts inherit the override.`
|
|
3356
4199
|
}
|
|
3357
4200
|
}
|
|
3358
4201
|
},
|
|
@@ -3463,6 +4306,17 @@ ambiguous no-platform request. Every reload goes over this workspace's Metro
|
|
|
3463
4306
|
websocket, on both platforms. It never reopens a development-client URL,
|
|
3464
4307
|
because that restarts the app rather than reloading its JavaScript.
|
|
3465
4308
|
|
|
4309
|
+
An Android reload first lists \`adb reverse\` on each live owned emulator
|
|
4310
|
+
launched against this Metro and re-applies the Metro port's reverse where it is
|
|
4311
|
+
missing. adb ties a reverse to its transport, and a transport that drops and
|
|
4312
|
+
reconnects comes back without it and without any error, which leaves the app
|
|
4313
|
+
cut off from Metro and Fast Refresh. After restoring one, Stim waits up to 8
|
|
4314
|
+
seconds, sending nothing, until the app on every emulator it checked is
|
|
4315
|
+
connected again, then sends the reload once. A dev server that cannot name
|
|
4316
|
+
its clients (bare React Native) gets a fixed 2.5-second wait instead. \`reverseRestored\` in the
|
|
4317
|
+
facts names the serials it restored. \`stim status\` reports the same loss as
|
|
4318
|
+
the android-reverse-missing issue.
|
|
4319
|
+
|
|
3466
4320
|
How the message is addressed depends on the dev server, and \`strategy\` in
|
|
3467
4321
|
the facts reports which you got. Where Metro can name its clients, Stim
|
|
3468
4322
|
addresses every peer matching the platform and reports \`metro-websocket\`. A
|
|
@@ -3508,9 +4362,10 @@ If the app is stuck before loading its first bundle, restart its process with
|
|
|
3508
4362
|
the printed force-stop and launcher commands. Foregrounding the same process
|
|
3509
4363
|
does not restart initialization. Confirm the bundle request and expected UI.
|
|
3510
4364
|
|
|
3511
|
-
Debug Android launches on an owned emulator
|
|
3512
|
-
|
|
3513
|
-
emulators, remote targets, and physical devices keep the
|
|
4365
|
+
Debug Android launches on an owned emulator that the run creates, or whose
|
|
4366
|
+
adoption from the pool it finishes, get up to 60 seconds for bundle loading;
|
|
4367
|
+
the verify phase names that budget. Existing emulators, remote targets, and physical devices keep the
|
|
4368
|
+
20-second budget.
|
|
3514
4369
|
Bundle delivery still requires the usual stability or app readiness check;
|
|
3515
4370
|
an observed fatal error ends verification without waiting for the deadline.
|
|
3516
4371
|
|
|
@@ -3590,7 +4445,11 @@ WAITING FOR A CHANGE
|
|
|
3590
4445
|
state, and recomputes every 30 seconds as a fallback. A log append updates
|
|
3591
4446
|
only the log error count and device activity, no sooner than 15 seconds
|
|
3592
4447
|
after the previous refresh. Ctrl+C, SIGTERM or closing its stdout ends it
|
|
3593
|
-
with exit 0.
|
|
4448
|
+
with exit 0. On Linux, so does the exit of the process that started it
|
|
4449
|
+
when its stdout is a pipe. It notices within a few seconds even when
|
|
4450
|
+
nothing changes, except a closed stdout pipe on Linux whose starter is
|
|
4451
|
+
still running, which it notices at the next change.
|
|
4452
|
+
Without --json it reprints the human view on change.`,
|
|
3594
4453
|
sections: {
|
|
3595
4454
|
eas: {
|
|
3596
4455
|
summary: "download a matching EAS development build; explicit profile, costs, cache and miss remedies",
|
|
@@ -3781,7 +4640,7 @@ result as proof instead of requiring an unrelated screenshot.`
|
|
|
3781
4640
|
column:
|
|
3782
4641
|
|
|
3783
4642
|
branch budget build cache caches carry
|
|
3784
|
-
checkout deps device
|
|
4643
|
+
checkout daemons deps device
|
|
3785
4644
|
devices error failed findings fingerprint gems
|
|
3786
4645
|
install installs ip.txt lan launch lease
|
|
3787
4646
|
lock log
|
|
@@ -3790,7 +4649,7 @@ result as proof instead of requiring an unrelated screenshot.`
|
|
|
3790
4649
|
result
|
|
3791
4650
|
services
|
|
3792
4651
|
setting settings setup state stats stop
|
|
3793
|
-
storage swap verify version workspace
|
|
4652
|
+
storage swap verify version workspace worktrees
|
|
3794
4653
|
|
|
3795
4654
|
\`app\` and \`compilation cache\` join them in the stdout block a successful
|
|
3796
4655
|
run ends with. When a native build runs, its compilation-cache result is
|
|
@@ -3901,6 +4760,16 @@ result as proof instead of requiring an unrelated screenshot.`
|
|
|
3901
4760
|
is never touched: \`Reclaimed the environment; the working tree stays (it
|
|
3902
4761
|
is the source checkout).\`
|
|
3903
4762
|
|
|
4763
|
+
\`gc\` names each slow step on stderr as it starts, in every mode; its
|
|
4764
|
+
report stays on stdout, and with \`--json\` stdout carries only the payload:
|
|
4765
|
+
|
|
4766
|
+
caches measuring 18 shared caches
|
|
4767
|
+
devices listing simulators and emulators
|
|
4768
|
+
worktrees checking 26 projects for merged or idle worktrees
|
|
4769
|
+
daemons watchman pid 49040: checking 12 roots
|
|
4770
|
+
daemons removing stale watchman root /w/app-409
|
|
4771
|
+
daemons waiting up to 10s for 1 Gradle daemon to exit: pid 51234
|
|
4772
|
+
|
|
3904
4773
|
A GAP BETWEEN HEARTBEATS IS NOT A HANG. Stim runs device tools
|
|
3905
4774
|
synchronously, so a long \`simctl\`, \`adb\` or copy call holds the timer
|
|
3906
4775
|
until it returns; the next heartbeat then lands on the grid, which is why an
|
|
@@ -3908,26 +4777,40 @@ result as proof instead of requiring an unrelated screenshot.`
|
|
|
3908
4777
|
killing a run.`
|
|
3909
4778
|
},
|
|
3910
4779
|
pool: {
|
|
3911
|
-
summary: "parked and adopted simulators: what park and adoption clear or keep, the model and runtime match",
|
|
4780
|
+
summary: "parked and adopted simulators and emulators: what park and adoption clear or keep, the model and runtime match, emulator storage, gc erase",
|
|
3912
4781
|
body: () => ` THE SIMULATOR POOL
|
|
3913
4782
|
\`worktree remove\` PARKS this workspace's owned simulator instead of
|
|
3914
4783
|
deleting it, and the next workspace that wants the same model and runtime
|
|
3915
|
-
ADOPTS it. Adoption reuses the simulator
|
|
4784
|
+
ADOPTS it. Adoption reuses the simulator and the app installed on it, but
|
|
4785
|
+
not the previous workspace's state:
|
|
3916
4786
|
|
|
3917
|
-
at park shut down
|
|
3918
|
-
|
|
3919
|
-
|
|
4787
|
+
at park shut down and renamed
|
|
4788
|
+
\`stim-parked (<model> <runtime>) <4 hex>\`; nothing is
|
|
4789
|
+
erased, and the pool record names the installed app's
|
|
4790
|
+
bundle id and build cache key
|
|
3920
4791
|
at adoption renamed for the adopting workspace, then, inside the boot
|
|
3921
4792
|
the run pays anyway, \`simctl privacy reset all\` and
|
|
3922
|
-
\`simctl keychain reset\`; at install,
|
|
3923
|
-
uninstalled
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
|
|
4793
|
+
\`simctl keychain reset\`; at install, every other user app
|
|
4794
|
+
is uninstalled, and this workspace's app, when it is
|
|
4795
|
+
installed, has its preferences domain deleted (an
|
|
4796
|
+
expo-dev-client app's is replaced by the two dev-menu-off
|
|
4797
|
+
keys, see \`guide facts devmenu\`) and its data
|
|
4798
|
+
container's Documents, Library, tmp and SystemData emptied
|
|
4799
|
+
|
|
4800
|
+
Adoption always clears the app's data, whichever workspace parked the
|
|
4801
|
+
simulator: a new workspace starts from a fresh app state. The app itself
|
|
4802
|
+
stays installed, so the usual byte comparison decides the install: the same
|
|
4803
|
+
build prints \`install unchanged\` and skips the copy. A parked record's
|
|
4804
|
+
cache key that differs from this run's skips that comparison. App-group
|
|
4805
|
+
containers, the pasteboard, photos, Safari data and Simulator settings are
|
|
4806
|
+
not cleared; \`gc --cache parked --delete\` erases a parked simulator when a
|
|
4807
|
+
clean system image is needed.
|
|
4808
|
+
|
|
4809
|
+
A parked simulator keeps its apps and data, so it can hold gigabytes. It
|
|
4810
|
+
uses the Mac's disk, not a fixed partition, so there is no free-space check
|
|
4811
|
+
on iOS; \`stim gc\` lists each parked simulator's app and size.
|
|
4812
|
+
|
|
4813
|
+
Park takes only a simulator that reports Shutdown. It waits up to 15s for
|
|
3931
4814
|
the shutdown to settle, retries the shutdown once, and waits up to 15s more.
|
|
3932
4815
|
A simulator that is still booted after that is deleted instead, and
|
|
3933
4816
|
\`worktree remove\` prints
|
|
@@ -3954,6 +4837,11 @@ result as proof instead of requiring an unrelated screenshot.`
|
|
|
3954
4837
|
the next attempt, unless it may have left native work running. Deletion also
|
|
3955
4838
|
puts an opaque marker on the pool record so older Stim versions keep it
|
|
3956
4839
|
protected. New versions resolve that marker through the same identity claim.
|
|
4840
|
+
When a marker outlives its owner, Stim cannot tell whether the erase or
|
|
4841
|
+
deletion ran, so adoption treats the device as erased and drops the recorded
|
|
4842
|
+
app, cache key and scheme approvals. A \`gc --cache parked --delete\` erase
|
|
4843
|
+
that fails after the erase command started keeps the device parked without
|
|
4844
|
+
them for the same reason.
|
|
3957
4845
|
A crash during a synchronous device-tool call leaves that work unverifiable; inspect
|
|
3958
4846
|
the old process and its native children before following the claim's removal
|
|
3959
4847
|
remedy. See \`stim guide errors STIM_CLAIM_REFUSED\`.
|
|
@@ -3968,8 +4856,7 @@ result as proof instead of requiring an unrelated screenshot.`
|
|
|
3968
4856
|
|
|
3969
4857
|
device stim-app-412 (iPhone 17 26.5) (9C1F..) adopted (17s)
|
|
3970
4858
|
|
|
3971
|
-
That time includes the
|
|
3972
|
-
runs longer than a plain boot.
|
|
4859
|
+
That time includes the two resets, so it runs longer than a plain boot.
|
|
3973
4860
|
\`stim status\` prints one line while the pool is not empty:
|
|
3974
4861
|
|
|
3975
4862
|
pool: 2 parked iOS simulators (max 3)
|
|
@@ -3977,12 +4864,21 @@ result as proof instead of requiring an unrelated screenshot.`
|
|
|
3977
4864
|
\`stim gc\` reports the pool, and \`stim gc --delete\` empties every entry
|
|
3978
4865
|
it can re-verify:
|
|
3979
4866
|
|
|
3980
|
-
Parked simulators (2,
|
|
3981
|
-
ios stim-parked (iPhone 17 26.5) 9c1f (9C1F..) iPhone 17 26.5 parked 3d ago
|
|
3982
|
-
--delete attempts
|
|
4867
|
+
Parked simulators (2, 4.1G):
|
|
4868
|
+
ios stim-parked (iPhone 17 26.5) 9c1f (9C1F..) iPhone 17 26.5 parked 3d ago com.example.app 2.3G
|
|
4869
|
+
--delete attempts verified deletions and keeps failures.
|
|
4870
|
+
|
|
4871
|
+
\`stim gc --cache parked\` reports only the pool, and with --delete ERASES
|
|
4872
|
+
each parked simulator (\`simctl erase\`) and emulator (user data and
|
|
4873
|
+
snapshots) it can re-verify instead of deleting it. The device stays parked
|
|
4874
|
+
for adoption, with no app recorded; the next adopter installs its app again.
|
|
4875
|
+
\`--cache all\` leaves them alone. Erase takes only a simulator
|
|
4876
|
+
that reports Shutdown and an emulator that is not running and no workspace
|
|
4877
|
+
references, under the same per-device claim as adoption.
|
|
3983
4878
|
|
|
3984
|
-
\`gc --older-than <days>\` reports and deletes
|
|
3985
|
-
least that long; one parked at an unknown
|
|
4879
|
+
\`gc --older-than <days>\` reports and deletes, or with \`--cache parked\`
|
|
4880
|
+
erases, only the devices parked at least that long; one parked at an unknown
|
|
4881
|
+
time stays.
|
|
3986
4882
|
|
|
3987
4883
|
If simulator listing or deletion fails, \`gc --delete\` reports the failure
|
|
3988
4884
|
and keeps that entry. It never turns an unverified absence into a dropped
|
|
@@ -3999,15 +4895,15 @@ result as proof instead of requiring an unrelated screenshot.`
|
|
|
3999
4895
|
\`pool.androidParkedMax\` (default 3) or STIM_POOL_ANDROID_PARKED_MAX.
|
|
4000
4896
|
A redirected STIM_HOME disables parking unless that environment override
|
|
4001
4897
|
is set. Zero disables parking and adoption. \`stop\` keeps the assignment;
|
|
4002
|
-
\`worktree remove\` parks eligible AVDs,
|
|
4003
|
-
|
|
4898
|
+
\`worktree remove\` parks eligible AVDs, \`gc --delete\` empties the pool
|
|
4899
|
+
and \`gc --cache parked --delete\` wipes their user data (with
|
|
4900
|
+
--older-than, only the AVDs parked that long).
|
|
4004
4901
|
|
|
4005
4902
|
Adoption matches the system image, data partition size, and the creation
|
|
4006
4903
|
settings from android.avdConfig / android.avdConfigFile. The AVD keeps its
|
|
4007
|
-
original stim-<label> name. Parking
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
boot (about 20s) and a fresh install.
|
|
4904
|
+
original stim-<label> name. Parking keeps its user data and Quick Boot
|
|
4905
|
+
snapshot, so the installed app stays and the pool record names its package
|
|
4906
|
+
and build cache key.
|
|
4011
4907
|
Incompatible AVDs stay parked until eviction or GC. AVDs created by older
|
|
4012
4908
|
versions without a recorded creation configuration are deleted at removal.
|
|
4013
4909
|
|
|
@@ -4023,10 +4919,9 @@ result as proof instead of requiring an unrelated screenshot.`
|
|
|
4023
4919
|
manually clearing a claim, and keep the AVD data while its process state
|
|
4024
4920
|
cannot be verified.
|
|
4025
4921
|
|
|
4026
|
-
Android cleanup happens AFTER boot, before install or launch
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
are uninstalled. If ADB goes offline or closes the
|
|
4922
|
+
Android cleanup happens AFTER boot, before install or launch:
|
|
4923
|
+
\`adb shell pm clear\` clears the adopting app's data while retaining its
|
|
4924
|
+
APK, and other third-party apps are uninstalled. If ADB goes offline or closes the
|
|
4030
4925
|
connection, Stim waits for boot readiness and retries cleanup for up to 30
|
|
4031
4926
|
seconds, verifying the same owned AVD before each destructive command.
|
|
4032
4927
|
Failed cleanup blocks launch and remains pending for a retry. The installed APK's SHA-256 must match the
|
|
@@ -4035,14 +4930,29 @@ result as proof instead of requiring an unrelated screenshot.`
|
|
|
4035
4930
|
If the retained APK has a conflicting signer or version, adoption uninstalls
|
|
4036
4931
|
it and retries installation. Adoption stays pending until installation succeeds.
|
|
4037
4932
|
|
|
4933
|
+
An emulator's /data partition is fixed, so an adopted AVD can run out of
|
|
4934
|
+
room. Before installing, adoption reads the free space on /data
|
|
4935
|
+
(\`adb shell df -k /data\`) and needs twice the APK size plus 512 MB. When
|
|
4936
|
+
there is less, it runs \`pm trim-caches\`; when that is not enough, it
|
|
4937
|
+
shuts the emulator down, wipes its user data through centralized teardown,
|
|
4938
|
+
boots it again and installs:
|
|
4939
|
+
|
|
4940
|
+
device stim-app has 97M free on /data after removing other apps and trimming caches; wiping its user data
|
|
4941
|
+
device stim-app (emulator-5556) booted with wiped user data
|
|
4942
|
+
|
|
4943
|
+
An install that fails with INSTALL_FAILED_INSUFFICIENT_STORAGE gets the
|
|
4944
|
+
same steps: trim caches and retry, then wipe and retry. Only an adopting
|
|
4945
|
+
run does this; a workspace's own emulator never loses data to it.
|
|
4946
|
+
|
|
4038
4947
|
The wipe resets apps, accounts, device settings and the shared storage
|
|
4039
4948
|
inside the data partition; the AVD's creation settings and any separate
|
|
4040
4949
|
SD card image remain. Status lists parked Android emulators; GC
|
|
4041
|
-
reports their system image, hardware profile, age and disk
|
|
4950
|
+
reports their system image, hardware profile, age, recorded app and disk
|
|
4951
|
+
size.
|
|
4042
4952
|
`
|
|
4043
4953
|
},
|
|
4044
4954
|
builds: {
|
|
4045
|
-
summary: "optional cache warm-up, build optimizations, fingerprints, .fingerprintignore, install unchanged, runtime state",
|
|
4955
|
+
summary: "optional cache warm-up, build optimizations, fingerprints, .fingerprintignore, install unchanged, running app restart, runtime state",
|
|
4046
4956
|
body: () => `OPTIONAL CACHE WARM-UP FOR REPEATED NATIVE WORK
|
|
4047
4957
|
When several native worktrees are coming, build the source checkout once to
|
|
4048
4958
|
seed the shared caches before warming the linked worktrees. Skip this extra
|
|
@@ -4092,8 +5002,9 @@ PREDICTING THE NEXT BUILD (--plan)
|
|
|
4092
5002
|
An Android plan reads the ABI from the emulator the slot records, or from
|
|
4093
5003
|
the system image a new one would use. A plan refuses --device, --remote,
|
|
4094
5004
|
--wait, --no-wait, --no-metro-check and --simulator-app with STIM_BAD_ARG.
|
|
4095
|
-
Without --eas-profile it also refuses the
|
|
4096
|
-
|
|
5005
|
+
Without --eas-profile it also refuses the ios.remote and android.remote
|
|
5006
|
+
settings with STIM_BAD_ARG. Without --eas-profile an Android plan also
|
|
5007
|
+
refuses the experimental compiler CAS with STIM_BAD_ARG, and refuses with
|
|
4097
5008
|
STIM_NO_DEVICE when no system image is installed.
|
|
4098
5009
|
|
|
4099
5010
|
IOS SCHEME SELECTION
|
|
@@ -4130,8 +5041,10 @@ AN ARTIFACT THE DEVICE ALREADY HOLDS IS NOT INSTALLED AGAIN
|
|
|
4130
5041
|
install unchanged (emulator-5584 already has this build) (0.4s)
|
|
4131
5042
|
|
|
4132
5043
|
On iOS the install line names the identity proof separately from the Expo
|
|
4133
|
-
dev-client
|
|
4134
|
-
to an install that did not run
|
|
5044
|
+
dev-client simulator writes, so a slow simulator command is never charged
|
|
5045
|
+
to an install that did not run. Those writes run only after a real install
|
|
5046
|
+
or for a scheme approval the device record lacks (\`stim guide facts
|
|
5047
|
+
devmenu\`), so a skipped rerun usually prints no second line:
|
|
4135
5048
|
|
|
4136
5049
|
install unchanged (stim-app already has this build) (0.4s)
|
|
4137
5050
|
install dev client prepared (0.9s)
|
|
@@ -4145,13 +5058,31 @@ AN ARTIFACT THE DEVICE ALREADY HOLDS IS NOT INSTALLED AGAIN
|
|
|
4145
5058
|
\`--json\` carries installSkipped so a caller can tell a skipped run from an
|
|
4146
5059
|
installed one.
|
|
4147
5060
|
|
|
5061
|
+
A RUNNING APP IS RESTARTED, LIKE XCODE'S RUN
|
|
5062
|
+
Before it launches, \`ios\` and \`android\` read the device's process list.
|
|
5063
|
+
When the app is already running, from an earlier run or after an install
|
|
5064
|
+
that was skipped, Stim stops it and launches it fresh: \`simctl terminate\`
|
|
5065
|
+
on a simulator, \`am force-stop\` on an emulator or an \`android --device\`
|
|
5066
|
+
phone. The new process requests its bundle, so launch verification has real
|
|
5067
|
+
evidence instead of an old process that fetched nothing. In-app state from
|
|
5068
|
+
the previous session does not survive. The launch line says so:
|
|
5069
|
+
|
|
5070
|
+
launch com.example.app restarted running app (was pid 4242) (0.9s)
|
|
5071
|
+
|
|
5072
|
+
A stop that fails refuses the launch rather than reusing the old process.
|
|
5073
|
+
When the process list cannot be read, Stim launches without stopping
|
|
5074
|
+
anything and verification reports what it observes. An iPhone run
|
|
5075
|
+
(\`ios --device\`) needs no extra step: its collector launches with
|
|
5076
|
+
devicectl's \`--terminate-existing\`. Remote targets launch through
|
|
5077
|
+
\`agent-device open --relaunch\`.
|
|
5078
|
+
|
|
4148
5079
|
RUNTIME STATE AND BUILD INPUTS
|
|
4149
5080
|
Runtime state is stored outside the project tree under
|
|
4150
5081
|
$STIM_HOME/workspaces/<project>--<digest>/ (default ~/.stim/workspaces/).
|
|
4151
5082
|
The aggregate run counters \`stats\` prints live beside it in
|
|
4152
5083
|
$STIM_HOME/stats.json, one bucket per project and platform plus a machine-wide
|
|
4153
5084
|
one, and the last 10 successful runs per project, platform and cache outcome
|
|
4154
|
-
that size \`status\`'s build estimate. The running build itself is the
|
|
5085
|
+
(plus the last 10 that set up their device) that size \`status\`'s build estimate. The running build itself is the
|
|
4155
5086
|
activeBuild key of the workspace's state.json, cleared when the run exits.
|
|
4156
5087
|
No .gitignore entry is created or required.
|
|
4157
5088
|
Native preparation can change project files: expo prebuild generates native
|
|
@@ -4554,6 +5485,27 @@ OPT-IN CONCURRENCY LIMITS (UNLIMITED BY DEFAULT)
|
|
|
4554
5485
|
4. trim shared cache entries nothing has used for 14 days
|
|
4555
5486
|
(\`gc --delete --older-than 14\` for the caches)
|
|
4556
5487
|
|
|
5488
|
+
IDLE SHUTDOWN (OFF BY DEFAULT): with devices.idleShutdownMinutes set, a
|
|
5489
|
+
workspace's supervisor shuts down that workspace's owned simulators and
|
|
5490
|
+
emulators once they have been idle that long, without waiting for a run
|
|
5491
|
+
to go over budget. Idle is step 1 with that many minutes in place of 10:
|
|
5492
|
+
booted, no driver, no Stim or agent-device lock, no build in progress, and
|
|
5493
|
+
no activity. A stim-server client viewing the device (the phone app's
|
|
5494
|
+
device viewer) counts as activity, here and in step 1. The supervisor
|
|
5495
|
+
checks once a minute after the workspace's last Stim command, in-app log
|
|
5496
|
+
and device log are all that old, rechecks under the workspace's native-run lock, and shuts devices down
|
|
5497
|
+
through the same teardown as \`stop\`: never deleted, and physical
|
|
5498
|
+
devices are never touched. It writes a device_idle_shutdown line to
|
|
5499
|
+
metro.ndjson and records deviceIdleShutdowns in state.json, so \`stim
|
|
5500
|
+
status\` shows "shut down after 30m idle" on the device until the next
|
|
5501
|
+
\`ios\` or \`android\` run boots it again. When metro.idleStopMinutes is
|
|
5502
|
+
shorter, the dev server's idle stop first shuts down the devices idle that
|
|
5503
|
+
long, because no supervisor is left to check afterwards. Nothing checks
|
|
5504
|
+
without a supervisor: release runs, and after \`stim stop\`. The setting
|
|
5505
|
+
is read when the supervisor starts. Stim Desktop's simulator view is not a
|
|
5506
|
+
stim-server client and does not count as a viewer. Turn it on with
|
|
5507
|
+
\`stim settings set devices.idleShutdownMinutes 30 --scope machine\`.
|
|
5508
|
+
|
|
4557
5509
|
Steps 3 and 4 run only for disk. Memory and workspace limits never refuse;
|
|
4558
5510
|
a run still over them prints one \`budget\` warning and continues. The
|
|
4559
5511
|
current workspace is never reclaimed.
|
|
@@ -4572,8 +5524,8 @@ OPT-IN CONCURRENCY LIMITS (UNLIMITED BY DEFAULT)
|
|
|
4572
5524
|
summary: "every flag per command, Android variants and flavors, the per-run simulator model, runtime and system image",
|
|
4573
5525
|
body: () => `THE OPTION SURFACE, IN FULL
|
|
4574
5526
|
start --json --wait <seconds> --remote --reset-cache
|
|
4575
|
-
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|eas>
|
|
4576
|
-
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>
|
|
5527
|
+
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 <proxy|eas>
|
|
5528
|
+
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>
|
|
4577
5529
|
reload [ios|android] --json
|
|
4578
5530
|
device lock <ios|android> [id] --slot <name> --for <duration> --wait <seconds> --json;
|
|
4579
5531
|
unlock [ios|android] --slot <name> --json
|
|
@@ -4585,7 +5537,7 @@ OPT-IN CONCURRENCY LIMITS (UNLIMITED BY DEFAULT)
|
|
|
4585
5537
|
set <key> <value> --scope <layer> --json; unset <key> --scope <layer> --json
|
|
4586
5538
|
doctor --json --fix --platform <ios|android>
|
|
4587
5539
|
(--platform keeps shared checks and filters native findings)
|
|
4588
|
-
gc --delete --older-than <days> --cache <name|all|workspaces> --worktrees --json
|
|
5540
|
+
gc --delete --older-than <days> --cache <name|all|workspaces|recordings|parked> --worktrees --json
|
|
4589
5541
|
worktree warm --refresh; remove [path] --force
|
|
4590
5542
|
|
|
4591
5543
|
DEVICE SLOTS
|
|
@@ -4609,23 +5561,32 @@ OPT-IN CONCURRENCY LIMITS (UNLIMITED BY DEFAULT)
|
|
|
4609
5561
|
A BUILD).
|
|
4610
5562
|
Named slots support local devices; remote sessions use the default slot.
|
|
4611
5563
|
Names use 1-64 letters, digits, underscores or hyphens, starting with a letter
|
|
4612
|
-
or digit. Reserved object-property names are refused.
|
|
5564
|
+
or digit. Reserved object-property names and web are refused.
|
|
4613
5565
|
|
|
4614
5566
|
stop --slot shuts down that slot's owned devices and releases its leases,
|
|
4615
5567
|
retaining Metro and sibling slots. stop --slot default targets only the
|
|
4616
5568
|
workspace's default device, the same one ios/android address with no
|
|
4617
5569
|
--slot and status reports with no [slot] label; it never touches a remote
|
|
4618
|
-
session even though remote sessions use the default slot.
|
|
4619
|
-
|
|
5570
|
+
session even though remote sessions use the default slot. stop --slot web
|
|
5571
|
+
closes only the owned Chrome from stim web. A slot the workspace has not
|
|
5572
|
+
recorded is refused with STIM_BAD_ARG and the list of its slots; nothing is
|
|
5573
|
+
stopped. A slot whose first build is still running counts as recorded, and
|
|
5574
|
+
that build is interrupted. Plain stop handles the whole workspace.
|
|
4620
5575
|
status reports named devices under slots and counts their memory. Device
|
|
4621
5576
|
caps count every slot. Recycling uses the same model/runtime-matched pool
|
|
4622
5577
|
for every slot, with one shared cap per platform and oldest-first eviction.
|
|
4623
5578
|
|
|
4624
|
-
|
|
4625
|
-
|
|
4626
|
-
|
|
4627
|
-
|
|
4628
|
-
|
|
5579
|
+
When slots coexist, a Debug launch counts a Metro bundle delivery as proof
|
|
5580
|
+
only when it is attributable to that launch's device. On macOS, Metro records
|
|
5581
|
+
which simulator app process requested an iOS bundle (through lsof), so each
|
|
5582
|
+
simulator slot proves its own launch. Any other delivery (Android, a physical
|
|
5583
|
+
iPhone, or a simulator request lsof could not resolve) proves the launch only
|
|
5584
|
+
while no other slot of the same platform is running: a booted owned device,
|
|
5585
|
+
a log collector or a device lease. stop --slot <name> ends all three.
|
|
5586
|
+
Otherwise the launch reports unverified with "not provably to this device":
|
|
5587
|
+
check the reported device directly. Release verification still checks its
|
|
5588
|
+
process. reload ios/android addresses matching Metro peers across slots; it
|
|
5589
|
+
is not a single-slot reload.
|
|
4629
5590
|
|
|
4630
5591
|
That is the whole surface today, and it is deliberately small. It can grow
|
|
4631
5592
|
when a flag is genuinely the best answer -- but project-specific knowledge
|
|
@@ -4638,6 +5599,12 @@ OPT-IN CONCURRENCY LIMITS (UNLIMITED BY DEFAULT)
|
|
|
4638
5599
|
worktrees of the same Git repository; the source checkout must be available.
|
|
4639
5600
|
Running it in the source checkout refuses.
|
|
4640
5601
|
|
|
5602
|
+
Warm registers the app it prepares, so \`stim status\` lists the workspace
|
|
5603
|
+
with phase "warming" while it runs and "ready" once it succeeds, until the
|
|
5604
|
+
first start, ios, android, web or reload there or for 2 hours
|
|
5605
|
+
(\`guide facts status\`). In a monorepo worktree root, that app is the one
|
|
5606
|
+
the source checkout has registered.
|
|
5607
|
+
|
|
4641
5608
|
\`stim doctor\` reports the source checkout's fitness as a seed -- how far
|
|
4642
5609
|
behind its upstream it is, uncommitted tracked changes, an interrupted rebase
|
|
4643
5610
|
or merge, a detached HEAD, a diverged branch, a branch that is not the
|
|
@@ -4718,7 +5685,7 @@ OPT-IN CONCURRENCY LIMITS (UNLIMITED BY DEFAULT)
|
|
|
4718
5685
|
paths eligible under the source checkout's Git ignore rules, including .env
|
|
4719
5686
|
and local configuration. The source's nonempty
|
|
4720
5687
|
.worktreeexclude replaces its resolved worktree.exclude setting. Nested
|
|
4721
|
-
registered worktrees, .DS_Store, .DerivedData, .idea, and
|
|
5688
|
+
registered worktrees, .DS_Store, .DerivedData, .idea, .jj, and
|
|
4722
5689
|
android/build/generated/autolinking caches are excluded, including inside
|
|
4723
5690
|
newly copied directories. Gradle regenerates autolinking
|
|
4724
5691
|
for the destination checkout on its next build. Warm also skips paths
|
|
@@ -4797,7 +5764,21 @@ OPT-IN CONCURRENCY LIMITS (UNLIMITED BY DEFAULT)
|
|
|
4797
5764
|
runtime when \`--runtime\` also resolved, which is what catches a pair like
|
|
4798
5765
|
\`--device-type "iPhone 8" --runtime 26.5\` that each half would pass alone.
|
|
4799
5766
|
\`--runtime\` takes a version (\`26.5\`) or a runtime's full name
|
|
4800
|
-
(\`iOS 26.5\`), exactly; no prefix or suffix matches.
|
|
5767
|
+
(\`iOS 26.5\`), exactly; no prefix or suffix matches. On a remote run
|
|
5768
|
+
(\`--remote\` or the ios.remote setting) \`--runtime\` refuses with
|
|
5769
|
+
STIM_BAD_ARG, because the remote backend picks the iOS version. So does
|
|
5770
|
+
\`--device-type\` on the proxy backend. The eas backend honors
|
|
5771
|
+
\`--device-type\`: Stim passes it to \`eas simulator:start --device\`,
|
|
5772
|
+
unchecked against local runtimes, and refuses with STIM_BAD_ARG when
|
|
5773
|
+
eas-cli is older than 22.2.0, the first release with that flag. A
|
|
5774
|
+
recorded EAS session still running another model refuses with
|
|
5775
|
+
STIM_REMOTE_DEVICE_MISMATCH instead of being reused; one that already
|
|
5776
|
+
ended is replaced on the requested model. The ios.deviceType and
|
|
5777
|
+
ios.runtime settings do not refuse on a remote run; it ignores them.
|
|
5778
|
+
\`android --system-image\` and \`--device-profile\` refuse with
|
|
5779
|
+
STIM_BAD_ARG on every remote run (\`--remote\` or the android.remote
|
|
5780
|
+
setting), and a remote run ignores android.systemImage and
|
|
5781
|
+
android.deviceProfile.
|
|
4801
5782
|
|
|
4802
5783
|
These flags describe a device that does not exist yet. When this workspace
|
|
4803
5784
|
ALREADY owns a simulator and \`--device-type\` names a different model, or
|
|
@@ -4865,11 +5846,25 @@ OPT-IN CONCURRENCY LIMITS (UNLIMITED BY DEFAULT)
|
|
|
4865
5846
|
and the remedy is the cable. Metro is unaffected: a Debug app reaches it
|
|
4866
5847
|
over the LAN either way.
|
|
4867
5848
|
|
|
5849
|
+
WATCHING A LEASED ANDROID PHONE: while this workspace holds the phone's
|
|
5850
|
+
lease, stim-server can stream its screen to paired clients that ask for the
|
|
5851
|
+
physical device, and let one with control tap and type on it. stim-server
|
|
5852
|
+
pushes the scrcpy server to /data/local/tmp for the stream and deletes it
|
|
5853
|
+
when the stream stops; it installs nothing and changes no setting. Control
|
|
5854
|
+
ends when the lease is released or expires, and stim-server never takes a
|
|
5855
|
+
phone's lease itself.
|
|
5856
|
+
|
|
4868
5857
|
\`stop\` releases this workspace's leases and stops its log collectors.
|
|
4869
5858
|
On a physical iPhone that also closes the app, because its collector owns
|
|
4870
5859
|
the devicectl launch session. \`gc --delete\` removes expired lease files;
|
|
4871
5860
|
neither command shuts down the phone or uninstalls the app.
|
|
4872
5861
|
|
|
5862
|
+
WATCHING THE PHONE: while this workspace holds the lease, stim-server
|
|
5863
|
+
streams a USB-cabled iPhone's screen to the phone app, view only; Stim
|
|
5864
|
+
sends a physical iPhone no input. The lease ends with the run, so
|
|
5865
|
+
\`device lock ios <udid>\` keeps it watchable between runs. A Wi-Fi phone
|
|
5866
|
+
has no stream, and a locked phone shows its last frame until unlocked.
|
|
5867
|
+
|
|
4873
5868
|
A device build is LOCAL-TIER ONLY. Its cache key is
|
|
4874
5869
|
\`<fingerprint>-<configuration>-device\`, so a device app can never collide
|
|
4875
5870
|
with the simulator one, and neither the build-cache provider nor the Expo
|
|
@@ -4895,8 +5890,9 @@ OPT-IN CONCURRENCY LIMITS (UNLIMITED BY DEFAULT)
|
|
|
4895
5890
|
this workspace's Metro, and then wires the app to it: an expo-dev-client app
|
|
4896
5891
|
through the deep link, passed to devicectl as \`--payload-url\` and followed
|
|
4897
5892
|
by \`-- -EXDevMenuShowsAtLaunch 0 -EXDevMenuShowFloatingActionButton 0\`,
|
|
4898
|
-
|
|
4899
|
-
bare app by writing \`<addr>:<port>\` into the app bundle's
|
|
5893
|
+
the same launch arguments Stim's own \`simctl launch --initialUrl\` carries on
|
|
5894
|
+
a simulator, and a bare app by writing \`<addr>:<port>\` into the app bundle's
|
|
5895
|
+
ip.txt --
|
|
4900
5896
|
RCTBundleURLProvider's own mechanism, which honours a colon-bearing value
|
|
4901
5897
|
verbatim and never consults the compiled RCT_METRO_PORT. Stim never sets
|
|
4902
5898
|
that define: it would put the reserved port into a compiled input and fork
|
|
@@ -5076,6 +6072,17 @@ THE POOL: WHICH DEVICE AN ID-LESS \`--device\` PICKS
|
|
|
5076
6072
|
android.keystorePassword override it (see \`guide settings\`). The cache
|
|
5077
6073
|
entry itself is never modified.
|
|
5078
6074
|
|
|
6075
|
+
AN EXPO-UPDATES APP GETS A FRESH EMBEDDED UPDATE. expo-updates launches the
|
|
6076
|
+
update with the newest commitTime for the binary's runtime version, and the
|
|
6077
|
+
embedded bundle is one of those updates. The cached artifact's embedded
|
|
6078
|
+
manifest (app.manifest) carries the CACHED build's id and time, so an
|
|
6079
|
+
update the app downloaded after that build would launch instead of the
|
|
6080
|
+
swapped JS. The swap gives the copy's manifest a new id and this run's time,
|
|
6081
|
+
exactly as a full build does, on both platforms. An update published after
|
|
6082
|
+
the swap can still download and launch on a later start, as it would on a
|
|
6083
|
+
fresh build. A manifest the swap cannot rewrite, or one inside a dynamic
|
|
6084
|
+
EXUpdates.framework on iOS, falls back to a full build.
|
|
6085
|
+
|
|
5079
6086
|
Before re-packing, THE ASSET GATE compares CONTENT HASHES of the assets
|
|
5080
6087
|
React Native emits: what this workspace just emitted under --assets-dest
|
|
5081
6088
|
against a manifest of what the cached build emitted, recorded as
|
|
@@ -5120,7 +6127,8 @@ THE POOL: WHICH DEVICE AN ID-LESS \`--device\` PICKS
|
|
|
5120
6127
|
its builder's baked-in JS, a cache hit regenerates THIS workspace's bundle
|
|
5121
6128
|
(the project's own \`expo export:embed\` / \`react-native bundle\`, plus
|
|
5122
6129
|
its own hermesc when Hermes is enabled) into a copy of the artifact,
|
|
5123
|
-
|
|
6130
|
+
refreshes an expo-updates manifest as described above, re-signs it and
|
|
6131
|
+
installs that; any swap failure falls back to a full build
|
|
5124
6132
|
rather than ever installing stale JS. Device logs are still collected, so
|
|
5125
6133
|
\`logs --errors\` answers "does it repro in release/Hermes bytecode".
|
|
5126
6134
|
A run with no \`--device\` installs on the simulator only. \`ios --device\`
|
|
@@ -5240,6 +6248,10 @@ WHAT RECLAIMS AN OWNED DEVICE
|
|
|
5240
6248
|
command has used in that long, even though the
|
|
5241
6249
|
project is still on disk, and clears only the
|
|
5242
6250
|
devices parked at least that long
|
|
6251
|
+
stim gc --cache parked --delete
|
|
6252
|
+
ERASES (never deletes) verified parked simulators
|
|
6253
|
+
and wipes parked emulators' user data; they stay
|
|
6254
|
+
parked for adoption (\`guide lifecycle pool\`)
|
|
5243
6255
|
stim gc --delete --worktrees
|
|
5244
6256
|
also runs \`stim worktree remove\` on every clean,
|
|
5245
6257
|
idle, Stim-managed linked worktree
|
|
@@ -5248,25 +6260,36 @@ WHAT RECLAIMS AN OWNED DEVICE
|
|
|
5248
6260
|
shuts DOWN (never deletes) owned devices with no
|
|
5249
6261
|
driver, claim or activity for that long
|
|
5250
6262
|
|
|
6263
|
+
\`gc --delete --cache watchman\` and \`gc --delete --cache gradle-daemons\`
|
|
6264
|
+
stop shared helper processes, not devices (\`guide cleanup memory\`).
|
|
6265
|
+
|
|
5251
6266
|
\`worktree remove\` and \`gc --delete\` are the only two commands that delete;
|
|
5252
6267
|
\`gc --delete\` deletes worktrees only through \`worktree remove\`. \`gc
|
|
5253
6268
|
--delete\` also clears workspace build outputs, trims oversized workspace logs
|
|
5254
6269
|
(\`guide cleanup disk\`) and removes orphaned workspace directories, never a
|
|
5255
|
-
checkout.
|
|
6270
|
+
checkout. Device recordings can also go without either: stim-server prunes
|
|
6271
|
+
them, and turning recording.enabled off deletes them (\`guide cleanup disk\`). \`stim stop\` shuts a device
|
|
5256
6272
|
DOWN and leaves it assigned, which is what makes returning to a branch cost a
|
|
5257
6273
|
boot rather than a create, a provision and a reinstall.
|
|
5258
6274
|
|
|
5259
6275
|
Before shutting down, parking or deleting an owned device, Stim best-effort
|
|
5260
6276
|
closes local agent-device sessions bound to its exact iOS UDID or live Android
|
|
5261
6277
|
serial. An Android session must also name the owned AVD, because the next
|
|
5262
|
-
emulator on a console port reuses its serial.
|
|
6278
|
+
emulator on a console port reuses its serial. For an emulator that is already
|
|
6279
|
+
shut down, Stim closes the sessions that name its AVD on any serial that no
|
|
6280
|
+
connected device now holds. \`stop\` closes a session only
|
|
5263
6281
|
when agent-device's claim on the device names it and was taken inside this
|
|
5264
|
-
workspace
|
|
6282
|
+
workspace. It also closes a session claimed from the workspace's git root
|
|
6283
|
+
itself, such as a worktree root above the app, but only after it has shut that
|
|
6284
|
+
device down and rechecked that the device is still stopped and that no other
|
|
6285
|
+
device holds its UDID or serial. Sessions from another workspace or claim,
|
|
6286
|
+
including sibling directories under the git root, stay open. Stim rechecks
|
|
5265
6287
|
device ownership and uses agent-device's rejecting session target guard;
|
|
5266
6288
|
sessions on other devices stay open. Physical devices are outside
|
|
5267
6289
|
this cleanup. agent-device is optional: a missing binary skips this step, and a
|
|
5268
6290
|
failed or timed-out list/close prints a device line on stderr while teardown
|
|
5269
|
-
continues.
|
|
6291
|
+
continues. Each cleanup pass allows at most 15 seconds of agent-device calls
|
|
6292
|
+
per device; the pass \`stop\` makes after shutdown has its own budget.
|
|
5270
6293
|
A local daemon that cannot use socket transport or a CLI without the target guard
|
|
5271
6294
|
skips cleanup with a warning; no remote daemon is used.
|
|
5272
6295
|
|
|
@@ -5315,7 +6338,8 @@ SWEEPING FINISHED WORKTREES
|
|
|
5315
6338
|
one that is idle: no recorded use for --older-than days, 7 without it; a
|
|
5316
6339
|
worktree whose last use is unknown is kept. Both need the same clean state;
|
|
5317
6340
|
a worktree is kept when it is the source checkout, bare, locked, in use,
|
|
5318
|
-
dirty (untracked files count; pod install churn
|
|
6341
|
+
dirty (untracked files count; pod install churn and
|
|
6342
|
+
watchman's untracked \`.watchman-cookie-*\` files alone do not), unpushed
|
|
5319
6343
|
(commits no remote-tracking ref or other local branch reaches), or has
|
|
5320
6344
|
initialized submodules. Without --worktrees, the report leaves out the
|
|
5321
6345
|
source checkout and roots outside git.
|
|
@@ -5370,11 +6394,14 @@ SWEEPING FINISHED WORKTREES
|
|
|
5370
6394
|
takes the pull request whose head is HEAD, else one whose head contains
|
|
5371
6395
|
HEAD. A pull request whose head HEAD is past ("PR #12 merged, and HEAD has
|
|
5372
6396
|
commits it does not"), one unrelated to HEAD (an older use of the branch
|
|
5373
|
-
name), and one from a fork
|
|
5374
|
-
|
|
5375
|
-
|
|
5376
|
-
|
|
5377
|
-
|
|
6397
|
+
name), and one from a fork that is not one of the repository's own remotes
|
|
6398
|
+
(a stranger's fork reusing the branch name) do not count; a pull request
|
|
6399
|
+
opened from the user's own fork counts like any other. A merged or closed
|
|
6400
|
+
one makes the worktree finished under the same clean state. Merged, its
|
|
6401
|
+
commits are kept on GitHub, so local commits whose remote branch was
|
|
6402
|
+
deleted do not block removal. Closed, they do: a closed pull request whose
|
|
6403
|
+
remote branch is gone keeps the worktree as unpushed. An open pull request
|
|
6404
|
+
never makes a worktree finished. When gh is
|
|
5378
6405
|
not installed, not signed in, or fails, the JSON pullRequestUnknown field
|
|
5379
6406
|
and the report say why, and gc judges from git alone. A detached HEAD is
|
|
5380
6407
|
not looked up. \`stim worktree remove\` makes the same check when
|
|
@@ -5419,6 +6446,20 @@ ORPHANED WORKSPACE DIRECTORIES
|
|
|
5419
6446
|
it. A directory with a missing or unparseable workspace.json, or a root on
|
|
5420
6447
|
an unmounted volume, is reported under Skipped and never deleted.
|
|
5421
6448
|
|
|
6449
|
+
STALE STATUS CACHE ENTRIES
|
|
6450
|
+
\`stim status\` caches each folder's size under $STIM_HOME/disk-usage and
|
|
6451
|
+
each worktree's pull request under $STIM_HOME/pull-requests. \`worktree
|
|
6452
|
+
remove\` drops the entries of the worktree it removed (its checkout,
|
|
6453
|
+
node_modules and workspace folder, and its pull request), not the size
|
|
6454
|
+
entries of its owned devices' data folders. \`gc\` reports the
|
|
6455
|
+
entries of a folder or worktree that was removed some other way under "Stale
|
|
6456
|
+
status cache entries", and \`gc --delete\` removes them. An entry counts as
|
|
6457
|
+
stale only when the path it records is gone and its volume is mounted (a
|
|
6458
|
+
measured node_modules folder follows its checkout). An entry that records no
|
|
6459
|
+
readable path, one whose path cannot be checked, and one on an unmounted
|
|
6460
|
+
volume are kept and counted under Skipped. \`status\` measures a folder again
|
|
6461
|
+
if it comes back.
|
|
6462
|
+
|
|
5422
6463
|
NAMED SERVER PORTS
|
|
5423
6464
|
worktree remove stops TCP listeners on each named allocation and releases
|
|
5424
6465
|
the ports. gc reports named allocations for missing workspaces; gc --delete
|
|
@@ -5610,9 +6651,65 @@ THE ONE CASE GC WILL NOT REAP
|
|
|
5610
6651
|
is gone, and the unrelated process is never signalled. A missing, malformed,
|
|
5611
6652
|
or unreadable identity leaves the record unverified and kept for a retry.
|
|
5612
6653
|
Wall-clock timestamps and command names are not ownership proof.`
|
|
6654
|
+
},
|
|
6655
|
+
memory: {
|
|
6656
|
+
summary: "watchman and Gradle and Kotlin daemon memory in gc, and when gc --delete --cache watchman or gradle-daemons stops them",
|
|
6657
|
+
body: () => `MEMORY
|
|
6658
|
+
Every \`gc\` without --cache, and \`gc --cache watchman\` or
|
|
6659
|
+
\`gc --cache gradle-daemons\`, reports the long-lived helpers that grow while
|
|
6660
|
+
they run: the shared watchman daemon, Gradle daemons and Kotlin compile
|
|
6661
|
+
daemons. Each line gives the pid, physical footprint (the measure status
|
|
6662
|
+
uses; resident size when the footprint cannot be read), uptime and whether
|
|
6663
|
+
it is idle, and a kept one says why. The reclaimable line totals what
|
|
6664
|
+
the two commands below would free now.
|
|
6665
|
+
|
|
6666
|
+
Only these two commands stop anything, and each acts only on its own kind.
|
|
6667
|
+
Neither a plain \`gc --delete\` nor \`--cache all\` touches them:
|
|
6668
|
+
stim gc --delete --cache watchman
|
|
6669
|
+
runs \`watchman watch-del\` on each stale root
|
|
6670
|
+
with no subscription or trigger, then
|
|
6671
|
+
\`watchman shutdown-server\` only when
|
|
6672
|
+
\`debug-status\` lists no client but gc's own
|
|
6673
|
+
call and no root has a trigger; otherwise
|
|
6674
|
+
the daemon is kept and the report names
|
|
6675
|
+
each client, as the Stim workspace whose
|
|
6676
|
+
dev server it belongs to where it can
|
|
6677
|
+
stim gc --delete --cache gradle-daemons
|
|
6678
|
+
stops each Gradle daemon its own
|
|
6679
|
+
\`gradle --status\` reports IDLE, then each
|
|
6680
|
+
Kotlin compile daemon with no open client
|
|
6681
|
+
connection once every Gradle daemon is idle
|
|
6682
|
+
|
|
6683
|
+
A stale watch root is one whose directory is gone from a mounted volume, or
|
|
6684
|
+
that sits in a linked worktree git pruned while its directory stayed.
|
|
6685
|
+
Watchman drops a root itself when its directory is deleted, so stale roots
|
|
6686
|
+
are rare. Removing one stops its recrawls but does not shrink the daemon;
|
|
6687
|
+
only a restart returns its memory. After a shutdown the next client that
|
|
6688
|
+
needs watchman starts it again and re-watches the roots in its state file.
|
|
6689
|
+
Every watchman call uses --no-spawn, so gc never starts the daemon.
|
|
6690
|
+
Shutting it down under a Metro that uses watchman would break that Metro's
|
|
6691
|
+
file watching, which is why any connected client keeps it.
|
|
6692
|
+
|
|
6693
|
+
gc finds a Gradle daemon's Gradle user home from the daemon log it holds
|
|
6694
|
+
open (lsof), and asks the daemon's own distribution for its status with
|
|
6695
|
+
that home and the daemon's Java. When every daemon that status lists for a
|
|
6696
|
+
home and version is idle, gc runs that distribution's \`gradle --stop\`;
|
|
6697
|
+
otherwise it sends SIGTERM to each idle daemon, after checking the pid still
|
|
6698
|
+
has the same start time. A build that picks a daemon between gc's last
|
|
6699
|
+
check and the stop fails and must be run again, so gc re-checks the build
|
|
6700
|
+
locks and the daemon's status right before each stop. Nothing is stopped while an Android build lock, or a build slot no
|
|
6701
|
+
iOS build holds, is live or unresolved. stim-server stops the daemons of
|
|
6702
|
+
offloaded builds (offload.gradleDaemonIdleMinutes, \`guide settings\`), so
|
|
6703
|
+
while it runs gc keeps those. A daemon whose home, distribution or status cannot be read is
|
|
6704
|
+
kept. Gradle stops an idle daemon itself after 3 hours and Kotlin after 2
|
|
6705
|
+
hours by default.
|
|
6706
|
+
|
|
6707
|
+
--older-than does not apply to these kinds and is refused with
|
|
6708
|
+
STIM_BAD_ARG. With STIM_HOME set, gc skips them: they are machine-global.
|
|
6709
|
+
\`stim doctor\` notes a watchman footprint over 2 GiB.`
|
|
5613
6710
|
},
|
|
5614
6711
|
disk: {
|
|
5615
|
-
summary: "disk usage, workspace build outputs and
|
|
6712
|
+
summary: "disk usage, workspace build outputs, logs and device recordings, AVD and build-log sizes, the data partition, trimming the shared caches",
|
|
5616
6713
|
body: () => `DISK
|
|
5617
6714
|
Logs, state, pidfiles and Xcode DerivedData are under the global workspace
|
|
5618
6715
|
directory, and \`worktree remove\` reclaims them. \`gc --delete\` clears the
|
|
@@ -5622,7 +6719,8 @@ THE ONE CASE GC WILL NOT REAP
|
|
|
5622
6719
|
|
|
5623
6720
|
Android AVDs normally live under ~/.android/avd, and a booted owned AVD can
|
|
5624
6721
|
use several GB. \`worktree remove\` parks the workspace's owned AVD with its
|
|
5625
|
-
user data and snapshots
|
|
6722
|
+
user data and snapshots kept, or deletes it when the pool is off or full;
|
|
6723
|
+
\`gc --cache parked --delete\` wipes a parked AVD's user data and snapshots;
|
|
5626
6724
|
plain \`stop\` only shuts it down for reuse. Stim uses Android's default Quick Boot
|
|
5627
6725
|
unless displayless Linux requires software rendering, where snapshots are
|
|
5628
6726
|
disabled. The first boot and a boot after the emulator, system image, or AVD
|
|
@@ -5634,7 +6732,7 @@ THE ONE CASE GC WILL NOT REAP
|
|
|
5634
6732
|
errors teardown\`).
|
|
5635
6733
|
New owned AVDs default to an 8 GiB data partition, though project settings can
|
|
5636
6734
|
change it. When enabled, Quick Boot keeps one automatic snapshot until the AVD is
|
|
5637
|
-
|
|
6735
|
+
wiped or deleted.
|
|
5638
6736
|
\`gc\` prints the on-disk size beside an orphaned or stale owned Android AVD
|
|
5639
6737
|
when its content directory can be read, and beside an orphaned or stale
|
|
5640
6738
|
owned simulator the data size simctl reports for it.
|
|
@@ -5665,15 +6763,20 @@ setting applies only to a newly created AVD; recreate the environment to adopt
|
|
|
5665
6763
|
a changed value.
|
|
5666
6764
|
|
|
5667
6765
|
WORKSPACE BUILD OUTPUTS
|
|
5668
|
-
Each workspace directory holds derived-data/, gradle-build/, android-cas
|
|
5669
|
-
|
|
5670
|
-
|
|
6766
|
+
Each workspace directory holds derived-data/, gradle-build/, android-cas/,
|
|
6767
|
+
cache-provider/, macos/build, the staged macos/<Product>.app and macos/staging-*
|
|
6768
|
+
leftovers. \`gc\` reports them as one detected cache, "Workspace build
|
|
6769
|
+
outputs", with a per-workspace size, last use and verdict. Plain
|
|
5671
6770
|
\`gc --delete\` clears them for every workspace not in use (see \`guide
|
|
5672
6771
|
cleanup gc\`), before anything else. \`--older-than <days>\` limits that to
|
|
5673
6772
|
workspaces idle at least that long, and keeps one whose last use is
|
|
5674
|
-
unknown. \`--cache workspaces\` acts on them alone
|
|
5675
|
-
|
|
5676
|
-
|
|
6773
|
+
unknown. \`--cache workspaces\` acts on them alone, except in the workspace
|
|
6774
|
+
directories that plain \`gc --delete\` removes whole (dead projects and
|
|
6775
|
+
orphaned directories); \`--cache all\` includes them. macos/ itself,
|
|
6776
|
+
macos/runtime.lock and its claim set, workspace.json, state.json, logs/,
|
|
6777
|
+
other macOS files, locks and device records stay, as does the project's
|
|
6778
|
+
own .build. The workspace keeps its devices and ports. A running, building,
|
|
6779
|
+
unverified or hosted macOS app keeps its workspace untouched.
|
|
5677
6780
|
stim gc --delete --cache workspaces --older-than 7
|
|
5678
6781
|
Last use is the newest of the lastUsedAt that start, ios, android, reload
|
|
5679
6782
|
and worktree warm record in state.json, lastBuild.startedAt,
|
|
@@ -5684,7 +6787,8 @@ WORKSPACE BUILD OUTPUTS
|
|
|
5684
6787
|
The next build of an unchanged app installs from the shared build cache.
|
|
5685
6788
|
After a native change the Xcode compilation cache speeds the rebuild, but on
|
|
5686
6789
|
React Native 0.86 Swift does not use it (explicit modules are off), so that
|
|
5687
|
-
build recompiles Swift.
|
|
6790
|
+
build recompiles Swift. The next \`stim macos\` rebuilds its scratch from
|
|
6791
|
+
nothing (a full Swift build) and restages the app.
|
|
5688
6792
|
|
|
5689
6793
|
WORKSPACE LOGS
|
|
5690
6794
|
\`gc\` reports the size of each workspace's logs/ (the workspaceLogs
|
|
@@ -5699,6 +6803,33 @@ WORKSPACE LOGS
|
|
|
5699
6803
|
stops the collector. Build transcripts keep the whole run and are never
|
|
5700
6804
|
trimmed, and nothing else under logs/ is touched.
|
|
5701
6805
|
|
|
6806
|
+
DEVICE RECORDINGS
|
|
6807
|
+
stim-server records owned simulators, emulators and the Stim-owned Chrome
|
|
6808
|
+
page while an automation tool drives them or a paired client watches them,
|
|
6809
|
+
into recordings/ in the workspace directory (\`guide settings\`). It keeps
|
|
6810
|
+
the last 15 minutes of recorded footage per device; idle time and time after
|
|
6811
|
+
\`stim stop\` age nothing out, so \`stop\` ends recording and the footage
|
|
6812
|
+
stays replayable. Across every workspace of this Stim home it keeps at most
|
|
6813
|
+
1 GiB, deleting the oldest footage first. Recordings are deleted by:
|
|
6814
|
+
|
|
6815
|
+
stim worktree remove that workspace's recordings, with its
|
|
6816
|
+
workspace directory
|
|
6817
|
+
stim gc --delete the recordings of dead or orphaned
|
|
6818
|
+
workspaces, with their workspace directory;
|
|
6819
|
+
a workspace whose project root cannot be
|
|
6820
|
+
proven gone keeps them
|
|
6821
|
+
stim gc --delete --older-than <days>
|
|
6822
|
+
also footage recorded before that many days
|
|
6823
|
+
ago, in every workspace
|
|
6824
|
+
stim gc --delete --cache recordings
|
|
6825
|
+
every workspace's recordings, and nothing
|
|
6826
|
+
else; --cache all includes them
|
|
6827
|
+
recording.enabled false the recordings of each workspace it turns off
|
|
6828
|
+
|
|
6829
|
+
\`gc\` lists them in the recordings section of \`gc --json\` with each
|
|
6830
|
+
workspace's bytes, what a delete would remove (deleteBytes), and why the rest
|
|
6831
|
+
stays.
|
|
6832
|
+
|
|
5702
6833
|
SHARED BUILD CACHES
|
|
5703
6834
|
The caches that make a second workspace fast are alive by design and never
|
|
5704
6835
|
included in a plain \`gc --delete\`. Every \`gc\` run reports them anyway,
|
|
@@ -5746,7 +6877,7 @@ read from, or an unknown key refuses with STIM_BAD_ARG naming the expected
|
|
|
5746
6877
|
shape, before anything is written. Writes to the machine file take its lock
|
|
5747
6878
|
and replace it atomically; a committed write keeps the file's other keys and
|
|
5748
6879
|
indentation. Run \`stim settings\` from the app directory: workspace and
|
|
5749
|
-
committed resolve from the nearest package.json, repo from its Git
|
|
6880
|
+
committed resolve from the nearest package.json or Package.swift, repo from its Git
|
|
5750
6881
|
repository. From a monorepo web package that resolves to its worktree's app
|
|
5751
6882
|
(see stim guide ports), workspace and committed are that app's entry and
|
|
5752
6883
|
.stim.json. worktree.exclude and worktree.defaultBranch are read only from
|
|
@@ -5765,9 +6896,11 @@ carries its dotted key, layers, and environment override under "x-stim".
|
|
|
5765
6896
|
Resolution order, first match wins:
|
|
5766
6897
|
1. workspace ~/.stim/config.json, under this project's entry
|
|
5767
6898
|
2. repo ~/.stim/config.json, under this repo's git common dir
|
|
5768
|
-
3. committed .stim.json beside the app's package.json
|
|
5769
|
-
4. machine ~/.stim/config.json, top-level optimizations
|
|
5770
|
-
android.
|
|
6899
|
+
3. committed .stim.json beside the app's package.json or Package.swift
|
|
6900
|
+
4. machine ~/.stim/config.json, top-level optimizations,
|
|
6901
|
+
ios.deviceType, ios.runtime, android.systemImage,
|
|
6902
|
+
android.deviceProfile and devices.idleShutdownMinutes
|
|
6903
|
+
only
|
|
5771
6904
|
5. Stim default
|
|
5772
6905
|
An environment override, where a setting has one, wins over every layer.
|
|
5773
6906
|
|
|
@@ -5797,15 +6930,21 @@ KEYS STIM READS
|
|
|
5797
6930
|
ios.deviceType e.g. "iPhone 17 Pro" -- the simulator model this
|
|
5798
6931
|
workspace's owned sim is created as, spelled exactly as
|
|
5799
6932
|
\`xcrun simctl list devicetypes\` names it, and one an
|
|
5800
|
-
installed runtime can create.
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
|
|
6933
|
+
installed runtime can create. Readable from the
|
|
6934
|
+
machine layer too, so one machine can default every
|
|
6935
|
+
workspace to a model; a project layer still wins. The
|
|
6936
|
+
\`--device-type\` flag overrides this per invocation. A
|
|
6937
|
+
name no installed runtime offers is STIM_BAD_ARG and
|
|
6938
|
+
the creatable names are printed, naming the layer the
|
|
6939
|
+
value came from
|
|
5804
6940
|
ios.runtime e.g. "26.2" -- the iOS runtime that sim is created on,
|
|
5805
6941
|
as a version ("26.2") or a runtime's full name
|
|
5806
|
-
("iOS 26.2"); nothing else matches.
|
|
5807
|
-
|
|
5808
|
-
|
|
6942
|
+
("iOS 26.2"); nothing else matches. Readable from the
|
|
6943
|
+
machine layer too, so one machine can default every
|
|
6944
|
+
workspace to a runtime; a project layer still wins.
|
|
6945
|
+
The \`--runtime\` flag overrides this per invocation,
|
|
6946
|
+
and an uninstalled version refuses the same way,
|
|
6947
|
+
naming the layer the value came from
|
|
5809
6948
|
ios.configuration e.g. "Release" -- the Xcode configuration to build
|
|
5810
6949
|
(simulator only). Committing
|
|
5811
6950
|
{ "ios": { "configuration": "Release" } } makes every
|
|
@@ -5857,9 +6996,12 @@ KEYS STIM READS
|
|
|
5857
6996
|
interface.
|
|
5858
6997
|
android.systemImage e.g. "system-images;android-36;google_apis;arm64-v8a"
|
|
5859
6998
|
-- the sdkmanager package id the owned AVD is created
|
|
5860
|
-
from.
|
|
5861
|
-
|
|
5862
|
-
|
|
6999
|
+
from. Readable from the machine layer too, so one
|
|
7000
|
+
machine can default every workspace to an image; a
|
|
7001
|
+
project layer still wins. The \`--system-image\` flag
|
|
7002
|
+
overrides this per invocation, and an id this SDK has
|
|
7003
|
+
not installed is STIM_BAD_ARG with the installed ids
|
|
7004
|
+
printed, naming the layer the value came from.
|
|
5863
7005
|
android.deviceProfile e.g. "pixel_fold" -- the avdmanager hardware profile a
|
|
5864
7006
|
new owned AVD is created with, spelled exactly as
|
|
5865
7007
|
\`avdmanager list device -c\` prints it. Defaults to
|
|
@@ -5963,15 +7105,35 @@ ${ANDROID_AVD_CONFIG_HELP.map((line) => ` ${line}`).joi
|
|
|
5963
7105
|
shares this machine and is the only mode that needs no
|
|
5964
7106
|
tunnel. "expo" lets the Expo dev server tunnel itself.
|
|
5965
7107
|
"cloudflared" and "ngrok" name a managed provider
|
|
5966
|
-
explicitly.
|
|
7108
|
+
explicitly. "tailscale" explicitly selects a tailnet-only
|
|
7109
|
+
foreground serve process, never Funnel; "auto" never
|
|
7110
|
+
selects it. The device must share the tailnet. See
|
|
7111
|
+
\`guide metro\`. Any other value is refused as invalid.
|
|
5967
7112
|
metro.ngrokUrl the stable managed ngrok URL. It requires metro.tunnel
|
|
5968
7113
|
"ngrok" and passes --url to ngrok http. Stim owns
|
|
5969
7114
|
this process.
|
|
5970
7115
|
metro.publicUrl an existing tunnel's URL. Takes precedence over
|
|
5971
7116
|
starting one, whatever metro.tunnel says -- Stim
|
|
5972
7117
|
did not create it, so a Metro request through it is
|
|
5973
|
-
still gated
|
|
7118
|
+
still gated through a public bundle probe. Set it
|
|
5974
7119
|
before Expo start so the manifest advertises it.
|
|
7120
|
+
metro.port this workspace's Metro port, 1024-65535;
|
|
7121
|
+
STIM_METRO_PORT overrides it. Stim reserves this
|
|
7122
|
+
number instead of picking one from 8082 up, for a
|
|
7123
|
+
workspace whose tools already expect a port. A
|
|
7124
|
+
committed value suits a single checkout; give each
|
|
7125
|
+
worktree its own in the workspace layer or the
|
|
7126
|
+
environment. start refuses with STIM_BAD_ARG when
|
|
7127
|
+
another workspace reserves it or another process
|
|
7128
|
+
holds it. start, ios, android and web refuse a
|
|
7129
|
+
changed pin while this workspace's dev server runs
|
|
7130
|
+
(run stim stop before retrying) or its supervisor
|
|
7131
|
+
cannot be verified (stop it with the tool that
|
|
7132
|
+
started it). A foreign holder is refused before
|
|
7133
|
+
anything starts; this project's Metro on the pin is
|
|
7134
|
+
attached to. --no-metro-check uses the pin, then the
|
|
7135
|
+
recorded port, then 8081, without probing or reserving;
|
|
7136
|
+
an invalid pin still refuses.
|
|
5975
7137
|
metro.warmupUrl optional object with per-platform bundle URLs:
|
|
5976
7138
|
metro.warmupUrl.ios
|
|
5977
7139
|
metro.warmupUrl.android
|
|
@@ -5999,6 +7161,23 @@ ${ANDROID_AVD_CONFIG_HELP.map((line) => ` ${line}`).joi
|
|
|
5999
7161
|
driven. Read
|
|
6000
7162
|
when \`start\` spawns the supervisor; see
|
|
6001
7163
|
\`guide metro\`.
|
|
7164
|
+
devices.idleShutdownMinutes
|
|
7165
|
+
minutes an owned simulator or emulator stays idle
|
|
7166
|
+
(\`gc --idle\` conditions; an open stim-server viewer
|
|
7167
|
+
counts as activity) before its workspace's supervisor
|
|
7168
|
+
shuts it down, never deletes it. Default 0, never.
|
|
7169
|
+
Machine or project layers. Read when the supervisor
|
|
7170
|
+
starts; see \`guide lifecycle budget\`.
|
|
7171
|
+
macos.product the explicit Swift Package executable product built in
|
|
7172
|
+
Debug by stim macos
|
|
7173
|
+
macos.infoPlist development Info.plist relative to Package.swift,
|
|
7174
|
+
without shared URL schemes or an update feed
|
|
7175
|
+
macos.arguments string array passed directly to the executable;
|
|
7176
|
+
see stim guide macos for the prototype's limits
|
|
7177
|
+
macos.assetCatalog optional .xcassets directory relative to Package.swift,
|
|
7178
|
+
compiled into Contents/Resources before signing
|
|
7179
|
+
macos.resources map of Contents/Resources destinations to source paths
|
|
7180
|
+
relative to Package.swift; see stim guide macos
|
|
6002
7181
|
web.url the page \`stim web\` opens in the owned Chrome, an
|
|
6003
7182
|
http:// or https:// URL. {port:<label>} becomes the
|
|
6004
7183
|
workspace's named port (allocated like \`stim ports
|
|
@@ -6178,7 +7357,9 @@ to its app path for every command it starts.
|
|
|
6178
7357
|
THE DEVICE POOL BOUNDS ARE MACHINE-LEVEL TOO
|
|
6179
7358
|
\`pool.iosParkedMax\` caps how many parked simulators \`worktree remove\` may
|
|
6180
7359
|
leave behind for a later workspace to adopt. It is machine-level for the same
|
|
6181
|
-
reason: the disk they sit on is the whole machine's
|
|
7360
|
+
reason: the disk they sit on is the whole machine's. A parked simulator keeps
|
|
7361
|
+
its apps and data, so it can hold gigabytes until \`stim gc --cache parked
|
|
7362
|
+
--delete\` erases it.
|
|
6182
7363
|
|
|
6183
7364
|
{
|
|
6184
7365
|
"pool": { "iosParkedMax": 3 }
|
|
@@ -6205,6 +7386,211 @@ test suites and the end-to-end harness use one -- and a scoped config must not
|
|
|
6205
7386
|
leave simulators on the machine it cannot account for. A redirected home that
|
|
6206
7387
|
wants a pool says so with the variable.
|
|
6207
7388
|
|
|
7389
|
+
HOSTING MACHINES ARE MACHINE-LEVEL
|
|
7390
|
+
\`hosting.machines\` names Macs that may host owned simulator sessions,
|
|
7391
|
+
by MagicDNS name with an optional serve port (default 7443). Name each node
|
|
7392
|
+
and port once. This is separate from build offloading:
|
|
7393
|
+
|
|
7394
|
+
stim settings set hosting.machines '["janics-mac-mini"]'
|
|
7395
|
+
stim doctor --fix
|
|
7396
|
+
|
|
7397
|
+
Only \`doctor --fix\` asks for device-host access. A person on that Mac
|
|
7398
|
+
approves the printed id with \`stim-server devices grant <id> --device-host\`.
|
|
7399
|
+
Hosting grants include no read, control or build capability.
|
|
7400
|
+
$STIM_HOME/device-host-machines.json stores a private token and pinned tailnet
|
|
7401
|
+
node. Doctor never prints the token; it reports each machine under deviceHosts
|
|
7402
|
+
in JSON. Stim sends tokens only to the pinned node's own tailnet address,
|
|
7403
|
+
with its MagicDNS name for TLS and Host routing. A changed node refuses
|
|
7404
|
+
access, and uncertain replies or unreadable credentials preserve the pin.
|
|
7405
|
+
Invalid hosting settings report an error and preserve every saved credential.
|
|
7406
|
+
\`doctor --fix\` forgets entries removed from hosting.machines; remove a
|
|
7407
|
+
replaced machine, run it, then re-add the name to request a new approval.
|
|
7408
|
+
A definite revoked or lapsed request can be requested again with --fix.
|
|
7409
|
+
An in-progress approval inspection reports busy rather than replacing its
|
|
7410
|
+
pending token. \`stim macos --remote <machine>\` runs a macOS app on an
|
|
7411
|
+
approved machine (stim guide macos); \`ios\` and \`android\` do not yet
|
|
7412
|
+
place sessions on these machines. To view or control a hosted macOS app, a person
|
|
7413
|
+
on that Mac approves Screen & System Audio Recording and Device Control and Data
|
|
7414
|
+
Access (Accessibility on macOS 26 and earlier) for Stim Host, the app
|
|
7415
|
+
\`stim-server service install\` runs the server under.
|
|
7416
|
+
|
|
7417
|
+
On a hosting Mac, \`hosting.agentDriver\` names the tool it starts so a
|
|
7418
|
+
client's coding agent can drive the macOS apps it hosts for that client.
|
|
7419
|
+
The default, \`none\`, starts nothing. \`agent-device\` starts its
|
|
7420
|
+
daemon only when that agent-device can lease a single macOS app (its
|
|
7421
|
+
\`macos-app\` lease backend); otherwise agent control reports \`none\` with a
|
|
7422
|
+
notice, and no client is handed the Mac's desktop. STIM_AGENT_DEVICE_BIN in
|
|
7423
|
+
stim-server's environment names an agent-device binary to use instead of
|
|
7424
|
+
~/.local/bin/agent-device. \`doctor\` on that Mac notes a hosted macOS app that runs while
|
|
7425
|
+
the setting is \`none\`.
|
|
7426
|
+
|
|
7427
|
+
BUILD MACHINES ARE MACHINE-LEVEL
|
|
7428
|
+
\`offload.machines\` lists the Macs on the tailnet that may build for this one,
|
|
7429
|
+
by MagicDNS name, each optionally with the port of its \`tailscale serve\`
|
|
7430
|
+
route (default 7443):
|
|
7431
|
+
|
|
7432
|
+
{
|
|
7433
|
+
"offload": { "machines": ["janics-mac-mini"] }
|
|
7434
|
+
}
|
|
7435
|
+
|
|
7436
|
+
\`doctor --fix\`, run in any app directory, asks each named Mac for build access and pins its tailnet
|
|
7437
|
+
node in $STIM_HOME/build-machines.json. A person approves the request on that
|
|
7438
|
+
Mac with \`stim-server devices grant <id> --build\`; doctor prints the id.
|
|
7439
|
+
Stim connects to a named Mac only while its name still belongs to the pinned
|
|
7440
|
+
node and never sends the token to another node. \`doctor\` reports each
|
|
7441
|
+
machine's pairing state and, for an approved machine, asks it for one build
|
|
7442
|
+
offer and lists every reason it would not take this app's iOS build: no
|
|
7443
|
+
answer, another Stim build, CPU, Xcode, simulator SDK or CocoaPods, no
|
|
7444
|
+
Bundler for an app whose Gemfile.lock pins CocoaPods, no iPhone simulator on
|
|
7445
|
+
the runtime \`stim ios\` builds for here, low disk, or busy. With Package.swift
|
|
7446
|
+
and no platform selection on a Mac, it also checks the macOS Xcode and SDK.
|
|
7447
|
+
|
|
7448
|
+
\`offload.machine\` (machine scope, default auto) selects placement for
|
|
7449
|
+
\`ios\`, \`android\` and \`macos\`. \`--build-machine <auto|local|name>\`
|
|
7450
|
+
overrides STIM_OFFLOAD_MACHINE, which overrides the setting. A blank environment
|
|
7451
|
+
value is unset. Trimmed auto/local are case-insensitive. Machine names match
|
|
7452
|
+
configured names case-insensitively, with port 7443 when omitted; reports use
|
|
7453
|
+
the configured entry.
|
|
7454
|
+
|
|
7455
|
+
auto follows offload.mode and keeps its local fallback behavior
|
|
7456
|
+
local builds only on this Mac for this invocation
|
|
7457
|
+
name requires a matching entry in offload.machines, already paired and
|
|
7458
|
+
approved; ignores offload.mode and this Mac's load/slot gating
|
|
7459
|
+
|
|
7460
|
+
A name never prompts for pairing, tries another machine or falls back here.
|
|
7461
|
+
Any refusal or failure is STIM_OFFLOAD_REFUSED with the machine and reason.
|
|
7462
|
+
Device, Release/non-Debug, --remote, Android CAS compiler builds, builds with
|
|
7463
|
+
the build cache off, and an unknown simulator runtime cannot build there and
|
|
7464
|
+
refuse on a cache miss. Invalid values, unlisted names and unpaired names refuse
|
|
7465
|
+
at setup before consulting the cache; they create no build record or failed-run
|
|
7466
|
+
stats. A listed paired name with a cache hit compiles nothing, contacts no worker
|
|
7467
|
+
and records buildMachine with builtOn absent. Build failures retain the selected
|
|
7468
|
+
value; builtOn is here or the configured machine entry when compilation ran.
|
|
7469
|
+
Prebuild and pod install still run on this Mac before a named offload. A refusal
|
|
7470
|
+
starts no local xcodebuild, Gradle or SwiftPM compile.
|
|
7471
|
+
Run stim doctor --fix to ask for build access if not paired. A person on the
|
|
7472
|
+
worker finds the id with stim-server devices and approves it with
|
|
7473
|
+
stim-server devices grant <id> --build. Check stim settings get offload.machines,
|
|
7474
|
+
or rerun with --build-machine auto or --build-machine local. Ctrl-C cancels
|
|
7475
|
+
the remote build without a local xcodebuild, Gradle or SwiftPM compile.
|
|
7476
|
+
|
|
7477
|
+
Copyable agent prompt:
|
|
7478
|
+
Run stim ios --build-machine janics-mac-mini. If it refuses, report the
|
|
7479
|
+
STIM_OFFLOAD_REFUSED reason and remedy; do not retry with another placement.
|
|
7480
|
+
|
|
7481
|
+
With offload.machine auto, \`offload.mode\` decides where an iOS simulator Debug build or an Android
|
|
7482
|
+
emulator debug build or a stim macos SwiftPM Debug build compiles:
|
|
7483
|
+
|
|
7484
|
+
auto (default) here while this Mac has capacity: a free
|
|
7485
|
+
\`concurrency.maxBuilds\` slot (always, with no build limit) and a
|
|
7486
|
+
load per core under \`offload.maxLoadPerCore\`. Otherwise on a
|
|
7487
|
+
build machine that accepts the build and is expected to be faster:
|
|
7488
|
+
while every slot here is busy, any machine that accepts; while only
|
|
7489
|
+
the load is high, a machine whose load per core is lower than this
|
|
7490
|
+
Mac's. A machine too old to report its load counts only while every
|
|
7491
|
+
slot here is busy.
|
|
7492
|
+
force on a build machine whenever one accepts it
|
|
7493
|
+
off always here
|
|
7494
|
+
|
|
7495
|
+
Load per core is the 5-minute load average divided by the CPU count; a Mac's
|
|
7496
|
+
native builds are its Stim runs in prebuild, pods or compile on that Mac, not
|
|
7497
|
+
the ones it offloaded.
|
|
7498
|
+
\`offload.maxLoadPerCore\` (default 2) is the load per core at which a Mac
|
|
7499
|
+
counts as saturated, both here and on a build machine.
|
|
7500
|
+
|
|
7501
|
+
STIM_OFFLOAD_MODE overrides it for one command. Device, Release and
|
|
7502
|
+
\`--remote\` builds, Android builds with the Apple Clang CAS compiler cache,
|
|
7503
|
+
and iOS/Android runs with the build cache off, always build here.
|
|
7504
|
+
macOS uses matching Xcode and macOS SDK, with a per-client SwiftPM dependency
|
|
7505
|
+
cache on the worker. It skips JavaScript dependencies, prebuild and pods; see
|
|
7506
|
+
stim guide macos. macOS artifacts are not cached.
|
|
7507
|
+
An offloaded iOS/Android build first runs prebuild (and \`pod install\` for iOS) here,
|
|
7508
|
+
then asks every paired machine what it can build. It takes one whose Stim
|
|
7509
|
+
build and CPU match this Mac exactly, with enough disk, that does not decline,
|
|
7510
|
+
preferring the one that already holds this repository, then the least loaded,
|
|
7511
|
+
and moves to the next one in that order when a machine that offered fails the
|
|
7512
|
+
sync or refuses to start the build.
|
|
7513
|
+
For iOS the machine's Xcode and simulator SDK must match, and it needs an
|
|
7514
|
+
iPhone simulator on the target runtime. Its CocoaPods must match too, unless
|
|
7515
|
+
the app's Gemfile.lock pins CocoaPods: both Macs then run that version through
|
|
7516
|
+
bundler, so the machine needs only Bundler on its stim-server PATH and
|
|
7517
|
+
installs the pinned gems itself on the first build. For Android its JDK major
|
|
7518
|
+
version must match, and its Android SDK must hold the NDK, build-tools and
|
|
7519
|
+
compile platform that the project's React Native version names in
|
|
7520
|
+
gradle/libs.versions.toml; Gradle and AGP come from the synced project. When
|
|
7521
|
+
no machine takes the build in auto placement, the run prints one line with each machine's
|
|
7522
|
+
reason, such as \`janics-mac-mini: busy (load at or above 2/core; load
|
|
7523
|
+
8.2/core, 2 builds) -> building here\`, and compiles here. It sends the files
|
|
7524
|
+
\`git ls-files -co --exclude-standard\` lists (the machine keeps only files it
|
|
7525
|
+
lacks, so an unignored secret such as \`.env\` is sent too), builds with
|
|
7526
|
+
Stim's own code there, refuses unless the fingerprint there equals this one,
|
|
7527
|
+
and brings the \`.app\` or APK back. An Android build there uses this Mac's
|
|
7528
|
+
variant, target ABI, Gradle build cache, PCH and compiler cache choices, so
|
|
7529
|
+
the APK matches the ABI-narrowed cache key. Stim checks the archive's sha256
|
|
7530
|
+
and fingerprints this checkout again before it stores the app under the
|
|
7531
|
+
post-mutation key and installs it the usual way, so an APK is still compared
|
|
7532
|
+
with the installed one before an install is skipped.
|
|
7533
|
+
While a machine builds, Stim pings it every 15 seconds and treats a minute of
|
|
7534
|
+
silence as a dropped connection while it waits for the build. When the
|
|
7535
|
+
connection drops without a close, Stim reconnects to the same pinned machine
|
|
7536
|
+
for up to 3 minutes and takes the running build back; the machine keeps it
|
|
7537
|
+
running for 5 minutes without a connection, then cancels it. With auto, a drop while the
|
|
7538
|
+
app is fetched still builds here. Interrupting the run with Ctrl-C closes the
|
|
7539
|
+
connection, which cancels the build there; a run killed another way, or
|
|
7540
|
+
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>
|
|
7541
|
+
-> building here\` and compiles here. The run's lastBuilds entry and
|
|
7542
|
+
\`--json\` facts record the reason as offloadFallback. Offloading holds no
|
|
7543
|
+
local build slot; that fallback takes one. \`stim stats\` keeps where each
|
|
7544
|
+
compiling build ran and why (\`guide facts stats\`).
|
|
7545
|
+
An offloaded app lands only in this Mac's build cache, not in a remote cache
|
|
7546
|
+
provider. A project whose xcodebuild changes its own fingerprinted inputs
|
|
7547
|
+
fails the offload fingerprint check: auto builds here; a named placement refuses.
|
|
7548
|
+
|
|
7549
|
+
On the build machine, stim-server keeps each client's checkouts, dependencies,
|
|
7550
|
+
DerivedData, compilation cache, ccache and Gradle home under
|
|
7551
|
+
\`offload.workerRoot\` (absolute; default $STIM_HOME/build-worker), one area
|
|
7552
|
+
and Stim home per client and repository, and one Gradle home per client. It
|
|
7553
|
+
runs one offloaded build at a time. It declines a build while that volume has
|
|
7554
|
+
less than 10 GB free, while its own native builds and the offloaded one fill
|
|
7555
|
+
its \`concurrency.maxBuilds\`, or while its load per core is at or above its
|
|
7556
|
+
\`offload.maxLoadPerCore\`, and reports the reason in its offer. It boots and
|
|
7557
|
+
installs nothing. While an offloaded build runs there, it holds one of that
|
|
7558
|
+
Mac's \`concurrency.maxBuilds\` slots, so a local Stim build there waits for it
|
|
7559
|
+
and counts it as busy; the slot is freed when the build ends, is cancelled or
|
|
7560
|
+
its process is gone. Android builds there run on the JDK in stim-server's
|
|
7561
|
+
JAVA_HOME (else the macOS default JDK) with the SDK in its ANDROID_HOME (else
|
|
7562
|
+
~/Library/Android/sdk). The client's Gradle daemon stays warm there for
|
|
7563
|
+
\`offload.gradleDaemonIdleMinutes\` (default 30; 0 stops it when each build
|
|
7564
|
+
ends; a new value applies from the next daemon) and never holds a build
|
|
7565
|
+
slot. While none of the client's builds runs, stim-server stops it sooner
|
|
7566
|
+
when a build is cancelled, the client is revoked, or the machine has less
|
|
7567
|
+
than 2 GB of memory available, and deleting the client's directory under the
|
|
7568
|
+
worker root stops it too.
|
|
7569
|
+
\`stim-server service install [--serve]\` on the build machine keeps stim-server
|
|
7570
|
+
running as a login LaunchAgent; \`--path-prepend <dir>\` and \`--env KEY=VALUE\`
|
|
7571
|
+
pin a PATH entry or variable such as a private CocoaPods that stim-server's
|
|
7572
|
+
login-shell environment would otherwise replace.
|
|
7573
|
+
\`stim-server service update --release <version>\` there moves the service to
|
|
7574
|
+
that exact stim-server release from the public npm registry once npm verifies
|
|
7575
|
+
its integrity and registry signatures; \`--from <dir>\` installs the packed
|
|
7576
|
+
packages of a checkout instead. It waits for offloaded builds and hosted
|
|
7577
|
+
sessions to finish, restarts the job, and switches back when the new server
|
|
7578
|
+
does not answer within 90 seconds; \`stim-server service rollback\` returns
|
|
7579
|
+
to the previous server. A client Mac approved for builds or device hosting
|
|
7580
|
+
there can ask for the same update over its tailnet connection: an npm release
|
|
7581
|
+
always, and its own packed build only while \`server.acceptClientBuilds\` is
|
|
7582
|
+
true on that machine (default false).
|
|
7583
|
+
Installation alone does not prove readiness; check \`stim-server service status\`
|
|
7584
|
+
and its reported log. The server listens before it touches the Stim home; a
|
|
7585
|
+
read-only child process reads the Stim home, server and recording directories,
|
|
7586
|
+
and until it returns \`/health\` answers 503 with \`startup.state\` pending.
|
|
7587
|
+
A read that does not return within 10 seconds, plus up to one second to stop
|
|
7588
|
+
the child, leaves the server listening and degraded, with the reason in
|
|
7589
|
+
\`/health\` and \`stim-server service status\`, and no status followers,
|
|
7590
|
+
watchers, native helpers or recorder; it reads again every 30 seconds and
|
|
7591
|
+
serves once a read returns. It preserves claims and recordings; it does not
|
|
7592
|
+
restore an inaccessible volume or grant filesystem access.
|
|
7593
|
+
|
|
6208
7594
|
THE GC WORKTREE GRACE PERIOD IS MACHINE-LEVEL
|
|
6209
7595
|
\`gc.worktreeGraceMinutes\` is how long \`gc --delete\` waits before it removes
|
|
6210
7596
|
a merged or idle linked worktree. The clock starts at the worktree's latest
|
|
@@ -6221,6 +7607,25 @@ which overrides the file. Absent means 120. \`0\` removes a finished worktree at
|
|
|
6221
7607
|
once. A value that is not a whole number 0 or more is warned about by \`gc\`,
|
|
6222
7608
|
which then uses 120. See \`guide cleanup\` for what else keeps a worktree.
|
|
6223
7609
|
|
|
7610
|
+
DEVICE RECORDINGS CAN BE TURNED OFF
|
|
7611
|
+
stim-server records the screens of owned simulators, emulators and the
|
|
7612
|
+
Stim-owned Chrome page while an automation tool drives them or a paired client
|
|
7613
|
+
watches them, so the phone app can scrub back through them. It keeps the last
|
|
7614
|
+
15 minutes of footage per device under $STIM_HOME/workspaces/<id>/recordings/,
|
|
7615
|
+
on this Mac only, and serves it only to paired clients. \`recording.enabled\`
|
|
7616
|
+
turns it off:
|
|
7617
|
+
|
|
7618
|
+
stim settings set recording.enabled false --scope machine
|
|
7619
|
+
|
|
7620
|
+
It is a boolean, true by default, set at machine, repo or workspace scope; the
|
|
7621
|
+
workspace layer wins, then repo, then machine. STIM_RECORDING (true, false, 1
|
|
7622
|
+
or 0) overrides every layer; stim-server reads it from its own environment when
|
|
7623
|
+
it starts. Turning it off takes effect at once: \`stim settings set\` deletes
|
|
7624
|
+
the recordings of every workspace it turns off, and stim-server stops recording
|
|
7625
|
+
them within seconds, as soon as \`stim status\` shows recording.enabled false
|
|
7626
|
+
for them. Turning it back on records from then on. See \`guide cleanup\` for
|
|
7627
|
+
how recordings are removed otherwise.
|
|
7628
|
+
|
|
6224
7629
|
STIM NEEDS NO PROJECT CHANGES TO RUN
|
|
6225
7630
|
Nothing above is required to use Stim. The performance caches that used to
|
|
6226
7631
|
be setup steps are supplied by Stim on the command lines it composes itself:
|
|
@@ -6516,7 +7921,7 @@ An https:// URL on a plain HTTP server fails with ERR_SSL_PROTOCOL_ERROR, and
|
|
|
6516
7921
|
an http:// URL on an HTTPS server with ERR_EMPTY_RESPONSE; the remedy line
|
|
6517
7922
|
prints the settings command that switches the scheme. web.viewport phone
|
|
6518
7923
|
gives the page a 390x844 touch screen at 3x instead of the 1280x800 desktop
|
|
6519
|
-
|
|
7924
|
+
page. See stim guide settings.
|
|
6520
7925
|
|
|
6521
7926
|
MONOREPO RECIPE: A VITE PACKAGE WITH A BASE PATH AND HTTPS
|
|
6522
7927
|
|
|
@@ -6576,11 +7981,28 @@ platform "web":
|
|
|
6576
7981
|
page document is error whatever its
|
|
6577
7982
|
status; another request's network failure or HTTP 5xx is
|
|
6578
7983
|
error, a 4xx warn, a canceled request debug; browser messages such
|
|
6579
|
-
as CSP violations; the browser's own lifecycle
|
|
7984
|
+
as CSP violations; the browser's own lifecycle; in-app
|
|
7985
|
+
route changes (history API or fragment) as web_route with
|
|
7986
|
+
url, at most four a second
|
|
7987
|
+
src agent input a DevTools client other than Stim's sent to the page
|
|
7988
|
+
while connected, as agent_action with driver (the tool),
|
|
7989
|
+
command click, type, press or scroll, msg (e.g. 'Clicked
|
|
7990
|
+
button "Save"', "Typed 12 characters into input#email"),
|
|
7991
|
+
deviceId (web.targetId) and details. Typed text is never
|
|
7992
|
+
recorded, only its length. Clicks, key presses, text input
|
|
7993
|
+
and wheel scrolls in the top frame are observed; pointer
|
|
7994
|
+
moves and iframes are not, and a typing, key or scroll burst
|
|
7995
|
+
is one record. Input from Stim Desktop's or the phone's Take
|
|
7996
|
+
over is not recorded, nor is other input within 3 seconds of
|
|
7997
|
+
it, nor input while no other client is connected. A person
|
|
7998
|
+
clicking a headed window while a tool is connected counts as
|
|
7999
|
+
that tool, and a script that disconnects within a few hundred
|
|
8000
|
+
milliseconds of its first input goes unrecorded
|
|
6580
8001
|
Expo also prints web console calls on Metro ("Web LOG"), so they can appear
|
|
6581
8002
|
twice: once from the page (client), once from Metro (metro, level info).
|
|
6582
8003
|
Each load of the page's top-level document is a page-load marker: stim web,
|
|
6583
|
-
stim reload, and a reload or navigation the page makes itself.
|
|
8004
|
+
stim reload, and a reload or navigation the page makes itself. A reload's
|
|
8005
|
+
marker carries reload: true. logs --errors
|
|
6584
8006
|
and the status error count report the page's records (platform web) from the
|
|
6585
8007
|
latest load only, the way Chrome DevTools clears its console when the page
|
|
6586
8008
|
navigates. A page load does not hide the native app's errors, and an ios or
|
|
@@ -6596,7 +8018,20 @@ them start their own browser:
|
|
|
6596
8018
|
The port reaches only the Stim profile: Chrome refuses remote debugging on
|
|
6597
8019
|
your default profile, and Stim never attaches to a browser it did not start.
|
|
6598
8020
|
The port is managed like metro: stim ports lists it as web-cdp (managed),
|
|
6599
|
-
and ports get, stop and release refuse it.
|
|
8021
|
+
and ports get, stop and release refuse it. While a tool is connected,
|
|
8022
|
+
status reports it as the browser's driver (web.activity, "driven by
|
|
8023
|
+
playwright"), the way it names agent-device on a simulator, and its clicks,
|
|
8024
|
+
typing and scrolls are agent actions: stim logs --source agent lists them,
|
|
8025
|
+
web.activity.recent["agent-action"] dates the newest, and Stim Desktop and
|
|
8026
|
+
the phone show them in the Web device's agent feed. web.targetId is the
|
|
8027
|
+
owned page; a tab another tool opens is not captured.
|
|
8028
|
+
|
|
8029
|
+
stim status also reports the document the page loaded last and how that
|
|
8030
|
+
load went: web.page { url, state: loading | loaded | failed, error?, route? }.
|
|
8031
|
+
route is the URL the page shows after an in-app route change since that
|
|
8032
|
+
load, and is absent when none moved it. A failed
|
|
8033
|
+
load (the dev server is down, a certificate error, a crash) shows on status's
|
|
8034
|
+
web line.
|
|
6600
8035
|
|
|
6601
8036
|
OWNERSHIP AND CLEANUP
|
|
6602
8037
|
|
|
@@ -6605,6 +8040,8 @@ claim for its lifetime; Chrome is the claim's child. Stim signals the
|
|
|
6605
8040
|
supervisor or Chrome only after verifying the process identity it recorded.
|
|
6606
8041
|
stim stop closes Chrome and keeps the profile, so cookies and
|
|
6607
8042
|
storage survive the next stim web
|
|
8043
|
+
stim stop --slot web closes only Chrome, keeping the profile, Metro and
|
|
8044
|
+
every device
|
|
6608
8045
|
stim worktree remove closes Chrome and deletes the profile
|
|
6609
8046
|
stim gc --delete does the same for a workspace whose path is gone,
|
|
6610
8047
|
and forgets ledger entries of deleted profiles
|
|
@@ -6614,10 +8051,385 @@ which would otherwise block the next launch. status reports
|
|
|
6614
8051
|
browser-unverified when an identity cannot be proven; follow stim guide
|
|
6615
8052
|
errors teardown.
|
|
6616
8053
|
|
|
8054
|
+
STIM DESKTOP
|
|
8055
|
+
|
|
8056
|
+
Stim Desktop shows the owned Chrome as a Web tile next to the simulators,
|
|
8057
|
+
with live frames of the page read from its DevTools endpoint, the page's URL,
|
|
8058
|
+
a "Page failed to load" pill and "Driven by" when a tool is attached. Take
|
|
8059
|
+
over sends clicks, scrolls and keys to the page. The tile's buttons open the
|
|
8060
|
+
URL in your own browser (never the Stim profile), run stim reload web, and
|
|
8061
|
+
close the browser with stim stop --slot web.
|
|
8062
|
+
|
|
8063
|
+
THE PHONE APP
|
|
8064
|
+
|
|
8065
|
+
The phone app shows the page as a Web tile in the devices grid and the
|
|
8066
|
+
workspace screen, and opens it in the device viewer as H.264 video through
|
|
8067
|
+
stim-server. With Control on, taps click, drags scroll and the keyboard types;
|
|
8068
|
+
Back goes back in the page's history. Reload in the workspace menu runs stim
|
|
8069
|
+
reload web. While stim-server recorded the page (\`guide settings\`, device
|
|
8070
|
+
recordings), the viewer can scrub back through it, with the agent's actions
|
|
8071
|
+
as markers.
|
|
8072
|
+
|
|
6617
8073
|
LIMITS
|
|
6618
8074
|
|
|
6619
|
-
Chrome and Chromium only.
|
|
6620
|
-
|
|
8075
|
+
Chrome and Chromium only. The owned page is one tab: a page the app opens in
|
|
8076
|
+
a new window is not captured.`
|
|
8077
|
+
},
|
|
8078
|
+
macos: {
|
|
8079
|
+
summary: "Swift Package macOS Debug apps: owned bundle, logs, local window viewing, and --remote on another Mac",
|
|
8080
|
+
body: () => `MACOS: SWIFT PACKAGE DEVELOPMENT PROTOTYPE
|
|
8081
|
+
|
|
8082
|
+
If Stim is not installed globally, replace stim with npx stim.
|
|
8083
|
+
|
|
8084
|
+
Run from the directory containing Package.swift. This prototype builds one
|
|
8085
|
+
explicit executable product in Debug, creates an isolated development app,
|
|
8086
|
+
and launches it. It does not use package scripts, Metro, simulators, Xcode
|
|
8087
|
+
projects, release builds, provisioning, or store distribution.
|
|
8088
|
+
|
|
8089
|
+
Set macos.product and macos.infoPlist explicitly in .stim.json. Optional
|
|
8090
|
+
macos.arguments is an array of arguments passed directly to the executable:
|
|
8091
|
+
|
|
8092
|
+
{ "macos": { "product": "MyApp", "infoPlist": "Support/Info-Development.plist" } }
|
|
8093
|
+
|
|
8094
|
+
The plist must contain CFBundleIdentifier and CFBundleExecutable matching the
|
|
8095
|
+
product. Use a development plist without shared URL schemes or an update feed.
|
|
8096
|
+
Stim gives the copied bundle a workspace-specific identifier. SwiftPM resource
|
|
8097
|
+
bundles and frameworks in the reported build directory are copied into it;
|
|
8098
|
+
macos.resources maps destinations under Contents/Resources to file or directory
|
|
8099
|
+
sources relative to the Swift Package directory, for example:
|
|
8100
|
+
|
|
8101
|
+
{ "macos": { "resources": { "AppIcon.icns": "Support/AppIcon-Dev.icns" },
|
|
8102
|
+
"assetCatalog": "Support/Assets.xcassets" } }
|
|
8103
|
+
|
|
8104
|
+
macos.assetCatalog selects an optional .xcassets directory compiled by fixed
|
|
8105
|
+
xcrun actool arguments. Copies and the compiled Assets.car are added before
|
|
8106
|
+
ad hoc signing. If actool does not emit Assets.car, staging refuses; set
|
|
8107
|
+
LSMinimumSystemVersion in the plist when Xcode requires a deployment target.
|
|
8108
|
+
Destinations are non-empty relative paths without empty, . or
|
|
8109
|
+
.. segments, at most 1024 characters, and cannot overlap another resource,
|
|
8110
|
+
Assets.car when a catalog is set, or a SwiftPM resource bundle. At most 256
|
|
8111
|
+
resources are allowed. Sources must exist as regular files or directories;
|
|
8112
|
+
their realpaths must stay inside the git repository root, or the Swift Package
|
|
8113
|
+
directory when there is no git root, and a source directory cannot contain
|
|
8114
|
+
symbolic links. Sources can use ../ to reach other files
|
|
8115
|
+
inside that repository. Custom packaging scripts are not run and extra
|
|
8116
|
+
executables, including Stim Desktop's sim-fold helper, are not built.
|
|
8117
|
+
|
|
8118
|
+
stim macos # fixed SwiftPM Debug build, then launch
|
|
8119
|
+
stim macos --json # one launch record; progress goes to stderr
|
|
8120
|
+
stim status --json # environments[].macos and build state
|
|
8121
|
+
stim logs --source build
|
|
8122
|
+
stim logs --errors
|
|
8123
|
+
stim stop # stop this workspace's owned app and supervisor
|
|
8124
|
+
|
|
8125
|
+
Each invocation stops its previous owned app, rebuilds and launches. SwiftPM
|
|
8126
|
+
keeps incremental outputs in the workspace's runtime directory under STIM_HOME.
|
|
8127
|
+
Local stim macos starts the app in the background without activating it or
|
|
8128
|
+
changing focus: it sets STIM_BACKGROUND_LAUNCH=1 in the app's environment, which
|
|
8129
|
+
Stim Desktop honors. An app that activates itself at launch still takes focus.
|
|
8130
|
+
Hosted launches (macos --remote) do not set it.
|
|
8131
|
+
macOS artifacts are not cached. This prototype has no --plan, --slot, or reload command.
|
|
8132
|
+
A failed build records its error and compiler output without launching an app.
|
|
8133
|
+
Runtime stdout and stderr become client records; build output becomes build
|
|
8134
|
+
records, all with platform "macos". Unexpected app exits are error records.
|
|
8135
|
+
Stim runs the app with NSUnbufferedIO=YES, so Swift print output arrives per
|
|
8136
|
+
line instead of when the app exits.
|
|
8137
|
+
|
|
8138
|
+
BUILD OFFLOAD
|
|
8139
|
+
|
|
8140
|
+
--build-machine <auto|local|name> overrides STIM_OFFLOAD_MACHINE and the
|
|
8141
|
+
machine setting offload.machine (default auto). local builds here; a name
|
|
8142
|
+
requires the matching configured and paired build machine, ignoring offload.mode
|
|
8143
|
+
and this Mac's capacity. Any failure is STIM_OFFLOAD_REFUSED without a local xcodebuild, Gradle or
|
|
8144
|
+
SwiftPM compile or another machine. Invalid, unlisted or unpaired selections
|
|
8145
|
+
refuse before stopping the app or changing its build record. See stim guide errors STIM_OFFLOAD_REFUSED.
|
|
8146
|
+
|
|
8147
|
+
With offload.machine auto, offload.mode places these SwiftPM Debug builds:
|
|
8148
|
+
auto builds here while this Mac has capacity, force uses an approved build
|
|
8149
|
+
machine when one accepts, and off always builds here. Configure offload.machines
|
|
8150
|
+
and approve build access as described in stim guide settings. The worker needs
|
|
8151
|
+
matching Stim, CPU architecture, Xcode and macOS SDK, plus network access to
|
|
8152
|
+
fetch package dependencies the first time. It keeps SwiftPM dependencies in a
|
|
8153
|
+
per-client cache and incremental outputs per repository. It runs fixed swift
|
|
8154
|
+
build commands without JavaScript installs, prebuild or pods. It receives only
|
|
8155
|
+
the files git lists (tracked and untracked, not ignored). Resource and catalog
|
|
8156
|
+
sources must be among those files. The client sends resolved sources relative
|
|
8157
|
+
to the repository root; the worker resolves them inside its checkout.
|
|
8158
|
+
|
|
8159
|
+
The client validates the development plist and resource entries before asking a machine. It verifies
|
|
8160
|
+
the returned archive's sha256, bundle identifier, executable, declared resources
|
|
8161
|
+
and ad hoc signature
|
|
8162
|
+
before replacing the owned bundle, then launches locally as usual. With auto, every offload
|
|
8163
|
+
failure falls back to the local build, including force. Failed staging preserves
|
|
8164
|
+
the previous bundle. No failed artifact is promoted or cached. The build record
|
|
8165
|
+
is unchanged by invalid, unlisted or unpaired setup refusals. It
|
|
8166
|
+
carries buildMachine (selected) and builtOn (here or the machine, absent until
|
|
8167
|
+
a build runs), and errorCode on a typed failure. It carries offloadedTo only for a remote build, and offloadFallback when an offload
|
|
8168
|
+
attempt falls back here. Placement and failure reasons are in build logs.
|
|
8169
|
+
|
|
8170
|
+
OWNERSHIP AND LOCAL VIEWING
|
|
8171
|
+
|
|
8172
|
+
The supervisor owns a process-identity claim with the app as its child. Stop
|
|
8173
|
+
signals only verified recorded identities. An unverifiable or malformed owner
|
|
8174
|
+
refuses with STIM_MACOS_OWNER_UNVERIFIED; other apps remain untouched.
|
|
8175
|
+
|
|
8176
|
+
Stim Desktop shows this app in its workspace, with Build and run, Open app (local
|
|
8177
|
+
apps only) and Stop, and a live preview that updates itself while the app runs. Capture and Open app verify the recorded executable, bundle
|
|
8178
|
+
identifier, PID and process start time. Open app rechecks that the captured window
|
|
8179
|
+
is still the app's front window, or raises the pinned one, then activates that
|
|
8180
|
+
owned app for normal native input.
|
|
8181
|
+
The preview follows the app's front standard window, its main window with any
|
|
8182
|
+
attached sheet, as the app opens, switches, closes or resizes windows. It never
|
|
8183
|
+
captures another process's windows, menus or the desktop. Without Device Control
|
|
8184
|
+
and Data Access permission (Accessibility on macOS 26 and earlier) Stim cannot
|
|
8185
|
+
tell which window is in front, so the preview shows only an app whose one window
|
|
8186
|
+
contains the others. The viewer never captures its own process recursively.
|
|
8187
|
+
The captured view is read-only; background mouse/keyboard relay is not included.
|
|
8188
|
+
|
|
8189
|
+
Capture requires existing Screen & System Audio Recording permission (Screen
|
|
8190
|
+
Recording on macOS 14); Open app also requires Device Control and Data Access
|
|
8191
|
+
permission (Accessibility on macOS 26 and earlier). The first native viewer opening
|
|
8192
|
+
shows one Desktop setup screen for both permissions, named for this Mac's macOS
|
|
8193
|
+
version, with statuses, Request permissions, Settings and Check again. Settings
|
|
8194
|
+
opens the matching System Settings > Privacy & Security pane. You approve normal macOS requests; Stim never resets or grants access
|
|
8195
|
+
automatically. Permissions on the app card reopens setup. Builds never prompt.
|
|
8196
|
+
If unavailable, use the normal app window and
|
|
8197
|
+
read the workspace logs.
|
|
8198
|
+
|
|
8199
|
+
PHONE VIEWING AND CONTROL
|
|
8200
|
+
|
|
8201
|
+
A paired phone shows native app and build state in the workspace and home list.
|
|
8202
|
+
Tap the app tile to view the app's front window through stim-server. The server
|
|
8203
|
+
requires read access and its macos-window feature; it rechecks the recorded PID,
|
|
8204
|
+
process start time, executable and bundle before capture and on every frame.
|
|
8205
|
+
It never captures the desktop or another application, and offers no replay.
|
|
8206
|
+
The view follows the app's front standard window like the Desktop preview. A
|
|
8207
|
+
server advertising macos-windows also sends a macos-windows event on the frames
|
|
8208
|
+
subscription with the captured window and the app's windows (id, title and frame
|
|
8209
|
+
in points), after subscribing and whenever they change. Viewing starts only with
|
|
8210
|
+
an open window; after the app closes its last one the view reports a delay until
|
|
8211
|
+
another opens.
|
|
8212
|
+
|
|
8213
|
+
WINDOWS
|
|
8214
|
+
|
|
8215
|
+
The view follows the app's front window by default. A controller can pin it to
|
|
8216
|
+
one of the app's windows instead: with a server advertising macos-window-select,
|
|
8217
|
+
input.window { session, window: <id from macos-windows> } brings that window to
|
|
8218
|
+
the front of the app, and capture and input stay on it while another window
|
|
8219
|
+
comes forward on the Mac. input.window { session, window: null } follows the
|
|
8220
|
+
front window again. The pin also ends when its window closes or minimizes, or
|
|
8221
|
+
when the Control session that set it ends for any reason, including release,
|
|
8222
|
+
takeover, disconnect and the five-minute idle timeout, since viewers without
|
|
8223
|
+
Control cannot unpin; macos-windows reports pinned true while it holds. Choosing
|
|
8224
|
+
a window while the app shows a modal dialog, or one that just closed, is dropped
|
|
8225
|
+
without ending Control. The phone's Control toolbar and Stim Desktop's app card and
|
|
8226
|
+
hosted viewer offer the same choice as a Window menu: Follow front window, or a
|
|
8227
|
+
window by title. Stim Desktop's local preview pins without Control because it
|
|
8228
|
+
only raises the window among the app's own windows. An agent that drives the app
|
|
8229
|
+
through the stim-server protocol sends the same input.window.
|
|
8230
|
+
|
|
8231
|
+
A server advertising macos-window-control also supports the phone's Control
|
|
8232
|
+
mode on a control pairing. Tap/click and drag act on the displayed window;
|
|
8233
|
+
Scroll mode turns a drag into pixel scrolling. The main toolbar offers Keyboard
|
|
8234
|
+
and Scroll. Keyboard types printable ASCII and attaches an extra-key strip with
|
|
8235
|
+
Tab, Escape, Backspace, arrows and labeled Select all, Undo, Save, Copy, Paste,
|
|
8236
|
+
Cut and Find shortcuts. Shift, Control, Option and Command apply to the next
|
|
8237
|
+
supported key, then clear; dismissing the keyboard clears them too.
|
|
8238
|
+
|
|
8239
|
+
A newly built phone client with Keyboard Controller is required; this native
|
|
8240
|
+
change cannot be delivered to an older client by a JavaScript update. Servers
|
|
8241
|
+
advertising macos-keyboard-extended accept modified a-z and 0-9 one key at a
|
|
8242
|
+
time. Older servers retain fixed shortcuts and navigation but cannot receive
|
|
8243
|
+
other modified letters or digits. Multi-character modified input and symbols
|
|
8244
|
+
are not supported; ordinary typing keeps using input.text.
|
|
8245
|
+
|
|
8246
|
+
Shortcuts for a-z and 0-9 use the key that types the character in the Mac's
|
|
8247
|
+
current keyboard layout, using the Command layer when Command is held. Dvorak
|
|
8248
|
+
and Dvorak-QWERTY Command are supported. On Russian and similar layouts
|
|
8249
|
+
(Cyrillic, Greek, Hebrew, Arabic), Latin-letter shortcuts work with Command;
|
|
8250
|
+
Control-only or Option-only letter shortcuts are refused. With Control, the
|
|
8251
|
+
layout's Control table must also yield the requested character or, for letters,
|
|
8252
|
+
its C0 control character; otherwise the shortcut is refused. The Mac's selected
|
|
8253
|
+
input source is read on each key, so switching layouts takes effect on
|
|
8254
|
+
the next key. Letters and digits available only with Shift or Option, or through
|
|
8255
|
+
a dead key (for example digits on AZERTY), are refused with a reason naming the
|
|
8256
|
+
layout, and Control ends. Stim does not add modifiers to reach those characters.
|
|
8257
|
+
Ordinary typing and navigation do not depend on the layout. Symbols such as
|
|
8258
|
+
comma remain unsupported key names.
|
|
8259
|
+
Control posts input to the owned process without activating it or raising its
|
|
8260
|
+
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
|
|
8261
|
+
attached sheet) does Stim activate the app to deliver input, waiting up to one
|
|
8262
|
+
second for focus. The helper then sends a controlActivated notice, which stim-server
|
|
8263
|
+
logs.
|
|
8264
|
+
Clicks on views that reject the first mouse, such as custom views and SwiftUI
|
|
8265
|
+
onTapGesture regions, do not land while the app is in the background. Use
|
|
8266
|
+
Desktop's Open app to bring the app to the front for those views.
|
|
8267
|
+
Control holds one exclusive server session per owned app, ends on disconnect,
|
|
8268
|
+
revocation, takeover or five minutes without input, and does not take a CLI
|
|
8269
|
+
simulator/device lock. Each action rechecks the exact owned process and that the
|
|
8270
|
+
captured window is still the app's front standard window, or the pinned one. The
|
|
8271
|
+
app's other windows are allowed; input goes to the captured window, and a sheet attached to it takes
|
|
8272
|
+
focus and pointer input. Input that arrives while the view moves to another
|
|
8273
|
+
window is dropped and Control continues. A modal dialog window refuses input. A
|
|
8274
|
+
sheet larger than the captured window is not supported. Existing Device
|
|
8275
|
+
Control and Data Access permission (Accessibility on macOS 26 and earlier) is
|
|
8276
|
+
required. The phone and server never request or reset it. A refusal ends Control
|
|
8277
|
+
with its reason while viewing and logs remain usable. Older servers remain
|
|
8278
|
+
view-only.
|
|
8279
|
+
Native Control uses dynamically resolved private CoreGraphics input SPI in the
|
|
8280
|
+
server helper, outside the phone and Mac App Store app binaries. A missing symbol
|
|
8281
|
+
or incompatible macOS version refuses Control while viewing and logs remain usable.
|
|
8282
|
+
|
|
8283
|
+
The existing capture host needs Screen & System Audio Recording permission (Screen
|
|
8284
|
+
Recording on macOS 14). If capture is denied,
|
|
8285
|
+
open Permissions in Stim Desktop on the host Mac, approve its normal OS requests
|
|
8286
|
+
and reconnect the phone viewer. The Desktop host presents setup on the first
|
|
8287
|
+
native viewer opening, including one initiated by the phone. A server started
|
|
8288
|
+
outside Desktop uses that launching host's permissions; granting this copy of Stim
|
|
8289
|
+
may not apply to it. The server never requests or resets permissions. Status and logs still work. Tap the build card for
|
|
8290
|
+
SwiftPM output or the logs card for native runtime stdout and stderr. Metro is not
|
|
8291
|
+
used.
|
|
8292
|
+
|
|
8293
|
+
ON ANOTHER MAC
|
|
8294
|
+
|
|
8295
|
+
stim macos --remote <machine> builds the Debug app on this Mac and runs it on an
|
|
8296
|
+
approved hosting Mac over the tailnet, without SSH. The machine must be listed
|
|
8297
|
+
in hosting.machines and approved: stim doctor --fix asks it, a person on that
|
|
8298
|
+
Mac runs stim-server devices grant <id> --device-host, and stim doctor then
|
|
8299
|
+
records the approval. Stim connects only to the machine's pinned tailnet node.
|
|
8300
|
+
A refusal or an unreachable host fails the command; it never launches here
|
|
8301
|
+
instead.
|
|
8302
|
+
|
|
8303
|
+
The --remote value is a hosting Mac name from hosting.machines. macOS refuses
|
|
8304
|
+
eas and proxy because it has neither backend. It also refuses auto until
|
|
8305
|
+
automatic placement ships. These reserved names are case-insensitive and
|
|
8306
|
+
trimmed; they refuse with STIM_BAD_ARG before state access or a connection.
|
|
8307
|
+
|
|
8308
|
+
Logs: the host records the hosted app's stdout, stderr and exit like a local run.
|
|
8309
|
+
stim logs, including --errors, --json and --follow, asks the host over the same
|
|
8310
|
+
approved connection, copies the records it has not copied yet into the
|
|
8311
|
+
workspace's logs/macos-host.ndjson and prints them with the local records.
|
|
8312
|
+
stim stop copies the last ones, including the exit record, before it forgets the
|
|
8313
|
+
placement, so the logs stay readable after stop. A host that cannot answer, because
|
|
8314
|
+
it is unreachable or runs a stim-server that predates this, costs one stderr
|
|
8315
|
+
warning (after up to 10 seconds of connecting); stdout still carries the records already copied, so logs --json stays
|
|
8316
|
+
valid NDJSON. The host's unified log is not collected: os.Logger output that is
|
|
8317
|
+
not written to stderr does not appear.
|
|
8318
|
+
|
|
8319
|
+
Phones and Stim Desktop view and control the hosted app through this Mac's
|
|
8320
|
+
stim-server, which relays to the host. The phone sends clicks, scrolls,
|
|
8321
|
+
text and keys; Stim Desktop's Control sends clicks, drags and typed text.
|
|
8322
|
+
|
|
8323
|
+
stim macos --remote mini # build here, deliver, launch on mini
|
|
8324
|
+
stim macos --remote mini --json # { platform, product, launchId, build, host }
|
|
8325
|
+
stim status --json # environments[].macos.state and macos.host
|
|
8326
|
+
stim stop # stop the session on mini and confirm it
|
|
8327
|
+
stim worktree remove <path> # also stops it
|
|
8328
|
+
|
|
8329
|
+
The copy keeps the plist's own CFBundleIdentifier. The host runs it as
|
|
8330
|
+
<id>.hosted<slot> from a fixed slot pool, so the bundle id stays the same
|
|
8331
|
+
across rebuilds; whether macOS keeps the permissions the hosted app asks for
|
|
8332
|
+
itself also depends on how the host signs it. Running the
|
|
8333
|
+
command again reuses the session and delivers a new copy. macos.arguments are
|
|
8334
|
+
passed to the hosted launch as plain arguments, without environment injection:
|
|
8335
|
+
at most 32 arguments, 1024 characters each and 8192 characters total. Empty
|
|
8336
|
+
strings are allowed; NUL, CR and LF are refused. Values are stored in the host's
|
|
8337
|
+
app receipt and visible in process listings, so do not put secrets there. Older
|
|
8338
|
+
hosts ignore them and Stim warns to update stim-server on the host.
|
|
8339
|
+
status --json reports the host's applied arguments under
|
|
8340
|
+
environments[].macos.arguments.
|
|
8341
|
+
A workspace has one macOS app: a local stim macos
|
|
8342
|
+
refuses while it runs on a host, and --remote with a different machine refuses
|
|
8343
|
+
until stim stop. When the host cannot be reached or does not confirm the stop,
|
|
8344
|
+
the placement stays recorded; restore the connection and run stim stop again.
|
|
8345
|
+
|
|
8346
|
+
Viewing and controlling the hosted app from a phone needs Screen & System Audio
|
|
8347
|
+
Recording and Device Control and Data Access (Accessibility on macOS 26 and
|
|
8348
|
+
earlier), granted once on the host to the app that runs stim-server, not to the
|
|
8349
|
+
hosted app. stim-server service install runs the server under the signed Stim
|
|
8350
|
+
Host app (dev.stim.host, installed in ~/Applications) and shows
|
|
8351
|
+
macOS's own requests on that Mac's screen; a person there approves them.
|
|
8352
|
+
stim-server service status shows the grants, and stim doctor here reports an
|
|
8353
|
+
approved host that lacks them. A server started by Stim Desktop uses Desktop's
|
|
8354
|
+
grants.
|
|
8355
|
+
|
|
8356
|
+
Hosted delivery carries the staged bundle with declared resources and compiled assets.
|
|
8357
|
+
When offload built it on the host's own tailnet node, the host copies the files
|
|
8358
|
+
from the build it kept for this Mac instead of receiving them again, admitting
|
|
8359
|
+
only bytes that match the bundle's digests here. That needs both the build and
|
|
8360
|
+
the device-host approval for this Mac. Files the host cannot take, an older
|
|
8361
|
+
host, or a build fetched more than 10 minutes earlier are uploaded as before.
|
|
8362
|
+
|
|
8363
|
+
status --json reports macos.host { machine, session, appSlot, appAttempt,
|
|
8364
|
+
bundleId, agent }. For hosted placements only, status asks the host for the
|
|
8365
|
+
session state with about a 3 s timeout per connection and request and a 10 s
|
|
8366
|
+
cache. It reports stopped when the host says the session stopped, for example
|
|
8367
|
+
after a stim-server restart there, and unverified when the host is unreachable
|
|
8368
|
+
or cannot confirm. Run stim macos --remote <machine> to launch a stopped app
|
|
8369
|
+
again, or stim stop to clear or reconcile the placement. The host field stays
|
|
8370
|
+
in status --json for cleanup.
|
|
8371
|
+
While a placement is recorded, gc treats the workspace as in use. agent says
|
|
8372
|
+
how a coding agent drives the app: { driver: "none", setting:
|
|
8373
|
+
"hosting.agentDriver" } until the hosting Mac's owner sets that setting there,
|
|
8374
|
+
or { driver: "agent-device", remoteConfig, command }. remoteConfig is a mode
|
|
8375
|
+
0600 file in the workspace directory that holds the credential; run the command
|
|
8376
|
+
it names and never print the file. stop, worktree remove and gc first run
|
|
8377
|
+
agent-device close and disconnect for the connection that agent-device reports
|
|
8378
|
+
as connected to that remote config (the default or active session), then delete
|
|
8379
|
+
the file, so the next hosted workspace needs no manual disconnect. Any other
|
|
8380
|
+
connection, including one under another session name, stays untouched, and a
|
|
8381
|
+
failure or a missing agent-device is reported without blocking the stop. When
|
|
8382
|
+
the hosted session ends, stim-server removes its agent-device session
|
|
8383
|
+
directories under its own state directory. Start with agent-device open
|
|
8384
|
+
<bundleId> --remote-config <path>, using macos.host.bundleId; the lease allows
|
|
8385
|
+
open, close, snapshot, wait, find, get, is, click, fill, press, type, focus,
|
|
8386
|
+
scroll, screenshot and batch on that app only. The agent-device on this Mac must
|
|
8387
|
+
know the macos-app lease backend. A request outside that command list, or a
|
|
8388
|
+
method the relay does not forward, is refused with an agent-device UNAUTHORIZED
|
|
8389
|
+
error whose details.reason is STIM_AGENT_REQUEST_REFUSED and whose message names
|
|
8390
|
+
the refused command or method; do not retry it.
|
|
8391
|
+
|
|
8392
|
+
Agents that built, launched and drove test copies on another Mac with an SSH
|
|
8393
|
+
script such as mini-desktop.sh use these instead:
|
|
8394
|
+
|
|
8395
|
+
script command stim
|
|
8396
|
+
build stim macos --remote <mac>
|
|
8397
|
+
launch the same command; the host launches it under the
|
|
8398
|
+
session's own home
|
|
8399
|
+
shot <out.png> agent-device screenshot --remote-config <path>
|
|
8400
|
+
click, rclick, key, type agent-device click, press or type --remote-config <path>
|
|
8401
|
+
quit stim stop (verified stop; the host frees the slot
|
|
8402
|
+
and removes the app's defaults)
|
|
8403
|
+
clean stim worktree remove
|
|
8404
|
+
3 concurrent slots concurrency.maxDevices on the host; one bundle id
|
|
8405
|
+
slot per session
|
|
8406
|
+
|
|
8407
|
+
The agent-device rows work once the host's agent field names agent-device.
|
|
8408
|
+
|
|
8409
|
+
DESKTOP DOGFOOD
|
|
8410
|
+
|
|
8411
|
+
This repository's apps/desktop/.stim.json selects the full StimDesktop app,
|
|
8412
|
+
its development plist, asset catalog, fonts, branding and licences. Its sim-fold
|
|
8413
|
+
helper is not built. It monitors the regular Stim home alongside the installed
|
|
8414
|
+
app, with a workspace-specific bundle identifier and separate preferences.
|
|
8415
|
+
Its launch arguments disable automatic cleanup and notification alerts in this development copy.
|
|
8416
|
+
Use Window > SwiftUI Playground for in-memory production screen fixtures.
|
|
8417
|
+
|
|
8418
|
+
cd apps/desktop
|
|
8419
|
+
stim macos
|
|
8420
|
+
stim logs --source build
|
|
8421
|
+
stim stop
|
|
8422
|
+
|
|
8423
|
+
When using an unreleased CLI from this repository, build the packages first and
|
|
8424
|
+
set STIM_BIN to the absolute packages/stim-cli/dist/cli.mjs path before running
|
|
8425
|
+
that executable's macos command. The development app inherits this CLI override;
|
|
8426
|
+
the installed app's CLI preference and running server remain unchanged.
|
|
8427
|
+
|
|
8428
|
+
Ask an agent: "In my Swift Package app, configure the executable product and a
|
|
8429
|
+
development Info.plist for stim macos. Build and show its owned window in Stim
|
|
8430
|
+
Desktop, verify a source edit and readable failed-build logs, then stop only
|
|
8431
|
+
this workspace's app. Do not change permissions or use custom build scripts."
|
|
8432
|
+
`
|
|
6621
8433
|
}
|
|
6622
8434
|
};
|
|
6623
8435
|
//#endregion
|
|
@@ -6699,7 +8511,7 @@ function renderIndex(version) {
|
|
|
6699
8511
|
return lines.join("\n");
|
|
6700
8512
|
}
|
|
6701
8513
|
function guideCommand(program, version, status = (running) => guideStatus({
|
|
6702
|
-
projectRoot: findProjectRoot(process.cwd()),
|
|
8514
|
+
projectRoot: findProjectRoot$1(process.cwd()),
|
|
6703
8515
|
running
|
|
6704
8516
|
})) {
|
|
6705
8517
|
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) => {
|