stim 1.19.0 → 1.20.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/README.md +20 -3
  2. package/dist/{activity-BSw0x84R.mjs → activity-BrGh9EAI.mjs} +3 -3
  3. package/dist/{agent-device-usage-output-Bjxu_VmX.mjs → agent-device-usage-output-DVyv_hWj.mjs} +7 -7
  4. package/dist/android-0vv-OruW.mjs +6 -0
  5. package/dist/android-Brx6EKvJ.mjs +1258 -0
  6. package/dist/{android-BdQC6zs1.mjs → android-BsYNR81W.mjs} +103 -45
  7. package/dist/{android-DE5g_5gv.mjs → android-CK3fpEyZ.mjs} +2 -2
  8. package/dist/{android-cas-C87geAg-.mjs → android-cas-DCAuXryb.mjs} +9 -5
  9. package/dist/android-cas-compiler.mjs +1 -1
  10. package/dist/api-run.mjs +159 -8
  11. package/dist/{api-DIf_s86s.mjs → api-zuX3ynYN.mjs} +5 -1
  12. package/dist/api.d.mts +98 -4
  13. package/dist/api.mjs +1 -1
  14. package/dist/{app-install-CFI4ptUL.mjs → app-install-0LgaQRT7.mjs} +9 -8
  15. package/dist/artifact-CnC0mfov.mjs +807 -0
  16. package/dist/artifact-DClXJNWx.mjs +772 -0
  17. package/dist/artifact-lifecycle-DzPpBYvK.mjs +30 -0
  18. package/dist/{browser-web-CDhqQxhv.mjs → browser-web-CzBmhmgs.mjs} +19 -19
  19. package/dist/{budget-Dz-kyXLv.mjs → budget-BONq9vXY.mjs} +3101 -3062
  20. package/dist/build-BixRI6sB.mjs +462 -0
  21. package/dist/{build-facts-B87vo-Im.d.mts → build-facts-DcLv2HSd.d.mts} +1 -32
  22. package/dist/build-plan-CP0tckNU.mjs +553 -0
  23. package/dist/{cache-manifest-DiwKSyv9.mjs → cache-manifest-qA3dT2x2.mjs} +1 -1
  24. package/dist/cache-manifest.mjs +1 -1
  25. package/dist/{manifest-CX0d1IkZ.mjs → ccache-CjHQ5F8g.mjs} +13 -19
  26. package/dist/{chrome-BTUOo8p2.mjs → chrome-Dp2lMLBe.mjs} +1 -1
  27. package/dist/{cli-C9Zvgbdc.mjs → cli-B2R8wH1F.mjs} +20 -20
  28. package/dist/cli.mjs +1 -1
  29. package/dist/{client-CuUo8Nvg.mjs → client-BLy1Pw2V.mjs} +89 -307
  30. package/dist/collector-run.d.mts +4 -0
  31. package/dist/collector-run.mjs +7 -7
  32. package/dist/{command-output-Bt8ygHE7.mjs → command-output-BjPN99ZB.mjs} +2 -2
  33. package/dist/{config-DC5jEtd1.mjs → config-D0LhTY3S.mjs} +8 -12
  34. package/dist/{created-devices-D0s26Hx5.mjs → created-devices-HU_DAb_Q.mjs} +7 -6
  35. package/dist/{dependency-state-DKaS1OM9.mjs → dependency-state-B2m8eqsS.mjs} +2 -2
  36. package/dist/{detached-entry-pB_IPfV4.mjs → detached-entry-BXq4fIl-.mjs} +1 -1
  37. package/dist/{dev-client-CDY_zAmD.mjs → dev-client-ClATaJoH.mjs} +2 -2
  38. package/dist/{device-BHw0WlHl.mjs → device-B7lsC0K1.mjs} +8 -8
  39. package/dist/device-DVCwAFlX.mjs +727 -0
  40. package/dist/{device-capacity-BoN1jQkO.mjs → device-capacity-Ca_ke1LE.mjs} +13 -14
  41. package/dist/device-host-worker.mjs +38 -18
  42. package/dist/device-ios-CRUqPxKw.mjs +413 -0
  43. package/dist/{device-lease-Cu7PtQpZ.mjs → device-lease-DHOBXGgS.mjs} +135 -22
  44. package/dist/{device-lease-run-D38SnZtt.mjs → device-lease-run-CvDeHTkG.mjs} +4 -4
  45. package/dist/{device-pool-CP4rAsHx.mjs → device-pool-L2-5GxMy.mjs} +3 -3
  46. package/dist/{device-remote-B5GiUifk.mjs → device-remote-Bmg42063.mjs} +17 -68
  47. package/dist/doctor-C2MSuMMg.mjs +515 -0
  48. package/dist/{doctor-PRJH94v6.mjs → doctor-DPAqhVCK.mjs} +333 -744
  49. package/dist/{doctor-watchman-wZhyTdp4.mjs → doctor-watchman-CB4NnVZm.mjs} +13 -12
  50. package/dist/{simslim-D9LkJxIO.mjs → eas-build-Dw_ec2Km.mjs} +6 -152
  51. package/dist/{stim-installations-DbmUjsLv.mjs → eas-session-ledger-DZlC-Fq3.mjs} +57 -4
  52. package/dist/{error-diagnostics--v5HGOU0.mjs → error-diagnostics-Du7I_GuY.mjs} +17 -12
  53. package/dist/errors-DzkMYh8Q.mjs +205 -0
  54. package/dist/{exec-i8GDPwq9.mjs → exec-B81Qodjn.mjs} +40 -10
  55. package/dist/{gc-CyHMcKcn.mjs → gc-PWp34zGb.mjs} +21 -20
  56. package/dist/{device-ios-B2ruohE-.mjs → gradle-C2OEKCCI.mjs} +21 -650
  57. package/dist/{guide-dRaQJJk9.mjs → guide-Dai9lphv.mjs} +504 -117
  58. package/dist/{hosted-android-Bs7fytj7.mjs → hosted-android-DQ-Agsd8.mjs} +5 -5
  59. package/dist/{hosted-client-C5G4h_OJ.mjs → hosted-client-Io01s5By.mjs} +9 -3
  60. package/dist/{hosted-ios-Bik035eL.mjs → hosted-ios-DIxFnYp-.mjs} +3 -3
  61. package/dist/{hosted-logs-CI9Az9a4.mjs → hosted-logs-DP-ABU_M.mjs} +8 -8
  62. package/dist/{hosted-macos-DABm3ScO.mjs → hosted-macos-Aziw9T_9.mjs} +7 -6
  63. package/dist/{hosted-native-DbV2aUVw.mjs → hosted-native-BdAQi21M.mjs} +17 -14
  64. package/dist/{idle-shutdown-DYiMBn34.mjs → idle-shutdown-DdMqoSUB.mjs} +7 -7
  65. package/dist/{ios-DYUr9LGc.mjs → ios-BZ9dg2Fr.mjs} +8 -7
  66. package/dist/{ios-BWbHFZVz.mjs → ios-CXLIUxXJ.mjs} +180 -24
  67. package/dist/ios-CZuv6gRv.mjs +1112 -0
  68. package/dist/{ios-DCrkgXEM.mjs → ios-VGvNdsv2.mjs} +1 -1
  69. package/dist/{ios-device-CrW8aMb5.mjs → ios-device-B1B14xND.mjs} +1 -1
  70. package/dist/{ios-device-CJ_ZOGky.mjs → ios-device-BreZOpM0.mjs} +3 -3
  71. package/dist/ios-project-B9drt_aY.mjs +197 -0
  72. package/dist/{ios-state-By0XuLTQ.mjs → ios-state-CjrL2qv-.mjs} +1 -1
  73. package/dist/js-swap-C8icNYIY.mjs +432 -0
  74. package/dist/launch-DxH0THLP.mjs +745 -0
  75. package/dist/{react-native-ios-CSpIxexZ.mjs → launch-YrbqaIFj.mjs} +59 -612
  76. package/dist/{launch-verify-DCveSE6d.mjs → launch-verify-BzRdbuO1.mjs} +26 -7
  77. package/dist/{logs-Bao1HOMy.mjs → logs-DHaRNfg7.mjs} +11 -11
  78. package/dist/macos-C0iljhsN.mjs +2 -0
  79. package/dist/{macos-ciyBdTY5.mjs → macos-V_3Ui7g4.mjs} +42 -50
  80. package/dist/macos-run.mjs +1 -1
  81. package/dist/maintenance-run.mjs +13 -10
  82. package/dist/{metro-DI2AjPF_.mjs → metro-D_j9sN-B.mjs} +18 -5
  83. package/dist/{metro-gateway-BxpJLIwh.mjs → metro-gateway-BBJldOdi.mjs} +2 -2
  84. package/dist/miss-reason-BPAzZS0r.mjs +238 -0
  85. package/dist/{named-ports-B4iWH4E-.mjs → named-ports-C2lvoPsb.mjs} +3 -3
  86. package/dist/native-android-lHzaew7r.mjs +226 -0
  87. package/dist/native-gradle-inputs-BQnIdAlL.mjs +356 -0
  88. package/dist/{native-runtime-BGxsbfny.mjs → native-runtime-BRmWL7ru.mjs} +9 -8
  89. package/dist/native-xcode-inputs-eutS19jU.mjs +307 -0
  90. package/dist/native-xcode-ios-DP3cHLwu.mjs +434 -0
  91. package/dist/{ndjson-LGZAMsqa.mjs → ndjson-GOnbyvso.mjs} +6 -6
  92. package/dist/offload-worker.d.mts +41 -7
  93. package/dist/offload-worker.mjs +409 -74
  94. package/dist/{ownership-EB0ySdGK.mjs → ownership-DXzMMSx6.mjs} +2 -2
  95. package/dist/{placement-log-Dxe34rJm.mjs → placement-log-reFbjqEr.mjs} +1 -1
  96. package/dist/plan-BDk2AeY_.mjs +260 -0
  97. package/dist/plan-placement-D_YYlE6N.mjs +73 -0
  98. package/dist/{ports-ByPc0_97.mjs → ports-jzvAJXXL.mjs} +3 -3
  99. package/dist/{prebuild-BKJ3wJI1.mjs → prebuild-Bw44QxVh.mjs} +4 -4
  100. package/dist/{preview-BbSottmO.mjs → preview-BaBJ72Al.mjs} +50 -32
  101. package/dist/project-B8_duAzW.mjs +1067 -0
  102. package/dist/project-plan-BPwbLwQu.mjs +10 -0
  103. package/dist/{pull-request-IHNdMnG8.mjs → pull-request-vOhLNmIb.mjs} +1 -1
  104. package/dist/pull-requests.mjs +1 -1
  105. package/dist/react-native-android-chm1DMlA.mjs +921 -0
  106. package/dist/react-native-build-oAw6gQeW.mjs +239 -0
  107. package/dist/react-native-doctor-DYXoh-6I.mjs +78 -0
  108. package/dist/react-native-ios-SCTK_CT5.mjs +595 -0
  109. package/dist/{recordings-Bv8McZN4.mjs → recordings-Bk-VivUV.mjs} +1 -1
  110. package/dist/{reload-rAPdqeRR.mjs → reload-D34GkY78.mjs} +17 -13
  111. package/dist/remote-BU9UyhCJ.mjs +192 -0
  112. package/dist/{remote-cache-N3hAEaAL.mjs → remote-cache-Bsts6f6Y.mjs} +5 -5
  113. package/dist/result-BDoRk1k3.mjs +186 -0
  114. package/dist/{run-lAkle1-c.mjs → run-qvff78hv.mjs} +3 -3
  115. package/dist/{server-bare-CkIjnegq.mjs → server-bare-dWjAarki.mjs} +5 -5
  116. package/dist/server-command-7XIKlWtO.mjs +69 -0
  117. package/dist/{server-expo-n3tUi3Em.mjs → server-expo-CsYu2aKL.mjs} +107 -82
  118. package/dist/server-expo-DV43vep4.mjs +2 -0
  119. package/dist/{settings-CxgngUZk.mjs → settings-Bbztm4oV.mjs} +179 -129
  120. package/dist/{settings-2am7TSxp.mjs → settings-CT3E71-i.mjs} +6 -6
  121. package/dist/{state-lKh8shBp.d.mts → settings-vk3mJGSm.d.mts} +4 -2
  122. package/dist/settings.schema.json +84 -0
  123. package/dist/simslim-ChMfe8rg.mjs +149 -0
  124. package/dist/{slot-launch-HV_KtPIw.mjs → slot-launch-B9hn96wd.mjs} +6 -6
  125. package/dist/{stage-BgaZJVZY.mjs → stage-BNKKdDNc.mjs} +6 -3
  126. package/dist/start-CbQCSnXQ.mjs +2 -0
  127. package/dist/{start-DtxthMfI.mjs → start-CdvcTaXr.mjs} +97 -68
  128. package/dist/{state-Tp4uImDS.mjs → state-Boo0IMJq.mjs} +1 -1
  129. package/dist/{state-D2KtnALG.mjs → state-DI84A-uu.mjs} +29 -10
  130. package/dist/{stats-8EJ5ivFT.mjs → stats-BIIDrjA8.mjs} +5 -5
  131. package/dist/{status-BEeL6IKz.mjs → status-BDGHwkCs.mjs} +3 -1
  132. package/dist/{status-BOyp0CUl.mjs → status-DARMkc9q.mjs} +81 -293
  133. package/dist/status-watch-DvmI3SmN.mjs +296 -0
  134. package/dist/{stim-desktop-C65jk_q2.mjs → stim-desktop-8GQX2OV8.mjs} +1 -1
  135. package/dist/{stop-DRipAg-e.mjs → stop-CCZS-C9m.mjs} +2 -2
  136. package/dist/{stop-D0UIpZuh.mjs → stop-WQPQHLuy.mjs} +5 -5
  137. package/dist/{stop-zDlknFoz.mjs → stop-iNemHsJC.mjs} +21 -20
  138. package/dist/supervisor-run.d.mts +7 -2
  139. package/dist/supervisor-run.mjs +32 -20
  140. package/dist/{support-DsNaFg6D.mjs → support-CnE_SIjt.mjs} +17 -7
  141. package/dist/{support-CNqYkV0y.mjs → support-DCco5fut.mjs} +61 -8
  142. package/dist/{swiftpm-macos-aJzPAj1t.mjs → swiftpm-macos-afeNUExW.mjs} +5 -5
  143. package/dist/tailnet-CoJUb2tB.mjs +265 -0
  144. package/dist/toolchain-BIupIro1.mjs +2 -0
  145. package/dist/{toolchain-C3xSoyUl.mjs → toolchain-DqyDXs30.mjs} +206 -53
  146. package/dist/{trigger-CAhmG-4s.mjs → trigger-C0MWBi8O.mjs} +2 -2
  147. package/dist/trigger-SHLOWJ4O.mjs +2 -0
  148. package/dist/{warm-progress-pFLB2e6P.mjs → warm-progress-C3ASD6z5.mjs} +6 -6
  149. package/dist/{watchman-BL1IdOtq.mjs → watchman-CQl51YZu.mjs} +6 -6
  150. package/dist/{web-DbbrPUn0.mjs → web-B3eldHFH.mjs} +9 -13
  151. package/dist/web-BLXpOe6b.mjs +2 -0
  152. package/dist/web-run.mjs +10 -10
  153. package/dist/{workspace-state-CR9X86xJ.mjs → workspace-state-CSIFuuDa.mjs} +33 -33
  154. package/dist/{workspaces-Djb2gfi9.mjs → workspaces-D2xSHBDY.mjs} +1260 -1258
  155. package/dist/{worktree-BL4M1rhB.mjs → worktree-BxDqaTev.mjs} +84 -48
  156. package/dist/worktree-q7_OZNY0.mjs +2 -0
  157. package/package.json +8 -6
  158. package/shim/native-android.gradle +101 -0
  159. package/dist/android-BXc6BmEi.mjs +0 -3732
  160. package/dist/android-BvV1gGdN.mjs +0 -4
  161. package/dist/ios-ITYno9rO.mjs +0 -2241
  162. package/dist/macos-nIOATJmR.mjs +0 -2
  163. package/dist/metro-store-BAHFcJm6.mjs +0 -106
  164. package/dist/miss-reason-CjQCOKqq.mjs +0 -1743
  165. package/dist/plan-placement-BVY59qa8.mjs +0 -290
  166. package/dist/project-B5eQ1USs.mjs +0 -612
  167. package/dist/react-native-ios-BMh2i25I.mjs +0 -2
  168. package/dist/server-expo-DGsdUGX2.mjs +0 -2
  169. package/dist/start-D8NbxNBb.mjs +0 -2
  170. package/dist/trigger-CoalsMTV.mjs +0 -2
  171. package/dist/web-DFpFeZu5.mjs +0 -2
  172. package/dist/worktree-DV-A0LNL.mjs +0 -2
@@ -1,9 +1,9 @@
1
- import { t as getExecutor } from "./exec-i8GDPwq9.mjs";
2
- import { E as upsertProject, d as getConfigDir, p as getProject } from "./config-DC5jEtd1.mjs";
3
- import { f as appProjectProblem } from "./project-B5eQ1USs.mjs";
4
- import { lt as listWorktrees, mt as repoRoot } from "./settings-CxgngUZk.mjs";
5
- import { o as runWatchman, r as parseWatchRoots } from "./watchman-BL1IdOtq.mjs";
6
- import { t as compareStimVersions } from "./stim-installations-DbmUjsLv.mjs";
1
+ import { t as getExecutor } from "./exec-B81Qodjn.mjs";
2
+ import { E as upsertProject, d as getConfigDir, p as getProject } from "./config-D0LhTY3S.mjs";
3
+ import { Et as repoRoot, xt as listWorktrees } from "./settings-Bbztm4oV.mjs";
4
+ import { d as projectRegistry } from "./project-B8_duAzW.mjs";
5
+ import { o as runWatchman, r as parseWatchRoots } from "./watchman-CQl51YZu.mjs";
6
+ import { o as compareStimVersions } from "./eas-session-ledger-DZlC-Fq3.mjs";
7
7
  import { mkdirSync, readFileSync, realpathSync, renameSync, rmSync, unlinkSync, writeFileSync } from "node:fs";
8
8
  import { dirname, isAbsolute, join, relative, sep } from "node:path";
9
9
  //#region src/guide-status.ts
@@ -12,17 +12,17 @@ const DOCTOR_STALE_MS = 7 * DAY_MS;
12
12
  const UPDATE_CHECK_INTERVAL_MS = DAY_MS;
13
13
  const UPDATE_CHECK_TIMEOUT_MS = 2e3;
14
14
  const LATEST_URL = "https://registry.npmjs.org/stim/latest";
15
- const PLATFORMS = ["ios", "android"];
16
- function recordDoctorRun(projectRoot, platform, version, now = /* @__PURE__ */ new Date()) {
15
+ function recordDoctorRun(projectRoot, platform, version, now = /* @__PURE__ */ new Date(), platforms) {
17
16
  const run = {
18
17
  at: now.toISOString(),
19
18
  version
20
19
  };
21
20
  try {
22
- if (appProjectProblem(projectRoot)) return;
21
+ const targets = platforms ?? projectRegistry.selectDoctor(projectRoot, platform).platforms;
22
+ if (targets.length === 0) return;
23
23
  upsertProject(projectRoot, (existing) => {
24
24
  const doctorRuns = { ...existing.doctorRuns };
25
- for (const target of platform ? [platform] : PLATFORMS) doctorRuns[target] = run;
25
+ for (const target of targets) doctorRuns[target] = run;
26
26
  return { doctorRuns };
27
27
  });
28
28
  } catch {}
@@ -102,12 +102,13 @@ function renderGuideStatus({ running, doctor, latest }) {
102
102
  }
103
103
  async function guideStatus({ projectRoot, running, now = /* @__PURE__ */ new Date(), fetch: fetchImpl = fetch }) {
104
104
  const doctor = [];
105
- if (projectRoot && !appProjectProblem(projectRoot)) {
105
+ if (projectRoot) {
106
+ const platforms = projectRegistry.selectDoctor(projectRoot).platforms;
106
107
  let runs = null;
107
108
  try {
108
109
  runs = getProject(projectRoot)?.doctorRuns ?? {};
109
110
  } catch {}
110
- if (runs) for (const platform of PLATFORMS) {
111
+ if (runs) for (const platform of platforms) {
111
112
  const reason = doctorDueReason(runs[platform], running, now);
112
113
  if (reason) doctor.push({
113
114
  platform,
@@ -1,12 +1,9 @@
1
- import { n as ownership_claim_exports, t as process_identity_exports } from "./process-identity-Biv3arCO.mjs";
2
- import { t as getExecutor } from "./exec-i8GDPwq9.mjs";
3
- import { n as stripAnsi, r as waitForChild, t as createLineReader } from "./process-output-Fpl8PYi8.mjs";
4
- import { d as getConfigDir } from "./config-DC5jEtd1.mjs";
5
- import { g as releaseBuildLock, m as acquireBuildLock } from "./device-lease-Cu7PtQpZ.mjs";
6
- import { f as resolveEasCliBin } from "./remote-cache-N3hAEaAL.mjs";
7
- import { s as readInstalledEasCliVersion, v as easCliSupport, y as easCliUpgradeRemedy } from "./device-remote-B5GiUifk.mjs";
1
+ import { n as ownership_claim_exports } from "./process-identity-Biv3arCO.mjs";
2
+ import { t as getExecutor } from "./exec-B81Qodjn.mjs";
3
+ import { g as releaseBuildLock, m as acquireBuildLock } from "./device-lease-DHOBXGgS.mjs";
4
+ import { f as resolveEasCliBin } from "./remote-cache-Bsts6f6Y.mjs";
5
+ import { g as easCliUpgradeRemedy, h as easCliSupport, s as readInstalledEasCliVersion } from "./device-remote-Bmg42063.mjs";
8
6
  import { existsSync, realpathSync, statSync } from "node:fs";
9
- import { join } from "node:path";
10
7
  import { createHash } from "node:crypto";
11
8
  //#region src/engine/eas-build.ts
12
9
  const MIN_EAS_CLI_BUILD_DOWNLOAD_VERSION = "18.9.0";
@@ -228,147 +225,4 @@ async function resolveEasDevelopmentBuild(args) {
228
225
  }
229
226
  }
230
227
  //#endregion
231
- //#region src/engine/simslim.ts
232
- async function reconcileSimSlim({ udid, profile, previouslyManaged = false, out = () => {}, spawn, timeoutMs = 72e4, cleanupMs = 1e4 }) {
233
- if (!profile && !previouslyManaged) return {
234
- managed: false,
235
- profile: null
236
- };
237
- const action = profile ? "on" : "off";
238
- const args = profile ? [
239
- "on",
240
- udid,
241
- "--profile",
242
- profile
243
- ] : ["off", udid];
244
- const lines = [];
245
- const onLine = (raw) => {
246
- const line = stripAnsi(raw).trim();
247
- if (!line) return;
248
- lines.push(line);
249
- if (lines.length > 20) lines.shift();
250
- out(`SimSlim: ${line}`);
251
- };
252
- const stdout = createLineReader(onLine);
253
- const stderr = createLineReader(onLine);
254
- const root = join(getConfigDir(), "simslim-locks", `${encodeURIComponent(udid.toLowerCase())}.lock`);
255
- const label = `SimSlim for ${udid}`;
256
- const attempt = (0, ownership_claim_exports.tryAcquireClaim)({
257
- root,
258
- mode: "exclusive",
259
- label
260
- });
261
- if (attempt.pending) (0, ownership_claim_exports.releaseClaim)(attempt.pending);
262
- const claim = attempt.acquired;
263
- if (!claim) throw new ownership_claim_exports.ClaimRefusedError({
264
- root,
265
- claimPath: attempt.held?.path ?? root,
266
- label,
267
- reason: "another SimSlim operation is still using this simulator"
268
- });
269
- let child;
270
- let result;
271
- let identity;
272
- let cancellation;
273
- const stopGroup = () => {
274
- if (child?.pid !== void 0 && identity && (0, process_identity_exports.inspectProcessIdentity)(identity) === "same") try {
275
- process.kill(-child.pid, "SIGKILL");
276
- } catch {}
277
- };
278
- const cancel = (signal) => {
279
- cancellation ??= signal;
280
- stopGroup();
281
- };
282
- const onInterrupt = () => cancel("SIGINT");
283
- const onTerminate = () => cancel("SIGTERM");
284
- const groupAlive = () => child?.pid !== void 0 && (0, ownership_claim_exports.processGroupAlive)(child.pid);
285
- const settled = () => result !== void 0 && !groupAlive();
286
- const waitUntil = async (deadline, cancellable = false) => {
287
- while (!settled() && Date.now() < deadline) {
288
- if (cancellable && cancellation) break;
289
- await new Promise((resolve) => setTimeout(resolve, Math.min(50, deadline - Date.now())));
290
- }
291
- return settled();
292
- };
293
- process.on("SIGINT", onInterrupt);
294
- process.on("SIGTERM", onTerminate);
295
- process.on("exit", stopGroup);
296
- try {
297
- (0, ownership_claim_exports.markClaimChildPending)(claim);
298
- try {
299
- child = (spawn ?? ((cmd, childArgs, opts) => getExecutor().spawn(cmd, childArgs, opts)))("simslim", args, {
300
- stdio: [
301
- "ignore",
302
- "pipe",
303
- "pipe"
304
- ],
305
- detached: true
306
- });
307
- } catch (error) {
308
- throw simslimLaunchError(error);
309
- }
310
- child.stdout?.on("data", (chunk) => stdout.push(chunk));
311
- child.stderr?.on("data", (chunk) => stderr.push(chunk));
312
- waitForChild(child).then((value) => {
313
- result = value;
314
- });
315
- if (child.pid !== void 0) {
316
- const captured = (0, process_identity_exports.captureProcessIdentity)(child.pid);
317
- if (captured.ok) {
318
- identity = {
319
- pid: child.pid,
320
- processToken: captured.token
321
- };
322
- try {
323
- (0, ownership_claim_exports.setClaimChild)(claim, identity);
324
- } catch {}
325
- }
326
- }
327
- const completed = await waitUntil(Date.now() + timeoutMs, true);
328
- if (cancellation || !completed) {
329
- stopGroup();
330
- const stopped = await waitUntil(Date.now() + cleanupMs);
331
- const detail = lines.length ? ` ${lines.join(" | ")}` : "";
332
- const recovery = stopped ? "Its process group has stopped. Retry stim ios to reconcile the retained simulator settings." : `Its process group could not be confirmed stopped. The claim remains at ${claim.path}. Inspect the SimSlim process group ${child.pid ?? "unknown"} before retrying. Only when nothing is using it, remove the claim: ${(0, ownership_claim_exports.claimRemoveCommand)(claim.path)}`;
333
- const message = `SimSlim ${action} ${cancellation ? `interrupted by ${cancellation}` : `exceeded ${Math.round(timeoutMs / 1e3)}s`}. ${recovery}${detail}`;
334
- if (cancellation) out(message);
335
- throw Object.assign(new Error(message), { code: cancellation ? "EINTR" : "ETIMEDOUT" });
336
- }
337
- } finally {
338
- if (!child || settled()) (0, ownership_claim_exports.releaseClaim)(claim);
339
- else {
340
- child.stdout?.destroy?.();
341
- child.stderr?.destroy?.();
342
- child.unref();
343
- }
344
- process.off("SIGINT", onInterrupt);
345
- process.off("SIGTERM", onTerminate);
346
- process.off("exit", stopGroup);
347
- if (cancellation) process.exit(cancellation === "SIGINT" ? 130 : 143);
348
- }
349
- stdout.flush();
350
- stderr.flush();
351
- if (result?.error) throw simslimLaunchError(result.error);
352
- if (result?.code !== 0) {
353
- const detail = lines.length ? ` ${lines.join(" | ")}` : "";
354
- throw new Error(`SimSlim ${action} failed with exit code ${result?.code ?? "unknown"}.${detail}`);
355
- }
356
- return {
357
- managed: Boolean(profile),
358
- profile: profile ?? null
359
- };
360
- }
361
- function simslimLaunchError(error) {
362
- const cause = error;
363
- if (cause?.code === "ENOENT") return new Error("SimSlim is configured but the `simslim` command is not installed. Run `brew install mobai-app/tap/simslim`.", { cause });
364
- return new Error(`Could not start SimSlim: ${String(cause?.message || cause)}`, { cause });
365
- }
366
- function simslimIsOnPath() {
367
- try {
368
- return Boolean(getExecutor().findExecutable("simslim"));
369
- } catch {
370
- return false;
371
- }
372
- }
373
- //#endregion
374
- export { isEasBuildFailure as a, easDeviceBuildRemedy as i, simslimIsOnPath as n, planEasDevelopmentBuild as o, MIN_EAS_CLI_BUILD_DOWNLOAD_VERSION as r, resolveEasDevelopmentBuild as s, reconcileSimSlim as t };
228
+ export { resolveEasDevelopmentBuild as a, planEasDevelopmentBuild as i, easDeviceBuildRemedy as n, isEasBuildFailure as r, MIN_EAS_CLI_BUILD_DOWNLOAD_VERSION as t };
@@ -1,6 +1,8 @@
1
- import { t as getExecutor } from "./exec-i8GDPwq9.mjs";
2
- import { accessSync, constants, realpathSync, statSync } from "node:fs";
3
- import { delimiter, join, resolve } from "node:path";
1
+ import { t as getExecutor } from "./exec-B81Qodjn.mjs";
2
+ import { D as withDirLock } from "./workspace-state-CSIFuuDa.mjs";
3
+ import { accessSync, constants, existsSync, mkdirSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
4
+ import { delimiter, dirname, join, resolve } from "node:path";
5
+ import { easMachineStateRoot, easMachineStateRoot as easMachineStateRoot$1, easSessionLedgerFile, easSessionLedgerLock, readEasSessionLedger, readEasSessionLedger as readEasSessionLedger$1, validEasSessionClaim } from "@stim-cli/core/state";
4
6
  //#region src/diagnostics/stim-installations.ts
5
7
  const VERSION_OUTPUT_LIMIT = 4096;
6
8
  function parsedVersion(version) {
@@ -149,4 +151,55 @@ async function inspectStimVersions(runningVersion, { pathValue = process.env.PAT
149
151
  return analyzeStimVersions(runningVersion, canonicalPath(runningPath), installations);
150
152
  }
151
153
  //#endregion
152
- export { inspectStimVersions as n, compareStimVersions as t };
154
+ //#region src/engine/eas-machine-root-guard-env.ts
155
+ const EAS_TEST_GUARD_ROOT_ENV = "STIM_TEST_GUARD_REAL_EAS_ROOT";
156
+ //#endregion
157
+ //#region src/engine/eas-session-ledger.ts
158
+ function assertEasMachineRootWritable(root) {
159
+ const guardedRoot = process.env[EAS_TEST_GUARD_ROOT_ENV];
160
+ if (!guardedRoot || resolve(root) !== resolve(guardedRoot)) return;
161
+ throw new Error(`Refusing to write the real EAS machine root ${root} from a test process. Pass a temporary ledgerRoot, machineRoot, or easLedgerRoot, or point HOME (and USERPROFILE on Windows) at a temporary directory.`);
162
+ }
163
+ function writeLedger(root, ledger) {
164
+ mkdirSync(root, { recursive: true });
165
+ const file = easSessionLedgerFile(root);
166
+ const tmp = `${file}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
167
+ writeFileSync(tmp, `${JSON.stringify(ledger, null, 2)}\n`);
168
+ try {
169
+ renameSync(tmp, file);
170
+ } catch (error) {
171
+ rmSync(tmp, { force: true });
172
+ throw error;
173
+ }
174
+ }
175
+ function recordEasSessionClaim(claim, root = easMachineStateRoot()) {
176
+ assertEasMachineRootWritable(root);
177
+ const normalized = validEasSessionClaim(claim.sessionId, claim);
178
+ if (!normalized) throw new Error(`Invalid EAS session claim for ${claim.sessionId}.`);
179
+ withDirLock(easSessionLedgerLock(root), () => {
180
+ const current = readEasSessionLedger(root);
181
+ if (!current.safe) throw new Error(current.notice ?? "EAS ownership ledger is unreadable.");
182
+ current.claims.set(normalized.sessionId, normalized);
183
+ writeLedger(root, {
184
+ version: 1,
185
+ claims: Object.fromEntries(current.claims)
186
+ });
187
+ }, { ensureParent: () => mkdirSync(dirname(easSessionLedgerLock(root)), { recursive: true }) });
188
+ }
189
+ function removeEasSessionClaim(sessionId, root = easMachineStateRoot()) {
190
+ const file = easSessionLedgerFile(root);
191
+ if (!existsSync(file)) return false;
192
+ assertEasMachineRootWritable(root);
193
+ return withDirLock(easSessionLedgerLock(root), () => {
194
+ const current = readEasSessionLedger(root);
195
+ if (!current.safe) return false;
196
+ if (!current.claims.delete(sessionId)) return false;
197
+ writeLedger(root, {
198
+ version: 1,
199
+ claims: Object.fromEntries(current.claims)
200
+ });
201
+ return true;
202
+ });
203
+ }
204
+ //#endregion
205
+ export { removeEasSessionClaim as a, recordEasSessionClaim as i, easMachineStateRoot$1 as n, compareStimVersions as o, readEasSessionLedger$1 as r, inspectStimVersions as s, assertEasMachineRootWritable as t };
@@ -1,18 +1,18 @@
1
- import { t as getExecutor } from "./exec-i8GDPwq9.mjs";
2
- import { v as launchErrorPreview } from "./command-output-Bt8ygHE7.mjs";
3
- import { R as deviceSlotKey, p as getProject, z as deviceSlotPlatforms } from "./config-DC5jEtd1.mjs";
4
- import { r as readWorkspaceState$1, w as withDirLock } from "./workspace-state-CR9X86xJ.mjs";
5
- import { A as readWorkspaceLaunches } from "./project-B5eQ1USs.mjs";
6
- import { u as resolveProjectMetro } from "./metro-DI2AjPF_.mjs";
7
- import { a as parseLease, n as fileLeaseIo, t as deviceLeasePath } from "./device-lease-Cu7PtQpZ.mjs";
8
- import { o as androidHome, u as androidToolCwd } from "./android-BdQC6zs1.mjs";
9
- import { r as deviceConsoleLevel } from "./ios-device-CrW8aMb5.mjs";
10
- import { n as parseLogcatLine, t as androidClockOffset } from "./android-DE5g_5gv.mjs";
1
+ import { t as getExecutor } from "./exec-B81Qodjn.mjs";
2
+ import { R as deviceSlotKey, p as getProject, z as deviceSlotPlatforms } from "./config-D0LhTY3S.mjs";
3
+ import { a as parseLease, n as fileLeaseIo, t as deviceLeasePath } from "./device-lease-DHOBXGgS.mjs";
4
+ import { D as withDirLock, r as readWorkspaceState$1 } from "./workspace-state-CSIFuuDa.mjs";
5
+ import { v as launchErrorPreview } from "./command-output-BjPN99ZB.mjs";
6
+ import { F as readWorkspaceLaunches } from "./project-B8_duAzW.mjs";
7
+ import { o as androidHome, u as androidToolCwd } from "./android-BsYNR81W.mjs";
8
+ import { u as resolveProjectMetro } from "./metro-D_j9sN-B.mjs";
9
+ import { r as deviceConsoleLevel } from "./ios-device-B1B14xND.mjs";
10
+ import { n as parseLogcatLine, t as androidClockOffset } from "./android-CK3fpEyZ.mjs";
11
11
  import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, statSync, unlinkSync, writeFileSync } from "node:fs";
12
12
  import { basename, dirname, join, resolve, sep } from "node:path";
13
13
  import { NATIVE_CRASH_LOG_PREFIX, attachExpoErrorContext, readLogRecords, readLogTimeline } from "@stim-cli/core/state";
14
- import { homedir } from "node:os";
15
14
  import { createHash, randomUUID } from "node:crypto";
15
+ import { homedir } from "node:os";
16
16
  //#region src/diagnostics/diagnostic-store.ts
17
17
  /** Publishes an immutable diagnostic snapshot atomically; existing evidence wins. */
18
18
  function writeDiagnosticOnce(path, content) {
@@ -617,7 +617,12 @@ async function errorDiagnostics(input, { root, logsDir, port, allowRequest = fal
617
617
  "bundle_response_started",
618
618
  "bundle_response_finished",
619
619
  "bundle_response_failed"
620
- ].includes(String(event.event)) || ["expo_stdout", "expo_stderr"].includes(String(event.event)) && /\bBundl(?:ing|ed)\b/.test(event.msg ?? "")) && Number(event.ts) > Number(record.ts) && (!event.platform || !record.platform || event.platform === record.platform))) {
620
+ ].includes(String(event.event)) || [
621
+ "expo_stdout",
622
+ "expo_stderr",
623
+ "command_stdout",
624
+ "command_stderr"
625
+ ].includes(String(event.event)) && /\bBundl(?:ing|ed)\b/.test(event.msg ?? "")) && Number(event.ts) > Number(record.ts) && (!event.platform || !record.platform || event.platform === record.platform))) {
621
626
  rendered[index] = {
622
627
  ...record,
623
628
  symbolicationNote: "Metro rebuilt after this error; captured coordinates retained rather than mapping against a different bundle."
@@ -0,0 +1,205 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { dirname, isAbsolute, join, resolve } from "node:path";
3
+ import { ANDROID_COMPILER_CACHE_CHOICES, ANDROID_PCH_CHOICES } from "@stim-cli/core/state";
4
+ import { fileURLToPath } from "node:url";
5
+ import { METRO_NAMED_CACHE_LAYOUT, metroCacheRoot, registerCache, sharedStoreRoot, tagSharedStore } from "@stim-cli/core";
6
+ import { createHash } from "node:crypto";
7
+ //#region src/optimizations.ts
8
+ function compilerCacheFallbackMessage({ fallback, compilerCache, file = null }) {
9
+ const origin = fallback.fromEnvironment ? "the environment" : file;
10
+ const backend = compilerCache === "none" ? "Android builds use no compiler cache, because optimizations.android.compilerCache is \"none\"." : "Android builds fall back to ccache when it is available.";
11
+ return `${fallback.key}${origin ? ` in ${origin}` : ""} ${fallback.reason}. ${backend}`;
12
+ }
13
+ function optimizationBuildProfile(platform, options) {
14
+ const selected = platform === "ios" ? {
15
+ ...options.ios,
16
+ swiftCompilationCache: options.ios.swiftCompilationCache ?? false
17
+ } : { pch: options.android.pch };
18
+ const defaults = platform === "ios" ? {
19
+ compilationCache: true,
20
+ swiftCompilationCache: false,
21
+ prefixMapping: true
22
+ } : { pch: "auto" };
23
+ if (JSON.stringify(selected) === JSON.stringify(defaults)) return void 0;
24
+ return `opt-${createHash("sha256").update(JSON.stringify(selected)).digest("hex").slice(0, 16)}`;
25
+ }
26
+ function resolveOptimizations(settings = {}, env = process.env) {
27
+ function choice(path, choices, fallback) {
28
+ const raw = optimizationValue(settings, path);
29
+ if (raw === void 0) return fallback;
30
+ if (typeof raw !== "string" || !choices.includes(raw)) throw new Error(`Invalid optimizations.${path}. Expected ${choices.join(", ")}.`);
31
+ return raw;
32
+ }
33
+ const compiler = choice("android.compilerCache", ANDROID_COMPILER_CACHE_CHOICES, "auto");
34
+ const fromEnvironment = Boolean(env.STIM_ANDROID_CAS_TOOLCHAIN);
35
+ const manifest = env.STIM_ANDROID_CAS_TOOLCHAIN || optimizationValue(settings, "android.casToolchain");
36
+ let fallback = null;
37
+ let toolchain = null;
38
+ if (manifest !== void 0) {
39
+ if (typeof manifest === "string" && isAbsolute(manifest) && !/[\r\n\0]/.test(manifest)) toolchain = manifest;
40
+ else fallback = {
41
+ key: fromEnvironment ? "STIM_ANDROID_CAS_TOOLCHAIN" : "optimizations.android.casToolchain",
42
+ reason: `is ${JSON.stringify(manifest)}, which is not an absolute path to a toolchain JSON manifest`,
43
+ fromEnvironment,
44
+ manifest: null
45
+ };
46
+ }
47
+ let compilerCache = compiler === "auto" ? toolchain ? "cas" : "ccache" : compiler;
48
+ if (compilerCache === "cas" && !toolchain) {
49
+ fallback ??= {
50
+ key: "optimizations.android.compilerCache",
51
+ reason: "is \"cas\", but no optimizations.android.casToolchain or STIM_ANDROID_CAS_TOOLCHAIN names the toolchain manifest",
52
+ fromEnvironment: false,
53
+ manifest: null
54
+ };
55
+ compilerCache = "ccache";
56
+ }
57
+ return {
58
+ buildCache: optimizationBoolean(settings, "buildCache"),
59
+ remoteBuildCache: optimizationBoolean(settings, "remoteBuildCache"),
60
+ releaseBundleSwap: optimizationBoolean(settings, "releaseBundleSwap"),
61
+ metroSharedCache: optimizationBoolean(settings, "metroSharedCache"),
62
+ metroWarmup: optimizationBoolean(settings, "metroWarmup"),
63
+ ios: {
64
+ compilationCache: optimizationBoolean(settings, "ios.compilationCache"),
65
+ swiftCompilationCache: optimizationValue(settings, "ios.swiftCompilationCache") === void 0 ? null : optimizationBoolean(settings, "ios.swiftCompilationCache"),
66
+ prefixMapping: optimizationBoolean(settings, "ios.prefixMapping")
67
+ },
68
+ android: {
69
+ compilerCache,
70
+ casToolchain: toolchain,
71
+ compilerCacheFallback: fallback,
72
+ pch: choice("android.pch", ANDROID_PCH_CHOICES, "auto"),
73
+ gradleBuildCache: optimizationBoolean(settings, "android.gradleBuildCache"),
74
+ targetAbiOnly: optimizationBoolean(settings, "android.targetAbiOnly")
75
+ }
76
+ };
77
+ }
78
+ function artifactCachePolicy(options, reuse, release) {
79
+ return {
80
+ read: reuse && options.buildCache && (!release || options.releaseBundleSwap),
81
+ write: options.buildCache,
82
+ remote: options.buildCache && options.remoteBuildCache
83
+ };
84
+ }
85
+ function optimizationValue(settings, path) {
86
+ let node = settings;
87
+ for (const key of ["optimizations", ...path.split(".")]) {
88
+ if (node === void 0) return void 0;
89
+ if (!node || typeof node !== "object" || Array.isArray(node)) throw new Error("Invalid optimizations setting. Expected nested objects.");
90
+ node = node[key];
91
+ }
92
+ return node;
93
+ }
94
+ function optimizationBoolean(settings, path, fallback = true) {
95
+ const raw = optimizationValue(settings, path);
96
+ if (raw === void 0) return fallback;
97
+ if (typeof raw !== "boolean") throw new Error(`Invalid optimizations.${path}. Expected true or false.`);
98
+ return raw;
99
+ }
100
+ function resolveMetroSharedCache(settings) {
101
+ return optimizationBoolean(settings, "metroSharedCache");
102
+ }
103
+ //#endregion
104
+ //#region src/supervisor/metro-store.ts
105
+ function metroStoreName(root) {
106
+ try {
107
+ const name = JSON.parse(readFileSync(join(root, "package.json"), "utf-8"))?.name;
108
+ if (typeof name === "string" && name.trim() !== "") return name;
109
+ } catch {}
110
+ return "app";
111
+ }
112
+ function metroStoreRoot(root) {
113
+ return metroCacheRoot(metroStoreName(root));
114
+ }
115
+ function registerMetroStore(storeRoot) {
116
+ const parent = resolve(metroCacheRoot());
117
+ const dir = resolve(storeRoot);
118
+ registerCache({
119
+ dir,
120
+ name: "Metro transform cache",
121
+ prune: "entries",
122
+ entriesDepth: 2,
123
+ layout: METRO_NAMED_CACHE_LAYOUT,
124
+ note: "shared Metro transforms, installed by stim start; no eviction of its own",
125
+ replaces: dir === parent ? [] : [{
126
+ dir: parent,
127
+ name: "Metro transform cache",
128
+ prune: "entries",
129
+ entriesDepth: 2,
130
+ layout: null
131
+ }]
132
+ });
133
+ }
134
+ function hasStoreAt(stores, storeRoot) {
135
+ if (!Array.isArray(stores)) return false;
136
+ return stores.some((s) => sharedStoreRoot(s) === storeRoot);
137
+ }
138
+ function appendCacheStore(config, { storeRoot, FileStore }) {
139
+ if (!config || typeof config !== "object") return {
140
+ added: false,
141
+ storeRoot,
142
+ reason: "no config to add it to"
143
+ };
144
+ const stores = config.cacheStores;
145
+ if (typeof stores === "function") {
146
+ const original = stores;
147
+ config.cacheStores = (cache) => {
148
+ const resolved = original(cache);
149
+ return (Array.isArray(resolved) ? resolved : []).concat([tagSharedStore(new FileStore({ root: storeRoot }), storeRoot)]);
150
+ };
151
+ return {
152
+ added: true,
153
+ storeRoot
154
+ };
155
+ }
156
+ if (hasStoreAt(stores, storeRoot)) return {
157
+ added: false,
158
+ storeRoot,
159
+ reason: "a store already points at it"
160
+ };
161
+ config.cacheStores = [...Array.isArray(stores) ? stores : [], tagSharedStore(new FileStore({ root: storeRoot }), storeRoot)];
162
+ return {
163
+ added: true,
164
+ storeRoot
165
+ };
166
+ }
167
+ const EXPO_ADAPTER_FILE = "expo-metro-config.cjs";
168
+ function expoMetroConfigPath(fromUrl = import.meta.url) {
169
+ const here = dirname(fileURLToPath(fromUrl));
170
+ for (const rel of ["../shim", "../../shim"]) {
171
+ const candidate = resolve(here, rel, EXPO_ADAPTER_FILE);
172
+ if (existsSync(candidate)) return candidate;
173
+ }
174
+ return null;
175
+ }
176
+ const STORE_OK_PREFIX = "stim-metro-store: sharing Metro transforms through ";
177
+ function metroStoreConfirmedRoot(line) {
178
+ if (!line.startsWith(STORE_OK_PREFIX)) return null;
179
+ const root = line.slice(51).trim();
180
+ return root === "" ? null : root;
181
+ }
182
+ function expoMetroStoreEnv({ root, storeRoot, adapterPath, existingOverride }) {
183
+ return {
184
+ EXPO_OVERRIDE_METRO_CONFIG: adapterPath,
185
+ STIM_METRO_STORE: storeRoot,
186
+ STIM_PROJECT_ROOT: root,
187
+ ...existingOverride ? { STIM_EXPO_METRO_CONFIG: existingOverride } : {}
188
+ };
189
+ }
190
+ //#endregion
191
+ //#region src/supervisor/errors.ts
192
+ function supervisorError(code, message, remedy) {
193
+ const err = new Error(message);
194
+ err.code = code;
195
+ if (remedy) err.remedy = remedy;
196
+ return err;
197
+ }
198
+ function describeError(err) {
199
+ if (!err) return "unknown error";
200
+ const e = err;
201
+ const message = e.message || String(err);
202
+ return `${e.code ? `${e.code}: ` : ""}${message}${e.remedy ? ` Remedy: ${e.remedy}` : ""}`;
203
+ }
204
+ //#endregion
205
+ export { expoMetroStoreEnv as a, registerMetroStore as c, optimizationBuildProfile as d, resolveMetroSharedCache as f, expoMetroConfigPath as i, artifactCachePolicy as l, supervisorError as n, metroStoreConfirmedRoot as o, resolveOptimizations as p, appendCacheStore as r, metroStoreRoot as s, describeError as t, compilerCacheFallbackMessage as u };
@@ -12,6 +12,28 @@ function nameTimeout(error, command, timeoutMs) {
12
12
  if (error?.code === "ETIMEDOUT" && error instanceof Error) error.message = `Command timed out after ${timeoutMs}ms: ${command}`;
13
13
  return error;
14
14
  }
15
+ const WINDOWS_BATCH_FILE = /\.(?:bat|cmd)$/i;
16
+ const BATCH_UNSAFE_ARGUMENT = /["\r\n]/;
17
+ function isUnsafeBatchSpawn(target, args, platform = process.platform) {
18
+ return platform === "win32" && WINDOWS_BATCH_FILE.test(target) && args.some((arg) => BATCH_UNSAFE_ARGUMENT.test(arg));
19
+ }
20
+ function refuseUnsafeBatchArguments(file, args) {
21
+ if (process.platform !== "win32") return;
22
+ const target = which.sync(file, { nothrow: true }) ?? file;
23
+ if (isUnsafeBatchSpawn(target, args)) throw new Error(`Refusing to run ${basename(target)}: an argument contains a character cmd.exe could misinterpret (a double quote or a line break).`);
24
+ }
25
+ function redactError(error, values) {
26
+ const secrets = values?.filter(Boolean) ?? [];
27
+ if (secrets.length === 0 || !error || typeof error !== "object") return error;
28
+ const scrub = (text) => secrets.reduce((acc, secret) => acc.split(secret).join("***"), text);
29
+ const fields = error;
30
+ for (const key of Object.getOwnPropertyNames(error)) {
31
+ const value = fields[key];
32
+ if (typeof value === "string") fields[key] = scrub(value);
33
+ else if (Array.isArray(value)) fields[key] = value.map((item) => typeof item === "string" ? scrub(item) : item);
34
+ }
35
+ return error;
36
+ }
15
37
  const defaultExecutor = {
16
38
  run(cmd, { timeoutMs, killSignal, cwd, env } = {}) {
17
39
  const opts = {
@@ -36,16 +58,17 @@ const defaultExecutor = {
36
58
  throw nameTimeout(error, cmd, timeoutMs);
37
59
  }
38
60
  },
39
- runFile(file, args = [], { timeoutMs, killSignal, cwd, env, omitEnv, input, untrimmed, rejectStderr } = {}) {
61
+ runFile(file, args = [], { timeoutMs, killSignal, cwd, env, omitEnv, input, untrimmed, rejectStderr, detachedSilent, redact } = {}) {
40
62
  const opts = {
41
63
  encoding: "utf-8",
42
- stdio: [
64
+ stdio: detachedSilent ? "ignore" : [
43
65
  "pipe",
44
66
  "pipe",
45
67
  "pipe"
46
68
  ],
47
69
  maxBuffer: MAX_BUFFER
48
70
  };
71
+ if (detachedSilent) opts.detached = true;
49
72
  if (timeoutMs) opts.timeout = timeoutMs;
50
73
  if (killSignal) opts.killSignal = killSignal;
51
74
  if (cwd) opts.cwd = cwd;
@@ -58,19 +81,25 @@ const defaultExecutor = {
58
81
  for (const key of omitEnv ?? []) delete childEnv[key];
59
82
  opts.env = childEnv;
60
83
  }
84
+ refuseUnsafeBatchArguments(file, args);
61
85
  const result = spawn.sync(file, args, opts);
62
- if (result.error) throw nameTimeout(Object.assign(result.error, result), [file, ...args].join(" "), timeoutMs);
86
+ if (result.error) throw redactError(nameTimeout(Object.assign(result.error, result), [file, ...args].join(" "), timeoutMs), redact);
63
87
  if (result.status !== 0 || rejectStderr && String(result.stderr ?? "").trim()) {
64
88
  const stderr = String(result.stderr ?? "");
65
89
  const message = `Command failed: ${[file, ...args].join(" ")}${stderr ? `\n${stderr}` : ""}`;
66
- throw Object.assign(new Error(message), result);
90
+ throw redactError(Object.assign(new Error(message), result), redact);
67
91
  }
92
+ if (detachedSilent) return "";
68
93
  return untrimmed ? String(result.stdout) : String(result.stdout).trim();
69
94
  },
70
- runFileAsync(file, args = [], { timeoutMs, killSignal, cwd, env, omitEnv, rejectStderr, onSpawn } = {}) {
95
+ runFileAsync(file, args = [], { timeoutMs, killSignal, cwd, env, omitEnv, rejectStderr, onSpawn, detachedSilent, redact } = {}) {
71
96
  const command = [file, ...args].join(" ");
72
97
  return new Promise((resolve, reject) => {
73
- const opts = { stdio: [
98
+ refuseUnsafeBatchArguments(file, args);
99
+ const opts = detachedSilent ? {
100
+ stdio: "ignore",
101
+ detached: true
102
+ } : { stdio: [
74
103
  "ignore",
75
104
  "pipe",
76
105
  "pipe"
@@ -103,7 +132,7 @@ const defaultExecutor = {
103
132
  child.stderr?.on("data", (chunk) => stderr.push(chunk));
104
133
  child.on("error", (error) => {
105
134
  clearTimeout(timer);
106
- reject(error);
135
+ reject(redactError(error, redact));
107
136
  });
108
137
  child.on("close", (status, signal) => {
109
138
  clearTimeout(timer);
@@ -115,9 +144,9 @@ const defaultExecutor = {
115
144
  stdout: out,
116
145
  stderr: err
117
146
  };
118
- if (timedOut) reject(nameTimeout(Object.assign(new Error(command), result, { code: "ETIMEDOUT" }), command, timeoutMs));
119
- else if (status !== 0 || rejectStderr && err.trim()) reject(Object.assign(/* @__PURE__ */ new Error(`Command failed: ${command}${err ? `\n${err}` : ""}`), result));
120
- else resolve(out.trim());
147
+ if (timedOut) reject(redactError(nameTimeout(Object.assign(new Error(command), result, { code: "ETIMEDOUT" }), command, timeoutMs), redact));
148
+ else if (status !== 0 || rejectStderr && err.trim()) reject(redactError(Object.assign(/* @__PURE__ */ new Error(`Command failed: ${command}${err ? `\n${err}` : ""}`), result), redact));
149
+ else resolve(detachedSilent ? "" : out.trim());
121
150
  });
122
151
  });
123
152
  },
@@ -136,6 +165,7 @@ const defaultExecutor = {
136
165
  }
137
166
  },
138
167
  spawn(cmd, args = [], opts = {}) {
168
+ refuseUnsafeBatchArguments(cmd, args);
139
169
  return spawn(cmd, args, opts);
140
170
  },
141
171
  findExecutable(name) {