stim 1.15.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.
Files changed (117) hide show
  1. package/dist/{activity-B2-kb7k0.mjs → activity-BxwdD81S.mjs} +29 -16
  2. package/dist/agent-device-usage-output-Dpih1Tfa.mjs +411 -0
  3. package/dist/{android-Db2eEMaf.mjs → android-B_NumVyQ.mjs} +21 -20
  4. package/dist/{android-Cu_L4RpL.mjs → android-Ift4kVvt.mjs} +56 -47
  5. package/dist/{android-cas-PnKfAdB2.mjs → android-cas-8U4DPqeR.mjs} +1 -1
  6. package/dist/{android-CYmq0jNi.mjs → android-r2rBL8kV.mjs} +1 -1
  7. package/dist/{app-install-CAAplXAt.mjs → app-install-CKWdbXIX.mjs} +48 -5
  8. package/dist/attempt-Bk2ouQVL.mjs +39 -0
  9. package/dist/{budget-CMiqghig.mjs → budget-DjrURfQn.mjs} +356 -366
  10. package/dist/{build-plan-aeGMFX7e.mjs → build-plan-DjeSb2Xl.mjs} +19 -20
  11. package/dist/{build-slots-Df1ApSuH.mjs → build-slots-rUk77xqh.mjs} +7 -2
  12. package/dist/{cli-0cX5s4NM.mjs → cli-BkYZ_6hy.mjs} +23 -17
  13. package/dist/cli.mjs +1 -1
  14. package/dist/{client-C2IS36Qf.mjs → client-BTY686R6.mjs} +57 -22
  15. package/dist/collector-run.mjs +5 -192
  16. package/dist/{command-output-CliHHn7a.mjs → command-output-DnJMdEY-.mjs} +1 -1
  17. package/dist/{created-devices-BwVb3liH.mjs → created-devices-D5owjN4C.mjs} +1 -1
  18. package/dist/{dependency-state-BmsjKZ2H.mjs → dependency-state-B6cMXZab.mjs} +1 -1
  19. package/dist/{deps-BFlKbOIp.mjs → deps-urbmfXig.mjs} +2 -2
  20. package/dist/{dev-client-CjzLoNs0.mjs → dev-client-CroAIjjj.mjs} +3 -40
  21. package/dist/{device-BZ1IyS5A.mjs → device-DZQjF7H5.mjs} +7 -6
  22. package/dist/{device-capacity-DMuKC7nx.mjs → device-capacity-BHwxNBlu.mjs} +2 -2
  23. package/dist/device-host-worker.mjs +223 -28
  24. package/dist/{device-ios-DMQBUPus.mjs → device-ios-BadmeL2V.mjs} +7 -7
  25. package/dist/{device-lease-W5Pi1q38.mjs → device-lease-D-hLqx1b.mjs} +2 -2
  26. package/dist/{device-pool-B2r38YjS.mjs → device-lease-run-CqjShqWW.mjs} +5 -125
  27. package/dist/device-pool-CUaEWMMm.mjs +125 -0
  28. package/dist/{device-remote-9ulPsdhY.mjs → device-remote-D5WVWqOq.mjs} +12 -12
  29. package/dist/{doctor-BcE5xsBn.mjs → doctor-Cimf-gGB.mjs} +80 -34
  30. package/dist/{error-diagnostics-Cb0P1WLS.mjs → error-diagnostics-B9L4Ae0x.mjs} +11 -15
  31. package/dist/{gc-BuL1Genr.mjs → gc-Bqy4bjEu.mjs} +194 -31
  32. package/dist/{gradle-CmdOBq1d.mjs → gradle-BsO1MdsH.mjs} +4 -4
  33. package/dist/{guide-cCSGRFd3.mjs → guide-B_uyKTxW.mjs} +588 -53
  34. package/dist/{guide-status-CIoWK6mG.mjs → guide-status-CjEZMAD4.mjs} +1 -1
  35. package/dist/hosted-client-a-WmqE2L.mjs +197 -0
  36. package/dist/hosted-ios-CIWelIqf.mjs +220 -0
  37. package/dist/hosted-logs-D5g041Q3.mjs +187 -0
  38. package/dist/hosted-macos-BSZyIb3y.mjs +135 -0
  39. package/dist/{build-progress-C2Jv1AtA.mjs → idle-DYVymG92.mjs} +233 -6
  40. package/dist/{idle-shutdown-B5LPaNyY.mjs → idle-shutdown-CTsDnAde.mjs} +6 -6
  41. package/dist/{in-use-CndPMilN.mjs → in-use-DvBw0Kxo.mjs} +29 -9
  42. package/dist/{ios-3CtCRhwr.mjs → ios-BaInn3sW.mjs} +2 -2
  43. package/dist/ios-Cr1h5gnO.mjs +189 -0
  44. package/dist/{ios-Chob8ny7.mjs → ios-aq3QoGI3.mjs} +729 -452
  45. package/dist/{ios-device-DERmMmk-.mjs → ios-device-FfCN_C8p.mjs} +1 -1
  46. package/dist/ios-state-DS5UpYTH.mjs +48 -0
  47. package/dist/{launch-verify-CLlofxRt.mjs → launch-verify-2DBRizjo.mjs} +3 -3
  48. package/dist/logs-CHEVK8zD.mjs +371 -0
  49. package/dist/{machines-CYtOCqcj.mjs → machines-BIBUdhpj.mjs} +16 -10
  50. package/dist/{macos-BwtbUtTN.mjs → macos-CSYEAFmF.mjs} +22 -17
  51. package/dist/macos-run.mjs +1 -1
  52. package/dist/maintenance-run.d.mts +4 -0
  53. package/dist/maintenance-run.mjs +254 -0
  54. package/dist/{metro-Di8EN2Tb.mjs → metro-BeK1ZPz1.mjs} +1 -1
  55. package/dist/metro-gateway-BJ-rxQAB.mjs +84 -0
  56. package/dist/{named-ports-Dj_XBNaL.mjs → named-ports-Bk_YXEmE.mjs} +1 -1
  57. package/dist/{native-run-DFDoIRbY.mjs → native-run-NaBrRFtQ.mjs} +2 -2
  58. package/dist/{native-runtime-i6OkVg-w.mjs → native-runtime-DBzRnEKS.mjs} +6 -6
  59. package/dist/{ndjson-CDY9IkVC.mjs → ndjson-PclOIwVY.mjs} +2 -2
  60. package/dist/offload-worker.d.mts +1 -0
  61. package/dist/offload-worker.mjs +13 -8
  62. package/dist/{ownership-BkYxUtuj.mjs → ownership-DiA16AsN.mjs} +2 -2
  63. package/dist/{stop-cause-CZ_Mr-v9.mjs → ownership-hCkPzGmr.mjs} +4 -98
  64. package/dist/{ports-DddwttKM.mjs → ports-R2k4Knz3.mjs} +2 -2
  65. package/dist/{prebuild-BvxDlD6C.mjs → prebuild-D2fsca_Z.mjs} +4 -4
  66. package/dist/preview-DhVbvPAZ.mjs +318 -0
  67. package/dist/{project-CV3tDFHl.mjs → project-Du91Ilvc.mjs} +67 -3
  68. package/dist/{recordings-GEXp4iBT.mjs → recordings-BIXOzVnr.mjs} +1 -1
  69. package/dist/{reload-ChPiwvot.mjs → reload-rM9jV1fz.mjs} +49 -12
  70. package/dist/{remote-cache-CO2elm_b.mjs → remote-cache-C8-V13XN.mjs} +4 -4
  71. package/dist/{run-B-xqRrsg.mjs → run-f4vI6EnK.mjs} +2 -2
  72. package/dist/{server-bare-X7PUgizL.mjs → server-bare-CpPGmnmO.mjs} +1 -1
  73. package/dist/server-expo-CVqO2c35.mjs +2 -0
  74. package/dist/{server-expo-DhtX6bom.mjs → server-expo-MC0rHlOC.mjs} +4 -4
  75. package/dist/settings-BBpMcDbD.mjs +25 -0
  76. package/dist/{settings-DbL6uhH8.mjs → settings-ZkDzkzvB.mjs} +10 -4
  77. package/dist/settings-oTSkkNrM.mjs +1439 -0
  78. package/dist/settings.schema.json +432 -39
  79. package/dist/{simslim-Ciluxlq4.mjs → simslim-B8-7AoTI.mjs} +3 -3
  80. package/dist/{slot-launch-BY0BrOLh.mjs → slot-launch-B8VrHxn1.mjs} +10 -7
  81. package/dist/{spawn-entry-DncpJvWL.mjs → spawn-entry-jw19m8Nl.mjs} +1 -0
  82. package/dist/{start-CPO4wRg7.mjs → start-CX8Ig-U2.mjs} +1 -1
  83. package/dist/{start-CUD9nasA.mjs → start-ClDfwXa4.mjs} +21 -17
  84. package/dist/{state-BxCgeTQu.mjs → state-3DwSQIBK.mjs} +2 -2
  85. package/dist/{state-DFm77wRn.mjs → state-C1jB7Q8q.mjs} +5 -5
  86. package/dist/{state-CaeaHbDA.mjs → state-DcW6vOd_.mjs} +1 -1
  87. package/dist/{stats-DBLLFmKd.mjs → stats-CEZ_i5je.mjs} +16 -5
  88. package/dist/{status-l84Gbvp2.mjs → status-BuvBFeII.mjs} +5 -3
  89. package/dist/{status-DYXxNOT0.mjs → status-Cs7VLg9D.mjs} +148 -44
  90. package/dist/status-DlKvVJBE.mjs +49 -0
  91. package/dist/{stop-Bvf1MflS.mjs → stop-B0Yxtmiy.mjs} +3 -3
  92. package/dist/{stop-Bad61z_2.mjs → stop-Bffozsac.mjs} +61 -20
  93. package/dist/{stop-CceYh1yG.mjs → stop-Dt_dM16A.mjs} +2 -2
  94. package/dist/stop-cause-CidxIqup.mjs +97 -0
  95. package/dist/supervisor-run.mjs +148 -17
  96. package/dist/{support-CDQbDkL5.mjs → support-CAP8qDUF.mjs} +4 -3
  97. package/dist/{support-WLOzVKUv.mjs → support-D4aYJniO.mjs} +34 -9
  98. package/dist/{teardown-BYuQafiA.mjs → teardown-CL6vDs2Y.mjs} +56 -47
  99. package/dist/{toolchain-DmeYjXD9.mjs → toolchain-DkUflduf.mjs} +10 -10
  100. package/dist/trigger-Bemu8ytw.mjs +65 -0
  101. package/dist/trigger-CXts6qt1.mjs +2 -0
  102. package/dist/{warm-progress-DVHLN4uC.mjs → warm-progress-CpGv3Wb6.mjs} +274 -5
  103. package/dist/{web-k6w8-HnE.mjs → web-DWUaMp8P.mjs} +15 -16
  104. package/dist/web-run.mjs +5 -5
  105. package/dist/{workspace-state-CL2V2Ons.mjs → workspace-state-B8-aJ1_5.mjs} +33 -33
  106. package/dist/workspaces-BV4pdHFz.mjs +306 -0
  107. package/dist/{worktree-qXKzADSC.mjs → worktree-K-xj9Db0.mjs} +497 -30
  108. package/dist/{worktree-BT_uULFx.mjs → worktree-jnG8Iph5.mjs} +1 -1
  109. package/package.json +5 -5
  110. package/dist/hosted-macos-DwowNU7W.mjs +0 -447
  111. package/dist/logs-h9h6MUvE.mjs +0 -568
  112. package/dist/machine-usage-D_YYMzfR.mjs +0 -283
  113. package/dist/merge-state-BnxLNQiM.mjs +0 -272
  114. package/dist/server-expo-KJeD109I.mjs +0 -2
  115. package/dist/settings-CduVcoxj.mjs +0 -680
  116. package/dist/watchman-Bx0wceoF.mjs +0 -238
  117. package/dist/worktree-C0c5Udwa.mjs +0 -753
@@ -1,10 +1,11 @@
1
1
  import { H as projectDeviceSlots, R as deviceSlotKey, V as parseDeviceSlotOption, p as getProject } from "./config-D0DbpZtM.mjs";
2
- import { a as findProjectRoot, t as NO_PROJECT_REFUSAL } from "./project-CV3tDFHl.mjs";
3
- import { r as clockTime } from "./command-output-CliHHn7a.mjs";
4
- import { n as fileLeaseIo, o as parseLeaseDuration, p as takeLease, u as releaseWorkspaceLeases } from "./device-lease-W5Pi1q38.mjs";
5
- import { E as getAvdNameForSerial, G as probeEmulatorSerial, N as memoizeEmulatorProbe, O as listAdbDevices, U as physicalDeviceModel, Y as resolvePhysicalDevice, c as androidPoolNoCandidatesRefusal, s as androidPoolCandidates } from "./android-Db2eEMaf.mjs";
6
- import { d as waitForDevice, s as parseDeviceWait, t as selectFromPool } from "./device-pool-B2r38YjS.mjs";
7
- import { a as iosPoolCandidates, c as isWirelessIosDevice, f as resolveIosPhysicalDevice, l as listIosDevices, o as iosPoolNoCandidatesRefusal } from "./ios-device-DERmMmk-.mjs";
2
+ import { r as clockTime } from "./command-output-DnJMdEY-.mjs";
3
+ import { o as findProjectRoot, t as NO_PROJECT_REFUSAL } from "./project-Du91Ilvc.mjs";
4
+ import { n as fileLeaseIo, o as parseLeaseDuration, p as takeLease, u as releaseWorkspaceLeases } from "./device-lease-D-hLqx1b.mjs";
5
+ import { E as getAvdNameForSerial, G as probeEmulatorSerial, N as memoizeEmulatorProbe, O as listAdbDevices, U as physicalDeviceModel, Y as resolvePhysicalDevice, c as androidPoolNoCandidatesRefusal, s as androidPoolCandidates } from "./android-B_NumVyQ.mjs";
6
+ import { l as parseDeviceWait, p as waitForDevice } from "./device-lease-run-CqjShqWW.mjs";
7
+ import { t as selectFromPool } from "./device-pool-CUaEWMMm.mjs";
8
+ import { a as iosPoolCandidates, c as isWirelessIosDevice, f as resolveIosPhysicalDevice, l as listIosDevices, o as iosPoolNoCandidatesRefusal } from "./ios-device-FfCN_C8p.mjs";
8
9
  import chalk from "chalk";
9
10
  //#region src/commands/device.ts
10
11
  const DEFAULT_FOR = "5m";
@@ -1,6 +1,6 @@
1
1
  import { H as projectDeviceSlots, _ as loadConfig, z as deviceSlotPlatforms } from "./config-D0DbpZtM.mjs";
2
- import { O as listAdbDevices } from "./android-Db2eEMaf.mjs";
3
- import { _ as parseRuntimeVersion, c as iosRuntimeMatches, u as listAllIosSims } from "./ios-3CtCRhwr.mjs";
2
+ import { O as listAdbDevices } from "./android-B_NumVyQ.mjs";
3
+ import { _ as parseRuntimeVersion, c as iosRuntimeMatches, u as listAllIosSims } from "./ios-BaInn3sW.mjs";
4
4
  //#region src/engine/device-capacity.ts
5
5
  function liveOwnedDeviceCount({ sims = [], adbEmulators = [], config = null } = {}) {
6
6
  let count = 0;
@@ -1,25 +1,196 @@
1
1
  import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
+ import { D as withConfigLock$1, _ as loadConfig$1, c as ensureConfig } from "./config-D0DbpZtM.mjs";
2
3
  import { t as process_identity_exports } from "./process-identity-BmRm1qV0.mjs";
3
- import { c as writeWorkspaceState, p as ensureWorkspaceStorage } from "./workspace-state-CL2V2Ons.mjs";
4
- import { A as listAvds, D as hostSystemImageArch, E as getAvdNameForSerial, O as listAdbDevices, R as ownedAvdName, W as pickDefaultSystemImage, a as androidDeviceAbi, b as createOwnedAvd, d as androidToolPath, j as listInstalledSystemImages, k as listAvdDeviceProfiles, t as DEFAULT_AVD_DEVICE_PROFILE, tt as waitForBoot, v as bootAndroidEmulator } from "./android-Db2eEMaf.mjs";
5
- import { i as teardownOwnedAvd, o as teardownOwnedIosSim } from "./teardown-BYuQafiA.mjs";
6
- import { C as resolveIosCreation, i as createOwnedIosSim, j as readHostMemoryPressure, n as bootIosSim, u as listAllIosSims } from "./ios-3CtCRhwr.mjs";
7
- import { a as requiredMacosRecord, r as macosProcess } from "./state-BxCgeTQu.mjs";
8
- import { _ as installIosApp, b as launchAndroidReleaseApp, h as installAndroidApp, s as androidAppProcess, v as iosAppProcess, x as launchIosApp } from "./app-install-CAAplXAt.mjs";
9
- import { h as findAapt } from "./support-CDQbDkL5.mjs";
10
- import { n as stopMacosAppHeld } from "./stop-Bvf1MflS.mjs";
11
- import { t as spawnEntry } from "./spawn-entry-DncpJvWL.mjs";
4
+ import { c as writeWorkspaceState, u as ensureWorkspaceStorage } from "./workspace-state-B8-aJ1_5.mjs";
5
+ import { t as forgetCreatedDevice } from "./created-devices-D5owjN4C.mjs";
6
+ import { A as listAvds, D as hostSystemImageArch, E as getAvdNameForSerial, O as listAdbDevices, R as ownedAvdName, W as pickDefaultSystemImage, _ as avdStorageRoots, a as androidDeviceAbi, b as createOwnedAvd, d as androidToolPath, h as avdPathExists, j as listInstalledSystemImages, k as listAvdDeviceProfiles, t as DEFAULT_AVD_DEVICE_PROFILE, tt as waitForBoot, v as bootAndroidEmulator } from "./android-B_NumVyQ.mjs";
7
+ import { C as resolveIosCreation, i as createOwnedIosSim, j as readHostMemoryPressure, n as bootIosSim, u as listAllIosSims } from "./ios-BaInn3sW.mjs";
8
+ import { i as teardownOwnedAvd, o as teardownOwnedIosSim } from "./teardown-CL6vDs2Y.mjs";
9
+ import { a as requiredMacosRecord, r as macosProcess } from "./state-3DwSQIBK.mjs";
10
+ import { r as createNdjsonWriter } from "./ndjson-PclOIwVY.mjs";
11
+ import { n as parseLogStreamLine } from "./ios-Cr1h5gnO.mjs";
12
+ import { _ as installIosApp, b as launchAndroidReleaseApp, h as installAndroidApp, s as androidAppProcess, v as iosAppProcess, x as launchIosApp } from "./app-install-CKWdbXIX.mjs";
13
+ import { h as findAapt } from "./support-CAP8qDUF.mjs";
14
+ import { n as stopMacosAppHeld } from "./stop-B0Yxtmiy.mjs";
15
+ import { t as spawnEntry } from "./spawn-entry-jw19m8Nl.mjs";
12
16
  import { createHash, randomUUID } from "node:crypto";
13
17
  import { chmodSync, closeSync, copyFileSync, createReadStream, existsSync, lstatSync, mkdirSync, openSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statfsSync, symlinkSync, writeFileSync } from "node:fs";
14
18
  import { dirname, isAbsolute, join, posix, relative, sep } from "node:path";
15
- import { configDir, withDirLock } from "@stim-cli/core";
16
- import { assertHostedDeviceLedger, hostedAppArea, hostedAppAttempt, hostedConsolePort, hostedMacosAppSlot, hostedMacosBundleId, isJsonObject, parseHostedOfferRequest, readHostedApp, readHostedDevice, readMacosRecord } from "@stim-cli/core/state";
19
+ import { LOG_ROTATE_BYTES, configDir, withDirLock } from "@stim-cli/core";
20
+ import { assertHostedDeviceLedger, hostedAppArea, hostedAppAttempt, hostedAppBlobs, hostedConsolePort, hostedIosLogsDir, hostedMacosAppSlot, hostedMacosBundleId, isJsonObject, parseHostedOfferRequest, readHostedApp, readHostedAppMetadata, readHostedDevice, readHostedIosLogsCheckpoint, readMacosRecord, readNdjsonGenerations } from "@stim-cli/core/state";
17
21
  import { availableParallelism, freemem, homedir, loadavg } from "node:os";
22
+ //#region src/device-host/ios-logs.ts
23
+ function logDate(ms) {
24
+ return new Date(ms).toISOString().replace("T", " ").replace(".000Z", "+0000");
25
+ }
26
+ /** Captures a bounded unified-log window only from this home's exact ledger-owned simulator. */
27
+ function collectHostedIosLogs(home, session, attempt, since, final = false) {
28
+ const device = readHostedDevice(home);
29
+ assertHostedDeviceLedger(home, device.udid);
30
+ if (readHostedAppMetadata(session, attempt, home).state !== "installed") return false;
31
+ const directory = hostedIosLogsDir(home);
32
+ const previous = readHostedIosLogsCheckpoint(home);
33
+ const beginning = Math.floor(since / 1e3) * 1e3;
34
+ let newest;
35
+ if (!previous) {
36
+ for (const entry of readNdjsonGenerations(join(directory, "device.ndjson"))) if (typeof entry.ts === "number" && (newest === void 0 || entry.ts > newest)) newest = entry.ts;
37
+ }
38
+ const checkpoint = previous?.until ?? Math.max(beginning, Math.floor((newest ?? beginning) / 1e3) * 1e3);
39
+ let from = Math.max(beginning, previous ? checkpoint - 5e3 : checkpoint);
40
+ const now = Math.floor(Date.now() / 1e3) * 1e3;
41
+ if (now < checkpoint) return false;
42
+ const backlogMs = Math.max(1e3, now - checkpoint);
43
+ let windowMs = !final && previous?.windowMs ? Math.min(previous.windowMs * 2, backlogMs) : backlogMs;
44
+ let until = Math.min(now, checkpoint + windowMs);
45
+ let end = until === now ? until + 1e3 : until;
46
+ const deadline = Date.now() + 1e4;
47
+ const exec = getExecutor();
48
+ const executable = exec.runFile("/usr/libexec/PlistBuddy", [
49
+ "-c",
50
+ "Print :CFBundleExecutable",
51
+ join(hostedAppArea(session, attempt, home), "App.app", "Info.plist")
52
+ ], {
53
+ timeoutMs: 2e3,
54
+ killSignal: "SIGKILL"
55
+ });
56
+ if (!executable || /["\\/\0\r\n]/.test(executable)) throw new Error("The app executable cannot form a log predicate.");
57
+ let output;
58
+ for (;;) {
59
+ const remaining = deadline - Date.now();
60
+ if (remaining <= 0) throw new Error("Hosted native log query exceeded its collection budget.");
61
+ try {
62
+ output = exec.runFile("xcrun", [
63
+ "simctl",
64
+ "spawn",
65
+ device.udid,
66
+ "log",
67
+ "show",
68
+ "--style",
69
+ "ndjson",
70
+ "--predicate",
71
+ `processImagePath ENDSWITH "/App.app/${executable}"`,
72
+ "--info",
73
+ "--start",
74
+ logDate(from),
75
+ "--end",
76
+ logDate(end)
77
+ ], {
78
+ timeoutMs: Math.min(remaining, 4e3),
79
+ killSignal: "SIGKILL"
80
+ });
81
+ break;
82
+ } catch (error) {
83
+ const span = final ? until - from : until - checkpoint;
84
+ if (span <= 1e3) throw error;
85
+ windowMs = Math.max(1e3, Math.floor(span / 4 / 1e3) * 1e3);
86
+ if (final) from = Math.max(beginning, now - windowMs);
87
+ else until = Math.min(now, checkpoint + windowMs);
88
+ end = until === now ? until + 1e3 : until;
89
+ if (previous) {
90
+ mkdirSync(directory, {
91
+ recursive: true,
92
+ mode: 448
93
+ });
94
+ withDirLock(join(directory, "query.lock"), () => {
95
+ const temporary = join(directory, "checkpoint.json.tmp");
96
+ writeFileSync(temporary, JSON.stringify({
97
+ until: checkpoint,
98
+ boundary: previous.boundary,
99
+ windowMs
100
+ }), { mode: 384 });
101
+ renameSync(temporary, join(directory, "checkpoint.json"));
102
+ });
103
+ }
104
+ }
105
+ }
106
+ const seen = /* @__PURE__ */ new Map();
107
+ for (const digest of previous?.boundary ?? []) seen.set(digest, (seen.get(digest) ?? 0) + 1);
108
+ const boundary = [];
109
+ const records = [];
110
+ if (final && from > checkpoint) records.push({
111
+ ts: from,
112
+ src: "device",
113
+ platform: "ios",
114
+ level: "warn",
115
+ msg: `Hosted iOS native log gap: dropped interval [${new Date(checkpoint).toISOString()}, ${new Date(from).toISOString()}) to collect the final tail within its budget.`
116
+ });
117
+ for (const line of output.split("\n")) {
118
+ const parsed = parseLogStreamLine(line);
119
+ if (!parsed || typeof parsed.ts !== "number" || parsed.ts < from || parsed.ts > end) continue;
120
+ const entry = JSON.parse(line);
121
+ const digest = createHash("sha256").update(JSON.stringify(Object.entries(entry).toSorted(([a], [b]) => a.localeCompare(b)))).digest("hex");
122
+ if (parsed.ts >= until - 5e3) boundary.push(digest);
123
+ if (newest !== void 0 && parsed.ts <= newest) continue;
124
+ const count = seen.get(digest) ?? 0;
125
+ if (count) {
126
+ seen.set(digest, count - 1);
127
+ continue;
128
+ }
129
+ records.push({
130
+ ...parsed,
131
+ platform: "ios"
132
+ });
133
+ }
134
+ mkdirSync(directory, {
135
+ recursive: true,
136
+ mode: 448
137
+ });
138
+ withDirLock(join(directory, "query.lock"), () => {
139
+ const writer = createNdjsonWriter(join(directory, "device.ndjson"), { maxBytes: LOG_ROTATE_BYTES });
140
+ try {
141
+ for (const entry of records) if (!writer.write(entry)) throw new Error("Could not persist hosted iOS native logs.");
142
+ } finally {
143
+ writer.close();
144
+ }
145
+ const temporary = join(directory, "checkpoint.json.tmp");
146
+ try {
147
+ writeFileSync(temporary, JSON.stringify({
148
+ until,
149
+ boundary,
150
+ ...until < now ? { windowMs } : {}
151
+ }), { mode: 384 });
152
+ renameSync(temporary, join(directory, "checkpoint.json"));
153
+ } finally {
154
+ rmSync(temporary, { force: true });
155
+ }
156
+ });
157
+ return until < now;
158
+ }
159
+ //#endregion
18
160
  //#region src/device-host/app.ts
19
161
  function inside(root, path) {
20
162
  const diff = relative(root, path);
21
163
  return diff !== "" && diff !== ".." && !diff.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) && !isAbsolute(diff);
22
164
  }
165
+ function removeHostedAppData(home) {
166
+ const area = realpathSync(join(home, ".."));
167
+ const assertInside = (path) => {
168
+ if (!inside(area, realpathSync(path))) throw new Error("Hosted app data resolves outside its private area.");
169
+ };
170
+ const remove = (path) => {
171
+ if (!lstatSync(path, { throwIfNoEntry: false })) return;
172
+ assertInside(path);
173
+ rmSync(path, {
174
+ recursive: true,
175
+ force: true
176
+ });
177
+ };
178
+ assertInside(home);
179
+ const apps = join(area, "apps");
180
+ if (existsSync(apps)) {
181
+ assertInside(apps);
182
+ for (const attempt of readdirSync(apps)) {
183
+ const directory = join(apps, attempt);
184
+ if (!lstatSync(directory).isDirectory()) continue;
185
+ assertInside(directory);
186
+ remove(join(directory, "App.app"));
187
+ remove(join(directory, "App.apk"));
188
+ remove(join(directory, "App.apk.tmp"));
189
+ remove(join(directory, "blobs"));
190
+ }
191
+ }
192
+ remove(join(area, "blobs"));
193
+ }
23
194
  async function digest$1(path) {
24
195
  const hash = createHash("sha256");
25
196
  for await (const chunk of createReadStream(path)) hash.update(chunk);
@@ -42,9 +213,10 @@ function newer(value, supported) {
42
213
  async function materializeHostedApp(home, session, attempt) {
43
214
  const record = readHostedApp(session, attempt, home);
44
215
  const area = hostedAppArea(session, attempt, home);
216
+ const blobs = hostedAppBlobs(session, home);
45
217
  const app = join(area, "App.app");
46
218
  if (record.state !== "installing") throw new Error("This app attempt has not been admitted for installation.");
47
- const manifest = join(area, "blobs", record.manifest.sha256);
219
+ const manifest = join(blobs, record.manifest.sha256);
48
220
  if (lstatSync(manifest).size !== record.manifest.size || await digest$1(manifest) !== record.manifest.sha256) throw new Error("The hosted app manifest differs from its digest.");
49
221
  rmSync(app, {
50
222
  recursive: true,
@@ -52,7 +224,7 @@ async function materializeHostedApp(home, session, attempt) {
52
224
  });
53
225
  mkdirSync(app, { mode: 448 });
54
226
  for (const file of record.files) {
55
- const blob = join(area, "blobs", file.sha256);
227
+ const blob = join(blobs, file.sha256);
56
228
  const stat = lstatSync(blob);
57
229
  if (!stat.isFile() || stat.size !== file.size || await digest$1(blob) !== file.sha256) throw new Error(`App content is incomplete or differs from its digest: ${file.path}`);
58
230
  if (file.kind === "link") continue;
@@ -65,7 +237,7 @@ async function materializeHostedApp(home, session, attempt) {
65
237
  chmodSync(target, file.kind === "exec" ? 493 : 420);
66
238
  }
67
239
  for (const file of record.files.filter((each) => each.kind === "link")) {
68
- const bytes = readFileSync(join(area, "blobs", file.sha256));
240
+ const bytes = readFileSync(join(blobs, file.sha256));
69
241
  const target = bytes.toString("utf8");
70
242
  const destination = posix.normalize(posix.join(posix.dirname(file.path), target));
71
243
  if (!target || !Buffer.from(target).equals(bytes) || /[\\\0\r\n]/.test(target) || posix.isAbsolute(target) || destination === "." || destination === ".." || destination.startsWith("../") || file.path.startsWith(`${destination}/`)) throw new Error(`The app link escapes its bundle or forms an ancestor cycle: ${file.path}`);
@@ -201,7 +373,7 @@ async function runHostedDevice(mode, selectors, app) {
201
373
  };
202
374
  }
203
375
  device = readHostedDevice(home);
204
- assertHostedDeviceLedger(home, device.udid);
376
+ const ledger = assertHostedDeviceLedger(home, device.udid, "ios", { allowEmpty: mode === "stop" });
205
377
  const current = inventory().find((sim) => sim.udid === device.udid);
206
378
  if (mode === "install") {
207
379
  if (!app || current?.state !== "Booted") throw new Error("Hosted app installation requires its booted owned simulator.");
@@ -212,14 +384,20 @@ async function runHostedDevice(mode, selectors, app) {
212
384
  launched
213
385
  };
214
386
  }
215
- if (!current) return {
216
- state: "stopped",
217
- device
218
- };
219
- const outcome = teardownOwnedIosSim(device.udid);
387
+ if (ledger === "empty") {
388
+ if (current) throw new Error("The hosted simulator remains without ledger ownership; it was kept.");
389
+ removeHostedAppData(home);
390
+ return {
391
+ state: "stopped",
392
+ device
393
+ };
394
+ }
395
+ const outcome = teardownOwnedIosSim(device.udid, { del: true });
220
396
  if (outcome.status !== "torn-down" && outcome.status !== "missing") throw new Error(outcome.reason ?? "Hosted simulator teardown was not established.");
221
397
  const after = inventory().find((sim) => sim.udid === device.udid);
222
- if (after && after.state !== "Shutdown") throw new Error("Hosted simulator shutdown could not be verified.");
398
+ if (after) throw new Error(`Hosted simulator ${device.udid} is still listed (${after.state}); deletion could not be verified. If its runtime is unavailable, delete it with xcrun simctl delete and stop the session again.`);
399
+ forgetCreatedDevice("ios", device.udid);
400
+ removeHostedAppData(home);
223
401
  return {
224
402
  state: "stopped",
225
403
  device
@@ -246,9 +424,9 @@ async function installHostedAndroidApp(home, session, attempt, device) {
246
424
  if (record.state !== "installing") throw new Error("This app attempt has not been admitted for installation.");
247
425
  const entry = record.files[0];
248
426
  if (record.files.length !== 1 || !entry || entry.path !== "App.apk" || entry.kind !== "file") throw new Error("Hosted Android delivery requires a single file entry named App.apk.");
249
- const manifest = join(area, "blobs", record.manifest.sha256);
427
+ const manifest = join(hostedAppBlobs(session, home), record.manifest.sha256);
250
428
  if (lstatSync(manifest).size !== record.manifest.size || await digest(manifest) !== record.manifest.sha256) throw new Error("The hosted APK manifest differs from its digest.");
251
- const blob = join(area, "blobs", entry.sha256);
429
+ const blob = join(hostedAppBlobs(session, home), entry.sha256);
252
430
  const stat = lstatSync(blob);
253
431
  if (!stat.isFile() || stat.size !== entry.size || await digest(blob) !== entry.sha256) throw new Error("APK content is incomplete or differs from its digest.");
254
432
  const temporary = join(area, "App.apk.tmp");
@@ -381,10 +559,19 @@ async function runHostedAndroidDevice(mode, request, app) {
381
559
  };
382
560
  }
383
561
  device = readHostedDevice(home, "android");
384
- assertHostedDeviceLedger(home, device.avdName, "android");
385
- const outcome = teardownOwnedAvd(device.avdName);
386
- if (outcome.status !== "torn-down" && outcome.status !== "missing") throw new Error(outcome.reason ?? "Hosted Android shutdown was not established.");
562
+ const ledger = assertHostedDeviceLedger(home, device.avdName, "android", { allowEmpty: true });
563
+ if (ledger === "listed") {
564
+ withConfigLock$1(() => {
565
+ if (!loadConfig$1()) ensureConfig();
566
+ });
567
+ const outcome = teardownOwnedAvd(device.avdName, { del: true });
568
+ if (outcome.status !== "torn-down" && outcome.status !== "missing") throw new Error(outcome.reason ?? "Hosted Android deletion was not established.");
569
+ }
570
+ const name = device.avdName;
571
+ if (listAvds({ timeoutMs: 5e3 }).includes(name) || avdStorageRoots().some((root) => avdPathExists(join(root, `${name}.ini`)) || avdPathExists(join(root, `${name}.avd`)))) throw new Error("The hosted AVD or its data still exists; deletion could not be verified.");
387
572
  if (getAvdNameForSerial(device.serial) === device.avdName) throw new Error("The hosted Android emulator is still running.");
573
+ if (ledger === "listed") forgetCreatedDevice("android", device.avdName);
574
+ removeHostedAppData(home);
388
575
  return {
389
576
  state: "stopped",
390
577
  device
@@ -504,6 +691,7 @@ async function runHostedMacosApp(mode, request, app) {
504
691
  removeAppData(area, join(directory, "blobs"));
505
692
  }
506
693
  }
694
+ removeAppData(area, join(home, "..", "blobs"));
507
695
  return {
508
696
  state: "stopped",
509
697
  device
@@ -724,7 +912,8 @@ async function main() {
724
912
  "prepare",
725
913
  "stop",
726
914
  "install",
727
- "offer"
915
+ "offer",
916
+ "logs"
728
917
  ].includes(String(input.mode))) throw new Error("Invalid hosted worker request.");
729
918
  if (input.mode === "offer") {
730
919
  const request = parseHostedOfferRequest(input);
@@ -740,9 +929,15 @@ async function main() {
740
929
  })}\n`);
741
930
  return;
742
931
  }
743
- if (input.mode === "install" && (typeof input.session !== "string" || !/^[a-f0-9-]{36}$/.test(input.session) || !hostedAppAttempt(input.attempt))) throw new Error("Invalid hosted app request.");
932
+ if ((input.mode === "install" || input.mode === "logs") && (typeof input.session !== "string" || !/^[a-f0-9-]{36}$/.test(input.session) || !hostedAppAttempt(input.attempt))) throw new Error("Invalid hosted app request.");
744
933
  if (input.metroPort !== void 0 && (typeof input.metroPort !== "number" || !Number.isInteger(input.metroPort) || input.metroPort < 1 || input.metroPort > 65535)) throw new Error("Invalid hosted Metro port.");
745
934
  if (input.platform === "macos" && !hostedMacosAppSlot(input.appSlot)) throw new Error("Invalid hosted macOS app slot.");
935
+ if (input.mode === "logs") {
936
+ if (input.platform !== "ios" || !process.env.STIM_HOME || typeof input.since !== "number" || !Number.isFinite(input.since)) throw new Error("Invalid hosted iOS log request.");
937
+ const more = collectHostedIosLogs(process.env.STIM_HOME, input.session, input.attempt, input.since, input.final === true);
938
+ process.stdout.write(`${JSON.stringify({ more })}\n`);
939
+ return;
940
+ }
746
941
  const result = input.platform === "macos" ? await runHostedMacosApp(input.mode, {
747
942
  session: typeof input.session === "string" ? input.session : "",
748
943
  appSlot: input.appSlot
@@ -1,13 +1,13 @@
1
1
  import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
2
  import { C as saveConfig, D as withConfigLock, H as projectDeviceSlots, _ as loadConfig, a as clearDevice, d as getConfigDir, w as setDevice, z as deviceSlotPlatforms } from "./config-D0DbpZtM.mjs";
3
- import { x as workspaceId } from "./workspace-state-CL2V2Ons.mjs";
3
+ import { l as phaseLine } from "./command-output-DnJMdEY-.mjs";
4
+ import { y as iosSimSlimProfileSetting } from "./settings-oTSkkNrM.mjs";
5
+ import { y as workspaceId } from "./workspace-state-B8-aJ1_5.mjs";
6
+ import { ct as dropParked, ft as parkedMaxSetting, ht as selectParked, pt as readParked, st as adoptParked } from "./android-B_NumVyQ.mjs";
7
+ import { A as hostMemoryPressureAdvice, C as resolveIosCreation, O as configuredIosSimulatorViewer, S as resetIosPrivacy, _ as parseRuntimeVersion, b as renameIosSim, f as listIosDeviceTypes, h as ownedSimName, i as createOwnedIosSim, j as readHostMemoryPressure, l as iosSimulatorFailureAdvice, n as bootIosSim, p as listIosRuntimes, u as listAllIosSims, w as resolveOwnedIosSim, x as resetIosKeychain } from "./ios-BaInn3sW.mjs";
8
+ import { c as teardownParkedIosSim } from "./teardown-CL6vDs2Y.mjs";
4
9
  import { t as withWorkspaceProcessLock } from "./workspace-process-lock-BL7Q0HCO.mjs";
5
- import { l as phaseLine } from "./command-output-CliHHn7a.mjs";
6
- import { dt as parkedMaxSetting, ft as readParked, mt as selectParked, ot as adoptParked, st as dropParked } from "./android-Db2eEMaf.mjs";
7
- import { y as iosSimSlimProfileSetting } from "./settings-CduVcoxj.mjs";
8
- import { c as teardownParkedIosSim } from "./teardown-BYuQafiA.mjs";
9
- import { A as hostMemoryPressureAdvice, C as resolveIosCreation, O as configuredIosSimulatorViewer, S as resetIosPrivacy, _ as parseRuntimeVersion, b as renameIosSim, f as listIosDeviceTypes, h as ownedSimName, i as createOwnedIosSim, j as readHostMemoryPressure, l as iosSimulatorFailureAdvice, n as bootIosSim, p as listIosRuntimes, u as listAllIosSims, w as resolveOwnedIosSim, x as resetIosKeychain } from "./ios-3CtCRhwr.mjs";
10
- import { a as runtimeMismatch, n as deviceTypeMismatch } from "./device-capacity-DMuKC7nx.mjs";
10
+ import { a as runtimeMismatch, n as deviceTypeMismatch } from "./device-capacity-BHwxNBlu.mjs";
11
11
  import chalk from "chalk";
12
12
  //#region src/engine/device-ios.ts
13
13
  function startIosBoot(udid, configure, label, out, simulatorApp) {
@@ -2,9 +2,9 @@ import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
2
  import { B as parseDeviceSlotKey, R as deviceSlotKey, W as validateDeviceSlot, d as getConfigDir } from "./config-D0DbpZtM.mjs";
3
3
  import { n as ownership_claim_exports } from "./process-identity-BmRm1qV0.mjs";
4
4
  import { r as register } from "./cache-manifest-DnUHBJaU.mjs";
5
- import { E as withDirLock$1, c as writeWorkspaceState, m as sharedBuildCache, n as clearWorkspaceStateKeys, o as withWorkspaceStateLock, r as readWorkspaceState } from "./workspace-state-CL2V2Ons.mjs";
5
+ import { a as formatElapsed, i as formatDuration, m as shortHash } from "./command-output-DnJMdEY-.mjs";
6
+ import { c as writeWorkspaceState, d as sharedBuildCache, n as clearWorkspaceStateKeys, o as withWorkspaceStateLock, r as readWorkspaceState, w as withDirLock$1 } from "./workspace-state-B8-aJ1_5.mjs";
6
7
  import { n as declareSpawnsOn, o as stopDeclaringSpawnsOn } from "./spawn-claims-D0XfSe8u.mjs";
7
- import { a as formatElapsed, i as formatDuration, m as shortHash } from "./command-output-CliHHn7a.mjs";
8
8
  import { createHash, randomBytes } from "node:crypto";
9
9
  import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
10
10
  import { join } from "node:path";
@@ -1,11 +1,12 @@
1
1
  import { R as deviceSlotKey } from "./config-D0DbpZtM.mjs";
2
- import { a as formatElapsed, r as clockTime } from "./command-output-CliHHn7a.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-W5Pi1q38.mjs";
4
- import { a as VERIFY_TIMEOUT_MS, m as APP_READINESS_TIMEOUT_MS } from "./launch-verify-CLlofxRt.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
- //#region src/engine/device-pool.ts
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 };