stim 1.14.0 → 1.16.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.
- package/README.md +2 -0
- package/dist/activity-BxwdD81S.mjs +609 -0
- package/dist/agent-device-usage-output-Dpih1Tfa.mjs +411 -0
- package/dist/{android-d6rGq1pA.mjs → android-B_NumVyQ.mjs} +169 -81
- package/dist/{android-LbPFRWVL.mjs → android-Ift4kVvt.mjs} +517 -413
- package/dist/android-cas-8U4DPqeR.mjs +167 -0
- package/dist/android-cas-compiler.mjs +2 -2
- package/dist/{android-XpvCrV7q.mjs → android-r2rBL8kV.mjs} +2 -2
- package/dist/{app-install-BpgSgkJj.mjs → app-install-CKWdbXIX.mjs} +128 -151
- package/dist/attempt-Bk2ouQVL.mjs +39 -0
- package/dist/budget-DjrURfQn.mjs +2700 -0
- package/dist/{build-plan-Dcr-3ie0.mjs → build-plan-DjeSb2Xl.mjs} +192 -653
- package/dist/build-slots-rUk77xqh.mjs +132 -0
- package/dist/{cache-manifest-C68QQ0ua.mjs → cache-manifest-DnUHBJaU.mjs} +2 -2
- package/dist/cache-manifest.mjs +1 -1
- package/dist/{chrome-BPCiC3ZX.mjs → chrome-BrcVWQyD.mjs} +1 -1
- package/dist/cli-BkYZ_6hy.mjs +51 -0
- package/dist/cli.mjs +6 -42
- package/dist/client-BTY686R6.mjs +1368 -0
- package/dist/collector-run.mjs +9 -195
- package/dist/{command-output-DVcpw6eE.mjs → command-output-DnJMdEY-.mjs} +1 -1
- package/dist/{config-lvc7d3C2.mjs → config-D0DbpZtM.mjs} +28 -29
- package/dist/{created-devices-CllH_8eb.mjs → created-devices-D5owjN4C.mjs} +2 -2
- package/dist/{dependency-state-DsT7aKeo.mjs → dependency-state-B6cMXZab.mjs} +2 -2
- package/dist/{deps-D9IcKqXS.mjs → deps-urbmfXig.mjs} +3 -3
- package/dist/{detached-entry-DJHoca49.mjs → detached-entry-CiTEgMR1.mjs} +1 -1
- package/dist/dev-client-CroAIjjj.mjs +43 -0
- package/dist/{device-CjuuAJjQ.mjs → device-DZQjF7H5.mjs} +8 -7
- package/dist/device-capacity-BHwxNBlu.mjs +136 -0
- package/dist/device-host-worker.d.mts +1 -0
- package/dist/device-host-worker.mjs +964 -0
- package/dist/device-ios-BadmeL2V.mjs +381 -0
- package/dist/{device-lease-5c3WrVRm.mjs → device-lease-D-hLqx1b.mjs} +43 -36
- package/dist/{device-pool-BiyDyPP-.mjs → device-lease-run-CqjShqWW.mjs} +6 -126
- package/dist/device-pool-CUaEWMMm.mjs +125 -0
- package/dist/{device-remote-D87zEzaq.mjs → device-remote-D5WVWqOq.mjs} +69 -163
- package/dist/{doctor-x9pnU9fq.mjs → doctor-Cimf-gGB.mjs} +241 -103
- package/dist/{error-diagnostics-C5HzcwCW.mjs → error-diagnostics-B9L4Ae0x.mjs} +15 -18
- package/dist/{exec-DK8KaVHK.mjs → exec-CaErVv7t.mjs} +5 -1
- package/dist/{gc-ko6mUAYD.mjs → gc-Bqy4bjEu.mjs} +599 -108
- package/dist/{android-cas-iZK3DOCr.mjs → gradle-BsO1MdsH.mjs} +8 -294
- package/dist/{guide-6R0VNFqI.mjs → guide-B_uyKTxW.mjs} +2511 -239
- package/dist/{guide-status-BZrFPIA_.mjs → guide-status-CjEZMAD4.mjs} +3 -3
- package/dist/hosted-client-a-WmqE2L.mjs +197 -0
- package/dist/hosted-ios-CIWelIqf.mjs +220 -0
- package/dist/hosted-logs-D5g041Q3.mjs +187 -0
- package/dist/hosted-macos-BSZyIb3y.mjs +135 -0
- package/dist/idle-DYVymG92.mjs +1099 -0
- package/dist/idle-shutdown-CTsDnAde.mjs +122 -0
- package/dist/in-use-DvBw0Kxo.mjs +130 -0
- package/dist/{ios-BwDPrnYQ.mjs → ios-BaInn3sW.mjs} +201 -10
- package/dist/ios-Cr1h5gnO.mjs +189 -0
- package/dist/{ios-P1f5nm2n.mjs → ios-aq3QoGI3.mjs} +1159 -633
- package/dist/{ios-device-BiOsSVzd.mjs → ios-device-Ee7sDXfp.mjs} +1 -1
- package/dist/{ios-device-DGglU3E_.mjs → ios-device-FfCN_C8p.mjs} +12 -7
- package/dist/ios-state-DS5UpYTH.mjs +48 -0
- package/dist/{launch-verify-Deg4tB3Z.mjs → launch-verify-2DBRizjo.mjs} +5 -5
- package/dist/logs-CHEVK8zD.mjs +371 -0
- package/dist/machines-BIBUdhpj.mjs +265 -0
- package/dist/macos-CSYEAFmF.mjs +473 -0
- package/dist/macos-run.d.mts +5 -0
- package/dist/macos-run.mjs +2 -0
- package/dist/maintenance-run.d.mts +4 -0
- package/dist/maintenance-run.mjs +254 -0
- package/dist/{metro-CcEo8Fl8.mjs → metro-BeK1ZPz1.mjs} +20 -120
- package/dist/metro-gateway-BJ-rxQAB.mjs +84 -0
- package/dist/{named-ports-Blg8IxOh.mjs → named-ports-Bk_YXEmE.mjs} +6 -59
- package/dist/native-run-NaBrRFtQ.mjs +147 -0
- package/dist/{native-runtime-DjBHRg5m.mjs → native-runtime-DBzRnEKS.mjs} +9 -5
- package/dist/{ndjson-CDY9IkVC.mjs → ndjson-PclOIwVY.mjs} +2 -2
- package/dist/offload-worker.d.mts +122 -0
- package/dist/offload-worker.mjs +606 -0
- package/dist/{ownership-DyYDF5ww.mjs → ownership-DiA16AsN.mjs} +3 -2
- package/dist/ownership-hCkPzGmr.mjs +571 -0
- package/dist/{page-BHUzTDFt.mjs → page-QF_Ke10s.mjs} +7 -2
- package/dist/{ports-BguS83ex.mjs → ports-R2k4Knz3.mjs} +3 -3
- package/dist/prebuild-D2fsca_Z.mjs +213 -0
- package/dist/preview-DhVbvPAZ.mjs +318 -0
- package/dist/process-identity-BmRm1qV0.mjs +12 -0
- package/dist/{project-CWlvEaeN.mjs → project-Du91Ilvc.mjs} +74 -19
- package/dist/pull-request-pxnDCQzn.mjs +322 -0
- package/dist/pull-requests.d.mts +120 -0
- package/dist/pull-requests.mjs +2 -0
- package/dist/recordings-BIXOzVnr.mjs +37 -0
- package/dist/{reload-BiniwsSZ.mjs → reload-rM9jV1fz.mjs} +109 -24
- package/dist/{remote-cache-CcR6o6lI.mjs → remote-cache-C8-V13XN.mjs} +5 -5
- package/dist/run-f4vI6EnK.mjs +119 -0
- package/dist/{server-bare-DGfgyJ5J.mjs → server-bare-CpPGmnmO.mjs} +33 -15
- package/dist/server-expo-CVqO2c35.mjs +2 -0
- package/dist/{server-expo-BVuwQ65v.mjs → server-expo-MC0rHlOC.mjs} +8 -12
- package/dist/settings-BBpMcDbD.mjs +25 -0
- package/dist/{settings-B-fsC-Om.mjs → settings-ZkDzkzvB.mjs} +22 -10
- package/dist/settings-oTSkkNrM.mjs +1439 -0
- package/dist/settings.schema.json +845 -97
- package/dist/{simslim-DrDN4WuV.mjs → simslim-B8-7AoTI.mjs} +8 -132
- package/dist/{slot-launch-D5t8EiMf.mjs → slot-launch-B8VrHxn1.mjs} +11 -8
- package/dist/{spawn-claims-Drw_o-Fb.mjs → spawn-claims-D0XfSe8u.mjs} +1 -1
- package/dist/spawn-entry-jw19m8Nl.mjs +17 -0
- package/dist/stage-CzaiwFsI.mjs +143 -0
- package/dist/start-CX8Ig-U2.mjs +2 -0
- package/dist/{start-s4RXp1wy.mjs → start-ClDfwXa4.mjs} +116 -65
- package/dist/state-3DwSQIBK.mjs +41 -0
- package/dist/state-B4PQFXMW.d.mts +12 -0
- package/dist/{state-Dk9RVXI7.mjs → state-C1jB7Q8q.mjs} +70 -8
- package/dist/{state-DHqvsbqC.mjs → state-DcW6vOd_.mjs} +7 -4
- package/dist/stats-CEZ_i5je.mjs +77 -0
- package/dist/{status-BgAuxESg.mjs → status-BuvBFeII.mjs} +118 -20
- package/dist/{status-BsPAnfOI.mjs → status-Cs7VLg9D.mjs} +542 -283
- package/dist/status-DlKvVJBE.mjs +49 -0
- package/dist/stdout-reader-C8yjre1x.mjs +23 -0
- package/dist/stim-desktop-Cnz8CPnl.mjs +63 -0
- package/dist/stim-footprint.swift +32 -0
- package/dist/{stim-installations-CINXy_su.mjs → stim-installations-QMEI32v1.mjs} +1 -1
- package/dist/stop-B0Yxtmiy.mjs +48 -0
- package/dist/{stop-BzreOWJp.mjs → stop-Bffozsac.mjs} +83 -16
- package/dist/{stop-BWIF_6BK.mjs → stop-Dt_dM16A.mjs} +2 -2
- package/dist/stop-cause-CidxIqup.mjs +97 -0
- package/dist/supervisor-run.d.mts +43 -11
- package/dist/supervisor-run.mjs +363 -24
- package/dist/support-CAP8qDUF.mjs +316 -0
- package/dist/support-D4aYJniO.mjs +201 -0
- package/dist/teardown-CL6vDs2Y.mjs +727 -0
- package/dist/{xcode-Cp5KvK9x.mjs → toolchain-DkUflduf.mjs} +364 -14
- package/dist/trigger-Bemu8ytw.mjs +65 -0
- package/dist/trigger-CXts6qt1.mjs +2 -0
- package/dist/warm-progress-CpGv3Wb6.mjs +732 -0
- package/dist/{web-BsQs1WlO.mjs → web-DWUaMp8P.mjs} +37 -21
- package/dist/web-run.mjs +310 -13
- package/dist/workspace-process-lock-BL7Q0HCO.mjs +57 -0
- package/dist/workspace-state-B8-aJ1_5.mjs +371 -0
- package/dist/workspaces-BV4pdHFz.mjs +306 -0
- package/dist/{worktree-UrQWXSeb.mjs → worktree-K-xj9Db0.mjs} +625 -256
- package/dist/worktree-jnG8Iph5.mjs +2 -0
- package/package.json +10 -5
- package/shim/bundle-response.cjs +28 -12
- package/dist/activity-CYVDcrt3.mjs +0 -356
- package/dist/budget-CGDkhvv_.mjs +0 -1745
- package/dist/build-progress-DxKpwnaS.mjs +0 -247
- package/dist/idle-DXtx3RbX.mjs +0 -191
- package/dist/in-use-DlqBDKqY.mjs +0 -249
- package/dist/logs-Civ4gbBH.mjs +0 -486
- package/dist/merge-state-ytFQCeh4.mjs +0 -262
- package/dist/server-expo-DIHDhvGg.mjs +0 -2
- package/dist/settings-D61iNOsB.mjs +0 -629
- package/dist/start-Cetfvy8l.mjs +0 -2
- package/dist/stats-CFpFGlJV.mjs +0 -62
- package/dist/stats-CX1Z8Tca.mjs +0 -368
- package/dist/stim-desktop-CXt8-Z3J.mjs +0 -40
- package/dist/stop-cause-hjGBOZTG.mjs +0 -1231
- package/dist/worktree-BVrHsYZG.mjs +0 -752
- package/dist/worktree-CHWWvm5v.mjs +0 -2
- package/dist/{metro-store-DzeWkfqW.mjs → metro-store-CDDZHtPl.mjs} +1 -1
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as formatElapsed, r as clockTime } from "./command-output-
|
|
3
|
-
import { a as parseLease, c as raiseLease,
|
|
4
|
-
import { a as VERIFY_TIMEOUT_MS, m as APP_READINESS_TIMEOUT_MS } from "./launch-verify-
|
|
1
|
+
import { R as deviceSlotKey } from "./config-D0DbpZtM.mjs";
|
|
2
|
+
import { a as formatElapsed, r as clockTime } from "./command-output-DnJMdEY-.mjs";
|
|
3
|
+
import { a as parseLease, c as raiseLease, l as releaseRunLease, n as fileLeaseIo, p as takeLease, r as leaseIsExpired, t as deviceLeasePath } from "./device-lease-D-hLqx1b.mjs";
|
|
4
|
+
import { a as VERIFY_TIMEOUT_MS, m as APP_READINESS_TIMEOUT_MS } from "./launch-verify-2DBRizjo.mjs";
|
|
5
5
|
//#region src/engine/device-lease-run.ts
|
|
6
6
|
const LEASE_STEP_FLOOR_MS = 6e4;
|
|
7
7
|
const DEBUG_VERIFY_STEP_MS = VERIFY_TIMEOUT_MS + APP_READINESS_TIMEOUT_MS;
|
|
8
8
|
const DEVICE_WAIT_POLL_MS = 2e3;
|
|
9
|
+
const DEVICE_WAIT_LINE_MS = 3e4;
|
|
9
10
|
const DEVICE_BUSY = "STIM_DEVICE_BUSY";
|
|
10
11
|
const DEVICE_LOST = "STIM_DEVICE_LOST";
|
|
11
12
|
function leaseStepMs(boundMs = 0) {
|
|
@@ -290,125 +291,4 @@ async function acquireRunLease({ root, platform, slot = "default", id, deviceNam
|
|
|
290
291
|
}
|
|
291
292
|
}
|
|
292
293
|
//#endregion
|
|
293
|
-
|
|
294
|
-
function heldPoolId(root, platform, now, io = fileLeaseIo, slot = "default") {
|
|
295
|
-
const record = io.readHolder(root)[deviceSlotKey(platform, slot)];
|
|
296
|
-
if (!record) return null;
|
|
297
|
-
const lease = parseLease(io.readLease(deviceLeasePath(platform, record.id)));
|
|
298
|
-
if (!lease || lease.token !== record.token || leaseIsExpired(lease, now)) return null;
|
|
299
|
-
return record.id;
|
|
300
|
-
}
|
|
301
|
-
function readPool(platform, candidates, io) {
|
|
302
|
-
const reading = {
|
|
303
|
-
usable: [],
|
|
304
|
-
leases: [],
|
|
305
|
-
unreadable: []
|
|
306
|
-
};
|
|
307
|
-
for (const candidate of candidates) {
|
|
308
|
-
const path = deviceLeasePath(platform, candidate.id);
|
|
309
|
-
const raw = io.readLease(path);
|
|
310
|
-
const lease = parseLease(raw);
|
|
311
|
-
if (raw !== null && !lease) {
|
|
312
|
-
reading.unreadable.push(path);
|
|
313
|
-
continue;
|
|
314
|
-
}
|
|
315
|
-
reading.usable.push(candidate);
|
|
316
|
-
if (lease) reading.leases.push(lease);
|
|
317
|
-
}
|
|
318
|
-
return reading;
|
|
319
|
-
}
|
|
320
|
-
function disconnectedHeldRefusal(id, idLabel) {
|
|
321
|
-
return {
|
|
322
|
-
code: "STIM_NO_DEVICE",
|
|
323
|
-
message: `This workspace leases ${id}, and it is not connected.`,
|
|
324
|
-
remedy: `Run \`stim device unlock\` to give it up, then run this command again. Naming another \`--device <${idLabel}>\` refuses the same way while this lease is held.`,
|
|
325
|
-
lease: null
|
|
326
|
-
};
|
|
327
|
-
}
|
|
328
|
-
const NO_LEASE_FACTS = {
|
|
329
|
-
platform: null,
|
|
330
|
-
id: null,
|
|
331
|
-
deviceName: null,
|
|
332
|
-
holder: null,
|
|
333
|
-
expiresAt: null
|
|
334
|
-
};
|
|
335
|
-
function unreadableSentence(unreadable) {
|
|
336
|
-
return unreadable.length ? ` Unreadable lease file, so no run may take that device around it: ${unreadable.join("; ")}.` : "";
|
|
337
|
-
}
|
|
338
|
-
function poolBusyRefusal(holders, leases, unreadable, now, waitSeconds, spare = [], idLabel = "id") {
|
|
339
|
-
const named = holders.map((holder) => `${holder.id} (${holder.holder}, until ${leaseExpiryText(holder.expiresAt, now)})`);
|
|
340
|
-
const first = holders[0];
|
|
341
|
-
const lease = first ? leases.find((entry) => entry.id === first.id) : void 0;
|
|
342
|
-
if (!first) return {
|
|
343
|
-
code: DEVICE_BUSY,
|
|
344
|
-
message: `Every connected device has an unreadable lease file, so Stim cannot tell who holds them: ${unreadable.join("; ")}.`,
|
|
345
|
-
remedy: "Read each file and, once you know no run depends on it, delete it. `stim gc` reports them on every run.",
|
|
346
|
-
lease: NO_LEASE_FACTS
|
|
347
|
-
};
|
|
348
|
-
return {
|
|
349
|
-
code: DEVICE_BUSY,
|
|
350
|
-
message: `Every connected device is leased by another workspace${waitSeconds > 0 ? `, and this run waited ${waitSeconds}s for one` : ""}: ${named.join("; ")}.${unreadableSentence(unreadable)}`,
|
|
351
|
-
remedy: (spare.length ? `Also connected and free: ${spare.join(", ")}. Name one with \`--device <${idLabel}>\` to use it now. ` : "") + "Wait longer with `--wait <seconds>`, connect another device, or pass `--no-wait` to install anyway -- which takes no lease and, when both workspaces build the same app id, terminates the holder's running app.",
|
|
352
|
-
lease: {
|
|
353
|
-
platform: lease?.platform ?? null,
|
|
354
|
-
id: first.id,
|
|
355
|
-
deviceName: lease?.deviceName ?? null,
|
|
356
|
-
holder: first.holder,
|
|
357
|
-
expiresAt: first.expiresAt
|
|
358
|
-
}
|
|
359
|
-
};
|
|
360
|
-
}
|
|
361
|
-
async function selectFromPool({ root, platform, slot = "default", idLabel, list, noCandidates, waitSeconds, deadline, noWait = false, now = Date.now, sleep = (ms) => new Promise((r) => setTimeout(r, ms)), warn = () => {}, io = fileLeaseIo }) {
|
|
362
|
-
const held = heldPoolId(root, platform, now(), io, slot);
|
|
363
|
-
const until = deadline ?? now() + waitSeconds * 1e3;
|
|
364
|
-
let lastLine = null;
|
|
365
|
-
for (;;) {
|
|
366
|
-
const { usable, leases, unreadable } = readPool(platform, list(), io);
|
|
367
|
-
const selection = selectPoolDevice({
|
|
368
|
-
candidates: usable,
|
|
369
|
-
leases,
|
|
370
|
-
held,
|
|
371
|
-
now: now()
|
|
372
|
-
});
|
|
373
|
-
if (selection.status === "selected") return selection;
|
|
374
|
-
if (selection.status === "held-disconnected") return {
|
|
375
|
-
status: "refused",
|
|
376
|
-
refusal: disconnectedHeldRefusal(selection.id, idLabel)
|
|
377
|
-
};
|
|
378
|
-
if (selection.status === "none") {
|
|
379
|
-
if (unreadable.length) return {
|
|
380
|
-
status: "refused",
|
|
381
|
-
refusal: poolBusyRefusal([], leases, unreadable, now(), waitSeconds)
|
|
382
|
-
};
|
|
383
|
-
const { message, remedy } = noCandidates();
|
|
384
|
-
return {
|
|
385
|
-
status: "refused",
|
|
386
|
-
refusal: {
|
|
387
|
-
code: "STIM_NO_DEVICE",
|
|
388
|
-
message,
|
|
389
|
-
remedy,
|
|
390
|
-
lease: null
|
|
391
|
-
}
|
|
392
|
-
};
|
|
393
|
-
}
|
|
394
|
-
const first = selection.holders[0];
|
|
395
|
-
if (noWait && first) {
|
|
396
|
-
const candidate = usable.find((entry) => entry.id === first.id);
|
|
397
|
-
if (candidate) return {
|
|
398
|
-
status: "selected",
|
|
399
|
-
candidate
|
|
400
|
-
};
|
|
401
|
-
}
|
|
402
|
-
if (now() >= until) return {
|
|
403
|
-
status: "refused",
|
|
404
|
-
refusal: poolBusyRefusal(selection.holders, leases, unreadable, now(), waitSeconds, selection.spare, idLabel)
|
|
405
|
-
};
|
|
406
|
-
if (lastLine === null || now() - lastLine >= 3e4) {
|
|
407
|
-
lastLine = now();
|
|
408
|
-
warn(`waiting for a free device; every connected one is leased: ${selection.holders.map((holder) => `${holder.id} (${holder.holder}, until ${leaseExpiryText(holder.expiresAt, now())})`).join("; ")}`);
|
|
409
|
-
}
|
|
410
|
-
await sleep(DEVICE_WAIT_POLL_MS);
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
//#endregion
|
|
414
|
-
export { lostLine as a, releaseLeaseOnSignal as c, waitForDevice as d, leaseExpiryText as i, runLease as l, DEBUG_VERIFY_STEP_MS as n, lostRefusal as o, acquireRunLease as r, parseDeviceWait as s, selectFromPool as t, waitFlagConflict as u };
|
|
294
|
+
export { acquireRunLease as a, lostRefusal as c, runLease as d, waitFlagConflict as f, DEVICE_WAIT_POLL_MS as i, parseDeviceWait as l, DEVICE_BUSY as n, leaseExpiryText as o, waitForDevice as p, DEVICE_WAIT_LINE_MS as r, lostLine as s, DEBUG_VERIFY_STEP_MS as t, releaseLeaseOnSignal as u };
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { R as deviceSlotKey } from "./config-D0DbpZtM.mjs";
|
|
2
|
+
import { a as parseLease, f as selectPoolDevice, n as fileLeaseIo, r as leaseIsExpired, t as deviceLeasePath } from "./device-lease-D-hLqx1b.mjs";
|
|
3
|
+
import { i as DEVICE_WAIT_POLL_MS, n as DEVICE_BUSY, o as leaseExpiryText } from "./device-lease-run-CqjShqWW.mjs";
|
|
4
|
+
//#region src/engine/device-pool.ts
|
|
5
|
+
function heldPoolId(root, platform, now, io = fileLeaseIo, slot = "default") {
|
|
6
|
+
const record = io.readHolder(root)[deviceSlotKey(platform, slot)];
|
|
7
|
+
if (!record) return null;
|
|
8
|
+
const lease = parseLease(io.readLease(deviceLeasePath(platform, record.id)));
|
|
9
|
+
if (!lease || lease.token !== record.token || leaseIsExpired(lease, now)) return null;
|
|
10
|
+
return record.id;
|
|
11
|
+
}
|
|
12
|
+
function readPool(platform, candidates, io) {
|
|
13
|
+
const reading = {
|
|
14
|
+
usable: [],
|
|
15
|
+
leases: [],
|
|
16
|
+
unreadable: []
|
|
17
|
+
};
|
|
18
|
+
for (const candidate of candidates) {
|
|
19
|
+
const path = deviceLeasePath(platform, candidate.id);
|
|
20
|
+
const raw = io.readLease(path);
|
|
21
|
+
const lease = parseLease(raw);
|
|
22
|
+
if (raw !== null && !lease) {
|
|
23
|
+
reading.unreadable.push(path);
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
reading.usable.push(candidate);
|
|
27
|
+
if (lease) reading.leases.push(lease);
|
|
28
|
+
}
|
|
29
|
+
return reading;
|
|
30
|
+
}
|
|
31
|
+
function disconnectedHeldRefusal(id, idLabel) {
|
|
32
|
+
return {
|
|
33
|
+
code: "STIM_NO_DEVICE",
|
|
34
|
+
message: `This workspace leases ${id}, and it is not connected.`,
|
|
35
|
+
remedy: `Run \`stim device unlock\` to give it up, then run this command again. Naming another \`--device <${idLabel}>\` refuses the same way while this lease is held.`,
|
|
36
|
+
lease: null
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
const NO_LEASE_FACTS = {
|
|
40
|
+
platform: null,
|
|
41
|
+
id: null,
|
|
42
|
+
deviceName: null,
|
|
43
|
+
holder: null,
|
|
44
|
+
expiresAt: null
|
|
45
|
+
};
|
|
46
|
+
function unreadableSentence(unreadable) {
|
|
47
|
+
return unreadable.length ? ` Unreadable lease file, so no run may take that device around it: ${unreadable.join("; ")}.` : "";
|
|
48
|
+
}
|
|
49
|
+
function poolBusyRefusal(holders, leases, unreadable, now, waitSeconds, spare = [], idLabel = "id") {
|
|
50
|
+
const named = holders.map((holder) => `${holder.id} (${holder.holder}, until ${leaseExpiryText(holder.expiresAt, now)})`);
|
|
51
|
+
const first = holders[0];
|
|
52
|
+
const lease = first ? leases.find((entry) => entry.id === first.id) : void 0;
|
|
53
|
+
if (!first) return {
|
|
54
|
+
code: DEVICE_BUSY,
|
|
55
|
+
message: `Every connected device has an unreadable lease file, so Stim cannot tell who holds them: ${unreadable.join("; ")}.`,
|
|
56
|
+
remedy: "Read each file and, once you know no run depends on it, delete it. `stim gc` reports them on every run.",
|
|
57
|
+
lease: NO_LEASE_FACTS
|
|
58
|
+
};
|
|
59
|
+
return {
|
|
60
|
+
code: DEVICE_BUSY,
|
|
61
|
+
message: `Every connected device is leased by another workspace${waitSeconds > 0 ? `, and this run waited ${waitSeconds}s for one` : ""}: ${named.join("; ")}.${unreadableSentence(unreadable)}`,
|
|
62
|
+
remedy: (spare.length ? `Also connected and free: ${spare.join(", ")}. Name one with \`--device <${idLabel}>\` to use it now. ` : "") + "Wait longer with `--wait <seconds>`, connect another device, or pass `--no-wait` to install anyway -- which takes no lease and, when both workspaces build the same app id, terminates the holder's running app.",
|
|
63
|
+
lease: {
|
|
64
|
+
platform: lease?.platform ?? null,
|
|
65
|
+
id: first.id,
|
|
66
|
+
deviceName: lease?.deviceName ?? null,
|
|
67
|
+
holder: first.holder,
|
|
68
|
+
expiresAt: first.expiresAt
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
async function selectFromPool({ root, platform, slot = "default", idLabel, list, noCandidates, waitSeconds, deadline, noWait = false, now = Date.now, sleep = (ms) => new Promise((r) => setTimeout(r, ms)), warn = () => {}, io = fileLeaseIo }) {
|
|
73
|
+
const held = heldPoolId(root, platform, now(), io, slot);
|
|
74
|
+
const until = deadline ?? now() + waitSeconds * 1e3;
|
|
75
|
+
let lastLine = null;
|
|
76
|
+
for (;;) {
|
|
77
|
+
const { usable, leases, unreadable } = readPool(platform, list(), io);
|
|
78
|
+
const selection = selectPoolDevice({
|
|
79
|
+
candidates: usable,
|
|
80
|
+
leases,
|
|
81
|
+
held,
|
|
82
|
+
now: now()
|
|
83
|
+
});
|
|
84
|
+
if (selection.status === "selected") return selection;
|
|
85
|
+
if (selection.status === "held-disconnected") return {
|
|
86
|
+
status: "refused",
|
|
87
|
+
refusal: disconnectedHeldRefusal(selection.id, idLabel)
|
|
88
|
+
};
|
|
89
|
+
if (selection.status === "none") {
|
|
90
|
+
if (unreadable.length) return {
|
|
91
|
+
status: "refused",
|
|
92
|
+
refusal: poolBusyRefusal([], leases, unreadable, now(), waitSeconds)
|
|
93
|
+
};
|
|
94
|
+
const { message, remedy } = noCandidates();
|
|
95
|
+
return {
|
|
96
|
+
status: "refused",
|
|
97
|
+
refusal: {
|
|
98
|
+
code: "STIM_NO_DEVICE",
|
|
99
|
+
message,
|
|
100
|
+
remedy,
|
|
101
|
+
lease: null
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
const first = selection.holders[0];
|
|
106
|
+
if (noWait && first) {
|
|
107
|
+
const candidate = usable.find((entry) => entry.id === first.id);
|
|
108
|
+
if (candidate) return {
|
|
109
|
+
status: "selected",
|
|
110
|
+
candidate
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
if (now() >= until) return {
|
|
114
|
+
status: "refused",
|
|
115
|
+
refusal: poolBusyRefusal(selection.holders, leases, unreadable, now(), waitSeconds, selection.spare, idLabel)
|
|
116
|
+
};
|
|
117
|
+
if (lastLine === null || now() - lastLine >= 3e4) {
|
|
118
|
+
lastLine = now();
|
|
119
|
+
warn(`waiting for a free device; every connected one is leased: ${selection.holders.map((holder) => `${holder.id} (${holder.holder}, until ${leaseExpiryText(holder.expiresAt, now())})`).join("; ")}`);
|
|
120
|
+
}
|
|
121
|
+
await sleep(DEVICE_WAIT_POLL_MS);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
//#endregion
|
|
125
|
+
export { selectFromPool as t };
|
|
@@ -1,20 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { a as
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import { c as readMetroRecords, o as isBundleProof } from "./launch-verify-
|
|
14
|
-
import { t as compareStimVersions } from "./stim-installations-
|
|
1
|
+
import { t as getExecutor } from "./exec-CaErVv7t.mjs";
|
|
2
|
+
import { d as getConfigDir } from "./config-D0DbpZtM.mjs";
|
|
3
|
+
import { a as formatElapsed } from "./command-output-DnJMdEY-.mjs";
|
|
4
|
+
import { K as planMetroReach } from "./settings-oTSkkNrM.mjs";
|
|
5
|
+
import { l as ownedDeviceLabel } from "./project-Du91Ilvc.mjs";
|
|
6
|
+
import { o as pidExists } from "./metro-BeK1ZPz1.mjs";
|
|
7
|
+
import { C as workspaceStateFile, v as workspaceDir, w as withDirLock, x as workspaceLogsDir$1, y as workspaceId } from "./workspace-state-B8-aJ1_5.mjs";
|
|
8
|
+
import { a as clearRemoteSession, c as readMetroTunnel, u as readRemoteSession } from "./state-DcW6vOd_.mjs";
|
|
9
|
+
import { T as sanitizeDeviceLabel } from "./ios-BaInn3sW.mjs";
|
|
10
|
+
import { t as withWorkspaceProcessLock } from "./workspace-process-lock-BL7Q0HCO.mjs";
|
|
11
|
+
import { u as probePublicHttp } from "./ownership-hCkPzGmr.mjs";
|
|
12
|
+
import { i as INSTALL_ERROR, o as LAUNCH_ERROR, u as devClientDeepLink } from "./app-install-CKWdbXIX.mjs";
|
|
13
|
+
import { c as readMetroRecords, o as isBundleProof } from "./launch-verify-2DBRizjo.mjs";
|
|
14
|
+
import { t as compareStimVersions } from "./stim-installations-QMEI32v1.mjs";
|
|
15
|
+
import { c as nativeRunWaitNotice } from "./native-run-NaBrRFtQ.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
|
|
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,16 +1279,21 @@ async function ensureMetroReachable({ ctx, metroPort, isExpo, tunnelMode = "auto
|
|
|
1378
1279
|
gate = true;
|
|
1379
1280
|
} else {
|
|
1380
1281
|
const port = Number(metroPort);
|
|
1381
|
-
|
|
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 =
|
|
1293
|
+
gate = recorded.provider !== "tailscale";
|
|
1388
1294
|
}
|
|
1389
1295
|
if (gate && resolvedUrl) {
|
|
1390
|
-
const logsDir = workspaceLogsDir(root);
|
|
1296
|
+
const logsDir = workspaceLogsDir$1(root);
|
|
1391
1297
|
const result = await gateOrigin({
|
|
1392
1298
|
origin: resolvedUrl,
|
|
1393
1299
|
metroPort,
|
|
@@ -1415,4 +1321,4 @@ function bundleEntryPoint(root, isExpo) {
|
|
|
1415
1321
|
return local ? normalized : `node_modules/${normalized}`;
|
|
1416
1322
|
}
|
|
1417
1323
|
//#endregion
|
|
1418
|
-
export {
|
|
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 };
|