stim 1.20.2 → 1.21.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 (217) hide show
  1. package/README.md +88 -8
  2. package/dist/acquire-artifact-CD4lbiWE.mjs +153 -0
  3. package/dist/act-DHAw2A2S.mjs +2667 -0
  4. package/dist/activity-BzVfHSrY.mjs +1376 -0
  5. package/dist/{agent-device-usage-output-Dzgv6lgR.mjs → agent-device-usage-output-zh1vhbLp.mjs} +21 -65
  6. package/dist/{android-HqP1_vB_.mjs → android-BEJ8Jpl7.mjs} +2 -2
  7. package/dist/{android-DCKZrNvV.mjs → android-BLgbNqox.mjs} +128 -78
  8. package/dist/{android-Dlzxshgp.mjs → android-CV_GCELZ.mjs} +5 -5
  9. package/dist/{android-Mz_Inn3w.mjs → android-DUYkNF4u.mjs} +184 -102
  10. package/dist/{android-cas-Bl-cLJBk.mjs → android-cas-Ct5b1xCK.mjs} +7 -6
  11. package/dist/android-cas-compiler.mjs +2 -2
  12. package/dist/android-project-BCAAmMwz.mjs +12 -0
  13. package/dist/api.d.mts +740 -8
  14. package/dist/api.mjs +585 -1
  15. package/dist/{artifact-HSwkIWEQ.mjs → artifact-B8T3S5gi.mjs} +223 -201
  16. package/dist/{artifact-D3czlUvT.mjs → artifact-DAuZ-FNU.mjs} +158 -148
  17. package/dist/{browser-web-CA-F1GQo.mjs → browser-web-QQzVjz3O.mjs} +26 -24
  18. package/dist/budget-Bs6SxxzO.mjs +9154 -0
  19. package/dist/{build-DYDbfkk5.mjs → build-CoPzQRNw.mjs} +144 -96
  20. package/dist/build-cache-DmyR9Zzc.mjs +602 -0
  21. package/dist/build-plan-CaFskB7_.mjs +188 -0
  22. package/dist/{cache-manifest-XH-fvDUj.mjs → cache-manifest-DkkqguoK.mjs} +2 -2
  23. package/dist/cache-manifest.mjs +1 -1
  24. package/dist/{ccache-zdKiYQ3E.mjs → ccache-BJRZ9pG3.mjs} +10 -9
  25. package/dist/{cdp-Dq_ypAlJ.mjs → cdp-CWmIUMbw.mjs} +18 -5
  26. package/dist/{chrome-BnzZMqsR.mjs → chrome-B6tfCta1.mjs} +7 -4
  27. package/dist/{cli-CLP3diBm.mjs → cli-0DYf8QhQ.mjs} +20 -20
  28. package/dist/cli.mjs +1 -1
  29. package/dist/client-B7QVOlCr.d.mts +253 -0
  30. package/dist/client-UXD70ZbM.mjs +1406 -0
  31. package/dist/collector-run.d.mts +4 -40
  32. package/dist/collector-run.mjs +9 -8
  33. package/dist/{command-output-BTy8hZ0F.mjs → command-output-B1UrjZHL.mjs} +1 -1
  34. package/dist/config-CYcBF478.mjs +949 -0
  35. package/dist/{created-devices-Dzd5C2LF.mjs → created-devices-CqeBlrEn.mjs} +3 -3
  36. package/dist/{dependency-state-DG0p31-y.mjs → dependency-state-BbKQW83h.mjs} +20 -5
  37. package/dist/{detached-entry-B9C_zeQC.mjs → detached-entry-DFRyG1_o.mjs} +1 -1
  38. package/dist/{dev-client-DAWVIGBu.mjs → dev-client-UOeESKm6.mjs} +2 -2
  39. package/dist/device-DCxH7_By.mjs +1068 -0
  40. package/dist/{device-BECOiufl.mjs → device-ExJq8Ei7.mjs} +35 -35
  41. package/dist/{device-capacity-CBUAcRyQ.mjs → device-capacity-CMlqEcvw.mjs} +43 -13
  42. package/dist/device-host-worker.mjs +65 -61
  43. package/dist/{device-ios-COSi6oS1.mjs → device-ios-DRUeKaFV.mjs} +48 -45
  44. package/dist/{device-lease-run-Cw2M4z51.mjs → device-lease-run-C8raGFoP.mjs} +14 -26
  45. package/dist/{device-pool-Bf-pOhSw.mjs → device-pool-DMRpVX9a.mjs} +5 -5
  46. package/dist/doctor-BLHCYxzi.mjs +2 -0
  47. package/dist/{doctor-kfMBnUw6.mjs → doctor-DTvaDOda.mjs} +281 -317
  48. package/dist/{doctor-CB8oZKCk.mjs → doctor-Dhqebm0q.mjs} +183 -91
  49. package/dist/doctor-fingerprint.mjs +19 -0
  50. package/dist/{doctor-watchman-BRyV2adA.mjs → doctor-watchman-Cv1F10Kj.mjs} +40 -21
  51. package/dist/{eas-build-JyfqyHfC.mjs → eas-build-BomfRnFT.mjs} +53 -20
  52. package/dist/{build-plan-B7SZsc2N.mjs → eas-fallback-DJwZpy0c.mjs} +26 -208
  53. package/dist/{watchman-CPQFZLXY.mjs → eas-session-ledger-op6A54yi.mjs} +229 -11
  54. package/dist/{error-diagnostics-BR677IDM.mjs → error-diagnostics-Bf0rZvG9.mjs} +172 -82
  55. package/dist/exec-H20xSg2T.d.mts +65 -0
  56. package/dist/exec-_UbtCQcG.mjs +750 -0
  57. package/dist/gc-C-WwL0yi.mjs +3 -0
  58. package/dist/{gc-qWOMdnER.mjs → gc-_-dCXdyL.mjs} +584 -796
  59. package/dist/git-C3iTsJaZ.mjs +73 -0
  60. package/dist/{guide-G9cAbVAc.mjs → guide-Yqwt2bd6.mjs} +941 -269
  61. package/dist/{hosted-android-C3MHGld1.mjs → hosted-android-Bw-honsd.mjs} +8 -6
  62. package/dist/{hosted-client-ZEKpS9vw.mjs → hosted-client-DB_gBPfY.mjs} +34 -19
  63. package/dist/{hosted-ios-l-o3iM9J.mjs → hosted-ios-CUvOf4Vr.mjs} +1 -1
  64. package/dist/{hosted-logs-EzQM7-3Z.mjs → hosted-logs-DL_P5AdN.mjs} +77 -43
  65. package/dist/{hosted-macos-BACVAVqE.mjs → hosted-macos-QI141yKk.mjs} +21 -17
  66. package/dist/{hosted-native-A4s1QPXe.mjs → hosted-native-Ksyjgzq-.mjs} +30 -21
  67. package/dist/idle-BVeRGroT.d.mts +24 -0
  68. package/dist/{idle-shutdown-DtxhPTKe.mjs → idle-shutdown-DaKmZi0z.mjs} +11 -10
  69. package/dist/{ios-BwVCF7oA.mjs → ios-CQ68hG9s.mjs} +8 -8
  70. package/dist/{ios-RkW4pnQf.mjs → ios-CpByZy12.mjs} +140 -80
  71. package/dist/{ios-device-B5veSDDQ.mjs → ios-device-CXGP1bsD.mjs} +1 -1
  72. package/dist/{ios-device-D7pi2Sca.mjs → ios-device-CwFMehUS.mjs} +51 -26
  73. package/dist/{ios-project-K9hY_Jab.mjs → ios-project-WuwwBdvW.mjs} +13 -13
  74. package/dist/{ios-nOLJGVdy.mjs → ios-qNJt4dFm.mjs} +1 -1
  75. package/dist/ios-signing-DTTIChGs.mjs +489 -0
  76. package/dist/{ios-state-CBLe1xYw.mjs → ios-state-DvFX-K_l.mjs} +1 -1
  77. package/dist/{ios-Ce7bc7br.mjs → ios-ubFsBzwq.mjs} +129 -86
  78. package/dist/{js-swap-Bb3nsS07.mjs → js-swap-Cc1dttps.mjs} +53 -26
  79. package/dist/{launch-F4sOqAhB.mjs → launch-CD1yVc7U.mjs} +91 -90
  80. package/dist/{launch-C3qJ6Agb.mjs → launch-Zq2mOSDy.mjs} +82 -541
  81. package/dist/logs-BS00Anxf.mjs +352 -0
  82. package/dist/logs-CMm05HxN.mjs +255 -0
  83. package/dist/{macos-D7uDvO-z.mjs → macos-B65--pPd.mjs} +1 -1
  84. package/dist/{macos-Ch5VJmcS.mjs → macos-C-0qJ6zq.mjs} +225 -212
  85. package/dist/macos-run.mjs +1 -1
  86. package/dist/maintenance-run.mjs +122 -247
  87. package/dist/{metro-DnCEjCxs.mjs → metro-DEXpIhHC.mjs} +67 -12
  88. package/dist/{metro-gateway-CX9Xlkq2.mjs → metro-gateway-BMJNlj3E.mjs} +20 -12
  89. package/dist/{named-ports-4WopRPg-.mjs → named-ports-BWTzt6mu.mjs} +8 -63
  90. package/dist/native-android-JACyjTZs.mjs +844 -0
  91. package/dist/{native-gradle-inputs-BsLRrl7u.mjs → native-gradle-inputs-Bep9JcSF.mjs} +128 -140
  92. package/dist/native-inputs-TRTTTRxt.mjs +91 -0
  93. package/dist/native-run-DY_RLx3C.mjs +277 -0
  94. package/dist/{native-runtime-k8dTIrbK.mjs → native-runtime-Dg1Q7tfF.mjs} +9 -8
  95. package/dist/{native-xcode-inputs-CykopJdM.mjs → native-xcode-inputs-CB2iQeWq.mjs} +27 -18
  96. package/dist/{native-xcode-ios-Bbf30Gm0.mjs → native-xcode-ios-CXX39F7o.mjs} +96 -47
  97. package/dist/{ndjson-GOnbyvso.mjs → ndjson-CgE_o3Dl.mjs} +3 -3
  98. package/dist/offload-worker.d.mts +1 -46
  99. package/dist/offload-worker.mjs +197 -66
  100. package/dist/{ownership-DCtw_2IA.mjs → ownership-DEmM5vhh.mjs} +3 -3
  101. package/dist/{page-DRHy329N.mjs → page-CPaHeYqj.mjs} +7 -4
  102. package/dist/paths-BsJMoPP2.mjs +12 -0
  103. package/dist/paths-DQtTgsBf.mjs +42 -0
  104. package/dist/{placement-log-MOfPVgaM.mjs → placement-log-BqGbTa61.mjs} +5 -5
  105. package/dist/{plan-placement-V0TdzYXp.mjs → plan-placement-Bwj-IjY1.mjs} +6 -6
  106. package/dist/{ports-DdSUicVo.mjs → ports-D-MmCTdv.mjs} +3 -3
  107. package/dist/prebuild-uD_ipCxF.mjs +449 -0
  108. package/dist/{project-_phNIJfs.mjs → project-BZNjmW_8.mjs} +65 -37
  109. package/dist/{pull-request-CQ8sPbZa.mjs → pull-request-Lfetn8Kr.mjs} +2 -2
  110. package/dist/pull-request-zjCSAryH.d.mts +120 -0
  111. package/dist/pull-requests.d.mts +1 -119
  112. package/dist/pull-requests.mjs +1 -1
  113. package/dist/{react-native-android-CM1XRDwv.mjs → react-native-android-DuzH0cn3.mjs} +74 -48
  114. package/dist/{gradle-DSrXb6lG.mjs → react-native-build-rhk4smwQ.mjs} +292 -15
  115. package/dist/react-native-doctor-CQpuIMso.mjs +234 -0
  116. package/dist/{react-native-ios-Dhq_eiYQ.mjs → react-native-ios-Bhq1rJeV.mjs} +46 -28
  117. package/dist/{recordings-D0d5aQt0.mjs → recordings-DYteQSu4.mjs} +12 -9
  118. package/dist/{reload-C8JhxjSh.mjs → reload-CmTRFJaX.mjs} +93 -47
  119. package/dist/reload-DzjSl5P_.mjs +2 -0
  120. package/dist/{remote-BiR6aYjy.mjs → remote-ByYkXdGD.mjs} +15 -10
  121. package/dist/result-DNiS-tTP.mjs +434 -0
  122. package/dist/{run-BtPgpKks.mjs → run-D23HmJMs.mjs} +4 -5
  123. package/dist/selection-CSl9i5qU.mjs +619 -0
  124. package/dist/{server-bare-R2m_gsH8.mjs → server-bare-CxTrvDg8.mjs} +3 -4
  125. package/dist/{server-command-CTtCkmcQ.mjs → server-command-CtzloH3v.mjs} +14 -7
  126. package/dist/server-expo-C-90EhcG.mjs +2 -0
  127. package/dist/{server-expo-DsguMGUF.mjs → server-expo-DT_F_uVI.mjs} +50 -9
  128. package/dist/{settings-B717odTD.mjs → settings-CsJDEXgK.mjs} +234 -24
  129. package/dist/{settings-vk3mJGSm.d.mts → settings-DkdmlelE.d.mts} +6 -4
  130. package/dist/settings-T1gtc3bf.mjs +114 -0
  131. package/dist/settings-write-HyVZ8eQ8.mjs +359 -0
  132. package/dist/settings.schema.json +163 -16
  133. package/dist/{simslim-CCr3rE-A.mjs → simslim-Cgk17SXV.mjs} +14 -12
  134. package/dist/{slot-launch-DPt0m9Nd.mjs → slot-launch-DF8VfT64.mjs} +15 -20
  135. package/dist/{spawn-entry-Dcb7sDUz.mjs → spawn-entry-jw19m8Nl.mjs} +0 -1
  136. package/dist/stage-BIS0ydsV.mjs +183 -0
  137. package/dist/{start-BWqzODpb.mjs → start-7lpH7hmN.mjs} +63 -44
  138. package/dist/{start-TXeMmmO2.mjs → start-RChynRDp.mjs} +1 -1
  139. package/dist/{state-D1Icyjlu.mjs → state-C4ZtUisW.mjs} +40 -13
  140. package/dist/{state-CG_OFoaA.mjs → state-XMziexv2.mjs} +3 -2
  141. package/dist/{stats-BoY5sEPU.mjs → stats-53T7OTb9.mjs} +7 -13
  142. package/dist/stats-report-BFfMpWWF.mjs +16 -0
  143. package/dist/{status-WgFbkRzj.mjs → status-BL7jVyAB.mjs} +535 -447
  144. package/dist/status-CSi6tlRq.mjs +2 -0
  145. package/dist/{status-BTA7kKiQ.mjs → status-DqEUSSxs.mjs} +6 -4
  146. package/dist/{status-watch-C23ILtf9.mjs → status-watch-GDCfqpTE.mjs} +187 -22
  147. package/dist/status-worker.d.mts +1 -0
  148. package/dist/status-worker.mjs +19 -0
  149. package/dist/stim-desktop-C12we7lz.mjs +112 -0
  150. package/dist/{stop-DR6VoDIk.mjs → stop-Bk_1qiV6.mjs} +2 -2
  151. package/dist/{stop-BNmBMi1F.mjs → stop-CPzlM1L-.mjs} +138 -98
  152. package/dist/{stop-DPEhunlw.mjs → stop-D7h6jAXQ.mjs} +11 -10
  153. package/dist/supervisor-run.d.mts +8 -17
  154. package/dist/supervisor-run.mjs +63 -27
  155. package/dist/{support-DMTqj3Hm.mjs → support-67vraa1Q.mjs} +18 -11
  156. package/dist/{support-CwjbFs9H.mjs → support-Dlu37g6H.mjs} +12 -12
  157. package/dist/{swiftpm-macos-DRNVJWlb.mjs → swiftpm-macos-p8WZbxKU.mjs} +35 -24
  158. package/dist/toolchain-DhKwp9iI.mjs +2 -0
  159. package/dist/toolchain-Hp9sCpK4.mjs +873 -0
  160. package/dist/{trigger-CigAYjeL.mjs → trigger-BAahxUPq.mjs} +20 -5
  161. package/dist/trigger-Bf3uDcpv.mjs +2 -0
  162. package/dist/{warm-progress-BuY-Fvfn.mjs → warm-progress-BHX5r0Ei.mjs} +250 -156
  163. package/dist/{web-BDBMxDpx.mjs → web-CJmIj6AI.mjs} +18 -13
  164. package/dist/web-DknQETPs.mjs +2 -0
  165. package/dist/web-run.mjs +11 -12
  166. package/dist/windows-report.d.mts +1 -0
  167. package/dist/windows-report.mjs +18 -0
  168. package/dist/windows-report.ps1 +43 -0
  169. package/dist/workspace-process-lock-1_1OoVW5.mjs +57 -0
  170. package/dist/{workspace-state-DJt_MMvf.mjs → workspace-state-Cjuigeeu.mjs} +62 -58
  171. package/dist/{workspaces-C36s2jb9.mjs → workspaces-BccMFmGQ.mjs} +1188 -1079
  172. package/dist/worktree-CK2MpJW5.mjs +2 -0
  173. package/dist/{worktree-CgvJYJ-Q.mjs → worktree-DQ6Zi2Mv.mjs} +304 -53
  174. package/dist/xcode-Zlx3qjEs.mjs +896 -0
  175. package/package.json +7 -5
  176. package/shim/android-optimizations.gradle +7 -5
  177. package/dist/activity-BSV745Kz.mjs +0 -629
  178. package/dist/api-qvOzWyfO.mjs +0 -154
  179. package/dist/api-run.mjs +0 -294
  180. package/dist/app-install-BIKtMfPq.mjs +0 -896
  181. package/dist/artifact-lifecycle-DzPpBYvK.mjs +0 -30
  182. package/dist/budget-f7516Zk0.mjs +0 -4106
  183. package/dist/build-facts-DcLv2HSd.d.mts +0 -111
  184. package/dist/cancellation-C_22VQTH.mjs +0 -24
  185. package/dist/client-qQAaXSUs.mjs +0 -1771
  186. package/dist/config-BYDxlMY3.mjs +0 -562
  187. package/dist/device-DuiPJQSn.mjs +0 -290
  188. package/dist/device-lease-BX1sJQ2E.mjs +0 -1224
  189. package/dist/device-remote-xRT0Vmo9.mjs +0 -1272
  190. package/dist/eas-session-ledger-Cm0Gwq2F.mjs +0 -207
  191. package/dist/exec-DqKo9GKJ.mjs +0 -268
  192. package/dist/git-DDfFzkMm.mjs +0 -45
  193. package/dist/launch-verify-B6Q8hZP-.mjs +0 -428
  194. package/dist/logs-BGrEUaEs.mjs +0 -383
  195. package/dist/miss-reason-mKzZ4VvN.mjs +0 -238
  196. package/dist/native-android-D5qc3-_v.mjs +0 -226
  197. package/dist/ndjson-Du3j0AQ9.d.mts +0 -17
  198. package/dist/plan-C4TjdPsR.mjs +0 -260
  199. package/dist/prebuild-DYbucPXy.mjs +0 -212
  200. package/dist/preview-78a9kR_-.mjs +0 -1011
  201. package/dist/react-native-build-CtCccJtb.mjs +0 -239
  202. package/dist/react-native-doctor-CfRnFfjs.mjs +0 -78
  203. package/dist/remote-cache-OyOyVIS3.mjs +0 -526
  204. package/dist/result-CaJg5Iat.mjs +0 -186
  205. package/dist/server-expo-Bigriskl.mjs +0 -2
  206. package/dist/settings-BORNaaCO.mjs +0 -402
  207. package/dist/spawn-claims-DVS5RkMS.mjs +0 -72
  208. package/dist/stage-KNTQopqv.mjs +0 -157
  209. package/dist/stim-desktop-BzBN2Em2.mjs +0 -63
  210. package/dist/tailnet-Dh-qiYzG.mjs +0 -265
  211. package/dist/toolchain-BFkAcstd.mjs +0 -2
  212. package/dist/toolchain-DxSsNssu.mjs +0 -1657
  213. package/dist/trigger-9_USgmxz.mjs +0 -2
  214. package/dist/web-Dq-i3wzJ.mjs +0 -2
  215. package/dist/worktree-DpLdCHga.mjs +0 -2
  216. package/dist/{api-run.d.mts → doctor-fingerprint.d.mts} +0 -0
  217. package/dist/{errors-DzkMYh8Q.mjs → errors-ccaPc4ox.mjs} +2 -2
@@ -1,6 +1,7 @@
1
- import { c as connectHost, s as call } from "./hosted-client-ZEKpS9vw.mjs";
2
- import { i as requestHostedMetro } from "./metro-gateway-CX9Xlkq2.mjs";
3
- import { n as prepareHostedNative, r as stopHostedNative, t as placeHostedNative } from "./hosted-native-A4s1QPXe.mjs";
1
+ import { en as throwIfCancelled } from "./settings-CsJDEXgK.mjs";
2
+ import { c as call, l as connectHost } from "./hosted-client-DB_gBPfY.mjs";
3
+ import { i as requestHostedMetro } from "./metro-gateway-BMJNlj3E.mjs";
4
+ import { n as prepareHostedNative, r as stopHostedNative, t as placeHostedNative } from "./hosted-native-Ksyjgzq-.mjs";
4
5
  //#region src/device-host/hosted-android.ts
5
6
  async function prepareHostedAndroid(machine, selectors, recorded, mode) {
6
7
  return await prepareHostedNative(machine, selectors, recorded, "android", false, mode);
@@ -15,10 +16,11 @@ async function placeHostedAndroid(target, options) {
15
16
  async function stopHostedAndroid(root, slot) {
16
17
  await stopHostedNative(root, slot, "android");
17
18
  }
18
- async function reopenHostedAndroidMetro(root, placement, clientMetroPort) {
19
- const host = await connectHost(placement.machine, void 0, true);
19
+ async function reopenHostedAndroidMetro(root, placement, clientMetroPort, signal) {
20
+ const host = await connectHost(placement.machine, void 0, true, signal);
20
21
  try {
21
- const gateway = await requestHostedMetro(root, placement.session, host.credential);
22
+ const gateway = await requestHostedMetro(root, placement.session, host.credential, signal);
23
+ throwIfCancelled(signal, "reload");
22
24
  await call(host, "device-host.metro.open", {
23
25
  session: placement.session,
24
26
  clientMetroPort,
@@ -1,19 +1,22 @@
1
- import { c as pinnedEndpoint$1 } from "./tailnet-Dh-qiYzG.mjs";
2
- import { r as throwIfCancelled, t as cancellableSleep } from "./cancellation-C_22VQTH.mjs";
3
- import { t as BuildConnection, x as configuredMachines } from "./client-qQAaXSUs.mjs";
1
+ import { it as resolveEndpoint } from "./config-CYcBF478.mjs";
2
+ import { Qt as cancellableSleep, en as throwIfCancelled } from "./settings-CsJDEXgK.mjs";
3
+ import { t as BuildConnection } from "./client-UXD70ZbM.mjs";
4
+ import { u as configuredMachines } from "./selection-CSl9i5qU.mjs";
5
+ import { createHash } from "node:crypto";
4
6
  import { lstatSync, readFileSync, readdirSync, readlinkSync } from "node:fs";
5
7
  import { join } from "node:path";
6
- import { DEVICE_HOST_LIMITS, HOSTED_APP_CHUNK_BYTES, hostedMacosAppSlot, hostedWorkerSettleMs, isJsonObject, readDeviceHostMachines } from "@stim-cli/core/state";
7
- import { createHash } from "node:crypto";
8
+ import { configDir } from "@stim-cli/core";
9
+ import { DEVICE_HOST_LIMITS, HOSTED_APP_CHUNK_BYTES, HOSTED_PARKED_CANDIDATES, hostedMacosAppSlot, hostedWorkerSettleMs, isJsonObject, readDeviceHostMachines } from "@stim-cli/core/state";
8
10
  //#region src/device-host/hosted-client.ts
9
11
  const CONNECT_TIMEOUT_MS = 1e4;
10
12
  const HOSTED_POLL_MARGIN_MS = 45e3;
11
13
  const HOSTED_REQUEST_MARGIN_MS = 5e3;
12
14
  const workerBoundMs = (deadlineMs) => deadlineMs + hostedWorkerSettleMs();
13
- const SESSION_TIMEOUT_MS = hostedWorkerSettleMs() + workerBoundMs(DEVICE_HOST_LIMITS.logsMs) + workerBoundMs(DEVICE_HOST_LIMITS.stopMs) + HOSTED_POLL_MARGIN_MS;
15
+ const SESSION_TIMEOUT_MS = hostedWorkerSettleMs() + workerBoundMs(DEVICE_HOST_LIMITS.logsMs) + 2 * workerBoundMs(DEVICE_HOST_LIMITS.stopMs) + HOSTED_POLL_MARGIN_MS;
14
16
  const PREPARE_TIMEOUT_MS = workerBoundMs(DEVICE_HOST_LIMITS.prepareMs) + HOSTED_POLL_MARGIN_MS;
15
17
  const INSTALL_TIMEOUT_MS = workerBoundMs(DEVICE_HOST_LIMITS.prepareMs) + HOSTED_POLL_MARGIN_MS;
16
18
  const OFFER_TIMEOUT_MS = workerBoundMs(DEVICE_HOST_LIMITS.offerMs) + HOSTED_REQUEST_MARGIN_MS;
19
+ const RESERVE_TIMEOUT_MS = HOSTED_PARKED_CANDIDATES * (workerBoundMs(DEVICE_HOST_LIMITS.offerMs) + workerBoundMs(DEVICE_HOST_LIMITS.stopMs)) + workerBoundMs(DEVICE_HOST_LIMITS.offerMs) + HOSTED_REQUEST_MARGIN_MS;
17
20
  const sha256 = (content) => createHash("sha256").update(content).digest("hex");
18
21
  function credentialRefusal(message, remedy = "Run stim doctor.") {
19
22
  return Object.assign(new Error(message), {
@@ -32,22 +35,26 @@ function hostingCredential(machine) {
32
35
  throw credentialRefusal("The hosting credentials are unreadable. Run stim doctor.");
33
36
  }
34
37
  const credential = credentials.find((each) => each.machine === machine);
35
- if (!credential) throw credentialRefusal(`This Mac has not asked ${machine} for hosting access. Run stim doctor --fix.`, "Run stim doctor --fix.");
38
+ if (!credential) throw credentialRefusal(`This machine has not asked ${machine} for hosting access. Run stim doctor --fix.`, "Run stim doctor --fix.");
36
39
  if (credential.state !== "approved") {
37
- const remedy = `A person on ${machine} approves it with stim-server devices grant ${credential.deviceId} --device-host; then run stim doctor.`;
38
- throw credentialRefusal(`${machine} has not confirmed hosting access for this Mac. ${remedy}`, remedy);
40
+ const remedy = `A person on ${machine} runs the stim-server setup command stim doctor --fix printed; then run stim doctor.`;
41
+ throw credentialRefusal(`${machine} has not confirmed hosting access for this machine. ${remedy}`, remedy);
39
42
  }
40
43
  return credential;
41
44
  }
42
- /** Connects only to the pinned node of an approved remote Mac, and only when it grants `device-host`. */
43
- async function connectHost(machine, timeoutMs = CONNECT_TIMEOUT_MS, strict = false) {
45
+ /** Connects only to the stim-server that proves it holds the approved machine's pinned key, and only for `device-host`. */
46
+ async function connectHost(machine, timeoutMs = CONNECT_TIMEOUT_MS, strict = false, signal) {
47
+ signal?.throwIfAborted();
44
48
  const credential = hostingCredential(machine);
45
- const target = await pinnedEndpoint$1(credential);
49
+ const target = await resolveEndpoint(credential, void 0, signal);
50
+ signal?.throwIfAborted();
46
51
  if (typeof target === "string") throw Object.assign(/* @__PURE__ */ new Error(`Stim does not connect to ${machine}: ${target}.`), { code: "STIM_HOSTING_REFUSED" });
47
- const opened = await BuildConnection.open(target, credential.deviceToken, timeoutMs, "device-host").catch((error) => {
52
+ const opened = await BuildConnection.open(target, credential.serverKey, timeoutMs, "device-host", signal).catch((error) => {
48
53
  throw Object.assign(new Error(`${machine} is unreachable. Check stim-server and its tailnet serve route on ${machine}.`, { cause: error }), { code: strict ? "STIM_HOSTING_REFUSED" : "closed" });
49
54
  });
50
- if (!(opened instanceof BuildConnection)) throw Object.assign(/* @__PURE__ */ new Error(opened.refused ? `${machine} refused this Mac: ${opened.failure.replaceAll(credential.deviceToken, "[redacted]").replace(/\.+$/, "")}. Run stim doctor.` : `${machine} is unreachable: ${opened.failure.replaceAll(credential.deviceToken, "[redacted]")}. Check stim-server and its tailnet serve route on ${machine}.`), {
55
+ if (signal?.aborted && opened instanceof BuildConnection) opened.close();
56
+ signal?.throwIfAborted();
57
+ if (!(opened instanceof BuildConnection)) throw Object.assign(/* @__PURE__ */ new Error(opened.code === "server-outdated" ? `${machine} runs a stim-server that speaks an older protocol than this Stim. Run stim doctor for the command that updates it there, then retry.` : opened.refused ? `${machine} refused this machine: ${opened.failure.replace(/\.+$/, "")}. Run stim doctor.` : `${machine} is unreachable: ${opened.failure}. Check stim-server and its tailnet serve route on ${machine}.`), {
51
58
  code: strict ? "STIM_HOSTING_REFUSED" : opened.refused ? "STIM_HOSTING_REFUSED" : opened.code ?? "closed",
52
59
  hostCode: opened.code
53
60
  });
@@ -63,8 +70,8 @@ function requireHostedAppMode(host, platform, mode) {
63
70
  }
64
71
  const gatewaySecrets = /* @__PURE__ */ new WeakMap();
65
72
  function redactHostText(host, message) {
66
- for (const secret of [host.credential.deviceToken, gatewaySecrets.get(host)]) if (secret) message = message.replaceAll(secret, "[redacted]");
67
- return message;
73
+ const secret = gatewaySecrets.get(host);
74
+ return secret ? message.replaceAll(secret, "[redacted]") : message;
68
75
  }
69
76
  async function call(host, method, params, timeoutMs) {
70
77
  if (method === "device-host.metro.open" && isJsonObject(params) && typeof params.secret === "string") gatewaySecrets.set(host, params.secret);
@@ -92,7 +99,11 @@ async function attach(host, session, timeoutMs, platform = "macos") {
92
99
  }
93
100
  const probeCache = /* @__PURE__ */ new Map();
94
101
  function probeHostedSession(placement, { timeoutMs = 3e3, ttlMs = 1e4 } = {}) {
95
- const key = JSON.stringify([placement.machine, placement.session]);
102
+ const key = JSON.stringify([
103
+ configDir(),
104
+ placement.machine,
105
+ placement.session
106
+ ]);
96
107
  const cached = probeCache.get(key);
97
108
  if (ttlMs > 0 && cached && cached.expiresAt > Date.now()) return cached.promise;
98
109
  const promise = probeSession(placement, timeoutMs);
@@ -145,8 +156,12 @@ async function settle(host, session, passing, timeoutMs, platform = "macos", sig
145
156
  }
146
157
  return session;
147
158
  }
159
+ const reason = (session) => session.notice ? `: ${session.notice.replace(/\.+$/, "")}` : "";
148
160
  function unknownSession(host, session) {
149
- return /* @__PURE__ */ new Error(`The hosted session ${session.id} on ${host.machine} is in an unknown state${session.notice ? `: ${session.notice}` : ""}. Run stim stop to reconcile it.`);
161
+ return /* @__PURE__ */ new Error(`The hosted session ${session.id} on ${host.machine} is in an unknown state${reason(session)}. Run stim stop to reconcile it.`);
162
+ }
163
+ function stoppedSession(host, session) {
164
+ return /* @__PURE__ */ new Error(`The hosted session ${session.id} on ${host.machine} stopped before it was ready${reason(session)}. Retry when the host can take it; it needs no stim stop.`);
150
165
  }
151
166
  function bundleManifest(bundle) {
152
167
  const files = [];
@@ -207,4 +222,4 @@ async function upload(host, ids, missing, content) {
207
222
  }
208
223
  }
209
224
  //#endregion
210
- export { attach as a, connectHost as c, probeHostedSession as d, requireHostedAppMode as f, upload as g, unknownSession as h, SESSION_TIMEOUT_MS as i, heldNoLonger as l, sha256 as m, OFFER_TIMEOUT_MS as n, bundleManifest as o, settle as p, PREPARE_TIMEOUT_MS as r, call as s, INSTALL_TIMEOUT_MS as t, hostedSession as u };
225
+ export { unknownSession as _, SESSION_TIMEOUT_MS as a, call as c, hostedSession as d, probeHostedSession as f, stoppedSession as g, sha256 as h, RESERVE_TIMEOUT_MS as i, connectHost as l, settle as m, OFFER_TIMEOUT_MS as n, attach as o, requireHostedAppMode as p, PREPARE_TIMEOUT_MS as r, bundleManifest as s, INSTALL_TIMEOUT_MS as t, heldNoLonger as u, upload as v };
@@ -1,4 +1,4 @@
1
- import { n as prepareHostedNative, r as stopHostedNative, t as placeHostedNative } from "./hosted-native-A4s1QPXe.mjs";
1
+ import { n as prepareHostedNative, r as stopHostedNative, t as placeHostedNative } from "./hosted-native-Ksyjgzq-.mjs";
2
2
  //#region src/device-host/hosted-ios.ts
3
3
  async function prepareHostedIos(machine, selectors, recorded, mode) {
4
4
  return await prepareHostedNative(machine, selectors, recorded, "ios", false, mode);
@@ -1,52 +1,84 @@
1
- import { K as validateDeviceSlot, z as deviceSlotFileKey } from "./config-BYDxlMY3.mjs";
2
- import { t as getExecutor } from "./exec-DqKo9GKJ.mjs";
3
- import { w as workspaceLogsDir$1, x as workspaceDir } from "./workspace-state-DJt_MMvf.mjs";
4
- import { l as phaseLine } from "./command-output-BTy8hZ0F.mjs";
5
- import { o as parseMachine$1 } from "./tailnet-Dh-qiYzG.mjs";
6
- import { r as createNdjsonWriter } from "./ndjson-GOnbyvso.mjs";
7
- import { n as macosDir } from "./state-CG_OFoaA.mjs";
8
- import { s as call } from "./hosted-client-ZEKpS9vw.mjs";
1
+ import { t as getExecutor } from "./exec-_UbtCQcG.mjs";
2
+ import { d as workspaceDir, m as workspaceLogsDir$1 } from "./paths-DQtTgsBf.mjs";
3
+ import { J as validateDeviceSlot, V as deviceSlotFileKey, tt as parseMachine$1 } from "./config-CYcBF478.mjs";
4
+ import { l as phaseLine } from "./command-output-B1UrjZHL.mjs";
5
+ import { n as macosDir } from "./state-XMziexv2.mjs";
6
+ import { r as createNdjsonWriter } from "./ndjson-CgE_o3Dl.mjs";
7
+ import { c as call } from "./hosted-client-DB_gBPfY.mjs";
9
8
  import { mkdirSync, readFileSync, realpathSync, renameSync, rmSync, writeFileSync } from "node:fs";
10
9
  import { dirname, join } from "node:path";
10
+ import { LOG_ROTATE_BYTES, ambientWrite, withDirLockAsync } from "@stim-cli/core";
11
11
  import { isJsonObject, parseHostedAgentGrant, parseHostedLogsCursor } from "@stim-cli/core/state";
12
- import { LOG_ROTATE_BYTES, withDirLock } from "@stim-cli/core";
12
+ import { loadAgentDriverModule } from "@stim-cli/agent-driver";
13
+ //#region src/device-host/agent-drivers.ts
14
+ const DRIVERS = { "agent-device": {
15
+ load: () => import("@stim-cli/agent-driver-agent-device"),
16
+ configFile: "agent-device-remote.json"
17
+ } };
18
+ function registered(name) {
19
+ return Object.hasOwn(DRIVERS, name);
20
+ }
21
+ /** The client side of a first-party driver, or a reason it is unavailable to this Stim. */
22
+ async function agentDriverClient(name) {
23
+ if (!registered(name)) return { unavailable: `this Stim has no ${name} driver` };
24
+ const { load, configFile } = DRIVERS[name];
25
+ const loaded = await loadAgentDriverModule(name, load);
26
+ if ("unavailable" in loaded) return loaded;
27
+ const { client } = loaded.module;
28
+ if (!client) return { unavailable: `the ${name} driver has no client` };
29
+ if (client.configFile !== configFile) return { unavailable: `the ${name} driver writes ${client.configFile}, not ${configFile}` };
30
+ return { client };
31
+ }
32
+ /**
33
+ * The config file names every first-party driver's client writes beside a placement. They are listed here rather
34
+ * than read from the drivers, so access is removed even when a driver package does not load.
35
+ */
36
+ function agentClientFiles() {
37
+ return Object.values(DRIVERS).map((driver) => driver.configFile);
38
+ }
39
+ //#endregion
13
40
  //#region src/device-host/hosted-agent.ts
14
- function agentAccess(file, credential, grant, note, platform = "macos") {
41
+ /** Removes every driver's client file beside `file`, the placement's agent-device config path. */
42
+ async function removeAgentAccess(file, keep) {
43
+ for (const name of agentClientFiles()) {
44
+ const path = join(dirname(file), name);
45
+ if (path !== keep) rmSync(path, { force: true });
46
+ }
47
+ }
48
+ async function agentAccess(file, credential, grant, note, platform = "macos") {
15
49
  const parsed = grant === void 0 ? null : parseHostedAgentGrant(grant);
16
- if (grant !== void 0 && !parsed) note(`${credential.machine} offered agent control this Stim does not support.`);
17
- if (!parsed || parsed.driver === "none" || parsed.lease.backend !== (platform === "macos" ? void 0 : `${platform}-instance`)) {
18
- rmSync(file, { force: true });
50
+ const issued = parsed && "token" in parsed ? parsed : null;
51
+ const loaded = issued ? await agentDriverClient(issued.driver) : null;
52
+ if (grant !== void 0 && !parsed || loaded && "unavailable" in loaded) note(`${credential.machine} offered agent control this Stim does not support.`);
53
+ const port = parseMachine$1(credential.machine)?.port ?? 443;
54
+ const client = loaded && "client" in loaded ? loaded.client : null;
55
+ const target = client ? join(dirname(file), client.configFile) : file;
56
+ const access = client && issued ? client.access({
57
+ grant: issued,
58
+ baseUrl: `https://${credential.address}${port === 443 ? "" : `:${port}`}${issued.path}`,
59
+ file: target,
60
+ platform
61
+ }) : null;
62
+ if (!issued || !access) {
63
+ await removeAgentAccess(file);
19
64
  return {
20
65
  driver: "none",
21
66
  setting: "hosting.agentDriver"
22
67
  };
23
68
  }
24
- const port = parseMachine$1(credential.machine)?.port ?? 443;
25
- const config = {
26
- daemonBaseUrl: `https://${credential.dnsName}${port === 443 ? "" : `:${port}`}${parsed.path}`,
27
- daemonAuthToken: parsed.token,
28
- tenant: parsed.lease.tenant,
29
- sessionIsolation: "tenant",
30
- runId: parsed.lease.runId,
31
- ...platform === "macos" ? { leaseId: parsed.scope } : {},
32
- leaseBackend: platform === "macos" ? "macos-app" : `${platform}-instance`,
33
- leaseProvider: "proxy",
34
- clientId: parsed.lease.clientId,
35
- deviceKey: parsed.lease.deviceKey,
36
- platform
37
- };
38
- mkdirSync(dirname(file), { recursive: true });
39
- const tmp = `${file}.${process.pid}.tmp`;
69
+ await removeAgentAccess(file, target);
70
+ mkdirSync(dirname(target), { recursive: true });
71
+ const tmp = `${target}.${process.pid}.tmp`;
40
72
  try {
41
- writeFileSync(tmp, `${JSON.stringify(config, null, 2)}\n`, { mode: 384 });
42
- renameSync(tmp, file);
73
+ writeFileSync(tmp, access.config, { mode: 384 });
74
+ renameSync(tmp, target);
43
75
  } finally {
44
76
  rmSync(tmp, { force: true });
45
77
  }
46
78
  return {
47
- driver: "agent-device",
48
- remoteConfig: file,
49
- command: `agent-device <command> --remote-config ${file}`
79
+ driver: issued.driver,
80
+ remoteConfig: target,
81
+ command: access.command
50
82
  };
51
83
  }
52
84
  //#endregion
@@ -59,7 +91,7 @@ function canonical(path) {
59
91
  }
60
92
  }
61
93
  function report(message) {
62
- process.stderr.write(`${phaseLine("device", message.replace(/\s+/g, " "))}\n`);
94
+ ambientWrite("stderr", `${phaseLine("device", message.replace(/\s+/g, " "))}\n`);
63
95
  }
64
96
  function closeAgentConnection(remoteConfig) {
65
97
  const exec = getExecutor();
@@ -118,10 +150,10 @@ function readCursor(root, session, target) {
118
150
  } catch {}
119
151
  }
120
152
  const sameCursor = (a, b) => JSON.stringify(Object.entries(a ?? {}).toSorted()) === JSON.stringify(Object.entries(b ?? {}).toSorted());
121
- function commitPage(root, session, before, records, cursor, target) {
153
+ async function commitPage(root, session, before, records, cursor, target) {
122
154
  if (!records.length && sameCursor(before, cursor)) return;
123
155
  const directory = logsStateDir(root, target);
124
- withDirLock(join(directory, "host-logs.lock"), () => {
156
+ await withDirLockAsync(join(directory, "host-logs.lock"), () => {
125
157
  if (!sameCursor(readCursor(root, session, target), before)) return;
126
158
  if (records.length) {
127
159
  const writer = createNdjsonWriter(hostLogFile(root, target), { maxBytes: LOG_ROTATE_BYTES });
@@ -149,14 +181,15 @@ function commitPage(root, session, before, records, cursor, target) {
149
181
  }
150
182
  }, { ensureParent: () => mkdirSync(directory, { recursive: true }) });
151
183
  }
152
- async function pullHostedMacosLogs(root, placement, host, target = MACOS, maxPages = MAX_PAGES, final = false) {
184
+ async function pullHostedMacosLogs(root, placement, host, target = MACOS, maxPages = MAX_PAGES, final = false, signal) {
153
185
  const deadline = Date.now() + (final ? 3e4 : 12e4);
154
186
  let checkpoint;
155
187
  let progressAt = 0;
156
188
  for (let page = 0; page < maxPages && Date.now() < deadline; page++) {
189
+ signal?.throwIfAborted();
157
190
  const before = readCursor(root, placement.session, target);
158
191
  if (final && Date.now() >= progressAt) {
159
- process.stderr.write(`Copying final native logs from ${host.machine} (page ${page + 1})\n`);
192
+ ambientWrite("stderr", `Copying final native logs from ${host.machine} (page ${page + 1})\n`);
160
193
  progressAt = Date.now() + 5e3;
161
194
  }
162
195
  const result = await call(host, "device-host.logs.query", {
@@ -165,7 +198,8 @@ async function pullHostedMacosLogs(root, placement, host, target = MACOS, maxPag
165
198
  }, Math.max(1, Math.min(2e4, deadline - Date.now())));
166
199
  const cursor = isJsonObject(result) ? parseHostedLogsCursor(result.cursor) : null;
167
200
  if (!isJsonObject(result) || !cursor || !Array.isArray(result.records) || !result.records.every(isJsonObject) || typeof result.more !== "boolean") throw new Error(`${host.machine} answered device-host.logs.query with an unexpected result.`);
168
- commitPage(root, placement.session, before, result.records, cursor, target);
201
+ signal?.throwIfAborted();
202
+ await commitPage(root, placement.session, before, result.records, cursor, target);
169
203
  if (!result.more) return;
170
204
  const nextCheckpoint = typeof result.checkpoint === "number" ? result.checkpoint : void 0;
171
205
  if (sameCursor(cursor, before) && nextCheckpoint === checkpoint) {
@@ -176,12 +210,12 @@ async function pullHostedMacosLogs(root, placement, host, target = MACOS, maxPag
176
210
  }
177
211
  if (target.platform !== "macos") throw new Error(final ? "Final native log copy reached its time or page bound; the host keeps collected logs after stop." : "Hosted native logs still have unread pages; run stim logs again.");
178
212
  }
179
- async function pullHostedNativeLogs(root, slot, placement, host, final = false, platform = "ios") {
213
+ async function pullHostedNativeLogs(root, slot, placement, host, final = false, platform = "ios", signal) {
180
214
  if (!host.connection.supports(platform === "ios" ? "hosted-ios-data" : "hosted-android-data")) throw new Error(`${host.machine} needs a newer stim-server to collect hosted ${platform === "ios" ? "iOS" : "Android"} native logs.`);
181
215
  await pullHostedMacosLogs(root, placement, host, {
182
216
  platform,
183
217
  slot
184
- }, final ? 1024 : MAX_PAGES, final);
218
+ }, final ? 1024 : MAX_PAGES, final, signal);
185
219
  }
186
220
  //#endregion
187
- export { agentAccess as i, pullHostedNativeLogs as n, closeAgentConnection as r, pullHostedMacosLogs as t };
221
+ export { removeAgentAccess as a, agentAccess as i, pullHostedNativeLogs as n, closeAgentConnection as r, pullHostedMacosLogs as t };
@@ -1,13 +1,13 @@
1
- import { l as phaseLine } from "./command-output-BTy8hZ0F.mjs";
2
- import { x as runCancellationSignal } from "./tailnet-Dh-qiYzG.mjs";
3
- import { t as cancellableSleep } from "./cancellation-C_22VQTH.mjs";
4
- import { n as macosDir } from "./state-CG_OFoaA.mjs";
5
- import { a as attach, c as connectHost, g as upload, h as unknownSession, i as SESSION_TIMEOUT_MS, l as heldNoLonger, m as sha256, o as bundleManifest, p as settle, r as PREPARE_TIMEOUT_MS, s as call, t as INSTALL_TIMEOUT_MS, u as hostedSession } from "./hosted-client-ZEKpS9vw.mjs";
6
- import { i as agentAccess, r as closeAgentConnection, t as pullHostedMacosLogs } from "./hosted-logs-EzQM7-3Z.mjs";
7
- import { rmSync } from "node:fs";
1
+ import { a as removeAgentAccess, i as agentAccess, r as closeAgentConnection, t as pullHostedMacosLogs } from "./hosted-logs-DL_P5AdN.mjs";
2
+ import { l as phaseLine } from "./command-output-B1UrjZHL.mjs";
3
+ import { Qt as cancellableSleep } from "./settings-CsJDEXgK.mjs";
4
+ import { n as macosDir } from "./state-XMziexv2.mjs";
5
+ import { d as runCancellationSignal } from "./native-run-DY_RLx3C.mjs";
6
+ import { _ as unknownSession, a as SESSION_TIMEOUT_MS, c as call, d as hostedSession, g as stoppedSession, h as sha256, i as RESERVE_TIMEOUT_MS, l as connectHost, m as settle, o as attach, r as PREPARE_TIMEOUT_MS, s as bundleManifest, t as INSTALL_TIMEOUT_MS, u as heldNoLonger, v as upload } from "./hosted-client-DB_gBPfY.mjs";
7
+ import { randomUUID } from "node:crypto";
8
8
  import { join } from "node:path";
9
+ import { ambientContext, ambientWrite } from "@stim-cli/core";
9
10
  import { hostedMacosBundleId, parseHostedMacosDevice, validHostedAppArguments } from "@stim-cli/core/state";
10
- import { randomUUID } from "node:crypto";
11
11
  //#region src/device-host/hosted-macos.ts
12
12
  const HANDOFF_TIMEOUT_MS = 3e5;
13
13
  const agentRemoteConfig = (root) => join(macosDir(root), "agent-device-remote.json");
@@ -20,8 +20,8 @@ async function placeHostedMacos(host, { root, bundle, bundleId, handoff, argumen
20
20
  let session = null;
21
21
  if (recorded) {
22
22
  try {
23
- session = await settle(host, await attach(host, recorded.session), ["preparing"], PREPARE_TIMEOUT_MS, "macos", runCancellationSignal());
24
- session = await settle(host, session, ["stopping"], SESSION_TIMEOUT_MS, "macos", runCancellationSignal());
23
+ session = await settle(host, await attach(host, recorded.session), ["preparing"], PREPARE_TIMEOUT_MS, "macos", runCancellationSignal(ambientContext()));
24
+ session = await settle(host, session, ["stopping"], SESSION_TIMEOUT_MS, "macos", runCancellationSignal(ambientContext()));
25
25
  } catch (error) {
26
26
  if (!heldNoLonger(error)) throw error;
27
27
  }
@@ -35,7 +35,7 @@ async function placeHostedMacos(host, { root, bundle, bundleId, handoff, argumen
35
35
  slot: "default",
36
36
  attempt: randomUUID(),
37
37
  platform: "macos"
38
- }));
38
+ }, RESERVE_TIMEOUT_MS));
39
39
  }
40
40
  const appSlot = session.appSlot;
41
41
  if (appSlot === void 0) throw new Error(`${host.machine} reserved a session without a macOS app slot.`);
@@ -52,8 +52,9 @@ async function placeHostedMacos(host, { root, bundle, bundleId, handoff, argumen
52
52
  }
53
53
  };
54
54
  reserved(placement);
55
- session = await settle(host, session, ["preparing"], PREPARE_TIMEOUT_MS, "macos", runCancellationSignal());
55
+ session = await settle(host, session, ["preparing"], PREPARE_TIMEOUT_MS, "macos", runCancellationSignal(ambientContext()));
56
56
  if (session.state === "unknown") throw unknownSession(host, session);
57
+ if (session.state === "stopped") throw stoppedSession(host, session);
57
58
  if (session.state !== "ready") throw new Error(`The hosted session ${session.id} on ${host.machine} is ${session.state}.`);
58
59
  const device = parseHostedMacosDevice(session.device);
59
60
  if (!device || device.appSlot !== appSlot) throw new Error(`${host.machine} reported a ready session without its reserved macOS app slot.`);
@@ -77,7 +78,7 @@ async function placeHostedMacos(host, { root, bundle, bundleId, handoff, argumen
77
78
  note(`Delivering ${files.length} files to ${host.machine} (macOS ${device.macosVersion}, ${device.architecture})`);
78
79
  await upload(host, ids, (await call(host, "device-host.app.offer", offer)).missing, content);
79
80
  let missing = (await call(host, "device-host.app.offer", offer)).missing;
80
- if (handoff && handoff.nodeId === host.credential.nodeId && Array.isArray(missing) && missing.length) {
81
+ if (handoff && handoff.serverKey === host.credential.serverKey && Array.isArray(missing) && missing.length) {
81
82
  try {
82
83
  const taken = await call(host, "device-host.app.handoff", {
83
84
  ...ids,
@@ -99,7 +100,10 @@ async function placeHostedMacos(host, { root, bundle, bundleId, handoff, argumen
99
100
  while (delivery.state !== "installed") {
100
101
  if (delivery.state === "unknown") throw new Error(`${host.machine} could not install or launch the app${typeof delivery.notice === "string" ? `: ${delivery.notice}` : ""}.`);
101
102
  if (Date.now() > deadline) throw new Error(`${host.machine} did not finish installing the app.`);
102
- await cancellableSleep(500);
103
+ await cancellableSleep(500, {
104
+ signal: runCancellationSignal(ambientContext()),
105
+ what: "hosted app installation"
106
+ });
103
107
  delivery = await call(host, "device-host.app.attach", ids);
104
108
  }
105
109
  const appliedArguments = validHostedAppArguments(delivery.arguments) ? delivery.arguments : [];
@@ -108,7 +112,7 @@ async function placeHostedMacos(host, { root, bundle, bundleId, handoff, argumen
108
112
  return {
109
113
  placement: {
110
114
  ...placement,
111
- agent: agentAccess(agentRemoteConfig(root), host.credential, delivery.agent, note)
115
+ agent: await agentAccess(agentRemoteConfig(root), host.credential, delivery.agent, note)
112
116
  },
113
117
  launched: delivery.launched === true ? true : "unverified",
114
118
  arguments: appliedArguments
@@ -124,7 +128,7 @@ async function stopHostedMacos(root, placement) {
124
128
  session = await settle(host, session, ["stopping"], SESSION_TIMEOUT_MS);
125
129
  if (session.state !== "stopped") throw unknownSession(host, session);
126
130
  await pullHostedMacosLogs(root, placement, host).catch((error) => {
127
- process.stderr.write(`${phaseLine("device", `Could not copy the final logs from ${host.machine}: ${error.message}`)}\n`);
131
+ ambientWrite("stderr", `${phaseLine("device", `Could not copy the final logs from ${host.machine}: ${error.message}`)}\n`);
128
132
  });
129
133
  } catch (error) {
130
134
  if (!heldNoLonger(error)) throw error;
@@ -132,7 +136,7 @@ async function stopHostedMacos(root, placement) {
132
136
  } finally {
133
137
  host.connection.close();
134
138
  }
135
- rmSync(agentRemoteConfig(root), { force: true });
139
+ await removeAgentAccess(agentRemoteConfig(root));
136
140
  }
137
141
  //#endregion
138
142
  export { stopHostedMacos as n, placeHostedMacos as t };
@@ -1,14 +1,16 @@
1
- import { r as readWorkspaceState$1, x as workspaceDir } from "./workspace-state-DJt_MMvf.mjs";
2
- import { x as runCancellationSignal } from "./tailnet-Dh-qiYzG.mjs";
3
- import { t as cancellableSleep } from "./cancellation-C_22VQTH.mjs";
4
- import { a as attach, c as connectHost, f as requireHostedAppMode, g as upload, h as unknownSession, i as SESSION_TIMEOUT_MS, l as heldNoLonger, m as sha256, n as OFFER_TIMEOUT_MS, o as bundleManifest, p as settle, r as PREPARE_TIMEOUT_MS, s as call, t as INSTALL_TIMEOUT_MS, u as hostedSession } from "./hosted-client-ZEKpS9vw.mjs";
5
- import { i as agentAccess, n as pullHostedNativeLogs, r as closeAgentConnection } from "./hosted-logs-EzQM7-3Z.mjs";
6
- import { a as requireHostedMetro, i as requestHostedMetro, n as closeHostedMetro } from "./metro-gateway-CX9Xlkq2.mjs";
7
- import { s as writeHostedNative } from "./ios-state-CBLe1xYw.mjs";
8
- import { readFileSync, rmSync } from "node:fs";
1
+ import { d as workspaceDir } from "./paths-DQtTgsBf.mjs";
2
+ import { a as removeAgentAccess, i as agentAccess, n as pullHostedNativeLogs, r as closeAgentConnection } from "./hosted-logs-DL_P5AdN.mjs";
3
+ import { l as withWorkspaceStateLockAsync, r as readWorkspaceState$1 } from "./workspace-state-Cjuigeeu.mjs";
4
+ import { Qt as cancellableSleep } from "./settings-CsJDEXgK.mjs";
5
+ import { d as runCancellationSignal } from "./native-run-DY_RLx3C.mjs";
6
+ import { _ as unknownSession, a as SESSION_TIMEOUT_MS, c as call, d as hostedSession, g as stoppedSession, h as sha256, i as RESERVE_TIMEOUT_MS, l as connectHost, m as settle, n as OFFER_TIMEOUT_MS, o as attach, p as requireHostedAppMode, r as PREPARE_TIMEOUT_MS, s as bundleManifest, t as INSTALL_TIMEOUT_MS, u as heldNoLonger, v as upload } from "./hosted-client-DB_gBPfY.mjs";
7
+ import { a as requireHostedMetro, i as requestHostedMetro, n as closeHostedMetro } from "./metro-gateway-BMJNlj3E.mjs";
8
+ import { s as writeHostedNative } from "./ios-state-DvFX-K_l.mjs";
9
+ import { randomUUID } from "node:crypto";
10
+ import { readFileSync } from "node:fs";
9
11
  import { join } from "node:path";
12
+ import { ambientContext, ambientWrite } from "@stim-cli/core";
10
13
  import { hostedNativeRecords, isJsonObject, parseHostedAndroidDevice, parseHostedChoice, parseHostedDevice, parseHostedNativeOffer, parseHostedNativePlacement, requireAutomaticMachine, unreadableHostedNative } from "@stim-cli/core/state";
11
- import { randomUUID } from "node:crypto";
12
14
  //#region src/device-host/hosted-native.ts
13
15
  function hostingRefusal(machine, error) {
14
16
  return Object.assign(/* @__PURE__ */ new Error(`${machine}: ${error instanceof Error ? error.message : String(error)}`), {
@@ -26,8 +28,8 @@ async function prepareHostedNative(machine, selectors, recorded, platform = "ios
26
28
  if (recorded) {
27
29
  let session = null;
28
30
  try {
29
- session = await settle(host, await attach(host, recorded.session, void 0, platform), ["preparing"], PREPARE_TIMEOUT_MS, platform, runCancellationSignal());
30
- session = await settle(host, session, ["stopping"], SESSION_TIMEOUT_MS, platform, runCancellationSignal());
31
+ session = await settle(host, await attach(host, recorded.session, void 0, platform), ["preparing"], PREPARE_TIMEOUT_MS, platform, runCancellationSignal(ambientContext()));
32
+ session = await settle(host, session, ["stopping"], SESSION_TIMEOUT_MS, platform, runCancellationSignal(ambientContext()));
31
33
  } catch (error) {
32
34
  if (!heldNoLonger(error)) throw error;
33
35
  }
@@ -73,7 +75,7 @@ async function prepareHostedNative(machine, selectors, recorded, platform = "ios
73
75
  async function reserveHostedNative(host, target, params, platform) {
74
76
  try {
75
77
  if (target.selection?.selected === "auto") requireAutomaticMachine("device", host.machine);
76
- return hostedSession(host, await call(host, "device-host.reserve", params), platform);
78
+ return hostedSession(host, await call(host, "device-host.reserve", params, RESERVE_TIMEOUT_MS), platform);
77
79
  } catch (error) {
78
80
  if (target.selection?.selected !== "auto") throw error;
79
81
  throw Object.assign(/* @__PURE__ */ new Error(`${error instanceof Error ? error.message : String(error)} Retry stim ${platform} --remote auto; the build already targets ${host.machine}'s architecture.`), { remedy: `Retry stim ${platform} --remote auto.` });
@@ -111,7 +113,8 @@ async function placeHostedNative(target, { root, slot, bundle, handoff, bundleId
111
113
  }
112
114
  };
113
115
  reserved(placement);
114
- session = await settle(host, session, ["preparing"], PREPARE_TIMEOUT_MS, platform, runCancellationSignal());
116
+ session = await settle(host, session, ["preparing"], PREPARE_TIMEOUT_MS, platform, runCancellationSignal(ambientContext()));
117
+ if (session.state === "stopped") throw stoppedSession(host, session);
115
118
  if (session.state !== "ready") throw unknownSession(host, session);
116
119
  const device = platform === "ios" ? parseHostedDevice(session.device) : parseHostedAndroidDevice(session.device);
117
120
  if (!device || device.architecture !== target.choice.architecture || "avdName" in device && "systemImage" in target.choice && (device.systemImage !== target.choice.systemImage || device.deviceProfile !== target.choice.deviceProfile)) throw new Error(platform === "ios" ? "The reserved simulator does not match the offered architecture." : "The reserved emulator does not match the offered architecture or selectors.");
@@ -164,7 +167,7 @@ async function placeHostedNative(target, { root, slot, bundle, handoff, bundleId
164
167
  note(`Delivering ${files.length} files to ${host.machine} (${"runtime" in device ? device.runtime : device.systemImage}, ${device.architecture})`);
165
168
  await upload(host, ids, (await call(host, "device-host.app.offer", offer)).missing, content);
166
169
  let missing = (await call(host, "device-host.app.offer", offer)).missing;
167
- if (handoff && handoff.nodeId === host.credential.nodeId && Array.isArray(missing) && missing.length) {
170
+ if (handoff && handoff.serverKey === host.credential.serverKey && Array.isArray(missing) && missing.length) {
168
171
  if (!host.connection.supports(platform === "android" ? "hosted-android-data" : "hosted-ios-data")) note(`${host.machine} needs a newer stim-server for ${platform === "ios" ? "iOS" : "Android"} build handoff; uploading the app instead`);
169
172
  else try {
170
173
  const taken = await call(host, "device-host.app.handoff", {
@@ -184,7 +187,10 @@ async function placeHostedNative(target, { root, slot, bundle, handoff, bundleId
184
187
  break;
185
188
  } catch (offerError) {
186
189
  if (!String(offerError).includes("native operation in progress") || Date.now() >= fallbackDeadline) throw offerError;
187
- await cancellableSleep(Math.min(500, Math.max(0, fallbackDeadline - Date.now())));
190
+ await cancellableSleep(Math.min(500, Math.max(0, fallbackDeadline - Date.now())), {
191
+ signal: runCancellationSignal(ambientContext()),
192
+ what: "hosted app offer"
193
+ });
188
194
  }
189
195
  }
190
196
  }
@@ -196,13 +202,16 @@ async function placeHostedNative(target, { root, slot, bundle, handoff, bundleId
196
202
  while (delivery.state !== "installed") {
197
203
  if (delivery.state === "unknown") throw new Error(typeof delivery.notice === "string" ? delivery.notice : "App installation or launch is unknown.");
198
204
  if (Date.now() > deadline) throw new Error("App installation did not finish.");
199
- await cancellableSleep(500);
205
+ await cancellableSleep(500, {
206
+ signal: runCancellationSignal(ambientContext()),
207
+ what: "hosted app installation"
208
+ });
200
209
  delivery = await call(host, "device-host.app.attach", ids);
201
210
  }
202
211
  if (typeof delivery.notice === "string") note(delivery.notice);
203
212
  placement = {
204
213
  ...placement,
205
- agent: 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)
214
+ 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)
206
215
  };
207
216
  return {
208
217
  placement,
@@ -230,14 +239,14 @@ async function stopHostedNative(root, slot, platform = "ios") {
230
239
  try {
231
240
  await pullHostedNativeLogs(root, name, placement, host, true, platform);
232
241
  } catch (error) {
233
- process.stderr.write(`Could not copy final native logs from ${placement.machine}: ${error.message}\n`);
242
+ ambientWrite("stderr", `Could not copy final native logs from ${placement.machine}: ${error.message}\n`);
234
243
  }
235
244
  const stopped = await settle(host, hostedSession(host, await call(host, "device-host.stop", { session: placement.session }), platform), ["stopping"], SESSION_TIMEOUT_MS, platform);
236
245
  if (stopped.state !== "stopped") throw unknownSession(host, stopped);
237
246
  try {
238
247
  await pullHostedNativeLogs(root, name, placement, host, true, platform);
239
248
  } catch (error) {
240
- process.stderr.write(`Could not copy the host's final native logs from ${placement.machine}: ${error.message}\n`);
249
+ ambientWrite("stderr", `Could not copy the host's final native logs from ${placement.machine}: ${error.message}\n`);
241
250
  }
242
251
  } catch (error) {
243
252
  const code = error instanceof Error && "code" in error ? error.code : void 0;
@@ -252,8 +261,8 @@ async function stopHostedNative(root, slot, platform = "ios") {
252
261
  }
253
262
  try {
254
263
  await closeHostedMetro(root, placement.session);
255
- rmSync(platform === "ios" ? iosAgentRemoteConfig(root, name) : androidAgentRemoteConfig(root, name), { force: true });
256
- writeHostedNative(root, name, null, platform);
264
+ await removeAgentAccess(platform === "ios" ? iosAgentRemoteConfig(root, name) : androidAgentRemoteConfig(root, name));
265
+ await withWorkspaceStateLockAsync(root, () => writeHostedNative(root, name, null, platform));
257
266
  } catch (error) {
258
267
  failures.push(`${placement.machine}: ${error.message}. The placement is kept; rerun stim stop.`);
259
268
  }
@@ -0,0 +1,24 @@
1
+ import "./settings-DkdmlelE.mjs";
2
+ import "./exec-H20xSg2T.mjs";
3
+ import { DeviceActivity, IOS_SIMULATOR_APPS } from "@stim-cli/core/state";
4
+ //#region src/commands/gc/idle.d.ts
5
+ interface IdleDevice {
6
+ kind: "ios" | "android";
7
+ id: string;
8
+ name: string;
9
+ project: string;
10
+ slot: string;
11
+ lastActivityAt: string | null;
12
+ idleForMs: number | null;
13
+ buildInProgress: boolean;
14
+ }
15
+ interface OwnedDeviceActivity {
16
+ kind: "ios" | "android";
17
+ id: string;
18
+ name: string;
19
+ project: string;
20
+ slot: string;
21
+ activity: DeviceActivity;
22
+ }
23
+ //#endregion
24
+ export { OwnedDeviceActivity as n, IdleDevice as t };
@@ -1,10 +1,11 @@
1
- import { B as deviceSlotKey, _ as loadConfig$1 } from "./config-BYDxlMY3.mjs";
2
- import { a as updateWorkspaceState, r as readWorkspaceState$1 } from "./workspace-state-DJt_MMvf.mjs";
3
- import { o as formatLongDuration } from "./command-output-BTy8hZ0F.mjs";
4
- import { T as canonicalPath, b as teardownOwnedIosSim, v as teardownOwnedAvd } from "./workspaces-C36s2jb9.mjs";
5
- import { f as collectOwnedDeviceActivity, g as workspaceBuildInProgress } from "./budget-f7516Zk0.mjs";
6
- import { m as workspaceDeviceLastUseAt } from "./activity-BSV745Kz.mjs";
7
- import { d as listAllIosSims } from "./ios-RkW4pnQf.mjs";
1
+ import { H as deviceSlotKey, _ as loadConfig$1 } from "./config-CYcBF478.mjs";
2
+ import { o as formatLongDuration } from "./command-output-B1UrjZHL.mjs";
3
+ import { o as updateWorkspaceState, r as readWorkspaceState$1 } from "./workspace-state-Cjuigeeu.mjs";
4
+ import { A as workspaceBuildInProgress, T as collectOwnedDeviceActivity } from "./budget-Bs6SxxzO.mjs";
5
+ import { t as canonicalPath } from "./paths-BsJMoPP2.mjs";
6
+ import { E as teardownOwnedAvd, O as teardownOwnedIosSim } from "./workspaces-BccMFmGQ.mjs";
7
+ import { _ as workspaceDeviceLastUseAt } from "./activity-BzVfHSrY.mjs";
8
+ import { d as listAllIosSims } from "./ios-CpByZy12.mjs";
8
9
  import { DEVICE_IDLE_SHUTDOWN_KEY, DEVICE_RECLAIM_REASON, readDeviceIdleShutdowns } from "@stim-cli/core/state";
9
10
  //#region src/devices/idle-shutdown.ts
10
11
  const SIM_LIST_TIMEOUT_MS = 1e4;
@@ -62,15 +63,15 @@ function dueIdleDevices(root, idleMs, now = Date.now()) {
62
63
  * and records why. The caller holds the workspace's native-run lock, so no `ios` or `android` run is booting or
63
64
  * installing on them.
64
65
  */
65
- function shutDownIdleDevices(root, idleMs, log, now = Date.now(), { only, reason } = {}) {
66
+ async function shutDownIdleDevices(root, idleMs, log, now = Date.now(), { only, reason } = {}) {
66
67
  let shutDown = 0;
67
68
  for (const { device, idleForMs } of dueIdleDevices(root, idleMs, now)) {
68
69
  if (only && (device.kind !== only.kind || device.id !== only.id || device.project !== only.project || device.slot !== only.slot)) continue;
69
70
  const what = `${device.kind === "ios" ? "simulator" : "emulator"} ${device.name}`;
70
- const outcome = device.kind === "ios" ? teardownOwnedIosSim(device.id, {
71
+ const outcome = device.kind === "ios" ? await teardownOwnedIosSim(device.id, {
71
72
  label: device.name,
72
73
  workspace: root
73
- }) : teardownOwnedAvd(device.id, {
74
+ }) : await teardownOwnedAvd(device.id, {
74
75
  owner: {
75
76
  projectPath: device.project,
76
77
  slot: device.slot