stim 1.20.2 → 1.21.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 (217) hide show
  1. package/README.md +88 -8
  2. package/dist/acquire-artifact-CD4lbiWE.mjs +153 -0
  3. package/dist/act-DHAw2A2S.mjs +2667 -0
  4. package/dist/activity-BzVfHSrY.mjs +1376 -0
  5. package/dist/{agent-device-usage-output-Dzgv6lgR.mjs → agent-device-usage-output-zh1vhbLp.mjs} +21 -65
  6. package/dist/{android-HqP1_vB_.mjs → android-BEJ8Jpl7.mjs} +2 -2
  7. package/dist/{android-DCKZrNvV.mjs → android-BLgbNqox.mjs} +128 -78
  8. package/dist/{android-Dlzxshgp.mjs → android-CV_GCELZ.mjs} +5 -5
  9. package/dist/{android-Mz_Inn3w.mjs → android-DUYkNF4u.mjs} +184 -102
  10. package/dist/{android-cas-Bl-cLJBk.mjs → android-cas-Ct5b1xCK.mjs} +7 -6
  11. package/dist/android-cas-compiler.mjs +2 -2
  12. package/dist/android-project-BCAAmMwz.mjs +12 -0
  13. package/dist/api.d.mts +740 -8
  14. package/dist/api.mjs +585 -1
  15. package/dist/{artifact-HSwkIWEQ.mjs → artifact-B8T3S5gi.mjs} +223 -201
  16. package/dist/{artifact-D3czlUvT.mjs → artifact-DAuZ-FNU.mjs} +158 -148
  17. package/dist/{browser-web-CA-F1GQo.mjs → browser-web-QQzVjz3O.mjs} +26 -24
  18. package/dist/budget-Bs6SxxzO.mjs +9154 -0
  19. package/dist/{build-DYDbfkk5.mjs → build-CoPzQRNw.mjs} +144 -96
  20. package/dist/build-cache-DmyR9Zzc.mjs +602 -0
  21. package/dist/build-plan-CaFskB7_.mjs +188 -0
  22. package/dist/{cache-manifest-XH-fvDUj.mjs → cache-manifest-DkkqguoK.mjs} +2 -2
  23. package/dist/cache-manifest.mjs +1 -1
  24. package/dist/{ccache-zdKiYQ3E.mjs → ccache-BJRZ9pG3.mjs} +10 -9
  25. package/dist/{cdp-Dq_ypAlJ.mjs → cdp-CWmIUMbw.mjs} +18 -5
  26. package/dist/{chrome-BnzZMqsR.mjs → chrome-B6tfCta1.mjs} +7 -4
  27. package/dist/{cli-CLP3diBm.mjs → cli-0DYf8QhQ.mjs} +20 -20
  28. package/dist/cli.mjs +1 -1
  29. package/dist/client-B7QVOlCr.d.mts +253 -0
  30. package/dist/client-UXD70ZbM.mjs +1406 -0
  31. package/dist/collector-run.d.mts +4 -40
  32. package/dist/collector-run.mjs +9 -8
  33. package/dist/{command-output-BTy8hZ0F.mjs → command-output-B1UrjZHL.mjs} +1 -1
  34. package/dist/config-CYcBF478.mjs +949 -0
  35. package/dist/{created-devices-Dzd5C2LF.mjs → created-devices-CqeBlrEn.mjs} +3 -3
  36. package/dist/{dependency-state-DG0p31-y.mjs → dependency-state-BbKQW83h.mjs} +20 -5
  37. package/dist/{detached-entry-B9C_zeQC.mjs → detached-entry-DFRyG1_o.mjs} +1 -1
  38. package/dist/{dev-client-DAWVIGBu.mjs → dev-client-UOeESKm6.mjs} +2 -2
  39. package/dist/device-DCxH7_By.mjs +1068 -0
  40. package/dist/{device-BECOiufl.mjs → device-ExJq8Ei7.mjs} +35 -35
  41. package/dist/{device-capacity-CBUAcRyQ.mjs → device-capacity-CMlqEcvw.mjs} +43 -13
  42. package/dist/device-host-worker.mjs +65 -61
  43. package/dist/{device-ios-COSi6oS1.mjs → device-ios-DRUeKaFV.mjs} +48 -45
  44. package/dist/{device-lease-run-Cw2M4z51.mjs → device-lease-run-C8raGFoP.mjs} +14 -26
  45. package/dist/{device-pool-Bf-pOhSw.mjs → device-pool-DMRpVX9a.mjs} +5 -5
  46. package/dist/doctor-BLHCYxzi.mjs +2 -0
  47. package/dist/{doctor-kfMBnUw6.mjs → doctor-DTvaDOda.mjs} +281 -317
  48. package/dist/{doctor-CB8oZKCk.mjs → doctor-Dhqebm0q.mjs} +183 -91
  49. package/dist/doctor-fingerprint.mjs +19 -0
  50. package/dist/{doctor-watchman-BRyV2adA.mjs → doctor-watchman-Cv1F10Kj.mjs} +40 -21
  51. package/dist/{eas-build-JyfqyHfC.mjs → eas-build-BomfRnFT.mjs} +53 -20
  52. package/dist/{build-plan-B7SZsc2N.mjs → eas-fallback-DJwZpy0c.mjs} +26 -208
  53. package/dist/{watchman-CPQFZLXY.mjs → eas-session-ledger-op6A54yi.mjs} +229 -11
  54. package/dist/{error-diagnostics-BR677IDM.mjs → error-diagnostics-Bf0rZvG9.mjs} +172 -82
  55. package/dist/exec-H20xSg2T.d.mts +65 -0
  56. package/dist/exec-_UbtCQcG.mjs +750 -0
  57. package/dist/gc-C-WwL0yi.mjs +3 -0
  58. package/dist/{gc-qWOMdnER.mjs → gc-_-dCXdyL.mjs} +584 -796
  59. package/dist/git-C3iTsJaZ.mjs +73 -0
  60. package/dist/{guide-G9cAbVAc.mjs → guide-Yqwt2bd6.mjs} +941 -269
  61. package/dist/{hosted-android-C3MHGld1.mjs → hosted-android-Bw-honsd.mjs} +8 -6
  62. package/dist/{hosted-client-ZEKpS9vw.mjs → hosted-client-DB_gBPfY.mjs} +34 -19
  63. package/dist/{hosted-ios-l-o3iM9J.mjs → hosted-ios-CUvOf4Vr.mjs} +1 -1
  64. package/dist/{hosted-logs-EzQM7-3Z.mjs → hosted-logs-DL_P5AdN.mjs} +77 -43
  65. package/dist/{hosted-macos-BACVAVqE.mjs → hosted-macos-QI141yKk.mjs} +21 -17
  66. package/dist/{hosted-native-A4s1QPXe.mjs → hosted-native-Ksyjgzq-.mjs} +30 -21
  67. package/dist/idle-BVeRGroT.d.mts +24 -0
  68. package/dist/{idle-shutdown-DtxhPTKe.mjs → idle-shutdown-DaKmZi0z.mjs} +11 -10
  69. package/dist/{ios-BwVCF7oA.mjs → ios-CQ68hG9s.mjs} +8 -8
  70. package/dist/{ios-RkW4pnQf.mjs → ios-CpByZy12.mjs} +140 -80
  71. package/dist/{ios-device-B5veSDDQ.mjs → ios-device-CXGP1bsD.mjs} +1 -1
  72. package/dist/{ios-device-D7pi2Sca.mjs → ios-device-CwFMehUS.mjs} +51 -26
  73. package/dist/{ios-project-K9hY_Jab.mjs → ios-project-WuwwBdvW.mjs} +13 -13
  74. package/dist/{ios-nOLJGVdy.mjs → ios-qNJt4dFm.mjs} +1 -1
  75. package/dist/ios-signing-DTTIChGs.mjs +489 -0
  76. package/dist/{ios-state-CBLe1xYw.mjs → ios-state-DvFX-K_l.mjs} +1 -1
  77. package/dist/{ios-Ce7bc7br.mjs → ios-ubFsBzwq.mjs} +129 -86
  78. package/dist/{js-swap-Bb3nsS07.mjs → js-swap-Cc1dttps.mjs} +53 -26
  79. package/dist/{launch-F4sOqAhB.mjs → launch-CD1yVc7U.mjs} +91 -90
  80. package/dist/{launch-C3qJ6Agb.mjs → launch-Zq2mOSDy.mjs} +82 -541
  81. package/dist/logs-BS00Anxf.mjs +352 -0
  82. package/dist/logs-CMm05HxN.mjs +255 -0
  83. package/dist/{macos-D7uDvO-z.mjs → macos-B65--pPd.mjs} +1 -1
  84. package/dist/{macos-Ch5VJmcS.mjs → macos-C-0qJ6zq.mjs} +225 -212
  85. package/dist/macos-run.mjs +1 -1
  86. package/dist/maintenance-run.mjs +122 -247
  87. package/dist/{metro-DnCEjCxs.mjs → metro-DEXpIhHC.mjs} +67 -12
  88. package/dist/{metro-gateway-CX9Xlkq2.mjs → metro-gateway-BMJNlj3E.mjs} +20 -12
  89. package/dist/{named-ports-4WopRPg-.mjs → named-ports-BWTzt6mu.mjs} +8 -63
  90. package/dist/native-android-JACyjTZs.mjs +844 -0
  91. package/dist/{native-gradle-inputs-BsLRrl7u.mjs → native-gradle-inputs-Bep9JcSF.mjs} +128 -140
  92. package/dist/native-inputs-TRTTTRxt.mjs +91 -0
  93. package/dist/native-run-DY_RLx3C.mjs +277 -0
  94. package/dist/{native-runtime-k8dTIrbK.mjs → native-runtime-Dg1Q7tfF.mjs} +9 -8
  95. package/dist/{native-xcode-inputs-CykopJdM.mjs → native-xcode-inputs-CB2iQeWq.mjs} +27 -18
  96. package/dist/{native-xcode-ios-Bbf30Gm0.mjs → native-xcode-ios-CXX39F7o.mjs} +96 -47
  97. package/dist/{ndjson-GOnbyvso.mjs → ndjson-CgE_o3Dl.mjs} +3 -3
  98. package/dist/offload-worker.d.mts +1 -46
  99. package/dist/offload-worker.mjs +197 -66
  100. package/dist/{ownership-DCtw_2IA.mjs → ownership-DEmM5vhh.mjs} +3 -3
  101. package/dist/{page-DRHy329N.mjs → page-CPaHeYqj.mjs} +7 -4
  102. package/dist/paths-BsJMoPP2.mjs +12 -0
  103. package/dist/paths-DQtTgsBf.mjs +42 -0
  104. package/dist/{placement-log-MOfPVgaM.mjs → placement-log-BqGbTa61.mjs} +5 -5
  105. package/dist/{plan-placement-V0TdzYXp.mjs → plan-placement-Bwj-IjY1.mjs} +6 -6
  106. package/dist/{ports-DdSUicVo.mjs → ports-D-MmCTdv.mjs} +3 -3
  107. package/dist/prebuild-uD_ipCxF.mjs +449 -0
  108. package/dist/{project-_phNIJfs.mjs → project-BZNjmW_8.mjs} +65 -37
  109. package/dist/{pull-request-CQ8sPbZa.mjs → pull-request-Lfetn8Kr.mjs} +2 -2
  110. package/dist/pull-request-zjCSAryH.d.mts +120 -0
  111. package/dist/pull-requests.d.mts +1 -119
  112. package/dist/pull-requests.mjs +1 -1
  113. package/dist/{react-native-android-CM1XRDwv.mjs → react-native-android-DuzH0cn3.mjs} +74 -48
  114. package/dist/{gradle-DSrXb6lG.mjs → react-native-build-rhk4smwQ.mjs} +292 -15
  115. package/dist/react-native-doctor-CQpuIMso.mjs +234 -0
  116. package/dist/{react-native-ios-Dhq_eiYQ.mjs → react-native-ios-Bhq1rJeV.mjs} +46 -28
  117. package/dist/{recordings-D0d5aQt0.mjs → recordings-DYteQSu4.mjs} +12 -9
  118. package/dist/{reload-C8JhxjSh.mjs → reload-CmTRFJaX.mjs} +93 -47
  119. package/dist/reload-DzjSl5P_.mjs +2 -0
  120. package/dist/{remote-BiR6aYjy.mjs → remote-ByYkXdGD.mjs} +15 -10
  121. package/dist/result-DNiS-tTP.mjs +434 -0
  122. package/dist/{run-BtPgpKks.mjs → run-D23HmJMs.mjs} +4 -5
  123. package/dist/selection-CSl9i5qU.mjs +619 -0
  124. package/dist/{server-bare-R2m_gsH8.mjs → server-bare-CxTrvDg8.mjs} +3 -4
  125. package/dist/{server-command-CTtCkmcQ.mjs → server-command-CtzloH3v.mjs} +14 -7
  126. package/dist/server-expo-C-90EhcG.mjs +2 -0
  127. package/dist/{server-expo-DsguMGUF.mjs → server-expo-DT_F_uVI.mjs} +50 -9
  128. package/dist/{settings-B717odTD.mjs → settings-CsJDEXgK.mjs} +234 -24
  129. package/dist/{settings-vk3mJGSm.d.mts → settings-DkdmlelE.d.mts} +6 -4
  130. package/dist/settings-T1gtc3bf.mjs +114 -0
  131. package/dist/settings-write-HyVZ8eQ8.mjs +359 -0
  132. package/dist/settings.schema.json +163 -16
  133. package/dist/{simslim-CCr3rE-A.mjs → simslim-Cgk17SXV.mjs} +14 -12
  134. package/dist/{slot-launch-DPt0m9Nd.mjs → slot-launch-DF8VfT64.mjs} +15 -20
  135. package/dist/{spawn-entry-Dcb7sDUz.mjs → spawn-entry-jw19m8Nl.mjs} +0 -1
  136. package/dist/stage-BIS0ydsV.mjs +183 -0
  137. package/dist/{start-BWqzODpb.mjs → start-7lpH7hmN.mjs} +63 -44
  138. package/dist/{start-TXeMmmO2.mjs → start-RChynRDp.mjs} +1 -1
  139. package/dist/{state-D1Icyjlu.mjs → state-C4ZtUisW.mjs} +40 -13
  140. package/dist/{state-CG_OFoaA.mjs → state-XMziexv2.mjs} +3 -2
  141. package/dist/{stats-BoY5sEPU.mjs → stats-53T7OTb9.mjs} +7 -13
  142. package/dist/stats-report-BFfMpWWF.mjs +16 -0
  143. package/dist/{status-WgFbkRzj.mjs → status-BL7jVyAB.mjs} +535 -447
  144. package/dist/status-CSi6tlRq.mjs +2 -0
  145. package/dist/{status-BTA7kKiQ.mjs → status-DqEUSSxs.mjs} +6 -4
  146. package/dist/{status-watch-C23ILtf9.mjs → status-watch-GDCfqpTE.mjs} +187 -22
  147. package/dist/status-worker.d.mts +1 -0
  148. package/dist/status-worker.mjs +19 -0
  149. package/dist/stim-desktop-C12we7lz.mjs +112 -0
  150. package/dist/{stop-DR6VoDIk.mjs → stop-Bk_1qiV6.mjs} +2 -2
  151. package/dist/{stop-BNmBMi1F.mjs → stop-CPzlM1L-.mjs} +138 -98
  152. package/dist/{stop-DPEhunlw.mjs → stop-D7h6jAXQ.mjs} +11 -10
  153. package/dist/supervisor-run.d.mts +8 -17
  154. package/dist/supervisor-run.mjs +63 -27
  155. package/dist/{support-DMTqj3Hm.mjs → support-67vraa1Q.mjs} +18 -11
  156. package/dist/{support-CwjbFs9H.mjs → support-Dlu37g6H.mjs} +12 -12
  157. package/dist/{swiftpm-macos-DRNVJWlb.mjs → swiftpm-macos-p8WZbxKU.mjs} +35 -24
  158. package/dist/toolchain-DhKwp9iI.mjs +2 -0
  159. package/dist/toolchain-Hp9sCpK4.mjs +873 -0
  160. package/dist/{trigger-CigAYjeL.mjs → trigger-BAahxUPq.mjs} +20 -5
  161. package/dist/trigger-Bf3uDcpv.mjs +2 -0
  162. package/dist/{warm-progress-BuY-Fvfn.mjs → warm-progress-BHX5r0Ei.mjs} +250 -156
  163. package/dist/{web-BDBMxDpx.mjs → web-CJmIj6AI.mjs} +18 -13
  164. package/dist/web-DknQETPs.mjs +2 -0
  165. package/dist/web-run.mjs +11 -12
  166. package/dist/windows-report.d.mts +1 -0
  167. package/dist/windows-report.mjs +18 -0
  168. package/dist/windows-report.ps1 +43 -0
  169. package/dist/workspace-process-lock-1_1OoVW5.mjs +57 -0
  170. package/dist/{workspace-state-DJt_MMvf.mjs → workspace-state-Cjuigeeu.mjs} +62 -58
  171. package/dist/{workspaces-C36s2jb9.mjs → workspaces-BccMFmGQ.mjs} +1188 -1079
  172. package/dist/worktree-CK2MpJW5.mjs +2 -0
  173. package/dist/{worktree-CgvJYJ-Q.mjs → worktree-DQ6Zi2Mv.mjs} +304 -53
  174. package/dist/xcode-Zlx3qjEs.mjs +896 -0
  175. package/package.json +7 -5
  176. package/shim/android-optimizations.gradle +7 -5
  177. package/dist/activity-BSV745Kz.mjs +0 -629
  178. package/dist/api-qvOzWyfO.mjs +0 -154
  179. package/dist/api-run.mjs +0 -294
  180. package/dist/app-install-BIKtMfPq.mjs +0 -896
  181. package/dist/artifact-lifecycle-DzPpBYvK.mjs +0 -30
  182. package/dist/budget-f7516Zk0.mjs +0 -4106
  183. package/dist/build-facts-DcLv2HSd.d.mts +0 -111
  184. package/dist/cancellation-C_22VQTH.mjs +0 -24
  185. package/dist/client-qQAaXSUs.mjs +0 -1771
  186. package/dist/config-BYDxlMY3.mjs +0 -562
  187. package/dist/device-DuiPJQSn.mjs +0 -290
  188. package/dist/device-lease-BX1sJQ2E.mjs +0 -1224
  189. package/dist/device-remote-xRT0Vmo9.mjs +0 -1272
  190. package/dist/eas-session-ledger-Cm0Gwq2F.mjs +0 -207
  191. package/dist/exec-DqKo9GKJ.mjs +0 -268
  192. package/dist/git-DDfFzkMm.mjs +0 -45
  193. package/dist/launch-verify-B6Q8hZP-.mjs +0 -428
  194. package/dist/logs-BGrEUaEs.mjs +0 -383
  195. package/dist/miss-reason-mKzZ4VvN.mjs +0 -238
  196. package/dist/native-android-D5qc3-_v.mjs +0 -226
  197. package/dist/ndjson-Du3j0AQ9.d.mts +0 -17
  198. package/dist/plan-C4TjdPsR.mjs +0 -260
  199. package/dist/prebuild-DYbucPXy.mjs +0 -212
  200. package/dist/preview-78a9kR_-.mjs +0 -1011
  201. package/dist/react-native-build-CtCccJtb.mjs +0 -239
  202. package/dist/react-native-doctor-CfRnFfjs.mjs +0 -78
  203. package/dist/remote-cache-OyOyVIS3.mjs +0 -526
  204. package/dist/result-CaJg5Iat.mjs +0 -186
  205. package/dist/server-expo-Bigriskl.mjs +0 -2
  206. package/dist/settings-BORNaaCO.mjs +0 -402
  207. package/dist/spawn-claims-DVS5RkMS.mjs +0 -72
  208. package/dist/stage-KNTQopqv.mjs +0 -157
  209. package/dist/stim-desktop-BzBN2Em2.mjs +0 -63
  210. package/dist/tailnet-Dh-qiYzG.mjs +0 -265
  211. package/dist/toolchain-BFkAcstd.mjs +0 -2
  212. package/dist/toolchain-DxSsNssu.mjs +0 -1657
  213. package/dist/trigger-9_USgmxz.mjs +0 -2
  214. package/dist/web-Dq-i3wzJ.mjs +0 -2
  215. package/dist/worktree-DpLdCHga.mjs +0 -2
  216. package/dist/{api-run.d.mts → doctor-fingerprint.d.mts} +0 -0
  217. package/dist/{errors-DzkMYh8Q.mjs → errors-ccaPc4ox.mjs} +2 -2
@@ -0,0 +1,359 @@
1
+ import { A as writeConfigSettingAsync, O as withConfigLockAsync$1, _ as loadConfig$1, f as getConfigPath$1, h as getRepoSettings, m as getProjectSettings } from "./config-CYcBF478.mjs";
2
+ import { Kt as gitPath, it as unknownSettingKeys, qt as killOnAbort, z as readCommittedSettings } from "./settings-CsJDEXgK.mjs";
3
+ import { r as findCommandWorkspaceAsync, t as NO_PROJECT_REFUSAL } from "./project-BZNjmW_8.mjs";
4
+ import { i as stimDesktopInstalledAsync, n as settingDefault, r as stimDesktopInstalled } from "./stim-desktop-C12we7lz.mjs";
5
+ import { t as withWorkspaceProcessLock } from "./workspace-process-lock-1_1OoVW5.mjs";
6
+ import { n as recordingLayers, t as deleteTurnedOffRecordings } from "./recordings-DYteQSu4.mjs";
7
+ import { existsSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
8
+ import { join } from "node:path";
9
+ import { configDir } from "@stim-cli/core";
10
+ import { SETTINGS, SETTING_SCOPES, coerceSettingText, isJsonObject, isSensitiveReference, settingDefinition, settingValueError } from "@stim-cli/core/state";
11
+ //#region src/workspace/settings-payload.ts
12
+ const MASK = "********";
13
+ const PRECEDENCE = [
14
+ "workspace",
15
+ "repo",
16
+ "committed",
17
+ "machine"
18
+ ];
19
+ var SettingsRefusal = class extends Error {
20
+ code;
21
+ remedy;
22
+ constructor(code, message, remedy) {
23
+ super(message);
24
+ this.code = code;
25
+ this.remedy = remedy;
26
+ }
27
+ };
28
+ /** Reads layers using exactly `env`; `signal` kills Git children. `resolvedProject` pins a mutation's readback target. */
29
+ async function readSettingsContext(cwd, env, note = () => {}, signal, resolvedProject) {
30
+ const repoRootOf = (dir) => gitPath(dir, ["--show-toplevel"], env, signal);
31
+ const projectPath = resolvedProject === void 0 ? await findCommandWorkspaceAsync(cwd, repoRootOf, note) : resolvedProject;
32
+ const start = projectPath ?? cwd;
33
+ const [common, root] = await Promise.all([gitPath(start, ["--path-format=absolute", "--git-common-dir"], env, signal), repoRootOf(start)]);
34
+ signal?.throwIfAborted();
35
+ const repoRoot = common ? root ?? projectPath : null;
36
+ const app = projectPath ?? repoRoot;
37
+ const repository = repoRoot ?? projectPath;
38
+ const committed = readCommittedSettings(app);
39
+ let installed;
40
+ return {
41
+ projectPath,
42
+ gitCommonDir: common,
43
+ repoRoot,
44
+ machine: loadConfig$1(),
45
+ workspace: projectPath ? getProjectSettings(projectPath) : {},
46
+ repo: common ? getRepoSettings(common) : {},
47
+ committed: {
48
+ app: committed,
49
+ repository: repository === app ? committed : readCommittedSettings(repository)
50
+ },
51
+ env,
52
+ desktopInstalled: () => installed ??= stimDesktopInstalled(process.platform, env)
53
+ };
54
+ }
55
+ function valueAt(settings, key) {
56
+ let node = settings;
57
+ for (const segment of key.split(".")) {
58
+ if (!isJsonObject(node)) return void 0;
59
+ node = node[segment];
60
+ }
61
+ return node;
62
+ }
63
+ function committedDirectory(context, setting) {
64
+ return setting?.committedAt === "repository" ? repositoryDirectory(context) : appDirectory(context);
65
+ }
66
+ function repositoryDirectory(context) {
67
+ return context.repoRoot ?? context.projectPath;
68
+ }
69
+ function appDirectory(context) {
70
+ return context.projectPath ?? context.repoRoot;
71
+ }
72
+ function layerFile(context, scope, setting) {
73
+ const machineFile = getConfigPath$1();
74
+ if (scope === "machine") return machineFile;
75
+ if (scope === "workspace") return context.projectPath ? `${machineFile} (projects["${context.projectPath}"].settings)` : null;
76
+ if (scope === "repo") return context.gitCommonDir ? `${machineFile} (repos["${context.gitCommonDir}"].settings)` : null;
77
+ const directory = committedDirectory(context, setting);
78
+ return directory ? join(directory, ".stim.json") : null;
79
+ }
80
+ function layerSettings(context, scope, setting) {
81
+ if (scope === "machine") return context.machine ?? {};
82
+ if (scope === "workspace") return context.workspace;
83
+ if (scope === "repo") return context.repo;
84
+ return setting?.committedAt === "repository" ? context.committed.repository : context.committed.app;
85
+ }
86
+ function masked(setting, value) {
87
+ return setting.sensitive && value !== void 0 && value !== null ? MASK : value;
88
+ }
89
+ function settingExample(setting) {
90
+ switch (setting.type.kind) {
91
+ case "boolean": return "false";
92
+ case "number": return String(setting.default ?? setting.type.minimum ?? 1);
93
+ case "strings": return `'["node_modules"]'`;
94
+ case "object": return `'{"key": "value"}'`;
95
+ case "choice": return setting.type.choices[0];
96
+ default: return "<value>";
97
+ }
98
+ }
99
+ function envSettingValue(setting, raw) {
100
+ const value = coerceSettingText(setting, raw);
101
+ const error = settingValueError(setting, value);
102
+ if (error) throw new SettingsRefusal("STIM_BAD_ARG", `Invalid ${setting.env} value ${JSON.stringify(raw)}. Expected ${error}.`, `Fix the environment variable, or unset it and run \`stim settings set ${setting.key} ${settingExample(setting)}\`.`);
103
+ return value;
104
+ }
105
+ function settingEntry(context, setting) {
106
+ const layers = {};
107
+ for (const scope of PRECEDENCE) {
108
+ if (!setting.scopes.includes(scope) || layerFile(context, scope, setting) === null) continue;
109
+ const value = valueAt(layerSettings(context, scope, setting), setting.key);
110
+ if (value !== void 0) layers[scope] = masked(setting, value);
111
+ }
112
+ const envValue = setting.env ? context.env[setting.env] : void 0;
113
+ const env = setting.env && envValue ? {
114
+ name: setting.env,
115
+ value: String(masked(setting, envValue))
116
+ } : void 0;
117
+ const scopedHome = !env && setting.scopedHomeValue !== void 0 && context.env.STIM_HOME;
118
+ const ci = !env && setting.ciValue !== void 0 && context.env.CI;
119
+ const winner = PRECEDENCE.find((scope) => scope in layers);
120
+ let value = null;
121
+ let origin = null;
122
+ let defaultReason = null;
123
+ if (env) {
124
+ value = masked(setting, envSettingValue(setting, envValue));
125
+ origin = "env";
126
+ } else if (scopedHome) {
127
+ value = setting.scopedHomeValue;
128
+ origin = "env";
129
+ } else if (ci) {
130
+ value = setting.ciValue;
131
+ origin = "env";
132
+ } else if (winner) {
133
+ value = layers[winner];
134
+ origin = winner;
135
+ } else if (setting.default !== void 0) {
136
+ const fallback = settingDefault(setting, context.desktopInstalled);
137
+ value = fallback.value;
138
+ origin = "default";
139
+ defaultReason = fallback.reason;
140
+ }
141
+ return {
142
+ key: setting.key,
143
+ value,
144
+ origin,
145
+ layers,
146
+ ...env ? { env } : scopedHome ? { env: {
147
+ name: "STIM_HOME",
148
+ value: context.env.STIM_HOME
149
+ } } : ci ? { env: {
150
+ name: "CI",
151
+ value: context.env.CI
152
+ } } : {},
153
+ ...setting.sensitive ? { sensitive: true } : {},
154
+ ...defaultReason ? { defaultReason } : {}
155
+ };
156
+ }
157
+ function unknownEntries(context) {
158
+ const sources = [
159
+ {
160
+ scope: "machine",
161
+ file: layerFile(context, "machine", null),
162
+ settings: { optimizations: context.machine?.optimizations }
163
+ },
164
+ {
165
+ scope: "workspace",
166
+ file: layerFile(context, "workspace", null),
167
+ settings: layerSettings(context, "workspace", null)
168
+ },
169
+ {
170
+ scope: "repo",
171
+ file: layerFile(context, "repo", null),
172
+ settings: layerSettings(context, "repo", null)
173
+ },
174
+ {
175
+ scope: "committed",
176
+ file: layerFile(context, "committed", null),
177
+ settings: layerSettings(context, "committed", null)
178
+ }
179
+ ];
180
+ const repository = repositoryDirectory(context);
181
+ if (repository && repository !== appDirectory(context)) sources.push({
182
+ scope: "committed",
183
+ file: join(repository, ".stim.json"),
184
+ settings: context.committed.repository
185
+ });
186
+ return sources.flatMap(({ scope, file, settings }) => file === null ? [] : unknownSettingKeys(settings).map((key) => ({
187
+ key,
188
+ scope,
189
+ file,
190
+ value: valueAt(settings, key)
191
+ })));
192
+ }
193
+ function settingsPayload(context) {
194
+ const files = {};
195
+ for (const scope of SETTING_SCOPES) {
196
+ const file = layerFile(context, scope, null);
197
+ if (file) files[scope] = file;
198
+ }
199
+ return {
200
+ project: context.projectPath,
201
+ files,
202
+ settings: SETTINGS.map((setting) => settingEntry(context, setting)),
203
+ unknown: unknownEntries(context)
204
+ };
205
+ }
206
+ /** Every setting with its effective value and layers; looks Stim Desktop up asynchronously, and only when a default depends on it. */
207
+ async function readSettingsPayload(context, signal) {
208
+ let asked = false;
209
+ const payload = settingsPayload({
210
+ ...context,
211
+ desktopInstalled: () => {
212
+ asked = true;
213
+ return false;
214
+ }
215
+ });
216
+ if (!asked) return payload;
217
+ const installed = await stimDesktopInstalledAsync(process.platform, context.env, killOnAbort(signal));
218
+ signal?.throwIfAborted();
219
+ return settingsPayload({
220
+ ...context,
221
+ desktopInstalled: () => installed
222
+ });
223
+ }
224
+ //#endregion
225
+ //#region src/workspace/settings-write.ts
226
+ function requireSetting(key) {
227
+ const setting = settingDefinition(key);
228
+ if (setting) return setting;
229
+ throw new SettingsRefusal("STIM_BAD_ARG", `${JSON.stringify(key)} is not a Stim setting.`, "Run `stim settings` to list every setting, or `stim guide settings` for what each one does.");
230
+ }
231
+ function readScope(raw, setting) {
232
+ if (raw === void 0) {
233
+ if (setting.scopes.length === 1) return setting.scopes[0];
234
+ throw new SettingsRefusal("STIM_BAD_ARG", `${setting.key} can live in more than one layer, so the command needs --scope.`, `Pass --scope ${setting.scopes.join("|")}.`);
235
+ }
236
+ if (!SETTING_SCOPES.includes(raw)) throw new SettingsRefusal("STIM_BAD_ARG", `--scope takes ${SETTING_SCOPES.join(", ")}, not ${JSON.stringify(raw)}.`, null);
237
+ const scope = raw;
238
+ if (!setting.scopes.includes(scope)) throw new SettingsRefusal("STIM_BAD_ARG", `${setting.key} is not read from the ${scope} layer.`, `Pass --scope ${setting.scopes.join("|")}.`);
239
+ return scope;
240
+ }
241
+ function requireLayer(context, scope, setting) {
242
+ const file = layerFile(context, scope, setting);
243
+ if (file) return file;
244
+ if (scope === "repo") throw new SettingsRefusal("STIM_BAD_ARG", "The repo layer is keyed by a Git repository, and this directory is not inside one.", "Run this from inside the repository, or pick another --scope.");
245
+ throw new SettingsRefusal(NO_PROJECT_REFUSAL.code, NO_PROJECT_REFUSAL.message, NO_PROJECT_REFUSAL.remedy);
246
+ }
247
+ function parseSettingValue(setting, raw) {
248
+ const invalid = (expected) => new SettingsRefusal("STIM_BAD_ARG", `Invalid ${setting.key} value ${JSON.stringify(raw)}. Expected ${expected}.`, `For example: stim settings set ${setting.key} ${settingExample(setting)}${setting.scopes.length > 1 ? ` --scope ${setting.scopes[0]}` : ""}`);
249
+ const kind = setting.type.kind;
250
+ const value = coerceSettingText(setting, raw);
251
+ const error = settingValueError(setting, value);
252
+ if (error) throw invalid(kind === "strings" || kind === "object" ? `${error}, written as JSON` : error);
253
+ return value;
254
+ }
255
+ function committedTarget(context, setting) {
256
+ const directory = committedDirectory(context, setting);
257
+ if (!directory) throw new SettingsRefusal(NO_PROJECT_REFUSAL.code, NO_PROJECT_REFUSAL.message, NO_PROJECT_REFUSAL.remedy);
258
+ return join(directory, ".stim.json");
259
+ }
260
+ function writeCommitted(file, key, value, signal) {
261
+ return withConfigLockAsync$1(() => {
262
+ const exists = existsSync(file);
263
+ const text = exists ? readFileSync(file, "utf-8") : "";
264
+ let parsed = {};
265
+ if (exists && text.trim() !== "") try {
266
+ parsed = JSON.parse(text);
267
+ } catch (error) {
268
+ throw new SettingsRefusal("STIM_BAD_ARG", `${file} is not valid JSON: ${error.message}`, `Repair ${file}, then run the command again.`);
269
+ }
270
+ if (!isJsonObject(parsed)) throw new SettingsRefusal("STIM_BAD_ARG", `${file} is not a JSON object.`, `Repair ${file}, then run the command again.`);
271
+ const parts = key.split(".");
272
+ const chain = [parsed];
273
+ for (const part of parts.slice(0, -1)) {
274
+ const node = chain.at(-1);
275
+ if (!isJsonObject(node[part])) {
276
+ if (value === void 0) return false;
277
+ node[part] = {};
278
+ }
279
+ chain.push(node[part]);
280
+ }
281
+ const leaf = parts.at(-1);
282
+ const parent = chain.at(-1);
283
+ if (value === void 0) {
284
+ if (!(leaf in parent)) return false;
285
+ delete parent[leaf];
286
+ for (let index = chain.length - 1; index > 0; index--) {
287
+ if (Object.keys(chain[index]).length > 0) break;
288
+ delete chain[index - 1][parts[index - 1]];
289
+ }
290
+ } else parent[leaf] = value;
291
+ const indent = /\n([ \t]+)"/.exec(text)?.[1] ?? 2;
292
+ const tmp = `${file}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
293
+ writeFileSync(tmp, `${JSON.stringify(parsed, null, indent)}\n`);
294
+ try {
295
+ renameSync(tmp, file);
296
+ } catch (error) {
297
+ try {
298
+ unlinkSync(tmp);
299
+ } catch {}
300
+ throw error;
301
+ }
302
+ return true;
303
+ }, signal);
304
+ }
305
+ function writeSetting(context, setting, scope, value, signal) {
306
+ if (scope === "committed") return writeCommitted(committedTarget(context, setting), setting.key, value, signal);
307
+ if (scope === "machine") return writeConfigSettingAsync({ scope }, setting.key, value, signal);
308
+ if (scope === "workspace") return writeConfigSettingAsync({
309
+ scope,
310
+ projectPath: context.projectPath
311
+ }, setting.key, value, signal);
312
+ return writeConfigSettingAsync({
313
+ scope,
314
+ gitCommonDir: context.gitCommonDir
315
+ }, setting.key, value, signal);
316
+ }
317
+ /** Shares CLI set/unset validation and side effects; after commit, recording cleanup finishes despite cancellation. */
318
+ async function mutateSetting(request, { cwd, env, note = () => {}, signal, beforeWrite }) {
319
+ signal?.throwIfAborted();
320
+ const { key } = request;
321
+ const setting = requireSetting(key);
322
+ const scope = readScope(request.scope, setting);
323
+ const value = request.action === "set" ? parseSettingValue(setting, request.value) : void 0;
324
+ if (setting.sensitive && scope === "committed" && value !== void 0 && !isSensitiveReference(value)) throw new SettingsRefusal("STIM_BAD_ARG", `${key} is sensitive, so a committed .stim.json takes only an env: or file: reference, never the secret.`, `Set the secret with --scope workspace or --scope repo, which store it in ${getConfigPath$1()}, readable only by you, or commit a reference such as env:MY_KEYSTORE_PASSWORD.`);
325
+ const context = await readSettingsContext(cwd, env, note, signal);
326
+ const file = requireLayer(context, scope, setting);
327
+ beforeWrite?.(context.projectPath);
328
+ const apply = async () => {
329
+ const recording = setting.key === "recording.enabled" ? recordingLayers() : null;
330
+ const changed = await writeSetting(context, setting, scope, value, signal);
331
+ const updated = await readSettingsContext(cwd, env, void 0, void 0, context.projectPath);
332
+ const recordingsDeleted = recording ? await deleteTurnedOffRecordings(recording) : void 0;
333
+ let entry = settingEntry({
334
+ ...updated,
335
+ desktopInstalled: () => false
336
+ }, setting);
337
+ if (entry.origin === "default" && setting.desktopDefault !== void 0) {
338
+ const installed = await stimDesktopInstalledAsync(process.platform, env);
339
+ entry = settingEntry({
340
+ ...updated,
341
+ desktopInstalled: () => installed
342
+ }, setting);
343
+ }
344
+ return {
345
+ key,
346
+ scope,
347
+ file,
348
+ changed,
349
+ setting: entry,
350
+ ...recordingsDeleted ? { recordingsDeleted } : {}
351
+ };
352
+ };
353
+ return key === "recording.enabled" ? withWorkspaceProcessLock(configDir(), "recording-settings", apply, {
354
+ external: true,
355
+ signal
356
+ }) : apply();
357
+ }
358
+ //#endregion
359
+ export { layerFile as a, settingEntry as c, SettingsRefusal as i, readScope as n, readSettingsContext as o, requireSetting as r, readSettingsPayload as s, mutateSetting as t };
@@ -287,7 +287,7 @@
287
287
  }
288
288
  },
289
289
  "remote": {
290
- "description": "Default iOS remote target: eas, proxy, or an approved Mac in remote.machines; auto places on an approved Mac when this Mac is full or busy; local runs here, overriding a lower layer",
290
+ "description": "Default iOS remote target: eas, proxy, or an approved Mac in remote.machines; auto places on an approved Mac when this machine is full or busy; local runs here, overriding a lower layer",
291
291
  "type": "string",
292
292
  "pattern": "^[A-Za-z0-9][A-Za-z0-9.-]*(?::(?!0+$)(?:[0-5]?[0-9]{1,4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]))?$",
293
293
  "x-stim": {
@@ -490,6 +490,19 @@
490
490
  ]
491
491
  }
492
492
  },
493
+ "artifactInputs": {
494
+ "description": "Complete repeatable native Gradle inputs for local APK caching, including declared local files and environment names",
495
+ "type": "object",
496
+ "x-stim": {
497
+ "key": "android.artifactInputs",
498
+ "kind": "object",
499
+ "scopes": [
500
+ "workspace",
501
+ "repo",
502
+ "committed"
503
+ ]
504
+ }
505
+ },
493
506
  "offloadInputs": {
494
507
  "description": "Native Gradle source sufficiency, exact ignored inputs and generated output directories for build workers",
495
508
  "type": "object",
@@ -532,7 +545,7 @@
532
545
  }
533
546
  },
534
547
  "remote": {
535
- "description": "Default Android remote target: eas, proxy, or an approved Mac in remote.machines; auto places on an approved Mac when this Mac is full or busy; local runs here, overriding a lower layer",
548
+ "description": "Default Android remote target: eas, proxy, or an approved Mac in remote.machines; auto places on an approved Mac when this machine is full or busy; local runs here, overriding a lower layer",
536
549
  "type": "string",
537
550
  "pattern": "^[A-Za-z0-9][A-Za-z0-9.-]*(?::(?!0+$)(?:[0-5]?[0-9]{1,4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]))?$",
538
551
  "x-stim": {
@@ -941,7 +954,7 @@
941
954
  "type": "object",
942
955
  "properties": {
943
956
  "keep": {
944
- "description": "Pin this workspace: automatic maintenance never clears its build outputs or removes its worktree",
957
+ "description": "Pin this workspace: automatic maintenance never clears its build outputs, deletes its owned devices or removes its worktree",
945
958
  "type": "boolean",
946
959
  "default": false,
947
960
  "x-stim": {
@@ -961,7 +974,7 @@
961
974
  "type": "object",
962
975
  "properties": {
963
976
  "easFallback": {
964
- "description": "When ios.remote or android.remote is auto, this Mac is at its concurrency.maxDevices cap and no Mac in remote.machines takes the run, run the simulator or emulator on a billed EAS Simulator instead of waiting or refusing, when eas-cli can",
977
+ "description": "When ios.remote or android.remote is auto, this machine is at its concurrency.maxDevices cap and no remote.machines entry takes the run, run the simulator or emulator on a billed EAS Simulator instead of waiting or refusing, when eas-cli can",
965
978
  "type": "boolean",
966
979
  "default": false,
967
980
  "x-stim": {
@@ -1243,7 +1256,7 @@
1243
1256
  }
1244
1257
  },
1245
1258
  "remote": {
1246
- "description": "Default iOS remote target: eas, proxy, or an approved Mac in remote.machines; auto places on an approved Mac when this Mac is full or busy; local runs here, overriding a lower layer",
1259
+ "description": "Default iOS remote target: eas, proxy, or an approved Mac in remote.machines; auto places on an approved Mac when this machine is full or busy; local runs here, overriding a lower layer",
1247
1260
  "type": "string",
1248
1261
  "pattern": "^[A-Za-z0-9][A-Za-z0-9.-]*(?::(?!0+$)(?:[0-5]?[0-9]{1,4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]))?$",
1249
1262
  "x-stim": {
@@ -1256,6 +1269,46 @@
1256
1269
  "committed"
1257
1270
  ]
1258
1271
  }
1272
+ },
1273
+ "appStoreConnectKeyPath": {
1274
+ "description": "App Store Connect API key (.p8) that `stim device provision ios` authenticates with",
1275
+ "type": "string",
1276
+ "x-stim": {
1277
+ "key": "ios.appStoreConnectKeyPath",
1278
+ "kind": "path",
1279
+ "scopes": [
1280
+ "machine"
1281
+ ],
1282
+ "absolute": true,
1283
+ "env": "STIM_ASC_KEY_PATH",
1284
+ "sensitive": true
1285
+ }
1286
+ },
1287
+ "appStoreConnectKeyId": {
1288
+ "description": "Key ID of ios.appStoreConnectKeyPath",
1289
+ "type": "string",
1290
+ "pattern": "^[A-Za-z0-9]+$",
1291
+ "x-stim": {
1292
+ "key": "ios.appStoreConnectKeyId",
1293
+ "kind": "string",
1294
+ "scopes": [
1295
+ "machine"
1296
+ ],
1297
+ "env": "STIM_ASC_KEY_ID"
1298
+ }
1299
+ },
1300
+ "appStoreConnectIssuerId": {
1301
+ "description": "Issuer ID of ios.appStoreConnectKeyPath",
1302
+ "type": "string",
1303
+ "pattern": "^[A-Za-z0-9-]+$",
1304
+ "x-stim": {
1305
+ "key": "ios.appStoreConnectIssuerId",
1306
+ "kind": "string",
1307
+ "scopes": [
1308
+ "machine"
1309
+ ],
1310
+ "env": "STIM_ASC_ISSUER_ID"
1311
+ }
1259
1312
  }
1260
1313
  },
1261
1314
  "additionalProperties": false
@@ -1294,7 +1347,7 @@
1294
1347
  }
1295
1348
  },
1296
1349
  "remote": {
1297
- "description": "Default Android remote target: eas, proxy, or an approved Mac in remote.machines; auto places on an approved Mac when this Mac is full or busy; local runs here, overriding a lower layer",
1350
+ "description": "Default Android remote target: eas, proxy, or an approved Mac in remote.machines; auto places on an approved Mac when this machine is full or busy; local runs here, overriding a lower layer",
1298
1351
  "type": "string",
1299
1352
  "pattern": "^[A-Za-z0-9][A-Za-z0-9.-]*(?::(?!0+$)(?:[0-5]?[0-9]{1,4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]))?$",
1300
1353
  "x-stim": {
@@ -1855,6 +1908,20 @@
1855
1908
  "env": "STIM_MAINTENANCE_OLDER_THAN_DAYS"
1856
1909
  }
1857
1910
  },
1911
+ "deviceIdleDays": {
1912
+ "description": "Days a workspace stays idle before the age sweep deletes its owned simulators and emulators; 0 turns that off",
1913
+ "type": "integer",
1914
+ "minimum": 0,
1915
+ "default": 3,
1916
+ "x-stim": {
1917
+ "key": "maintenance.deviceIdleDays",
1918
+ "kind": "number",
1919
+ "scopes": [
1920
+ "machine"
1921
+ ],
1922
+ "env": "STIM_MAINTENANCE_DEVICE_IDLE_DAYS"
1923
+ }
1924
+ },
1858
1925
  "protectRecentHours": {
1859
1926
  "description": "Cache entries used within this many hours are never evicted",
1860
1927
  "type": "integer",
@@ -1869,8 +1936,64 @@
1869
1936
  "env": "STIM_MAINTENANCE_PROTECT_RECENT_HOURS"
1870
1937
  }
1871
1938
  },
1939
+ "urgentFreeDiskGb": {
1940
+ "description": "Free disk, in GB, below which automatic maintenance reports disk pressure level 2; 0 turns that threshold off",
1941
+ "type": "number",
1942
+ "minimum": 0,
1943
+ "default": 12,
1944
+ "x-stim": {
1945
+ "key": "maintenance.urgentFreeDiskGb",
1946
+ "kind": "number",
1947
+ "scopes": [
1948
+ "machine"
1949
+ ],
1950
+ "env": "STIM_MAINTENANCE_URGENT_FREE_DISK_GB"
1951
+ }
1952
+ },
1953
+ "criticalFreeDiskGb": {
1954
+ "description": "Free disk, in GB, below which automatic maintenance enters disk pressure level 3, also at or below budget.hardFloorDiskGb; 0 leaves only the hard floor",
1955
+ "type": "number",
1956
+ "minimum": 0,
1957
+ "default": 6,
1958
+ "x-stim": {
1959
+ "key": "maintenance.criticalFreeDiskGb",
1960
+ "kind": "number",
1961
+ "scopes": [
1962
+ "machine"
1963
+ ],
1964
+ "env": "STIM_MAINTENANCE_CRITICAL_FREE_DISK_GB"
1965
+ }
1966
+ },
1967
+ "pressureProtectMinutes": {
1968
+ "description": "Protection window, in minutes, that replaces maintenance.protectRecentHours at disk pressure levels 2 and 3",
1969
+ "type": "integer",
1970
+ "minimum": 0,
1971
+ "default": 10,
1972
+ "x-stim": {
1973
+ "key": "maintenance.pressureProtectMinutes",
1974
+ "kind": "number",
1975
+ "scopes": [
1976
+ "machine"
1977
+ ],
1978
+ "env": "STIM_MAINTENANCE_PRESSURE_PROTECT_MINUTES"
1979
+ }
1980
+ },
1981
+ "gradleVersionIdleDays": {
1982
+ "description": "At disk pressure level 3, stop idle Gradle and Kotlin daemons, then delete the Gradle version caches and wrapper distributions no registered project pins once their directories are unmodified this many days; 0 turns this off",
1983
+ "type": "integer",
1984
+ "minimum": 0,
1985
+ "default": 7,
1986
+ "x-stim": {
1987
+ "key": "maintenance.gradleVersionIdleDays",
1988
+ "kind": "number",
1989
+ "scopes": [
1990
+ "machine"
1991
+ ],
1992
+ "env": "STIM_MAINTENANCE_GRADLE_VERSION_IDLE_DAYS"
1993
+ }
1994
+ },
1872
1995
  "removeFinishedWorktrees": {
1873
- "description": "Remove clean linked worktrees whose branch or pull request finished, under gc.worktreeGraceMinutes",
1996
+ "description": "Remove clean linked worktrees whose branch or pull request finished, under gc.worktreeGraceMinutes, and at disk pressure level 3 clean, pushed ones with no commits of their own",
1874
1997
  "type": "boolean",
1875
1998
  "default": true,
1876
1999
  "x-stim": {
@@ -2045,7 +2168,7 @@
2045
2168
  "type": "object",
2046
2169
  "properties": {
2047
2170
  "logs": {
2048
- "description": "Debug-level logging. While on, the CLI records its commands, child processes and requests to other Macs, and stim-server records every request with its timings, under STIM_HOME/logs/debug/ with rotation. Nothing is sent anywhere, and records never carry arguments, tokens or tickets",
2171
+ "description": "Debug-level logging. While on, the CLI records its commands, child processes and requests to other machines, and stim-server records every request with its timings, under STIM_HOME/logs/debug/ with rotation. Nothing is sent anywhere, and records never carry arguments, tokens or tickets",
2049
2172
  "type": "boolean",
2050
2173
  "default": false,
2051
2174
  "x-stim": {
@@ -2085,8 +2208,32 @@
2085
2208
  "server": {
2086
2209
  "type": "object",
2087
2210
  "properties": {
2211
+ "buildsPaused": {
2212
+ "description": "Temporarily refuse new incoming builds without interrupting running builds, serving cached artifacts, or changing client approvals; local CLI builds remain available",
2213
+ "type": "boolean",
2214
+ "default": false,
2215
+ "x-stim": {
2216
+ "key": "server.buildsPaused",
2217
+ "kind": "boolean",
2218
+ "scopes": [
2219
+ "machine"
2220
+ ]
2221
+ }
2222
+ },
2223
+ "devicesPaused": {
2224
+ "description": "Temporarily refuse new hosted iOS, Android and macOS sessions without interrupting existing sessions or changing client approvals; local CLI devices remain available",
2225
+ "type": "boolean",
2226
+ "default": false,
2227
+ "x-stim": {
2228
+ "key": "server.devicesPaused",
2229
+ "kind": "boolean",
2230
+ "scopes": [
2231
+ "machine"
2232
+ ]
2233
+ }
2234
+ },
2088
2235
  "acceptClientBuilds": {
2089
- "description": "Whether a Mac approved for builds or device hosting here may update this Mac's stim-server service to that Mac's own stim-server build; releases from npm need no setting",
2236
+ "description": "Whether a machine approved for builds or device hosting here may update this machine's stim-server service to that machine's own stim-server build; releases from npm need no setting",
2090
2237
  "type": "boolean",
2091
2238
  "default": false,
2092
2239
  "x-stim": {
@@ -2098,7 +2245,7 @@
2098
2245
  }
2099
2246
  },
2100
2247
  "maxLoadPerCore": {
2101
- "description": "Load per core (5-minute load average divided by the CPU count) at which this Mac counts as saturated: it declines builds other Macs send it, automatic build placement stops preferring it, and automatic iOS and Android device placement treats it as busy",
2248
+ "description": "Load per core (5-minute load average divided by the CPU count) at which this machine counts as saturated: it declines builds other machines send it, automatic build placement stops preferring it, and automatic iOS and Android device placement treats it as busy",
2102
2249
  "type": "number",
2103
2250
  "minimum": 0.1,
2104
2251
  "default": 2,
@@ -2111,7 +2258,7 @@
2111
2258
  }
2112
2259
  },
2113
2260
  "workerRoot": {
2114
- "description": "Directory where stim-server keeps the checkouts, dependencies and build state of the Macs that build here; default $STIM_HOME/build-worker",
2261
+ "description": "Directory where stim-server keeps the checkouts, dependencies and build state of the machines that build here; default $STIM_HOME/build-worker",
2115
2262
  "type": "string",
2116
2263
  "x-stim": {
2117
2264
  "key": "server.workerRoot",
@@ -2123,7 +2270,7 @@
2123
2270
  }
2124
2271
  },
2125
2272
  "gradleDaemonIdleMinutes": {
2126
- "description": "Minutes the Gradle daemon of an Android build another Mac sent here stays warm after the build; 0 stops it when the build ends",
2273
+ "description": "Minutes the Gradle daemon of an Android build another machine sent here stays warm after the build; 0 stops it when the build ends",
2127
2274
  "type": "integer",
2128
2275
  "minimum": 0,
2129
2276
  "maximum": 35791,
@@ -2143,7 +2290,7 @@
2143
2290
  "type": "object",
2144
2291
  "properties": {
2145
2292
  "machines": {
2146
- "description": "Tailscale names of the remote Macs this Mac may build on and host owned simulators on, each optionally with :<port> of its tailscale serve route (default 7443); what each is used for follows the build and device-host approvals it grants, which doctor --fix asks for separately",
2293
+ "description": "Tailscale names of the remote machines this machine may build on and host owned simulators on, each optionally with :<port> of its tailscale serve route (default 7443); what each is used for follows the build and device-host approvals it grants, which doctor --fix asks for separately",
2147
2294
  "type": "array",
2148
2295
  "items": {
2149
2296
  "type": "string"
@@ -2187,7 +2334,7 @@
2187
2334
  }
2188
2335
  },
2189
2336
  "easFallback": {
2190
- "description": "When ios.remote or android.remote is auto, this Mac is at its concurrency.maxDevices cap and no Mac in remote.machines takes the run, run the simulator or emulator on a billed EAS Simulator instead of waiting or refusing, when eas-cli can",
2337
+ "description": "When ios.remote or android.remote is auto, this machine is at its concurrency.maxDevices cap and no remote.machines entry takes the run, run the simulator or emulator on a billed EAS Simulator instead of waiting or refusing, when eas-cli can",
2191
2338
  "type": "boolean",
2192
2339
  "default": false,
2193
2340
  "x-stim": {
@@ -2202,7 +2349,7 @@
2202
2349
  }
2203
2350
  },
2204
2351
  "build": {
2205
- "description": "Build placement: auto follows remote.buildMode within enabled automatic pool members; local always builds here; a name in remote.machines requires that remote Mac without fallback. The ios, android and macos --remote-build flag overrides this setting and its environment override",
2352
+ "description": "Build placement: auto follows remote.buildMode within enabled automatic pool members; local always builds here; a name in remote.machines requires that remote machine without fallback. The ios, android and macos --remote-build flag overrides this setting and its environment override",
2206
2353
  "type": "string",
2207
2354
  "pattern": "^[A-Za-z0-9][A-Za-z0-9.-]*(?::(?!0+$)(?:[0-5]?[0-9]{1,4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]))?$",
2208
2355
  "default": "auto",
@@ -2216,7 +2363,7 @@
2216
2363
  }
2217
2364
  },
2218
2365
  "buildMode": {
2219
- "description": "When remote.build is auto, where iOS simulator Debug, Android emulator debug and macOS SwiftPM Debug builds run: auto builds here while this Mac has a free concurrency.maxBuilds slot and its load is under server.maxLoadPerCore, and otherwise builds on a less loaded Mac in remote.machines; force builds on an enabled remote Mac whenever one can take the build; off builds here when local is enabled in remote.buildPoolDisabled",
2366
+ "description": "When remote.build is auto, where iOS simulator Debug, Android emulator debug and macOS SwiftPM Debug builds run: auto builds here while this machine has a free concurrency.maxBuilds slot and its load is under server.maxLoadPerCore, and otherwise builds on a less loaded machine in remote.machines; force builds on an enabled remote machine whenever one can take the build; off builds here when local is enabled in remote.buildPoolDisabled",
2220
2367
  "type": "string",
2221
2368
  "enum": [
2222
2369
  "auto",