stim 1.30.0 → 1.32.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.
Files changed (144) hide show
  1. package/dist/{acquire-artifact-CrZ4rEbI.mjs → acquire-artifact-CI9fIokp.mjs} +1 -1
  2. package/dist/{act-BaKjDWGw.mjs → act-BfChMxcU.mjs} +71 -22
  3. package/dist/{activity-BTaUf0tc.mjs → activity-yjH7Q3zn.mjs} +4 -4
  4. package/dist/{agent-device-usage-output-DYBpO17K.mjs → agent-device-usage-output-DO412bsG.mjs} +4 -4
  5. package/dist/{android-BWqdDK6R.mjs → android-BNm5A40S.mjs} +41 -39
  6. package/dist/{android-BEQUDpOg.mjs → android-BeeC15Ve.mjs} +1 -1
  7. package/dist/{android-BCYLwmH6.mjs → android-D56S_GX3.mjs} +5 -5
  8. package/dist/{android-DdKG6lyD.mjs → android-DsEj2CCs.mjs} +27 -6
  9. package/dist/{android-cas-BH6K12__.mjs → android-cas-CbumHXIq.mjs} +4 -4
  10. package/dist/api.mjs +26 -26
  11. package/dist/{apk-signed-tail-B8kw4a8K.mjs → apk-signed-tail-B9Usseps.mjs} +2 -2
  12. package/dist/{artifact-M6W2l-xh.mjs → artifact-C8Nvp07y.mjs} +12 -12
  13. package/dist/{artifact-DTHXcmQ3.mjs → artifact-k1x9Jrn0.mjs} +12 -12
  14. package/dist/{auto-placement-CZaWDmcY.mjs → auto-placement--nfdQ9rp.mjs} +10 -10
  15. package/dist/{browser-web-JQ7lFej3.mjs → browser-web-6RDAeH0z.mjs} +11 -11
  16. package/dist/{build-C2ZWoCmg.mjs → build-VXX_dtl5.mjs} +22 -22
  17. package/dist/{build-cache-DlC3owap.mjs → build-cache-BsKim4V7.mjs} +4 -4
  18. package/dist/{build-plan-J3iRR3gI.mjs → build-plan-BK34X2iY.mjs} +6 -6
  19. package/dist/{cache-manifest-evF_zJ9U.mjs → cache-manifest-CfiEny4T.mjs} +1 -1
  20. package/dist/cache-manifest.mjs +1 -1
  21. package/dist/{ccache-B8gY50-h.mjs → ccache-BrwGIxmV.mjs} +15 -15
  22. package/dist/{cli-xRPXJPtA.mjs → cli-Jc7obreB.mjs} +18 -18
  23. package/dist/cli.mjs +1 -1
  24. package/dist/{client-DrHykA24.mjs → client-lDmA-5jx.mjs} +4 -4
  25. package/dist/collector-run.mjs +4 -4
  26. package/dist/{command-output-BBW8eWPL.mjs → command-output-CfPW1bKP.mjs} +1 -1
  27. package/dist/{config-AbUjIBOQ.mjs → config-D5qNWJEP.mjs} +2 -2
  28. package/dist/{dependencies-vUh9mp5z.mjs → dependencies-DkZ8681S.mjs} +37 -37
  29. package/dist/{dependency-state-CPKAKCZ8.mjs → dependency-state-DZVln1P9.mjs} +1 -1
  30. package/dist/{dev-client-CASgAOu7.mjs → dev-client-eDZy6RoX.mjs} +2 -2
  31. package/dist/{device-CsIdRXfm.mjs → device-CTozKhU0.mjs} +11 -11
  32. package/dist/{device-ChnO7bR9.mjs → device-CeIikWac.mjs} +12 -12
  33. package/dist/{device-android-CcdQqQWg.mjs → device-android-zgD9We85.mjs} +20 -14
  34. package/dist/{device-capacity-C5C_jOQd.mjs → device-capacity-Bki6B-7h.mjs} +173 -74
  35. package/dist/device-host-worker.mjs +19 -15
  36. package/dist/{device-ios-DbmtPIiw.mjs → device-ios-TE5ejeXx.mjs} +8 -8
  37. package/dist/{device-lease-run-Do6zYA7y.mjs → device-lease-run-DeNGjJZM.mjs} +4 -4
  38. package/dist/{device-pool-CpoYiCGO.mjs → device-pool-mjyUYaau.mjs} +3 -3
  39. package/dist/{device-provision-B5Ms94qY.mjs → device-provision-j_B7G0e6.mjs} +8 -8
  40. package/dist/{doctor-CJSB5q-s.mjs → doctor-BlrcoQ7B.mjs} +36 -25
  41. package/dist/{doctor-CpfbYh3Q.mjs → doctor-CNZuvTSe.mjs} +1 -1
  42. package/dist/doctor-fingerprint.mjs +1 -1
  43. package/dist/{doctor-CLE4AZEI.mjs → doctor-sSLtn-j1.mjs} +15 -15
  44. package/dist/{doctor-watchman-D1IyMle9.mjs → doctor-watchman-CCuboyj1.mjs} +4 -4
  45. package/dist/{eas-build-DIDOHw2P.mjs → eas-build-CaG-Sfs9.mjs} +2 -2
  46. package/dist/{eas-fallback-DVS9oBSc.mjs → eas-fallback-BpX1lpco.mjs} +4 -4
  47. package/dist/{eas-session-ledger-CW3l-inB.mjs → eas-session-ledger-B-nTfdfk.mjs} +19 -6
  48. package/dist/{error-diagnostics-XJ9eWdtq.mjs → error-diagnostics-q56Im4D8.mjs} +8 -8
  49. package/dist/{gc-BVIcxCxE.mjs → gc-By2Kc-Ek.mjs} +2 -2
  50. package/dist/{gc-Co7PE36D.mjs → gc-Dv6nb9nf.mjs} +17 -17
  51. package/dist/{guide-DkepBgHT.mjs → guide-CwpsGWTJ.mjs} +141 -39
  52. package/dist/{hosted-android-Du7NxufJ.mjs → hosted-android-c5nPJA6S.mjs} +3 -3
  53. package/dist/{hosted-client-Cect92SZ.mjs → hosted-client-Hs-fD4L3.mjs} +36 -12
  54. package/dist/{hosted-logs-Opcjv1Lr.mjs → hosted-logs-BteXG8rm.mjs} +16 -10
  55. package/dist/{hosted-logs-sync-wsI7N1Hv.mjs → hosted-logs-sync-B8bjLEMs.mjs} +5 -5
  56. package/dist/{hosted-macos-BP9Kucat.mjs → hosted-macos-B4byscei.mjs} +5 -5
  57. package/dist/{hosted-native-DmdLSmvu.mjs → hosted-native-Bh9IJ_t5.mjs} +7 -7
  58. package/dist/{idle-shutdown-BcMN_cLm.mjs → idle-shutdown-BaeCo4ps.mjs} +6 -6
  59. package/dist/{ios-CXnoVO_z.mjs → ios-CdAV4-1E.mjs} +4 -4
  60. package/dist/{ios-BFkBHuRT.mjs → ios-D1XKSITX.mjs} +8 -8
  61. package/dist/{ios-CG0IsLtt.mjs → ios-D3oK9-O4.mjs} +32 -31
  62. package/dist/{ios-device-ClSfuu-_.mjs → ios-device-D7Gi3e1I.mjs} +1 -1
  63. package/dist/{ios-project-BM9TbTE7.mjs → ios-project-DDQlX_jX.mjs} +7 -7
  64. package/dist/{ios-state-Ce_dystM.mjs → ios-state-BEIJRJ7P.mjs} +1 -1
  65. package/dist/{js-swap-9Rx-5Nqz.mjs → js-swap-C8N5DXHl.mjs} +4 -4
  66. package/dist/{launch-DvJV1Mx4.mjs → launch-DxpvL6il.mjs} +19 -19
  67. package/dist/{lightened-23pqe7LS.mjs → lightened-Df6II_XI.mjs} +4 -4
  68. package/dist/{logs-DJgZlf2K.mjs → logs-CuWJWF88.mjs} +7 -7
  69. package/dist/{logs-Pkeh3wZk.mjs → logs-zv3ge7c6.mjs} +5 -5
  70. package/dist/{macos-DPvPQadc.mjs → macos-C6U-4Iip.mjs} +19 -19
  71. package/dist/{macos-Du07NsOB.mjs → macos-CbRZheLw.mjs} +1 -1
  72. package/dist/macos-run.mjs +1 -1
  73. package/dist/maintenance-run.mjs +5 -5
  74. package/dist/{metro-BMQHczcf.mjs → metro-mKHEMO-a.mjs} +2 -2
  75. package/dist/{metro-request-BtF7ox53.mjs → metro-request-DODHNXCN.mjs} +8 -2
  76. package/dist/{named-ports-DcktE2Pw.mjs → named-ports-B0hCk2v-.mjs} +2 -2
  77. package/dist/{native-android-CBOM1_gB.mjs → native-android-9dVFqvTT.mjs} +16 -16
  78. package/dist/{native-gradle-artifact-inputs-D_iAfPuS.mjs → native-gradle-artifact-inputs-BlY97pt8.mjs} +3 -3
  79. package/dist/{native-runtime-D4cTwU0Z.mjs → native-runtime-DQfxW5Au.mjs} +6 -6
  80. package/dist/{native-xcode-inputs-BF-ogSnG.mjs → native-xcode-inputs-tDuUvSU8.mjs} +2 -2
  81. package/dist/{native-xcode-ios-DwJBfMP6.mjs → native-xcode-ios-CG-PD-7g.mjs} +15 -15
  82. package/dist/offload-worker.mjs +19 -19
  83. package/dist/{ownership-Dtz9G5Gl.mjs → ownership-CCfiBfOP.mjs} +2 -2
  84. package/dist/{phone-reverses-CvhhkGMH.mjs → phone-reverses-CnNwdz9A.mjs} +4 -4
  85. package/dist/{placement-log-CRR_TE1r.mjs → placement-log-CRL0IY-k.mjs} +1 -1
  86. package/dist/{plan-placement-CdLc-pTb.mjs → plan-placement-2imvnlWP.mjs} +6 -6
  87. package/dist/{ports-g4N_cfyg.mjs → ports-CKktj5lV.mjs} +3 -3
  88. package/dist/{prebuild-DBJMhwSS.mjs → prebuild-CyNrUWgV.mjs} +9 -9
  89. package/dist/{project-DPQjnpcI.mjs → project-Crtcbfl0.mjs} +15 -15
  90. package/dist/{react-native-android-idMWOwkX.mjs → react-native-android-Cabe2YER.mjs} +22 -22
  91. package/dist/{react-native-build-BSoh4HkH.mjs → react-native-build-DhWBUV_H.mjs} +7 -7
  92. package/dist/{react-native-doctor-CYibY8-4.mjs → react-native-doctor-D-twmBon.mjs} +5 -5
  93. package/dist/{react-native-ios-13KIIOOy.mjs → react-native-ios-u_ley41B.mjs} +14 -14
  94. package/dist/{recordings-CHMaF73C.mjs → recordings-BWnIlhZH.mjs} +1 -1
  95. package/dist/{reload-BUQI3dSu.mjs → reload-D2TpUlns.mjs} +14 -14
  96. package/dist/{reload-B9DZOjcV.mjs → reload-f9hvEHE9.mjs} +1 -1
  97. package/dist/{remote-C-RGHhAP.mjs → remote-BsBAjlD2.mjs} +8 -8
  98. package/dist/{result-W4HCum4A.mjs → result-EOsWV6sp.mjs} +16 -16
  99. package/dist/{run-BQAmrohh.mjs → run-2CHAJ9Oj.mjs} +1 -1
  100. package/dist/{selection-PCMT97XD.mjs → selection-C4ZQz_ER.mjs} +3 -3
  101. package/dist/{server-bare-CEQdB_F4.mjs → server-bare-CFyVFZXJ.mjs} +1 -1
  102. package/dist/{server-command-oqi3TDy7.mjs → server-command-BMyKtnjH.mjs} +4 -4
  103. package/dist/{server-expo-C1K07shx.mjs → server-expo-BLxcN2x2.mjs} +2 -2
  104. package/dist/server-expo-DwR3eD-a.mjs +2 -0
  105. package/dist/{settings-D776ygML.mjs → settings-C6UTYW2K.mjs} +1 -1
  106. package/dist/{settings-CKlMOmhj.mjs → settings-jeS0WmUF.mjs} +2 -2
  107. package/dist/{settings-payload-CE-NZ6Lq.mjs → settings-payload-CrX933Xi.mjs} +4 -4
  108. package/dist/{settings-write-D7Xwa_-M.mjs → settings-write-DMcT23Kl.mjs} +5 -5
  109. package/dist/settings.schema.json +47 -5
  110. package/dist/{simslim-CbpHERcv.mjs → simslim-93xmPh-d.mjs} +1 -1
  111. package/dist/{slot-launch-CeIK1iUI.mjs → slot-launch-CSpQcFZw.mjs} +6 -6
  112. package/dist/{stage-Blqe6bWb.mjs → stage-Cf9uSM1a.mjs} +1 -1
  113. package/dist/{start-B7J5a9nS.mjs → start-CLN_r9EG.mjs} +10 -10
  114. package/dist/{start-CvP2pJjc.mjs → start-CquYgzVf.mjs} +1 -1
  115. package/dist/{state-D5ZxwCGJ.mjs → state-CM_HvvkS.mjs} +2 -2
  116. package/dist/{state-C2AUjjhg.mjs → state-CiflgxTP.mjs} +1 -1
  117. package/dist/{stats-Uo7l8OAk.mjs → stats-BVEZuSlg.mjs} +4 -4
  118. package/dist/{status-CqJI1TgG.mjs → status-Bhdy07v0.mjs} +1 -1
  119. package/dist/{status-_rWXFSO7.mjs → status-D0ruTmPl.mjs} +26 -25
  120. package/dist/{status-CK7ZoEhV.mjs → status-GQ-JqvRi.mjs} +1 -1
  121. package/dist/{status-watch-CnSyXUmj.mjs → status-watch-DSu_hhg6.mjs} +4 -4
  122. package/dist/status-worker.mjs +1 -1
  123. package/dist/{stim-desktop-CYXLFpFS.mjs → stim-desktop-1XxET4S8.mjs} +1 -1
  124. package/dist/{stop-CMVfaA-6.mjs → stop-B616ZyTv.mjs} +4 -4
  125. package/dist/{stop-Bogo9oO2.mjs → stop-RZqER_Ww.mjs} +2 -2
  126. package/dist/{stop-CnF9ixYe.mjs → stop-W4DK8qZq.mjs} +15 -15
  127. package/dist/supervisor-run.mjs +134 -42
  128. package/dist/{support-DUevTPur.mjs → support-NxnD8QXd.mjs} +9 -9
  129. package/dist/{support-BGqpYMV8.mjs → support-S_5nkkzs.mjs} +4 -4
  130. package/dist/{swiftpm-artifact-inputs-BcJk6-3u.mjs → swiftpm-artifact-inputs-yp8D5LE4.mjs} +1 -1
  131. package/dist/{swiftpm-macos-DBrmoocG.mjs → swiftpm-macos-Gtq9bNQb.mjs} +8 -8
  132. package/dist/{toolchain-Cw9VVHOY.mjs → toolchain-B2bN8jEo.mjs} +1 -1
  133. package/dist/{toolchain-DLdoyxIt.mjs → toolchain-Bnm8vUCV.mjs} +4 -4
  134. package/dist/{warm-progress-DQ2wQZKp.mjs → warm-progress-_aTMvkIH.mjs} +3 -3
  135. package/dist/{web-g9U7L1sR.mjs → web-BdWG8b5_.mjs} +8 -8
  136. package/dist/web-DwuluWJt.mjs +2 -0
  137. package/dist/web-run.mjs +4 -4
  138. package/dist/{workspace-state-DGwkPXwq.mjs → workspace-state-3bF4TB8P.mjs} +2 -2
  139. package/dist/{workspaces-7_RWoi6c.mjs → workspaces-C59v9bpD.mjs} +19 -15
  140. package/dist/{worktree-qKJ3x07S.mjs → worktree-CinoSsGy.mjs} +24 -24
  141. package/dist/{worktree-DEet9H3p.mjs → worktree-K9F50fD_.mjs} +1 -1
  142. package/package.json +7 -7
  143. package/dist/server-expo-BUFAq2In.mjs +0 -2
  144. package/dist/web-CI_qCRPw.mjs +0 -2
@@ -1,8 +1,8 @@
1
- import { t as ANDROID_AVD_CONFIG_HELP } from "./settings-D776ygML.mjs";
2
- import { i as findProjectRoot$1 } from "./project-DPQjnpcI.mjs";
3
- import { h as RECENT_LAUNCH_MS } from "./eas-session-ledger-CW3l-inB.mjs";
4
- import { o as guideStatus, t as WATCHMAN_NESTED_WORKTREES } from "./doctor-watchman-D1IyMle9.mjs";
5
- import "./status-watch-CnSyXUmj.mjs";
1
+ import { t as ANDROID_AVD_CONFIG_HELP } from "./settings-C6UTYW2K.mjs";
2
+ import { i as findProjectRoot$1 } from "./project-Crtcbfl0.mjs";
3
+ import { h as RECENT_LAUNCH_MS } from "./eas-session-ledger-B-nTfdfk.mjs";
4
+ import { o as guideStatus, t as WATCHMAN_NESTED_WORKTREES } from "./doctor-watchman-CCuboyj1.mjs";
5
+ import "./status-watch-DSu_hhg6.mjs";
6
6
  import { SETTINGS, SETTINGS_SCHEMA_URL, TUTORIAL_VERSION } from "@stim-cli/core/state";
7
7
  import chalk from "chalk";
8
8
  //#region src/guide/agent.ts
@@ -291,7 +291,8 @@ replacement runs while one is queued. See \`stim guide lifecycle concurrency\`.
291
291
  After maintenance.idleStopHours (default 6) with no Stim command, native run,
292
292
  Metro bundle or HMR request, agent-device command or live view, automatic
293
293
  maintenance stops the workspace as stim stop does. Device and app logs do not
294
- count. After a long pause, run stim ios or android again before driving the
294
+ count. A device hosted on a remote Mac stops with it, and stim-server stops a hosted
295
+ session whose client has been gone that long. After a long pause, run stim ios or android again before driving the
295
296
  app; see \`stim guide cleanup\`.
296
297
 
297
298
  Treat a refusal as an ownership or state mismatch: read its code and remedy.
@@ -2504,8 +2505,9 @@ RULES
2504
2505
  \`guide lifecycle\`), so the sums can differ from what they sum.
2505
2506
 
2506
2507
  capacity.load is always present. capacity.builds is present only while
2507
- concurrency.maxBuilds is set, and capacity.devices only while
2508
- concurrency.maxDevices is set (see \`guide lifecycle concurrency\`):
2508
+ concurrency.maxBuilds is set, capacity.devices only while
2509
+ concurrency.maxDevices is set, and capacity.platformDevices only for the
2510
+ platforms whose cap is set (see \`guide lifecycle concurrency\`):
2509
2511
 
2510
2512
  load { cpus, loadPerCore, busyAt, limit, state }. loadPerCore is the
2511
2513
  5-minute load average divided by cpus, to one decimal. busyAt is
@@ -2528,6 +2530,11 @@ RULES
2528
2530
  device, with its device slot; for a device no workspace records,
2529
2531
  label is its name and workspace and slot are null. inUse is null
2530
2532
  and unknown says why when the devices could not be listed.
2533
+ platformDevices { ios?: { max, inUse, unknown? }, android?: { max, inUse,
2534
+ unknown? } }, the booted owned simulators and emulators counted
2535
+ against concurrency.maxIosDevices and concurrency.maxAndroidDevices.
2536
+ Each key is present only while its cap is set, whether or not
2537
+ concurrency.maxDevices is.
2531
2538
 
2532
2539
  The memory budget plans before a boot
2533
2540
  and always uses the estimate. What is using CPU and memory now is the
@@ -2668,7 +2675,9 @@ CAPACITY REFUSALS
2668
2675
  capacityRefusals?: [{ at, kind: "device", platform: "ios" | "android",
2669
2676
  max, workspace }]
2670
2677
  Only device refusals with STIM_AT_CAPACITY are recorded; build caps wait.
2671
- at is the ISO timestamp, max is concurrency.maxDevices, and workspace is
2678
+ at is the ISO timestamp, max is the cap of the limit that was full
2679
+ (concurrency.maxDevices, or the platform's concurrency.maxIosDevices or
2680
+ concurrency.maxAndroidDevices), and workspace is
2672
2681
  the workspace id used by status and Desktop. The list is newest first:
2673
2682
  the last 50 from the last 7 days, omitted when empty. Desktop uses events
2674
2683
  within 6 hours for its device-limit suggestion, including terminal runs.
@@ -2838,7 +2847,7 @@ HOSTED IOS METRO
2838
2847
  Macs; a running Metro or a pin on a port the host uses refuses the run before
2839
2848
  the build. metro.tunnel and metro.publicUrl are ignored. Supervisor restart
2840
2849
  reopens the recorded links; stop or a stopped hosted session closes them. A recorded
2841
- hosted session prevents Metro idle stop. Release runs skip the bridge.
2850
+ hosted session prevents Metro idle stop; maintenance.idleStopHours still stops the workspace and its hosted session (guide cleanup). Release runs skip the bridge.
2842
2851
  Hosted Debug runs require a supervisor and a stim-server that support these
2843
2852
  links before reservation. --no-metro-check refuses; restart a missing or older supervisor
2844
2853
  with stim stop; stim start.
@@ -4779,8 +4788,10 @@ so a Debug run on one is wired to a LAN origin instead of localhost.`,
4779
4788
  STIM_AT_CAPACITY: {
4780
4789
  summary: "owned-device slot wait timed out, or --no-wait reached the cap or queue",
4781
4790
  body: () => `STIM_AT_CAPACITY
4782
- Only when concurrency.maxDevices is set (unset by default). New owned
4783
- devices queue in FIFO order across $STIM_HOME for 600 seconds by default.
4791
+ Only when concurrency.maxDevices, concurrency.maxIosDevices or
4792
+ concurrency.maxAndroidDevices is set (all unset by default). A boot must fit
4793
+ its platform cap and concurrency.maxDevices; the message names the limit that
4794
+ was full with its count and cap. New owned devices queue in FIFO order across $STIM_HOME for 600 seconds by default.
4784
4795
  This refusal means the wait expired, or \`--no-wait\` / \`--wait 0\`
4785
4796
  found the cap full or another run queued. The message gives the current
4786
4797
  count and, on timeout, how long it waited. iOS waits beside its build,
@@ -4798,7 +4809,10 @@ so a Debug run on one is wired to a LAN origin instead of localhost.`,
4798
4809
  --no-wait and --wait 0 refuse at once. Builds under remote-only refuse with
4799
4810
  STIM_OFFLOAD_REFUSED instead.
4800
4811
  Stop an environment (\`stim stop\`), pass a longer \`--wait <seconds>\`,
4801
- or raise concurrency.maxDevices. With --remote auto, remote.machines and
4812
+ or raise the limit the message names (concurrency.maxDevices, or the
4813
+ platform's concurrency.maxIosDevices / maxAndroidDevices). On a stim-server
4814
+ worker the hosted offer and reserve name the full limit too, including
4815
+ concurrency.maxMacosApps. With --remote auto, remote.machines and
4802
4816
  the opt-in remote.easFallback (billed EAS Simulator) are tried before this
4803
4817
  wait; \`stim logs --source placement\` says why neither took the run. Waiting prints holder names and elapsed
4804
4818
  time; status JSON exposes build.waitingFor independently of phase.
@@ -5660,6 +5674,17 @@ not on any remote" (worktree remove)
5660
5674
  };
5661
5675
  //#endregion
5662
5676
  //#region src/guide/lifecycle.ts
5677
+ const HOSTED_RECORDING = (noun) => `\
5678
+ agent-device record start [path] --remote-config <file> records the ${noun};
5679
+ record stop downloads the video to path, or ./recording-<time>.mp4, on this
5680
+ machine. The host writes it only under the session's private agent-device state
5681
+ and deletes the files after the download, on stim stop or session teardown and
5682
+ when its daemon restarts. One recording runs per session; a second start, a host output
5683
+ path, record inside batch and a record stop from another agent-device session
5684
+ are refused. The host stops a recording after 10 minutes; a record stop within
5685
+ the next 15 minutes downloads it. A finished recording over 512 MiB is deleted
5686
+ instead. A host whose Stim predates hosted recording refuses
5687
+ record as a command outside its allowed list.`;
5663
5688
  const lifecycle = {
5664
5689
  summary: "The full worktree -> start -> ios/android -> logs -> teardown flow, with sections for builds, devices and flags",
5665
5690
  preamble: () => `ENVIRONMENT LIFECYCLE
@@ -6205,7 +6230,7 @@ Status adds android.host { machine, session, selected, agent, device: { name,
6205
6230
  systemImage, api }, state } per slot. The public name is the profile and API
6206
6231
  level, never the host serial or AVD name. ready, stopped and unverified are
6207
6232
  session probe states; a conflicting local emulator remains visible with a
6208
- warning. A recorded placement keeps the workspace in use and blocks idle stop.
6233
+ warning. A recorded placement keeps the workspace in use and blocks the supervisor's idle stop and device reclaim; maintenance.idleStopHours still stops it once the host confirms the session (guide cleanup, IDLE STOP).
6209
6234
 
6210
6235
  stop and worktree remove wait for device-host.stop, park the host's eligible owned
6211
6236
  emulator within pool.androidParkedMax (delete otherwise), close the Metro links and clear placement. stop --json reports
@@ -6227,6 +6252,11 @@ The host verifies the policy digest and android-instance backend before granting
6227
6252
  control. An older host, missing binary or unsupported policy reports driver: none;
6228
6253
  the host's notice explains unavailable control.
6229
6254
 
6255
+ On a local: machine, daemonBaseUrl is http://127.0.0.1:<port> of this
6256
+ workspace's Metro supervisor, which carries each request to the host over its
6257
+ own authenticated device-host connection (host feature hosted-agent-link). It
6258
+ works for Debug runs while stim start runs; Release runs report driver: none.
6259
+
6230
6260
  android.host.agent in stim status --json provides driver, remoteConfig and
6231
6261
  command, never its token. Each slot has a separate 0600 file under the workspace
6232
6262
  state directory at hosted-android/<slot>/agent-device-remote.json. Use that file:
@@ -6242,12 +6272,15 @@ session's tenant. devices is permitted for the client's open flow; inventory
6242
6272
  contains only this emulator. Never use its serial with a local agent-device.
6243
6273
  Allowed commands are devices, open, close, snapshot, diff, wait, find, get, is,
6244
6274
  click, fill, press, type, focus, scroll, screenshot, longpress, swipe, back, home,
6245
- orientation, appstate, alert and batch. Stim installs; install, reinstall,
6246
- uninstall, boot, shutdown, close --shutdown, record, logs, uploads and device-wide
6247
- actions are refused. Other serials in commands, flags, inputs, nested batches or
6248
- lease requests are refused. Ambient client paths and owner fields are stripped;
6249
- the host pins platform, serial, tenant, runId, clientId, deviceKey and backend.
6250
- Host paths and launch inputs are refused. Screenshots use only remote artifacts.
6275
+ orientation, appstate, alert, batch, record start and record stop. Stim installs;
6276
+ install, reinstall, uninstall, boot, shutdown, close --shutdown, logs, uploads and
6277
+ device-wide actions are refused. Other serials in commands, flags, inputs, nested
6278
+ batches or lease requests are refused. Ambient client paths and owner fields are
6279
+ stripped; the host pins platform, serial, tenant, runId, clientId, deviceKey and
6280
+ backend. Host paths and launch inputs are refused. Screenshots use only remote
6281
+ artifacts.
6282
+
6283
+ ${HOSTED_RECORDING("emulator")}
6251
6284
 
6252
6285
  Stop closes the matching agent-device connection and removes the slot file only
6253
6286
  once the host confirms stop or revocation. Reinstall, stop, revocation and server
@@ -6319,7 +6352,7 @@ The same setup serves macos --remote and macos.remote. Approval is separate from
6319
6352
  and phone control. Set ios.remote to the machine name for a default (machine scope for every app on this machine);
6320
6353
  no flag or setting runs here. eas and proxy keep their remote backend meanings.
6321
6354
  --remote auto (or ios.remote = auto / android.remote = auto) stays here when
6322
- there is a free concurrency.maxDevices slot (0 means unlimited), no device
6355
+ there is a free concurrency.maxDevices slot and platform cap slot (0 means unlimited), no device
6323
6356
  waiter ahead, normal host memory pressure, no budget shortfall,
6324
6357
  and 5-minute load per core below server.maxLoadPerCore. Without remote.machines,
6325
6358
  auto and remote are local and remote-only refuses with STIM_HOSTING_REFUSED. An unknown local device count also stays local; boot admission
@@ -6354,7 +6387,7 @@ when it names a machine, then remote.machines order, so the first listed host wi
6354
6387
  room fills before the next gets a run. Load and free memory do not rank hosts.
6355
6388
  Automatic build offload resolves later, after the device architecture is known.
6356
6389
 
6357
- When no host admits and this machine is at its concurrency.maxDevices cap (or runs
6390
+ When no host admits and this machine is at its concurrency.maxDevices or per-platform cap (or runs
6358
6391
  are queued ahead), remote.easFallback (default false; EAS Simulator is billed)
6359
6392
  runs the device on an EAS Simulator exactly as --remote eas would: the build
6360
6393
  stays here, nothing starts an EAS cloud build, stop ends the session, and
@@ -6395,7 +6428,8 @@ refuses. The host boots headless and ignores this Mac's iosSimulatorApp setting.
6395
6428
  the offered simulator architecture, not this machine's. --remote-build remains
6396
6429
  independent and selects a compatible build worker for a Debug cache miss.
6397
6430
 
6398
- The host's own owned devices count toward concurrency.maxDevices.
6431
+ The host's own owned devices count toward concurrency.maxDevices and its per-platform cap
6432
+ (concurrency.maxIosDevices, concurrency.maxAndroidDevices; hosted macOS apps use concurrency.maxMacosApps).
6399
6433
 
6400
6434
  The host's person sees Hosted here rows in stim-server devices; --json adds
6401
6435
  hostedSessions beside devices. Rows show client, platform, device, installed
@@ -6526,6 +6560,11 @@ session and rotates its grant; rerun stim ios to reattach and refresh the config
6526
6560
  An unresolved proxy or daemon retains its claim and blocks reinstall and native
6527
6561
  teardown; clear a named claim only after proving its owner and child are gone.
6528
6562
 
6563
+ On a local: machine, daemonBaseUrl is http://127.0.0.1:<port> of this
6564
+ workspace's Metro supervisor, which carries each request to the host over its
6565
+ own authenticated device-host connection (host feature hosted-agent-link). It
6566
+ works for Debug runs while stim start runs; Release runs report driver: none.
6567
+
6529
6568
  The client writes a separate 0600 remote-config file for each workspace slot.
6530
6569
  Status prints agent: <command>; ios.host.agent in --json carries driver,
6531
6570
  remoteConfig and command, never the token. Use the returned file:
@@ -6547,13 +6586,13 @@ targeting its exact simulator UDID before deleting the simulator, even without c
6547
6586
 
6548
6587
  Allowed: devices, open, close, snapshot, diff, wait, find, get, is, click, fill,
6549
6588
  press, type, focus, scroll, screenshot, longpress, swipe, back, home, orientation,
6550
- appstate, alert and batch. These inspect or interact with the simulator and
6551
- installed apps. devices lists only the session's simulator, filtered by the
6552
- daemon policy. Its device claims are session-local and record / as the workspace;
6589
+ appstate, alert, batch, record start and record stop. These inspect, interact
6590
+ with or record the simulator and installed apps. devices lists only the
6591
+ session's simulator, filtered by the daemon policy. Its device claims are session-local and record / as the workspace;
6553
6592
  a host-side agent-device using the same simulator is not visible to the hosted daemon.
6554
6593
  Client device selectors are stripped and requests are pinned to iOS and that
6555
6594
  UDID; an explicitly named foreign UDID is refused. Selecting another device,
6556
- boot/shutdown/erase, installs, uninstall, uploads, push, record, logs, network,
6595
+ boot/shutdown/erase, installs, uninstall, uploads, push, logs, network,
6557
6596
  perf, trace, clipboard and settings are refused. Stim installs; the agent cannot
6558
6597
  enumerate other host devices or write host files. Ambient client fields (cwd,
6559
6598
  stateDir, config and remoteConfig) are stripped, including inside batch steps.
@@ -6563,6 +6602,8 @@ Only agent-device's generated screenshot temp artifact is accepted and returned
6563
6602
  to the client; arbitrary host screenshot destinations are refused. Runtime
6564
6603
  hints are dropped and only reporting/artifact metadata passes through.
6565
6604
 
6605
+ ${HOSTED_RECORDING("simulator")}
6606
+
6566
6607
  If hosting.agentDriver is none, the driver cannot enforce its policy, or the
6567
6608
  host does not advertise hosted-ios-agent, agent reports
6568
6609
  { driver: 'none', setting: 'hosting.agentDriver' }. Update the host and check
@@ -7739,7 +7780,8 @@ OPT-IN CONCURRENCY LIMITS (UNLIMITED BY DEFAULT)
7739
7780
  wait also records a capacityWaits event in stats,
7740
7781
  with reclaimed when one or more devices were
7741
7782
  shut down for it.
7742
- While at the cap, only the queue head reclaims:
7783
+ While at the cap, only the queue head reclaims (or a run behind a
7784
+ head held back only by its own platform's cap):
7743
7785
  it shuts down the longest-idle eligible owned
7744
7786
  device in another workspace of this Stim home,
7745
7787
  one per attempt, at most once every 15 seconds,
@@ -7759,6 +7801,35 @@ OPT-IN CONCURRENCY LIMITS (UNLIMITED BY DEFAULT)
7759
7801
  and the command to remove that exact claim.
7760
7802
  See \`guide errors STIM_AT_CAPACITY\`.
7761
7803
 
7804
+ concurrency.maxIosDevices booted owned iOS simulators at once.
7805
+ concurrency.maxAndroidDevices booted owned Android emulators at once.
7806
+ concurrency.maxMacosApps hosted macOS apps on a worker at once.
7807
+ Each is 0 (no per-platform limit) unless set, and
7808
+ each only tightens: a run must fit its platform's
7809
+ cap AND concurrency.maxDevices. maxDevices counts
7810
+ simulators and emulators together, local and
7811
+ hosted, and counts hosted macOS apps as a pool of
7812
+ their own, so a macOS app is bounded by
7813
+ maxMacosApps and by maxDevices, and never adds to
7814
+ the simulator and emulator total. Leaving the new
7815
+ settings unset changes no existing cap.
7816
+ Local admission applies the iOS and Android caps
7817
+ with the same queue and reclaim as maxDevices; a
7818
+ waiter held back only by its own platform's cap
7819
+ does not hold up a waiter of another platform.
7820
+ Reclaim for a full platform cap only shuts down
7821
+ devices of that platform. Hosted admission on a
7822
+ worker (offer and reserve) applies all three,
7823
+ counting the worker's local devices too.
7824
+ Local \`stim macos\` runs are not counted, so
7825
+ maxMacosApps limits hosted macOS apps only.
7826
+ A refusal names the full limit, its count and its
7827
+ cap, for example "1 Stim iOS simulator(s) are
7828
+ already booted and concurrency.maxIosDevices is 1".
7829
+ Status JSON capacity.platformDevices reports
7830
+ { max, inUse } for each of ios and android whose
7831
+ cap is set.
7832
+
7762
7833
  \`stim doctor\` prints one note echoing the caps and the current active count,
7763
7834
  but ONLY when a cap is set. \`stim gc\` reports stale build slots the way it
7764
7835
  reports stale build locks, and \`gc --delete\` clears them. Set the caps
@@ -8805,7 +8876,7 @@ status's maintenance block and the maintenance log.
8805
8876
 
8806
8877
  IDLE STOP: the idle check, and gc --delete unless maintenance.mode is off (its
8807
8878
  dry run lists them in the maintenance plan), stop a workspace whose dev server
8808
- runs or whose owned simulator or emulator is booted once it has had no real
8879
+ runs, whose owned simulator or emulator is booted, or whose device is hosted on a remote Mac once it has had no real
8809
8880
  activity for maintenance.idleStopHours (default 6; 0 never; machine or project
8810
8881
  layers; STIM_MAINTENANCE_IDLE_STOP_HOURS overrides; fractions work). Six hours
8811
8882
  outlasts a lunch break, a meeting or an agent's pause between tasks, and still
@@ -8820,7 +8891,7 @@ only stays connected does not either. The stop is stim stop's: the dev
8820
8891
  server stops, the owned devices shut down through centralized teardown
8821
8892
  (never deleted), and the port is released; device leases stay. A build, a
8822
8893
  held native-run or metro-start lock, a build lock or slot, a running macOS
8823
- app, a hosted or remote session, an unexpired device lease, a managed tunnel
8894
+ app, a remote (EAS) session, an unexpired device lease, a managed tunnel
8824
8895
  or remote lock, a running owned Chrome, a device driver other than
8825
8896
  agent-device, a live agent-device claim with no session activity to judge,
8826
8897
  device activity, an emulator list or an emulator's AVD name that cannot be read keeps
@@ -8833,7 +8904,21 @@ after stim stop, and the supervisor_stopped line in metro.ndjson names
8833
8904
  automatic maintenance and the idle time as the requester. The next start, ios or android starts it
8834
8905
  again. An owned emulator the activity check missed keeps the workspace while
8835
8906
  adb lists it, and so does any emulator adb lists without an AVD name, such as
8836
- a stale offline entry that adb kill-server clears. A dev server that may still run after its supervisor
8907
+ a stale offline entry that adb kill-server clears. A workspace whose iOS, Android or macOS device is hosted on a remote Mac is
8908
+ stopped the same way once the host confirms the session is ready; stim stop's
8909
+ hosted path ends the session and the host parks or tears down the device. A host
8910
+ that does not answer, a session it cannot confirm (unknown, as after a worker
8911
+ crash) and a placement that cannot be read keep the workspace, and a live view of
8912
+ the hosted device is not seen here. On the host, stim-server stops a ready session
8913
+ whose client has had no connection, live stream or Metro link and sent no
8914
+ agent-device request for the host's own machine maintenance.idleStopHours (default 6; 0
8915
+ never; STIM_MAINTENANCE_IDLE_STOP_HOURS overrides), only while its maintenance.mode is on;
8916
+ a project value or maintenance.keep on the client does not reach it. Server downtime is not
8917
+ counted. It uses the stop a person uses, never touches a session whose journal state is
8918
+ preparing, stopping or unknown, or one with a native operation or a claim it cannot prove free
8919
+ (a session a crashed server left ready counts once its claim is proven free),
8920
+ and writes the reason to the server log and the session's idleStop journal field.
8921
+ The count is per client, not per session. A dev server that may still run after its supervisor
8837
8922
  exited is kept until stim stop ends it. So is a dev server whose requests Stim
8838
8923
  does not record: an Expo project before SDK 54, which cannot load Stim's Metro config
8839
8924
  adapter, or a dev server an older Stim started, until stim stop and stim start
@@ -10415,8 +10500,9 @@ ${ANDROID_AVD_CONFIG_HELP.map((line) => ` ${line}`).joi
10415
10500
  lifecycle hosted-android.
10416
10501
  remote.easFallback true lets "auto" or "remote" (ios.remote, android.remote
10417
10502
  or --remote) run the simulator or emulator on a
10418
- billed EAS Simulator when this machine is at its
10419
- concurrency.maxDevices cap (or has runs queued) and no
10503
+ billed EAS Simulator when this machine is at a
10504
+ device cap (concurrency.maxDevices or a per-platform
10505
+ cap, or has runs queued) and no
10420
10506
  remote.machines entry takes the run, instead of waiting
10421
10507
  or refusing with STIM_AT_CAPACITY. Default false. A
10422
10508
  busy machine with a free slot never uses it. Stim checks
@@ -10650,12 +10736,20 @@ machine's. They live under a top-level \`concurrency\` key in
10650
10736
  "concurrency": { "maxBuilds": 2, "maxDevices": 3 }
10651
10737
  }
10652
10738
 
10653
- The keys are concurrency.maxBuilds and concurrency.maxDevices.
10739
+ The keys are concurrency.maxBuilds and concurrency.maxDevices, plus the
10740
+ per-platform device caps concurrency.maxIosDevices,
10741
+ concurrency.maxAndroidDevices and concurrency.maxMacosApps. A boot must fit
10742
+ its platform cap and concurrency.maxDevices; the per-platform caps only
10743
+ tighten (see \`guide lifecycle concurrency\`).
10654
10744
 
10655
10745
  or via the environment, which overrides the file:
10656
10746
 
10657
10747
  STIM_MAX_BUILDS=2 STIM_MAX_DEVICES=3 stim ios
10658
10748
 
10749
+ concurrency.maxIosDevices STIM_MAX_IOS_DEVICES
10750
+ concurrency.maxAndroidDevices STIM_MAX_ANDROID_DEVICES
10751
+ concurrency.maxMacosApps STIM_MAX_MACOS_APPS
10752
+
10659
10753
  Unset, 0, or any non-positive value means NO enforcement -- the default, where
10660
10754
  Stim limits nothing. See \`guide lifecycle concurrency\` for what each cap
10661
10755
  does.
@@ -10691,7 +10785,8 @@ STIM_HOME and CI make the mode off unless STIM_MAINTENANCE is set.
10691
10785
  maintenance.keep is a project setting: pin a workspace so a pass never clears
10692
10786
  its build outputs or removes its worktree.
10693
10787
  maintenance.idleStopHours takes machine or project layers, so one workspace can
10694
- stop sooner, later or never.
10788
+ stop sooner, later or never. On a hosting Mac stim-server reads its machine value
10789
+ to stop a hosted session whose client has been gone that long.
10695
10790
  See \`guide cleanup\` for checks, plans and log paths.
10696
10791
 
10697
10792
  ${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}`}
@@ -10888,7 +10983,10 @@ ticket, which is a credential: whoever spends it first, after setup stores it,
10888
10983
  gets the approval, and on a local network that takes only the ticket and a
10889
10984
  route to the worker, not tailnet membership. Agents show it only to the user.
10890
10985
  Offloaded builds and hosted Debug and
10891
- Release runs work there; agent control of hosted apps needs a tailnet machine.
10986
+ Release runs work there. Agent control of hosted iOS and Android Debug runs
10987
+ goes through a loopback port of this workspace's Metro supervisor, which
10988
+ carries it over its own authenticated connection; Release runs and hosted macOS
10989
+ apps there report agent control as none.
10892
10990
  \`stim-server pair\` puts \`ws://<address>:<port>\` in the phone pairing code
10893
10991
  when Tailscale is not running and \`server.lanAddresses\` is set.
10894
10992
 
@@ -12180,6 +12278,10 @@ backend. Reserved names are case-insensitive and trimmed; they refuse with
12180
12278
  STIM_BAD_ARG before state access or a connection. The device pool switches
12181
12279
  (remote.devicePoolDisabled) also apply to automatic macOS placement.
12182
12280
 
12281
+ A hosted app is not kept forever: the idle workspace stop ends its hosted session
12282
+ after maintenance.idleStopHours without activity, and stim-server stops a session
12283
+ whose client has been gone that long (guide cleanup, IDLE STOP).
12284
+
12183
12285
  Logs: the host records the hosted app's stdout, stderr and exit like a local run.
12184
12286
  stim logs, including --errors, --json and --follow, asks the host over the same
12185
12287
  approved connection, copies the records it has not copied yet into the
@@ -12284,9 +12386,9 @@ script such as mini-desktop.sh use these instead:
12284
12386
  quit stim stop (verified stop; the host frees the slot
12285
12387
  and removes the app's defaults)
12286
12388
  clean stim worktree remove
12287
- 3 concurrent slots concurrency.maxDevices on the host, counted apart
12288
- from simulators and emulators; one bundle id
12289
- slot per session
12389
+ 3 concurrent slots concurrency.maxMacosApps and concurrency.maxDevices
12390
+ on the host, counted apart from simulators and
12391
+ emulators; one bundle id slot per session
12290
12392
 
12291
12393
  The agent-device rows work once the host's agent field names agent-device.
12292
12394
 
@@ -1,7 +1,7 @@
1
1
  import { c as throwIfCancelled } from "./exec-aULwtNly.mjs";
2
- import { c as call, l as connectHost } from "./hosted-client-Cect92SZ.mjs";
3
- import { i as requestHostedMetro } from "./metro-request-BtF7ox53.mjs";
4
- import { a as stopHostedNative, i as prepareHostedNative, r as placeHostedNative } from "./hosted-native-DmdLSmvu.mjs";
2
+ import { c as call, l as connectHost } from "./hosted-client-Hs-fD4L3.mjs";
3
+ import { a as requestHostedMetro } from "./metro-request-DODHNXCN.mjs";
4
+ import { a as stopHostedNative, i as prepareHostedNative, r as placeHostedNative } from "./hosted-native-Bh9IJ_t5.mjs";
5
5
  //#region src/device-host/hosted-android.ts
6
6
  async function prepareHostedAndroid(machine, selectors, recorded, mode) {
7
7
  return await prepareHostedNative(machine, selectors, recorded, "android", false, mode);
@@ -1,7 +1,7 @@
1
1
  import { c as throwIfCancelled, o as cancellableSleep } from "./exec-aULwtNly.mjs";
2
- import { Y as diagnoseConnectFailure, st as resolveEndpoint } from "./config-AbUjIBOQ.mjs";
3
- import { t as BuildConnection } from "./client-DrHykA24.mjs";
4
- import { g as configuredMachines } from "./selection-PCMT97XD.mjs";
2
+ import { X as diagnoseConnectFailure, ct as resolveEndpoint } from "./config-D5qNWJEP.mjs";
3
+ import { t as BuildConnection } from "./client-lDmA-5jx.mjs";
4
+ import { g as configuredMachines } from "./selection-C4ZQz_ER.mjs";
5
5
  import { createHash } from "node:crypto";
6
6
  import { lstatSync, readFileSync, readdirSync, readlinkSync } from "node:fs";
7
7
  import { join } from "node:path";
@@ -232,25 +232,49 @@ function bundleManifest(bundle) {
232
232
  content
233
233
  };
234
234
  }
235
+ /** stim-server answers one connection's requests in arrival order, so chunks sent in order reach it in order. */
236
+ const UPLOAD_WINDOW = 64;
235
237
  async function upload(host, ids, missing, content) {
236
238
  if (!Array.isArray(missing)) throw new Error(`${host.machine} answered device-host.app.offer without missing content.`);
237
- for (const entry of missing) {
239
+ const files = missing.map((entry) => {
238
240
  const read = isJsonObject(entry) && typeof entry.sha256 === "string" ? content.get(entry.sha256) : void 0;
239
- if (!read || !isJsonObject(entry) || typeof entry.offset !== "number") throw new Error(`${host.machine} asked for content this app does not have.`);
240
- const bytes = read();
241
- let offset = entry.offset;
242
- do {
243
- const data = bytes.subarray(offset, offset + HOSTED_APP_CHUNK_BYTES).toString("base64");
241
+ if (!read || !isJsonObject(entry) || typeof entry.sha256 !== "string" || typeof entry.offset !== "number") throw new Error(`${host.machine} asked for content this app does not have.`);
242
+ return {
243
+ digest: entry.sha256,
244
+ start: entry.offset,
245
+ read
246
+ };
247
+ });
248
+ const inFlight = /* @__PURE__ */ new Set();
249
+ let failure;
250
+ const send = async (digest, offset, end, data) => {
251
+ try {
244
252
  const next = (await call(host, "device-host.app.chunk", {
245
253
  ...ids,
246
- sha256: entry.sha256,
254
+ sha256: digest,
247
255
  offset,
248
256
  data
249
257
  })).offset;
250
- if (typeof next !== "number" || next <= offset && next !== bytes.length) throw new Error(`${host.machine} did not accept app content at byte ${offset}.`);
251
- offset = next;
258
+ if (typeof next !== "number" || next < end) throw new Error(`${host.machine} did not accept app content at byte ${offset}.`);
259
+ } catch (error) {
260
+ failure ??= error instanceof Error ? error : new Error(String(error));
261
+ }
262
+ };
263
+ for (const { digest, start, read } of files) {
264
+ if (failure) break;
265
+ const bytes = read();
266
+ let offset = start;
267
+ do {
268
+ if (inFlight.size >= UPLOAD_WINDOW) await Promise.race(inFlight);
269
+ if (failure) break;
270
+ const end = Math.min(offset + HOSTED_APP_CHUNK_BYTES, bytes.length);
271
+ const pending = send(digest, offset, end, bytes.subarray(offset, end).toString("base64")).finally(() => inFlight.delete(pending));
272
+ inFlight.add(pending);
273
+ offset = end;
252
274
  } while (offset < bytes.length);
253
275
  }
276
+ await Promise.all(inFlight);
277
+ if (failure) throw failure;
254
278
  }
255
279
  //#endregion
256
280
  export { stoppedSession as _, SESSION_TIMEOUT_MS as a, call as c, hostConnector as d, hostedSession as f, sha256 as g, settle as h, RESERVE_TIMEOUT_MS as i, connectHost as l, requireHostedAppMode as m, OFFER_TIMEOUT_MS as n, attach as o, probeHostedSession as p, PREPARE_TIMEOUT_MS as r, bundleManifest as s, INSTALL_TIMEOUT_MS as t, heldNoLonger as u, unknownSession as v, upload as y };
@@ -1,11 +1,11 @@
1
1
  import { t as getExecutor } from "./exec-aULwtNly.mjs";
2
- import { E as workspaceDir, k as workspaceLogsDir$1 } from "./workspace-state-DGwkPXwq.mjs";
3
- import { B as deviceSlotFileKey, it as parseMachine$1, q as validateDeviceSlot } from "./config-AbUjIBOQ.mjs";
4
- import { l as phaseLine } from "./command-output-BBW8eWPL.mjs";
5
- import { n as macosDir } from "./state-C2AUjjhg.mjs";
6
- import { n as androidLogGroups, r as isAndroidCrashText } from "./android-BEQUDpOg.mjs";
2
+ import { E as workspaceDir, k as workspaceLogsDir$1 } from "./workspace-state-3bF4TB8P.mjs";
3
+ import { J as validateDeviceSlot, V as deviceSlotFileKey, at as parseMachine$1 } from "./config-D5qNWJEP.mjs";
4
+ import { l as phaseLine } from "./command-output-CfPW1bKP.mjs";
5
+ import { n as macosDir } from "./state-CiflgxTP.mjs";
6
+ import { n as androidLogGroups, r as isAndroidCrashText } from "./android-BeeC15Ve.mjs";
7
7
  import { c as readNdjsonGenerations$1, i as createNdjsonWriter } from "./ndjson-CrjIsGzw.mjs";
8
- import { c as call } from "./hosted-client-Cect92SZ.mjs";
8
+ import { c as call } from "./hosted-client-Hs-fD4L3.mjs";
9
9
  import { mkdirSync, readFileSync, realpathSync, renameSync, rmSync, writeFileSync } from "node:fs";
10
10
  import { dirname, join } from "node:path";
11
11
  import { LOG_ROTATE_BYTES, ambientWrite, withDirLockAsync } from "@stim-cli/core";
@@ -46,10 +46,16 @@ async function removeAgentAccess(file, keep) {
46
46
  if (path !== keep) rmSync(path, { force: true });
47
47
  }
48
48
  }
49
- async function agentAccess(file, credential, grant, note, platform = "macos") {
49
+ /**
50
+ * Writes the remote config that lets agent-device drive a hosted app and says how to use it. A tailnet machine is
51
+ * reached at its `tailscale serve` route. A local network machine is reached only through `relay`, the loopback port
52
+ * of this workspace's supervisor relay, or not at all when `relay` is the reason there is none.
53
+ */
54
+ async function agentAccess(file, credential, grant, note, platform = "macos", relay = `Agent control of apps hosted on ${credential.machine} over the local network is relayed only for hosted iOS and Android Debug runs.`) {
50
55
  const machine = parseMachine$1(credential.machine);
51
- if (machine?.transport === "local") {
52
- if (grant !== void 0) note(`Agent control of apps hosted on ${credential.machine} needs a tailnet machine; the local network does not carry it.`);
56
+ const local = machine?.transport === "local";
57
+ if (local && typeof relay === "string") {
58
+ if (grant !== void 0) note(relay);
53
59
  await removeAgentAccess(file);
54
60
  return {
55
61
  driver: "none",
@@ -65,7 +71,7 @@ async function agentAccess(file, credential, grant, note, platform = "macos") {
65
71
  const target = client ? join(dirname(file), client.configFile) : file;
66
72
  const access = client && issued ? client.access({
67
73
  grant: issued,
68
- baseUrl: `https://${credential.address}${port === 443 ? "" : `:${port}`}${issued.path}`,
74
+ baseUrl: local ? `http://127.0.0.1:${relay}${issued.path}` : `https://${credential.address}${port === 443 ? "" : `:${port}`}${issued.path}`,
69
75
  file: target,
70
76
  platform
71
77
  }) : null;
@@ -1,8 +1,8 @@
1
- import { n as pullHostedNativeLogs, r as readHostedNativeLogs, t as pullHostedMacosLogs } from "./hosted-logs-Opcjv1Lr.mjs";
2
- import { l as phaseLine } from "./command-output-BBW8eWPL.mjs";
3
- import { l as connectHost } from "./hosted-client-Cect92SZ.mjs";
4
- import { n as readHostedIos, t as readHostedAndroid } from "./ios-state-Ce_dystM.mjs";
5
- import "./hosted-macos-BP9Kucat.mjs";
1
+ import { n as pullHostedNativeLogs, r as readHostedNativeLogs, t as pullHostedMacosLogs } from "./hosted-logs-BteXG8rm.mjs";
2
+ import { l as phaseLine } from "./command-output-CfPW1bKP.mjs";
3
+ import { l as connectHost } from "./hosted-client-Hs-fD4L3.mjs";
4
+ import { n as readHostedIos, t as readHostedAndroid } from "./ios-state-BEIJRJ7P.mjs";
5
+ import "./hosted-macos-B4byscei.mjs";
6
6
  import { watch } from "node:fs";
7
7
  import { dirname, relative, resolve, sep } from "node:path";
8
8
  import { ambientWrite, configDir } from "@stim-cli/core";
@@ -1,9 +1,9 @@
1
1
  import { o as cancellableSleep } from "./exec-aULwtNly.mjs";
2
- import { a as agentAccess, i as closeAgentConnection, o as removeAgentAccess, t as pullHostedMacosLogs } from "./hosted-logs-Opcjv1Lr.mjs";
3
- import { l as phaseLine } from "./command-output-BBW8eWPL.mjs";
4
- import { n as macosDir } from "./state-C2AUjjhg.mjs";
5
- import { st as runCancellationSignal } from "./toolchain-DLdoyxIt.mjs";
6
- import { _ as stoppedSession, a as SESSION_TIMEOUT_MS, c as call, f as hostedSession, g as sha256, h as settle, i as RESERVE_TIMEOUT_MS, l as connectHost, o as attach, r as PREPARE_TIMEOUT_MS, s as bundleManifest, t as INSTALL_TIMEOUT_MS, u as heldNoLonger, v as unknownSession, y as upload } from "./hosted-client-Cect92SZ.mjs";
2
+ import { a as agentAccess, i as closeAgentConnection, o as removeAgentAccess, t as pullHostedMacosLogs } from "./hosted-logs-BteXG8rm.mjs";
3
+ import { l as phaseLine } from "./command-output-CfPW1bKP.mjs";
4
+ import { n as macosDir } from "./state-CiflgxTP.mjs";
5
+ import { st as runCancellationSignal } from "./toolchain-Bnm8vUCV.mjs";
6
+ import { _ as stoppedSession, a as SESSION_TIMEOUT_MS, c as call, f as hostedSession, g as sha256, h as settle, i as RESERVE_TIMEOUT_MS, l as connectHost, o as attach, r as PREPARE_TIMEOUT_MS, s as bundleManifest, t as INSTALL_TIMEOUT_MS, u as heldNoLonger, v as unknownSession, y as upload } from "./hosted-client-Hs-fD4L3.mjs";
7
7
  import { randomUUID } from "node:crypto";
8
8
  import { join } from "node:path";
9
9
  import { ambientContext, ambientWrite } from "@stim-cli/core";
@@ -1,10 +1,10 @@
1
1
  import { o as cancellableSleep, x as withDrainingCancellation } from "./exec-aULwtNly.mjs";
2
- import { E as workspaceDir, l as withWorkspaceStateLockAsync, r as readWorkspaceState$1 } from "./workspace-state-DGwkPXwq.mjs";
3
- import { a as agentAccess, i as closeAgentConnection, n as pullHostedNativeLogs, o as removeAgentAccess } from "./hosted-logs-Opcjv1Lr.mjs";
4
- import { st as runCancellationSignal } from "./toolchain-DLdoyxIt.mjs";
5
- import { _ as stoppedSession, a as SESSION_TIMEOUT_MS, c as call, f as hostedSession, g as sha256, h as settle, i as RESERVE_TIMEOUT_MS, l as connectHost, m as requireHostedAppMode, n as OFFER_TIMEOUT_MS, o as attach, r as PREPARE_TIMEOUT_MS, s as bundleManifest, t as INSTALL_TIMEOUT_MS, u as heldNoLonger, v as unknownSession, y as upload } from "./hosted-client-Cect92SZ.mjs";
6
- import { a as requireHostedMetro, i as requestHostedMetro, n as closeHostedMetro } from "./metro-request-BtF7ox53.mjs";
7
- import { s as writeHostedNative } from "./ios-state-Ce_dystM.mjs";
2
+ import { E as workspaceDir, l as withWorkspaceStateLockAsync, r as readWorkspaceState$1 } from "./workspace-state-3bF4TB8P.mjs";
3
+ import { a as agentAccess, i as closeAgentConnection, n as pullHostedNativeLogs, o as removeAgentAccess } from "./hosted-logs-BteXG8rm.mjs";
4
+ import { st as runCancellationSignal } from "./toolchain-Bnm8vUCV.mjs";
5
+ import { _ as stoppedSession, a as SESSION_TIMEOUT_MS, c as call, f as hostedSession, g as sha256, h as settle, i as RESERVE_TIMEOUT_MS, l as connectHost, m as requireHostedAppMode, n as OFFER_TIMEOUT_MS, o as attach, r as PREPARE_TIMEOUT_MS, s as bundleManifest, t as INSTALL_TIMEOUT_MS, u as heldNoLonger, v as unknownSession, y as upload } from "./hosted-client-Hs-fD4L3.mjs";
6
+ import { a as requestHostedMetro, n as closeHostedMetro, o as requireHostedMetro, r as hostedAgentPort } from "./metro-request-DODHNXCN.mjs";
7
+ import { s as writeHostedNative } from "./ios-state-BEIJRJ7P.mjs";
8
8
  import { randomUUID } from "node:crypto";
9
9
  import { readFileSync } from "node:fs";
10
10
  import { join } from "node:path";
@@ -268,7 +268,7 @@ async function placeHostedNativeOnce(target, { root, slot, bundle, handoff, bund
268
268
  if (typeof delivery.notice === "string") note(delivery.notice);
269
269
  placement = {
270
270
  ...placement,
271
- agent: await agentAccess(platform === "ios" ? iosAgentRemoteConfig(root, slot) : androidAgentRemoteConfig(root, slot), host.credential, host.connection.supports(platform === "ios" ? "hosted-ios-agent" : "hosted-android-agent") ? delivery.agent : void 0, note, platform)
271
+ agent: await agentAccess(platform === "ios" ? iosAgentRemoteConfig(root, slot) : androidAgentRemoteConfig(root, slot), host.credential, host.connection.supports(platform === "ios" ? "hosted-ios-agent" : "hosted-android-agent") ? delivery.agent : void 0, note, platform, mode !== "development" ? void 0 : !host.connection.supports("hosted-agent-link") ? `${host.machine} runs a stim-server that cannot carry agent control over the local network. Update stim-server there, then retry.` : hostedAgentPort(root, session.id) ?? `The Metro supervisor has no agent relay ready for ${host.machine}. Rerun this command; if it persists, run stim stop; stim start, then retry.`)
272
272
  };
273
273
  return {
274
274
  placement,
@@ -1,11 +1,11 @@
1
- import { k as workspaceLogsDir$1, r as readWorkspaceState$1, s as updateWorkspaceStateAsync } from "./workspace-state-DGwkPXwq.mjs";
2
- import { V as deviceSlotKey, _ as loadConfig$1 } from "./config-AbUjIBOQ.mjs";
3
- import { o as formatLongDuration } from "./command-output-BBW8eWPL.mjs";
1
+ import { k as workspaceLogsDir$1, r as readWorkspaceState$1, s as updateWorkspaceStateAsync } from "./workspace-state-3bF4TB8P.mjs";
2
+ import { H as deviceSlotKey, v as loadConfig$1 } from "./config-D5qNWJEP.mjs";
3
+ import { o as formatLongDuration } from "./command-output-CfPW1bKP.mjs";
4
4
  import { i as createNdjsonWriter } from "./ndjson-CrjIsGzw.mjs";
5
- import { B as workspaceBuildInProgress, P as collectOwnedDeviceActivity, ht as teardownOwnedIosSim, pt as teardownOwnedAvd } from "./workspaces-7_RWoi6c.mjs";
5
+ import { B as workspaceBuildInProgress, P as collectOwnedDeviceActivity, ht as teardownOwnedIosSim, pt as teardownOwnedAvd } from "./workspaces-C59v9bpD.mjs";
6
6
  import { t as canonicalPath } from "./paths-BsJMoPP2.mjs";
7
- import { _ as workspaceDeviceLastUseAt } from "./activity-BTaUf0tc.mjs";
8
- import { d as listAllIosSims } from "./ios-CXnoVO_z.mjs";
7
+ import { _ as workspaceDeviceLastUseAt } from "./activity-yjH7Q3zn.mjs";
8
+ import { d as listAllIosSims } from "./ios-CdAV4-1E.mjs";
9
9
  import { join } from "node:path";
10
10
  import { LOG_ROTATE_BYTES } from "@stim-cli/core";
11
11
  import { DEVICE_IDLE_SHUTDOWN_KEY, DEVICE_RECLAIM_REASON, readDeviceIdleShutdowns } from "@stim-cli/core/state";
@@ -1,10 +1,10 @@
1
1
  import { l as WorkspaceProcessCleanupError, n as isTimeoutError, o as cancellableSleep, t as getExecutor } from "./exec-aULwtNly.mjs";
2
- import { P as formatBytes, _ as loadConfig$1 } from "./config-AbUjIBOQ.mjs";
2
+ import { F as formatBytes, v as loadConfig$1 } from "./config-D5qNWJEP.mjs";
3
3
  import { n as stripAnsi, r as waitForChild, t as createLineReader } from "./process-output-Fpl8PYi8.mjs";
4
4
  import { n as runOperationFileQuiet, t as runOperationFile } from "./operation-exec-Dp4FuIGR.mjs";
5
- import { i as stimDesktopInstalledAsync, n as settingDefault, t as STIM_DESKTOP_OPEN_OPTIONS } from "./stim-desktop-CYXLFpFS.mjs";
6
- import { Ct as forgetCreatedDeviceAsync, Et as recordCreatedDeviceAsync, dt as isStimOwnedSim } from "./android-DdKG6lyD.mjs";
7
- import { n as signedTailFor } from "./apk-signed-tail-B8kw4a8K.mjs";
5
+ import { i as stimDesktopInstalledAsync, n as settingDefault, t as STIM_DESKTOP_OPEN_OPTIONS } from "./stim-desktop-1XxET4S8.mjs";
6
+ import { Ot as recordCreatedDeviceAsync, Tt as forgetCreatedDeviceAsync, pt as isStimOwnedSim } from "./android-DsEj2CCs.mjs";
7
+ import { n as signedTailFor } from "./apk-signed-tail-B9Usseps.mjs";
8
8
  import { createHash } from "node:crypto";
9
9
  import { closeSync, existsSync, lstatSync, mkdtempSync, openSync, readSync, readdirSync, rmSync, writeFileSync } from "node:fs";
10
10
  import { tmpdir, totalmem } from "node:os";