stim 1.17.2 → 1.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -1
- package/dist/{activity-IRvJY_ru.mjs → activity-BSw0x84R.mjs} +4 -4
- package/dist/{agent-device-usage-output-BK41vnzN.mjs → agent-device-usage-output-Bjxu_VmX.mjs} +8 -8
- package/dist/{android-B9yaNKwa.mjs → android-BXc6BmEi.mjs} +658 -404
- package/dist/{android-B_YNZy61.mjs → android-BdQC6zs1.mjs} +9 -9
- package/dist/android-BvV1gGdN.mjs +4 -0
- package/dist/{android-DE4v59Mb.mjs → android-DE5g_5gv.mjs} +2 -2
- package/dist/{android-cas-ZFpjJBL_.mjs → android-cas-C87geAg-.mjs} +5 -5
- package/dist/android-cas-compiler.mjs +2 -2
- package/dist/api-DIf_s86s.mjs +150 -0
- package/dist/api-run.d.mts +1 -0
- package/dist/api-run.mjs +145 -0
- package/dist/api.d.mts +218 -0
- package/dist/api.mjs +2 -0
- package/dist/{app-install-VPkI5WEg.mjs → app-install-CFI4ptUL.mjs} +33 -13
- package/dist/{attempt-Bk2ouQVL.mjs → attempt-DW-mQqM7.mjs} +14 -4
- package/dist/{web-Dl0KSRHf.mjs → browser-web-CDhqQxhv.mjs} +138 -176
- package/dist/{budget-DfcgFdQB.mjs → budget-Dz-kyXLv.mjs} +1391 -58
- package/dist/build-facts-B87vo-Im.d.mts +142 -0
- package/dist/{cache-manifest-DnUHBJaU.mjs → cache-manifest-DiwKSyv9.mjs} +3 -3
- package/dist/cache-manifest.mjs +1 -1
- package/dist/{chrome-BrcVWQyD.mjs → chrome-BTUOo8p2.mjs} +1 -1
- package/dist/cli-C9Zvgbdc.mjs +68 -0
- package/dist/cli.mjs +1 -1
- package/dist/{client-SvBdgObD.mjs → client-CuUo8Nvg.mjs} +381 -49
- package/dist/collector-run.d.mts +6 -0
- package/dist/collector-run.mjs +9 -9
- package/dist/{command-output-CDAIFD-8.mjs → command-output-Bt8ygHE7.mjs} +2 -2
- package/dist/{config-D0DbpZtM.mjs → config-DC5jEtd1.mjs} +9 -5
- package/dist/{created-devices-CpvzBL25.mjs → created-devices-D0s26Hx5.mjs} +2 -2
- package/dist/{dependency-state-CLcBHH2M.mjs → dependency-state-DKaS1OM9.mjs} +2 -2
- package/dist/{detached-entry-CiTEgMR1.mjs → detached-entry-pB_IPfV4.mjs} +1 -1
- package/dist/{dev-client-B-XC4uRH.mjs → dev-client-CDY_zAmD.mjs} +2 -2
- package/dist/{device-C8IOoJPv.mjs → device-BHw0WlHl.mjs} +8 -8
- package/dist/{device-capacity-ZC4XSZ4R.mjs → device-capacity-BoN1jQkO.mjs} +25 -14
- package/dist/device-host-worker.mjs +19 -19
- package/dist/{gradle-dJpre3Yn.mjs → device-ios-B2ruohE-.mjs} +661 -107
- package/dist/{device-lease-DpoS36_u.mjs → device-lease-Cu7PtQpZ.mjs} +9 -9
- package/dist/{device-lease-run-B8YltIH_.mjs → device-lease-run-D38SnZtt.mjs} +4 -4
- package/dist/{device-pool-3o7s5lo4.mjs → device-pool-CP4rAsHx.mjs} +3 -3
- package/dist/{device-remote-hFSkpfQu.mjs → device-remote-B5GiUifk.mjs} +15 -16
- package/dist/{doctor-CFv1fCrF.mjs → doctor-PRJH94v6.mjs} +61 -34
- package/dist/doctor-watchman-wZhyTdp4.mjs +259 -0
- package/dist/{error-diagnostics-C-NMnhrk.mjs → error-diagnostics--v5HGOU0.mjs} +11 -11
- package/dist/{exec-CaErVv7t.mjs → exec-i8GDPwq9.mjs} +93 -6
- package/dist/{gc-DEmkg3Xh.mjs → gc-CyHMcKcn.mjs} +25 -471
- package/dist/{guide-CSmhMTf8.mjs → guide-dRaQJJk9.mjs} +681 -507
- package/dist/{hosted-android-E1Heoqnv.mjs → hosted-android-Bs7fytj7.mjs} +3 -3
- package/dist/{hosted-client-Bp4knSwu.mjs → hosted-client-C5G4h_OJ.mjs} +2 -2
- package/dist/{hosted-ios-BnbeYOUD.mjs → hosted-ios-Bik035eL.mjs} +1 -1
- package/dist/{hosted-logs-OcgIBvCT.mjs → hosted-logs-CI9Az9a4.mjs} +9 -9
- package/dist/{hosted-macos-DcxKjtfH.mjs → hosted-macos-DABm3ScO.mjs} +5 -5
- package/dist/{hosted-native-1Ebbig4w.mjs → hosted-native-DbV2aUVw.mjs} +13 -9
- package/dist/{idle-shutdown-IGyNV8Cr.mjs → idle-shutdown-DYiMBn34.mjs} +7 -8
- package/dist/{ios-DSfnPfse.mjs → ios-BWbHFZVz.mjs} +19 -7
- package/dist/{ios-Cr1h5gnO.mjs → ios-DCrkgXEM.mjs} +1 -1
- package/dist/ios-DYUr9LGc.mjs +8 -0
- package/dist/ios-ITYno9rO.mjs +2241 -0
- package/dist/{ios-device-pAN4t54-.mjs → ios-device-CJ_ZOGky.mjs} +3 -3
- package/dist/{ios-device-Ee7sDXfp.mjs → ios-device-CrW8aMb5.mjs} +1 -1
- package/dist/{ios-state-8L3lgum6.mjs → ios-state-By0XuLTQ.mjs} +1 -1
- package/dist/{launch-verify-lTnWZikR.mjs → launch-verify-DCveSE6d.mjs} +5 -5
- package/dist/{logs-BAaS7gS2.mjs → logs-Bao1HOMy.mjs} +13 -13
- package/dist/{macos-BIRQS90f.mjs → macos-ciyBdTY5.mjs} +194 -132
- package/dist/macos-nIOATJmR.mjs +2 -0
- package/dist/macos-run.mjs +1 -1
- package/dist/maintenance-run.mjs +349 -47
- package/dist/manifest-CX0d1IkZ.mjs +148 -0
- package/dist/{metro-g8NqiJp-.mjs → metro-DI2AjPF_.mjs} +196 -43
- package/dist/{metro-gateway-C-ijGI2F.mjs → metro-gateway-BxpJLIwh.mjs} +4 -4
- package/dist/{metro-store-CDDZHtPl.mjs → metro-store-BAHFcJm6.mjs} +16 -2
- package/dist/{build-plan-BDsSi09V.mjs → miss-reason-CjQCOKqq.mjs} +207 -249
- package/dist/{named-ports-C8m8EQs2.mjs → named-ports-B4iWH4E-.mjs} +70 -8
- package/dist/{native-runtime-BEK171xQ.mjs → native-runtime-BGxsbfny.mjs} +6 -6
- package/dist/{ndjson-PclOIwVY.mjs → ndjson-LGZAMsqa.mjs} +8 -4
- package/dist/offload-worker.d.mts +3 -45
- package/dist/offload-worker.mjs +11 -11
- package/dist/{ownership-DyRQru0q.mjs → ownership-EB0ySdGK.mjs} +3 -3
- package/dist/{page-QF_Ke10s.mjs → page-Bv9s2KwY.mjs} +1 -1
- package/dist/placement-log-Dxe34rJm.mjs +88 -0
- package/dist/plan-placement-BVY59qa8.mjs +290 -0
- package/dist/{ports-4nYqxPWC.mjs → ports-ByPc0_97.mjs} +3 -3
- package/dist/{prebuild-D7RUMkwN.mjs → prebuild-BKJ3wJI1.mjs} +5 -5
- package/dist/preview-BbSottmO.mjs +986 -0
- package/dist/{rolldown-runtime-BsOwBuO_.mjs → process-identity-Biv3arCO.mjs} +11 -1
- package/dist/project-B5eQ1USs.mjs +612 -0
- package/dist/{pull-request-pxnDCQzn.mjs → pull-request-IHNdMnG8.mjs} +1 -1
- package/dist/pull-requests.mjs +1 -1
- package/dist/react-native-ios-BMh2i25I.mjs +2 -0
- package/dist/react-native-ios-CSpIxexZ.mjs +2083 -0
- package/dist/{recordings-BNgsD1Tr.mjs → recordings-Bv8McZN4.mjs} +1 -1
- package/dist/{reload-DcRxj3qG.mjs → reload-rAPdqeRR.mjs} +14 -15
- package/dist/{remote-cache-Ci4DQYYj.mjs → remote-cache-N3hAEaAL.mjs} +5 -5
- package/dist/{run-CkE9y0nc.mjs → run-lAkle1-c.mjs} +5 -5
- package/dist/{server-bare-BkrSxSsZ.mjs → server-bare-CkIjnegq.mjs} +5 -6
- package/dist/server-expo-DGsdUGX2.mjs +2 -0
- package/dist/{server-expo-CHTyXD0G.mjs → server-expo-n3tUi3Em.mjs} +5 -6
- package/dist/{settings-SR9OmW9h.mjs → settings-2am7TSxp.mjs} +5 -5
- package/dist/{settings-Cfd-0r2d.mjs → settings-CxgngUZk.mjs} +116 -103
- package/dist/{settings-BBpMcDbD.mjs → settings-Djl-mpjX.mjs} +1 -1
- package/dist/settings.schema.json +198 -9
- package/dist/{simslim-BWNrc3M3.mjs → simslim-D9LkJxIO.mjs} +7 -7
- package/dist/{slot-launch-DmjBmpQV.mjs → slot-launch-HV_KtPIw.mjs} +6 -6
- package/dist/{spawn-claims-D0XfSe8u.mjs → spawn-claims-Kqiifsk2.mjs} +1 -1
- package/dist/{spawn-entry-jw19m8Nl.mjs → spawn-entry-Dcb7sDUz.mjs} +1 -0
- package/dist/{stage-CzaiwFsI.mjs → stage-BgaZJVZY.mjs} +14 -3
- package/dist/{start-AlfCPRVU.mjs → start-D8NbxNBb.mjs} +1 -1
- package/dist/{start-DUz3mYdb.mjs → start-DtxthMfI.mjs} +34 -27
- package/dist/{state-PeQiJuIu.mjs → state-D2KtnALG.mjs} +4 -4
- package/dist/{state-DyaNTgfR.mjs → state-Tp4uImDS.mjs} +5 -3
- package/dist/{state-B4PQFXMW.d.mts → state-lKh8shBp.d.mts} +2 -3
- package/dist/{stats-Bq4fJOdB.mjs → stats-8EJ5ivFT.mjs} +6 -6
- package/dist/{status-DlKvVJBE.mjs → status-BEeL6IKz.mjs} +6 -4
- package/dist/{status-BA2pehwf.mjs → status-BOyp0CUl.mjs} +31 -34
- package/dist/{stim-desktop-Cnz8CPnl.mjs → stim-desktop-C65jk_q2.mjs} +1 -1
- package/dist/{stim-installations-QMEI32v1.mjs → stim-installations-DbmUjsLv.mjs} +1 -1
- package/dist/stop-D0UIpZuh.mjs +72 -0
- package/dist/{stop-zF1y04f2.mjs → stop-DRipAg-e.mjs} +2 -2
- package/dist/{stop-DQIasgKp.mjs → stop-zDlknFoz.mjs} +79 -33
- package/dist/supervisor-run.d.mts +24 -22
- package/dist/supervisor-run.mjs +21 -22
- package/dist/{support-CsJNVfOc.mjs → support-CNqYkV0y.mjs} +7 -7
- package/dist/{support-WvD__tSV.mjs → support-DsNaFg6D.mjs} +5 -5
- package/dist/swiftpm-macos-aJzPAj1t.mjs +119 -0
- package/dist/{toolchain-DNyR3NPq.mjs → toolchain-C3xSoyUl.mjs} +475 -269
- package/dist/{trigger-Bemu8ytw.mjs → trigger-CAhmG-4s.mjs} +6 -6
- package/dist/trigger-CoalsMTV.mjs +2 -0
- package/dist/{warm-progress-DF4JRvYI.mjs → warm-progress-pFLB2e6P.mjs} +135 -38
- package/dist/{status-CzS4bPO9.mjs → watchman-BL1IdOtq.mjs} +75 -7
- package/dist/web-DFpFeZu5.mjs +2 -0
- package/dist/web-DbbrPUn0.mjs +127 -0
- package/dist/web-run.mjs +10 -10
- package/dist/{workspace-state-DqcCbpE7.mjs → workspace-state-CR9X86xJ.mjs} +3 -3
- package/dist/{ownership-BMLqyBgJ.mjs → workspaces-Djb2gfi9.mjs} +661 -23
- package/dist/{worktree-Be_7NLKy.mjs → worktree-BL4M1rhB.mjs} +252 -50
- package/dist/{worktree-j3IcFAI5.mjs → worktree-DV-A0LNL.mjs} +1 -1
- package/package.json +10 -5
- package/shim/bundle-response.cjs +2 -1
- package/shim/bundle-response.d.cts +1 -1
- package/shim/expo-metro-config.cjs +1 -1
- package/dist/build-progress-D0WkjCPd.mjs +0 -1036
- package/dist/build-slots-41v_d2x9.mjs +0 -142
- package/dist/cli-CYXb29oE.mjs +0 -51
- package/dist/deps-iKw0eD4P.mjs +0 -438
- package/dist/device-ios-C2Um-XUR.mjs +0 -412
- package/dist/errors-B6W7FkxQ.mjs +0 -15
- package/dist/guide-status-Du-ZQ3FD.mjs +0 -125
- package/dist/idle-BXG__1BA.mjs +0 -176
- package/dist/in-use-CvGOUn-x.mjs +0 -130
- package/dist/ios-BJ4XUSe1.mjs +0 -3867
- package/dist/native-run-D1Q56rAD.mjs +0 -158
- package/dist/preview-B-g9d7kV.mjs +0 -318
- package/dist/process-identity-BmRm1qV0.mjs +0 -12
- package/dist/project-D47Rh0ly.mjs +0 -276
- package/dist/server-expo-CeakhI8P.mjs +0 -2
- package/dist/state-BKa8wNN5.mjs +0 -148
- package/dist/stop-BIiM49EY.mjs +0 -48
- package/dist/stop-cause-rhmGfwvt.mjs +0 -97
- package/dist/trigger-CXts6qt1.mjs +0 -2
- package/dist/watchman-APYjXSQa.mjs +0 -48
- package/dist/workspace-process-lock-2mNzOlaB.mjs +0 -58
- package/dist/workspaces-Bcib97-6.mjs +0 -306
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { SETTINGS, SETTINGS_SCHEMA_URL } from "@stim-cli/core/state";
|
|
1
|
+
import { r as findProjectRoot } from "./project-B5eQ1USs.mjs";
|
|
2
|
+
import { t as ANDROID_AVD_CONFIG_HELP } from "./settings-CxgngUZk.mjs";
|
|
3
|
+
import { s as RECENT_LAUNCH_MS } from "./watchman-BL1IdOtq.mjs";
|
|
4
|
+
import { o as guideStatus, t as WATCHMAN_NESTED_WORKTREES } from "./doctor-watchman-wZhyTdp4.mjs";
|
|
5
|
+
import { SETTINGS, SETTINGS_SCHEMA_URL, TUTORIAL_VERSION } from "@stim-cli/core/state";
|
|
6
6
|
import chalk from "chalk";
|
|
7
7
|
//#region src/guide/agent.ts
|
|
8
8
|
var agent_default = {
|
|
@@ -293,6 +293,11 @@ Ask the user before these actions:
|
|
|
293
293
|
nothing else.
|
|
294
294
|
- gc --delete --worktrees, because it runs worktree remove on every clean,
|
|
295
295
|
idle linked worktree Stim manages, across projects.
|
|
296
|
+
- settings set maintenance.mode on in a home where it is off or report, because
|
|
297
|
+
it lets Stim itself clear build outputs, trim caches and remove finished
|
|
298
|
+
worktrees later, with no command from you. It is on by default outside CI
|
|
299
|
+
and scoped STIM_HOME homes. maintenance.keep true pins a workspace against
|
|
300
|
+
that; maintenance.mode report or off stops it.
|
|
296
301
|
- stop when the workspace owns an EAS session, because it irreversibly ends
|
|
297
302
|
that remote session. For a local device, stop shuts it down but does not
|
|
298
303
|
delete it. An explicit stop shuts down a Stim-owned simulator even when
|
|
@@ -382,6 +387,7 @@ FULL TOPIC LIST
|
|
|
382
387
|
stim guide lifecycle options # every flag, Android variants, --device-type, --system-image, --device-profile
|
|
383
388
|
stim guide lifecycle devices # ios --device and android --device on a physical phone
|
|
384
389
|
stim guide lifecycle release # Release configurations and ...Release variants
|
|
390
|
+
stim guide api # typed run, stop, diagnostics and cancellation from the main package
|
|
385
391
|
stim guide facts # the --json payloads
|
|
386
392
|
stim guide facts devmenu # the Expo dev menu or Tools button over the app
|
|
387
393
|
stim guide macos # Swift Package Debug apps, logs, local preview, --remote on another Mac
|
|
@@ -397,6 +403,64 @@ FULL TOPIC LIST
|
|
|
397
403
|
stim guide settings # configuration files and supported keys`
|
|
398
404
|
};
|
|
399
405
|
//#endregion
|
|
406
|
+
//#region src/guide/api.ts
|
|
407
|
+
var api_default = {
|
|
408
|
+
summary: "Typed lifecycle API: createStim, run, stop, diagnostics, and cancellation",
|
|
409
|
+
body: () => `PROGRAMMATIC API
|
|
410
|
+
|
|
411
|
+
Install stim as a project dependency: npm install --save-dev stim.
|
|
412
|
+
Import { createStim, StimError } from 'stim'. Node 22.12 or later is required.
|
|
413
|
+
|
|
414
|
+
const stim = createStim({ projectRoot: process.cwd() });
|
|
415
|
+
try {
|
|
416
|
+
const result = await stim.run({ platform: 'ios' });
|
|
417
|
+
} finally {
|
|
418
|
+
try {
|
|
419
|
+
const diagnostics = await stim.diagnostics({ errors: true });
|
|
420
|
+
} finally {
|
|
421
|
+
const cleanup = await stim.stop();
|
|
422
|
+
if (!cleanup.ok) throw new Error(cleanup.summary);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
createStim takes projectRoot, optional absolute home and buildCache paths,
|
|
427
|
+
and onProgress({ stream, message }). message is an output chunk. Without
|
|
428
|
+
onProgress, operations write nothing to the caller's stdout or stderr. Each
|
|
429
|
+
operation uses a bundled worker that calls the same lifecycle operations as
|
|
430
|
+
the CLI; it never parses CLI arguments or changes the caller's cwd/environment.
|
|
431
|
+
|
|
432
|
+
run takes platform plus its options:
|
|
433
|
+
ios: configuration, scheme, deviceType, runtime
|
|
434
|
+
android: variant, systemImage, deviceProfile
|
|
435
|
+
macos: remoteBuild
|
|
436
|
+
web: headed (default false)
|
|
437
|
+
iOS and Android also accept slot, metroCheck, buildCache, and remoteBuild.
|
|
438
|
+
All methods accept signal. run builds, installs and launches; build-only is
|
|
439
|
+
not available. Web requires a running server, just like stim web.
|
|
440
|
+
|
|
441
|
+
run returns { platform, facts }. iOS facts include udid; Android includes
|
|
442
|
+
serial; both include bundleId, appPath, metroPort, cacheHit and launched.
|
|
443
|
+
macOS facts include bundle, bundleId, executable, pid, build and launched.
|
|
444
|
+
Web facts contain the owned browser's existing launch and page facts.
|
|
445
|
+
Do not interpret 'bundling' or 'unverified' as proven application readiness.
|
|
446
|
+
|
|
447
|
+
stop({ slot? }) returns { ok, outcomes, summary }. It acts on the workspace,
|
|
448
|
+
including resources from earlier runs. Use a dedicated workspace in CI.
|
|
449
|
+
Cancellation waits for the operation worker to exit; stop with a fresh signal
|
|
450
|
+
after cancellation or partial failure. Always inspect cleanup.ok.
|
|
451
|
+
|
|
452
|
+
diagnostics({ tail: 200, errors: false }) returns { directory, records } from
|
|
453
|
+
the local timeline, even before a successful run. tail: 0 returns paths only.
|
|
454
|
+
It does not fetch remote logs or capture new crashes. Stim failures reject with
|
|
455
|
+
StimError carrying code, message, remedy and details (including the log path).
|
|
456
|
+
An exception thrown by onProgress cancels its worker and propagates unchanged.
|
|
457
|
+
|
|
458
|
+
Normal ownership, device creation, cache locks and coordination remain active.
|
|
459
|
+
All concurrent artifact-cache writers must share the same coordinating home.
|
|
460
|
+
Do not point independent Stim homes at a concurrently writable shared cache.
|
|
461
|
+
`
|
|
462
|
+
};
|
|
463
|
+
//#endregion
|
|
400
464
|
//#region src/guide/facts.ts
|
|
401
465
|
const facts = {
|
|
402
466
|
summary: "The --json payloads: `start`, `ios`, `android`, `web`, `ios|android --plan`, `reload`, `stop`, `status`, `doctor`, `device lock`/`unlock`, `gc`, and the error contract",
|
|
@@ -945,7 +1009,7 @@ leased until <time>" for each one.`,
|
|
|
945
1009
|
reasons as { code, reason } with that code. capacity
|
|
946
1010
|
is the machine's offer: { running, max, diskFreeBytes,
|
|
947
1011
|
minDiskFreeBytes, cpus?, loadPerCore?, builds?, maxBuilds?,
|
|
948
|
-
maxLoadPerCore?, declined? }; an older stim-server omits the
|
|
1012
|
+
maxLoadPerCore?, memoryUsedBytes?, memoryTotalBytes?, declined? }; an older stim-server omits the
|
|
949
1013
|
optional fields
|
|
950
1014
|
findings the diagnostic findings; a lower resolved Stim is a
|
|
951
1015
|
costs-time finding with a PATH or installation remedy
|
|
@@ -1203,7 +1267,7 @@ RULES
|
|
|
1203
1267
|
cannot read. A macOS privacy denial (EPERM) names the
|
|
1204
1268
|
Privacy & Security setting to grant
|
|
1205
1269
|
maintenance top-level { mode, pressure, actions, blocked,
|
|
1206
|
-
skips, note, invalid? }, the next
|
|
1270
|
+
skips, note, invalid? }, the next maintenance plan
|
|
1207
1271
|
from live pressure and cached sizes. note says when no
|
|
1208
1272
|
pass has run yet. No du runs for this preview.
|
|
1209
1273
|
sections one array per report section, in the text order. Every key
|
|
@@ -1368,8 +1432,9 @@ RULES
|
|
|
1368
1432
|
body: () => ` stim status --json
|
|
1369
1433
|
|
|
1370
1434
|
logs { dir, errorsSinceMarker }, or null without a log directory
|
|
1371
|
-
agentDevice { stateDir }: absolute workspace agent-device state
|
|
1372
|
-
every environment, shared by its slots
|
|
1435
|
+
agentDevice { stateDir, installed }: absolute workspace agent-device state
|
|
1436
|
+
path on every environment, shared by its slots, and whether
|
|
1437
|
+
the agent-device executable is on PATH. Reporting it creates
|
|
1373
1438
|
no directory; agent-device creates it when used.
|
|
1374
1439
|
|
|
1375
1440
|
Each environment carries phase, where the workspace is in its lifecycle:
|
|
@@ -1685,14 +1750,18 @@ RULES
|
|
|
1685
1750
|
once the app is ready and covers waiting for the
|
|
1686
1751
|
device: its boot, adoption cleanup, or a physical
|
|
1687
1752
|
device's lease and connection check. A boot that
|
|
1688
|
-
finishes during the build adds no device time.
|
|
1753
|
+
finishes during the build adds no device time. For a
|
|
1754
|
+
device hosted on another Mac, device covers reserving
|
|
1755
|
+
and preparing it there, install covers delivering the
|
|
1756
|
+
app, and launch starts when the host launches it. An
|
|
1689
1757
|
--eas-profile run has no cache lookup, so its outcome
|
|
1690
1758
|
stays the project's most recent one until install.
|
|
1691
1759
|
A stim macos run enters only prepare, compile (SwiftPM,
|
|
1692
1760
|
with detail), install (staging the bundle, or fetching
|
|
1693
1761
|
it from a build machine) and launch. It has no cache
|
|
1694
1762
|
lookup: outcome is null, plannedPhases is null, and
|
|
1695
|
-
builds.macos holds its finished runs
|
|
1763
|
+
builds.macos holds its finished runs, their phases and
|
|
1764
|
+
compileSteps.
|
|
1696
1765
|
startedAt when the run started; phaseStartedAt when its phase did
|
|
1697
1766
|
outcome "cold" after the local/provider lookups resolve a miss;
|
|
1698
1767
|
"hit" after a cached artifact is ready to reuse, including
|
|
@@ -1877,7 +1946,8 @@ RULES
|
|
|
1877
1946
|
disk (environment) { worktreeBytes, nodeModulesBytes, buildBytes,
|
|
1878
1947
|
measuredAt }
|
|
1879
1948
|
worktreeBytes the linked worktree, or else the checkout holding the
|
|
1880
|
-
workspace, node_modules included
|
|
1949
|
+
workspace, node_modules included; the linked
|
|
1950
|
+
worktrees nested inside a checkout are not counted
|
|
1881
1951
|
nodeModulesBytes node_modules at that root and at the workspace path;
|
|
1882
1952
|
part of worktreeBytes
|
|
1883
1953
|
buildBytes Stim's folder for the workspace: Xcode derived data,
|
|
@@ -1887,11 +1957,13 @@ RULES
|
|
|
1887
1957
|
disk (device) { bytes, measuredAt }: the simulator's data folder
|
|
1888
1958
|
under CoreSimulator/Devices, or the AVD's .avd folder
|
|
1889
1959
|
|
|
1890
|
-
\`status --watch\` runs one du at a time
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1960
|
+
\`status --watch\` runs one du at a time on the whole machine, at low
|
|
1961
|
+
priority, off its refresh path, and measures a folder at most every 5
|
|
1962
|
+
minutes while its environment is active and every hour otherwise. A walk
|
|
1963
|
+
that fails or times out is not repeated by any watcher for that period. It
|
|
1964
|
+
caches each size under $STIM_HOME/disk-usage, which one-shot status only
|
|
1965
|
+
reads, so the fields appear once a watcher, such as stim-server or Stim
|
|
1966
|
+
Desktop, has measured.
|
|
1895
1967
|
|
|
1896
1968
|
An environment carries agents when coding-agent sessions work in it, most
|
|
1897
1969
|
recently active first:
|
|
@@ -1957,13 +2029,15 @@ RULES
|
|
|
1957
2029
|
and always uses the estimate. What is using CPU and memory now is the
|
|
1958
2030
|
top-level machine section:
|
|
1959
2031
|
|
|
1960
|
-
maintenance { mode, lastChecks: { pressure, size }, pressure, sizes,
|
|
2032
|
+
maintenance { mode, lastChecks: { pressure, size, worktree, sweep }, pressure, sizes,
|
|
1961
2033
|
lastPass, running, recent, plan, invalid?, claim? }
|
|
1962
|
-
|
|
2034
|
+
mode is off, report or on. Plan actions start with would-; the records
|
|
2035
|
+
of actions taken drop the prefix (clear-outputs, trim-cache, empty-cache,
|
|
2036
|
+
remove-worktree, remove-orphan, unregister-cache).
|
|
1963
2037
|
lastChecks are epoch milliseconds or null. running comes only from a
|
|
1964
2038
|
live maintenance/run.claims owner, never from a check stamp.
|
|
1965
|
-
lastPass carries startedAt, durationMs, trigger, mode, freedBytes
|
|
1966
|
-
actions, stopped (0), blocked. recent holds the last 20 action, failure
|
|
2039
|
+
lastPass carries startedAt, durationMs, trigger, mode, freedBytes
|
|
2040
|
+
(0 in report mode), actions, stopped (0), blocked. recent holds the last 20 action, failure
|
|
1967
2041
|
and blocked NDJSON records from maintenance/maintenance.ndjson.
|
|
1968
2042
|
invalid names invalid settings; invalid cache caps use their defaults,
|
|
1969
2043
|
while invalid maintenance settings disable passes. claim contains an
|
|
@@ -2018,7 +2092,7 @@ RULES
|
|
|
2018
2092
|
|
|
2019
2093
|
{ platform, slot?, fingerprint, cacheKey, cacheHit, provider,
|
|
2020
2094
|
cacheSkipped, prebuild, outcome, expectedMs, basis, missReason?,
|
|
2021
|
-
refusal? }
|
|
2095
|
+
placement?, refusal? }
|
|
2022
2096
|
|
|
2023
2097
|
fingerprint the fingerprint the run would look up first; with
|
|
2024
2098
|
--eas-profile, the one EAS CLI computes
|
|
@@ -2044,6 +2118,9 @@ RULES
|
|
|
2044
2118
|
plan does not, so changes compare the fingerprint before that
|
|
2045
2119
|
prebuild; changeCount 0 then means those inputs match the
|
|
2046
2120
|
baseline. rekeyedBy is empty.
|
|
2121
|
+
placement with ios.remote or android.remote set to auto or a Mac,
|
|
2122
|
+
where the plan assumes the device runs: "on <machine>" or
|
|
2123
|
+
"this Mac; auto may use <machines>"; absent otherwise
|
|
2047
2124
|
refusal { code, message, remedy } when the run would refuse:
|
|
2048
2125
|
STIM_PREBUILD_FAILED for a tracked native dir the fingerprint
|
|
2049
2126
|
leaves out, STIM_EAS_BUILD_MISSING for an EAS miss
|
|
@@ -2182,8 +2259,9 @@ HOW A RUN IS COUNTED (\`stats\`)
|
|
|
2182
2259
|
|
|
2183
2260
|
DEVICE PLACEMENT (\`ios|android --remote auto\`)
|
|
2184
2261
|
Auto runs include devicePlacement: { decision, reason, machine? } in the run
|
|
2185
|
-
facts, lastBuilds and build history. decision is "local", "hosted"
|
|
2186
|
-
"waited-locally" (the local run actually waited for a device slot)
|
|
2262
|
+
facts, lastBuilds and build history. decision is "local", "hosted",
|
|
2263
|
+
"waited-locally" (the local run actually waited for a device slot) or "eas"
|
|
2264
|
+
(remote.easFallback put it on an EAS Simulator, reported like --remote eas). The same
|
|
2187
2265
|
optional devicePlacement appears on the status device entry for each slot.
|
|
2188
2266
|
Hosted host facts include selected: "auto" or the named machine, and reason
|
|
2189
2267
|
for automatic placement. Plain status prints (auto: <reason>) after the host.
|
|
@@ -2530,9 +2608,15 @@ then 8081, without probing or reserving; an invalid pin still refuses.
|
|
|
2530
2608
|
|
|
2531
2609
|
New allocations scan TCP ports 8900-8999. They skip registry reservations
|
|
2532
2610
|
and existing listeners, announcing occupied ports and upward retries on
|
|
2533
|
-
stderr.
|
|
2534
|
-
|
|
2535
|
-
|
|
2611
|
+
stderr. All 100 ports occupied or reserved is a refusal; stop or release
|
|
2612
|
+
unused allocations in their owning workspaces.
|
|
2613
|
+
|
|
2614
|
+
Metro allocation (from 8082 up) and named allocation find listeners in the
|
|
2615
|
+
native TCP table: netstat on macOS and Windows, /proc/net on Linux. When that
|
|
2616
|
+
table is denied, empty or unreadable, as in some sandboxes, each candidate is
|
|
2617
|
+
checked with an lsof listener scan and connects to 127.0.0.1 and ::1 instead.
|
|
2618
|
+
Allocation refuses only when none of them can answer: start refuses with
|
|
2619
|
+
STIM_PORT_INSPECTION_FAILED, and ports get prints the same message.
|
|
2536
2620
|
|
|
2537
2621
|
The machine registry, under STIM_HOME, serializes allocation and cleanup.
|
|
2538
2622
|
The workspace is the nearest package.json directory, resolved through
|
|
@@ -2555,6 +2639,7 @@ ports lists named labels and ports, plus Metro marked managed.
|
|
|
2555
2639
|
ports stop [label] kills TCP listeners on those named ports and releases
|
|
2556
2640
|
the allocations. It sends SIGTERM, waits two seconds, then SIGKILL if needed;
|
|
2557
2641
|
on Windows it terminates the listener's process tree with taskkill.
|
|
2642
|
+
Stopping listeners requires lsof on macOS and Linux, or netstat on Windows.
|
|
2558
2643
|
It prints the PID and command (the image name on Windows) for each stopped
|
|
2559
2644
|
process. The listener's cwd can be anywhere: the named reservation is
|
|
2560
2645
|
permission to stop that listener.
|
|
@@ -2646,7 +2731,7 @@ FLAGS
|
|
|
2646
2731
|
--slot <name> only this slot's records plus, once it has launched, the
|
|
2647
2732
|
shared untagged Metro and app client records (not the web
|
|
2648
2733
|
page's); default also includes untagged legacy records
|
|
2649
|
-
--source <s...> metro, client, device, build, agent, maintenance (one or more), or all.
|
|
2734
|
+
--source <s...> metro, client, device, build, agent, maintenance, placement (one or more), or all.
|
|
2650
2735
|
An unknown value is REJECTED rather than quietly matching
|
|
2651
2736
|
nothing.
|
|
2652
2737
|
--level <l> minimum level: debug, info, warn, error, fatal
|
|
@@ -2783,6 +2868,12 @@ THE RECORD
|
|
|
2783
2868
|
event the producer's own event name (bundle_build_done, client_log, ...)
|
|
2784
2869
|
stack frames of { file, line, column, fn }, passed through as reported
|
|
2785
2870
|
marker true on the records that close an error window
|
|
2871
|
+
runId the id of the stim invocation that wrote the record: STIM_RUN_ID
|
|
2872
|
+
when set to a valid id (letters, digits, . _ -, at most 64),
|
|
2873
|
+
else generated per run. Processes a command starts, such as the
|
|
2874
|
+
Metro supervisor and the collectors, keep the id of the command
|
|
2875
|
+
that started them. Debug records and the hello to stim-server
|
|
2876
|
+
carry it too, and stim-server puts it on its log lines.
|
|
2786
2877
|
deviceTs Android logcat's original epoch milliseconds; ts is aligned to
|
|
2787
2878
|
host time using a bounded clock query at each collector attachment
|
|
2788
2879
|
clockOffsetMs the offset added to deviceTs; absent if the query failed.
|
|
@@ -2800,6 +2891,63 @@ Add --errors to --source maintenance to show only maintenance_failure events;
|
|
|
2800
2891
|
failures before a later launch marker are hidden. Machine passes are reported in
|
|
2801
2892
|
status and maintenance/maintenance.ndjson; this command reads workspace logs.
|
|
2802
2893
|
|
|
2894
|
+
REMOTE REQUEST RECORDS
|
|
2895
|
+
A request to another Mac that fails is a warn record with src: build in the
|
|
2896
|
+
run's build log: remote_connect_failed { host, capability, ms, timeoutMs, msg,
|
|
2897
|
+
code } (could not connect or say hello, e.g. "no reply in time" when the Mac
|
|
2898
|
+
did not answer hello) and remote_request_failed { method, ms, code, msg }.
|
|
2899
|
+
Timings of requests that succeed are debug records (below).
|
|
2900
|
+
stim-server writes to its service log (~/Library/Logs/Stim/<label>.log), with
|
|
2901
|
+
no tokens or tickets: "request failed method=<m> client=<device id> run=<runId>
|
|
2902
|
+
ms=<n> error=<code>" for an error reply (once a minute per client, method and
|
|
2903
|
+
code) and "host_connect ... error=<reason_with_underscores>" when its connection to a hosting
|
|
2904
|
+
Mac fails. With debug.logs on or STIM_DEBUG=1 for the server it logs every
|
|
2905
|
+
request instead: "debug request method=<m> client=<id> run=<runId> ms=<n>
|
|
2906
|
+
slow=true error=<code> whois=<ms> probe=<ms>" (slow means 1000 ms or more;
|
|
2907
|
+
whois and probe are the hello's Tailscale identity lookup and its wait for
|
|
2908
|
+
the host permission probe; a method name that is not a plain dotted lowercase
|
|
2909
|
+
name is logged as unknown)
|
|
2910
|
+
and "debug host_connect host=<mac> ms=<n> connectMs=<n> helloMs=<n>" (reused=true instead of the two
|
|
2911
|
+
timings when an open connection was shared), also as
|
|
2912
|
+
records in STIM_HOME/logs/debug/server.ndjson. Search the run id in both logs.
|
|
2913
|
+
|
|
2914
|
+
DEBUG LOGS
|
|
2915
|
+
With debug.logs on or STIM_DEBUG=1 (stim guide settings), the CLI also appends
|
|
2916
|
+
debug records to STIM_HOME/logs/debug/cli.ndjson, outside any workspace, so
|
|
2917
|
+
stim logs does not show them. Read them with jq:
|
|
2918
|
+
jq -c 'select(.event=="exec" and .ms>1000)' ~/.stim/logs/debug/cli.ndjson
|
|
2919
|
+
Events: run_start, run_end { exit, ms }, exec { program, ms, ok, exit? }, and
|
|
2920
|
+
remote_connect and remote_request { ms, ok, code? } for requests to another
|
|
2921
|
+
Mac. They carry no arguments, tokens or tickets.
|
|
2922
|
+
|
|
2923
|
+
Placement records use src: placement and sit in the run's build log
|
|
2924
|
+
(build-*.ndjson), so a new run replaces the previous run's. A run writes one
|
|
2925
|
+
record per decision it makes (a build that compiles and has a remote Mac to
|
|
2926
|
+
consider, or ios/android --remote auto), with event build_placement,
|
|
2927
|
+
device_placement or placement_fallback. A cache hit, a project with no paired
|
|
2928
|
+
remote Mac and a named --remote target write none:
|
|
2929
|
+
stim logs --source placement
|
|
2930
|
+
stim logs --source placement --json
|
|
2931
|
+
Fields: kind (build or device), platform, settings [{ key, value, from }] with
|
|
2932
|
+
from flag, env, setting or default (remote.build and remote.buildMode for a
|
|
2933
|
+
build, ios.remote or android.remote, plus remote.easFallback when it is on, for
|
|
2934
|
+
a device), candidates [{ machine, code,
|
|
2935
|
+
msg, detail? }], choice { machine, code, msg } with machine local when the run
|
|
2936
|
+
stays on this Mac, and fallback { code, msg, machine? } when a remote Mac that
|
|
2937
|
+
was meant to take the run did not (level warn). The msg is the same text the
|
|
2938
|
+
placement: and build: phase lines print.
|
|
2939
|
+
Candidate codes: accepted, unreachable, busy, disk, load, version-mismatch
|
|
2940
|
+
(detail lists the toolchain parts: xcode, arch, jdk, ...), no-matching-device,
|
|
2941
|
+
declined, memory, no-capacity; with machine eas, why an EAS Simulator was not
|
|
2942
|
+
used: eas-named-slot, eas-local-flags, eas-no-agent-device, eas-no-cli,
|
|
2943
|
+
eas-cli-too-old, eas-session-busy, eas-metro-unreachable, eas-logged-out, eas-not-enabled,
|
|
2944
|
+
eas-unavailable. Choice codes for a build: placed, named,
|
|
2945
|
+
forced, this-mac-busy, this-mac-free, mode-off, local-selected, no-remote-mac,
|
|
2946
|
+
unsupported; fallback codes: no-remote-mac-took-it, offload-failed, fallback.
|
|
2947
|
+
Choice codes for a device: placed, sticky, this-mac-free, no-remote-mac,
|
|
2948
|
+
device-count-unknown, no-host-admits, eas-fallback (machine eas). Machine names appear in these records;
|
|
2949
|
+
they stay in the local logs. These records carry no tokens.
|
|
2950
|
+
|
|
2803
2951
|
WHAT WRITES WHAT
|
|
2804
2952
|
maintenance.ndjson workspace maintenance actions, failures and explaining skips
|
|
2805
2953
|
metro.ndjson the bundler, in both supervisor modes
|
|
@@ -3005,6 +3153,9 @@ Branch on the code, never on the message.`,
|
|
|
3005
3153
|
an unavailable choice. A session that exists stays recorded even if delivery
|
|
3006
3154
|
fails: retry stim ios|android --remote <machine>, or run stim stop to reconcile it.
|
|
3007
3155
|
An unreachable stop keeps the placement; rerun stim stop when the host answers.
|
|
3156
|
+
To see why a host was slow or refused: stim logs --source placement (hosts checked and
|
|
3157
|
+
reason codes), stim logs --source build (remote_connect_failed, remote_request_failed),
|
|
3158
|
+
and the host's stim-server service log, searched for the run id (stim guide logs).
|
|
3008
3159
|
A failed build handoff uses upload instead. If native log queries are unavailable,
|
|
3009
3160
|
logs prints a note on stderr and shows copied records. Update an older stim-server
|
|
3010
3161
|
on the host to enable handoff and native logs (hello features hosted-ios-data
|
|
@@ -3308,9 +3459,14 @@ Branch on the code, never on the message.`,
|
|
|
3308
3459
|
works. See stim guide macos.`
|
|
3309
3460
|
},
|
|
3310
3461
|
STIM_OFFLOAD_REFUSED: {
|
|
3311
|
-
summary: "the selected remote Mac cannot build this app",
|
|
3462
|
+
summary: "the selected remote Mac or automatic build pool cannot build this app",
|
|
3312
3463
|
body: () => `STIM_OFFLOAD_REFUSED
|
|
3313
3464
|
|
|
3465
|
+
Automatic placement also refuses when local is excluded in remote.buildPoolDisabled
|
|
3466
|
+
and no enabled remote can finish this build, or when the build requires a local
|
|
3467
|
+
compiler. Enable local or choose an explicit --remote-build placement. Existing
|
|
3468
|
+
builds are not interrupted and a cache hit needs no compiler.
|
|
3469
|
+
|
|
3314
3470
|
A named --remote-build selection is strict. The message names the machine
|
|
3315
3471
|
and why it cannot take or finish the build: not configured or paired, approval
|
|
3316
3472
|
pending or denied, unreachable or changed pinned identity, incompatible
|
|
@@ -3756,7 +3912,9 @@ so a Debug run on one is wired to a LAN origin instead of localhost.`,
|
|
|
3756
3912
|
physical, hosted, remote, parked and other homes' devices and never deletes.
|
|
3757
3913
|
A failed reclaim is logged and skipped; the run keeps waiting.
|
|
3758
3914
|
Stop an environment (\`stim stop\`), pass a longer \`--wait <seconds>\`,
|
|
3759
|
-
or raise concurrency.maxDevices.
|
|
3915
|
+
or raise concurrency.maxDevices. With --remote auto, remote.machines and
|
|
3916
|
+
the opt-in remote.easFallback (billed EAS Simulator) are tried before this
|
|
3917
|
+
wait; \`stim logs --source placement\` says why neither took the run. Waiting prints holder names and elapsed
|
|
3760
3918
|
time; status JSON exposes build.waitingFor independently of phase.
|
|
3761
3919
|
Stats records capacityWaits for waits and capacityRefusals for this code.
|
|
3762
3920
|
See \`guide lifecycle concurrency\`.`
|
|
@@ -4043,6 +4201,17 @@ captured" (in metro.ndjson, bare RN)
|
|
|
4043
4201
|
printed the last lines of the global workspace logs/supervisor.log above this -- read
|
|
4044
4202
|
them. A cold Metro on a large graph can genuinely need more than the default
|
|
4045
4203
|
60s: re-run with \`--wait 180\`. Otherwise \`stim stop\`, then \`start\`.`
|
|
4204
|
+
},
|
|
4205
|
+
STIM_PORT_INSPECTION_FAILED: {
|
|
4206
|
+
summary: "no free Metro port could be confirmed: netstat, lsof and loopback connects all failed",
|
|
4207
|
+
body: () => `STIM_PORT_INSPECTION_FAILED
|
|
4208
|
+
Stim reads the native TCP listener table (netstat on macOS and Windows,
|
|
4209
|
+
/proc/net on Linux) to find a free Metro port. When that table is denied,
|
|
4210
|
+
empty or unreadable, it checks each candidate with an lsof listener scan and
|
|
4211
|
+
connects to 127.0.0.1 and ::1. This refusal means none of them could answer,
|
|
4212
|
+
usually because a sandbox denies them. Allow Stim to run netstat or lsof, or
|
|
4213
|
+
to connect to loopback, then retry. A metro.port pin skips the scan for
|
|
4214
|
+
Metro. \`stim ports get\` prints the same message.`
|
|
4046
4215
|
},
|
|
4047
4216
|
STIM_SUPERVISOR_EXITED: {
|
|
4048
4217
|
summary: "the dev server failed outright; the quoted supervisor.log tail is the real error",
|
|
@@ -4397,7 +4566,12 @@ not on any remote" (worktree remove)
|
|
|
4397
4566
|
Warm copied ignored Pods from the source checkout, but their Manifest.lock
|
|
4398
4567
|
differs from the tracked Podfile.lock in this worktree. Warm does not change
|
|
4399
4568
|
tracked files. Run the printed pod-install command before building directly.
|
|
4400
|
-
\`stim ios\` detects a mismatch and runs \`pod install\` for you.
|
|
4569
|
+
\`stim ios\` detects a mismatch and runs \`pod install\` for you. A mismatch
|
|
4570
|
+
limited to checksums of podspecs that embed the source checkout's path is
|
|
4571
|
+
resolved by warm itself ("carry moved <dir>/Pods to this checkout's path")
|
|
4572
|
+
and does not print this line. When the locks match but Pods still name the
|
|
4573
|
+
source path and warm cannot move it, it prints "removed <dir>/Pods/Manifest.lock
|
|
4574
|
+
so pod install runs" and \`stim ios\` runs \`pod install\`.
|
|
4401
4575
|
|
|
4402
4576
|
"carry carried <dir>/Pods but there is no <dir>/Podfile.lock"
|
|
4403
4577
|
Warm copied Pods but the destination has no Podfile.lock. Follow the printed
|
|
@@ -4523,6 +4697,22 @@ not on any remote" (worktree remove)
|
|
|
4523
4697
|
Expo build-cache provider would each use a different store. Set the named
|
|
4524
4698
|
variable to an absolute path, or unset it to use the default. Metro and the
|
|
4525
4699
|
cache provider, which cannot refuse, ignore a relative value with a warning.`
|
|
4700
|
+
},
|
|
4701
|
+
STIM_WORKER_FAILED: {
|
|
4702
|
+
summary: "a programmatic API worker exited without returning an operation result",
|
|
4703
|
+
body: () => `STIM_WORKER_FAILED (programmatic API)
|
|
4704
|
+
The worker process ended without a structured result. StimError.details
|
|
4705
|
+
includes its final stderr output. Inspect that output and call diagnostics()
|
|
4706
|
+
for existing workspace logs, then stop() with a fresh signal to clean up
|
|
4707
|
+
any resources created before the failure. See stim guide api.`
|
|
4708
|
+
},
|
|
4709
|
+
STIM_RUN_FAILED: {
|
|
4710
|
+
summary: "a programmatic operation failed without a more specific Stim code",
|
|
4711
|
+
body: () => `STIM_RUN_FAILED (programmatic API)
|
|
4712
|
+
The underlying operation threw an error without a specific code. Its
|
|
4713
|
+
message and workspace log path are preserved in StimError. Read diagnostics()
|
|
4714
|
+
and call stop() in cleanup, including after a partial run. If the error is
|
|
4715
|
+
unexpected, report its message and logs at github.com/appandflow/stim/issues.`
|
|
4526
4716
|
},
|
|
4527
4717
|
STIM_NODE_UNSUPPORTED: {
|
|
4528
4718
|
summary: "stim or stim-server started on a Node older than 22.12.0, often a project pin",
|
|
@@ -4806,6 +4996,46 @@ WAITING FOR A CHANGE
|
|
|
4806
4996
|
still running, which it notices at the next change.
|
|
4807
4997
|
Without --json it reprints the human view on change.`,
|
|
4808
4998
|
sections: {
|
|
4999
|
+
ci: {
|
|
5000
|
+
summary: "Run an app and tests through @stim-cli/ci with diagnostics and scoped cleanup",
|
|
5001
|
+
body: () => `CONTINUOUS INTEGRATION
|
|
5002
|
+
|
|
5003
|
+
Install app dependencies and platform tools first. Use a dedicated checkout:
|
|
5004
|
+
cleanup stops that workspace, including resources created before a build fails.
|
|
5005
|
+
|
|
5006
|
+
npx --yes --package @stim-cli/ci stim-ci run --platform ios --project ./app --timeout 1800 -- pnpm test:e2e
|
|
5007
|
+
|
|
5008
|
+
Or install npm install --global @stim-cli/ci and use stim-ci directly.
|
|
5009
|
+
Platforms: ios, android, macos, web. Everything after -- is argv, not a shell.
|
|
5010
|
+
--artifacts selects the result directory (a fresh temporary directory by default).
|
|
5011
|
+
An explicit artifacts directory must be empty; use a new directory per run.
|
|
5012
|
+
Stdout is one JSON result; progress and test output go to stderr.
|
|
5013
|
+
|
|
5014
|
+
Tests receive STIM_CI_PLATFORM, STIM_CI_DEVICE_ID, STIM_CI_APP_ID,
|
|
5015
|
+
STIM_CI_METRO_PORT, STIM_CI_ARTIFACTS_DIR and STIM_CI_RUN_RESULT. The last is
|
|
5016
|
+
run.json with the exact public API result. Use that target and check app
|
|
5017
|
+
readiness; launched can still be bundling or unverified.
|
|
5018
|
+
|
|
5019
|
+
result.json preserves the test exit code even if diagnostics or cleanup fail.
|
|
5020
|
+
Passing tests with failed cleanup return 1; timeout returns 124; cancellation
|
|
5021
|
+
returns 130. Leave 70 seconds before the job's hard timeout for diagnostics and
|
|
5022
|
+
stop. SIGKILL and runner loss cannot run cleanup. Stop shuts down owned devices;
|
|
5023
|
+
it never deletes them.
|
|
5024
|
+
|
|
5025
|
+
GitHub-hosted jobs default to $RUNNER_TEMP/stim-ci/home and
|
|
5026
|
+
$RUNNER_TEMP/stim-ci/build-cache, reused between steps in the job. Detection
|
|
5027
|
+
requires GITHUB_ACTIONS=true, RUNNER_ENVIRONMENT=github-hosted and RUNNER_TEMP.
|
|
5028
|
+
An explicit --home or STIM_HOME keeps the selected home and its normal cache
|
|
5029
|
+
configuration; --build-cache or STIM_BUILD_CACHE overrides the cache path.
|
|
5030
|
+
Self-hosted runners and local runs keep normal Stim configuration. Other
|
|
5031
|
+
exclusive disposable providers can set their job-local paths explicitly.
|
|
5032
|
+
CI=true alone does not change defaults, ownership or coordination. Persist only
|
|
5033
|
+
cache artifacts, not state, claims or device ledgers. Independent homes must
|
|
5034
|
+
not share a writable filesystem cache because its claims live in the home.
|
|
5035
|
+
|
|
5036
|
+
The programmatic runner is import { runCI } from '@stim-cli/ci'. See its package
|
|
5037
|
+
README for result types, cancellation, cache policy, and coordination findings.`
|
|
5038
|
+
},
|
|
4809
5039
|
"hosted-android": {
|
|
4810
5040
|
summary: "Android on a named approved Mac: strict placement, private Metro, status and stop",
|
|
4811
5041
|
body: () => `ANDROID ON A HOSTING MAC
|
|
@@ -4972,7 +5202,10 @@ there is a free concurrency.maxDevices slot (0 means unlimited), no device
|
|
|
4972
5202
|
waiter ahead, normal host memory pressure, no budget shortfall,
|
|
4973
5203
|
and 5-minute load per core below server.maxLoadPerCore. Without remote.machines,
|
|
4974
5204
|
auto is local. An unknown local device count also stays local; boot admission
|
|
4975
|
-
still decides. The default without a flag or setting stays local.
|
|
5205
|
+
still decides. The default without a flag or setting stays local. remote.devicePoolDisabled excludes
|
|
5206
|
+
members from new automatic placement only; an excluded local member is never a
|
|
5207
|
+
fallback. Existing sessions keep their owner and named placement bypasses the pool.
|
|
5208
|
+
See stim guide settings for separate build/device membership controls.
|
|
4976
5209
|
|
|
4977
5210
|
Otherwise Stim asks every approved remote.machines host in parallel, with a
|
|
4978
5211
|
3-second probe timeout and this run's model/runtime or image/profile selectors.
|
|
@@ -4983,9 +5216,26 @@ Hosts rank by the build preference (flag > STIM_REMOTE_BUILD > remote.build)
|
|
|
4983
5216
|
when it names a machine, then lowest load, most free memory and remote.machines order. Automatic build
|
|
4984
5217
|
offload resolves later, after the device architecture is known.
|
|
4985
5218
|
|
|
4986
|
-
When no host admits
|
|
5219
|
+
When no host admits and this Mac is at its concurrency.maxDevices cap (or runs
|
|
5220
|
+
are queued ahead), remote.easFallback (default false; EAS Simulator is billed)
|
|
5221
|
+
runs the device on an EAS Simulator exactly as --remote eas would: the build
|
|
5222
|
+
stays here, nothing starts an EAS cloud build, stop ends the session, and
|
|
5223
|
+
status reports it under remoteDevices. A busy Mac with a free slot never uses
|
|
5224
|
+
it. Before choosing it Stim checks, without starting a session, that eas-cli
|
|
5225
|
+
has simulator commands (and 22.2.0+ for --device-type), eas
|
|
5226
|
+
simulator:availability says this project's account can use it, agent-device
|
|
5227
|
+
is on PATH, the slot is default, no --runtime, --system-image or
|
|
5228
|
+
--device-profile flag is given, no EAS Simulator session of this workspace runs
|
|
5229
|
+
another platform or model, and a Debug run's Metro is reachable (not
|
|
5230
|
+
metro.tunnel off; on an Expo tunnel, run stim start --remote first). A recorded
|
|
5231
|
+
EAS session is not sticky: once this Mac has room, auto runs here and the
|
|
5232
|
+
session bills until stim stop. If any check fails, or the
|
|
5233
|
+
setting is off, auto runs here and may wait in the existing FIFO device
|
|
4987
5234
|
slot queue; --no-wait and --wait 0 refuse with STIM_AT_CAPACITY. The placement
|
|
4988
|
-
line explains the decision
|
|
5235
|
+
line explains the decision, the skipped hosts and why EAS was not used
|
|
5236
|
+
(machine "eas"). JSON progress goes to stderr.
|
|
5237
|
+
The same decision, with a reason code per host, is a src: placement record in
|
|
5238
|
+
stim logs (stim guide logs).
|
|
4989
5239
|
A recorded hosted session wins over load; a live local owned slot stays here.
|
|
4990
5240
|
A stopped recorded session places again; unreachable or unknown sessions refuse.
|
|
4991
5241
|
|
|
@@ -5736,8 +5986,17 @@ PREDICTING THE NEXT BUILD (--plan)
|
|
|
5736
5986
|
An Android plan reads the ABI from the emulator the slot records, or from
|
|
5737
5987
|
the system image a new one would use. A plan refuses --device, --remote,
|
|
5738
5988
|
--wait, --no-wait, --no-metro-check and --simulator-app with STIM_BAD_ARG.
|
|
5739
|
-
|
|
5740
|
-
|
|
5989
|
+
With ios.remote or android.remote set to a Mac name, a plan reads the
|
|
5990
|
+
architecture or ABI from that Mac's device offer, the question a run asks
|
|
5991
|
+
before it reserves anything. With auto it repeats the placement decision a
|
|
5992
|
+
run would make now and reports where in the payload's placement field, for
|
|
5993
|
+
example "this Mac; auto may use janics-mac-mini". When the plan stays on
|
|
5994
|
+
this Mac and a listed Mac would build for another architecture, placement
|
|
5995
|
+
says so, since that Mac's key differs. The plan only predicts the key: a
|
|
5996
|
+
run reads the architecture of the device it actually gets. A Mac that does
|
|
5997
|
+
not answer, ios.remote or android.remote set to eas or proxy, or auto that
|
|
5998
|
+
would use an EAS Simulator now (remote.easFallback), refuses with
|
|
5999
|
+
STIM_BAD_ARG. Without --eas-profile an Android plan also
|
|
5741
6000
|
refuses the experimental compiler CAS with STIM_BAD_ARG, and refuses with
|
|
5742
6001
|
STIM_NO_DEVICE when no system image is installed.
|
|
5743
6002
|
|
|
@@ -6493,6 +6752,24 @@ OPT-IN CONCURRENCY LIMITS (UNLIMITED BY DEFAULT)
|
|
|
6493
6752
|
overlapping a nested destination worktree or below a symlink ancestor.
|
|
6494
6753
|
Tracked .idea settings come from Git and stay untouched by warm.
|
|
6495
6754
|
|
|
6755
|
+
A carried ios/Pods still names the source checkout's path in its generated
|
|
6756
|
+
files and in the checksum of a podspec that embeds that path (the
|
|
6757
|
+
precompiled ExpoModulesCore). When ios/Podfile.lock and the carried
|
|
6758
|
+
ios/Pods/Manifest.lock differ only by those checksums, warm rewrites the
|
|
6759
|
+
source path to the worktree's path in Pods/Target Support Files,
|
|
6760
|
+
Pods.xcodeproj, Local Podspecs and absolute symlinks, then makes
|
|
6761
|
+
Manifest.lock equal Podfile.lock, so the first \`stim ios\` skips
|
|
6762
|
+
\`pod install\`. Any other difference, a missing podspec, or a podspec
|
|
6763
|
+
that does not embed the source path, any other file in Pods that names
|
|
6764
|
+
the source path, or carried node_modules that do not match the worktree's
|
|
6765
|
+
lockfile leaves Pods as copied, and \`pod install\` runs. Warm does not edit Podfile.lock.
|
|
6766
|
+
|
|
6767
|
+
When the two locks already match but the copied Pods still name the source
|
|
6768
|
+
path, warm applies the same rewrite and scan so builds do not read the
|
|
6769
|
+
source checkout's files (entitlements, Podfile.properties.json). If it
|
|
6770
|
+
cannot (stale node_modules, a leftover path, an error), warm deletes
|
|
6771
|
+
ios/Pods/Manifest.lock so \`stim ios\` runs \`pod install\`.
|
|
6772
|
+
|
|
6496
6773
|
Other generated state stays eligible: .gradle, .cxx, *.tsbuildinfo, build
|
|
6497
6774
|
directories, and embedded JavaScript need project-specific decisions about
|
|
6498
6775
|
regeneration. Native intermediates can record the source checkout's paths;
|
|
@@ -7042,35 +7319,66 @@ $STIM_HOME/archive by default. See stim guide cleanup archive.
|
|
|
7042
7319
|
|
|
7043
7320
|
MAINTENANCE
|
|
7044
7321
|
|
|
7045
|
-
Automatic maintenance
|
|
7046
|
-
|
|
7047
|
-
|
|
7048
|
-
|
|
7049
|
-
or
|
|
7322
|
+
Automatic maintenance has three modes. on (the default) runs the disk actions
|
|
7323
|
+
below through gc's own removal code and logs each one. report measures, plans
|
|
7324
|
+
and logs, and never deletes anything. off disables it. It is off in CI and
|
|
7325
|
+
scoped STIM_HOME homes unless STIM_MAINTENANCE is explicit, so a scratch home
|
|
7326
|
+
or CI run never deletes on its own.
|
|
7327
|
+
Commands trigger a detached pass when a check is due: disk and memory pressure
|
|
7328
|
+
(every minute), directory sizes (hourly), finished worktrees (every 15
|
|
7329
|
+
minutes) and the age sweep (daily). guide, settings and help do not trigger
|
|
7050
7330
|
it; gc --delete also skips the hook. status --watch also triggers checks.
|
|
7051
|
-
Size checks defer under high load. Attempts back off for at
|
|
7052
|
-
Measured directories are workspace build outputs and Stim's
|
|
7053
|
-
Metro, ccache, Swift compilation and registered caches.
|
|
7054
|
-
free disk on the Stim home, projects and worker root
|
|
7055
|
-
memory signal is the sysctl pressure level, with no
|
|
7056
|
-
Other platforms use os.freemem(); macOS never falls
|
|
7057
|
-
Memory pressure is recorded only;
|
|
7331
|
+
Size, worktree and sweep checks defer under high load. Attempts back off for at
|
|
7332
|
+
least one minute. Measured directories are workspace build outputs and Stim's
|
|
7333
|
+
shared native, Metro, ccache, Swift compilation and registered caches.
|
|
7334
|
+
Pressure checks read free disk on the Stim home, projects and worker root
|
|
7335
|
+
volumes. On macOS the memory signal is the sysctl pressure level, with no
|
|
7336
|
+
signal if sysctl fails. Other platforms use os.freemem(); macOS never falls
|
|
7337
|
+
back to it. Memory pressure is recorded only; stopping devices, dev servers
|
|
7338
|
+
and helpers is not automatic.
|
|
7339
|
+
|
|
7340
|
+
In on mode a pass runs, cheapest to rebuild first:
|
|
7341
|
+
1. orphaned workspace directories whose project is gone, and registered
|
|
7342
|
+
caches whose directory no longer exists
|
|
7343
|
+
2. build outputs of idle workspaces, over maintenance.workspaceOutputsMaxGb,
|
|
7344
|
+
below the free-disk floor, or unused for maintenance.olderThanDays
|
|
7345
|
+
3. build-cache and Metro entries, least recently used first, down to
|
|
7346
|
+
maintenance.capTargetPercent of caches.buildCacheMaxGb or
|
|
7347
|
+
caches.metroCacheMaxGb, and entries unused for olderThanDays
|
|
7348
|
+
4. the Swift compilation cache, emptied whole, only below
|
|
7349
|
+
budget.hardFloorDiskGb and with no build lock or slot held
|
|
7350
|
+
5. linked worktrees whose branch or pull request finished (gc's finished
|
|
7351
|
+
worktree rules and gc.worktreeGraceMinutes; maintenance.removeFinishedWorktrees)
|
|
7352
|
+
Disk-driven steps stop once free space is back above the floor. Kept for
|
|
7353
|
+
every pass: a workspace that is in use, holds a lock, was used within
|
|
7354
|
+
maintenance.protectRecentHours, is pinned with maintenance.keep, or is the
|
|
7355
|
+
workspace of the command that started the pass; cache entries used within
|
|
7356
|
+
protectRecentHours, named by a project's last builds, a parked device or a
|
|
7357
|
+
live build lock, or in a Metro store with a running or unverifiable dev
|
|
7358
|
+
server. Anything the pass cannot verify is kept. A pass never shuts down idle devices or dev servers or stops watchman; removing a
|
|
7359
|
+
finished worktree or orphaned directory does tear down that workspace's own
|
|
7360
|
+
dev server, owned devices and Chrome profile, as gc --delete does. ccache evicts by itself under
|
|
7361
|
+
caches.ccacheMaxGb.
|
|
7058
7362
|
|
|
7059
7363
|
stim status last checks, plan and running pass
|
|
7060
7364
|
stim status --json maintenance observations and recent records
|
|
7061
7365
|
stim logs --source maintenance this workspace's maintenance records
|
|
7062
7366
|
stim gc live pressure and cached-size preview
|
|
7063
|
-
stim settings set maintenance.mode off
|
|
7367
|
+
stim settings set maintenance.mode off turn automatic cleanup off
|
|
7368
|
+
stim settings set maintenance.mode report plan and log, delete nothing
|
|
7064
7369
|
|
|
7065
7370
|
The machine log is $STIM_HOME/maintenance/maintenance.ndjson, rotated at
|
|
7066
7371
|
maintenance.logMaxMb with the old generation retained for
|
|
7067
7372
|
maintenance.logRetentionDays. Child crashes use maintenance/child.log.
|
|
7068
|
-
|
|
7069
|
-
|
|
7373
|
+
In report mode actions and explaining skips are logged only when newly
|
|
7374
|
+
planned; in on mode every action taken, failure and explaining skip is logged
|
|
7375
|
+
with its bytes, and a pass record carries the bytes freed. Removal of a
|
|
7376
|
+
worktree or orphaned directory is logged to the machine log only.
|
|
7070
7377
|
maintenance.logChecks enables debug observations; default false.
|
|
7071
7378
|
status and doctor report invalid settings and unresolved claims with a removal
|
|
7072
7379
|
command to run only after confirming the holder is gone. Invalid cache caps
|
|
7073
7380
|
fall back to their own defaults; invalid maintenance settings disable passes.
|
|
7381
|
+
Pin a workspace with \`stim settings set maintenance.keep true\` in its project.
|
|
7074
7382
|
The run claim serializes passes with gc --delete; gc refuses a held claim
|
|
7075
7383
|
with the holder and recovery guidance instead of waiting.
|
|
7076
7384
|
|
|
@@ -7661,7 +7969,23 @@ THE ONE CASE GC WILL NOT REAP
|
|
|
7661
7969
|
|
|
7662
7970
|
--older-than does not apply to these kinds and is refused with
|
|
7663
7971
|
STIM_BAD_ARG. With STIM_HOME set, gc skips them: they are machine-global.
|
|
7664
|
-
\`stim doctor\` notes a watchman footprint over 2 GiB
|
|
7972
|
+
\`stim doctor\` notes a watchman footprint over 2 GiB.
|
|
7973
|
+
|
|
7974
|
+
A checkout that holds its linked worktrees inside it, such as
|
|
7975
|
+
.worktrees/<name>, makes a watchman root there crawl every worktree's files,
|
|
7976
|
+
node_modules and build output. \`stim doctor\` reports each such checkout
|
|
7977
|
+
whose .watchmanconfig ignore_dirs does not exclude them, as
|
|
7978
|
+
${WATCHMAN_NESTED_WORKTREES}, and says when watchman watches it now.
|
|
7979
|
+
\`stim doctor --fix\` merges the worktrees' shared parent (.worktrees) when
|
|
7980
|
+
git tracks nothing in it, otherwise each worktree path, into that
|
|
7981
|
+
checkout's .watchmanconfig and keeps every other key; it refuses a file
|
|
7982
|
+
that is not a JSON object or whose ignore_dirs is not an array. Watchman
|
|
7983
|
+
matches entries literally: \`.worktrees/\` or \`./.worktrees\` ignores
|
|
7984
|
+
nothing. Commit the file. Watchman reads ignore_dirs only from a root's
|
|
7985
|
+
.watchmanconfig, never from a global config, and only when it adds the
|
|
7986
|
+
root, so an existing root needs \`watchman watch-del <root>\` before the
|
|
7987
|
+
ignore applies. Doctor never runs watch-del or shutdown-server; a watch-del
|
|
7988
|
+
plus a watchman restart frees the memory now.`
|
|
7665
7989
|
},
|
|
7666
7990
|
disk: {
|
|
7667
7991
|
summary: "disk usage, workspace build outputs, logs and device recordings, AVD and build-log sizes, the data partition, trimming the shared caches",
|
|
@@ -7918,7 +8242,8 @@ KEYS STIM READS
|
|
|
7918
8242
|
ios.remote "proxy", "eas", or a named approved Mac from
|
|
7919
8243
|
remote.machines, with the same meaning as --remote.
|
|
7920
8244
|
"auto" places on an approved Mac when this Mac is full or
|
|
7921
|
-
busy. Unset runs here
|
|
8245
|
+
busy. Unset runs here; "local" runs here even when a
|
|
8246
|
+
lower layer says otherwise. See lifecycle hosted-ios.
|
|
7922
8247
|
ios.simslimProfile a SimSlim JSON profile under the app directory,
|
|
7923
8248
|
at most 64 KiB. Install the
|
|
7924
8249
|
external tool once with
|
|
@@ -8060,8 +8385,23 @@ ${ANDROID_AVD_CONFIG_HELP.map((line) => ` ${line}`).joi
|
|
|
8060
8385
|
means the debug keystore's fixed "android".
|
|
8061
8386
|
android.remote "proxy", "eas", or a named approved Mac in
|
|
8062
8387
|
remote.machines; "auto" places on an approved Mac when
|
|
8063
|
-
this Mac is full or busy. Unset runs here
|
|
8388
|
+
this Mac is full or busy. Unset runs here; "local"
|
|
8389
|
+
runs here even when a lower layer says otherwise.
|
|
8064
8390
|
See lifecycle hosted-android.
|
|
8391
|
+
remote.easFallback true lets "auto" (ios.remote, android.remote or
|
|
8392
|
+
--remote auto) run the simulator or emulator on a
|
|
8393
|
+
billed EAS Simulator when this Mac is at its
|
|
8394
|
+
concurrency.maxDevices cap (or has runs queued) and no
|
|
8395
|
+
remote.machines Mac takes the run, instead of waiting
|
|
8396
|
+
or refusing with STIM_AT_CAPACITY. Default false. A
|
|
8397
|
+
busy Mac with a free slot never uses it. Stim checks
|
|
8398
|
+
first that the run could use --remote eas (eas-cli
|
|
8399
|
+
with simulator commands, eas simulator:availability,
|
|
8400
|
+
agent-device, the default slot, a reachable Metro);
|
|
8401
|
+
otherwise it waits or refuses as before. Machine or
|
|
8402
|
+
project scope; a committed value opts in everyone who
|
|
8403
|
+
runs the app. Only the user enables it. See lifecycle
|
|
8404
|
+
hosted-ios.
|
|
8065
8405
|
metro.tunnel selects how a remote device reaches this workspace's
|
|
8066
8406
|
Metro after remote intent exists. Plain \`start\` stays
|
|
8067
8407
|
local. For Expo and bare React Native, "auto" (default)
|
|
@@ -8267,12 +8607,26 @@ overrides the file:
|
|
|
8267
8607
|
budget is off. A value of the wrong shape refuses start, ios and android with
|
|
8268
8608
|
STIM_BAD_ARG. See \`guide lifecycle budget\` for what each limit reclaims.
|
|
8269
8609
|
|
|
8610
|
+
DEBUG LOGS ARE MACHINE-LEVEL AND OFF BY DEFAULT
|
|
8611
|
+
debug.logs STIM_DEBUG default false. STIM_DEBUG=1 (or 0) overrides the setting
|
|
8612
|
+
for one command. While on, the CLI writes debug records to
|
|
8613
|
+
STIM_HOME/logs/debug/cli.ndjson (run_start, run_end, exec with the program name,
|
|
8614
|
+
duration and exit status but never arguments, remote_connect and
|
|
8615
|
+
remote_request with durations and codes); stim-server writes server.ndjson there
|
|
8616
|
+
and logs every request with its timings to its service log. The file rotates at about 8 MiB and keeps one previous
|
|
8617
|
+
generation, and nothing is sent anywhere. Keys named like a secret are redacted.
|
|
8618
|
+
stim settings set debug.logs true --scope machine
|
|
8619
|
+
See \`guide logs\` for reading the files.
|
|
8620
|
+
|
|
8270
8621
|
AUTOMATIC MAINTENANCE IS MACHINE-LEVEL
|
|
8271
|
-
|
|
8622
|
+
maintenance.mode on (the default) removes what gc would under the caps and
|
|
8623
|
+
floors below; report measures and plans and deletes nothing; off disables it.
|
|
8272
8624
|
STIM_HOME and CI make the mode off unless STIM_MAINTENANCE is set.
|
|
8625
|
+
maintenance.keep is a project setting: pin a workspace so a pass never clears
|
|
8626
|
+
its build outputs or removes its worktree.
|
|
8273
8627
|
See \`guide cleanup\` for checks, plans and log paths.
|
|
8274
8628
|
|
|
8275
|
-
${SETTINGS.filter((setting) => setting.key.startsWith("maintenance.") || /^caches\..*MaxGb$/.test(setting.key)).map((setting) => ` ${setting.key} ${setting.env} ${setting.default === void 0 ? "unset" : `default ${setting.default}`}
|
|
8629
|
+
${SETTINGS.filter((setting) => setting.key.startsWith("maintenance.") || /^caches\..*MaxGb$/.test(setting.key)).map((setting) => ` ${setting.key} ${setting.env ?? "project setting"} ${setting.default === void 0 ? "unset" : `default ${setting.default}`}
|
|
8276
8630
|
${setting.description}`).join("\n")}
|
|
8277
8631
|
|
|
8278
8632
|
Maintenance uses the sysctl pressure level on macOS, with no memory signal
|
|
@@ -8296,8 +8650,9 @@ To show the booted simulator in Stim Desktop and open no simulator window:
|
|
|
8296
8650
|
|
|
8297
8651
|
{ "iosSimulatorApp": "stim-desktop" }
|
|
8298
8652
|
|
|
8299
|
-
Stim Desktop
|
|
8300
|
-
|
|
8653
|
+
Stim Desktop keeps the current page and shows a launch card. Clicking Show
|
|
8654
|
+
opens the workspace that owns the simulator and focuses that device.
|
|
8655
|
+
It only displays the simulator; it never boots or shuts it down.
|
|
8301
8656
|
When Stim Desktop is not running, Stim starts it without the command's
|
|
8302
8657
|
\`STIM_HOME\`, so it reads the same Stim home as when you open it yourself.
|
|
8303
8658
|
It shows only devices from that home: under another \`STIM_HOME\`, pick
|
|
@@ -8326,7 +8681,8 @@ headlessly and show it in Stim Desktop:
|
|
|
8326
8681
|
{ "androidEmulatorApp": "stim-desktop" }
|
|
8327
8682
|
|
|
8328
8683
|
Stim then starts the emulator with \`-no-window -gpu host\` and opens
|
|
8329
|
-
\`stim-desktop://open?serial=<serial>\` in the background. Stim Desktop
|
|
8684
|
+
\`stim-desktop://open?serial=<serial>\` in the background. Stim Desktop shows a
|
|
8685
|
+
launch card and opens the emulator only when Show is clicked. It reads
|
|
8330
8686
|
frames and sends input over the emulator's gRPC endpoint. The setting applies
|
|
8331
8687
|
only when Stim boots the emulator: one that is already running keeps its
|
|
8332
8688
|
current display until it next boots, and physical devices are unaffected.
|
|
@@ -8409,6 +8765,32 @@ simulator sessions on, by MagicDNS name with an optional serve port (default
|
|
|
8409
8765
|
stim settings set remote.machines '["janics-mac-mini"]'
|
|
8410
8766
|
stim doctor --fix
|
|
8411
8767
|
|
|
8768
|
+
Automatic membership is separate from approval. In Desktop Settings > Remote Macs,
|
|
8769
|
+
use Automatic builds and Automatic simulators for this Mac or a configured remote.
|
|
8770
|
+
The equivalent machine settings list the excluded members; both default to []:
|
|
8771
|
+
|
|
8772
|
+
stim settings set remote.buildPoolDisabled '["local"]'
|
|
8773
|
+
stim settings set remote.devicePoolDisabled '["janics-mac-mini"]'
|
|
8774
|
+
stim settings unset remote.buildPoolDisabled
|
|
8775
|
+
|
|
8776
|
+
Use local for this Mac and exact remote.machines entries for remotes, including
|
|
8777
|
+
case and port. Names are not trimmed; unmatched entries exclude nothing. Copy the
|
|
8778
|
+
configured name or use Desktop's switches. Each pool
|
|
8779
|
+
must retain local or at least one configured remote already approved for that role.
|
|
8780
|
+
Offline approved members count as configured members, but placement still requires
|
|
8781
|
+
an available compatible host. Settings refuses removing the last member, including
|
|
8782
|
+
removing it from remote.machines. Disabling does not unpair a machine or stop a run.
|
|
8783
|
+
|
|
8784
|
+
These settings apply only to new automatic work requested by this Mac. They do not
|
|
8785
|
+
change which work other requesters send to a host. Named placement and --remote-build
|
|
8786
|
+
local bypass membership; the default device placement without --remote auto remains
|
|
8787
|
+
local. Existing local and hosted device sessions keep their owner. Cache hits remain
|
|
8788
|
+
usable without compiling. If local is excluded, automatic placement cannot fall back
|
|
8789
|
+
to a local compile or boot; unavailable hosts and unsupported offloads refuse. Excluding
|
|
8790
|
+
local alone does not trigger billed EAS fallback; an existing explicit EAS opt-in still
|
|
8791
|
+
requires the physical device cap or queue condition. Restore membership before retrying or choose
|
|
8792
|
+
an explicit placement. Build and simulator memberships are independent.
|
|
8793
|
+
|
|
8412
8794
|
A remote Mac is used for a capability only after it grants that approval. Build
|
|
8413
8795
|
and device-host approvals are separate: a Mac in the list that never granted
|
|
8414
8796
|
one is not an error, it is not used for that capability, and doctor reports
|
|
@@ -8418,7 +8800,7 @@ DEVICE HOSTING
|
|
|
8418
8800
|
Only \`doctor --fix\` asks for device-host access. A person on that Mac
|
|
8419
8801
|
approves the printed id with \`stim-server devices grant <id> --device-host\`.
|
|
8420
8802
|
A person can also run \`stim-server setup\` on the worker: one node, one ticket,
|
|
8421
|
-
one expiry, at most one request per capability, with per-grant
|
|
8803
|
+
one expiry, at most one request per capability, with a per-grant Y/n question (Enter approves) in a
|
|
8422
8804
|
terminal or \`--yes\` otherwise. Agents never run \`stim-server setup\` or
|
|
8423
8805
|
approve requests. Setup never changes TCC or enables Funnel; an SSH-driven
|
|
8424
8806
|
run is not offered. Without a terminal or --yes, setup refuses before installing
|
|
@@ -8500,7 +8882,7 @@ value is unset. Trimmed auto/local are case-insensitive. Machine names match
|
|
|
8500
8882
|
configured names case-insensitively, with port 7443 when omitted; reports use
|
|
8501
8883
|
the configured entry.
|
|
8502
8884
|
|
|
8503
|
-
auto follows remote.buildMode
|
|
8885
|
+
auto follows remote.buildMode, considering only enabled automatic pool members
|
|
8504
8886
|
local builds only on this Mac for this invocation
|
|
8505
8887
|
name requires a matching entry in remote.machines, already paired and
|
|
8506
8888
|
approved for builds; ignores remote.buildMode and this Mac's load/slot gating
|
|
@@ -8539,7 +8921,7 @@ emulator debug build or a stim macos SwiftPM Debug build compiles:
|
|
|
8539
8921
|
Mac's. A Mac too old to report its load counts only while every
|
|
8540
8922
|
slot here is busy.
|
|
8541
8923
|
force on a remote Mac whenever one accepts it
|
|
8542
|
-
off
|
|
8924
|
+
off here when local remains enabled in the automatic build pool
|
|
8543
8925
|
|
|
8544
8926
|
Load per core is the 5-minute load average divided by the CPU count; a Mac's
|
|
8545
8927
|
native builds are its Stim runs in prebuild, pods or compile on that Mac, not
|
|
@@ -8563,10 +8945,12 @@ preferring the one that already holds this repository, then the least loaded,
|
|
|
8563
8945
|
and moves to the next one in that order when a machine that offered fails the
|
|
8564
8946
|
sync or refuses to start the build.
|
|
8565
8947
|
For iOS the machine's Xcode and simulator SDK must match, and it needs an
|
|
8566
|
-
iPhone simulator on the target runtime. Its CocoaPods must match too, unless
|
|
8948
|
+
iPhone simulator on the target runtime. Its project-selected CocoaPods must match too, unless
|
|
8567
8949
|
the app's Gemfile.lock pins CocoaPods: both Macs then run that version through
|
|
8568
8950
|
bundler, so the machine needs only Bundler on its stim-server PATH and
|
|
8569
|
-
installs the pinned gems itself on the first build.
|
|
8951
|
+
installs the pinned gems itself on the first build. The comparison selects
|
|
8952
|
+
the app's .ruby-version when installed, with pod install's UTF-8 locale defaults.
|
|
8953
|
+
For Android its JDK major
|
|
8570
8954
|
version must match, and its Android SDK must hold the NDK, build-tools and
|
|
8571
8955
|
compile platform that the project's React Native version names in
|
|
8572
8956
|
gradle/libs.versions.toml; Gradle and AGP come from the synced project. When
|
|
@@ -9157,7 +9541,14 @@ macos.arguments is an array of arguments passed directly to the executable:
|
|
|
9157
9541
|
|
|
9158
9542
|
The plist must contain CFBundleIdentifier and CFBundleExecutable matching the
|
|
9159
9543
|
product. Use a development plist without shared URL schemes or an update feed.
|
|
9160
|
-
Stim gives the copied bundle a workspace-specific identifier
|
|
9544
|
+
Stim gives the copied bundle a workspace-specific identifier and name: it sets
|
|
9545
|
+
CFBundleDisplayName and CFBundleName on the copy to "<product> \u00b7 <label>",
|
|
9546
|
+
where label is the owned-device label (worktree and app directory names, cleaned
|
|
9547
|
+
to letters, digits, . _ -; at most 24 characters, cut with an ellipsis). The Dock,
|
|
9548
|
+
Cmd-Tab and lsappinfo show it, so several runs tell apart; the process name in
|
|
9549
|
+
System Events stays the executable. The project plist and the app's window titles
|
|
9550
|
+
are untouched. The name is displayName in the launch payload and status. Hosted
|
|
9551
|
+
(--remote) and offloaded builds get the same name. SwiftPM resource
|
|
9161
9552
|
bundles and frameworks in the reported build directory are copied into it;
|
|
9162
9553
|
macos.resources maps destinations under Contents/Resources to file or directory
|
|
9163
9554
|
sources relative to the Swift Package directory, for example:
|
|
@@ -9181,26 +9572,48 @@ executables, including Stim Desktop's sim-fold helper, are not built.
|
|
|
9181
9572
|
|
|
9182
9573
|
stim macos # fixed SwiftPM Debug build, then launch
|
|
9183
9574
|
stim macos --json # one launch record; progress goes to stderr
|
|
9575
|
+
stim macos --plan --json # validate the next build without running it
|
|
9184
9576
|
stim status --json # environments[].macos and build state
|
|
9185
9577
|
stim logs --source build
|
|
9186
9578
|
stim logs --errors
|
|
9187
9579
|
stim stop # stop this workspace's owned app and supervisor
|
|
9188
9580
|
|
|
9189
|
-
Each invocation
|
|
9581
|
+
Each invocation validates the project settings, development plist and resource
|
|
9582
|
+
entries before stopping its previous owned app, rebuilding and launching. SwiftPM
|
|
9190
9583
|
keeps incremental outputs in the workspace's runtime directory under STIM_HOME.
|
|
9584
|
+
Every process running an app bundle from that directory is the workspace's
|
|
9585
|
+
owned app, including copies opened through LaunchServices (open, agent-device
|
|
9586
|
+
open). stim macos, stim stop, stim worktree remove and stim gc --delete stop all
|
|
9587
|
+
of them: SIGTERM, up to 5 s, then SIGKILL, and success only once each has
|
|
9588
|
+
exited. When an app cannot be verified or does not exit, stop reports
|
|
9589
|
+
STIM_MACOS_OWNER_UNVERIFIED or a failure, and removal keeps the workspace.
|
|
9191
9590
|
Local stim macos starts the app in the background without activating it or
|
|
9192
9591
|
changing focus: it sets STIM_BACKGROUND_LAUNCH=1 in the app's environment, which
|
|
9193
|
-
Stim Desktop honors
|
|
9592
|
+
Stim Desktop honors, including for reopen events from open -g. An app that
|
|
9593
|
+
activates itself at launch or on reopen still takes focus.
|
|
9194
9594
|
Hosted launches (macos --remote) do not set it.
|
|
9195
|
-
macOS artifacts are not cached. This prototype has no --
|
|
9595
|
+
macOS artifacts are not cached. This prototype has no --slot or reload command.
|
|
9596
|
+
--plan validates the Swift Package directory, macos settings, development plist
|
|
9597
|
+
and declared resources without building, signing, staging, stopping or launching
|
|
9598
|
+
an app, writing workspace state or claiming a build slot. It does not execute
|
|
9599
|
+
Package.swift, so it cannot validate the executable product or package dependencies.
|
|
9600
|
+
--remote-build is respected, including named-machine setup refusals; no worker
|
|
9601
|
+
is contacted and live worker availability is unknown. --remote is launch-only
|
|
9602
|
+
and refuses with --plan. The JSON plan has platform "macos", product and
|
|
9603
|
+
buildMachine; fingerprint, cacheKey, provider, prebuild, outcome and expectedMs
|
|
9604
|
+
are null, cacheHit and cacheSkipped are false, and basis is 0. SwiftPM decides
|
|
9605
|
+
incremental compile work when a build runs; the plan predicts no cache outcome
|
|
9606
|
+
or duration. Desktop checks automatically while build details are visible,
|
|
9607
|
+
reusing a completed build or check for 60 seconds and skipping running builds.
|
|
9196
9608
|
A failed build records its error and compiler output without launching an app.
|
|
9197
9609
|
While it runs, stim status --json reports it as environments[].build with
|
|
9198
9610
|
platform "macos": phase prepare, compile, install, then launch, and during
|
|
9199
9611
|
compile detail.unit "steps" with SwiftPM's [done / total] counts (fetching and
|
|
9200
9612
|
planning are detail.step "configure"). It has no cache lookup, so outcome and
|
|
9201
9613
|
plannedPhases are null; finished runs and their phase times are in
|
|
9202
|
-
environments[].builds.macos
|
|
9203
|
-
|
|
9614
|
+
environments[].builds.macos, where phases includes launch once the launch step finishes
|
|
9615
|
+
and compileSteps is SwiftPM's step total. An offloaded build reports the worker's
|
|
9616
|
+
steps the same way.
|
|
9204
9617
|
Runtime stdout and stderr become client records; build output becomes build
|
|
9205
9618
|
records, all with platform "macos". Unexpected app exits are error records.
|
|
9206
9619
|
Stim runs the app with NSUnbufferedIO=YES, so Swift print output arrives per
|
|
@@ -9217,7 +9630,10 @@ refuse before stopping the app or changing its build record. See stim guide erro
|
|
|
9217
9630
|
|
|
9218
9631
|
With remote.build auto, remote.buildMode places these SwiftPM Debug builds:
|
|
9219
9632
|
auto builds here while this Mac has capacity, force uses an approved build
|
|
9220
|
-
machine when one accepts, and off
|
|
9633
|
+
machine when one accepts, and off builds here when local remains in the automatic
|
|
9634
|
+
build pool. remote.buildPoolDisabled excludes members from automatic placement,
|
|
9635
|
+
including its local fallback. Explicit local or named placement bypasses membership.
|
|
9636
|
+
Configure remote.machines
|
|
9221
9637
|
and approve build access as described in stim guide settings. The worker needs
|
|
9222
9638
|
matching Stim, CPU architecture, Xcode and macOS SDK, plus network access to
|
|
9223
9639
|
fetch package dependencies the first time. It keeps SwiftPM dependencies in a
|
|
@@ -9535,295 +9951,153 @@ this workspace's app. Do not change permissions or use custom build scripts."
|
|
|
9535
9951
|
};
|
|
9536
9952
|
//#endregion
|
|
9537
9953
|
//#region src/guide/tutorial-data.ts
|
|
9538
|
-
const
|
|
9539
|
-
|
|
9540
|
-
template: "expo-template-blank@58.0.15"
|
|
9541
|
-
};
|
|
9542
|
-
const TUTORIAL_FILES = {
|
|
9543
|
-
"App.js": `import { useEffect, useState } from 'react';
|
|
9544
|
-
import { Pressable, StyleSheet, Text, View } from 'react-native';
|
|
9545
|
-
import { StatusBar } from 'expo-status-bar';
|
|
9546
|
-
import { TITLE_COLOR } from './theme';
|
|
9547
|
-
|
|
9548
|
-
const TAG = '[stim:tutorial]';
|
|
9549
|
-
|
|
9550
|
-
function Button({ label, onPress }) {
|
|
9551
|
-
return (
|
|
9552
|
-
<Pressable accessibilityRole="button" onPress={onPress} style={styles.button}>
|
|
9553
|
-
<Text style={styles.buttonText}>{label}</Text>
|
|
9554
|
-
</Pressable>
|
|
9555
|
-
);
|
|
9556
|
-
}
|
|
9557
|
-
|
|
9558
|
-
export default function App() {
|
|
9559
|
-
const [note, setNote] = useState('');
|
|
9560
|
-
|
|
9561
|
-
useEffect(() => {
|
|
9562
|
-
console.log(\`\${TAG} title color=\${TITLE_COLOR}\`);
|
|
9563
|
-
}, [TITLE_COLOR]);
|
|
9564
|
-
|
|
9565
|
-
const logError = () => {
|
|
9566
|
-
console.error(\`\${TAG} error-button test error\`);
|
|
9567
|
-
setNote('Logged an error.');
|
|
9568
|
-
};
|
|
9569
|
-
|
|
9570
|
-
const crash = () => {
|
|
9571
|
-
setTimeout(() => {
|
|
9572
|
-
throw new Error(\`\${TAG} crash-button uncaught test error\`);
|
|
9573
|
-
}, 0);
|
|
9574
|
-
};
|
|
9575
|
-
|
|
9576
|
-
const slowRequest = async () => {
|
|
9577
|
-
const started = Date.now();
|
|
9578
|
-
setNote('Waiting 3 seconds...');
|
|
9579
|
-
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
9580
|
-
const elapsed = Date.now() - started;
|
|
9581
|
-
console.warn(\`\${TAG} slow-request \${elapsed}ms\`);
|
|
9582
|
-
setNote(\`Slow request took \${elapsed}ms.\`);
|
|
9583
|
-
};
|
|
9584
|
-
|
|
9585
|
-
return (
|
|
9586
|
-
<View style={styles.container}>
|
|
9587
|
-
<Text style={[styles.title, { color: TITLE_COLOR }]}>Stim Tutorial</Text>
|
|
9588
|
-
<Text style={styles.body}>Tap a button, then look at Stim Desktop > Logs.</Text>
|
|
9589
|
-
<Button label="Log an error" onPress={logError} />
|
|
9590
|
-
<Button label="Crash me" onPress={crash} />
|
|
9591
|
-
<Button label="Slow request" onPress={slowRequest} />
|
|
9592
|
-
<Text style={styles.note}>{note}</Text>
|
|
9593
|
-
<StatusBar style="auto" />
|
|
9594
|
-
</View>
|
|
9595
|
-
);
|
|
9596
|
-
}
|
|
9597
|
-
|
|
9598
|
-
const styles = StyleSheet.create({
|
|
9599
|
-
container: { flex: 1, backgroundColor: '#fff', alignItems: 'center', justifyContent: 'center', padding: 24 },
|
|
9600
|
-
title: { fontSize: 32, fontWeight: '700', marginBottom: 8 },
|
|
9601
|
-
body: { fontSize: 16, color: '#4b5563', textAlign: 'center', marginBottom: 24 },
|
|
9602
|
-
button: { backgroundColor: '#111827', borderRadius: 10, paddingVertical: 14, paddingHorizontal: 28, marginBottom: 12 },
|
|
9603
|
-
buttonText: { color: '#fff', fontSize: 17, fontWeight: '600' },
|
|
9604
|
-
note: { marginTop: 12, fontSize: 15, color: '#374151' },
|
|
9605
|
-
});
|
|
9606
|
-
`,
|
|
9607
|
-
"theme.js": `export const TITLE_COLOR = '#1f2937';
|
|
9608
|
-
`,
|
|
9609
|
-
"app.json": `{
|
|
9610
|
-
"expo": {
|
|
9611
|
-
"name": "Stim Tutorial",
|
|
9612
|
-
"slug": "stim-tutorial",
|
|
9613
|
-
"version": "1.0.0",
|
|
9614
|
-
"orientation": "portrait",
|
|
9615
|
-
"icon": "./assets/icon.png",
|
|
9616
|
-
"userInterfaceStyle": "light",
|
|
9617
|
-
"ios": {
|
|
9618
|
-
"supportsTablet": true,
|
|
9619
|
-
"bundleIdentifier": "dev.stim.tutorial"
|
|
9620
|
-
},
|
|
9621
|
-
"android": {
|
|
9622
|
-
"adaptiveIcon": {
|
|
9623
|
-
"backgroundColor": "#E6F4FE",
|
|
9624
|
-
"foregroundImage": "./assets/android-icon-foreground.png",
|
|
9625
|
-
"backgroundImage": "./assets/android-icon-background.png",
|
|
9626
|
-
"monochromeImage": "./assets/android-icon-monochrome.png"
|
|
9627
|
-
},
|
|
9628
|
-
"package": "dev.stim.tutorial"
|
|
9629
|
-
},
|
|
9630
|
-
"web": {
|
|
9631
|
-
"favicon": "./assets/favicon.png"
|
|
9632
|
-
},
|
|
9633
|
-
"extra": {
|
|
9634
|
-
"stimTutorial": 1
|
|
9635
|
-
}
|
|
9636
|
-
}
|
|
9637
|
-
}
|
|
9638
|
-
`,
|
|
9639
|
-
".gitignore": `/ios
|
|
9640
|
-
/android
|
|
9641
|
-
/tutorial*.ad
|
|
9642
|
-
/tutorial*.png
|
|
9643
|
-
`
|
|
9644
|
-
};
|
|
9645
|
-
const TUTORIAL_PROMPTS = {
|
|
9646
|
-
begin: "Run the Stim tutorial.",
|
|
9647
|
-
rebuild: "Continue the Stim tutorial: rebuild",
|
|
9648
|
-
agent: "Continue the Stim tutorial: agent",
|
|
9649
|
-
refresh: "Continue the Stim tutorial: refresh",
|
|
9650
|
-
machine: "Continue the Stim tutorial: machine",
|
|
9651
|
-
finish: "Continue the Stim tutorial: finish"
|
|
9652
|
-
};
|
|
9954
|
+
const TUTORIAL_REPO = "appandflow/stim-tutorial";
|
|
9955
|
+
const TUTORIAL_BUNDLE_ID = "dev.stim.tutorial";
|
|
9653
9956
|
const TUTORIAL_RESTART_PROMPT = "Restart the Stim tutorial.";
|
|
9957
|
+
/**
|
|
9958
|
+
* The requests Stim Desktop offers to copy, written the way a developer would ask. {base}, {tour}, {worktrees} and
|
|
9959
|
+
* {machine} are filled in by Desktop. Only the first and last name a guide section, which holds the folder and cleanup safety rules.
|
|
9960
|
+
*/
|
|
9961
|
+
const TUTORIAL_ASKS = {
|
|
9962
|
+
begin: `Clone ${TUTORIAL_REPO} into {base} and install its dependencies, then run stim doctor for iOS there so Stim registers it. Use a fresh folder: if {base} already exists or is inside another git repository, stop and ask me for another folder, and never git add in my own repo. Follow stim guide tutorial run.`,
|
|
9963
|
+
agent: "Open the app on the iOS simulator, take a screenshot and confirm the title color.",
|
|
9964
|
+
machine: "Build the app for iOS on {machine} with stim instead of on this Mac. Do not approve or pair anything.",
|
|
9965
|
+
finish: "I'm done with these experiments in {base} and don't need the changes. Stop the apps and remove the worktrees {worktrees}, and keep the clone. Follow stim guide tutorial finish.",
|
|
9966
|
+
share: `Open a pull request to ${TUTORIAL_REPO} with my title color change, and include a screenshot of it running in the simulator. See stim guide tutorial share.`,
|
|
9967
|
+
retry: "The first iOS build of the tutorial app in {tour} failed. Find out why and run it on iOS again."
|
|
9968
|
+
};
|
|
9654
9969
|
const TUTORIAL_STEPS = [
|
|
9655
9970
|
{
|
|
9656
9971
|
id: "begin",
|
|
9657
|
-
title: "
|
|
9972
|
+
title: "Get the Test App",
|
|
9658
9973
|
who: "agent",
|
|
9659
9974
|
optional: false,
|
|
9660
|
-
|
|
9975
|
+
ask: TUTORIAL_ASKS.begin,
|
|
9661
9976
|
section: "run",
|
|
9662
|
-
|
|
9663
|
-
"base=\"{base}\"",
|
|
9664
|
-
"mkdir -p \"${base%/*}\"",
|
|
9665
|
-
"if [ ! -e \"$base\" ]; then",
|
|
9666
|
-
"cd \"${base%/*}\"",
|
|
9667
|
-
"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",
|
|
9668
|
-
`npx --yes create-expo-app@${TUTORIAL_PINS.createExpoApp} stim-tutorial --template ${TUTORIAL_PINS.template} --no-install --no-agents-md --yes`,
|
|
9669
|
-
"cd \"$base\"",
|
|
9670
|
-
"stim guide tutorial app",
|
|
9671
|
-
...Object.entries(TUTORIAL_FILES).flatMap(([name, content]) => [`cat ${name === ".gitignore" ? ">>" : ">"} ${name} <<'STIM_TUTORIAL_EOF'`].concat(content.trimEnd().split("\n"), "STIM_TUTORIAL_EOF")),
|
|
9672
|
-
"npm install --prefer-offline",
|
|
9673
|
-
"npm pkg set scripts.ios=\"expo run:ios\" scripts.android=\"expo run:android\"",
|
|
9674
|
-
"git init",
|
|
9675
|
-
"git add -A",
|
|
9676
|
-
"git -c user.name=Stim -c user.email=stim@localhost -c commit.gpgsign=false commit -m \"Stim tutorial\"",
|
|
9677
|
-
"else",
|
|
9678
|
-
"cd \"$base\"",
|
|
9679
|
-
`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); }'`,
|
|
9680
|
-
`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); }'`,
|
|
9681
|
-
"fi"
|
|
9682
|
-
]
|
|
9683
|
-
},
|
|
9684
|
-
{
|
|
9685
|
-
id: "sidebar",
|
|
9686
|
-
title: "Workspace in sidebar",
|
|
9687
|
-
who: "you",
|
|
9688
|
-
optional: false,
|
|
9689
|
-
prompt: null,
|
|
9690
|
-
section: null,
|
|
9691
|
-
manual: []
|
|
9977
|
+
commands: []
|
|
9692
9978
|
},
|
|
9693
9979
|
{
|
|
9694
9980
|
id: "build",
|
|
9695
|
-
title: "
|
|
9981
|
+
title: "Make a Change",
|
|
9696
9982
|
who: "you",
|
|
9697
9983
|
optional: false,
|
|
9698
|
-
|
|
9984
|
+
ask: null,
|
|
9699
9985
|
section: null,
|
|
9700
|
-
|
|
9701
|
-
"cd \"{base}\"",
|
|
9702
|
-
"git worktree add -B stim-tutorial/tour \"{tour}\" HEAD",
|
|
9703
|
-
"cd \"{tour}\"",
|
|
9704
|
-
"stim worktree warm",
|
|
9705
|
-
"stim guide agent",
|
|
9706
|
-
"stim doctor --platform ios",
|
|
9707
|
-
"stim start",
|
|
9708
|
-
"stim ios"
|
|
9709
|
-
]
|
|
9986
|
+
commands: []
|
|
9710
9987
|
},
|
|
9711
9988
|
{
|
|
9712
|
-
id: "
|
|
9713
|
-
title: "
|
|
9714
|
-
who: "agent",
|
|
9715
|
-
optional: false,
|
|
9716
|
-
prompt: TUTORIAL_PROMPTS.rebuild,
|
|
9717
|
-
section: "rebuild",
|
|
9718
|
-
manual: [
|
|
9719
|
-
"cd \"{tour}\"",
|
|
9720
|
-
"stim ios",
|
|
9721
|
-
"stim status --json"
|
|
9722
|
-
]
|
|
9723
|
-
},
|
|
9724
|
-
{
|
|
9725
|
-
id: "device",
|
|
9726
|
-
title: "Live view and control",
|
|
9989
|
+
id: "parallel",
|
|
9990
|
+
title: "Change It Again in Parallel",
|
|
9727
9991
|
who: "you",
|
|
9728
9992
|
optional: false,
|
|
9729
|
-
|
|
9993
|
+
ask: null,
|
|
9730
9994
|
section: null,
|
|
9731
|
-
|
|
9995
|
+
commands: []
|
|
9732
9996
|
},
|
|
9733
9997
|
{
|
|
9734
|
-
id: "
|
|
9735
|
-
title: "
|
|
9998
|
+
id: "device",
|
|
9999
|
+
title: "Live View and Control",
|
|
9736
10000
|
who: "you",
|
|
9737
|
-
optional:
|
|
9738
|
-
|
|
10001
|
+
optional: true,
|
|
10002
|
+
ask: null,
|
|
9739
10003
|
section: null,
|
|
9740
|
-
|
|
10004
|
+
commands: ["stim status"]
|
|
9741
10005
|
},
|
|
9742
10006
|
{
|
|
9743
10007
|
id: "agent",
|
|
9744
|
-
title: "Agent
|
|
10008
|
+
title: "Agent Actions and Replay",
|
|
9745
10009
|
who: "agent",
|
|
9746
|
-
optional:
|
|
9747
|
-
|
|
9748
|
-
section:
|
|
9749
|
-
|
|
10010
|
+
optional: true,
|
|
10011
|
+
ask: TUTORIAL_ASKS.agent,
|
|
10012
|
+
section: null,
|
|
10013
|
+
commands: [
|
|
9750
10014
|
"cd \"{tour}\"",
|
|
9751
10015
|
"export AGENT_DEVICE_STATE_DIR=\"{stateDir}\"",
|
|
9752
|
-
|
|
9753
|
-
"read -r iosUdid",
|
|
9754
|
-
"agent-device open dev.stim.tutorial --platform ios --udid \"$iosUdid\" --save-script=tutorial.ad",
|
|
9755
|
-
"agent-device react-native dismiss-overlay || true",
|
|
9756
|
-
`agent-device press 'label="Log an error"' --settle`,
|
|
10016
|
+
`agent-device open ${TUTORIAL_BUNDLE_ID} --platform ios --udid {udid}`,
|
|
9757
10017
|
"agent-device screenshot tutorial.png",
|
|
9758
10018
|
"agent-device close",
|
|
9759
|
-
"grep -v -e 'target-v1' -e 'dismiss-overlay' tutorial.ad > tutorial-replay.ad",
|
|
9760
|
-
"agent-device replay tutorial-replay.ad --platform ios --udid \"$iosUdid\"",
|
|
9761
10019
|
"stim logs --source agent --tail 10"
|
|
9762
10020
|
]
|
|
9763
10021
|
},
|
|
9764
10022
|
{
|
|
9765
|
-
id: "
|
|
9766
|
-
title: "
|
|
9767
|
-
who: "
|
|
9768
|
-
optional:
|
|
9769
|
-
|
|
9770
|
-
section:
|
|
9771
|
-
|
|
9772
|
-
"cd \"{tour}\"",
|
|
9773
|
-
"cat > theme.js <<'STIM_TUTORIAL_EOF'",
|
|
9774
|
-
"export const TITLE_COLOR = '#7c3aed';",
|
|
9775
|
-
"STIM_TUTORIAL_EOF",
|
|
9776
|
-
"sleep 6",
|
|
9777
|
-
"stim logs --errors",
|
|
9778
|
-
"stim logs --grep 'title color'"
|
|
9779
|
-
]
|
|
10023
|
+
id: "logs",
|
|
10024
|
+
title: "App Logs",
|
|
10025
|
+
who: "you",
|
|
10026
|
+
optional: true,
|
|
10027
|
+
ask: null,
|
|
10028
|
+
section: null,
|
|
10029
|
+
commands: ["stim logs --errors", "stim logs --grep stim:tutorial"]
|
|
9780
10030
|
},
|
|
9781
10031
|
{
|
|
9782
10032
|
id: "phone",
|
|
9783
|
-
title: "Watch on
|
|
10033
|
+
title: "Watch on Your Phone",
|
|
9784
10034
|
who: "you",
|
|
9785
10035
|
optional: true,
|
|
9786
|
-
|
|
10036
|
+
ask: null,
|
|
9787
10037
|
section: null,
|
|
9788
|
-
|
|
10038
|
+
commands: []
|
|
9789
10039
|
},
|
|
9790
10040
|
{
|
|
9791
10041
|
id: "machine",
|
|
9792
|
-
title: "Build on
|
|
10042
|
+
title: "Build on Another Mac",
|
|
9793
10043
|
who: "both",
|
|
9794
10044
|
optional: true,
|
|
9795
|
-
|
|
9796
|
-
section:
|
|
9797
|
-
|
|
10045
|
+
ask: TUTORIAL_ASKS.machine,
|
|
10046
|
+
section: null,
|
|
10047
|
+
commands: ["cd \"{tour}\"", "stim ios --remote local --remote-build \"{machine}\" --no-build-cache"]
|
|
10048
|
+
},
|
|
10049
|
+
{
|
|
10050
|
+
id: "share",
|
|
10051
|
+
title: "Share Your Finish",
|
|
10052
|
+
who: "you",
|
|
10053
|
+
optional: true,
|
|
10054
|
+
ask: TUTORIAL_ASKS.share,
|
|
10055
|
+
section: "share",
|
|
10056
|
+
commands: [
|
|
10057
|
+
"cd \"{tour}\"",
|
|
10058
|
+
"git commit -am \"Tutorial change\" -m \"Build <time>, second build cache <hit or miss>\"",
|
|
10059
|
+
"xcrun simctl io {udid} screenshot finish.png",
|
|
10060
|
+
`gh repo fork ${TUTORIAL_REPO} --remote --remote-name fork`,
|
|
10061
|
+
"git push -u fork HEAD",
|
|
10062
|
+
`gh pr create --repo ${TUTORIAL_REPO} --fill --attach "finish.png#The change running in the simulator"`
|
|
10063
|
+
]
|
|
9798
10064
|
},
|
|
9799
10065
|
{
|
|
9800
10066
|
id: "finish",
|
|
9801
|
-
title: "Finish and
|
|
10067
|
+
title: "Finish and Archive",
|
|
9802
10068
|
who: "agent",
|
|
9803
10069
|
optional: false,
|
|
9804
|
-
|
|
10070
|
+
ask: TUTORIAL_ASKS.finish,
|
|
9805
10071
|
section: "finish",
|
|
9806
|
-
|
|
9807
|
-
"git -C \"{tour}\" checkout -- theme.js",
|
|
10072
|
+
commands: [
|
|
9808
10073
|
"cd \"{tour}\"",
|
|
9809
10074
|
"stim stop",
|
|
10075
|
+
"cd \"{second}\"",
|
|
10076
|
+
"stim stop",
|
|
9810
10077
|
"cd \"{base}\"",
|
|
9811
|
-
"stim worktree remove \"{tour}\""
|
|
10078
|
+
"stim worktree remove \"{tour}\"",
|
|
10079
|
+
"stim worktree remove \"{second}\""
|
|
9812
10080
|
]
|
|
9813
10081
|
}
|
|
9814
10082
|
];
|
|
9815
10083
|
//#endregion
|
|
9816
10084
|
//#region src/guide/tutorial.ts
|
|
9817
|
-
const paths = `Use {base} = ~/stim-tutorial
|
|
9818
|
-
|
|
9819
|
-
|
|
10085
|
+
const paths = `Use {base} = ~/stim-tutorial unless the user named another folder. Expand ~
|
|
10086
|
+
to the absolute home path when substituting inside quotes. Keep the tutorial
|
|
10087
|
+
outside the user's own projects.`;
|
|
10088
|
+
const local = `The tutorial runs on this Mac. Every time you run the tutorial app on iOS
|
|
10089
|
+
from a worktree of {base}, use stim ios --remote local --remote-build local,
|
|
10090
|
+
even when the user's settings would place the device or the build on another
|
|
10091
|
+
Mac. Only an explicit request to build on another Mac changes the build, with
|
|
10092
|
+
stim ios --remote local --remote-build "<machine>"; the device stays here.`;
|
|
9820
10093
|
function commands(id) {
|
|
9821
|
-
return TUTORIAL_STEPS.find((step) => step.id === id).
|
|
10094
|
+
return TUTORIAL_STEPS.find((step) => step.id === id).commands.join("\n");
|
|
9822
10095
|
}
|
|
9823
10096
|
//#endregion
|
|
9824
10097
|
//#region src/guide/index.ts
|
|
9825
10098
|
const TOPICS = {
|
|
9826
10099
|
agent: agent_default,
|
|
10100
|
+
api: api_default,
|
|
9827
10101
|
facts,
|
|
9828
10102
|
metro: metro_default,
|
|
9829
10103
|
ports: ports_default,
|
|
@@ -9835,260 +10109,160 @@ const TOPICS = {
|
|
|
9835
10109
|
web: web_default,
|
|
9836
10110
|
macos: macos_default,
|
|
9837
10111
|
tutorial: {
|
|
9838
|
-
summary: "
|
|
10112
|
+
summary: "A cloned test app: a first build, a second change in parallel with a cache hit, and cleanup",
|
|
9839
10113
|
sectionHint: "run",
|
|
9840
10114
|
preamble: () => `STIM TUTORIAL
|
|
9841
10115
|
|
|
9842
|
-
|
|
9843
|
-
|
|
9844
|
-
|
|
10116
|
+
The tutorial clones ${TUTORIAL_REPO}, a tiny Expo app, into {base}; the clone is
|
|
10117
|
+
never run or removed. The user then asks for a visual change, and for another
|
|
10118
|
+
change while the first builds. Each change runs in its own linked worktree of
|
|
10119
|
+
{base}, so each has its own simulator and Metro port, and the second worktree's
|
|
10120
|
+
first iOS build reuses the first one's native build. Only the clone, the
|
|
10121
|
+
optional share and the cleanup need this guide: the changes are ordinary
|
|
10122
|
+
requests, so follow stim guide agent for them, and check each change on the
|
|
10123
|
+
device.
|
|
9845
10124
|
|
|
9846
|
-
|
|
9847
|
-
Stim Desktop, and give the next prompt. Pause never means stim stop.
|
|
9848
|
-
"${TUTORIAL_PROMPTS.begin}" means stim guide tutorial run.
|
|
9849
|
-
"Continue the Stim tutorial: <section>" means stim guide tutorial <section>.
|
|
9850
|
-
"${TUTORIAL_RESTART_PROMPT}" means stim guide tutorial restart.
|
|
10125
|
+
${local}
|
|
9851
10126
|
|
|
9852
|
-
|
|
9853
|
-
|
|
9854
|
-
|
|
9855
|
-
phones, approve machines, or grant access on the user's behalf.
|
|
10127
|
+
"Follow stim guide tutorial run" means stim guide tutorial run.
|
|
10128
|
+
"Follow stim guide tutorial finish" means stim guide tutorial finish.
|
|
10129
|
+
"${TUTORIAL_RESTART_PROMPT}" means stim guide tutorial restart.
|
|
9856
10130
|
|
|
9857
10131
|
${paths}
|
|
9858
10132
|
|
|
9859
|
-
|
|
9860
|
-
|
|
9861
|
-
required npm and native dependencies are already cached.`,
|
|
10133
|
+
Never pair phones, approve machines, or grant access on the user's behalf.
|
|
10134
|
+
For commands to type yourself, read stim guide tutorial manual.`,
|
|
9862
10135
|
sections: {
|
|
9863
10136
|
run: {
|
|
9864
|
-
summary: "
|
|
10137
|
+
summary: "Clone the test app into a fresh folder and install its dependencies",
|
|
9865
10138
|
body: () => `RUN THE TUTORIAL
|
|
9866
10139
|
|
|
9867
10140
|
${paths}
|
|
9868
10141
|
|
|
9869
|
-
Folder safety:
|
|
9870
|
-
folder
|
|
9871
|
-
|
|
9872
|
-
folder
|
|
9873
|
-
Never
|
|
10142
|
+
Folder safety: use a fresh folder. If {base} exists, stop and ask the user for
|
|
10143
|
+
another folder; never overwrite or delete it. First check the parent folder:
|
|
10144
|
+
create it if absent, then run git -C <parent> rev-parse --is-inside-work-tree.
|
|
10145
|
+
If that succeeds, the folder is inside another repository: stop and ask the
|
|
10146
|
+
user for another folder. Never git add in the user's repo.
|
|
9874
10147
|
|
|
9875
|
-
|
|
9876
|
-
then run git -C <parent> rev-parse --is-inside-work-tree. If it succeeds, the
|
|
9877
|
-
folder is inside another repository: stop and ask the user for another folder.
|
|
9878
|
-
Never git add in the user's repo. Then, from the parent folder, run:
|
|
10148
|
+
Then, from the parent folder, run:
|
|
9879
10149
|
|
|
9880
|
-
|
|
10150
|
+
git clone https://github.com/${TUTORIAL_REPO}.git stim-tutorial
|
|
9881
10151
|
|
|
9882
|
-
|
|
9883
|
-
|
|
10152
|
+
Check that app.json in the clone has expo.extra.stimTutorial equal to
|
|
10153
|
+
${TUTORIAL_VERSION}. If it does not, report the mismatch and stop; the user needs a newer Stim.
|
|
10154
|
+
Enter the clone and install its dependencies (npm ci) so the worktrees made for
|
|
10155
|
+
the changes inherit them, then run stim doctor --platform ios there: it registers
|
|
10156
|
+
the clone with Stim so Stim Desktop sees it, and builds or boots nothing. Do not
|
|
10157
|
+
run the app: the clone is only the base for the user's changes and is never removed.
|
|
10158
|
+
Report the findings of stim doctor and do not act on them: no SimSlim install, no
|
|
10159
|
+
--fix, nothing that changes the machine during the tutorial. On npm or network failure, report stderr
|
|
10160
|
+
and stop.
|
|
9884
10161
|
|
|
9885
|
-
|
|
9886
|
-
npm pkg set scripts.ios="expo run:ios" scripts.android="expo run:android"
|
|
9887
|
-
git init
|
|
9888
|
-
git add -A
|
|
9889
|
-
git -c user.name=Stim -c user.email=stim@localhost -c commit.gpgsign=false commit -m "Stim tutorial"
|
|
10162
|
+
${local}
|
|
9890
10163
|
|
|
9891
|
-
|
|
9892
|
-
|
|
9893
|
-
|
|
9894
|
-
|
|
9895
|
-
Set the scripts before the commit because Expo prebuild rewrites them to
|
|
9896
|
-
expo run:ios and expo run:android; otherwise the dirty worktree blocks removal.
|
|
9897
|
-
On npm or network failure, report stderr and stop.
|
|
9898
|
-
|
|
9899
|
-
From the base folder, add the tour worktree and run:
|
|
9900
|
-
|
|
9901
|
-
git worktree add -B stim-tutorial/tour "{tour}" HEAD
|
|
9902
|
-
cd "{tour}"
|
|
9903
|
-
stim worktree warm
|
|
9904
|
-
stim guide agent
|
|
9905
|
-
|
|
9906
|
-
Apply the guide agent doctor rule before native work: if its STATUS block
|
|
9907
|
-
says doctor is due, run stim doctor --platform ios from the tour worktree and
|
|
9908
|
-
resolve its findings. No STATUS block means doctor is current. Then run:
|
|
9909
|
-
|
|
9910
|
-
stim start
|
|
9911
|
-
stim ios
|
|
9912
|
-
|
|
9913
|
-
Tell the user the first build can take about four minutes on a cold cache.
|
|
9914
|
-
Relay the Open in Stim Desktop link printed by stim ios once.
|
|
9915
|
-
|
|
9916
|
-
PAUSE: end the turn. Ask the user to look at the Build section, then the
|
|
9917
|
-
device, then Logs in Stim Desktop. Without Desktop, use stim status,
|
|
9918
|
-
stim stats, and stim logs --errors. Give the next prompt:
|
|
9919
|
-
"${TUTORIAL_PROMPTS.rebuild}". Leave the workspace running.`
|
|
9920
|
-
},
|
|
9921
|
-
app: {
|
|
9922
|
-
summary: "Pinned template, verbatim app files, and .gitignore additions",
|
|
9923
|
-
body: () => `TUTORIAL APP
|
|
9924
|
-
|
|
9925
|
-
create-expo-app: ${TUTORIAL_PINS.createExpoApp}
|
|
9926
|
-
Template: ${TUTORIAL_PINS.template}
|
|
9927
|
-
Write the app files verbatim. Append the .gitignore lines to the template's
|
|
9928
|
-
existing file. App log lines start with [stim:tutorial]. Crash me raises an
|
|
9929
|
-
uncaught JavaScript error, and Slow request times a local three-second timer;
|
|
9930
|
-
Stim does not capture native network requests.
|
|
9931
|
-
|
|
9932
|
-
${Object.entries(TUTORIAL_FILES).map(([name, content]) => `${name}\n\n\`\`\`${name.endsWith(".json") ? "json" : name.endsWith(".js") ? "js" : "text"}\n${content}\`\`\``).join("\n\n")}`
|
|
9933
|
-
},
|
|
9934
|
-
rebuild: {
|
|
9935
|
-
summary: "Repeat the iOS build and explain the cache result",
|
|
9936
|
-
body: () => `REBUILD
|
|
9937
|
-
|
|
9938
|
-
${paths}
|
|
9939
|
-
|
|
9940
|
-
Run the same build again, never with --no-build-cache:
|
|
9941
|
-
|
|
9942
|
-
${commands("rebuild")}
|
|
9943
|
-
|
|
9944
|
-
Read this workspace's environments[].lastBuilds.ios.cacheHit from
|
|
9945
|
-
stim status --json, and the miss reason printed by stim ios. Explain a local or remote hit, or the actual miss
|
|
9946
|
-
reason when cacheHit is false. A repeat run can miss; report the evidence.
|
|
9947
|
-
|
|
9948
|
-
PAUSE: end the turn. Point at the Build section and cache badge, or stim stats.
|
|
9949
|
-
Ask the user to open the device's live view and tap Log an error; without
|
|
9950
|
-
Desktop use the simulator. Then inspect Logs or stim logs --errors. Crash me
|
|
9951
|
-
and Slow request are optional: the former shows a red box, the latter prints
|
|
9952
|
-
a timing line. Give the next prompt: "${TUTORIAL_PROMPTS.agent}".`
|
|
9953
|
-
},
|
|
9954
|
-
agent: {
|
|
9955
|
-
summary: "Record a tap and screenshot, replay it, and inspect agent logs",
|
|
9956
|
-
body: () => `AGENT ACTIONS
|
|
9957
|
-
|
|
9958
|
-
${paths}
|
|
9959
|
-
|
|
9960
|
-
Set {stateDir} to this workspace's agentDevice.stateDir from stim ios or
|
|
9961
|
-
stim status --json. Replace <ios.udid> with this workspace's ios.udid from
|
|
9962
|
-
stim status --json. Use that exact owned simulator, not a guessed one.
|
|
9963
|
-
Run from the tour worktree:
|
|
9964
|
-
|
|
9965
|
-
${commands("agent").replace("read -r iosUdid", "iosUdid=\"<ios.udid>\"")}
|
|
9966
|
-
|
|
9967
|
-
Use --save-script=tutorial.ad with the equals sign: agent-device treats a
|
|
9968
|
-
separate path as a URL and refuses. The dismiss-overlay step clears a red box that would cover the buttons.
|
|
9969
|
-
Replay with the same --platform and --udid so its steps reach the agent log.
|
|
9970
|
-
Remove the target-v1 evidence and dismiss-overlay lines before replay: replaying
|
|
9971
|
-
them can fail with REPLAY_DIVERGENCE on the recorded button identity.
|
|
9972
|
-
The scripts and screenshot stay in the tour worktree and are git-ignored.
|
|
9973
|
-
|
|
9974
|
-
PAUSE: end the turn. Point at Agent actions, or the agent log records just
|
|
9975
|
-
printed. Expect another error-button line after replay. When screen recording
|
|
9976
|
-
is enabled, the user can scrub Replay in Desktop. Give the next prompt:
|
|
9977
|
-
"${TUTORIAL_PROMPTS.refresh}".`
|
|
9978
|
-
},
|
|
9979
|
-
refresh: {
|
|
9980
|
-
summary: "Change the title to purple and verify Fast Refresh through logs",
|
|
9981
|
-
body: () => `FAST REFRESH
|
|
9982
|
-
|
|
9983
|
-
${paths}
|
|
9984
|
-
|
|
9985
|
-
Set TITLE_COLOR in theme.js to '#7c3aed' (purple). Wait a few seconds for
|
|
9986
|
-
Fast Refresh, without reloading the app:
|
|
9987
|
-
|
|
9988
|
-
${commands("refresh")}
|
|
9989
|
-
|
|
9990
|
-
The line [stim:tutorial] title color=#7c3aed is the proof that the edit reached
|
|
9991
|
-
the app. The intentional button errors may still be in the error log; check
|
|
9992
|
-
whether the edit introduced a new error.
|
|
9993
|
-
|
|
9994
|
-
PAUSE: end the turn. Point at the purple title in the device view or simulator
|
|
9995
|
-
and the title color log line. Phone viewing and an approved remote Mac are
|
|
9996
|
-
optional user steps; skip them if unwanted. Never pair, approve, or grant
|
|
9997
|
-
anything. If the user names an approved machine, the next prompt is
|
|
9998
|
-
"${TUTORIAL_PROMPTS.machine}". Otherwise give
|
|
9999
|
-
"${TUTORIAL_PROMPTS.finish}".`
|
|
10000
|
-
},
|
|
10001
|
-
machine: {
|
|
10002
|
-
summary: "Optionally build using a machine the user names and has approved",
|
|
10003
|
-
body: () => `REMOTE MAC (OPTIONAL)
|
|
10004
|
-
|
|
10005
|
-
${paths}
|
|
10006
|
-
|
|
10007
|
-
Proceed only when the user names an approved remote Mac. Substitute that
|
|
10008
|
-
name for {machine}. Never approve, pair, or grant anything. If none is named,
|
|
10009
|
-
ask for the name or let the user skip this step.
|
|
10010
|
-
|
|
10011
|
-
${commands("machine")}
|
|
10012
|
-
|
|
10013
|
-
This step bypasses the artifact cache so the build can use the named machine.
|
|
10014
|
-
A named machine refuses without a local fallback. If it refuses, report the
|
|
10015
|
-
refusal and offer --remote-build auto or local; do not retry silently.
|
|
10016
|
-
|
|
10017
|
-
PAUSE: end the turn. Point at the build's machine in Desktop or its report in
|
|
10018
|
-
stim status --json. Give the next prompt: "${TUTORIAL_PROMPTS.finish}".`
|
|
10164
|
+
PAUSE: end the turn. Tell the user to ask for a visual change next, such as
|
|
10165
|
+
making the title purple, in their own words. Each change runs in a new linked
|
|
10166
|
+
worktree of {base} (stim guide agent), and its first iOS build takes a few
|
|
10167
|
+
minutes.`
|
|
10019
10168
|
},
|
|
10020
10169
|
finish: {
|
|
10021
|
-
summary: "
|
|
10170
|
+
summary: "Stop and remove the two tutorial worktrees, keeping the clone",
|
|
10022
10171
|
body: () => `FINISH
|
|
10023
10172
|
|
|
10024
10173
|
${paths}
|
|
10025
10174
|
|
|
10026
|
-
The
|
|
10027
|
-
|
|
10028
|
-
|
|
10175
|
+
The worktrees to remove are the two the tutorial tracked: the linked
|
|
10176
|
+
worktrees of {base} made for the user's changes. The finish request names them
|
|
10177
|
+
as {tour} and {second}. Never touch any other worktree, an earlier tutorial
|
|
10178
|
+
clone, or the clone itself. For each, stop from its path, then remove it from
|
|
10179
|
+
the clone:
|
|
10029
10180
|
|
|
10030
10181
|
${commands("finish")}
|
|
10031
10182
|
|
|
10032
|
-
|
|
10033
|
-
|
|
10034
|
-
|
|
10183
|
+
Use a plain remove first. The user's finish request says they do not need the
|
|
10184
|
+
changes, which is the consent stim guide agent asks for before worktree remove
|
|
10185
|
+
--force, for exactly those two paths: if the plain remove refuses one of them
|
|
10186
|
+
only because of uncommitted changes or commits found nowhere else, remove that
|
|
10187
|
+
worktree with --force. Never use --force on the clone or on any other
|
|
10188
|
+
worktree, and on any other refusal report it and stop. Keep the clone. Print
|
|
10189
|
+
these optional cleanup commands for the user; do not run them:
|
|
10035
10190
|
|
|
10036
10191
|
rm -rf "{base}"
|
|
10037
|
-
git -C "{base}" branch -D stim-tutorial/tour
|
|
10038
10192
|
|
|
10039
|
-
If archive is enabled, tell the user the
|
|
10040
|
-
Desktop. With archive disabled, report removal without promising an
|
|
10041
|
-
Without Desktop, inspect stim status --json for the removed
|
|
10042
|
-
|
|
10193
|
+
If archive is enabled, tell the user the worktrees appear under Archived in
|
|
10194
|
+
Stim Desktop. With archive disabled, report removal without promising an
|
|
10195
|
+
archive. Without Desktop, inspect stim status --json for the removed
|
|
10196
|
+
environments. End the turn.`
|
|
10197
|
+
},
|
|
10198
|
+
share: {
|
|
10199
|
+
summary: "Optionally open a public pull request with a screenshot of the change",
|
|
10200
|
+
body: () => `SHARE YOUR FINISH (OPTIONAL)
|
|
10201
|
+
|
|
10202
|
+
Only on the user's explicit request, which the share prompt is, and before the
|
|
10203
|
+
finish step removes the worktrees. The pull request is public: the user's GitHub
|
|
10204
|
+
name and change appear on ${TUTORIAL_REPO}, and a bot replies and closes it. It
|
|
10205
|
+
needs gh signed in. Never open it unprompted or from any other step.
|
|
10206
|
+
|
|
10207
|
+
Work from the worktree holding the user's change, {tour}. Commit the change
|
|
10208
|
+
there, fork the repository and push the branch to the fork, since the user has
|
|
10209
|
+
no write access:
|
|
10210
|
+
|
|
10211
|
+
gh repo fork ${TUTORIAL_REPO} --remote --remote-name fork
|
|
10212
|
+
git push -u fork HEAD
|
|
10213
|
+
|
|
10214
|
+
Screenshot: take a PNG under 1 MB of the app showing the change, with
|
|
10215
|
+
agent-device screenshot when it is installed, otherwise
|
|
10216
|
+
xcrun simctl io <ios.udid> screenshot finish.png, using the udid of that
|
|
10217
|
+
worktree from stim status --json, never booted. If the app is no longer
|
|
10218
|
+
running, run it again from the branch first.
|
|
10219
|
+
|
|
10220
|
+
Open the pull request with gh pr create --repo ${TUTORIAL_REPO} --fill. Put one
|
|
10221
|
+
short line with the build time and whether the second worktree's first iOS build
|
|
10222
|
+
was a cache hit, when you know them from stim status --json or stim stats, in the
|
|
10223
|
+
commit message body (git commit -m "<title>" -m "<that line>"), so --fill carries it
|
|
10224
|
+
into the pull request body.
|
|
10225
|
+
|
|
10226
|
+
Attach the screenshot with gh: gh pr create --attach "finish.png#The change
|
|
10227
|
+
running in the simulator" uploads the image and appends it to the body. gh
|
|
10228
|
+
2.99.0 (2026-09-01) has --attach; confirm with gh pr create --help rather than
|
|
10229
|
+
guessing a version. When gh has no --attach, commit the PNG on the PR branch as
|
|
10230
|
+
finish/<github-login>.png before pushing and embed it in the body with a
|
|
10231
|
+
relative link: .`
|
|
10043
10232
|
},
|
|
10044
10233
|
restart: {
|
|
10045
|
-
summary: "
|
|
10234
|
+
summary: "Start the tutorial again without removing anything",
|
|
10046
10235
|
body: () => `RESTART
|
|
10047
10236
|
|
|
10048
10237
|
${paths}
|
|
10049
10238
|
|
|
10050
|
-
For "${TUTORIAL_RESTART_PROMPT}",
|
|
10051
|
-
|
|
10052
|
-
|
|
10053
|
-
|
|
10054
|
-
|
|
10055
|
-
|
|
10056
|
-
|
|
10057
|
-
and redo run from the worktree step. Pause after the build as run instructs.`
|
|
10239
|
+
For "${TUTORIAL_RESTART_PROMPT}", remove nothing: the user may still want the
|
|
10240
|
+
earlier worktrees. Finish only removes the pair named in its request, so leave
|
|
10241
|
+
any earlier worktrees and tell the user they stay until they ask for each by
|
|
10242
|
+
path; never use --force for them. Reuse the clone at {base} only if its
|
|
10243
|
+
stimTutorial marker equals ${TUTORIAL_VERSION}; otherwise follow stim guide
|
|
10244
|
+
tutorial run into a fresh folder. Pause as run instructs. Stim Desktop starts
|
|
10245
|
+
over: only worktrees and builds after the restart count.`
|
|
10058
10246
|
},
|
|
10059
10247
|
manual: {
|
|
10060
|
-
summary: "
|
|
10248
|
+
summary: "The commands behind each step, for typing yourself",
|
|
10061
10249
|
body: () => `MANUAL TUTORIAL
|
|
10062
10250
|
|
|
10063
10251
|
${paths}
|
|
10064
10252
|
|
|
10065
|
-
|
|
10066
|
-
|
|
10067
|
-
|
|
10068
|
-
|
|
10069
|
-
|
|
10070
|
-
|
|
10071
|
-
|
|
10072
|
-
|
|
10073
|
-
|
|
10074
|
-
|
|
10075
|
-
|
|
10076
|
-
|
|
10077
|
-
|
|
10078
|
-
minutes on a cold cache). Compare cacheHit and missReason on rebuild. At Live
|
|
10079
|
-
view and control, open the device viewer and tap Log an error; without Desktop
|
|
10080
|
-
use the simulator. At App logs, try Crash me or Slow request if wanted. A JS
|
|
10081
|
-
crash shows a red box; the slow request is a local timer, not network capture.
|
|
10082
|
-
At Watch on your phone, optionally open an already paired Stim phone to see
|
|
10083
|
-
the tour workspace; phone setup and machine approval stay with you.
|
|
10084
|
-
Use stim status, stim logs --errors, and stim stats without Desktop.
|
|
10085
|
-
|
|
10086
|
-
${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")}
|
|
10087
|
-
|
|
10088
|
-
Finish removes only the tour worktree. Never use --force; report a refusal.
|
|
10089
|
-
With archive enabled, find the tour under Archived in Desktop or archived[]
|
|
10090
|
-
in stim status --json. Keep the base repository and branch. To delete them,
|
|
10091
|
-
read stim guide tutorial finish for commands to review and run yourself.`
|
|
10253
|
+
Replace {base}, {tour} and {second} with absolute paths: {base} is the clone,
|
|
10254
|
+
{tour} the first worktree and {second} the second. For Agent Actions, replace
|
|
10255
|
+
{stateDir} with agentDevice.stateDir from stim ios or stim status --json and
|
|
10256
|
+
{udid} with the workspace's ios.udid. For the optional machine step, replace
|
|
10257
|
+
{machine} with a machine you have already approved, or skip it. Clone with git
|
|
10258
|
+
clone https://github.com/${TUTORIAL_REPO}.git into a fresh folder outside any
|
|
10259
|
+
repository. Each change runs in its own worktree of that clone (stim guide
|
|
10260
|
+
agent); the clone itself is never run. Run each worktree on iOS with
|
|
10261
|
+
stim ios --remote local --remote-build local so it stays on this Mac.
|
|
10262
|
+
|
|
10263
|
+
${TUTORIAL_STEPS.map((step) => `${step.title}${step.optional ? " (optional)" : ""}\n\n${step.commands.length ? `\`\`\`sh\n${step.commands.join("\n")}\n\`\`\`` : "Ask your agent in your own words, or observe this step in Stim Desktop."}`).join("\n\n")}
|
|
10264
|
+
|
|
10265
|
+
Finish removes only the two tutorial worktrees, never the clone; stim guide tutorial finish says when --force is allowed for them.`
|
|
10092
10266
|
}
|
|
10093
10267
|
}
|
|
10094
10268
|
}
|
|
@@ -10172,7 +10346,7 @@ function renderIndex(version) {
|
|
|
10172
10346
|
return lines.join("\n");
|
|
10173
10347
|
}
|
|
10174
10348
|
function guideCommand(program, version, status = (running) => guideStatus({
|
|
10175
|
-
projectRoot: findProjectRoot
|
|
10349
|
+
projectRoot: findProjectRoot(process.cwd()),
|
|
10176
10350
|
running
|
|
10177
10351
|
})) {
|
|
10178
10352
|
program.command("guide [topic] [section]").description("Print reference documentation for THIS version of Stim (topics: " + topicNames().join(", ") + "). A topic with sections prints its section index; name a section to print only it, e.g. `stim guide errors STIM_NO_METRO` or `stim guide lifecycle builds`. Generated by the binary, so it cannot drift from the installed CLI.").action(async (topic, section) => {
|