stim 1.30.0 → 1.32.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 (144) hide show
  1. package/dist/{acquire-artifact-CrZ4rEbI.mjs → acquire-artifact-CI9fIokp.mjs} +1 -1
  2. package/dist/{act-BaKjDWGw.mjs → act-BfChMxcU.mjs} +71 -22
  3. package/dist/{activity-BTaUf0tc.mjs → activity-yjH7Q3zn.mjs} +4 -4
  4. package/dist/{agent-device-usage-output-DYBpO17K.mjs → agent-device-usage-output-DO412bsG.mjs} +4 -4
  5. package/dist/{android-BWqdDK6R.mjs → android-BNm5A40S.mjs} +41 -39
  6. package/dist/{android-BEQUDpOg.mjs → android-BeeC15Ve.mjs} +1 -1
  7. package/dist/{android-BCYLwmH6.mjs → android-D56S_GX3.mjs} +5 -5
  8. package/dist/{android-DdKG6lyD.mjs → android-DsEj2CCs.mjs} +27 -6
  9. package/dist/{android-cas-BH6K12__.mjs → android-cas-CbumHXIq.mjs} +4 -4
  10. package/dist/api.mjs +26 -26
  11. package/dist/{apk-signed-tail-B8kw4a8K.mjs → apk-signed-tail-B9Usseps.mjs} +2 -2
  12. package/dist/{artifact-M6W2l-xh.mjs → artifact-C8Nvp07y.mjs} +12 -12
  13. package/dist/{artifact-DTHXcmQ3.mjs → artifact-k1x9Jrn0.mjs} +12 -12
  14. package/dist/{auto-placement-CZaWDmcY.mjs → auto-placement--nfdQ9rp.mjs} +10 -10
  15. package/dist/{browser-web-JQ7lFej3.mjs → browser-web-6RDAeH0z.mjs} +11 -11
  16. package/dist/{build-C2ZWoCmg.mjs → build-VXX_dtl5.mjs} +22 -22
  17. package/dist/{build-cache-DlC3owap.mjs → build-cache-BsKim4V7.mjs} +4 -4
  18. package/dist/{build-plan-J3iRR3gI.mjs → build-plan-BK34X2iY.mjs} +6 -6
  19. package/dist/{cache-manifest-evF_zJ9U.mjs → cache-manifest-CfiEny4T.mjs} +1 -1
  20. package/dist/cache-manifest.mjs +1 -1
  21. package/dist/{ccache-B8gY50-h.mjs → ccache-BrwGIxmV.mjs} +15 -15
  22. package/dist/{cli-xRPXJPtA.mjs → cli-Jc7obreB.mjs} +18 -18
  23. package/dist/cli.mjs +1 -1
  24. package/dist/{client-DrHykA24.mjs → client-lDmA-5jx.mjs} +4 -4
  25. package/dist/collector-run.mjs +4 -4
  26. package/dist/{command-output-BBW8eWPL.mjs → command-output-CfPW1bKP.mjs} +1 -1
  27. package/dist/{config-AbUjIBOQ.mjs → config-D5qNWJEP.mjs} +2 -2
  28. package/dist/{dependencies-vUh9mp5z.mjs → dependencies-DkZ8681S.mjs} +37 -37
  29. package/dist/{dependency-state-CPKAKCZ8.mjs → dependency-state-DZVln1P9.mjs} +1 -1
  30. package/dist/{dev-client-CASgAOu7.mjs → dev-client-eDZy6RoX.mjs} +2 -2
  31. package/dist/{device-CsIdRXfm.mjs → device-CTozKhU0.mjs} +11 -11
  32. package/dist/{device-ChnO7bR9.mjs → device-CeIikWac.mjs} +12 -12
  33. package/dist/{device-android-CcdQqQWg.mjs → device-android-zgD9We85.mjs} +20 -14
  34. package/dist/{device-capacity-C5C_jOQd.mjs → device-capacity-Bki6B-7h.mjs} +173 -74
  35. package/dist/device-host-worker.mjs +19 -15
  36. package/dist/{device-ios-DbmtPIiw.mjs → device-ios-TE5ejeXx.mjs} +8 -8
  37. package/dist/{device-lease-run-Do6zYA7y.mjs → device-lease-run-DeNGjJZM.mjs} +4 -4
  38. package/dist/{device-pool-CpoYiCGO.mjs → device-pool-mjyUYaau.mjs} +3 -3
  39. package/dist/{device-provision-B5Ms94qY.mjs → device-provision-j_B7G0e6.mjs} +8 -8
  40. package/dist/{doctor-CJSB5q-s.mjs → doctor-BlrcoQ7B.mjs} +36 -25
  41. package/dist/{doctor-CpfbYh3Q.mjs → doctor-CNZuvTSe.mjs} +1 -1
  42. package/dist/doctor-fingerprint.mjs +1 -1
  43. package/dist/{doctor-CLE4AZEI.mjs → doctor-sSLtn-j1.mjs} +15 -15
  44. package/dist/{doctor-watchman-D1IyMle9.mjs → doctor-watchman-CCuboyj1.mjs} +4 -4
  45. package/dist/{eas-build-DIDOHw2P.mjs → eas-build-CaG-Sfs9.mjs} +2 -2
  46. package/dist/{eas-fallback-DVS9oBSc.mjs → eas-fallback-BpX1lpco.mjs} +4 -4
  47. package/dist/{eas-session-ledger-CW3l-inB.mjs → eas-session-ledger-B-nTfdfk.mjs} +19 -6
  48. package/dist/{error-diagnostics-XJ9eWdtq.mjs → error-diagnostics-q56Im4D8.mjs} +8 -8
  49. package/dist/{gc-BVIcxCxE.mjs → gc-By2Kc-Ek.mjs} +2 -2
  50. package/dist/{gc-Co7PE36D.mjs → gc-Dv6nb9nf.mjs} +17 -17
  51. package/dist/{guide-DkepBgHT.mjs → guide-CwpsGWTJ.mjs} +141 -39
  52. package/dist/{hosted-android-Du7NxufJ.mjs → hosted-android-c5nPJA6S.mjs} +3 -3
  53. package/dist/{hosted-client-Cect92SZ.mjs → hosted-client-Hs-fD4L3.mjs} +36 -12
  54. package/dist/{hosted-logs-Opcjv1Lr.mjs → hosted-logs-BteXG8rm.mjs} +16 -10
  55. package/dist/{hosted-logs-sync-wsI7N1Hv.mjs → hosted-logs-sync-B8bjLEMs.mjs} +5 -5
  56. package/dist/{hosted-macos-BP9Kucat.mjs → hosted-macos-B4byscei.mjs} +5 -5
  57. package/dist/{hosted-native-DmdLSmvu.mjs → hosted-native-Bh9IJ_t5.mjs} +7 -7
  58. package/dist/{idle-shutdown-BcMN_cLm.mjs → idle-shutdown-BaeCo4ps.mjs} +6 -6
  59. package/dist/{ios-CXnoVO_z.mjs → ios-CdAV4-1E.mjs} +4 -4
  60. package/dist/{ios-BFkBHuRT.mjs → ios-D1XKSITX.mjs} +8 -8
  61. package/dist/{ios-CG0IsLtt.mjs → ios-D3oK9-O4.mjs} +32 -31
  62. package/dist/{ios-device-ClSfuu-_.mjs → ios-device-D7Gi3e1I.mjs} +1 -1
  63. package/dist/{ios-project-BM9TbTE7.mjs → ios-project-DDQlX_jX.mjs} +7 -7
  64. package/dist/{ios-state-Ce_dystM.mjs → ios-state-BEIJRJ7P.mjs} +1 -1
  65. package/dist/{js-swap-9Rx-5Nqz.mjs → js-swap-C8N5DXHl.mjs} +4 -4
  66. package/dist/{launch-DvJV1Mx4.mjs → launch-DxpvL6il.mjs} +19 -19
  67. package/dist/{lightened-23pqe7LS.mjs → lightened-Df6II_XI.mjs} +4 -4
  68. package/dist/{logs-DJgZlf2K.mjs → logs-CuWJWF88.mjs} +7 -7
  69. package/dist/{logs-Pkeh3wZk.mjs → logs-zv3ge7c6.mjs} +5 -5
  70. package/dist/{macos-DPvPQadc.mjs → macos-C6U-4Iip.mjs} +19 -19
  71. package/dist/{macos-Du07NsOB.mjs → macos-CbRZheLw.mjs} +1 -1
  72. package/dist/macos-run.mjs +1 -1
  73. package/dist/maintenance-run.mjs +5 -5
  74. package/dist/{metro-BMQHczcf.mjs → metro-mKHEMO-a.mjs} +2 -2
  75. package/dist/{metro-request-BtF7ox53.mjs → metro-request-DODHNXCN.mjs} +8 -2
  76. package/dist/{named-ports-DcktE2Pw.mjs → named-ports-B0hCk2v-.mjs} +2 -2
  77. package/dist/{native-android-CBOM1_gB.mjs → native-android-9dVFqvTT.mjs} +16 -16
  78. package/dist/{native-gradle-artifact-inputs-D_iAfPuS.mjs → native-gradle-artifact-inputs-BlY97pt8.mjs} +3 -3
  79. package/dist/{native-runtime-D4cTwU0Z.mjs → native-runtime-DQfxW5Au.mjs} +6 -6
  80. package/dist/{native-xcode-inputs-BF-ogSnG.mjs → native-xcode-inputs-tDuUvSU8.mjs} +2 -2
  81. package/dist/{native-xcode-ios-DwJBfMP6.mjs → native-xcode-ios-CG-PD-7g.mjs} +15 -15
  82. package/dist/offload-worker.mjs +19 -19
  83. package/dist/{ownership-Dtz9G5Gl.mjs → ownership-CCfiBfOP.mjs} +2 -2
  84. package/dist/{phone-reverses-CvhhkGMH.mjs → phone-reverses-CnNwdz9A.mjs} +4 -4
  85. package/dist/{placement-log-CRR_TE1r.mjs → placement-log-CRL0IY-k.mjs} +1 -1
  86. package/dist/{plan-placement-CdLc-pTb.mjs → plan-placement-2imvnlWP.mjs} +6 -6
  87. package/dist/{ports-g4N_cfyg.mjs → ports-CKktj5lV.mjs} +3 -3
  88. package/dist/{prebuild-DBJMhwSS.mjs → prebuild-CyNrUWgV.mjs} +9 -9
  89. package/dist/{project-DPQjnpcI.mjs → project-Crtcbfl0.mjs} +15 -15
  90. package/dist/{react-native-android-idMWOwkX.mjs → react-native-android-Cabe2YER.mjs} +22 -22
  91. package/dist/{react-native-build-BSoh4HkH.mjs → react-native-build-DhWBUV_H.mjs} +7 -7
  92. package/dist/{react-native-doctor-CYibY8-4.mjs → react-native-doctor-D-twmBon.mjs} +5 -5
  93. package/dist/{react-native-ios-13KIIOOy.mjs → react-native-ios-u_ley41B.mjs} +14 -14
  94. package/dist/{recordings-CHMaF73C.mjs → recordings-BWnIlhZH.mjs} +1 -1
  95. package/dist/{reload-BUQI3dSu.mjs → reload-D2TpUlns.mjs} +14 -14
  96. package/dist/{reload-B9DZOjcV.mjs → reload-f9hvEHE9.mjs} +1 -1
  97. package/dist/{remote-C-RGHhAP.mjs → remote-BsBAjlD2.mjs} +8 -8
  98. package/dist/{result-W4HCum4A.mjs → result-EOsWV6sp.mjs} +16 -16
  99. package/dist/{run-BQAmrohh.mjs → run-2CHAJ9Oj.mjs} +1 -1
  100. package/dist/{selection-PCMT97XD.mjs → selection-C4ZQz_ER.mjs} +3 -3
  101. package/dist/{server-bare-CEQdB_F4.mjs → server-bare-CFyVFZXJ.mjs} +1 -1
  102. package/dist/{server-command-oqi3TDy7.mjs → server-command-BMyKtnjH.mjs} +4 -4
  103. package/dist/{server-expo-C1K07shx.mjs → server-expo-BLxcN2x2.mjs} +2 -2
  104. package/dist/server-expo-DwR3eD-a.mjs +2 -0
  105. package/dist/{settings-D776ygML.mjs → settings-C6UTYW2K.mjs} +1 -1
  106. package/dist/{settings-CKlMOmhj.mjs → settings-jeS0WmUF.mjs} +2 -2
  107. package/dist/{settings-payload-CE-NZ6Lq.mjs → settings-payload-CrX933Xi.mjs} +4 -4
  108. package/dist/{settings-write-D7Xwa_-M.mjs → settings-write-DMcT23Kl.mjs} +5 -5
  109. package/dist/settings.schema.json +47 -5
  110. package/dist/{simslim-CbpHERcv.mjs → simslim-93xmPh-d.mjs} +1 -1
  111. package/dist/{slot-launch-CeIK1iUI.mjs → slot-launch-CSpQcFZw.mjs} +6 -6
  112. package/dist/{stage-Blqe6bWb.mjs → stage-Cf9uSM1a.mjs} +1 -1
  113. package/dist/{start-B7J5a9nS.mjs → start-CLN_r9EG.mjs} +10 -10
  114. package/dist/{start-CvP2pJjc.mjs → start-CquYgzVf.mjs} +1 -1
  115. package/dist/{state-D5ZxwCGJ.mjs → state-CM_HvvkS.mjs} +2 -2
  116. package/dist/{state-C2AUjjhg.mjs → state-CiflgxTP.mjs} +1 -1
  117. package/dist/{stats-Uo7l8OAk.mjs → stats-BVEZuSlg.mjs} +4 -4
  118. package/dist/{status-CqJI1TgG.mjs → status-Bhdy07v0.mjs} +1 -1
  119. package/dist/{status-_rWXFSO7.mjs → status-D0ruTmPl.mjs} +26 -25
  120. package/dist/{status-CK7ZoEhV.mjs → status-GQ-JqvRi.mjs} +1 -1
  121. package/dist/{status-watch-CnSyXUmj.mjs → status-watch-DSu_hhg6.mjs} +4 -4
  122. package/dist/status-worker.mjs +1 -1
  123. package/dist/{stim-desktop-CYXLFpFS.mjs → stim-desktop-1XxET4S8.mjs} +1 -1
  124. package/dist/{stop-CMVfaA-6.mjs → stop-B616ZyTv.mjs} +4 -4
  125. package/dist/{stop-Bogo9oO2.mjs → stop-RZqER_Ww.mjs} +2 -2
  126. package/dist/{stop-CnF9ixYe.mjs → stop-W4DK8qZq.mjs} +15 -15
  127. package/dist/supervisor-run.mjs +134 -42
  128. package/dist/{support-DUevTPur.mjs → support-NxnD8QXd.mjs} +9 -9
  129. package/dist/{support-BGqpYMV8.mjs → support-S_5nkkzs.mjs} +4 -4
  130. package/dist/{swiftpm-artifact-inputs-BcJk6-3u.mjs → swiftpm-artifact-inputs-yp8D5LE4.mjs} +1 -1
  131. package/dist/{swiftpm-macos-DBrmoocG.mjs → swiftpm-macos-Gtq9bNQb.mjs} +8 -8
  132. package/dist/{toolchain-Cw9VVHOY.mjs → toolchain-B2bN8jEo.mjs} +1 -1
  133. package/dist/{toolchain-DLdoyxIt.mjs → toolchain-Bnm8vUCV.mjs} +4 -4
  134. package/dist/{warm-progress-DQ2wQZKp.mjs → warm-progress-_aTMvkIH.mjs} +3 -3
  135. package/dist/{web-g9U7L1sR.mjs → web-BdWG8b5_.mjs} +8 -8
  136. package/dist/web-DwuluWJt.mjs +2 -0
  137. package/dist/web-run.mjs +4 -4
  138. package/dist/{workspace-state-DGwkPXwq.mjs → workspace-state-3bF4TB8P.mjs} +2 -2
  139. package/dist/{workspaces-7_RWoi6c.mjs → workspaces-C59v9bpD.mjs} +19 -15
  140. package/dist/{worktree-qKJ3x07S.mjs → worktree-CinoSsGy.mjs} +24 -24
  141. package/dist/{worktree-DEet9H3p.mjs → worktree-K9F50fD_.mjs} +1 -1
  142. package/package.json +7 -7
  143. package/dist/server-expo-BUFAq2In.mjs +0 -2
  144. package/dist/web-CI_qCRPw.mjs +0 -2
@@ -1,12 +1,12 @@
1
1
  import { D as ownership_claim_exports, d as workspaceProcessLockError, u as withWorkspaceProcessLock } from "./exec-aULwtNly.mjs";
2
- import { G as projectDeviceSlots, H as deviceSlotPlatforms, O as withConfigLockAsync$1, _ as loadConfig$1, d as getConfigDir, u as getConcurrencyLimits } from "./config-AbUjIBOQ.mjs";
3
- import { a as formatElapsed, l as phaseLine } from "./command-output-BBW8eWPL.mjs";
4
- import { g as deviceReclaimIdleMinutesSetting } from "./settings-D776ygML.mjs";
5
- import { M as listAdbDevicesAsync, j as listAdbDevices } from "./android-DdKG6lyD.mjs";
6
- import { ot as recordCapacityRefusal, st as recordCapacityWait, v as withIdleWorkspace } from "./workspaces-7_RWoi6c.mjs";
2
+ import { K as projectDeviceSlots, U as deviceSlotPlatforms, d as getConfigDir, k as withConfigLockAsync$1, p as getPlatformDeviceLimits, u as getConcurrencyLimits, v as loadConfig$1 } from "./config-D5qNWJEP.mjs";
3
+ import { a as formatElapsed, l as phaseLine } from "./command-output-CfPW1bKP.mjs";
4
+ import { g as deviceReclaimIdleMinutesSetting } from "./settings-C6UTYW2K.mjs";
5
+ import { N as listAdbDevices, P as listAdbDevicesAsync } from "./android-DsEj2CCs.mjs";
6
+ import { ot as recordCapacityRefusal, st as recordCapacityWait, v as withIdleWorkspace } from "./workspaces-C59v9bpD.mjs";
7
7
  import { t as canonicalPath } from "./paths-BsJMoPP2.mjs";
8
- import { d as listAllIosSims, f as listAllIosSimsAsync, l as iosRuntimeMatches, y as parseRuntimeVersion } from "./ios-CXnoVO_z.mjs";
9
- import { i as writeIdleShutdownEvent, n as dueIdleDevices, r as shutDownIdleDevices } from "./idle-shutdown-BcMN_cLm.mjs";
8
+ import { d as listAllIosSims, f as listAllIosSimsAsync, l as iosRuntimeMatches, y as parseRuntimeVersion } from "./ios-CdAV4-1E.mjs";
9
+ import { i as writeIdleShutdownEvent, n as dueIdleDevices, r as shutDownIdleDevices } from "./idle-shutdown-BaeCo4ps.mjs";
10
10
  import { realpathSync } from "node:fs";
11
11
  import { basename, join } from "node:path";
12
12
  import { workspaceId } from "@stim-cli/core";
@@ -57,7 +57,7 @@ function workspaceDisplayLabel(root, config) {
57
57
  }
58
58
  //#endregion
59
59
  //#region src/devices/queue-reclaim.ts
60
- async function reclaimIdleDevice(root, idleMs, out, now, skipped) {
60
+ async function reclaimIdleDevice(root, idleMs, out, now, skipped, platform) {
61
61
  try {
62
62
  const self = canonicalPath(root);
63
63
  const candidate = dueIdleDevices(void 0, idleMs, now()).filter(({ device }) => {
@@ -66,7 +66,7 @@ async function reclaimIdleDevice(root, idleMs, out, now, skipped) {
66
66
  } catch {
67
67
  return false;
68
68
  }
69
- }).toSorted((a, b) => b.idleForMs - a.idleForMs).find(({ device }) => !skipped.has(`${device.kind}:${device.id}`));
69
+ }).filter(({ device }) => !platform || device.kind === platform).toSorted((a, b) => b.idleForMs - a.idleForMs).find(({ device }) => !skipped.has(`${device.kind}:${device.id}`));
70
70
  if (!candidate) return 0;
71
71
  const { device, idleForMs } = candidate;
72
72
  const label = workspaceDisplayLabel(device.project, loadConfig$1());
@@ -188,17 +188,61 @@ function inventoryKeys(inventory, exclude = []) {
188
188
  exclude
189
189
  });
190
190
  }
191
- function atCapacityRefusal(count, max) {
191
+ const PLATFORM_CAPS = {
192
+ ios: {
193
+ setting: "concurrency.maxIosDevices",
194
+ noun: "iOS simulator"
195
+ },
196
+ android: {
197
+ setting: "concurrency.maxAndroidDevices",
198
+ noun: "Android emulator"
199
+ }
200
+ };
201
+ function isCappedPlatform(platform) {
202
+ return platform === "ios" || platform === "android";
203
+ }
204
+ function platformCount(keys, platform) {
205
+ return [...keys].filter((key) => key.startsWith(`${platform}:`)).length;
206
+ }
207
+ /** The caps that apply to a boot on `platform` given the booted `keys`, the platform's own cap first. */
208
+ function deviceLimits(keys, platform, max, platformMax) {
209
+ const limits = [];
210
+ if (platformMax > 0 && isCappedPlatform(platform)) limits.push({
211
+ ...PLATFORM_CAPS[platform],
212
+ count: platformCount(keys, platform),
213
+ max: platformMax,
214
+ platform
215
+ });
216
+ if (max > 0) limits.push({
217
+ setting: "concurrency.maxDevices",
218
+ noun: "device",
219
+ count: keys.size,
220
+ max
221
+ });
222
+ return limits;
223
+ }
224
+ /** Whether a queued waiter is held back by its own platform cap, so it does not hold up the runs behind it. */
225
+ function platformWalled(keys, waiter, platformMax) {
226
+ const platform = String(waiter.details.platform);
227
+ if (!isCappedPlatform(platform)) return false;
228
+ const cap = platformMax[platform] ?? 0;
229
+ return cap > 0 && platformCount(keys, platform) >= cap;
230
+ }
231
+ /** The first cap with no room left, or the first cap when all have room (for a run waiting behind another). */
232
+ function bindingLimit(limits) {
233
+ return limits.find((limit) => limit.count >= limit.max) ?? limits[0];
234
+ }
235
+ function atCapacityRefusal({ count, max, setting, noun }) {
192
236
  return {
193
237
  code: "STIM_AT_CAPACITY",
194
- message: count < max ? `${count}/${max} Stim devices are in use, but another run is ahead in the device slot queue.` : `${count} Stim device(s) are already booted and concurrency.maxDevices is ${max}, so booting another would exceed the cap.`,
195
- remedy: "stop an environment (stim stop) or raise concurrency.maxDevices"
238
+ message: count < max ? `${count}/${max} Stim ${noun}s are in use, but another run is ahead in the device slot queue.` : `${count} Stim ${noun}(s) are already booted and ${setting} is ${max}, so booting another would exceed the cap.`,
239
+ remedy: `stop an environment (stim stop) or raise ${setting}`
196
240
  };
197
241
  }
198
242
  function uncountedRefusal(error) {
199
243
  return {
200
244
  code: "STIM_NO_DEVICE",
201
- message: `concurrency.maxDevices is set, and Stim could not count the booted devices: ${error?.message || error}`,
245
+ message: `A device cap (concurrency.maxDevices, concurrency.maxIosDevices or concurrency.maxAndroidDevices) is set, and Stim could not count the booted devices: ${error?.message || error}`,
202
246
  remedy: `Listing devices times out when the machine is overloaded: retry once the load falls. Otherwise run \`stim doctor\` to check the simulator and adb toolchains, and check that ${join(getConfigDir(), "config.json")} and ${bootingDevicesRoot()} are readable.`
203
247
  };
204
248
  }
@@ -257,9 +301,19 @@ function readInventory({ sims = listSimsForCount, adb = listAdbForCount, config
257
301
  * run is booting under `concurrency.maxDevices`, each counted once unless excluded; when a listing fails, why the count is unknown.
258
302
  */
259
303
  function countLiveOwnedDevices(sources = {}, { exclude = [] } = {}) {
304
+ const counts = countLiveOwnedDevicesByPlatform(sources, { exclude });
305
+ return "unknown" in counts ? counts : counts.ios + counts.android;
306
+ }
307
+ /** The counts of {@link countLiveOwnedDevices} split by platform. */
308
+ function countLiveOwnedDevicesByPlatform(sources = {}, { exclude = [] } = {}) {
260
309
  try {
261
310
  const inventory = readInventory(sources);
262
- return "code" in inventory ? { unknown: inventory.message } : inventoryKeys(inventory, exclude).size;
311
+ if ("code" in inventory) return { unknown: inventory.message };
312
+ const keys = inventoryKeys(inventory, exclude);
313
+ return {
314
+ ios: platformCount(keys, "ios"),
315
+ android: platformCount(keys, "android")
316
+ };
263
317
  } catch (error) {
264
318
  if ((0, ownership_claim_exports.isClaimRefusal)(error)) return { unknown: error.message };
265
319
  throw error;
@@ -309,8 +363,8 @@ function workspaceHasLiveDevice({ platform, project, slot = "default", sims = []
309
363
  if (platform === "ios") return sims.some((s) => s.udid === record.deviceUdid && LIVE_SIM_STATES.has(s.state));
310
364
  return typeof record.consolePort === "number" && adbEmulators.some((e) => e.consolePort === record.consolePort);
311
365
  }
312
- function deviceCapacityRefusal({ platform, project, slot = "default", max, sims = [], adb = null, config = null, booting = [] } = {}) {
313
- if (!max || max <= 0) return null;
366
+ function deviceCapacityRefusal({ platform, project, slot = "default", max, platformMax, sims = [], adb = null, config = null, booting = [] } = {}) {
367
+ if (!(max && max > 0) && !(platformMax && platformMax > 0)) return null;
314
368
  const adbEmulators = liveEmulatorPorts(adb ?? NO_ADB);
315
369
  if (workspaceHasLiveDevice({
316
370
  platform,
@@ -319,20 +373,23 @@ function deviceCapacityRefusal({ platform, project, slot = "default", max, sims
319
373
  sims,
320
374
  adbEmulators
321
375
  })) return null;
322
- const count = liveOwnedDeviceKeys({
376
+ const full = deviceLimits(liveOwnedDeviceKeys({
323
377
  sims,
324
378
  adbEmulators,
325
379
  config,
326
380
  booting
327
- }).size;
328
- return count < max ? null : atCapacityRefusal(count, max);
381
+ }), platform ?? "", max ?? 0, platformMax ?? 0).find((limit) => limit.count >= limit.max);
382
+ return full ? {
383
+ ...atCapacityRefusal(full),
384
+ max: full.max
385
+ } : null;
329
386
  }
330
387
  /**
331
388
  * The early `concurrency.maxDevices` check, before Metro starts or a device is created. It is advisory, so an
332
389
  * unknown count passes: `withDeviceBootAdmission` makes the binding decision when the boot starts.
333
390
  */
334
- async function checkDeviceCapacity({ platform, project, slot = "default", max, ...sources } = {}) {
335
- if (!max || max <= 0) return null;
391
+ async function checkDeviceCapacity({ platform, project, slot = "default", max, platformMax = platform && isCappedPlatform(platform) ? getPlatformDeviceLimits()[platform] : 0, ...sources } = {}) {
392
+ if (!(max && max > 0) && !(platformMax && platformMax > 0)) return null;
336
393
  let inventory;
337
394
  try {
338
395
  inventory = await readInventoryAsync(sources);
@@ -346,10 +403,11 @@ async function checkDeviceCapacity({ platform, project, slot = "default", max, .
346
403
  project,
347
404
  slot,
348
405
  max,
406
+ platformMax,
349
407
  ...inventory
350
408
  });
351
409
  }
352
- async function peekDeviceSlots({ platform, project, slot = "default", max = getConcurrencyLimits().maxDevices, sources = {} }) {
410
+ async function peekDeviceSlots({ platform, project, slot = "default", max = getConcurrencyLimits().maxDevices, platformMax = getPlatformDeviceLimits()[platform], sources = {} }) {
353
411
  try {
354
412
  const inventory = await readInventoryAsync(sources);
355
413
  if ("code" in inventory) return {
@@ -359,17 +417,24 @@ async function peekDeviceSlots({ platform, project, slot = "default", max = getC
359
417
  localLive: false
360
418
  };
361
419
  const owned = deviceSlotPlatforms(project, slot)?.[platform]?.owned === true;
420
+ const keys = inventoryKeys(inventory);
421
+ const limits = deviceLimits(keys, platform, max, platformMax);
422
+ const tightest = limits.length ? limits.reduce((a, b) => b.max - b.count < a.max - a.count ? b : a) : void 0;
362
423
  return {
363
- count: inventoryKeys(inventory).size,
364
- max,
365
- queued: liveWaiters().length,
424
+ count: tightest?.count ?? keys.size,
425
+ max: tightest?.max ?? max,
426
+ queued: liveWaiters().filter((waiter) => !platformWalled(keys, waiter, {
427
+ ...getPlatformDeviceLimits(),
428
+ [platform]: platformMax
429
+ })).length,
366
430
  localLive: owned && workspaceHasLiveDevice({
367
431
  platform,
368
432
  project,
369
433
  slot,
370
434
  sims: inventory.sims,
371
435
  adbEmulators: liveEmulatorPorts(inventory.adb)
372
- })
436
+ }),
437
+ ...tightest?.platform ? { limit: tightest.setting } : {}
373
438
  };
374
439
  } catch (error) {
375
440
  if (!(0, ownership_claim_exports.isClaimRefusal)(error)) throw error;
@@ -382,8 +447,8 @@ async function peekDeviceSlots({ platform, project, slot = "default", max = getC
382
447
  }
383
448
  }
384
449
  const DEFAULT_DEVICE_SLOT_WAIT_MS = 6e5;
385
- function deviceSlotWaitingLine({ count, max, holders, elapsedMs, queue }) {
386
- return `${"device".padEnd(11)} waiting for a device slot (${count}/${max} in use${holders.length ? `: ${holders.join(", ")}` : ""}), ${formatElapsed(elapsedMs)} elapsed -- stim guide lifecycle concurrency${queue ? `; ${queue}` : ""}`;
450
+ function deviceSlotWaitingLine({ count, max, holders, elapsedMs, queue, setting }) {
451
+ return `${"device".padEnd(11)} waiting for a device slot (${count}/${max} in use${setting ? ` by ${setting}` : ""}${holders.length ? `: ${holders.join(", ")}` : ""}), ${formatElapsed(elapsedMs)} elapsed -- stim guide lifecycle concurrency${queue ? `; ${queue}` : ""}`;
387
452
  }
388
453
  function unrecordedHolder(label) {
389
454
  return {
@@ -445,32 +510,57 @@ function deviceHolders(inventory, keys) {
445
510
  return [...new Set(slotHolders(inventory, keys).map((holder) => holder.label))].toSorted();
446
511
  }
447
512
  /**
448
- * `concurrency.maxDevices` usage for status: the owned devices the cap counts and who holds each. `sims` is the
449
- * listing status already read; an unknown count reports why instead of failing.
513
+ * `concurrency.maxDevices` usage for status (the owned devices the cap counts and who holds each) and the usage of each
514
+ * per-platform cap that is set, from one inventory read. `sims` is the listing status already read; an unknown count
515
+ * reports why instead of failing.
450
516
  */
451
- async function deviceSlotUsage(max, sources = {}) {
517
+ async function deviceSlotUsages(max, platformMax, sources = {}) {
518
+ const capped = [];
519
+ for (const platform of ["ios", "android"]) {
520
+ const cap = platformMax[platform] ?? 0;
521
+ if (cap > 0) capped.push([platform, cap]);
522
+ }
523
+ const platformDevices = (count) => capped.length ? { platformDevices: Object.fromEntries(capped.map(([platform, cap]) => [platform, {
524
+ max: cap,
525
+ ...count(platform)
526
+ }])) } : {};
452
527
  try {
453
528
  const inventory = await readInventoryAsync(sources);
454
529
  if ("code" in inventory) return {
455
- max,
456
- inUse: null,
457
- unknown: inventory.message,
458
- holders: []
530
+ ...max > 0 ? { devices: {
531
+ max,
532
+ inUse: null,
533
+ unknown: inventory.message,
534
+ holders: []
535
+ } } : {},
536
+ ...platformDevices(() => ({
537
+ inUse: null,
538
+ unknown: inventory.message
539
+ }))
459
540
  };
460
541
  const keys = inventoryKeys(inventory);
461
542
  return {
462
- max,
463
- inUse: keys.size,
464
- holders: slotHolders(inventory, keys)
543
+ ...max > 0 ? { devices: {
544
+ max,
545
+ inUse: keys.size,
546
+ holders: slotHolders(inventory, keys)
547
+ } } : {},
548
+ ...platformDevices((platform) => ({ inUse: platformCount(keys, platform) }))
465
549
  };
466
550
  } catch (error) {
467
- if ((0, ownership_claim_exports.isClaimRefusal)(error)) return {
468
- max,
469
- inUse: null,
470
- unknown: error.message,
471
- holders: []
551
+ if (!(0, ownership_claim_exports.isClaimRefusal)(error)) throw error;
552
+ return {
553
+ ...max > 0 ? { devices: {
554
+ max,
555
+ inUse: null,
556
+ unknown: error.message,
557
+ holders: []
558
+ } } : {},
559
+ ...platformDevices(() => ({
560
+ inUse: null,
561
+ unknown: error.message
562
+ }))
472
563
  };
473
- throw error;
474
564
  }
475
565
  }
476
566
  function liveWaiters() {
@@ -513,7 +603,7 @@ function takeBootMarker(device) {
513
603
  if (attempt.acquired) return attempt.acquired;
514
604
  throw new DeviceAdmissionRefusal({
515
605
  code: "STIM_NO_DEVICE",
516
- message: `Another process holds ${bootingDevicesRoot()} exclusively, so this boot cannot be counted toward concurrency.maxDevices.`,
606
+ message: `Another process holds ${bootingDevicesRoot()} exclusively, so this boot cannot be counted toward the device caps.`,
517
607
  remedy: "Retry once that process finishes."
518
608
  });
519
609
  }
@@ -537,13 +627,13 @@ async function admissionTransaction(action, lockWaitMs, out, signal) {
537
627
  if (workspaceProcessLockError(error) !== "timeout") throw error;
538
628
  throw new DeviceAdmissionRefusal({
539
629
  code: "STIM_NO_DEVICE",
540
- message: `Waited ${formatElapsed(lockWaitMs)} for other runs to finish counting booted devices for concurrency.maxDevices.`,
630
+ message: `Waited ${formatElapsed(lockWaitMs)} for other runs to finish counting booted devices for the device caps.`,
541
631
  remedy: "Listing devices is slow, which usually means the machine is overloaded. Retry once the load falls."
542
632
  });
543
633
  }
544
634
  }
545
635
  /** Boots an owned device under the Stim home's cap, reserving capacity before releasing admission. */
546
- async function withDeviceBootAdmission(device, boot, { root, max = getConcurrencyLimits().maxDevices, sources = {}, lockWaitMs = ADMISSION_LOCK_WAIT_MS, out = () => {}, waitMs = DEFAULT_DEVICE_SLOT_WAIT_MS, noWait = false, automatic = false, displayName = workspaceDisplayLabel(root, loadConfig$1()), reclaimIdleMinutes = deviceReclaimIdleMinutesSetting({}), now = Date.now, signal, sleep = (ms) => setTimeout(ms, void 0, { signal }), waitingFor = () => {}, onWait = () => {}, onBootStart = () => {} }) {
636
+ async function withDeviceBootAdmission(device, boot, { root, max = getConcurrencyLimits().maxDevices, platformMax = getPlatformDeviceLimits(), sources = {}, lockWaitMs = ADMISSION_LOCK_WAIT_MS, out = () => {}, waitMs = DEFAULT_DEVICE_SLOT_WAIT_MS, noWait = false, automatic = false, displayName = workspaceDisplayLabel(root, loadConfig$1()), reclaimIdleMinutes = deviceReclaimIdleMinutesSetting({}), now = Date.now, signal, sleep = (ms) => setTimeout(ms, void 0, { signal }), waitingFor = () => {}, onWait = () => {}, onBootStart = () => {} }) {
547
637
  const requireMembership = () => {
548
638
  if (automatic && !automaticMachineEnabled("device", "local")) throw new DeviceAdmissionRefusal({
549
639
  code: "STIM_HOSTING_REFUSED",
@@ -553,7 +643,8 @@ async function withDeviceBootAdmission(device, boot, { root, max = getConcurrenc
553
643
  };
554
644
  signal?.throwIfAborted();
555
645
  requireMembership();
556
- if (!max || max <= 0) {
646
+ const ownCap = isCappedPlatform(device.platform) ? platformMax[device.platform] ?? 0 : 0;
647
+ if (!(max > 0) && !(ownCap > 0)) {
557
648
  onBootStart();
558
649
  return boot();
559
650
  }
@@ -568,10 +659,9 @@ async function withDeviceBootAdmission(device, boot, { root, max = getConcurrenc
568
659
  let started;
569
660
  let lastLine = -Infinity;
570
661
  let visibleWait = false;
571
- let lastInventory = {
572
- count: 0,
573
- holders: []
574
- };
662
+ let lastInventory;
663
+ let lastTurn = false;
664
+ let binding;
575
665
  let lastListed = -Infinity;
576
666
  let reclaimed = 0;
577
667
  let lastReclaim = -Infinity;
@@ -584,39 +674,41 @@ async function withDeviceBootAdmission(device, boot, { root, max = getConcurrenc
584
674
  requireMembership();
585
675
  const waiters = liveWaiters();
586
676
  const first = waiters[0];
587
- const turn = !first || first.claimId === ticket?.claimId;
677
+ const mine = waiters.findIndex((holder) => holder.claimId === ticket?.claimId);
678
+ const ahead = mine < 0 ? waiters : waiters.slice(0, mine);
588
679
  const admit = () => {
589
680
  const admitted = takeBootMarker(device);
590
681
  if ((0, ownership_claim_exports.releaseClaim)(ticket)) ticket = void 0;
591
682
  return { marker: admitted };
592
683
  };
593
- const wait = ({ count, holders }) => {
684
+ const wait = ({ limit, holders }, turn) => {
594
685
  const ticketId = ticket?.claimId;
595
686
  const position = ticketId ? waiters.findIndex((holder) => holder.claimId === ticketId) + 1 : waiters.length + 1;
596
687
  const headName = first?.details.displayName ?? displayName;
597
688
  const queue = `queue position ${position}${position > 1 ? " behind" : ", head"} ${headName}; queue: ${join(getConfigDir(), "device-waits")}`;
598
- if (noWait || waitMs === 0) throw new DeviceAdmissionRefusal(atCapacityRefusal(count, max));
689
+ if (noWait || waitMs === 0) throw new DeviceAdmissionRefusal(atCapacityRefusal(limit));
599
690
  if (started !== void 0 && now() - started >= waitMs) throw new DeviceAdmissionRefusal({
600
691
  code: "STIM_AT_CAPACITY",
601
- message: `Waited ${formatElapsed(now() - started)} for a device slot; ${count}/${max} Stim devices are in use; ${queue}.`,
602
- remedy: "Stop an environment (stim stop), retry with a longer --wait <seconds>, or raise concurrency.maxDevices."
692
+ message: `Waited ${formatElapsed(now() - started)} for a device slot; ${limit.count}/${limit.max} Stim ${limit.noun}s are in use (${limit.setting}); ${queue}.`,
693
+ remedy: `Stop an environment (stim stop), retry with a longer --wait <seconds>, or raise ${limit.setting}.`
603
694
  });
604
695
  if (!ticket) {
605
696
  ticket = takeWaitTicket(device, waiters);
606
697
  started = now();
607
698
  }
699
+ const firstOfPlatform = ahead.every((holder) => holder.details.platform !== device.platform);
608
700
  return {
609
- count,
701
+ limit,
610
702
  holders,
611
703
  queue,
612
- reclaim: turn && count >= max
704
+ reclaim: turn && firstOfPlatform && limit.count >= limit.max
613
705
  };
614
706
  };
615
- if (ticket && !turn && now() - lastListed < INVENTORY_REFRESH_MS) {
707
+ if (ticket && lastInventory && ahead.length > 0 && !lastTurn && now() - lastListed < INVENTORY_REFRESH_MS) {
616
708
  const booting = sources.booting ?? readBootingDevices;
617
709
  const own = deviceKey(device.platform, device.key);
618
710
  if ((typeof booting === "function" ? booting() : booting).some((entry) => deviceKey(entry.platform, entry.key) === own)) return admit();
619
- return wait(lastInventory);
711
+ return wait(lastInventory, false);
620
712
  }
621
713
  const inventory = await readInventoryAsync(sources);
622
714
  if ("code" in inventory) throw new DeviceAdmissionRefusal(inventory);
@@ -625,13 +717,19 @@ async function withDeviceBootAdmission(device, boot, { root, max = getConcurrenc
625
717
  const ownBooting = inventory.booting.some((entry) => deviceKey(entry.platform, entry.key) === own);
626
718
  if (device.platform === "ios" && keys.has(own) || ownBooting) return admit();
627
719
  keys.delete(own);
628
- if (keys.size < max && turn) return admit();
720
+ const limits = deviceLimits(keys, device.platform, max, ownCap);
721
+ const turn = ahead.every((waiter) => platformWalled(keys, waiter, platformMax));
722
+ if (turn && limits.every((limit) => limit.count < limit.max)) return admit();
629
723
  lastListed = now();
724
+ lastTurn = turn;
725
+ const limit = bindingLimit(limits);
726
+ if (!limit) return admit();
727
+ binding = limit;
630
728
  lastInventory = {
631
- count: keys.size,
632
- holders: deviceHolders(inventory, keys)
729
+ limit,
730
+ holders: deviceHolders(inventory, limit.platform ? new Set([...keys].filter((key) => key.startsWith(`${limit.platform}:`))) : keys)
633
731
  };
634
- return wait(lastInventory);
732
+ return wait(lastInventory, turn);
635
733
  }, lockWaitMs, out, signal);
636
734
  if ("marker" in result) {
637
735
  marker = result.marker;
@@ -643,25 +741,26 @@ async function withDeviceBootAdmission(device, boot, { root, max = getConcurrenc
643
741
  visibleWait = true;
644
742
  waitingFor({
645
743
  kind: "device-slot",
646
- inUse: result.count,
647
- max,
744
+ inUse: result.limit.count,
745
+ max: result.limit.max,
648
746
  since: new Date(started).toISOString()
649
747
  });
650
748
  if (now() - lastLine >= 1e4) {
651
749
  lastLine = now();
652
750
  out(deviceSlotWaitingLine({
653
- count: result.count,
654
- max,
751
+ count: result.limit.count,
752
+ max: result.limit.max,
655
753
  holders: result.holders,
656
754
  elapsedMs,
657
- queue: result.queue
755
+ queue: result.queue,
756
+ ...result.limit.platform ? { setting: result.limit.setting } : {}
658
757
  }));
659
758
  }
660
759
  signal?.throwIfAborted();
661
760
  if (result.reclaim && now() - lastReclaim >= 15e3) {
662
761
  lastReclaim = now();
663
762
  try {
664
- if (reclaimIdleMinutes > 0) reclaimed += await reclaimIdleDevice(root, reclaimIdleMinutes * 6e4, out, now, skippedReclaims);
763
+ if (reclaimIdleMinutes > 0) reclaimed += await reclaimIdleDevice(root, reclaimIdleMinutes * 6e4, out, now, skippedReclaims, result.limit.platform);
665
764
  } catch (error) {
666
765
  out(phaseLine("device", `could not reclaim an idle device: ${error?.message || error}`));
667
766
  }
@@ -673,7 +772,7 @@ async function withDeviceBootAdmission(device, boot, { root, max = getConcurrenc
673
772
  const at = now();
674
773
  await withConfigLockAsync$1(() => recordCapacityRefusal({
675
774
  platform: device.platform,
676
- max,
775
+ max: binding?.max ?? max,
677
776
  workspace
678
777
  }, at)).catch(() => {});
679
778
  }
@@ -687,7 +786,7 @@ async function withDeviceBootAdmission(device, boot, { root, max = getConcurrenc
687
786
  await withConfigLockAsync$1(() => recordCapacityWait({
688
787
  platform: device.platform,
689
788
  ms,
690
- max,
789
+ max: binding?.max ?? max,
691
790
  workspace,
692
791
  ...reclaimed ? { reclaimed } : {}
693
792
  }, at)).catch(() => {});
@@ -765,4 +864,4 @@ function unknownAndroidDeviceProfileRefusal(requested, profiles) {
765
864
  };
766
865
  }
767
866
  //#endregion
768
- export { countLiveOwnedDevicesAsync as a, deviceTypeMismatch as c, runtimeMismatch as d, unknownAndroidDeviceProfileRefusal as f, withDeviceBootAdmission as g, unknownIosRuntimeRefusal as h, countLiveOwnedDevices as i, layerNote as l, unknownIosDeviceTypeRefusal as m, DeviceAdmissionRefusal as n, countLiveOwnedDevicesFrom as o, unknownAndroidSystemImageRefusal as p, checkDeviceCapacity as r, deviceSlotUsage as s, DEFAULT_DEVICE_SLOT_WAIT_MS as t, peekDeviceSlots as u };
867
+ export { withDeviceBootAdmission as _, countLiveOwnedDevicesAsync as a, deviceSlotUsages as c, peekDeviceSlots as d, runtimeMismatch as f, unknownIosRuntimeRefusal as g, unknownIosDeviceTypeRefusal as h, countLiveOwnedDevices as i, deviceTypeMismatch as l, unknownAndroidSystemImageRefusal as m, DeviceAdmissionRefusal as n, countLiveOwnedDevicesByPlatform as o, unknownAndroidDeviceProfileRefusal as p, checkDeviceCapacity as r, countLiveOwnedDevicesFrom as s, DEFAULT_DEVICE_SLOT_WAIT_MS as t, layerNote as u };
@@ -1,19 +1,19 @@
1
1
  import { t as getExecutor } from "./exec-aULwtNly.mjs";
2
- import { _ as ensureWorkspaceStorage, d as writeWorkspaceState } from "./workspace-state-DGwkPXwq.mjs";
3
- import { O as withConfigLockAsync$1, _ as loadConfig$1, c as ensureConfig } from "./config-AbUjIBOQ.mjs";
4
- import { i as macosProcess, o as requiredMacosRecord } from "./state-C2AUjjhg.mjs";
5
- import { A as hostSystemImageArch, Ct as forgetCreatedDeviceAsync, I as listAvdsAsync, K as ownedAvdSystemImage, L as listInstalledSystemImages, M as listAdbDevicesAsync, N as listAvdDeviceProfiles, U as ownedAvdName, X as pickDefaultSystemImage, a as androidDeviceAbi, ct as waitForBoot, d as androidToolPath, f as assertOwnedAvdStopped, g as avdPathExists, k as getAvdNameForSerialAsync, nt as resolveOwnedAvdSerialAsync, t as DEFAULT_AVD_DEVICE_PROFILE, tt as resetAdoptedAvd, v as avdStorageRoots, x as createOwnedAvd, y as bootAndroidEmulator } from "./android-DdKG6lyD.mjs";
6
- import { a as parsePidof, i as parseLogcatLine, t as androidClockOffset } from "./android-BEQUDpOg.mjs";
2
+ import { _ as ensureWorkspaceStorage, d as writeWorkspaceState } from "./workspace-state-3bF4TB8P.mjs";
3
+ import { c as ensureConfig, k as withConfigLockAsync$1, v as loadConfig$1 } from "./config-D5qNWJEP.mjs";
4
+ import { i as macosProcess, o as requiredMacosRecord } from "./state-CiflgxTP.mjs";
5
+ import { A as getAvdNameForSerialAsync, F as listAvdDeviceProfiles, G as ownedAvdName, J as ownedAvdSystemImage, P as listAdbDevicesAsync, Q as pickDefaultSystemImage, R as listAvdsAsync, S as createOwnedAvd, Tt as forgetCreatedDeviceAsync, a as androidDeviceAbi, b as bootAndroidEmulator, d as androidToolPath, f as assertOwnedAvdStopped, g as avdPathExists, it as resolveOwnedAvdSerialAsync, j as hostSystemImageArch, rt as resetAdoptedAvd, t as DEFAULT_AVD_DEVICE_PROFILE, ut as waitForBoot, y as avdStorageRoots, z as listInstalledSystemImages } from "./android-DsEj2CCs.mjs";
6
+ import { a as parsePidof, i as parseLogcatLine, t as androidClockOffset } from "./android-BeeC15Ve.mjs";
7
7
  import { i as createNdjsonWriter } from "./ndjson-CrjIsGzw.mjs";
8
- import { _n as reverseMetroPorts, dn as installIosApp, fn as iosAppProcess, hn as launchIosApp, ln as installAndroidApp, mn as launchAndroidReleaseApp, pn as launchAndroidApp, tn as androidAppProcess, vn as writeDebugHttpHost } from "./ccache-B8gY50-h.mjs";
9
- import { ht as teardownOwnedIosSim, pt as teardownOwnedAvd } from "./workspaces-7_RWoi6c.mjs";
10
- import { E as resolveIosCreation, R as readHostMemoryPressure, T as resetIosPrivacy, a as createOwnedIosSim, d as listAllIosSims, g as listUserApps, j as uninstallIosApp, n as bootIosSim, w as resetIosKeychain } from "./ios-CXnoVO_z.mjs";
11
- import { i as stopMacosAppHeld, n as stopHostedBundleInstances } from "./stop-CMVfaA-6.mjs";
8
+ import { _n as reverseMetroPorts, dn as installIosApp, fn as iosAppProcess, hn as launchIosApp, ln as installAndroidApp, mn as launchAndroidReleaseApp, pn as launchAndroidApp, tn as androidAppProcess, vn as writeDebugHttpHost } from "./ccache-BrwGIxmV.mjs";
9
+ import { ht as teardownOwnedIosSim, pt as teardownOwnedAvd } from "./workspaces-C59v9bpD.mjs";
10
+ import { E as resolveIosCreation, R as readHostMemoryPressure, T as resetIosPrivacy, a as createOwnedIosSim, d as listAllIosSims, g as listUserApps, j as uninstallIosApp, n as bootIosSim, w as resetIosKeychain } from "./ios-CdAV4-1E.mjs";
11
+ import { i as stopMacosAppHeld, n as stopHostedBundleInstances } from "./stop-B616ZyTv.mjs";
12
12
  import { t as spawnEntry } from "./spawn-entry-jw19m8Nl.mjs";
13
- import { i as countLiveOwnedDevices } from "./device-capacity-C5C_jOQd.mjs";
14
- import { a as muteBootedSimulator } from "./device-ios-DbmtPIiw.mjs";
15
- import { _ as findAapt } from "./support-BGqpYMV8.mjs";
16
- import { i as muteBootedEmulator } from "./device-android-CcdQqQWg.mjs";
13
+ import { o as countLiveOwnedDevicesByPlatform } from "./device-capacity-Bki6B-7h.mjs";
14
+ import { a as muteBootedSimulator } from "./device-ios-TE5ejeXx.mjs";
15
+ import { _ as findAapt } from "./support-S_5nkkzs.mjs";
16
+ import { i as muteBootedEmulator } from "./device-android-zgD9We85.mjs";
17
17
  import { t as waitForMacosRegistration } from "./registration-BwCe64PH.mjs";
18
18
  import { n as parseLogStreamLine } from "./ios-CsRVik64.mjs";
19
19
  import { createHash, randomUUID } from "node:crypto";
@@ -1143,13 +1143,17 @@ async function inspectHostedDevice(request, options) {
1143
1143
  const disk = statfsSync(configDir());
1144
1144
  workerDiskFreeBytes = disk.bavail * disk.bsize;
1145
1145
  } catch {}
1146
+ const local = options ? countLiveOwnedDevicesByPlatform({}, options) : void 0;
1146
1147
  const resources = {
1147
1148
  cpus,
1148
1149
  loadPerCore: loadavg()[1] / cpus,
1149
1150
  memoryFreeBytes: freemem(),
1150
1151
  memoryPressure: readHostMemoryPressure() ?? readHostMemoryPressure(),
1151
1152
  workerDiskFreeBytes,
1152
- ...options ? { localDevices: countLiveOwnedDevices({}, options) } : {}
1153
+ ...local ? {
1154
+ localDevices: "unknown" in local ? local : local.ios + local.android,
1155
+ localPlatforms: local
1156
+ } : {}
1153
1157
  };
1154
1158
  try {
1155
1159
  if (process.platform !== "darwin") throw new Error("Hosted device sessions require a Mac.");
@@ -1209,7 +1213,7 @@ async function main() {
1209
1213
  exclude = input.exclude;
1210
1214
  }
1211
1215
  if (input.mode === "count") {
1212
- process.stdout.write(`${JSON.stringify(countLiveOwnedDevices({}, { exclude }))}\n`);
1216
+ process.stdout.write(`${JSON.stringify(countLiveOwnedDevicesByPlatform({}, { exclude }))}\n`);
1213
1217
  return;
1214
1218
  }
1215
1219
  if (input.mode === "offer") {
@@ -1,13 +1,13 @@
1
1
  import { D as ownership_claim_exports, n as isTimeoutError, o as cancellableSleep, t as getExecutor, u as withWorkspaceProcessLock } from "./exec-aULwtNly.mjs";
2
- import { D as workspaceId$1, k as workspaceLogsDir$1 } from "./workspace-state-DGwkPXwq.mjs";
3
- import { C as saveConfig, G as projectDeviceSlots, H as deviceSlotPlatforms, O as withConfigLockAsync$1, _ as loadConfig$1, a as clearDevice, d as getConfigDir, w as setDevice } from "./config-AbUjIBOQ.mjs";
4
- import { l as phaseLine } from "./command-output-BBW8eWPL.mjs";
5
- import { D as iosSimSlimProfileSetting, E as iosSimSlimDefaultSetting } from "./settings-D776ygML.mjs";
6
- import { mt as dropParked, pt as adoptParked, vt as parkedMaxSetting, xt as selectParked, yt as readParked } from "./android-DdKG6lyD.mjs";
2
+ import { D as workspaceId$1, k as workspaceLogsDir$1 } from "./workspace-state-3bF4TB8P.mjs";
3
+ import { K as projectDeviceSlots, T as setDevice, U as deviceSlotPlatforms, a as clearDevice, d as getConfigDir, k as withConfigLockAsync$1, v as loadConfig$1, w as saveConfig } from "./config-D5qNWJEP.mjs";
4
+ import { l as phaseLine } from "./command-output-CfPW1bKP.mjs";
5
+ import { D as iosSimSlimProfileSetting, E as iosSimSlimDefaultSetting } from "./settings-C6UTYW2K.mjs";
6
+ import { Ct as selectParked, bt as parkedMaxSetting, gt as dropParked, ht as adoptParked, xt as readParked } from "./android-DsEj2CCs.mjs";
7
7
  import { i as createNdjsonWriter } from "./ndjson-CrjIsGzw.mjs";
8
- import { _t as teardownParkedIosSim, ht as teardownOwnedIosSim } from "./workspaces-7_RWoi6c.mjs";
9
- import { A as springBoardRunning, B as reportMemoryCulprits, C as renameIosSim, D as resolveOwnedIosSim, E as resolveIosCreation, F as hostMemoryPressureAdvice, M as waitForSpringBoard, N as configuredIosSimulatorViewer, R as readHostMemoryPressure, S as readSpringBoardPid, T as resetIosPrivacy, _ as ownedSimName, a as createOwnedIosSim, d as listAllIosSims, f as listAllIosSimsAsync, h as listIosRuntimesAsync, n as bootIosSim, p as listIosDeviceTypesAsync, u as iosSimulatorFailureAdvice, w as resetIosKeychain, y as parseRuntimeVersion } from "./ios-CXnoVO_z.mjs";
10
- import { c as deviceTypeMismatch, d as runtimeMismatch, g as withDeviceBootAdmission, n as DeviceAdmissionRefusal } from "./device-capacity-C5C_jOQd.mjs";
8
+ import { _t as teardownParkedIosSim, ht as teardownOwnedIosSim } from "./workspaces-C59v9bpD.mjs";
9
+ import { A as springBoardRunning, B as reportMemoryCulprits, C as renameIosSim, D as resolveOwnedIosSim, E as resolveIosCreation, F as hostMemoryPressureAdvice, M as waitForSpringBoard, N as configuredIosSimulatorViewer, R as readHostMemoryPressure, S as readSpringBoardPid, T as resetIosPrivacy, _ as ownedSimName, a as createOwnedIosSim, d as listAllIosSims, f as listAllIosSimsAsync, h as listIosRuntimesAsync, n as bootIosSim, p as listIosDeviceTypesAsync, u as iosSimulatorFailureAdvice, w as resetIosKeychain, y as parseRuntimeVersion } from "./ios-CdAV4-1E.mjs";
10
+ import { _ as withDeviceBootAdmission, f as runtimeMismatch, l as deviceTypeMismatch, n as DeviceAdmissionRefusal } from "./device-capacity-Bki6B-7h.mjs";
11
11
  import { createHash } from "node:crypto";
12
12
  import { readFileSync } from "node:fs";
13
13
  import { homedir } from "node:os";
@@ -1,8 +1,8 @@
1
1
  import { b as operationState } from "./exec-aULwtNly.mjs";
2
- import { V as deviceSlotKey } from "./config-AbUjIBOQ.mjs";
3
- import { a as formatElapsed, r as clockTime } from "./command-output-BBW8eWPL.mjs";
4
- import { Xt as APP_READINESS_TIMEOUT_MS, zt as MANIFEST_VERIFY_TIMEOUT_MS } from "./ccache-B8gY50-h.mjs";
5
- import { E as releaseRunLease, S as parseLease, T as raiseLease, b as leaseIsExpired, j as takeLease, v as deviceLeasePath, y as fileLeaseIo } from "./activity-BTaUf0tc.mjs";
2
+ import { H as deviceSlotKey } from "./config-D5qNWJEP.mjs";
3
+ import { a as formatElapsed, r as clockTime } from "./command-output-CfPW1bKP.mjs";
4
+ import { Xt as APP_READINESS_TIMEOUT_MS, zt as MANIFEST_VERIFY_TIMEOUT_MS } from "./ccache-BrwGIxmV.mjs";
5
+ import { E as releaseRunLease, S as parseLease, T as raiseLease, b as leaseIsExpired, j as takeLease, v as deviceLeasePath, y as fileLeaseIo } from "./activity-yjH7Q3zn.mjs";
6
6
  //#region src/engine/device-lease-run.ts
7
7
  const LEASE_STEP_FLOOR_MS = 6e4;
8
8
  const DEBUG_VERIFY_STEP_MS = MANIFEST_VERIFY_TIMEOUT_MS + APP_READINESS_TIMEOUT_MS;
@@ -1,6 +1,6 @@
1
- import { V as deviceSlotKey } from "./config-AbUjIBOQ.mjs";
2
- import { A as selectPoolDevice, S as parseLease, b as leaseIsExpired, v as deviceLeasePath, y as fileLeaseIo } from "./activity-BTaUf0tc.mjs";
3
- import { i as DEVICE_WAIT_POLL_MS, n as DEVICE_BUSY, o as leaseExpiryText } from "./device-lease-run-Do6zYA7y.mjs";
1
+ import { H as deviceSlotKey } from "./config-D5qNWJEP.mjs";
2
+ import { A as selectPoolDevice, S as parseLease, b as leaseIsExpired, v as deviceLeasePath, y as fileLeaseIo } from "./activity-yjH7Q3zn.mjs";
3
+ import { i as DEVICE_WAIT_POLL_MS, n as DEVICE_BUSY, o as leaseExpiryText } from "./device-lease-run-DeNGjJZM.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,14 +1,14 @@
1
1
  import { b as operationState, t as getExecutor } from "./exec-aULwtNly.mjs";
2
- import { _ as loadConfig$1 } from "./config-AbUjIBOQ.mjs";
2
+ import { v as loadConfig$1 } from "./config-D5qNWJEP.mjs";
3
3
  import { t as createLineReader } from "./process-output-Fpl8PYi8.mjs";
4
- import { X as resolveProjectSettings, Y as resolveIosProjectDir, an as makeTemporaryDirectory, it as settingValueAt, on as removeTemporaryEntry } from "./settings-D776ygML.mjs";
5
- import { f as projectRegistry, g as selectNativeXcodeProject, i as findProjectRoot$1, p as NativeXcodeError, t as NO_PROJECT_REFUSAL } from "./project-DPQjnpcI.mjs";
6
- import { U as resolveScheme, q as xcodebuildArgs } from "./toolchain-DLdoyxIt.mjs";
7
- import { a as discoverXcodeProject } from "./react-native-build-BSoh4HkH.mjs";
8
- import { m as resolveConfiguration, y as resolveSchemeSelection } from "./support-DUevTPur.mjs";
4
+ import { X as resolveProjectSettings, Y as resolveIosProjectDir, an as makeTemporaryDirectory, it as settingValueAt, on as removeTemporaryEntry } from "./settings-C6UTYW2K.mjs";
5
+ import { f as projectRegistry, g as selectNativeXcodeProject, i as findProjectRoot$1, p as NativeXcodeError, t as NO_PROJECT_REFUSAL } from "./project-Crtcbfl0.mjs";
6
+ import { U as resolveScheme, q as xcodebuildArgs } from "./toolchain-Bnm8vUCV.mjs";
7
+ import { a as discoverXcodeProject } from "./react-native-build-DhWBUV_H.mjs";
8
+ import { m as resolveConfiguration, y as resolveSchemeSelection } from "./support-NxnD8QXd.mjs";
9
9
  import { n as findSigningIdentities, o as parsePlist, s as parseProvisioningProfilePlist, t as decodeProvisioningProfile } from "./ios-signing-BJzvDPfU.mjs";
10
- import { f as resolveIosPhysicalDevice, l as listIosDevices } from "./ios-device-ClSfuu-_.mjs";
11
- import { i as nativeXcodePackages, t as nativeXcodeHasPackages } from "./native-xcode-inputs-BF-ogSnG.mjs";
10
+ import { f as resolveIosPhysicalDevice, l as listIosDevices } from "./ios-device-D7Gi3e1I.mjs";
11
+ import { i as nativeXcodePackages, t as nativeXcodeHasPackages } from "./native-xcode-inputs-tDuUvSU8.mjs";
12
12
  import { existsSync, readdirSync, realpathSync, statSync } from "node:fs";
13
13
  import { homedir } from "node:os";
14
14
  import { join } from "node:path";