stim 1.26.0 → 1.28.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 (172) hide show
  1. package/dist/{acquire-artifact-DVsvSwf7.mjs → acquire-artifact-CKByBnTD.mjs} +2 -2
  2. package/dist/{act-C-BqJEpu.mjs → act-8iswyfRa.mjs} +319 -126
  3. package/dist/{activity-Lnb_EoRY.mjs → activity-DRMg4_mD.mjs} +6 -6
  4. package/dist/{agent-device-usage-output-DbXy5tg8.mjs → agent-device-usage-output-tE90fDdo.mjs} +5 -5
  5. package/dist/{android-CiNQw3kq.mjs → android-CHmSU4B4.mjs} +101 -45
  6. package/dist/{android-DTAcRwBC.mjs → android-CsPs-7Mg.mjs} +23 -18
  7. package/dist/{android-B5P81yI-.mjs → android-D577ApKG.mjs} +37 -15
  8. package/dist/{android-CSbLRD53.mjs → android-YWCbmkVK.mjs} +5 -5
  9. package/dist/{android-cas-BTVhZ0Qs.mjs → android-cas-_aDifWW0.mjs} +4 -4
  10. package/dist/android-cas-compiler.mjs +1 -1
  11. package/dist/api.d.mts +14 -4
  12. package/dist/api.mjs +39 -29
  13. package/dist/{apk-signed-tail-B46U9hXc.mjs → apk-signed-tail-CQbwM0qO.mjs} +2 -2
  14. package/dist/{artifact-CsAP6AGK.mjs → artifact-DYtHjxsn.mjs} +19 -24
  15. package/dist/{artifact-BxREhAaV.mjs → artifact-bFkfD3vf.mjs} +18 -23
  16. package/dist/{artifact-platform-CMRc0M_r.mjs → artifact-platform-BO2Uvk_A.mjs} +1 -1
  17. package/dist/auto-placement-BVqs8AJi.mjs +350 -0
  18. package/dist/{browser-web-rYPyqhTf.mjs → browser-web-BUaLcafY.mjs} +16 -16
  19. package/dist/{build-DPhApsjV.mjs → build-cQURy_KW.mjs} +35 -31
  20. package/dist/{build-cache-DrXNCy3T.mjs → build-cache-CZULpXVl.mjs} +43 -21
  21. package/dist/{build-plan-DrMMdayj.mjs → build-plan-CxoYfxsc.mjs} +7 -6
  22. package/dist/{cache-manifest-BEZ9Bpl1.mjs → cache-manifest-DEjqGGat.mjs} +1 -1
  23. package/dist/cache-manifest.mjs +1 -1
  24. package/dist/{budget-k2mZGvvk.mjs → ccache-Bm_A1xX9.mjs} +562 -1604
  25. package/dist/{cdp-BG3FJCCx.mjs → cdp-C3svvXiE.mjs} +1 -1
  26. package/dist/{chrome-BvV8ssI3.mjs → chrome-DyDWurWG.mjs} +1 -1
  27. package/dist/{cli-BBKFGOFl.mjs → cli-CpbaLDa7.mjs} +24 -23
  28. package/dist/cli.mjs +1 -1
  29. package/dist/{client-D9CaRks-.mjs → client-YxCer6ab.mjs} +84 -65
  30. package/dist/collector-run.d.mts +10 -9
  31. package/dist/collector-run.mjs +42 -27
  32. package/dist/{command-output-BF5AR2dL.mjs → command-output-C8R1KIvi.mjs} +1 -1
  33. package/dist/{config-BktkY_zC.mjs → config-Cn94Jc0g.mjs} +23 -4
  34. package/dist/{dependencies-Dw6wfQdv.mjs → dependencies-DKeMzwXY.mjs} +108 -60
  35. package/dist/{dependency-state-DFB41vfr.mjs → dependency-state-CojA2o0t.mjs} +14 -7
  36. package/dist/{detached-entry-KmieOKgN.mjs → detached-entry-BPlSaCwC.mjs} +1 -1
  37. package/dist/{dev-client-DXR_tPVY.mjs → dev-client-D-BW5uiq.mjs} +2 -2
  38. package/dist/{device-BK-zwyd_.mjs → device-CgXfbcz5.mjs} +25 -24
  39. package/dist/{device-capacity-DCSvUF0l.mjs → device-capacity-Cd8lMSn3.mjs} +175 -22
  40. package/dist/{device-BcYeHSFu.mjs → device-eeK95Lir.mjs} +16 -16
  41. package/dist/device-host-worker.mjs +22 -20
  42. package/dist/{device-ios-BLGfXQeK.mjs → device-ios-Cc9ulihb.mjs} +251 -23
  43. package/dist/{device-lease-run-BmvIl7bI.mjs → device-lease-run-Mtzp4THX.mjs} +5 -5
  44. package/dist/{device-pool-Dluix4H3.mjs → device-pool-eRz6DtCg.mjs} +3 -3
  45. package/dist/{doctor-Bhg1ii70.mjs → doctor-Bz_ouYAW.mjs} +28 -16
  46. package/dist/{doctor-Cp-BhzTS.mjs → doctor-C2QyhejJ.mjs} +1 -1
  47. package/dist/{doctor-fingerprint-D3a04Mhu.mjs → doctor-fingerprint-o7ZI9YLn.mjs} +1 -1
  48. package/dist/doctor-fingerprint.mjs +1 -1
  49. package/dist/{doctor-watchman-BStK05nt.mjs → doctor-watchman-BTW82PDC.mjs} +5 -5
  50. package/dist/{doctor-OnwP94ne.mjs → doctor-xf7ht-5n.mjs} +76 -32
  51. package/dist/{eas-build-Dsj93iM9.mjs → eas-build-Dm6q68QY.mjs} +4 -4
  52. package/dist/eas-fallback-DajNpxf-.mjs +87 -0
  53. package/dist/{eas-session-ledger-B5vygTiD.mjs → eas-session-ledger-CBFo_Uo1.mjs} +37 -8
  54. package/dist/{error-diagnostics-BOOm9Db7.mjs → error-diagnostics-D1QfyJzQ.mjs} +51 -25
  55. package/dist/{exec-VFRpu1iA.mjs → exec-aULwtNly.mjs} +57 -6
  56. package/dist/{gc-ldgr-hXU.mjs → gc-DM5oMm-J.mjs} +161 -45
  57. package/dist/{gc-C_79zvz4.mjs → gc-PLFwb0k4.mjs} +2 -2
  58. package/dist/{guide-D7K0X6KG.mjs → guide-C4I46XAk.mjs} +674 -213
  59. package/dist/{hosted-android-Bk54ej6n.mjs → hosted-android-DlaKLbW0.mjs} +6 -7
  60. package/dist/{hosted-client-fHO8yqzc.mjs → hosted-client-DDRm9hPJ.mjs} +51 -20
  61. package/dist/{hosted-logs-BfbmDM4M.mjs → hosted-logs-bSebOKbz.mjs} +8 -8
  62. package/dist/{hosted-logs-sync-B0pvmOk-.mjs → hosted-logs-sync-B0c53W_c.mjs} +5 -5
  63. package/dist/{hosted-macos-CPl0OZ9h.mjs → hosted-macos-B7-8Pv4H.mjs} +6 -6
  64. package/dist/{hosted-native-CpDB3-PO.mjs → hosted-native-CDe33CSr.mjs} +22 -18
  65. package/dist/{idle-shutdown-FfxL1KdG.mjs → idle-shutdown-CQ0QwU9J.mjs} +7 -8
  66. package/dist/{ios-WxpDJ5T1.mjs → ios-B1DYuOjh.mjs} +27 -11
  67. package/dist/{ios-B7-lToDu.mjs → ios-B1HvafEG.mjs} +8 -8
  68. package/dist/{ios-ENC4sRPA.mjs → ios-CsRVik64.mjs} +7 -1
  69. package/dist/{ios-device-CsyryvpT.mjs → ios-device-B4elVFKi.mjs} +3 -3
  70. package/dist/{ios-device-B6HK951C.mjs → ios-device-CiBA-er9.mjs} +1 -1
  71. package/dist/{ios-project-Ty5pHl9O.mjs → ios-project-CR4mZ7CH.mjs} +22 -9
  72. package/dist/{ios-signing-4c_qXvQm.mjs → ios-signing-BJzvDPfU.mjs} +2 -2
  73. package/dist/{ios-state-Cw7-WQf2.mjs → ios-state-BPHqMDdZ.mjs} +1 -1
  74. package/dist/{ios-ftxdQbJS.mjs → ios-tg4tp3q1.mjs} +94 -37
  75. package/dist/{js-swap-BMhXYVDS.mjs → js-swap-CVpSW1fn.mjs} +8 -7
  76. package/dist/{launch-DOp8RjDz.mjs → launch-DoySmhjz.mjs} +42 -38
  77. package/dist/lightened-DFAJgfIk.mjs +171 -0
  78. package/dist/{logs-s1dRIG1E.mjs → logs-BGsW7iPe.mjs} +46 -21
  79. package/dist/{logs-dQDki6Tt.mjs → logs-DI5ILkYB.mjs} +7 -7
  80. package/dist/{macos-DBBPNnIA.mjs → macos-Bo9st9Di.mjs} +73 -38
  81. package/dist/{macos-B0VEV3Gf.mjs → macos-C12RMJQF.mjs} +1 -1
  82. package/dist/macos-run.mjs +1 -1
  83. package/dist/maintenance-run.mjs +7 -7
  84. package/dist/{metro-CjC10t6E.mjs → metro-BnhXV2Mj.mjs} +4 -4
  85. package/dist/{metro-gateway-B6_PgD0L.mjs → metro-request-DXyKjVDF.mjs} +26 -47
  86. package/dist/{named-ports-BG0j1X4g.mjs → named-ports-Phoi2POD.mjs} +3 -3
  87. package/dist/{native-android-CL2NGiik.mjs → native-android-vxgtLD7w.mjs} +17 -18
  88. package/dist/{native-gradle-artifact-inputs-btSUulme.mjs → native-gradle-artifact-inputs-BN96IVw8.mjs} +5 -5
  89. package/dist/{native-runtime-Dq3RBcON.mjs → native-runtime-DG4Rodzj.mjs} +9 -8
  90. package/dist/{native-source-D-4Dh4JU.mjs → native-source-D1AzWiuP.mjs} +1 -1
  91. package/dist/{native-xcode-inputs-BW7H8JGu.mjs → native-xcode-inputs-BKarCoKg.mjs} +3 -3
  92. package/dist/{native-xcode-ios-CBXJAxfC.mjs → native-xcode-ios-8A-efGa5.mjs} +17 -17
  93. package/dist/{ndjson-CgE_o3Dl.mjs → ndjson-Dy2W4vRa.mjs} +2 -2
  94. package/dist/offload-worker.mjs +23 -24
  95. package/dist/{operation-exec-CUZifp6Z.mjs → operation-exec-Dp4FuIGR.mjs} +1 -1
  96. package/dist/{ownership-BjYvMNX_.mjs → ownership-DJCq48IJ.mjs} +3 -3
  97. package/dist/{page-CzEA_-5_.mjs → page-CQJlcj3x.mjs} +2 -2
  98. package/dist/{phone-reverses-lmj6kcPm.mjs → phone-reverses-BzTa8g1F.mjs} +18 -4
  99. package/dist/{placement-log-DIFPQAXp.mjs → placement-log-Cvhta5ib.mjs} +3 -3
  100. package/dist/{plan-placement-IiiSQhtz.mjs → plan-placement-WkZdccuN.mjs} +36 -10
  101. package/dist/{ports-C2_w1Z0Z.mjs → ports-UV4mJgyC.mjs} +3 -3
  102. package/dist/{prebuild-gmouvmJZ.mjs → prebuild-k8d_tqOq.mjs} +16 -17
  103. package/dist/{project-BUu6Is4k.mjs → project-Sui52ywT.mjs} +17 -17
  104. package/dist/{pull-request-CbNlkFok.mjs → pull-request-BzLmzAIQ.mjs} +2 -2
  105. package/dist/pull-requests.mjs +1 -1
  106. package/dist/{react-native-android-Cgg99REN.mjs → react-native-android-DANlGGQf.mjs} +32 -26
  107. package/dist/{react-native-build-oNwqF5Wb.mjs → react-native-build-jUHnIfZ9.mjs} +9 -9
  108. package/dist/{react-native-doctor-BhA3KUte.mjs → react-native-doctor-DCWRjS7K.mjs} +6 -6
  109. package/dist/{react-native-ios-B-nf4rjN.mjs → react-native-ios-BwMsFMXi.mjs} +23 -17
  110. package/dist/{recordings-BldzVKq0.mjs → recordings-iy0mkm7b.mjs} +1 -1
  111. package/dist/{registration-CN6nlhcD.mjs → registration-BwCe64PH.mjs} +1 -1
  112. package/dist/{reload-C9OduiNc.mjs → reload-Bj7QyuDy.mjs} +1 -1
  113. package/dist/{reload-CCR7KC_l.mjs → reload-c_uEyc_X.mjs} +16 -16
  114. package/dist/{remote-C8S7FlPF.mjs → remote-DFlpAqdV.mjs} +23 -10
  115. package/dist/{result-7F5hEwQJ.mjs → result-BIcXREyC.mjs} +32 -21
  116. package/dist/{run-BqB0v3pt.mjs → run-y2HwyNvg.mjs} +3 -3
  117. package/dist/{selection-BNPBZ6T5.mjs → selection-CXgM_VK_.mjs} +37 -29
  118. package/dist/{server-bare-A75rDIdD.mjs → server-bare-2N4vSRZG.mjs} +2 -2
  119. package/dist/{server-command-CssKFOD7.mjs → server-command-qneHPcRI.mjs} +6 -6
  120. package/dist/{server-expo-eQfOdydb.mjs → server-expo-Ccm9zB0o.mjs} +4 -4
  121. package/dist/server-expo-WulkyAwT.mjs +2 -0
  122. package/dist/{settings-CosW5X-S.mjs → settings-CY5JZlNN.mjs} +375 -62
  123. package/dist/{settings-OaGQF-ri.mjs → settings-Cfd_Q6ys.mjs} +24 -3
  124. package/dist/settings-payload-DwQiEMCn.mjs +257 -0
  125. package/dist/settings-write-CJpjkeAd.mjs +185 -0
  126. package/dist/settings.schema.json +51 -33
  127. package/dist/{simslim-Bax1qcxu.mjs → simslim-DCQwzoPy.mjs} +2 -2
  128. package/dist/{slot-launch-CYCEhfBY.mjs → slot-launch-BpmanOnk.mjs} +6 -6
  129. package/dist/{stage-jKQ7CP_B.mjs → stage-CmugrSG5.mjs} +2 -2
  130. package/dist/{start-C_wefNKB.mjs → start-CHH3zgIU.mjs} +1 -1
  131. package/dist/{start-_BdJ32cZ.mjs → start-DYLk6GXn.mjs} +27 -22
  132. package/dist/{state-C1i2nMOP.mjs → state-B2QJbDwz.mjs} +4 -4
  133. package/dist/{state-C4OdpOTF.mjs → state-BXC1Lfxw.mjs} +2 -2
  134. package/dist/{stats-tEpEEH-u.mjs → stats-DxKqhcGp.mjs} +5 -5
  135. package/dist/{stats-report-n2U5plhk.mjs → stats-report-B86Cv7Uc.mjs} +1 -1
  136. package/dist/{status-CRXz-Gtj.mjs → status-7ULDEJ1e.mjs} +1 -1
  137. package/dist/{status-DQrqodLj.mjs → status-BHCkOZga.mjs} +51 -28
  138. package/dist/{status-B9M7x4CZ.mjs → status-D_Ljc89p.mjs} +1 -1
  139. package/dist/{status-watch-B5wDIH_o.mjs → status-watch-BmsPqYSU.mjs} +18 -7
  140. package/dist/status-worker.mjs +1 -1
  141. package/dist/{stim-desktop-6Dqp-gjX.mjs → stim-desktop-CbWo21kb.mjs} +2 -2
  142. package/dist/{stop-DRqQVqDJ.mjs → stop-D-dGE9gh.mjs} +21 -20
  143. package/dist/{stop-ySNQHXMG.mjs → stop-DQvE-P76.mjs} +10 -6
  144. package/dist/{stop-CmdNhGlC.mjs → stop-b2pouxAO.mjs} +2 -2
  145. package/dist/supervisor-run.mjs +237 -63
  146. package/dist/{support-BGSCBoib.mjs → support-04IhBUTT.mjs} +6 -6
  147. package/dist/{support-CVOCp0Ft.mjs → support-BtlJKjyQ.mjs} +12 -11
  148. package/dist/{swiftpm-artifact-inputs-CfUzt_0Z.mjs → swiftpm-artifact-inputs-Cj2UpmPN.mjs} +4 -4
  149. package/dist/{swiftpm-macos-x6HPpl_2.mjs → swiftpm-macos-BhF2stxY.mjs} +11 -12
  150. package/dist/{toolchain-J4J8MrAr.mjs → toolchain-mLA_km-b.mjs} +321 -15
  151. package/dist/{toolchain-DoavrvIA.mjs → toolchain-qktiV8xc.mjs} +1 -1
  152. package/dist/{trigger-KVaWWNyn.mjs → trigger-DMou-X7j.mjs} +2 -2
  153. package/dist/trigger-qp2ji5wE.mjs +2 -0
  154. package/dist/{warm-progress-DvhTX_eu.mjs → warm-progress-B0Vpuvpl.mjs} +731 -727
  155. package/dist/{web-Bihbf7-u.mjs → web-BYmK0TjL.mjs} +9 -9
  156. package/dist/web-CrVLVQZY.mjs +2 -0
  157. package/dist/web-run.mjs +9 -9
  158. package/dist/windows-report.mjs +1 -1
  159. package/dist/{workspace-state-pUg3_RRA.mjs → workspace-state-kIZrdlSg.mjs} +3 -3
  160. package/dist/{workspaces-g2lhdBXF.mjs → workspaces-CmOcceDN.mjs} +1879 -74
  161. package/dist/{worktree-BKuklFh_.mjs → worktree-2dk5edza.mjs} +933 -534
  162. package/dist/worktree-nliAoHxB.mjs +2 -0
  163. package/package.json +7 -7
  164. package/dist/ccache-Bx5TbxwG.mjs +0 -143
  165. package/dist/eas-fallback-FwkeqqGi.mjs +0 -381
  166. package/dist/hosted-ios-D55QhYaD.mjs +0 -15
  167. package/dist/native-run-BfIDFKLH.mjs +0 -275
  168. package/dist/server-expo-wvcj5tiZ.mjs +0 -2
  169. package/dist/settings-write-QjYfSrjF.mjs +0 -360
  170. package/dist/trigger-DzJu208Q.mjs +0 -2
  171. package/dist/web-BFLueSh-.mjs +0 -2
  172. package/dist/worktree-Bm4cb-nU.mjs +0 -2
@@ -0,0 +1,257 @@
1
+ import { _ as loadConfig$1, f as getConfigPath$1, h as getRepoSettings, m as getProjectSettings } from "./config-Cn94Jc0g.mjs";
2
+ import { a as killOnAbort, i as gitPath } from "./operation-exec-Dp4FuIGR.mjs";
3
+ import { K as removedMachineSettings, V as readCommittedSettings, st as unknownSettingKeys } from "./settings-CY5JZlNN.mjs";
4
+ import { r as findCommandWorkspaceAsync } from "./project-Sui52ywT.mjs";
5
+ import { i as stimDesktopInstalledAsync, n as settingDefault, r as stimDesktopInstalled } from "./stim-desktop-CbWo21kb.mjs";
6
+ import { join } from "node:path";
7
+ import { SETTINGS, SETTING_SCOPES, coerceSettingText, isJsonObject, replacementAdvice, settingDefinition, settingValueError } from "@stim-cli/core/state";
8
+ //#region src/workspace/settings-payload.ts
9
+ const MASK = "********";
10
+ const PRECEDENCE = [
11
+ "workspace",
12
+ "repo",
13
+ "committed",
14
+ "machine"
15
+ ];
16
+ var SettingsRefusal = class extends Error {
17
+ code;
18
+ remedy;
19
+ constructor(code, message, remedy) {
20
+ super(message);
21
+ this.code = code;
22
+ this.remedy = remedy;
23
+ }
24
+ };
25
+ /** Reads layers using exactly `env`; `signal` kills Git children. `resolvedProject` pins a mutation's readback target. */
26
+ async function readSettingsContext(cwd, env, note = () => {}, signal, resolvedProject) {
27
+ const repoRootOf = (dir) => gitPath(dir, ["--show-toplevel"], env, signal);
28
+ const projectPath = resolvedProject === void 0 ? await findCommandWorkspaceAsync(cwd, repoRootOf, note) : resolvedProject;
29
+ const start = projectPath ?? cwd;
30
+ const [common, root] = await Promise.all([gitPath(start, ["--path-format=absolute", "--git-common-dir"], env, signal), repoRootOf(start)]);
31
+ signal?.throwIfAborted();
32
+ const repoRoot = common ? root ?? projectPath : null;
33
+ const app = projectPath ?? repoRoot;
34
+ const repository = repoRoot ?? projectPath;
35
+ const committed = readCommittedSettings(app);
36
+ let installed;
37
+ return {
38
+ projectPath,
39
+ gitCommonDir: common,
40
+ repoRoot,
41
+ machine: loadConfig$1(),
42
+ workspace: projectPath ? getProjectSettings(projectPath) : {},
43
+ repo: common ? getRepoSettings(common) : {},
44
+ committed: {
45
+ app: committed,
46
+ repository: repository === app ? committed : readCommittedSettings(repository)
47
+ },
48
+ env,
49
+ desktopInstalled: () => installed ??= stimDesktopInstalled(process.platform, env)
50
+ };
51
+ }
52
+ function valueAt(settings, key) {
53
+ let node = settings;
54
+ for (const segment of key.split(".")) {
55
+ if (!isJsonObject(node)) return void 0;
56
+ node = node[segment];
57
+ }
58
+ return node;
59
+ }
60
+ function committedDirectory(context, setting) {
61
+ return setting?.committedAt === "repository" ? repositoryDirectory(context) : appDirectory(context);
62
+ }
63
+ function repositoryDirectory(context) {
64
+ return context.repoRoot ?? context.projectPath;
65
+ }
66
+ function appDirectory(context) {
67
+ return context.projectPath ?? context.repoRoot;
68
+ }
69
+ function layerFile(context, scope, setting) {
70
+ const machineFile = getConfigPath$1();
71
+ if (scope === "machine") return machineFile;
72
+ if (scope === "workspace") return context.projectPath ? `${machineFile} (projects["${context.projectPath}"].settings)` : null;
73
+ if (scope === "repo") return context.gitCommonDir ? `${machineFile} (repos["${context.gitCommonDir}"].settings)` : null;
74
+ const directory = committedDirectory(context, setting);
75
+ return directory ? join(directory, ".stim.json") : null;
76
+ }
77
+ function layerSettings(context, scope, setting) {
78
+ if (scope === "machine") return context.machine ?? {};
79
+ if (scope === "workspace") return context.workspace;
80
+ if (scope === "repo") return context.repo;
81
+ return setting?.committedAt === "repository" ? context.committed.repository : context.committed.app;
82
+ }
83
+ function masked(setting, value) {
84
+ return setting.sensitive && value !== void 0 && value !== null ? MASK : value;
85
+ }
86
+ function settingExample(setting) {
87
+ switch (setting.type.kind) {
88
+ case "boolean": return "false";
89
+ case "number": return String(setting.default ?? setting.type.minimum ?? 1);
90
+ case "strings": return `'["node_modules"]'`;
91
+ case "object": return `'{"key": "value"}'`;
92
+ case "choice": return setting.type.choices[0];
93
+ default: return "<value>";
94
+ }
95
+ }
96
+ function envSettingValue(setting, raw) {
97
+ const value = coerceSettingText(setting, raw);
98
+ const error = settingValueError(setting, value);
99
+ 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)}\`.`);
100
+ return value;
101
+ }
102
+ /** The refusal `stim settings get` gives for an invalid environment override of `key`, or null when it is unset, blank or valid. */
103
+ function envSettingRefusal(key, env) {
104
+ const setting = settingDefinition(key);
105
+ const raw = setting?.env ? env[setting.env]?.trim() : void 0;
106
+ if (!setting || !raw) return null;
107
+ try {
108
+ envSettingValue(setting, raw);
109
+ return null;
110
+ } catch (error) {
111
+ if (error instanceof SettingsRefusal) return error;
112
+ throw error;
113
+ }
114
+ }
115
+ /** The refusal for an invalid configured value of a machine setting, or null when it is unset or valid. */
116
+ function configSettingRefusal(key, value) {
117
+ const setting = settingDefinition(key);
118
+ const checked = typeof value === "string" ? value.trim() : value;
119
+ const error = setting && checked !== void 0 && checked !== null ? settingValueError(setting, checked) : null;
120
+ if (!error) return null;
121
+ return new SettingsRefusal("STIM_BAD_ARG", `Invalid ${key} setting ${JSON.stringify(value)}. Expected ${error}.`, `Replace it with \`stim settings set ${key} <value>\`; \`stim guide settings\` lists the values it takes.`);
122
+ }
123
+ /** The refusal for an invalid `remote.build` that placement would read: the environment override first, else the configured value. */
124
+ function buildPreferenceRefusal(env, configured) {
125
+ const fromEnvironment = envSettingRefusal("remote.build", env);
126
+ if (fromEnvironment) return fromEnvironment;
127
+ return env.STIM_REMOTE_BUILD?.trim() ? null : configSettingRefusal("remote.build", configured);
128
+ }
129
+ function settingEntry(context, setting) {
130
+ const layers = {};
131
+ for (const scope of PRECEDENCE) {
132
+ if (!setting.scopes.includes(scope) || layerFile(context, scope, setting) === null) continue;
133
+ const value = valueAt(layerSettings(context, scope, setting), setting.key);
134
+ if (value !== void 0) layers[scope] = masked(setting, value);
135
+ }
136
+ const envValue = setting.env ? context.env[setting.env] : void 0;
137
+ const env = setting.env && envValue ? {
138
+ name: setting.env,
139
+ value: String(masked(setting, envValue))
140
+ } : void 0;
141
+ const scopedHome = !env && setting.scopedHomeValue !== void 0 && context.env.STIM_HOME;
142
+ const ci = !env && setting.ciValue !== void 0 && context.env.CI;
143
+ const winner = PRECEDENCE.find((scope) => scope in layers);
144
+ let value = null;
145
+ let origin = null;
146
+ let defaultReason = null;
147
+ if (env) {
148
+ value = masked(setting, envSettingValue(setting, envValue));
149
+ origin = "env";
150
+ } else if (scopedHome) {
151
+ value = setting.scopedHomeValue;
152
+ origin = "env";
153
+ } else if (ci) {
154
+ value = setting.ciValue;
155
+ origin = "env";
156
+ } else if (winner) {
157
+ value = layers[winner];
158
+ origin = winner;
159
+ } else if (setting.default !== void 0) {
160
+ const fallback = settingDefault(setting, context.desktopInstalled);
161
+ value = fallback.value;
162
+ origin = "default";
163
+ defaultReason = fallback.reason;
164
+ }
165
+ return {
166
+ key: setting.key,
167
+ value,
168
+ origin,
169
+ layers,
170
+ ...env ? { env } : scopedHome ? { env: {
171
+ name: "STIM_HOME",
172
+ value: context.env.STIM_HOME
173
+ } } : ci ? { env: {
174
+ name: "CI",
175
+ value: context.env.CI
176
+ } } : {},
177
+ ...setting.sensitive ? { sensitive: true } : {},
178
+ ...defaultReason ? { defaultReason } : {}
179
+ };
180
+ }
181
+ function unknownEntries(context) {
182
+ const sources = [
183
+ {
184
+ scope: "machine",
185
+ file: layerFile(context, "machine", null),
186
+ settings: {
187
+ optimizations: context.machine?.optimizations,
188
+ ...removedMachineSettings(context.machine)
189
+ }
190
+ },
191
+ {
192
+ scope: "workspace",
193
+ file: layerFile(context, "workspace", null),
194
+ settings: layerSettings(context, "workspace", null)
195
+ },
196
+ {
197
+ scope: "repo",
198
+ file: layerFile(context, "repo", null),
199
+ settings: layerSettings(context, "repo", null)
200
+ },
201
+ {
202
+ scope: "committed",
203
+ file: layerFile(context, "committed", null),
204
+ settings: layerSettings(context, "committed", null)
205
+ }
206
+ ];
207
+ const repository = repositoryDirectory(context);
208
+ if (repository && repository !== appDirectory(context)) sources.push({
209
+ scope: "committed",
210
+ file: join(repository, ".stim.json"),
211
+ settings: context.committed.repository
212
+ });
213
+ return sources.flatMap(({ scope, file, settings }) => file === null ? [] : unknownSettingKeys(settings).map((key) => {
214
+ const value = valueAt(settings, key);
215
+ const command = replacementAdvice(key, value)?.command;
216
+ const entry = {
217
+ key,
218
+ scope,
219
+ file,
220
+ value
221
+ };
222
+ return command ? Object.assign(entry, { replacement: command }) : entry;
223
+ }));
224
+ }
225
+ function settingsPayload(context) {
226
+ const files = {};
227
+ for (const scope of SETTING_SCOPES) {
228
+ const file = layerFile(context, scope, null);
229
+ if (file) files[scope] = file;
230
+ }
231
+ return {
232
+ project: context.projectPath,
233
+ files,
234
+ settings: SETTINGS.map((setting) => settingEntry(context, setting)),
235
+ unknown: unknownEntries(context)
236
+ };
237
+ }
238
+ /** Every setting with its effective value and layers; looks Stim Desktop up asynchronously, and only when a default depends on it. */
239
+ async function readSettingsPayload(context, signal) {
240
+ let asked = false;
241
+ const payload = settingsPayload({
242
+ ...context,
243
+ desktopInstalled: () => {
244
+ asked = true;
245
+ return false;
246
+ }
247
+ });
248
+ if (!asked) return payload;
249
+ const installed = await stimDesktopInstalledAsync(process.platform, context.env, killOnAbort(signal));
250
+ signal?.throwIfAborted();
251
+ return settingsPayload({
252
+ ...context,
253
+ desktopInstalled: () => installed
254
+ });
255
+ }
256
+ //#endregion
257
+ export { envSettingRefusal as a, readSettingsPayload as c, unknownEntries as d, configSettingRefusal as i, settingEntry as l, buildPreferenceRefusal as n, layerFile as o, committedDirectory as r, readSettingsContext as s, SettingsRefusal as t, settingExample as u };
@@ -0,0 +1,185 @@
1
+ import { u as withWorkspaceProcessLock } from "./exec-aULwtNly.mjs";
2
+ import { A as writeConfigSettingAsync, O as withConfigLockAsync$1, f as getConfigPath$1 } from "./config-Cn94Jc0g.mjs";
3
+ import { t as NO_PROJECT_REFUSAL } from "./project-Sui52ywT.mjs";
4
+ import { i as stimDesktopInstalledAsync } from "./stim-desktop-CbWo21kb.mjs";
5
+ import { n as recordingLayers, t as deleteTurnedOffRecordings } from "./recordings-iy0mkm7b.mjs";
6
+ import { d as unknownEntries, l as settingEntry, o as layerFile, r as committedDirectory, s as readSettingsContext, t as SettingsRefusal, u as settingExample } from "./settings-payload-DwQiEMCn.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 { REPLACED_SETTINGS, SETTING_SCOPES, coerceSettingText, isJsonObject, isSensitiveReference, replacementAdvice, settingDefinition, settingValueError } from "@stim-cli/core/state";
11
+ //#region src/workspace/settings-write.ts
12
+ function requireSetting(key, raw) {
13
+ const setting = settingDefinition(key);
14
+ if (setting) return setting;
15
+ const replaced = Object.hasOwn(REPLACED_SETTINGS, key) ? REPLACED_SETTINGS[key] : void 0;
16
+ if (replaced) {
17
+ const command = replacementAdvice(key, raw)?.command;
18
+ throw new SettingsRefusal("STIM_BAD_ARG", `${JSON.stringify(key)} was replaced by ${replaced.key} and is no longer read by Stim.`, `${command ? `Run \`${command}\`` : `Set ${replaced.key} (\`stim guide settings\` lists its values)`}, then run \`stim settings unset ${key}\` to remove the leftover key.`);
19
+ }
20
+ 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.");
21
+ }
22
+ function parseScopeName(raw) {
23
+ if (!SETTING_SCOPES.includes(raw)) throw new SettingsRefusal("STIM_BAD_ARG", `--scope takes ${SETTING_SCOPES.join(", ")}, not ${JSON.stringify(raw)}.`, null);
24
+ return raw;
25
+ }
26
+ function readScope(raw, setting) {
27
+ if (raw === void 0) {
28
+ if (setting.scopes.length === 1) return setting.scopes[0];
29
+ 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("|")}.`);
30
+ }
31
+ const scope = parseScopeName(raw);
32
+ 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("|")}.`);
33
+ return scope;
34
+ }
35
+ function requireLayer(context, scope, setting) {
36
+ const file = layerFile(context, scope, setting);
37
+ if (file) return file;
38
+ 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.");
39
+ throw new SettingsRefusal(NO_PROJECT_REFUSAL.code, NO_PROJECT_REFUSAL.message, NO_PROJECT_REFUSAL.remedy);
40
+ }
41
+ function parseSettingValue(setting, raw) {
42
+ 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]}` : ""}`);
43
+ const kind = setting.type.kind;
44
+ const value = coerceSettingText(setting, raw);
45
+ const error = settingValueError(setting, value);
46
+ if (error) throw invalid(kind === "strings" || kind === "object" ? `${error}, written as JSON` : error);
47
+ return value;
48
+ }
49
+ function committedTarget(context, setting) {
50
+ const directory = committedDirectory(context, setting);
51
+ if (!directory) throw new SettingsRefusal(NO_PROJECT_REFUSAL.code, NO_PROJECT_REFUSAL.message, NO_PROJECT_REFUSAL.remedy);
52
+ return join(directory, ".stim.json");
53
+ }
54
+ function writeCommitted(file, key, value, signal) {
55
+ return withConfigLockAsync$1(() => {
56
+ const exists = existsSync(file);
57
+ const text = exists ? readFileSync(file, "utf-8") : "";
58
+ let parsed = {};
59
+ if (exists && text.trim() !== "") try {
60
+ parsed = JSON.parse(text);
61
+ } catch (error) {
62
+ throw new SettingsRefusal("STIM_BAD_ARG", `${file} is not valid JSON: ${error.message}`, `Repair ${file}, then run the command again.`);
63
+ }
64
+ if (!isJsonObject(parsed)) throw new SettingsRefusal("STIM_BAD_ARG", `${file} is not a JSON object.`, `Repair ${file}, then run the command again.`);
65
+ const parts = key.split(".");
66
+ const chain = [parsed];
67
+ for (const part of parts.slice(0, -1)) {
68
+ const node = chain.at(-1);
69
+ if (!isJsonObject(node[part])) {
70
+ if (value === void 0) return false;
71
+ node[part] = {};
72
+ }
73
+ chain.push(node[part]);
74
+ }
75
+ const leaf = parts.at(-1);
76
+ const parent = chain.at(-1);
77
+ if (value === void 0) {
78
+ if (!(leaf in parent)) return false;
79
+ delete parent[leaf];
80
+ for (let index = chain.length - 1; index > 0; index--) {
81
+ if (Object.keys(chain[index]).length > 0) break;
82
+ delete chain[index - 1][parts[index - 1]];
83
+ }
84
+ } else parent[leaf] = value;
85
+ const indent = /\n([ \t]+)"/.exec(text)?.[1] ?? 2;
86
+ const tmp = `${file}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
87
+ writeFileSync(tmp, `${JSON.stringify(parsed, null, indent)}\n`);
88
+ try {
89
+ renameSync(tmp, file);
90
+ } catch (error) {
91
+ try {
92
+ unlinkSync(tmp);
93
+ } catch {}
94
+ throw error;
95
+ }
96
+ return true;
97
+ }, signal);
98
+ }
99
+ function writeSetting(context, setting, scope, value, signal) {
100
+ if (scope === "committed") return writeCommitted(committedTarget(context, setting), setting.key, value, signal);
101
+ if (scope === "machine") return writeConfigSettingAsync({ scope }, setting.key, value, signal);
102
+ if (scope === "workspace") return writeConfigSettingAsync({
103
+ scope,
104
+ projectPath: context.projectPath
105
+ }, setting.key, value, signal);
106
+ return writeConfigSettingAsync({
107
+ scope,
108
+ gitCommonDir: context.gitCommonDir
109
+ }, setting.key, value, signal);
110
+ }
111
+ /** Shares CLI set/unset validation and side effects; after commit, recording cleanup finishes despite cancellation. */
112
+ async function mutateSetting(request, { cwd, env, note = () => {}, signal, beforeWrite }) {
113
+ signal?.throwIfAborted();
114
+ const { key } = request;
115
+ const setting = requireSetting(key, request.action === "set" ? request.value : void 0);
116
+ const scope = readScope(request.scope, setting);
117
+ const value = request.action === "set" ? parseSettingValue(setting, request.value) : void 0;
118
+ 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.`);
119
+ const context = await readSettingsContext(cwd, env, note, signal);
120
+ const file = requireLayer(context, scope, setting);
121
+ beforeWrite?.(context.projectPath);
122
+ const apply = async () => {
123
+ const recording = setting.key === "recording.enabled" ? recordingLayers() : null;
124
+ const changed = await writeSetting(context, setting, scope, value, signal);
125
+ const updated = await readSettingsContext(cwd, env, void 0, void 0, context.projectPath);
126
+ const recordingsDeleted = recording ? await deleteTurnedOffRecordings(recording) : void 0;
127
+ let entry = settingEntry({
128
+ ...updated,
129
+ desktopInstalled: () => false
130
+ }, setting);
131
+ if (entry.origin === "default" && setting.desktopDefault !== void 0) {
132
+ const installed = await stimDesktopInstalledAsync(process.platform, env);
133
+ entry = settingEntry({
134
+ ...updated,
135
+ desktopInstalled: () => installed
136
+ }, setting);
137
+ }
138
+ return {
139
+ key,
140
+ scope,
141
+ file,
142
+ changed,
143
+ setting: entry,
144
+ ...recordingsDeleted ? { recordingsDeleted } : {}
145
+ };
146
+ };
147
+ return key === "recording.enabled" ? withWorkspaceProcessLock(configDir(), "recording-settings", apply, {
148
+ external: true,
149
+ signal
150
+ }) : apply();
151
+ }
152
+ function configTarget(context, scope) {
153
+ if (scope === "workspace") return context.projectPath ? {
154
+ scope,
155
+ projectPath: context.projectPath
156
+ } : null;
157
+ if (scope === "repo") return context.gitCommonDir ? {
158
+ scope,
159
+ gitCommonDir: context.gitCommonDir
160
+ } : null;
161
+ return scope === "machine" ? { scope } : null;
162
+ }
163
+ /** Removes a key Stim no longer reads from every layer that has it, or from one with `scope`. */
164
+ async function unsetReplacedSetting(request, { cwd, env, note = () => {}, signal }) {
165
+ signal?.throwIfAborted();
166
+ const { key } = request;
167
+ const only = request.scope === void 0 ? void 0 : parseScopeName(request.scope);
168
+ const context = await readSettingsContext(cwd, env, note, signal);
169
+ const removed = [];
170
+ for (const entry of unknownEntries(context)) {
171
+ if (entry.key !== key || only && entry.scope !== only) continue;
172
+ const target = configTarget(context, entry.scope);
173
+ if (entry.scope === "committed" ? await writeCommitted(entry.file, key, void 0, signal) : target && await writeConfigSettingAsync(target, key, void 0, signal)) removed.push({
174
+ scope: entry.scope,
175
+ file: entry.file
176
+ });
177
+ }
178
+ return {
179
+ key,
180
+ replacedBy: REPLACED_SETTINGS[key]?.key ?? key,
181
+ removed
182
+ };
183
+ }
184
+ //#endregion
185
+ export { unsetReplacedSetting as i, readScope as n, requireSetting as r, 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 machine is full or busy; local runs here, overriding a lower layer",
290
+ "description": "Where the iOS simulator runs: local here (overriding a lower layer); auto here first, then a remote.machines entry when this machine is full or busy; remote on a remote.machines entry in order, here only when none takes it; remote-only the same but never here; a machine name exactly that machine; eas or proxy a remote device backend",
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": {
@@ -564,7 +564,7 @@
564
564
  }
565
565
  },
566
566
  "remote": {
567
- "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",
567
+ "description": "Where the Android emulator runs: local here (overriding a lower layer); auto here first, then a remote.machines entry when this machine is full or busy; remote on a remote.machines entry in order, here only when none takes it; remote-only the same but never here; a machine name exactly that machine; eas or proxy a remote device backend",
568
568
  "type": "string",
569
569
  "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]))?$",
570
570
  "x-stim": {
@@ -811,6 +811,21 @@
811
811
  ]
812
812
  }
813
813
  },
814
+ "remote": {
815
+ "description": "Where stim macos launches the app: local here; auto here unless this machine is busy, then a remote.machines entry; remote on a remote.machines entry in order, here only when none takes it; remote-only the same but never here; a machine name exactly that machine. The --remote flag overrides it",
816
+ "type": "string",
817
+ "pattern": "^(?![Ee][Aa][Ss]$|[Pp][Rr][Oo][Xx][Yy]$)[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]))?$",
818
+ "x-stim": {
819
+ "key": "macos.remote",
820
+ "kind": "string",
821
+ "scopes": [
822
+ "machine",
823
+ "workspace",
824
+ "repo",
825
+ "committed"
826
+ ]
827
+ }
828
+ },
814
829
  "assetCatalog": {
815
830
  "description": "Optional .xcassets directory relative to the Swift Package directory.",
816
831
  "type": "string",
@@ -897,7 +912,7 @@
897
912
  "type": "object",
898
913
  "properties": {
899
914
  "enabled": {
900
- "description": "Keep workspace history on removal; false under STIM_HOME unless explicitly enabled",
915
+ "description": "Keep workspace history on removal, and archive a dirty finished worktree's changes before gc removes it; false keeps such worktrees, and is the default under STIM_HOME unless explicitly enabled",
901
916
  "type": "boolean",
902
917
  "default": true,
903
918
  "x-stim": {
@@ -1006,7 +1021,7 @@
1006
1021
  "type": "object",
1007
1022
  "properties": {
1008
1023
  "easFallback": {
1009
- "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",
1024
+ "description": "When ios.remote or android.remote is auto or remote, 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; remote-only never uses it",
1010
1025
  "type": "boolean",
1011
1026
  "default": false,
1012
1027
  "x-stim": {
@@ -1288,7 +1303,7 @@
1288
1303
  }
1289
1304
  },
1290
1305
  "remote": {
1291
- "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",
1306
+ "description": "Where the iOS simulator runs: local here (overriding a lower layer); auto here first, then a remote.machines entry when this machine is full or busy; remote on a remote.machines entry in order, here only when none takes it; remote-only the same but never here; a machine name exactly that machine; eas or proxy a remote device backend",
1292
1307
  "type": "string",
1293
1308
  "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]))?$",
1294
1309
  "x-stim": {
@@ -1398,7 +1413,7 @@
1398
1413
  }
1399
1414
  },
1400
1415
  "remote": {
1401
- "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",
1416
+ "description": "Where the Android emulator runs: local here (overriding a lower layer); auto here first, then a remote.machines entry when this machine is full or busy; remote on a remote.machines entry in order, here only when none takes it; remote-only the same but never here; a machine name exactly that machine; eas or proxy a remote device backend",
1402
1417
  "type": "string",
1403
1418
  "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]))?$",
1404
1419
  "x-stim": {
@@ -1453,6 +1468,27 @@
1453
1468
  },
1454
1469
  "additionalProperties": false
1455
1470
  },
1471
+ "macos": {
1472
+ "type": "object",
1473
+ "properties": {
1474
+ "remote": {
1475
+ "description": "Where stim macos launches the app: local here; auto here unless this machine is busy, then a remote.machines entry; remote on a remote.machines entry in order, here only when none takes it; remote-only the same but never here; a machine name exactly that machine. The --remote flag overrides it",
1476
+ "type": "string",
1477
+ "pattern": "^(?![Ee][Aa][Ss]$|[Pp][Rr][Oo][Xx][Yy]$)[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]))?$",
1478
+ "x-stim": {
1479
+ "key": "macos.remote",
1480
+ "kind": "string",
1481
+ "scopes": [
1482
+ "machine",
1483
+ "workspace",
1484
+ "repo",
1485
+ "committed"
1486
+ ]
1487
+ }
1488
+ }
1489
+ },
1490
+ "additionalProperties": false
1491
+ },
1456
1492
  "recording": {
1457
1493
  "type": "object",
1458
1494
  "properties": {
@@ -1478,7 +1514,7 @@
1478
1514
  "type": "object",
1479
1515
  "properties": {
1480
1516
  "enabled": {
1481
- "description": "Keep workspace history on removal; false under STIM_HOME unless explicitly enabled",
1517
+ "description": "Keep workspace history on removal, and archive a dirty finished worktree's changes before gc removes it; false keeps such worktrees, and is the default under STIM_HOME unless explicitly enabled",
1482
1518
  "type": "boolean",
1483
1519
  "default": true,
1484
1520
  "x-stim": {
@@ -1494,7 +1530,7 @@
1494
1530
  }
1495
1531
  },
1496
1532
  "maxAgeDays": {
1497
- "description": "Days to keep archived workspace records; 0 keeps nothing",
1533
+ "description": "Days to keep archived workspace records and worktree change archives; 0 keeps nothing",
1498
1534
  "type": "integer",
1499
1535
  "minimum": 0,
1500
1536
  "default": 30,
@@ -1522,7 +1558,7 @@
1522
1558
  }
1523
1559
  },
1524
1560
  "maxTotalGb": {
1525
- "description": "Total archived bytes in binary GB; 0 keeps no artifacts",
1561
+ "description": "Total archived bytes in binary GB, and separately the worktree change archives; 0 keeps no artifacts",
1526
1562
  "type": "number",
1527
1563
  "minimum": 0,
1528
1564
  "default": 5,
@@ -2030,7 +2066,7 @@
2030
2066
  }
2031
2067
  },
2032
2068
  "pressureProtectMinutes": {
2033
- "description": "Protection window, in minutes, that replaces maintenance.protectRecentHours at disk pressure levels 2 and 3",
2069
+ "description": "Protection window, in minutes, that replaces maintenance.protectRecentHours at disk pressure levels 2 and 3, except for workspace build outputs and build directories, which level 2 keeps for 15 minutes after the last ios, android or macos run",
2034
2070
  "type": "integer",
2035
2071
  "minimum": 0,
2036
2072
  "default": 10,
@@ -2058,7 +2094,7 @@
2058
2094
  }
2059
2095
  },
2060
2096
  "removeFinishedWorktrees": {
2061
- "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",
2097
+ "description": "Remove linked worktrees whose branch or pull request finished, under gc.worktreeGraceMinutes, archiving a dirty one's uncommitted files first, and clean, pushed ones with no commits of their own once idle",
2062
2098
  "type": "boolean",
2063
2099
  "default": true,
2064
2100
  "x-stim": {
@@ -2323,7 +2359,7 @@
2323
2359
  }
2324
2360
  },
2325
2361
  "maxLoadPerCore": {
2326
- "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",
2362
+ "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, reports the limit in its device offers so other machines skip it like a full one, stops preferring itself for automatic placement, and automatic iOS, Android and macOS placement treats it as busy",
2327
2363
  "type": "number",
2328
2364
  "minimum": 0.1,
2329
2365
  "default": 2,
@@ -2368,7 +2404,7 @@
2368
2404
  "type": "object",
2369
2405
  "properties": {
2370
2406
  "machines": {
2371
- "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",
2407
+ "description": "Tailscale names of the remote machines this machine may build on and host owned simulators, emulators and macOS apps on, each optionally with :<port> of its tailscale serve route (default 7443). The order is the placement priority: each workload goes to the first machine with room and a load under its own limit. What each is used for follows the build and device-host approvals it grants, which doctor --fix asks for separately",
2372
2408
  "type": "array",
2373
2409
  "items": {
2374
2410
  "type": "string"
@@ -2412,7 +2448,7 @@
2412
2448
  }
2413
2449
  },
2414
2450
  "easFallback": {
2415
- "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",
2451
+ "description": "When ios.remote or android.remote is auto or remote, 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; remote-only never uses it",
2416
2452
  "type": "boolean",
2417
2453
  "default": false,
2418
2454
  "x-stim": {
@@ -2440,7 +2476,7 @@
2440
2476
  }
2441
2477
  },
2442
2478
  "build": {
2443
- "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",
2479
+ "description": "Where iOS simulator Debug, Android emulator debug and macOS SwiftPM Debug builds run: local here; auto here while it has a free concurrency.maxBuilds slot and load under server.maxLoadPerCore, otherwise on a remote.machines entry; remote on a remote.machines entry in order, here only when none takes the build; remote-only the same but never here; a machine name exactly that machine without fallback. The ios, android and macos --remote-build flag overrides this setting and its environment override",
2444
2480
  "type": "string",
2445
2481
  "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]))?$",
2446
2482
  "default": "auto",
@@ -2452,24 +2488,6 @@
2452
2488
  ],
2453
2489
  "env": "STIM_REMOTE_BUILD"
2454
2490
  }
2455
- },
2456
- "buildMode": {
2457
- "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",
2458
- "type": "string",
2459
- "enum": [
2460
- "auto",
2461
- "force",
2462
- "off"
2463
- ],
2464
- "default": "auto",
2465
- "x-stim": {
2466
- "key": "remote.buildMode",
2467
- "kind": "choice",
2468
- "scopes": [
2469
- "machine"
2470
- ],
2471
- "env": "STIM_REMOTE_BUILD_MODE"
2472
- }
2473
2491
  }
2474
2492
  },
2475
2493
  "additionalProperties": false
@@ -1,5 +1,5 @@
1
- import { D as ownership_claim_exports, S as process_identity_exports, b as operationState, t as getExecutor, y as isTermination } from "./exec-VFRpu1iA.mjs";
2
- import { d as getConfigDir } from "./config-BktkY_zC.mjs";
1
+ import { D as ownership_claim_exports, S as process_identity_exports, b as operationState, t as getExecutor, y as isTermination } from "./exec-aULwtNly.mjs";
2
+ import { d as getConfigDir } from "./config-Cn94Jc0g.mjs";
3
3
  import { n as stripAnsi, r as waitForChild, t as createLineReader } from "./process-output-Fpl8PYi8.mjs";
4
4
  import { join } from "node:path";
5
5
  //#region src/engine/simslim.ts