stim 1.16.0 → 1.17.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 (107) 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--GorydsK.mjs} +679 -317
  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-DM984mnd.mjs} +34 -18
  9. package/dist/{build-plan-DjeSb2Xl.mjs → build-plan-CmQCoyT-.mjs} +345 -86
  10. package/dist/{build-slots-rUk77xqh.mjs → build-slots-41v_d2x9.mjs} +35 -25
  11. package/dist/{cli-BkYZ_6hy.mjs → cli-Daxug5-K.mjs} +17 -17
  12. package/dist/cli.mjs +1 -1
  13. package/dist/collector-run.mjs +3 -3
  14. package/dist/{command-output-DnJMdEY-.mjs → command-output-CDAIFD-8.mjs} +3 -3
  15. package/dist/{created-devices-D5owjN4C.mjs → created-devices-CpvzBL25.mjs} +1 -1
  16. package/dist/{dependency-state-B6cMXZab.mjs → dependency-state-CLcBHH2M.mjs} +1 -1
  17. package/dist/{deps-urbmfXig.mjs → deps-DNWaQs4V.mjs} +1 -1
  18. package/dist/{dev-client-CroAIjjj.mjs → dev-client-B-XC4uRH.mjs} +2 -2
  19. package/dist/{device-DZQjF7H5.mjs → device-C8IOoJPv.mjs} +7 -7
  20. package/dist/device-capacity-D0GWHnTM.mjs +580 -0
  21. package/dist/device-host-worker.mjs +436 -154
  22. package/dist/{device-ios-BadmeL2V.mjs → device-ios-Bc7vbGwl.mjs} +75 -44
  23. package/dist/{device-lease-D-hLqx1b.mjs → device-lease-DpoS36_u.mjs} +2 -2
  24. package/dist/{device-lease-run-CqjShqWW.mjs → device-lease-run-B8YltIH_.mjs} +3 -3
  25. package/dist/{device-pool-CUaEWMMm.mjs → device-pool-3o7s5lo4.mjs} +2 -2
  26. package/dist/{device-remote-D5WVWqOq.mjs → device-remote-hFSkpfQu.mjs} +12 -12
  27. package/dist/{doctor-Cimf-gGB.mjs → doctor-Cnk9d7Hm.mjs} +32 -51
  28. package/dist/{error-diagnostics-B9L4Ae0x.mjs → error-diagnostics-C-NMnhrk.mjs} +7 -7
  29. package/dist/{gc-Bqy4bjEu.mjs → gc-DxCL2A46.mjs} +308 -26
  30. package/dist/{gradle-BsO1MdsH.mjs → gradle-D7-L2Th3.mjs} +3 -3
  31. package/dist/{guide-B_uyKTxW.mjs → guide-DjrTQGik.mjs} +1662 -565
  32. package/dist/{guide-status-CjEZMAD4.mjs → guide-status-Du-ZQ3FD.mjs} +1 -1
  33. package/dist/hosted-android-DwLS4Mse.mjs +32 -0
  34. package/dist/{hosted-client-a-WmqE2L.mjs → hosted-client-wk1W_CNW.mjs} +2 -3
  35. package/dist/hosted-ios-aMo7Cljf.mjs +15 -0
  36. package/dist/{hosted-logs-D5g041Q3.mjs → hosted-logs-DqYmWtXc.mjs} +16 -16
  37. package/dist/{hosted-macos-BSZyIb3y.mjs → hosted-macos-dlHXEeF5.mjs} +4 -4
  38. package/dist/{hosted-ios-CIWelIqf.mjs → hosted-native-C9xJ9rpA.mjs} +77 -42
  39. package/dist/{idle-DYVymG92.mjs → idle-B2m-Ovod.mjs} +122 -58
  40. package/dist/{idle-shutdown-CTsDnAde.mjs → idle-shutdown-_3oOV1V_.mjs} +25 -17
  41. package/dist/{in-use-DvBw0Kxo.mjs → in-use-CvGOUn-x.mjs} +11 -11
  42. package/dist/{ios-aq3QoGI3.mjs → ios-DE6duQoz.mjs} +170 -88
  43. package/dist/{ios-BaInn3sW.mjs → ios-DSfnPfse.mjs} +4 -4
  44. package/dist/{ios-device-FfCN_C8p.mjs → ios-device-pAN4t54-.mjs} +1 -1
  45. package/dist/ios-state-8L3lgum6.mjs +89 -0
  46. package/dist/{launch-verify-2DBRizjo.mjs → launch-verify-lTnWZikR.mjs} +2 -2
  47. package/dist/{logs-CHEVK8zD.mjs → logs-DUoKlDoS.mjs} +36 -23
  48. package/dist/{client-BTY686R6.mjs → machines-C2XiNwh9.mjs} +278 -20
  49. package/dist/{macos-CSYEAFmF.mjs → macos-32po3kUq.mjs} +14 -14
  50. package/dist/macos-run.mjs +1 -1
  51. package/dist/maintenance-run.mjs +2 -2
  52. package/dist/{metro-BeK1ZPz1.mjs → metro-g8NqiJp-.mjs} +1 -1
  53. package/dist/{metro-gateway-BJ-rxQAB.mjs → metro-gateway-DmslUM30.mjs} +22 -3
  54. package/dist/{named-ports-Bk_YXEmE.mjs → named-ports-C8m8EQs2.mjs} +2 -2
  55. package/dist/{native-run-NaBrRFtQ.mjs → native-run-D1Q56rAD.mjs} +17 -6
  56. package/dist/{native-runtime-DBzRnEKS.mjs → native-runtime-DhJI3IVQ.mjs} +6 -6
  57. package/dist/offload-worker.mjs +8 -8
  58. package/dist/{teardown-CL6vDs2Y.mjs → ownership-BMLqyBgJ.mjs} +603 -26
  59. package/dist/{ownership-DiA16AsN.mjs → ownership-DyRQru0q.mjs} +2 -2
  60. package/dist/{ports-R2k4Knz3.mjs → ports-4nYqxPWC.mjs} +2 -2
  61. package/dist/{prebuild-D2fsca_Z.mjs → prebuild-D7RUMkwN.mjs} +3 -3
  62. package/dist/{preview-DhVbvPAZ.mjs → preview-CxPRIEht.mjs} +7 -7
  63. package/dist/{project-Du91Ilvc.mjs → project-D47Rh0ly.mjs} +7 -2
  64. package/dist/{recordings-BIXOzVnr.mjs → recordings-BNgsD1Tr.mjs} +1 -1
  65. package/dist/{reload-rM9jV1fz.mjs → reload-qZei6iWn.mjs} +62 -35
  66. package/dist/{remote-cache-C8-V13XN.mjs → remote-cache-Ci4DQYYj.mjs} +3 -3
  67. package/dist/{run-f4vI6EnK.mjs → run-CkE9y0nc.mjs} +1 -1
  68. package/dist/{server-bare-CpPGmnmO.mjs → server-bare-BkrSxSsZ.mjs} +1 -1
  69. package/dist/{server-expo-MC0rHlOC.mjs → server-expo-CHTyXD0G.mjs} +2 -2
  70. package/dist/server-expo-CeakhI8P.mjs +2 -0
  71. package/dist/{settings-oTSkkNrM.mjs → settings-Cfd-0r2d.mjs} +10 -9
  72. package/dist/{settings-ZkDzkzvB.mjs → settings-SR9OmW9h.mjs} +3 -3
  73. package/dist/settings.schema.json +42 -13
  74. package/dist/{simslim-B8-7AoTI.mjs → simslim-BWNrc3M3.mjs} +3 -3
  75. package/dist/{slot-launch-B8VrHxn1.mjs → slot-launch-DmjBmpQV.mjs} +8 -8
  76. package/dist/{start-CX8Ig-U2.mjs → start-BopZcr4n.mjs} +1 -1
  77. package/dist/{start-ClDfwXa4.mjs → start-uee1wWjY.mjs} +11 -11
  78. package/dist/{state-DcW6vOd_.mjs → state-BKa8wNN5.mjs} +1 -1
  79. package/dist/{state-3DwSQIBK.mjs → state-DyaNTgfR.mjs} +1 -1
  80. package/dist/{state-C1jB7Q8q.mjs → state-PeQiJuIu.mjs} +2 -2
  81. package/dist/{stats-CEZ_i5je.mjs → stats-Bq4fJOdB.mjs} +7 -4
  82. package/dist/{status-BuvBFeII.mjs → status-CzS4bPO9.mjs} +4 -4
  83. package/dist/{status-Cs7VLg9D.mjs → status-dXX2JX1C.mjs} +125 -35
  84. package/dist/{stop-Dt_dM16A.mjs → stop-BCwvvs7F.mjs} +2 -2
  85. package/dist/{stop-Bffozsac.mjs → stop-BjizrQwv.mjs} +48 -27
  86. package/dist/{stop-B0Yxtmiy.mjs → stop-Cr1d8DSE.mjs} +4 -4
  87. package/dist/{stop-cause-CidxIqup.mjs → stop-cause-rhmGfwvt.mjs} +1 -1
  88. package/dist/supervisor-run.d.mts +7 -4
  89. package/dist/supervisor-run.mjs +26 -28
  90. package/dist/{support-D4aYJniO.mjs → support-BJPgYCu-.mjs} +44 -19
  91. package/dist/{support-CAP8qDUF.mjs → support-F9-sr0Oi.mjs} +6 -6
  92. package/dist/{toolchain-DkUflduf.mjs → toolchain-CNIBTIOv.mjs} +7 -7
  93. package/dist/{warm-progress-CpGv3Wb6.mjs → warm-progress-DF4JRvYI.mjs} +3 -3
  94. package/dist/watchman-APYjXSQa.mjs +48 -0
  95. package/dist/{web-DWUaMp8P.mjs → web-exQtGkoc.mjs} +12 -13
  96. package/dist/web-run.mjs +4 -4
  97. package/dist/{workspace-process-lock-BL7Q0HCO.mjs → workspace-process-lock-2mNzOlaB.mjs} +3 -2
  98. package/dist/{workspace-state-B8-aJ1_5.mjs → workspace-state-DqcCbpE7.mjs} +2 -2
  99. package/dist/{workspaces-BV4pdHFz.mjs → workspaces-Bcib97-6.mjs} +3 -3
  100. package/dist/{worktree-jnG8Iph5.mjs → worktree-Dc98cE6_.mjs} +1 -1
  101. package/dist/{worktree-K-xj9Db0.mjs → worktree-ZpZR3S1k.mjs} +141 -40
  102. package/package.json +5 -5
  103. package/dist/device-capacity-BHwxNBlu.mjs +0 -136
  104. package/dist/ios-state-DS5UpYTH.mjs +0 -48
  105. package/dist/machines-BIBUdhpj.mjs +0 -265
  106. package/dist/ownership-hCkPzGmr.mjs +0 -571
  107. package/dist/server-expo-CVqO2c35.mjs +0 -2
@@ -1,11 +1,11 @@
1
1
  import { P as measuredDirectorySize } from "./config-D0DbpZtM.mjs";
2
- import { o as findProjectRoot$1 } from "./project-Du91Ilvc.mjs";
3
- import { X as discoverCaches, j as planCacheEmptying, o as readVolumeSpace, r as enforceBudget, s as resolveBudget } from "./budget-DjrURfQn.mjs";
4
- import { o as canonicalPath } from "./in-use-DvBw0Kxo.mjs";
5
- import { h as listBuildLocks } from "./device-lease-D-hLqx1b.mjs";
6
- import { n as listBuildSlots } from "./build-slots-rUk77xqh.mjs";
7
- import { j as readHostMemoryPressure } from "./ios-BaInn3sW.mjs";
8
- import { a as collectWorkspaceOutputs } from "./workspaces-BV4pdHFz.mjs";
2
+ import { s as findProjectRoot$1 } from "./project-D47Rh0ly.mjs";
3
+ import { M as planCacheEmptying, Z as discoverCaches, c as resolveBudget, r as enforceBudget, s as readVolumeSpace } from "./budget-DM984mnd.mjs";
4
+ import { o as canonicalPath } from "./in-use-CvGOUn-x.mjs";
5
+ import { h as listBuildLocks } from "./device-lease-DpoS36_u.mjs";
6
+ import { n as listBuildSlots } from "./build-slots-41v_d2x9.mjs";
7
+ import { j as readHostMemoryPressure } from "./ios-DSfnPfse.mjs";
8
+ import { a as collectWorkspaceOutputs } from "./workspaces-Bcib97-6.mjs";
9
9
  import { t as resolveMaintenanceSettings } from "./settings-BBpMcDbD.mjs";
10
10
  import { existsSync } from "node:fs";
11
11
  import { join } from "node:path";
@@ -1,5 +1,5 @@
1
1
  import { _ as loadConfig$1, g as isPathPrefix, l as findEnclosingWorktreeRoot, p as getProject } from "./config-D0DbpZtM.mjs";
2
- import { U as webSettings, gt as repoRoot, z as settingValueAt } from "./settings-oTSkkNrM.mjs";
2
+ import { U as webSettings, gt as repoRoot, z as settingValueAt } from "./settings-Cfd-0r2d.mjs";
3
3
  import { existsSync, readFileSync, readdirSync, realpathSync } from "fs";
4
4
  import { basename, dirname, join, resolve } from "path";
5
5
  import { findProjectRoot, findProjectRoot as findProjectRoot$1 } from "@stim-cli/core/state";
@@ -153,6 +153,11 @@ function detectIsExpo(projectRoot) {
153
153
  if (podfileUsesExpoModules(projectRoot)) return true;
154
154
  return false;
155
155
  }
156
+ function detectTutorial(appJson) {
157
+ const extra = appJson?.expo?.extra;
158
+ const version = typeof extra === "object" && extra !== null ? extra.stimTutorial : null;
159
+ return typeof version === "number" && Number.isInteger(version) && version > 0 ? { version } : void 0;
160
+ }
156
161
  function detectPlatforms(root, settings) {
157
162
  const platforms = /* @__PURE__ */ new Set();
158
163
  const product = settingValueAt(settings, "macos.product");
@@ -268,4 +273,4 @@ function readAppConfigText(projectRoot) {
268
273
  return null;
269
274
  }
270
275
  //#endregion
271
- export { findCommandWorkspace as a, isPackageResolvable as c, readAppConfigText as d, readAppJson as f, detectPlatforms as i, ownedDeviceLabel as l, appProjectProblem as n, findProjectRoot$1 as o, resolvePackageJson as p, detectIsExpo as r, findServerWorkspace as s, NO_PROJECT_REFUSAL as t, projectShortcut as u };
276
+ export { detectTutorial as a, findServerWorkspace as c, projectShortcut as d, readAppConfigText as f, detectPlatforms as i, isPackageResolvable as l, resolvePackageJson as m, appProjectProblem as n, findCommandWorkspace as o, readAppJson as p, detectIsExpo as r, findProjectRoot$1 as s, NO_PROJECT_REFUSAL as t, ownedDeviceLabel as u };
@@ -1,4 +1,4 @@
1
- import { at as gitCommonDirOnDisk } from "./settings-oTSkkNrM.mjs";
1
+ import { at as gitCommonDirOnDisk } from "./settings-Cfd-0r2d.mjs";
2
2
  import { existsSync, rmSync } from "node:fs";
3
3
  import { loadConfig, recordingEnabled, workspaceRecordingsDir } from "@stim-cli/core/state";
4
4
  //#region src/workspace/recordings.ts
@@ -1,18 +1,19 @@
1
1
  import { B as parseDeviceSlotKey, p as getProject, z as deviceSlotPlatforms } from "./config-D0DbpZtM.mjs";
2
- import { f as refuseNoProject, l as phaseLine } from "./command-output-DnJMdEY-.mjs";
3
- import { a as findCommandWorkspace } from "./project-Du91Ilvc.mjs";
4
- import { l as resolveProjectMetro } from "./metro-BeK1ZPz1.mjs";
5
- import { i as recordWorkspaceUse, r as readWorkspaceState$1 } from "./workspace-state-B8-aJ1_5.mjs";
6
- import { o as readWebRecord, r as cdpEndpoint, u as webFacts } from "./state-C1jB7Q8q.mjs";
7
- import { f as readWorkspaceLaunches } from "./state-DcW6vOd_.mjs";
8
- import { J as resolveOwnedAvdSerial } from "./android-B_NumVyQ.mjs";
9
- import { w as resolveOwnedIosSim } from "./ios-BaInn3sW.mjs";
10
- import { l as probeHostedSession } from "./hosted-client-a-WmqE2L.mjs";
11
- import { p as ensureMetroReverse, s as androidAppProcess, v as iosAppProcess } from "./app-install-CKWdbXIX.mjs";
12
- import { n as nativeRunCommand } from "./slot-launch-B8VrHxn1.mjs";
13
- import { t as readHostedIos } from "./ios-state-DS5UpYTH.mjs";
2
+ import { f as refuseNoProject, l as phaseLine } from "./command-output-CDAIFD-8.mjs";
3
+ import { o as findCommandWorkspace } from "./project-D47Rh0ly.mjs";
4
+ import { l as resolveProjectMetro } from "./metro-g8NqiJp-.mjs";
5
+ import { i as recordWorkspaceUse, r as readWorkspaceState$1 } from "./workspace-state-DqcCbpE7.mjs";
6
+ import { o as readWebRecord, r as cdpEndpoint, u as webFacts } from "./state-PeQiJuIu.mjs";
7
+ import { f as readWorkspaceLaunches } from "./state-BKa8wNN5.mjs";
8
+ import { J as resolveOwnedAvdSerial } from "./android-B_YNZy61.mjs";
9
+ import { w as resolveOwnedIosSim } from "./ios-DSfnPfse.mjs";
10
+ import { l as probeHostedSession } from "./hosted-client-wk1W_CNW.mjs";
11
+ import { p as ensureMetroReverse, s as androidAppProcess, v as iosAppProcess } from "./app-install-VPkI5WEg.mjs";
12
+ import { r as readHostedNative } from "./ios-state-8L3lgum6.mjs";
13
+ import { r as reopenHostedAndroidMetro } from "./hosted-android-DwLS4Mse.mjs";
14
+ import { n as nativeRunCommand } from "./slot-launch-DmjBmpQV.mjs";
14
15
  import { n as sendToOwnedPage, t as liveWebRecord } from "./page-QF_Ke10s.mjs";
15
- import { hostedIosRecords } from "@stim-cli/core/state";
16
+ import { hostedAndroidDeviceName, hostedNativeRecords } from "@stim-cli/core/state";
16
17
  import chalk from "chalk";
17
18
  import { WebSocket } from "ws";
18
19
  //#region src/engine/reload.ts
@@ -148,6 +149,7 @@ const DEFAULT_DEPS = {
148
149
  return facts?.status === "unverified" ? "unverified" : liveWebRecord(facts?.record ?? null);
149
150
  },
150
151
  reloadPage: (record) => sendToOwnedPage(record, "Page.reload"),
152
+ reopenHostedMetro: reopenHostedAndroidMetro,
151
153
  ensureReverse: (serial, metroPort) => ensureMetroReverse({
152
154
  serial,
153
155
  metroPort
@@ -286,37 +288,47 @@ async function runReload({ root, platform = null, deps = {} }) {
286
288
  ok: false,
287
289
  error: failure("STIM_RELOAD_STOPPED", "No owned Chrome is running for this workspace.", "Run `stim web` first.")
288
290
  };
289
- const hosted = platform === "android" ? {} : readHostedIos(root);
291
+ const hosted = {
292
+ ios: platform === "android" ? {} : readHostedNative(root, "ios"),
293
+ android: platform === "ios" ? {} : readHostedNative(root, "android")
294
+ };
290
295
  const hostedFailures = {};
291
- if (platform !== "android") for (const slot of Object.keys(hostedIosRecords(readWorkspaceState$1(root)))) {
292
- if (hosted[slot]) continue;
293
- try {
294
- readHostedIos(root, slot);
295
- } catch (error) {
296
- hostedFailures[slot] = {
297
- platform: "ios",
298
- error: failure("STIM_HOSTING_REFUSED", describe(error), error instanceof Error && "remedy" in error && typeof error.remedy === "string" ? error.remedy : null)
299
- };
296
+ for (const native of ["ios", "android"]) {
297
+ if (platform && platform !== native) continue;
298
+ for (const slot of Object.keys(hostedNativeRecords(readWorkspaceState$1(root), native))) {
299
+ if (hosted[native][slot]) continue;
300
+ try {
301
+ readHostedNative(root, native, slot);
302
+ } catch (error) {
303
+ hostedFailures[`${native}:${slot}`] = {
304
+ platform: native,
305
+ error: failure("STIM_HOSTING_REFUSED", describe(error), error instanceof Error && "remedy" in error && typeof error.remedy === "string" ? error.remedy : null)
306
+ };
307
+ }
300
308
  }
301
309
  }
302
- const hostedProbes = Object.fromEntries(await Promise.all(Object.entries(hosted).map(async ([slot, placement]) => [slot, await probeHostedSession(placement)])));
310
+ const hostedProbes = Object.fromEntries(await Promise.all(["ios", "android"].flatMap((native) => Object.entries(hosted[native]).map(async ([slot, placement]) => [`${native}:${slot}`, await probeHostedSession({
311
+ ...placement,
312
+ platform: native
313
+ })]))));
303
314
  const launches = d.readLaunches(root);
304
315
  const inspected = Object.entries(launches).flatMap(([key, record]) => {
305
316
  const parsed = parseDeviceSlotKey(key);
306
317
  if (!parsed || platform && parsed.platform !== platform) return [];
307
- if (parsed.platform === "ios" && hostedFailures[parsed.slot]) return [];
308
- const placement = parsed.platform === "ios" ? hosted[parsed.slot] : void 0;
309
- if (placement && record.deviceId === placement.session) {
310
- const probe = hostedProbes[parsed.slot];
311
- return [probe.state === "ready" ? {
312
- platform: "ios",
318
+ if (hostedFailures[`${parsed.platform}:${parsed.slot}`]) return [];
319
+ const placement = hosted[parsed.platform][parsed.slot];
320
+ if (placement) {
321
+ const probe = hostedProbes[`${parsed.platform}:${parsed.slot}`];
322
+ return [probe.state === "ready" && record.deviceId === placement.session ? {
323
+ platform: parsed.platform,
313
324
  slot: parsed.slot,
314
325
  record,
315
326
  hosted: true,
316
- deviceName: `${placement.device?.name ?? "iOS simulator"} on ${placement.machine}`
327
+ ...parsed.platform === "android" ? { hostedAndroid: placement } : {},
328
+ deviceName: `${placement.device ? "udid" in placement.device ? placement.device.name : hostedAndroidDeviceName(placement.device) : `${parsed.platform} device`} on ${placement.machine}`
317
329
  } : {
318
- platform: "ios",
319
- error: failure("STIM_RELOAD_PROBE_FAILED", `The iOS session on ${placement.machine} is ${probe.state}.`, `Run stim ios --remote ${placement.machine} or stim stop to reconcile it.`)
330
+ platform: parsed.platform,
331
+ error: failure("STIM_RELOAD_PROBE_FAILED", `The ${parsed.platform} session on ${placement.machine} is ${probe.state}.`, `Run stim ${parsed.platform} --remote ${placement.machine} or stim stop to reconcile it.`)
320
332
  }];
321
333
  }
322
334
  return [inspectTarget(parsed.platform, record, {
@@ -370,8 +382,21 @@ async function runReload({ root, platform = null, deps = {} }) {
370
382
  error: stopped
371
383
  };
372
384
  const reverseRestored = [];
385
+ let hostedReopened = false;
373
386
  const reverseChecked = target.platform === "android" ? live.filter((c) => c.platform === "android" && !c.record.release && c.record.metroPort === port) : [];
374
387
  for (const candidate of reverseChecked) {
388
+ if (candidate.hostedAndroid) {
389
+ try {
390
+ await d.reopenHostedMetro(root, candidate.hostedAndroid, port);
391
+ hostedReopened = true;
392
+ } catch (error) {
393
+ return {
394
+ ok: false,
395
+ error: failure("STIM_HOSTING_REFUSED", describe(error), `Retry stim reload android when ${candidate.hostedAndroid.machine} answers, or run stim stop to reconcile the session.`)
396
+ };
397
+ }
398
+ continue;
399
+ }
375
400
  const serial = candidate.record.deviceId;
376
401
  const ensured = d.ensureReverse(serial, port);
377
402
  if (ensured.failed) return {
@@ -385,7 +410,9 @@ async function runReload({ root, platform = null, deps = {} }) {
385
410
  appId: target.record.appId
386
411
  };
387
412
  const expectedPeers = reverseChecked.filter((c) => c.record.appId === target.record.appId).length;
388
- for (let poll = 0; reverseRestored.length && poll < RECONNECT_POLLS; poll++) {
413
+ const awaitReconnect = reverseRestored.length > 0 || hostedReopened;
414
+ for (let poll = 0; poll < RECONNECT_POLLS; poll++) {
415
+ if (!awaitReconnect) break;
389
416
  const peers = await d.reloadMetro(port, {
390
417
  ...request,
391
418
  peersOnly: true
@@ -403,7 +430,7 @@ async function runReload({ root, platform = null, deps = {} }) {
403
430
  };
404
431
  if (target.hosted) return {
405
432
  ok: false,
406
- error: failure("STIM_RELOAD_FAILED", reloaded.reason, `Metro could not confirm a peer for the hosted iOS app. Check the app on its hosting Mac and run stim reload ios again.`)
433
+ error: failure("STIM_RELOAD_FAILED", reloaded.reason, `Metro could not confirm a peer for the hosted ${target.platform} app. Check the app on its hosting Mac and run stim reload ${target.platform} again.`)
407
434
  };
408
435
  const snapshot = `agent-device snapshot -i --platform ${target.platform} --${target.platform === "ios" ? "udid" : "serial"} ${target.record.deviceId}`;
409
436
  const relaunch = `agent-device open ${target.record.appId} --platform ${target.platform} --${target.platform === "ios" ? "udid" : "serial"} ${target.record.deviceId} --metro-port ${port} --relaunch`;
@@ -1,9 +1,9 @@
1
1
  import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
2
  import { n as stripAnsi } from "./process-output-Fpl8PYi8.mjs";
3
- import { p as resolvePackageJson } from "./project-Du91Ilvc.mjs";
4
- import { x as workspaceLogsDir } from "./workspace-state-B8-aJ1_5.mjs";
3
+ import { m as resolvePackageJson } from "./project-D47Rh0ly.mjs";
4
+ import { x as workspaceLogsDir } from "./workspace-state-DqcCbpE7.mjs";
5
5
  import { r as createNdjsonWriter } from "./ndjson-PclOIwVY.mjs";
6
- import { n as expoBinFromPackage, r as expoBinPath, s as findBinUpward } from "./server-expo-MC0rHlOC.mjs";
6
+ import { n as expoBinFromPackage, r as expoBinPath, s as findBinUpward } from "./server-expo-CHTyXD0G.mjs";
7
7
  import { createRequire } from "node:module";
8
8
  import { existsSync, readFileSync } from "node:fs";
9
9
  import { join } from "node:path";
@@ -1,6 +1,6 @@
1
1
  import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
2
  import { n as ownership_claim_exports, t as process_identity_exports } from "./process-identity-BmRm1qV0.mjs";
3
- import { i as macosRuntimeClaim, n as macosLogFile, o as updateMacosRecord, r as macosProcess } from "./state-3DwSQIBK.mjs";
3
+ import { i as macosRuntimeClaim, n as macosLogFile, o as updateMacosRecord, r as macosProcess } from "./state-DyaNTgfR.mjs";
4
4
  import { r as createNdjsonWriter } from "./ndjson-PclOIwVY.mjs";
5
5
  import { LOG_ROTATE_BYTES } from "@stim-cli/core";
6
6
  import { readMacosRecord } from "@stim-cli/core/state";
@@ -1,6 +1,6 @@
1
1
  import { i as __require, t as __commonJSMin } from "./rolldown-runtime-BsOwBuO_.mjs";
2
2
  import { t as getExecutor } from "./exec-CaErVv7t.mjs";
3
- import { D as projectMetroSharedCache } from "./settings-oTSkkNrM.mjs";
3
+ import { D as projectMetroSharedCache } from "./settings-Cfd-0r2d.mjs";
4
4
  import { n as supervisorError } from "./errors-B6W7FkxQ.mjs";
5
5
  import { a as metroStoreRoot, o as registerMetroStore, t as appendCacheStore } from "./metro-store-CDDZHtPl.mjs";
6
6
  import { createRequire } from "node:module";
@@ -1,7 +1,7 @@
1
1
  import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
2
  import { n as stripAnsi, t as createLineReader } from "./process-output-Fpl8PYi8.mjs";
3
- import { D as projectMetroSharedCache } from "./settings-oTSkkNrM.mjs";
4
- import { p as resolvePackageJson } from "./project-Du91Ilvc.mjs";
3
+ import { D as projectMetroSharedCache } from "./settings-Cfd-0r2d.mjs";
4
+ import { m as resolvePackageJson } from "./project-D47Rh0ly.mjs";
5
5
  import { n as supervisorError } from "./errors-B6W7FkxQ.mjs";
6
6
  import { r as createNdjsonWriter } from "./ndjson-PclOIwVY.mjs";
7
7
  import { a as metroStoreRoot, i as metroStoreConfirmedRoot, n as expoMetroConfigPath, o as registerMetroStore, r as expoMetroStoreEnv } from "./metro-store-CDDZHtPl.mjs";
@@ -0,0 +1,2 @@
1
+ import { p as startExpoServer } from "./server-expo-CHTyXD0G.mjs";
2
+ export { startExpoServer };
@@ -933,7 +933,7 @@ function settingShapeErrors(settings) {
933
933
  if (SETTINGS_WITH_RESOLVE_TIME_FALLBACK.has(setting.key)) continue;
934
934
  const value = settingValueAt(settings, setting.key);
935
935
  if (value === void 0) continue;
936
- const expected = setting.key === "ios.remote" || setting.type.kind === "number" && !BOUNDS_CHECKED_WHEN_USED.has(setting.key) ? settingValueError(setting, value) : acceptsShape(setting, value) ? null : expectedShape(setting.type);
936
+ const expected = setting.key === "ios.remote" || setting.key === "android.remote" || setting.type.kind === "number" && !BOUNDS_CHECKED_WHEN_USED.has(setting.key) ? settingValueError(setting, value) : acceptsShape(setting, value) ? null : expectedShape(setting.type);
937
937
  if (expected) errors.push(`Invalid ${setting.key} setting ${JSON.stringify(value)}. Expected ${expected}.`);
938
938
  }
939
939
  return errors;
@@ -1351,13 +1351,8 @@ function remoteIosSetting(settings) {
1351
1351
  return isPlainObject(ios) && typeof ios.remote === "string" ? parseIosRemote(ios.remote) : null;
1352
1352
  }
1353
1353
  function remoteAndroidSetting(settings) {
1354
- return remoteSetting(settings, "android");
1355
- }
1356
- function remoteSetting(settings, platform) {
1357
- const block = settings[platform];
1358
- if (!isPlainObject(block)) return null;
1359
- const remote = block.remote;
1360
- return typeof remote === "string" && REMOTE_DEVICE_BACKENDS.includes(remote) ? remote : null;
1354
+ const android = settings.android;
1355
+ return isPlainObject(android) && typeof android.remote === "string" ? parseIosRemote(android.remote) : null;
1361
1356
  }
1362
1357
  function minutesSetting(settings, block, name) {
1363
1358
  const parent = settings[block];
@@ -1371,6 +1366,12 @@ function metroIdleStopMinutesSetting(settings) {
1371
1366
  function deviceIdleShutdownMinutesSetting(settings) {
1372
1367
  return minutesSetting(settings, "devices", "idleShutdownMinutes");
1373
1368
  }
1369
+ function deviceReclaimIdleMinutesSetting(settings) {
1370
+ return minutesSetting(settings, "devices", "reclaimIdleMinutes");
1371
+ }
1372
+ function projectDeviceReclaimIdleMinutes(root) {
1373
+ return deviceReclaimIdleMinutesSetting(settingsForProject(root));
1374
+ }
1374
1375
  function tunnelModeSetting(settings) {
1375
1376
  const block = settings.metro;
1376
1377
  if (typeof block !== "object" || block === null) return null;
@@ -1436,4 +1437,4 @@ function webSettings(settings) {
1436
1437
  };
1437
1438
  }
1438
1439
  //#endregion
1439
- export { cloneIgnoredEntries as $, readCommittedSettings as A, settingsLayers as B, metroTunnelSettingError as C, filesystemDevice as Ct, projectMetroSharedCache as D, parseIosRemote as E, temporaryRoot as Et, settingFile as F, detectProviders as G, unknownSettingKeys as H, settingOrigin as I, artifactCachePolicy as J, planMetroReach as K, settingOriginScope as L, remoteIosSetting as M, resolveCacheProviderConfig as N, projectOptimizations as O, resolveSettings as P, branchExists as Q, settingShapeErrors as R, metroPortSetting as S, warmWorktreePaths as St, ngrokUrlSetting as T, removeTemporaryEntry as Tt, webSettings as U, tunnelModeSetting as V, PUBLIC_METRO_ENV as W, optimizationBuildProfile as X, compilerCacheFallbackMessage as Y, resolveOptimizations as Z, iosSigningIdentitySha1Setting as _, resolveFullRef as _t, SETTING_SHAPE_REMEDY as a, gitCommonDirOnDisk as at, mergeSettingsLayers as b, sourceCheckoutOf as bt, androidDataPartitionSizeBytes as c, hasUncommittedWork as ct, cacheProviderSettingError as d, listWorktrees as dt, deleteBranch as et, deviceIdleShutdownMinutesSetting as f, locallyKnownUpstream as ft, iosSigningIdentitySettingError as g, repoRoot as gt, iosSigningIdentitySetting as h, removeWorktree as ht, SETTINGS_WITH_RESOLVE_TIME_FALLBACK as i, gitCommonDir as it, remoteAndroidSetting as j, publicUrlSetting as k, androidDataPartitionSizeGbSetting as l, isPodInstallChurn as lt, iosLanHostSettingError as m, readWorktreeExclude as mt, PATH_SETTINGS as n, dirtyFingerprintFiles as nt, androidAvdConfigSetting as o, hasPopulatedSubmodules as ot, iosLanHostSetting as p, podsOutOfSync as pt, resolveTailscaleBinary as q, REMOTE_DEVICE_BACKENDS as r, dirtyPaths as rt, androidAvdConfigSettingError as s, hasRemote as st, ANDROID_AVD_CONFIG_HELP as t, depsOutOfSync as tt, androidDataPartitionSizeGbSettingError as u, linkedWorktreesOnDisk as ut, iosSigningIdentitySha1SettingError as v, resolveSourceCheckout as vt, metroWarmupUrlSetting as w, makeTemporaryDirectory as wt, metroIdleStopMinutesSetting as x, unpushedCommits as xt, iosSimSlimProfileSetting as y, restoreFile as yt, settingValueAt as z };
1440
+ export { cloneIgnoredEntries as $, readCommittedSettings as A, settingsLayers as B, metroWarmupUrlSetting as C, filesystemDevice as Ct, projectMetroSharedCache as D, projectDeviceReclaimIdleMinutes as E, temporaryRoot as Et, settingFile as F, detectProviders as G, unknownSettingKeys as H, settingOrigin as I, artifactCachePolicy as J, planMetroReach as K, settingOriginScope as L, remoteIosSetting as M, resolveCacheProviderConfig as N, projectOptimizations as O, resolveSettings as P, branchExists as Q, settingShapeErrors as R, metroTunnelSettingError as S, warmWorktreePaths as St, parseIosRemote as T, removeTemporaryEntry as Tt, webSettings as U, tunnelModeSetting as V, PUBLIC_METRO_ENV as W, optimizationBuildProfile as X, compilerCacheFallbackMessage as Y, resolveOptimizations as Z, iosSigningIdentitySha1SettingError as _, resolveFullRef as _t, androidAvdConfigSetting as a, gitCommonDirOnDisk as at, metroIdleStopMinutesSetting as b, sourceCheckoutOf as bt, androidDataPartitionSizeGbSetting as c, hasUncommittedWork as ct, deviceIdleShutdownMinutesSetting as d, listWorktrees as dt, deleteBranch as et, iosLanHostSetting as f, locallyKnownUpstream as ft, iosSigningIdentitySha1Setting as g, repoRoot as gt, iosSigningIdentitySettingError as h, removeWorktree as ht, SETTING_SHAPE_REMEDY as i, gitCommonDir as it, remoteAndroidSetting as j, publicUrlSetting as k, androidDataPartitionSizeGbSettingError as l, isPodInstallChurn as lt, iosSigningIdentitySetting as m, readWorktreeExclude as mt, PATH_SETTINGS as n, dirtyFingerprintFiles as nt, androidAvdConfigSettingError as o, hasPopulatedSubmodules as ot, iosLanHostSettingError as p, podsOutOfSync as pt, resolveTailscaleBinary as q, SETTINGS_WITH_RESOLVE_TIME_FALLBACK as r, dirtyPaths as rt, androidDataPartitionSizeBytes as s, hasRemote as st, ANDROID_AVD_CONFIG_HELP as t, depsOutOfSync as tt, cacheProviderSettingError as u, linkedWorktreesOnDisk as ut, iosSimSlimProfileSetting as v, resolveSourceCheckout as vt, ngrokUrlSetting as w, makeTemporaryDirectory as wt, metroPortSetting as x, unpushedCommits as xt, mergeSettingsLayers as y, restoreFile as yt, settingValueAt as z };
@@ -1,8 +1,8 @@
1
1
  import { D as withConfigLock$1, O as writeConfigSetting, _ as loadConfig$1, f as getConfigPath$1, h as getRepoSettings, m as getProjectSettings } from "./config-D0DbpZtM.mjs";
2
- import { A as readCommittedSettings, H as unknownSettingKeys, gt as repoRoot, it as gitCommonDir } from "./settings-oTSkkNrM.mjs";
3
- import { a as findCommandWorkspace, t as NO_PROJECT_REFUSAL } from "./project-Du91Ilvc.mjs";
2
+ import { A as readCommittedSettings, H as unknownSettingKeys, gt as repoRoot, it as gitCommonDir } from "./settings-Cfd-0r2d.mjs";
3
+ import { o as findCommandWorkspace, t as NO_PROJECT_REFUSAL } from "./project-D47Rh0ly.mjs";
4
4
  import { n as settingDefault, r as stimDesktopInstalled } from "./stim-desktop-Cnz8CPnl.mjs";
5
- import { n as recordingLayers, t as deleteTurnedOffRecordings } from "./recordings-BIXOzVnr.mjs";
5
+ import { n as recordingLayers, t as deleteTurnedOffRecordings } from "./recordings-BNgsD1Tr.mjs";
6
6
  import { existsSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
7
7
  import { join } from "node:path";
8
8
  import { SETTINGS, SETTING_SCOPES, coerceSettingText, isJsonObject, isSensitiveReference, settingDefinition, settingValueError } from "@stim-cli/core/state";
@@ -209,7 +209,7 @@
209
209
  }
210
210
  },
211
211
  "targetAbiOnly": {
212
- "description": "Narrow Debug builds to the device ABI",
212
+ "description": "Narrow Debug and hosted Android builds to the device ABI",
213
213
  "type": "boolean",
214
214
  "default": true,
215
215
  "x-stim": {
@@ -275,7 +275,7 @@
275
275
  }
276
276
  },
277
277
  "remote": {
278
- "description": "Default iOS remote target: eas, proxy, or an approved Mac in hosting.machines; auto is not available yet",
278
+ "description": "Default iOS remote target: eas, proxy, or an approved Mac in hosting.machines; auto places on an approved Mac when this Mac is full or busy",
279
279
  "type": "string",
280
280
  "pattern": "^[A-Za-z0-9][A-Za-z0-9.-]*(?::(?!0+$)(?:[0-5]?[0-9]{1,4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]))?$",
281
281
  "x-stim": {
@@ -463,15 +463,12 @@
463
463
  }
464
464
  },
465
465
  "remote": {
466
- "description": "Default remote backend for Android",
466
+ "description": "Default Android remote target: eas, proxy, or an approved Mac in hosting.machines; auto places on an approved Mac when this Mac is full or busy",
467
467
  "type": "string",
468
- "enum": [
469
- "proxy",
470
- "eas"
471
- ],
468
+ "pattern": "^[A-Za-z0-9][A-Za-z0-9.-]*(?::(?!0+$)(?:[0-5]?[0-9]{1,4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]))?$",
472
469
  "x-stim": {
473
470
  "key": "android.remote",
474
- "kind": "choice",
471
+ "kind": "string",
475
472
  "scopes": [
476
473
  "workspace",
477
474
  "repo",
@@ -606,7 +603,7 @@
606
603
  "description": "Minutes an owned simulator or emulator stays idle before the workspace supervisor shuts it down; 0 never",
607
604
  "type": "integer",
608
605
  "minimum": 0,
609
- "default": 0,
606
+ "default": 30,
610
607
  "x-stim": {
611
608
  "key": "devices.idleShutdownMinutes",
612
609
  "kind": "number",
@@ -617,6 +614,22 @@
617
614
  "committed"
618
615
  ]
619
616
  }
617
+ },
618
+ "reclaimIdleMinutes": {
619
+ "description": "Minutes an owned simulator or emulator stays idle before a waiting run reclaims its device slot; 0 never",
620
+ "type": "integer",
621
+ "minimum": 0,
622
+ "default": 10,
623
+ "x-stim": {
624
+ "key": "devices.reclaimIdleMinutes",
625
+ "kind": "number",
626
+ "scopes": [
627
+ "machine",
628
+ "workspace",
629
+ "repo",
630
+ "committed"
631
+ ]
632
+ }
620
633
  }
621
634
  },
622
635
  "additionalProperties": false
@@ -1052,7 +1065,7 @@
1052
1065
  }
1053
1066
  },
1054
1067
  "targetAbiOnly": {
1055
- "description": "Narrow Debug builds to the device ABI",
1068
+ "description": "Narrow Debug and hosted Android builds to the device ABI",
1056
1069
  "type": "boolean",
1057
1070
  "default": true,
1058
1071
  "x-stim": {
@@ -1148,7 +1161,7 @@
1148
1161
  "description": "Minutes an owned simulator or emulator stays idle before the workspace supervisor shuts it down; 0 never",
1149
1162
  "type": "integer",
1150
1163
  "minimum": 0,
1151
- "default": 0,
1164
+ "default": 30,
1152
1165
  "x-stim": {
1153
1166
  "key": "devices.idleShutdownMinutes",
1154
1167
  "kind": "number",
@@ -1159,6 +1172,22 @@
1159
1172
  "committed"
1160
1173
  ]
1161
1174
  }
1175
+ },
1176
+ "reclaimIdleMinutes": {
1177
+ "description": "Minutes an owned simulator or emulator stays idle before a waiting run reclaims its device slot; 0 never",
1178
+ "type": "integer",
1179
+ "minimum": 0,
1180
+ "default": 10,
1181
+ "x-stim": {
1182
+ "key": "devices.reclaimIdleMinutes",
1183
+ "kind": "number",
1184
+ "scopes": [
1185
+ "machine",
1186
+ "workspace",
1187
+ "repo",
1188
+ "committed"
1189
+ ]
1190
+ }
1162
1191
  }
1163
1192
  },
1164
1193
  "additionalProperties": false
@@ -1789,7 +1818,7 @@
1789
1818
  }
1790
1819
  },
1791
1820
  "agentDriver": {
1792
- "description": "Tool this Mac starts so a client's coding agent can drive the macOS apps and iOS simulators it hosts for that client; none starts nothing",
1821
+ "description": "Tool this Mac starts so a client's coding agent can drive the macOS apps, iOS simulators and Android emulators it hosts for that client; none starts nothing",
1793
1822
  "type": "string",
1794
1823
  "enum": [
1795
1824
  "none",
@@ -1875,7 +1904,7 @@
1875
1904
  }
1876
1905
  },
1877
1906
  "maxLoadPerCore": {
1878
- "description": "Load per core (5-minute load average divided by the CPU count) at which a Mac counts as saturated: a build machine declines offloaded builds, and auto offload stops preferring this Mac",
1907
+ "description": "Load per core (5-minute load average divided by the CPU count) at which a Mac counts as saturated: a build machine declines offloaded builds, and auto offload stops preferring this Mac; also the busy threshold for automatic iOS and Android device placement",
1879
1908
  "type": "number",
1880
1909
  "minimum": 0.1,
1881
1910
  "default": 2,
@@ -2,9 +2,9 @@ import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
2
  import { d as getConfigDir } from "./config-D0DbpZtM.mjs";
3
3
  import { n as ownership_claim_exports, t as process_identity_exports } from "./process-identity-BmRm1qV0.mjs";
4
4
  import { n as stripAnsi, r as waitForChild, t as createLineReader } from "./process-output-Fpl8PYi8.mjs";
5
- import { g as releaseBuildLock, m as acquireBuildLock } from "./device-lease-D-hLqx1b.mjs";
6
- import { f as resolveEasCliBin } from "./remote-cache-C8-V13XN.mjs";
7
- import { _ as easCliSupport, s as readInstalledEasCliVersion, v as easCliUpgradeRemedy } from "./device-remote-D5WVWqOq.mjs";
5
+ import { g as releaseBuildLock, m as acquireBuildLock } from "./device-lease-DpoS36_u.mjs";
6
+ import { f as resolveEasCliBin } from "./remote-cache-Ci4DQYYj.mjs";
7
+ import { _ as easCliSupport, s as readInstalledEasCliVersion, v as easCliUpgradeRemedy } from "./device-remote-hFSkpfQu.mjs";
8
8
  import { createHash } from "node:crypto";
9
9
  import { existsSync, realpathSync, statSync } from "node:fs";
10
10
  import { join } from "node:path";
@@ -1,16 +1,16 @@
1
1
  import { B as parseDeviceSlotKey, H as projectDeviceSlots, p as getProject } from "./config-D0DbpZtM.mjs";
2
- import { r as readWorkspaceState$1 } from "./workspace-state-B8-aJ1_5.mjs";
3
- import { n as fileLeaseIo } from "./device-lease-D-hLqx1b.mjs";
4
- import { E as getAvdNameForSerial, O as listAdbDevices } from "./android-B_NumVyQ.mjs";
5
- import { u as listAllIosSims } from "./ios-BaInn3sW.mjs";
6
- import { f as deviceShellArg } from "./app-install-CKWdbXIX.mjs";
7
- import { hostedIosRecords } from "@stim-cli/core/state";
2
+ import { r as readWorkspaceState$1 } from "./workspace-state-DqcCbpE7.mjs";
3
+ import { n as fileLeaseIo } from "./device-lease-DpoS36_u.mjs";
4
+ import { E as getAvdNameForSerial, O as listAdbDevices } from "./android-B_YNZy61.mjs";
5
+ import { u as listAllIosSims } from "./ios-DSfnPfse.mjs";
6
+ import { f as deviceShellArg } from "./app-install-VPkI5WEg.mjs";
7
+ import { hostedNativeRecords } from "@stim-cli/core/state";
8
8
  //#region src/engine/slot-launch.ts
9
9
  function launchSlotScope(root, slot = "default") {
10
10
  if (slot !== "default") return slot;
11
11
  if (Object.keys(getProject(root)?.deviceSlots ?? {}).length) return slot;
12
12
  const state = readWorkspaceState$1(root);
13
- if (Object.keys(hostedIosRecords(state)).some((other) => other !== "default")) return slot;
13
+ if (["ios", "android"].some((platform) => Object.keys(hostedNativeRecords(state, platform)).some((other) => other !== "default"))) return slot;
14
14
  if (Object.keys({
15
15
  ...state?.collectors,
16
16
  ...fileLeaseIo.readHolder(root)
@@ -46,7 +46,7 @@ function ownedDeviceRunning(platform) {
46
46
  function siblingPlatformSlots(root, platform, slot = "default", { deviceRunning = ownedDeviceRunning(platform) } = {}) {
47
47
  const slots = /* @__PURE__ */ new Set();
48
48
  const state = readWorkspaceState$1(root);
49
- if (platform === "ios") for (const hosted of Object.keys(hostedIosRecords(state))) slots.add(hosted);
49
+ for (const hosted of Object.keys(hostedNativeRecords(state, platform))) slots.add(hosted);
50
50
  for (const key of Object.keys({
51
51
  ...state?.collectors,
52
52
  ...fileLeaseIo.readHolder(root)
@@ -1,2 +1,2 @@
1
- import { a as registerStart, c as startDevServer, d as tailLines, f as wantsExpoOwnTunnel, i as readLogTail, l as startFacts, n as liveSupervisor, o as resolveWorkspaceMetroPort, r as parseWait, s as startCommand, t as failureEvidence, u as supervisorEntry } from "./start-ClDfwXa4.mjs";
1
+ import { a as registerStart, c as startDevServer, d as tailLines, f as wantsExpoOwnTunnel, i as readLogTail, l as startFacts, n as liveSupervisor, o as resolveWorkspaceMetroPort, r as parseWait, s as startCommand, t as failureEvidence, u as supervisorEntry } from "./start-uee1wWjY.mjs";
2
2
  export { startCommand as default, failureEvidence, liveSupervisor, parseWait, readLogTail, registerStart, resolveWorkspaceMetroPort, startDevServer, startFacts, supervisorEntry, tailLines, wantsExpoOwnTunnel };
@@ -2,19 +2,19 @@ import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
2
  import { E as upsertProject, p as getProject } from "./config-D0DbpZtM.mjs";
3
3
  import { t as process_identity_exports } from "./process-identity-BmRm1qV0.mjs";
4
4
  import { n as windowsLauncherArgs } from "./detached-entry-CiTEgMR1.mjs";
5
- import { _ as stepTimer, l as phaseLine } from "./command-output-DnJMdEY-.mjs";
6
- import { C as metroTunnelSettingError, G as detectProviders, H as unknownSettingKeys, K as planMetroReach, M as remoteIosSetting, N as resolveCacheProviderConfig, P as resolveSettings, R as settingShapeErrors, S as metroPortSetting, T as ngrokUrlSetting, V as tunnelModeSetting, W as PUBLIC_METRO_ENV, a as SETTING_SHAPE_REMEDY, d as cacheProviderSettingError, f as deviceIdleShutdownMinutesSetting, gt as repoRoot, it as gitCommonDir, j as remoteAndroidSetting, k as publicUrlSetting, x as metroIdleStopMinutesSetting } from "./settings-oTSkkNrM.mjs";
7
- import { d as readAppConfigText, f as readAppJson, n as appProjectProblem, o as findProjectRoot$1, r as detectIsExpo, t as NO_PROJECT_REFUSAL } from "./project-Du91Ilvc.mjs";
8
- import { l as resolveProjectMetro, o as pidExists, p as reserveMetroPort, u as signalProcessTree } from "./metro-BeK1ZPz1.mjs";
9
- import { c as writeWorkspaceState, g as workspaceAgentDeviceDir, i as recordWorkspaceUse, m as supervisorLogFile, n as clearWorkspaceStateKeys, r as readWorkspaceState$1, u as ensureWorkspaceStorage, x as workspaceLogsDir$1 } from "./workspace-state-B8-aJ1_5.mjs";
10
- import { c as stopOwnedMetroForReset, t as budgetGate } from "./budget-DjrURfQn.mjs";
11
- import { c as readMetroTunnel, i as clearManagedMetroTunnel } from "./state-DcW6vOd_.mjs";
5
+ import { _ as stepTimer, l as phaseLine } from "./command-output-CDAIFD-8.mjs";
6
+ import { G as detectProviders, H as unknownSettingKeys, K as planMetroReach, M as remoteIosSetting, N as resolveCacheProviderConfig, P as resolveSettings, R as settingShapeErrors, S as metroTunnelSettingError, V as tunnelModeSetting, W as PUBLIC_METRO_ENV, b as metroIdleStopMinutesSetting, d as deviceIdleShutdownMinutesSetting, gt as repoRoot, i as SETTING_SHAPE_REMEDY, it as gitCommonDir, j as remoteAndroidSetting, k as publicUrlSetting, u as cacheProviderSettingError, w as ngrokUrlSetting, x as metroPortSetting } from "./settings-Cfd-0r2d.mjs";
7
+ import { f as readAppConfigText, n as appProjectProblem, p as readAppJson, r as detectIsExpo, s as findProjectRoot$1, t as NO_PROJECT_REFUSAL } from "./project-D47Rh0ly.mjs";
8
+ import { l as resolveProjectMetro, o as pidExists, p as reserveMetroPort, u as signalProcessTree } from "./metro-g8NqiJp-.mjs";
9
+ import { c as writeWorkspaceState, g as workspaceAgentDeviceDir, i as recordWorkspaceUse, m as supervisorLogFile, n as clearWorkspaceStateKeys, r as readWorkspaceState$1, u as ensureWorkspaceStorage, x as workspaceLogsDir$1 } from "./workspace-state-DqcCbpE7.mjs";
10
+ import { l as stopOwnedMetroForReset, t as budgetGate } from "./budget-DM984mnd.mjs";
11
+ import { c as readMetroTunnel, i as clearManagedMetroTunnel } from "./state-BKa8wNN5.mjs";
12
12
  import { a as workspaceLinks, i as workspaceLinkLine } from "./stim-desktop-Cnz8CPnl.mjs";
13
- import { n as workspaceProcessLockError, t as withWorkspaceProcessLock } from "./workspace-process-lock-BL7Q0HCO.mjs";
14
- import { a as terminateChild, c as withManagedTunnelLock, i as stopTunnel, o as withManagedRemoteWorktreeLock, r as startTunnelSequence, t as resolveSupervisorTarget } from "./ownership-hCkPzGmr.mjs";
13
+ import { a as terminateChild, c as withManagedTunnelLock, i as stopTunnel, o as withManagedRemoteWorktreeLock, r as startTunnelSequence, t as resolveSupervisorTarget } from "./ownership-BMLqyBgJ.mjs";
14
+ import { n as workspaceProcessLockError, t as withWorkspaceProcessLock } from "./workspace-process-lock-2mNzOlaB.mjs";
15
15
  import { i as levelRank } from "./ndjson-PclOIwVY.mjs";
16
16
  import { t as spawnEntry } from "./spawn-entry-jw19m8Nl.mjs";
17
- import { d as readProjectConfig } from "./remote-cache-C8-V13XN.mjs";
17
+ import { d as readProjectConfig } from "./remote-cache-Ci4DQYYj.mjs";
18
18
  import { appendFileSync, mkdirSync, openSync, readFileSync } from "node:fs";
19
19
  import { dirname } from "node:path";
20
20
  import { EventEmitter } from "node:events";
@@ -340,7 +340,7 @@ async function startDevServer({ root, settings, waitSeconds = DEFAULT_WAIT_SECON
340
340
  message: settingError,
341
341
  remedy: "Set metro.tunnel to \"ngrok\" and metro.ngrokUrl to an HTTPS URL, or remove metro.ngrokUrl."
342
342
  });
343
- const remote = remoteFlag || remoteIosSetting(settings)?.kind === "backend" || remoteAndroidSetting(settings) !== null;
343
+ const remote = remoteFlag || remoteIosSetting(settings)?.kind === "backend" || remoteAndroidSetting(settings)?.kind === "backend";
344
344
  const tunnelMode = tunnelModeSetting(settings) ?? "auto";
345
345
  const publicUrl = publicUrlSetting(settings);
346
346
  const tunnel = wantsExpoOwnTunnel({
@@ -1,6 +1,6 @@
1
1
  import { B as parseDeviceSlotKey, R as deviceSlotKey } from "./config-D0DbpZtM.mjs";
2
2
  import { t as process_identity_exports } from "./process-identity-BmRm1qV0.mjs";
3
- import { C as workspaceStateFile, a as updateWorkspaceState, h as supervisorPidFile, o as withWorkspaceStateLock, r as readWorkspaceState, t as clearWorkspaceStateKey } from "./workspace-state-B8-aJ1_5.mjs";
3
+ import { C as workspaceStateFile, a as updateWorkspaceState, h as supervisorPidFile, o as withWorkspaceStateLock, r as readWorkspaceState, t as clearWorkspaceStateKey } from "./workspace-state-DqcCbpE7.mjs";
4
4
  import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
5
5
  import { dirname } from "node:path";
6
6
  //#region src/supervisor/state.ts
@@ -1,5 +1,5 @@
1
1
  import { t as process_identity_exports } from "./process-identity-BmRm1qV0.mjs";
2
- import { a as updateWorkspaceState, r as readWorkspaceState$1, v as workspaceDir, x as workspaceLogsDir$1 } from "./workspace-state-B8-aJ1_5.mjs";
2
+ import { a as updateWorkspaceState, r as readWorkspaceState$1, v as workspaceDir, x as workspaceLogsDir$1 } from "./workspace-state-DqcCbpE7.mjs";
3
3
  import { join } from "node:path";
4
4
  import { readMacosRecord } from "@stim-cli/core/state";
5
5
  //#region src/macos/state.ts
@@ -1,6 +1,6 @@
1
1
  import { n as ownership_claim_exports, t as process_identity_exports } from "./process-identity-BmRm1qV0.mjs";
2
- import { o as pidExists } from "./metro-BeK1ZPz1.mjs";
3
- import { a as updateWorkspaceState, c as writeWorkspaceState, r as readWorkspaceState$1, t as clearWorkspaceStateKey, v as workspaceDir, x as workspaceLogsDir$1 } from "./workspace-state-B8-aJ1_5.mjs";
2
+ import { o as pidExists } from "./metro-g8NqiJp-.mjs";
3
+ import { a as updateWorkspaceState, c as writeWorkspaceState, r as readWorkspaceState$1, t as clearWorkspaceStateKey, v as workspaceDir, x as workspaceLogsDir$1 } from "./workspace-state-DqcCbpE7.mjs";
4
4
  import { readFileSync, readlinkSync, rmSync } from "node:fs";
5
5
  import { join } from "node:path";
6
6
  import { rotatedLogPath } from "@stim-cli/core";
@@ -1,8 +1,8 @@
1
1
  import { j as formatBytes } from "./config-D0DbpZtM.mjs";
2
- import { o as formatLongDuration } from "./command-output-DnJMdEY-.mjs";
3
- import { gt as repoRoot, it as gitCommonDir } from "./settings-oTSkkNrM.mjs";
4
- import { o as findProjectRoot$1 } from "./project-Du91Ilvc.mjs";
5
- import { i as getSwiftpmCacheUsage, n as swiftpmCacheLines, r as getAgentDeviceUsage, t as agentDeviceLines } from "./agent-device-usage-output-Dpih1Tfa.mjs";
2
+ import { o as formatLongDuration } from "./command-output-CDAIFD-8.mjs";
3
+ import { gt as repoRoot, it as gitCommonDir } from "./settings-Cfd-0r2d.mjs";
4
+ import { s as findProjectRoot$1 } from "./project-D47Rh0ly.mjs";
5
+ import { i as getSwiftpmCacheUsage, n as swiftpmCacheLines, r as getAgentDeviceUsage, t as agentDeviceLines } from "./agent-device-usage-output-BK41vnzN.mjs";
6
6
  import { archivedUsage, readArchives, readStatsReport, statsProjectKey } from "@stim-cli/core/state";
7
7
  import chalk from "chalk";
8
8
  //#region src/commands/stats.ts
@@ -34,6 +34,9 @@ function statsCommand(program) {
34
34
  const usage = archivedUsage(readArchives());
35
35
  lines.push("archive", ` archived workspaces: ${usage.count}, ${formatBytes(usage.bytes)}`);
36
36
  if (offload.placements.length || Object.keys(offload.machines).length) lines.push(...placementLines(offload));
37
+ const reclaimedWaits = (report.capacityWaits ?? []).filter((event) => event.reclaimed);
38
+ if (reclaimedWaits.length) lines.push("device queue reclaim");
39
+ for (const wait of reclaimedWaits) lines.push(chalk.dim(` ${wait.at} ${wait.platform} device slot wait ${formatLongDuration(wait.ms)}, reclaimed ${wait.reclaimed}`));
37
40
  const agentLines = agentDeviceLines(report.agentDevice);
38
41
  if (agentLines.length) lines.push(...agentLines, chalk.dim(" Stim never trims or deletes the shared runner builds, sessions, logs and other state or the hosted driver dir; a workspace's own agent-device dir goes only with its workspace."));
39
42
  lines.push(...swiftpmCacheLines(report.swiftpmCache));
@@ -1,8 +1,8 @@
1
1
  import { H as projectDeviceSlots, R as deviceSlotKey } from "./config-D0DbpZtM.mjs";
2
- import { a as formatElapsed, o as formatLongDuration, r as clockTime, u as plural } from "./command-output-DnJMdEY-.mjs";
3
- import { n as isInsideProject } from "./metro-BeK1ZPz1.mjs";
4
- import { g as workspaceAgentDeviceDir } from "./workspace-state-B8-aJ1_5.mjs";
5
- import { r as cdpEndpoint } from "./state-C1jB7Q8q.mjs";
2
+ import { a as formatElapsed, o as formatLongDuration, r as clockTime, u as plural } from "./command-output-CDAIFD-8.mjs";
3
+ import { n as isInsideProject } from "./metro-g8NqiJp-.mjs";
4
+ import { g as workspaceAgentDeviceDir } from "./workspace-state-DqcCbpE7.mjs";
5
+ import { r as cdpEndpoint } from "./state-PeQiJuIu.mjs";
6
6
  import { basename, sep } from "path";
7
7
  import { ACTIVITY_RECENCY_BASES } from "@stim-cli/core/state";
8
8
  //#region src/status.ts