stim 1.15.0 → 1.17.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 (123) hide show
  1. package/dist/{activity-B2-kb7k0.mjs → activity-IRvJY_ru.mjs} +49 -16
  2. package/dist/agent-device-usage-output-BK41vnzN.mjs +411 -0
  3. package/dist/{android-Cu_L4RpL.mjs → android--GorydsK.mjs} +692 -321
  4. package/dist/{android-Db2eEMaf.mjs → android-B_YNZy61.mjs} +25 -60
  5. package/dist/{android-CYmq0jNi.mjs → android-DE4v59Mb.mjs} +12 -3
  6. package/dist/{android-cas-PnKfAdB2.mjs → android-cas-ZFpjJBL_.mjs} +6 -5
  7. package/dist/{app-install-CAAplXAt.mjs → app-install-VPkI5WEg.mjs} +52 -8
  8. package/dist/attempt-Bk2ouQVL.mjs +39 -0
  9. package/dist/{budget-CMiqghig.mjs → budget-DM984mnd.mjs} +372 -366
  10. package/dist/{build-plan-aeGMFX7e.mjs → build-plan-CmQCoyT-.mjs} +346 -88
  11. package/dist/{build-slots-Df1ApSuH.mjs → build-slots-41v_d2x9.mjs} +36 -21
  12. package/dist/{cli-0cX5s4NM.mjs → cli-Daxug5-K.mjs} +23 -17
  13. package/dist/cli.mjs +1 -1
  14. package/dist/collector-run.mjs +5 -192
  15. package/dist/{command-output-CliHHn7a.mjs → command-output-CDAIFD-8.mjs} +3 -3
  16. package/dist/{created-devices-BwVb3liH.mjs → created-devices-CpvzBL25.mjs} +1 -1
  17. package/dist/{dependency-state-BmsjKZ2H.mjs → dependency-state-CLcBHH2M.mjs} +1 -1
  18. package/dist/{deps-BFlKbOIp.mjs → deps-DNWaQs4V.mjs} +2 -2
  19. package/dist/{dev-client-CjzLoNs0.mjs → dev-client-B-XC4uRH.mjs} +3 -40
  20. package/dist/{device-BZ1IyS5A.mjs → device-C8IOoJPv.mjs} +7 -6
  21. package/dist/device-capacity-D0GWHnTM.mjs +580 -0
  22. package/dist/device-host-worker.mjs +587 -110
  23. package/dist/{device-ios-DMQBUPus.mjs → device-ios-Bc7vbGwl.mjs} +75 -44
  24. package/dist/{device-lease-W5Pi1q38.mjs → device-lease-DpoS36_u.mjs} +2 -2
  25. package/dist/{device-pool-B2r38YjS.mjs → device-lease-run-B8YltIH_.mjs} +5 -125
  26. package/dist/device-pool-3o7s5lo4.mjs +125 -0
  27. package/dist/{device-remote-9ulPsdhY.mjs → device-remote-hFSkpfQu.mjs} +13 -13
  28. package/dist/{doctor-BcE5xsBn.mjs → doctor-Cnk9d7Hm.mjs} +86 -59
  29. package/dist/{error-diagnostics-Cb0P1WLS.mjs → error-diagnostics-C-NMnhrk.mjs} +11 -15
  30. package/dist/{gc-BuL1Genr.mjs → gc-DxCL2A46.mjs} +478 -33
  31. package/dist/{gradle-CmdOBq1d.mjs → gradle-D7-L2Th3.mjs} +4 -4
  32. package/dist/{guide-cCSGRFd3.mjs → guide-DjrTQGik.mjs} +2198 -566
  33. package/dist/{guide-status-CIoWK6mG.mjs → guide-status-Du-ZQ3FD.mjs} +1 -1
  34. package/dist/hosted-android-DwLS4Mse.mjs +32 -0
  35. package/dist/hosted-client-wk1W_CNW.mjs +196 -0
  36. package/dist/hosted-ios-aMo7Cljf.mjs +15 -0
  37. package/dist/hosted-logs-DqYmWtXc.mjs +187 -0
  38. package/dist/hosted-macos-dlHXEeF5.mjs +135 -0
  39. package/dist/hosted-native-C9xJ9rpA.mjs +255 -0
  40. package/dist/{build-progress-C2Jv1AtA.mjs → idle-B2m-Ovod.mjs} +300 -9
  41. package/dist/{idle-shutdown-B5LPaNyY.mjs → idle-shutdown-_3oOV1V_.mjs} +25 -17
  42. package/dist/{in-use-CndPMilN.mjs → in-use-CvGOUn-x.mjs} +30 -10
  43. package/dist/ios-Cr1h5gnO.mjs +189 -0
  44. package/dist/{ios-Chob8ny7.mjs → ios-DE6duQoz.mjs} +819 -460
  45. package/dist/{ios-3CtCRhwr.mjs → ios-DSfnPfse.mjs} +4 -4
  46. package/dist/{ios-device-DERmMmk-.mjs → ios-device-pAN4t54-.mjs} +1 -1
  47. package/dist/ios-state-8L3lgum6.mjs +89 -0
  48. package/dist/{launch-verify-CLlofxRt.mjs → launch-verify-lTnWZikR.mjs} +3 -3
  49. package/dist/logs-DUoKlDoS.mjs +384 -0
  50. package/dist/{client-C2IS36Qf.mjs → machines-C2XiNwh9.mjs} +330 -37
  51. package/dist/{macos-BwtbUtTN.mjs → macos-32po3kUq.mjs} +23 -18
  52. package/dist/macos-run.mjs +1 -1
  53. package/dist/maintenance-run.d.mts +4 -0
  54. package/dist/maintenance-run.mjs +254 -0
  55. package/dist/{metro-Di8EN2Tb.mjs → metro-g8NqiJp-.mjs} +1 -1
  56. package/dist/metro-gateway-DmslUM30.mjs +103 -0
  57. package/dist/{named-ports-Dj_XBNaL.mjs → named-ports-C8m8EQs2.mjs} +2 -2
  58. package/dist/{native-run-DFDoIRbY.mjs → native-run-D1Q56rAD.mjs} +17 -6
  59. package/dist/{native-runtime-i6OkVg-w.mjs → native-runtime-DhJI3IVQ.mjs} +6 -6
  60. package/dist/{ndjson-CDY9IkVC.mjs → ndjson-PclOIwVY.mjs} +2 -2
  61. package/dist/offload-worker.d.mts +1 -0
  62. package/dist/offload-worker.mjs +13 -8
  63. package/dist/{teardown-BYuQafiA.mjs → ownership-BMLqyBgJ.mjs} +639 -53
  64. package/dist/{ownership-BkYxUtuj.mjs → ownership-DyRQru0q.mjs} +2 -2
  65. package/dist/{ports-DddwttKM.mjs → ports-4nYqxPWC.mjs} +2 -2
  66. package/dist/{prebuild-BvxDlD6C.mjs → prebuild-D7RUMkwN.mjs} +4 -4
  67. package/dist/preview-CxPRIEht.mjs +318 -0
  68. package/dist/{project-CV3tDFHl.mjs → project-D47Rh0ly.mjs} +72 -3
  69. package/dist/{recordings-GEXp4iBT.mjs → recordings-BNgsD1Tr.mjs} +1 -1
  70. package/dist/{reload-ChPiwvot.mjs → reload-qZei6iWn.mjs} +77 -13
  71. package/dist/{remote-cache-CO2elm_b.mjs → remote-cache-Ci4DQYYj.mjs} +4 -4
  72. package/dist/{run-B-xqRrsg.mjs → run-CkE9y0nc.mjs} +2 -2
  73. package/dist/{server-bare-X7PUgizL.mjs → server-bare-BkrSxSsZ.mjs} +1 -1
  74. package/dist/{server-expo-DhtX6bom.mjs → server-expo-CHTyXD0G.mjs} +4 -4
  75. package/dist/server-expo-CeakhI8P.mjs +2 -0
  76. package/dist/settings-BBpMcDbD.mjs +25 -0
  77. package/dist/settings-Cfd-0r2d.mjs +1440 -0
  78. package/dist/{settings-DbL6uhH8.mjs → settings-SR9OmW9h.mjs} +10 -4
  79. package/dist/settings.schema.json +472 -50
  80. package/dist/{simslim-Ciluxlq4.mjs → simslim-BWNrc3M3.mjs} +3 -3
  81. package/dist/{slot-launch-BY0BrOLh.mjs → slot-launch-DmjBmpQV.mjs} +10 -7
  82. package/dist/{spawn-entry-DncpJvWL.mjs → spawn-entry-jw19m8Nl.mjs} +1 -0
  83. package/dist/{start-CPO4wRg7.mjs → start-BopZcr4n.mjs} +1 -1
  84. package/dist/{start-CUD9nasA.mjs → start-uee1wWjY.mjs} +21 -17
  85. package/dist/{state-CaeaHbDA.mjs → state-BKa8wNN5.mjs} +1 -1
  86. package/dist/{state-BxCgeTQu.mjs → state-DyaNTgfR.mjs} +2 -2
  87. package/dist/{state-DFm77wRn.mjs → state-PeQiJuIu.mjs} +5 -5
  88. package/dist/{stats-DBLLFmKd.mjs → stats-Bq4fJOdB.mjs} +19 -5
  89. package/dist/{status-l84Gbvp2.mjs → status-CzS4bPO9.mjs} +5 -3
  90. package/dist/status-DlKvVJBE.mjs +49 -0
  91. package/dist/{status-DYXxNOT0.mjs → status-dXX2JX1C.mjs} +244 -50
  92. package/dist/{stop-CceYh1yG.mjs → stop-BCwvvs7F.mjs} +2 -2
  93. package/dist/{stop-Bad61z_2.mjs → stop-BjizrQwv.mjs} +83 -21
  94. package/dist/{stop-Bvf1MflS.mjs → stop-Cr1d8DSE.mjs} +4 -4
  95. package/dist/stop-cause-rhmGfwvt.mjs +97 -0
  96. package/dist/supervisor-run.d.mts +7 -4
  97. package/dist/supervisor-run.mjs +156 -27
  98. package/dist/{support-WLOzVKUv.mjs → support-BJPgYCu-.mjs} +61 -11
  99. package/dist/{support-CDQbDkL5.mjs → support-F9-sr0Oi.mjs} +6 -5
  100. package/dist/{toolchain-DmeYjXD9.mjs → toolchain-CNIBTIOv.mjs} +11 -11
  101. package/dist/trigger-Bemu8ytw.mjs +65 -0
  102. package/dist/trigger-CXts6qt1.mjs +2 -0
  103. package/dist/{warm-progress-DVHLN4uC.mjs → warm-progress-DF4JRvYI.mjs} +274 -5
  104. package/dist/watchman-APYjXSQa.mjs +48 -0
  105. package/dist/{web-k6w8-HnE.mjs → web-exQtGkoc.mjs} +14 -16
  106. package/dist/web-run.mjs +5 -5
  107. package/dist/{workspace-process-lock-BL7Q0HCO.mjs → workspace-process-lock-2mNzOlaB.mjs} +3 -2
  108. package/dist/{workspace-state-CL2V2Ons.mjs → workspace-state-DqcCbpE7.mjs} +34 -34
  109. package/dist/workspaces-Bcib97-6.mjs +306 -0
  110. package/dist/{worktree-BT_uULFx.mjs → worktree-Dc98cE6_.mjs} +1 -1
  111. package/dist/{worktree-qXKzADSC.mjs → worktree-ZpZR3S1k.mjs} +601 -33
  112. package/package.json +5 -5
  113. package/dist/device-capacity-DMuKC7nx.mjs +0 -136
  114. package/dist/hosted-macos-DwowNU7W.mjs +0 -447
  115. package/dist/logs-h9h6MUvE.mjs +0 -568
  116. package/dist/machine-usage-D_YYMzfR.mjs +0 -283
  117. package/dist/machines-CYtOCqcj.mjs +0 -259
  118. package/dist/merge-state-BnxLNQiM.mjs +0 -272
  119. package/dist/server-expo-KJeD109I.mjs +0 -2
  120. package/dist/settings-CduVcoxj.mjs +0 -680
  121. package/dist/stop-cause-CZ_Mr-v9.mjs +0 -665
  122. package/dist/watchman-Bx0wceoF.mjs +0 -238
  123. package/dist/worktree-C0c5Udwa.mjs +0 -753
@@ -1,29 +1,34 @@
1
1
  import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
- import { D as withConfigLock$1, H as projectDeviceSlots, _ as loadConfig$1, a as clearDevice, b as releaseAndroidConsolePort, i as clearAndroidBootPending, k as acquireAvdClaim, t as allConsolePortsAndSerials, w as setDevice, z as deviceSlotPlatforms } from "./config-D0DbpZtM.mjs";
3
- import { m as sharedBuildCache } from "./workspace-state-CL2V2Ons.mjs";
4
- import { D as makeTemporaryDirectory, O as removeTemporaryEntry, c as gitCommonDirOnDisk } from "./worktree-C0c5Udwa.mjs";
5
- import { c as ownedDeviceLabel } from "./project-CV3tDFHl.mjs";
2
+ import { D as withConfigLock$1, H as projectDeviceSlots, _ as loadConfig$1, a as clearDevice, b as releaseAndroidConsolePort, i as clearAndroidBootPending, k as acquireAvdClaim, p as getProject, t as allConsolePortsAndSerials, w as setDevice, z as deviceSlotPlatforms } from "./config-D0DbpZtM.mjs";
3
+ import { n as ownership_claim_exports } from "./process-identity-BmRm1qV0.mjs";
6
4
  import { r as waitForChild, t as createLineReader } from "./process-output-Fpl8PYi8.mjs";
7
- import { i as formatDuration, l as phaseLine, m as shortHash, u as plural } from "./command-output-CliHHn7a.mjs";
8
- import { C as diffFingerprintSources, I as storedSources, w as entryDir, y as artifactIn } from "./device-lease-W5Pi1q38.mjs";
9
- import { A as listAvds, B as ownedAvdSystemImage, E as getAvdNameForSerial, F as ownedAvdDeviceProfile, J as resolveOwnedAvdSerial, L as ownedAvdMatchesConfiguration, O as listAdbDevices, P as nextConsolePort, R as ownedAvdName, W as pickDefaultSystemImage, b as createOwnedAvd, dt as parkedMaxSetting, f as assertOwnedAvdStopped, ft as readParked, g as avdPoolConfiguration, j as listInstalledSystemImages, lt as isLegacyDeletionClaim, n as OWNED_AVD_CONFIG_DEFAULTS, ot as adoptParked, t as DEFAULT_AVD_DEVICE_PROFILE, tt as waitForBoot, v as bootAndroidEmulator, y as configureNewOwnedAvd } from "./android-Db2eEMaf.mjs";
10
- import { o as pidExists } from "./metro-Di8EN2Tb.mjs";
11
- import { l as androidDataPartitionSizeGbSetting, o as androidAvdConfigSetting } from "./settings-CduVcoxj.mjs";
12
- import { i as teardownOwnedAvd, s as teardownParkedAvd } from "./teardown-BYuQafiA.mjs";
13
- import { j as readHostMemoryPressure, u as listAllIosSims } from "./ios-3CtCRhwr.mjs";
14
- import { o as estimateBuild, p as readStats } from "./build-progress-C2Jv1AtA.mjs";
15
- import { t as clearDeviceIdleShutdown } from "./idle-shutdown-B5LPaNyY.mjs";
16
- import { i as liveOwnedDeviceCount } from "./device-capacity-DMuKC7nx.mjs";
17
- import { b as tailLines, u as HEARTBEAT_INTERVAL_MS, y as startBuildHeartbeat } from "./toolchain-DmeYjXD9.mjs";
18
- import { t as cleanLine } from "./server-expo-DhtX6bom.mjs";
19
- import { i as staleNativeDirRefusal } from "./prebuild-BvxDlD6C.mjs";
20
- import { i as ensureOwnedIosDevice, r as ensureIosBooted } from "./device-ios-DMQBUPus.mjs";
21
- import { a as easAuthNote } from "./remote-cache-CO2elm_b.mjs";
22
- import { t as reconcileSimSlim } from "./simslim-Ciluxlq4.mjs";
5
+ import { i as formatDuration, l as phaseLine, m as shortHash, u as plural } from "./command-output-CDAIFD-8.mjs";
6
+ import { Tt as removeTemporaryEntry, a as androidAvdConfigSetting, at as gitCommonDirOnDisk, c as androidDataPartitionSizeGbSetting, wt as makeTemporaryDirectory } from "./settings-Cfd-0r2d.mjs";
7
+ import { u as ownedDeviceLabel } from "./project-D47Rh0ly.mjs";
8
+ import { o as pidExists } from "./metro-g8NqiJp-.mjs";
9
+ import { d as sharedBuildCache } from "./workspace-state-DqcCbpE7.mjs";
10
+ import { o as peekBudget } from "./budget-DM984mnd.mjs";
11
+ import { C as diffFingerprintSources, I as storedSources, w as entryDir, y as artifactIn } from "./device-lease-DpoS36_u.mjs";
12
+ import { A as listAvds, B as ownedAvdSystemImage, E as getAvdNameForSerial, F as ownedAvdDeviceProfile, J as resolveOwnedAvdSerial, L as ownedAvdMatchesConfiguration, O as listAdbDevices, P as nextConsolePort, R as ownedAvdName, W as pickDefaultSystemImage, b as createOwnedAvd, f as assertOwnedAvdStopped, ft as parkedMaxSetting, g as avdPoolConfiguration, j as listInstalledSystemImages, n as OWNED_AVD_CONFIG_DEFAULTS, pt as readParked, st as adoptParked, t as DEFAULT_AVD_DEVICE_PROFILE, tt as waitForBoot, ut as isLegacyDeletionClaim, v as bootAndroidEmulator, y as configureNewOwnedAvd } from "./android-B_YNZy61.mjs";
13
+ import { j as readHostMemoryPressure, u as listAllIosSims } from "./ios-DSfnPfse.mjs";
14
+ import { _ as teardownParkedAvd, m as teardownOwnedAvd } from "./ownership-BMLqyBgJ.mjs";
15
+ import { b as readStats$1, p as estimateBuild } from "./idle-B2m-Ovod.mjs";
16
+ import { b as tailLines, u as HEARTBEAT_INTERVAL_MS, y as startBuildHeartbeat } from "./toolchain-CNIBTIOv.mjs";
17
+ import { t as cleanLine } from "./server-expo-CHTyXD0G.mjs";
18
+ import { g as resolveBuildMachine, m as namedBuildMachine, t as configuredMachines } from "./machines-C2XiNwh9.mjs";
19
+ import { a as call, o as connectHost } from "./hosted-client-wk1W_CNW.mjs";
20
+ import { t as clearDeviceIdleShutdown } from "./idle-shutdown-_3oOV1V_.mjs";
21
+ import { i as countLiveOwnedDevices, n as DeviceAdmissionRefusal, p as withDeviceBootAdmission, s as peekDeviceSlots } from "./device-capacity-D0GWHnTM.mjs";
22
+ import { i as staleNativeDirRefusal } from "./prebuild-D7RUMkwN.mjs";
23
+ import { i as ensureOwnedIosDevice, r as ensureIosBooted } from "./device-ios-Bc7vbGwl.mjs";
24
+ import { r as readHostedNative, s as writeHostedNative } from "./ios-state-8L3lgum6.mjs";
25
+ import { n as prepareHostedNative } from "./hosted-native-C9xJ9rpA.mjs";
26
+ import { a as easAuthNote } from "./remote-cache-Ci4DQYYj.mjs";
27
+ import { t as reconcileSimSlim } from "./simslim-BWNrc3M3.mjs";
23
28
  import { randomUUID } from "node:crypto";
24
29
  import { existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, rmSync, writeFileSync } from "node:fs";
25
30
  import { basename, dirname, join, relative } from "node:path";
26
- import { LAST_BUILD_KEYS, loadConfig, readWorkspaceState } from "@stim-cli/core/state";
31
+ import { LAST_BUILD_KEYS, isJsonObject, loadConfig, machineCapacity, parseHostedChoice, parseHostedNativeOffer, readWorkspaceState } from "@stim-cli/core/state";
27
32
  import { claimRemoveCommand, clearClaimChild, markClaimChildPending, processGroupAlive, releaseClaim, setClaimChild } from "@stim-cli/core/ownership-claim";
28
33
  import { captureProcessIdentity } from "@stim-cli/core/process-identity";
29
34
  import chalk from "chalk";
@@ -31,6 +36,219 @@ import { isDeepStrictEqual } from "node:util";
31
36
  import * as expoFingerprint from "@expo/fingerprint";
32
37
  import { resolveTieredBuild } from "@stim-cli/cache";
33
38
  import { request } from "node:http";
39
+ //#region src/device-host/placement.ts
40
+ function local$1(reason, skipped = []) {
41
+ return {
42
+ kind: "local",
43
+ reason,
44
+ skipped
45
+ };
46
+ }
47
+ function decideDevicePlacement(inputs) {
48
+ if ("sticky" in inputs) {
49
+ if ("machine" in inputs.sticky) {
50
+ const reason = `recorded session on ${inputs.sticky.machine}`;
51
+ return {
52
+ kind: "host",
53
+ reason,
54
+ machines: [{
55
+ machine: inputs.sticky.machine,
56
+ reason
57
+ }],
58
+ skipped: []
59
+ };
60
+ }
61
+ return {
62
+ kind: "local",
63
+ reason: "this workspace's device runs here",
64
+ skipped: []
65
+ };
66
+ }
67
+ const { platform, here, offers, buildMachine, noWait } = inputs;
68
+ const { count, max, queued } = here.devices;
69
+ const cannotTake = !(max === 0 || count !== null && count < max) || queued > 0;
70
+ const usage = max > 0 ? `${count} of ${max} devices in use` : `${count} devices in use, no device cap`;
71
+ const localReason = count === null ? "cannot tell how many devices run here; local boot admission decides" : cannotTake ? `${usage}${queued ? `, ${queued} runs queued ahead` : ""}` : here.budgetRefusal ?? (here.memoryPressure !== "normal" ? `host memory pressure ${here.memoryPressure ?? "unknown"} here` : `load ${here.loadPerCore.toFixed(1)}/core here, ${usage}`);
72
+ if (!offers.length) return local$1("no hosting machines configured; " + localReason);
73
+ if (count === null) return local$1(localReason);
74
+ if (!cannotTake && here.memoryPressure === "normal" && !here.budgetRefusal && here.loadPerCore < here.maxLoadPerCore) return local$1(localReason);
75
+ const skipped = [];
76
+ const admitted = [];
77
+ offers.forEach((probe, index) => {
78
+ let reason = null;
79
+ if ("failure" in probe) reason = probe.failure;
80
+ else {
81
+ const { offer } = probe;
82
+ if (offer.declined !== null) reason = `declined: ${offer.declined}`;
83
+ else if (offer.platform !== platform || !offer.choice) reason = `no matching ${platform} device choice`;
84
+ else if (offer.resources.memoryPressure !== "normal") reason = "host memory pressure unknown or elevated";
85
+ else if (offer.capacity.available === 0) reason = "no hosted device capacity available";
86
+ else if (!cannotTake && (offer.resources.loadPerCore === null || offer.resources.loadPerCore >= here.loadPerCore)) reason = offer.resources.loadPerCore === null ? "host load unknown while this Mac has room" : "host is not less loaded than this Mac";
87
+ else admitted.push({
88
+ machine: probe.machine,
89
+ offer,
90
+ index
91
+ });
92
+ }
93
+ if (reason) skipped.push({
94
+ machine: probe.machine,
95
+ reason
96
+ });
97
+ });
98
+ admitted.sort((a, b) => Number(b.machine === buildMachine) - Number(a.machine === buildMachine) || (a.offer.resources.loadPerCore ?? Infinity) - (b.offer.resources.loadPerCore ?? Infinity) || b.offer.resources.memoryFreeBytes - a.offer.resources.memoryFreeBytes || a.index - b.index);
99
+ if (!admitted.length) return local$1(`${localReason}; no host admits${cannotTake && !noWait ? "; waiting locally if needed" : ""}`, skipped);
100
+ const machines = admitted.map(({ machine, offer }) => ({
101
+ machine,
102
+ reason: `${localReason}; ${machine}${machine === buildMachine ? " is the explicit build machine," : ""} load ${offer.resources.loadPerCore === null ? "unknown" : offer.resources.loadPerCore.toFixed(1) + "/core"}`
103
+ }));
104
+ return {
105
+ kind: "host",
106
+ machines,
107
+ reason: machines[0].reason,
108
+ skipped
109
+ };
110
+ }
111
+ //#endregion
112
+ //#region src/device-host/auto-placement.ts
113
+ async function probeHost(machine, platform, selectors) {
114
+ let host;
115
+ const deadline = Date.now() + 3e3;
116
+ try {
117
+ host = await connectHost(machine, 3e3, true);
118
+ const offer = await call(host, "device-host.offer", {
119
+ platform,
120
+ ...selectors
121
+ }, Math.max(1, deadline - Date.now()));
122
+ const resources = isJsonObject(offer.resources) ? offer.resources : {};
123
+ const parsed = parseHostedNativeOffer({
124
+ ...offer,
125
+ resources: {
126
+ ...resources,
127
+ loadPerCore: resources.loadPerCore ?? 0
128
+ }
129
+ });
130
+ let choice = platform === "ios" ? offer.platform === "ios" ? parseHostedChoice(offer.choice) : null : parsed?.platform === "android" ? parsed.choice : null;
131
+ if (choice && ("runtime" in choice ? selectors.deviceType && selectors.deviceType !== choice.deviceType || selectors.runtime && selectors.runtime.replace(/^iOS /, "") !== choice.runtime.replace(/^iOS /, "") : selectors.systemImage && selectors.systemImage !== choice.systemImage || selectors.deviceProfile && selectors.deviceProfile !== choice.deviceProfile)) choice = null;
132
+ if (!isJsonObject(offer.capacity) || offer.declined !== null && typeof offer.declined !== "string") throw new Error("unreadable device offer");
133
+ return {
134
+ probe: {
135
+ machine,
136
+ offer: {
137
+ platform,
138
+ choice,
139
+ declined: offer.declined,
140
+ capacity: { available: typeof offer.capacity.available === "number" ? offer.capacity.available : null },
141
+ resources: {
142
+ loadPerCore: typeof resources.loadPerCore === "number" && Number.isFinite(resources.loadPerCore) && resources.loadPerCore >= 0 ? resources.loadPerCore : null,
143
+ memoryFreeBytes: typeof resources.memoryFreeBytes === "number" ? resources.memoryFreeBytes : 0,
144
+ memoryPressure: typeof resources.memoryPressure === "string" ? resources.memoryPressure : null
145
+ }
146
+ }
147
+ },
148
+ ...choice ? { target: {
149
+ host,
150
+ choice,
151
+ session: null
152
+ } } : {}
153
+ };
154
+ } catch (error) {
155
+ return { probe: {
156
+ machine,
157
+ failure: error instanceof Error ? error.message : String(error)
158
+ } };
159
+ } finally {
160
+ host?.connection.close();
161
+ }
162
+ }
163
+ function hosted(target, reason, skipped = []) {
164
+ target.selection = {
165
+ selected: "auto",
166
+ reason
167
+ };
168
+ return {
169
+ target,
170
+ placement: {
171
+ decision: "hosted",
172
+ machine: target.host.machine,
173
+ reason
174
+ },
175
+ skipped
176
+ };
177
+ }
178
+ function local(reason, skipped = []) {
179
+ return {
180
+ target: null,
181
+ placement: {
182
+ decision: "local",
183
+ reason
184
+ },
185
+ skipped
186
+ };
187
+ }
188
+ async function automaticDevicePlacement({ root, slot, platform, selectors, buildMachine, noWait }, { machines = configuredMachines, read = readHostedNative, write = writeHostedNative, resume = prepareHostedNative, peek = () => peekDeviceSlots({
189
+ platform,
190
+ project: getProject(root),
191
+ slot
192
+ }), capacity = machineCapacity, memory = readHostMemoryPressure, probe = probeHost, budget = peekBudget } = {}) {
193
+ const recorded = read(root, platform, slot)[slot];
194
+ if (recorded) {
195
+ const target = await resume(recorded.machine, selectors, recorded, platform, true);
196
+ if (target) return {
197
+ target,
198
+ placement: {
199
+ decision: "hosted",
200
+ machine: recorded.machine,
201
+ reason: recorded.reason ?? decideDevicePlacement({ sticky: { machine: recorded.machine } }).reason
202
+ },
203
+ skipped: [],
204
+ sticky: true
205
+ };
206
+ write(root, slot, null, platform);
207
+ }
208
+ const devices = peek();
209
+ if (devices.localLive) return {
210
+ ...local(decideDevicePlacement({ sticky: { local: true } }).reason),
211
+ sticky: true
212
+ };
213
+ const entries = machines();
214
+ if (entries === null) throw Object.assign(/* @__PURE__ */ new Error("hosting.machines is invalid. Run stim guide settings and correct it."), { code: "STIM_HOSTING_REFUSED" });
215
+ const preference = resolveBuildMachine(buildMachine, process.env.STIM_OFFLOAD_MACHINE, loadConfig$1()?.offload?.machine);
216
+ buildMachine = namedBuildMachine(preference) ? preference : void 0;
217
+ const load = capacity();
218
+ const here = {
219
+ loadPerCore: load.loadPerCore,
220
+ maxLoadPerCore: load.maxLoadPerCore,
221
+ memoryPressure: memory(),
222
+ devices,
223
+ budgetRefusal: await budget(root)
224
+ };
225
+ const early = decideDevicePlacement({
226
+ platform,
227
+ here,
228
+ offers: entries.map((machine) => ({
229
+ machine,
230
+ failure: "not probed"
231
+ })),
232
+ buildMachine,
233
+ noWait
234
+ });
235
+ if (early.kind === "local" && early.skipped.length === 0) return local(early.reason);
236
+ const probes = await Promise.all(entries.map((machine) => probe(machine, platform, selectors)));
237
+ const decision = decideDevicePlacement({
238
+ platform,
239
+ here,
240
+ offers: probes.map((each) => each.probe),
241
+ buildMachine,
242
+ noWait
243
+ });
244
+ if (decision.kind === "local") return local(decision.reason, decision.skipped);
245
+ const target = probes.find((each) => each.probe.machine === decision.machines[0].machine).target;
246
+ return hosted(target, decision.reason, decision.skipped);
247
+ }
248
+ function devicePlacementLine(placement, skipped) {
249
+ return `${placement.machine ?? "local"}, ${placement.reason}${placement.decision !== "hosted" && skipped.length ? "; " + skipped.map((each) => `${each.machine}: ${each.reason}`).join("; ") : ""}`;
250
+ }
251
+ //#endregion
34
252
  //#region src/engine/metro-warmup.ts
35
253
  const WARMUP_TIMEOUT_MS = 6e4;
36
254
  async function requestLocal(url, platform, prefetch) {
@@ -326,6 +544,7 @@ async function ensureOwnedAndroidDevice({ record, projectPath, slot, settingsRoo
326
544
  out(chalk.dim(`Recorded port for owned AVD ${record.avdName} is not currently ours; booting it on a freshly allocated port...`));
327
545
  return {
328
546
  ...await bootOwnedAvdOnFreshPort({
547
+ deviceSlotWait: flags.deviceSlotWait,
329
548
  avdName: record.avdName,
330
549
  metadata: record,
331
550
  projectPath,
@@ -398,6 +617,7 @@ async function ensureOwnedAndroidDevice({ record, projectPath, slot, settingsRoo
398
617
  }
399
618
  return {
400
619
  ...await bootOwnedAvdOnFreshPort({
620
+ deviceSlotWait: flags.deviceSlotWait,
401
621
  avdName: parked.name,
402
622
  projectPath,
403
623
  slot,
@@ -436,6 +656,7 @@ async function ensureOwnedAndroidDevice({ record, projectPath, slot, settingsRoo
436
656
  }
437
657
  return {
438
658
  ...await bootOwnedAvdOnFreshPort({
659
+ deviceSlotWait: flags.deviceSlotWait,
439
660
  avdName: created.avdName,
440
661
  metadata: created.created ? {
441
662
  poolConfiguration: configuration,
@@ -473,41 +694,50 @@ function liveAndroidConsolePorts() {
473
694
  const adbLive = listAdbDevices();
474
695
  return [...adbLive.emulators.map((e) => e.consolePort), ...adbLive.unhealthy.map((u) => u.consolePort).filter((p) => p != null)];
475
696
  }
476
- async function bootOwnedAvdOnFreshPort({ avdName, metadata, projectPath, slot, deviceName, out, logFile = null, alive = pidExists }) {
477
- const claim = claimAndroidConsolePort({
478
- projectPath,
479
- slot,
480
- avdName,
481
- deviceName,
482
- livePorts: liveAndroidConsolePorts(),
483
- metadata
484
- });
485
- const serial = `emulator-${claim.consolePort}`;
486
- try {
487
- reportAndroidMemoryPressure(out);
488
- const pid = bootAndroidEmulator(avdName, claim.consolePort, { logFile });
489
- out(chalk.dim(phaseLine("device", `waiting for ${serial} to finish booting`)));
490
- const result = await waitForAndroidBoot({
491
- serial,
492
- timeoutMs: ANDROID_FRESH_BOOT_TIMEOUT_MS,
493
- pid,
494
- alive,
495
- out
697
+ async function bootOwnedAvdOnFreshPort({ avdName, deviceSlotWait, metadata, projectPath, slot, deviceName, out, logFile = null, alive = pidExists }) {
698
+ return withDeviceBootAdmission({
699
+ platform: "android",
700
+ key: avdName
701
+ }, async () => {
702
+ const claim = claimAndroidConsolePort({
703
+ projectPath,
704
+ slot,
705
+ avdName,
706
+ deviceName,
707
+ livePorts: liveAndroidConsolePorts(),
708
+ metadata
496
709
  });
497
- if (result.failed) throw new AvdBootError(result.reason, result.remedy);
498
- const running = getAvdNameForSerial(serial);
499
- if (running && running !== avdName) throw new Error(`${serial} is running AVD ${running}, not this workspace's owned AVD ${avdName}; refusing to use it.`);
500
- clearAndroidBootPending(projectPath, avdName, slot);
501
- const { bootPending: _booted, ...booted } = claim;
502
- return {
503
- ...booted,
504
- serial,
505
- setup: true
506
- };
507
- } catch (error) {
508
- releaseAndroidConsolePort(projectPath, claim.consolePort, slot);
509
- throw error;
510
- }
710
+ const serial = `emulator-${claim.consolePort}`;
711
+ try {
712
+ reportAndroidMemoryPressure(out);
713
+ const pid = bootAndroidEmulator(avdName, claim.consolePort, { logFile });
714
+ out(chalk.dim(phaseLine("device", `waiting for ${serial} to finish booting`)));
715
+ const result = await waitForAndroidBoot({
716
+ serial,
717
+ timeoutMs: ANDROID_FRESH_BOOT_TIMEOUT_MS,
718
+ pid,
719
+ alive,
720
+ out
721
+ });
722
+ if (result.failed) throw new AvdBootError(result.reason, result.remedy);
723
+ const running = getAvdNameForSerial(serial);
724
+ if (running && running !== avdName) throw new Error(`${serial} is running AVD ${running}, not this workspace's owned AVD ${avdName}; refusing to use it.`);
725
+ clearAndroidBootPending(projectPath, avdName, slot);
726
+ const { bootPending: _booted, ...booted } = claim;
727
+ return {
728
+ ...booted,
729
+ serial,
730
+ setup: true
731
+ };
732
+ } catch (error) {
733
+ releaseAndroidConsolePort(projectPath, claim.consolePort, slot);
734
+ throw error;
735
+ }
736
+ }, {
737
+ ...deviceSlotWait,
738
+ root: projectPath,
739
+ out
740
+ });
511
741
  }
512
742
  function reportAndroidMemoryPressure(out) {
513
743
  const pressure = readHostMemoryPressure();
@@ -516,11 +746,15 @@ function reportAndroidMemoryPressure(out) {
516
746
  function androidBootRemedy(pressure) {
517
747
  let count = null;
518
748
  try {
519
- count = liveOwnedDeviceCount({
520
- sims: process.platform === "darwin" ? listAllIosSims({ timeoutMs: 2e3 }) : [],
521
- adbEmulators: listAdbDevices({ timeoutMs: 2e3 }).emulators,
522
- config: loadConfig$1()
749
+ const live = countLiveOwnedDevices({
750
+ sims: () => process.platform === "darwin" ? listAllIosSims({ timeoutMs: 2e3 }) : [],
751
+ adb: () => ({
752
+ ...listAdbDevices({ timeoutMs: 2e3 }),
753
+ unhealthy: []
754
+ }),
755
+ booting: []
523
756
  });
757
+ count = typeof live === "number" ? live : null;
524
758
  } catch {}
525
759
  const observation = pressure === "warning" || pressure === "critical" ? `macOS reports ${pressure} host memory pressure. ` : "";
526
760
  const devices = count ? `${count} Stim-owned ${count === 1 ? "device is" : "devices are"} running. ` : "";
@@ -558,7 +792,7 @@ async function waitForAndroidBoot({ serial, timeoutMs, pid = null, alive = pidEx
558
792
  remedy: exited ? "Inspect the emulator log for the process exit, fix what it reports, then run `stim android` again." : androidBootRemedy(pressure)
559
793
  };
560
794
  }
561
- async function ensureAndroidBooted({ device, projectPath, slot, timeoutMs, out, logFile = null, alive = pidExists }) {
795
+ async function ensureAndroidBooted({ device, deviceSlotWait, projectPath, slot, timeoutMs, out, logFile = null, alive = pidExists }) {
562
796
  if (!device?.avdName || !projectPath) return {
563
797
  failed: true,
564
798
  reason: "No owned Android emulator is recorded for this project."
@@ -596,36 +830,57 @@ async function ensureAndroidBooted({ device, projectPath, slot, timeoutMs, out,
596
830
  timeoutMs,
597
831
  out
598
832
  }));
599
- const claim = claimAndroidConsolePort({
600
- projectPath,
601
- slot,
602
- avdName: device.avdName,
603
- deviceName: device.deviceName,
604
- livePorts: liveAndroidConsolePorts(),
605
- metadata: device
606
- });
607
- const serial = `emulator-${claim.consolePort}`;
608
- reportAndroidMemoryPressure(out);
609
- out(chalk.dim(phaseLine("device", `booting ${device.avdName} as ${serial}`)));
610
- let pid = null;
833
+ const boot = async () => {
834
+ const claim = claimAndroidConsolePort({
835
+ projectPath,
836
+ slot,
837
+ avdName,
838
+ deviceName: device.deviceName,
839
+ livePorts: liveAndroidConsolePorts(),
840
+ metadata: device
841
+ });
842
+ const serial = `emulator-${claim.consolePort}`;
843
+ reportAndroidMemoryPressure(out);
844
+ out(chalk.dim(phaseLine("device", `booting ${avdName} as ${serial}`)));
845
+ let pid = null;
846
+ try {
847
+ pid = bootAndroidEmulator(avdName, claim.consolePort, { logFile });
848
+ } catch (e) {
849
+ releaseAndroidConsolePort(projectPath, claim.consolePort, slot);
850
+ return {
851
+ failed: true,
852
+ reason: `Could not start emulator for AVD ${avdName}: ${e?.message || e}`
853
+ };
854
+ }
855
+ const result = await waitForAndroidBoot({
856
+ serial,
857
+ timeoutMs,
858
+ pid,
859
+ alive,
860
+ out
861
+ });
862
+ if (result.failed) releaseAndroidConsolePort(projectPath, claim.consolePort, slot);
863
+ return booted(result);
864
+ };
611
865
  try {
612
- pid = bootAndroidEmulator(device.avdName, claim.consolePort, { logFile });
613
- } catch (e) {
614
- releaseAndroidConsolePort(projectPath, claim.consolePort, slot);
866
+ return await withDeviceBootAdmission({
867
+ platform: "android",
868
+ key: avdName
869
+ }, boot, {
870
+ ...deviceSlotWait,
871
+ root: projectPath,
872
+ out
873
+ });
874
+ } catch (error) {
875
+ const refusal = error instanceof DeviceAdmissionRefusal ? error : (0, ownership_claim_exports.claimFailure)(error, "stim android");
876
+ if (!refusal) throw error;
615
877
  return {
616
878
  failed: true,
617
- reason: `Could not start emulator for AVD ${device.avdName}: ${e?.message || e}`
879
+ code: refusal.code,
880
+ reason: refusal.message,
881
+ remedy: refusal.remedy
618
882
  };
619
883
  }
620
- const result = await waitForAndroidBoot({
621
- serial,
622
- timeoutMs,
623
- pid,
624
- alive,
625
- out
626
- });
627
- if (result.failed) releaseAndroidConsolePort(projectPath, claim.consolePort, slot);
628
- return booted(result);
629
884
  }
630
885
  //#endregion
631
886
  //#region src/engine/device.ts
@@ -669,9 +924,11 @@ async function ensureOwnedDevice({ platform, project, projectPath, slot, setting
669
924
  });
670
925
  }
671
926
  const BOOT_POLL_MS = 500;
672
- async function ensureBooted({ platform, device, simulatorApp, timeoutMs, pollMs = BOOT_POLL_MS, out = () => {}, logFile = null, alive = pidExists, projectPath, slot } = {}) {
927
+ async function ensureBooted({ platform, device, simulatorApp, timeoutMs, pollMs = BOOT_POLL_MS, out = () => {}, logFile = null, alive = pidExists, projectPath, slot, deviceSlotWait }) {
673
928
  if (platform === "ios") return ensureIosBooted({
674
929
  device,
930
+ projectPath,
931
+ deviceSlotWait,
675
932
  simulatorApp,
676
933
  timeoutMs: timeoutMs ?? 6e5,
677
934
  pollMs,
@@ -679,6 +936,7 @@ async function ensureBooted({ platform, device, simulatorApp, timeoutMs, pollMs
679
936
  });
680
937
  if (platform === "android") return ensureAndroidBooted({
681
938
  device,
939
+ deviceSlotWait,
682
940
  projectPath,
683
941
  slot,
684
942
  timeoutMs: timeoutMs ?? ANDROID_BOOT_TIMEOUT_MS,
@@ -1467,7 +1725,7 @@ async function planCachedBuild(lookup, deps) {
1467
1725
  function planPayload({ platform, slot, projectKey }, found) {
1468
1726
  const { refusal, missReason, ...rest } = found;
1469
1727
  const outcome = refusal ? null : found.cacheHit ? "hit" : "cold";
1470
- const estimate = outcome ? estimateBuild(readStats().record?.history?.[projectKey], platform, outcome) : {
1728
+ const estimate = outcome ? estimateBuild(readStats$1().record?.history?.[projectKey], platform, outcome) : {
1471
1729
  expectedMs: null,
1472
1730
  basis: 0
1473
1731
  };
@@ -1524,4 +1782,4 @@ function planFlagRefusal(flag) {
1524
1782
  };
1525
1783
  }
1526
1784
  //#endregion
1527
- export { AvdRecoveryError as _, refusePlan as a, skippedMissReason as c, refreshUpdatesManifestFile as d, swapJsBundle as f, AvdBootError as g, ensureOwnedDevice as h, printPlan as i, UPDATES_MANIFEST_NAME as l, ensureBooted as m, planFlagRefusal as n, explainBuildMiss as o, didSetUpDevice as p, planPayload as r, fingerprintErrorMissReason as s, planCachedBuild as t, detectEntryFile as u, warmMetro as v };
1785
+ export { AvdRecoveryError as _, refusePlan as a, devicePlacementLine as b, skippedMissReason as c, refreshUpdatesManifestFile as d, swapJsBundle as f, AvdBootError as g, ensureOwnedDevice as h, printPlan as i, UPDATES_MANIFEST_NAME as l, ensureBooted as m, planFlagRefusal as n, explainBuildMiss as o, didSetUpDevice as p, planPayload as r, fingerprintErrorMissReason as s, planCachedBuild as t, detectEntryFile as u, warmMetro as v, automaticDevicePlacement as y };
@@ -1,6 +1,6 @@
1
1
  import { n as ownership_claim_exports } from "./process-identity-BmRm1qV0.mjs";
2
+ import { a as formatElapsed } from "./command-output-CDAIFD-8.mjs";
2
3
  import { n as declareSpawnsOn, o as stopDeclaringSpawnsOn } from "./spawn-claims-D0XfSe8u.mjs";
3
- import { a as formatElapsed } from "./command-output-CliHHn7a.mjs";
4
4
  import { readdirSync, statSync } from "node:fs";
5
5
  import { join } from "node:path";
6
6
  import { buildSlotsDir, tryAcquireBuildSlotClaim } from "@stim-cli/core/state";
@@ -53,34 +53,49 @@ function tryAcquireBuildSlot({ max, root = null, logFile = null }) {
53
53
  function slotWaitingLine({ max, elapsedMs }) {
54
54
  return `${"build".padEnd(11)} waiting for a build slot (all ${max} in use, ${formatElapsed(elapsedMs)} elapsed) -- stim guide lifecycle concurrency`;
55
55
  }
56
- async function acquireBuildSlot({ max, root = null, logFile = null, now = Date.now, out = () => {}, sleep = sleepAsync, intervalMs = SLOT_POLL_MS, progressMs = SLOT_PROGRESS_MS, ceilingMs = SLOT_CEILING_MS }) {
56
+ async function acquireBuildSlot({ max, root = null, logFile = null, now = Date.now, out = () => {}, waitingFor = () => {}, sleep = sleepAsync, intervalMs = SLOT_POLL_MS, progressMs = SLOT_PROGRESS_MS, ceilingMs = SLOT_CEILING_MS }) {
57
57
  if (!max || max <= 0) return {
58
58
  acquired: true,
59
59
  unlimited: true
60
60
  };
61
61
  const started = now();
62
62
  let lastProgress = started;
63
- for (;;) {
64
- const got = tryAcquireBuildSlot({
65
- max,
66
- root,
67
- logFile
68
- });
69
- if (got) return got;
70
- const elapsed = now() - started;
71
- if (elapsed >= ceilingMs) {
72
- const err = /* @__PURE__ */ new Error(`Waited ${formatElapsed(elapsed)} for one of ${max} build slots, and every slot is held by a process that is still running, or by a holder Stim cannot identify. Slots live under ` + buildSlotsDir() + "; remove a slot directory whose builder is not really building, or raise concurrency.maxBuilds.");
73
- err.code = "STIM_BUILD_SLOT_TIMEOUT";
74
- throw err;
75
- }
76
- if (now() - lastProgress >= progressMs) {
77
- lastProgress = now();
78
- out(slotWaitingLine({
63
+ let waited = false;
64
+ try {
65
+ for (;;) {
66
+ const got = tryAcquireBuildSlot({
67
+ max,
68
+ root,
69
+ logFile
70
+ });
71
+ if (got) return {
72
+ ...got,
73
+ slotWaitMs: waited ? Math.max(0, Math.round(now() - started)) : 0
74
+ };
75
+ waited = true;
76
+ waitingFor({
77
+ kind: "build-slot",
78
+ inUse: max,
79
79
  max,
80
- elapsedMs: elapsed
81
- }));
80
+ since: new Date(started).toISOString()
81
+ });
82
+ const elapsed = now() - started;
83
+ if (elapsed >= ceilingMs) {
84
+ const err = /* @__PURE__ */ new Error(`Waited ${formatElapsed(elapsed)} for one of ${max} build slots, and every slot is held by a process that is still running, or by a holder Stim cannot identify. Slots live under ` + buildSlotsDir() + "; remove a slot directory whose builder is not really building, or raise concurrency.maxBuilds.");
85
+ err.code = "STIM_BUILD_SLOT_TIMEOUT";
86
+ throw err;
87
+ }
88
+ if (now() - lastProgress >= progressMs) {
89
+ lastProgress = now();
90
+ out(slotWaitingLine({
91
+ max,
92
+ elapsedMs: elapsed
93
+ }));
94
+ }
95
+ await sleep(intervalMs);
82
96
  }
83
- await sleep(intervalMs);
97
+ } finally {
98
+ waitingFor(null);
84
99
  }
85
100
  }
86
101
  function releaseBuildSlot(handle) {
@@ -3,23 +3,23 @@ import { readFileSync } from "node:fs";
3
3
  import { Command } from "commander";
4
4
  //#region bin/cli.ts
5
5
  const commands = /* @__PURE__ */ new Map([
6
- ["doctor", () => import("./doctor-BcE5xsBn.mjs")],
7
- ["worktree", () => import("./worktree-BT_uULFx.mjs")],
8
- ["start", () => import("./start-CPO4wRg7.mjs")],
9
- ["ports", () => import("./ports-DddwttKM.mjs")],
10
- ["stop", () => import("./stop-CceYh1yG.mjs")],
11
- ["ios", () => import("./ios-Chob8ny7.mjs")],
12
- ["android", () => import("./android-Cu_L4RpL.mjs")],
13
- ["web", () => import("./web-k6w8-HnE.mjs")],
14
- ["macos", () => import("./macos-BwtbUtTN.mjs")],
15
- ["reload", () => import("./reload-ChPiwvot.mjs")],
16
- ["device", () => import("./device-BZ1IyS5A.mjs")],
17
- ["logs", () => import("./logs-h9h6MUvE.mjs")],
18
- ["settings", () => import("./settings-DbL6uhH8.mjs")],
19
- ["status", () => import("./status-DYXxNOT0.mjs")],
20
- ["stats", () => import("./stats-DBLLFmKd.mjs")],
21
- ["gc", () => import("./gc-BuL1Genr.mjs")],
22
- ["guide", () => import("./guide-cCSGRFd3.mjs")]
6
+ ["doctor", () => import("./doctor-Cnk9d7Hm.mjs")],
7
+ ["worktree", () => import("./worktree-Dc98cE6_.mjs")],
8
+ ["start", () => import("./start-BopZcr4n.mjs")],
9
+ ["ports", () => import("./ports-4nYqxPWC.mjs")],
10
+ ["stop", () => import("./stop-BCwvvs7F.mjs")],
11
+ ["ios", () => import("./ios-DE6duQoz.mjs")],
12
+ ["android", () => import("./android--GorydsK.mjs")],
13
+ ["web", () => import("./web-exQtGkoc.mjs")],
14
+ ["macos", () => import("./macos-32po3kUq.mjs")],
15
+ ["reload", () => import("./reload-qZei6iWn.mjs")],
16
+ ["device", () => import("./device-C8IOoJPv.mjs")],
17
+ ["logs", () => import("./logs-DUoKlDoS.mjs")],
18
+ ["settings", () => import("./settings-SR9OmW9h.mjs")],
19
+ ["status", () => import("./status-dXX2JX1C.mjs")],
20
+ ["stats", () => import("./stats-Bq4fJOdB.mjs")],
21
+ ["gc", () => import("./gc-DxCL2A46.mjs")],
22
+ ["guide", () => import("./guide-DjrTQGik.mjs")]
23
23
  ]);
24
24
  const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf-8"));
25
25
  const program = new Command();
@@ -33,6 +33,12 @@ try {
33
33
  const modules = await Promise.all([...commands.values()].map((load) => load()));
34
34
  for (const module of modules) module.default(program, pkg.version);
35
35
  }
36
+ program.hook("preAction", async (_command, action) => {
37
+ try {
38
+ const { triggerMaintenance } = await import("./trigger-CXts6qt1.mjs");
39
+ triggerMaintenance(action.name());
40
+ } catch {}
41
+ });
36
42
  await program.parseAsync();
37
43
  } catch (err) {
38
44
  const code = err?.code;