stim 1.16.0 → 1.17.1

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 (108) hide show
  1. package/dist/{activity-BxwdD81S.mjs → activity-IRvJY_ru.mjs} +23 -3
  2. package/dist/{agent-device-usage-output-Dpih1Tfa.mjs → agent-device-usage-output-BK41vnzN.mjs} +3 -3
  3. package/dist/{android-Ift4kVvt.mjs → android-B6rDWrTm.mjs} +681 -319
  4. package/dist/{android-B_NumVyQ.mjs → android-B_YNZy61.mjs} +7 -43
  5. package/dist/{android-r2rBL8kV.mjs → android-DE4v59Mb.mjs} +12 -3
  6. package/dist/{android-cas-8U4DPqeR.mjs → android-cas-ZFpjJBL_.mjs} +6 -5
  7. package/dist/{app-install-CKWdbXIX.mjs → app-install-VPkI5WEg.mjs} +6 -5
  8. package/dist/{budget-DjrURfQn.mjs → budget-CUDz870h.mjs} +62 -25
  9. package/dist/{build-plan-DjeSb2Xl.mjs → build-plan-BJSXzsCL.mjs} +345 -86
  10. package/dist/{idle-DYVymG92.mjs → build-progress-D0WkjCPd.mjs} +170 -233
  11. package/dist/{build-slots-rUk77xqh.mjs → build-slots-41v_d2x9.mjs} +35 -25
  12. package/dist/{cli-BkYZ_6hy.mjs → cli-BLQqDGlX.mjs} +17 -17
  13. package/dist/cli.mjs +1 -1
  14. package/dist/{client-BTY686R6.mjs → client-CxNotugx.mjs} +313 -44
  15. package/dist/collector-run.mjs +3 -3
  16. package/dist/{command-output-DnJMdEY-.mjs → command-output-CDAIFD-8.mjs} +3 -3
  17. package/dist/{created-devices-D5owjN4C.mjs → created-devices-CpvzBL25.mjs} +1 -1
  18. package/dist/{dependency-state-B6cMXZab.mjs → dependency-state-CLcBHH2M.mjs} +1 -1
  19. package/dist/{deps-urbmfXig.mjs → deps-CVxlsRFn.mjs} +1 -1
  20. package/dist/{dev-client-CroAIjjj.mjs → dev-client-B-XC4uRH.mjs} +2 -2
  21. package/dist/{device-DZQjF7H5.mjs → device-C8IOoJPv.mjs} +7 -7
  22. package/dist/device-capacity-ZC4XSZ4R.mjs +580 -0
  23. package/dist/device-host-worker.mjs +436 -154
  24. package/dist/{device-ios-BadmeL2V.mjs → device-ios-C2Um-XUR.mjs} +75 -44
  25. package/dist/{device-lease-D-hLqx1b.mjs → device-lease-DpoS36_u.mjs} +2 -2
  26. package/dist/{device-lease-run-CqjShqWW.mjs → device-lease-run-B8YltIH_.mjs} +3 -3
  27. package/dist/{device-pool-CUaEWMMm.mjs → device-pool-3o7s5lo4.mjs} +2 -2
  28. package/dist/{device-remote-D5WVWqOq.mjs → device-remote-hFSkpfQu.mjs} +12 -12
  29. package/dist/{doctor-Cimf-gGB.mjs → doctor-BF_435OC.mjs} +40 -58
  30. package/dist/{error-diagnostics-B9L4Ae0x.mjs → error-diagnostics-C-NMnhrk.mjs} +7 -7
  31. package/dist/{gc-Bqy4bjEu.mjs → gc-BsWKa_-d.mjs} +313 -30
  32. package/dist/{gradle-BsO1MdsH.mjs → gradle-bsBRy6i9.mjs} +3 -3
  33. package/dist/{guide-B_uyKTxW.mjs → guide-DC3n6-NH.mjs} +1776 -655
  34. package/dist/{guide-status-CjEZMAD4.mjs → guide-status-Du-ZQ3FD.mjs} +1 -1
  35. package/dist/hosted-android-DgU31wY3.mjs +32 -0
  36. package/dist/{hosted-client-a-WmqE2L.mjs → hosted-client-CdSoH-G6.mjs} +5 -6
  37. package/dist/hosted-ios-BTaVj4Jb.mjs +15 -0
  38. package/dist/{hosted-logs-D5g041Q3.mjs → hosted-logs-SxfBvSvc.mjs} +16 -16
  39. package/dist/{hosted-macos-BSZyIb3y.mjs → hosted-macos-CT9E3meL.mjs} +4 -4
  40. package/dist/{hosted-ios-CIWelIqf.mjs → hosted-native-CZLOrMrN.mjs} +77 -42
  41. package/dist/idle-BXG__1BA.mjs +176 -0
  42. package/dist/{idle-shutdown-CTsDnAde.mjs → idle-shutdown-IGyNV8Cr.mjs} +25 -17
  43. package/dist/{in-use-DvBw0Kxo.mjs → in-use-CvGOUn-x.mjs} +11 -11
  44. package/dist/{ios-aq3QoGI3.mjs → ios-C7JuT1bI.mjs} +173 -91
  45. package/dist/{ios-BaInn3sW.mjs → ios-DSfnPfse.mjs} +4 -4
  46. package/dist/{ios-device-FfCN_C8p.mjs → ios-device-pAN4t54-.mjs} +1 -1
  47. package/dist/ios-state-8L3lgum6.mjs +89 -0
  48. package/dist/{launch-verify-2DBRizjo.mjs → launch-verify-lTnWZikR.mjs} +2 -2
  49. package/dist/{logs-CHEVK8zD.mjs → logs-DNxKcok9.mjs} +36 -23
  50. package/dist/{macos-CSYEAFmF.mjs → macos-BEs8cwKx.mjs} +75 -34
  51. package/dist/macos-run.mjs +1 -1
  52. package/dist/maintenance-run.mjs +2 -2
  53. package/dist/{metro-BeK1ZPz1.mjs → metro-g8NqiJp-.mjs} +1 -1
  54. package/dist/{metro-gateway-BJ-rxQAB.mjs → metro-gateway-CHqK0p-a.mjs} +22 -3
  55. package/dist/{named-ports-Bk_YXEmE.mjs → named-ports-C8m8EQs2.mjs} +2 -2
  56. package/dist/{native-run-NaBrRFtQ.mjs → native-run-D1Q56rAD.mjs} +17 -6
  57. package/dist/{native-runtime-DBzRnEKS.mjs → native-runtime-YhaSxP4v.mjs} +6 -6
  58. package/dist/offload-worker.mjs +9 -9
  59. package/dist/{teardown-CL6vDs2Y.mjs → ownership-BMLqyBgJ.mjs} +603 -26
  60. package/dist/{ownership-DiA16AsN.mjs → ownership-DyRQru0q.mjs} +2 -2
  61. package/dist/{ports-R2k4Knz3.mjs → ports-4nYqxPWC.mjs} +2 -2
  62. package/dist/{prebuild-D2fsca_Z.mjs → prebuild-D7RUMkwN.mjs} +3 -3
  63. package/dist/{preview-DhVbvPAZ.mjs → preview-C_JjkEy4.mjs} +7 -7
  64. package/dist/{project-Du91Ilvc.mjs → project-D47Rh0ly.mjs} +7 -2
  65. package/dist/{recordings-BIXOzVnr.mjs → recordings-BNgsD1Tr.mjs} +1 -1
  66. package/dist/{reload-rM9jV1fz.mjs → reload-byOjE3P4.mjs} +62 -35
  67. package/dist/{remote-cache-C8-V13XN.mjs → remote-cache-Ci4DQYYj.mjs} +3 -3
  68. package/dist/{run-f4vI6EnK.mjs → run-CkE9y0nc.mjs} +1 -1
  69. package/dist/{server-bare-CpPGmnmO.mjs → server-bare-BkrSxSsZ.mjs} +1 -1
  70. package/dist/{server-expo-MC0rHlOC.mjs → server-expo-CHTyXD0G.mjs} +2 -2
  71. package/dist/server-expo-CeakhI8P.mjs +2 -0
  72. package/dist/{settings-oTSkkNrM.mjs → settings-Cfd-0r2d.mjs} +10 -9
  73. package/dist/{settings-ZkDzkzvB.mjs → settings-SR9OmW9h.mjs} +3 -3
  74. package/dist/settings.schema.json +123 -76
  75. package/dist/{simslim-B8-7AoTI.mjs → simslim-BWNrc3M3.mjs} +3 -3
  76. package/dist/{slot-launch-B8VrHxn1.mjs → slot-launch-DmjBmpQV.mjs} +8 -8
  77. package/dist/{start-CX8Ig-U2.mjs → start-BEP-XmEA.mjs} +1 -1
  78. package/dist/{start-ClDfwXa4.mjs → start-BHIersSm.mjs} +11 -11
  79. package/dist/{state-DcW6vOd_.mjs → state-BKa8wNN5.mjs} +1 -1
  80. package/dist/{state-3DwSQIBK.mjs → state-DyaNTgfR.mjs} +1 -1
  81. package/dist/{state-C1jB7Q8q.mjs → state-PeQiJuIu.mjs} +2 -2
  82. package/dist/{stats-CEZ_i5je.mjs → stats-Bq4fJOdB.mjs} +7 -4
  83. package/dist/{status-Cs7VLg9D.mjs → status-CqeWbIWW.mjs} +128 -38
  84. package/dist/{status-BuvBFeII.mjs → status-CzS4bPO9.mjs} +4 -4
  85. package/dist/{stop-Bffozsac.mjs → stop-Cj01je8D.mjs} +48 -27
  86. package/dist/{stop-B0Yxtmiy.mjs → stop-D03prQnW.mjs} +4 -4
  87. package/dist/{stop-Dt_dM16A.mjs → stop-DOxwkH4j.mjs} +2 -2
  88. package/dist/{stop-cause-CidxIqup.mjs → stop-cause-rhmGfwvt.mjs} +1 -1
  89. package/dist/supervisor-run.d.mts +7 -4
  90. package/dist/supervisor-run.mjs +26 -28
  91. package/dist/{support-D4aYJniO.mjs → support-BE-tbOP9.mjs} +45 -20
  92. package/dist/{support-CAP8qDUF.mjs → support-WvD__tSV.mjs} +7 -7
  93. package/dist/{toolchain-DkUflduf.mjs → toolchain-CdrxgUeA.mjs} +8 -8
  94. package/dist/{warm-progress-CpGv3Wb6.mjs → warm-progress-DF4JRvYI.mjs} +3 -3
  95. package/dist/watchman-APYjXSQa.mjs +48 -0
  96. package/dist/{web-DWUaMp8P.mjs → web-DddUdJzr.mjs} +12 -13
  97. package/dist/web-run.mjs +4 -4
  98. package/dist/{workspace-process-lock-BL7Q0HCO.mjs → workspace-process-lock-2mNzOlaB.mjs} +3 -2
  99. package/dist/{workspace-state-B8-aJ1_5.mjs → workspace-state-DqcCbpE7.mjs} +2 -2
  100. package/dist/{workspaces-BV4pdHFz.mjs → workspaces-Bcib97-6.mjs} +3 -3
  101. package/dist/{worktree-K-xj9Db0.mjs → worktree-AK_m-d_m.mjs} +141 -40
  102. package/dist/{worktree-jnG8Iph5.mjs → worktree-CsAjSmEo.mjs} +1 -1
  103. package/package.json +5 -5
  104. package/dist/device-capacity-BHwxNBlu.mjs +0 -136
  105. package/dist/ios-state-DS5UpYTH.mjs +0 -48
  106. package/dist/machines-BIBUdhpj.mjs +0 -265
  107. package/dist/ownership-hCkPzGmr.mjs +0 -571
  108. package/dist/server-expo-CVqO2c35.mjs +0 -2
@@ -1,24 +1,33 @@
1
1
  import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
2
  import { C as saveConfig, D as withConfigLock, H as projectDeviceSlots, _ as loadConfig, a as clearDevice, d as getConfigDir, w as setDevice, z as deviceSlotPlatforms } from "./config-D0DbpZtM.mjs";
3
- import { l as phaseLine } from "./command-output-DnJMdEY-.mjs";
4
- import { y as iosSimSlimProfileSetting } from "./settings-oTSkkNrM.mjs";
5
- import { y as workspaceId } from "./workspace-state-B8-aJ1_5.mjs";
6
- import { ct as dropParked, ft as parkedMaxSetting, ht as selectParked, pt as readParked, st as adoptParked } from "./android-B_NumVyQ.mjs";
7
- import { A as hostMemoryPressureAdvice, C as resolveIosCreation, O as configuredIosSimulatorViewer, S as resetIosPrivacy, _ as parseRuntimeVersion, b as renameIosSim, f as listIosDeviceTypes, h as ownedSimName, i as createOwnedIosSim, j as readHostMemoryPressure, l as iosSimulatorFailureAdvice, n as bootIosSim, p as listIosRuntimes, u as listAllIosSims, w as resolveOwnedIosSim, x as resetIosKeychain } from "./ios-BaInn3sW.mjs";
8
- import { c as teardownParkedIosSim } from "./teardown-CL6vDs2Y.mjs";
9
- import { t as withWorkspaceProcessLock } from "./workspace-process-lock-BL7Q0HCO.mjs";
10
- import { a as runtimeMismatch, n as deviceTypeMismatch } from "./device-capacity-BHwxNBlu.mjs";
3
+ import { n as ownership_claim_exports } from "./process-identity-BmRm1qV0.mjs";
4
+ import { l as phaseLine } from "./command-output-CDAIFD-8.mjs";
5
+ import { v as iosSimSlimProfileSetting } from "./settings-Cfd-0r2d.mjs";
6
+ import { y as workspaceId } from "./workspace-state-DqcCbpE7.mjs";
7
+ import { ct as dropParked, ft as parkedMaxSetting, ht as selectParked, pt as readParked, st as adoptParked } from "./android-B_YNZy61.mjs";
8
+ import { A as hostMemoryPressureAdvice, C as resolveIosCreation, O as configuredIosSimulatorViewer, S as resetIosPrivacy, _ as parseRuntimeVersion, b as renameIosSim, f as listIosDeviceTypes, h as ownedSimName, i as createOwnedIosSim, j as readHostMemoryPressure, l as iosSimulatorFailureAdvice, n as bootIosSim, p as listIosRuntimes, u as listAllIosSims, w as resolveOwnedIosSim, x as resetIosKeychain } from "./ios-DSfnPfse.mjs";
9
+ import { v as teardownParkedIosSim } from "./ownership-BMLqyBgJ.mjs";
10
+ import { t as withWorkspaceProcessLock } from "./workspace-process-lock-2mNzOlaB.mjs";
11
+ import { a as deviceTypeMismatch, c as runtimeMismatch, n as DeviceAdmissionRefusal, p as withDeviceBootAdmission } from "./device-capacity-ZC4XSZ4R.mjs";
11
12
  import chalk from "chalk";
12
13
  //#region src/engine/device-ios.ts
13
- function startIosBoot(udid, configure, label, out, simulatorApp) {
14
- const done = (async () => {
14
+ function startIosBoot(udid, configure, label, out, simulatorApp, root, deviceSlotWait) {
15
+ const done = withDeviceBootAdmission({
16
+ platform: "ios",
17
+ key: udid
18
+ }, async () => {
19
+ out(chalk.dim(phaseLine("device", `booting ${label} (${udid})`)));
15
20
  await bootIosSim(udid, {
16
21
  label,
17
22
  out,
18
23
  simulatorApp
19
24
  });
20
25
  await configure();
21
- })();
26
+ }, {
27
+ ...deviceSlotWait,
28
+ root,
29
+ out
30
+ });
22
31
  done.catch(() => {});
23
32
  return {
24
33
  udid,
@@ -34,7 +43,8 @@ async function ensureOwnedIosDevice({ record, projectPath, slot, settingsRoot, l
34
43
  const simslimProfile = iosSimSlimProfileSetting(settings, settingsRoot);
35
44
  if (record?.deviceUdid) {
36
45
  if (record.owned) {
37
- const resolved = resolveOwnedIosSim(record.deviceUdid);
46
+ const listed = listAllIosSims({ includeUnavailable: true });
47
+ const resolved = resolveOwnedIosSim(record.deviceUdid, listed);
38
48
  if (resolved.notOwned) note(chalk.yellow(`Note: recorded sim is now named "${resolved.notOwned}", not Stim-owned -- creating a fresh owned sim instead of booting it.`));
39
49
  else if (resolved.missing) {} else {
40
50
  const sim = resolved.sim;
@@ -48,7 +58,7 @@ async function ensureOwnedIosDevice({ record, projectPath, slot, settingsRoot, l
48
58
  model: deviceTypes.find((d) => d.identifier === sim.deviceTypeIdentifier)?.name ?? null,
49
59
  runtime: parseRuntimeVersion(sim.runtime)
50
60
  };
51
- const name = await withIosDeviceNameLock(() => renameToOwnedName(sim, label, model, projectPath));
61
+ const name = ownedIosName(sim, label, model, projectPath, listed) === sim.name ? sim.name : await withIosDeviceNameLock(() => renameToOwnedName(sim, label, model, projectPath));
52
62
  const updated = {
53
63
  deviceUdid: sim.udid,
54
64
  owned: true,
@@ -74,17 +84,15 @@ async function ensureOwnedIosDevice({ record, projectPath, slot, settingsRoot, l
74
84
  deviceType: model.model,
75
85
  runtime: model.runtime
76
86
  };
77
- if (sim.state !== "Booted") {
78
- out(chalk.dim(phaseLine("device", `booting ${name} (${sim.udid})`)));
79
- return {
80
- ...updated,
81
- booting: startIosBoot(sim.udid, configure, name, out, flags.simulatorApp),
82
- ...facts
83
- };
84
- }
87
+ if (sim.state !== "Booted") return {
88
+ ...updated,
89
+ booting: startIosBoot(sim.udid, configure, name, out, flags.simulatorApp, projectPath, flags.deviceSlotWait),
90
+ ...facts
91
+ };
85
92
  return {
86
93
  ...await configure(),
87
- ...facts
94
+ ...facts,
95
+ booted: sim.udid
88
96
  };
89
97
  }
90
98
  } else {
@@ -116,7 +124,6 @@ async function ensureOwnedIosDevice({ record, projectPath, slot, settingsRoot, l
116
124
  out
117
125
  })) : null;
118
126
  if (adopted) {
119
- out(chalk.dim(phaseLine("device", `booting ${adopted.deviceName} (${adopted.deviceUdid})`)));
120
127
  const booting = startIosBoot(adopted.deviceUdid, async () => {
121
128
  resetAdoptedSim(adopted.deviceUdid, out);
122
129
  await configureOwnedIosSim({
@@ -127,7 +134,7 @@ async function ensureOwnedIosDevice({ record, projectPath, slot, settingsRoot, l
127
134
  out,
128
135
  reconcileIosSimulator
129
136
  });
130
- }, adopted.deviceName, out, flags.simulatorApp);
137
+ }, adopted.deviceName, out, flags.simulatorApp, projectPath, flags.deviceSlotWait);
131
138
  return {
132
139
  ...adopted,
133
140
  booting,
@@ -160,7 +167,7 @@ async function ensureOwnedIosDevice({ record, projectPath, slot, settingsRoot, l
160
167
  profile: simslimProfile,
161
168
  out,
162
169
  reconcileIosSimulator
163
- }), created.name, out, flags.simulatorApp);
170
+ }), created.name, out, flags.simulatorApp, projectPath, flags.deviceSlotWait);
164
171
  return {
165
172
  ...newRecord,
166
173
  created: true,
@@ -172,8 +179,8 @@ async function ensureOwnedIosDevice({ record, projectPath, slot, settingsRoot, l
172
179
  function withIosDeviceNameLock(fn) {
173
180
  return withWorkspaceProcessLock(getConfigDir(), "ios-device-names", async () => fn(), { external: true });
174
181
  }
175
- function ownedIosNameSuffix(label, model, projectPath, udid) {
176
- const names = new Set(listAllIosSims({ includeUnavailable: true }).filter((sim) => sim.udid !== udid).map((sim) => sim.name));
182
+ function ownedIosNameSuffix(label, model, projectPath, udid, listed = listAllIosSims({ includeUnavailable: true })) {
183
+ const names = new Set(listed.filter((sim) => sim.udid !== udid).map((sim) => sim.name));
177
184
  for (const project of Object.values(loadConfig()?.projects ?? {})) for (const { platforms } of projectDeviceSlots(project)) {
178
185
  const record = platforms.ios;
179
186
  if (record?.deviceUdid !== udid && record?.deviceName) names.add(record.deviceName);
@@ -183,8 +190,11 @@ function ownedIosNameSuffix(label, model, projectPath, udid) {
183
190
  for (let attempt = 0; names.has(ownedSimName(label, model, suffix)); attempt++) suffix = ` ${workspaceId(projectPath)}${attempt ? `-${attempt}` : ""}`;
184
191
  return suffix;
185
192
  }
193
+ function ownedIosName(sim, label, model, projectPath, listed) {
194
+ return ownedSimName(label, model, ownedIosNameSuffix(label, model, projectPath, sim.udid, listed));
195
+ }
186
196
  function renameToOwnedName(sim, label, model, projectPath) {
187
- const wanted = ownedSimName(label, model, ownedIosNameSuffix(label, model, projectPath, sim.udid));
197
+ const wanted = ownedIosName(sim, label, model, projectPath);
188
198
  if (sim.name === wanted) return wanted;
189
199
  try {
190
200
  renameIosSim(sim.udid, wanted);
@@ -284,7 +294,7 @@ async function configureOwnedIosSim({ record, projectPath, slot, profile, out, r
284
294
  setDevice(projectPath, "ios", updated, slot);
285
295
  return updated;
286
296
  }
287
- async function ensureIosBooted({ device, simulatorApp, timeoutMs, pollMs, out }) {
297
+ async function ensureIosBooted({ device, projectPath, deviceSlotWait, simulatorApp, timeoutMs, pollMs, out }) {
288
298
  const udid = device?.deviceUdid;
289
299
  if (!udid) return {
290
300
  failed: true,
@@ -315,13 +325,15 @@ async function ensureIosBooted({ device, simulatorApp, timeoutMs, pollMs, out })
315
325
  try {
316
326
  await booting.done;
317
327
  } catch (e) {
318
- return {
319
- failed: true,
320
- reason: `Could not boot simulator ${udid}: ${e?.message || e}`
321
- };
328
+ return bootFailure(udid, e);
322
329
  }
323
330
  return ready();
324
331
  }
332
+ const readiness = device?.booted === udid ? ready() : null;
333
+ if (readiness?.ok) {
334
+ if (simulatorApp !== void 0) configuredIosSimulatorViewer(simulatorApp).open(udid);
335
+ return readiness;
336
+ }
325
337
  let resolved;
326
338
  try {
327
339
  resolved = resolveOwnedIosSim(udid);
@@ -341,24 +353,30 @@ async function ensureIosBooted({ device, simulatorApp, timeoutMs, pollMs, out })
341
353
  };
342
354
  const sim = resolved.sim;
343
355
  if (sim.state === "Booted") {
344
- const result = ready();
356
+ const result = readiness ?? ready();
345
357
  if (result.ok && simulatorApp !== void 0) configuredIosSimulatorViewer(simulatorApp).open(udid);
346
358
  return result;
347
359
  }
348
- out(chalk.dim(phaseLine("device", `booting ${sim.name} (${udid})`)));
349
360
  const bootDeadline = Date.now() + timeoutMs;
350
361
  try {
351
- await bootIosSim(udid, {
352
- timeoutMs,
353
- label: sim.name,
354
- out,
355
- simulatorApp
362
+ await withDeviceBootAdmission({
363
+ platform: "ios",
364
+ key: udid
365
+ }, () => {
366
+ out(chalk.dim(phaseLine("device", `booting ${sim.name} (${udid})`)));
367
+ return bootIosSim(udid, {
368
+ timeoutMs,
369
+ label: sim.name,
370
+ out,
371
+ simulatorApp
372
+ });
373
+ }, {
374
+ ...deviceSlotWait,
375
+ root: projectPath,
376
+ out
356
377
  });
357
378
  } catch (e) {
358
- return {
359
- failed: true,
360
- reason: `Could not boot simulator ${udid}: ${e?.message || e}`
361
- };
379
+ return bootFailure(udid, e);
362
380
  }
363
381
  const deadline = Math.max(bootDeadline, Date.now() + 2 * pollMs);
364
382
  while (Date.now() < deadline) {
@@ -374,6 +392,19 @@ async function ensureIosBooted({ device, simulatorApp, timeoutMs, pollMs, out })
374
392
  reason: `Simulator ${udid} did not reach the Booted state within ${Math.round(timeoutMs / 1e3)}s.`
375
393
  };
376
394
  }
395
+ function bootFailure(udid, error) {
396
+ const refusal = error instanceof DeviceAdmissionRefusal ? error : (0, ownership_claim_exports.claimFailure)(error, "stim ios");
397
+ if (refusal) return {
398
+ failed: true,
399
+ code: refusal.code,
400
+ reason: refusal.message,
401
+ remedy: refusal.remedy
402
+ };
403
+ return {
404
+ failed: true,
405
+ reason: `Could not boot simulator ${udid}: ${error?.message || error}`
406
+ };
407
+ }
377
408
  function sleep(ms) {
378
409
  return new Promise((r) => setTimeout(r, ms));
379
410
  }
@@ -2,8 +2,8 @@ import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
2
  import { B as parseDeviceSlotKey, R as deviceSlotKey, W as validateDeviceSlot, d as getConfigDir } from "./config-D0DbpZtM.mjs";
3
3
  import { n as ownership_claim_exports } from "./process-identity-BmRm1qV0.mjs";
4
4
  import { r as register } from "./cache-manifest-DnUHBJaU.mjs";
5
- import { a as formatElapsed, i as formatDuration, m as shortHash } from "./command-output-DnJMdEY-.mjs";
6
- import { c as writeWorkspaceState, d as sharedBuildCache, n as clearWorkspaceStateKeys, o as withWorkspaceStateLock, r as readWorkspaceState, w as withDirLock$1 } from "./workspace-state-B8-aJ1_5.mjs";
5
+ import { a as formatElapsed, i as formatDuration, m as shortHash } from "./command-output-CDAIFD-8.mjs";
6
+ import { c as writeWorkspaceState, d as sharedBuildCache, n as clearWorkspaceStateKeys, o as withWorkspaceStateLock, r as readWorkspaceState, w as withDirLock$1 } from "./workspace-state-DqcCbpE7.mjs";
7
7
  import { n as declareSpawnsOn, o as stopDeclaringSpawnsOn } from "./spawn-claims-D0XfSe8u.mjs";
8
8
  import { createHash, randomBytes } from "node:crypto";
9
9
  import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
@@ -1,7 +1,7 @@
1
1
  import { R as deviceSlotKey } from "./config-D0DbpZtM.mjs";
2
- import { a as formatElapsed, r as clockTime } from "./command-output-DnJMdEY-.mjs";
3
- import { a as parseLease, c as raiseLease, l as releaseRunLease, n as fileLeaseIo, p as takeLease, r as leaseIsExpired, t as deviceLeasePath } from "./device-lease-D-hLqx1b.mjs";
4
- import { a as VERIFY_TIMEOUT_MS, m as APP_READINESS_TIMEOUT_MS } from "./launch-verify-2DBRizjo.mjs";
2
+ import { a as formatElapsed, r as clockTime } from "./command-output-CDAIFD-8.mjs";
3
+ import { a as parseLease, c as raiseLease, l as releaseRunLease, n as fileLeaseIo, p as takeLease, r as leaseIsExpired, t as deviceLeasePath } from "./device-lease-DpoS36_u.mjs";
4
+ import { a as VERIFY_TIMEOUT_MS, m as APP_READINESS_TIMEOUT_MS } from "./launch-verify-lTnWZikR.mjs";
5
5
  //#region src/engine/device-lease-run.ts
6
6
  const LEASE_STEP_FLOOR_MS = 6e4;
7
7
  const DEBUG_VERIFY_STEP_MS = VERIFY_TIMEOUT_MS + APP_READINESS_TIMEOUT_MS;
@@ -1,6 +1,6 @@
1
1
  import { R as deviceSlotKey } from "./config-D0DbpZtM.mjs";
2
- import { a as parseLease, f as selectPoolDevice, n as fileLeaseIo, r as leaseIsExpired, t as deviceLeasePath } from "./device-lease-D-hLqx1b.mjs";
3
- import { i as DEVICE_WAIT_POLL_MS, n as DEVICE_BUSY, o as leaseExpiryText } from "./device-lease-run-CqjShqWW.mjs";
2
+ import { a as parseLease, f as selectPoolDevice, n as fileLeaseIo, r as leaseIsExpired, t as deviceLeasePath } from "./device-lease-DpoS36_u.mjs";
3
+ import { i as DEVICE_WAIT_POLL_MS, n as DEVICE_BUSY, o as leaseExpiryText } from "./device-lease-run-B8YltIH_.mjs";
4
4
  //#region src/engine/device-pool.ts
5
5
  function heldPoolId(root, platform, now, io = fileLeaseIo, slot = "default") {
6
6
  const record = io.readHolder(root)[deviceSlotKey(platform, slot)];
@@ -1,18 +1,18 @@
1
1
  import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
2
  import { d as getConfigDir } from "./config-D0DbpZtM.mjs";
3
- import { a as formatElapsed } from "./command-output-DnJMdEY-.mjs";
4
- import { K as planMetroReach } from "./settings-oTSkkNrM.mjs";
5
- import { l as ownedDeviceLabel } from "./project-Du91Ilvc.mjs";
6
- import { o as pidExists } from "./metro-BeK1ZPz1.mjs";
7
- import { C as workspaceStateFile, v as workspaceDir, w as withDirLock, x as workspaceLogsDir$1, y as workspaceId } from "./workspace-state-B8-aJ1_5.mjs";
8
- import { a as clearRemoteSession, c as readMetroTunnel, u as readRemoteSession } from "./state-DcW6vOd_.mjs";
9
- import { T as sanitizeDeviceLabel } from "./ios-BaInn3sW.mjs";
10
- import { t as withWorkspaceProcessLock } from "./workspace-process-lock-BL7Q0HCO.mjs";
11
- import { u as probePublicHttp } from "./ownership-hCkPzGmr.mjs";
12
- import { i as INSTALL_ERROR, o as LAUNCH_ERROR, u as devClientDeepLink } from "./app-install-CKWdbXIX.mjs";
13
- import { c as readMetroRecords, o as isBundleProof } from "./launch-verify-2DBRizjo.mjs";
3
+ import { a as formatElapsed } from "./command-output-CDAIFD-8.mjs";
4
+ import { K as planMetroReach } from "./settings-Cfd-0r2d.mjs";
5
+ import { u as ownedDeviceLabel } from "./project-D47Rh0ly.mjs";
6
+ import { o as pidExists } from "./metro-g8NqiJp-.mjs";
7
+ import { C as workspaceStateFile, v as workspaceDir, w as withDirLock, x as workspaceLogsDir$1, y as workspaceId } from "./workspace-state-DqcCbpE7.mjs";
8
+ import { a as clearRemoteSession, c as readMetroTunnel, u as readRemoteSession } from "./state-BKa8wNN5.mjs";
9
+ import { T as sanitizeDeviceLabel } from "./ios-DSfnPfse.mjs";
10
+ import { u as probePublicHttp } from "./ownership-BMLqyBgJ.mjs";
11
+ import { t as withWorkspaceProcessLock } from "./workspace-process-lock-2mNzOlaB.mjs";
12
+ import { i as INSTALL_ERROR, o as LAUNCH_ERROR, u as devClientDeepLink } from "./app-install-VPkI5WEg.mjs";
13
+ import { c as readMetroRecords, o as isBundleProof } from "./launch-verify-lTnWZikR.mjs";
14
14
  import { t as compareStimVersions } from "./stim-installations-QMEI32v1.mjs";
15
- import { c as nativeRunWaitNotice } from "./native-run-NaBrRFtQ.mjs";
15
+ import { c as nativeRunWaitNotice } from "./native-run-D1Q56rAD.mjs";
16
16
  import { existsSync, mkdirSync, realpathSync, renameSync, rmSync, writeFileSync } from "node:fs";
17
17
  import { dirname, join, resolve } from "node:path";
18
18
  import { easMachineStateRoot, easMachineStateRoot as easMachineStateRoot$1, easSessionLedgerFile, easSessionLedgerLock, readEasSessionLedger, readEasSessionLedger as readEasSessionLedger$1, readJsonObject, validEasSessionClaim } from "@stim-cli/core/state";
@@ -1,29 +1,28 @@
1
1
  import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
2
  import { _ as loadConfig$1, p as getProject, s as configCorruptRepair, u as getConcurrencyLimits } from "./config-D0DbpZtM.mjs";
3
- import { d as quotedPath, f as refuseNoProject, l as phaseLine, u as plural } from "./command-output-DnJMdEY-.mjs";
4
- import { B as settingsLayers, Ct as filesystemDevice, Et as temporaryRoot, H as unknownSettingKeys, I as settingOrigin, M as remoteIosSetting, O as projectOptimizations, P as resolveSettings, R as settingShapeErrors, U as webSettings, Y as compilerCacheFallbackMessage, Z as resolveOptimizations, a as SETTING_SHAPE_REMEDY, b as mergeSettingsLayers, dt as listWorktrees, ft as locallyKnownUpstream, gt as repoRoot, i as SETTINGS_WITH_RESOLVE_TIME_FALLBACK, it as gitCommonDir, j as remoteAndroidSetting, n as PATH_SETTINGS, nt as dirtyFingerprintFiles, vt as resolveSourceCheckout, wt as makeTemporaryDirectory, y as iosSimSlimProfileSetting } from "./settings-oTSkkNrM.mjs";
5
- import { c as isPackageResolvable, n as appProjectProblem, o as findProjectRoot$1, r as detectIsExpo } from "./project-Du91Ilvc.mjs";
6
- import { _ as workspaceDerivedData, d as sharedBuildCache } from "./workspace-state-B8-aJ1_5.mjs";
7
- import { i as inspectBudget, n as budgetLine, z as inspectWatchmanMemory } from "./budget-DjrURfQn.mjs";
8
- import { C as diffFingerprintSources, D as fingerprintProject, h as listBuildLocks } from "./device-lease-D-hLqx1b.mjs";
9
- import { n as listBuildSlots } from "./build-slots-rUk77xqh.mjs";
10
- import { D as hostSystemImageArch, O as listAdbDevices, ft as parkedMaxSetting, o as androidHome, ot as POOL_SETTING_REMEDY } from "./android-B_NumVyQ.mjs";
11
- import { A as hostMemoryPressureAdvice, c as iosRuntimeMatches, j as readHostMemoryPressure, p as listIosRuntimes, u as listAllIosSims, y as pickDefaultIosCreation } from "./ios-BaInn3sW.mjs";
12
- import { C as podInstallCommand, D as projectCmakeLauncher, S as bundlerPin, f as ccacheEnabled, i as macosToolchain, m as detectXcodeMajor, n as androidToolchain, r as iosToolchain, t as androidRequirements } from "./toolchain-DkUflduf.mjs";
13
- import { b as realIo, h as inspectBuildMachines, o as offloadCheck, u as simulatorRuntime } from "./client-BTY686R6.mjs";
14
- import { n as inspectDeviceHostMachines } from "./machines-BIBUdhpj.mjs";
3
+ import { d as quotedPath, f as refuseNoProject, l as phaseLine, u as plural } from "./command-output-CDAIFD-8.mjs";
4
+ import { B as settingsLayers, Ct as filesystemDevice, Et as temporaryRoot, H as unknownSettingKeys, I as settingOrigin, M as remoteIosSetting, O as projectOptimizations, P as resolveSettings, R as settingShapeErrors, U as webSettings, Y as compilerCacheFallbackMessage, Z as resolveOptimizations, dt as listWorktrees, ft as locallyKnownUpstream, gt as repoRoot, i as SETTING_SHAPE_REMEDY, it as gitCommonDir, j as remoteAndroidSetting, n as PATH_SETTINGS, nt as dirtyFingerprintFiles, r as SETTINGS_WITH_RESOLVE_TIME_FALLBACK, v as iosSimSlimProfileSetting, vt as resolveSourceCheckout, wt as makeTemporaryDirectory, y as mergeSettingsLayers } from "./settings-Cfd-0r2d.mjs";
5
+ import { l as isPackageResolvable, n as appProjectProblem, r as detectIsExpo, s as findProjectRoot$1 } from "./project-D47Rh0ly.mjs";
6
+ import { _ as workspaceDerivedData, d as sharedBuildCache } from "./workspace-state-DqcCbpE7.mjs";
7
+ import { V as inspectWatchmanMemory, i as inspectBudget, n as budgetLine } from "./budget-CUDz870h.mjs";
8
+ import { C as diffFingerprintSources, D as fingerprintProject, h as listBuildLocks } from "./device-lease-DpoS36_u.mjs";
9
+ import { n as listBuildSlots } from "./build-slots-41v_d2x9.mjs";
10
+ import { D as hostSystemImageArch, ft as parkedMaxSetting, o as androidHome, ot as POOL_SETTING_REMEDY } from "./android-B_YNZy61.mjs";
11
+ import { A as hostMemoryPressureAdvice, c as iosRuntimeMatches, j as readHostMemoryPressure, p as listIosRuntimes, y as pickDefaultIosCreation } from "./ios-DSfnPfse.mjs";
12
+ import { C as podInstallCommand, D as projectCmakeLauncher, S as bundlerPin, f as ccacheEnabled, i as macosToolchain, m as detectXcodeMajor, n as androidToolchain, r as iosToolchain, t as androidRequirements } from "./toolchain-CdrxgUeA.mjs";
13
+ import { S as realIo, g as inspectBuildMachines, o as offloadCheck, u as simulatorRuntime, y as inspectDeviceHostMachines } from "./client-CxNotugx.mjs";
15
14
  import { a as findChrome, t as CHROME_INSTALL_REMEDY } from "./chrome-BrcVWQyD.mjs";
16
- import { i as liveOwnedDeviceCount } from "./device-capacity-BHwxNBlu.mjs";
17
- import { d as androidPathRoomRemedy, l as androidPathRoom, n as apkOutputsDir, t as androidSdkRefusal, u as androidPathRoomMessage } from "./gradle-BsO1MdsH.mjs";
18
- import { h as resolveRuntime, p as resolveDeviceType } from "./support-D4aYJniO.mjs";
19
- import { f as resolveEasCliBin, i as checkEasAuth$1, l as ownerFromConfig, u as providerFromConfig } from "./remote-cache-C8-V13XN.mjs";
20
- import { _ as easCliSupport, g as MIN_EAS_CLI_SIMULATOR_VERSION, s as readInstalledEasCliVersion, v as easCliUpgradeRemedy } from "./device-remote-D5WVWqOq.mjs";
15
+ import { i as countLiveOwnedDevices } from "./device-capacity-ZC4XSZ4R.mjs";
16
+ import { d as androidPathRoomRemedy, l as androidPathRoom, n as apkOutputsDir, t as androidSdkRefusal, u as androidPathRoomMessage } from "./gradle-bsBRy6i9.mjs";
17
+ import { g as resolveRuntime, m as resolveDeviceType } from "./support-BE-tbOP9.mjs";
18
+ import { f as resolveEasCliBin, i as checkEasAuth$1, l as ownerFromConfig, u as providerFromConfig } from "./remote-cache-Ci4DQYYj.mjs";
19
+ import { _ as easCliSupport, g as MIN_EAS_CLI_SIMULATOR_VERSION, s as readInstalledEasCliVersion, v as easCliUpgradeRemedy } from "./device-remote-hFSkpfQu.mjs";
21
20
  import { n as inspectStimVersions, t as compareStimVersions } from "./stim-installations-QMEI32v1.mjs";
22
- import { n as simslimIsOnPath, r as MIN_EAS_CLI_BUILD_DOWNLOAD_VERSION } from "./simslim-B8-7AoTI.mjs";
23
- import { r as resolveAndroidCompilerCache, t as readAndroidCasToolchain } from "./android-cas-8U4DPqeR.mjs";
21
+ import { n as simslimIsOnPath, r as MIN_EAS_CLI_BUILD_DOWNLOAD_VERSION } from "./simslim-BWNrc3M3.mjs";
22
+ import { r as resolveAndroidCompilerCache, t as readAndroidCasToolchain } from "./android-cas-ZFpjJBL_.mjs";
24
23
  import { r as maintenanceStatus, t as maintenanceLine } from "./status-DlKvVJBE.mjs";
25
- import { n as recordDoctorRun } from "./guide-status-CjEZMAD4.mjs";
26
- import { n as hasInstalledDependencies, r as installedNpmTreeIsValid, t as dependencyState } from "./dependency-state-B6cMXZab.mjs";
24
+ import { n as recordDoctorRun } from "./guide-status-Du-ZQ3FD.mjs";
25
+ import { n as hasInstalledDependencies, r as installedNpmTreeIsValid, t as dependencyState } from "./dependency-state-CLcBHH2M.mjs";
27
26
  import { existsSync, lstatSync, readFileSync, readdirSync, realpathSync, rmSync } from "node:fs";
28
27
  import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
29
28
  import { existsSync as existsSync$1, lstatSync as lstatSync$1, mkdirSync as mkdirSync$1, readFileSync as readFileSync$1, readdirSync as readdirSync$1, realpathSync as realpathSync$1, renameSync as renameSync$1, rmSync as rmSync$1, writeFileSync as writeFileSync$1 } from "fs";
@@ -673,13 +672,13 @@ function checkOffloadCandidate(placements, machines, peers, now) {
673
672
  if (average <= 18e4) return null;
674
673
  return {
675
674
  code: "offload-candidate",
676
- ...finding("note", "Builds could run on another Mac", `Cold builds averaged ~${Math.round(average / 6e4)} min over ${cold.length} builds this week.`, "In Stim Desktop, open Settings > Build machines > Add.")
675
+ ...finding("note", "Builds could run on another Mac", `Cold builds averaged ~${Math.round(average / 6e4)} min over ${cold.length} builds this week.`, "In Stim Desktop, open Settings > Remote Macs > Add.")
677
676
  };
678
677
  }
679
678
  function readOffloadCandidate(tailnetStatus, now, host) {
680
679
  if (host !== "darwin") return null;
681
680
  try {
682
- const entries = loadConfig$1()?.offload?.machines;
681
+ const entries = loadConfig$1()?.remote?.machines;
683
682
  const machines = Array.isArray(entries) ? entries.filter((entry) => typeof entry === "string") : [];
684
683
  const placements = readStats().record?.placements ?? [];
685
684
  if (machines.length || placements.length < 3) return null;
@@ -940,7 +939,7 @@ function checkEasAuth({ provider, owner = null, auth = null } = {}) {
940
939
  }
941
940
  function checkConcurrency({ maxBuilds = 0, maxDevices = 0, liveDevices = 0, activeBuilds = 0 } = {}) {
942
941
  if (!maxBuilds && !maxDevices) return null;
943
- return finding("note", "Concurrency limits are set", `${`maxBuilds ${maxBuilds || "unlimited"}, maxDevices ${maxDevices || "unlimited"}`}. Right now ${liveDevices} Stim device(s) are booted and ${activeBuilds} build slot(s) are in use on this machine. At the device cap a new \`stim ios\`/\`android\` is refused with STIM_AT_CAPACITY (stop an environment or raise it); at the build cap a compile waits for a free slot.`, null);
942
+ return finding("note", "Concurrency limits are set", `${`maxBuilds ${maxBuilds || "unlimited"}, maxDevices ${maxDevices || "unlimited"}`}. Right now ${typeof liveDevices === "number" ? `${liveDevices} Stim device(s) are booted` : `the number of booted Stim devices is unknown (${liveDevices.unknown})`} and ${activeBuilds} build slot(s) are in use on this machine. At the device cap a new \`stim ios\`/\`android\` waits in FIFO order for 600s (--wait changes it; --no-wait refuses with STIM_AT_CAPACITY); at the build cap a compile waits for a free slot.`, null);
944
943
  }
945
944
  function readProjectEasCliVersion(projectRoot) {
946
945
  const bin = resolveEasCliBin(projectRoot);
@@ -998,7 +997,8 @@ function checkXcodeEnvLineEndings(projectRoot, platform, host) {
998
997
  }
999
998
  function configuredRemoteBackends(projectSettings, platform) {
1000
999
  const iosTarget = remoteIosSetting(projectSettings);
1001
- return [.../* @__PURE__ */ new Set([...platform !== "android" ? [iosTarget?.kind === "backend" ? iosTarget.backend : null] : [], ...platform !== "ios" ? [remoteAndroidSetting(projectSettings)] : []])].filter((backend) => backend !== null);
1000
+ const androidTarget = remoteAndroidSetting(projectSettings);
1001
+ return [.../* @__PURE__ */ new Set([...platform !== "android" ? [iosTarget?.kind === "backend" ? iosTarget.backend : null] : [], ...platform !== "ios" ? [androidTarget?.kind === "backend" ? androidTarget.backend : null] : []])].filter((backend) => backend !== null);
1002
1002
  }
1003
1003
  function runDoctor(projectRoot, { readFile = readFileSync$1, xcodeMajor = null, easAuth = checkEasAuth$1, concurrency = getConcurrencyLimits, liveDevices = null, activeBuilds = null, remoteEnv = process.env, lookupAgentDevice = null, lookupEasCli = null, lookupSimSlim = null, memoryPressure = readHostMemoryPressure, lookupCcache = null, lookupChrome, tailnetStatus = realIo.status, now = Date.now, platform, host = process.platform } = {}) {
1004
1004
  const localIos = platform !== "android" && host === "darwin";
@@ -1039,7 +1039,7 @@ function runDoctor(projectRoot, { readFile = readFileSync$1, xcodeMajor = null,
1039
1039
  if (limits && (limits.maxBuilds || limits.maxDevices)) concurrencyFinding = checkConcurrency({
1040
1040
  maxBuilds: limits.maxBuilds,
1041
1041
  maxDevices: limits.maxDevices,
1042
- liveDevices: liveDevices ? liveDevices() : countLiveDevices(),
1042
+ liveDevices: liveDevices ? liveDevices() : countLiveOwnedDevices(),
1043
1043
  activeBuilds: activeBuilds ? activeBuilds() : countActiveBuilds()
1044
1044
  });
1045
1045
  const settingShapeFindings = settingShapeErrors(projectSettings).map((error) => finding("cost", "A setting has the wrong type", error, SETTING_SHAPE_REMEDY));
@@ -1183,29 +1183,6 @@ function agentDeviceIsOnPath() {
1183
1183
  return true;
1184
1184
  }
1185
1185
  }
1186
- function countLiveDevices() {
1187
- let sims = [];
1188
- let adb = {
1189
- emulators: [],
1190
- physical: [],
1191
- unhealthy: []
1192
- };
1193
- let config = null;
1194
- try {
1195
- sims = listAllIosSims() || [];
1196
- } catch {}
1197
- try {
1198
- adb = listAdbDevices() || adb;
1199
- } catch {}
1200
- try {
1201
- config = loadConfig$1();
1202
- } catch {}
1203
- return liveOwnedDeviceCount({
1204
- sims,
1205
- adbEmulators: adb.emulators || [],
1206
- config
1207
- });
1208
- }
1209
1186
  function countActiveBuilds() {
1210
1187
  try {
1211
1188
  return listBuildSlots().filter((s) => s.alive).length;
@@ -1371,14 +1348,18 @@ function hostedAgentDriverFinding(driver, apps) {
1371
1348
  code: "hosted-agent-driver",
1372
1349
  level: "note",
1373
1350
  title: `${apps === 1 ? "A hosted app runs" : `${apps} hosted apps run`} with no agent driver`,
1374
- detail: "The client's coding agent cannot drive these apps because `hosting.agentDriver` is none, so stim-server starts no driver. agent-device confines macOS control to one macos-app lease and iOS control to one simulator through its daemon policy.",
1375
- fix: "stim settings set hosting.agentDriver agent-device, with agent-device 0.21.20 or later"
1351
+ detail: "The client's coding agent cannot drive these apps because `hosting.agentDriver` is none, so stim-server starts no driver. agent-device confines macOS control to one macos-app lease and iOS or Android control to one device through its daemon policy.",
1352
+ fix: "stim settings set hosting.agentDriver agent-device, with agent-device 0.21.22 or later"
1376
1353
  };
1377
1354
  }
1378
1355
  function runningHostedApps() {
1379
1356
  try {
1380
1357
  return readHostedSessions().filter((session) => {
1381
- if (!["macos", "ios"].includes(session.platform) || session.state !== "ready" || !session.appAttempt) return false;
1358
+ if (![
1359
+ "macos",
1360
+ "ios",
1361
+ "android"
1362
+ ].includes(session.platform) || session.state !== "ready" || !session.appAttempt) return false;
1382
1363
  try {
1383
1364
  return readHostedAppMetadata(session.id, session.appAttempt).state === "installed";
1384
1365
  } catch {
@@ -1389,7 +1370,7 @@ function runningHostedApps() {
1389
1370
  return 0;
1390
1371
  }
1391
1372
  }
1392
- /** Notes installed hosted macOS and iOS apps on this Mac while `hosting.agentDriver` starts no driver. */
1373
+ /** Notes installed hosted apps on this Mac while `hosting.agentDriver` starts no driver. */
1393
1374
  function inspectHostedAgentDriver() {
1394
1375
  const hosting = loadConfig$1()?.hosting;
1395
1376
  return hostedAgentDriverFinding(isJsonObject(hosting) ? hosting.agentDriver : void 0, runningHostedApps());
@@ -1525,7 +1506,7 @@ function applySandboxFix(root, env = process.env) {
1525
1506
  console.error(chalk.dim("Added writes to Stim's state directory, the simulator XPC service, and local port binding. Claude Code reads project settings from the directory a session starts in, so this file only counts for sessions rooted there. Restart the session for it to take effect."));
1526
1507
  }
1527
1508
  function doctorCommand(program, version, inspectVersions = inspectStimVersions, host = process.platform) {
1528
- program.command("doctor").description("Inspect the source checkout and report project state that can make native worktrees slow or invalid. The checkout is left untouched unless --fix is passed; --platform filters native findings. Each run in a React Native or Expo app is recorded in Stim state so guide can say when doctor is due.").option("--json", "print the findings as JSON").option("--platform <platform>", "report shared findings plus only this native platform: ios or android", parseDoctorPlatform).option("--fix", "repair the sandbox allowance when the report names it, and stale Android .cxx configurations in this checkout; ask each machine in offload.machines for build access. Stop native builds first. Generated CMake output must be ignored and untracked; custom launcher settings and source files are preserved.").action(async (opts) => {
1509
+ program.command("doctor").description("Inspect the source checkout and report project state that can make native worktrees slow or invalid. The checkout is left untouched unless --fix is passed; --platform filters native findings. Each run in a React Native or Expo app is recorded in Stim state so guide can say when doctor is due.").option("--json", "print the findings as JSON").option("--platform <platform>", "report shared findings plus only this native platform: ios or android", parseDoctorPlatform).option("--fix", "repair the sandbox allowance when the report names it, and stale Android .cxx configurations in this checkout; ask each remote Mac in remote.machines for build and device-host approval. Stop native builds first. Generated CMake output must be ignored and untracked; custom launcher settings and source files are preserved.").action(async (opts) => {
1529
1510
  const root = findProjectRoot$1(process.cwd());
1530
1511
  if (!root) {
1531
1512
  refuseNoProject({ json: Boolean(opts.json) });
@@ -1583,13 +1564,14 @@ function doctorCommand(program, version, inspectVersions = inspectStimVersions,
1583
1564
  local: macosToolchain()
1584
1565
  }] : []
1585
1566
  ];
1586
- const buildMachines = await inspectBuildMachines({
1567
+ const remoteMachines = await inspectBuildMachines({
1587
1568
  fix: opts.fix === true,
1588
1569
  check: checksIos || checksAndroid || checksMacos ? offloadCheck(root, offloadTargets) : null
1589
1570
  });
1590
- findings.push(...buildMachines.findings);
1571
+ findings.push(...remoteMachines.findings);
1591
1572
  const deviceHosts = await inspectDeviceHostMachines({ fix: opts.fix === true });
1592
- findings.push(...deviceHosts.findings);
1573
+ const reported = new Set(remoteMachines.findings.map((each) => each.title));
1574
+ findings.push(...deviceHosts.findings.filter((each) => !reported.has(each.title)));
1593
1575
  const agentDriver = inspectHostedAgentDriver();
1594
1576
  if (agentDriver) findings.push(agentDriver);
1595
1577
  const maintenance = maintenanceStatus();
@@ -1608,7 +1590,7 @@ function doctorCommand(program, version, inspectVersions = inspectStimVersions,
1608
1590
  ...maintenance,
1609
1591
  logPath: maintenanceNdjsonFile()
1610
1592
  },
1611
- buildMachines: buildMachines.machines,
1593
+ remoteMachines: remoteMachines.machines,
1612
1594
  deviceHosts: deviceHosts.machines,
1613
1595
  findings
1614
1596
  }));
@@ -1,13 +1,13 @@
1
1
  import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
2
  import { R as deviceSlotKey, p as getProject, z as deviceSlotPlatforms } from "./config-D0DbpZtM.mjs";
3
- import { v as launchErrorPreview } from "./command-output-DnJMdEY-.mjs";
4
- import { l as resolveProjectMetro } from "./metro-BeK1ZPz1.mjs";
5
- import { r as readWorkspaceState$1, w as withDirLock } from "./workspace-state-B8-aJ1_5.mjs";
6
- import { a as parseLease, n as fileLeaseIo, t as deviceLeasePath } from "./device-lease-D-hLqx1b.mjs";
7
- import { f as readWorkspaceLaunches } from "./state-DcW6vOd_.mjs";
8
- import { o as androidHome, u as androidToolCwd } from "./android-B_NumVyQ.mjs";
3
+ import { v as launchErrorPreview } from "./command-output-CDAIFD-8.mjs";
4
+ import { l as resolveProjectMetro } from "./metro-g8NqiJp-.mjs";
5
+ import { r as readWorkspaceState$1, w as withDirLock } from "./workspace-state-DqcCbpE7.mjs";
6
+ import { a as parseLease, n as fileLeaseIo, t as deviceLeasePath } from "./device-lease-DpoS36_u.mjs";
7
+ import { f as readWorkspaceLaunches } from "./state-BKa8wNN5.mjs";
8
+ import { o as androidHome, u as androidToolCwd } from "./android-B_YNZy61.mjs";
9
9
  import { r as deviceConsoleLevel } from "./ios-device-Ee7sDXfp.mjs";
10
- import { n as parseLogcatLine, t as androidClockOffset } from "./android-r2rBL8kV.mjs";
10
+ import { n as parseLogcatLine, t as androidClockOffset } from "./android-DE4v59Mb.mjs";
11
11
  import { createHash, randomUUID } from "node:crypto";
12
12
  import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, statSync, unlinkSync, writeFileSync } from "node:fs";
13
13
  import { basename, dirname, join, resolve, sep } from "node:path";