stim 1.18.0 → 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 +1 -1
- package/dist/{activity-BxnkDZDi.mjs → activity-BSw0x84R.mjs} +3 -3
- package/dist/{agent-device-usage-output-CZ66YA4T.mjs → agent-device-usage-output-Bjxu_VmX.mjs} +5 -5
- package/dist/{android-BvwuMUJ9.mjs → android-BXc6BmEi.mjs} +507 -347
- package/dist/{android-DC3ZLuw0.mjs → android-BdQC6zs1.mjs} +6 -6
- package/dist/{android-BKA16DTf.mjs → android-BvV1gGdN.mjs} +3 -3
- package/dist/{android-CBwCNicO.mjs → android-DE5g_5gv.mjs} +2 -2
- package/dist/{android-cas-BBFyr9CA.mjs → android-cas-C87geAg-.mjs} +3 -3
- package/dist/android-cas-compiler.mjs +1 -1
- package/dist/{api-D0VtFhqk.mjs → api-DIf_s86s.mjs} +5 -7
- package/dist/api-run.mjs +8 -8
- package/dist/api.d.mts +8 -2
- package/dist/api.mjs +1 -1
- package/dist/{app-install-D-PUENug.mjs → app-install-CFI4ptUL.mjs} +33 -13
- package/dist/{web-DTU0uiWj.mjs → browser-web-CDhqQxhv.mjs} +134 -172
- package/dist/{budget-DIhL2BKx.mjs → budget-Dz-kyXLv.mjs} +15 -202
- package/dist/{build-facts-DcLv2HSd.d.mts → build-facts-B87vo-Im.d.mts} +32 -1
- package/dist/{cache-manifest-BI7ozfmH.mjs → cache-manifest-DiwKSyv9.mjs} +1 -1
- package/dist/cache-manifest.mjs +1 -1
- package/dist/{chrome-DJnmnS5q.mjs → chrome-BTUOo8p2.mjs} +1 -1
- package/dist/{cli-DNJMwADx.mjs → cli-C9Zvgbdc.mjs} +20 -20
- package/dist/cli.mjs +1 -1
- package/dist/{client-DzhapoMB.mjs → client-CuUo8Nvg.mjs} +248 -23
- package/dist/collector-run.d.mts +6 -0
- package/dist/collector-run.mjs +6 -6
- package/dist/{command-output-CBtzyIgF.mjs → command-output-Bt8ygHE7.mjs} +1 -1
- package/dist/{config-CgYMahz7.mjs → config-DC5jEtd1.mjs} +6 -2
- package/dist/{created-devices-DKbeWsiK.mjs → created-devices-D0s26Hx5.mjs} +2 -2
- package/dist/{dependency-state-xKoOypBm.mjs → dependency-state-DKaS1OM9.mjs} +2 -2
- package/dist/{detached-entry-BLfcgc4O.mjs → detached-entry-pB_IPfV4.mjs} +1 -1
- package/dist/{dev-client-CLnIH02x.mjs → dev-client-CDY_zAmD.mjs} +2 -2
- package/dist/{device-D7oCHGWi.mjs → device-BHw0WlHl.mjs} +8 -8
- package/dist/{device-capacity-EUOOYr8T.mjs → device-capacity-BoN1jQkO.mjs} +23 -12
- package/dist/device-host-worker.mjs +14 -14
- package/dist/{gradle-C6GKnunQ.mjs → device-ios-B2ruohE-.mjs} +659 -105
- package/dist/{device-lease-Co-EwifR.mjs → device-lease-Cu7PtQpZ.mjs} +5 -5
- package/dist/{device-lease-run-w1xhCqIW.mjs → device-lease-run-D38SnZtt.mjs} +4 -4
- package/dist/{device-pool-F5WvRPtD.mjs → device-pool-CP4rAsHx.mjs} +3 -3
- package/dist/{device-remote-tHbd1fKy.mjs → device-remote-B5GiUifk.mjs} +15 -16
- package/dist/{doctor-B9ckYUoU.mjs → doctor-PRJH94v6.mjs} +53 -29
- package/dist/doctor-watchman-wZhyTdp4.mjs +259 -0
- package/dist/{error-diagnostics-BRXOqDzG.mjs → error-diagnostics--v5HGOU0.mjs} +10 -10
- package/dist/{exec-DWhCZDGm.mjs → exec-i8GDPwq9.mjs} +32 -8
- package/dist/{gc-Dh6mXOKC.mjs → gc-CyHMcKcn.mjs} +19 -19
- package/dist/{guide-tm6igAaa.mjs → guide-dRaQJJk9.mjs} +409 -472
- package/dist/{hosted-android-Bv285t_t.mjs → hosted-android-Bs7fytj7.mjs} +3 -3
- package/dist/{hosted-client-C-rMtppF.mjs → hosted-client-C5G4h_OJ.mjs} +1 -1
- package/dist/{hosted-ios-D5mNmtj4.mjs → hosted-ios-Bik035eL.mjs} +1 -1
- package/dist/{hosted-logs-DN8Iz4mJ.mjs → hosted-logs-CI9Az9a4.mjs} +7 -7
- package/dist/{hosted-macos-B-wEjlwG.mjs → hosted-macos-DABm3ScO.mjs} +4 -4
- package/dist/{hosted-native-DeeOG8xZ.mjs → hosted-native-DbV2aUVw.mjs} +12 -8
- package/dist/{idle-shutdown-jjtxAn5C.mjs → idle-shutdown-DYiMBn34.mjs} +7 -7
- package/dist/{ios-B3SbOWdw.mjs → ios-BWbHFZVz.mjs} +5 -5
- package/dist/{ios-Bvbf0SGY.mjs → ios-DCrkgXEM.mjs} +1 -1
- package/dist/{ios-DNpdygiv.mjs → ios-DYUr9LGc.mjs} +7 -6
- package/dist/ios-ITYno9rO.mjs +2241 -0
- package/dist/{ios-device-vOaByMhw.mjs → ios-device-CJ_ZOGky.mjs} +3 -3
- package/dist/{ios-device-Bn7jWDB1.mjs → ios-device-CrW8aMb5.mjs} +1 -1
- package/dist/{ios-state-B2pES6Vs.mjs → ios-state-By0XuLTQ.mjs} +1 -1
- package/dist/{launch-verify-hskNcWkZ.mjs → launch-verify-DCveSE6d.mjs} +4 -4
- package/dist/{logs-1Ylc1Ri6.mjs → logs-Bao1HOMy.mjs} +10 -10
- package/dist/{macos-DF6C7wUU.mjs → macos-ciyBdTY5.mjs} +108 -130
- package/dist/macos-nIOATJmR.mjs +2 -0
- package/dist/macos-run.mjs +1 -1
- package/dist/maintenance-run.mjs +7 -7
- package/dist/manifest-CX0d1IkZ.mjs +148 -0
- package/dist/{metro-Dh8T4Ytd.mjs → metro-DI2AjPF_.mjs} +194 -41
- package/dist/{metro-gateway-CEUw9RMH.mjs → metro-gateway-BxpJLIwh.mjs} +2 -2
- package/dist/{plan-placement-CtxkCO54.mjs → miss-reason-CjQCOKqq.mjs} +180 -296
- package/dist/{named-ports-BFIz1Shz.mjs → named-ports-B4iWH4E-.mjs} +12 -5
- package/dist/{native-runtime-D_603JiC.mjs → native-runtime-BGxsbfny.mjs} +6 -6
- package/dist/offload-worker.d.mts +1 -29
- package/dist/offload-worker.mjs +9 -8
- package/dist/{ownership-D6Se7gg_.mjs → ownership-EB0ySdGK.mjs} +2 -2
- package/dist/{placement-log-BcPhRwR4.mjs → placement-log-Dxe34rJm.mjs} +7 -3
- package/dist/plan-placement-BVY59qa8.mjs +290 -0
- package/dist/{ports-BLTtfBq-.mjs → ports-ByPc0_97.mjs} +3 -3
- package/dist/{prebuild-juCyreaR.mjs → prebuild-BKJ3wJI1.mjs} +4 -4
- package/dist/{preview-D5V07Nrl.mjs → preview-BbSottmO.mjs} +16 -17
- package/dist/project-B5eQ1USs.mjs +612 -0
- package/dist/{pull-request-u4vp7zmK.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-BAWltsVt.mjs → recordings-Bv8McZN4.mjs} +1 -1
- package/dist/{reload-DvyWzVjJ.mjs → reload-rAPdqeRR.mjs} +13 -14
- package/dist/{remote-cache-BGg7aGIZ.mjs → remote-cache-N3hAEaAL.mjs} +4 -4
- package/dist/{run-vhtDgpHO.mjs → run-lAkle1-c.mjs} +2 -2
- package/dist/{server-bare-B9E7S1aR.mjs → server-bare-CkIjnegq.mjs} +3 -3
- package/dist/server-expo-DGsdUGX2.mjs +2 -0
- package/dist/{server-expo-DoBHhuzK.mjs → server-expo-n3tUi3Em.mjs} +3 -3
- package/dist/{settings-Byp2AQ_E.mjs → settings-2am7TSxp.mjs} +5 -6
- package/dist/{settings-CQyMElEP.mjs → settings-CxgngUZk.mjs} +767 -15
- package/dist/settings.schema.json +72 -6
- package/dist/{simslim-EHBQ2R4G.mjs → simslim-D9LkJxIO.mjs} +5 -5
- package/dist/{slot-launch-C1nqh4mu.mjs → slot-launch-HV_KtPIw.mjs} +6 -6
- package/dist/{stage-g_WqWeaE.mjs → stage-BgaZJVZY.mjs} +1 -1
- package/dist/{start-NTc1Ifqs.mjs → start-D8NbxNBb.mjs} +1 -1
- package/dist/{start-D-IeOyoK.mjs → start-DtxthMfI.mjs} +29 -22
- package/dist/{state-BG-aAk4k.mjs → state-D2KtnALG.mjs} +2 -2
- package/dist/{state-xnqpx3AD.mjs → state-Tp4uImDS.mjs} +4 -2
- package/dist/{stats-DY6ekz8F.mjs → stats-8EJ5ivFT.mjs} +6 -6
- package/dist/{status-DCjZeE-K.mjs → status-BOyp0CUl.mjs} +25 -27
- package/dist/{stim-desktop-Behws_fi.mjs → stim-desktop-C65jk_q2.mjs} +1 -1
- package/dist/{stim-installations-aR_WsNOA.mjs → stim-installations-DbmUjsLv.mjs} +1 -1
- package/dist/stop-D0UIpZuh.mjs +72 -0
- package/dist/{stop-DIJtD2GL.mjs → stop-DRipAg-e.mjs} +2 -2
- package/dist/{stop-BTjs3xbc.mjs → stop-zDlknFoz.mjs} +78 -31
- package/dist/supervisor-run.mjs +14 -14
- package/dist/{support-BaayeYCf.mjs → support-CNqYkV0y.mjs} +7 -7
- package/dist/{support-Bwvbrq5-.mjs → support-DsNaFg6D.mjs} +5 -5
- package/dist/swiftpm-macos-aJzPAj1t.mjs +119 -0
- package/dist/{toolchain-Bnyd_K3A.mjs → toolchain-C3xSoyUl.mjs} +27 -270
- package/dist/{trigger-CJMhwXe8.mjs → trigger-CAhmG-4s.mjs} +3 -3
- package/dist/trigger-CoalsMTV.mjs +2 -0
- package/dist/{warm-progress-CjB-MP6H.mjs → warm-progress-pFLB2e6P.mjs} +133 -36
- package/dist/{status-Dcr0Qexd.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 +7 -7
- package/dist/{workspace-state-C59dqG-Z.mjs → workspace-state-CR9X86xJ.mjs} +1 -1
- package/dist/{workspaces-xXItD8Dj.mjs → workspaces-Djb2gfi9.mjs} +102 -18
- package/dist/{worktree-BSYlofkq.mjs → worktree-BL4M1rhB.mjs} +45 -42
- package/dist/{worktree-BJfnU6r5.mjs → worktree-DV-A0LNL.mjs} +1 -1
- package/package.json +5 -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/device-ios-6MewKKnc.mjs +0 -412
- package/dist/guide-status-Bhf9SJXc.mjs +0 -125
- package/dist/ios-CPdkoKaa.mjs +0 -3953
- package/dist/macos-Ck7OBMNY.mjs +0 -2
- package/dist/native-run-B06FkmZx.mjs +0 -158
- package/dist/project-C3ChRLZy.mjs +0 -212
- package/dist/projects-DD7dBTCj.mjs +0 -92
- package/dist/server-expo-Db9HBUoa.mjs +0 -2
- package/dist/stop-BGhKMOuH.mjs +0 -48
- package/dist/trigger-DWAq9hkw.mjs +0 -2
- package/dist/web-B6_bFDn7.mjs +0 -2
- package/dist/worktree-CsKYexJM.mjs +0 -753
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as
|
|
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 = {
|
|
@@ -1432,8 +1432,9 @@ RULES
|
|
|
1432
1432
|
body: () => ` stim status --json
|
|
1433
1433
|
|
|
1434
1434
|
logs { dir, errorsSinceMarker }, or null without a log directory
|
|
1435
|
-
agentDevice { stateDir }: absolute workspace agent-device state
|
|
1436
|
-
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
|
|
1437
1438
|
no directory; agent-device creates it when used.
|
|
1438
1439
|
|
|
1439
1440
|
Each environment carries phase, where the workspace is in its lifecycle:
|
|
@@ -1749,7 +1750,10 @@ RULES
|
|
|
1749
1750
|
once the app is ready and covers waiting for the
|
|
1750
1751
|
device: its boot, adoption cleanup, or a physical
|
|
1751
1752
|
device's lease and connection check. A boot that
|
|
1752
|
-
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
|
|
1753
1757
|
--eas-profile run has no cache lookup, so its outcome
|
|
1754
1758
|
stays the project's most recent one until install.
|
|
1755
1759
|
A stim macos run enters only prepare, compile (SwiftPM,
|
|
@@ -1942,7 +1946,8 @@ RULES
|
|
|
1942
1946
|
disk (environment) { worktreeBytes, nodeModulesBytes, buildBytes,
|
|
1943
1947
|
measuredAt }
|
|
1944
1948
|
worktreeBytes the linked worktree, or else the checkout holding the
|
|
1945
|
-
workspace, node_modules included
|
|
1949
|
+
workspace, node_modules included; the linked
|
|
1950
|
+
worktrees nested inside a checkout are not counted
|
|
1946
1951
|
nodeModulesBytes node_modules at that root and at the workspace path;
|
|
1947
1952
|
part of worktreeBytes
|
|
1948
1953
|
buildBytes Stim's folder for the workspace: Xcode derived data,
|
|
@@ -1952,11 +1957,13 @@ RULES
|
|
|
1952
1957
|
disk (device) { bytes, measuredAt }: the simulator's data folder
|
|
1953
1958
|
under CoreSimulator/Devices, or the AVD's .avd folder
|
|
1954
1959
|
|
|
1955
|
-
\`status --watch\` runs one du at a time
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
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.
|
|
1960
1967
|
|
|
1961
1968
|
An environment carries agents when coding-agent sessions work in it, most
|
|
1962
1969
|
recently active first:
|
|
@@ -2252,8 +2259,9 @@ HOW A RUN IS COUNTED (\`stats\`)
|
|
|
2252
2259
|
|
|
2253
2260
|
DEVICE PLACEMENT (\`ios|android --remote auto\`)
|
|
2254
2261
|
Auto runs include devicePlacement: { decision, reason, machine? } in the run
|
|
2255
|
-
facts, lastBuilds and build history. decision is "local", "hosted"
|
|
2256
|
-
"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
|
|
2257
2265
|
optional devicePlacement appears on the status device entry for each slot.
|
|
2258
2266
|
Hosted host facts include selected: "auto" or the named machine, and reason
|
|
2259
2267
|
for automatic placement. Plain status prints (auto: <reason>) after the host.
|
|
@@ -2600,9 +2608,15 @@ then 8081, without probing or reserving; an invalid pin still refuses.
|
|
|
2600
2608
|
|
|
2601
2609
|
New allocations scan TCP ports 8900-8999. They skip registry reservations
|
|
2602
2610
|
and existing listeners, announcing occupied ports and upward retries on
|
|
2603
|
-
stderr.
|
|
2604
|
-
|
|
2605
|
-
|
|
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.
|
|
2606
2620
|
|
|
2607
2621
|
The machine registry, under STIM_HOME, serializes allocation and cleanup.
|
|
2608
2622
|
The workspace is the nearest package.json directory, resolved through
|
|
@@ -2625,6 +2639,7 @@ ports lists named labels and ports, plus Metro marked managed.
|
|
|
2625
2639
|
ports stop [label] kills TCP listeners on those named ports and releases
|
|
2626
2640
|
the allocations. It sends SIGTERM, waits two seconds, then SIGKILL if needed;
|
|
2627
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.
|
|
2628
2643
|
It prints the PID and command (the image name on Windows) for each stopped
|
|
2629
2644
|
process. The listener's cwd can be anywhere: the named reservation is
|
|
2630
2645
|
permission to stop that listener.
|
|
@@ -2915,18 +2930,22 @@ remote Mac and a named --remote target write none:
|
|
|
2915
2930
|
stim logs --source placement --json
|
|
2916
2931
|
Fields: kind (build or device), platform, settings [{ key, value, from }] with
|
|
2917
2932
|
from flag, env, setting or default (remote.build and remote.buildMode for a
|
|
2918
|
-
build, ios.remote or android.remote
|
|
2933
|
+
build, ios.remote or android.remote, plus remote.easFallback when it is on, for
|
|
2934
|
+
a device), candidates [{ machine, code,
|
|
2919
2935
|
msg, detail? }], choice { machine, code, msg } with machine local when the run
|
|
2920
2936
|
stays on this Mac, and fallback { code, msg, machine? } when a remote Mac that
|
|
2921
2937
|
was meant to take the run did not (level warn). The msg is the same text the
|
|
2922
2938
|
placement: and build: phase lines print.
|
|
2923
2939
|
Candidate codes: accepted, unreachable, busy, disk, load, version-mismatch
|
|
2924
2940
|
(detail lists the toolchain parts: xcode, arch, jdk, ...), no-matching-device,
|
|
2925
|
-
declined, memory, no-capacity
|
|
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,
|
|
2926
2945
|
forced, this-mac-busy, this-mac-free, mode-off, local-selected, no-remote-mac,
|
|
2927
2946
|
unsupported; fallback codes: no-remote-mac-took-it, offload-failed, fallback.
|
|
2928
2947
|
Choice codes for a device: placed, sticky, this-mac-free, no-remote-mac,
|
|
2929
|
-
device-count-unknown, no-host-admits. Machine names appear in these records;
|
|
2948
|
+
device-count-unknown, no-host-admits, eas-fallback (machine eas). Machine names appear in these records;
|
|
2930
2949
|
they stay in the local logs. These records carry no tokens.
|
|
2931
2950
|
|
|
2932
2951
|
WHAT WRITES WHAT
|
|
@@ -3440,9 +3459,14 @@ Branch on the code, never on the message.`,
|
|
|
3440
3459
|
works. See stim guide macos.`
|
|
3441
3460
|
},
|
|
3442
3461
|
STIM_OFFLOAD_REFUSED: {
|
|
3443
|
-
summary: "the selected remote Mac cannot build this app",
|
|
3462
|
+
summary: "the selected remote Mac or automatic build pool cannot build this app",
|
|
3444
3463
|
body: () => `STIM_OFFLOAD_REFUSED
|
|
3445
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
|
+
|
|
3446
3470
|
A named --remote-build selection is strict. The message names the machine
|
|
3447
3471
|
and why it cannot take or finish the build: not configured or paired, approval
|
|
3448
3472
|
pending or denied, unreachable or changed pinned identity, incompatible
|
|
@@ -3888,7 +3912,9 @@ so a Debug run on one is wired to a LAN origin instead of localhost.`,
|
|
|
3888
3912
|
physical, hosted, remote, parked and other homes' devices and never deletes.
|
|
3889
3913
|
A failed reclaim is logged and skipped; the run keeps waiting.
|
|
3890
3914
|
Stop an environment (\`stim stop\`), pass a longer \`--wait <seconds>\`,
|
|
3891
|
-
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
|
|
3892
3918
|
time; status JSON exposes build.waitingFor independently of phase.
|
|
3893
3919
|
Stats records capacityWaits for waits and capacityRefusals for this code.
|
|
3894
3920
|
See \`guide lifecycle concurrency\`.`
|
|
@@ -4175,6 +4201,17 @@ captured" (in metro.ndjson, bare RN)
|
|
|
4175
4201
|
printed the last lines of the global workspace logs/supervisor.log above this -- read
|
|
4176
4202
|
them. A cold Metro on a large graph can genuinely need more than the default
|
|
4177
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.`
|
|
4178
4215
|
},
|
|
4179
4216
|
STIM_SUPERVISOR_EXITED: {
|
|
4180
4217
|
summary: "the dev server failed outright; the quoted supervisor.log tail is the real error",
|
|
@@ -4959,6 +4996,46 @@ WAITING FOR A CHANGE
|
|
|
4959
4996
|
still running, which it notices at the next change.
|
|
4960
4997
|
Without --json it reprints the human view on change.`,
|
|
4961
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
|
+
},
|
|
4962
5039
|
"hosted-android": {
|
|
4963
5040
|
summary: "Android on a named approved Mac: strict placement, private Metro, status and stop",
|
|
4964
5041
|
body: () => `ANDROID ON A HOSTING MAC
|
|
@@ -5125,7 +5202,10 @@ there is a free concurrency.maxDevices slot (0 means unlimited), no device
|
|
|
5125
5202
|
waiter ahead, normal host memory pressure, no budget shortfall,
|
|
5126
5203
|
and 5-minute load per core below server.maxLoadPerCore. Without remote.machines,
|
|
5127
5204
|
auto is local. An unknown local device count also stays local; boot admission
|
|
5128
|
-
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.
|
|
5129
5209
|
|
|
5130
5210
|
Otherwise Stim asks every approved remote.machines host in parallel, with a
|
|
5131
5211
|
3-second probe timeout and this run's model/runtime or image/profile selectors.
|
|
@@ -5136,9 +5216,24 @@ Hosts rank by the build preference (flag > STIM_REMOTE_BUILD > remote.build)
|
|
|
5136
5216
|
when it names a machine, then lowest load, most free memory and remote.machines order. Automatic build
|
|
5137
5217
|
offload resolves later, after the device architecture is known.
|
|
5138
5218
|
|
|
5139
|
-
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
|
|
5140
5234
|
slot queue; --no-wait and --wait 0 refuse with STIM_AT_CAPACITY. The placement
|
|
5141
|
-
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.
|
|
5142
5237
|
The same decision, with a reason code per host, is a src: placement record in
|
|
5143
5238
|
stim logs (stim guide logs).
|
|
5144
5239
|
A recorded hosted session wins over load; a live local owned slot stays here.
|
|
@@ -5899,8 +5994,9 @@ PREDICTING THE NEXT BUILD (--plan)
|
|
|
5899
5994
|
this Mac and a listed Mac would build for another architecture, placement
|
|
5900
5995
|
says so, since that Mac's key differs. The plan only predicts the key: a
|
|
5901
5996
|
run reads the architecture of the device it actually gets. A Mac that does
|
|
5902
|
-
not answer,
|
|
5903
|
-
|
|
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
|
|
5904
6000
|
refuses the experimental compiler CAS with STIM_BAD_ARG, and refuses with
|
|
5905
6001
|
STIM_NO_DEVICE when no system image is installed.
|
|
5906
6002
|
|
|
@@ -7873,7 +7969,23 @@ THE ONE CASE GC WILL NOT REAP
|
|
|
7873
7969
|
|
|
7874
7970
|
--older-than does not apply to these kinds and is refused with
|
|
7875
7971
|
STIM_BAD_ARG. With STIM_HOME set, gc skips them: they are machine-global.
|
|
7876
|
-
\`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.`
|
|
7877
7989
|
},
|
|
7878
7990
|
disk: {
|
|
7879
7991
|
summary: "disk usage, workspace build outputs, logs and device recordings, AVD and build-log sizes, the data partition, trimming the shared caches",
|
|
@@ -8130,7 +8242,8 @@ KEYS STIM READS
|
|
|
8130
8242
|
ios.remote "proxy", "eas", or a named approved Mac from
|
|
8131
8243
|
remote.machines, with the same meaning as --remote.
|
|
8132
8244
|
"auto" places on an approved Mac when this Mac is full or
|
|
8133
|
-
busy. Unset runs here
|
|
8245
|
+
busy. Unset runs here; "local" runs here even when a
|
|
8246
|
+
lower layer says otherwise. See lifecycle hosted-ios.
|
|
8134
8247
|
ios.simslimProfile a SimSlim JSON profile under the app directory,
|
|
8135
8248
|
at most 64 KiB. Install the
|
|
8136
8249
|
external tool once with
|
|
@@ -8272,8 +8385,23 @@ ${ANDROID_AVD_CONFIG_HELP.map((line) => ` ${line}`).joi
|
|
|
8272
8385
|
means the debug keystore's fixed "android".
|
|
8273
8386
|
android.remote "proxy", "eas", or a named approved Mac in
|
|
8274
8387
|
remote.machines; "auto" places on an approved Mac when
|
|
8275
|
-
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.
|
|
8276
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.
|
|
8277
8405
|
metro.tunnel selects how a remote device reaches this workspace's
|
|
8278
8406
|
Metro after remote intent exists. Plain \`start\` stays
|
|
8279
8407
|
local. For Expo and bare React Native, "auto" (default)
|
|
@@ -8522,8 +8650,9 @@ To show the booted simulator in Stim Desktop and open no simulator window:
|
|
|
8522
8650
|
|
|
8523
8651
|
{ "iosSimulatorApp": "stim-desktop" }
|
|
8524
8652
|
|
|
8525
|
-
Stim Desktop
|
|
8526
|
-
|
|
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.
|
|
8527
8656
|
When Stim Desktop is not running, Stim starts it without the command's
|
|
8528
8657
|
\`STIM_HOME\`, so it reads the same Stim home as when you open it yourself.
|
|
8529
8658
|
It shows only devices from that home: under another \`STIM_HOME\`, pick
|
|
@@ -8552,7 +8681,8 @@ headlessly and show it in Stim Desktop:
|
|
|
8552
8681
|
{ "androidEmulatorApp": "stim-desktop" }
|
|
8553
8682
|
|
|
8554
8683
|
Stim then starts the emulator with \`-no-window -gpu host\` and opens
|
|
8555
|
-
\`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
|
|
8556
8686
|
frames and sends input over the emulator's gRPC endpoint. The setting applies
|
|
8557
8687
|
only when Stim boots the emulator: one that is already running keeps its
|
|
8558
8688
|
current display until it next boots, and physical devices are unaffected.
|
|
@@ -8635,6 +8765,32 @@ simulator sessions on, by MagicDNS name with an optional serve port (default
|
|
|
8635
8765
|
stim settings set remote.machines '["janics-mac-mini"]'
|
|
8636
8766
|
stim doctor --fix
|
|
8637
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
|
+
|
|
8638
8794
|
A remote Mac is used for a capability only after it grants that approval. Build
|
|
8639
8795
|
and device-host approvals are separate: a Mac in the list that never granted
|
|
8640
8796
|
one is not an error, it is not used for that capability, and doctor reports
|
|
@@ -8726,7 +8882,7 @@ value is unset. Trimmed auto/local are case-insensitive. Machine names match
|
|
|
8726
8882
|
configured names case-insensitively, with port 7443 when omitted; reports use
|
|
8727
8883
|
the configured entry.
|
|
8728
8884
|
|
|
8729
|
-
auto follows remote.buildMode
|
|
8885
|
+
auto follows remote.buildMode, considering only enabled automatic pool members
|
|
8730
8886
|
local builds only on this Mac for this invocation
|
|
8731
8887
|
name requires a matching entry in remote.machines, already paired and
|
|
8732
8888
|
approved for builds; ignores remote.buildMode and this Mac's load/slot gating
|
|
@@ -8765,7 +8921,7 @@ emulator debug build or a stim macos SwiftPM Debug build compiles:
|
|
|
8765
8921
|
Mac's. A Mac too old to report its load counts only while every
|
|
8766
8922
|
slot here is busy.
|
|
8767
8923
|
force on a remote Mac whenever one accepts it
|
|
8768
|
-
off
|
|
8924
|
+
off here when local remains enabled in the automatic build pool
|
|
8769
8925
|
|
|
8770
8926
|
Load per core is the 5-minute load average divided by the CPU count; a Mac's
|
|
8771
8927
|
native builds are its Stim runs in prebuild, pods or compile on that Mac, not
|
|
@@ -9416,18 +9572,39 @@ executables, including Stim Desktop's sim-fold helper, are not built.
|
|
|
9416
9572
|
|
|
9417
9573
|
stim macos # fixed SwiftPM Debug build, then launch
|
|
9418
9574
|
stim macos --json # one launch record; progress goes to stderr
|
|
9575
|
+
stim macos --plan --json # validate the next build without running it
|
|
9419
9576
|
stim status --json # environments[].macos and build state
|
|
9420
9577
|
stim logs --source build
|
|
9421
9578
|
stim logs --errors
|
|
9422
9579
|
stim stop # stop this workspace's owned app and supervisor
|
|
9423
9580
|
|
|
9424
|
-
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
|
|
9425
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.
|
|
9426
9590
|
Local stim macos starts the app in the background without activating it or
|
|
9427
9591
|
changing focus: it sets STIM_BACKGROUND_LAUNCH=1 in the app's environment, which
|
|
9428
|
-
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.
|
|
9429
9594
|
Hosted launches (macos --remote) do not set it.
|
|
9430
|
-
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.
|
|
9431
9608
|
A failed build records its error and compiler output without launching an app.
|
|
9432
9609
|
While it runs, stim status --json reports it as environments[].build with
|
|
9433
9610
|
platform "macos": phase prepare, compile, install, then launch, and during
|
|
@@ -9453,7 +9630,10 @@ refuse before stopping the app or changing its build record. See stim guide erro
|
|
|
9453
9630
|
|
|
9454
9631
|
With remote.build auto, remote.buildMode places these SwiftPM Debug builds:
|
|
9455
9632
|
auto builds here while this Mac has capacity, force uses an approved build
|
|
9456
|
-
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
|
|
9457
9637
|
and approve build access as described in stim guide settings. The worker needs
|
|
9458
9638
|
matching Stim, CPU architecture, Xcode and macOS SDK, plus network access to
|
|
9459
9639
|
fetch package dependencies the first time. It keeps SwiftPM dependencies in a
|
|
@@ -9771,290 +9951,147 @@ this workspace's app. Do not change permissions or use custom build scripts."
|
|
|
9771
9951
|
};
|
|
9772
9952
|
//#endregion
|
|
9773
9953
|
//#region src/guide/tutorial-data.ts
|
|
9774
|
-
const
|
|
9775
|
-
|
|
9776
|
-
template: "expo-template-blank@58.0.15"
|
|
9777
|
-
};
|
|
9778
|
-
const TUTORIAL_FILES = {
|
|
9779
|
-
"App.js": `import { useEffect, useState } from 'react';
|
|
9780
|
-
import { Pressable, StyleSheet, Text, View } from 'react-native';
|
|
9781
|
-
import { StatusBar } from 'expo-status-bar';
|
|
9782
|
-
import { TITLE_COLOR } from './theme';
|
|
9783
|
-
|
|
9784
|
-
const TAG = '[stim:tutorial]';
|
|
9785
|
-
|
|
9786
|
-
function Button({ label, onPress }) {
|
|
9787
|
-
return (
|
|
9788
|
-
<Pressable accessibilityRole="button" onPress={onPress} style={styles.button}>
|
|
9789
|
-
<Text style={styles.buttonText}>{label}</Text>
|
|
9790
|
-
</Pressable>
|
|
9791
|
-
);
|
|
9792
|
-
}
|
|
9793
|
-
|
|
9794
|
-
export default function App() {
|
|
9795
|
-
const [note, setNote] = useState('');
|
|
9796
|
-
|
|
9797
|
-
useEffect(() => {
|
|
9798
|
-
console.log(\`\${TAG} title color=\${TITLE_COLOR}\`);
|
|
9799
|
-
}, [TITLE_COLOR]);
|
|
9800
|
-
|
|
9801
|
-
const logError = () => {
|
|
9802
|
-
console.error(\`\${TAG} error-button test error\`);
|
|
9803
|
-
setNote('Logged an error.');
|
|
9804
|
-
};
|
|
9805
|
-
|
|
9806
|
-
const crash = () => {
|
|
9807
|
-
setTimeout(() => {
|
|
9808
|
-
throw new Error(\`\${TAG} crash-button uncaught test error\`);
|
|
9809
|
-
}, 0);
|
|
9810
|
-
};
|
|
9811
|
-
|
|
9812
|
-
const slowRequest = async () => {
|
|
9813
|
-
const started = Date.now();
|
|
9814
|
-
setNote('Waiting 3 seconds...');
|
|
9815
|
-
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
9816
|
-
const elapsed = Date.now() - started;
|
|
9817
|
-
console.warn(\`\${TAG} slow-request \${elapsed}ms\`);
|
|
9818
|
-
setNote(\`Slow request took \${elapsed}ms.\`);
|
|
9819
|
-
};
|
|
9820
|
-
|
|
9821
|
-
return (
|
|
9822
|
-
<View style={styles.container}>
|
|
9823
|
-
<Text style={[styles.title, { color: TITLE_COLOR }]}>Stim Tutorial</Text>
|
|
9824
|
-
<Text style={styles.body}>Tap a button, then look at Stim Desktop > Logs.</Text>
|
|
9825
|
-
<Button label="Log an error" onPress={logError} />
|
|
9826
|
-
<Button label="Crash me" onPress={crash} />
|
|
9827
|
-
<Button label="Slow request" onPress={slowRequest} />
|
|
9828
|
-
<Text style={styles.note}>{note}</Text>
|
|
9829
|
-
<StatusBar style="auto" />
|
|
9830
|
-
</View>
|
|
9831
|
-
);
|
|
9832
|
-
}
|
|
9833
|
-
|
|
9834
|
-
const styles = StyleSheet.create({
|
|
9835
|
-
container: { flex: 1, backgroundColor: '#fff', alignItems: 'center', justifyContent: 'center', padding: 24 },
|
|
9836
|
-
title: { fontSize: 32, fontWeight: '700', marginBottom: 8 },
|
|
9837
|
-
body: { fontSize: 16, color: '#4b5563', textAlign: 'center', marginBottom: 24 },
|
|
9838
|
-
button: { backgroundColor: '#111827', borderRadius: 10, paddingVertical: 14, paddingHorizontal: 28, marginBottom: 12 },
|
|
9839
|
-
buttonText: { color: '#fff', fontSize: 17, fontWeight: '600' },
|
|
9840
|
-
note: { marginTop: 12, fontSize: 15, color: '#374151' },
|
|
9841
|
-
});
|
|
9842
|
-
`,
|
|
9843
|
-
"theme.js": `export const TITLE_COLOR = '#1f2937';
|
|
9844
|
-
`,
|
|
9845
|
-
"app.json": `{
|
|
9846
|
-
"expo": {
|
|
9847
|
-
"name": "Stim Tutorial",
|
|
9848
|
-
"slug": "stim-tutorial",
|
|
9849
|
-
"version": "1.0.0",
|
|
9850
|
-
"orientation": "portrait",
|
|
9851
|
-
"icon": "./assets/icon.png",
|
|
9852
|
-
"userInterfaceStyle": "light",
|
|
9853
|
-
"ios": {
|
|
9854
|
-
"supportsTablet": true,
|
|
9855
|
-
"bundleIdentifier": "dev.stim.tutorial"
|
|
9856
|
-
},
|
|
9857
|
-
"android": {
|
|
9858
|
-
"adaptiveIcon": {
|
|
9859
|
-
"backgroundColor": "#E6F4FE",
|
|
9860
|
-
"foregroundImage": "./assets/android-icon-foreground.png",
|
|
9861
|
-
"backgroundImage": "./assets/android-icon-background.png",
|
|
9862
|
-
"monochromeImage": "./assets/android-icon-monochrome.png"
|
|
9863
|
-
},
|
|
9864
|
-
"package": "dev.stim.tutorial"
|
|
9865
|
-
},
|
|
9866
|
-
"web": {
|
|
9867
|
-
"favicon": "./assets/favicon.png"
|
|
9868
|
-
},
|
|
9869
|
-
"extra": {
|
|
9870
|
-
"stimTutorial": 1
|
|
9871
|
-
}
|
|
9872
|
-
}
|
|
9873
|
-
}
|
|
9874
|
-
`,
|
|
9875
|
-
".gitignore": `/ios
|
|
9876
|
-
/android
|
|
9877
|
-
/tutorial*.ad
|
|
9878
|
-
/tutorial*.png
|
|
9879
|
-
`
|
|
9880
|
-
};
|
|
9881
|
-
const TUTORIAL_PROMPTS = {
|
|
9882
|
-
begin: "Run the Stim tutorial.",
|
|
9883
|
-
rebuild: "Continue the Stim tutorial: rebuild",
|
|
9884
|
-
agent: "Continue the Stim tutorial: agent",
|
|
9885
|
-
refresh: "Continue the Stim tutorial: refresh",
|
|
9886
|
-
machine: "Continue the Stim tutorial: machine",
|
|
9887
|
-
finish: "Continue the Stim tutorial: finish"
|
|
9888
|
-
};
|
|
9954
|
+
const TUTORIAL_REPO = "appandflow/stim-tutorial";
|
|
9955
|
+
const TUTORIAL_BUNDLE_ID = "dev.stim.tutorial";
|
|
9889
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
|
+
};
|
|
9890
9969
|
const TUTORIAL_STEPS = [
|
|
9891
9970
|
{
|
|
9892
9971
|
id: "begin",
|
|
9893
|
-
title: "
|
|
9972
|
+
title: "Get the Test App",
|
|
9894
9973
|
who: "agent",
|
|
9895
9974
|
optional: false,
|
|
9896
|
-
|
|
9975
|
+
ask: TUTORIAL_ASKS.begin,
|
|
9897
9976
|
section: "run",
|
|
9898
|
-
|
|
9899
|
-
"base=\"{base}\"",
|
|
9900
|
-
"mkdir -p \"${base%/*}\"",
|
|
9901
|
-
"if [ ! -e \"$base\" ]; then",
|
|
9902
|
-
"cd \"${base%/*}\"",
|
|
9903
|
-
"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",
|
|
9904
|
-
`npx --yes create-expo-app@${TUTORIAL_PINS.createExpoApp} stim-tutorial --template ${TUTORIAL_PINS.template} --no-install --no-agents-md --yes`,
|
|
9905
|
-
"cd \"$base\"",
|
|
9906
|
-
"stim guide tutorial app",
|
|
9907
|
-
...Object.entries(TUTORIAL_FILES).flatMap(([name, content]) => [`cat ${name === ".gitignore" ? ">>" : ">"} ${name} <<'STIM_TUTORIAL_EOF'`].concat(content.trimEnd().split("\n"), "STIM_TUTORIAL_EOF")),
|
|
9908
|
-
"npm install --prefer-offline",
|
|
9909
|
-
"npm pkg set scripts.ios=\"expo run:ios\" scripts.android=\"expo run:android\"",
|
|
9910
|
-
"git init",
|
|
9911
|
-
"git add -A",
|
|
9912
|
-
"git -c user.name=Stim -c user.email=stim@localhost -c commit.gpgsign=false commit -m \"Stim tutorial\"",
|
|
9913
|
-
"else",
|
|
9914
|
-
"cd \"$base\"",
|
|
9915
|
-
`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); }'`,
|
|
9916
|
-
`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); }'`,
|
|
9917
|
-
"fi"
|
|
9918
|
-
]
|
|
9977
|
+
commands: []
|
|
9919
9978
|
},
|
|
9920
9979
|
{
|
|
9921
|
-
id: "
|
|
9922
|
-
title: "
|
|
9980
|
+
id: "build",
|
|
9981
|
+
title: "Make a Change",
|
|
9923
9982
|
who: "you",
|
|
9924
9983
|
optional: false,
|
|
9925
|
-
|
|
9984
|
+
ask: null,
|
|
9926
9985
|
section: null,
|
|
9927
|
-
|
|
9986
|
+
commands: []
|
|
9928
9987
|
},
|
|
9929
9988
|
{
|
|
9930
|
-
id: "
|
|
9931
|
-
title: "
|
|
9989
|
+
id: "parallel",
|
|
9990
|
+
title: "Change It Again in Parallel",
|
|
9932
9991
|
who: "you",
|
|
9933
9992
|
optional: false,
|
|
9934
|
-
|
|
9993
|
+
ask: null,
|
|
9935
9994
|
section: null,
|
|
9936
|
-
|
|
9937
|
-
"cd \"{base}\"",
|
|
9938
|
-
"git worktree add -B stim-tutorial/tour \"{tour}\" HEAD",
|
|
9939
|
-
"cd \"{tour}\"",
|
|
9940
|
-
"stim worktree warm",
|
|
9941
|
-
"stim guide agent",
|
|
9942
|
-
"stim doctor --platform ios",
|
|
9943
|
-
"stim start",
|
|
9944
|
-
"stim ios"
|
|
9945
|
-
]
|
|
9946
|
-
},
|
|
9947
|
-
{
|
|
9948
|
-
id: "rebuild",
|
|
9949
|
-
title: "Rebuild from Cache",
|
|
9950
|
-
who: "agent",
|
|
9951
|
-
optional: false,
|
|
9952
|
-
prompt: TUTORIAL_PROMPTS.rebuild,
|
|
9953
|
-
section: "rebuild",
|
|
9954
|
-
manual: [
|
|
9955
|
-
"cd \"{tour}\"",
|
|
9956
|
-
"stim ios",
|
|
9957
|
-
"stim status --json"
|
|
9958
|
-
]
|
|
9995
|
+
commands: []
|
|
9959
9996
|
},
|
|
9960
9997
|
{
|
|
9961
9998
|
id: "device",
|
|
9962
9999
|
title: "Live View and Control",
|
|
9963
10000
|
who: "you",
|
|
9964
|
-
optional:
|
|
9965
|
-
|
|
9966
|
-
section: null,
|
|
9967
|
-
manual: ["stim status"]
|
|
9968
|
-
},
|
|
9969
|
-
{
|
|
9970
|
-
id: "logs",
|
|
9971
|
-
title: "App Logs",
|
|
9972
|
-
who: "you",
|
|
9973
|
-
optional: false,
|
|
9974
|
-
prompt: null,
|
|
10001
|
+
optional: true,
|
|
10002
|
+
ask: null,
|
|
9975
10003
|
section: null,
|
|
9976
|
-
|
|
10004
|
+
commands: ["stim status"]
|
|
9977
10005
|
},
|
|
9978
10006
|
{
|
|
9979
10007
|
id: "agent",
|
|
9980
10008
|
title: "Agent Actions and Replay",
|
|
9981
10009
|
who: "agent",
|
|
9982
|
-
optional:
|
|
9983
|
-
|
|
9984
|
-
section:
|
|
9985
|
-
|
|
10010
|
+
optional: true,
|
|
10011
|
+
ask: TUTORIAL_ASKS.agent,
|
|
10012
|
+
section: null,
|
|
10013
|
+
commands: [
|
|
9986
10014
|
"cd \"{tour}\"",
|
|
9987
10015
|
"export AGENT_DEVICE_STATE_DIR=\"{stateDir}\"",
|
|
9988
|
-
|
|
9989
|
-
"read -r iosUdid",
|
|
9990
|
-
"agent-device open dev.stim.tutorial --platform ios --udid \"$iosUdid\" --save-script=tutorial.ad",
|
|
9991
|
-
"agent-device react-native dismiss-overlay || true",
|
|
9992
|
-
`agent-device press 'label="Log an error"' --settle`,
|
|
10016
|
+
`agent-device open ${TUTORIAL_BUNDLE_ID} --platform ios --udid {udid}`,
|
|
9993
10017
|
"agent-device screenshot tutorial.png",
|
|
9994
10018
|
"agent-device close",
|
|
9995
|
-
"grep -v -e 'target-v1' -e 'dismiss-overlay' tutorial.ad > tutorial-replay.ad",
|
|
9996
|
-
"agent-device replay tutorial-replay.ad --platform ios --udid \"$iosUdid\"",
|
|
9997
10019
|
"stim logs --source agent --tail 10"
|
|
9998
10020
|
]
|
|
9999
10021
|
},
|
|
10000
10022
|
{
|
|
10001
|
-
id: "
|
|
10002
|
-
title: "
|
|
10003
|
-
who: "
|
|
10004
|
-
optional:
|
|
10005
|
-
|
|
10006
|
-
section:
|
|
10007
|
-
|
|
10008
|
-
"cd \"{tour}\"",
|
|
10009
|
-
"cat > theme.js <<'STIM_TUTORIAL_EOF'",
|
|
10010
|
-
"export const TITLE_COLOR = '#7c3aed';",
|
|
10011
|
-
"STIM_TUTORIAL_EOF",
|
|
10012
|
-
"sleep 6",
|
|
10013
|
-
"stim logs --errors",
|
|
10014
|
-
"stim logs --grep 'title color'"
|
|
10015
|
-
]
|
|
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"]
|
|
10016
10030
|
},
|
|
10017
10031
|
{
|
|
10018
10032
|
id: "phone",
|
|
10019
10033
|
title: "Watch on Your Phone",
|
|
10020
10034
|
who: "you",
|
|
10021
10035
|
optional: true,
|
|
10022
|
-
|
|
10036
|
+
ask: null,
|
|
10023
10037
|
section: null,
|
|
10024
|
-
|
|
10038
|
+
commands: []
|
|
10025
10039
|
},
|
|
10026
10040
|
{
|
|
10027
10041
|
id: "machine",
|
|
10028
10042
|
title: "Build on Another Mac",
|
|
10029
10043
|
who: "both",
|
|
10030
10044
|
optional: true,
|
|
10031
|
-
|
|
10032
|
-
section:
|
|
10033
|
-
|
|
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
|
+
]
|
|
10034
10064
|
},
|
|
10035
10065
|
{
|
|
10036
10066
|
id: "finish",
|
|
10037
10067
|
title: "Finish and Archive",
|
|
10038
10068
|
who: "agent",
|
|
10039
10069
|
optional: false,
|
|
10040
|
-
|
|
10070
|
+
ask: TUTORIAL_ASKS.finish,
|
|
10041
10071
|
section: "finish",
|
|
10042
|
-
|
|
10043
|
-
"git -C \"{tour}\" checkout -- theme.js",
|
|
10072
|
+
commands: [
|
|
10044
10073
|
"cd \"{tour}\"",
|
|
10045
10074
|
"stim stop",
|
|
10075
|
+
"cd \"{second}\"",
|
|
10076
|
+
"stim stop",
|
|
10046
10077
|
"cd \"{base}\"",
|
|
10047
|
-
"stim worktree remove \"{tour}\""
|
|
10078
|
+
"stim worktree remove \"{tour}\"",
|
|
10079
|
+
"stim worktree remove \"{second}\""
|
|
10048
10080
|
]
|
|
10049
10081
|
}
|
|
10050
10082
|
];
|
|
10051
10083
|
//#endregion
|
|
10052
10084
|
//#region src/guide/tutorial.ts
|
|
10053
|
-
const paths = `Use {base} = ~/stim-tutorial
|
|
10054
|
-
|
|
10055
|
-
|
|
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.`;
|
|
10056
10093
|
function commands(id) {
|
|
10057
|
-
return TUTORIAL_STEPS.find((step) => step.id === id).
|
|
10094
|
+
return TUTORIAL_STEPS.find((step) => step.id === id).commands.join("\n");
|
|
10058
10095
|
}
|
|
10059
10096
|
//#endregion
|
|
10060
10097
|
//#region src/guide/index.ts
|
|
@@ -10072,260 +10109,160 @@ const TOPICS = {
|
|
|
10072
10109
|
web: web_default,
|
|
10073
10110
|
macos: macos_default,
|
|
10074
10111
|
tutorial: {
|
|
10075
|
-
summary: "
|
|
10112
|
+
summary: "A cloned test app: a first build, a second change in parallel with a cache hit, and cleanup",
|
|
10076
10113
|
sectionHint: "run",
|
|
10077
10114
|
preamble: () => `STIM TUTORIAL
|
|
10078
10115
|
|
|
10079
|
-
|
|
10080
|
-
|
|
10081
|
-
|
|
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.
|
|
10082
10124
|
|
|
10083
|
-
|
|
10084
|
-
Stim Desktop, and give the next prompt. Pause never means stim stop.
|
|
10085
|
-
"${TUTORIAL_PROMPTS.begin}" means stim guide tutorial run.
|
|
10086
|
-
"Continue the Stim tutorial: <section>" means stim guide tutorial <section>.
|
|
10087
|
-
"${TUTORIAL_RESTART_PROMPT}" means stim guide tutorial restart.
|
|
10125
|
+
${local}
|
|
10088
10126
|
|
|
10089
|
-
|
|
10090
|
-
|
|
10091
|
-
|
|
10092
|
-
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.
|
|
10093
10130
|
|
|
10094
10131
|
${paths}
|
|
10095
10132
|
|
|
10096
|
-
|
|
10097
|
-
|
|
10098
|
-
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.`,
|
|
10099
10135
|
sections: {
|
|
10100
10136
|
run: {
|
|
10101
|
-
summary: "
|
|
10137
|
+
summary: "Clone the test app into a fresh folder and install its dependencies",
|
|
10102
10138
|
body: () => `RUN THE TUTORIAL
|
|
10103
10139
|
|
|
10104
10140
|
${paths}
|
|
10105
10141
|
|
|
10106
|
-
Folder safety:
|
|
10107
|
-
folder
|
|
10108
|
-
|
|
10109
|
-
folder
|
|
10110
|
-
Never
|
|
10111
|
-
|
|
10112
|
-
For a new app, first check the selected parent folder: create it if absent,
|
|
10113
|
-
then run git -C <parent> rev-parse --is-inside-work-tree. If it succeeds, the
|
|
10114
|
-
folder is inside another repository: stop and ask the user for another folder.
|
|
10115
|
-
Never git add in the user's repo. Then, from the parent folder, run:
|
|
10116
|
-
|
|
10117
|
-
npx --yes create-expo-app@${TUTORIAL_PINS.createExpoApp} stim-tutorial --template ${TUTORIAL_PINS.template} --no-install --no-agents-md --yes
|
|
10118
|
-
|
|
10119
|
-
Enter the base folder. Run stim guide tutorial app, write its three app files
|
|
10120
|
-
verbatim, and append its .gitignore lines. Then run:
|
|
10121
|
-
|
|
10122
|
-
npm install --prefer-offline
|
|
10123
|
-
npm pkg set scripts.ios="expo run:ios" scripts.android="expo run:android"
|
|
10124
|
-
git init
|
|
10125
|
-
git add -A
|
|
10126
|
-
git -c user.name=Stim -c user.email=stim@localhost -c commit.gpgsign=false commit -m "Stim tutorial"
|
|
10127
|
-
|
|
10128
|
-
On reuse, run git rev-parse --show-toplevel in the base folder before adding
|
|
10129
|
-
a worktree; if it does not equal the base folder, the folder is inside another
|
|
10130
|
-
repository: stop and ask the user for another folder.
|
|
10131
|
-
|
|
10132
|
-
Set the scripts before the commit because Expo prebuild rewrites them to
|
|
10133
|
-
expo run:ios and expo run:android; otherwise the dirty worktree blocks removal.
|
|
10134
|
-
On npm or network failure, report stderr and stop.
|
|
10135
|
-
|
|
10136
|
-
From the base folder, add the tour worktree and run:
|
|
10137
|
-
|
|
10138
|
-
git worktree add -B stim-tutorial/tour "{tour}" HEAD
|
|
10139
|
-
cd "{tour}"
|
|
10140
|
-
stim worktree warm
|
|
10141
|
-
stim guide agent
|
|
10142
|
-
|
|
10143
|
-
Apply the guide agent doctor rule before native work: if its STATUS block
|
|
10144
|
-
says doctor is due, run stim doctor --platform ios from the tour worktree and
|
|
10145
|
-
resolve its findings. No STATUS block means doctor is current. Then run:
|
|
10146
|
-
|
|
10147
|
-
stim start
|
|
10148
|
-
stim ios
|
|
10149
|
-
|
|
10150
|
-
Tell the user the first build can take about four minutes on a cold cache.
|
|
10151
|
-
Relay the Open in Stim Desktop link printed by stim ios once.
|
|
10152
|
-
|
|
10153
|
-
PAUSE: end the turn. Ask the user to look at the Build section, then the
|
|
10154
|
-
device, then Logs in Stim Desktop. Without Desktop, use stim status,
|
|
10155
|
-
stim stats, and stim logs --errors. Give the next prompt:
|
|
10156
|
-
"${TUTORIAL_PROMPTS.rebuild}". Leave the workspace running.`
|
|
10157
|
-
},
|
|
10158
|
-
app: {
|
|
10159
|
-
summary: "Pinned template, verbatim app files, and .gitignore additions",
|
|
10160
|
-
body: () => `TUTORIAL APP
|
|
10161
|
-
|
|
10162
|
-
create-expo-app: ${TUTORIAL_PINS.createExpoApp}
|
|
10163
|
-
Template: ${TUTORIAL_PINS.template}
|
|
10164
|
-
Write the app files verbatim. Append the .gitignore lines to the template's
|
|
10165
|
-
existing file. App log lines start with [stim:tutorial]. Crash me raises an
|
|
10166
|
-
uncaught JavaScript error, and Slow request times a local three-second timer;
|
|
10167
|
-
Stim does not capture native network requests.
|
|
10168
|
-
|
|
10169
|
-
${Object.entries(TUTORIAL_FILES).map(([name, content]) => `${name}\n\n\`\`\`${name.endsWith(".json") ? "json" : name.endsWith(".js") ? "js" : "text"}\n${content}\`\`\``).join("\n\n")}`
|
|
10170
|
-
},
|
|
10171
|
-
rebuild: {
|
|
10172
|
-
summary: "Repeat the iOS build and explain the cache result",
|
|
10173
|
-
body: () => `REBUILD
|
|
10174
|
-
|
|
10175
|
-
${paths}
|
|
10176
|
-
|
|
10177
|
-
Run the same build again, never with --no-build-cache:
|
|
10178
|
-
|
|
10179
|
-
${commands("rebuild")}
|
|
10180
|
-
|
|
10181
|
-
Read this workspace's environments[].lastBuilds.ios.cacheHit from
|
|
10182
|
-
stim status --json, and the miss reason printed by stim ios. Explain a local or remote hit, or the actual miss
|
|
10183
|
-
reason when cacheHit is false. A repeat run can miss; report the evidence.
|
|
10184
|
-
|
|
10185
|
-
PAUSE: end the turn. Point at the Build section and cache badge, or stim stats.
|
|
10186
|
-
Ask the user to open the device's live view and tap Log an error; without
|
|
10187
|
-
Desktop use the simulator. Then inspect Logs or stim logs --errors. Crash me
|
|
10188
|
-
and Slow request are optional: the former shows a red box, the latter prints
|
|
10189
|
-
a timing line. Give the next prompt: "${TUTORIAL_PROMPTS.agent}".`
|
|
10190
|
-
},
|
|
10191
|
-
agent: {
|
|
10192
|
-
summary: "Record a tap and screenshot, replay it, and inspect agent logs",
|
|
10193
|
-
body: () => `AGENT ACTIONS
|
|
10194
|
-
|
|
10195
|
-
${paths}
|
|
10196
|
-
|
|
10197
|
-
Set {stateDir} to this workspace's agentDevice.stateDir from stim ios or
|
|
10198
|
-
stim status --json. Replace <ios.udid> with this workspace's ios.udid from
|
|
10199
|
-
stim status --json. Use that exact owned simulator, not a guessed one.
|
|
10200
|
-
Run from the tour worktree:
|
|
10201
|
-
|
|
10202
|
-
${commands("agent").replace("read -r iosUdid", "iosUdid=\"<ios.udid>\"")}
|
|
10203
|
-
|
|
10204
|
-
Use --save-script=tutorial.ad with the equals sign: agent-device treats a
|
|
10205
|
-
separate path as a URL and refuses. The dismiss-overlay step clears a red box that would cover the buttons.
|
|
10206
|
-
Replay with the same --platform and --udid so its steps reach the agent log.
|
|
10207
|
-
Remove the target-v1 evidence and dismiss-overlay lines before replay: replaying
|
|
10208
|
-
them can fail with REPLAY_DIVERGENCE on the recorded button identity.
|
|
10209
|
-
The scripts and screenshot stay in the tour worktree and are git-ignored.
|
|
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.
|
|
10210
10147
|
|
|
10211
|
-
|
|
10212
|
-
printed. Expect another error-button line after replay. When screen recording
|
|
10213
|
-
is enabled, the user can scrub Replay in Desktop. Give the next prompt:
|
|
10214
|
-
"${TUTORIAL_PROMPTS.refresh}".`
|
|
10215
|
-
},
|
|
10216
|
-
refresh: {
|
|
10217
|
-
summary: "Change the title to purple and verify Fast Refresh through logs",
|
|
10218
|
-
body: () => `FAST REFRESH
|
|
10219
|
-
|
|
10220
|
-
${paths}
|
|
10148
|
+
Then, from the parent folder, run:
|
|
10221
10149
|
|
|
10222
|
-
|
|
10223
|
-
Fast Refresh, without reloading the app:
|
|
10150
|
+
git clone https://github.com/${TUTORIAL_REPO}.git stim-tutorial
|
|
10224
10151
|
|
|
10225
|
-
|
|
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.
|
|
10226
10161
|
|
|
10227
|
-
|
|
10228
|
-
the app. The intentional button errors may still be in the error log; check
|
|
10229
|
-
whether the edit introduced a new error.
|
|
10162
|
+
${local}
|
|
10230
10163
|
|
|
10231
|
-
PAUSE: end the turn.
|
|
10232
|
-
|
|
10233
|
-
|
|
10234
|
-
|
|
10235
|
-
"${TUTORIAL_PROMPTS.machine}". Otherwise give
|
|
10236
|
-
"${TUTORIAL_PROMPTS.finish}".`
|
|
10237
|
-
},
|
|
10238
|
-
machine: {
|
|
10239
|
-
summary: "Optionally build using a machine the user names and has approved",
|
|
10240
|
-
body: () => `REMOTE MAC (OPTIONAL)
|
|
10241
|
-
|
|
10242
|
-
${paths}
|
|
10243
|
-
|
|
10244
|
-
Proceed only when the user names an approved remote Mac. Substitute that
|
|
10245
|
-
name for {machine}. Never approve, pair, or grant anything. If none is named,
|
|
10246
|
-
ask for the name or let the user skip this step.
|
|
10247
|
-
|
|
10248
|
-
${commands("machine")}
|
|
10249
|
-
|
|
10250
|
-
This step bypasses the artifact cache so the build can use the named machine.
|
|
10251
|
-
A named machine refuses without a local fallback. If it refuses, report the
|
|
10252
|
-
refusal and offer --remote-build auto or local; do not retry silently.
|
|
10253
|
-
|
|
10254
|
-
PAUSE: end the turn. Point at the build's machine in Desktop or its report in
|
|
10255
|
-
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.`
|
|
10256
10168
|
},
|
|
10257
10169
|
finish: {
|
|
10258
|
-
summary: "
|
|
10170
|
+
summary: "Stop and remove the two tutorial worktrees, keeping the clone",
|
|
10259
10171
|
body: () => `FINISH
|
|
10260
10172
|
|
|
10261
10173
|
${paths}
|
|
10262
10174
|
|
|
10263
|
-
The
|
|
10264
|
-
|
|
10265
|
-
|
|
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:
|
|
10266
10180
|
|
|
10267
10181
|
${commands("finish")}
|
|
10268
10182
|
|
|
10269
|
-
|
|
10270
|
-
|
|
10271
|
-
|
|
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:
|
|
10272
10190
|
|
|
10273
10191
|
rm -rf "{base}"
|
|
10274
|
-
git -C "{base}" branch -D stim-tutorial/tour
|
|
10275
10192
|
|
|
10276
|
-
If archive is enabled, tell the user the
|
|
10277
|
-
Desktop. With archive disabled, report removal without promising an
|
|
10278
|
-
Without Desktop, inspect stim status --json for the removed
|
|
10279
|
-
|
|
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: .`
|
|
10280
10232
|
},
|
|
10281
10233
|
restart: {
|
|
10282
|
-
summary: "
|
|
10234
|
+
summary: "Start the tutorial again without removing anything",
|
|
10283
10235
|
body: () => `RESTART
|
|
10284
10236
|
|
|
10285
10237
|
${paths}
|
|
10286
10238
|
|
|
10287
|
-
For "${TUTORIAL_RESTART_PROMPT}",
|
|
10288
|
-
|
|
10289
|
-
|
|
10290
|
-
|
|
10291
|
-
|
|
10292
|
-
|
|
10293
|
-
|
|
10294
|
-
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.`
|
|
10295
10246
|
},
|
|
10296
10247
|
manual: {
|
|
10297
|
-
summary: "
|
|
10248
|
+
summary: "The commands behind each step, for typing yourself",
|
|
10298
10249
|
body: () => `MANUAL TUTORIAL
|
|
10299
10250
|
|
|
10300
10251
|
${paths}
|
|
10301
10252
|
|
|
10302
|
-
|
|
10303
|
-
|
|
10304
|
-
|
|
10305
|
-
|
|
10306
|
-
|
|
10307
|
-
|
|
10308
|
-
|
|
10309
|
-
|
|
10310
|
-
|
|
10311
|
-
|
|
10312
|
-
|
|
10313
|
-
|
|
10314
|
-
|
|
10315
|
-
minutes on a cold cache). Compare cacheHit and missReason on rebuild. At Live
|
|
10316
|
-
view and control, open the device viewer and tap Log an error; without Desktop
|
|
10317
|
-
use the simulator. At App Logs, try Crash me or Slow request if wanted. A JS
|
|
10318
|
-
crash shows a red box; the slow request is a local timer, not network capture.
|
|
10319
|
-
At Watch on Your Phone, optionally open an already paired Stim phone to see
|
|
10320
|
-
the tour workspace; phone setup and machine approval stay with you.
|
|
10321
|
-
Use stim status, stim logs --errors, and stim stats without Desktop.
|
|
10322
|
-
|
|
10323
|
-
${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")}
|
|
10324
|
-
|
|
10325
|
-
Finish removes only the tour worktree. Never use --force; report a refusal.
|
|
10326
|
-
With archive enabled, find the tour under Archived in Desktop or archived[]
|
|
10327
|
-
in stim status --json. Keep the base repository and branch. To delete them,
|
|
10328
|
-
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.`
|
|
10329
10266
|
}
|
|
10330
10267
|
}
|
|
10331
10268
|
}
|
|
@@ -10409,7 +10346,7 @@ function renderIndex(version) {
|
|
|
10409
10346
|
return lines.join("\n");
|
|
10410
10347
|
}
|
|
10411
10348
|
function guideCommand(program, version, status = (running) => guideStatus({
|
|
10412
|
-
projectRoot: findProjectRoot
|
|
10349
|
+
projectRoot: findProjectRoot(process.cwd()),
|
|
10413
10350
|
running
|
|
10414
10351
|
})) {
|
|
10415
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) => {
|