stim 1.9.0 → 1.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/dist/{android-_y4Ohi5z.mjs → android-787HhP5w.mjs} +119 -29
  2. package/dist/{android-BPGizezh.mjs → android-Bu4mbDzc.mjs} +168 -95
  3. package/dist/{android-DEgo4oCo.mjs → android-DPov-fv2.mjs} +2 -2
  4. package/dist/{android-cas-eiExNLf7.mjs → android-cas-CITmIeWY.mjs} +9 -8
  5. package/dist/android-cas-compiler.mjs +1 -1
  6. package/dist/{app-install-Uf3a83Tv.mjs → app-install-Cfmqr3TQ.mjs} +2 -2
  7. package/dist/budget-nDCEwtJv.mjs +620 -0
  8. package/dist/build-progress-BXx-ZBe4.mjs +512 -0
  9. package/dist/{cache-manifest-Dg7OnMrp.mjs → cache-manifest-Pubfp8wV.mjs} +1 -1
  10. package/dist/cache-manifest.mjs +1 -1
  11. package/dist/cli.mjs +16 -16
  12. package/dist/collector-run.d.mts +5 -1
  13. package/dist/collector-run.mjs +7 -7
  14. package/dist/{command-output-gByVmIjy.mjs → command-output-D5z2_bcF.mjs} +1 -1
  15. package/dist/{config-Cp9lM3wX.mjs → config-BEq0Qw_a.mjs} +14 -131
  16. package/dist/{dependency-state-Bq5DcLGn.mjs → dependency-state-r9UesA9a.mjs} +2 -2
  17. package/dist/{deps-Cip5vUm1.mjs → deps-DP4eWqea.mjs} +3 -3
  18. package/dist/{detached-entry-KEu0_AXL.mjs → detached-entry-DJHoca49.mjs} +1 -1
  19. package/dist/{dev-client-HyqISM0h.mjs → dev-client-DExzPiOa.mjs} +137 -250
  20. package/dist/{device-D4U-xLty.mjs → device-Czh5kO8A.mjs} +7 -7
  21. package/dist/{build-lock-DPXPLgTc.mjs → device-lease-CbEaIk7b.mjs} +370 -12
  22. package/dist/{device-pool-CnTAz8X1.mjs → device-pool-BBWLAr55.mjs} +4 -4
  23. package/dist/{device-remote-AQ5rQPqr.mjs → device-remote-powUHdbK.mjs} +547 -468
  24. package/dist/{doctor-B_Ei3bZu.mjs → doctor-CmqK1LcQ.mjs} +58 -22
  25. package/dist/{error-diagnostics-Vfz83GCF.mjs → error-diagnostics-xxWKNO3-.mjs} +11 -12
  26. package/dist/{exec-jIAlgilk.mjs → exec-DK8KaVHK.mjs} +3 -2
  27. package/dist/{gc-Ckw8RQHi.mjs → gc-zCDpmdHU.mjs} +500 -1114
  28. package/dist/{guide-BiAbhs1J.mjs → guide-DwmaD_Wa.mjs} +882 -528
  29. package/dist/guide-status-CWvti2Lg.mjs +125 -0
  30. package/dist/idle--jn7pBNe.mjs +157 -0
  31. package/dist/in-use-CNIWU-r-.mjs +245 -0
  32. package/dist/{ios-BL2NFckr.mjs → ios-COGcGEcN.mjs} +39 -65
  33. package/dist/{ios-BzUJWk1K.mjs → ios-CbdqTqOc.mjs} +130 -89
  34. package/dist/{ios-device-DPsSm0v_.mjs → ios-device-BiOsSVzd.mjs} +1 -1
  35. package/dist/{ios-device-D972i9wf.mjs → ios-device-DNjPXBnb.mjs} +3 -3
  36. package/dist/{launch-verify-Q_OtW1Rf.mjs → launch-verify-BhgkYAws.mjs} +5 -5
  37. package/dist/{logs-CI9NQSK7.mjs → logs-MARIy6m9.mjs} +9 -9
  38. package/dist/{metro-DrLHaMn2.mjs → metro-BWLOJ_Wr.mjs} +116 -10
  39. package/dist/{named-ports-CmLQSIRj.mjs → named-ports-DiTfSz6p.mjs} +4 -4
  40. package/dist/{ndjson-9uFNRwfe.mjs → ndjson-CDY9IkVC.mjs} +3 -56
  41. package/dist/{ndjson-DvHSsNU9.d.mts → ndjson-Du3j0AQ9.d.mts} +1 -0
  42. package/dist/ownership-BfPNRf3-.mjs +1180 -0
  43. package/dist/{ownership-D15Op8vN.mjs → ownership-D2xbLVoz.mjs} +2 -3
  44. package/dist/{ports-BNKoWVWZ.mjs → ports-CLSSFOuz.mjs} +3 -3
  45. package/dist/{project-DS112DYB.mjs → project-BXZzOABt.mjs} +2 -2
  46. package/dist/{reload-ZNJUo1pl.mjs → reload-CymyF6Kk.mjs} +9 -10
  47. package/dist/{server-bare-DWkkcIxV.mjs → server-bare-DVbb7kXH.mjs} +1 -1
  48. package/dist/{server-expo-B_lctBR3.mjs → server-expo-BvOCeakp.mjs} +4 -4
  49. package/dist/server-expo-DBVTdult.mjs +2 -0
  50. package/dist/{settings-D030k0t4.mjs → settings-DOugPlPz.mjs} +16 -428
  51. package/dist/{settings-BKNh5rl4.mjs → settings-EWYNwe72.mjs} +16 -14
  52. package/dist/settings.schema.json +75 -0
  53. package/dist/{simslim-B_LsGLuJ.mjs → simslim-DZUxzsjq.mjs} +182 -5
  54. package/dist/{slot-launch-DU9A-gYw.mjs → slot-launch-Dk4oTREM.mjs} +4 -4
  55. package/dist/{spawn-claims-Dn3C_N6w.mjs → spawn-claims-CUleC2cM.mjs} +1 -1
  56. package/dist/{start-DYkkKF0s.mjs → start-RbmFPeZ7.mjs} +147 -98
  57. package/dist/start-qWapeB4W.mjs +2 -0
  58. package/dist/{state-NcJzMEBF.mjs → state-WGdIbi_d.mjs} +2 -2
  59. package/dist/{stats-DG9e66Rz.mjs → stats-BAgJaLA4.mjs} +1 -1
  60. package/dist/{stats-B63-JVAY.mjs → stats-DBYJ5bxL.mjs} +4 -4
  61. package/dist/status-Dr4fRkym.mjs +216 -0
  62. package/dist/{status-S6ehL4Ui.mjs → status-Zp8kvexe.mjs} +63 -231
  63. package/dist/{guide-status-BAMVU0ZA.mjs → stim-installations-CINXy_su.mjs} +3 -124
  64. package/dist/{stop-T1R-cZRz.mjs → stop-DI4BG4Os.mjs} +2 -2
  65. package/dist/{stop-BO28HGqF.mjs → stop-DcEv5sk-.mjs} +22 -18
  66. package/dist/supervisor-run.d.mts +11 -2
  67. package/dist/supervisor-run.mjs +185 -17
  68. package/dist/{workspace-process-lock-DWaiRD0e.mjs → workspace-process-lock-BXA96myD.mjs} +2 -2
  69. package/dist/workspaces-wrH6dXfu.mjs +1003 -0
  70. package/dist/{worktree-LGB-UwgG.mjs → worktree-Bd4dRvx7.mjs} +40 -122
  71. package/dist/{worktree-R_cUlE7s.mjs → worktree-BlizKDpZ.mjs} +143 -18
  72. package/dist/{worktree-DKhSF0UX.mjs → worktree-DEG2o9F8.mjs} +1 -1
  73. package/dist/{xcode-BZqyxh3e.mjs → xcode-B2tBkf0m.mjs} +9 -158
  74. package/package.json +6 -5
  75. package/dist/build-progress-CVqkfPC6.mjs +0 -188
  76. package/dist/device-lease-Crs-HkJY.mjs +0 -366
  77. package/dist/logs-query-Cqma3WIu.mjs +0 -298
  78. package/dist/ownership-DCQPuWEc.mjs +0 -489
  79. package/dist/public-http-probe-CyMDrQ3c.mjs +0 -61
  80. package/dist/server-expo-CeILEu86.mjs +0 -2
  81. package/dist/sim-pool-KaIsp2Ft.mjs +0 -723
  82. package/dist/spawn-entry-nCE6m770.mjs +0 -14
  83. package/dist/workspace-state-DF2gCFhx.mjs +0 -169
@@ -1,15 +1,15 @@
1
- import { k as ownership_claim_exports, u as getConfigDir } from "./config-Cp9lM3wX.mjs";
2
- import { t as getExecutor } from "./exec-jIAlgilk.mjs";
3
- import { r as register } from "./cache-manifest-Dg7OnMrp.mjs";
4
- import { d as sharedBuildCache } from "./workspace-state-DF2gCFhx.mjs";
5
- import { a as formatElapsed, i as formatDuration, m as shortHash } from "./command-output-gByVmIjy.mjs";
6
- import { r as stopDeclaringSpawnsOn, t as declareSpawnsOn } from "./spawn-claims-Dn3C_N6w.mjs";
7
- import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
1
+ import { B as parseDeviceSlotKey, R as deviceSlotKey, W as validateDeviceSlot, k as ownership_claim_exports, u as getConfigDir } from "./config-BEq0Qw_a.mjs";
2
+ import { t as getExecutor } from "./exec-DK8KaVHK.mjs";
3
+ import { r as register } from "./cache-manifest-Pubfp8wV.mjs";
4
+ import { P as withDirLock, S as sharedBuildCache, _ as withWorkspaceStateLock, m as readWorkspaceState, p as clearWorkspaceStateKeys, y as writeWorkspaceState } from "./metro-BWLOJ_Wr.mjs";
5
+ import { r as stopDeclaringSpawnsOn, t as declareSpawnsOn } from "./spawn-claims-CUleC2cM.mjs";
6
+ import { a as formatElapsed, i as formatDuration, m as shortHash } from "./command-output-D5z2_bcF.mjs";
7
+ import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
8
8
  import { existsSync as existsSync$1, mkdirSync as mkdirSync$1, readFileSync as readFileSync$1, rmSync as rmSync$1, writeFileSync as writeFileSync$1 } from "fs";
9
9
  import { join } from "path";
10
10
  import { buildCacheKey, resolveArtifact, storeArtifact } from "@stim-cli/core";
11
11
  import { join as join$1 } from "node:path";
12
- import { createHash } from "node:crypto";
12
+ import { createHash, randomBytes } from "node:crypto";
13
13
  import * as expoFingerprint from "@expo/fingerprint";
14
14
  import { buildUploadTimeoutMs } from "@stim-cli/cache";
15
15
  const ASSET_MANIFEST_FILE = "assets-manifest.json";
@@ -425,7 +425,7 @@ function fingerprintDiffRecord({ changed, previousHash, hash }) {
425
425
  }
426
426
  //#endregion
427
427
  //#region src/engine/build-lock.ts
428
- const LOCK_SUFFIX = ".lock";
428
+ const LOCK_SUFFIX$1 = ".lock";
429
429
  const WAIT_POLL_MS = 1e3;
430
430
  const WAIT_PROGRESS_MS = 3e4;
431
431
  const WAIT_CEILING_MS = 54e5;
@@ -439,7 +439,7 @@ function buildLocksDir() {
439
439
  return join$1(getConfigDir(), "build-locks");
440
440
  }
441
441
  function buildLockPath(platform, key) {
442
- return join$1(buildLocksDir(), `${segment(platform)}-${segment(key)}${LOCK_SUFFIX}`);
442
+ return join$1(buildLocksDir(), `${segment(platform)}-${segment(key)}${LOCK_SUFFIX$1}`);
443
443
  }
444
444
  function label(platform) {
445
445
  return `${platform} build`;
@@ -510,7 +510,7 @@ function listBuildLocks() {
510
510
  }
511
511
  const locks = [];
512
512
  for (const name of names) {
513
- if (!name.endsWith(LOCK_SUFFIX)) continue;
513
+ if (!name.endsWith(LOCK_SUFFIX$1)) continue;
514
514
  const path = join$1(dir, name);
515
515
  try {
516
516
  if (!statSync(path).isDirectory()) continue;
@@ -696,5 +696,363 @@ async function waitForSharedBuild({ platform, key, fingerprint, root, logFile, c
696
696
  }
697
697
  }
698
698
  }
699
+ const DEFAULT_LEASE_MS = 3e5;
700
+ const LEASE_SUFFIX = ".json";
701
+ const LOCK_SUFFIX = ".lock";
702
+ const DURATION = /^[1-9][0-9]*(s|m)$/;
703
+ function deviceLocksDir() {
704
+ return join$1(getConfigDir(), "device-locks");
705
+ }
706
+ function leaseStem(platform, id) {
707
+ return `${segment(platform)}-${segment(id)}`;
708
+ }
709
+ function deviceLeasePath(platform, id) {
710
+ return join$1(deviceLocksDir(), `${leaseStem(platform, id)}${LEASE_SUFFIX}`);
711
+ }
712
+ function deviceLeaseLockPath(platform, id) {
713
+ return join$1(deviceLocksDir(), `${leaseStem(platform, id)}${LOCK_SUFFIX}`);
714
+ }
715
+ const fileLeaseIo = {
716
+ withHolderLock: withWorkspaceStateLock,
717
+ now: () => Date.now(),
718
+ readLease(path) {
719
+ try {
720
+ return readFileSync(path, "utf-8");
721
+ } catch {
722
+ return null;
723
+ }
724
+ },
725
+ writeLease(path, body) {
726
+ mkdirSync(deviceLocksDir(), { recursive: true });
727
+ const tmp = `${path}.tmp-${process.pid}-${randomBytes(4).toString("hex")}`;
728
+ writeFileSync(tmp, body);
729
+ try {
730
+ renameSync(tmp, path);
731
+ } catch (error) {
732
+ rmSync(tmp, { force: true });
733
+ throw error;
734
+ }
735
+ },
736
+ removeLease(path) {
737
+ rmSync(path, { force: true });
738
+ },
739
+ listLeaseNames() {
740
+ try {
741
+ return readdirSync(deviceLocksDir());
742
+ } catch {
743
+ return [];
744
+ }
745
+ },
746
+ withLeaseLock(lockPath, fn) {
747
+ return withDirLock(lockPath, fn, { ensureParent: () => mkdirSync(deviceLocksDir(), { recursive: true }) });
748
+ },
749
+ readHolder(root) {
750
+ return parseWorkspaceLeases(readWorkspaceState(root)?.deviceLeases);
751
+ },
752
+ writeHolder(root, leases) {
753
+ if (Object.keys(leases).length === 0) clearWorkspaceStateKeys(root, ["deviceLeases"]);
754
+ else writeWorkspaceState(root, { deviceLeases: leases });
755
+ }
756
+ };
757
+ function text(value) {
758
+ return typeof value === "string" && value.trim() !== "" ? value : null;
759
+ }
760
+ function isPlatform(value) {
761
+ return value === "ios" || value === "android";
762
+ }
763
+ function parseLease(raw) {
764
+ if (typeof raw !== "string") return null;
765
+ let parsed;
766
+ try {
767
+ parsed = JSON.parse(raw);
768
+ } catch {
769
+ return null;
770
+ }
771
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
772
+ const entry = parsed;
773
+ if (entry.version !== 1 || !isPlatform(entry.platform)) return null;
774
+ const id = text(entry.id);
775
+ const holder = text(entry.holder);
776
+ const token = text(entry.token);
777
+ const expiresAt = text(entry.expiresAt);
778
+ if (!id || !holder || !token || !expiresAt || !Number.isFinite(Date.parse(expiresAt))) return null;
779
+ if (entry.slot !== void 0) {
780
+ if (typeof entry.slot !== "string") return null;
781
+ try {
782
+ validateDeviceSlot(entry.slot);
783
+ } catch {
784
+ return null;
785
+ }
786
+ }
787
+ return {
788
+ version: 1,
789
+ ...typeof entry.slot === "string" ? { slot: entry.slot } : {},
790
+ platform: entry.platform,
791
+ id,
792
+ deviceName: text(entry.deviceName),
793
+ holder,
794
+ token,
795
+ grantedAt: text(entry.grantedAt),
796
+ expiresAt
797
+ };
798
+ }
799
+ function parseWorkspaceLeases(value) {
800
+ if (!value || typeof value !== "object" || Array.isArray(value)) return {};
801
+ const leases = {};
802
+ for (const [platform, entry] of Object.entries(value)) {
803
+ if (!parseDeviceSlotKey(platform) || !entry || typeof entry !== "object") continue;
804
+ const record = entry;
805
+ const id = text(record.id);
806
+ const token = text(record.token);
807
+ if (!id || !token) continue;
808
+ leases[platform] = {
809
+ id,
810
+ token,
811
+ kind: record.kind === "run" ? "run" : "declared"
812
+ };
813
+ }
814
+ return leases;
815
+ }
816
+ function leaseIsExpired(lease, now) {
817
+ return Date.parse(lease.expiresAt) <= now;
818
+ }
819
+ function parseLeaseDuration(value) {
820
+ const raw = String(value ?? "").trim();
821
+ const match = DURATION.exec(raw);
822
+ if (!match) return { error: `--for must be a whole number of seconds or minutes, such as 90s or 5m (got "${raw}").` };
823
+ const ms = Number(raw.slice(0, -1)) * (match[1] === "s" ? 1e3 : 6e4);
824
+ if (ms < 1e4 || ms > 18e5) return { error: `--for must be between 10s and 30m (got "${raw}").` };
825
+ return { ms };
826
+ }
827
+ function listLeaseFiles(io = fileLeaseIo) {
828
+ const dir = deviceLocksDir();
829
+ const entries = [];
830
+ for (const name of io.listLeaseNames().toSorted()) {
831
+ if (!name.endsWith(LEASE_SUFFIX)) continue;
832
+ const path = join$1(dir, name);
833
+ const lease = parseLease(io.readLease(path));
834
+ const stem = name.slice(0, -5);
835
+ const cut = stem.indexOf("-");
836
+ entries.push({
837
+ path,
838
+ name,
839
+ platform: lease?.platform ?? (cut > 0 ? stem.slice(0, cut) : stem),
840
+ id: lease?.id ?? (cut > 0 ? stem.slice(cut + 1) : null),
841
+ lease
842
+ });
843
+ }
844
+ return entries;
845
+ }
846
+ function writeLease(lease, io) {
847
+ io.writeLease(deviceLeasePath(lease.platform, lease.id), `${JSON.stringify(lease, null, 2)}\n`);
848
+ return lease;
849
+ }
850
+ function takeLease({ root, platform, slot = "default", id, deviceName = null, kind, durationMs = DEFAULT_LEASE_MS }, io = fileLeaseIo) {
851
+ return withHolderLock(io, root, () => {
852
+ const key = deviceSlotKey(platform, slot);
853
+ const previous = io.readHolder(root)[key];
854
+ if (previous && previous.id !== id) releaseHeld(root, platform, previous, io, slot);
855
+ for (const entry of listLeaseFiles(io)) {
856
+ const other = entry.lease;
857
+ if (!other || other.platform !== platform || other.holder !== root || (other.slot ?? "default") !== slot || other.id === id) continue;
858
+ releaseByRoot(root, platform, other.id, io);
859
+ }
860
+ const declared = previous?.kind === "declared" && previous.id === id;
861
+ const path = deviceLeasePath(platform, id);
862
+ const result = io.withLeaseLock(deviceLeaseLockPath(platform, id), () => {
863
+ const now = io.now();
864
+ const raw = io.readLease(path);
865
+ const current = parseLease(raw);
866
+ if (raw !== null && !current) return {
867
+ status: "unreadable",
868
+ path
869
+ };
870
+ const asked = now + durationMs;
871
+ if (current && !leaseIsExpired(current, now)) {
872
+ if (current.holder !== root || (current.slot ?? "default") !== slot) return {
873
+ status: "held",
874
+ lease: current
875
+ };
876
+ const floor = declared && kind === "run" ? Math.max(asked, Date.parse(current.expiresAt)) : asked;
877
+ return {
878
+ status: "set",
879
+ lease: writeLease({
880
+ ...current,
881
+ deviceName: deviceName ?? current.deviceName,
882
+ expiresAt: new Date(floor).toISOString()
883
+ }, io)
884
+ };
885
+ }
886
+ const expiresAt = new Date(asked).toISOString();
887
+ return {
888
+ status: "taken",
889
+ lease: writeLease({
890
+ version: 1,
891
+ ...slot === "default" ? {} : { slot },
892
+ platform,
893
+ id,
894
+ deviceName,
895
+ holder: root,
896
+ token: randomBytes(12).toString("hex"),
897
+ grantedAt: new Date(now).toISOString(),
898
+ expiresAt
899
+ }, io)
900
+ };
901
+ });
902
+ if (result.status === "taken" || result.status === "set") {
903
+ const recorded = result.status === "set" && declared ? "declared" : kind;
904
+ io.writeHolder(root, {
905
+ ...io.readHolder(root),
906
+ [key]: {
907
+ id,
908
+ token: result.lease.token,
909
+ kind: recorded
910
+ }
911
+ });
912
+ }
913
+ return result;
914
+ });
915
+ }
916
+ function raiseLease({ root, platform, slot = "default", minMs }, io = fileLeaseIo) {
917
+ const record = io.readHolder(root)[deviceSlotKey(platform, slot)];
918
+ if (!record) return { status: "none" };
919
+ const path = deviceLeasePath(platform, record.id);
920
+ return io.withLeaseLock(deviceLeaseLockPath(platform, record.id), () => {
921
+ const current = parseLease(io.readLease(path));
922
+ if (!current || current.token !== record.token) return {
923
+ status: "lost",
924
+ lease: current
925
+ };
926
+ const floor = io.now() + minMs;
927
+ if (Date.parse(current.expiresAt) >= floor) return {
928
+ status: "raised",
929
+ lease: current
930
+ };
931
+ return {
932
+ status: "raised",
933
+ lease: writeLease({
934
+ ...current,
935
+ expiresAt: new Date(floor).toISOString()
936
+ }, io)
937
+ };
938
+ });
939
+ }
940
+ function summarize(lease) {
941
+ return {
942
+ platform: lease.platform,
943
+ id: lease.id,
944
+ deviceName: lease.deviceName,
945
+ expiresAt: lease.expiresAt
946
+ };
947
+ }
948
+ function releaseHeld(root, platform, record, io, slot = "default") {
949
+ const key = deviceSlotKey(platform, slot);
950
+ const path = deviceLeasePath(platform, record.id);
951
+ const released = io.withLeaseLock(deviceLeaseLockPath(platform, record.id), () => {
952
+ const current = parseLease(io.readLease(path));
953
+ if (!current || current.token !== record.token) return null;
954
+ io.removeLease(path);
955
+ return current;
956
+ });
957
+ const held = io.readHolder(root);
958
+ if (held[key]?.token === record.token) {
959
+ delete held[key];
960
+ io.writeHolder(root, held);
961
+ }
962
+ return released;
963
+ }
964
+ function releaseByRoot(root, platform, id, io) {
965
+ const path = deviceLeasePath(platform, id);
966
+ return io.withLeaseLock(deviceLeaseLockPath(platform, id), () => {
967
+ const current = parseLease(io.readLease(path));
968
+ if (!current || current.holder !== root) return null;
969
+ io.removeLease(path);
970
+ return current;
971
+ });
972
+ }
973
+ function releaseRunLease({ root, platform, slot = "default" }, io = fileLeaseIo) {
974
+ return withHolderLock(io, root, () => {
975
+ const record = io.readHolder(root)[deviceSlotKey(platform, slot)];
976
+ if (!record || record.kind !== "run") return null;
977
+ const released = releaseHeld(root, platform, record, io, slot);
978
+ return released ? summarize(released) : null;
979
+ });
980
+ }
981
+ function releaseWorkspaceLeases(root, { platform = null, slot = null } = {}, io = fileLeaseIo) {
982
+ return withHolderLock(io, root, () => {
983
+ const released = [];
984
+ for (const [name, record] of Object.entries(io.readHolder(root))) {
985
+ const parsed = parseDeviceSlotKey(name);
986
+ if (!parsed || platform && parsed.platform !== platform || slot !== null && parsed.slot !== slot) continue;
987
+ const lease = releaseHeld(root, parsed.platform, record, io, parsed.slot);
988
+ if (lease) released.push(summarize(lease));
989
+ }
990
+ for (const entry of listLeaseFiles(io)) {
991
+ const lease = entry.lease;
992
+ if (!lease || lease.holder !== root) continue;
993
+ if (platform && lease.platform !== platform) continue;
994
+ if (slot !== null && (lease.slot ?? "default") !== slot) continue;
995
+ const byRoot = releaseByRoot(root, lease.platform, lease.id, io);
996
+ if (byRoot) released.push(summarize(byRoot));
997
+ }
998
+ return released;
999
+ });
1000
+ }
1001
+ function removeExpiredLease(entry, io = fileLeaseIo) {
1002
+ const lease = entry.lease;
1003
+ if (!lease) return false;
1004
+ return io.withLeaseLock(deviceLeaseLockPath(lease.platform, lease.id), () => {
1005
+ const current = parseLease(io.readLease(entry.path));
1006
+ if (!current || !leaseIsExpired(current, io.now())) return false;
1007
+ io.removeLease(entry.path);
1008
+ return true;
1009
+ });
1010
+ }
1011
+ function selectPoolDevice({ candidates, leases, held = null, now }) {
1012
+ const ordered = candidates.toSorted((a, b) => {
1013
+ const left = a.id.toLowerCase();
1014
+ const right = b.id.toLowerCase();
1015
+ return left < right ? -1 : left > right ? 1 : 0;
1016
+ });
1017
+ if (held) {
1018
+ const mine = ordered.find((candidate) => candidate.id === held);
1019
+ return mine ? {
1020
+ status: "selected",
1021
+ candidate: mine
1022
+ } : {
1023
+ status: "held-disconnected",
1024
+ id: held
1025
+ };
1026
+ }
1027
+ const preferred = ordered.filter((candidate) => !candidate.fallback);
1028
+ const pool = preferred.length > 0 ? preferred : ordered;
1029
+ if (pool.length === 0) return { status: "none" };
1030
+ const byId = new Map(leases.map((lease) => [lease.id, lease]));
1031
+ const free = (candidate) => {
1032
+ const lease = byId.get(candidate.id);
1033
+ return !lease || leaseIsExpired(lease, now);
1034
+ };
1035
+ const holders = [];
1036
+ for (const candidate of pool) {
1037
+ if (free(candidate)) return {
1038
+ status: "selected",
1039
+ candidate
1040
+ };
1041
+ const lease = byId.get(candidate.id);
1042
+ holders.push({
1043
+ id: lease.id,
1044
+ holder: lease.holder,
1045
+ expiresAt: lease.expiresAt
1046
+ });
1047
+ }
1048
+ return {
1049
+ status: "busy",
1050
+ holders,
1051
+ spare: ordered.filter((candidate) => candidate.fallback && free(candidate)).map((candidate) => candidate.id)
1052
+ };
1053
+ }
1054
+ function withHolderLock(io, root, fn) {
1055
+ return io.withHolderLock ? io.withHolderLock(root, fn) : fn();
1056
+ }
699
1057
  //#endregion
700
- export { assetDiffReason as C, readAssetManifest as E, untrackedNativeFiles as S, compareAssetManifests as T, refingerprintAfterMutation as _, waitForBuild as a, storedAssetManifest as b, describeFingerprintMiss as c, fingerprintDiffRecord as d, fingerprintDiffSuffix as f, providerUploadOutcome as g, providerDownloadPath as h, segment as i, diffFingerprintSources as l, prepareProviderDownloadDir as m, listBuildLocks as n, waitForSharedBuild as o, fingerprintProject as p, releaseBuildLock as r, buildCacheKey as s, acquireBuildLock as t, filesystemBuildCapability as u, resolveBuild as v, captureAssetManifest as w, untrackedMissLine as x, storeBuild as y };
1058
+ export { resolveBuild as A, fingerprintDiffRecord as C, providerDownloadPath as D, prepareProviderDownloadDir as E, assetDiffReason as F, captureAssetManifest as I, compareAssetManifests as L, storedAssetManifest as M, untrackedMissLine as N, providerUploadOutcome as O, untrackedNativeFiles as P, readAssetManifest as R, filesystemBuildCapability as S, fingerprintProject as T, waitForBuild as _, parseLease as a, describeFingerprintMiss as b, raiseLease as c, removeExpiredLease as d, selectPoolDevice as f, releaseBuildLock as g, listBuildLocks as h, listLeaseFiles as i, storeBuild as j, refingerprintAfterMutation as k, releaseRunLease as l, acquireBuildLock as m, fileLeaseIo as n, parseLeaseDuration as o, takeLease as p, leaseIsExpired as r, parseWorkspaceLeases as s, deviceLeasePath as t, releaseWorkspaceLeases as u, waitForSharedBuild as v, fingerprintDiffSuffix as w, diffFingerprintSources as x, buildCacheKey as y };
@@ -1,7 +1,7 @@
1
- import { H as deviceSlotKey } from "./config-Cp9lM3wX.mjs";
2
- import { a as formatElapsed, r as clockTime } from "./command-output-gByVmIjy.mjs";
3
- import { a as VERIFY_TIMEOUT_MS, p as APP_READINESS_TIMEOUT_MS } from "./launch-verify-Q_OtW1Rf.mjs";
4
- import { a as parseLease, c as releaseRunLease, d as selectPoolDevice, f as takeLease, n as fileLeaseIo, r as leaseIsExpired, s as raiseLease, t as deviceLeasePath } from "./device-lease-Crs-HkJY.mjs";
1
+ import { R as deviceSlotKey } from "./config-BEq0Qw_a.mjs";
2
+ import { a as formatElapsed, r as clockTime } from "./command-output-D5z2_bcF.mjs";
3
+ import { a as parseLease, c as raiseLease, f as selectPoolDevice, l as releaseRunLease, n as fileLeaseIo, p as takeLease, r as leaseIsExpired, t as deviceLeasePath } from "./device-lease-CbEaIk7b.mjs";
4
+ import { a as VERIFY_TIMEOUT_MS, p as APP_READINESS_TIMEOUT_MS } from "./launch-verify-BhgkYAws.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;