stim 1.18.0 → 1.20.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 (178) hide show
  1. package/README.md +21 -4
  2. package/dist/{activity-BxnkDZDi.mjs → activity-BrGh9EAI.mjs} +3 -3
  3. package/dist/{agent-device-usage-output-CZ66YA4T.mjs → agent-device-usage-output-DVyv_hWj.mjs} +7 -7
  4. package/dist/android-0vv-OruW.mjs +6 -0
  5. package/dist/android-Brx6EKvJ.mjs +1258 -0
  6. package/dist/{android-DC3ZLuw0.mjs → android-BsYNR81W.mjs} +103 -45
  7. package/dist/{android-CBwCNicO.mjs → android-CK3fpEyZ.mjs} +2 -2
  8. package/dist/{android-cas-BBFyr9CA.mjs → android-cas-DCAuXryb.mjs} +9 -5
  9. package/dist/android-cas-compiler.mjs +1 -1
  10. package/dist/api-run.mjs +159 -8
  11. package/dist/{api-D0VtFhqk.mjs → api-zuX3ynYN.mjs} +9 -7
  12. package/dist/api.d.mts +104 -4
  13. package/dist/api.mjs +1 -1
  14. package/dist/{app-install-D-PUENug.mjs → app-install-0LgaQRT7.mjs} +39 -18
  15. package/dist/artifact-CnC0mfov.mjs +807 -0
  16. package/dist/artifact-DClXJNWx.mjs +772 -0
  17. package/dist/artifact-lifecycle-DzPpBYvK.mjs +30 -0
  18. package/dist/{web-DTU0uiWj.mjs → browser-web-CzBmhmgs.mjs} +136 -174
  19. package/dist/{budget-DIhL2BKx.mjs → budget-BONq9vXY.mjs} +3096 -3244
  20. package/dist/build-BixRI6sB.mjs +462 -0
  21. package/dist/build-plan-CP0tckNU.mjs +553 -0
  22. package/dist/{cache-manifest-BI7ozfmH.mjs → cache-manifest-qA3dT2x2.mjs} +1 -1
  23. package/dist/cache-manifest.mjs +1 -1
  24. package/dist/ccache-CjHQ5F8g.mjs +142 -0
  25. package/dist/{chrome-DJnmnS5q.mjs → chrome-Dp2lMLBe.mjs} +1 -1
  26. package/dist/{cli-DNJMwADx.mjs → cli-B2R8wH1F.mjs} +20 -20
  27. package/dist/cli.mjs +1 -1
  28. package/dist/{client-DzhapoMB.mjs → client-BLy1Pw2V.mjs} +172 -165
  29. package/dist/collector-run.d.mts +10 -0
  30. package/dist/collector-run.mjs +7 -7
  31. package/dist/{command-output-CBtzyIgF.mjs → command-output-BjPN99ZB.mjs} +2 -2
  32. package/dist/{config-CgYMahz7.mjs → config-D0LhTY3S.mjs} +13 -13
  33. package/dist/{created-devices-DKbeWsiK.mjs → created-devices-HU_DAb_Q.mjs} +7 -6
  34. package/dist/{dependency-state-xKoOypBm.mjs → dependency-state-B2m8eqsS.mjs} +2 -2
  35. package/dist/{detached-entry-BLfcgc4O.mjs → detached-entry-BXq4fIl-.mjs} +1 -1
  36. package/dist/{dev-client-CLnIH02x.mjs → dev-client-ClATaJoH.mjs} +2 -2
  37. package/dist/{device-D7oCHGWi.mjs → device-B7lsC0K1.mjs} +8 -8
  38. package/dist/device-DVCwAFlX.mjs +727 -0
  39. package/dist/{device-capacity-EUOOYr8T.mjs → device-capacity-Ca_ke1LE.mjs} +25 -15
  40. package/dist/device-host-worker.mjs +38 -18
  41. package/dist/{device-ios-6MewKKnc.mjs → device-ios-CRUqPxKw.mjs} +12 -11
  42. package/dist/{device-lease-Co-EwifR.mjs → device-lease-DHOBXGgS.mjs} +135 -22
  43. package/dist/{device-lease-run-w1xhCqIW.mjs → device-lease-run-CvDeHTkG.mjs} +4 -4
  44. package/dist/{device-pool-F5WvRPtD.mjs → device-pool-L2-5GxMy.mjs} +3 -3
  45. package/dist/{device-remote-tHbd1fKy.mjs → device-remote-Bmg42063.mjs} +17 -69
  46. package/dist/doctor-C2MSuMMg.mjs +515 -0
  47. package/dist/{doctor-B9ckYUoU.mjs → doctor-DPAqhVCK.mjs} +334 -721
  48. package/dist/doctor-watchman-CB4NnVZm.mjs +260 -0
  49. package/dist/{simslim-EHBQ2R4G.mjs → eas-build-Dw_ec2Km.mjs} +6 -152
  50. package/dist/{stim-installations-aR_WsNOA.mjs → eas-session-ledger-DZlC-Fq3.mjs} +57 -4
  51. package/dist/{error-diagnostics-BRXOqDzG.mjs → error-diagnostics-Du7I_GuY.mjs} +17 -12
  52. package/dist/errors-DzkMYh8Q.mjs +205 -0
  53. package/dist/{exec-DWhCZDGm.mjs → exec-B81Qodjn.mjs} +69 -15
  54. package/dist/{gc-Dh6mXOKC.mjs → gc-PWp34zGb.mjs} +23 -22
  55. package/dist/{gradle-C6GKnunQ.mjs → gradle-C2OEKCCI.mjs} +50 -125
  56. package/dist/{guide-tm6igAaa.mjs → guide-Dai9lphv.mjs} +862 -538
  57. package/dist/{hosted-android-Bv285t_t.mjs → hosted-android-DQ-Agsd8.mjs} +5 -5
  58. package/dist/{hosted-client-C-rMtppF.mjs → hosted-client-Io01s5By.mjs} +9 -3
  59. package/dist/{hosted-ios-D5mNmtj4.mjs → hosted-ios-DIxFnYp-.mjs} +3 -3
  60. package/dist/{hosted-logs-DN8Iz4mJ.mjs → hosted-logs-DP-ABU_M.mjs} +8 -8
  61. package/dist/{hosted-macos-B-wEjlwG.mjs → hosted-macos-Aziw9T_9.mjs} +7 -6
  62. package/dist/{hosted-native-DeeOG8xZ.mjs → hosted-native-BdAQi21M.mjs} +23 -16
  63. package/dist/{idle-shutdown-jjtxAn5C.mjs → idle-shutdown-DdMqoSUB.mjs} +7 -7
  64. package/dist/ios-BZ9dg2Fr.mjs +9 -0
  65. package/dist/{ios-B3SbOWdw.mjs → ios-CXLIUxXJ.mjs} +180 -24
  66. package/dist/ios-CZuv6gRv.mjs +1112 -0
  67. package/dist/{ios-Bvbf0SGY.mjs → ios-VGvNdsv2.mjs} +1 -1
  68. package/dist/{ios-device-Bn7jWDB1.mjs → ios-device-B1B14xND.mjs} +1 -1
  69. package/dist/{ios-device-vOaByMhw.mjs → ios-device-BreZOpM0.mjs} +3 -3
  70. package/dist/ios-project-B9drt_aY.mjs +197 -0
  71. package/dist/{ios-state-B2pES6Vs.mjs → ios-state-CjrL2qv-.mjs} +1 -1
  72. package/dist/js-swap-C8icNYIY.mjs +432 -0
  73. package/dist/launch-DxH0THLP.mjs +745 -0
  74. package/dist/launch-YrbqaIFj.mjs +1530 -0
  75. package/dist/{launch-verify-hskNcWkZ.mjs → launch-verify-BzRdbuO1.mjs} +26 -7
  76. package/dist/{logs-1Ylc1Ri6.mjs → logs-DHaRNfg7.mjs} +11 -11
  77. package/dist/macos-C0iljhsN.mjs +2 -0
  78. package/dist/{macos-DF6C7wUU.mjs → macos-V_3Ui7g4.mjs} +125 -155
  79. package/dist/macos-run.mjs +1 -1
  80. package/dist/maintenance-run.mjs +13 -10
  81. package/dist/{metro-Dh8T4Ytd.mjs → metro-D_j9sN-B.mjs} +209 -43
  82. package/dist/{metro-gateway-CEUw9RMH.mjs → metro-gateway-BBJldOdi.mjs} +2 -2
  83. package/dist/miss-reason-BPAzZS0r.mjs +238 -0
  84. package/dist/{named-ports-BFIz1Shz.mjs → named-ports-C2lvoPsb.mjs} +12 -5
  85. package/dist/native-android-lHzaew7r.mjs +226 -0
  86. package/dist/native-gradle-inputs-BQnIdAlL.mjs +356 -0
  87. package/dist/{native-runtime-D_603JiC.mjs → native-runtime-BRmWL7ru.mjs} +9 -8
  88. package/dist/native-xcode-inputs-eutS19jU.mjs +307 -0
  89. package/dist/native-xcode-ios-DP3cHLwu.mjs +434 -0
  90. package/dist/{ndjson-LGZAMsqa.mjs → ndjson-GOnbyvso.mjs} +6 -6
  91. package/dist/offload-worker.d.mts +12 -6
  92. package/dist/offload-worker.mjs +409 -73
  93. package/dist/{ownership-D6Se7gg_.mjs → ownership-DXzMMSx6.mjs} +2 -2
  94. package/dist/{placement-log-BcPhRwR4.mjs → placement-log-reFbjqEr.mjs} +7 -3
  95. package/dist/plan-BDk2AeY_.mjs +260 -0
  96. package/dist/plan-placement-D_YYlE6N.mjs +73 -0
  97. package/dist/{ports-BLTtfBq-.mjs → ports-jzvAJXXL.mjs} +3 -3
  98. package/dist/{prebuild-juCyreaR.mjs → prebuild-Bw44QxVh.mjs} +4 -4
  99. package/dist/{preview-D5V07Nrl.mjs → preview-BaBJ72Al.mjs} +51 -34
  100. package/dist/project-B8_duAzW.mjs +1067 -0
  101. package/dist/project-plan-BPwbLwQu.mjs +10 -0
  102. package/dist/{pull-request-u4vp7zmK.mjs → pull-request-vOhLNmIb.mjs} +1 -1
  103. package/dist/pull-requests.mjs +1 -1
  104. package/dist/react-native-android-chm1DMlA.mjs +921 -0
  105. package/dist/react-native-build-oAw6gQeW.mjs +239 -0
  106. package/dist/react-native-doctor-DYXoh-6I.mjs +78 -0
  107. package/dist/react-native-ios-SCTK_CT5.mjs +595 -0
  108. package/dist/{recordings-BAWltsVt.mjs → recordings-Bk-VivUV.mjs} +1 -1
  109. package/dist/{reload-DvyWzVjJ.mjs → reload-D34GkY78.mjs} +17 -14
  110. package/dist/remote-BU9UyhCJ.mjs +192 -0
  111. package/dist/{remote-cache-BGg7aGIZ.mjs → remote-cache-Bsts6f6Y.mjs} +5 -5
  112. package/dist/result-BDoRk1k3.mjs +186 -0
  113. package/dist/{run-vhtDgpHO.mjs → run-qvff78hv.mjs} +3 -3
  114. package/dist/{server-bare-B9E7S1aR.mjs → server-bare-dWjAarki.mjs} +6 -6
  115. package/dist/server-command-7XIKlWtO.mjs +69 -0
  116. package/dist/{server-expo-DoBHhuzK.mjs → server-expo-CsYu2aKL.mjs} +107 -82
  117. package/dist/server-expo-DV43vep4.mjs +2 -0
  118. package/dist/settings-Bbztm4oV.mjs +1503 -0
  119. package/dist/{settings-Byp2AQ_E.mjs → settings-CT3E71-i.mjs} +6 -7
  120. package/dist/{state-lKh8shBp.d.mts → settings-vk3mJGSm.d.mts} +4 -2
  121. package/dist/settings.schema.json +156 -6
  122. package/dist/simslim-ChMfe8rg.mjs +149 -0
  123. package/dist/{slot-launch-C1nqh4mu.mjs → slot-launch-B9hn96wd.mjs} +6 -6
  124. package/dist/{stage-g_WqWeaE.mjs → stage-BNKKdDNc.mjs} +6 -3
  125. package/dist/start-CbQCSnXQ.mjs +2 -0
  126. package/dist/{start-D-IeOyoK.mjs → start-CdvcTaXr.mjs} +113 -77
  127. package/dist/{state-xnqpx3AD.mjs → state-Boo0IMJq.mjs} +4 -2
  128. package/dist/{state-BG-aAk4k.mjs → state-DI84A-uu.mjs} +29 -10
  129. package/dist/{stats-DY6ekz8F.mjs → stats-BIIDrjA8.mjs} +6 -6
  130. package/dist/{status-BEeL6IKz.mjs → status-BDGHwkCs.mjs} +3 -1
  131. package/dist/{status-DCjZeE-K.mjs → status-DARMkc9q.mjs} +80 -294
  132. package/dist/status-watch-DvmI3SmN.mjs +296 -0
  133. package/dist/{stim-desktop-Behws_fi.mjs → stim-desktop-8GQX2OV8.mjs} +1 -1
  134. package/dist/{stop-DIJtD2GL.mjs → stop-CCZS-C9m.mjs} +2 -2
  135. package/dist/stop-WQPQHLuy.mjs +72 -0
  136. package/dist/{stop-BTjs3xbc.mjs → stop-iNemHsJC.mjs} +81 -33
  137. package/dist/supervisor-run.d.mts +7 -2
  138. package/dist/supervisor-run.mjs +32 -20
  139. package/dist/{support-Bwvbrq5-.mjs → support-CnE_SIjt.mjs} +17 -7
  140. package/dist/{support-BaayeYCf.mjs → support-DCco5fut.mjs} +61 -8
  141. package/dist/swiftpm-macos-afeNUExW.mjs +119 -0
  142. package/dist/{native-run-B06FkmZx.mjs → tailnet-CoJUb2tB.mjs} +113 -6
  143. package/dist/toolchain-BIupIro1.mjs +2 -0
  144. package/dist/{toolchain-Bnyd_K3A.mjs → toolchain-DqyDXs30.mjs} +225 -315
  145. package/dist/{trigger-CJMhwXe8.mjs → trigger-C0MWBi8O.mjs} +3 -3
  146. package/dist/trigger-SHLOWJ4O.mjs +2 -0
  147. package/dist/{warm-progress-CjB-MP6H.mjs → warm-progress-C3ASD6z5.mjs} +134 -37
  148. package/dist/{status-Dcr0Qexd.mjs → watchman-CQl51YZu.mjs} +75 -7
  149. package/dist/web-B3eldHFH.mjs +123 -0
  150. package/dist/web-BLXpOe6b.mjs +2 -0
  151. package/dist/web-run.mjs +10 -10
  152. package/dist/{workspace-state-C59dqG-Z.mjs → workspace-state-CSIFuuDa.mjs} +33 -33
  153. package/dist/{workspaces-xXItD8Dj.mjs → workspaces-D2xSHBDY.mjs} +1297 -1211
  154. package/dist/{worktree-BSYlofkq.mjs → worktree-BxDqaTev.mjs} +102 -63
  155. package/dist/worktree-q7_OZNY0.mjs +2 -0
  156. package/package.json +8 -6
  157. package/shim/bundle-response.cjs +2 -1
  158. package/shim/bundle-response.d.cts +1 -1
  159. package/shim/expo-metro-config.cjs +1 -1
  160. package/shim/native-android.gradle +101 -0
  161. package/dist/android-BKA16DTf.mjs +0 -4
  162. package/dist/android-BvwuMUJ9.mjs +0 -3572
  163. package/dist/guide-status-Bhf9SJXc.mjs +0 -125
  164. package/dist/ios-CPdkoKaa.mjs +0 -3953
  165. package/dist/ios-DNpdygiv.mjs +0 -7
  166. package/dist/macos-Ck7OBMNY.mjs +0 -2
  167. package/dist/metro-store-BAHFcJm6.mjs +0 -106
  168. package/dist/plan-placement-CtxkCO54.mjs +0 -1859
  169. package/dist/project-C3ChRLZy.mjs +0 -212
  170. package/dist/projects-DD7dBTCj.mjs +0 -92
  171. package/dist/server-expo-Db9HBUoa.mjs +0 -2
  172. package/dist/settings-CQyMElEP.mjs +0 -701
  173. package/dist/start-NTc1Ifqs.mjs +0 -2
  174. package/dist/stop-BGhKMOuH.mjs +0 -48
  175. package/dist/trigger-DWAq9hkw.mjs +0 -2
  176. package/dist/web-B6_bFDn7.mjs +0 -2
  177. package/dist/worktree-BJfnU6r5.mjs +0 -2
  178. package/dist/worktree-CsKYexJM.mjs +0 -753
@@ -0,0 +1,296 @@
1
+ import { t as getExecutor } from "./exec-B81Qodjn.mjs";
2
+ import { d as androidToolPath } from "./android-BsYNR81W.mjs";
3
+ import { b as parseSimctlList } from "./ios-CXLIUxXJ.mjs";
4
+ import { n as easMachineStateRoot } from "./eas-session-ledger-DZlC-Fq3.mjs";
5
+ import { readdirSync, watch } from "node:fs";
6
+ import { join } from "node:path";
7
+ //#region src/status-watch.ts
8
+ const WATCH_FALLBACK_MS = 3e4;
9
+ const WATCH_LIGHT_INTERVAL_MS = 15e3;
10
+ const WATCH_SIMCTL_INTERVAL_MS = 5e3;
11
+ const ADB_RESTART_MIN_MS = 1e3;
12
+ const ADB_RESTART_MAX_MS = 6e4;
13
+ const SIMCTL_TIMEOUT_MS = 1e4;
14
+ /**
15
+ * Coalesces change signals into refreshes: a burst of full triggers runs `run('full')` once after `debounceMs` of
16
+ * quiet, at most one run is in flight, and a trigger that arrives during a run causes exactly one more run after it.
17
+ * A `light` trigger runs `run('light')` no sooner than `debounceMs` from the trigger and `lightIntervalMs` from the
18
+ * start of the previous run, unless a queued full run covers it; later light triggers do not postpone it.
19
+ */
20
+ function createRefreshScheduler({ debounceMs, lightIntervalMs = 0, run }) {
21
+ let timer = null;
22
+ let queued = "full";
23
+ let running = false;
24
+ let pending = null;
25
+ let stopped = false;
26
+ let lastRunAt = -Infinity;
27
+ const fire = async () => {
28
+ timer = null;
29
+ running = true;
30
+ lastRunAt = Date.now();
31
+ try {
32
+ await run(queued);
33
+ } finally {
34
+ running = false;
35
+ if (pending) {
36
+ const next = pending;
37
+ pending = null;
38
+ trigger(next);
39
+ }
40
+ }
41
+ };
42
+ function trigger(kind = "full", delayMs = debounceMs) {
43
+ if (stopped) return;
44
+ if (running) {
45
+ pending = pending === "full" ? "full" : kind;
46
+ return;
47
+ }
48
+ if (kind === "light") {
49
+ if (timer) return;
50
+ queued = "light";
51
+ timer = setTimeout(() => void fire(), Math.max(delayMs, lastRunAt + lightIntervalMs - Date.now()));
52
+ return;
53
+ }
54
+ if (timer) clearTimeout(timer);
55
+ queued = "full";
56
+ timer = setTimeout(() => void fire(), delayMs);
57
+ }
58
+ return {
59
+ trigger,
60
+ stop() {
61
+ stopped = true;
62
+ if (timer) clearTimeout(timer);
63
+ timer = null;
64
+ }
65
+ };
66
+ }
67
+ /**
68
+ * Which refresh a change to `name` in a watched `$STIM_HOME` directory needs: `light` for a log append or a running
69
+ * build's detail, which can change only the log-derived fields, `full` for anything else that can change the payload,
70
+ * null for none. A null name means the platform did not report one.
71
+ */
72
+ function statusChange(dir, name) {
73
+ if (dir === "archive" && (name?.startsWith(".incoming-") || name?.startsWith(".removing-") || name?.startsWith(".lock"))) return null;
74
+ if (name?.includes(".lock")) return null;
75
+ if (dir === "logs") return "light";
76
+ if (name === null) return "full";
77
+ switch (dir) {
78
+ case "home":
79
+ if (name === "maintenance") return "light";
80
+ return name.startsWith("config.json") || name === "archive" || name === "workspaces" || name === "device-locks" ? "full" : null;
81
+ case "workspace":
82
+ if (name.startsWith("build-detail.json")) return "light";
83
+ return name.startsWith("state.json") || name.startsWith("ended-agents.json") || name === "logs" ? "full" : null;
84
+ case "maintenance": return name === "state.json" ? "light" : null;
85
+ case "eas": return name.startsWith("sessions.json") ? "full" : null;
86
+ default: return "full";
87
+ }
88
+ }
89
+ /** A stable summary of every simulator's state, or null when the listing is unreadable. */
90
+ function simulatorSignature(simctlJson) {
91
+ try {
92
+ return parseSimctlList(simctlJson, { includeUnavailable: true }).map((sim) => `${sim.udid}:${sim.state}`).toSorted().join(",");
93
+ } catch {
94
+ return null;
95
+ }
96
+ }
97
+ /**
98
+ * Calls `onChange` when anything `stim status` reports may have changed:
99
+ * `$STIM_HOME` files, adb device arrivals and departures, and simulator
100
+ * state. simctl has no push API reachable from Node, so it is polled.
101
+ */
102
+ function watchStatusSources({ home, onChange, platform = process.platform, simctlIntervalMs = WATCH_SIMCTL_INTERVAL_MS }) {
103
+ let stopped = false;
104
+ const watchers = /* @__PURE__ */ new Map();
105
+ const desiredDirs = () => {
106
+ const dirs = /* @__PURE__ */ new Map([
107
+ [home, "home"],
108
+ [join(home, "archive"), "archive"],
109
+ [join(home, "workspaces"), "workspaces"],
110
+ [join(home, "maintenance"), "maintenance"],
111
+ [join(home, "device-locks"), "leases"],
112
+ [easMachineStateRoot(), "eas"]
113
+ ]);
114
+ let names = [];
115
+ try {
116
+ names = readdirSync(join(home, "workspaces"));
117
+ } catch {}
118
+ for (const name of names) {
119
+ const dir = join(home, "workspaces", name);
120
+ dirs.set(dir, "workspace");
121
+ dirs.set(join(dir, "logs"), "logs");
122
+ }
123
+ return dirs;
124
+ };
125
+ const reconcile = () => {
126
+ if (stopped) return;
127
+ const desired = desiredDirs();
128
+ for (const [dir, watcher] of watchers) if (!desired.has(dir)) {
129
+ watcher.close();
130
+ watchers.delete(dir);
131
+ }
132
+ for (const [dir, kind] of desired) {
133
+ if (watchers.has(dir)) continue;
134
+ try {
135
+ const watcher = watch(dir, (_event, name) => {
136
+ const change = statusChange(kind, name === null ? null : String(name));
137
+ if (!change) return;
138
+ if (kind === "home" || kind === "workspaces" || kind === "workspace") {
139
+ listingStaleBefore = Date.now();
140
+ reconcile();
141
+ }
142
+ onChange(change);
143
+ });
144
+ watcher.on("error", () => {
145
+ watcher.close();
146
+ watchers.delete(dir);
147
+ });
148
+ watchers.set(dir, watcher);
149
+ } catch {}
150
+ }
151
+ };
152
+ const adb = trackAdbDevices(() => onChange("full"));
153
+ let simctl = null;
154
+ let listing = null;
155
+ let listingStaleBefore = 0;
156
+ let simTimer = null;
157
+ if (platform === "darwin") {
158
+ let last = null;
159
+ const check = () => {
160
+ if (simctl || stopped) return;
161
+ const startedAt = Date.now();
162
+ let out = "";
163
+ const child = getExecutor().spawn("xcrun", [
164
+ "simctl",
165
+ "list",
166
+ "devices",
167
+ "--json"
168
+ ], {
169
+ stdio: [
170
+ "ignore",
171
+ "pipe",
172
+ "ignore"
173
+ ],
174
+ timeout: SIMCTL_TIMEOUT_MS,
175
+ killSignal: "SIGKILL"
176
+ });
177
+ simctl = child;
178
+ child.stdout?.on("data", (chunk) => out += chunk.toString());
179
+ child.on("error", () => {
180
+ simctl = null;
181
+ });
182
+ child.on("close", () => {
183
+ simctl = null;
184
+ const signature = simulatorSignature(out);
185
+ if (signature === null || stopped) return;
186
+ listing = {
187
+ at: startedAt,
188
+ json: out
189
+ };
190
+ if (last !== null && signature !== last) onChange("full");
191
+ last = signature;
192
+ });
193
+ };
194
+ check();
195
+ simTimer = setInterval(check, simctlIntervalMs);
196
+ }
197
+ reconcile();
198
+ return {
199
+ reconcile,
200
+ simulatorListing: () => listing && listing.at > listingStaleBefore && Date.now() - listing.at <= 2 * simctlIntervalMs ? listing.json : null,
201
+ stop() {
202
+ stopped = true;
203
+ for (const watcher of watchers.values()) watcher.close();
204
+ watchers.clear();
205
+ if (simTimer) clearInterval(simTimer);
206
+ simctl?.kill("SIGKILL");
207
+ adb.stop();
208
+ }
209
+ };
210
+ }
211
+ /**
212
+ * Runs `adb track-devices` and calls `onChange` on every device list it
213
+ * reports. adb exits when its server restarts, so the tracker restarts with a
214
+ * doubling delay that resets once a run lasts a minute.
215
+ */
216
+ function trackAdbDevices(onChange) {
217
+ const exec = getExecutor();
218
+ const resolved = androidToolPath("adb");
219
+ const adb = resolved === "adb" ? exec.findExecutable("adb") : resolved;
220
+ if (!adb) return { stop() {} };
221
+ let child = null;
222
+ let restart = null;
223
+ let delay = ADB_RESTART_MIN_MS;
224
+ let stopped = false;
225
+ const start = () => {
226
+ restart = null;
227
+ if (stopped) return;
228
+ const startedAt = Date.now();
229
+ const current = exec.spawn(adb, ["track-devices"], { stdio: [
230
+ "ignore",
231
+ "pipe",
232
+ "ignore"
233
+ ] });
234
+ child = current;
235
+ current.stdout?.on("data", () => onChange());
236
+ current.on("error", () => {});
237
+ current.on("close", () => {
238
+ if (child === current) child = null;
239
+ if (stopped) return;
240
+ if (Date.now() - startedAt >= ADB_RESTART_MAX_MS) delay = ADB_RESTART_MIN_MS;
241
+ restart = setTimeout(start, delay);
242
+ delay = Math.min(delay * 2, ADB_RESTART_MAX_MS);
243
+ });
244
+ };
245
+ start();
246
+ return { stop() {
247
+ stopped = true;
248
+ if (restart) clearTimeout(restart);
249
+ child?.kill("SIGTERM");
250
+ } };
251
+ }
252
+ function ownerKey(owner) {
253
+ return JSON.stringify([
254
+ owner.kind,
255
+ owner.name,
256
+ owner.workspace,
257
+ owner.slot ?? null,
258
+ owner.id,
259
+ owner.owned
260
+ ]);
261
+ }
262
+ function ownerSteady(before, after) {
263
+ return Math.abs(after.cpuPercent - before.cpuPercent) < 5 && Math.abs(after.memoryMb - before.memoryMb) < 16;
264
+ }
265
+ /**
266
+ * The payload `status --watch --json` prints. A usage figure keeps its value from the `previous` printed payload until it moves by a step: an owner's `cpuPercent`
267
+ * and `memoryMb`, an environment's `memoryMb` and `capacity.committedMb`. An owner's row is kept whole, `residentMb`
268
+ * and `processes` included, until one of its two figures moves, so a Mac whose processes only jitter prints no new line.
269
+ */
270
+ function watchPayload(previous, next) {
271
+ const payload = { ...next };
272
+ if (!previous) return payload;
273
+ if (previous.machine && next.machine && previous.machine.memorySource === next.machine.memorySource) {
274
+ const before = new Map(previous.machine.owners.map((owner) => [ownerKey(owner), owner]));
275
+ payload.machine = {
276
+ ...next.machine,
277
+ owners: next.machine.owners.map((owner) => {
278
+ const old = before.get(ownerKey(owner));
279
+ return old && ownerSteady(old, owner) ? old : owner;
280
+ })
281
+ };
282
+ }
283
+ const memory = new Map(previous.environments.map((env) => [env.path, env]));
284
+ payload.environments = payload.environments.map((env) => {
285
+ const old = memory.get(env.path);
286
+ return old && old.memorySource === env.memorySource && Math.abs(env.memoryMb - old.memoryMb) < 16 ? {
287
+ ...env,
288
+ memoryMb: old.memoryMb
289
+ } : env;
290
+ });
291
+ const { capacity } = previous;
292
+ if (capacity.overCapacity === next.capacity.overCapacity && capacity.liveCount === next.capacity.liveCount && capacity.totalMemoryMb === next.capacity.totalMemoryMb && Math.abs(next.capacity.committedMb - capacity.committedMb) < 16) payload.capacity = capacity;
293
+ return payload;
294
+ }
295
+ //#endregion
296
+ export { watchStatusSources as a, watchPayload as i, WATCH_LIGHT_INTERVAL_MS as n, createRefreshScheduler as r, WATCH_FALLBACK_MS as t };
@@ -1,4 +1,4 @@
1
- import { t as getExecutor } from "./exec-DWhCZDGm.mjs";
1
+ import { t as getExecutor } from "./exec-B81Qodjn.mjs";
2
2
  //#region src/devices/stim-desktop.ts
3
3
  const LAUNCH_SERVICES_LOOKUP = `ObjC.import('AppKit'); const url = $.NSWorkspace.sharedWorkspace.URLForApplicationWithBundleIdentifier('dev.stim.desktop'); url.isNil() ? '' : url.path.js`;
4
4
  const SCHEME_HANDLER_LOOKUP = `ObjC.import('AppKit'); const url = $.NSWorkspace.sharedWorkspace.URLForApplicationToOpenURL($.NSURL.URLWithString('stim-desktop://workspace')); url.isNil() ? '' : url.path.js`;
@@ -1,3 +1,3 @@
1
- import { m as resolveSupervisorTarget } from "./workspaces-xXItD8Dj.mjs";
2
- import { a as resolveCollectorTargets, c as stopWorkspaceNow, i as readSupervisorState, n as clearSupervisorState, o as runStop, r as readCollectorState, s as stopCommand, t as clearCollectorState } from "./stop-BTjs3xbc.mjs";
1
+ import { P as resolveSupervisorTarget } from "./workspaces-D2xSHBDY.mjs";
2
+ import { a as resolveCollectorTargets, c as stopWorkspaceNow, i as readSupervisorState, n as clearSupervisorState, o as runStop, r as readCollectorState, s as stopCommand, t as clearCollectorState } from "./stop-iNemHsJC.mjs";
3
3
  export { clearCollectorState, clearSupervisorState, stopCommand as default, readCollectorState, readSupervisorState, resolveCollectorTargets, resolveSupervisorTarget, runStop, stopWorkspaceNow };
@@ -0,0 +1,72 @@
1
+ import { n as ownership_claim_exports, t as process_identity_exports } from "./process-identity-Biv3arCO.mjs";
2
+ import { c as writeWorkspaceState, x as workspaceDir } from "./workspace-state-CSIFuuDa.mjs";
3
+ import { m as runningBundleInstances } from "./workspaces-D2xSHBDY.mjs";
4
+ import { a as withWorkspaceProcessLock } from "./named-ports-C2lvoPsb.mjs";
5
+ import { a as macosRuntimeClaim, o as requiredMacosRecord } from "./state-Boo0IMJq.mjs";
6
+ import { n as stopHostedMacos } from "./hosted-macos-Aziw9T_9.mjs";
7
+ import { readMacosRecord } from "@stim-cli/core/state";
8
+ //#region src/macos/stop.ts
9
+ const APP_EXIT_MS = 5e3;
10
+ const SUPERVISOR_EXIT_MS = 12e3;
11
+ function signal(pid, name) {
12
+ try {
13
+ process.kill(pid, name);
14
+ } catch (error) {
15
+ if (error.code !== "ESRCH") throw error;
16
+ }
17
+ }
18
+ async function stopProcesses(records, waitMs = APP_EXIT_MS) {
19
+ let live = [...new Map(records.map((record) => [record.pid, record])).values()];
20
+ for (const name of ["SIGTERM", "SIGKILL"]) {
21
+ live = live.filter((record) => {
22
+ const identity = (0, process_identity_exports.inspectProcessIdentity)(record);
23
+ if (identity === "gone" || identity === "different") return false;
24
+ if (identity !== "same") throw Object.assign(/* @__PURE__ */ new Error(`Cannot verify macOS owner pid ${record.pid}; no signal was sent.`), { code: "STIM_MACOS_OWNER_UNVERIFIED" });
25
+ return true;
26
+ });
27
+ for (const record of live) signal(record.pid, name);
28
+ const exited = await Promise.all(live.map((record) => (0, process_identity_exports.waitForProcessExit)(record, waitMs)));
29
+ live = live.filter((_, index) => !exited[index]);
30
+ if (!live.length) return;
31
+ }
32
+ throw new Error(`Owned macOS process ${live.map((record) => record.pid).join(", ")} did not exit.`);
33
+ }
34
+ /** Stops every running copy of the workspace's owned app bundle, including copies LaunchServices started. */
35
+ async function stopBundleInstances(root) {
36
+ const instances = runningBundleInstances(root);
37
+ await stopProcesses(instances);
38
+ return instances.length > 0;
39
+ }
40
+ async function stopMacosAppHeld(root) {
41
+ const record = requiredMacosRecord(root);
42
+ if (record?.supervisor) await stopProcesses([record.supervisor], SUPERVISOR_EXIT_MS);
43
+ const app = readMacosRecord(root)?.app ?? record?.app;
44
+ if (app) await stopProcesses([app]);
45
+ const instances = runningBundleInstances(root);
46
+ await stopProcesses(instances);
47
+ if (!record) return instances.length > 0;
48
+ const claims = (0, ownership_claim_exports.readClaimSet)(macosRuntimeClaim(root));
49
+ if (claims.unresolved.length || claims.live.length) throw Object.assign(/* @__PURE__ */ new Error("The macOS runtime claim cannot be released; inspect its recorded owner before cleanup."), { code: "STIM_MACOS_OWNER_UNVERIFIED" });
50
+ return true;
51
+ }
52
+ /** Stops the workspace's app here, or its session on the hosting Mac, which must confirm the session stopped. */
53
+ function stopMacosApp(root) {
54
+ const stop = async () => {
55
+ const record = requiredMacosRecord(root);
56
+ if (!record?.host) return stopMacosAppHeld(root);
57
+ await stopHostedMacos(root, record.host);
58
+ const { host: _host, hostLaunched: _launched, ...stopped } = record;
59
+ writeWorkspaceState(root, { macos: {
60
+ ...stopped,
61
+ supervisor: void 0
62
+ } });
63
+ return true;
64
+ };
65
+ return withWorkspaceProcessLock(workspaceDir(root), "macos-launch", stop, {
66
+ external: true,
67
+ waitMs: 0,
68
+ ownerPurpose: "stop macOS app"
69
+ });
70
+ }
71
+ //#endregion
72
+ export { stopMacosApp as n, stopMacosAppHeld as r, stopBundleInstances as t };
@@ -1,20 +1,22 @@
1
1
  import { n as ownership_claim_exports, t as process_identity_exports } from "./process-identity-Biv3arCO.mjs";
2
- import { f as refuseNoProject, l as phaseLine, p as releasedLeaseFact, u as plural } from "./command-output-CBtzyIgF.mjs";
3
- import { B as parseDeviceSlotKey, D as withConfigLock$1, E as upsertProject, H as projectDeviceSlots, V as parseDeviceSlotOption, W as validateDeviceSlot, o as clearSupervisor, p as getProject, z as deviceSlotPlatforms } from "./config-CgYMahz7.mjs";
4
- import { o as findCommandWorkspace } from "./project-C3ChRLZy.mjs";
5
- import { l as resolveProjectMetro, o as pidExists, r as killMetroTree, u as signalProcessTree } from "./metro-Dh8T4Ytd.mjs";
6
- import { a as updateWorkspaceState, c as writeWorkspaceState, n as clearWorkspaceStateKeys, o as withWorkspaceStateLock, r as readWorkspaceState$1, v as workspaceDir } from "./workspace-state-C59dqG-Z.mjs";
7
- import { Bt as clearWorkspaceLaunches, Ht as readMetroTunnel, Rt as clearManagedMetroTunnel, Vt as clearWorkspaceSupervisor, Wt as readRemoteSession, b as withdrawDevServerStopRequest, g as logVanishedSupervisor, v as requestDevServerStop, zt as clearRemoteSession } from "./budget-DIhL2BKx.mjs";
8
- import { D as teardownOwnedBrowser, E as teardownOwnedAvd, O as teardownOwnedIosSim, g as stopTunnel, m as resolveSupervisorTarget } from "./workspaces-xXItD8Dj.mjs";
9
- import { n as fileLeaseIo, u as releaseWorkspaceLeases } from "./device-lease-Co-EwifR.mjs";
10
- import { a as withWorkspaceProcessLock } from "./named-ports-BFIz1Shz.mjs";
11
- import { i as stopHostedAndroid } from "./hosted-android-Bv285t_t.mjs";
12
- import { f as resolveEasCliBin } from "./remote-cache-BGg7aGIZ.mjs";
13
- import { i as endRecordedSession } from "./device-remote-tHbd1fKy.mjs";
14
- import { a as decideStopAction, c as nativeRunWaitNotice, i as clearNativeRunCancel, l as requestNativeRunCancel, n as NATIVE_RUN_WAIT_MS, o as describeNativeRunHolder, s as nativeRunHolder, t as NATIVE_RUN_LOCK } from "./native-run-B06FkmZx.mjs";
15
- import { n as verifyCollectorOwnership } from "./ownership-D6Se7gg_.mjs";
16
- import { r as stopHostedIos } from "./hosted-ios-D5mNmtj4.mjs";
17
- import { t as stopMacosApp } from "./stop-BGhKMOuH.mjs";
2
+ import { n as debugLog } from "./exec-B81Qodjn.mjs";
3
+ import { B as parseDeviceSlotKey, D as withConfigLock$1, E as upsertProject, H as projectDeviceSlots, V as parseDeviceSlotOption, W as validateDeviceSlot, o as clearSupervisor, p as getProject, z as deviceSlotPlatforms } from "./config-D0LhTY3S.mjs";
4
+ import { n as fileLeaseIo, u as releaseWorkspaceLeases } from "./device-lease-DHOBXGgS.mjs";
5
+ import { a as updateWorkspaceState, c as writeWorkspaceState, n as clearWorkspaceStateKeys, o as withWorkspaceStateLock, r as readWorkspaceState$1, x as workspaceDir } from "./workspace-state-CSIFuuDa.mjs";
6
+ import { f as refuseNoProject, l as phaseLine, p as releasedLeaseFact, u as plural } from "./command-output-BjPN99ZB.mjs";
7
+ import { A as clearWorkspaceSupervisor, D as clearManagedMetroTunnel, N as readRemoteSession, O as clearRemoteSession, j as readMetroTunnel, k as clearWorkspaceLaunches, n as findCommandWorkspace, w as MODE_COMMAND } from "./project-B8_duAzW.mjs";
8
+ import { d as signalProcessTree, r as killMetroTree, s as pidExists, u as resolveProjectMetro } from "./metro-D_j9sN-B.mjs";
9
+ import { D as stopTunnel, P as resolveSupervisorTarget, b as teardownOwnedIosSim, v as teardownOwnedAvd, y as teardownOwnedBrowser } from "./workspaces-D2xSHBDY.mjs";
10
+ import { _t as withdrawDevServerStopRequest, ht as requestDevServerStop, pt as logVanishedSupervisor } from "./budget-BONq9vXY.mjs";
11
+ import { a as withWorkspaceProcessLock } from "./named-ports-C2lvoPsb.mjs";
12
+ import { _ as nativeRunHolder, d as NATIVE_RUN_LOCK, f as NATIVE_RUN_WAIT_MS, g as describeNativeRunHolder, h as decideStopAction, m as clearNativeRunCancel, v as nativeRunWaitNotice, y as requestNativeRunCancel } from "./tailnet-CoJUb2tB.mjs";
13
+ import { f as resolveEasCliBin } from "./remote-cache-Bsts6f6Y.mjs";
14
+ import { t as macosAppPresent } from "./state-Boo0IMJq.mjs";
15
+ import { i as stopHostedAndroid } from "./hosted-android-DQ-Agsd8.mjs";
16
+ import { i as endRecordedSession } from "./device-remote-Bmg42063.mjs";
17
+ import { n as verifyCollectorOwnership } from "./ownership-DXzMMSx6.mjs";
18
+ import { r as stopHostedIos } from "./hosted-ios-DIxFnYp-.mjs";
19
+ import { n as stopMacosApp } from "./stop-WQPQHLuy.mjs";
18
20
  import { hostedAndroidDeviceName, hostedAndroidRecords, hostedIosRecords, hostedNativeRecords, isJsonObject, parseHostedNativePlacement } from "@stim-cli/core/state";
19
21
  import chalk from "chalk";
20
22
  //#region src/commands/stop.ts
@@ -249,10 +251,9 @@ async function stopWorkspaceHostedNative(root, slot, report, failed) {
249
251
  };
250
252
  }
251
253
  async function stopWorkspaceMacos(root, failed, enabled) {
252
- if (enabled === false || readWorkspaceState$1(root)?.macos === void 0) return void 0;
254
+ if (enabled === false || !macosAppPresent(root)) return void 0;
253
255
  try {
254
- await stopMacosApp(root);
255
- return { status: "stopped" };
256
+ return await stopMacosApp(root) ? { status: "stopped" } : void 0;
256
257
  } catch (error) {
257
258
  const reason = error instanceof Error ? error.message : String(error);
258
259
  failed(reason);
@@ -262,7 +263,7 @@ async function stopWorkspaceMacos(root, failed, enabled) {
262
263
  };
263
264
  }
264
265
  }
265
- async function stopWorkspace({ root, project = void 0, state = void 0, collectors = void 0, signalCollector = (pid) => signalProcessTree(pid), verifyCollector = verifyCollectorOwnership, clearCollectors = clearCollectorState, isAlive = pidExists, killGroup = killMetroTree, signalServer = (pid) => signalProcessTree(pid), inspectIdentity = process_identity_exports.inspectProcessIdentity, waitForDeath = void 0, waitMs = DEFAULT_WAIT_MS, resolveMetro = resolveProjectMetro, teardownIos = teardownOwnedIosSim, teardownAvd = teardownOwnedAvd, teardownBrowser = (projectRoot) => teardownOwnedBrowser(projectRoot), stopMacos, hostedSlot, remoteDevice = void 0, teardownRemoteSession = defaultTeardownRemoteSession, metroTunnel = void 0, stopMetroTunnel = stopTunnel, releaseLeases = releaseWorkspaceLeases, freePort = defaultFreePort, clearRegistration = defaultClearRegistration, clearState = clearSupervisorState, report = (line) => console.error(line) }) {
266
+ async function stopWorkspace({ root, project = void 0, state = void 0, collectors = void 0, signalCollector = (pid) => signalProcessTree(pid), verifyCollector = verifyCollectorOwnership, clearCollectors = clearCollectorState, isAlive = pidExists, killGroup = killMetroTree, signalServer = (pid, group) => signalProcessTree(pid, "SIGTERM", { group }), inspectIdentity = process_identity_exports.inspectProcessIdentity, waitForDeath = void 0, waitMs = DEFAULT_WAIT_MS, resolveMetro = resolveProjectMetro, teardownIos = teardownOwnedIosSim, teardownAvd = teardownOwnedAvd, teardownBrowser = (projectRoot) => teardownOwnedBrowser(projectRoot), stopMacos, hostedSlot, remoteDevice = void 0, teardownRemoteSession = defaultTeardownRemoteSession, metroTunnel = void 0, stopMetroTunnel = stopTunnel, releaseLeases = releaseWorkspaceLeases, freePort = defaultFreePort, clearRegistration = defaultClearRegistration, clearState = clearSupervisorState, report = (line) => console.error(line) }) {
266
267
  const proj = project === void 0 ? getProject(root) : project;
267
268
  const sup = state === void 0 ? readSupervisorState(root) : state;
268
269
  const collectorRecords = collectors === void 0 ? readCollectorState(root) : collectors;
@@ -292,6 +293,7 @@ async function stopWorkspace({ root, project = void 0, state = void 0, collector
292
293
  let ok = true;
293
294
  let stillHolding = null;
294
295
  let keepPort = null;
296
+ debugLog.log("stop.phase.start", { phase: "supervisor" });
295
297
  const target = resolveSupervisorTarget({
296
298
  state: sup,
297
299
  record: proj?.supervisor ?? null,
@@ -329,6 +331,11 @@ async function stopWorkspace({ root, project = void 0, state = void 0, collector
329
331
  stillHolding = outcomes.supervisor.reason;
330
332
  }
331
333
  }
334
+ debugLog.log("stop.phase.end", {
335
+ phase: "supervisor",
336
+ status: outcomes.supervisor.status
337
+ });
338
+ debugLog.log("stop.phase.start", { phase: "collectors" });
332
339
  outcomes.collectors = await reapCollectors(root, collectorRecords, {
333
340
  isAlive,
334
341
  signal: signalCollector,
@@ -344,10 +351,22 @@ async function stopWorkspace({ root, project = void 0, state = void 0, collector
344
351
  } else if (outcomes.collectors.entries.some((entry) => entry.status === "failed")) {
345
352
  ok = false;
346
353
  stillHolding ??= "a collector could not be stopped";
347
- } else if (outcomes.collectors.entries.length && clearCollectors(root, collectorRecords) === false) {
348
- ok = false;
349
- stillHolding ??= "a replacement collector appeared during cleanup";
354
+ } else if (outcomes.collectors.entries.length) {
355
+ debugLog.log("stop.phase.start", { phase: "clear-collectors" });
356
+ const cleared = clearCollectors(root, collectorRecords);
357
+ debugLog.log("stop.phase.end", {
358
+ phase: "clear-collectors",
359
+ cleared
360
+ });
361
+ if (cleared === false) {
362
+ ok = false;
363
+ stillHolding ??= "a replacement collector appeared during cleanup";
364
+ }
350
365
  }
366
+ debugLog.log("stop.phase.end", {
367
+ phase: "collectors",
368
+ status: outcomes.collectors.status
369
+ });
351
370
  if (outcomes.supervisor.status === "stopped" || outcomes.supervisor.status === "timeout" || outcomes.supervisor.status === "unverified" || outcomes.supervisor.status === "failed") outcomes.metro = {
352
371
  status: "skipped",
353
372
  reason: "the supervisor owns the dev server on this port"
@@ -627,6 +646,7 @@ async function stopSupervisor(target, { root, killGroup, waiter, report }) {
627
646
  const reason = `supervisor pid ${target.pid} did not exit within ${Math.round(DEFAULT_WAIT_MS / 1e3)}s of SIGTERM`;
628
647
  report(chalk.red(phaseLine("stop", reason)));
629
648
  report(chalk.dim(phaseLine("", `inspect it with \`ps -p ${target.pid}\`, or signal it yourself: kill -9 -${target.pid}`)));
649
+ if (target.mode === "command-child") report(chalk.dim(phaseLine("", "metro.command runs in its own process group: once the supervisor is gone, run stim stop again to stop it")));
630
650
  return {
631
651
  status: "timeout",
632
652
  pid: target.pid,
@@ -659,7 +679,7 @@ async function stopMetro(port, root, { server, supervisorGone, signalServer, ins
659
679
  report(chalk.dim(phaseLine("metro", `sending SIGTERM to dev server pid ${serverPid} left by the supervisor`)));
660
680
  let signalled = false;
661
681
  try {
662
- signalled = signalServer(serverPid);
682
+ signalled = signalServer(serverPid, server?.mode === MODE_COMMAND);
663
683
  } catch {}
664
684
  if (signalled ? await waiter(serverPid, serverProcessToken) : ["gone", "different"].includes(inspectIdentity(serverRecord))) {
665
685
  report(chalk.green(phaseLine("metro", `dev server pid ${serverPid}`)));
@@ -731,11 +751,25 @@ function shutDownDevices(project, workspace, { teardownIos, teardownAvd, report
731
751
  reason: "Stim does not own this device"
732
752
  };
733
753
  report(chalk.dim(phaseLine("device", `${iosUdid} is not Stim-owned, leaving it running`)));
734
- } else device[iosKey] = reportDevice(iosUdid, teardownIos(iosUdid, {
735
- del: false,
736
- label: iosName,
737
- workspace
738
- }), report, "ios");
754
+ } else {
755
+ debugLog.log("stop.device.start", {
756
+ platform: "ios",
757
+ slot,
758
+ deviceId: iosUdid
759
+ });
760
+ const result = teardownIos(iosUdid, {
761
+ del: false,
762
+ label: iosName,
763
+ workspace
764
+ });
765
+ debugLog.log("stop.device.end", {
766
+ platform: "ios",
767
+ slot,
768
+ deviceId: iosUdid,
769
+ status: result.status
770
+ });
771
+ device[iosKey] = reportDevice(iosUdid, result, report, "ios");
772
+ }
739
773
  }
740
774
  const android = platforms.android;
741
775
  if (android?.avdName) {
@@ -747,10 +781,24 @@ function shutDownDevices(project, workspace, { teardownIos, teardownAvd, report
747
781
  reason: "Stim does not own this device"
748
782
  };
749
783
  report(chalk.dim(phaseLine("device", `${android.avdName} is not Stim-owned, leaving it running`)));
750
- } else device[androidKey] = reportDevice(android.avdName, teardownAvd(android.avdName, {
751
- del: false,
752
- workspace
753
- }), report, "android");
784
+ } else {
785
+ debugLog.log("stop.device.start", {
786
+ platform: "android",
787
+ slot,
788
+ deviceId: android.avdName
789
+ });
790
+ const result = teardownAvd(android.avdName, {
791
+ del: false,
792
+ workspace
793
+ });
794
+ debugLog.log("stop.device.end", {
795
+ platform: "android",
796
+ slot,
797
+ deviceId: android.avdName,
798
+ status: result.status
799
+ });
800
+ device[androidKey] = reportDevice(android.avdName, result, report, "android");
801
+ }
754
802
  }
755
803
  }
756
804
  return device;
@@ -1,4 +1,4 @@
1
- import { a as readPidFile, i as clearWorkspaceSupervisor, n as MODE_EXPO, o as writePidFile, r as clearExpoMetroTunnel, t as MODE_BARE } from "./state-lKh8shBp.mjs";
1
+ import { a as clearWorkspaceSupervisor, i as clearExpoMetroTunnel, n as MODE_BARE, o as readPidFile, r as MODE_EXPO, s as writePidFile, t as SettingsObject } from "./settings-vk3mJGSm.mjs";
2
2
  import { t as NdjsonWriter } from "./ndjson-Du3j0AQ9.mjs";
3
3
  import { DevServerStopRecord, DeviceActivity, DeviceIdleShutdownRecord, IOS_SIMULATOR_APPS } from "@stim-cli/core/state";
4
4
  //#region src/supervisor/types.d.ts
@@ -21,6 +21,7 @@ type ServerStarter = (opts: {
21
21
  tunnel?: boolean;
22
22
  resetCache?: boolean;
23
23
  onTunnelUrl?: ((url: string) => void) | null;
24
+ settings?: SettingsObject;
24
25
  }) => Promise<ServerHandle>;
25
26
  //#endregion
26
27
  //#region src/supervisor/idle-stop.d.ts
@@ -91,13 +92,17 @@ interface RunSupervisorOptions {
91
92
  isExpo?: (projectRoot: string) => boolean;
92
93
  startBare?: ServerStarter | null;
93
94
  startExpo?: ServerStarter | null;
95
+ startCommand?: ((opts: Parameters<ServerStarter>[0] & {
96
+ command: readonly string[];
97
+ }) => Promise<ServerHandle>) | null;
98
+ settings?: SettingsObject | null;
94
99
  now?: () => number;
95
100
  onExit?: (code: number) => void;
96
101
  attachSignals?: boolean;
97
102
  stderr?: (line: string) => void;
98
103
  watchman?: WatchmanCommand;
99
104
  }
100
- declare function runSupervisor({ root, port, tunnel, resetCache, idleStopMinutes, idleProbe, deviceIdleMinutes, deviceIdle, isExpo, startBare, startExpo, now, onExit, attachSignals, stderr, watchman }: RunSupervisorOptions): Promise<{
105
+ declare function runSupervisor({ root, port, tunnel, resetCache, idleStopMinutes, idleProbe, deviceIdleMinutes, deviceIdle, isExpo, startBare, startExpo, startCommand, settings: settingsOverride, now, onExit, attachSignals, stderr, watchman }: RunSupervisorOptions): Promise<{
101
106
  mode: string;
102
107
  server: ServerHandle | undefined;
103
108
  shutdown: (code: number, event: string, msg: string, stop?: DevServerStopRecord) => Promise<void>;