stim 1.20.2 → 1.22.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 (231) hide show
  1. package/README.md +141 -8
  2. package/dist/acquire-artifact-Co2k4f7O.mjs +171 -0
  3. package/dist/act-DsuqEkiM.mjs +2801 -0
  4. package/dist/activity-CMLmzp7o.mjs +1397 -0
  5. package/dist/{agent-device-usage-output-Dzgv6lgR.mjs → agent-device-usage-output-DUFaTMdd.mjs} +23 -67
  6. package/dist/{android-DCKZrNvV.mjs → android-ByRMUoiJ.mjs} +157 -93
  7. package/dist/{android-Mz_Inn3w.mjs → android-C7sVmyI9.mjs} +299 -116
  8. package/dist/{android-Dlzxshgp.mjs → android-DGkNqa_c.mjs} +5 -5
  9. package/dist/{android-HqP1_vB_.mjs → android-DR5lZ2Az.mjs} +2 -2
  10. package/dist/android-cas-compiler.mjs +2 -2
  11. package/dist/{android-cas-Bl-cLJBk.mjs → android-cas-ngferVq2.mjs} +7 -6
  12. package/dist/android-project-BCAAmMwz.mjs +12 -0
  13. package/dist/android-project-DVWyCbWU.d.mts +391 -0
  14. package/dist/api.d.mts +810 -64
  15. package/dist/api.mjs +782 -1
  16. package/dist/{artifact-HSwkIWEQ.mjs → artifact-8EY8SAPw.mjs} +240 -213
  17. package/dist/{artifact-D3czlUvT.mjs → artifact-DJPy518Y.mjs} +203 -164
  18. package/dist/artifact-platform-CXcmX_j3.mjs +27 -0
  19. package/dist/{browser-web-CA-F1GQo.mjs → browser-web-Bcv99PBM.mjs} +194 -64
  20. package/dist/budget-Cvwn54qV.mjs +9285 -0
  21. package/dist/{build-DYDbfkk5.mjs → build-CabC3G23.mjs} +153 -98
  22. package/dist/build-cache-Dv7RN200.mjs +602 -0
  23. package/dist/build-plan-DUC8Ka9y.mjs +248 -0
  24. package/dist/{cache-manifest-XH-fvDUj.mjs → cache-manifest-DSeQjt-Q.mjs} +2 -2
  25. package/dist/cache-manifest.mjs +1 -1
  26. package/dist/{ccache-zdKiYQ3E.mjs → ccache-Btwc36-I.mjs} +10 -9
  27. package/dist/{cdp-Dq_ypAlJ.mjs → cdp-3fwICjzi.mjs} +18 -5
  28. package/dist/{chrome-BnzZMqsR.mjs → chrome-Bbw689-6.mjs} +7 -4
  29. package/dist/{cli-CLP3diBm.mjs → cli-CHZa9Lvk.mjs} +20 -20
  30. package/dist/cli.mjs +1 -1
  31. package/dist/client-OyRoEuSr.mjs +1577 -0
  32. package/dist/collector-run.d.mts +4 -40
  33. package/dist/collector-run.mjs +9 -8
  34. package/dist/{command-output-BTy8hZ0F.mjs → command-output-gwpm28fu.mjs} +1 -1
  35. package/dist/config-DlrsduwG.mjs +955 -0
  36. package/dist/{created-devices-Dzd5C2LF.mjs → created-devices-x9rN6bhs.mjs} +14 -4
  37. package/dist/{dependency-state-DG0p31-y.mjs → dependency-state-1I9KZT1Y.mjs} +20 -5
  38. package/dist/{detached-entry-B9C_zeQC.mjs → detached-entry-BwNEtIrP.mjs} +1 -1
  39. package/dist/{dev-client-DAWVIGBu.mjs → dev-client-BSDR5h3F.mjs} +2 -2
  40. package/dist/{device-BECOiufl.mjs → device-B-L3Fbxy.mjs} +91 -34
  41. package/dist/device-DklWZLqb.mjs +1068 -0
  42. package/dist/{device-capacity-CBUAcRyQ.mjs → device-capacity-DK5_EwRS.mjs} +42 -13
  43. package/dist/device-host-worker.mjs +76 -79
  44. package/dist/{device-ios-COSi6oS1.mjs → device-ios-DoB6KFCO.mjs} +77 -51
  45. package/dist/{device-lease-run-Cw2M4z51.mjs → device-lease-run-xJPQ_QQG.mjs} +14 -26
  46. package/dist/{device-pool-Bf-pOhSw.mjs → device-pool-BMkdsefq.mjs} +5 -5
  47. package/dist/{doctor-kfMBnUw6.mjs → doctor-Bo7BXLux.mjs} +271 -320
  48. package/dist/{doctor-CB8oZKCk.mjs → doctor-D5_EzwfA.mjs} +186 -91
  49. package/dist/doctor-fingerprint-DZLoYFVb.mjs +16 -0
  50. package/dist/doctor-fingerprint.mjs +19 -0
  51. package/dist/doctor-thjWAV0z.mjs +2 -0
  52. package/dist/{doctor-watchman-BRyV2adA.mjs → doctor-watchman-N2wDotwQ.mjs} +40 -21
  53. package/dist/{eas-build-JyfqyHfC.mjs → eas-build-CwknaQYj.mjs} +53 -20
  54. package/dist/{build-plan-B7SZsc2N.mjs → eas-fallback-CSgAulr5.mjs} +41 -214
  55. package/dist/{watchman-CPQFZLXY.mjs → eas-session-ledger-DlY_serQ.mjs} +230 -11
  56. package/dist/{error-diagnostics-BR677IDM.mjs → error-diagnostics-DBWO2rxE.mjs} +172 -82
  57. package/dist/exec-DEoAhcze.mjs +858 -0
  58. package/dist/exec-DnYc4HGH.d.mts +78 -0
  59. package/dist/{gc-qWOMdnER.mjs → gc-CXxN-4SE.mjs} +1091 -1029
  60. package/dist/gc-DnNDQyaP.mjs +3 -0
  61. package/dist/{guide-G9cAbVAc.mjs → guide-Br0LEfKy.mjs} +1165 -297
  62. package/dist/{hosted-android-C3MHGld1.mjs → hosted-android-B-9f-jkd.mjs} +10 -8
  63. package/dist/{hosted-client-ZEKpS9vw.mjs → hosted-client-DZAPrcro.mjs} +34 -19
  64. package/dist/{hosted-ios-l-o3iM9J.mjs → hosted-ios-CsCS21hn.mjs} +1 -1
  65. package/dist/{hosted-logs-EzQM7-3Z.mjs → hosted-logs-FIRx9X2f.mjs} +77 -43
  66. package/dist/{hosted-macos-BACVAVqE.mjs → hosted-macos-_awYSpfv.mjs} +35 -22
  67. package/dist/{hosted-native-A4s1QPXe.mjs → hosted-native-BdzE668-.mjs} +49 -27
  68. package/dist/idle-C4cZ757K.d.mts +24 -0
  69. package/dist/{idle-shutdown-DtxhPTKe.mjs → idle-shutdown-DDg3SdvY.mjs} +11 -10
  70. package/dist/ios-CDvlmUs7.d.mts +24 -0
  71. package/dist/{ios-BwVCF7oA.mjs → ios-Ds1clbIt.mjs} +8 -8
  72. package/dist/{ios-device-D7pi2Sca.mjs → ios-device--h_-8ENa.mjs} +51 -26
  73. package/dist/{ios-device-B5veSDDQ.mjs → ios-device-BXZ0c7Kb.mjs} +1 -1
  74. package/dist/{ios-RkW4pnQf.mjs → ios-hcBpnEGk.mjs} +200 -93
  75. package/dist/{ios-Ce7bc7br.mjs → ios-pm4nL6By.mjs} +160 -108
  76. package/dist/{ios-project-K9hY_Jab.mjs → ios-project-CZhKODF3.mjs} +13 -13
  77. package/dist/ios-signing-BtAffm0A.mjs +489 -0
  78. package/dist/{ios-state-CBLe1xYw.mjs → ios-state-Dtw5K2aI.mjs} +1 -1
  79. package/dist/{ios-nOLJGVdy.mjs → ios-xtlRHaeB.mjs} +1 -1
  80. package/dist/{js-swap-Bb3nsS07.mjs → js-swap-Bwhh4S7V.mjs} +30 -25
  81. package/dist/{launch-C3qJ6Agb.mjs → launch-BjxemNpK.mjs} +85 -543
  82. package/dist/{launch-F4sOqAhB.mjs → launch-CCzXlc8r.mjs} +91 -90
  83. package/dist/logs-BfwwadsZ.mjs +479 -0
  84. package/dist/logs-jTS_lHSo.mjs +248 -0
  85. package/dist/{macos-D7uDvO-z.mjs → macos-DVlSSyUg.mjs} +1 -1
  86. package/dist/macos-NfV2D7dO.mjs +694 -0
  87. package/dist/macos-run.mjs +1 -1
  88. package/dist/maintenance-run.mjs +123 -248
  89. package/dist/{metro-DnCEjCxs.mjs → metro-DiXtA3Ty.mjs} +107 -30
  90. package/dist/metro-gateway-CKZqKqx7.mjs +209 -0
  91. package/dist/{named-ports-4WopRPg-.mjs → named-ports-DkEzOfzO.mjs} +42 -90
  92. package/dist/native-android-CqgnOgsT.mjs +645 -0
  93. package/dist/native-gradle-artifact-inputs-DHFnmrVf.mjs +270 -0
  94. package/dist/native-gradle-inputs-Dnh84Zbd.mjs +211 -0
  95. package/dist/native-inputs-TRTTTRxt.mjs +91 -0
  96. package/dist/native-run-NwUuxbkg.mjs +276 -0
  97. package/dist/{native-runtime-k8dTIrbK.mjs → native-runtime-B7KIafpM.mjs} +11 -9
  98. package/dist/native-source-Y0yLhFyQ.mjs +133 -0
  99. package/dist/{native-xcode-inputs-CykopJdM.mjs → native-xcode-inputs-D_VOfFL7.mjs} +26 -18
  100. package/dist/{native-xcode-ios-Bbf30Gm0.mjs → native-xcode-ios-BYDOHWLN.mjs} +104 -60
  101. package/dist/{ndjson-GOnbyvso.mjs → ndjson-CgE_o3Dl.mjs} +3 -3
  102. package/dist/offload-worker.d.mts +59 -41
  103. package/dist/offload-worker.mjs +478 -115
  104. package/dist/operation-exec-BW0em61G.mjs +171 -0
  105. package/dist/{ownership-DCtw_2IA.mjs → ownership-C5fo5Edb.mjs} +3 -3
  106. package/dist/{page-DRHy329N.mjs → page-DBL2PiUU.mjs} +12 -5
  107. package/dist/paths-BsJMoPP2.mjs +12 -0
  108. package/dist/paths-DQtTgsBf.mjs +42 -0
  109. package/dist/{placement-log-MOfPVgaM.mjs → placement-log-BnGgJRRZ.mjs} +5 -5
  110. package/dist/{plan-placement-V0TdzYXp.mjs → plan-placement-DIsMtN-d.mjs} +15 -11
  111. package/dist/{ports-DdSUicVo.mjs → ports-Cy1RkmuQ.mjs} +3 -3
  112. package/dist/prebuild-D4JoEAEs.mjs +484 -0
  113. package/dist/{project-_phNIJfs.mjs → project-wM_XFTvE.mjs} +65 -37
  114. package/dist/{pull-request-CQ8sPbZa.mjs → pull-request-W1IuLXDt.mjs} +102 -20
  115. package/dist/pull-request-eODyIqZF.d.mts +124 -0
  116. package/dist/pull-requests.d.mts +2 -120
  117. package/dist/pull-requests.mjs +2 -2
  118. package/dist/{react-native-android-CM1XRDwv.mjs → react-native-android-6OZ7MSM7.mjs} +121 -72
  119. package/dist/{gradle-DSrXb6lG.mjs → react-native-build-Dvd8hC21.mjs} +292 -15
  120. package/dist/react-native-doctor-DsoSF1Y9.mjs +234 -0
  121. package/dist/{react-native-ios-Dhq_eiYQ.mjs → react-native-ios-4u1Rn-GR.mjs} +71 -39
  122. package/dist/{recordings-D0d5aQt0.mjs → recordings-CQmfzAh7.mjs} +12 -9
  123. package/dist/registration-BcRgF6Ox.mjs +51 -0
  124. package/dist/{reload-C8JhxjSh.mjs → reload-BzkYabB-.mjs} +94 -47
  125. package/dist/reload-D7AVlb8N.mjs +2 -0
  126. package/dist/{remote-BiR6aYjy.mjs → remote-DjGDmlTZ.mjs} +26 -18
  127. package/dist/result-D2Ykz_iD.mjs +435 -0
  128. package/dist/{run-BtPgpKks.mjs → run-DidHghD1.mjs} +5 -5
  129. package/dist/selection-DSMbWEX2.mjs +620 -0
  130. package/dist/{server-bare-R2m_gsH8.mjs → server-bare-CafzuyFB.mjs} +3 -4
  131. package/dist/{server-command-CTtCkmcQ.mjs → server-command-C6QjeupS.mjs} +14 -7
  132. package/dist/{server-expo-DsguMGUF.mjs → server-expo-CrjB29pq.mjs} +54 -11
  133. package/dist/server-expo-D58c9L0M.mjs +2 -0
  134. package/dist/{settings-B717odTD.mjs → settings-C7O8A6ZI.mjs} +309 -119
  135. package/dist/settings-DpR-1Wnn.mjs +114 -0
  136. package/dist/settings-write-CzD60rOD.mjs +360 -0
  137. package/dist/settings.schema.json +176 -16
  138. package/dist/{simslim-CCr3rE-A.mjs → simslim-BNckWpXi.mjs} +14 -12
  139. package/dist/{slot-launch-DPt0m9Nd.mjs → slot-launch-CbfmNgM6.mjs} +15 -20
  140. package/dist/{spawn-entry-Dcb7sDUz.mjs → spawn-entry-jw19m8Nl.mjs} +0 -1
  141. package/dist/stage-Bps5oius.mjs +200 -0
  142. package/dist/{start-TXeMmmO2.mjs → start-Br-oxqMD.mjs} +1 -1
  143. package/dist/start-EojN4WFz.mjs +1364 -0
  144. package/dist/{state-D1Icyjlu.mjs → state--smjFSTL.mjs} +55 -13
  145. package/dist/{state-CG_OFoaA.mjs → state-08I3hw3_.mjs} +3 -2
  146. package/dist/{stats-BoY5sEPU.mjs → stats-D3GerlFW.mjs} +7 -13
  147. package/dist/stats-report-BzaPrRm6.mjs +16 -0
  148. package/dist/{status-BTA7kKiQ.mjs → status-BRROykC8.mjs} +7 -5
  149. package/dist/status-EvrK72L2.mjs +2 -0
  150. package/dist/{status-WgFbkRzj.mjs → status-WBDajew2.mjs} +554 -452
  151. package/dist/{status-watch-C23ILtf9.mjs → status-watch-Bg_u-pwR.mjs} +222 -25
  152. package/dist/status-worker.d.mts +1 -0
  153. package/dist/status-worker.mjs +19 -0
  154. package/dist/stim-desktop-D5OGpYZM.mjs +112 -0
  155. package/dist/{stop-DR6VoDIk.mjs → stop-DTZ9uTw3.mjs} +2 -2
  156. package/dist/{stop-DPEhunlw.mjs → stop-Dky10AEJ.mjs} +33 -23
  157. package/dist/{stop-BNmBMi1F.mjs → stop-Mv6W9FAk.mjs} +137 -98
  158. package/dist/supervisor-run.d.mts +8 -17
  159. package/dist/supervisor-run.mjs +56 -28
  160. package/dist/{support-CwjbFs9H.mjs → support-CNioewsd.mjs} +14 -14
  161. package/dist/{support-DMTqj3Hm.mjs → support-yUiOjnVY.mjs} +20 -13
  162. package/dist/swiftpm-artifact-inputs-DFcGsXDP.mjs +153 -0
  163. package/dist/swiftpm-macos-BIak-Rxv.mjs +532 -0
  164. package/dist/toolchain-BY8X8HXx.mjs +2 -0
  165. package/dist/toolchain-DpJMHq0n.mjs +876 -0
  166. package/dist/{trigger-CigAYjeL.mjs → trigger-Cbyc41d_.mjs} +21 -6
  167. package/dist/trigger-yb_nFzPF.mjs +2 -0
  168. package/dist/{warm-progress-BuY-Fvfn.mjs → warm-progress-CA3TgT3-.mjs} +263 -156
  169. package/dist/web-CZ07gd77.mjs +2 -0
  170. package/dist/{web-BDBMxDpx.mjs → web-DKx6yoFA.mjs} +20 -14
  171. package/dist/web-run.mjs +13 -12
  172. package/dist/windows-report.d.mts +1 -0
  173. package/dist/windows-report.mjs +18 -0
  174. package/dist/windows-report.ps1 +43 -0
  175. package/dist/{workspace-state-DJt_MMvf.mjs → workspace-state-U08dEzhG.mjs} +62 -58
  176. package/dist/{workspaces-C36s2jb9.mjs → workspaces-CpANmBlq.mjs} +1723 -1286
  177. package/dist/{worktree-CgvJYJ-Q.mjs → worktree-CHecZ4Oq.mjs} +1097 -547
  178. package/dist/worktree-ZqZVjD9K.mjs +2 -0
  179. package/dist/xcode-BXn9-xYx.mjs +896 -0
  180. package/package.json +7 -5
  181. package/shim/android-audio/Main.java +24 -0
  182. package/shim/android-audio/README.md +17 -0
  183. package/shim/android-audio.jar +0 -0
  184. package/shim/android-optimizations.gradle +7 -5
  185. package/dist/activity-BSV745Kz.mjs +0 -629
  186. package/dist/api-qvOzWyfO.mjs +0 -154
  187. package/dist/api-run.mjs +0 -294
  188. package/dist/app-install-BIKtMfPq.mjs +0 -896
  189. package/dist/artifact-lifecycle-DzPpBYvK.mjs +0 -30
  190. package/dist/budget-f7516Zk0.mjs +0 -4106
  191. package/dist/build-facts-DcLv2HSd.d.mts +0 -111
  192. package/dist/cancellation-C_22VQTH.mjs +0 -24
  193. package/dist/client-qQAaXSUs.mjs +0 -1771
  194. package/dist/config-BYDxlMY3.mjs +0 -562
  195. package/dist/device-DuiPJQSn.mjs +0 -290
  196. package/dist/device-lease-BX1sJQ2E.mjs +0 -1224
  197. package/dist/device-remote-xRT0Vmo9.mjs +0 -1272
  198. package/dist/eas-session-ledger-Cm0Gwq2F.mjs +0 -207
  199. package/dist/exec-DqKo9GKJ.mjs +0 -268
  200. package/dist/git-DDfFzkMm.mjs +0 -45
  201. package/dist/launch-verify-B6Q8hZP-.mjs +0 -428
  202. package/dist/logs-BGrEUaEs.mjs +0 -383
  203. package/dist/macos-Ch5VJmcS.mjs +0 -584
  204. package/dist/metro-gateway-CX9Xlkq2.mjs +0 -103
  205. package/dist/miss-reason-mKzZ4VvN.mjs +0 -238
  206. package/dist/native-android-D5qc3-_v.mjs +0 -226
  207. package/dist/native-gradle-inputs-BsLRrl7u.mjs +0 -349
  208. package/dist/ndjson-Du3j0AQ9.d.mts +0 -17
  209. package/dist/plan-C4TjdPsR.mjs +0 -260
  210. package/dist/prebuild-DYbucPXy.mjs +0 -212
  211. package/dist/preview-78a9kR_-.mjs +0 -1011
  212. package/dist/react-native-build-CtCccJtb.mjs +0 -239
  213. package/dist/react-native-doctor-CfRnFfjs.mjs +0 -78
  214. package/dist/remote-cache-OyOyVIS3.mjs +0 -526
  215. package/dist/result-CaJg5Iat.mjs +0 -186
  216. package/dist/server-expo-Bigriskl.mjs +0 -2
  217. package/dist/settings-BORNaaCO.mjs +0 -402
  218. package/dist/settings-vk3mJGSm.d.mts +0 -13
  219. package/dist/spawn-claims-DVS5RkMS.mjs +0 -72
  220. package/dist/stage-KNTQopqv.mjs +0 -157
  221. package/dist/start-BWqzODpb.mjs +0 -1116
  222. package/dist/stim-desktop-BzBN2Em2.mjs +0 -63
  223. package/dist/swiftpm-macos-DRNVJWlb.mjs +0 -119
  224. package/dist/tailnet-Dh-qiYzG.mjs +0 -265
  225. package/dist/toolchain-BFkAcstd.mjs +0 -2
  226. package/dist/toolchain-DxSsNssu.mjs +0 -1657
  227. package/dist/trigger-9_USgmxz.mjs +0 -2
  228. package/dist/web-Dq-i3wzJ.mjs +0 -2
  229. package/dist/worktree-DpLdCHga.mjs +0 -2
  230. package/dist/{api-run.d.mts → doctor-fingerprint.d.mts} +0 -0
  231. package/dist/{errors-DzkMYh8Q.mjs → errors-ccaPc4ox.mjs} +2 -2
@@ -1,1116 +0,0 @@
1
- import { E as upsertProject, k as process_identity_exports, p as getProject } from "./config-BYDxlMY3.mjs";
2
- import { t as getExecutor } from "./exec-DqKo9GKJ.mjs";
3
- import { t as spawnEntry } from "./spawn-entry-Dcb7sDUz.mjs";
4
- import { A as metroIdleStopMinutesSetting, B as remoteAndroidSetting, G as resolveIosProjectDir, H as remoteIosSetting, K as resolveProjectSettings, L as publicUrlSetting, M as metroTunnelSettingError, O as metroCommandSetting, P as ngrokUrlSetting, Q as settingShapeErrors, U as resolveAndroidLayout, W as resolveCacheProviderConfig, a as SETTING_SHAPE_REMEDY, at as PUBLIC_METRO_ENV, h as deviceIdleShutdownMinutesSetting, j as metroPortSetting, k as metroCommandSettingError, m as defaultAndroidLayout, nt as unknownSettingKeys, ot as detectProviders, p as cacheProviderSettingError, st as planMetroReach, tt as tunnelModeSetting } from "./settings-B717odTD.mjs";
5
- import { _ as supervisorLogFile, c as writeWorkspaceState, i as recordWorkspaceUse, n as clearWorkspaceStateKeys, p as ensureWorkspaceStorage, r as readWorkspaceState$1, w as workspaceLogsDir$1, y as workspaceAgentDeviceDir } from "./workspace-state-DJt_MMvf.mjs";
6
- import { _ as stepTimer, l as phaseLine } from "./command-output-BTy8hZ0F.mjs";
7
- import { C as MODE_BARE, D as clearManagedMetroTunnel, T as MODE_EXPO, _ as detectIsExpo, b as readAppConfigText, j as readMetroTunnel, r as findProjectRoot, s as projectProblem, t as NO_PROJECT_REFUSAL, w as MODE_COMMAND, x as readAppJson } from "./project-_phNIJfs.mjs";
8
- import { a as workspaceLinks, i as workspaceLinkLine } from "./stim-desktop-BzBN2Em2.mjs";
9
- import { d as signalProcessTree, g as PortInspectionError, h as PORT_INSPECTION_REMEDY, m as reserveMetroPort, s as pidExists, u as resolveProjectMetro } from "./metro-DnCEjCxs.mjs";
10
- import { D as stopTunnel, E as startTunnelSequence, O as terminateChild, P as resolveSupervisorTarget, j as withManagedTunnelLock, k as withManagedRemoteWorktreeLock } from "./workspaces-C36s2jb9.mjs";
11
- import { ct as stopOwnedMetroForReset, t as budgetGate } from "./budget-f7516Zk0.mjs";
12
- import { a as withWorkspaceProcessLock, o as workspaceProcessLockError } from "./named-ports-4WopRPg-.mjs";
13
- import { t as cancellableSleep } from "./cancellation-C_22VQTH.mjs";
14
- import { i as levelRank } from "./ndjson-GOnbyvso.mjs";
15
- import { d as readProjectConfig } from "./remote-cache-OyOyVIS3.mjs";
16
- import { n as windowsLauncherArgs } from "./detached-entry-B9C_zeQC.mjs";
17
- import { appendFileSync, mkdirSync, openSync, readFileSync } from "node:fs";
18
- import { dirname } from "node:path";
19
- import { join as join$1 } from "path";
20
- import { IDLE_STOP_KEY, queryLogs } from "@stim-cli/core/state";
21
- import { CACHE_PROVIDER_ENV, cacheProviderEnv } from "@stim-cli/cache";
22
- import { existsSync as existsSync$1, readFileSync as readFileSync$1, readdirSync as readdirSync$1 } from "fs";
23
- import chalk from "chalk";
24
- import { EventEmitter } from "node:events";
25
- //#region src/workspace/app-id.ts
26
- const ANDROID_PACKAGE_NAME = /^[A-Za-z0-9_]+(?:\.[A-Za-z0-9_]+)+$/;
27
- function androidPackageFilter(problems) {
28
- return (id, source) => {
29
- if (id === null || ANDROID_PACKAGE_NAME.test(id)) return id;
30
- problems.push(`Invalid Android package ${JSON.stringify(id)} in ${source}. Expected dot-separated letters, digits and underscores, such as com.example.app.`);
31
- return null;
32
- };
33
- }
34
- function configString(config, platform, key) {
35
- if (!config || typeof config !== "object") return null;
36
- const expoField = config.expo;
37
- const section = (expoField && typeof expoField === "object" ? expoField : config)[platform];
38
- if (!section || typeof section !== "object") return null;
39
- const value = section[key];
40
- return typeof value === "string" && value ? value : null;
41
- }
42
- function literalString(text, key) {
43
- return text?.match(new RegExp(`${key}\\s*:\\s*["']([^"']+)["']`))?.[1] ?? null;
44
- }
45
- function idsFromConfig(projectRoot) {
46
- const read = readProjectConfig(projectRoot);
47
- if (!read.unavailable) return {
48
- bundleId: configString(read.config, "ios", "bundleIdentifier"),
49
- androidPackage: configString(read.config, "android", "package")
50
- };
51
- const appJson = readAppJson(projectRoot);
52
- const text = readAppConfigText(projectRoot);
53
- return {
54
- bundleId: configString(appJson, "ios", "bundleIdentifier") ?? literalString(text, "bundleIdentifier"),
55
- androidPackage: configString(appJson, "android", "package") ?? literalString(text, "package")
56
- };
57
- }
58
- function detectAppIds(projectRoot, iosDir = join$1(projectRoot, "ios"), androidModuleDir = defaultAndroidLayout(projectRoot).moduleDir) {
59
- const ids = idsFromConfig(projectRoot);
60
- const problems = [];
61
- const accept = androidPackageFilter(problems);
62
- return {
63
- bundleId: ids.bundleId ?? detectBundleIdFromPbxproj(iosDir),
64
- androidPackage: accept(ids.androidPackage, "the Expo config android.package") ?? detectAndroidPackageFromGradle(androidModuleDir, accept),
65
- androidPackageProblem: problems.length > 0 ? problems.join(" ") : null
66
- };
67
- }
68
- function detectBundleId(projectRoot, iosDir = join$1(projectRoot, "ios")) {
69
- return detectAppIds(projectRoot, iosDir).bundleId;
70
- }
71
- function detectAndroidPackage(projectRoot, androidModuleDir = defaultAndroidLayout(projectRoot).moduleDir) {
72
- return detectAppIds(projectRoot, void 0, androidModuleDir).androidPackage;
73
- }
74
- function detectBundleIdFromPbxproj(iosDir) {
75
- if (!existsSync$1(iosDir)) return null;
76
- let entries;
77
- try {
78
- entries = readdirSync$1(iosDir, { withFileTypes: true });
79
- } catch {
80
- return null;
81
- }
82
- for (const entry of entries) {
83
- if (!entry.isDirectory() || !entry.name.endsWith(".xcodeproj")) continue;
84
- const pbx = join$1(iosDir, entry.name, "project.pbxproj");
85
- if (!existsSync$1(pbx)) continue;
86
- let text;
87
- try {
88
- text = readFileSync$1(pbx, "utf-8");
89
- } catch {
90
- continue;
91
- }
92
- const concrete = [...text.matchAll(/PRODUCT_BUNDLE_IDENTIFIER\s*=\s*([^;\s"]+)\s*;/g)].map((m) => m[1]).filter((id) => !!id && !id.startsWith("$") && !id.includes("("));
93
- if (concrete.length === 0) continue;
94
- const counts = {};
95
- for (const id of concrete) counts[id] = (counts[id] || 0) + 1;
96
- let best = null, bestCount = 0, bestLen = Infinity;
97
- for (const [id, count] of Object.entries(counts)) if (count > bestCount || count === bestCount && id.length < bestLen) {
98
- best = id;
99
- bestCount = count;
100
- bestLen = id.length;
101
- }
102
- return best;
103
- }
104
- return null;
105
- }
106
- function detectAndroidPackageFromGradle(moduleDir, accept) {
107
- const gradle = [join$1(moduleDir, "build.gradle"), join$1(moduleDir, "build.gradle.kts")].find((file) => existsSync$1(file));
108
- if (!gradle) return null;
109
- let text;
110
- try {
111
- text = readFileSync$1(gradle, "utf-8");
112
- } catch {
113
- return null;
114
- }
115
- const nsId = text.match(/namespace\s*=?\s*["']([^"']+)["']/)?.[1];
116
- const namespace = nsId ? accept(nsId, gradle) : null;
117
- if (namespace) return namespace;
118
- const appId = text.match(/applicationId\s*=?\s*["']([^"']+)["']/)?.[1];
119
- return appId ? accept(appId, gradle) : null;
120
- }
121
- //#endregion
122
- //#region src/commands/start.ts
123
- const DEFAULT_WAIT_SECONDS = 60;
124
- const POLL_MS = 500;
125
- const LOG_TAIL_LINES = 5;
126
- function writeNote(line) {
127
- console.error(line);
128
- }
129
- function supervisorEntry() {
130
- return spawnEntry("supervisor-run");
131
- }
132
- function wantsExpoOwnTunnel({ isExpo, remote, mode, publicUrl }) {
133
- return remote && isExpo && !publicUrl && mode === "expo";
134
- }
135
- function parseWait(value) {
136
- if (value === void 0 || value === null) return { seconds: DEFAULT_WAIT_SECONDS };
137
- const seconds = Number(value);
138
- if (!Number.isFinite(seconds) || seconds <= 0) return { error: `Invalid --wait value ${JSON.stringify(value)}. Pass a number of seconds, e.g. --wait 90.` };
139
- return { seconds };
140
- }
141
- function liveSupervisor({ state, project, port, isAlive = pidExists, inspectIdentity = process_identity_exports.inspectProcessIdentity } = {}) {
142
- const target = resolveSupervisorTarget({
143
- state: state?.supervisor,
144
- record: project?.supervisor,
145
- reservedPort: port,
146
- isAlive,
147
- inspectIdentity
148
- });
149
- return target.status === "ours" && target.port === port ? {
150
- pid: target.pid,
151
- port: target.port,
152
- mode: target.mode ?? null,
153
- startedAt: target.startedAt ?? null
154
- } : null;
155
- }
156
- function startFacts({ root, port, supervisor, logsDir, alreadyRunning }) {
157
- return {
158
- port,
159
- supervisorPid: supervisor?.pid ?? null,
160
- mode: supervisor?.mode ?? null,
161
- logsDir,
162
- ...root ? { agentDevice: { stateDir: workspaceAgentDeviceDir(root) } } : {},
163
- alreadyRunning: Boolean(alreadyRunning)
164
- };
165
- }
166
- function startError({ code, message, remedy = null }) {
167
- return {
168
- code,
169
- message,
170
- remedy: remedy ?? null
171
- };
172
- }
173
- function tailLines(text, n = LOG_TAIL_LINES) {
174
- return String(text || "").split("\n").filter((l) => l.trim() !== "").slice(-n);
175
- }
176
- function readLogTail(file, n = LOG_TAIL_LINES) {
177
- try {
178
- return tailLines(readFileSync(file, "utf-8"), n);
179
- } catch {
180
- return [];
181
- }
182
- }
183
- function startCommand(program) {
184
- registerStart(program);
185
- }
186
- function supervisorSignalable(child, inspectIdentity = process_identity_exports.inspectProcessIdentity) {
187
- return !child.recordedIdentity || inspectIdentity(child.recordedIdentity) === "same";
188
- }
189
- function recordedSupervisorProcess(recorded) {
190
- const pid = typeof recorded?.pid === "number" ? recorded.pid : void 0;
191
- return Object.assign(new EventEmitter(), {
192
- pid,
193
- ...recorded ? { recordedIdentity: recorded } : {},
194
- stdout: null,
195
- stderr: null,
196
- kill: (signal = "SIGTERM") => pid !== void 0 && signalProcessTree(pid, typeof signal === "number" ? "SIGTERM" : signal),
197
- unref() {}
198
- });
199
- }
200
- const RECORDED_SUPERVISOR_WAIT_MS = 15e3;
201
- function providersOnPath(mode) {
202
- return detectProviders((bin) => {
203
- try {
204
- return Boolean(getExecutor().findExecutable(bin));
205
- } catch {
206
- return false;
207
- }
208
- }, mode);
209
- }
210
- const DEFAULT_START_DEPS = {
211
- providers: providersOnPath,
212
- startTunnelSequence,
213
- isTunnelAlive: pidExists,
214
- writeTunnelRecord: writeWorkspaceState,
215
- stopTunnel,
216
- writeSupervisorRecord: writeWorkspaceState,
217
- terminateSupervisorChild: (child) => terminateChild(child, {
218
- alreadyExited: false,
219
- timeoutMs: 1e3,
220
- now: Date.now,
221
- sleep: cancellableSleep,
222
- isAlive: pidExists
223
- }),
224
- withWorktreeLock: withManagedRemoteWorktreeLock,
225
- withTunnelLock: withManagedTunnelLock,
226
- clearTunnelRecord: clearManagedMetroTunnel,
227
- budgetGate,
228
- platform: process.platform
229
- };
230
- function normalizeManagedTunnelUrl(url) {
231
- return url.trim().replace(/\/+$/, "");
232
- }
233
- function registerStart(program, overrides = {}) {
234
- const d = {
235
- ...DEFAULT_START_DEPS,
236
- ...overrides
237
- };
238
- program.command("start").description("Start this workspace's dev server under a detached supervisor and wait until it verifies as this project's. Idempotent: a healthy dev server on the reserved port is a no-op. Structured logs land in the global workspace logs directory.").option("--json", "Emit the facts as a single JSON line on stdout; every other line goes to stderr").option("--wait <seconds>", `How long to wait for the dev server to answer (default ${DEFAULT_WAIT_SECONDS})`).option("--remote", "Prepare the dev server for a remote device").option("--reset-cache", "Restart owned Metro and clear this app's Metro caches").action(async (opts) => {
239
- const json = Boolean(opts.json);
240
- const out = (line) => {
241
- if (json) console.error(line);
242
- else console.log(line);
243
- };
244
- const note = writeNote;
245
- const fail = ({ code, message, remedy = null, lines = [], reclaimed = [] }) => {
246
- note(chalk.red(message));
247
- for (const line of lines) note(chalk.dim(` ${line}`));
248
- if (remedy) note(chalk.dim(remedy));
249
- note(chalk.red(`failed: ${code}`));
250
- if (json) console.log(JSON.stringify({
251
- ...startError({
252
- code,
253
- message,
254
- remedy
255
- }),
256
- ...reclaimed.length ? { reclaimed } : {}
257
- }));
258
- process.exit(1);
259
- };
260
- const wait = parseWait(opts.wait);
261
- if (wait.error) return fail({
262
- code: "STIM_BAD_ARG",
263
- message: wait.error,
264
- remedy: "Pass a whole number of seconds, e.g. --wait 90."
265
- });
266
- const waitSeconds = wait.seconds;
267
- const root = findProjectRoot(process.cwd());
268
- if (!root) return fail(NO_PROJECT_REFUSAL);
269
- const problem = projectProblem(root, "dev-server");
270
- if (problem) return fail({
271
- code: "STIM_NO_PROJECT",
272
- message: problem.message,
273
- remedy: problem.remedy
274
- });
275
- try {
276
- ensureWorkspaceStorage(root);
277
- } catch (error) {
278
- return fail({
279
- code: error?.code || "STIM_WORKSPACE_STATE",
280
- message: `Could not prepare this workspace's Stim state: ${error?.message || error}`,
281
- remedy: "Check that STIM_HOME is writable and has free space. An EPERM on a directory you can write is a sandbox: allow writes to STIM_HOME, or run Stim with the sandbox disabled (`stim guide errors sandbox`)."
282
- });
283
- }
284
- recordWorkspaceUse(root);
285
- const { context: settingsContext, settings } = resolveProjectSettings(root);
286
- const [shapeError, ...moreShapeErrors] = settingShapeErrors(settings);
287
- if (shapeError) return fail({
288
- code: "STIM_BAD_ARG",
289
- message: shapeError,
290
- lines: moreShapeErrors,
291
- remedy: SETTING_SHAPE_REMEDY
292
- });
293
- for (const key of unknownSettingKeys(settings)) note(chalk.yellow(`Warning: setting "${key}" is not read by Stim and will be ignored.`));
294
- const cacheProviderError = cacheProviderSettingError(settings);
295
- if (cacheProviderError) note(chalk.yellow(`Warning: ${cacheProviderError} No cache provider is used.`));
296
- const result = await startDevServer({
297
- root,
298
- settings,
299
- settingsContext,
300
- waitSeconds,
301
- remote: Boolean(opts.remote),
302
- resetCache: Boolean(opts.resetCache),
303
- out,
304
- note
305
- }, d);
306
- if (!result.ok) return fail({
307
- ...result.error,
308
- lines: result.lines,
309
- reclaimed: result.reclaimed
310
- });
311
- report({
312
- json,
313
- out,
314
- facts: result.facts,
315
- waited: result.waited,
316
- reclaimed: result.reclaimed,
317
- links: workspaceLinks(root)
318
- });
319
- });
320
- }
321
- var StartRefusal = class extends Error {
322
- refusal;
323
- constructor(refusal) {
324
- super(refusal.message);
325
- this.refusal = refusal;
326
- }
327
- };
328
- async function startDevServer({ root, settings, settingsContext, waitSeconds = DEFAULT_WAIT_SECONDS, remote: remoteFlag = false, resetCache = false, out, note }, overrides = {}) {
329
- const d = {
330
- ...DEFAULT_START_DEPS,
331
- ...overrides
332
- };
333
- const waitTimer = stepTimer();
334
- let reclaimed = [];
335
- const fail = (refusal) => {
336
- throw new StartRefusal(refusal);
337
- };
338
- const isExpo = detectIsExpo(root);
339
- const metroCommand = metroCommandSetting(settings);
340
- const serverMode = metroCommand ? MODE_COMMAND : isExpo ? MODE_EXPO : MODE_BARE;
341
- const expoServer = serverMode === MODE_EXPO;
342
- const worktreeRoot = settingsContext.repoRoot ?? root;
343
- const cacheProvider = resolveCacheProviderConfig(settingsContext);
344
- const run = async () => {
345
- const settingError = metroTunnelSettingError(settings);
346
- if (settingError) return fail({
347
- code: "STIM_BAD_ARG",
348
- message: settingError,
349
- remedy: "Set metro.tunnel to \"ngrok\" and metro.ngrokUrl to an HTTPS URL, or remove metro.ngrokUrl."
350
- });
351
- if (metroCommand && d.platform === "win32") return fail({
352
- code: "STIM_BAD_ARG",
353
- message: "metro.command is not supported on Windows: Stim cannot read a process working directory there, so it cannot prove the Metro a wrapper starts belongs to this app.",
354
- remedy: "Remove metro.command on Windows, or run this app from macOS or Linux."
355
- });
356
- if (metroCommand && resetCache) return fail({
357
- code: "STIM_BAD_ARG",
358
- message: "--reset-cache cannot reach a dev server started by metro.command; Stim does not know its reset flag.",
359
- remedy: "Add the command's own cache-reset flag to metro.command (for React Native, --reset-cache), then run stim stop and stim start."
360
- });
361
- const commandError = metroCommandSettingError(settings);
362
- if (commandError) return fail({
363
- code: "STIM_BAD_ARG",
364
- message: commandError,
365
- remedy: "Set metro.command to the argv that starts the dev server, e.g. [\"npx\", \"react-native\", \"start\", \"--port\", \"{port}\"], or remove it."
366
- });
367
- const remote = remoteFlag || remoteIosSetting(settings)?.kind === "backend" || remoteAndroidSetting(settings)?.kind === "backend";
368
- const tunnelMode = tunnelModeSetting(settings) ?? "auto";
369
- const publicUrl = publicUrlSetting(settings);
370
- const tunnel = wantsExpoOwnTunnel({
371
- isExpo: expoServer,
372
- remote,
373
- mode: tunnelMode,
374
- publicUrl
375
- });
376
- if (tunnel) note(chalk.dim("note requesting an Expo tunnel for this workspace's dev server"));
377
- const managedRemote = remote && !tunnel && !publicUrl && tunnelMode !== "off";
378
- const runStart = async () => {
379
- const appIds = detectAppIds(root, resolveIosProjectDir(settings, root).dir, resolveAndroidLayout(settings, root, settingsContext.repoRoot ?? root).moduleDir);
380
- upsertProject(root, {
381
- bundleId: appIds.bundleId ?? void 0,
382
- androidPackage: appIds.androidPackage ?? void 0,
383
- isExpo
384
- });
385
- const gateBudget = async () => {
386
- const budget = await d.budgetGate({
387
- root,
388
- note
389
- });
390
- reclaimed = budget.reclaimed;
391
- if (budget.refusal) return fail(budget.refusal);
392
- };
393
- if (resetCache) {
394
- await gateBudget();
395
- const pin = metroPortSetting(settings);
396
- if (pin.port !== null || pin.error) {
397
- const pinCheck = await resolveWorkspaceMetroPort(root, settings, note, "start", false);
398
- if (typeof pinCheck !== "number") return fail(pinCheck);
399
- }
400
- try {
401
- await stopOwnedMetroForReset(root);
402
- } catch (error) {
403
- const failure = error;
404
- return fail({
405
- code: failure.code ?? "STIM_WORKSPACE_STATE",
406
- message: failure.message,
407
- remedy: failure.remedy
408
- });
409
- }
410
- note(phaseLine("cache", "clearing this app's Metro transform and file-map caches; devices preserved"));
411
- }
412
- const logsDir = workspaceLogsDir$1(root);
413
- const logFile = supervisorLogFile(root);
414
- const port = await resolvePort(root, settings, note, fail);
415
- let publicOrigin = remote ? publicUrl : null;
416
- let resolution = await resolveProjectMetro(port, root);
417
- let supervisor = liveSupervisor({
418
- state: readWorkspaceState$1(root),
419
- project: getProject(root),
420
- port
421
- });
422
- const recordedTarget = resolveSupervisorTarget({
423
- state: readWorkspaceState$1(root)?.supervisor,
424
- record: getProject(root)?.supervisor,
425
- reservedPort: port
426
- });
427
- if (recordedTarget.status === "unverified") return fail({
428
- code: "STIM_SUPERVISOR_EXITED",
429
- message: `Cannot reuse or replace the recorded supervisor: ${recordedTarget.reason}.`,
430
- remedy: "Stop it with the tool that started it, then retry `stim start`. Stim leaves unverified processes alone."
431
- });
432
- if (!resetCache && !resolution.metro && !supervisor) await gateBudget();
433
- let managedTunnel = null;
434
- let spawnedChild = null;
435
- let spawnedTs = null;
436
- let childExit = null;
437
- const spawnDirect = (supervisorArgs, childEnv) => {
438
- const fd = openSync(logFile, "a");
439
- const child = getExecutor().spawn(process.execPath, supervisorArgs, {
440
- cwd: root,
441
- detached: true,
442
- stdio: [
443
- "ignore",
444
- fd,
445
- fd
446
- ],
447
- env: childEnv
448
- });
449
- child.unref?.();
450
- child.on?.("exit", (code, signal) => {
451
- childExit = {
452
- code,
453
- signal
454
- };
455
- });
456
- child.on?.("error", (err) => {
457
- childExit = {
458
- code: null,
459
- signal: null,
460
- error: err
461
- };
462
- });
463
- return child;
464
- };
465
- const recordedSupervisor = async (since) => {
466
- const deadline = Date.now() + RECORDED_SUPERVISOR_WAIT_MS;
467
- while (Date.now() < deadline) {
468
- const record = readWorkspaceState$1(root)?.supervisor;
469
- if (record?.pid && record.port === port && Date.parse(String(record.startedAt)) >= since && pidExists(record.pid)) return {
470
- pid: record.pid,
471
- processToken: record.processToken
472
- };
473
- await cancellableSleep(25);
474
- }
475
- return null;
476
- };
477
- const spawnThroughWindowsShell = async (supervisorArgs, childEnv) => {
478
- const [entry, ...args] = supervisorArgs;
479
- const launcher = windowsLauncherArgs({
480
- entry,
481
- args,
482
- cwd: root,
483
- logFile
484
- });
485
- const since = Date.now();
486
- const shell = getExecutor().spawn(launcher.file, launcher.args, {
487
- cwd: root,
488
- stdio: [
489
- "ignore",
490
- "ignore",
491
- "pipe"
492
- ],
493
- env: {
494
- ...childEnv,
495
- ...launcher.env
496
- },
497
- windowsHide: true
498
- });
499
- const stderr = [];
500
- shell.stderr?.on("data", (chunk) => stderr.push(String(chunk)));
501
- const exit = await new Promise((resolve) => {
502
- shell.on?.("close", (code, signal) => resolve({
503
- code,
504
- signal
505
- }));
506
- shell.on?.("error", (error) => resolve({
507
- code: null,
508
- signal: null,
509
- error
510
- }));
511
- });
512
- const launcherFailed = exit.code !== 0 || exit.error;
513
- const recorded = launcherFailed ? null : await recordedSupervisor(since);
514
- if (recorded === null) {
515
- const reason = launcherFailed ? `the supervisor launcher exited (${exit.error ? exit.error.message : `code ${exit.code}`})` : `the supervisor did not record itself within ${RECORDED_SUPERVISOR_WAIT_MS / 1e3}s`;
516
- appendFileSync(logFile, `Stim start: ${reason}.\n${stderr.join("")}`);
517
- childExit = {
518
- code: exit.code,
519
- signal: exit.signal,
520
- ...exit.error ? { error: exit.error } : {}
521
- };
522
- }
523
- return recordedSupervisorProcess(recorded);
524
- };
525
- const spawnSupervisor = async (origin) => {
526
- mkdirSync(logsDir, { recursive: true });
527
- spawnedTs = Date.now();
528
- const supervisorArgs = [
529
- supervisorEntry(),
530
- "--root",
531
- root,
532
- "--port",
533
- String(port),
534
- ...tunnel ? ["--tunnel"] : [],
535
- ...resetCache ? ["--reset-cache"] : [],
536
- "--idle-stop-minutes",
537
- String(metroIdleStopMinutesSetting(settings)),
538
- "--device-idle-minutes",
539
- String(deviceIdleShutdownMinutesSetting(settings))
540
- ];
541
- const childEnv = {
542
- ...process.env,
543
- ...origin ? {
544
- [PUBLIC_METRO_ENV]: origin,
545
- EXPO_PACKAGER_PROXY_URL: origin
546
- } : {}
547
- };
548
- childEnv[CACHE_PROVIDER_ENV] = cacheProviderEnv(cacheProvider);
549
- const child = d.platform === "win32" ? await spawnThroughWindowsShell(supervisorArgs, childEnv) : spawnDirect(supervisorArgs, childEnv);
550
- out(chalk.dim(phaseLine("metro", `starting on port ${port} (${serverMode}, supervisor pid ${child.pid})`)));
551
- spawnedChild = child;
552
- return child;
553
- };
554
- const waitForSupervisorHandoff = async (child) => {
555
- const deadline = Date.now() + 5e3;
556
- while (Date.now() < deadline) {
557
- const found = liveSupervisor({
558
- state: readWorkspaceState$1(root),
559
- project: getProject(root),
560
- port
561
- });
562
- if (found?.pid === child.pid) return found;
563
- if (childExit !== null || (child.pid ? !pidExists(child.pid) : true)) return null;
564
- await cancellableSleep(25);
565
- }
566
- return null;
567
- };
568
- if (remote && !tunnel && !publicUrl && tunnelMode !== "off") {
569
- const available = d.providers(tunnelMode);
570
- const plan = planMetroReach({
571
- mode: tunnelMode,
572
- metroPort: port,
573
- publicUrl,
574
- isExpo: expoServer,
575
- available
576
- });
577
- if ("failed" in plan) return fail({
578
- code: "STIM_REMOTE_METRO_UNREACHABLE",
579
- message: plan.failed,
580
- remedy: plan.remedy
581
- });
582
- if ("start" in plan) {
583
- const candidates = tunnelMode === "auto" ? available.filter((provider) => provider !== "tailscale") : [plan.start];
584
- const expectedStableUrl = ngrokUrlSetting(settings);
585
- let acquisition;
586
- try {
587
- acquisition = await d.withTunnelLock(root, async () => {
588
- let tracking;
589
- let startedCleanup = null;
590
- const recorded = readMetroTunnel(root);
591
- if (recorded?.kind === "managed" && d.isTunnelAlive(recorded.pid)) {
592
- if (!recorded.processToken) return { failed: {
593
- code: "STIM_REMOTE_START_REQUIRED",
594
- message: "The recorded managed Metro tunnel has no process identity token.",
595
- remedy: "Inspect the process, stop it with the provider tooling, remove the stale metroTunnel state, and retry."
596
- } };
597
- if (!(recorded.port === port && candidates.includes(recorded.provider) && (!expectedStableUrl || normalizeManagedTunnelUrl(recorded.url) === expectedStableUrl))) return { failed: {
598
- code: "STIM_REMOTE_START_REQUIRED",
599
- message: `A different managed Metro tunnel is already running for this workspace.`,
600
- remedy: "Run `stim stop`, then `stim start --remote`."
601
- } };
602
- tracking = {
603
- record: {
604
- provider: recorded.provider,
605
- pid: recorded.pid,
606
- url: normalizeManagedTunnelUrl(recorded.url),
607
- port: recorded.port,
608
- startedAt: recorded.startedAt,
609
- processToken: recorded.processToken,
610
- logFile: recorded.logFile
611
- },
612
- startedHere: false
613
- };
614
- } else {
615
- const currentResolution = await resolveProjectMetro(port, root);
616
- const currentSupervisor = liveSupervisor({
617
- state: readWorkspaceState$1(root),
618
- project: getProject(root),
619
- port
620
- });
621
- if (currentResolution.metro || currentSupervisor) return { failed: {
622
- code: "STIM_REMOTE_START_REQUIRED",
623
- message: `The dev server on port ${port} is local-only and cannot gain a managed tunnel while it is running.`,
624
- remedy: "Run `stim stop`, then `stim start --remote`."
625
- } };
626
- const started = await d.startTunnelSequence({
627
- providers: candidates,
628
- port,
629
- ngrokUrl: expectedStableUrl,
630
- requireReachable: false
631
- });
632
- if ("failed" in started) return { failed: {
633
- code: "STIM_REMOTE_METRO_UNREACHABLE",
634
- message: `Could not start a managed Metro tunnel for port ${port}.`,
635
- remedy: started.reason
636
- } };
637
- const record = {
638
- provider: started.provider,
639
- pid: started.pid,
640
- url: normalizeManagedTunnelUrl(started.url),
641
- port,
642
- startedAt: (/* @__PURE__ */ new Date()).toISOString(),
643
- processToken: started.processToken,
644
- logFile: started.logFile
645
- };
646
- startedCleanup = started.cleanup;
647
- try {
648
- d.writeTunnelRecord(root, { metroTunnel: {
649
- kind: "managed",
650
- ...record
651
- } });
652
- } catch (err) {
653
- const stopped = await started.cleanup();
654
- return { failed: {
655
- code: "STIM_REMOTE_METRO_UNREACHABLE",
656
- message: `Could not record the managed Metro tunnel: ${err?.message || err}`,
657
- remedy: stopped.status === "failed" ? `Cleanup failed. Unmanaged pid ${record.pid} may still be running: ${stopped.reason ?? "unknown error"}` : "The tunnel process was stopped. Fix the workspace write error, then retry `stim start --remote`."
658
- } };
659
- }
660
- tracking = {
661
- record,
662
- startedHere: true
663
- };
664
- }
665
- const currentResolution = await resolveProjectMetro(port, root);
666
- const currentSupervisor = liveSupervisor({
667
- state: readWorkspaceState$1(root),
668
- project: getProject(root),
669
- port
670
- });
671
- if (currentResolution.metro || currentSupervisor) {
672
- if (tracking.startedHere && startedCleanup) {
673
- const stopped = await startedCleanup();
674
- if (stopped.status === "failed") return { failed: {
675
- code: "STIM_REMOTE_START_REQUIRED",
676
- message: `The dev server on port ${port} started before the managed tunnel was ready.`,
677
- remedy: `Tunnel cleanup failed for pid ${tracking.record.pid}: ${stopped.reason ?? "unknown error"}. The tunnel record remains available to \`stim stop\`.`
678
- } };
679
- d.clearTunnelRecord(root, tracking.record);
680
- return { failed: {
681
- code: "STIM_REMOTE_START_REQUIRED",
682
- message: `The dev server on port ${port} started before the managed tunnel was ready.`,
683
- remedy: "Run `stim stop`, then `stim start --remote`."
684
- } };
685
- }
686
- return {
687
- origin: tracking.record.url,
688
- tunnel: tracking
689
- };
690
- }
691
- if (!d.isTunnelAlive(tracking.record.pid)) {
692
- if (tracking.startedHere && startedCleanup) {
693
- const stopped = await startedCleanup();
694
- if (stopped.status === "failed") return { failed: {
695
- code: "STIM_REMOTE_METRO_UNREACHABLE",
696
- message: `The managed ${tracking.record.provider} tunnel exited before the supervisor started.`,
697
- remedy: `Cleanup failed. Unmanaged pid ${tracking.record.pid} may still be running: ${stopped.reason ?? "unknown error"}. The tunnel record remains available to \`stim stop\`.`
698
- } };
699
- }
700
- d.clearTunnelRecord(root, tracking.record);
701
- return { failed: {
702
- code: "STIM_REMOTE_METRO_UNREACHABLE",
703
- message: `The managed ${tracking.record.provider} tunnel exited before the supervisor started.`,
704
- remedy: "Retry `stim start --remote`."
705
- } };
706
- }
707
- let child;
708
- try {
709
- child = await spawnSupervisor(tracking.record.url);
710
- } catch (err) {
711
- return { failed: {
712
- code: "STIM_SUPERVISOR_EXITED",
713
- message: `Could not spawn the dev server supervisor: ${err?.message || err}`,
714
- remedy: "Fix the spawn error, then retry `stim start --remote`."
715
- } };
716
- }
717
- const handoffRecord = {
718
- pid: child.pid,
719
- processToken: child.recordedIdentity ? child.recordedIdentity.processToken ?? null : child.pid ? (0, process_identity_exports.captureProcessToken)(child.pid) : null,
720
- port,
721
- mode: serverMode,
722
- startedAt: new Date(spawnedTs ?? Date.now()).toISOString()
723
- };
724
- try {
725
- d.writeSupervisorRecord(root, { supervisor: handoffRecord });
726
- } catch {
727
- if (!await waitForSupervisorHandoff(child)) {
728
- const stopped = supervisorSignalable(child) && await d.terminateSupervisorChild(child);
729
- return { failed: {
730
- code: "STIM_SUPERVISOR_EXITED",
731
- message: `Could not record supervisor pid ${child.pid ?? "unknown"} before releasing the managed start lock.`,
732
- remedy: stopped ? "The supervisor process was stopped. Retry `stim start --remote`." : `Cleanup failed. Unmanaged supervisor pid ${child.pid ?? "unknown"} may still be running. Stop it with \`stim stop\`${child.pid === void 0 ? "" : `, or \`${d.platform === "win32" ? `taskkill /PID ${child.pid} /T /F` : `kill ${child.pid}`}\``} before retrying.`
733
- } };
734
- }
735
- }
736
- return {
737
- origin: tracking.record.url,
738
- tunnel: tracking
739
- };
740
- });
741
- } catch (err) {
742
- return fail({
743
- code: "STIM_REMOTE_METRO_UNREACHABLE",
744
- message: `Could not acquire the managed Metro tunnel lock: ${err?.message || err}`,
745
- remedy: "Retry `stim start --remote` after the other start command finishes."
746
- });
747
- }
748
- if ("failed" in acquisition) return fail(acquisition.failed);
749
- publicOrigin = acquisition.origin;
750
- managedTunnel = acquisition.tunnel;
751
- if (managedTunnel.record.provider === "tailscale") note(phaseLine("lan", `tailnet-only Metro: ${publicOrigin}`));
752
- resolution = await resolveProjectMetro(port, root);
753
- supervisor = liveSupervisor({
754
- state: readWorkspaceState$1(root),
755
- project: getProject(root),
756
- port
757
- });
758
- }
759
- }
760
- const managedTunnelExited = () => managedTunnel !== null && !d.isTunnelAlive(managedTunnel.record.pid);
761
- const failExitedManagedTunnel = async () => {
762
- const tracked = managedTunnel;
763
- const record = tracked.record;
764
- const stopped = tracked.startedHere ? await d.stopTunnel(record) : { status: "missing" };
765
- if (stopped.status !== "failed") d.clearTunnelRecord(root, record);
766
- return fail({
767
- code: "STIM_REMOTE_METRO_UNREACHABLE",
768
- message: `The managed ${record.provider} tunnel exited before the dev server became ready.`,
769
- remedy: stopped.status === "failed" ? `The tunnel cleanup also failed: ${stopped.reason ?? "unknown error"}. Run \`stim stop\`, then retry.` : "Run `stim stop`, then `stim start --remote`."
770
- });
771
- };
772
- const requireExpoTunnel = () => {
773
- if (tunnel && (!supervisor || readMetroTunnel(root)?.kind !== "expo")) fail({
774
- code: "STIM_REMOTE_START_REQUIRED",
775
- message: `The Expo dev server on port ${port} is local-only and cannot gain a tunnel while it is running.`,
776
- remedy: "Run `stim stop`, then `stim start --remote`."
777
- });
778
- };
779
- if (!spawnedChild && resolution.metro) {
780
- if (managedTunnelExited()) return failExitedManagedTunnel();
781
- if (tunnel && supervisor) {
782
- if (!await waitForExpoTunnel({
783
- root,
784
- seconds: waitSeconds,
785
- aborted: () => !liveSupervisor({
786
- state: readWorkspaceState$1(root),
787
- project: getProject(root),
788
- port
789
- })
790
- })) {
791
- const stillLive = liveSupervisor({
792
- state: readWorkspaceState$1(root),
793
- project: getProject(root),
794
- port
795
- });
796
- return fail({
797
- code: "STIM_REMOTE_START_REQUIRED",
798
- message: stillLive ? `The Expo dev server on port ${port} is local-only and cannot gain a tunnel while it is running.` : `The Expo dev server on port ${port} stopped before its tunnel became ready.`,
799
- remedy: stillLive ? "Run `stim stop`, then `stim start --remote`." : "Run `stim start --remote` again."
800
- });
801
- }
802
- supervisor = liveSupervisor({
803
- state: readWorkspaceState$1(root),
804
- project: getProject(root),
805
- port
806
- });
807
- }
808
- requireExpoTunnel();
809
- if (!supervisor) {
810
- note(chalk.dim(`A dev server for this project already answers on port ${port}, started outside Stim.`));
811
- note(chalk.dim("Leaving it alone: Stim will not start a second bundler over a working one."));
812
- }
813
- if (managedTunnelExited()) return failExitedManagedTunnel();
814
- clearWorkspaceStateKeys(root, [IDLE_STOP_KEY]);
815
- return startFacts({
816
- root,
817
- port,
818
- supervisor,
819
- logsDir,
820
- alreadyRunning: true
821
- });
822
- }
823
- if (!spawnedChild && supervisor) {
824
- note(chalk.dim(`Supervisor pid ${supervisor.pid} is already running for this workspace; waiting for it to answer on port ${port}...`));
825
- if (!await waitForMetro({
826
- root,
827
- port,
828
- seconds: waitSeconds,
829
- aborted: managedTunnel ? managedTunnelExited : void 0
830
- })) {
831
- if (managedTunnelExited()) return failExitedManagedTunnel();
832
- return fail({
833
- code: "STIM_METRO_TIMEOUT",
834
- message: `Supervisor pid ${supervisor.pid} did not serve port ${port} within ${waitSeconds}s.`,
835
- lines: logTailLines(logFile),
836
- remedy: "Run `stim stop` to halt it, then `stim start` again."
837
- });
838
- }
839
- if (tunnel) {
840
- if (!await waitForExpoTunnel({
841
- root,
842
- seconds: waitSeconds,
843
- aborted: () => !liveSupervisor({
844
- state: readWorkspaceState$1(root),
845
- project: getProject(root),
846
- port
847
- })
848
- })) {
849
- const stillLive = liveSupervisor({
850
- state: readWorkspaceState$1(root),
851
- project: getProject(root),
852
- port
853
- });
854
- return fail({
855
- code: "STIM_REMOTE_START_REQUIRED",
856
- message: stillLive ? `The Expo dev server on port ${port} is local-only and cannot gain a tunnel while it is running.` : `The Expo dev server on port ${port} stopped before its tunnel became ready.`,
857
- remedy: stillLive ? "Run `stim stop`, then `stim start --remote`." : "Run `stim start --remote` again."
858
- });
859
- }
860
- }
861
- supervisor = liveSupervisor({
862
- state: readWorkspaceState$1(root),
863
- project: getProject(root),
864
- port
865
- }) || (tunnel ? null : supervisor);
866
- requireExpoTunnel();
867
- if (managedTunnelExited()) return failExitedManagedTunnel();
868
- return startFacts({
869
- root,
870
- port,
871
- supervisor,
872
- logsDir,
873
- alreadyRunning: true
874
- });
875
- }
876
- if (managedTunnelExited()) return failExitedManagedTunnel();
877
- const child = spawnedChild ?? await spawnSupervisor(publicOrigin);
878
- const attemptStartedTs = spawnedTs ?? Date.now();
879
- if (!await waitForMetro({
880
- root,
881
- port,
882
- seconds: waitSeconds,
883
- aborted: () => childExit !== null || (child.pid ? !pidExists(child.pid) : false) || managedTunnelExited()
884
- })) {
885
- if (managedTunnelExited()) return failExitedManagedTunnel();
886
- const gone = childExit !== null || (child.pid ? !pidExists(child.pid) : false);
887
- const exitInfo = childExit;
888
- const how = exitInfo ? exitInfo.signal ? `signal ${exitInfo.signal}` : `code ${exitInfo.code}` : "without being observed";
889
- return fail(gone ? {
890
- code: "STIM_SUPERVISOR_EXITED",
891
- message: `The supervisor exited (${how}) before the dev server came up on port ${port}.`,
892
- lines: failureEvidence({
893
- logFile,
894
- logsDir,
895
- sinceTs: attemptStartedTs
896
- }),
897
- remedy: "Fix the error above and run `stim start` again; `stim logs --errors` has the full records."
898
- } : {
899
- code: "STIM_METRO_TIMEOUT",
900
- message: `The dev server did not answer on port ${port} within ${waitSeconds}s.`,
901
- lines: failureEvidence({
902
- logFile,
903
- logsDir,
904
- sinceTs: attemptStartedTs
905
- }),
906
- remedy: "It may still be starting. Run `stim stop` to halt it, or `stim logs` to follow along."
907
- });
908
- }
909
- if (managedTunnelExited()) return failExitedManagedTunnel();
910
- if (tunnel) {
911
- if (!await waitForExpoTunnel({
912
- root,
913
- seconds: waitSeconds,
914
- aborted: () => childExit !== null || (child.pid ? !pidExists(child.pid) : false)
915
- })) {
916
- const gone = childExit !== null || (child.pid ? !pidExists(child.pid) : false);
917
- return fail({
918
- code: gone ? "STIM_SUPERVISOR_EXITED" : "STIM_METRO_TIMEOUT",
919
- message: gone ? `The supervisor exited before the Expo tunnel became ready on port ${port}.` : `Expo did not report a tunnel URL within ${waitSeconds}s.`,
920
- remedy: "Run `stim stop`, then `stim start --remote`."
921
- });
922
- }
923
- }
924
- supervisor = liveSupervisor({
925
- state: readWorkspaceState$1(root),
926
- project: getProject(root),
927
- port
928
- }) || {
929
- pid: child.pid,
930
- port,
931
- mode: null,
932
- startedAt: null
933
- };
934
- return startFacts({
935
- root,
936
- port,
937
- supervisor,
938
- logsDir,
939
- alreadyRunning: false
940
- });
941
- };
942
- const startLocked = async () => {
943
- try {
944
- return await withWorkspaceProcessLock(dirname(workspaceLogsDir$1(root)), "metro-start", runStart, { external: true });
945
- } catch (error) {
946
- if (workspaceProcessLockError(error) !== "timeout") throw error;
947
- return fail({
948
- code: "STIM_METRO_TIMEOUT",
949
- message: "Another Metro start or reset is still running for this app.",
950
- remedy: "Wait for it to finish, then retry `stim start`."
951
- });
952
- }
953
- };
954
- if (!managedRemote) return startLocked();
955
- try {
956
- return await d.withWorktreeLock(worktreeRoot, startLocked);
957
- } catch (err) {
958
- const lockError = workspaceProcessLockError(err);
959
- if (lockError === "refused") return fail({
960
- code: "STIM_WORKTREE_REMOVAL_IN_PROGRESS",
961
- message: `The worktree at ${worktreeRoot} is being removed.`,
962
- remedy: "Retry `stim start --remote` after `stim worktree remove` finishes."
963
- });
964
- if (lockError === "timeout") return fail({
965
- code: "STIM_REMOTE_METRO_UNREACHABLE",
966
- message: `Could not acquire the managed remote worktree lock: ${err?.message || err}`,
967
- remedy: "Retry `stim start --remote` after the other remote start command finishes."
968
- });
969
- throw err;
970
- }
971
- };
972
- try {
973
- return {
974
- ok: true,
975
- facts: await run(),
976
- waited: waitTimer(),
977
- reclaimed
978
- };
979
- } catch (error) {
980
- if (!(error instanceof StartRefusal)) throw error;
981
- return {
982
- ok: false,
983
- error: startError(error.refusal),
984
- lines: error.refusal.lines ?? [],
985
- reclaimed
986
- };
987
- }
988
- }
989
- async function resolvePort(root, settings, note, fail) {
990
- const result = await resolveWorkspaceMetroPort(root, settings, note, "start");
991
- return typeof result === "number" ? result : fail(result);
992
- }
993
- async function resolveWorkspaceMetroPort(root, settings, note, command, reserve = true) {
994
- const setting = metroPortSetting(settings);
995
- if (setting.error) return {
996
- code: "STIM_BAD_ARG",
997
- message: setting.error,
998
- remedy: SETTING_SHAPE_REMEDY
999
- };
1000
- const pinned = setting.port;
1001
- const project = getProject(root);
1002
- const recorded = project?.metroPort;
1003
- if (pinned === null && command === "web") return recorded ?? await reservePort(root, null);
1004
- if (recorded) {
1005
- const supervisor = resolveSupervisorTarget({
1006
- state: readWorkspaceState$1(root)?.supervisor,
1007
- record: project.supervisor,
1008
- reservedPort: recorded
1009
- });
1010
- if (supervisor.status !== "none" && supervisor.status !== "stale") {
1011
- if (pinned === null || recorded === pinned) return recorded;
1012
- if (supervisor.status === "unverified") return {
1013
- code: "STIM_BAD_ARG",
1014
- message: `Cannot move this workspace's Metro to metro.port ${pinned}: ${supervisor.reason}.`,
1015
- remedy: `Stop that supervisor with the tool that started it, then retry, or restore metro.port to ${recorded}. Stim leaves unverified processes alone.`
1016
- };
1017
- return {
1018
- code: "STIM_BAD_ARG",
1019
- message: `This workspace's dev server is running on port ${recorded}, and metro.port pins it to ${pinned}.`,
1020
- remedy: "Run `stim stop`, then retry, or unset or restore metro.port."
1021
- };
1022
- }
1023
- }
1024
- if (pinned !== null) {
1025
- const held = await resolveProjectMetro(pinned, root);
1026
- if (held.notOurs) return {
1027
- code: "STIM_BAD_ARG",
1028
- message: `Port ${pinned} is held by something else (${held.notOurs}), and metro.port pins this workspace's Metro to it.`,
1029
- remedy: "Stop the process on that port, or set another metro.port."
1030
- };
1031
- if (recorded === pinned) return recorded;
1032
- } else if (recorded) {
1033
- const held = await resolveProjectMetro(recorded, root);
1034
- if (!held.notOurs) return recorded;
1035
- const fresh = await reservePort(root, null);
1036
- if (typeof fresh === "number" && fresh !== recorded) {
1037
- note(chalk.yellow(`Port ${recorded} is held by something else (${held.notOurs}).`));
1038
- note(chalk.dim(`Reserved port ${fresh} for this project instead.`));
1039
- }
1040
- return fresh;
1041
- }
1042
- if (!reserve && pinned !== null) return pinned;
1043
- return reservePort(root, pinned);
1044
- }
1045
- async function reservePort(root, pinned) {
1046
- try {
1047
- return await reserveMetroPort(root, void 0, void 0, pinned);
1048
- } catch (error) {
1049
- if (error instanceof PortInspectionError) return {
1050
- code: error.code,
1051
- message: error.reason,
1052
- remedy: PORT_INSPECTION_REMEDY
1053
- };
1054
- return {
1055
- code: "STIM_BAD_ARG",
1056
- message: error.message,
1057
- remedy: "Give each workspace its own metro.port, in the workspace layer or the environment."
1058
- };
1059
- }
1060
- }
1061
- async function waitForMetro({ root, port, seconds, aborted = () => false, probe = resolveProjectMetro }) {
1062
- const deadline = Date.now() + seconds * 1e3;
1063
- while (Date.now() < deadline) {
1064
- if ((await probe(port, root)).metro) return true;
1065
- if (aborted()) return false;
1066
- await cancellableSleep(POLL_MS);
1067
- }
1068
- const last = await probe(port, root);
1069
- return Boolean(last.metro);
1070
- }
1071
- async function waitForExpoTunnel({ root, seconds, aborted = () => false }) {
1072
- const deadline = Date.now() + seconds * 1e3;
1073
- while (Date.now() < deadline) {
1074
- if (aborted()) return false;
1075
- if (readMetroTunnel(root)?.kind === "expo") return true;
1076
- await cancellableSleep(POLL_MS);
1077
- }
1078
- return !aborted() && readMetroTunnel(root)?.kind === "expo";
1079
- }
1080
- function logTailLines(logFile) {
1081
- return [...readLogTail(logFile), `Supervisor log: ${logFile}`];
1082
- }
1083
- function failureEvidence({ logFile, logsDir, sinceTs }) {
1084
- const supTail = readLogTail(logFile);
1085
- const lines = supTail.length > 0 ? [...supTail, `Supervisor log: ${logFile}`] : [];
1086
- let errors = [];
1087
- let all = [];
1088
- try {
1089
- all = queryLogs({ dir: logsDir });
1090
- errors = all.filter((record) => levelRank(record.level) >= levelRank("error"));
1091
- } catch {}
1092
- const since = (rs) => rs.filter((r) => typeof r.ts === "number" && r.ts >= sinceTs).slice(-8);
1093
- let recent = since(errors);
1094
- const fellBack = recent.length === 0;
1095
- if (fellBack) recent = since(all);
1096
- for (const r of recent) lines.push(`${String(r.src ?? "?")}: ${String(r.msg ?? "").split("\n")[0] ?? ""}`);
1097
- if (recent.length > 0) lines.push(fellBack ? "Full records: `stim logs`" : "Full records: `stim logs --errors`");
1098
- return lines;
1099
- }
1100
- function report({ json, out, facts, waited, reclaimed, links }) {
1101
- if (json) {
1102
- console.log(JSON.stringify({
1103
- ...facts,
1104
- ...links ? { links } : {},
1105
- ...reclaimed.length ? { reclaimed } : {}
1106
- }));
1107
- return;
1108
- }
1109
- const who = facts.supervisorPid ? `supervisor pid ${facts.supervisorPid}${facts.mode ? ` (${facts.mode})` : ""}` : "started outside Stim";
1110
- out(chalk.green(`OK: dev server on port ${facts.port}, ${who}${facts.alreadyRunning ? " (already running)" : ""} ${waited}`));
1111
- out(chalk.dim(phaseLine("logs", facts.logsDir)));
1112
- if (facts.agentDevice) out(chalk.dim(phaseLine("agent-device", `AGENT_DEVICE_STATE_DIR=${facts.agentDevice.stateDir}`)));
1113
- if (links) console.error(chalk.dim(workspaceLinkLine(links)));
1114
- }
1115
- //#endregion
1116
- export { registerStart as a, startDevServer as c, supervisorSignalable as d, tailLines as f, detectBundleId as g, detectAppIds as h, readLogTail as i, startFacts as l, detectAndroidPackage as m, liveSupervisor as n, resolveWorkspaceMetroPort as o, wantsExpoOwnTunnel as p, parseWait as r, startCommand as s, failureEvidence as t, supervisorEntry as u };