stim 1.13.0 → 1.15.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 (133) hide show
  1. package/README.md +2 -0
  2. package/dist/{activity-DfVKZTEB.mjs → activity-B2-kb7k0.mjs} +281 -41
  3. package/dist/{android-BHfAu2mf.mjs → android-CYmq0jNi.mjs} +2 -2
  4. package/dist/{android-CD8pr8Q2.mjs → android-Cu_L4RpL.mjs} +524 -412
  5. package/dist/{android-qx_31wJo.mjs → android-Db2eEMaf.mjs} +152 -65
  6. package/dist/android-cas-PnKfAdB2.mjs +167 -0
  7. package/dist/android-cas-compiler.mjs +2 -2
  8. package/dist/{app-install-CDFY2nUL.mjs → app-install-CAAplXAt.mjs} +162 -173
  9. package/dist/{budget-E5o91NR1.mjs → budget-CMiqghig.mjs} +1048 -77
  10. package/dist/{build-plan-BTa93o_r.mjs → build-plan-aeGMFX7e.mjs} +193 -650
  11. package/dist/build-progress-C2Jv1AtA.mjs +872 -0
  12. package/dist/build-slots-Df1ApSuH.mjs +127 -0
  13. package/dist/{cache-manifest-DWPF4RKJ.mjs → cache-manifest-DnUHBJaU.mjs} +2 -2
  14. package/dist/cache-manifest.mjs +1 -1
  15. package/dist/{chrome-B5hWgQJS.mjs → chrome-BrcVWQyD.mjs} +4 -4
  16. package/dist/cli-0cX5s4NM.mjs +45 -0
  17. package/dist/cli.mjs +6 -42
  18. package/dist/client-C2IS36Qf.mjs +1333 -0
  19. package/dist/collector-run.mjs +8 -7
  20. package/dist/{command-output-BIL7rlX7.mjs → command-output-CliHHn7a.mjs} +1 -1
  21. package/dist/{config-0jCcMM0E.mjs → config-D0DbpZtM.mjs} +29 -29
  22. package/dist/{created-devices-Cd558h31.mjs → created-devices-BwVb3liH.mjs} +2 -2
  23. package/dist/{dependency-state-DNN0yfx6.mjs → dependency-state-BmsjKZ2H.mjs} +2 -2
  24. package/dist/{deps-2Wo81np9.mjs → deps-BFlKbOIp.mjs} +3 -3
  25. package/dist/{detached-entry-DJHoca49.mjs → detached-entry-CiTEgMR1.mjs} +1 -1
  26. package/dist/dev-client-CjzLoNs0.mjs +80 -0
  27. package/dist/{device-Cf7vl-so.mjs → device-BZ1IyS5A.mjs} +7 -7
  28. package/dist/device-capacity-DMuKC7nx.mjs +136 -0
  29. package/dist/device-host-worker.d.mts +1 -0
  30. package/dist/device-host-worker.mjs +769 -0
  31. package/dist/device-ios-DMQBUPus.mjs +381 -0
  32. package/dist/{device-lease-CL9dZf73.mjs → device-lease-W5Pi1q38.mjs} +43 -36
  33. package/dist/{device-pool-MuoiyRcu.mjs → device-pool-B2r38YjS.mjs} +4 -4
  34. package/dist/{device-remote-DT9ngZiD.mjs → device-remote-9ulPsdhY.mjs} +68 -162
  35. package/dist/{doctor-D2iysNNH.mjs → doctor-BcE5xsBn.mjs} +191 -99
  36. package/dist/{error-diagnostics-BoiOhck8.mjs → error-diagnostics-Cb0P1WLS.mjs} +12 -11
  37. package/dist/{exec-DK8KaVHK.mjs → exec-CaErVv7t.mjs} +5 -1
  38. package/dist/{gc-eA-PRBX8.mjs → gc-BuL1Genr.mjs} +430 -102
  39. package/dist/{android-cas-B9Kv9aIy.mjs → gradle-CmdOBq1d.mjs} +8 -294
  40. package/dist/{guide-BfZ0OmiP.mjs → guide-cCSGRFd3.mjs} +2052 -240
  41. package/dist/{guide-status-BaMYu55M.mjs → guide-status-CIoWK6mG.mjs} +3 -3
  42. package/dist/hosted-macos-DwowNU7W.mjs +447 -0
  43. package/dist/idle-shutdown-B5LPaNyY.mjs +122 -0
  44. package/dist/in-use-CndPMilN.mjs +110 -0
  45. package/dist/{ios-CmEzZiOj.mjs → ios-3CtCRhwr.mjs} +201 -10
  46. package/dist/{ios-CcfCeZ19.mjs → ios-Chob8ny7.mjs} +560 -259
  47. package/dist/{ios-device-BW-t_Lkv.mjs → ios-device-DERmMmk-.mjs} +12 -7
  48. package/dist/{ios-device-BiOsSVzd.mjs → ios-device-Ee7sDXfp.mjs} +1 -1
  49. package/dist/{launch-verify-CuJaBpEa.mjs → launch-verify-CLlofxRt.mjs} +42 -11
  50. package/dist/{logs-DcSRbC2t.mjs → logs-h9h6MUvE.mjs} +95 -13
  51. package/dist/machine-usage-D_YYMzfR.mjs +283 -0
  52. package/dist/machines-CYtOCqcj.mjs +259 -0
  53. package/dist/macos-BwtbUtTN.mjs +468 -0
  54. package/dist/macos-run.d.mts +5 -0
  55. package/dist/macos-run.mjs +2 -0
  56. package/dist/{merge-state-ytFQCeh4.mjs → merge-state-BnxLNQiM.mjs} +22 -12
  57. package/dist/{metro-BPeY1YMh.mjs → metro-Di8EN2Tb.mjs} +20 -120
  58. package/dist/{named-ports-n7tnJsI8.mjs → named-ports-Dj_XBNaL.mjs} +8 -64
  59. package/dist/native-run-DFDoIRbY.mjs +147 -0
  60. package/dist/{native-runtime-CiiFHt0e.mjs → native-runtime-i6OkVg-w.mjs} +11 -6
  61. package/dist/offload-worker.d.mts +121 -0
  62. package/dist/offload-worker.mjs +601 -0
  63. package/dist/{ownership-Bz2kETAS.mjs → ownership-BkYxUtuj.mjs} +3 -2
  64. package/dist/{page-BTu7I60K.mjs → page-QF_Ke10s.mjs} +7 -2
  65. package/dist/{ports-C5UutqpJ.mjs → ports-DddwttKM.mjs} +3 -3
  66. package/dist/prebuild-BvxDlD6C.mjs +213 -0
  67. package/dist/process-identity-BmRm1qV0.mjs +12 -0
  68. package/dist/{project-DGeclhvO.mjs → project-CV3tDFHl.mjs} +9 -18
  69. package/dist/pull-request-pxnDCQzn.mjs +322 -0
  70. package/dist/pull-requests.d.mts +120 -0
  71. package/dist/pull-requests.mjs +2 -0
  72. package/dist/recordings-GEXp4iBT.mjs +37 -0
  73. package/dist/{reload-C-cV5yy_.mjs → reload-ChPiwvot.mjs} +70 -22
  74. package/dist/{remote-cache--aehr4Pr.mjs → remote-cache-CO2elm_b.mjs} +4 -4
  75. package/dist/run-B-xqRrsg.mjs +119 -0
  76. package/dist/{server-bare-Bq8BpSIo.mjs → server-bare-X7PUgizL.mjs} +60 -13
  77. package/dist/{server-expo-DUCg52-K.mjs → server-expo-DhtX6bom.mjs} +6 -10
  78. package/dist/server-expo-KJeD109I.mjs +2 -0
  79. package/dist/{settings-xB0Frkg6.mjs → settings-CduVcoxj.mjs} +77 -26
  80. package/dist/{settings-ssVBToDR.mjs → settings-DbL6uhH8.mjs} +16 -10
  81. package/dist/settings.schema.json +357 -2
  82. package/dist/{simslim-Bbg9UWrt.mjs → simslim-Ciluxlq4.mjs} +8 -132
  83. package/dist/slot-launch-BY0BrOLh.mjs +65 -0
  84. package/dist/{spawn-claims-PT8aQpCa.mjs → spawn-claims-D0XfSe8u.mjs} +1 -1
  85. package/dist/spawn-entry-DncpJvWL.mjs +16 -0
  86. package/dist/stage-CzaiwFsI.mjs +143 -0
  87. package/dist/start-CPO4wRg7.mjs +2 -0
  88. package/dist/{start-BZhItkiL.mjs → start-CUD9nasA.mjs} +108 -61
  89. package/dist/state-B4PQFXMW.d.mts +12 -0
  90. package/dist/state-BxCgeTQu.mjs +41 -0
  91. package/dist/{state-DwAU2_-D.mjs → state-CaeaHbDA.mjs} +7 -4
  92. package/dist/{state-l1pYwOre.mjs → state-DFm77wRn.mjs} +67 -5
  93. package/dist/stats-DBLLFmKd.mjs +66 -0
  94. package/dist/{status-TJlL0HWo.mjs → status-DYXxNOT0.mjs} +425 -260
  95. package/dist/{status-sszP4XKU.mjs → status-l84Gbvp2.mjs} +122 -20
  96. package/dist/stdout-reader-C8yjre1x.mjs +23 -0
  97. package/dist/stim-desktop-Cnz8CPnl.mjs +63 -0
  98. package/dist/stim-footprint.swift +32 -0
  99. package/dist/{stim-installations-CINXy_su.mjs → stim-installations-QMEI32v1.mjs} +1 -1
  100. package/dist/{stop-BZ2qMiBP.mjs → stop-Bad61z_2.mjs} +128 -19
  101. package/dist/stop-Bvf1MflS.mjs +48 -0
  102. package/dist/{stop-DskDKNTU.mjs → stop-CceYh1yG.mjs} +2 -2
  103. package/dist/stop-cause-CZ_Mr-v9.mjs +665 -0
  104. package/dist/supervisor-run.d.mts +44 -12
  105. package/dist/supervisor-run.mjs +276 -33
  106. package/dist/support-CDQbDkL5.mjs +315 -0
  107. package/dist/support-WLOzVKUv.mjs +176 -0
  108. package/dist/teardown-BYuQafiA.mjs +718 -0
  109. package/dist/{xcode-LM6A8cNZ.mjs → toolchain-DmeYjXD9.mjs} +363 -13
  110. package/dist/warm-progress-DVHLN4uC.mjs +463 -0
  111. package/dist/{idle-DxLi5OkN.mjs → watchman-Bx0wceoF.mjs} +55 -8
  112. package/dist/{web-sWXd5iek.mjs → web-k6w8-HnE.mjs} +39 -22
  113. package/dist/web-run.mjs +326 -13
  114. package/dist/workspace-process-lock-BL7Q0HCO.mjs +57 -0
  115. package/dist/workspace-state-CL2V2Ons.mjs +371 -0
  116. package/dist/worktree-BT_uULFx.mjs +2 -0
  117. package/dist/{worktree-DeBQSVtR.mjs → worktree-C0c5Udwa.mjs} +55 -54
  118. package/dist/{worktree-D4Do_XiQ.mjs → worktree-qXKzADSC.mjs} +161 -255
  119. package/package.json +10 -5
  120. package/shim/bundle-response.cjs +55 -10
  121. package/shim/bundle-response.d.cts +1 -0
  122. package/shim/expo-metro-config.cjs +10 -2
  123. package/dist/build-progress-l7d-X-ZP.mjs +0 -247
  124. package/dist/in-use-wYEaKQM9.mjs +0 -249
  125. package/dist/ownership-PUP4Dwai.mjs +0 -1138
  126. package/dist/server-expo-BeYwP_Xy.mjs +0 -2
  127. package/dist/slot-launch-BIWEW-wI.mjs +0 -19
  128. package/dist/start-Rw2GD3nV.mjs +0 -2
  129. package/dist/stats-CaSwmsSZ.mjs +0 -62
  130. package/dist/stats-D8rUTPHd.mjs +0 -368
  131. package/dist/stim-desktop-CXt8-Z3J.mjs +0 -40
  132. package/dist/worktree-3cBa-bQt.mjs +0 -2
  133. package/dist/{metro-store-DzeWkfqW.mjs → metro-store-CDDZHtPl.mjs} +1 -1
@@ -1,20 +1,21 @@
1
- import { d as getConfigDir } from "./config-0jCcMM0E.mjs";
2
- import { t as getExecutor } from "./exec-DK8KaVHK.mjs";
3
- import { A as workspaceId, F as withDirLock, M as workspaceLogsDir, P as workspaceStateFile, _ as updateWorkspaceState, a as pidExists, h as readWorkspaceState$1, k as workspaceDir, p as clearWorkspaceStateKey } from "./metro-BPeY1YMh.mjs";
4
- import { c as ownedDeviceLabel } from "./project-DGeclhvO.mjs";
5
- import { a as clearRemoteSession, c as readMetroTunnel, u as readRemoteSession } from "./state-DwAU2_-D.mjs";
6
- import { i as signalDeclaredSpawns, r as refuseDeclaredSpawns, t as allowDeclaredSpawns } from "./spawn-claims-PT8aQpCa.mjs";
7
- import { a as withWorkspaceProcessLock } from "./named-ports-n7tnJsI8.mjs";
8
- import { a as formatElapsed } from "./command-output-BIL7rlX7.mjs";
9
- import { V as planMetroReach } from "./settings-xB0Frkg6.mjs";
10
- import { u as probePublicHttp } from "./ownership-PUP4Dwai.mjs";
11
- import { C as sanitizeDeviceLabel } from "./ios-CmEzZiOj.mjs";
12
- import { a as LAUNCH_ERROR, i as INSTALL_ERROR, l as devClientDeepLink } from "./app-install-CDFY2nUL.mjs";
13
- import { c as readMetroRecords, o as isBundleProof } from "./launch-verify-CuJaBpEa.mjs";
14
- import { t as compareStimVersions } from "./stim-installations-CINXy_su.mjs";
1
+ import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
+ import { d as getConfigDir } from "./config-D0DbpZtM.mjs";
3
+ import { C as workspaceLogsDir, E as withDirLock, T as workspaceStateFile, b as workspaceDir, x as workspaceId } from "./workspace-state-CL2V2Ons.mjs";
4
+ import { c as ownedDeviceLabel } from "./project-CV3tDFHl.mjs";
5
+ import { a as clearRemoteSession, c as readMetroTunnel, u as readRemoteSession } from "./state-CaeaHbDA.mjs";
6
+ import { t as withWorkspaceProcessLock } from "./workspace-process-lock-BL7Q0HCO.mjs";
7
+ import { a as formatElapsed } from "./command-output-CliHHn7a.mjs";
8
+ import { o as pidExists } from "./metro-Di8EN2Tb.mjs";
9
+ import { W as planMetroReach } from "./settings-CduVcoxj.mjs";
10
+ import { T as sanitizeDeviceLabel } from "./ios-3CtCRhwr.mjs";
11
+ import { y as probePublicHttp } from "./stop-cause-CZ_Mr-v9.mjs";
12
+ import { i as INSTALL_ERROR, o as LAUNCH_ERROR, u as devClientDeepLink } from "./app-install-CAAplXAt.mjs";
13
+ import { c as readMetroRecords, o as isBundleProof } from "./launch-verify-CLlofxRt.mjs";
14
+ import { t as compareStimVersions } from "./stim-installations-QMEI32v1.mjs";
15
+ import { c as nativeRunWaitNotice } from "./native-run-DFDoIRbY.mjs";
15
16
  import { existsSync, mkdirSync, realpathSync, renameSync, rmSync, writeFileSync } from "node:fs";
16
- import { easMachineStateRoot, easMachineStateRoot as easMachineStateRoot$1, easSessionLedgerFile, easSessionLedgerLock, readEasSessionLedger, readEasSessionLedger as readEasSessionLedger$1, readJsonObject, validEasSessionClaim } from "@stim-cli/core/state";
17
17
  import { dirname, join, resolve } from "node:path";
18
+ import { easMachineStateRoot, easMachineStateRoot as easMachineStateRoot$1, easSessionLedgerFile, easSessionLedgerLock, readEasSessionLedger, readEasSessionLedger as readEasSessionLedger$1, readJsonObject, validEasSessionClaim } from "@stim-cli/core/state";
18
19
  //#region src/engine/metro-gate.ts
19
20
  const REMOTE_METRO_WRONG = "STIM_REMOTE_METRO_WRONG";
20
21
  const GATE_TIMEOUT_MS = 25e3;
@@ -145,6 +146,27 @@ function disconnectArgs(profilePath) {
145
146
  function closeArgs(profilePath) {
146
147
  return withProfile(profilePath, ["close"]);
147
148
  }
149
+ const HEALTH_TIMEOUT_MS = 3e3;
150
+ function archField(value) {
151
+ if (!value || typeof value !== "object") return null;
152
+ const arch = value.hostArch;
153
+ return arch === "arm64" || arch === "x86_64" ? arch : null;
154
+ }
155
+ /** agent-device proxies report the daemon machine under `upstream`; a daemon reports itself at the top level. */
156
+ function parseHealthHostArch(body) {
157
+ if (!body || typeof body !== "object") return null;
158
+ return archField(body.upstream) ?? archField(body);
159
+ }
160
+ async function readRemoteSimulatorArch(daemon, { fetch: fetchImpl = fetch, timeoutMs = HEALTH_TIMEOUT_MS } = {}) {
161
+ if (!daemon) return "arm64";
162
+ try {
163
+ const response = await fetchImpl(`${daemon.baseUrl.replace(/\/+$/, "")}/health`, { signal: AbortSignal.timeout(timeoutMs) });
164
+ if (!response.ok) return "arm64";
165
+ return parseHealthHostArch(await response.json()) ?? "arm64";
166
+ } catch {
167
+ return "arm64";
168
+ }
169
+ }
148
170
  //#endregion
149
171
  //#region src/engine/eas-simulator.ts
150
172
  const OWNED_PREFIX = "stim-";
@@ -153,6 +175,7 @@ const LIVE_SESSION_STATUSES = /* @__PURE__ */ new Set(["NEW", "IN_PROGRESS"]);
153
175
  const TERMINAL_SESSION_STATUSES = /* @__PURE__ */ new Set(["STOPPED", "ERRORED"]);
154
176
  const SESSION_PLATFORMS = /* @__PURE__ */ new Set(["ios", "android"]);
155
177
  const MIN_EAS_CLI_SIMULATOR_VERSION = "21.6.0";
178
+ const MIN_EAS_CLI_DEVICE_VERSION = "22.2.0";
156
179
  function easCliUpgradeRemedy(minimum) {
157
180
  return `Upgrade eas-cli to ${minimum} or later (\`npm install --global eas-cli@latest\`, or the project's eas-cli dependency)`;
158
181
  }
@@ -171,7 +194,7 @@ function ownedSessionName(label) {
171
194
  function isOwnedSessionName(name) {
172
195
  return typeof name === "string" && name.startsWith(OWNED_PREFIX);
173
196
  }
174
- function createSessionArgs({ label, platform, maxDurationMinutes = null }) {
197
+ function createSessionArgs({ label, platform, maxDurationMinutes = null, device = null }) {
175
198
  const args = [
176
199
  "sim",
177
200
  "--platform",
@@ -184,6 +207,7 @@ function createSessionArgs({ label, platform, maxDurationMinutes = null }) {
184
207
  ownedSessionName(label)
185
208
  ];
186
209
  if (maxDurationMinutes) args.push("--max-duration-minutes", String(maxDurationMinutes));
210
+ if (device) args.push("--device", device);
187
211
  return args;
188
212
  }
189
213
  function getSessionArgs(sessionId) {
@@ -504,144 +528,6 @@ function withEasProjectLock(_root, fn, options = {}) {
504
528
  });
505
529
  }
506
530
  //#endregion
507
- //#region src/engine/native-run.ts
508
- const NATIVE_RUN_LOCK = "native-run";
509
- const NATIVE_RUN_WAIT_MS = 18e5;
510
- const WAIT_HEARTBEAT_MS = 3e4;
511
- const CANCEL_REQUEST_KEY = "nativeRunCancel";
512
- function nativeRunHolder(holder) {
513
- const { command, platform, slot } = holder.details;
514
- return {
515
- command: typeof command === "string" ? command : null,
516
- platform: typeof platform === "string" ? platform : null,
517
- slot: typeof slot === "string" ? slot : "default"
518
- };
519
- }
520
- function describeNativeRunHolder(holder, now) {
521
- const { command, slot } = nativeRunHolder(holder);
522
- const started = Date.parse(holder.startedAt);
523
- const facts = [`pid ${holder.owner.pid}`];
524
- if (Number.isFinite(started)) facts.push(`running for ${formatElapsed(now - started)}`);
525
- if (command === null) return `another Stim run (${facts.join(", ")})`;
526
- return `\`stim ${command}${slot === "default" ? "" : ` --slot ${slot}`}\` (${facts.join(", ")})`;
527
- }
528
- /**
529
- * Report a wait on the native-run lock: one line as soon as a holder is seen, a line for each new
530
- * holder, and a heartbeat every WAIT_HEARTBEAT_MS while the same holder keeps it.
531
- */
532
- function nativeRunWaitNotice({ write, now = Date.now, describe = describeNativeRunHolder }) {
533
- let claimId = null;
534
- let lastAt = 0;
535
- return (holder, firstLine) => {
536
- const at = now();
537
- if (holder.claimId !== claimId) {
538
- claimId = holder.claimId;
539
- lastAt = at;
540
- write(firstLine ?? `waiting for ${describe(holder, at)} in this workspace to finish`);
541
- return;
542
- }
543
- if (at - lastAt < WAIT_HEARTBEAT_MS) return;
544
- lastAt = at;
545
- write(`still waiting for ${describe(holder, at)}`);
546
- };
547
- }
548
- /**
549
- * What `stop` does about the run holding the native-run lock. A build is interrupted only when the stop
550
- * leaves it nothing to deploy to: the whole workspace stops, the build targets the stopped slot, or the
551
- * stopped slot is the workspace's only device. A build for another slot that stays is left running and
552
- * the stop proceeds without the lock. Only an owner whose identity is proven live is signalled.
553
- */
554
- function decideStopAction({ stopSlot, holder, ownerIdentity, deviceSlots }) {
555
- if (holder.command !== "ios" && holder.command !== "android") return { action: "wait" };
556
- if (!(stopSlot === void 0 || holder.slot === stopSlot || deviceSlots.includes(stopSlot) && deviceSlots.every((slot) => slot === stopSlot))) return { action: "proceed" };
557
- return ownerIdentity === "same" ? { action: "interrupt" } : { action: "refuse" };
558
- }
559
- function requestNativeRunCancel(root, claimId, pid = process.pid) {
560
- updateWorkspaceState(root, (state) => ({
561
- ...state,
562
- [CANCEL_REQUEST_KEY]: {
563
- claimId,
564
- pid,
565
- at: (/* @__PURE__ */ new Date()).toISOString()
566
- }
567
- }));
568
- }
569
- function cancelRequestFor(root, claimId) {
570
- const request = readWorkspaceState$1(root)?.[CANCEL_REQUEST_KEY];
571
- if (request?.claimId !== claimId || typeof request.pid !== "number") return null;
572
- return { pid: request.pid };
573
- }
574
- function clearNativeRunCancel(root, claimId) {
575
- try {
576
- clearWorkspaceStateKey(root, CANCEL_REQUEST_KEY, (value) => value?.claimId === claimId);
577
- } catch {}
578
- }
579
- let cancelled = null;
580
- /** Why this process's native run was cancelled, or null while it was not. */
581
- function runCancellation() {
582
- return cancelled;
583
- }
584
- function cancelledFailure(platform, { code, message }) {
585
- if (cancelled === null) return null;
586
- return {
587
- code: "STIM_CANCELLED",
588
- message: `The ${platform} run was ${cancelled} before it finished.`,
589
- remedy: `Run \`stim ${platform}\` again when you want the app on a device.`,
590
- lines: [`it stopped at ${code ?? "an unfinished step"}${message ? `: ${message}` : ""}`]
591
- };
592
- }
593
- function cancelOnInterrupt({ root, claim, write, exit }) {
594
- let signals = 0;
595
- const onInterrupt = () => {
596
- signals += 1;
597
- if (signals > 1) {
598
- exit(130);
599
- return;
600
- }
601
- const request = cancelRequestFor(root, claim.claimId);
602
- cancelled = request ? `cancelled by \`stim stop\` (pid ${request.pid})` : "cancelled by an interrupt";
603
- refuseDeclaredSpawns(`the run was ${cancelled}`);
604
- const signalled = signalDeclaredSpawns("SIGINT");
605
- write(signalled > 0 ? `${cancelled}; stopping the running build tool` : request ? `${cancelled}; stopping before the next build tool or install` : cancelled);
606
- if (signalled === 0 && !request) exit(130);
607
- };
608
- process.on("SIGINT", onInterrupt);
609
- return () => {
610
- process.off("SIGINT", onInterrupt);
611
- allowDeclaredSpawns();
612
- clearNativeRunCancel(root, claim.claimId);
613
- };
614
- }
615
- /**
616
- * Run an `ios` or `android` invocation under the workspace's native-run lock: the claim records the
617
- * command, platform and slot, and a wait for another holder is reported on `write`. The first SIGINT
618
- * forwards the interrupt to the running declared build tool; a run `stim stop` cancelled between build
619
- * tools starts no further one; a terminal interrupt with no build tool running, or a second SIGINT, exits
620
- * with 130 at once.
621
- */
622
- function withNativeBuildRun(root, details, fn, { write, exit = (code) => process.exit(code) }) {
623
- const notice = nativeRunWaitNotice({ write });
624
- return withWorkspaceProcessLock(workspaceDir(root), NATIVE_RUN_LOCK, async (claim) => {
625
- const dispose = cancelOnInterrupt({
626
- root,
627
- claim,
628
- write,
629
- exit
630
- });
631
- try {
632
- return await fn(claim);
633
- } finally {
634
- dispose();
635
- }
636
- }, {
637
- external: true,
638
- waitMs: NATIVE_RUN_WAIT_MS,
639
- declareSpawns: true,
640
- details: { ...details },
641
- onHeld: (holder) => notice(holder)
642
- });
643
- }
644
- //#endregion
645
531
  //#region src/engine/device-remote.ts
646
532
  const REMOTE_SESSION_ERROR = "STIM_NO_REMOTE_SESSION";
647
533
  const REMOTE_METRO_ERROR = "STIM_REMOTE_METRO_UNREACHABLE";
@@ -726,6 +612,12 @@ function remoteDeviceDeps(ctx) {
726
612
  };
727
613
  if (recorded) {
728
614
  const existing = readLiveDaemon(ctx, recorded.sessionId);
615
+ if (existing && ctx.deviceType && recorded.deviceType !== ctx.deviceType) return {
616
+ failed: true,
617
+ code: "STIM_REMOTE_DEVICE_MISMATCH",
618
+ reason: `EAS Simulator session ${recorded.sessionId} runs ${recorded.deviceType ? `"${recorded.deviceType}"` : "the model EAS chose"}, not "${ctx.deviceType}". Stim will not reuse a session on a different model.`,
619
+ remedy: `Run \`stim stop\` to end that session, then run \`stim ${ctx.platform ?? "ios"} --remote eas --device-type "${ctx.deviceType}"\` again to start one on the requested model.`
620
+ };
729
621
  if (existing) {
730
622
  note(`Reusing EAS Simulator session ${recorded.sessionId}.`);
731
623
  daemon = existing;
@@ -750,7 +642,8 @@ function remoteDeviceDeps(ctx) {
750
642
  stdout = getExecutor().runFile(ctx.easBin, createSessionArgs({
751
643
  label: ctx.label,
752
644
  platform: ctx.platform ?? "ios",
753
- maxDurationMinutes: ctx.maxDurationMinutes ?? null
645
+ maxDurationMinutes: ctx.maxDurationMinutes ?? null,
646
+ device: ctx.deviceType ?? null
754
647
  }), {
755
648
  ...easEnv,
756
649
  timeoutMs: EAS_SESSION_CREATE_TIMEOUT_MS,
@@ -926,7 +819,7 @@ function remoteDeviceDeps(ctx) {
926
819
  failureRemedy: () => ctx.backend === "eas" && session?.id ? `EAS Simulator session ${session.id} is still running and billed until it reaches its time limit or \`stim stop\` ends it. Fix the error above and run this command again to reuse the session, or run \`stim stop\` to end it.` : "Check that the remote daemon is reachable and still has the device, then run this command again."
927
820
  };
928
821
  }
929
- async function resolveRemoteContext({ root, backend, platform = "ios", easBin, env = process.env, lookupAgentDevice = defaultLookupAgentDevice, readEasCliVersion = readInstalledEasCliVersion, maxDurationMinutes = null }) {
822
+ async function resolveRemoteContext({ root, backend, platform = "ios", easBin, env = process.env, lookupAgentDevice = defaultLookupAgentDevice, readEasCliVersion = readInstalledEasCliVersion, maxDurationMinutes = null, deviceType = null }) {
930
823
  const agentDeviceBin = lookupAgentDevice();
931
824
  if (!agentDeviceBin) return {
932
825
  failed: "agent-device is not on PATH, and it is what drives a remote device.",
@@ -961,12 +854,18 @@ async function resolveRemoteContext({ root, backend, platform = "ios", easBin, e
961
854
  code: "STIM_REMOTE_EAS_UNAVAILABLE"
962
855
  };
963
856
  else {
964
- const { supported, version } = easCliSupport(readEasCliVersion(easBin, root), MIN_EAS_CLI_SIMULATOR_VERSION);
857
+ const versionOutput = readEasCliVersion(easBin, root);
858
+ const { supported, version } = easCliSupport(versionOutput, MIN_EAS_CLI_SIMULATOR_VERSION);
965
859
  if (!supported) return {
966
860
  failed: version ? `eas-cli ${version} (${easBin}) has no EAS Simulator commands; the eas backend needs eas-cli ${MIN_EAS_CLI_SIMULATOR_VERSION} or later.` : `Could not read an eas-cli version from \`${easBin} --version\`; the eas backend needs eas-cli ${MIN_EAS_CLI_SIMULATOR_VERSION} or later.`,
967
861
  remedy: `${easCliUpgradeRemedy(MIN_EAS_CLI_SIMULATOR_VERSION)}, then run the device command with \`--remote eas\` again.`,
968
862
  code: "STIM_REMOTE_EAS_UNAVAILABLE"
969
863
  };
864
+ if (deviceType && !easCliSupport(versionOutput, "22.2.0").supported) return {
865
+ failed: `eas-cli ${version} (${easBin}) cannot choose the EAS Simulator model; --device-type on the eas backend needs eas-cli ${MIN_EAS_CLI_DEVICE_VERSION} or later.`,
866
+ remedy: `${easCliUpgradeRemedy(MIN_EAS_CLI_DEVICE_VERSION)}, or drop --device-type to let EAS pick the model.`,
867
+ code: "STIM_BAD_ARG"
868
+ };
970
869
  }
971
870
  return { ctx: {
972
871
  root,
@@ -976,6 +875,7 @@ async function resolveRemoteContext({ root, backend, platform = "ios", easBin, e
976
875
  easBin: easBin ?? "",
977
876
  agentDeviceBin,
978
877
  maxDurationMinutes,
878
+ deviceType: backend === "eas" ? deviceType : null,
979
879
  publicMetroUrl: null,
980
880
  existingDaemon
981
881
  } };
@@ -1066,7 +966,7 @@ function withRemoteSessionLock(root, fn, options = {}) {
1066
966
  });
1067
967
  }
1068
968
  const EAS_START_HOLD_MS = 108e4 + Math.ceil(DAEMON_WAIT_MS / DAEMON_POLL_MS) * 35e3;
1069
- async function ensureRemoteBootOwned({ root, platform, sessionName, startedAt, boot, createdSessionId, abandonCreatedSession, webPreviewUrl = () => null, writeState, register = () => {}, withProjectLock = withEasProjectLock, withLock = withRemoteSessionLock, removeEasSessionClaim: removeClaim = removeEasSessionClaim, ledgerRoot = easMachineStateRoot$1(), notice = () => {}, now = Date.now }) {
969
+ async function ensureRemoteBootOwned({ root, platform, sessionName, startedAt, deviceType = null, boot, createdSessionId, abandonCreatedSession, webPreviewUrl = () => null, writeState, register = () => {}, withProjectLock = withEasProjectLock, withLock = withRemoteSessionLock, removeEasSessionClaim: removeClaim = removeEasSessionClaim, ledgerRoot = easMachineStateRoot$1(), notice = () => {}, now = Date.now }) {
1070
970
  const waitNotice = nativeRunWaitNotice({
1071
971
  write: notice,
1072
972
  now,
@@ -1098,7 +998,8 @@ async function ensureRemoteBootOwned({ root, platform, sessionName, startedAt, b
1098
998
  platform,
1099
999
  sessionId,
1100
1000
  startedAt,
1101
- ...preview ? { webPreviewUrl: preview } : {}
1001
+ ...preview ? { webPreviewUrl: preview } : {},
1002
+ ...deviceType ? { deviceType } : {}
1102
1003
  } });
1103
1004
  return booted;
1104
1005
  } catch (err) {
@@ -1378,13 +1279,18 @@ async function ensureMetroReachable({ ctx, metroPort, isExpo, tunnelMode = "auto
1378
1279
  gate = true;
1379
1280
  } else {
1380
1281
  const port = Number(metroPort);
1381
- const providerMatches = tunnelMode === "auto" || recorded?.kind === "managed" && recorded.provider === plan.start;
1282
+ if (plan.start === "tailscale" && ctx.backend === "eas") return {
1283
+ failed: "metro.tunnel is \"tailscale\", but an EAS Simulator is not on your tailnet and cannot reach it.",
1284
+ remedy: "Use the proxy backend with a device on the same tailnet, or set metro.tunnel to \"auto\" or \"ngrok\".",
1285
+ code: REMOTE_METRO_ERROR
1286
+ };
1287
+ const providerMatches = recorded?.kind === "managed" && recorded.provider === plan.start;
1382
1288
  if (recorded && recorded.kind === "managed" && recorded.port === port && providerMatches && isTunnelAlive(recorded.pid)) resolvedUrl = recorded.url;
1383
1289
  else return {
1384
1290
  failed: `No live managed Metro tunnel is recorded for port ${port}.`,
1385
1291
  remedy: "Run `stim start --remote`, then retry the device command."
1386
1292
  };
1387
- gate = true;
1293
+ gate = recorded.provider !== "tailscale";
1388
1294
  }
1389
1295
  if (gate && resolvedUrl) {
1390
1296
  const logsDir = workspaceLogsDir(root);
@@ -1415,4 +1321,4 @@ function bundleEntryPoint(root, isExpo) {
1415
1321
  return local ? normalized : `node_modules/${normalized}`;
1416
1322
  }
1417
1323
  //#endregion
1418
- export { findOrphanedOwnedSessions as A, withEasProjectLock as C, MIN_EAS_CLI_SIMULATOR_VERSION as D, removeEasSessionClaim as E, ownedSessionName as F, parseSessionListPage as I, stopSessionArgs as L, inspectSessionForTeardown as M, isDefinitiveMissingSessionError as N, easCliSupport as O, listOwnedSessionsArgs as P, verifyStoppedSession as R, withNativeBuildRun as S, readEasSessionLedger$1 as T, describeNativeRunHolder as _, ensureMetroReachable as a, requestNativeRunCancel as b, remoteAndroidDeps as c, withRemoteSessionLock as d, NATIVE_RUN_LOCK as f, decideStopAction as g, clearNativeRunCancel as h, endRecordedSession as i, getSessionArgs as j, easCliUpgradeRemedy as k, remoteIosDeps as l, cancelledFailure as m, binOnPath as n, ensureRemoteBootOwned as o, NATIVE_RUN_WAIT_MS as p, bundleEntryPoint as r, readInstalledEasCliVersion as s, REMOTE_SESSION_ERROR as t, resolveRemoteContext as u, nativeRunHolder as v, easMachineStateRoot$1 as w, runCancellation as x, nativeRunWaitNotice as y, gateMetroOrigin as z };
1324
+ export { listOwnedSessionsArgs as C, verifyStoppedSession as D, stopSessionArgs as E, readRemoteSimulatorArch as O, isDefinitiveMissingSessionError as S, parseSessionListPage as T, easCliSupport as _, ensureMetroReachable as a, getSessionArgs as b, remoteAndroidDeps as c, withRemoteSessionLock as d, withEasProjectLock as f, MIN_EAS_CLI_SIMULATOR_VERSION as g, removeEasSessionClaim as h, endRecordedSession as i, gateMetroOrigin as k, remoteIosDeps as l, readEasSessionLedger$1 as m, binOnPath as n, ensureRemoteBootOwned as o, easMachineStateRoot$1 as p, bundleEntryPoint as r, readInstalledEasCliVersion as s, REMOTE_SESSION_ERROR as t, resolveRemoteContext as u, easCliUpgradeRemedy as v, ownedSessionName as w, inspectSessionForTeardown as x, findOrphanedOwnedSessions as y };