stim 1.16.0 → 1.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{activity-BxwdD81S.mjs → activity-IRvJY_ru.mjs} +23 -3
- package/dist/{agent-device-usage-output-Dpih1Tfa.mjs → agent-device-usage-output-BK41vnzN.mjs} +3 -3
- package/dist/{android-Ift4kVvt.mjs → android--GorydsK.mjs} +679 -317
- package/dist/{android-B_NumVyQ.mjs → android-B_YNZy61.mjs} +7 -43
- package/dist/{android-r2rBL8kV.mjs → android-DE4v59Mb.mjs} +12 -3
- package/dist/{android-cas-8U4DPqeR.mjs → android-cas-ZFpjJBL_.mjs} +6 -5
- package/dist/{app-install-CKWdbXIX.mjs → app-install-VPkI5WEg.mjs} +6 -5
- package/dist/{budget-DjrURfQn.mjs → budget-DM984mnd.mjs} +34 -18
- package/dist/{build-plan-DjeSb2Xl.mjs → build-plan-CmQCoyT-.mjs} +345 -86
- package/dist/{build-slots-rUk77xqh.mjs → build-slots-41v_d2x9.mjs} +35 -25
- package/dist/{cli-BkYZ_6hy.mjs → cli-Daxug5-K.mjs} +17 -17
- package/dist/cli.mjs +1 -1
- package/dist/collector-run.mjs +3 -3
- package/dist/{command-output-DnJMdEY-.mjs → command-output-CDAIFD-8.mjs} +3 -3
- package/dist/{created-devices-D5owjN4C.mjs → created-devices-CpvzBL25.mjs} +1 -1
- package/dist/{dependency-state-B6cMXZab.mjs → dependency-state-CLcBHH2M.mjs} +1 -1
- package/dist/{deps-urbmfXig.mjs → deps-DNWaQs4V.mjs} +1 -1
- package/dist/{dev-client-CroAIjjj.mjs → dev-client-B-XC4uRH.mjs} +2 -2
- package/dist/{device-DZQjF7H5.mjs → device-C8IOoJPv.mjs} +7 -7
- package/dist/device-capacity-D0GWHnTM.mjs +580 -0
- package/dist/device-host-worker.mjs +436 -154
- package/dist/{device-ios-BadmeL2V.mjs → device-ios-Bc7vbGwl.mjs} +75 -44
- package/dist/{device-lease-D-hLqx1b.mjs → device-lease-DpoS36_u.mjs} +2 -2
- package/dist/{device-lease-run-CqjShqWW.mjs → device-lease-run-B8YltIH_.mjs} +3 -3
- package/dist/{device-pool-CUaEWMMm.mjs → device-pool-3o7s5lo4.mjs} +2 -2
- package/dist/{device-remote-D5WVWqOq.mjs → device-remote-hFSkpfQu.mjs} +12 -12
- package/dist/{doctor-Cimf-gGB.mjs → doctor-Cnk9d7Hm.mjs} +32 -51
- package/dist/{error-diagnostics-B9L4Ae0x.mjs → error-diagnostics-C-NMnhrk.mjs} +7 -7
- package/dist/{gc-Bqy4bjEu.mjs → gc-DxCL2A46.mjs} +308 -26
- package/dist/{gradle-BsO1MdsH.mjs → gradle-D7-L2Th3.mjs} +3 -3
- package/dist/{guide-B_uyKTxW.mjs → guide-DjrTQGik.mjs} +1662 -565
- package/dist/{guide-status-CjEZMAD4.mjs → guide-status-Du-ZQ3FD.mjs} +1 -1
- package/dist/hosted-android-DwLS4Mse.mjs +32 -0
- package/dist/{hosted-client-a-WmqE2L.mjs → hosted-client-wk1W_CNW.mjs} +2 -3
- package/dist/hosted-ios-aMo7Cljf.mjs +15 -0
- package/dist/{hosted-logs-D5g041Q3.mjs → hosted-logs-DqYmWtXc.mjs} +16 -16
- package/dist/{hosted-macos-BSZyIb3y.mjs → hosted-macos-dlHXEeF5.mjs} +4 -4
- package/dist/{hosted-ios-CIWelIqf.mjs → hosted-native-C9xJ9rpA.mjs} +77 -42
- package/dist/{idle-DYVymG92.mjs → idle-B2m-Ovod.mjs} +122 -58
- package/dist/{idle-shutdown-CTsDnAde.mjs → idle-shutdown-_3oOV1V_.mjs} +25 -17
- package/dist/{in-use-DvBw0Kxo.mjs → in-use-CvGOUn-x.mjs} +11 -11
- package/dist/{ios-aq3QoGI3.mjs → ios-DE6duQoz.mjs} +170 -88
- package/dist/{ios-BaInn3sW.mjs → ios-DSfnPfse.mjs} +4 -4
- package/dist/{ios-device-FfCN_C8p.mjs → ios-device-pAN4t54-.mjs} +1 -1
- package/dist/ios-state-8L3lgum6.mjs +89 -0
- package/dist/{launch-verify-2DBRizjo.mjs → launch-verify-lTnWZikR.mjs} +2 -2
- package/dist/{logs-CHEVK8zD.mjs → logs-DUoKlDoS.mjs} +36 -23
- package/dist/{client-BTY686R6.mjs → machines-C2XiNwh9.mjs} +278 -20
- package/dist/{macos-CSYEAFmF.mjs → macos-32po3kUq.mjs} +14 -14
- package/dist/macos-run.mjs +1 -1
- package/dist/maintenance-run.mjs +2 -2
- package/dist/{metro-BeK1ZPz1.mjs → metro-g8NqiJp-.mjs} +1 -1
- package/dist/{metro-gateway-BJ-rxQAB.mjs → metro-gateway-DmslUM30.mjs} +22 -3
- package/dist/{named-ports-Bk_YXEmE.mjs → named-ports-C8m8EQs2.mjs} +2 -2
- package/dist/{native-run-NaBrRFtQ.mjs → native-run-D1Q56rAD.mjs} +17 -6
- package/dist/{native-runtime-DBzRnEKS.mjs → native-runtime-DhJI3IVQ.mjs} +6 -6
- package/dist/offload-worker.mjs +8 -8
- package/dist/{teardown-CL6vDs2Y.mjs → ownership-BMLqyBgJ.mjs} +603 -26
- package/dist/{ownership-DiA16AsN.mjs → ownership-DyRQru0q.mjs} +2 -2
- package/dist/{ports-R2k4Knz3.mjs → ports-4nYqxPWC.mjs} +2 -2
- package/dist/{prebuild-D2fsca_Z.mjs → prebuild-D7RUMkwN.mjs} +3 -3
- package/dist/{preview-DhVbvPAZ.mjs → preview-CxPRIEht.mjs} +7 -7
- package/dist/{project-Du91Ilvc.mjs → project-D47Rh0ly.mjs} +7 -2
- package/dist/{recordings-BIXOzVnr.mjs → recordings-BNgsD1Tr.mjs} +1 -1
- package/dist/{reload-rM9jV1fz.mjs → reload-qZei6iWn.mjs} +62 -35
- package/dist/{remote-cache-C8-V13XN.mjs → remote-cache-Ci4DQYYj.mjs} +3 -3
- package/dist/{run-f4vI6EnK.mjs → run-CkE9y0nc.mjs} +1 -1
- package/dist/{server-bare-CpPGmnmO.mjs → server-bare-BkrSxSsZ.mjs} +1 -1
- package/dist/{server-expo-MC0rHlOC.mjs → server-expo-CHTyXD0G.mjs} +2 -2
- package/dist/server-expo-CeakhI8P.mjs +2 -0
- package/dist/{settings-oTSkkNrM.mjs → settings-Cfd-0r2d.mjs} +10 -9
- package/dist/{settings-ZkDzkzvB.mjs → settings-SR9OmW9h.mjs} +3 -3
- package/dist/settings.schema.json +42 -13
- package/dist/{simslim-B8-7AoTI.mjs → simslim-BWNrc3M3.mjs} +3 -3
- package/dist/{slot-launch-B8VrHxn1.mjs → slot-launch-DmjBmpQV.mjs} +8 -8
- package/dist/{start-CX8Ig-U2.mjs → start-BopZcr4n.mjs} +1 -1
- package/dist/{start-ClDfwXa4.mjs → start-uee1wWjY.mjs} +11 -11
- package/dist/{state-DcW6vOd_.mjs → state-BKa8wNN5.mjs} +1 -1
- package/dist/{state-3DwSQIBK.mjs → state-DyaNTgfR.mjs} +1 -1
- package/dist/{state-C1jB7Q8q.mjs → state-PeQiJuIu.mjs} +2 -2
- package/dist/{stats-CEZ_i5je.mjs → stats-Bq4fJOdB.mjs} +7 -4
- package/dist/{status-BuvBFeII.mjs → status-CzS4bPO9.mjs} +4 -4
- package/dist/{status-Cs7VLg9D.mjs → status-dXX2JX1C.mjs} +125 -35
- package/dist/{stop-Dt_dM16A.mjs → stop-BCwvvs7F.mjs} +2 -2
- package/dist/{stop-Bffozsac.mjs → stop-BjizrQwv.mjs} +48 -27
- package/dist/{stop-B0Yxtmiy.mjs → stop-Cr1d8DSE.mjs} +4 -4
- package/dist/{stop-cause-CidxIqup.mjs → stop-cause-rhmGfwvt.mjs} +1 -1
- package/dist/supervisor-run.d.mts +7 -4
- package/dist/supervisor-run.mjs +26 -28
- package/dist/{support-D4aYJniO.mjs → support-BJPgYCu-.mjs} +44 -19
- package/dist/{support-CAP8qDUF.mjs → support-F9-sr0Oi.mjs} +6 -6
- package/dist/{toolchain-DkUflduf.mjs → toolchain-CNIBTIOv.mjs} +7 -7
- package/dist/{warm-progress-CpGv3Wb6.mjs → warm-progress-DF4JRvYI.mjs} +3 -3
- package/dist/watchman-APYjXSQa.mjs +48 -0
- package/dist/{web-DWUaMp8P.mjs → web-exQtGkoc.mjs} +12 -13
- package/dist/web-run.mjs +4 -4
- package/dist/{workspace-process-lock-BL7Q0HCO.mjs → workspace-process-lock-2mNzOlaB.mjs} +3 -2
- package/dist/{workspace-state-B8-aJ1_5.mjs → workspace-state-DqcCbpE7.mjs} +2 -2
- package/dist/{workspaces-BV4pdHFz.mjs → workspaces-Bcib97-6.mjs} +3 -3
- package/dist/{worktree-jnG8Iph5.mjs → worktree-Dc98cE6_.mjs} +1 -1
- package/dist/{worktree-K-xj9Db0.mjs → worktree-ZpZR3S1k.mjs} +141 -40
- package/package.json +5 -5
- package/dist/device-capacity-BHwxNBlu.mjs +0 -136
- package/dist/ios-state-DS5UpYTH.mjs +0 -48
- package/dist/machines-BIBUdhpj.mjs +0 -265
- package/dist/ownership-hCkPzGmr.mjs +0 -571
- package/dist/server-expo-CVqO2c35.mjs +0 -2
|
@@ -1,15 +1,13 @@
|
|
|
1
|
-
import { t as ANDROID_AVD_CONFIG_HELP } from "./settings-
|
|
2
|
-
import {
|
|
3
|
-
import { t as RECENT_LAUNCH_MS } from "./status-
|
|
4
|
-
import { t as guideStatus } from "./guide-status-
|
|
1
|
+
import { t as ANDROID_AVD_CONFIG_HELP } from "./settings-Cfd-0r2d.mjs";
|
|
2
|
+
import { s as findProjectRoot$1 } from "./project-D47Rh0ly.mjs";
|
|
3
|
+
import { t as RECENT_LAUNCH_MS } from "./status-CzS4bPO9.mjs";
|
|
4
|
+
import { t as guideStatus } from "./guide-status-Du-ZQ3FD.mjs";
|
|
5
5
|
import { SETTINGS, SETTINGS_SCHEMA_URL } from "@stim-cli/core/state";
|
|
6
6
|
import chalk from "chalk";
|
|
7
|
-
//#
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
summary: "The normal coding-agent workflow, safety rules, and topic routing",
|
|
12
|
-
body: () => `AGENT WORKFLOW
|
|
7
|
+
//#region src/guide/agent.ts
|
|
8
|
+
var agent_default = {
|
|
9
|
+
summary: "The normal coding-agent workflow, safety rules, and topic routing",
|
|
10
|
+
body: () => `AGENT WORKFLOW
|
|
13
11
|
|
|
14
12
|
Use Stim to run React Native and Expo apps without sharing a Metro port or
|
|
15
13
|
device with another workspace. Prefer plain output: it streams each phase and
|
|
@@ -43,15 +41,23 @@ reach multiple devices. A launch counts a bundle delivery only when it can
|
|
|
43
41
|
attribute it to its own device; read guide lifecycle options for the rule.
|
|
44
42
|
Use stop --slot <name> for one slot, or plain stop for the whole workspace.
|
|
45
43
|
|
|
46
|
-
HOSTED IOS
|
|
44
|
+
HOSTED IOS AND ANDROID
|
|
47
45
|
|
|
48
|
-
For ios --remote <machine>, read guide lifecycle hosted-ios.
|
|
46
|
+
For ios --remote <machine|auto>, read guide lifecycle hosted-ios.
|
|
47
|
+
For android --remote <machine|auto>, read guide lifecycle hosted-android. Native
|
|
48
|
+
logcat records arrive through stim logs, including --errors and --json;
|
|
49
|
+
Desktop and the phone view/control it through the local stim-server relay.
|
|
50
|
+
Stop before switching
|
|
49
51
|
between a local simulator and a hosting Mac. Hosted Debug requires the local
|
|
50
52
|
Metro supervisor; restart a missing or older one with stim stop; stim start.
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
A named Mac never falls back here; auto can use the local device queue when no
|
|
54
|
+
host admits. Auto keeps a live local slot or recorded hosted session. Follow the
|
|
55
|
+
placement reason and the reported device. Stop reconciles a recorded host
|
|
56
|
+
before changing named placement.
|
|
57
|
+
For hosted iOS or Android, use agent-device <command> --remote-config <file>
|
|
58
|
+
from ios.host.agent or android.host.agent in stim status --json. Open the
|
|
59
|
+
installed app first; never use the host UDID or serial locally. See guide
|
|
60
|
+
lifecycle hosted-ios or hosted-android for the agent policy and setup.
|
|
55
61
|
|
|
56
62
|
When driving a device yourself with agent-device, set AGENT_DEVICE_STATE_DIR
|
|
57
63
|
from agentDevice.stateDir in stim ios|android|macos --json or status --json.
|
|
@@ -135,6 +141,7 @@ or hosting.* for the user, or approve requests. Read stim guide settings for
|
|
|
135
141
|
Desktop access tickets and person-owned approval.
|
|
136
142
|
Read stim guide settings to select a build machine. Keep a requested named
|
|
137
143
|
placement when recovering STIM_OFFLOAD_REFUSED: report its reason and remedy.
|
|
144
|
+
When the user mentions slow builds, a busy Mac or wanting updates away from the desk, tell them about Stim Desktop > Settings > Build machines > Add or Phones > Pair a Phone; see stim guide settings.
|
|
138
145
|
|
|
139
146
|
Read stim guide lifecycle concurrency when a build waits on another workspace
|
|
140
147
|
or a build call times out. A native build can outlive a shell timeout; if the
|
|
@@ -250,6 +257,17 @@ stop and worktree remove release this workspace's leases. On a physical
|
|
|
250
257
|
iPhone, stop also closes the app by ending its log collector; it does not
|
|
251
258
|
shut down the phone or uninstall the app.
|
|
252
259
|
|
|
260
|
+
Owned-device caps wait in FIFO order across the Stim home for 600s by default.
|
|
261
|
+
Use --wait <seconds> to change the bound or --no-wait to refuse at once.
|
|
262
|
+
At the cap, the queue head shuts down one longest-idle eligible owned device
|
|
263
|
+
from another workspace, at most once every 15 seconds, then rechecks capacity.
|
|
264
|
+
devices.reclaimIdleMinutes is 10 by default; 0 off.
|
|
265
|
+
It never deletes devices or touches this workspace, physical, hosted, remote,
|
|
266
|
+
parked or other homes' devices. Drivers, locks, builds, viewers and recent
|
|
267
|
+
activity prevent reclaim. The supervisor's own idle shutdown default is 30m.
|
|
268
|
+
Read the holder names in progress and build.waitingFor in status; do not start
|
|
269
|
+
replacement runs while one is queued. See \`stim guide lifecycle concurrency\`.
|
|
270
|
+
|
|
253
271
|
Treat a refusal as an ownership or state mismatch: read its code and remedy.
|
|
254
272
|
Never reach for --force first.
|
|
255
273
|
Stim leaves externally started Metro servers alone. Stop them with their original
|
|
@@ -338,6 +356,7 @@ Read the matching guide before acting in these situations:
|
|
|
338
356
|
| Swift Package macOS development | stim guide macos |
|
|
339
357
|
| macOS app on another Mac (stim macos --remote) | stim guide macos |
|
|
340
358
|
| Unfamiliar state or JSON field | stim guide facts payloads |
|
|
359
|
+
| User asks for the Stim tutorial | stim guide tutorial |
|
|
341
360
|
| Refusal without a code | stim guide errors |
|
|
342
361
|
|
|
343
362
|
Use the CODE exactly as printed; codes sharing a header resolve to the same
|
|
@@ -373,11 +392,15 @@ FULL TOPIC LIST
|
|
|
373
392
|
stim guide cleanup # what reclaims a device, and what deletes
|
|
374
393
|
stim guide cleanup collector # an unproven collector pid; why the app on a phone closed
|
|
375
394
|
stim guide cleanup memory # watchman and Gradle daemon memory; gc --cache watchman
|
|
395
|
+
stim guide tutorial # tutorial sections and manual commands
|
|
396
|
+
stim guide tutorial run # create the app and build in a tour worktree
|
|
376
397
|
stim guide settings # configuration files and supported keys`
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
398
|
+
};
|
|
399
|
+
//#endregion
|
|
400
|
+
//#region src/guide/facts.ts
|
|
401
|
+
const facts = {
|
|
402
|
+
summary: "The --json payloads: `start`, `ios`, `android`, `web`, `ios|android --plan`, `reload`, `stop`, `status`, `doctor`, `device lock`/`unlock`, `gc`, and the error contract",
|
|
403
|
+
preamble: () => `SLOTS
|
|
381
404
|
Named ios/android runs add slot to their JSON facts. Default-run fields remain
|
|
382
405
|
compatible. status adds a slots array per environment with each named slot's
|
|
383
406
|
ios/android device facts; top-level ios/android still describe default.
|
|
@@ -395,6 +418,14 @@ status adds archived (newest first, bounded by archive.maxCount) and
|
|
|
395
418
|
archivedUsage { count, bytes, byKind: { logs, recordings, agentActions, record } }.
|
|
396
419
|
Each archive carries id, projectRoot, project, workspace, worktree facts,
|
|
397
420
|
removedAt, removedBy, lastUsedAt, builds, agents, bytes, expires and version.
|
|
421
|
+
When the worktree folder was already gone, as when gc prunes a dead project,
|
|
422
|
+
the worktree facts come from the pull request cache of that gone worktree
|
|
423
|
+
when Stim cached one: branch, head and pullRequest, with merged true only when
|
|
424
|
+
that pull request merged and null otherwise; repository and subject are null.
|
|
425
|
+
Without a cached entry every worktree fact is null.
|
|
426
|
+
Archive builds is a summary { count, last, lastErrorCount }. With read access,
|
|
427
|
+
server archive.detail { archive: id } returns live-shaped builds and recordings
|
|
428
|
+
{ platform, slot, spans } for each retained recording slot with footage.
|
|
398
429
|
replacedBy is the live environment path when its canonical root matches.
|
|
399
430
|
Older producers may omit both fields.
|
|
400
431
|
Plain status prints Archived: <count> workspace(s), <size> when count is positive;
|
|
@@ -529,10 +560,10 @@ workspace leases no physical device.
|
|
|
529
560
|
|
|
530
561
|
Plain status prints "ios: Old iPhone (physical, iPhone 12 Pro) connected --
|
|
531
562
|
leased until <time>" for each one.`,
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
563
|
+
sections: {
|
|
564
|
+
payloads: {
|
|
565
|
+
summary: "every field of the start, ios, android, macos, web and reload payloads, the error contract, the device rules",
|
|
566
|
+
body: () => ` stim start --json
|
|
536
567
|
|
|
537
568
|
port the Metro port RESERVED for this workspace
|
|
538
569
|
supervisorPid the detached supervisor's pid, or NULL when a dev server was
|
|
@@ -969,10 +1000,10 @@ RULES
|
|
|
969
1000
|
\`android --device\` and
|
|
970
1001
|
\`ios --device\`, which use a connected physical device Stim never
|
|
971
1002
|
creates, boots, or deletes.`
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
1003
|
+
},
|
|
1004
|
+
devmenu: {
|
|
1005
|
+
summary: "why the Expo dev menu or Tools button is or is not over the app, per platform and device kind",
|
|
1006
|
+
body: () => ` EVERY DEV-CLIENT DEEP LINK CARRIES disableOnboarding=1
|
|
976
1007
|
INSIDE ITS PROJECT URL
|
|
977
1008
|
(\`...?url=http%3A%2F%2Fhost%3Aport%2F%3FdisableOnboarding%3D1&disableFab=1\`),
|
|
978
1009
|
and expo-dev-launcher finishes its own dev-menu ONBOARDING
|
|
@@ -1097,10 +1128,10 @@ RULES
|
|
|
1097
1128
|
developer trust, has no API at all and is always the user's.
|
|
1098
1129
|
\`guide errors unverified\` has the signature and the
|
|
1099
1130
|
full commands.`
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1131
|
+
},
|
|
1132
|
+
gc: {
|
|
1133
|
+
summary: "the gc report payload: mode, sections, reasons, failures, results, inventory, and the gc refusals",
|
|
1134
|
+
body: () => ` stim gc [--delete] [--older-than <days>] [--cache <name|all|workspaces|recordings|parked>]
|
|
1104
1135
|
[--worktrees] [--idle <duration>] --json
|
|
1105
1136
|
|
|
1106
1137
|
The report the text prints, as one payload. Show the user its sections
|
|
@@ -1127,7 +1158,8 @@ RULES
|
|
|
1127
1158
|
id, bytes, detail } per entry it acted on; empty on a dry
|
|
1128
1159
|
run. status is "done", "kept" (left alone, detail says
|
|
1129
1160
|
why) or "failed" (detail says why and what to retry).
|
|
1130
|
-
kind: device, parkedDevice,
|
|
1161
|
+
kind: device, parkedDevice, parkedHostedDevice, idleDevice,
|
|
1162
|
+
deviceRecord,
|
|
1131
1163
|
workspaceOutputs, recording, workspaceDirectory, project,
|
|
1132
1164
|
buildLock, buildSlot, deviceLease, easSession, worktree,
|
|
1133
1165
|
cache.
|
|
@@ -1199,6 +1231,14 @@ RULES
|
|
|
1199
1231
|
parkedEmulators { name, systemImage, deviceProfile, parkedAt, app,
|
|
1200
1232
|
bytes, listed } app is the package name;
|
|
1201
1233
|
likewise
|
|
1234
|
+
parkedHostedDevices { session, client, platform, id, name, parkedAt,
|
|
1235
|
+
listed } parked iOS/Android sessions
|
|
1236
|
+
on this host; client is its registry id, id is
|
|
1237
|
+
the UDID or AVD name, name is the device label.
|
|
1238
|
+
listed means its private home ledger lists the
|
|
1239
|
+
device; false means already removed and is not
|
|
1240
|
+
actionable; null means unreadable. --older-than
|
|
1241
|
+
filters by parkedAt; --cache scopes leave this empty
|
|
1202
1242
|
orphanedDevices { kind, id, name, bytes, directory }
|
|
1203
1243
|
unverifiedDevices { kind, id, name, command } stim-* devices this
|
|
1204
1244
|
Stim home has no record of creating; never
|
|
@@ -1318,10 +1358,10 @@ RULES
|
|
|
1318
1358
|
caches on this machine
|
|
1319
1359
|
- --cache together with --worktrees or --idle
|
|
1320
1360
|
- --cache watchman or gradle-daemons together with --older-than`
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1361
|
+
},
|
|
1362
|
+
status: {
|
|
1363
|
+
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",
|
|
1364
|
+
body: () => ` stim status --json
|
|
1325
1365
|
|
|
1326
1366
|
logs { dir, errorsSinceMarker }, or null without a log directory
|
|
1327
1367
|
agentDevice { stateDir }: absolute workspace agent-device state path on
|
|
@@ -1350,6 +1390,11 @@ RULES
|
|
|
1350
1390
|
other route to web. macOS needs Package.swift, macos.product
|
|
1351
1391
|
and macos.infoPlist. Detection reads config literals and
|
|
1352
1392
|
files, never runs project scripts.
|
|
1393
|
+
tutorial { version }: present only when app.json sets
|
|
1394
|
+
expo.extra.stimTutorial to a positive integer, the marker
|
|
1395
|
+
of the Stim tutorial app (stim guide tutorial). Status
|
|
1396
|
+
reports whatever version it finds; Desktop decides which
|
|
1397
|
+
versions it supports. Static app.json only.
|
|
1353
1398
|
recording { enabled }: whether stim-server may record the workspace's
|
|
1354
1399
|
device screens for replay, from recording.enabled
|
|
1355
1400
|
|
|
@@ -1502,9 +1547,10 @@ RULES
|
|
|
1502
1547
|
lock, argent, xcodebuild, idb, maestro, appium, simctl,
|
|
1503
1548
|
uiautomator or instrumentation; pid and since are null when
|
|
1504
1549
|
the claim does not record them
|
|
1505
|
-
lastActivityAt the newest of this device's app log records,
|
|
1506
|
-
|
|
1507
|
-
|
|
1550
|
+
lastActivityAt the newest of this device's app log records, changes to
|
|
1551
|
+
the workspace's client.ndjson and device.ndjson logs,
|
|
1552
|
+
this platform's Metro bundle requests, the workspace's
|
|
1553
|
+
last Stim run, while agent-device drives it the agent's last
|
|
1508
1554
|
recorded action, and now while a stim-server client views
|
|
1509
1555
|
it, rounded down to the minute; absent when none is
|
|
1510
1556
|
recorded
|
|
@@ -1533,12 +1579,14 @@ RULES
|
|
|
1533
1579
|
Plain \`stim status\` appends it to each device line: "driven by
|
|
1534
1580
|
agent-device for 12m", "active", "idle 3h", or "activity unknown (...)".
|
|
1535
1581
|
|
|
1536
|
-
An owned device that is not booted after
|
|
1537
|
-
|
|
1582
|
+
An owned device that is not booted after supervisor idle shutdown or queue
|
|
1583
|
+
reclaim carries idleShutdown, and plain \`status\`
|
|
1538
1584
|
appends "shut down after 30m idle at <at>" to its line; the next \`ios\` or
|
|
1539
1585
|
\`android\` run for that slot clears it (\`guide lifecycle budget\`):
|
|
1540
1586
|
|
|
1541
|
-
idleShutdown { at, idleMinutes }
|
|
1587
|
+
idleShutdown { at, idleMinutes, reason?: "idle" | "reclaimed for a waiting run" }
|
|
1588
|
+
Absent reason or "idle" means supervisor idle shutdown; reclaim adds
|
|
1589
|
+
"reclaimed for a waiting run" to plain status.
|
|
1542
1590
|
\`gc --idle <duration>\` shuts down owned devices idle that long
|
|
1543
1591
|
(\`guide cleanup gc\`).
|
|
1544
1592
|
|
|
@@ -1617,7 +1665,7 @@ RULES
|
|
|
1617
1665
|
outcome, outcomeKnown, cacheLookupOutcome?, expectedMs, expectedPhaseMs,
|
|
1618
1666
|
completedPhaseMs?, basis,
|
|
1619
1667
|
plannedPhases, missReason?, missProvisional?, detail?, placement,
|
|
1620
|
-
waitingOn? }
|
|
1668
|
+
waitingOn?, waitingFor? }
|
|
1621
1669
|
|
|
1622
1670
|
state "running" while the run's own native-run claim is live;
|
|
1623
1671
|
"stale" when that claim was released or its process is
|
|
@@ -1647,7 +1695,7 @@ RULES
|
|
|
1647
1695
|
the project's most recent one
|
|
1648
1696
|
cacheLookupOutcome "hit" or "miss" once an actual cache lookup resolves;
|
|
1649
1697
|
absent before resolution and on runs that skip lookup,
|
|
1650
|
-
such as --eas-profile
|
|
1698
|
+
such as --eas-profile or --no-build-cache
|
|
1651
1699
|
expectedMs the median duration of this project's last successful
|
|
1652
1700
|
runs with that outcome on that platform, or null with
|
|
1653
1701
|
no history. The run estimates twice: when it starts,
|
|
@@ -1703,6 +1751,9 @@ RULES
|
|
|
1703
1751
|
startedAt is when the offload started and phaseStartedAt
|
|
1704
1752
|
when that step did. Meanwhile phase above follows it as
|
|
1705
1753
|
prebuild, pods or compile.
|
|
1754
|
+
waitingFor optional { kind: "build-slot" | "device-slot", inUse, max,
|
|
1755
|
+
since } for a slot wait, independently of phase. since is
|
|
1756
|
+
ISO. Overlapping waits show the first, then the remaining one.
|
|
1706
1757
|
waitingOn while phase is "wait" and the holder is known: { path },
|
|
1707
1758
|
the workspace whose build of the same artifact this run
|
|
1708
1759
|
waits for; absent otherwise. The lock is machine-wide, so
|
|
@@ -1720,7 +1771,7 @@ RULES
|
|
|
1720
1771
|
lastBuilds { ios?, android? }, each { platform, status, cacheHit,
|
|
1721
1772
|
cacheSkipped, durationMs, fingerprint, startedAt, finishedAt,
|
|
1722
1773
|
errorCode?, missReason?, buildMachine?, builtOn?, offloadedTo?, offloadFallback?,
|
|
1723
|
-
diagnostics? }
|
|
1774
|
+
diagnostics?, devicePlacement? }
|
|
1724
1775
|
|
|
1725
1776
|
status "ok" or "failed"
|
|
1726
1777
|
cacheHit "local" or "remote" for an app from that cache tier; false
|
|
@@ -1947,10 +1998,10 @@ RULES
|
|
|
1947
1998
|
simulator is booted, no workspace is live and no build runs: status then
|
|
1948
1999
|
runs neither. \`status --watch --json\` rereads both every 15 seconds while
|
|
1949
2000
|
machine is not null, with no other subprocess.`
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
2001
|
+
},
|
|
2002
|
+
plan: {
|
|
2003
|
+
summary: "the ios and android --plan payload: fingerprint, cacheHit, prebuild, missReason, expectedMs and basis",
|
|
2004
|
+
body: () => ` stim ios --plan --json # or: stim android --plan --json
|
|
1954
2005
|
|
|
1955
2006
|
{ platform, slot?, fingerprint, cacheKey, cacheHit, provider,
|
|
1956
2007
|
cacheSkipped, prebuild, outcome, expectedMs, basis, missReason?,
|
|
@@ -1989,10 +2040,10 @@ RULES
|
|
|
1989
2040
|
STIM_NO_FINGERPRINT, STIM_EAS_UNAVAILABLE, or STIM_BAD_ARG for a flag that
|
|
1990
2041
|
picks a device. When and why a plan and the run can differ: \`guide
|
|
1991
2042
|
lifecycle builds\`.`
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
2043
|
+
},
|
|
2044
|
+
stats: {
|
|
2045
|
+
summary: "the stats payload, what counts as a run, hit, miss and failed, timeSavedMs, the heartbeat estimate",
|
|
2046
|
+
body: () => ` stim stats --json
|
|
1996
2047
|
|
|
1997
2048
|
{ "version": 1,
|
|
1998
2049
|
"project": { "key": "<path>", "ios": <bucket|null>,
|
|
@@ -2002,6 +2053,8 @@ RULES
|
|
|
2002
2053
|
"machines": { "<machine>": { "today": <day>,
|
|
2003
2054
|
"total": <totals> } },
|
|
2004
2055
|
"placements": [<placement>, ...] },
|
|
2056
|
+
"capacityRefusals": [<capacityRefusal>, ...],
|
|
2057
|
+
"capacityWaits": [<capacityWait>, ...],
|
|
2005
2058
|
"agentDevice": <AgentDeviceUsage|null>,
|
|
2006
2059
|
"swiftpmCache": <SwiftpmCacheUsage|null> }
|
|
2007
2060
|
|
|
@@ -2014,6 +2067,25 @@ RULES
|
|
|
2014
2067
|
average, time saved and build estimates stay local. Milliseconds are
|
|
2015
2068
|
integers.
|
|
2016
2069
|
|
|
2070
|
+
CAPACITY REFUSALS
|
|
2071
|
+
capacityRefusals?: [{ at, kind: "device", platform: "ios" | "android",
|
|
2072
|
+
max, workspace }]
|
|
2073
|
+
Only device refusals with STIM_AT_CAPACITY are recorded; build caps wait.
|
|
2074
|
+
at is the ISO timestamp, max is concurrency.maxDevices, and workspace is
|
|
2075
|
+
the workspace id used by status and Desktop. The list is newest first:
|
|
2076
|
+
the last 50 from the last 7 days, omitted when empty. Desktop uses events
|
|
2077
|
+
within 6 hours for its device-limit suggestion, including terminal runs.
|
|
2078
|
+
Recording is best effort and does not change the refusal. Plain stats
|
|
2079
|
+
output is unchanged.
|
|
2080
|
+
capacityWaits?: [{ at, kind: "device-wait", platform: "ios" | "android",
|
|
2081
|
+
ms, max, workspace, reclaimed? }]
|
|
2082
|
+
Each owned-device slot wait records its whole elapsed milliseconds,
|
|
2083
|
+
including waits that time out or fail. It has the same 50-event, 7-day
|
|
2084
|
+
retention, newest-first order, workspace id and best-effort recording.
|
|
2085
|
+
reclaimed is the whole count of devices shut down for the waiting run,
|
|
2086
|
+
omitted when zero. Plain stats reports waits with a positive reclaim count.
|
|
2087
|
+
It is omitted when empty.
|
|
2088
|
+
|
|
2017
2089
|
AGENT-DEVICE DISK USAGE
|
|
2018
2090
|
agentDevice: { version: 1, measuredAt, bytes, complete, stateDir,
|
|
2019
2091
|
runnerBuilds, workspaces, hosted }
|
|
@@ -2095,10 +2167,20 @@ HOW A RUN IS COUNTED (\`stats\`)
|
|
|
2095
2167
|
ignores what it cannot read, so nothing about statistics can change a
|
|
2096
2168
|
run's outcome.
|
|
2097
2169
|
|
|
2170
|
+
DEVICE PLACEMENT (\`ios|android --remote auto\`)
|
|
2171
|
+
Auto runs include devicePlacement: { decision, reason, machine? } in the run
|
|
2172
|
+
facts, lastBuilds and build history. decision is "local", "hosted" or
|
|
2173
|
+
"waited-locally" (the local run actually waited for a device slot). The same
|
|
2174
|
+
optional devicePlacement appears on the status device entry for each slot.
|
|
2175
|
+
Hosted host facts include selected: "auto" or the named machine, and reason
|
|
2176
|
+
for automatic placement. Plain status prints (auto: <reason>) after the host.
|
|
2177
|
+
Old hosted records without selected still read as the machine name.
|
|
2178
|
+
|
|
2098
2179
|
BUILD PLACEMENT (\`offload\`)
|
|
2099
2180
|
Every run that compiles records where it built and why, in a placement: { at, project, platform,
|
|
2100
|
-
decision, reason, machine?, buildMs?, slotWaitMs?, localEstimateMs?, failed? }.
|
|
2101
|
-
slotWaitMs
|
|
2181
|
+
decision, reason, machine?, buildMs?, slotWaitMs?, deviceSlotWaitMs?, localEstimateMs?, failed? }.
|
|
2182
|
+
slotWaitMs and deviceSlotWaitMs are whole milliseconds waiting for build
|
|
2183
|
+
and owned-device slots respectively, present only when positive.
|
|
2102
2184
|
decision is "here", "offloaded", or "fell-back" (it tried a build machine
|
|
2103
2185
|
and built here). reason is why: the run's \`placement:\` reason, such as
|
|
2104
2186
|
"load 0.6/core, 1 of 3 build slots busy here" (auto keeps the build here
|
|
@@ -2117,12 +2199,14 @@ BUILD PLACEMENT (\`offload\`)
|
|
|
2117
2199
|
machine was slower. \`offload.today\` counts the day's placements by
|
|
2118
2200
|
decision. A fallback counts against a machine only when the run knows
|
|
2119
2201
|
which one: the machine that failed, or the only one paired.`
|
|
2120
|
-
}
|
|
2121
2202
|
}
|
|
2122
|
-
}
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2203
|
+
}
|
|
2204
|
+
};
|
|
2205
|
+
//#endregion
|
|
2206
|
+
//#region src/guide/metro.ts
|
|
2207
|
+
var metro_default = {
|
|
2208
|
+
summary: "The dev server: `stim start`, the supervisor, and starting your own",
|
|
2209
|
+
body: () => `THE DEV SERVER
|
|
2126
2210
|
|
|
2127
2211
|
stim start
|
|
2128
2212
|
stim start --remote # prepare Metro for a remote device
|
|
@@ -2156,9 +2240,10 @@ HOSTED IOS METRO
|
|
|
2156
2240
|
Hosted Debug runs require the supervisor's private gateway support before
|
|
2157
2241
|
reservation. --no-metro-check refuses; restart a missing or older supervisor
|
|
2158
2242
|
with stim stop; stim start.
|
|
2159
|
-
Native iOS logs are pulled from the host by stim logs, including --errors;
|
|
2243
|
+
Native iOS and Android logs are pulled from the host by stim logs, including --errors;
|
|
2160
2244
|
JavaScript logs still arrive through Metro. Stop pulls native logs before
|
|
2161
|
-
deleting the
|
|
2245
|
+
deleting the owned device and copies the final host tail afterwards. Read guide
|
|
2246
|
+
lifecycle hosted-ios or hosted-android for placement and cleanup.
|
|
2162
2247
|
|
|
2163
2248
|
TAILNET-ONLY METRO
|
|
2164
2249
|
Install Tailscale and sign in on this machine and the remote device or
|
|
@@ -2207,6 +2292,13 @@ BUNDLE WARMUP
|
|
|
2207
2292
|
waits for the app's own bundle response. With a bundler started outside Stim,
|
|
2208
2293
|
device logs may prove a request, but bundle completion may stay unverified.
|
|
2209
2294
|
|
|
2295
|
+
HOSTED ANDROID
|
|
2296
|
+
android --remote <machine> keeps Metro here through the private tailnet
|
|
2297
|
+
gateway. The host reverses the client Metro port into its loopback bridge
|
|
2298
|
+
on the exact owned emulator. Each run and reload restores that reverse;
|
|
2299
|
+
no adb command for the host serial runs here. metro.tunnel and metro.publicUrl
|
|
2300
|
+
are ignored. See guide lifecycle hosted-android.
|
|
2301
|
+
|
|
2210
2302
|
REMOTE DEVICE BACKENDS
|
|
2211
2303
|
Metro exposure and device selection are separate:
|
|
2212
2304
|
|
|
@@ -2302,7 +2394,7 @@ WHAT THE SUPERVISOR IS
|
|
|
2302
2394
|
connected app alone does not keep the server. It records a
|
|
2303
2395
|
supervisor_idle_stopped line in metro.ndjson and devServerStop in
|
|
2304
2396
|
state.json, so \`status\` shows "stopped (idle)" rather than a crash.
|
|
2305
|
-
Devices stay booted, unless devices.idleShutdownMinutes is
|
|
2397
|
+
Devices stay booted, unless devices.idleShutdownMinutes is above 0 (the default is 30): then the
|
|
2306
2398
|
idle stop first shuts down the workspace's owned devices idle for the
|
|
2307
2399
|
shorter of the two (\`guide lifecycle budget\`). The next \`stim start\`
|
|
2308
2400
|
starts it again and clears the
|
|
@@ -2395,10 +2487,12 @@ STARTING YOUR OWN BUNDLER STILL WORKS
|
|
|
2395
2487
|
\`stop\` leaves an externally started server running and keeps the port
|
|
2396
2488
|
reserved while it answers from this project. Stop it with the tool that
|
|
2397
2489
|
started it.`
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2490
|
+
};
|
|
2491
|
+
//#endregion
|
|
2492
|
+
//#region src/guide/ports.ts
|
|
2493
|
+
var ports_default = {
|
|
2494
|
+
summary: "Workspace ports for web and API servers that Stim does not manage",
|
|
2495
|
+
body: () => `NAMED SERVER PORTS
|
|
2402
2496
|
|
|
2403
2497
|
If Stim is not installed globally, replace stim with npx stim.
|
|
2404
2498
|
|
|
@@ -2466,10 +2560,12 @@ cleanup: versions without ports do not know about named allocations.
|
|
|
2466
2560
|
|
|
2467
2561
|
A shared API does not get a shared reservation. Pass its port by environment
|
|
2468
2562
|
instead of allocating a separate label in every worktree.`
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2563
|
+
};
|
|
2564
|
+
//#endregion
|
|
2565
|
+
//#region src/guide/logs.ts
|
|
2566
|
+
var logs_default = {
|
|
2567
|
+
summary: "Querying the merged NDJSON timeline, and what --errors means",
|
|
2568
|
+
body: () => `LOGS
|
|
2473
2569
|
|
|
2474
2570
|
If Stim is not installed globally, replace stim with npx stim.
|
|
2475
2571
|
|
|
@@ -2836,7 +2932,7 @@ WHAT WRITES WHAT
|
|
|
2836
2932
|
session records (~/.agent-device/sessions, or
|
|
2837
2933
|
AGENT_DEVICE_STATE_DIR, plus existing workspace
|
|
2838
2934
|
agent-device directories) for this workspace's owned
|
|
2839
|
-
simulators and
|
|
2935
|
+
simulators, emulators and native Mac app, read-only. An action is
|
|
2840
2936
|
info (msg is agent-device's summary, e.g. "Tapped
|
|
2841
2937
|
(201, 731)"; event agent_action with command, session,
|
|
2842
2938
|
deviceId and details, and startedAt when agent-device
|
|
@@ -2847,7 +2943,13 @@ WHAT WRITES WHAT
|
|
|
2847
2943
|
match a simulator through their runner.log, Android
|
|
2848
2944
|
sessions only while agent-device's claim on the
|
|
2849
2945
|
emulator is live (released claims drop their
|
|
2850
|
-
actions from later queries). A session
|
|
2946
|
+
actions from later queries). A macOS session opened
|
|
2947
|
+
with explicit --surface app matches its isolated
|
|
2948
|
+
bundle ID and current launch (guide macos). App
|
|
2949
|
+
switches, unrecorded opens and rotated event logs
|
|
2950
|
+
clear that match; native screenshots are omitted
|
|
2951
|
+
because their surface is not in the event metadata.
|
|
2952
|
+
A session in an
|
|
2851
2953
|
unrecognized format yields one warn record
|
|
2852
2954
|
(agent_format_unknown) instead of its actions.
|
|
2853
2955
|
build-ios.ndjson the xcodebuild / gradle transcript at level debug, the
|
|
@@ -2863,57 +2965,65 @@ WHAT WRITES WHAT
|
|
|
2863
2965
|
|
|
2864
2966
|
A collector is killed and replaced on the next \`ios\` / \`android\` run for
|
|
2865
2967
|
that platform, and reaped by \`stop\`.`
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2968
|
+
};
|
|
2969
|
+
//#endregion
|
|
2970
|
+
//#region src/guide/errors.ts
|
|
2971
|
+
const errors = {
|
|
2972
|
+
summary: "Every refusal Stim can print: an index of codes, and one section for each",
|
|
2973
|
+
sectionHint: "<CODE>",
|
|
2974
|
+
preamble: () => `WHAT STIM REFUSES, AND WHY
|
|
2871
2975
|
|
|
2872
2976
|
Every refusal listed here carries a stable CODE, whichever command prints it.
|
|
2873
2977
|
Branch on the code, never on the message.`,
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2978
|
+
sections: {
|
|
2979
|
+
STIM_HOSTING_REFUSED: {
|
|
2980
|
+
summary: "a hosting Mac refused or could not confirm its native session; no fallback after reservation",
|
|
2981
|
+
body: () => `STIM_HOSTING_REFUSED
|
|
2878
2982
|
The message names the hosting Mac and its reason: unreachable or changed
|
|
2879
|
-
tailnet node, no installed simulator choice, no capacity, elevated or unknown
|
|
2983
|
+
tailnet node, no installed simulator or emulator choice, no capacity, elevated or unknown
|
|
2880
2984
|
memory pressure, or an unresolved reservation or delivery. Stim boots nothing
|
|
2881
|
-
locally and never tries another Mac
|
|
2882
|
-
|
|
2985
|
+
locally and never tries another Mac after choosing a named host or creating a
|
|
2986
|
+
hosted session. Auto skips declined offers before building, then uses the
|
|
2987
|
+
local queue if none admits. A reserve refusal after an accepted offer fails
|
|
2988
|
+
with this code and asks to retry; it does not re-place the built architecture.
|
|
2989
|
+
Check stim-server and Tailscale there,
|
|
2990
|
+
then run stim doctor. Correct --device-type / --runtime for iOS or
|
|
2991
|
+
--system-image / --device-profile for Android when the offer names
|
|
2883
2992
|
an unavailable choice. A session that exists stays recorded even if delivery
|
|
2884
|
-
fails: retry stim ios --remote <machine>, or run stim stop to reconcile it.
|
|
2993
|
+
fails: retry stim ios|android --remote <machine>, or run stim stop to reconcile it.
|
|
2885
2994
|
An unreachable stop keeps the placement; rerun stim stop when the host answers.
|
|
2886
2995
|
A failed build handoff uses upload instead. If native log queries are unavailable,
|
|
2887
2996
|
logs prints a note on stderr and shows copied records. Update an older stim-server
|
|
2888
|
-
on the host to enable
|
|
2997
|
+
on the host to enable handoff and native logs (hello features hosted-ios-data
|
|
2998
|
+
for iOS and hosted-android-data for Android).
|
|
2889
2999
|
A name outside hosting.machines is STIM_BAD_ARG. Missing or pending hosting
|
|
2890
3000
|
approval keeps the doctor --fix and stim-server devices grant remedies.`
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
3001
|
+
},
|
|
3002
|
+
STIM_EAS_BUILD_MISSING: {
|
|
3003
|
+
summary: "no completed EAS development build matches; build only with session authorization",
|
|
3004
|
+
separator: "--- EAS BUILD CODES (`ios --eas-profile` / `android --eas-profile`) ---",
|
|
3005
|
+
body: () => `STIM_EAS_BUILD_MISSING
|
|
2896
3006
|
No compatible build matches the selected EAS project, profile, platform and
|
|
2897
3007
|
native fingerprint. No device was acquired and no local or cloud build was
|
|
2898
3008
|
started. The remedy prints the exact npx eas-cli build command. Check session
|
|
2899
3009
|
authorization for its potential cost before running it, then retry Stim.
|
|
2900
3010
|
See stim guide lifecycle eas.`
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
3011
|
+
},
|
|
3012
|
+
STIM_EAS_UNAVAILABLE: {
|
|
3013
|
+
summary: "EAS lookup/download failed, or another run holds the artifact claim",
|
|
3014
|
+
body: () => `STIM_EAS_UNAVAILABLE
|
|
2905
3015
|
EAS CLI is unavailable or older than 18.9.0, a profile/fingerprint/list/download
|
|
2906
3016
|
operation failed, the response could not be validated, or another run holds
|
|
2907
3017
|
the artifact claim.
|
|
2908
3018
|
Follow the printed remedy: inspect the named EAS command or retry once the
|
|
2909
3019
|
holder finishes. This is not proof that a build is missing. No native build
|
|
2910
3020
|
is started. See stim guide lifecycle eas.`
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
3021
|
+
},
|
|
3022
|
+
STIM_WORKSPACE_STATE: {
|
|
3023
|
+
summary: "$STIM_HOME/workspaces could not be prepared, or the digest directory belongs to another project",
|
|
3024
|
+
aliases: ["STIM_WORKSPACE_COLLISION"],
|
|
3025
|
+
separator: "--- BUILD-PATH CODES (`stim ios` / `stim android`) ---",
|
|
3026
|
+
body: () => `STIM_WORKSPACE_STATE / STIM_WORKSPACE_COLLISION
|
|
2917
3027
|
Stim could not prepare this project's global workspace directory under
|
|
2918
3028
|
$STIM_HOME/workspaces. Check that STIM_HOME is writable and has free
|
|
2919
3029
|
space. An EPERM on a directory the user CAN write is a sandbox, not a
|
|
@@ -2921,10 +3031,10 @@ Branch on the code, never on the message.`,
|
|
|
2921
3031
|
readable-name-plus-digest directory already has a workspace.json for a
|
|
2922
3032
|
different canonical project path; do not overwrite it until you identify
|
|
2923
3033
|
which workspace owns it.`
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
3034
|
+
},
|
|
3035
|
+
STIM_NO_METRO: {
|
|
3036
|
+
summary: "the recorded launch's port is not this workspace's live dev server (reload)",
|
|
3037
|
+
body: () => `STIM_NO_METRO
|
|
2928
3038
|
Reload requires the recorded launch's port to be this workspace's live
|
|
2929
3039
|
Metro. It refuses a missing, changed, unresponsive, or foreign port. Run
|
|
2930
3040
|
\`stim start\`, or run \`ios\` or \`android\` again.
|
|
@@ -2935,19 +3045,19 @@ Branch on the code, never on the message.`,
|
|
|
2935
3045
|
(STIM_METRO_TIMEOUT, STIM_SUPERVISOR_EXITED, ...). A port held by SOMETHING
|
|
2936
3046
|
ELSE, usually a bundler started from the wrong directory or another repo's
|
|
2937
3047
|
Metro, gets a fresh reservation.`
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
3048
|
+
},
|
|
3049
|
+
STIM_NO_FINGERPRINT: {
|
|
3050
|
+
summary: "@expo/fingerprint produced no hash, so the shared cache cannot be addressed",
|
|
3051
|
+
body: () => `STIM_NO_FINGERPRINT
|
|
2942
3052
|
\`@expo/fingerprint\` produced no hash, so the shared build cache cannot be
|
|
2943
3053
|
addressed. Stim uses its declared @expo/fingerprint dependency directly,
|
|
2944
3054
|
independently of the target project's package graph. This is a refusal
|
|
2945
3055
|
rather than a silent full build because an unaddressable cache means every
|
|
2946
3056
|
workspace on the commit compiles from scratch, forever.`
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
3057
|
+
},
|
|
3058
|
+
STIM_PREBUILD_FAILED: {
|
|
3059
|
+
summary: "expo prebuild could not generate or regenerate the native directory",
|
|
3060
|
+
body: () => `STIM_PREBUILD_FAILED
|
|
2951
3061
|
\`expo prebuild\` could not generate the missing native directory, or
|
|
2952
3062
|
regenerate a stale one. The extracted output is above the code; the
|
|
2953
3063
|
transcript is in the global workspace logs/build-<platform>.ndjson file.
|
|
@@ -2958,10 +3068,10 @@ Branch on the code, never on the message.`,
|
|
|
2958
3068
|
directory from the fingerprint (.fingerprintignore), or gitignore and
|
|
2959
3069
|
untrack it so Stim regenerates it. When git itself is failing, fix the
|
|
2960
3070
|
checkout first.`
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
3071
|
+
},
|
|
3072
|
+
STIM_DEPS_FAILED: {
|
|
3073
|
+
summary: "pod install or gradle sync failed; the bundler ladder and BUNDLE_FROZEN",
|
|
3074
|
+
body: () => `STIM_DEPS_FAILED
|
|
2965
3075
|
\`pod install\` (iOS) or the gradle dependency sync (Android) failed. On iOS
|
|
2966
3076
|
this runs only when Podfile.lock and Pods/Manifest.lock disagree, or Pods is
|
|
2967
3077
|
absent -- which is exactly what a carried worktree produces.
|
|
@@ -2989,10 +3099,10 @@ Branch on the code, never on the message.`,
|
|
|
2989
3099
|
\`yarn install\`, \`bun install\`, \`npm ci\`) or that same pod ladder. The
|
|
2990
3100
|
message names the command, quotes its last lines, and nothing is copied: fix
|
|
2991
3101
|
the source checkout, then warm again.`
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
3102
|
+
},
|
|
3103
|
+
STIM_BUILD_FAILED: {
|
|
3104
|
+
summary: "xcodebuild or gradle failed; the Android missing-SDK and APK refusals; a damaged compilation-cache object",
|
|
3105
|
+
body: () => `STIM_BUILD_FAILED
|
|
2996
3106
|
xcodebuild or gradle failed. The EXTRACTED diagnostics are printed (capped),
|
|
2997
3107
|
not the transcript. Read the log path on the next line for the rest.
|
|
2998
3108
|
Three Android refusals share this code without gradle itself failing:
|
|
@@ -3029,10 +3139,10 @@ Branch on the code, never on the message.`,
|
|
|
3029
3139
|
object, and upgrading the CLI does not clear it. Empty that one cache with
|
|
3030
3140
|
\`gc --delete --cache "compilation cache"\`, then build again. The next
|
|
3031
3141
|
build is a cold one.`
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3142
|
+
},
|
|
3143
|
+
STIM_PATH_TOO_LONG: {
|
|
3144
|
+
summary: "Windows only: the project root leaves no room for the NDK object paths ninja must open",
|
|
3145
|
+
body: () => `STIM_PATH_TOO_LONG (android, Windows only)
|
|
3036
3146
|
The NDK's ninja is not long-path aware, so every CMake object path has to
|
|
3037
3147
|
stay under Windows' MAX_PATH. React Native's codegen objects are named
|
|
3038
3148
|
after their mangled absolute source path, so each carries the project root
|
|
@@ -3048,10 +3158,10 @@ Branch on the code, never on the message.`,
|
|
|
3048
3158
|
preflight does not inspect that configured path and may refuse it too.
|
|
3049
3159
|
\`doctor --platform android\` reports the same root as a finding for the
|
|
3050
3160
|
emulator's ABI.`
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3161
|
+
},
|
|
3162
|
+
fallbacks: {
|
|
3163
|
+
summary: "release cache-hit notes that are not codes: swap failure, asset gate, uninstall, device fallbacks",
|
|
3164
|
+
body: () => `FALLBACK NOTES THAT ARE NOT CODES (release cache hits)
|
|
3055
3165
|
On a release cache hit Stim regenerates this workspace's JS bundle into a
|
|
3056
3166
|
COPY of the cached artifact before installing it -- \`ios --configuration
|
|
3057
3167
|
Release\` into a copy of the .app, \`android --variant ...Release\` into a
|
|
@@ -3121,10 +3231,10 @@ Branch on the code, never on the message.`,
|
|
|
3121
3231
|
The same refusal on a FRESHLY BUILT app is a code (STIM_NO_PROFILE,
|
|
3122
3232
|
STIM_PROFILE_MISMATCH, STIM_NO_SIGNING_IDENTITY), not a note: building again
|
|
3123
3233
|
would produce the same app and refuse again.`
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3234
|
+
},
|
|
3235
|
+
STIM_BUILD_WAIT_TIMEOUT: {
|
|
3236
|
+
summary: "waited ~90 minutes for another workspace's build of the same fingerprint",
|
|
3237
|
+
body: () => `STIM_BUILD_WAIT_TIMEOUT
|
|
3128
3238
|
This run was waiting for ANOTHER workspace's build of the same fingerprint
|
|
3129
3239
|
(see \`guide lifecycle concurrency\`), and no artifact arrived within ~90
|
|
3130
3240
|
minutes.
|
|
@@ -3133,10 +3243,10 @@ Branch on the code, never on the message.`,
|
|
|
3133
3243
|
may have failed. The message names the current pid and lock directory:
|
|
3134
3244
|
check the pid, and if it is not really building, remove that directory and
|
|
3135
3245
|
run the command again.`
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3246
|
+
},
|
|
3247
|
+
STIM_CLAIM_REFUSED: {
|
|
3248
|
+
summary: "an ownership claim blocks the operation; inspect its holder before removing an unresolved claim",
|
|
3249
|
+
body: () => `STIM_CLAIM_REFUSED
|
|
3140
3250
|
A build lock records the holder's process IDENTITY, not just its pid, so a
|
|
3141
3251
|
recycled pid reads as a gone builder rather than a live one, and a builder
|
|
3142
3252
|
busy in a long \`simctl\` or gradle call reads as live rather than as stale.
|
|
@@ -3174,19 +3284,19 @@ Branch on the code, never on the message.`,
|
|
|
3174
3284
|
Legacy inline \`deletionClaim\` fields in config.json have no process
|
|
3175
3285
|
identity and require separate manual inspection; \`stim guide lifecycle pool\`
|
|
3176
3286
|
describes their field-only recovery.`
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3287
|
+
},
|
|
3288
|
+
STIM_MACOS_OWNER_UNVERIFIED: {
|
|
3289
|
+
summary: "the macOS app owner cannot be verified; no signal is sent",
|
|
3290
|
+
body: () => `STIM_MACOS_OWNER_UNVERIFIED
|
|
3181
3291
|
The recorded macOS process identity is unavailable or its workspace record
|
|
3182
3292
|
is malformed. Stim sends no signal to an owner it cannot verify. Inspect the
|
|
3183
3293
|
named process and workspace record before repairing it; do not replace a PID
|
|
3184
3294
|
or token with another running app. Retry stim stop once identity inspection
|
|
3185
3295
|
works. See stim guide macos.`
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3296
|
+
},
|
|
3297
|
+
STIM_OFFLOAD_REFUSED: {
|
|
3298
|
+
summary: "the selected build machine cannot build this app",
|
|
3299
|
+
body: () => `STIM_OFFLOAD_REFUSED
|
|
3190
3300
|
|
|
3191
3301
|
A named --build-machine selection is strict. The message names the machine
|
|
3192
3302
|
and why it cannot take or finish the build: not configured or paired, approval
|
|
@@ -3207,10 +3317,26 @@ or runs stim-server setup on the worker with its node, ticket and expiry
|
|
|
3207
3317
|
Rerun with --build-machine auto for normal placement and local fallback, or
|
|
3208
3318
|
--build-machine local to keep the build here.
|
|
3209
3319
|
`
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3320
|
+
},
|
|
3321
|
+
STIM_WORKTREE_SERVICE: {
|
|
3322
|
+
summary: "worktree remove refuses a folder a stim-server service runs from",
|
|
3323
|
+
body: () => `STIM_WORKTREE_SERVICE
|
|
3324
|
+
A LaunchAgent that \`stim-server service install\` wrote (it carries a
|
|
3325
|
+
StimService marker in ~/Library/LaunchAgents/<label>.plist, default label
|
|
3326
|
+
dev.stim.server) runs a program from inside this worktree. Removing the
|
|
3327
|
+
folder would delete the running server's code. The refusal also covers a
|
|
3328
|
+
service that is installed but stopped, because launchd loads it again at the
|
|
3329
|
+
next login, and a marked plist Stim could not read. \`--force\` does not
|
|
3330
|
+
override it, and gc leaves the worktree in place.
|
|
3331
|
+
Reinstall the service from another build (stim-server service install --label
|
|
3332
|
+
<label> from a checkout that stays), or remove it with stim-server service
|
|
3333
|
+
uninstall --label <label>, then run stim worktree remove again. Nothing was
|
|
3334
|
+
reclaimed or removed. Other processes with their working directory in the
|
|
3335
|
+
folder are not detected.`
|
|
3336
|
+
},
|
|
3337
|
+
STIM_CLAIM_UNAVAILABLE: {
|
|
3338
|
+
summary: "a process identity or warm claim store is unavailable, so the protected operation refuses",
|
|
3339
|
+
body: () => `STIM_CLAIM_UNAVAILABLE
|
|
3214
3340
|
Every ownership claim records the holder's process identity, captured through
|
|
3215
3341
|
the \`unique-pid\` native module. This code is that capture failing: no
|
|
3216
3342
|
prebuilt binary for this platform and architecture, or the OS refusing to
|
|
@@ -3230,10 +3356,10 @@ Rerun with --build-machine auto for normal placement and local fallback, or
|
|
|
3230
3356
|
access to the existing claim store, checking parent permissions, symlink
|
|
3231
3357
|
targets, mount access and sandbox rules, then retry. Do not switch STIM_HOME to evade a claim:
|
|
3232
3358
|
concurrent runs must coordinate through the same store.`
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3359
|
+
},
|
|
3360
|
+
STIM_INSTALL_FAILED: {
|
|
3361
|
+
summary: "simctl, adb, or devicectl refused the artifact; the one signer-conflict retry",
|
|
3362
|
+
body: () => `STIM_INSTALL_FAILED
|
|
3237
3363
|
The artifact built or came from cache, but \`simctl install\` / \`adb install\` /
|
|
3238
3364
|
\`devicectl device install app\` refused it. A signature or architecture
|
|
3239
3365
|
mismatch, or a full device.
|
|
@@ -3255,10 +3381,10 @@ Rerun with --build-machine auto for normal placement and local fallback, or
|
|
|
3255
3381
|
On a REMOTE device (\`--remote\`) agent-device refused the upload or install.
|
|
3256
3382
|
With \`--remote eas\` the EAS Simulator session stays up and billed; the
|
|
3257
3383
|
remedy names it. Rerun to reuse it, or run \`stim stop\` to end it.`
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3384
|
+
},
|
|
3385
|
+
STIM_LAUNCH_FAILED: {
|
|
3386
|
+
summary: "installed but would not start; the developer-trust tap on a phone",
|
|
3387
|
+
body: () => `STIM_LAUNCH_FAILED
|
|
3262
3388
|
Installed, but the app would not start. On Android this usually means no
|
|
3263
3389
|
launchable activity resolved.
|
|
3264
3390
|
On a local iOS simulator, a timed-out launch can mean the simulator cannot
|
|
@@ -3278,25 +3404,25 @@ Rerun with --build-machine auto for normal placement and local fallback, or
|
|
|
3278
3404
|
signer-conflict retry performs.
|
|
3279
3405
|
With \`--remote eas\` the remedy names the EAS Simulator session that is
|
|
3280
3406
|
still running, as for STIM_INSTALL_FAILED.`
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3407
|
+
},
|
|
3408
|
+
STIM_NO_SCHEME: {
|
|
3409
|
+
summary: "Xcode schemes unavailable or no unambiguous app scheme in ios/",
|
|
3410
|
+
body: () => `STIM_NO_SCHEME
|
|
3285
3411
|
Stim could not list or select an app scheme in ios/. Share the intended app
|
|
3286
3412
|
scheme so xcodebuild can see it. Select an available exact name with
|
|
3287
3413
|
\`stim ios --scheme <name>\`. An unknown explicit name prints available choices.
|
|
3288
3414
|
Without an explicit selector, a workspace
|
|
3289
3415
|
name match wins; otherwise Stim accepts a sole non-test scheme, or a listed
|
|
3290
3416
|
scheme matching app.json. Unmatched ambiguous schemes are refused.`
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3417
|
+
},
|
|
3418
|
+
STIM_NO_PROFILE: {
|
|
3419
|
+
summary: "no or undecodable embedded.mobileprovision; build once from Xcode",
|
|
3420
|
+
separator: "--- iOS SIGNING CODES (`ios --device`, and only there) ---",
|
|
3421
|
+
context: `A simulator build needs no signature, which is why none of these can fire on
|
|
3296
3422
|
the normal path. A device build carries one, and Stim re-seals any bundle it
|
|
3297
3423
|
modifies with the identity the bundle already names -- so it checks, before
|
|
3298
3424
|
spending a build or a bundle, that the check can succeed.`,
|
|
3299
|
-
|
|
3425
|
+
body: () => `STIM_NO_PROFILE
|
|
3300
3426
|
The built or cached .app has no embedded.mobileprovision, or
|
|
3301
3427
|
\`openssl smime\` could not decode the one it has. The first means the build
|
|
3302
3428
|
produced an unsigned app -- almost always a simulator-sliced artifact.
|
|
@@ -3305,14 +3431,14 @@ spending a build or a bundle, that the check can succeed.`,
|
|
|
3305
3431
|
profile. Stim will not do that step: registering a device or minting a
|
|
3306
3432
|
profile changes your Apple Developer account, so Stim never passes
|
|
3307
3433
|
-allowProvisioningUpdates.`
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3434
|
+
},
|
|
3435
|
+
STIM_PROFILE_MISMATCH: {
|
|
3436
|
+
summary: "the profile is expired, has no ProvisionedDevices, or does not name this UDID",
|
|
3437
|
+
context: `A simulator build needs no signature, which is why none of these can fire on
|
|
3312
3438
|
the normal path. A device build carries one, and Stim re-seals any bundle it
|
|
3313
3439
|
modifies with the identity the bundle already names -- so it checks, before
|
|
3314
3440
|
spending a build or a bundle, that the check can succeed.`,
|
|
3315
|
-
|
|
3441
|
+
body: () => `STIM_PROFILE_MISMATCH
|
|
3316
3442
|
The profile inside the app cannot admit this phone. Three shapes, and the
|
|
3317
3443
|
message names which one and the profile type it found:
|
|
3318
3444
|
- it expired, or carries no ExpirationDate at all;
|
|
@@ -3326,14 +3452,14 @@ spending a build or a bundle, that the check can succeed.`,
|
|
|
3326
3452
|
With --eas-profile, follow the EAS device:create and build commands in the
|
|
3327
3453
|
refusal instead. Registration, signing changes and cloud builds need session
|
|
3328
3454
|
authorization. See stim guide lifecycle eas.`
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3455
|
+
},
|
|
3456
|
+
STIM_NO_SIGNING_IDENTITY: {
|
|
3457
|
+
summary: "no single keychain identity resolves; ios.signingIdentitySha1 for two certificates",
|
|
3458
|
+
context: `A simulator build needs no signature, which is why none of these can fire on
|
|
3333
3459
|
the normal path. A device build carries one, and Stim re-seals any bundle it
|
|
3334
3460
|
modifies with the identity the bundle already names -- so it checks, before
|
|
3335
3461
|
spending a build or a bundle, that the check can succeed.`,
|
|
3336
|
-
|
|
3462
|
+
body: () => `STIM_NO_SIGNING_IDENTITY
|
|
3337
3463
|
No single keychain identity could be resolved to re-seal with. Either
|
|
3338
3464
|
\`security find-identity -v -p codesigning\` lists nothing, or the identity
|
|
3339
3465
|
the artifact's own profile names is absent, or two certificates share that
|
|
@@ -3341,14 +3467,14 @@ spending a build or a bundle, that the check can succeed.`,
|
|
|
3341
3467
|
Open Xcode > Settings > Accounts and download your certificates, or unlock
|
|
3342
3468
|
the login keychain with \`security unlock-keychain\`. For the two-certificate
|
|
3343
3469
|
case, set ios.signingIdentitySha1 to the SHA-1 hash beside the one you want.`
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3470
|
+
},
|
|
3471
|
+
STIM_CODESIGN_FAILED: {
|
|
3472
|
+
summary: "codesign failed on the modified copy; the cache entry is untouched and the run builds fresh",
|
|
3473
|
+
context: `A simulator build needs no signature, which is why none of these can fire on
|
|
3348
3474
|
the normal path. A device build carries one, and Stim re-seals any bundle it
|
|
3349
3475
|
modifies with the identity the bundle already names -- so it checks, before
|
|
3350
3476
|
spending a build or a bundle, that the check can succeed.`,
|
|
3351
|
-
|
|
3477
|
+
body: () => `STIM_CODESIGN_FAILED
|
|
3352
3478
|
\`codesign --force --sign\` or \`codesign --verify --strict\` exited non-zero
|
|
3353
3479
|
on the modified copy. The verbatim codesign stderr is quoted, because it is
|
|
3354
3480
|
the answer: a locked login keychain reports errSecInternalComponent, an
|
|
@@ -3356,14 +3482,14 @@ spending a build or a bundle, that the check can succeed.`,
|
|
|
3356
3482
|
and confirm exactly one identity matches the name. The cache entry itself is
|
|
3357
3483
|
never modified -- the failure is on a temporary copy, and the run builds
|
|
3358
3484
|
fresh.`
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3485
|
+
},
|
|
3486
|
+
STIM_NO_LAN_ADDRESS: {
|
|
3487
|
+
summary: "the Mac has no non-internal IPv4 interface; a tunnel cannot help a phone",
|
|
3488
|
+
separator: "--- iOS DEVICE DEBUG REACHABILITY CODES (`ios --device` in Debug) ---",
|
|
3489
|
+
context: `A phone does not share the host's loopback and USB carries no reverse forward,
|
|
3364
3490
|
so a Debug run on one is wired to a LAN origin instead of localhost. Both codes
|
|
3365
3491
|
fire BEFORE the build, because a refusal that costs a build is a bad refusal.`,
|
|
3366
|
-
|
|
3492
|
+
body: () => `STIM_NO_LAN_ADDRESS
|
|
3367
3493
|
This Mac reports no non-internal IPv4 interface, so there is no address to
|
|
3368
3494
|
give the phone: it is offline, or on nothing but utun/awdl/bridge. Join a
|
|
3369
3495
|
Wi-Fi or Ethernet network, or connect this Mac by cable, and run again.
|
|
@@ -3372,13 +3498,13 @@ fire BEFORE the build, because a refusal that costs a build is a bad refusal.`,
|
|
|
3372
3498
|
ip.txt is read by RCTBundleURLProvider, which prefixes the scheme. A tunnel
|
|
3373
3499
|
cannot be expressed to a phone, so --device ignores metro.publicUrl,
|
|
3374
3500
|
metro.tunnel and metro.ngrokUrl and says so when one is set.`
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3501
|
+
},
|
|
3502
|
+
STIM_LAN_METRO_UNREACHABLE: {
|
|
3503
|
+
summary: "the LAN origin did not answer as this workspace's Metro; ios.lanHost on a multi-NIC Mac",
|
|
3504
|
+
context: `A phone does not share the host's loopback and USB carries no reverse forward,
|
|
3379
3505
|
so a Debug run on one is wired to a LAN origin instead of localhost. Both codes
|
|
3380
3506
|
fire BEFORE the build, because a refusal that costs a build is a bad refusal.`,
|
|
3381
|
-
|
|
3507
|
+
body: () => `STIM_LAN_METRO_UNREACHABLE
|
|
3382
3508
|
The chosen LAN origin did not answer as THIS workspace's Metro: no answer, a
|
|
3383
3509
|
5xx, or a dev server that is not this one -- the message says which.
|
|
3384
3510
|
\`stim start\` prints the port it reserved. On a Mac with several interfaces
|
|
@@ -3388,12 +3514,12 @@ fire BEFORE the build, because a refusal that costs a build is a bad refusal.`,
|
|
|
3388
3514
|
routes a host connection to its own address over loopback, so the gate passes
|
|
3389
3515
|
through a firewall that will block the phone. That evidence only ever arrives
|
|
3390
3516
|
from the phone's own bundle request, which is what \`launched\` reports.`
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3517
|
+
},
|
|
3518
|
+
unverified: {
|
|
3519
|
+
summary: "launched: \"unverified\" with the Local Network path reason, and the routed recovery",
|
|
3520
|
+
context: `A phone does not share the host's loopback and USB carries no reverse forward,
|
|
3395
3521
|
so a Debug run on one is wired to a LAN origin instead of localhost.`,
|
|
3396
|
-
|
|
3522
|
+
body: () => `LAUNCH UNVERIFIED, LOCAL NETWORK NOT GRANTED (not a code -- a routed remedy)
|
|
3397
3523
|
An app that has not been granted Local Network reaches nothing on the LAN,
|
|
3398
3524
|
and CFNetwork reports each attempt as NSURLErrorDomain -1009 "The Internet
|
|
3399
3525
|
connection appears to be offline." with the path reason
|
|
@@ -3479,11 +3605,11 @@ so a Debug run on one is wired to a LAN origin instead of localhost.`,
|
|
|
3479
3605
|
General > VPN & Device Management) is refused to automation by the same gate
|
|
3480
3606
|
that refuses the app, agent-device's own runner included, so its remedy is
|
|
3481
3607
|
"ask the user" and nothing else. An uninstall clears both.`
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3608
|
+
},
|
|
3609
|
+
STIM_NO_DEVICE: {
|
|
3610
|
+
summary: "no usable phone, or the owned simulator or emulator could not be created or booted",
|
|
3611
|
+
separator: "--- DEVICE AND CAPACITY CODES ---",
|
|
3612
|
+
body: () => `STIM_NO_DEVICE
|
|
3487
3613
|
With \`--device\`, no physical device answered the selection: none connected,
|
|
3488
3614
|
a named serial/UDID that is not connected, several connected with none named
|
|
3489
3615
|
(the refusal lists them), or one that is connected but unusable -- an
|
|
@@ -3495,6 +3621,10 @@ so a Debug run on one is wired to a LAN origin instead of localhost.`,
|
|
|
3495
3621
|
reach a booted state. \`stim doctor\` checks the toolchain; \`stim status\` says what
|
|
3496
3622
|
Stim thinks it owns. Re-running the command creates a fresh owned device
|
|
3497
3623
|
when the recorded one is gone.
|
|
3624
|
+
With concurrency.maxDevices set, it also means Stim could not count the
|
|
3625
|
+
booted devices before a boot: a simctl or adb listing failed or timed out,
|
|
3626
|
+
usually under heavy load, or other runs held the device-admission lock for
|
|
3627
|
+
5 minutes. Retry once the load falls.
|
|
3498
3628
|
If Android creation says an AVD already exists on disk but is not listed,
|
|
3499
3629
|
run \`npx stim gc\` to inspect orphaned owned AVDs, then \`npx stim gc --delete\`
|
|
3500
3630
|
to reclaim those safe to delete before retrying. Keep anything GC cannot
|
|
@@ -3552,10 +3682,10 @@ so a Debug run on one is wired to a LAN origin instead of localhost.`,
|
|
|
3552
3682
|
The Android counterpart, from \`--device-profile\` or android.deviceProfile.
|
|
3553
3683
|
Reap the AVD the same way, or pass \`--slot <name>\` to create the requested
|
|
3554
3684
|
profile beside it.`
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3685
|
+
},
|
|
3686
|
+
STIM_DEVICE_BUSY: {
|
|
3687
|
+
summary: "another workspace holds the lease on that phone and the wait ran out",
|
|
3688
|
+
body: () => `STIM_DEVICE_BUSY
|
|
3559
3689
|
Only on a \`--device\` run. Another workspace holds the lease on that phone,
|
|
3560
3690
|
and the wait ran out: the message names the holder root, the device, and the
|
|
3561
3691
|
expiry as a clock time and a remaining duration, and \`--json\` adds
|
|
@@ -3569,10 +3699,10 @@ so a Debug run on one is wired to a LAN origin instead of localhost.`,
|
|
|
3569
3699
|
no token left in its \`state.json\` (its workspace directory was recreated).
|
|
3570
3700
|
The remedy for that last one is \`stim device unlock\`, which releases by
|
|
3571
3701
|
holder rather than by token.`
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3702
|
+
},
|
|
3703
|
+
STIM_DEVICE_WIRELESS_FAILED: {
|
|
3704
|
+
summary: "an iPhone paired over Wi-Fi timed out or dropped during install or launch; use the cable",
|
|
3705
|
+
body: () => `STIM_DEVICE_WIRELESS_FAILED
|
|
3576
3706
|
Only on an \`ios --device\` run whose iPhone devicectl reaches over Wi-Fi
|
|
3577
3707
|
(transportType localNetwork). The \`devicectl device install app\` ran past
|
|
3578
3708
|
its 15-minute Wi-Fi bound, the phone did not appear in its own process list
|
|
@@ -3584,10 +3714,10 @@ so a Debug run on one is wired to a LAN origin instead of localhost.`,
|
|
|
3584
3714
|
untrusted host, Developer Mode off, full storage, the developer-trust tap --
|
|
3585
3715
|
keeps STIM_INSTALL_FAILED or STIM_LAUNCH_FAILED and its own remedy, even
|
|
3586
3716
|
when the step also timed out and even during a signer-conflict reinstall.`
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3717
|
+
},
|
|
3718
|
+
STIM_DEVICE_LOST: {
|
|
3719
|
+
summary: "the lease was gone or re-held at the pre-install check; rerun",
|
|
3720
|
+
body: () => `STIM_DEVICE_LOST
|
|
3591
3721
|
Only on a \`--device\` run. The run held a lease, and the raise before the
|
|
3592
3722
|
install found it gone or held under another token -- another workspace took
|
|
3593
3723
|
the device in that window. The message names the new holder and its expiry.
|
|
@@ -3595,24 +3725,32 @@ so a Debug run on one is wired to a LAN origin instead of localhost.`,
|
|
|
3595
3725
|
AFTER the install has started this is not a failure: the app is already on
|
|
3596
3726
|
the phone, so the run prints one warning, continues, and reports
|
|
3597
3727
|
\`lease: null\` in \`--json\`.`
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
Only when concurrency.maxDevices is set (
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3728
|
+
},
|
|
3729
|
+
STIM_AT_CAPACITY: {
|
|
3730
|
+
summary: "owned-device slot wait timed out, or --no-wait reached the cap or queue",
|
|
3731
|
+
body: () => `STIM_AT_CAPACITY
|
|
3732
|
+
Only when concurrency.maxDevices is set (unset by default). New owned
|
|
3733
|
+
devices queue in FIFO order across $STIM_HOME for 600 seconds by default.
|
|
3734
|
+
This refusal means the wait expired, or \`--no-wait\` / \`--wait 0\`
|
|
3735
|
+
found the cap full or another run queued. The message gives the current
|
|
3736
|
+
count and, on timeout, how long it waited. iOS waits beside its build,
|
|
3737
|
+
so a rerun can reuse the artifact. A workspace's own booted or booting
|
|
3738
|
+
device bypasses the queue. At the cap, the FIFO head shuts down the
|
|
3739
|
+
longest-idle eligible owned device from another workspace, one per attempt,
|
|
3740
|
+
at most once every 15 seconds, then rechecks capacity.
|
|
3741
|
+
devices.reclaimIdleMinutes defaults to 10; 0 off. The same idle checks as supervisor shutdown (default 30 minutes) protect drivers,
|
|
3742
|
+
locks, builds, viewers and recent activity. Reclaim excludes this workspace,
|
|
3743
|
+
physical, hosted, remote, parked and other homes' devices and never deletes.
|
|
3744
|
+
A failed reclaim is logged and skipped; the run keeps waiting.
|
|
3745
|
+
Stop an environment (\`stim stop\`), pass a longer \`--wait <seconds>\`,
|
|
3746
|
+
or raise concurrency.maxDevices. Waiting prints holder names and elapsed
|
|
3747
|
+
time; status JSON exposes build.waitingFor independently of phase.
|
|
3748
|
+
Stats records capacityWaits for waits and capacityRefusals for this code.
|
|
3749
|
+
See \`guide lifecycle concurrency\`.`
|
|
3750
|
+
},
|
|
3751
|
+
STIM_LOW_DISK: {
|
|
3752
|
+
summary: "free disk stayed below budget.hardFloorDiskGb after reclaiming",
|
|
3753
|
+
body: () => `STIM_LOW_DISK
|
|
3616
3754
|
\`start\`, \`ios\` and \`android\` check free disk on the volumes holding
|
|
3617
3755
|
the app and $STIM_HOME before they build or boot anything. Below
|
|
3618
3756
|
budget.minFreeDiskGb (20 GB by default) they first reclaim what Stim can
|
|
@@ -3633,10 +3771,10 @@ so a Debug run on one is wired to a LAN origin instead of localhost.`,
|
|
|
3633
3771
|
Ask before deleting anything outside Stim, such as Xcode DerivedData or
|
|
3634
3772
|
simulators Stim did not create. Lowering budget.hardFloorDiskGb (0 never
|
|
3635
3773
|
refuses) only removes the protection.`
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3774
|
+
},
|
|
3775
|
+
STIM_BUILD_SLOT_TIMEOUT: {
|
|
3776
|
+
summary: "the maxBuilds wait gave up with every slot held by a running process",
|
|
3777
|
+
body: () => `STIM_BUILD_SLOT_TIMEOUT
|
|
3640
3778
|
Only when concurrency.maxBuilds is set. The build cap does not refuse, it
|
|
3641
3779
|
WAITS -- this code is that wait giving up: ~90 minutes elapsed and every one
|
|
3642
3780
|
of the N slots was still held by a running process, or by a holder Stim could
|
|
@@ -3650,44 +3788,44 @@ so a Debug run on one is wired to a LAN origin instead of localhost.`,
|
|
|
3650
3788
|
the message names the directory: remove the slot of a builder that is not
|
|
3651
3789
|
building, or raise concurrency.maxBuilds
|
|
3652
3790
|
(\`guide lifecycle concurrency\`).`
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3791
|
+
},
|
|
3792
|
+
STIM_NO_REMOTE_SESSION: {
|
|
3793
|
+
summary: "the backend could not use agent-device, or metro.tunnel names an unusable provider",
|
|
3794
|
+
separator: "--- REMOTE-DEVICE CODES (`ios --remote <proxy|eas>` / `android --remote <proxy|eas>`) ---",
|
|
3795
|
+
body: () => `STIM_NO_REMOTE_SESSION
|
|
3658
3796
|
The selected backend could not use agent-device, or metro.tunnel names a
|
|
3659
3797
|
provider or mode this workspace cannot use (e.g. "expo" on a bare RN
|
|
3660
3798
|
project). The remedy line says which. Nothing was created yet.`
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3799
|
+
},
|
|
3800
|
+
STIM_REMOTE_PROXY_CONFIG: {
|
|
3801
|
+
summary: "--remote proxy needs AGENT_DEVICE_DAEMON_BASE_URL and AGENT_DEVICE_DAEMON_AUTH_TOKEN",
|
|
3802
|
+
body: () => `STIM_REMOTE_PROXY_CONFIG
|
|
3665
3803
|
\`--remote proxy\` requires AGENT_DEVICE_DAEMON_BASE_URL and
|
|
3666
3804
|
AGENT_DEVICE_DAEMON_AUTH_TOKEN. These variables provide credentials after
|
|
3667
3805
|
proxy is selected. They never select the backend.`
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3806
|
+
},
|
|
3807
|
+
STIM_REMOTE_EAS_UNAVAILABLE: {
|
|
3808
|
+
summary: "--remote eas needs eas-cli 21.6.0 or later",
|
|
3809
|
+
body: () => `STIM_REMOTE_EAS_UNAVAILABLE
|
|
3672
3810
|
\`--remote eas\` requires eas-cli 21.6.0 or later, the first release with
|
|
3673
3811
|
the \`eas simulator:*\` commands Stim runs. Stim reads \`eas --version\` and
|
|
3674
3812
|
refuses before any build or session work when eas-cli is missing, older, or
|
|
3675
3813
|
reports no version. Upgrade it (\`npm install --global eas-cli@latest\`, or
|
|
3676
3814
|
the project's eas-cli dependency). Proxy environment variables do not change
|
|
3677
3815
|
this selection and are not passed to EAS.`
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3816
|
+
},
|
|
3817
|
+
STIM_REMOTE_PLATFORM_MISMATCH: {
|
|
3818
|
+
summary: "the recorded remote session belongs to the other platform; stop, then rerun",
|
|
3819
|
+
body: () => `STIM_REMOTE_PLATFORM_MISMATCH
|
|
3682
3820
|
This workspace already has a recorded remote session, and it belongs to the
|
|
3683
3821
|
OTHER platform ("Session <id> belongs to android, not ios"). A workspace
|
|
3684
3822
|
holds one remote session, and Stim will not end the recorded one to make
|
|
3685
3823
|
room -- it may be mid-run for whoever started it. Run \`stim stop\` for this
|
|
3686
3824
|
workspace, then re-run with the platform you want. Nothing was created here.`
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3825
|
+
},
|
|
3826
|
+
STIM_REMOTE_DEVICE_MISMATCH: {
|
|
3827
|
+
summary: "the recorded EAS session runs another model than --device-type asks for; stop, then rerun",
|
|
3828
|
+
body: () => `STIM_REMOTE_DEVICE_MISMATCH
|
|
3691
3829
|
\`stim ios --remote eas --device-type <name>\` found this workspace's
|
|
3692
3830
|
recorded EAS Simulator session still running another model, or the model
|
|
3693
3831
|
EAS chose when no --device-type was given. EAS cannot change a running
|
|
@@ -3696,20 +3834,20 @@ so a Debug run on one is wired to a LAN origin instead of localhost.`,
|
|
|
3696
3834
|
workspace, then rerun with the model you want. A recorded session that has
|
|
3697
3835
|
already ended is replaced on the requested model instead, and without
|
|
3698
3836
|
--device-type a live one is reused as it is. Nothing was created here.`
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3837
|
+
},
|
|
3838
|
+
STIM_REMOTE_SESSION_STATE: {
|
|
3839
|
+
summary: "the EAS session was created but its state could not be recorded, so Stim stopped it",
|
|
3840
|
+
body: () => `STIM_REMOTE_SESSION_STATE
|
|
3703
3841
|
The EAS session was created and is healthy, but recording it in this
|
|
3704
3842
|
workspace's state failed (an unwritable STIM_HOME, a full disk). A session
|
|
3705
3843
|
nothing references is a session nothing will ever stop, so Stim stopped the
|
|
3706
3844
|
one it had just created and removed its ownership claim before reporting:
|
|
3707
3845
|
this code means nothing is running and nothing is still billing. Repair the
|
|
3708
3846
|
state storage the message names, then run the remote command again.`
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
3847
|
+
},
|
|
3848
|
+
STIM_REMOTE_SESSION_CLEANUP: {
|
|
3849
|
+
summary: "Stim could not prove an EAS session ended; eas simulator:stop --id",
|
|
3850
|
+
body: () => `STIM_REMOTE_SESSION_CLEANUP
|
|
3713
3851
|
Stim tried to end an EAS session and could not PROVE it ended: \`eas
|
|
3714
3852
|
simulator:stop\` failed, or its output did not confirm the stop, or the
|
|
3715
3853
|
session stopped but its claim in the machine ledger could not be removed.
|
|
@@ -3718,10 +3856,10 @@ so a Debug run on one is wired to a LAN origin instead of localhost.`,
|
|
|
3718
3856
|
\`eas simulator:stop --id <id>\` -- and for a ledger that outlived its
|
|
3719
3857
|
session, the ledger path to repair. The same code covers a recorded session
|
|
3720
3858
|
that could not be verified before replacement: inspect it, then \`stim stop\`.`
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3859
|
+
},
|
|
3860
|
+
STIM_REMOTE_METRO_WRONG: {
|
|
3861
|
+
summary: "the tunnel reaches a Metro that is not this workspace's",
|
|
3862
|
+
body: () => `STIM_REMOTE_METRO_WRONG
|
|
3725
3863
|
The gate that proves a tunnel still reaches THIS workspace's Metro failed --
|
|
3726
3864
|
before a session or a build, whether the tunnel is Expo's own, one Stim
|
|
3727
3865
|
started (metro.tunnel: cloudflared/ngrok/auto), or a named metro.publicUrl.
|
|
@@ -3731,32 +3869,32 @@ so a Debug run on one is wired to a LAN origin instead of localhost.`,
|
|
|
3731
3869
|
port), and it now serves ANOTHER workspace's dev server -- healthy, and
|
|
3732
3870
|
wrong. Re-run \`stim start\` (it prints the port it reserved) and, for a
|
|
3733
3871
|
manual tunnel, rebuild it against that port.`
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3872
|
+
},
|
|
3873
|
+
STIM_REMOTE_METRO_UNREACHABLE: {
|
|
3874
|
+
summary: "a remote start could not create its managed tunnel or tell the device where Metro is",
|
|
3875
|
+
body: () => `STIM_REMOTE_METRO_UNREACHABLE
|
|
3738
3876
|
A remote start could not create its selected managed tunnel, or the device
|
|
3739
3877
|
could not be told where Metro is. Follows the same remedy as
|
|
3740
3878
|
STIM_NO_REMOTE_SESSION's tunnel guidance -- set metro.tunnel, or use
|
|
3741
3879
|
metro.publicUrl for an existing endpoint.`
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3880
|
+
},
|
|
3881
|
+
STIM_RELOAD_AMBIGUOUS: {
|
|
3882
|
+
summary: "more than one owned app or the owned Chrome is live; name the platform",
|
|
3883
|
+
separator: "--- RELOAD CODES (`stim reload [ios|android|web]`) ---",
|
|
3884
|
+
body: () => `STIM_RELOAD_AMBIGUOUS
|
|
3747
3885
|
More than one of the owned iOS app, Android app and Chrome page is live.
|
|
3748
3886
|
Name ios, android or web; Stim never guesses.`
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3887
|
+
},
|
|
3888
|
+
STIM_RELOAD_RELEASE: {
|
|
3889
|
+
summary: "the live app has embedded JS; run a Debug build first",
|
|
3890
|
+
body: () => `STIM_RELOAD_RELEASE
|
|
3753
3891
|
The live app was launched with embedded JavaScript. Run the platform command
|
|
3754
3892
|
with a Debug configuration or variant first.`
|
|
3755
|
-
|
|
3756
|
-
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
|
|
3893
|
+
},
|
|
3894
|
+
STIM_RELOAD_STOPPED: {
|
|
3895
|
+
summary: "the recorded app is gone, its device is not live and owned, or the process could not be proven",
|
|
3896
|
+
aliases: ["STIM_RELOAD_UNOWNED", "STIM_RELOAD_PROBE_FAILED"],
|
|
3897
|
+
body: () => `STIM_RELOAD_STOPPED / STIM_RELOAD_UNOWNED / STIM_RELOAD_PROBE_FAILED
|
|
3760
3898
|
The recorded app is gone, its exact device is not live and owned by this
|
|
3761
3899
|
workspace, or simctl/adb could not prove the process exists. For web,
|
|
3762
3900
|
STOPPED means no owned Chrome is running (run stim web) and PROBE_FAILED
|
|
@@ -3764,10 +3902,10 @@ so a Debug run on one is wired to a LAN origin instead of localhost.`,
|
|
|
3764
3902
|
only when no native launch is recorded. No launch or
|
|
3765
3903
|
device lifecycle action is taken; follow the printed platform-command or
|
|
3766
3904
|
process-probe remedy.`
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3905
|
+
},
|
|
3906
|
+
STIM_RELOAD_FAILED: {
|
|
3907
|
+
summary: "the reload failed; the remedy differs by shape -- read it before acting",
|
|
3908
|
+
body: () => `STIM_RELOAD_FAILED
|
|
3771
3909
|
The Metro websocket reload did not reach a peer Stim could identify. Two
|
|
3772
3910
|
shapes reach this code and the remedy differs. Read it rather than assuming.
|
|
3773
3911
|
|
|
@@ -3798,72 +3936,72 @@ so a Debug run on one is wired to a LAN origin instead of localhost.`,
|
|
|
3798
3936
|
MORE THAN ONE MATCHING PEER IS NOT A FAILURE. A workspace Metro serves one
|
|
3799
3937
|
app, so several matching peers are that app on several devices. Stim reloads
|
|
3800
3938
|
every one of them and reports the count in the facts as targets.`
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3939
|
+
},
|
|
3940
|
+
STIM_WEB_NO_CHROME: {
|
|
3941
|
+
summary: "stim web found no installed Chrome or Chromium",
|
|
3942
|
+
separator: "--- WEB CODES (`stim web`) ---",
|
|
3943
|
+
body: () => `STIM_WEB_NO_CHROME
|
|
3806
3944
|
stim web drives the installed Google Chrome (or Chromium) with a profile
|
|
3807
3945
|
Stim creates. It looks in /Applications and ~/Applications on macOS, in
|
|
3808
3946
|
Program Files on Windows, and for google-chrome or chromium on PATH. Stim
|
|
3809
3947
|
never installs a browser. Install Chrome, then run stim doctor.`
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
|
|
3948
|
+
},
|
|
3949
|
+
STIM_WEB_NO_URL: {
|
|
3950
|
+
summary: "not an Expo app and web.url is unset, so Stim does not know which page to open",
|
|
3951
|
+
body: () => `STIM_WEB_NO_URL
|
|
3814
3952
|
Only Expo serves web from Metro, so for any other app Stim needs web.url.
|
|
3815
3953
|
stim web never starts a web server, for any framework. Start yours on a
|
|
3816
3954
|
named port and point web.url at it:
|
|
3817
3955
|
pnpm exec vite --port "$(stim ports get web)" --strictPort
|
|
3818
3956
|
stim settings set web.url 'http://localhost:{port:web}/' --scope workspace
|
|
3819
3957
|
See stim guide web.`
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
|
|
3958
|
+
},
|
|
3959
|
+
STIM_WEB_DEPS_MISSING: {
|
|
3960
|
+
summary: "an Expo app without react-native-web cannot render on the web",
|
|
3961
|
+
body: () => `STIM_WEB_DEPS_MISSING
|
|
3824
3962
|
The Expo app does not resolve react-native-web, so Metro cannot build a web
|
|
3825
3963
|
bundle. Install the web dependencies, start Metro, then run stim web again:
|
|
3826
3964
|
npx expo install react-dom react-native-web @expo/metro-runtime
|
|
3827
3965
|
stim start`
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3966
|
+
},
|
|
3967
|
+
STIM_WEB_BROWSER_HELD: {
|
|
3968
|
+
summary: "the previous owned Chrome could not be stopped or verified; it was left running",
|
|
3969
|
+
body: () => `STIM_WEB_BROWSER_HELD
|
|
3832
3970
|
stim web replaces the workspace's owned Chrome when its options change, and
|
|
3833
3971
|
the previous one could not be stopped: its supervisor or Chrome did not
|
|
3834
3972
|
exit, or their process identities could not be verified. Stim never signals
|
|
3835
3973
|
a process it cannot verify. Check stim status for browser-unverified, then
|
|
3836
3974
|
follow stim guide errors teardown.`
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
|
|
3975
|
+
},
|
|
3976
|
+
STIM_WEB_LAUNCH_FAILED: {
|
|
3977
|
+
summary: "the owned Chrome did not start or did not open DevTools on its reserved port",
|
|
3978
|
+
body: () => `STIM_WEB_LAUNCH_FAILED
|
|
3841
3979
|
The browser supervisor exited before Chrome answered on its reserved
|
|
3842
3980
|
DevTools port. The remedy names the supervisor log; web.ndjson carries the
|
|
3843
3981
|
failure as web_browser_failed, and browser.log holds Chrome's own output.
|
|
3844
3982
|
A port another process bound first also lands here: run stim web again to
|
|
3845
3983
|
reserve a fresh one.`
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3984
|
+
},
|
|
3985
|
+
STIM_WORKTREE_REMOVAL_IN_PROGRESS: {
|
|
3986
|
+
summary: "a managed remote start found worktree remove holding the lock; wait, then rerun",
|
|
3987
|
+
separator: "--- DEV-SERVER CODES (`stim start`) ---",
|
|
3988
|
+
body: () => `STIM_WORKTREE_REMOVAL_IN_PROGRESS
|
|
3851
3989
|
A managed remote start found that \`stim worktree remove\` owns the
|
|
3852
3990
|
worktree lock. The start did not register the project or create a tunnel.
|
|
3853
3991
|
Wait for removal to finish, then run \`stim start --remote\` again.`
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
3992
|
+
},
|
|
3993
|
+
STIM_REMOTE_START_REQUIRED: {
|
|
3994
|
+
summary: "a running server cannot gain a remote tunnel; stop, then start --remote, or metro.publicUrl",
|
|
3995
|
+
body: () => `STIM_REMOTE_START_REQUIRED
|
|
3858
3996
|
A healthy bare or Expo server was started without its required remote
|
|
3859
3997
|
tunnel. A running server cannot gain that option. For a Stim supervisor,
|
|
3860
3998
|
run \`stim stop\`, then \`stim start --remote\`. For an external server,
|
|
3861
3999
|
configure metro.publicUrl or let Stim supervise the server.`
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
4000
|
+
},
|
|
4001
|
+
STIM_BARE_DEPS: {
|
|
4002
|
+
summary: "the supervisor cannot host Metro from the project's node_modules; the @stim-cli/metro capture note",
|
|
4003
|
+
aliases: ["STIM_BARE_LOAD", "STIM_BARE_API"],
|
|
4004
|
+
body: () => `STIM_BARE_DEPS / STIM_BARE_LOAD / STIM_BARE_API (bare RN)
|
|
3867
4005
|
The supervisor hosts Metro out of the PROJECT's node_modules, so metro,
|
|
3868
4006
|
@react-native/dev-middleware and @react-native-community/cli-server-api must
|
|
3869
4007
|
be installed there and must match the project's React Native. DEPS = not
|
|
@@ -3875,15 +4013,15 @@ captured" (in metro.ndjson, bare RN)
|
|
|
3875
4013
|
The dev server is serving; only capture is missing, so \`logs\` would report
|
|
3876
4014
|
a quiet timeline for a broken build. Install \`@stim-cli/metro\` as a
|
|
3877
4015
|
devDependency of the project.`
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
4016
|
+
},
|
|
4017
|
+
STIM_EXPO_BIN: {
|
|
4018
|
+
summary: "node_modules/.bin/expo is missing; install dependencies",
|
|
4019
|
+
body: () => `STIM_EXPO_BIN (Expo)
|
|
3882
4020
|
node_modules/.bin/expo does not exist. Install the project's dependencies.`
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
4021
|
+
},
|
|
4022
|
+
STIM_METRO_TIMEOUT: {
|
|
4023
|
+
summary: "the supervisor is alive but Metro or the tunnel was not ready within the wait; --wait 180",
|
|
4024
|
+
body: () => `STIM_METRO_TIMEOUT
|
|
3887
4025
|
"The dev server did not answer on port <n> within <s>s."
|
|
3888
4026
|
The supervisor is alive, but Metro or its requested Expo tunnel is not ready.
|
|
3889
4027
|
A Debug \`ios\` or \`android\` run that starts the dev server reports the
|
|
@@ -3892,10 +4030,10 @@ captured" (in metro.ndjson, bare RN)
|
|
|
3892
4030
|
printed the last lines of the global workspace logs/supervisor.log above this -- read
|
|
3893
4031
|
them. A cold Metro on a large graph can genuinely need more than the default
|
|
3894
4032
|
60s: re-run with \`--wait 180\`. Otherwise \`stim stop\`, then \`start\`.`
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
4033
|
+
},
|
|
4034
|
+
STIM_SUPERVISOR_EXITED: {
|
|
4035
|
+
summary: "the dev server failed outright; the quoted supervisor.log tail is the real error",
|
|
4036
|
+
body: () => `STIM_SUPERVISOR_EXITED
|
|
3899
4037
|
"The supervisor exited (<code|signal>) before the dev server came up"
|
|
3900
4038
|
The dev server failed outright, and the quoted evidence is the real error:
|
|
3901
4039
|
the supervisor.log tail if it wrote one, plus this attempt's error records
|
|
@@ -3910,11 +4048,11 @@ captured" (in metro.ndjson, bare RN)
|
|
|
3910
4048
|
Stim's processes. For a legacy record, stop the server with the tool that
|
|
3911
4049
|
started it before retrying. Never reconstruct ownership from a process
|
|
3912
4050
|
name, port, or wall-clock timestamp.`
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
|
|
4051
|
+
},
|
|
4052
|
+
STIM_BAD_ARG: {
|
|
4053
|
+
summary: "an argument, setting, directory, flavor, or device name refused before anything starts",
|
|
4054
|
+
aliases: ["STIM_NO_PROJECT"],
|
|
4055
|
+
body: () => `STIM_BAD_ARG / STIM_NO_PROJECT
|
|
3918
4056
|
The command refused before doing anything: an unusable --wait value, a known
|
|
3919
4057
|
setting with the wrong type ("Invalid <key> setting <value>. Expected <shape>."
|
|
3920
4058
|
-- \`guide settings\` names the type each key takes), an invalid
|
|
@@ -3932,8 +4070,9 @@ captured" (in metro.ndjson, bare RN)
|
|
|
3932
4070
|
on an eas/proxy run (\`--remote\` or ios.remote; that backend picks the
|
|
3933
4071
|
iOS version), \`ios --device-type\` on the proxy backend or with an
|
|
3934
4072
|
eas-cli older than 22.2.0 on the eas backend, \`android --system-image\` or
|
|
3935
|
-
\`--device-profile\` on
|
|
3936
|
-
|
|
4073
|
+
\`--device-profile\` on an eas/proxy run (\`--remote\` or android.remote), a
|
|
4074
|
+
\`android --remote auto\`, a changed hosted placement, or hosted Debug
|
|
4075
|
+
with \`--no-metro-check\` (see lifecycle hosted-android), a working directory
|
|
3937
4076
|
with no package.json above it, or one whose nearest package.json does not
|
|
3938
4077
|
parse or depends on neither react-native nor expo, so the directory is not
|
|
3939
4078
|
an app (the refusal names that package.json and says which of the two it
|
|
@@ -3982,11 +4121,11 @@ captured" (in metro.ndjson, bare RN)
|
|
|
3982
4121
|
list device -c\`) runs only when
|
|
3983
4122
|
a name was actually given, and a listing that fails is reported as
|
|
3984
4123
|
STIM_NO_DEVICE naming the tool, never as a crash.`
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
4124
|
+
},
|
|
4125
|
+
STIM_LOCK_REFUSED: {
|
|
4126
|
+
summary: "a directory lock is held by a removal, which is never waited out",
|
|
4127
|
+
separator: "--- COORDINATION CODES (any command that shares a resource) ---",
|
|
4128
|
+
body: () => `STIM_LOCK_REFUSED
|
|
3990
4129
|
A directory lock that serialises two commands over the same thing -- this
|
|
3991
4130
|
workspace's managed tunnel, its managed remote worktree, the machine's EAS
|
|
3992
4131
|
project ledger -- is held by a REMOVAL, and a removal is never waited out:
|
|
@@ -3995,10 +4134,10 @@ captured" (in metro.ndjson, bare RN)
|
|
|
3995
4134
|
\`workspace removal\` -- both are \`stim worktree remove\`). Let it finish,
|
|
3996
4135
|
then run the command again. \`start --remote\` reports this same case as
|
|
3997
4136
|
STIM_WORKTREE_REMOVAL_IN_PROGRESS instead.`
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4137
|
+
},
|
|
4138
|
+
STIM_CANCELLED: {
|
|
4139
|
+
summary: "an ios or android run was interrupted by `stim stop` or Ctrl-C before it finished",
|
|
4140
|
+
body: () => `STIM_CANCELLED (ios, android; exit 130)
|
|
4002
4141
|
"The ios run was cancelled by \`stim stop\` (pid <n>) before it finished."
|
|
4003
4142
|
The run received SIGINT: from \`stim stop\`, which interrupts a build it
|
|
4004
4143
|
leaves with nothing to deploy to, or from Ctrl-C. It forwarded the interrupt
|
|
@@ -4010,10 +4149,10 @@ captured" (in metro.ndjson, bare RN)
|
|
|
4010
4149
|
second SIGINT exits at once, and so does a run that holds a physical-device
|
|
4011
4150
|
lease; those exits print no payload. Run the same command again when you want the
|
|
4012
4151
|
app on a device.`
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4152
|
+
},
|
|
4153
|
+
STIM_STOP_BLOCKED: {
|
|
4154
|
+
summary: "`stop` could not interrupt the ios or android run holding this workspace; names its pid and claim",
|
|
4155
|
+
body: () => `STIM_STOP_BLOCKED (stop)
|
|
4017
4156
|
\`stop\` found a live \`ios\` or \`android\` run holding this workspace's
|
|
4018
4157
|
native-run claim and could not end it:
|
|
4019
4158
|
- "... did not exit within 60s of SIGINT": the run was interrupted but is
|
|
@@ -4030,10 +4169,10 @@ captured" (in metro.ndjson, bare RN)
|
|
|
4030
4169
|
session never waits on a build. The JSON payload carries the session
|
|
4031
4170
|
outcome under device.remote. Devices, collectors and the dev server are left
|
|
4032
4171
|
as they were.`
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
|
|
4172
|
+
},
|
|
4173
|
+
STIM_LOCK_TIMEOUT: {
|
|
4174
|
+
summary: "a lock held past the wait; workspace-process and short directory locks",
|
|
4175
|
+
body: () => `STIM_LOCK_TIMEOUT
|
|
4037
4176
|
The same locks, held by an ordinary command that is still running, for
|
|
4038
4177
|
longer than the wait -- 60s by default, 4 minutes for the remote-session lock
|
|
4039
4178
|
and for \`gc\` deleting EAS sessions under the EAS project lock (its sweep
|
|
@@ -4078,11 +4217,11 @@ captured" (in metro.ndjson, bare RN)
|
|
|
4078
4217
|
using it: if none is running, remove the named directory with that command.
|
|
4079
4218
|
Standalone Metro and Expo cache packages use the
|
|
4080
4219
|
same core protocol and do not require the Stim CLI.`
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
|
|
4220
|
+
},
|
|
4221
|
+
teardown: {
|
|
4222
|
+
summary: "an unmanaged port, an unverified supervisor, and a failed device teardown",
|
|
4223
|
+
separator: "--- TEARDOWN AND WORKSPACE REFUSALS ---",
|
|
4224
|
+
body: () => `"metro refusing to kill port <n>: ... runs from <dir>, outside
|
|
4086
4225
|
<project>" (stop)
|
|
4087
4226
|
Stim only signals processes it launched and whose saved identity still
|
|
4088
4227
|
matches. Stop an externally started server with the tool that started it.
|
|
@@ -4140,10 +4279,10 @@ emulator process <pid> is still running"
|
|
|
4140
4279
|
no process-table check and Stim signals nothing there: it waits for the pid
|
|
4141
4280
|
in the AVD's process lock after \`adb emu kill\`, and refuses at once when
|
|
4142
4281
|
adb cannot reach the emulator.`
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4282
|
+
},
|
|
4283
|
+
remove: {
|
|
4284
|
+
summary: "worktree remove refused a dirty tree: what it restores itself and what --force discards",
|
|
4285
|
+
body: () => `"Refusing to remove <path>: uncommitted changes / untracked files / commits
|
|
4147
4286
|
not on any remote" (worktree remove)
|
|
4148
4287
|
A native build rewrites tracked files, and Stim now RESTORES the one class
|
|
4149
4288
|
it can prove is not work: when the only dirt left is \`pod install\` churn
|
|
@@ -4167,10 +4306,10 @@ not on any remote" (worktree remove)
|
|
|
4167
4306
|
refusal actually named.
|
|
4168
4307
|
Use --force only when you genuinely intend to discard work; it deletes
|
|
4169
4308
|
uncommitted and untracked files permanently.`
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4309
|
+
},
|
|
4310
|
+
STIM_MAIN_DIRTY: {
|
|
4311
|
+
summary: "warm --refresh will not move a source checkout with local work or an operation in progress",
|
|
4312
|
+
body: () => `STIM_MAIN_DIRTY
|
|
4174
4313
|
\`worktree warm --refresh\` writes to the SOURCE CHECKOUT, and it refuses one
|
|
4175
4314
|
it cannot move: tracked files with uncommitted changes (the refusal names
|
|
4176
4315
|
them), or a rebase or merge in progress. Untracked files are not a reason to
|
|
@@ -4180,28 +4319,28 @@ not on any remote" (worktree remove)
|
|
|
4180
4319
|
or \`git -C <source-checkout> rebase --abort\`. Nothing was installed or
|
|
4181
4320
|
copied. Plain \`stim worktree warm\` does not care: it copies from a dirty
|
|
4182
4321
|
source checkout exactly as it always has.`
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
|
|
4186
|
-
|
|
4322
|
+
},
|
|
4323
|
+
STIM_MAIN_DETACHED: {
|
|
4324
|
+
summary: "warm --refresh needs a branch to fast-forward, not a detached HEAD",
|
|
4325
|
+
body: () => `STIM_MAIN_DETACHED
|
|
4187
4326
|
The source checkout's HEAD is detached, so there is no branch to fast-forward
|
|
4188
4327
|
and no upstream to fast-forward it to. Run
|
|
4189
4328
|
\`git -C <source-checkout> checkout <branch>\` and warm again. \`--refresh\`
|
|
4190
4329
|
never picks a branch for you; a checkout whose job is to seed worktrees
|
|
4191
4330
|
should sit on a branch someone chose.`
|
|
4192
|
-
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4331
|
+
},
|
|
4332
|
+
STIM_MAIN_DIVERGED: {
|
|
4333
|
+
summary: "the source checkout is both ahead of and behind its upstream; warm --refresh will not merge",
|
|
4334
|
+
body: () => `STIM_MAIN_DIVERGED
|
|
4196
4335
|
The source checkout's branch has commits its upstream does not, AND its
|
|
4197
4336
|
upstream has commits it does not. A fast-forward is impossible, and
|
|
4198
4337
|
\`--refresh\` will not merge or reset someone else's checkout to make one:
|
|
4199
4338
|
that decision is yours. Rebase or merge it yourself, then warm again. The
|
|
4200
4339
|
message reports both counts. Nothing was installed or copied.`
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4340
|
+
},
|
|
4341
|
+
STIM_DEPS_INCOMPLETE: {
|
|
4342
|
+
summary: "the last install recorded for the lockfile on disk now did not finish, so warm will not copy it",
|
|
4343
|
+
body: () => `STIM_DEPS_INCOMPLETE
|
|
4205
4344
|
\`worktree warm\` refuses to copy dependencies the source checkout never
|
|
4206
4345
|
finished installing. \`--refresh\` records a COMPLETED install of the lockfile
|
|
4207
4346
|
it read under ~/.stim/warm-installs; an install that failed, or whose process
|
|
@@ -4219,20 +4358,20 @@ not on any remote" (worktree remove)
|
|
|
4219
4358
|
repository before its first \`--refresh\` -- copies as it always has.
|
|
4220
4359
|
In a monorepo the record is keyed on the directory that owns the lockfile,
|
|
4221
4360
|
which is usually the repository root, so every app of it reads the same one.`
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
|
|
4361
|
+
},
|
|
4362
|
+
warm: {
|
|
4363
|
+
summary: "worktree warm claim storage refusals and recovery",
|
|
4364
|
+
body: () => `Both plain warm and --refresh require an ownership claim.
|
|
4226
4365
|
Permission-denied or read-only claim storage reports STIM_CLAIM_UNAVAILABLE
|
|
4227
4366
|
and names the path. Restore access to the existing claim store and retry.
|
|
4228
4367
|
A non-directory claim ancestor reports STIM_CLAIM_REFUSED and names the
|
|
4229
4368
|
blocking file. Inspect it and use the printed move-aside command to preserve
|
|
4230
4369
|
its contents before retrying. See guide errors STIM_CLAIM_UNAVAILABLE and
|
|
4231
4370
|
guide errors STIM_CLAIM_REFUSED for the recovery details.`
|
|
4232
|
-
|
|
4233
|
-
|
|
4234
|
-
|
|
4235
|
-
|
|
4371
|
+
},
|
|
4372
|
+
carry: {
|
|
4373
|
+
summary: "worktree warm copy results, lockfile mismatches, and remedies",
|
|
4374
|
+
body: () => `"carry incomplete: ... ignored entries copied, ... kept, ... failed"
|
|
4236
4375
|
(worktree warm)
|
|
4237
4376
|
At least one entry could not be copied. The command exits 1 and names each
|
|
4238
4377
|
failure. Existing entries stay untouched; any files already published remain.
|
|
@@ -4273,11 +4412,11 @@ not on any remote" (worktree remove)
|
|
|
4273
4412
|
the app is ready, unless \`--refresh\` installed them in the SOURCE CHECKOUT
|
|
4274
4413
|
first; even then the copy can still carry a lockfile this branch does not
|
|
4275
4414
|
have, which is exactly what these carry warnings report.`
|
|
4276
|
-
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4415
|
+
},
|
|
4416
|
+
environment: {
|
|
4417
|
+
summary: "npx registry E401/E404, the Node floor, no free Metro port, the reservation race",
|
|
4418
|
+
separator: "--- ENVIRONMENT ---",
|
|
4419
|
+
body: () => `"npm error code E401 / E404" while \`npx\` resolves the stim package
|
|
4281
4420
|
The repo probably pins a private registry in \`.npmrc\`, so \`npx\` looked for
|
|
4282
4421
|
the package there instead of on npm. Use the public registry for this command:
|
|
4283
4422
|
|
|
@@ -4298,10 +4437,10 @@ not on any remote" (worktree remove)
|
|
|
4298
4437
|
"Could not reserve a Metro port after 5 attempts"
|
|
4299
4438
|
Several commands raced for the same ports and each one lost. Nothing is
|
|
4300
4439
|
wrong; retry.`
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
|
|
4440
|
+
},
|
|
4441
|
+
sandbox: {
|
|
4442
|
+
summary: "running under a sandboxing harness: EPERM under STIM_HOME, CoreSimulatorService, adb",
|
|
4443
|
+
body: () => `RUNNING UNDER A SANDBOX
|
|
4305
4444
|
|
|
4306
4445
|
An agent harness that sandboxes shell commands typically permits writes
|
|
4307
4446
|
inside the project and blocks the rest. Three things Stim needs sit outside
|
|
@@ -4348,10 +4487,10 @@ not on any remote" (worktree remove)
|
|
|
4348
4487
|
|
|
4349
4488
|
A git credential helper is often blocked too. It prints \`failed to store\`
|
|
4350
4489
|
on a fetch that otherwise succeeded, and is safe to ignore.`
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
|
|
4354
|
-
|
|
4490
|
+
},
|
|
4491
|
+
STIM_CONFIG_CORRUPT: {
|
|
4492
|
+
summary: "~/.stim/config.json is not a valid JSON object and Stim never resets it",
|
|
4493
|
+
body: () => `STIM_CONFIG_CORRUPT ("Stim config at <path> is not valid JSON",
|
|
4355
4494
|
"... is not a JSON object",
|
|
4356
4495
|
"... has a projects that is not an object",
|
|
4357
4496
|
"... has a projects entry "<key>" that is not an object")
|
|
@@ -4362,19 +4501,19 @@ not on any remote" (worktree remove)
|
|
|
4362
4501
|
resets it for you -- a silent reset would orphan every simulator it names.
|
|
4363
4502
|
Repair the file, or move it aside (\`mv <path> <path>.broken\`) and accept
|
|
4364
4503
|
that the devices it recorded become orphans you delete by hand.`
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
|
|
4504
|
+
},
|
|
4505
|
+
STIM_RELATIVE_PATH: {
|
|
4506
|
+
summary: "STIM_HOME, STIM_BUILD_CACHE or STIM_METRO_CACHE is set to a relative path",
|
|
4507
|
+
body: () => `STIM_RELATIVE_PATH ("<NAME>=<value> is not an absolute path")
|
|
4369
4508
|
Any command refuses before it reads or writes state. A relative value would
|
|
4370
4509
|
resolve against each process's working directory, so the CLI, Metro and the
|
|
4371
4510
|
Expo build-cache provider would each use a different store. Set the named
|
|
4372
4511
|
variable to an absolute path, or unset it to use the default. Metro and the
|
|
4373
4512
|
cache provider, which cannot refuse, ignore a relative value with a warning.`
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4513
|
+
},
|
|
4514
|
+
STIM_NODE_UNSUPPORTED: {
|
|
4515
|
+
summary: "stim or stim-server started on a Node older than 22.12.0, often a project pin",
|
|
4516
|
+
body: () => `STIM_NODE_UNSUPPORTED ("Stim needs Node <floor> or later; this is Node <version> at <path>")
|
|
4378
4517
|
stim and stim-server refuse before loading anything else when the Node that
|
|
4379
4518
|
runs them is older than their engines floor. Both start through
|
|
4380
4519
|
\`#!/usr/bin/env node\`, so the working directory can choose that Node: asdf,
|
|
@@ -4395,12 +4534,14 @@ not on any remote" (worktree remove)
|
|
|
4395
4534
|
is set for command stim" instead of running Stim. When that version is
|
|
4396
4535
|
older than 22.12.0, first install Stim with npm under a supported version.
|
|
4397
4536
|
The tools Stim starts inherit the override.`
|
|
4398
|
-
}
|
|
4399
4537
|
}
|
|
4400
|
-
}
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4538
|
+
}
|
|
4539
|
+
};
|
|
4540
|
+
//#endregion
|
|
4541
|
+
//#region src/guide/lifecycle.ts
|
|
4542
|
+
const lifecycle = {
|
|
4543
|
+
summary: "The full worktree -> start -> ios/android -> logs -> teardown flow, with sections for builds, devices and flags",
|
|
4544
|
+
preamble: () => `ENVIRONMENT LIFECYCLE
|
|
4404
4545
|
|
|
4405
4546
|
Two workflows share steps 2 through 6.
|
|
4406
4547
|
|
|
@@ -4651,10 +4792,152 @@ WAITING FOR A CHANGE
|
|
|
4651
4792
|
nothing changes, except a closed stdout pipe on Linux whose starter is
|
|
4652
4793
|
still running, which it notices at the next change.
|
|
4653
4794
|
Without --json it reprints the human view on change.`,
|
|
4654
|
-
|
|
4655
|
-
|
|
4656
|
-
|
|
4657
|
-
|
|
4795
|
+
sections: {
|
|
4796
|
+
"hosted-android": {
|
|
4797
|
+
summary: "Android on a named approved Mac: strict placement, private Metro, status and stop",
|
|
4798
|
+
body: () => `ANDROID ON A HOSTING MAC
|
|
4799
|
+
|
|
4800
|
+
If Stim is not installed globally, replace stim with npx stim.
|
|
4801
|
+
|
|
4802
|
+
stim settings set hosting.machines '["janics-mac-mini"]'
|
|
4803
|
+
stim doctor --fix
|
|
4804
|
+
stim doctor
|
|
4805
|
+
stim android --remote janics-mac-mini --device-profile pixel_7
|
|
4806
|
+
stim status --json
|
|
4807
|
+
stim reload android
|
|
4808
|
+
stim stop
|
|
4809
|
+
|
|
4810
|
+
A person on the host approves device-host access, separately from build, read
|
|
4811
|
+
and control. The setup in hosted-ios also serves Android. android.remote sets
|
|
4812
|
+
a workspace default. eas and proxy remain backends. --remote auto or
|
|
4813
|
+
android.remote = auto places automatically; see hosted-ios for the shared
|
|
4814
|
+
policy. No flag or setting runs on this Mac.
|
|
4815
|
+
|
|
4816
|
+
A named Mac is strict: refusal, unreachable, declined or failed preparation
|
|
4817
|
+
returns STIM_HOSTING_REFUSED without fallback. --device, a different recorded
|
|
4818
|
+
machine, a running local owned emulator in the slot, and --no-metro-check for
|
|
4819
|
+
hosted Debug refuse STIM_BAD_ARG. Run stim stop before switching placement.
|
|
4820
|
+
Unreadable android.host blocks only its slot; restore its recorded machine and
|
|
4821
|
+
session from the host before stopping that slot.
|
|
4822
|
+
|
|
4823
|
+
--system-image and --device-profile select installed choices on the host.
|
|
4824
|
+
The build targets the offered ABI and --build-machine independently selects a
|
|
4825
|
+
compatible build worker. Stim builds before reserving, records the session as
|
|
4826
|
+
soon as it exists, then delivers one App.apk. The emulator boots headless;
|
|
4827
|
+
androidEmulatorApp is ignored with a note.
|
|
4828
|
+
|
|
4829
|
+
Debug requires the local Metro supervisor. metro.publicUrl and metro.tunnel
|
|
4830
|
+
are ignored with a note. A private tailnet gateway reaches the host loopback
|
|
4831
|
+
bridge; the host reverses the client's Metro port into that bridge on its exact
|
|
4832
|
+
ledger-owned serial and sets debug_http_host to localhost:<clientMetroPort>.
|
|
4833
|
+
Each run and stim reload android restores the reverse, including after a host
|
|
4834
|
+
adb server restart. Reload broadcasts through the local Metro and never runs
|
|
4835
|
+
adb against the host serial on this Mac. Release variants skip Metro. Launch
|
|
4836
|
+
is true only with client bundle evidence or a live host release process;
|
|
4837
|
+
bundling requires a bundle request and unverified has no launch evidence.
|
|
4838
|
+
|
|
4839
|
+
Status adds android.host { machine, session, selected, agent, device: { name,
|
|
4840
|
+
systemImage, api }, state } per slot. The public name is the profile and API
|
|
4841
|
+
level, never the host serial or AVD name. ready, stopped and unverified are
|
|
4842
|
+
session probe states; a conflicting local emulator remains visible with a
|
|
4843
|
+
warning. A recorded placement keeps the workspace in use and blocks idle stop.
|
|
4844
|
+
|
|
4845
|
+
stop and worktree remove wait for device-host.stop, park the host's eligible owned
|
|
4846
|
+
emulator within pool.androidParkedMax (delete otherwise), close the gateway and clear placement. stop --json reports
|
|
4847
|
+
outcomes.device["android:host:<slot>"]. Forbidden or unknown-session also clears
|
|
4848
|
+
placement; an unreachable host keeps it and fails cleanup. A clean host restart
|
|
4849
|
+
parks eligible devices; a later reserve by the same client adopts a compatible
|
|
4850
|
+
parked emulator with all third-party apps removed. Running sessions left by a
|
|
4851
|
+
crash stay unknown until explicit stop; booted devices are never re-attached
|
|
4852
|
+
after restart. See hosted-ios for retention and eviction. JavaScript logs
|
|
4853
|
+
arrive through local Metro.
|
|
4854
|
+
|
|
4855
|
+
AGENT CONTROL
|
|
4856
|
+
|
|
4857
|
+
Set hosting.agentDriver to agent-device on the hosting Mac. Both Macs need
|
|
4858
|
+
agent-device 0.21.22 or later. The host advertises hosted-android-agent and
|
|
4859
|
+
starts one loopback daemon per installed Android session. Its policy allows
|
|
4860
|
+
only the exact ledger-owned emulator serial and denies device shutdown.
|
|
4861
|
+
The host verifies the policy digest and android-instance backend before granting
|
|
4862
|
+
control. An older host, missing binary or unsupported policy reports driver: none;
|
|
4863
|
+
the host's notice explains unavailable control.
|
|
4864
|
+
|
|
4865
|
+
android.host.agent in stim status --json provides driver, remoteConfig and
|
|
4866
|
+
command, never its token. Each slot has a separate 0600 file under the workspace
|
|
4867
|
+
state directory at hosted-android/<slot>/agent-device-remote.json. Use that file:
|
|
4868
|
+
|
|
4869
|
+
agent-device open <packageId> --remote-config <file>
|
|
4870
|
+
agent-device snapshot --remote-config <file>
|
|
4871
|
+
agent-device click <ref> --remote-config <file>
|
|
4872
|
+
agent-device screenshot --remote-config <file>
|
|
4873
|
+
|
|
4874
|
+
The command shape is agent-device <command> --remote-config <file>. Open leases
|
|
4875
|
+
the emulator automatically with android-instance, the proxy provider and this
|
|
4876
|
+
session's tenant. devices is permitted for the client's open flow; inventory
|
|
4877
|
+
contains only this emulator. Never use its serial with a local agent-device.
|
|
4878
|
+
Allowed commands are devices, open, close, snapshot, diff, wait, find, get, is,
|
|
4879
|
+
click, fill, press, type, focus, scroll, screenshot, longpress, swipe, back, home,
|
|
4880
|
+
orientation, appstate, alert and batch. Stim installs; install, reinstall,
|
|
4881
|
+
uninstall, boot, shutdown, close --shutdown, record, logs, uploads and device-wide
|
|
4882
|
+
actions are refused. Other serials in commands, flags, inputs, nested batches or
|
|
4883
|
+
lease requests are refused. Ambient client paths and owner fields are stripped;
|
|
4884
|
+
the host pins platform, serial, tenant, runId, clientId, deviceKey and backend.
|
|
4885
|
+
Host paths and launch inputs are refused. Screenshots use only remote artifacts.
|
|
4886
|
+
|
|
4887
|
+
Stop closes the matching agent-device connection and removes the slot file only
|
|
4888
|
+
once the host confirms stop or revocation. Reinstall, stop, revocation and server
|
|
4889
|
+
close stop the daemon before native work or device deletion. An unresolved
|
|
4890
|
+
proxy, daemon or helper retains the child-aware daemon claim and refuses
|
|
4891
|
+
replacement or deletion, including after a server restart. Agent-device uses
|
|
4892
|
+
one-shot Android snapshots without helper adb forwards; teardown stops its
|
|
4893
|
+
snapshot and IME helper processes only on the pinned serial. Helper APKs remain
|
|
4894
|
+
on the owned emulator until Stim deletes it. A daemon exit rotates only that
|
|
4895
|
+
session's grant; rerun stim android to refresh its config.
|
|
4896
|
+
|
|
4897
|
+
Stim Desktop and the phone app view and control the emulator through this
|
|
4898
|
+
Mac's local stim-server relay, with an "on <machine>" label. Turn on Serve to
|
|
4899
|
+
phones in Desktop and pair the phone with this Mac, not the hosting Mac.
|
|
4900
|
+
frames.subscribe and control.begin address workspace, platform android and
|
|
4901
|
+
slot, including named slots. The host verifies its exact ledger-owned serial,
|
|
4902
|
+
AVD identity and ABI before capture or input; clients never use that serial
|
|
4903
|
+
locally. Capture uses the host's stim-frames helper and scrcpy server jar over
|
|
4904
|
+
adb; stopping capture removes its jar and forward before emulator teardown.
|
|
4905
|
+
Stopped sessions show a rerun remedy; unavailable hosts report why frames
|
|
4906
|
+
cannot arrive. Replay (at/rate), duoFrame and physical hosted targets are
|
|
4907
|
+
refused. Desktop hides local emulator windows, rotation, clipboard and emulator
|
|
4908
|
+
options; touch, text and hardware buttons use the relay. Rotation and posture
|
|
4909
|
+
are unavailable through the scrcpy path.
|
|
4910
|
+
|
|
4911
|
+
Reruns send only missing manifest and APK digests from the session-scoped blob
|
|
4912
|
+
store. The host verifies reused bytes; each new attempt still installs the APK.
|
|
4913
|
+
When an Android build ran on the hosting Mac's pinned node, the host takes only
|
|
4914
|
+
the single App.apk matching the client's manifest. The build and hosting approvals
|
|
4915
|
+
must belong to the same tailnet node. Handoff failure falls back to upload,
|
|
4916
|
+
retrying a busy host for at most one minute.
|
|
4917
|
+
|
|
4918
|
+
stim logs, including --errors and --json, pulls native logcat records as src: device.
|
|
4919
|
+
The host rechecks the exact ledger-owned serial and AVD name, resolves the app's
|
|
4920
|
+
pid on each collection and also drains its last observed pid after a crash or
|
|
4921
|
+
restart. A successful drain retires the exited pid. A failed pid lookup preserves
|
|
4922
|
+
the checkpoint without querying logcat, so the next collection can retry.
|
|
4923
|
+
If no pid was ever observed,
|
|
4924
|
+
native logs cannot be attributed to the app.
|
|
4925
|
+
PID-based logcat cannot distinguish historical reuse of an exited pid.
|
|
4926
|
+
Queries use bounded logcat -d -v epoch -T <epoch> --pid <pid> output, a ten-second
|
|
4927
|
+
budget, a persisted checkpoint and five-second overlap to deduplicate records.
|
|
4928
|
+
Logcat retains a finite buffer; records already evicted or persisted beyond the
|
|
4929
|
+
overlap may be unavailable. An oversized query retries the recent tail and writes
|
|
4930
|
+
a device warning naming any dropped interval. Followers share a child-aware log
|
|
4931
|
+
claim; install and stop cancel and settle the worker before native operations.
|
|
4932
|
+
Stop copies logs before and after the host's final collection and teardown, with
|
|
4933
|
+
each client drain bounded to 30 seconds and progress on stderr. Collected logs
|
|
4934
|
+
remain readable after stop. The host advertises hosted-android-data for Android
|
|
4935
|
+
handoff and native logs. Older hosts get a newer stim-server note, use upload
|
|
4936
|
+
and show records already copied here.`
|
|
4937
|
+
},
|
|
4938
|
+
"hosted-ios": {
|
|
4939
|
+
summary: "iOS on a named approved Mac: strict placement, private Metro, status and stop",
|
|
4940
|
+
body: () => `IOS ON A HOSTING MAC
|
|
4658
4941
|
|
|
4659
4942
|
If Stim is not installed globally, replace stim with npx stim.
|
|
4660
4943
|
|
|
@@ -4671,9 +4954,36 @@ If Stim is not installed globally, replace stim with npx stim.
|
|
|
4671
4954
|
The same setup serves macos --remote. Approval is separate from build, read
|
|
4672
4955
|
and phone control. Set ios.remote to the machine name for a workspace default;
|
|
4673
4956
|
no flag or setting runs here. eas and proxy keep their remote backend meanings.
|
|
4674
|
-
auto
|
|
4675
|
-
|
|
4676
|
-
|
|
4957
|
+
--remote auto (or ios.remote = auto / android.remote = auto) stays here when
|
|
4958
|
+
there is a free concurrency.maxDevices slot (0 means unlimited), no device
|
|
4959
|
+
waiter ahead, normal host memory pressure, no budget shortfall,
|
|
4960
|
+
and 5-minute load per core below offload.maxLoadPerCore. Without hosting.machines,
|
|
4961
|
+
auto is local. An unknown local device count also stays local; boot admission
|
|
4962
|
+
still decides. The default without a flag or setting stays local.
|
|
4963
|
+
|
|
4964
|
+
Otherwise Stim asks every approved hosting.machines host in parallel, with a
|
|
4965
|
+
3-second probe timeout and this run's model/runtime or image/profile selectors.
|
|
4966
|
+
A compatible choice, no declined reason, normal memory pressure and available
|
|
4967
|
+
capacity are required. With a free local slot, a host must report a lower load;
|
|
4968
|
+
with a full cap or a queue ahead, an admitted host with unknown load is allowed.
|
|
4969
|
+
Hosts rank by the build preference (flag > STIM_OFFLOAD_MACHINE > offload.machine)
|
|
4970
|
+
when it names a machine, then lowest load, most free memory and hosting.machines order. Automatic build
|
|
4971
|
+
offload resolves later, after the device architecture is known.
|
|
4972
|
+
|
|
4973
|
+
When no host admits, auto runs here and may wait in the existing FIFO device
|
|
4974
|
+
slot queue; --no-wait and --wait 0 refuse with STIM_AT_CAPACITY. The placement
|
|
4975
|
+
line explains the decision and the skipped hosts. JSON progress goes to stderr.
|
|
4976
|
+
A recorded hosted session wins over load; a live local owned slot stays here.
|
|
4977
|
+
A stopped recorded session places again; unreachable or unknown sessions refuse.
|
|
4978
|
+
|
|
4979
|
+
A reserve refusal after a successful offer can race with another run. It fails
|
|
4980
|
+
with STIM_HOSTING_REFUSED naming the host and asking to retry. Stim does not
|
|
4981
|
+
try the next host or local placement after the build has targeted that host's
|
|
4982
|
+
architecture, and never falls back once a hosted session exists. A named machine remains strict. --device
|
|
4983
|
+
cannot combine with auto. eas/proxy and macOS placement are unchanged.
|
|
4984
|
+
--simulator-app is honoured locally with auto and ignored with a note on a host.
|
|
4985
|
+
Placement measures budgets without reclaiming; only a new local run reclaims.
|
|
4986
|
+
Android --remote <machine> uses an owned emulator on that Mac; see hosted-android.
|
|
4677
4987
|
|
|
4678
4988
|
A named Mac is strict: STIM_HOSTING_REFUSED names its reason; nothing boots
|
|
4679
4989
|
here or elsewhere. --device cannot use a hosting Mac, and --simulator-app
|
|
@@ -4683,6 +4993,24 @@ refuses. The host boots headless and ignores this Mac's iosSimulatorApp setting.
|
|
|
4683
4993
|
the offered simulator architecture, not this Mac's. --build-machine remains
|
|
4684
4994
|
independent and selects a compatible build worker for a Debug cache miss.
|
|
4685
4995
|
|
|
4996
|
+
The host's own owned devices count toward concurrency.maxDevices.
|
|
4997
|
+
|
|
4998
|
+
The host's person sees Hosted here rows in stim-server devices; --json adds
|
|
4999
|
+
hostedSessions beside devices. Rows show client, platform, device, installed
|
|
5000
|
+
app, state and since, newest first. Plain stopped sessions are omitted;
|
|
5001
|
+
parked and unknown sessions stay visible, with parked rows using the parking
|
|
5002
|
+
time. An unowned active session reads unknown on this server.
|
|
5003
|
+
Loopback device-host.sessions takes no params or {} and returns { sessions };
|
|
5004
|
+
rows carry id, client { id, name }, platform, device and app (nullable), state,
|
|
5005
|
+
parked, since (ISO), and workspace. device-host.sessions.stop takes { session }
|
|
5006
|
+
and returns { id, state: stopping | stopped }, stopping any client's session
|
|
5007
|
+
through the existing stop path. Both require an authenticated local Desktop
|
|
5008
|
+
control connection; read-only local connections, phones and tailnet clients
|
|
5009
|
+
get forbidden. An absent session id gets unknown-session; stopped is
|
|
5010
|
+
idempotent. The approved-client device-host.stop still stops only its own
|
|
5011
|
+
sessions.
|
|
5012
|
+
|
|
5013
|
+
|
|
4686
5014
|
Stim builds or fetches before reserving, records the session immediately, then
|
|
4687
5015
|
uploads the app on every run. Debug keeps Metro here; its supervisor owns a
|
|
4688
5016
|
private gateway bound to this Mac's Tailscale address, pinned to the host peer
|
|
@@ -4703,10 +5031,12 @@ agent, state } per slot. A shutdown local simulator is replaced in status; a
|
|
|
4703
5031
|
booted or unknown local simulator stays visible alongside ios.host with a warning.
|
|
4704
5032
|
The default slot appears only in ios, never in slots[]. No host UDID or gateway secret enters local device state or
|
|
4705
5033
|
status. Ready is normal; stopped has a rerun hint; unknown or unreachable is
|
|
4706
|
-
unverified. A host server restart stops its sessions
|
|
5034
|
+
unverified. A clean host server restart stops its sessions and retains eligible
|
|
5035
|
+
parked devices. A running session left by a crash stays unknown until explicit
|
|
5036
|
+
stop; the restarted server never re-attaches to a booted device.
|
|
4707
5037
|
|
|
4708
|
-
stop and worktree remove wait for the host to stop,
|
|
4709
|
-
simulator
|
|
5038
|
+
stop and worktree remove wait for the host to stop, park its eligible owned
|
|
5039
|
+
simulator within pool.iosParkedMax (delete otherwise), close the gateway and clear placement. stop --json
|
|
4710
5040
|
reports each hosted slot under outcomes.device["ios:host:<slot>"], retaining
|
|
4711
5041
|
local device outcomes and successful siblings when another slot fails. Revoked or
|
|
4712
5042
|
missing sessions clear placement too. An unreachable host retains placement
|
|
@@ -4725,18 +5055,47 @@ ran on the hosting Mac's pinned node, it takes matching files from that build.
|
|
|
4725
5055
|
A refused or timed-out handoff falls back to upload, retrying a still-busy host
|
|
4726
5056
|
for at most one minute.
|
|
4727
5057
|
|
|
5058
|
+
HOSTED PARKING
|
|
5059
|
+
|
|
5060
|
+
The host reads pool.iosParkedMax and pool.androidParkedMax from its own home,
|
|
5061
|
+
with STIM_POOL_IOS_PARKED_MAX and STIM_POOL_ANDROID_PARKED_MAX overrides.
|
|
5062
|
+
Each platform has a separate hosted limit across all clients; 0 disables parking.
|
|
5063
|
+
With STIM_HOME set, parking requires the explicit platform environment override.
|
|
5064
|
+
Only healthy devices of still-approved clients are parked. The journal reports
|
|
5065
|
+
stopped to clients, and the device stays ledger-owned in its session's private
|
|
5066
|
+
home, outside the host's local parked pool. Parked devices persist across a clean
|
|
5067
|
+
stim-server restart.
|
|
5068
|
+
|
|
5069
|
+
A new reserve adopts the oldest compatible parked device of the same client.
|
|
5070
|
+
iOS matches resolved device type, runtime and architecture; Android matches
|
|
5071
|
+
system image, device profile and architecture. Adoption clears app data by
|
|
5072
|
+
removing every third-party app; iOS also resets privacy and keychain. Parking and
|
|
5073
|
+
adoption remove session app copies but keep the content-addressed upload store,
|
|
5074
|
+
so the first run after adoption uploads only changed files. Devices are never reused across clients.
|
|
5075
|
+
The oldest parked devices beyond the host's platform limit are deleted through
|
|
5076
|
+
owned-device teardown, also after a limit decrease. Adoption-time reconciliation
|
|
5077
|
+
retires missing, renamed, running or unowned candidates; failed retirement keeps
|
|
5078
|
+
the record. Revocation retires that client's parked devices.
|
|
5079
|
+
On the hosting Mac, unscoped \`stim gc\` lists parked hosted devices and
|
|
5080
|
+
\`stim gc --delete\` deletes them through their session homes under a session
|
|
5081
|
+
claim. Held claims and unverified ownership keep the device. Android deletion
|
|
5082
|
+
requires visibility from this shell's Android environment; otherwise run gc with
|
|
5083
|
+
the server's ANDROID_AVD_HOME/HOME. Records no longer listed in their session
|
|
5084
|
+
ledger are already removed and skipped. Reconciliation
|
|
5085
|
+
clears its parked marker after the session ledger becomes empty.
|
|
5086
|
+
|
|
4728
5087
|
stim logs and stim logs --errors pull native device records from bounded host
|
|
4729
5088
|
queries. Concurrent followers share a collection, throttled per session, without
|
|
4730
5089
|
blocking app delivery, viewing or control. Stop limits each log drain to 30 seconds with
|
|
4731
5090
|
progress on stderr and a no-progress guard. The host collects a bounded final
|
|
4732
|
-
tail before deletion, including on revocation or server close; stop copies it
|
|
5091
|
+
tail before parking or deletion, including on revocation or server close; stop copies it
|
|
4733
5092
|
back afterwards. If the final collection drops a backlog interval and eventually
|
|
4734
5093
|
succeeds, a device warning record names that interval. A damaged collection checkpoint is ignored and
|
|
4735
5094
|
rebuilt; it never blocks reading collected records. The client waits up to
|
|
4736
5095
|
180 seconds for stop. The final collection and worker termination paths fit within
|
|
4737
5096
|
that wait; an in-flight handoff copy and closing Metro or view transports are
|
|
4738
5097
|
outside those worker bounds.
|
|
4739
|
-
Collected records remain in the session home after deletion;
|
|
5098
|
+
Collected records remain in the session home after parking or deletion;
|
|
4740
5099
|
app blobs and materialized bundles are removed. Native queries read persisted
|
|
4741
5100
|
entries, overlap by five seconds and de-duplicate; info-level or later-persisted
|
|
4742
5101
|
entries may be unavailable. JavaScript logs arrive through Metro.
|
|
@@ -4811,10 +5170,10 @@ frames cannot arrive. Replay, duoFrame and physical targets are refused for
|
|
|
4811
5170
|
hosted subscriptions. Desktop hides local Simulator.app, rotation, hardware
|
|
4812
5171
|
buttons and simulator options; touch and text use the relay. The phone can also
|
|
4813
5172
|
send the host's supported button, rotation and posture inputs.`
|
|
4814
|
-
|
|
4815
|
-
|
|
4816
|
-
|
|
4817
|
-
|
|
5173
|
+
},
|
|
5174
|
+
eas: {
|
|
5175
|
+
summary: "download a matching EAS development build; explicit profile, costs, cache and miss remedies",
|
|
5176
|
+
body: () => `EAS DEVELOPMENT BUILDS
|
|
4818
5177
|
|
|
4819
5178
|
stim ios --eas-profile ios-simulator
|
|
4820
5179
|
stim android --eas-profile development
|
|
@@ -4890,10 +5249,10 @@ An eas-cli older than 18.9.0 cannot run build:download --build-id; Stim reads
|
|
|
4890
5249
|
eas --version first and refuses with STIM_EAS_UNAVAILABLE and the upgrade
|
|
4891
5250
|
command before any EAS lookup. stim doctor reports it in a project with eas.json.
|
|
4892
5251
|
If another run holds the artifact claim, wait for it to finish and retry.`
|
|
4893
|
-
|
|
4894
|
-
|
|
4895
|
-
|
|
4896
|
-
|
|
5252
|
+
},
|
|
5253
|
+
readiness: {
|
|
5254
|
+
summary: "implement optional pending/ready app logs, deadlines, errors, and platform isolation",
|
|
5255
|
+
body: () => `OPTIONAL APP READINESS
|
|
4897
5256
|
|
|
4898
5257
|
No package or SDK is needed. This applies to debug ios/android launches with
|
|
4899
5258
|
Metro verification enabled, not release builds or --no-metro-check.
|
|
@@ -4953,10 +5312,10 @@ including when device logs are unavailable, the default check applies.
|
|
|
4953
5312
|
The app declares readiness; Stim does not inspect a rendered frame. This does
|
|
4954
5313
|
not change launched JSON semantics or the need to inspect the expected UI and
|
|
4955
5314
|
stim logs --errors. A reload request does not run this launch check.`
|
|
4956
|
-
|
|
4957
|
-
|
|
4958
|
-
|
|
4959
|
-
|
|
5315
|
+
},
|
|
5316
|
+
verification: {
|
|
5317
|
+
summary: "reproduce the affected behavior, verify the change on the reported device, and retain proof",
|
|
5318
|
+
body: () => `VERIFY THE CHANGE
|
|
4960
5319
|
|
|
4961
5320
|
For a UI change or bug fix, decide what observable result would prove the task
|
|
4962
5321
|
is complete. A successful build, a live process, or an empty log query does
|
|
@@ -4986,10 +5345,10 @@ not prove that result.
|
|
|
4986
5345
|
|
|
4987
5346
|
For a change without a UI effect, use the relevant runtime output or test
|
|
4988
5347
|
result as proof instead of requiring an unrelated screenshot.`
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
|
|
5348
|
+
},
|
|
5349
|
+
progress: {
|
|
5350
|
+
summary: "phase lines, the label set, heartbeats and their ~ estimate, what warm, start, stop and remove print",
|
|
5351
|
+
body: () => `PROGRESS ON A LONG RUN
|
|
4993
5352
|
Native build progress goes to stderr. In \`--json\` mode, stdout carries only
|
|
4994
5353
|
the result payload. Plain \`start\` also prints progress on stdout. Every
|
|
4995
5354
|
progress line has the same shape -- two spaces, a label padded to eleven
|
|
@@ -5137,10 +5496,10 @@ result as proof instead of requiring an unrelated screenshot.`
|
|
|
5137
5496
|
until it returns; the next heartbeat then lands on the grid, which is why an
|
|
5138
5497
|
elapsed value can jump. Read the phase lines, not the wall clock, before
|
|
5139
5498
|
killing a run.`
|
|
5140
|
-
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
|
|
5499
|
+
},
|
|
5500
|
+
pool: {
|
|
5501
|
+
summary: "parked and adopted simulators and emulators: what park and adoption clear or keep, the model and runtime match, emulator storage, gc erase",
|
|
5502
|
+
body: () => ` THE SIMULATOR POOL
|
|
5144
5503
|
\`worktree remove\` PARKS this workspace's owned simulator instead of
|
|
5145
5504
|
deleting it, and the next workspace that wants the same model and runtime
|
|
5146
5505
|
ADOPTS it. Adoption reuses the simulator and the app installed on it, but
|
|
@@ -5312,10 +5671,10 @@ result as proof instead of requiring an unrelated screenshot.`
|
|
|
5312
5671
|
reports their system image, hardware profile, age, recorded app and disk
|
|
5313
5672
|
size.
|
|
5314
5673
|
`
|
|
5315
|
-
|
|
5316
|
-
|
|
5317
|
-
|
|
5318
|
-
|
|
5674
|
+
},
|
|
5675
|
+
builds: {
|
|
5676
|
+
summary: "optional cache warm-up, build optimizations, fingerprints, .fingerprintignore, install unchanged, running app restart, runtime state",
|
|
5677
|
+
body: () => `OPTIONAL CACHE WARM-UP FOR REPEATED NATIVE WORK
|
|
5319
5678
|
When several native worktrees are coming, build the source checkout once to
|
|
5320
5679
|
seed the shared caches before warming the linked worktrees. Skip this extra
|
|
5321
5680
|
build for one-off or JavaScript-only work. For local simulator or emulator
|
|
@@ -5466,7 +5825,7 @@ with machine or project optimization settings; see \`guide settings\`:
|
|
|
5466
5825
|
no org.gradle.caching=true in gradle.properties. Debug builds also
|
|
5467
5826
|
carry -PreactNativeArchitectures=<target ABI>, using the owned
|
|
5468
5827
|
emulator system-image ABI or the physical device's primary ABI.
|
|
5469
|
-
Unknown targets and Release builds stay universal.
|
|
5828
|
+
Unknown targets and local Release builds stay universal.
|
|
5470
5829
|
ccache the same gradlew run carries an absolute
|
|
5471
5830
|
CMAKE_C_COMPILER_LAUNCHER / CMAKE_CXX_COMPILER_LAUNCHER plus
|
|
5472
5831
|
CCACHE_DIR, CCACHE_BASEDIR, CCACHE_NOHASHDIR, CCACHE_SLOPPINESS
|
|
@@ -5721,10 +6080,10 @@ WHAT MAKES THE CACHE ACTUALLY HIT: .FINGERPRINTIGNORE
|
|
|
5721
6080
|
mismatch naming the differing sources. Untracked, non-gitignored files under
|
|
5722
6081
|
ios/ or android/ count too -- they are hashed like any other source, so a
|
|
5723
6082
|
stray file there moves the key on your machine and nowhere else.`
|
|
5724
|
-
|
|
5725
|
-
|
|
5726
|
-
|
|
5727
|
-
|
|
6083
|
+
},
|
|
6084
|
+
concurrency: {
|
|
6085
|
+
summary: "waiting on another workspace's build, --no-build-cache, concurrency.maxBuilds and maxDevices",
|
|
6086
|
+
body: () => `ONE COMPILE PER FINGERPRINT, ACROSS EVERY WORKSPACE
|
|
5728
6087
|
The cache makes the SECOND workspace on a commit free -- but only once the
|
|
5729
6088
|
first has finished. Three agents starting within the same minute all miss it,
|
|
5730
6089
|
and without this all three compile the same app at once, fighting for the
|
|
@@ -5790,12 +6149,50 @@ OPT-IN CONCURRENCY LIMITS (UNLIMITED BY DEFAULT)
|
|
|
5790
6149
|
lock does, and a dead builder frees its slot within
|
|
5791
6150
|
a poll (process identity, like the lock).
|
|
5792
6151
|
|
|
5793
|
-
concurrency.maxDevices how many Stim-owned devices are BOOTED at once
|
|
5794
|
-
|
|
5795
|
-
|
|
5796
|
-
|
|
5797
|
-
|
|
5798
|
-
|
|
6152
|
+
concurrency.maxDevices how many Stim-owned devices are BOOTED at once,
|
|
6153
|
+
counting sims that are booting, emulators adb lists
|
|
6154
|
+
in any state, and devices other runs are booting.
|
|
6155
|
+
Admission and the boot claim are taken under one
|
|
6156
|
+
lock in $STIM_HOME. At the cap, a new owned device
|
|
6157
|
+
waits in FIFO order across this Stim home, for
|
|
6158
|
+
600 seconds by default. \`--wait <seconds>\` changes
|
|
6159
|
+
the bound; \`--no-wait\` or \`--wait 0\` refuses at
|
|
6160
|
+
once with STIM_AT_CAPACITY and cannot jump queued
|
|
6161
|
+
runs. A workspace's own booted or booting device
|
|
6162
|
+
bypasses the queue. iOS waits beside its build.
|
|
6163
|
+
Waiting prints the count, holder workspace names
|
|
6164
|
+
and elapsed time at once and about every 10s, then
|
|
6165
|
+
the queue position, head workspace name and
|
|
6166
|
+
$STIM_HOME/device-waits path. Timeout names the
|
|
6167
|
+
same head and path. Stop cancels a queued run and
|
|
6168
|
+
releases its ticket without booting its device.
|
|
6169
|
+
Status JSON adds build.waitingFor independently
|
|
6170
|
+
of phase: { kind: "device-slot" | "build-slot",
|
|
6171
|
+
inUse, max, since }. Overlapping waits show the
|
|
6172
|
+
one that started first, then the remaining wait.
|
|
6173
|
+
Positive deviceSlotWaitMs is recorded beside
|
|
6174
|
+
slotWaitMs in compiling run placements; every
|
|
6175
|
+
wait also records a capacityWaits event in stats,
|
|
6176
|
+
with reclaimed when one or more devices were
|
|
6177
|
+
shut down for it.
|
|
6178
|
+
While at the cap, only the queue head reclaims:
|
|
6179
|
+
it shuts down the longest-idle eligible owned
|
|
6180
|
+
device in another workspace of this Stim home,
|
|
6181
|
+
one per attempt, at most once every 15 seconds,
|
|
6182
|
+
then rechecks queue and capacity.
|
|
6183
|
+
devices.reclaimIdleMinutes defaults to 10; 0 off.
|
|
6184
|
+
The waiter's effective setting applies.
|
|
6185
|
+
Idle checks are the same as supervisor
|
|
6186
|
+
idle shutdown below: no driver, lock, build,
|
|
6187
|
+
viewer or recent activity. Physical, hosted,
|
|
6188
|
+
remote, parked and other homes' devices, and
|
|
6189
|
+
the waiting workspace itself, are excluded.
|
|
6190
|
+
Shutdown never deletes. Reclaim failures are
|
|
6191
|
+
logged and skipped without failing the run.
|
|
6192
|
+
A listing failure leaves the count unknown and
|
|
6193
|
+
admission refuses with STIM_NO_DEVICE. Unresolved
|
|
6194
|
+
wait or boot claims refuse with the claim code
|
|
6195
|
+
and the command to remove that exact claim.
|
|
5799
6196
|
See \`guide errors STIM_AT_CAPACITY\`.
|
|
5800
6197
|
|
|
5801
6198
|
\`stim doctor\` prints one note echoing the caps and the current live count,
|
|
@@ -5803,10 +6200,10 @@ OPT-IN CONCURRENCY LIMITS (UNLIMITED BY DEFAULT)
|
|
|
5803
6200
|
reports stale build locks, and \`gc --delete\` clears them. Set the caps
|
|
5804
6201
|
with \`stim settings set concurrency.maxBuilds 2\`, by editing
|
|
5805
6202
|
~/.stim/config.json, or via the two env vars (see \`guide settings\`).`
|
|
5806
|
-
|
|
5807
|
-
|
|
5808
|
-
|
|
5809
|
-
|
|
6203
|
+
},
|
|
6204
|
+
budget: {
|
|
6205
|
+
summary: "disk and memory budgets: what start, ios and android reclaim first, and STIM_LOW_DISK",
|
|
6206
|
+
body: () => `DISK AND MEMORY BUDGETS (ON BY DEFAULT)
|
|
5810
6207
|
Before \`start\`, \`ios\` and \`android\` build or boot anything, they
|
|
5811
6208
|
compare the machine against four MACHINE-level settings:
|
|
5812
6209
|
|
|
@@ -5847,8 +6244,8 @@ OPT-IN CONCURRENCY LIMITS (UNLIMITED BY DEFAULT)
|
|
|
5847
6244
|
4. trim shared cache entries nothing has used for 14 days
|
|
5848
6245
|
(\`gc --delete --older-than 14\` for the caches)
|
|
5849
6246
|
|
|
5850
|
-
IDLE SHUTDOWN (
|
|
5851
|
-
workspace's supervisor shuts down that workspace's owned simulators and
|
|
6247
|
+
IDLE SHUTDOWN (30 MINUTES BY DEFAULT): with devices.idleShutdownMinutes above
|
|
6248
|
+
0, a workspace's supervisor shuts down that workspace's owned simulators and
|
|
5852
6249
|
emulators once they have been idle that long, without waiting for a run
|
|
5853
6250
|
to go over budget. Idle is step 1 with that many minutes in place of 10:
|
|
5854
6251
|
booted, no driver, no Stim or agent-device lock, no build in progress, and
|
|
@@ -5862,11 +6259,35 @@ OPT-IN CONCURRENCY LIMITS (UNLIMITED BY DEFAULT)
|
|
|
5862
6259
|
status\` shows "shut down after 30m idle" on the device until the next
|
|
5863
6260
|
\`ios\` or \`android\` run boots it again. When metro.idleStopMinutes is
|
|
5864
6261
|
shorter, the dev server's idle stop first shuts down the devices idle that
|
|
5865
|
-
long, because no supervisor is left to check afterwards.
|
|
5866
|
-
without a supervisor: release runs, and after
|
|
5867
|
-
is read when the supervisor starts. Stim Desktop's simulator view is not a
|
|
5868
|
-
stim-server client and does not count as a viewer.
|
|
5869
|
-
\`stim settings set devices.idleShutdownMinutes
|
|
6262
|
+
long, because no supervisor is left to check afterwards. The supervisor
|
|
6263
|
+
path does not check without a supervisor: release runs, and after
|
|
6264
|
+
\`stim stop\`. The setting is read when the supervisor starts. Stim Desktop's simulator view is not a
|
|
6265
|
+
stim-server client and does not count as a viewer. Change it with
|
|
6266
|
+
\`stim settings set devices.idleShutdownMinutes <minutes> --scope machine\`;
|
|
6267
|
+
0 turns it off, and an explicit 0 stays 0.
|
|
6268
|
+
|
|
6269
|
+
QUEUE RECLAIM (10 MINUTES BY DEFAULT): a run waiting at concurrency.maxDevices
|
|
6270
|
+
uses devices.reclaimIdleMinutes from its own effective workspace settings.
|
|
6271
|
+
Only the FIFO head shuts down the longest-idle eligible device across this
|
|
6272
|
+
Stim home's other workspaces, one per attempt, at most once every 15 seconds,
|
|
6273
|
+
rechecking capacity before another. 0 disables it. The supervisor's own idle
|
|
6274
|
+
shutdown defaults to 30 minutes.
|
|
6275
|
+
Reclaim uses the same idle check: no driver, Stim or agent-device lock,
|
|
6276
|
+
build, device lock or viewer, and no Stim command, app or device log,
|
|
6277
|
+
Metro bundle or agent action within that interval. It rechecks under the
|
|
6278
|
+
target workspace's native-run lock and re-resolves ownership in centralized
|
|
6279
|
+
teardown. It shuts down, never deletes, and excludes the waiting workspace,
|
|
6280
|
+
physical, hosted, remote, parked and other homes' devices. Failed locks or
|
|
6281
|
+
teardown are reported once per device and skipped for the rest of the wait.
|
|
6282
|
+
Progress on stderr names the reclaimed
|
|
6283
|
+
device and workspace; the target's status and device_idle_shutdown log
|
|
6284
|
+
record "reclaimed for a waiting run", and the wait's stats carry reclaimed.
|
|
6285
|
+
Any workspace of this Stim home with a device idle for
|
|
6286
|
+
devices.reclaimIdleMinutes is eligible, even with no supervisor and even
|
|
6287
|
+
when its own devices.idleShutdownMinutes is 0. Stim Desktop's simulator view
|
|
6288
|
+
and manual input in Simulator.app do not count as activity.
|
|
6289
|
+
\`stim settings set devices.reclaimIdleMinutes 0 --scope machine\` opts out
|
|
6290
|
+
machine-wide; the waiter's effective setting decides.
|
|
5870
6291
|
|
|
5871
6292
|
Steps 3 and 4 run only for disk. Memory and workspace limits never refuse;
|
|
5872
6293
|
a run still over them prints one \`budget\` warning and continues. The
|
|
@@ -5881,13 +6302,18 @@ OPT-IN CONCURRENCY LIMITS (UNLIMITED BY DEFAULT)
|
|
|
5881
6302
|
memory against the budget, and a finding with the reclaim plan when over it.
|
|
5882
6303
|
Change a limit with \`stim settings set budget.minFreeDiskGb 30\`.
|
|
5883
6304
|
See \`guide errors STIM_LOW_DISK\`.`
|
|
5884
|
-
|
|
5885
|
-
|
|
5886
|
-
|
|
5887
|
-
|
|
6305
|
+
},
|
|
6306
|
+
options: {
|
|
6307
|
+
summary: "every flag per command, Android variants and flavors, the per-run simulator model, runtime and system image",
|
|
6308
|
+
body: () => `THE OPTION SURFACE, IN FULL
|
|
5888
6309
|
start --json --wait <seconds> --remote --reset-cache
|
|
5889
6310
|
ios --build-machine <auto|local|name> --slot <name> --json --plan --no-metro-check --no-build-cache --scheme <name> --configuration <name> --device-type <name> --runtime <version> --simulator-app <xcode|siniulator|stim-desktop> --device [udid] --wait <seconds> --no-wait --remote <eas|proxy|auto|machine>
|
|
5890
6311
|
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>
|
|
6312
|
+
ios/android --wait bounds owned-device slot waits (default 600s), or physical
|
|
6313
|
+
leases with --device (default 60s). Remote targets do not join this local
|
|
6314
|
+
slot queue. --no-wait refuses a full/queued owned
|
|
6315
|
+
slot at once; its existing physical lease bypass is unchanged.
|
|
6316
|
+
|
|
5891
6317
|
reload [ios|android] --json
|
|
5892
6318
|
device lock <ios|android> [id] --slot <name> --for <duration> --wait <seconds> --json;
|
|
5893
6319
|
unlock [ios|android] --slot <name> --json
|
|
@@ -6140,9 +6566,9 @@ OPT-IN CONCURRENCY LIMITS (UNLIMITED BY DEFAULT)
|
|
|
6140
6566
|
A named hosting Mac uses both selectors and its installed choices instead;
|
|
6141
6567
|
an unavailable choice is STIM_HOSTING_REFUSED. See hosted-ios.
|
|
6142
6568
|
\`android --system-image\` and \`--device-profile\` refuse with
|
|
6143
|
-
STIM_BAD_ARG on
|
|
6144
|
-
setting),
|
|
6145
|
-
android.
|
|
6569
|
+
STIM_BAD_ARG on eas/proxy runs (\`--remote\` or the android.remote
|
|
6570
|
+
setting), which ignore android.systemImage and android.deviceProfile.
|
|
6571
|
+
A named hosting Mac honors both selectors; see hosted-android.
|
|
6146
6572
|
|
|
6147
6573
|
These flags describe a device that does not exist yet. When this workspace
|
|
6148
6574
|
ALREADY owns a simulator and \`--device-type\` names a different model, or
|
|
@@ -6164,10 +6590,10 @@ OPT-IN CONCURRENCY LIMITS (UNLIMITED BY DEFAULT)
|
|
|
6164
6590
|
The --json payload reports what was actually used: \`deviceType\` and
|
|
6165
6591
|
\`runtime\` on iOS, \`systemImage\` and \`deviceProfile\` on Android, read
|
|
6166
6592
|
from the device itself, so a settings-driven run reports them too.`
|
|
6167
|
-
|
|
6168
|
-
|
|
6169
|
-
|
|
6170
|
-
|
|
6593
|
+
},
|
|
6594
|
+
devices: {
|
|
6595
|
+
summary: "ios --device and android --device on a phone: what the run skips, signing, the LAN wiring, the collector",
|
|
6596
|
+
body: () => ` \`android --device [serial]\` installs and launches on a physical device
|
|
6171
6597
|
connected to this machine instead of this workspace's owned emulator. With
|
|
6172
6598
|
no serial it takes the first device it can lease (\`guide lifecycle lease\`).
|
|
6173
6599
|
It cannot be combined with --remote.
|
|
@@ -6306,10 +6732,10 @@ OPT-IN CONCURRENCY LIMITS (UNLIMITED BY DEFAULT)
|
|
|
6306
6732
|
by hashing the installed container; there is no cheap equivalent through
|
|
6307
6733
|
devicectl, so a device run always installs. It is an upgrade install: the
|
|
6308
6734
|
app's data, and the Local Network permission the phone granted it, survive.`
|
|
6309
|
-
|
|
6310
|
-
|
|
6311
|
-
|
|
6312
|
-
|
|
6735
|
+
},
|
|
6736
|
+
lease: {
|
|
6737
|
+
summary: "run-scoped leases, --wait and --no-wait, device lock and unlock, which phone an id-less --device picks",
|
|
6738
|
+
body: () => `THE DEVICE LEASE ON A \`--device\` RUN
|
|
6313
6739
|
A physical device is shared, so a \`--device\` run takes a lease on it. The
|
|
6314
6740
|
lease step sits AFTER the build (a build touches no device) and before the
|
|
6315
6741
|
install, and the run releases what it took when the command exits: on
|
|
@@ -6334,8 +6760,9 @@ OPT-IN CONCURRENCY LIMITS (UNLIMITED BY DEFAULT)
|
|
|
6334
6760
|
holder's running app, a different one means the launch only backgrounds it,
|
|
6335
6761
|
and when Stim cannot read the holder's app id it says so rather than
|
|
6336
6762
|
guessing. A free device is leased as usual under \`--no-wait\`. The two flags
|
|
6337
|
-
together are STIM_BAD_ARG
|
|
6338
|
-
|
|
6763
|
+
together are STIM_BAD_ARG. Without \`--device\`, they instead bound the
|
|
6764
|
+
FIFO owned-device slot wait (default 600s); \`--no-wait\` refuses at once
|
|
6765
|
+
when the cap is full or another run is queued. See \`guide lifecycle concurrency\`.
|
|
6339
6766
|
|
|
6340
6767
|
A successful \`--device\` run reports \`lease: { kind, expiresAt }\` in its
|
|
6341
6768
|
\`--json\`; a run that proceeded without one, or lost one after the install,
|
|
@@ -6411,10 +6838,10 @@ THE POOL: WHICH DEVICE AN ID-LESS \`--device\` PICKS
|
|
|
6411
6838
|
The chosen device is on the phase line and in \`--json\` (\`udid\` or
|
|
6412
6839
|
\`serial\`, plus \`deviceName\`), so an agent can hand the same id to its
|
|
6413
6840
|
device tool.`
|
|
6414
|
-
|
|
6415
|
-
|
|
6416
|
-
|
|
6417
|
-
|
|
6841
|
+
},
|
|
6842
|
+
release: {
|
|
6843
|
+
summary: "Release configurations and ...Release variants: Metro skipped, process-proven launch, the JS swap",
|
|
6844
|
+
body: () => ` A VARIANT WHOSE NAME ENDS IN "Release" IS A RELEASE BUILD (\`release\`,
|
|
6418
6845
|
\`productionRelease\`), and that is the whole opt-in -- there is no second
|
|
6419
6846
|
flag. It is the Android half of \`ios --configuration Release\` and behaves
|
|
6420
6847
|
the same way: AGP's bundle task embeds the JS, so Metro is skipped ENTIRELY
|
|
@@ -6499,10 +6926,10 @@ THE POOL: WHICH DEVICE AN ID-LESS \`--device\` PICKS
|
|
|
6499
6926
|
builds the \`iphoneos\` slice for a cabled iPhone and keys its cache
|
|
6500
6927
|
\`-device\` instead of \`-sim\`, but does not install it yet. Archives,
|
|
6501
6928
|
\`.ipa\` export, store signing and distribution stay out of scope.`
|
|
6502
|
-
|
|
6503
|
-
|
|
6504
|
-
|
|
6505
|
-
|
|
6929
|
+
},
|
|
6930
|
+
simslim: {
|
|
6931
|
+
summary: "recommended SimSlim profiles and recovery from host memory pressure",
|
|
6932
|
+
body: () => `SIMSLIM FOR PARALLEL IOS WORK
|
|
6506
6933
|
SimSlim is recommended as an optional way to reduce simulator background
|
|
6507
6934
|
services and memory use, especially with several workspaces. Review which
|
|
6508
6935
|
services your app and tests need; a slim profile can disable those features.
|
|
@@ -6579,12 +7006,14 @@ HOST MEMORY PRESSURE AND STALLED SIMULATORS
|
|
|
6579
7006
|
Rebooting a simulator under the same pressure
|
|
6580
7007
|
can repeat the stall. Consider fewer concurrent builds/devices (guide lifecycle
|
|
6581
7008
|
concurrency) and a reviewed SimSlim profile for future runs.`
|
|
6582
|
-
}
|
|
6583
7009
|
}
|
|
6584
|
-
}
|
|
6585
|
-
|
|
6586
|
-
|
|
6587
|
-
|
|
7010
|
+
}
|
|
7011
|
+
};
|
|
7012
|
+
//#endregion
|
|
7013
|
+
//#region src/guide/cleanup.ts
|
|
7014
|
+
const cleanup = {
|
|
7015
|
+
summary: "Where simulators come from, and how they get reclaimed",
|
|
7016
|
+
preamble: () => `DEVICE SLOTS
|
|
6588
7017
|
|
|
6589
7018
|
Cleanup enumerates every slot. stop --slot <name> keeps the shared server and
|
|
6590
7019
|
other slots; plain stop and worktree remove handle the whole workspace.
|
|
@@ -6595,7 +7024,7 @@ versions cannot reliably manage their assignments.
|
|
|
6595
7024
|
|
|
6596
7025
|
ARCHIVE
|
|
6597
7026
|
|
|
6598
|
-
|
|
7027
|
+
Worktree removal and gc dead-project pruning keep history under
|
|
6599
7028
|
$STIM_HOME/archive by default. See stim guide cleanup archive.
|
|
6600
7029
|
|
|
6601
7030
|
MAINTENANCE
|
|
@@ -6711,20 +7140,27 @@ start the counters over. A file this version cannot read -- unparseable, or
|
|
|
6711
7140
|
written by a newer Stim -- costs one dim line on stderr and is otherwise left
|
|
6712
7141
|
alone; only the next \`ios\` or \`android\` run moves an unparseable one aside
|
|
6713
7142
|
to stats.json.corrupt-<unix ms> and starts a new one.`,
|
|
6714
|
-
|
|
6715
|
-
|
|
6716
|
-
|
|
6717
|
-
|
|
6718
|
-
|
|
7143
|
+
sections: {
|
|
7144
|
+
archive: {
|
|
7145
|
+
summary: "retained workspace history and explicit archive cleanup",
|
|
7146
|
+
body: () => `ARCHIVE
|
|
7147
|
+
Worktree removal, including the environment reclaim of a source checkout, and
|
|
7148
|
+
gc pruning a registered root that is gone keep
|
|
6719
7149
|
state, ended agents, the error index, logs, closed recording segments and
|
|
6720
7150
|
workspace-local agent-device sessions under $STIM_HOME/archive.
|
|
6721
7151
|
Build outputs and open recording segments are deleted with the workspace.
|
|
6722
7152
|
Archives have no checkout, devices, ports or running processes. A recreated
|
|
6723
7153
|
path starts fresh; status links its earlier archives with replacedBy.
|
|
6724
7154
|
Paired clients with read can select archived[].id with archive instead of
|
|
6725
|
-
workspace for server log queries and replay.
|
|
6726
|
-
|
|
6727
|
-
|
|
7155
|
+
workspace for server log queries and replay.
|
|
7156
|
+
archive.detail takes { archive: id } and returns { builds, recordings }:
|
|
7157
|
+
live-shaped iOS/Android build history and { platform, slot, spans } for every
|
|
7158
|
+
retained recording slot with spans. Missing state gives builds {}; no footage
|
|
7159
|
+
gives recordings []. status keeps archive build summaries.
|
|
7160
|
+
Older servers refuse archive.detail with unknown-method.
|
|
7161
|
+
Archived log subscriptions send retained records, then logs-ended.
|
|
7162
|
+
Archived frame subscriptions need at and cannot be physical or go live.
|
|
7163
|
+
Replay ranges report recording disabled.
|
|
6728
7164
|
Reads stay on this Mac.
|
|
6729
7165
|
An updated server is required; older servers refuse with bad-request when
|
|
6730
7166
|
workspace is omitted. There is no CLI archive log flag.
|
|
@@ -6758,10 +7194,23 @@ to stats.json.corrupt-<unix ms> and starts a new one.`,
|
|
|
6758
7194
|
Directories are 0700 and files 0600. No redaction is applied; logs and agent
|
|
6759
7195
|
actions can contain secrets. Use archive.enabled false for a sensitive repo.
|
|
6760
7196
|
Archived logs and replay are not exposed through CLI or app readers yet.`
|
|
6761
|
-
|
|
6762
|
-
|
|
6763
|
-
|
|
6764
|
-
|
|
7197
|
+
},
|
|
7198
|
+
gc: {
|
|
7199
|
+
summary: "what gc and worktree remove delete, keep and refuse: orphans, stale records, locks, leases, EAS sessions",
|
|
7200
|
+
body: () => `PARKED HOSTED DEVICES
|
|
7201
|
+
On a hosting Mac, unscoped gc lists parked hosted iOS/Android devices by session,
|
|
7202
|
+
client id, device label, parked time and private-home ledger ownership.
|
|
7203
|
+
gc --delete takes each session claim and deletes through the packaged stop worker
|
|
7204
|
+
in that session's home; held claims, changed sessions and unverified ownership
|
|
7205
|
+
keep the device. Android deletion also requires the AVD to be visible from this
|
|
7206
|
+
shell; otherwise run gc with the server's ANDROID_AVD_HOME/HOME. Devices no longer
|
|
7207
|
+
listed in their session ledger are already removed and skipped. An unreadable
|
|
7208
|
+
journal keeps every hosted device. --older-than
|
|
7209
|
+
filters by parked time; --cache scopes omit hosted sessions. The CLI never writes
|
|
7210
|
+
the server journal: stim-server reconciliation clears the parked marker after
|
|
7211
|
+
successful deletion leaves the session ledger empty.
|
|
7212
|
+
|
|
7213
|
+
AGENT-DEVICE (REPORT ONLY)
|
|
6765
7214
|
Unscoped gc reports agent-device runner builds by platform and entry, last use,
|
|
6766
7215
|
agent-device and Xcode versions, sessions, logs, other state, workspace state
|
|
6767
7216
|
and hosted driver state. Stim never selects this state as a cache.
|
|
@@ -6806,7 +7255,9 @@ IN USE
|
|
|
6806
7255
|
that names no workspace blocks nothing, because every build also holds its
|
|
6807
7256
|
own workspace's native-run.lock; gc lists it with the command that removes
|
|
6808
7257
|
it. \`worktree remove\` re-checks uncommitted and unpushed work under its
|
|
6809
|
-
removal locks, just before it reclaims anything.
|
|
7258
|
+
removal locks, just before it reclaims anything. It refuses, even with
|
|
7259
|
+
--force, a worktree that an installed stim-server service runs from
|
|
7260
|
+
(STIM_WORKTREE_SERVICE; see guide errors). A project root whose
|
|
6810
7261
|
existence cannot be read (a permission error) is never treated as deleted.
|
|
6811
7262
|
|
|
6812
7263
|
SWEEPING FINISHED WORKTREES
|
|
@@ -6955,10 +7406,18 @@ ON THE SOURCE CHECKOUT
|
|
|
6955
7406
|
(including nested monorepo app dirs) dropped, and the global workspace
|
|
6956
7407
|
directory deleted. The tree itself is never touched, which is also why the
|
|
6957
7408
|
dirty-tree and unpushed guards do not apply on that path.
|
|
7409
|
+
Run from the checkout root it reclaims every registered project under the
|
|
7410
|
+
checkout. Run from a subfolder (for example apps/mobile) it reclaims only
|
|
7411
|
+
the nearest registered project at or above that folder and the projects nested
|
|
7412
|
+
under it, leaves the other
|
|
7413
|
+
projects' devices, ports and records alone, and refuses with exit 1 when no
|
|
7414
|
+
project is registered at or above it.
|
|
6958
7415
|
It ends with:
|
|
6959
7416
|
Reclaimed the environment; the working tree stays (it is the source checkout).
|
|
6960
7417
|
A registered project directory that is not a git repo at all gets the same
|
|
6961
7418
|
environment reclaim -- there is nothing else remove could mean there.
|
|
7419
|
+
Either reclaim archives each workspace it removes, as removal of a linked
|
|
7420
|
+
worktree does.
|
|
6962
7421
|
|
|
6963
7422
|
The delete paths and \`stop\` do not check simulator occupancy. An explicit
|
|
6964
7423
|
\`stim stop\` shuts down this workspace's Stim-owned simulator, including a
|
|
@@ -7074,10 +7533,10 @@ THE ONE CASE GC WILL NOT REAP
|
|
|
7074
7533
|
it found, so you can judge. Delete them yourself:
|
|
7075
7534
|
xcrun simctl delete <udid>
|
|
7076
7535
|
avdmanager delete avd -n <name>`
|
|
7077
|
-
|
|
7078
|
-
|
|
7079
|
-
|
|
7080
|
-
|
|
7536
|
+
},
|
|
7537
|
+
collector: {
|
|
7538
|
+
summary: "log collector reaping: an unproven collector pid, and why the app on a phone closed",
|
|
7539
|
+
body: () => `WHAT ELSE STOP REAPS
|
|
7081
7540
|
The device-log collectors (\`simctl log stream\` / \`adb logcat\`) that
|
|
7082
7541
|
\`ios\` / \`android\` attach after launch. They are recorded in
|
|
7083
7542
|
the global workspace state.json, and nothing outside this workspace can name them,
|
|
@@ -7131,10 +7590,10 @@ THE ONE CASE GC WILL NOT REAP
|
|
|
7131
7590
|
is gone, and the unrelated process is never signalled. A missing, malformed,
|
|
7132
7591
|
or unreadable identity leaves the record unverified and kept for a retry.
|
|
7133
7592
|
Wall-clock timestamps and command names are not ownership proof.`
|
|
7134
|
-
|
|
7135
|
-
|
|
7136
|
-
|
|
7137
|
-
|
|
7593
|
+
},
|
|
7594
|
+
memory: {
|
|
7595
|
+
summary: "watchman and Gradle and Kotlin daemon memory in gc, and when gc --delete --cache watchman or gradle-daemons stops them",
|
|
7596
|
+
body: () => `MEMORY
|
|
7138
7597
|
Every \`gc\` without --cache, and \`gc --cache watchman\` or
|
|
7139
7598
|
\`gc --cache gradle-daemons\`, reports the long-lived helpers that grow while
|
|
7140
7599
|
they run: the shared watchman daemon, Gradle daemons and Kotlin compile
|
|
@@ -7187,10 +7646,10 @@ THE ONE CASE GC WILL NOT REAP
|
|
|
7187
7646
|
--older-than does not apply to these kinds and is refused with
|
|
7188
7647
|
STIM_BAD_ARG. With STIM_HOME set, gc skips them: they are machine-global.
|
|
7189
7648
|
\`stim doctor\` notes a watchman footprint over 2 GiB.`
|
|
7190
|
-
|
|
7191
|
-
|
|
7192
|
-
|
|
7193
|
-
|
|
7649
|
+
},
|
|
7650
|
+
disk: {
|
|
7651
|
+
summary: "disk usage, workspace build outputs, logs and device recordings, AVD and build-log sizes, the data partition, trimming the shared caches",
|
|
7652
|
+
body: () => `DISK
|
|
7194
7653
|
Logs, state, pidfiles and Xcode DerivedData are under the global workspace
|
|
7195
7654
|
directory, and \`worktree remove\` reclaims them. \`gc --delete\` clears the
|
|
7196
7655
|
build outputs of workspaces nobody is using (WORKSPACE BUILD OUTPUTS
|
|
@@ -7331,12 +7790,14 @@ SHARED BUILD CACHES
|
|
|
7331
7790
|
|
|
7332
7791
|
Trim rather than empty. Emptying costs the next build in every project the
|
|
7333
7792
|
time the cache was saving.`
|
|
7334
|
-
}
|
|
7335
7793
|
}
|
|
7336
|
-
}
|
|
7337
|
-
|
|
7338
|
-
|
|
7339
|
-
|
|
7794
|
+
}
|
|
7795
|
+
};
|
|
7796
|
+
//#endregion
|
|
7797
|
+
//#region src/guide/settings.ts
|
|
7798
|
+
var settings_default = {
|
|
7799
|
+
summary: "Settings Stim reads, and where they can live",
|
|
7800
|
+
body: () => `SETTINGS
|
|
7340
7801
|
|
|
7341
7802
|
Settings are JSON: machine layers in ~/.stim/config.json and a committed
|
|
7342
7803
|
.stim.json. Command-line selectors override their matching settings.
|
|
@@ -7379,8 +7840,8 @@ Resolution order, first match wins:
|
|
|
7379
7840
|
3. committed .stim.json beside the app's package.json or Package.swift
|
|
7380
7841
|
4. machine ~/.stim/config.json, top-level optimizations,
|
|
7381
7842
|
ios.deviceType, ios.runtime, android.systemImage,
|
|
7382
|
-
android.deviceProfile
|
|
7383
|
-
and archive.enabled
|
|
7843
|
+
android.deviceProfile, devices.idleShutdownMinutes,
|
|
7844
|
+
devices.reclaimIdleMinutes and archive.enabled
|
|
7384
7845
|
5. Stim default
|
|
7385
7846
|
An environment override, where a setting has one, wins over every layer.
|
|
7386
7847
|
|
|
@@ -7440,8 +7901,8 @@ KEYS STIM READS
|
|
|
7440
7901
|
flag overrides this per invocation. Unset means Debug.
|
|
7441
7902
|
ios.remote "proxy", "eas", or a named approved Mac from
|
|
7442
7903
|
hosting.machines, with the same meaning as --remote.
|
|
7443
|
-
"auto"
|
|
7444
|
-
|
|
7904
|
+
"auto" places on an approved Mac when this Mac is full or
|
|
7905
|
+
busy. Unset runs here. See lifecycle hosted-ios.
|
|
7445
7906
|
ios.simslimProfile a SimSlim JSON profile under the app directory,
|
|
7446
7907
|
at most 64 KiB. Install the
|
|
7447
7908
|
external tool once with
|
|
@@ -7581,7 +8042,10 @@ ${ANDROID_AVD_CONFIG_HELP.map((line) => ` ${line}`).joi
|
|
|
7581
8042
|
committed .stim.json avoids carrying a secret; a
|
|
7582
8043
|
bare string is used as the literal password. Unset
|
|
7583
8044
|
means the debug keystore's fixed "android".
|
|
7584
|
-
android.remote "proxy"
|
|
8045
|
+
android.remote "proxy", "eas", or a named approved Mac in
|
|
8046
|
+
hosting.machines; "auto" places on an approved Mac when
|
|
8047
|
+
this Mac is full or busy. Unset runs here.
|
|
8048
|
+
See lifecycle hosted-android.
|
|
7585
8049
|
metro.tunnel selects how a remote device reaches this workspace's
|
|
7586
8050
|
Metro after remote intent exists. Plain \`start\` stays
|
|
7587
8051
|
local. For Expo and bare React Native, "auto" (default)
|
|
@@ -7652,9 +8116,17 @@ ${ANDROID_AVD_CONFIG_HELP.map((line) => ` ${line}`).joi
|
|
|
7652
8116
|
minutes an owned simulator or emulator stays idle
|
|
7653
8117
|
(\`gc --idle\` conditions; an open stim-server viewer
|
|
7654
8118
|
counts as activity) before its workspace's supervisor
|
|
7655
|
-
shuts it down, never deletes it. Default 0
|
|
8119
|
+
shuts it down, never deletes it. Default 30; 0 never.
|
|
7656
8120
|
Machine or project layers. Read when the supervisor
|
|
7657
8121
|
starts; see \`guide lifecycle budget\`.
|
|
8122
|
+
devices.reclaimIdleMinutes
|
|
8123
|
+
minutes idle before the head of the device slot queue
|
|
8124
|
+
shuts down the longest-idle eligible owned device in
|
|
8125
|
+
another workspace of this Stim home. Default 10; 0 off.
|
|
8126
|
+
Machine or project layers, resolved for the waiting
|
|
8127
|
+
workspace on each poll. Same idle checks as supervisor
|
|
8128
|
+
shutdown, one device per poll, never deleted. See
|
|
8129
|
+
\`guide lifecycle concurrency\`.
|
|
7658
8130
|
macos.product the explicit Swift Package executable product built in
|
|
7659
8131
|
Debug by stim macos
|
|
7660
8132
|
macos.infoPlist development Info.plist relative to Package.swift,
|
|
@@ -7722,7 +8194,8 @@ ${ANDROID_AVD_CONFIG_HELP.map((line) => ` ${line}`).joi
|
|
|
7722
8194
|
them from the environment or the machine layers.
|
|
7723
8195
|
|
|
7724
8196
|
Each setting takes its documented type: string, array of strings, number,
|
|
7725
|
-
boolean, or object.
|
|
8197
|
+
boolean, or object. ios.remote and android.remote accept backend names or
|
|
8198
|
+
tailnet machine names. metro.tunnel, web.viewport,
|
|
7726
8199
|
optimizations.android.compilerCache and optimizations.android.pch take only
|
|
7727
8200
|
their listed choices. A value of the wrong
|
|
7728
8201
|
type or outside those choices is refused by name on every command that resolves
|
|
@@ -7878,6 +8351,13 @@ platform bounds. Android adoption matches the system image and AVD creation
|
|
|
7878
8351
|
settings, preserves the APK, and clears app data before launch. See
|
|
7879
8352
|
\`guide lifecycle pool\` for cleanup and the system state that remains.
|
|
7880
8353
|
|
|
8354
|
+
The host also uses these bounds for hosted iOS and Android parking on stop,
|
|
8355
|
+
separately from its local pool and across all hosted clients per platform.
|
|
8356
|
+
It adopts only a compatible device of the same client, clears app data and
|
|
8357
|
+
removes other apps. Parked devices persist across a clean stim-server restart;
|
|
8358
|
+
eviction, adoption-time reconciliation and revocation delete them through
|
|
8359
|
+
owned-device teardown. See guide lifecycle hosted-ios.
|
|
8360
|
+
|
|
7881
8361
|
When STIM_HOME is set, parking and adoption are OFF unless
|
|
7882
8362
|
the corresponding STIM_POOL_IOS_PARKED_MAX or STIM_POOL_ANDROID_PARKED_MAX
|
|
7883
8363
|
is set too. A redirected home is a scoped config --
|
|
@@ -7896,6 +8376,15 @@ ticket; matching hashes and approved credentials are preserved.
|
|
|
7896
8376
|
Approved doctor entries under buildMachines and deviceHosts include host
|
|
7897
8377
|
{ name, screenRecording, accessibility } when the worker reports it.
|
|
7898
8378
|
|
|
8379
|
+
BUILD MACHINES AND PHONE APP
|
|
8380
|
+
When the user mentions slow builds, a busy Mac, a full disk or device limit, or
|
|
8381
|
+
wanting updates away from the desk, tell them about Stim Desktop > Settings >
|
|
8382
|
+
Build machines > Add (a Mac on the tailnet that builds or hosts simulators for
|
|
8383
|
+
this one) or Phones > Pair a Phone (the Stim phone app). Stim Desktop also
|
|
8384
|
+
suggests these itself, at most once a day and never during a build. Agents only
|
|
8385
|
+
tell the user: they never run \`stim-server setup\`, never edit \`offload.*\` or
|
|
8386
|
+
\`hosting.*\` for the user, and never approve requests.
|
|
8387
|
+
|
|
7899
8388
|
HOSTING MACHINES ARE MACHINE-LEVEL
|
|
7900
8389
|
\`hosting.machines\` names Macs that may host owned simulator sessions,
|
|
7901
8390
|
by MagicDNS name with an optional serve port (default 7443). Name each node
|
|
@@ -7915,9 +8404,22 @@ unless every chosen capability already has a matching approval. Desktop reuse
|
|
|
7915
8404
|
requires an existing tailnet route. Ctrl-C or SIGTERM completes the journal,
|
|
7916
8405
|
releases the setup claim and exits 1; a typed N also exits 1. Hosting grants
|
|
7917
8406
|
include no read, control or build capability.
|
|
8407
|
+
On the hosting Mac, Stim Desktop > Settings > Phones > Hosted here lists the
|
|
8408
|
+
simulators, emulators and apps approved Macs run there, below Device hosting
|
|
8409
|
+
approvals. Stop asks for confirmation, ends the session and deletes or parks
|
|
8410
|
+
its device on that Mac. Parked sessions remain listed without Stop. The list
|
|
8411
|
+
refreshes every five seconds and stays hidden when the local server does not
|
|
8412
|
+
support it.
|
|
7918
8413
|
$STIM_HOME/device-host-machines.json stores a private token and pinned tailnet
|
|
7919
8414
|
node. Doctor never prints the token; it reports each machine under deviceHosts
|
|
7920
|
-
in JSON.
|
|
8415
|
+
in JSON. Desktop uses placement set by config or agents, such as
|
|
8416
|
+
stim settings set ios.remote auto --scope workspace (or android.remote),
|
|
8417
|
+
or per run stim ios --remote auto / stim ios --remote <machine>.
|
|
8418
|
+
New Desktop runs pass no --remote flag; recorded hosted sessions keep their
|
|
8419
|
+
machine until stim stop. Only devices outside this Mac show "on <machine>"
|
|
8420
|
+
on tiles, workspace pages, viewer toolbars and sidebar rows, with the reason
|
|
8421
|
+
as hover text.
|
|
8422
|
+
Stim sends tokens only to the pinned node's own tailnet address,
|
|
7921
8423
|
with its MagicDNS name for TLS and Host routing. A changed node refuses
|
|
7922
8424
|
access, and uncertain replies or unreadable credentials preserve the pin.
|
|
7923
8425
|
Invalid hosting settings report an error and preserve every saved credential.
|
|
@@ -7933,14 +8435,16 @@ Access (Accessibility on macOS 26 and earlier) for Stim Host, the app
|
|
|
7933
8435
|
\`stim-server service install\` runs the server under.
|
|
7934
8436
|
|
|
7935
8437
|
On a hosting Mac, \`hosting.agentDriver\` names the tool it starts so a
|
|
7936
|
-
client's coding agent can drive the macOS apps
|
|
8438
|
+
client's coding agent can drive the macOS apps, iOS simulators and Android emulators it hosts for that client.
|
|
7937
8439
|
The default, \`none\`, starts nothing. For macOS, \`agent-device\` starts its
|
|
7938
8440
|
shared daemon only when that agent-device can lease a single app (its
|
|
7939
8441
|
\`macos-app\` lease backend); otherwise agent control reports \`none\` with a
|
|
7940
8442
|
notice, and no client is handed the Mac's desktop. STIM_AGENT_DEVICE_BIN in
|
|
7941
8443
|
stim-server's environment names an agent-device binary to use instead of
|
|
7942
8444
|
~/.local/bin/agent-device. Hosted iOS uses one daemon per simulator, pinned by
|
|
7943
|
-
its UDID policy; both Macs need agent-device 0.21.20 or later.
|
|
8445
|
+
its UDID policy; both Macs need agent-device 0.21.20 or later. Hosted Android
|
|
8446
|
+
uses one daemon per emulator, pinned by its serial policy, with agent-device
|
|
8447
|
+
0.21.22 or later on both Macs. See guide lifecycle
|
|
7944
8448
|
hosted-ios. \`doctor\` on that Mac notes an installed hosted app that runs while
|
|
7945
8449
|
the setting is \`none\`.
|
|
7946
8450
|
|
|
@@ -8019,7 +8523,8 @@ emulator debug build or a stim macos SwiftPM Debug build compiles:
|
|
|
8019
8523
|
Load per core is the 5-minute load average divided by the CPU count; a Mac's
|
|
8020
8524
|
native builds are its Stim runs in prebuild, pods or compile on that Mac, not
|
|
8021
8525
|
the ones it offloaded.
|
|
8022
|
-
\`offload.maxLoadPerCore\` (default 2) is the
|
|
8526
|
+
\`offload.maxLoadPerCore\` (default 2) is also the busy threshold for
|
|
8527
|
+
automatic iOS and Android device placement. It is the load per core at which a Mac
|
|
8023
8528
|
counts as saturated, both here and on a build machine.
|
|
8024
8529
|
|
|
8025
8530
|
STIM_OFFLOAD_MODE overrides it for one command. Device, Release and
|
|
@@ -8170,7 +8675,7 @@ be setup steps are supplied by Stim on the command lines it composes itself:
|
|
|
8170
8675
|
gradle.properties. Debug builds add
|
|
8171
8676
|
-PreactNativeArchitectures=<target ABI> when the owned
|
|
8172
8677
|
emulator system image or physical device proves the ABI;
|
|
8173
|
-
unknown targets and Release builds stay universal. The same run
|
|
8678
|
+
unknown targets and local Release builds stay universal. The same run
|
|
8174
8679
|
carries the ccache launcher and CCACHE_BASEDIR /
|
|
8175
8680
|
CCACHE_NOHASHDIR when ccache is on PATH -- so no
|
|
8176
8681
|
externalNativeBuild cmake arguments in a committed
|
|
@@ -8284,8 +8789,8 @@ The example shows the defaults. Full setting names and behavior:
|
|
|
8284
8789
|
optimizations.android.gradleBuildCache
|
|
8285
8790
|
false passes --no-build-cache to Gradle, overriding org.gradle.caching=true.
|
|
8286
8791
|
optimizations.android.targetAbiOnly
|
|
8287
|
-
false stops narrowing Debug builds to the device ABI.
|
|
8288
|
-
universal; project ABI filters still apply.
|
|
8792
|
+
false stops narrowing Debug and hosted Android builds to the device ABI.
|
|
8793
|
+
Local Release builds are universal; project ABI filters still apply.
|
|
8289
8794
|
|
|
8290
8795
|
Android CAS, explicit PCH modes, and changed iOS compiler options use separate
|
|
8291
8796
|
native artifact keys. Android ccache and none share an artifact key when their
|
|
@@ -8383,10 +8888,12 @@ platform instead of one entry. Pass prune: 'atomic' for a cache whose index
|
|
|
8383
8888
|
references its own data (an LLVM CAS): it is then left alone by --older-than
|
|
8384
8889
|
and emptied whole only by 'gc --delete --cache all'.
|
|
8385
8890
|
Registration is idempotent and keyed on the directory.`
|
|
8386
|
-
|
|
8387
|
-
|
|
8388
|
-
|
|
8389
|
-
|
|
8891
|
+
};
|
|
8892
|
+
//#endregion
|
|
8893
|
+
//#region src/guide/web.ts
|
|
8894
|
+
var web_default = {
|
|
8895
|
+
summary: "stim web: an owned headless Chrome per workspace, its logs, launched, and teardown",
|
|
8896
|
+
body: () => `WEB: AN OWNED CHROME PER WORKSPACE
|
|
8390
8897
|
|
|
8391
8898
|
If Stim is not installed globally, replace stim with npx stim.
|
|
8392
8899
|
|
|
@@ -8604,10 +9111,12 @@ LIMITS
|
|
|
8604
9111
|
|
|
8605
9112
|
Chrome and Chromium only. The owned page is one tab: a page the app opens in
|
|
8606
9113
|
a new window is not captured.`
|
|
8607
|
-
|
|
8608
|
-
|
|
8609
|
-
|
|
8610
|
-
|
|
9114
|
+
};
|
|
9115
|
+
//#endregion
|
|
9116
|
+
//#region src/guide/macos.ts
|
|
9117
|
+
var macos_default = {
|
|
9118
|
+
summary: "Swift Package macOS Debug apps: owned bundle, logs, local window viewing, and --remote on another Mac",
|
|
9119
|
+
body: () => `MACOS: SWIFT PACKAGE DEVELOPMENT PROTOTYPE
|
|
8611
9120
|
|
|
8612
9121
|
If Stim is not installed globally, replace stim with npx stim.
|
|
8613
9122
|
|
|
@@ -8700,6 +9209,33 @@ carries buildMachine (selected) and builtOn (here or the machine, absent until
|
|
|
8700
9209
|
a build runs), and errorCode on a typed failure. It carries offloadedTo only for a remote build, and offloadFallback when an offload
|
|
8701
9210
|
attempt falls back here. Placement and failure reasons are in build logs.
|
|
8702
9211
|
|
|
9212
|
+
AGENT ACTIONS
|
|
9213
|
+
|
|
9214
|
+
Launch through stim macos first. Use its isolated bundleId from the JSON
|
|
9215
|
+
launch record or status --json:
|
|
9216
|
+
|
|
9217
|
+
agent-device open <bundleId> --platform macos --surface app --foreground
|
|
9218
|
+
agent-device click <ref> --settle
|
|
9219
|
+
stim logs --source agent
|
|
9220
|
+
|
|
9221
|
+
Stim reads agent-device's recorded app-scoped actions and failures into the
|
|
9222
|
+
native workspace's agent feed in Desktop and the phone viewer. Text stays
|
|
9223
|
+
redacted by agent-device. Matching uses the workspace-specific bundle ID and
|
|
9224
|
+
the current launch's start time; open/close attempts, app switches and event
|
|
9225
|
+
log rotation clear the binding. Open must explicitly include --surface app:
|
|
9226
|
+
agent-device can otherwise inherit a previous desktop or menubar surface.
|
|
9227
|
+
An unrecorded open needs a new recorded app open before actions appear.
|
|
9228
|
+
Native screenshot events are omitted because their surface override is not
|
|
9229
|
+
recorded. Generic computer-use tools and phone Control are not agent-device
|
|
9230
|
+
actions. Manually duplicated bundles sharing the isolated identifier are
|
|
9231
|
+
unsupported; leave app launch and stop to Stim. No native replay is added.
|
|
9232
|
+
The feed requires agent-device to record the explicit surface in its open
|
|
9233
|
+
event. agent-device 0.21.23 is the minimum; older versions such as 0.21.12 omit
|
|
9234
|
+
it, so native actions remain unavailable with them. Stim does not infer the
|
|
9235
|
+
surface from the bundle ID. Actions are attributed only while the launch is
|
|
9236
|
+
recorded as running: after the app exits or is relaunched, earlier launches'
|
|
9237
|
+
actions are no longer returned, and a failed open is not shown.
|
|
9238
|
+
|
|
8703
9239
|
OWNERSHIP AND LOCAL VIEWING
|
|
8704
9240
|
|
|
8705
9241
|
The supervisor owns a process-identity claim with the app as its child. Stop
|
|
@@ -8939,6 +9475,8 @@ script such as mini-desktop.sh use these instead:
|
|
|
8939
9475
|
3 concurrent slots concurrency.maxDevices on the host; one bundle id
|
|
8940
9476
|
slot per session
|
|
8941
9477
|
|
|
9478
|
+
The host's own owned devices count toward concurrency.maxDevices.
|
|
9479
|
+
|
|
8942
9480
|
The agent-device rows work once the host's agent field names agent-device.
|
|
8943
9481
|
|
|
8944
9482
|
DESKTOP DOGFOOD
|
|
@@ -8965,6 +9503,565 @@ development Info.plist for stim macos. Build and show its owned window in Stim
|
|
|
8965
9503
|
Desktop, verify a source edit and readable failed-build logs, then stop only
|
|
8966
9504
|
this workspace's app. Do not change permissions or use custom build scripts."
|
|
8967
9505
|
`
|
|
9506
|
+
};
|
|
9507
|
+
//#endregion
|
|
9508
|
+
//#region src/guide/tutorial-data.ts
|
|
9509
|
+
const TUTORIAL_PINS = {
|
|
9510
|
+
createExpoApp: "5.0.0",
|
|
9511
|
+
template: "expo-template-blank@58.0.15"
|
|
9512
|
+
};
|
|
9513
|
+
const TUTORIAL_FILES = {
|
|
9514
|
+
"App.js": `import { useEffect, useState } from 'react';
|
|
9515
|
+
import { Pressable, StyleSheet, Text, View } from 'react-native';
|
|
9516
|
+
import { StatusBar } from 'expo-status-bar';
|
|
9517
|
+
import { TITLE_COLOR } from './theme';
|
|
9518
|
+
|
|
9519
|
+
const TAG = '[stim:tutorial]';
|
|
9520
|
+
|
|
9521
|
+
function Button({ label, onPress }) {
|
|
9522
|
+
return (
|
|
9523
|
+
<Pressable accessibilityRole="button" onPress={onPress} style={styles.button}>
|
|
9524
|
+
<Text style={styles.buttonText}>{label}</Text>
|
|
9525
|
+
</Pressable>
|
|
9526
|
+
);
|
|
9527
|
+
}
|
|
9528
|
+
|
|
9529
|
+
export default function App() {
|
|
9530
|
+
const [note, setNote] = useState('');
|
|
9531
|
+
|
|
9532
|
+
useEffect(() => {
|
|
9533
|
+
console.log(\`\${TAG} title color=\${TITLE_COLOR}\`);
|
|
9534
|
+
}, [TITLE_COLOR]);
|
|
9535
|
+
|
|
9536
|
+
const logError = () => {
|
|
9537
|
+
console.error(\`\${TAG} error-button test error\`);
|
|
9538
|
+
setNote('Logged an error.');
|
|
9539
|
+
};
|
|
9540
|
+
|
|
9541
|
+
const crash = () => {
|
|
9542
|
+
setTimeout(() => {
|
|
9543
|
+
throw new Error(\`\${TAG} crash-button uncaught test error\`);
|
|
9544
|
+
}, 0);
|
|
9545
|
+
};
|
|
9546
|
+
|
|
9547
|
+
const slowRequest = async () => {
|
|
9548
|
+
const started = Date.now();
|
|
9549
|
+
setNote('Waiting 3 seconds...');
|
|
9550
|
+
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
9551
|
+
const elapsed = Date.now() - started;
|
|
9552
|
+
console.warn(\`\${TAG} slow-request \${elapsed}ms\`);
|
|
9553
|
+
setNote(\`Slow request took \${elapsed}ms.\`);
|
|
9554
|
+
};
|
|
9555
|
+
|
|
9556
|
+
return (
|
|
9557
|
+
<View style={styles.container}>
|
|
9558
|
+
<Text style={[styles.title, { color: TITLE_COLOR }]}>Stim Tutorial</Text>
|
|
9559
|
+
<Text style={styles.body}>Tap a button, then look at Stim Desktop > Logs.</Text>
|
|
9560
|
+
<Button label="Log an error" onPress={logError} />
|
|
9561
|
+
<Button label="Crash me" onPress={crash} />
|
|
9562
|
+
<Button label="Slow request" onPress={slowRequest} />
|
|
9563
|
+
<Text style={styles.note}>{note}</Text>
|
|
9564
|
+
<StatusBar style="auto" />
|
|
9565
|
+
</View>
|
|
9566
|
+
);
|
|
9567
|
+
}
|
|
9568
|
+
|
|
9569
|
+
const styles = StyleSheet.create({
|
|
9570
|
+
container: { flex: 1, backgroundColor: '#fff', alignItems: 'center', justifyContent: 'center', padding: 24 },
|
|
9571
|
+
title: { fontSize: 32, fontWeight: '700', marginBottom: 8 },
|
|
9572
|
+
body: { fontSize: 16, color: '#4b5563', textAlign: 'center', marginBottom: 24 },
|
|
9573
|
+
button: { backgroundColor: '#111827', borderRadius: 10, paddingVertical: 14, paddingHorizontal: 28, marginBottom: 12 },
|
|
9574
|
+
buttonText: { color: '#fff', fontSize: 17, fontWeight: '600' },
|
|
9575
|
+
note: { marginTop: 12, fontSize: 15, color: '#374151' },
|
|
9576
|
+
});
|
|
9577
|
+
`,
|
|
9578
|
+
"theme.js": `export const TITLE_COLOR = '#1f2937';
|
|
9579
|
+
`,
|
|
9580
|
+
"app.json": `{
|
|
9581
|
+
"expo": {
|
|
9582
|
+
"name": "Stim Tutorial",
|
|
9583
|
+
"slug": "stim-tutorial",
|
|
9584
|
+
"version": "1.0.0",
|
|
9585
|
+
"orientation": "portrait",
|
|
9586
|
+
"icon": "./assets/icon.png",
|
|
9587
|
+
"userInterfaceStyle": "light",
|
|
9588
|
+
"ios": {
|
|
9589
|
+
"supportsTablet": true,
|
|
9590
|
+
"bundleIdentifier": "dev.stim.tutorial"
|
|
9591
|
+
},
|
|
9592
|
+
"android": {
|
|
9593
|
+
"adaptiveIcon": {
|
|
9594
|
+
"backgroundColor": "#E6F4FE",
|
|
9595
|
+
"foregroundImage": "./assets/android-icon-foreground.png",
|
|
9596
|
+
"backgroundImage": "./assets/android-icon-background.png",
|
|
9597
|
+
"monochromeImage": "./assets/android-icon-monochrome.png"
|
|
9598
|
+
},
|
|
9599
|
+
"package": "dev.stim.tutorial"
|
|
9600
|
+
},
|
|
9601
|
+
"web": {
|
|
9602
|
+
"favicon": "./assets/favicon.png"
|
|
9603
|
+
},
|
|
9604
|
+
"extra": {
|
|
9605
|
+
"stimTutorial": 1
|
|
9606
|
+
}
|
|
9607
|
+
}
|
|
9608
|
+
}
|
|
9609
|
+
`,
|
|
9610
|
+
".gitignore": `/ios
|
|
9611
|
+
/android
|
|
9612
|
+
/tutorial*.ad
|
|
9613
|
+
/tutorial*.png
|
|
9614
|
+
`
|
|
9615
|
+
};
|
|
9616
|
+
const TUTORIAL_PROMPTS = {
|
|
9617
|
+
begin: "Run the Stim tutorial.",
|
|
9618
|
+
rebuild: "Continue the Stim tutorial: rebuild",
|
|
9619
|
+
agent: "Continue the Stim tutorial: agent",
|
|
9620
|
+
refresh: "Continue the Stim tutorial: refresh",
|
|
9621
|
+
machine: "Continue the Stim tutorial: machine",
|
|
9622
|
+
finish: "Continue the Stim tutorial: finish"
|
|
9623
|
+
};
|
|
9624
|
+
const TUTORIAL_RESTART_PROMPT = "Restart the Stim tutorial.";
|
|
9625
|
+
const TUTORIAL_STEPS = [
|
|
9626
|
+
{
|
|
9627
|
+
id: "begin",
|
|
9628
|
+
title: "Create the tutorial",
|
|
9629
|
+
who: "agent",
|
|
9630
|
+
optional: false,
|
|
9631
|
+
prompt: TUTORIAL_PROMPTS.begin,
|
|
9632
|
+
section: "run",
|
|
9633
|
+
manual: [
|
|
9634
|
+
"base=\"{base}\"",
|
|
9635
|
+
"mkdir -p \"${base%/*}\"",
|
|
9636
|
+
"if [ ! -e \"$base\" ]; then",
|
|
9637
|
+
"cd \"${base%/*}\"",
|
|
9638
|
+
"if git -C \"${base%/*}\" rev-parse --is-inside-work-tree >/dev/null 2>&1; then echo \"Stop: inside another repository. Ask for another folder; never git add in the user repository.\"; exit 1; fi",
|
|
9639
|
+
`npx --yes create-expo-app@${TUTORIAL_PINS.createExpoApp} stim-tutorial --template ${TUTORIAL_PINS.template} --no-install --no-agents-md --yes`,
|
|
9640
|
+
"cd \"$base\"",
|
|
9641
|
+
"stim guide tutorial app",
|
|
9642
|
+
...Object.entries(TUTORIAL_FILES).flatMap(([name, content]) => [`cat ${name === ".gitignore" ? ">>" : ">"} ${name} <<'STIM_TUTORIAL_EOF'`].concat(content.trimEnd().split("\n"), "STIM_TUTORIAL_EOF")),
|
|
9643
|
+
"npm install --prefer-offline",
|
|
9644
|
+
"npm pkg set scripts.ios=\"expo run:ios\" scripts.android=\"expo run:android\"",
|
|
9645
|
+
"git init",
|
|
9646
|
+
"git add -A",
|
|
9647
|
+
"git -c user.name=Stim -c user.email=stim@localhost -c commit.gpgsign=false commit -m \"Stim tutorial\"",
|
|
9648
|
+
"else",
|
|
9649
|
+
"cd \"$base\"",
|
|
9650
|
+
`node -e 'const fs = require("node:fs"); const app = fs.existsSync("app.json") ? JSON.parse(fs.readFileSync("app.json", "utf8")) : null; if (app?.expo?.extra?.stimTutorial !== ${JSON.parse(TUTORIAL_FILES["app.json"]).expo.extra.stimTutorial}) { console.error("Stop: existing non-tutorial folder or another version. Ask for another folder; never overwrite or delete it."); process.exit(1); }'`,
|
|
9651
|
+
`git rev-parse --show-toplevel | node -e 'const fs = require("node:fs"); const root = fs.readFileSync(0, "utf8").trim(); if (fs.realpathSync(root) !== fs.realpathSync(process.cwd())) { console.error("Stop: inside another repository. Ask for another folder; never git add in the user repository."); process.exit(1); }'`,
|
|
9652
|
+
"fi"
|
|
9653
|
+
]
|
|
9654
|
+
},
|
|
9655
|
+
{
|
|
9656
|
+
id: "sidebar",
|
|
9657
|
+
title: "Workspace in sidebar",
|
|
9658
|
+
who: "you",
|
|
9659
|
+
optional: false,
|
|
9660
|
+
prompt: null,
|
|
9661
|
+
section: null,
|
|
9662
|
+
manual: []
|
|
9663
|
+
},
|
|
9664
|
+
{
|
|
9665
|
+
id: "build",
|
|
9666
|
+
title: "First iOS build",
|
|
9667
|
+
who: "you",
|
|
9668
|
+
optional: false,
|
|
9669
|
+
prompt: null,
|
|
9670
|
+
section: null,
|
|
9671
|
+
manual: [
|
|
9672
|
+
"cd \"{base}\"",
|
|
9673
|
+
"git worktree add -B stim-tutorial/tour \"{tour}\" HEAD",
|
|
9674
|
+
"cd \"{tour}\"",
|
|
9675
|
+
"stim worktree warm",
|
|
9676
|
+
"stim guide agent",
|
|
9677
|
+
"stim doctor --platform ios",
|
|
9678
|
+
"stim start",
|
|
9679
|
+
"stim ios"
|
|
9680
|
+
]
|
|
9681
|
+
},
|
|
9682
|
+
{
|
|
9683
|
+
id: "rebuild",
|
|
9684
|
+
title: "Rebuild from cache",
|
|
9685
|
+
who: "agent",
|
|
9686
|
+
optional: false,
|
|
9687
|
+
prompt: TUTORIAL_PROMPTS.rebuild,
|
|
9688
|
+
section: "rebuild",
|
|
9689
|
+
manual: [
|
|
9690
|
+
"cd \"{tour}\"",
|
|
9691
|
+
"stim ios",
|
|
9692
|
+
"stim status --json"
|
|
9693
|
+
]
|
|
9694
|
+
},
|
|
9695
|
+
{
|
|
9696
|
+
id: "device",
|
|
9697
|
+
title: "Live view and control",
|
|
9698
|
+
who: "you",
|
|
9699
|
+
optional: false,
|
|
9700
|
+
prompt: null,
|
|
9701
|
+
section: null,
|
|
9702
|
+
manual: ["stim status"]
|
|
9703
|
+
},
|
|
9704
|
+
{
|
|
9705
|
+
id: "logs",
|
|
9706
|
+
title: "App logs",
|
|
9707
|
+
who: "you",
|
|
9708
|
+
optional: false,
|
|
9709
|
+
prompt: null,
|
|
9710
|
+
section: null,
|
|
9711
|
+
manual: ["stim logs --errors", "stim logs --grep '\\[stim:tutorial\\]'"]
|
|
9712
|
+
},
|
|
9713
|
+
{
|
|
9714
|
+
id: "agent",
|
|
9715
|
+
title: "Agent actions and replay",
|
|
9716
|
+
who: "agent",
|
|
9717
|
+
optional: false,
|
|
9718
|
+
prompt: TUTORIAL_PROMPTS.agent,
|
|
9719
|
+
section: "agent",
|
|
9720
|
+
manual: [
|
|
9721
|
+
"cd \"{tour}\"",
|
|
9722
|
+
"export AGENT_DEVICE_STATE_DIR=\"{stateDir}\"",
|
|
9723
|
+
"stim status --json",
|
|
9724
|
+
"read -r iosUdid",
|
|
9725
|
+
"agent-device open dev.stim.tutorial --platform ios --udid \"$iosUdid\" --save-script=tutorial.ad",
|
|
9726
|
+
"agent-device react-native dismiss-overlay || true",
|
|
9727
|
+
`agent-device press 'label="Log an error"' --settle`,
|
|
9728
|
+
"agent-device screenshot tutorial.png",
|
|
9729
|
+
"agent-device close",
|
|
9730
|
+
"grep -v -e 'target-v1' -e 'dismiss-overlay' tutorial.ad > tutorial-replay.ad",
|
|
9731
|
+
"agent-device replay tutorial-replay.ad --platform ios --udid \"$iosUdid\"",
|
|
9732
|
+
"stim logs --source agent --tail 10"
|
|
9733
|
+
]
|
|
9734
|
+
},
|
|
9735
|
+
{
|
|
9736
|
+
id: "refresh",
|
|
9737
|
+
title: "Fast Refresh",
|
|
9738
|
+
who: "agent",
|
|
9739
|
+
optional: false,
|
|
9740
|
+
prompt: TUTORIAL_PROMPTS.refresh,
|
|
9741
|
+
section: "refresh",
|
|
9742
|
+
manual: [
|
|
9743
|
+
"cd \"{tour}\"",
|
|
9744
|
+
"cat > theme.js <<'STIM_TUTORIAL_EOF'",
|
|
9745
|
+
"export const TITLE_COLOR = '#7c3aed';",
|
|
9746
|
+
"STIM_TUTORIAL_EOF",
|
|
9747
|
+
"sleep 6",
|
|
9748
|
+
"stim logs --errors",
|
|
9749
|
+
"stim logs --grep 'title color'"
|
|
9750
|
+
]
|
|
9751
|
+
},
|
|
9752
|
+
{
|
|
9753
|
+
id: "phone",
|
|
9754
|
+
title: "Watch on your phone",
|
|
9755
|
+
who: "you",
|
|
9756
|
+
optional: true,
|
|
9757
|
+
prompt: null,
|
|
9758
|
+
section: null,
|
|
9759
|
+
manual: []
|
|
9760
|
+
},
|
|
9761
|
+
{
|
|
9762
|
+
id: "machine",
|
|
9763
|
+
title: "Build on another Mac",
|
|
9764
|
+
who: "both",
|
|
9765
|
+
optional: true,
|
|
9766
|
+
prompt: TUTORIAL_PROMPTS.machine,
|
|
9767
|
+
section: "machine",
|
|
9768
|
+
manual: ["cd \"{tour}\"", "stim ios --build-machine \"{machine}\" --no-build-cache"]
|
|
9769
|
+
},
|
|
9770
|
+
{
|
|
9771
|
+
id: "finish",
|
|
9772
|
+
title: "Finish and archive",
|
|
9773
|
+
who: "agent",
|
|
9774
|
+
optional: false,
|
|
9775
|
+
prompt: TUTORIAL_PROMPTS.finish,
|
|
9776
|
+
section: "finish",
|
|
9777
|
+
manual: [
|
|
9778
|
+
"git -C \"{tour}\" checkout -- theme.js",
|
|
9779
|
+
"cd \"{tour}\"",
|
|
9780
|
+
"stim stop",
|
|
9781
|
+
"cd \"{base}\"",
|
|
9782
|
+
"stim worktree remove \"{tour}\""
|
|
9783
|
+
]
|
|
9784
|
+
}
|
|
9785
|
+
];
|
|
9786
|
+
//#endregion
|
|
9787
|
+
//#region src/guide/tutorial.ts
|
|
9788
|
+
const paths = `Use {base} = ~/stim-tutorial and {tour} = ~/stim-tutorial-tour unless the
|
|
9789
|
+
user named another parent folder. Expand ~ to the absolute home path when
|
|
9790
|
+
substituting inside quotes. Keep the tutorial outside the user's project.`;
|
|
9791
|
+
function commands(id) {
|
|
9792
|
+
return TUTORIAL_STEPS.find((step) => step.id === id).manual.join("\n");
|
|
9793
|
+
}
|
|
9794
|
+
//#endregion
|
|
9795
|
+
//#region src/guide/index.ts
|
|
9796
|
+
const TOPICS = {
|
|
9797
|
+
agent: agent_default,
|
|
9798
|
+
facts,
|
|
9799
|
+
metro: metro_default,
|
|
9800
|
+
ports: ports_default,
|
|
9801
|
+
logs: logs_default,
|
|
9802
|
+
errors,
|
|
9803
|
+
lifecycle,
|
|
9804
|
+
cleanup,
|
|
9805
|
+
settings: settings_default,
|
|
9806
|
+
web: web_default,
|
|
9807
|
+
macos: macos_default,
|
|
9808
|
+
tutorial: {
|
|
9809
|
+
summary: "An iOS tutorial: isolated worktree, builds, devices, logs, agent actions, and cleanup",
|
|
9810
|
+
sectionHint: "run",
|
|
9811
|
+
preamble: () => `STIM TUTORIAL
|
|
9812
|
+
|
|
9813
|
+
Create a small Expo app in its own repository and tour worktree. Follow the
|
|
9814
|
+
normal Stim flow on iOS, then inspect builds, cache reuse, device control,
|
|
9815
|
+
logs, agent actions, Fast Refresh, and cleanup.
|
|
9816
|
+
|
|
9817
|
+
Run one section per user request. Then end the turn, say what to look at in
|
|
9818
|
+
Stim Desktop, and give the next prompt. Pause never means stim stop.
|
|
9819
|
+
"${TUTORIAL_PROMPTS.begin}" means stim guide tutorial run.
|
|
9820
|
+
"Continue the Stim tutorial: <section>" means stim guide tutorial <section>.
|
|
9821
|
+
"${TUTORIAL_RESTART_PROMPT}" means stim guide tutorial restart.
|
|
9822
|
+
|
|
9823
|
+
Without Stim Desktop, use the simulator, stim status for the workspace and
|
|
9824
|
+
device, stim logs --errors for errors, and stim stats for build performance.
|
|
9825
|
+
Follow stim guide agent for doctor, errors, consent, and cleanup. Never pair
|
|
9826
|
+
phones, approve machines, or grant access on the user's behalf.
|
|
9827
|
+
|
|
9828
|
+
${paths}
|
|
9829
|
+
|
|
9830
|
+
Start with stim guide tutorial run. For commands to type yourself, read
|
|
9831
|
+
stim guide tutorial manual. The first run needs network access unless the
|
|
9832
|
+
required npm and native dependencies are already cached.`,
|
|
9833
|
+
sections: {
|
|
9834
|
+
run: {
|
|
9835
|
+
summary: "Create or reuse the app, warm a tour worktree, and build on iOS",
|
|
9836
|
+
body: () => `RUN THE TUTORIAL
|
|
9837
|
+
|
|
9838
|
+
${paths}
|
|
9839
|
+
|
|
9840
|
+
Folder safety: create the base folder only if absent. Reuse an existing
|
|
9841
|
+
folder only if app.json's expo.extra.stimTutorial equals this guide's version,
|
|
9842
|
+
${JSON.parse(TUTORIAL_FILES["app.json"]).expo.extra.stimTutorial}; skip creation and file writes when reusing it. For an existing non-tutorial
|
|
9843
|
+
folder or another tutorial version, stop and ask the user for another folder.
|
|
9844
|
+
Never overwrite or delete it.
|
|
9845
|
+
|
|
9846
|
+
For a new app, first check the selected parent folder: create it if absent,
|
|
9847
|
+
then run git -C <parent> rev-parse --is-inside-work-tree. If it succeeds, the
|
|
9848
|
+
folder is inside another repository: stop and ask the user for another folder.
|
|
9849
|
+
Never git add in the user's repo. Then, from the parent folder, run:
|
|
9850
|
+
|
|
9851
|
+
npx --yes create-expo-app@${TUTORIAL_PINS.createExpoApp} stim-tutorial --template ${TUTORIAL_PINS.template} --no-install --no-agents-md --yes
|
|
9852
|
+
|
|
9853
|
+
Enter the base folder. Run stim guide tutorial app, write its three app files
|
|
9854
|
+
verbatim, and append its .gitignore lines. Then run:
|
|
9855
|
+
|
|
9856
|
+
npm install --prefer-offline
|
|
9857
|
+
npm pkg set scripts.ios="expo run:ios" scripts.android="expo run:android"
|
|
9858
|
+
git init
|
|
9859
|
+
git add -A
|
|
9860
|
+
git -c user.name=Stim -c user.email=stim@localhost -c commit.gpgsign=false commit -m "Stim tutorial"
|
|
9861
|
+
|
|
9862
|
+
On reuse, run git rev-parse --show-toplevel in the base folder before adding
|
|
9863
|
+
a worktree; if it does not equal the base folder, the folder is inside another
|
|
9864
|
+
repository: stop and ask the user for another folder.
|
|
9865
|
+
|
|
9866
|
+
Set the scripts before the commit because Expo prebuild rewrites them to
|
|
9867
|
+
expo run:ios and expo run:android; otherwise the dirty worktree blocks removal.
|
|
9868
|
+
On npm or network failure, report stderr and stop.
|
|
9869
|
+
|
|
9870
|
+
From the base folder, add the tour worktree and run:
|
|
9871
|
+
|
|
9872
|
+
git worktree add -B stim-tutorial/tour "{tour}" HEAD
|
|
9873
|
+
cd "{tour}"
|
|
9874
|
+
stim worktree warm
|
|
9875
|
+
stim guide agent
|
|
9876
|
+
|
|
9877
|
+
Apply the guide agent doctor rule before native work: if its STATUS block
|
|
9878
|
+
says doctor is due, run stim doctor --platform ios from the tour worktree and
|
|
9879
|
+
resolve its findings. No STATUS block means doctor is current. Then run:
|
|
9880
|
+
|
|
9881
|
+
stim start
|
|
9882
|
+
stim ios
|
|
9883
|
+
|
|
9884
|
+
Tell the user the first build can take about four minutes on a cold cache.
|
|
9885
|
+
Relay the Open in Stim Desktop link printed by stim ios once.
|
|
9886
|
+
|
|
9887
|
+
PAUSE: end the turn. Ask the user to look at the Build section, then the
|
|
9888
|
+
device, then Logs in Stim Desktop. Without Desktop, use stim status,
|
|
9889
|
+
stim stats, and stim logs --errors. Give the next prompt:
|
|
9890
|
+
"${TUTORIAL_PROMPTS.rebuild}". Leave the workspace running.`
|
|
9891
|
+
},
|
|
9892
|
+
app: {
|
|
9893
|
+
summary: "Pinned template, verbatim app files, and .gitignore additions",
|
|
9894
|
+
body: () => `TUTORIAL APP
|
|
9895
|
+
|
|
9896
|
+
create-expo-app: ${TUTORIAL_PINS.createExpoApp}
|
|
9897
|
+
Template: ${TUTORIAL_PINS.template}
|
|
9898
|
+
Write the app files verbatim. Append the .gitignore lines to the template's
|
|
9899
|
+
existing file. App log lines start with [stim:tutorial]. Crash me raises an
|
|
9900
|
+
uncaught JavaScript error, and Slow request times a local three-second timer;
|
|
9901
|
+
Stim does not capture native network requests.
|
|
9902
|
+
|
|
9903
|
+
${Object.entries(TUTORIAL_FILES).map(([name, content]) => `${name}\n\n\`\`\`${name.endsWith(".json") ? "json" : name.endsWith(".js") ? "js" : "text"}\n${content}\`\`\``).join("\n\n")}`
|
|
9904
|
+
},
|
|
9905
|
+
rebuild: {
|
|
9906
|
+
summary: "Repeat the iOS build and explain the cache result",
|
|
9907
|
+
body: () => `REBUILD
|
|
9908
|
+
|
|
9909
|
+
${paths}
|
|
9910
|
+
|
|
9911
|
+
Run the same build again, never with --no-build-cache:
|
|
9912
|
+
|
|
9913
|
+
${commands("rebuild")}
|
|
9914
|
+
|
|
9915
|
+
Read this workspace's environments[].lastBuilds.ios.cacheHit from
|
|
9916
|
+
stim status --json, and the miss reason printed by stim ios. Explain a local or remote hit, or the actual miss
|
|
9917
|
+
reason when cacheHit is false. A repeat run can miss; report the evidence.
|
|
9918
|
+
|
|
9919
|
+
PAUSE: end the turn. Point at the Build section and cache badge, or stim stats.
|
|
9920
|
+
Ask the user to open the device's live view and tap Log an error; without
|
|
9921
|
+
Desktop use the simulator. Then inspect Logs or stim logs --errors. Crash me
|
|
9922
|
+
and Slow request are optional: the former shows a red box, the latter prints
|
|
9923
|
+
a timing line. Give the next prompt: "${TUTORIAL_PROMPTS.agent}".`
|
|
9924
|
+
},
|
|
9925
|
+
agent: {
|
|
9926
|
+
summary: "Record a tap and screenshot, replay it, and inspect agent logs",
|
|
9927
|
+
body: () => `AGENT ACTIONS
|
|
9928
|
+
|
|
9929
|
+
${paths}
|
|
9930
|
+
|
|
9931
|
+
Set {stateDir} to this workspace's agentDevice.stateDir from stim ios or
|
|
9932
|
+
stim status --json. Replace <ios.udid> with this workspace's ios.udid from
|
|
9933
|
+
stim status --json. Use that exact owned simulator, not a guessed one.
|
|
9934
|
+
Run from the tour worktree:
|
|
9935
|
+
|
|
9936
|
+
${commands("agent").replace("read -r iosUdid", "iosUdid=\"<ios.udid>\"")}
|
|
9937
|
+
|
|
9938
|
+
Use --save-script=tutorial.ad with the equals sign: agent-device treats a
|
|
9939
|
+
separate path as a URL and refuses. The dismiss-overlay step clears a red box that would cover the buttons.
|
|
9940
|
+
Replay with the same --platform and --udid so its steps reach the agent log.
|
|
9941
|
+
Remove the target-v1 evidence and dismiss-overlay lines before replay: replaying
|
|
9942
|
+
them can fail with REPLAY_DIVERGENCE on the recorded button identity.
|
|
9943
|
+
The scripts and screenshot stay in the tour worktree and are git-ignored.
|
|
9944
|
+
|
|
9945
|
+
PAUSE: end the turn. Point at Agent actions, or the agent log records just
|
|
9946
|
+
printed. Expect another error-button line after replay. When screen recording
|
|
9947
|
+
is enabled, the user can scrub Replay in Desktop. Give the next prompt:
|
|
9948
|
+
"${TUTORIAL_PROMPTS.refresh}".`
|
|
9949
|
+
},
|
|
9950
|
+
refresh: {
|
|
9951
|
+
summary: "Change the title to purple and verify Fast Refresh through logs",
|
|
9952
|
+
body: () => `FAST REFRESH
|
|
9953
|
+
|
|
9954
|
+
${paths}
|
|
9955
|
+
|
|
9956
|
+
Set TITLE_COLOR in theme.js to '#7c3aed' (purple). Wait a few seconds for
|
|
9957
|
+
Fast Refresh, without reloading the app:
|
|
9958
|
+
|
|
9959
|
+
${commands("refresh")}
|
|
9960
|
+
|
|
9961
|
+
The line [stim:tutorial] title color=#7c3aed is the proof that the edit reached
|
|
9962
|
+
the app. The intentional button errors may still be in the error log; check
|
|
9963
|
+
whether the edit introduced a new error.
|
|
9964
|
+
|
|
9965
|
+
PAUSE: end the turn. Point at the purple title in the device view or simulator
|
|
9966
|
+
and the title color log line. Phone viewing and an approved build machine are
|
|
9967
|
+
optional user steps; skip them if unwanted. Never pair, approve, or grant
|
|
9968
|
+
anything. If the user names an approved machine, the next prompt is
|
|
9969
|
+
"${TUTORIAL_PROMPTS.machine}". Otherwise give
|
|
9970
|
+
"${TUTORIAL_PROMPTS.finish}".`
|
|
9971
|
+
},
|
|
9972
|
+
machine: {
|
|
9973
|
+
summary: "Optionally build using a machine the user names and has approved",
|
|
9974
|
+
body: () => `BUILD MACHINE (OPTIONAL)
|
|
9975
|
+
|
|
9976
|
+
${paths}
|
|
9977
|
+
|
|
9978
|
+
Proceed only when the user names an approved build machine. Substitute that
|
|
9979
|
+
name for {machine}. Never approve, pair, or grant anything. If none is named,
|
|
9980
|
+
ask for the name or let the user skip this step.
|
|
9981
|
+
|
|
9982
|
+
${commands("machine")}
|
|
9983
|
+
|
|
9984
|
+
This step bypasses the artifact cache so the build can use the named machine.
|
|
9985
|
+
A named machine refuses without a local fallback. If it refuses, report the
|
|
9986
|
+
refusal and offer --build-machine auto or local; do not retry silently.
|
|
9987
|
+
|
|
9988
|
+
PAUSE: end the turn. Point at the build's machine in Desktop or its report in
|
|
9989
|
+
stim status --json. Give the next prompt: "${TUTORIAL_PROMPTS.finish}".`
|
|
9990
|
+
},
|
|
9991
|
+
finish: {
|
|
9992
|
+
summary: "Revert the tutorial edit, stop, and remove only the tour worktree",
|
|
9993
|
+
body: () => `FINISH
|
|
9994
|
+
|
|
9995
|
+
${paths}
|
|
9996
|
+
|
|
9997
|
+
The user's finish request authorizes removing this tour worktree only.
|
|
9998
|
+
Revert the refresh edit before removal; worktree remove refuses dirty trees.
|
|
9999
|
+
Stop from the tour path, then remove from the base checkout:
|
|
10000
|
+
|
|
10001
|
+
${commands("finish")}
|
|
10002
|
+
|
|
10003
|
+
Never use --force. On a refusal, report it and stop. Keep the base folder
|
|
10004
|
+
and branch. Print these optional cleanup commands for the user; do not run. Deleting
|
|
10005
|
+
the base folder also removes the branch, so they are alternatives:
|
|
10006
|
+
|
|
10007
|
+
rm -rf "{base}"
|
|
10008
|
+
git -C "{base}" branch -D stim-tutorial/tour
|
|
10009
|
+
|
|
10010
|
+
If archive is enabled, tell the user the tour appears under Archived in Stim
|
|
10011
|
+
Desktop. With archive disabled, report removal without promising an archive.
|
|
10012
|
+
Without Desktop, inspect stim status --json for the removed environment and,
|
|
10013
|
+
when enabled, its archived entry. End the turn.`
|
|
10014
|
+
},
|
|
10015
|
+
restart: {
|
|
10016
|
+
summary: "Remove the existing tour safely and repeat from the worktree step",
|
|
10017
|
+
body: () => `RESTART
|
|
10018
|
+
|
|
10019
|
+
${paths}
|
|
10020
|
+
|
|
10021
|
+
For "${TUTORIAL_RESTART_PROMPT}", if the tour worktree is present, revert
|
|
10022
|
+
the refresh edit, stop, and remove it using the finish section's commands:
|
|
10023
|
+
|
|
10024
|
+
${commands("finish")}
|
|
10025
|
+
|
|
10026
|
+
Never use --force. On a refusal, report it and stop. Keep the base repository.
|
|
10027
|
+
Read stim guide tutorial run, recheck its folder and repository safety rules,
|
|
10028
|
+
and redo run from the worktree step. Pause after the build as run instructs.`
|
|
10029
|
+
},
|
|
10030
|
+
manual: {
|
|
10031
|
+
summary: "Commands for every step, including heredocs for the app files",
|
|
10032
|
+
body: () => `MANUAL TUTORIAL
|
|
10033
|
+
|
|
10034
|
+
${paths}
|
|
10035
|
+
|
|
10036
|
+
These commands are for a person typing them, one step at a time,
|
|
10037
|
+
as scripts: save a block to a file and run it with sh -e, so it stops on the
|
|
10038
|
+
first failure. Read stderr and do not continue to later commands. Follow stim guide tutorial run for folder and repository
|
|
10039
|
+
safety. Reuse only this version's tutorial app, never overwrite another folder.
|
|
10040
|
+
The creation block skips writes on reuse and checks the repository root.
|
|
10041
|
+
|
|
10042
|
+
Replace {base} and {tour} with absolute paths; {base} must end in stim-tutorial. For Agent actions, replace
|
|
10043
|
+
{stateDir} with agentDevice.stateDir from stim ios or stim status --json;
|
|
10044
|
+
when read -r iosUdid waits, type this workspace's ios.udid from that status.
|
|
10045
|
+
For the optional machine step,
|
|
10046
|
+
replace {machine} with a machine you have already approved, or skip it.
|
|
10047
|
+
|
|
10048
|
+
Look at the sidebar during warm and Build during the first build (about four
|
|
10049
|
+
minutes on a cold cache). Compare cacheHit and missReason on rebuild. At Live
|
|
10050
|
+
view and control, open the device viewer and tap Log an error; without Desktop
|
|
10051
|
+
use the simulator. At App logs, try Crash me or Slow request if wanted. A JS
|
|
10052
|
+
crash shows a red box; the slow request is a local timer, not network capture.
|
|
10053
|
+
At Watch on your phone, optionally open an already paired Stim phone to see
|
|
10054
|
+
the tour workspace; phone setup and machine approval stay with you.
|
|
10055
|
+
Use stim status, stim logs --errors, and stim stats without Desktop.
|
|
10056
|
+
|
|
10057
|
+
${TUTORIAL_STEPS.map((step) => `${step.title}${step.optional ? " (optional)" : ""}\n\n${step.manual.length ? `\`\`\`sh\n${step.manual.join("\n")}\n\`\`\`` : "Observe this step in Stim Desktop, or skip it without Desktop."}`).join("\n\n")}
|
|
10058
|
+
|
|
10059
|
+
Finish removes only the tour worktree. Never use --force; report a refusal.
|
|
10060
|
+
With archive enabled, find the tour under Archived in Desktop or archived[]
|
|
10061
|
+
in stim status --json. Keep the base repository and branch. To delete them,
|
|
10062
|
+
read stim guide tutorial finish for commands to review and run yourself.`
|
|
10063
|
+
}
|
|
10064
|
+
}
|
|
8968
10065
|
}
|
|
8969
10066
|
};
|
|
8970
10067
|
//#endregion
|