stim 1.27.0 → 1.29.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 (154) hide show
  1. package/dist/{acquire-artifact-BKN2Yuhg.mjs → acquire-artifact-C2DKfenN.mjs} +1 -1
  2. package/dist/{act-ROviTF_t.mjs → act-CrTcskiE.mjs} +502 -34
  3. package/dist/{activity-Cd-ysFes.mjs → activity-BR5oHSO5.mjs} +6 -5
  4. package/dist/{agent-device-usage-output-C5nCKWp9.mjs → agent-device-usage-output-W0x4SoC7.mjs} +4 -4
  5. package/dist/{android-DriNoAVy.mjs → android-BHKgeOuq.mjs} +5 -5
  6. package/dist/{android--HBgpvpW.mjs → android-BPmCmyAy.mjs} +5 -5
  7. package/dist/{android-Deb9X3_Y.mjs → android-CLU_MMnb.mjs} +1 -1
  8. package/dist/{android-cas-D5e6ecUg.mjs → android-cas-BH6K12__.mjs} +4 -4
  9. package/dist/{android-Cd6w7_qw.mjs → android-l616dRP1.mjs} +42 -39
  10. package/dist/{android-project-DUGMbq28.d.mts → android-project-CCEQyYJK.d.mts} +6 -2
  11. package/dist/api.d.mts +8 -5
  12. package/dist/api.mjs +27 -27
  13. package/dist/{apk-signed-tail-BHQHx3Rm.mjs → apk-signed-tail-B-wKoF_8.mjs} +2 -2
  14. package/dist/{artifact-0AGJYB9O.mjs → artifact-DHuFYfhD.mjs} +12 -12
  15. package/dist/{artifact-faemIZv5.mjs → artifact-Dm9ScLrh.mjs} +12 -12
  16. package/dist/{auto-placement-z-H5pJza.mjs → auto-placement-C8sJsyMN.mjs} +8 -8
  17. package/dist/{browser-web-slD8AvLp.mjs → browser-web-_tQTbQUs.mjs} +12 -12
  18. package/dist/{build-hS0QyFlQ.mjs → build-DtCU9Kim.mjs} +23 -23
  19. package/dist/{build-cache-gBJ-WKzA.mjs → build-cache-BGwTsIEa.mjs} +4 -4
  20. package/dist/{build-plan-CBqYbzYM.mjs → build-plan-CWyjMPKo.mjs} +6 -6
  21. package/dist/{cache-manifest-DEjqGGat.mjs → cache-manifest-evF_zJ9U.mjs} +1 -1
  22. package/dist/cache-manifest.mjs +1 -1
  23. package/dist/{ccache-BWTze1Ls.mjs → ccache-DsDkdP72.mjs} +19 -18
  24. package/dist/{cli-ftXDLto1.mjs → cli-3fjOg-Oc.mjs} +19 -19
  25. package/dist/cli.mjs +1 -1
  26. package/dist/{client-xsdxrhWA.mjs → client-BTUH8TF1.mjs} +41 -8
  27. package/dist/collector-run.mjs +5 -5
  28. package/dist/{command-output-Dj-WT4BP.mjs → command-output-DjCsmzLZ.mjs} +1 -1
  29. package/dist/{config-Cn94Jc0g.mjs → config-AbUjIBOQ.mjs} +121 -19
  30. package/dist/{dependencies-BGokZ-Ng.mjs → dependencies-CHNqX6Pr.mjs} +38 -38
  31. package/dist/{dependency-state-C3OtgG94.mjs → dependency-state-CPKAKCZ8.mjs} +1 -1
  32. package/dist/{dev-client-BMca82xo.mjs → dev-client-CROrxj1x.mjs} +2 -2
  33. package/dist/{device-tM41-TUq.mjs → device-Bmm5qjM-.mjs} +14 -14
  34. package/dist/device-BvpasThQ.mjs +297 -0
  35. package/dist/{device-capacity-6LP7QsaA.mjs → device-capacity-B2ChA-bz.mjs} +86 -18
  36. package/dist/device-host-worker.mjs +15 -13
  37. package/dist/{device-ios-534c14fz.mjs → device-ios-CYzivd31.mjs} +9 -9
  38. package/dist/{device-lease-run-DwsGfjUN.mjs → device-lease-run-qRq_2W2V.mjs} +4 -4
  39. package/dist/{device-pool-BnQqvs6o.mjs → device-pool-CrmiE4_x.mjs} +3 -3
  40. package/dist/{device-BJm0irrq.mjs → device-provision-CK2XiEp1.mjs} +11 -302
  41. package/dist/{doctor-Dbx1ZpD2.mjs → doctor-BO_kaFWX.mjs} +1 -1
  42. package/dist/{doctor-DyqE9n1J.mjs → doctor-Cp4-HN1f.mjs} +20 -15
  43. package/dist/{doctor-F-3VUUyN.mjs → doctor-Dr3C1iXV.mjs} +19 -19
  44. package/dist/doctor-fingerprint.mjs +1 -1
  45. package/dist/{doctor-watchman-CxDbyrrt.mjs → doctor-watchman-CJr2aXXv.mjs} +4 -4
  46. package/dist/{eas-build-CjNSWmjo.mjs → eas-build-D-9HmGZS.mjs} +2 -2
  47. package/dist/{eas-fallback-9fqb46kW.mjs → eas-fallback-Cemt2GVL.mjs} +4 -4
  48. package/dist/{eas-session-ledger-DrP4OuuM.mjs → eas-session-ledger-B0KLZx5m.mjs} +36 -7
  49. package/dist/{error-diagnostics-snej9k4j.mjs → error-diagnostics-CV_1RoEH.mjs} +8 -8
  50. package/dist/{gc-CGZ1Yg88.mjs → gc-CKHqL4aS.mjs} +81 -18
  51. package/dist/{gc-BRwAY3qT.mjs → gc-DjXwQXHK.mjs} +2 -2
  52. package/dist/{guide-_rfEh_zM.mjs → guide-CeRlWVsS.mjs} +230 -62
  53. package/dist/{hosted-android-BMKzUNnY.mjs → hosted-android-DI_vvv7U.mjs} +5 -6
  54. package/dist/{hosted-client-D7Pk_Oo1.mjs → hosted-client-CgoxdU9j.mjs} +44 -17
  55. package/dist/{hosted-logs-B1gqQuva.mjs → hosted-logs-C-4INmkn.mjs} +17 -8
  56. package/dist/{hosted-logs-sync-Dg3qrZmV.mjs → hosted-logs-sync-Bf5s02fi.mjs} +5 -5
  57. package/dist/{hosted-macos-B9w4H8hc.mjs → hosted-macos-Doh85YUM.mjs} +5 -5
  58. package/dist/{hosted-native-CdgaiuyP.mjs → hosted-native-DvZENAL-.mjs} +11 -8
  59. package/dist/{idle-shutdown-Cbvo5KEh.mjs → idle-shutdown-BM37gMfU.mjs} +7 -7
  60. package/dist/{ios-Bk4mWbtS.mjs → ios-CNDH6fNF.mjs} +8 -8
  61. package/dist/{ios-E26qSpkb.mjs → ios-CoMCWfub.mjs} +4 -4
  62. package/dist/{ios-B5HlVMhQ.mjs → ios-UM_dManb.mjs} +31 -29
  63. package/dist/{ios-device-C_QkIf1Z.mjs → ios-device-L1-pota_.mjs} +1 -1
  64. package/dist/{ios-project-D8Jcy_br.mjs → ios-project-DJYptOCh.mjs} +7 -7
  65. package/dist/{ios-state-BZ03NFgF.mjs → ios-state-Ce_dystM.mjs} +1 -1
  66. package/dist/{js-swap-Dg2a945i.mjs → js-swap-BthE4kZC.mjs} +4 -4
  67. package/dist/{launch-CRyBeNsA.mjs → launch-D798aSm8.mjs} +19 -19
  68. package/dist/{lightened-BCsdAGj_.mjs → lightened-vSxkstg6.mjs} +4 -4
  69. package/dist/{logs-aJQb-RDg.mjs → logs-BND8MiBq.mjs} +9 -8
  70. package/dist/{logs-CC_-ZzEE.mjs → logs-CilPftm3.mjs} +5 -5
  71. package/dist/{macos-BGmTpnv9.mjs → macos-BOQrKYeJ.mjs} +1 -1
  72. package/dist/{macos-DlCBhMw5.mjs → macos-CxUi9xcE.mjs} +20 -20
  73. package/dist/macos-run.mjs +1 -1
  74. package/dist/maintenance-run.mjs +13 -9
  75. package/dist/{metro-CvyD-xwe.mjs → metro-BMQHczcf.mjs} +2 -2
  76. package/dist/{metro-gateway-BnHdqMO_.mjs → metro-request-BtF7ox53.mjs} +25 -46
  77. package/dist/{named-ports-BCyMWA6b.mjs → named-ports-DcktE2Pw.mjs} +2 -2
  78. package/dist/{native-android-DlpxCyOP.mjs → native-android-BkhrCjdX.mjs} +16 -16
  79. package/dist/{native-gradle-artifact-inputs-C13qT7R_.mjs → native-gradle-artifact-inputs-Cw4P6Gyr.mjs} +3 -3
  80. package/dist/{native-runtime-DqaIBO5w.mjs → native-runtime-DyH2gkXK.mjs} +6 -6
  81. package/dist/{native-xcode-inputs-B1W8SrxB.mjs → native-xcode-inputs-DV8jr3kJ.mjs} +2 -2
  82. package/dist/{native-xcode-ios-QPP0Rm68.mjs → native-xcode-ios-DdpPG2iT.mjs} +15 -15
  83. package/dist/{ndjson-Dy2W4vRa.mjs → ndjson-CrjIsGzw.mjs} +2 -2
  84. package/dist/offload-worker.d.mts +1 -1
  85. package/dist/offload-worker.mjs +22 -22
  86. package/dist/{ownership-Bx-0cku-.mjs → ownership-Dtz9G5Gl.mjs} +2 -2
  87. package/dist/{phone-reverses-C9yAaKEv.mjs → phone-reverses-DZRad8HT.mjs} +4 -4
  88. package/dist/{placement-log-BXUbiZM6.mjs → placement-log-CoN6xrjY.mjs} +1 -1
  89. package/dist/{plan-placement-UxnQjv5Z.mjs → plan-placement-7sHuqR-r.mjs} +6 -6
  90. package/dist/{ports-uUrghqWN.mjs → ports-VQSAPZhl.mjs} +3 -3
  91. package/dist/{prebuild-BDpToDgR.mjs → prebuild-Bqp0LFGN.mjs} +9 -9
  92. package/dist/{project-DYj-HVx6.mjs → project-D1n0K4yg.mjs} +15 -15
  93. package/dist/{react-native-android-DDBccgJw.mjs → react-native-android-FQ-yX3Mv.mjs} +22 -22
  94. package/dist/{react-native-build-DaK2IFyP.mjs → react-native-build-D58bIqV9.mjs} +7 -7
  95. package/dist/{react-native-doctor-B-Mw6DcR.mjs → react-native-doctor-AkUH2aca.mjs} +5 -5
  96. package/dist/{react-native-ios-DjYwUF9U.mjs → react-native-ios-Dmsv8j7T.mjs} +14 -14
  97. package/dist/{recordings-C20iUZfp.mjs → recordings-CHMaF73C.mjs} +1 -1
  98. package/dist/{reload-fUdkH2_p.mjs → reload-BV0nD-k9.mjs} +14 -14
  99. package/dist/{reload-DJBeF_rC.mjs → reload-CDKEzzaf.mjs} +1 -1
  100. package/dist/{remote-y6n99WMW.mjs → remote-C8xyslUa.mjs} +9 -9
  101. package/dist/{result-CTqBw2Te.mjs → result-9fqcJ90W.mjs} +16 -16
  102. package/dist/{run-DaCeNKRm.mjs → run-BQAmrohh.mjs} +2 -2
  103. package/dist/{selection-Bs8phwhN.mjs → selection-D1ZDzHFX.mjs} +105 -62
  104. package/dist/{server-bare-BOh-iZEj.mjs → server-bare-CEQdB_F4.mjs} +37 -1
  105. package/dist/{server-command-8CuA51Cv.mjs → server-command-RlS-Kl4a.mjs} +5 -5
  106. package/dist/server-expo-BVV9qy31.mjs +2 -0
  107. package/dist/{server-expo-ABQs2XFA.mjs → server-expo-bB1VV2k-.mjs} +8 -5
  108. package/dist/{settings-CMHhQNwI.mjs → settings-BcVCcAT5.mjs} +2 -2
  109. package/dist/{settings-BDVI8Ivc.mjs → settings-D776ygML.mjs} +47 -3
  110. package/dist/{settings-Djl-mpjX.mjs → settings-D9LtQaUw.mjs} +2 -1
  111. package/dist/{settings-payload-cm60Zhib.mjs → settings-payload-_aatELz2.mjs} +4 -4
  112. package/dist/{settings-write-BdNRLlBn.mjs → settings-write-CkTVGQ4O.mjs} +5 -5
  113. package/dist/settings.schema.json +58 -9
  114. package/dist/{simslim-DCQwzoPy.mjs → simslim-CbpHERcv.mjs} +1 -1
  115. package/dist/{slot-launch-0l7RqSpr.mjs → slot-launch-Dbgfh8gc.mjs} +6 -6
  116. package/dist/{stage-F4cAfJGi.mjs → stage-BuHuRj1y.mjs} +1 -1
  117. package/dist/{start-DcqnF9p8.mjs → start-Cr-c3Xhq.mjs} +1 -1
  118. package/dist/{start-DrPwER_2.mjs → start-Dlv4XXTP.mjs} +11 -11
  119. package/dist/{state-CZxLyetD.mjs → state-C2AUjjhg.mjs} +1 -1
  120. package/dist/{state-BX1y5syV.mjs → state-D5ZxwCGJ.mjs} +2 -2
  121. package/dist/{stats-GxuCbles.mjs → stats-D5c-uIBp.mjs} +4 -4
  122. package/dist/{status-7ULDEJ1e.mjs → status-CK7ZoEhV.mjs} +2 -2
  123. package/dist/{status-BfnK9pUd.mjs → status-D6PBK6_-.mjs} +49 -26
  124. package/dist/{status-Bj7ZYvvc.mjs → status-DbQv--Yb.mjs} +1 -1
  125. package/dist/{status-watch-BfabQn03.mjs → status-watch-okrpea4i.mjs} +17 -6
  126. package/dist/status-worker.mjs +1 -1
  127. package/dist/{stim-desktop-CbWo21kb.mjs → stim-desktop-CYXLFpFS.mjs} +1 -1
  128. package/dist/{stop-x1Yu3He4.mjs → stop-CHtAiv79.mjs} +2 -2
  129. package/dist/{stop-D-EAr25B.mjs → stop-p9uSKuGE.mjs} +20 -19
  130. package/dist/{stop-CH79S3IZ.mjs → stop-uRQ-zYcl.mjs} +9 -5
  131. package/dist/supervisor-run.d.mts +1 -0
  132. package/dist/supervisor-run.mjs +243 -65
  133. package/dist/{support-Be0UeVZU.mjs → support-BMpQIug0.mjs} +11 -10
  134. package/dist/{support-CNg_YsOk.mjs → support-PFyTkrnr.mjs} +4 -4
  135. package/dist/{swiftpm-artifact-inputs-uPqYvGBv.mjs → swiftpm-artifact-inputs-Cddq6jN2.mjs} +1 -1
  136. package/dist/{swiftpm-macos-Ch4YjY_A.mjs → swiftpm-macos-BSj9iZlI.mjs} +8 -8
  137. package/dist/{toolchain-JYAOquPg.mjs → toolchain-B-mzR9Rh.mjs} +1 -1
  138. package/dist/{toolchain-CbCG6C5Z.mjs → toolchain-CLGnEUgT.mjs} +4 -4
  139. package/dist/{trigger-DMou-X7j.mjs → trigger-CfnOfpDd.mjs} +1 -1
  140. package/dist/trigger-DBZ-GBrl.mjs +2 -0
  141. package/dist/{warm-progress-C0RsbngN.mjs → warm-progress--KkkBBaq.mjs} +3 -3
  142. package/dist/web-BxSo493O.mjs +2 -0
  143. package/dist/web-run.mjs +5 -5
  144. package/dist/{web-Cq_AQ4DK.mjs → web-xlVssjQf.mjs} +8 -8
  145. package/dist/{workspace-state-BjqdjnJR.mjs → workspace-state-DGwkPXwq.mjs} +2 -2
  146. package/dist/{workspaces-DFbj07jp.mjs → workspaces-CvSYD_78.mjs} +106 -26
  147. package/dist/{worktree-COAJ3-OD.mjs → worktree-CaQBf6kh.mjs} +26 -47
  148. package/dist/worktree-DTOIohFe.mjs +2 -0
  149. package/package.json +7 -7
  150. package/shim/bundle-response.cjs +37 -0
  151. package/dist/server-expo-Ba_Y21vB.mjs +0 -2
  152. package/dist/trigger-qp2ji5wE.mjs +0 -2
  153. package/dist/web-Je6abcpz.mjs +0 -2
  154. package/dist/worktree-JGUd_53L.mjs +0 -2
@@ -1,10 +1,10 @@
1
1
  import { T as trackReportResource } from "./exec-aULwtNly.mjs";
2
- import { i as formatDuration, l as phaseLine, m as shortHash, u as plural } from "./command-output-Dj-WT4BP.mjs";
3
- import { $t as makeTemporaryDirectory, tn as removeTemporaryEntryAsync } from "./settings-BDVI8Ivc.mjs";
4
- import { jn as easAuthNote } from "./ccache-BWTze1Ls.mjs";
5
- import { W as estimateBuild, nt as readStats } from "./workspaces-DFbj07jp.mjs";
6
- import { s as entryDir, t as artifactIn } from "./build-cache-gBJ-WKzA.mjs";
7
- import { l as predictBuildMiss, o as staleNativeDirRefusal } from "./prebuild-BDpToDgR.mjs";
2
+ import { i as formatDuration, l as phaseLine, m as shortHash, u as plural } from "./command-output-DjCsmzLZ.mjs";
3
+ import { an as makeTemporaryDirectory, sn as removeTemporaryEntryAsync } from "./settings-D776ygML.mjs";
4
+ import { jn as easAuthNote } from "./ccache-DsDkdP72.mjs";
5
+ import { at as readStats, q as estimateBuild } from "./workspaces-CvSYD_78.mjs";
6
+ import { s as entryDir, t as artifactIn } from "./build-cache-BGwTsIEa.mjs";
7
+ import { l as predictBuildMiss, o as staleNativeDirRefusal } from "./prebuild-Bqp0LFGN.mjs";
8
8
  import chalk from "chalk";
9
9
  import { PROVIDER_LOAD_TIMEOUT_ENV, PROVIDER_LOAD_TIMEOUT_MS, buildResolveTimeoutMs, resolveTieredBuild, timeoutFromEnv } from "@stim-cli/cache";
10
10
  //#region src/commands/build-plan.ts
@@ -1,4 +1,4 @@
1
- import { d as getConfigDir } from "./config-Cn94Jc0g.mjs";
1
+ import { d as getConfigDir } from "./config-AbUjIBOQ.mjs";
2
2
  import { cacheDirRefusal, readCacheManifest, updateCacheManifest } from "@stim-cli/core";
3
3
  import { join, resolve } from "path";
4
4
  import { existsSync } from "fs";
@@ -1,2 +1,2 @@
1
- import { a as unregister, i as registeredCaches, n as readManifest, r as register, t as manifestPath } from "./cache-manifest-DEjqGGat.mjs";
1
+ import { a as unregister, i as registeredCaches, n as readManifest, r as register, t as manifestPath } from "./cache-manifest-evF_zJ9U.mjs";
2
2
  export { manifestPath, readManifest, register, registeredCaches, unregister };
@@ -1,26 +1,26 @@
1
1
  import { D as ownership_claim_exports, E as debugLog, S as process_identity_exports, b as operationState, c as throwIfCancelled, d as workspaceProcessLockError, l as WorkspaceProcessCleanupError, n as isTimeoutError, o as cancellableSleep, s as cancelledError, t as getExecutor, u as withWorkspaceProcessLock, y as isTermination } from "./exec-aULwtNly.mjs";
2
- import { D as workspaceId$1, E as workspaceDir, M as withDirLock$1, b as sharedCcache$1, d as writeWorkspaceState, j as workspaceStateFile$1, k as workspaceLogsDir$1, l as withWorkspaceStateLockAsync, r as readWorkspaceState$1, t as clearWorkspaceStateKey, y as sharedBuildCache } from "./workspace-state-BjqdjnJR.mjs";
3
- import { F as isOnMountedVolume, G as projectDeviceSlots, H as deviceSlotPlatforms, L as measuredDirectorySizeAsync, M as directorySize, N as directorySizeAsync, O as withConfigLockAsync$1, P as formatBytes, R as volumeRootFor, _ as loadConfig$1, a as clearDevice, d as getConfigDir, o as clearSupervisor, p as getProject } from "./config-Cn94Jc0g.mjs";
2
+ import { D as workspaceId$1, E as workspaceDir, M as withDirLock$1, b as sharedCcache$1, d as writeWorkspaceState, j as workspaceStateFile$1, k as workspaceLogsDir$1, l as withWorkspaceStateLockAsync, r as readWorkspaceState$1, t as clearWorkspaceStateKey, y as sharedBuildCache } from "./workspace-state-DGwkPXwq.mjs";
3
+ import { F as isOnMountedVolume, G as projectDeviceSlots, H as deviceSlotPlatforms, L as measuredDirectorySizeAsync, M as directorySize, N as directorySizeAsync, O as withConfigLockAsync$1, P as formatBytes, R as volumeRootFor, _ as loadConfig$1, a as clearDevice, d as getConfigDir, o as clearSupervisor, p as getProject } from "./config-AbUjIBOQ.mjs";
4
4
  import { n as stripAnsi } from "./process-output-Fpl8PYi8.mjs";
5
- import { a as formatElapsed, l as phaseLine, o as formatLongDuration, s as isAppLaunchError, u as plural } from "./command-output-Dj-WT4BP.mjs";
5
+ import { a as formatElapsed, l as phaseLine, o as formatLongDuration, s as isAppLaunchError, u as plural } from "./command-output-DjCsmzLZ.mjs";
6
6
  import { a as killOnAbort, n as runOperationFileQuiet, r as childEnvironment, t as runOperationFile } from "./operation-exec-Dp4FuIGR.mjs";
7
- import { Z as resolveProjectSettingsAsync, d as androidLayoutSetting, ft as planMetroReach, m as defaultAndroidLayout } from "./settings-BDVI8Ivc.mjs";
8
- import { C as resolvePackageJson, M as readMetroTunnel, N as readPidFile, P as readRemoteSession, i as findProjectRoot$1, j as clearWorkspaceSupervisor, k as clearRemoteSession, s as ownedDeviceLabel, v as detectIsExpo } from "./project-DYj-HVx6.mjs";
9
- import { F as listAvdsAsync, G as ownedAvdSerialResolverAsync, I as listInstalledSystemImages, J as parseAvdSystemImage, V as ownedAvdDirectory, d as androidToolPath, dt as isStimOwnedSim, m as avdIniPaths, mt as dropParked, o as androidHome, q as parseAvdDeviceProfile, ut as isStimOwnedAvd, v as avdStorageRoots, yt as readParked } from "./android-DriNoAVy.mjs";
10
- import { a as listeningPidsAsync, c as pidExists, d as resolveProjectMetro, h as isMetroRunning, r as killMetroTreeAsync, u as processCwds } from "./metro-CvyD-xwe.mjs";
11
- import { c as readNdjsonGenerations$1, i as createNdjsonWriter, r as createNdjsonTail } from "./ndjson-Dy2W4vRa.mjs";
12
- import { C as environmentState, a as removeEasSessionClaim, d as parseWatchRoots, f as parseWatchmanClients, i as recordEasSessionClaim, l as parseSubscriberNames, n as easMachineStateRoot$1, o as removeEasSessionClaimAsync, p as parseWatchmanPid, r as readEasSessionLedger$1, s as compareStimVersions, t as assertEasMachineRootWritable, u as parseTriggerCount, w as formatSpace, y as capacity } from "./eas-session-ledger-DrP4OuuM.mjs";
13
- import { i as registeredCaches, r as register } from "./cache-manifest-DEjqGGat.mjs";
14
- import { A as collectIdleDevicesAsync, Ct as createGcResults, Et as mapLimited, I as shutDownIdleDevices, L as workspaceBuildInProgress, M as collectOwnedDeviceActivityAsync, Ot as sizeConcurrency, P as idleShutdownCandidates, St as GC_OUTPUT, _ as withIdleWorkspace, _t as measureDu, b as resolveSupervisorTarget, bt as listBuildSlots, ct as eraseParkedIosSim, ft as teardownOwnedIosSim, gt as canonical$1, ht as probePublicHttp, i as clearWorkspaceOutputs, mt as teardownParkedIosSim, o as collectWorkspaceOutputs, pt as teardownParkedAvd, st as eraseParkedAvd, ut as teardownOwnedAvd, vt as writeUsageCache } from "./workspaces-DFbj07jp.mjs";
7
+ import { Z as resolveProjectSettingsAsync, d as androidLayoutSetting, ft as planMetroReach, m as defaultAndroidLayout } from "./settings-D776ygML.mjs";
8
+ import { C as resolvePackageJson, M as readMetroTunnel, N as readPidFile, P as readRemoteSession, i as findProjectRoot$1, j as clearWorkspaceSupervisor, k as clearRemoteSession, s as ownedDeviceLabel, v as detectIsExpo } from "./project-D1n0K4yg.mjs";
9
+ import { F as listAvdsAsync, G as ownedAvdSerialResolverAsync, I as listInstalledSystemImages, J as parseAvdSystemImage, V as ownedAvdDirectory, d as androidToolPath, dt as isStimOwnedSim, m as avdIniPaths, mt as dropParked, o as androidHome, q as parseAvdDeviceProfile, ut as isStimOwnedAvd, v as avdStorageRoots, yt as readParked } from "./android-BHKgeOuq.mjs";
10
+ import { a as listeningPidsAsync, c as pidExists, d as resolveProjectMetro, h as isMetroRunning, r as killMetroTreeAsync, u as processCwds } from "./metro-BMQHczcf.mjs";
11
+ import { c as readNdjsonGenerations$1, i as createNdjsonWriter, r as createNdjsonTail } from "./ndjson-CrjIsGzw.mjs";
12
+ import { T as formatSpace, a as removeEasSessionClaim, d as parseWatchRoots, f as parseWatchmanClients, i as recordEasSessionClaim, l as parseSubscriberNames, n as easMachineStateRoot$1, o as removeEasSessionClaimAsync, p as parseWatchmanPid, r as readEasSessionLedger$1, s as compareStimVersions, t as assertEasMachineRootWritable, u as parseTriggerCount, w as environmentState, y as capacity } from "./eas-session-ledger-B0KLZx5m.mjs";
13
+ import { i as registeredCaches, r as register } from "./cache-manifest-evF_zJ9U.mjs";
14
+ import { At as mapLimited, B as workspaceBuildInProgress, Dt as createGcResults, Et as GC_OUTPUT, F as collectOwnedDeviceActivityAsync, L as idleShutdownCandidates, Mt as sizeConcurrency, N as collectIdleDevicesAsync, _t as teardownParkedIosSim, bt as measureDu, dt as eraseParkedIosSim, gt as teardownParkedAvd, ht as teardownOwnedIosSim, i as clearWorkspaceOutputs, l as keptByLock, o as collectWorkspaceOutputs, pt as teardownOwnedAvd, ut as eraseParkedAvd, v as withIdleWorkspace, vt as probePublicHttp, wt as listBuildSlots, x as resolveSupervisorTarget, xt as writeUsageCache, yt as canonical$1, z as shutDownIdleDevices } from "./workspaces-CvSYD_78.mjs";
15
15
  import { t as canonicalPath } from "./paths-BsJMoPP2.mjs";
16
- import { lt as claimForEmptying, ot as runCancellationSignal, rt as nativeRunWaitNotice } from "./toolchain-CbCG6C5Z.mjs";
17
- import { N as listBuildLocks, b as leaseIsExpired, g as workspaceActivity, o as envDir, p as readHostProcessTableAsync, x as listLeaseFiles } from "./activity-Cd-ysFes.mjs";
18
- import { l as expoConfigContents } from "./build-cache-gBJ-WKzA.mjs";
19
- import { G as deviceHoldsBundle, O as sanitizeDeviceLabel, W as deviceHoldsApk, b as parseSimctlList, f as listAllIosSimsAsync, g as listUserApps, j as uninstallIosApp, p as listIosDeviceTypesAsync, u as iosSimulatorFailureAdvice, y as parseRuntimeVersion } from "./ios-E26qSpkb.mjs";
16
+ import { lt as claimForEmptying, ot as runCancellationSignal, rt as nativeRunWaitNotice } from "./toolchain-CLGnEUgT.mjs";
17
+ import { N as listBuildLocks, b as leaseIsExpired, g as workspaceActivity, o as envDir, p as readHostProcessTableAsync, x as listLeaseFiles } from "./activity-BR5oHSO5.mjs";
18
+ import { l as expoConfigContents } from "./build-cache-BGwTsIEa.mjs";
19
+ import { G as deviceHoldsBundle, O as sanitizeDeviceLabel, W as deviceHoldsApk, b as parseSimctlList, f as listAllIosSimsAsync, g as listUserApps, j as uninstallIosApp, p as listIosDeviceTypesAsync, u as iosSimulatorFailureAdvice, y as parseRuntimeVersion } from "./ios-CoMCWfub.mjs";
20
20
  import { n as supervisorError } from "./errors-DHm_U5gv.mjs";
21
- import { c as findBinUpward, n as expoBinFromPackage, r as expoBinPath, s as expoTelemetryEnv } from "./server-expo-ABQs2XFA.mjs";
21
+ import { c as findBinUpward, n as expoBinFromPackage, r as expoBinPath, s as expoTelemetryEnv } from "./server-expo-bB1VV2k-.mjs";
22
22
  import { t as DEV_MENU_LAUNCH_ARGS } from "./ios-device-CiBA-er9.mjs";
23
- import { t as resolveMaintenanceSettings } from "./settings-Djl-mpjX.mjs";
23
+ import { t as resolveMaintenanceSettings } from "./settings-D9LtQaUw.mjs";
24
24
  import { createRequire } from "node:module";
25
25
  import { createHash, randomUUID } from "node:crypto";
26
26
  import { closeSync, existsSync, lstatSync, mkdirSync, openSync, readFileSync, readSync, readdirSync, realpathSync, renameSync, rmSync, statSync, statfsSync, unlinkSync, writeFileSync } from "node:fs";
@@ -8039,9 +8039,10 @@ async function reclaimWorkspaceOutputs({ root, dryRun, now, note, record, diskRe
8039
8039
  targets: clearable.map((entry) => entry.projectRoot),
8040
8040
  failures: 0
8041
8041
  };
8042
+ const locked = report.workspaces.filter((entry) => keptByLock(entry) && entry.projectRoot !== null && other(entry.projectRoot));
8042
8043
  const targets = [];
8043
8044
  let failures = 0;
8044
- for (const entry of clearable) {
8045
+ for (const entry of [...locked, ...clearable]) {
8045
8046
  const result = await clearWorkspaceOutputs({
8046
8047
  ...report,
8047
8048
  workspaces: [entry]
@@ -1,27 +1,27 @@
1
1
  import { E as debugLog } from "./exec-aULwtNly.mjs";
2
- import { y as refuseRelativeStimPaths } from "./config-Cn94Jc0g.mjs";
2
+ import { y as refuseRelativeStimPaths } from "./config-AbUjIBOQ.mjs";
3
3
  import { readFileSync } from "node:fs";
4
4
  import { runId, trace } from "@stim-cli/core/state";
5
5
  import { Command } from "commander";
6
6
  //#region bin/cli.ts
7
7
  const commands = /* @__PURE__ */ new Map([
8
- ["doctor", () => import("./doctor-Dbx1ZpD2.mjs")],
9
- ["worktree", () => import("./worktree-JGUd_53L.mjs")],
10
- ["start", () => import("./start-DcqnF9p8.mjs")],
11
- ["ports", () => import("./ports-uUrghqWN.mjs")],
12
- ["stop", () => import("./stop-x1Yu3He4.mjs")],
13
- ["ios", () => import("./ios-Bk4mWbtS.mjs")],
14
- ["android", () => import("./android--HBgpvpW.mjs")],
15
- ["web", () => import("./web-Je6abcpz.mjs")],
16
- ["macos", () => import("./macos-BGmTpnv9.mjs")],
17
- ["reload", () => import("./reload-DJBeF_rC.mjs")],
18
- ["device", () => import("./device-BJm0irrq.mjs")],
19
- ["logs", () => import("./logs-aJQb-RDg.mjs")],
20
- ["settings", () => import("./settings-CMHhQNwI.mjs")],
21
- ["status", () => import("./status-Bj7ZYvvc.mjs")],
22
- ["stats", () => import("./stats-GxuCbles.mjs")],
23
- ["gc", () => import("./gc-BRwAY3qT.mjs")],
24
- ["guide", () => import("./guide-_rfEh_zM.mjs")]
8
+ ["doctor", () => import("./doctor-BO_kaFWX.mjs")],
9
+ ["worktree", () => import("./worktree-DTOIohFe.mjs")],
10
+ ["start", () => import("./start-Cr-c3Xhq.mjs")],
11
+ ["ports", () => import("./ports-VQSAPZhl.mjs")],
12
+ ["stop", () => import("./stop-CHtAiv79.mjs")],
13
+ ["ios", () => import("./ios-CNDH6fNF.mjs")],
14
+ ["android", () => import("./android-BPmCmyAy.mjs")],
15
+ ["web", () => import("./web-BxSo493O.mjs")],
16
+ ["macos", () => import("./macos-BOQrKYeJ.mjs")],
17
+ ["reload", () => import("./reload-CDKEzzaf.mjs")],
18
+ ["device", () => import("./device-BvpasThQ.mjs")],
19
+ ["logs", () => import("./logs-BND8MiBq.mjs")],
20
+ ["settings", () => import("./settings-BcVCcAT5.mjs")],
21
+ ["status", () => import("./status-DbQv--Yb.mjs")],
22
+ ["stats", () => import("./stats-D5c-uIBp.mjs")],
23
+ ["gc", () => import("./gc-DjXwQXHK.mjs")],
24
+ ["guide", () => import("./guide-CeRlWVsS.mjs")]
25
25
  ]);
26
26
  const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf-8"));
27
27
  const program = new Command();
@@ -53,7 +53,7 @@ try {
53
53
  }));
54
54
  }
55
55
  try {
56
- const { triggerMaintenance } = await import("./trigger-qp2ji5wE.mjs");
56
+ const { triggerMaintenance } = await import("./trigger-DBZ-GBrl.mjs");
57
57
  triggerMaintenance(action.name());
58
58
  } catch {}
59
59
  });
package/dist/cli.mjs CHANGED
@@ -4,6 +4,6 @@ const encode = (version) => version.split(".", 3).reduce((sum, part) => sum * 1e
4
4
  if (encode(process.versions.node) < encode("22.12.0")) {
5
5
  process.stderr.write(`STIM_NODE_UNSUPPORTED: Stim needs Node 22.12.0 or later; this is Node ${process.versions.node} at ${process.execPath}. To run Stim where a project pins an older Node, see https://stim.appandflow.com/docs/requirements#older-node-pins\n`);
6
6
  process.exitCode = 1;
7
- } else import("./cli-ftXDLto1.mjs");
7
+ } else import("./cli-3fjOg-Oc.mjs");
8
8
  //#endregion
9
9
  export {};
@@ -1,11 +1,11 @@
1
1
  import { C as reportSignal, E as debugLog, b as operationState, c as throwIfCancelled, t as getExecutor } from "./exec-aULwtNly.mjs";
2
- import { _ as loadConfig$1, ot as resolveEndpoint, st as sayHello, tt as openAuthenticated } from "./config-Cn94Jc0g.mjs";
2
+ import { _ as loadConfig$1, ct as sayHello, nt as openAuthenticated, st as resolveEndpoint } from "./config-AbUjIBOQ.mjs";
3
3
  import { o as git, s as gitAsync } from "./operation-exec-Dp4FuIGR.mjs";
4
- import { G as planCacheEmptying, R as maintenanceLogger, n as CCACHE_UNAVAILABLE, t as CCACHE_NOT_RUN, ur as discoverCaches, z as emptyCaches } from "./ccache-BWTze1Ls.mjs";
4
+ import { G as planCacheEmptying, R as maintenanceLogger, n as CCACHE_UNAVAILABLE, t as CCACHE_NOT_RUN, ur as discoverCaches, z as emptyCaches } from "./ccache-DsDkdP72.mjs";
5
5
  import { t as canonicalPath } from "./paths-BsJMoPP2.mjs";
6
- import { A as COMPILATION_CACHE_UNAVAILABLE, E as readRubyVersion, a as cocoapodsDifference, k as COMPILATION_CACHE_NOT_RUN, ot as runCancellationSignal, s as iosToolchainAsync, v as toolchainMismatches, x as workerToolchain, y as toolchainNotes } from "./toolchain-CbCG6C5Z.mjs";
7
- import { t as resolveMaintenanceSettings } from "./settings-Djl-mpjX.mjs";
8
- import { f as workerFailureReason, m as pairedMachines, o as namedBuildMachine, t as OffloadRefusal } from "./selection-Bs8phwhN.mjs";
6
+ import { A as COMPILATION_CACHE_UNAVAILABLE, E as readRubyVersion, a as cocoapodsDifference, k as COMPILATION_CACHE_NOT_RUN, ot as runCancellationSignal, s as iosToolchainAsync, v as toolchainMismatches, x as workerToolchain, y as toolchainNotes } from "./toolchain-CLGnEUgT.mjs";
7
+ import { t as resolveMaintenanceSettings } from "./settings-D9LtQaUw.mjs";
8
+ import { f as workerFailureReason, m as pairedMachines, o as namedBuildMachine, t as OffloadRefusal } from "./selection-D1ZDzHFX.mjs";
9
9
  import { n as fingerprintNativeInputs, r as manifestDigest } from "./native-inputs-TRTTTRxt.mjs";
10
10
  import { a as sourceManifest, i as nativeTransferManifest, n as gitVisiblePaths, r as nativeFileDigest, t as checkNativeTransferMembership } from "./native-source-D1AzWiuP.mjs";
11
11
  import { createHash, webcrypto } from "node:crypto";
@@ -599,7 +599,10 @@ var BuildConnection = class BuildConnection {
599
599
  writeWaiters = /* @__PURE__ */ new Set();
600
600
  progress = null;
601
601
  binary = null;
602
+ event = null;
603
+ dropped = null;
602
604
  closed = null;
605
+ lastWrite = Promise.resolve();
603
606
  heard = Date.now();
604
607
  watched = false;
605
608
  ended = false;
@@ -631,6 +634,7 @@ var BuildConnection = class BuildConnection {
631
634
  }
632
635
  if (!isJsonObject(message)) return;
633
636
  if (message.event === "build.progress") return this.progress?.(message);
637
+ if (typeof message.event === "string") return this.event?.(message);
634
638
  const resolve = typeof message.id === "number" ? this.pending.get(message.id) : void 0;
635
639
  if (resolve) {
636
640
  this.pending.delete(message.id);
@@ -655,6 +659,7 @@ var BuildConnection = class BuildConnection {
655
659
  message: this.closed
656
660
  }
657
661
  });
662
+ this.dropped?.(code);
658
663
  });
659
664
  socket.on("error", () => {});
660
665
  }
@@ -771,8 +776,11 @@ var BuildConnection = class BuildConnection {
771
776
  if (this.closed) return;
772
777
  let completed = false;
773
778
  let resume;
779
+ let written;
780
+ this.lastWrite = new Promise((resolve) => written = resolve);
774
781
  this.socket.send(frame, { binary: true }, () => {
775
782
  completed = true;
783
+ written?.();
776
784
  resume?.();
777
785
  });
778
786
  if (completed || this.closed || this.socket.bufferedAmount <= MAX_BUFFERED) return;
@@ -785,12 +793,31 @@ var BuildConnection = class BuildConnection {
785
793
  this.writeWaiters.add(wake);
786
794
  });
787
795
  }
796
+ /** Resolves once the last binary frame left this process, or at once when the connection has ended. */
797
+ flushed() {
798
+ return this.closed ? Promise.resolve() : this.lastWrite;
799
+ }
800
+ /** Stops reading frames from the machine until `resume()`, so the machine's sends wait on the network. */
801
+ pause() {
802
+ this.socket.pause();
803
+ }
804
+ resume() {
805
+ this.socket.resume();
806
+ }
788
807
  onProgress(listener) {
789
808
  this.progress = listener;
790
809
  }
791
810
  onBinary(listener) {
792
811
  this.binary = listener;
793
812
  }
813
+ /** Server events other than `build.progress`, such as `device-host.metro.link.open`. */
814
+ onEvent(listener) {
815
+ this.event = listener;
816
+ }
817
+ /** Called once with the close code when the machine or the network ends the connection; never after `close()` or `drop()`. */
818
+ onDropped(listener) {
819
+ this.dropped = listener;
820
+ }
794
821
  get failure() {
795
822
  return this.closed;
796
823
  }
@@ -802,14 +829,18 @@ var BuildConnection = class BuildConnection {
802
829
  this.heard = Date.now();
803
830
  this.watched = true;
804
831
  }
832
+ /** Stops treating silence as a dropped connection, as before `watch()`. */
833
+ unwatch() {
834
+ this.watched = false;
835
+ }
805
836
  /** Closes with a close frame, which tells the machine to cancel this connection's jobs. */
806
- close() {
837
+ close(code = 1e3) {
807
838
  if (this.ended) return;
808
839
  this.forget();
809
840
  if (this.socket.readyState === WebSocket.CLOSED) return;
810
841
  const timer = setTimeout(() => this.socket.terminate(), CLOSE_TIMEOUT_MS);
811
842
  this.socket.once("close", () => clearTimeout(timer));
812
- this.socket.close(1e3);
843
+ this.socket.close(code);
813
844
  }
814
845
  /** Ends the connection without a close frame, which leaves its jobs on the machine for a new connection. */
815
846
  drop() {
@@ -828,6 +859,8 @@ var BuildConnection = class BuildConnection {
828
859
  this.pending.clear();
829
860
  this.progress = null;
830
861
  this.binary = null;
862
+ this.event = null;
863
+ this.dropped = null;
831
864
  clearInterval(this.keepalive);
832
865
  this.socket.removeAllListeners("close");
833
866
  this.unregisterExitCleanup?.();
@@ -1251,7 +1284,7 @@ async function fetchArtifact({ connection, method, params, artifact, request, st
1251
1284
  if (badFrame) return { failure: "fetch: an artifact frame had another digest or could not be written here" };
1252
1285
  if (digest !== artifact.sha256 || declared.sha256 !== digest || declared.size !== received) return { failure: `fetch: the artifact's sha256 ${digest.slice(0, 12)} does not match ${String(artifact.sha256).slice(0, 12)}` };
1253
1286
  if (cacheHit) {
1254
- if ((await getExecutor().runFileAsync("tar", ["-tf", archive], { timeoutMs: 6e5 })).split("\n").filter(Boolean).some((member) => {
1287
+ if ((await getExecutor().runFileAsync("tar", ["-tf", archive], { timeoutMs: 6e5 })).split(/\r?\n/).filter(Boolean).some((member) => {
1255
1288
  const clean = member.replace(/^\.\//, "");
1256
1289
  return !(clean === name || clean.startsWith(`${name}/`)) || clean.split("/").includes("..");
1257
1290
  })) return { failure: "fetch: the archive contains paths outside its app" };
@@ -1,11 +1,11 @@
1
1
  import { S as process_identity_exports } from "./exec-aULwtNly.mjs";
2
- import { k as workspaceLogsDir } from "./workspace-state-BjqdjnJR.mjs";
3
- import { V as deviceSlotKey, q as validateDeviceSlot } from "./config-Cn94Jc0g.mjs";
2
+ import { k as workspaceLogsDir } from "./workspace-state-DGwkPXwq.mjs";
3
+ import { V as deviceSlotKey, q as validateDeviceSlot } from "./config-AbUjIBOQ.mjs";
4
4
  import { t as createLineReader } from "./process-output-Fpl8PYi8.mjs";
5
- import { c as watchAppPid, i as parseLogcatLine, o as startAndroidLogcat, s as waitForAppPid, t as androidClockOffset } from "./android-Deb9X3_Y.mjs";
6
- import { i as createNdjsonWriter } from "./ndjson-Dy2W4vRa.mjs";
5
+ import { c as watchAppPid, i as parseLogcatLine, o as startAndroidLogcat, s as waitForAppPid, t as androidClockOffset } from "./android-CLU_MMnb.mjs";
6
+ import { i as createNdjsonWriter } from "./ndjson-CrjIsGzw.mjs";
7
7
  import { a as startIosDeviceConsole, i as parseDeviceConsoleLine } from "./ios-device-CiBA-er9.mjs";
8
- import { a as unregisterCollector, i as registerCollector, r as readCollectors, t as collectorProcessTitle } from "./ownership-Bx-0cku-.mjs";
8
+ import { a as unregisterCollector, i as registerCollector, r as readCollectors, t as collectorProcessTitle } from "./ownership-Dtz9G5Gl.mjs";
9
9
  import { n as parseLogStreamLine, r as startIosLogStream, t as appNameFromBundleId } from "./ios-CsRVik64.mjs";
10
10
  import { existsSync } from "node:fs";
11
11
  import { isAbsolute, join, resolve } from "node:path";
@@ -1,5 +1,5 @@
1
1
  import { n as stripAnsi } from "./process-output-Fpl8PYi8.mjs";
2
- import { t as NO_PROJECT_REFUSAL } from "./project-DYj-HVx6.mjs";
2
+ import { t as NO_PROJECT_REFUSAL } from "./project-D1n0K4yg.mjs";
3
3
  import { quotedPath as quotedPath$1 } from "@stim-cli/core";
4
4
  import chalk from "chalk";
5
5
  //#region src/diagnostics/launch-error-preview.ts
@@ -2,11 +2,12 @@ import { C as reportSignal, D as ownership_claim_exports, S as process_identity_
2
2
  import { t as runOperationFile } from "./operation-exec-Dp4FuIGR.mjs";
3
3
  import { join } from "node:path";
4
4
  import { ambientEnv, configDir, ensurePrivateDir } from "@stim-cli/core";
5
- import { allMetroPorts, cannotHost, clientIdentity, configCorruptRepair, endpoint, endpoint as endpoint$1, findEnclosingWorktreeRoot, findPeer, findPeer as findPeer$1, getConcurrencyLimits, getConfigPath, getConfigPath as getConfigPath$1, getProject, getProjectSettings, getRepoSettings, isJsonObject, isPathPrefix as isPathPrefix$1, loadConfig, loadConfig as loadConfig$1, machineEndpoint, parseMachine, parseMachine as parseMachine$1, validateAutomaticPool, withConfigLock, withConfigLock as withConfigLock$1, withConfigLockAsync, withConfigLockAsync as withConfigLockAsync$1 } from "@stim-cli/core/state";
5
+ import { DEFAULT_SERVER_PORT, allMetroPorts, cannotHost, clientIdentity, configCorruptRepair, endpoint, endpoint as endpoint$1, findEnclosingWorktreeRoot, findPeer, findPeer as findPeer$1, getConcurrencyLimits, getConfigPath, getConfigPath as getConfigPath$1, getProject, getProjectSettings, getRepoSettings, isJsonObject, isPathPrefix as isPathPrefix$1, loadConfig, loadConfig as loadConfig$1, machineEndpoint, parseMachine, parseMachine as parseMachine$1, validateAutomaticPool, withConfigLock, withConfigLock as withConfigLock$1, withConfigLockAsync, withConfigLockAsync as withConfigLockAsync$1 } from "@stim-cli/core/state";
6
6
  import { dirname as dirname$1, isAbsolute as isAbsolute$1, join as join$1, normalize, parse, sep as sep$1 } from "path";
7
- import { request } from "node:https";
7
+ import { request } from "node:http";
8
+ import { request as request$1 } from "node:https";
8
9
  import { WebSocket } from "ws";
9
- import { HandshakeRefusal, MAX_SERVER_MESSAGE_BYTES, TRANSPORT_OVERHEAD_BYTES, decodeSecret, initiateNoise, newSecret } from "@stim-cli/core/noise-session";
10
+ import { HandshakeRefusal, MAX_SERVER_MESSAGE_BYTES, TRANSPORT_OVERHEAD_BYTES, decodeSecret, initiateNoise, keyFingerprint, newSecret } from "@stim-cli/core/noise-session";
10
11
  import { existsSync, lstatSync, readdirSync, readlinkSync, renameSync, statSync, unlinkSync, writeFileSync } from "fs";
11
12
  import { InvalidArgumentError } from "commander";
12
13
  //#region src/offload/access-ticket.ts
@@ -203,6 +204,52 @@ function diagnoseUnreachable(input) {
203
204
  remedy: `On ${entry}, run \`stim-server service status\`. To keep it running there, run \`stim-server service install --serve\`.`
204
205
  };
205
206
  }
207
+ /**
208
+ * Why a `local:` machine did not connect. Nothing below stim-server's own port is in the way, so the causes are the
209
+ * network, the machine's firewall, or a server that does not listen on that address.
210
+ */
211
+ function diagnoseLocalUnreachable(input) {
212
+ const { entry, port, transport } = input;
213
+ if (transport?.kind === "refused") return {
214
+ layer: "server",
215
+ code: "lan-not-listening",
216
+ title: "Not listening on the local network",
217
+ message: `${entry} answers on the local network, but nothing accepts connections on port ${port}: its stim-server is not running or does not listen on that address.`,
218
+ remedy: `On ${entry}, run \`stim-server service status\`, and check that server.lanAddresses names the network interface or address this machine reaches (\`npx --yes stim settings get server.lanAddresses\`). Its stim-server log names each entry it cannot use.`
219
+ };
220
+ if (transport?.kind === "silent") return {
221
+ layer: "peer",
222
+ code: "lan-no-answer",
223
+ title: "Not answering",
224
+ message: `${entry} did not answer a connection on port ${port}.`,
225
+ remedy: `Check that ${entry} is awake, on the same network as this machine and has that address. On a Mac, the macOS firewall drops connections until a person allows incoming connections for stim-server's node there (System Settings > Network > Firewall > Options).`
226
+ };
227
+ if (transport?.kind === "http" && transport.status === 503) return diagnoseUnreachable({
228
+ ...input,
229
+ peer: {
230
+ nodeId: "",
231
+ dnsName: entry,
232
+ address: entry
233
+ }
234
+ });
235
+ return {
236
+ layer: "server",
237
+ code: "connect-failed",
238
+ title: "Not answering",
239
+ message: `Could not connect to stim-server on ${entry}: ${sentence(input.reason)}`,
240
+ remedy: `On ${entry}, run \`stim-server service status\`. To keep it running there, run \`stim-server service install\`.`
241
+ };
242
+ }
243
+ /** The doctor finding for a `local:` machine whose server key no person has confirmed, so Stim has not pinned it. */
244
+ function unconfirmedKeyFinding(code, entry, fingerprint) {
245
+ return {
246
+ code,
247
+ level: "note",
248
+ title: `Remote machine ${entry} needs its server key confirmed`,
249
+ detail: `${entry} approved this machine and answered with the stim-server key fingerprint ${fingerprint}. On the local network nothing vouches for that key, so Stim pins it only after a person checks it against the fingerprint \`stim-server setup\` printed there. Until then this machine does not use ${entry}.`,
250
+ fix: `Run \`stim doctor --fix\` in a terminal here and answer y only if the fingerprint matches. If it does not, another machine answered at that address: do not confirm, and check the address in remote.machines.`
251
+ };
252
+ }
206
253
  function diagnosisFinding(code, entry, diagnosis) {
207
254
  return {
208
255
  code,
@@ -241,11 +288,12 @@ function rawTailscaleStatus() {
241
288
  function peerStartup({ url, servername, host }) {
242
289
  return new Promise((resolve) => {
243
290
  const base = new URL(url);
244
- const request$1 = request({
291
+ const plain = base.protocol === "ws:";
292
+ const request$2 = (plain ? request : request$1)({
245
293
  host: base.hostname.replace(/^\[|\]$/g, ""),
246
- port: base.port || 443,
294
+ port: base.port || (plain ? 80 : 443),
247
295
  path: "/health",
248
- servername,
296
+ ...plain ? {} : { servername },
249
297
  headers: { Host: host },
250
298
  timeout: HEALTH_TIMEOUT_MS
251
299
  }, (response) => {
@@ -253,15 +301,15 @@ function peerStartup({ url, servername, host }) {
253
301
  let size = 0;
254
302
  response.on("data", (chunk) => {
255
303
  size += chunk.length;
256
- if (size > 4096) request$1.destroy();
304
+ if (size > 4096) request$2.destroy();
257
305
  else chunks.push(chunk);
258
306
  });
259
307
  response.on("end", () => resolve(parsePeerStartup(parseJson(Buffer.concat(chunks).toString("utf8")))));
260
308
  response.on("error", () => resolve(null));
261
309
  });
262
- request$1.on("timeout", () => request$1.destroy());
263
- request$1.on("error", () => resolve(null));
264
- request$1.end();
310
+ request$2.on("timeout", () => request$2.destroy());
311
+ request$2.on("error", () => resolve(null));
312
+ request$2.end();
265
313
  });
266
314
  }
267
315
  function tailscaleStatus() {
@@ -388,8 +436,9 @@ function openAuthenticated(target, auth, timeoutMs, signal, operation) {
388
436
  }), timeoutMs);
389
437
  signal?.addEventListener("abort", cancel, { once: true });
390
438
  if (signal?.aborted) return cancel();
439
+ const served = target.url.startsWith("wss:");
391
440
  const failed = (error, status) => finish({
392
- failure: status === 502 ? "no stim-server answers behind its tailscale serve route (HTTP 502)" : status === 503 ? "stim-server answers behind its tailscale serve route but is not serving clients (HTTP 503)" : status !== void 0 ? `its tailscale serve route answered HTTP ${status}` : error.message,
441
+ failure: status === 502 && served ? "no stim-server answers behind its tailscale serve route (HTTP 502)" : status === 503 ? `stim-server answers${served ? " behind its tailscale serve route" : ""} but is not serving clients (HTTP 503)` : status !== void 0 ? `${served ? "its tailscale serve route" : "it"} answered HTTP ${status}` : error.message,
393
442
  refused: false,
394
443
  transport: opened && status === void 0 ? {
395
444
  kind: "other",
@@ -556,6 +605,17 @@ async function diagnoseOutcome(io, input) {
556
605
  async function diagnoseConnectFailure(machine, failure, signal) {
557
606
  const parsed = parseMachine(machine);
558
607
  if (!parsed) return null;
608
+ if (parsed.transport === "local") {
609
+ const target = machineEndpoint(machine, null);
610
+ const startup = typeof target !== "string" && failure.transport?.kind === "http" && failure.transport.status === 503 ? await peerStartup(target) : null;
611
+ return diagnoseLocalUnreachable({
612
+ entry: machine,
613
+ port: parsed.port,
614
+ reason: failure.failure,
615
+ startup,
616
+ ...failure.transport ? { transport: failure.transport } : {}
617
+ });
618
+ }
559
619
  const peer = findPeer(await tailscaleStatusAsync(signal), parsed.name);
560
620
  if (typeof peer === "string") return null;
561
621
  return diagnoseOutcome({ startup: peerStartup }, {
@@ -574,19 +634,45 @@ async function diagnoseTailscale(io) {
574
634
  }
575
635
  /** Where to reach a paired machine now, or why Stim cannot; the handshake then checks its pinned key. */
576
636
  async function resolveEndpoint(credential, status = () => tailscaleStatusAsync(signal), signal) {
577
- return machineEndpoint(credential.machine, parseMachine(credential.machine) ? await status() : null);
637
+ return machineEndpoint(credential.machine, parseMachine(credential.machine)?.transport === "tailscale" ? await status() : null);
638
+ }
639
+ /**
640
+ * Shows a local network machine's server key fingerprint and asks a person whether it matches the one
641
+ * `stim-server setup` printed there. Questions go one at a time, and each machine and key is asked once per doctor
642
+ * run, so its build and device-host checks share the answer. Without a terminal every answer is no.
643
+ */
644
+ function keyConfirmer(ask) {
645
+ const answers = /* @__PURE__ */ new Map();
646
+ let queue = Promise.resolve();
647
+ return (machine, serverKey) => {
648
+ if (!ask) return Promise.resolve(false);
649
+ const id = `${machine}\0${serverKey}`;
650
+ let answer = answers.get(id);
651
+ if (!answer) {
652
+ const fingerprint = keyFingerprint(Buffer.from(serverKey, "base64url"));
653
+ answer = queue.then(() => ask(`${machine} answered with this stim-server key fingerprint:\n ${fingerprint}\nDoes it match the fingerprint \`stim-server setup\` printed on ${machine}? Only then does Stim trust it. [y/N] `));
654
+ queue = answer.catch(() => false);
655
+ answers.set(id, answer);
656
+ }
657
+ return answer;
658
+ };
578
659
  }
579
660
  /** The remedy for a pending request: the printed setup command, or where the person finds it. */
580
661
  function setupRemedy(entry, command) {
581
662
  return command ? `A person on ${entry} runs this there to approve this machine: ${command}\nAfter approving, run \`stim doctor\` on this machine to reconnect.` : `A person on ${entry} runs the \`stim-server setup\` command Stim Desktop showed for this machine.`;
582
663
  }
583
- function setupCommand(ticket, expiresAt, flags, version) {
584
- return `STIM_SETUP_TICKET=${ticket} STIM_SETUP_ORIGIN=doctor npx --yes --package @stim-cli/server@${version} stim-server setup --expires ${expiresAt} ${flags.map((flag) => `--${flag}`).join(" ")}`;
664
+ function setupCommand(machine, ticket, expiresAt, flags, version) {
665
+ const parsed = parseMachine(machine);
666
+ const port = parsed?.transport === "local" && parsed.port !== DEFAULT_SERVER_PORT ? ` --port ${parsed.port}` : "";
667
+ return `STIM_SETUP_TICKET=${ticket} STIM_SETUP_ORIGIN=doctor npx --yes --package @stim-cli/server@${version} stim-server setup --expires ${expiresAt} ${flags.map((flag) => `--${flag}`).join(" ")}${port}`;
668
+ }
669
+ function transportOf(machine) {
670
+ return parseMachine(machine)?.transport ?? "tailscale";
585
671
  }
586
672
  function newPending(input, ticket, expiresAt, now) {
587
673
  return {
588
674
  machine: input.machine,
589
- transport: "tailscale",
675
+ transport: transportOf(input.machine),
590
676
  address: input.address,
591
677
  state: "pending",
592
678
  requestedAt: new Date(now).toISOString(),
@@ -599,7 +685,7 @@ function approved(input, reply, requestedAt) {
599
685
  state: "approved",
600
686
  credential: {
601
687
  machine: input.machine,
602
- transport: "tailscale",
688
+ transport: transportOf(input.machine),
603
689
  address: input.address,
604
690
  state: "approved",
605
691
  requestedAt,
@@ -644,11 +730,13 @@ async function reachMachine(input) {
644
730
  });
645
731
  if ("result" in resumed && resumed.result.capabilities.includes(access)) return approved(input, resumed, pending.requestedAt);
646
732
  }
733
+ const known = (serverKey) => pending.serverKey === serverKey || input.other()?.serverKey === serverKey;
647
734
  const reply = await io.hello(input.endpoint, {
648
735
  access,
649
736
  ticket: pending.ticket,
650
737
  name: input.deviceName,
651
738
  onServerKey: (serverKey) => {
739
+ if (input.confirmKey && !known(serverKey)) return;
652
740
  pending = {
653
741
  ...pending,
654
742
  serverKey
@@ -656,8 +744,22 @@ async function reachMachine(input) {
656
744
  return input.persist(pending);
657
745
  }
658
746
  });
659
- if ("result" in reply && reply.result.capabilities.includes(access)) return approved(input, reply, pending.requestedAt);
660
- const command = pending.expiresAt ? setupCommand(pending.ticket, pending.expiresAt, input.setupFlags, input.serverVersion) : null;
747
+ if ("result" in reply && reply.result.capabilities.includes(access)) {
748
+ if (input.confirmKey && !known(reply.serverKey)) {
749
+ if (!await input.confirmKey(reply.serverKey)) return {
750
+ state: "unconfirmed",
751
+ credential: pending,
752
+ fingerprint: keyFingerprint(Buffer.from(reply.serverKey, "base64url"))
753
+ };
754
+ pending = {
755
+ ...pending,
756
+ serverKey: reply.serverKey
757
+ };
758
+ await input.persist(pending);
759
+ }
760
+ return approved(input, reply, pending.requestedAt);
761
+ }
762
+ const command = pending.expiresAt ? setupCommand(input.machine, pending.ticket, pending.expiresAt, input.setupFlags, input.serverVersion) : null;
661
763
  if ("error" in reply && reply.error.code === "server-outdated") return {
662
764
  state: "outdated",
663
765
  credential: pending,
@@ -1277,4 +1379,4 @@ function allConsolePortsAndSerials({ isMounted = isOnMountedVolume } = {}) {
1277
1379
  return result;
1278
1380
  }
1279
1381
  //#endregion
1280
- export { endpoint$1 as $, writeConfigSettingAsync as A, deviceSlotFileKey as B, saveConfig as C, withConfigLock$1 as D, upsertProject as E, isOnMountedVolume as F, projectDeviceSlots as G, deviceSlotPlatforms as H, listMountedVolumes as I, SERVER_OUTDATED as J, removeSlotDevice as K, measuredDirectorySizeAsync as L, directorySize as M, directorySizeAsync as N, withConfigLockAsync$1 as O, formatBytes as P, doctorIo as Q, volumeRootFor as R, removeProject as S, setSupervisor as T, parseDeviceSlotKey as U, deviceSlotKey as V, parseDeviceSlotOption as W, diagnoseOutcome as X, diagnoseConnectFailure as Y, diagnoseTailscale as Z, loadConfig$1 as _, clearDevice as a, realIo as at, releaseAndroidConsolePort as b, ensureConfig as c, setupRemedy as ct, getConfigDir as d, notOnTailnetDiagnosis as dt, findPeer$1 as et, getConfigPath$1 as f, readHostPermissions as ft, isPathPrefix$1 as g, getRepoSettings as h, clearAndroidBootPending as i, reachMachine as it, acquireAvdClaim as j, writeConfigSetting as k, findEnclosingWorktreeRoot as l, tailscaleStatusAsync as lt, getProjectSettings as m, allMetroPorts as n, outdatedServer as nt, clearSupervisor as o, resolveEndpoint as ot, getProject as p, validateDeviceSlot as q, claimMetroPort as r, parseMachine$1 as rt, configCorruptRepair as s, sayHello as st, allConsolePortsAndSerials as t, openAuthenticated as tt, getConcurrencyLimits as u, diagnosisFinding as ut, recordIosSchemeApprovals as v, setDevice as w, releaseMetroPort as x, refuseRelativeStimPaths as y, assignSlotDevice as z };
1382
+ export { endpoint$1 as $, writeConfigSettingAsync as A, deviceSlotFileKey as B, saveConfig as C, withConfigLock$1 as D, upsertProject as E, isOnMountedVolume as F, projectDeviceSlots as G, deviceSlotPlatforms as H, listMountedVolumes as I, SERVER_OUTDATED as J, removeSlotDevice as K, measuredDirectorySizeAsync as L, directorySize as M, directorySizeAsync as N, withConfigLockAsync$1 as O, formatBytes as P, doctorIo as Q, volumeRootFor as R, removeProject as S, setSupervisor as T, parseDeviceSlotKey as U, deviceSlotKey as V, parseDeviceSlotOption as W, diagnoseOutcome as X, diagnoseConnectFailure as Y, diagnoseTailscale as Z, loadConfig$1 as _, clearDevice as a, reachMachine as at, releaseAndroidConsolePort as b, ensureConfig as c, sayHello as ct, getConfigDir as d, diagnoseLocalUnreachable as dt, findPeer$1 as et, getConfigPath$1 as f, diagnosisFinding as ft, isPathPrefix$1 as g, getRepoSettings as h, readHostPermissions as ht, clearAndroidBootPending as i, parseMachine$1 as it, acquireAvdClaim as j, writeConfigSetting as k, findEnclosingWorktreeRoot as l, setupRemedy as lt, getProjectSettings as m, unconfirmedKeyFinding as mt, allMetroPorts as n, openAuthenticated as nt, clearSupervisor as o, realIo as ot, getProject as p, notOnTailnetDiagnosis as pt, validateDeviceSlot as q, claimMetroPort as r, outdatedServer as rt, configCorruptRepair as s, resolveEndpoint as st, allConsolePortsAndSerials as t, keyConfirmer as tt, getConcurrencyLimits as u, tailscaleStatusAsync as ut, recordIosSchemeApprovals as v, setDevice as w, releaseMetroPort as x, refuseRelativeStimPaths as y, assignSlotDevice as z };