stim 1.8.0 → 1.9.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 (80) hide show
  1. package/README.md +23 -155
  2. package/dist/{android-BRgBYzU0.mjs → android-BPGizezh.mjs} +112 -135
  3. package/dist/{android-DeYdjjBP.mjs → android-DEgo4oCo.mjs} +2 -2
  4. package/dist/{android-DWssqHQ8.mjs → android-_y4Ohi5z.mjs} +54 -15
  5. package/dist/android-cas-compiler.mjs +2 -2
  6. package/dist/{android-cas-SWIjzeuL.mjs → android-cas-eiExNLf7.mjs} +21 -14
  7. package/dist/app-install-Uf3a83Tv.mjs +819 -0
  8. package/dist/{build-lock-CgtsB6TV.mjs → build-lock-DPXPLgTc.mjs} +120 -24
  9. package/dist/build-progress-CVqkfPC6.mjs +188 -0
  10. package/dist/{cache-manifest-CYJi8-Hx.mjs → cache-manifest-Dg7OnMrp.mjs} +4 -4
  11. package/dist/cache-manifest.mjs +1 -1
  12. package/dist/cli.mjs +22 -19
  13. package/dist/collector-run.d.mts +2 -0
  14. package/dist/collector-run.mjs +10 -9
  15. package/dist/{command-output-B3ycpsYv.mjs → command-output-gByVmIjy.mjs} +11 -2
  16. package/dist/{config-B7iyKWkL.mjs → config-Cp9lM3wX.mjs} +187 -57
  17. package/dist/{dependency-state-D0M7oKA7.mjs → dependency-state-Bq5DcLGn.mjs} +2 -2
  18. package/dist/{deps-oCXVNEqQ.mjs → deps-Cip5vUm1.mjs} +8 -5
  19. package/dist/{detached-entry-Bpo6ZY_I.mjs → detached-entry-KEu0_AXL.mjs} +1 -1
  20. package/dist/{device-BUoZyMoR.mjs → dev-client-HyqISM0h.mjs} +1178 -635
  21. package/dist/{device-RdujjHLB.mjs → device-D4U-xLty.mjs} +13 -20
  22. package/dist/{device-lease-Ffz_M5t0.mjs → device-lease-Crs-HkJY.mjs} +16 -9
  23. package/dist/{device-pool-BxRwdufO.mjs → device-pool-CnTAz8X1.mjs} +7 -7
  24. package/dist/{device-remote-ByN8P6CB.mjs → device-remote-AQ5rQPqr.mjs} +482 -35
  25. package/dist/{doctor-BMt156qe.mjs → doctor-B_Ei3bZu.mjs} +95 -107
  26. package/dist/{error-diagnostics-D1Kaf-eT.mjs → error-diagnostics-Vfz83GCF.mjs} +22 -16
  27. package/dist/exec-jIAlgilk.mjs +132 -0
  28. package/dist/{gc-v2YcoJvm.mjs → gc-Ckw8RQHi.mjs} +763 -126
  29. package/dist/{guide-BTAlD1_y.mjs → guide-BiAbhs1J.mjs} +891 -501
  30. package/dist/{guide-status-Craq9z0t.mjs → guide-status-BAMVU0ZA.mjs} +5 -3
  31. package/dist/{ios-BcCiI4TS.mjs → ios-BL2NFckr.mjs} +43 -20
  32. package/dist/{ios-BP04pY5f.mjs → ios-BzUJWk1K.mjs} +141 -175
  33. package/dist/{ios-device-CauFJiYx.mjs → ios-device-D972i9wf.mjs} +149 -52
  34. package/dist/{ios-device-CFXA46ez.mjs → ios-device-DPsSm0v_.mjs} +3 -2
  35. package/dist/launch-verify-Q_OtW1Rf.mjs +365 -0
  36. package/dist/{logs-CakdBTtm.mjs → logs-CI9NQSK7.mjs} +18 -13
  37. package/dist/{logs-query-BGL-Pl12.mjs → logs-query-Cqma3WIu.mjs} +36 -28
  38. package/dist/{metro-C7bcJ0uo.mjs → metro-DrLHaMn2.mjs} +78 -8
  39. package/dist/{named-ports-DzMOlmaX.mjs → named-ports-CmLQSIRj.mjs} +4 -4
  40. package/dist/{ndjson-BZwI1NBv.mjs → ndjson-9uFNRwfe.mjs} +36 -3
  41. package/dist/{ownership-Cp52QAK1.mjs → ownership-D15Op8vN.mjs} +3 -3
  42. package/dist/{ownership-wolE-0Do.mjs → ownership-DCQPuWEc.mjs} +26 -7
  43. package/dist/{ports-BIbJaKNW.mjs → ports-BNKoWVWZ.mjs} +3 -3
  44. package/dist/{process-output-Dl5uKE03.mjs → process-output-Fpl8PYi8.mjs} +1 -1
  45. package/dist/{project-DDZWf_16.mjs → project-DS112DYB.mjs} +8 -3
  46. package/dist/{reload-BdypPbjM.mjs → reload-ZNJUo1pl.mjs} +12 -13
  47. package/dist/{server-bare-BQkyxa_b.mjs → server-bare-DWkkcIxV.mjs} +2 -2
  48. package/dist/{server-expo-BVAA1OqJ.mjs → server-expo-B_lctBR3.mjs} +8 -7
  49. package/dist/server-expo-CeILEu86.mjs +2 -0
  50. package/dist/settings-BKNh5rl4.mjs +376 -0
  51. package/dist/{settings-BXXorVM2.mjs → settings-D030k0t4.mjs} +464 -152
  52. package/dist/settings.schema.json +989 -0
  53. package/dist/{teardown-BJqEzPo_.mjs → sim-pool-KaIsp2Ft.mjs} +14 -419
  54. package/dist/simslim-B_LsGLuJ.mjs +259 -0
  55. package/dist/{slot-launch-ZUUe0TQP.mjs → slot-launch-DU9A-gYw.mjs} +4 -4
  56. package/dist/spawn-claims-Dn3C_N6w.mjs +49 -0
  57. package/dist/{start-CmxtOKjU.mjs → start-DYkkKF0s.mjs} +27 -31
  58. package/dist/{state-DZobzxLW.mjs → state-NcJzMEBF.mjs} +5 -3
  59. package/dist/{stats-CLg5RuDS.mjs → stats-B63-JVAY.mjs} +4 -4
  60. package/dist/{stats-BYcZ6n1h.mjs → stats-DG9e66Rz.mjs} +66 -5
  61. package/dist/status-S6ehL4Ui.mjs +932 -0
  62. package/dist/{stop-C8JW6kKp.mjs → stop-BO28HGqF.mjs} +101 -25
  63. package/dist/{stop-B1kOpYei.mjs → stop-T1R-cZRz.mjs} +2 -2
  64. package/dist/supervisor-run.mjs +52 -34
  65. package/dist/{workspace-process-lock-BYfZz_S-.mjs → workspace-process-lock-DWaiRD0e.mjs} +14 -9
  66. package/dist/{workspace-state-BT5E3SA2.mjs → workspace-state-DF2gCFhx.mjs} +52 -29
  67. package/dist/worktree-DKhSF0UX.mjs +2 -0
  68. package/dist/{worktree-CFLA-6MX.mjs → worktree-LGB-UwgG.mjs} +603 -59
  69. package/dist/{worktree-FG_ZKKdb.mjs → worktree-R_cUlE7s.mjs} +62 -41
  70. package/dist/{xcode-B6YaWeg0.mjs → xcode-BZqyxh3e.mjs} +216 -58
  71. package/package.json +5 -6
  72. package/skill/SKILL.md +2 -0
  73. package/dist/app-install-BDbsSgCl.mjs +0 -1175
  74. package/dist/build-slots-C-oAHRT1.mjs +0 -151
  75. package/dist/dev-client-BmT-ioFI.mjs +0 -797
  76. package/dist/exec-B29XNuhP.mjs +0 -77
  77. package/dist/reclaim-KJ-0uWEF.mjs +0 -361
  78. package/dist/server-expo-YDDG3eP7.mjs +0 -2
  79. package/dist/status-DdkLki47.mjs +0 -426
  80. package/dist/{metro-store-CDDZHtPl.mjs → metro-store-DzeWkfqW.mjs} +1 -1
@@ -1,22 +1,23 @@
1
- import { t as getExecutor } from "./exec-B29XNuhP.mjs";
2
- import { g as loadConfig, u as getConfigDir } from "./config-B7iyKWkL.mjs";
1
+ import { g as loadConfig, u as getConfigDir } from "./config-Cp9lM3wX.mjs";
2
+ import { t as getExecutor } from "./exec-jIAlgilk.mjs";
3
3
  import { accessSync, chmodSync, constants, lstatSync, mkdirSync, mkdtempSync, readdirSync, realpathSync, rmSync, statSync } from "node:fs";
4
- import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
5
4
  import { chmodSync as chmodSync$1, existsSync as existsSync$1, lstatSync as lstatSync$1, mkdirSync as mkdirSync$1, readFileSync as readFileSync$1, readdirSync as readdirSync$1, realpathSync as realpathSync$1, utimesSync } from "fs";
6
- import { dirname as dirname$1, isAbsolute as isAbsolute$1, join as join$1, relative as relative$1, resolve as resolve$1, sep as sep$1 } from "path";
5
+ import { dirname, isAbsolute, join, relative, resolve, sep } from "path";
6
+ import { buildCacheRoot } from "@stim-cli/core";
7
+ import { basename as basename$1, dirname as dirname$1, isAbsolute as isAbsolute$1, join as join$1, relative as relative$1, resolve as resolve$1, sep as sep$1 } from "node:path";
7
8
  import { tmpdir } from "node:os";
8
9
  //#region src/temporary.ts
9
10
  function existingPath(path) {
10
11
  const missing = [];
11
- let current = resolve(path);
12
+ let current = resolve$1(path);
12
13
  while (!lstatSync(current, { throwIfNoEntry: false })) {
13
- missing.unshift(basename(current));
14
- current = dirname(current);
14
+ missing.unshift(basename$1(current));
15
+ current = dirname$1(current);
15
16
  }
16
17
  const existing = realpathSync(current);
17
18
  return {
18
19
  existing,
19
- resolved: join(existing, ...missing)
20
+ resolved: join$1(existing, ...missing)
20
21
  };
21
22
  }
22
23
  function filesystemDevice(path) {
@@ -25,21 +26,22 @@ function filesystemDevice(path) {
25
26
  function outsideWorkingTree(path) {
26
27
  let current = path;
27
28
  while (true) {
28
- if (lstatSync(join(current, ".git"), { throwIfNoEntry: false })) return false;
29
- const parent = dirname(current);
29
+ if (lstatSync(join$1(current, ".git"), { throwIfNoEntry: false })) return false;
30
+ const parent = dirname$1(current);
30
31
  if (parent === current) return true;
31
32
  current = parent;
32
33
  }
33
34
  }
35
+ function inside(parent, path) {
36
+ const rel = relative$1(parent, path);
37
+ return rel === "" || rel !== ".." && !rel.startsWith(`..${sep$1}`) && !isAbsolute$1(rel);
38
+ }
34
39
  function temporaryRoot(near) {
35
40
  const reference = existingPath(near);
36
- const insideReference = (path) => {
37
- const rel = relative(reference.resolved, path);
38
- return rel === "" || rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute(rel);
39
- };
41
+ const insideReference = (path) => inside(reference.resolved, path);
40
42
  const override = process.env.STIM_TMPDIR ?? loadConfig()?.tempDir;
41
43
  if (override !== void 0) {
42
- if (typeof override !== "string" || !isAbsolute(override)) throw new Error("STIM_TMPDIR / tempDir must be an absolute directory path.");
44
+ if (typeof override !== "string" || !isAbsolute$1(override)) throw new Error("STIM_TMPDIR / tempDir must be an absolute directory path.");
43
45
  const { existing, resolved } = existingPath(override);
44
46
  if (!statSync(existing).isDirectory() || !outsideWorkingTree(existing)) throw new Error(`Temporary directory ${override} must be outside Git working trees.`);
45
47
  if (insideReference(resolved)) throw new Error(`Temporary directory ${override} must not be inside ${near}.`);
@@ -47,22 +49,23 @@ function temporaryRoot(near) {
47
49
  return resolved;
48
50
  }
49
51
  const { existing } = reference;
52
+ const cache = existingPath(buildCacheRoot()).resolved;
50
53
  const device = statSync(existing).dev;
51
54
  const candidates = [tmpdir()];
52
- let current = existing === reference.resolved || !statSync(existing).isDirectory() ? dirname(existing) : existing;
55
+ let current = existing === reference.resolved || !statSync(existing).isDirectory() ? dirname$1(existing) : existing;
53
56
  while (statSync(current).dev === device) {
54
57
  candidates.push(current);
55
- const parent = dirname(current);
58
+ const parent = dirname$1(current);
56
59
  if (parent === current) break;
57
60
  current = parent;
58
61
  }
59
62
  for (const candidate of candidates) try {
60
63
  const path = realpathSync(candidate);
61
- if (statSync(path).dev !== device || insideReference(path) || !outsideWorkingTree(path)) continue;
64
+ if (statSync(path).dev !== device || insideReference(path) || inside(cache, path) || !outsideWorkingTree(path)) continue;
62
65
  accessSync(path, constants.W_OK);
63
66
  return path;
64
67
  } catch {}
65
- throw new Error(`No writable temporary directory on the filesystem containing ${near}. Set STIM_TMPDIR or tempDir in ${join(getConfigDir(), "config.json")} to a directory outside Git working trees.`);
68
+ throw new Error(`No writable temporary directory on the filesystem containing ${near}. Set STIM_TMPDIR or tempDir in ${join$1(getConfigDir(), "config.json")} to a directory outside Git working trees.`);
66
69
  }
67
70
  function makeTemporaryDirectory(near, prefix) {
68
71
  const root = temporaryRoot(near);
@@ -70,13 +73,13 @@ function makeTemporaryDirectory(near, prefix) {
70
73
  recursive: true,
71
74
  mode: 448
72
75
  });
73
- return mkdtempSync(join(root, prefix));
76
+ return mkdtempSync(join$1(root, prefix));
74
77
  }
75
78
  function removeTemporaryEntry(path) {
76
79
  const stat = lstatSync(path, { throwIfNoEntry: false });
77
80
  if (stat?.isDirectory()) {
78
81
  chmodSync(path, stat.mode | 448);
79
- for (const entry of readdirSync(path, { withFileTypes: true })) if (entry.isDirectory()) removeTemporaryEntry(join(path, entry.name));
82
+ for (const entry of readdirSync(path, { withFileTypes: true })) if (entry.isDirectory()) removeTemporaryEntry(join$1(path, entry.name));
80
83
  }
81
84
  rmSync(path, {
82
85
  recursive: true,
@@ -94,7 +97,7 @@ function isCarrySkipped(rel) {
94
97
  return /(^|\/)android\/build\/generated\/autolinking(\/|$)/.test(rel) || String(rel).split("/").some((seg) => CARRY_SKIP_BASENAMES.has(seg));
95
98
  }
96
99
  function nativePath(path) {
97
- return sep$1 === "/" ? path : path.replaceAll("/", sep$1);
100
+ return sep === "/" ? path : path.replaceAll("/", sep);
98
101
  }
99
102
  function gitCommonDir(cwd) {
100
103
  const out = getExecutor().runFileQuiet("git", [
@@ -173,7 +176,7 @@ function matchesInclude(path, patterns) {
173
176
  return false;
174
177
  }
175
178
  function readWorktreeExclude(root) {
176
- return readPatternFile(join$1(root, ".worktreeexclude"));
179
+ return readPatternFile(join(root, ".worktreeexclude"));
177
180
  }
178
181
  function readPatternFile(p) {
179
182
  if (!existsSync$1(p)) return null;
@@ -183,7 +186,7 @@ function canonicalPath(path) {
183
186
  try {
184
187
  return realpathSync$1(path);
185
188
  } catch {
186
- return resolve$1(path);
189
+ return resolve(path);
187
190
  }
188
191
  }
189
192
  function nestedWorktreePaths(root) {
@@ -198,9 +201,9 @@ function nestedWorktreePaths(root) {
198
201
  ]);
199
202
  if (out === null) throw new Error("Could not list Git worktrees. Refusing to carry ignored files.");
200
203
  for (const entry of parseWorktrees(out)) {
201
- const rel = relative$1(source, canonicalPath(entry.path));
202
- if (!rel || rel === ".." || rel.startsWith(`..${sep$1}`) || isAbsolute$1(rel)) continue;
203
- paths.add(rel.split(sep$1).join("/"));
204
+ const rel = relative(source, canonicalPath(entry.path));
205
+ if (!rel || rel === ".." || rel.startsWith(`..${sep}`) || isAbsolute(rel)) continue;
206
+ paths.add(rel.split(sep).join("/"));
204
207
  }
205
208
  return [...paths];
206
209
  }
@@ -260,11 +263,11 @@ function missingDestinationReason(target, rel) {
260
263
  const parts = rel.split("/");
261
264
  for (let i = 1; i < parts.length; i++) {
262
265
  const parent = parts.slice(0, i).join("/");
263
- const stat = lstatSync$1(join$1(target, parent), { throwIfNoEntry: false });
266
+ const stat = lstatSync$1(join(target, parent), { throwIfNoEntry: false });
264
267
  if (stat?.isSymbolicLink()) return `symlink ancestor: ${parent}`;
265
268
  if (stat && !stat.isDirectory()) return `non-directory ancestor: ${parent}`;
266
269
  }
267
- return lstatSync$1(join$1(target, rel), { throwIfNoEntry: false }) ? "exists" : null;
270
+ return lstatSync$1(join(target, rel), { throwIfNoEntry: false }) ? "exists" : null;
268
271
  }
269
272
  function removeCopiedExclusions(path, rel) {
270
273
  const entries = readdirSync$1(path, { withFileTypes: true });
@@ -273,7 +276,7 @@ function removeCopiedExclusions(path, rel) {
273
276
  const stat = lstatSync$1(path);
274
277
  chmodSync$1(path, stat.mode | 448);
275
278
  try {
276
- for (const entry of excluded) removeTemporaryEntry(join$1(path, entry.name));
279
+ for (const entry of excluded) removeTemporaryEntry(join(path, entry.name));
277
280
  } finally {
278
281
  utimesSync(path, stat.atime, stat.mtime);
279
282
  chmodSync$1(path, stat.mode);
@@ -281,7 +284,7 @@ function removeCopiedExclusions(path, rel) {
281
284
  }
282
285
  for (const entry of entries) {
283
286
  const child = `${rel}/${entry.name}`;
284
- if (entry.isDirectory() && !isCarrySkipped(child)) removeCopiedExclusions(join$1(path, entry.name), child);
287
+ if (entry.isDirectory() && !isCarrySkipped(child)) removeCopiedExclusions(join(path, entry.name), child);
285
288
  }
286
289
  }
287
290
  function cloneIgnoredEntries({ root, target, patterns }) {
@@ -293,8 +296,8 @@ function cloneIgnoredEntries({ root, target, patterns }) {
293
296
  if (!guard.known) throw new Error("Could not list the destination's tracked files.");
294
297
  const nested = nestedWorktreePaths(target);
295
298
  for (const rel of listCarryableIgnoredEntries(root, patterns)) {
296
- const from = join$1(root, rel);
297
- const to = join$1(target, rel);
299
+ const from = join(root, rel);
300
+ const to = join(target, rel);
298
301
  try {
299
302
  const reason = guard.covers(rel) ? "tracked" : overlapsNestedWorktree(rel, nested) ? "nested worktree" : missingDestinationReason(target, rel);
300
303
  if (reason) {
@@ -304,7 +307,7 @@ function cloneIgnoredEntries({ root, target, patterns }) {
304
307
  });
305
308
  continue;
306
309
  }
307
- mkdirSync$1(dirname$1(to), { recursive: true });
310
+ mkdirSync$1(dirname(to), { recursive: true });
308
311
  try {
309
312
  getExecutor().runFile("cp", [
310
313
  "-Rc",
@@ -341,8 +344,8 @@ function podsOutOfSync(target, copiedEntries, { read = readFileSync$1 } = {}) {
341
344
  for (const rel of copiedEntries || []) {
342
345
  if (rel !== "Pods" && !rel.endsWith("/Pods")) continue;
343
346
  const iosDir = rel === "Pods" ? "" : rel.slice(0, -5);
344
- const manifest = join$1(target, rel, "Manifest.lock");
345
- const podfileLock = join$1(target, iosDir, "Podfile.lock");
347
+ const manifest = join(target, rel, "Manifest.lock");
348
+ const podfileLock = join(target, iosDir, "Podfile.lock");
346
349
  if (!existsSync$1(manifest)) continue;
347
350
  if (!existsSync$1(podfileLock)) {
348
351
  problems.push({
@@ -373,8 +376,8 @@ function depsOutOfSync(root, target, copiedEntries, { read = readFileSync$1 } =
373
376
  if (rel !== "node_modules" && !rel.endsWith("/node_modules")) continue;
374
377
  const dir = rel === "node_modules" ? "" : rel.slice(0, -13);
375
378
  for (const name of LOCKFILE_NAMES) {
376
- const source = join$1(root, dir, name);
377
- const branch = join$1(target, dir, name);
379
+ const source = join(root, dir, name);
380
+ const branch = join(target, dir, name);
378
381
  if (!existsSync$1(source) || !existsSync$1(branch)) continue;
379
382
  try {
380
383
  if (read(source, "utf-8") !== read(branch, "utf-8")) problems.push({
@@ -546,10 +549,28 @@ function parseWorktrees(out) {
546
549
  current = { path: nativePath(line.slice(9)) };
547
550
  } else if (line.startsWith("branch ")) current.branch = line.slice(7).replace("refs/heads/", "");
548
551
  else if (line === "prunable" || line.startsWith("prunable ")) current.prunable = true;
552
+ else if (line === "locked" || line.startsWith("locked ")) current.locked = true;
549
553
  else if (line === "bare") current.bare = true;
550
554
  if (current.path) entries.push(current);
551
555
  return entries;
552
556
  }
557
+ function hasPopulatedSubmodules(worktree) {
558
+ const modules = getExecutor().runFileQuiet("git", [
559
+ "-C",
560
+ worktree,
561
+ "rev-parse",
562
+ "--git-path",
563
+ "modules"
564
+ ]);
565
+ if (modules && existsSync$1(resolve(worktree, nativePath(modules)))) return true;
566
+ return (getExecutor().runFileQuiet("git", [
567
+ "-C",
568
+ worktree,
569
+ "ls-files",
570
+ "--stage",
571
+ "-z"
572
+ ]) ?? "").split("\0").some((line) => line.startsWith("160000 ") && existsSync$1(join(worktree, line.slice(line.indexOf(" ") + 1), ".git")));
573
+ }
553
574
  function listWorktrees(cwd) {
554
575
  const out = getExecutor().runFileQuiet("git", [
555
576
  "-C",
@@ -574,8 +595,8 @@ function selectSourceCheckout(entries, bareHead) {
574
595
  if (only && matches.length === 1) return { path: only.path };
575
596
  if (!only) {
576
597
  const stale = entries.find((entry) => entry.prunable && entry.branch === bareHead);
577
- const beside = checkouts[0] ? dirname$1(checkouts[0].path) : bare;
578
- return { refusal: `The bare repository at ${bare} points HEAD at ${bareHead}, but no worktree has ${bareHead} checked out, so Stim has no source checkout to copy from. ${stale ? `Its worktree at ${stale.path} is missing. Recreate it, then retry:\n git -C ${bare} worktree prune\n git -C ${bare} worktree add ${stale.path} ${bareHead}\n` : `Create one, then retry:\n git -C ${bare} worktree add ${join$1(beside, bareHead)} ${bareHead}\n`}Or point HEAD at a branch that is checked out (${listed}):\n${pointHead}` };
598
+ const beside = checkouts[0] ? dirname(checkouts[0].path) : bare;
599
+ return { refusal: `The bare repository at ${bare} points HEAD at ${bareHead}, but no worktree has ${bareHead} checked out, so Stim has no source checkout to copy from. ${stale ? `Its worktree at ${stale.path} is missing. Recreate it, then retry:\n git -C ${bare} worktree prune\n git -C ${bare} worktree add ${stale.path} ${bareHead}\n` : `Create one, then retry:\n git -C ${bare} worktree add ${join(beside, bareHead)} ${bareHead}\n`}Or point HEAD at a branch that is checked out (${listed}):\n${pointHead}` };
579
600
  }
580
601
  return { refusal: `The bare repository at ${bare} points HEAD at ${bareHead}, which is checked out in more than one worktree: ${matches.map((entry) => entry.path).join(", ")}. Remove the extra worktrees, or point HEAD at a branch checked out once, then retry:\n${pointHead}` };
581
602
  }
@@ -601,4 +622,4 @@ function resolveSourceCheckout(cwd) {
601
622
  };
602
623
  }
603
624
  //#endregion
604
- export { filesystemDevice as C, temporaryRoot as E, warmWorktreePaths as S, removeTemporaryEntry as T, resolveFullRef as _, dirtyFingerprintFiles as a, sourceCheckoutOf as b, hasRemote as c, listWorktrees as d, locallyKnownUpstream as f, repoRoot as g, removeWorktree as h, depsOutOfSync as i, hasUncommittedWork as l, readWorktreeExclude as m, cloneIgnoredEntries as n, dirtyPaths as o, podsOutOfSync as p, deleteBranch as r, gitCommonDir as s, branchExists as t, isPodInstallChurn as u, resolveSourceCheckout as v, makeTemporaryDirectory as w, unpushedCommits as x, restoreFile as y };
625
+ export { warmWorktreePaths as C, temporaryRoot as D, removeTemporaryEntry as E, unpushedCommits as S, makeTemporaryDirectory as T, repoRoot as _, dirtyFingerprintFiles as a, restoreFile as b, hasPopulatedSubmodules as c, isPodInstallChurn as d, listWorktrees as f, removeWorktree as g, readWorktreeExclude as h, depsOutOfSync as i, hasRemote as l, podsOutOfSync as m, cloneIgnoredEntries as n, dirtyPaths as o, locallyKnownUpstream as p, deleteBranch as r, gitCommonDir as s, branchExists as t, hasUncommittedWork as u, resolveFullRef as v, filesystemDevice as w, sourceCheckoutOf as x, resolveSourceCheckout as y };
@@ -1,15 +1,165 @@
1
- import { t as getExecutor } from "./exec-B29XNuhP.mjs";
2
- import { r as register } from "./cache-manifest-CYJi8-Hx.mjs";
3
- import { d as sharedCompilationCache, m as workspaceDerivedData } from "./workspace-state-BT5E3SA2.mjs";
4
- import { l as resolvePackageJson } from "./project-DDZWf_16.mjs";
5
- import { t as createLineReader } from "./process-output-Dl5uKE03.mjs";
6
- import { U as resolveOptimizations } from "./settings-BXXorVM2.mjs";
7
- import { a as formatElapsed, l as phaseLine } from "./command-output-B3ycpsYv.mjs";
8
- import { t as cleanLine } from "./server-expo-BVAA1OqJ.mjs";
1
+ import { k as ownership_claim_exports, u as getConfigDir } from "./config-Cp9lM3wX.mjs";
2
+ import { t as getExecutor } from "./exec-jIAlgilk.mjs";
3
+ import { r as register } from "./cache-manifest-Dg7OnMrp.mjs";
4
+ import { g as workspaceDerivedData, p as sharedCompilationCache } from "./workspace-state-DF2gCFhx.mjs";
5
+ import { u as resolvePackageJson } from "./project-DS112DYB.mjs";
6
+ import { t as createLineReader } from "./process-output-Fpl8PYi8.mjs";
7
+ import { Y as resolveOptimizations } from "./settings-D030k0t4.mjs";
8
+ import { a as formatElapsed, l as phaseLine } from "./command-output-gByVmIjy.mjs";
9
+ import { n as spawnDeclared, r as stopDeclaringSpawnsOn, t as declareSpawnsOn } from "./spawn-claims-Dn3C_N6w.mjs";
10
+ import { t as cleanLine } from "./server-expo-B_lctBR3.mjs";
9
11
  import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
10
- import { createHash } from "node:crypto";
11
12
  import { basename, isAbsolute, join, relative, resolve, sep } from "node:path";
13
+ import { createHash } from "node:crypto";
12
14
  import chalk from "chalk";
15
+ //#region src/engine/build-slots.ts
16
+ const SLOT_PREFIX = "slot-";
17
+ const SLOT_LABEL = "build slot";
18
+ const SLOT_POLL_MS = 1e3;
19
+ const SLOT_PROGRESS_MS = 3e4;
20
+ const SLOT_CEILING_MS = 54e5;
21
+ function sleepAsync(ms) {
22
+ return new Promise((resolve) => setTimeout(resolve, ms));
23
+ }
24
+ function buildSlotsDir() {
25
+ return join(getConfigDir(), "build-slots");
26
+ }
27
+ function buildSlotPath(index) {
28
+ return join(buildSlotsDir(), `${SLOT_PREFIX}${index}`);
29
+ }
30
+ function toRecord(holder) {
31
+ const details = holder.details;
32
+ return {
33
+ pid: holder.owner.pid,
34
+ index: typeof details.index === "number" ? details.index : null,
35
+ projectRoot: typeof details.projectRoot === "string" ? details.projectRoot : null,
36
+ startedAt: holder.startedAt || null,
37
+ logFile: typeof details.logFile === "string" ? details.logFile : null
38
+ };
39
+ }
40
+ function tryAcquireBuildSlot({ max, root = null, logFile = null }) {
41
+ if (!max || max <= 0) return {
42
+ acquired: true,
43
+ unlimited: true
44
+ };
45
+ let refusal = null;
46
+ let busy = false;
47
+ for (let index = 0; index < max; index++) {
48
+ const path = buildSlotPath(index);
49
+ let attempt;
50
+ try {
51
+ attempt = (0, ownership_claim_exports.tryAcquireClaim)({
52
+ root: path,
53
+ mode: "exclusive",
54
+ label: SLOT_LABEL,
55
+ details: {
56
+ index,
57
+ projectRoot: root,
58
+ logFile
59
+ }
60
+ });
61
+ } catch (err) {
62
+ if (!(0, ownership_claim_exports.isClaimRefusal)(err)) throw err;
63
+ refusal ??= err;
64
+ continue;
65
+ }
66
+ if (attempt.pending) (0, ownership_claim_exports.releaseClaim)(attempt.pending);
67
+ if (!attempt.acquired) {
68
+ busy = true;
69
+ continue;
70
+ }
71
+ declareSpawnsOn(attempt.acquired);
72
+ return {
73
+ acquired: true,
74
+ path,
75
+ index,
76
+ slot: {
77
+ pid: attempt.acquired.owner.pid,
78
+ index,
79
+ projectRoot: root,
80
+ startedAt: attempt.acquired.startedAt,
81
+ logFile
82
+ },
83
+ claim: attempt.acquired
84
+ };
85
+ }
86
+ if (refusal && !busy) throw refusal;
87
+ return null;
88
+ }
89
+ function slotWaitingLine({ max, elapsedMs }) {
90
+ return `${"build".padEnd(11)} waiting for a build slot (all ${max} in use, ${formatElapsed(elapsedMs)} elapsed) -- stim guide lifecycle concurrency`;
91
+ }
92
+ async function acquireBuildSlot({ max, root = null, logFile = null, now = Date.now, out = () => {}, sleep = sleepAsync, intervalMs = SLOT_POLL_MS, progressMs = SLOT_PROGRESS_MS, ceilingMs = SLOT_CEILING_MS }) {
93
+ if (!max || max <= 0) return {
94
+ acquired: true,
95
+ unlimited: true
96
+ };
97
+ const started = now();
98
+ let lastProgress = started;
99
+ for (;;) {
100
+ const got = tryAcquireBuildSlot({
101
+ max,
102
+ root,
103
+ logFile
104
+ });
105
+ if (got) return got;
106
+ const elapsed = now() - started;
107
+ if (elapsed >= ceilingMs) {
108
+ const err = /* @__PURE__ */ new Error(`Waited ${formatElapsed(elapsed)} for one of ${max} build slots, and every slot is held by a process that is still running, or by a holder Stim cannot identify. Slots live under ` + buildSlotsDir() + "; remove a slot directory whose builder is not really building, or raise concurrency.maxBuilds.");
109
+ err.code = "STIM_BUILD_SLOT_TIMEOUT";
110
+ throw err;
111
+ }
112
+ if (now() - lastProgress >= progressMs) {
113
+ lastProgress = now();
114
+ out(slotWaitingLine({
115
+ max,
116
+ elapsedMs: elapsed
117
+ }));
118
+ }
119
+ await sleep(intervalMs);
120
+ }
121
+ }
122
+ function releaseBuildSlot(handle) {
123
+ if (!handle || handle.unlimited) return false;
124
+ stopDeclaringSpawnsOn(handle.claim);
125
+ return (0, ownership_claim_exports.releaseClaim)(handle.claim);
126
+ }
127
+ function listBuildSlots() {
128
+ const dir = buildSlotsDir();
129
+ let names;
130
+ try {
131
+ names = readdirSync(dir);
132
+ } catch {
133
+ return [];
134
+ }
135
+ const slots = [];
136
+ for (const name of names) {
137
+ if (!name.startsWith(SLOT_PREFIX)) continue;
138
+ const path = join(dir, name);
139
+ try {
140
+ if (!statSync(path).isDirectory()) continue;
141
+ } catch {
142
+ continue;
143
+ }
144
+ const survey = (0, ownership_claim_exports.readClaimSet)(path);
145
+ const holder = survey.live[0] ?? survey.dead[0];
146
+ const info = holder ? toRecord(holder) : null;
147
+ const index = Number(name.slice(5));
148
+ slots.push({
149
+ path,
150
+ name,
151
+ index: Number.isFinite(index) ? index : null,
152
+ pid: info?.pid ?? null,
153
+ projectRoot: info?.projectRoot ?? null,
154
+ startedAt: info?.startedAt ?? null,
155
+ logFile: info?.logFile ?? null,
156
+ alive: survey.live.length > 0,
157
+ unresolved: survey.unresolved.length > 0
158
+ });
159
+ }
160
+ return slots;
161
+ }
162
+ //#endregion
13
163
  //#region src/engine/bundler.ts
14
164
  const COCOAPODS_SPEC = /^ {4}cocoapods \(/m;
15
165
  function bundlerPin(root) {
@@ -87,66 +237,69 @@ function makeDiagnostic({ file = null, line = null, column = null, message }, ro
87
237
  function dedupeKey(d) {
88
238
  return `${d.file || ""}|${d.line || ""}|${d.column || ""}|${d.message}`;
89
239
  }
90
- function extractXcodeDiagnostics(transcript, root = null, sdk = "iphonesimulator") {
91
- if (typeof transcript !== "string" || transcript === "") return [];
92
- const lines = transcript.split("\n");
240
+ function createXcodeDiagnosticCollector(root = null, sdk = "iphonesimulator") {
93
241
  const out = [];
94
242
  const seen = /* @__PURE__ */ new Set();
95
- const push = (d) => {
243
+ let finished = false;
244
+ let inUndefinedSymbols = false;
245
+ const add = (d) => {
96
246
  const key = dedupeKey(d);
97
247
  if (seen.has(key)) return;
98
248
  seen.add(key);
99
249
  out.push(d);
100
250
  };
101
- for (let i = 0; i < lines.length; i += 1) {
102
- const rawLine = lines[i];
103
- if (rawLine === void 0) continue;
104
- const raw = rawLine.replace(/\r$/, "");
105
- if (raw.includes(BUILD_FAILED)) break;
106
- if (UNDEFINED_HEADER.test(raw)) {
107
- for (let j = i + 1; j < lines.length; j += 1) {
108
- const symLine = lines[j];
109
- if (symLine === void 0) continue;
110
- const sym = UNDEFINED_SYMBOL.exec(symLine.replace(/\r$/, ""));
111
- if (sym) {
112
- const symbol = sym[1];
113
- if (symbol !== void 0) push(makeDiagnostic({ message: undefinedSymbolMessage(symbol) }, root, sdk));
114
- continue;
115
- }
116
- if (/^\s+\S/.test(symLine) && !/^\S/.test(symLine)) continue;
117
- i = j - 1;
118
- break;
251
+ const push = (line) => {
252
+ if (finished) return;
253
+ const raw = line.replace(/\r$/, "");
254
+ if (inUndefinedSymbols) {
255
+ const sym = UNDEFINED_SYMBOL.exec(raw);
256
+ if (sym) {
257
+ const symbol = sym[1];
258
+ if (symbol !== void 0) add(makeDiagnostic({ message: undefinedSymbolMessage(symbol) }, root, sdk));
259
+ return;
119
260
  }
120
- continue;
261
+ if (/^\s+\S/.test(line) && !/^\S/.test(line)) return;
262
+ inUndefinedSymbols = false;
263
+ }
264
+ if (raw.includes(BUILD_FAILED)) {
265
+ finished = true;
266
+ return;
267
+ }
268
+ if (UNDEFINED_HEADER.test(raw)) {
269
+ inUndefinedSymbols = true;
270
+ return;
121
271
  }
122
272
  const ld = LD_ERROR.exec(raw);
123
273
  if (ld) {
124
- push(makeDiagnostic({ message: `ld: ${ld[1]}` }, root, sdk));
125
- continue;
274
+ add(makeDiagnostic({ message: `ld: ${ld[1]}` }, root, sdk));
275
+ return;
126
276
  }
127
277
  const positioned = POSITIONED.exec(raw);
128
278
  if (positioned) {
129
279
  const posMsg = positioned[4];
130
- if (posMsg === void 0) continue;
131
- push(makeDiagnostic({
280
+ if (posMsg === void 0) return;
281
+ add(makeDiagnostic({
132
282
  file: positioned[1],
133
283
  line: Number(positioned[2]),
134
284
  column: positioned[3] === void 0 ? null : Number(positioned[3]),
135
285
  message: posMsg
136
286
  }, root, sdk));
137
- continue;
287
+ return;
138
288
  }
139
289
  const plain = UNPOSITIONED.exec(raw);
140
290
  if (plain) {
141
291
  const plainMsg = plain[2];
142
- if (plainMsg === void 0) continue;
143
- push(makeDiagnostic({
292
+ if (plainMsg === void 0) return;
293
+ add(makeDiagnostic({
144
294
  file: fileFromPrefix(plain[1] || ""),
145
295
  message: plainMsg
146
296
  }, root, sdk));
147
297
  }
148
- }
149
- return out;
298
+ };
299
+ return {
300
+ push,
301
+ diagnostics: out
302
+ };
150
303
  }
151
304
  function capDiagnostics(diagnostics, max = 10) {
152
305
  const list = Array.isArray(diagnostics) ? diagnostics : [];
@@ -553,7 +706,8 @@ function readBundleExecutable(appPath, { exec = null } = {}) {
553
706
  return null;
554
707
  }
555
708
  }
556
- function tailLines(lines, count = 5) {
709
+ const TAIL_LINES = 5;
710
+ function tailLines(lines, count = TAIL_LINES) {
557
711
  return (Array.isArray(lines) ? lines : []).filter((l) => typeof l === "string" && l.trim() !== "").slice(-count);
558
712
  }
559
713
  const HEARTBEAT_INTERVAL_MS = 3e4;
@@ -714,13 +868,18 @@ async function buildIos({ root, udid = null, logWriter, project = null, scheme =
714
868
  msg: `xcodebuild ${args.join(" ")}`,
715
869
  event: "build_start"
716
870
  });
717
- const transcript = [];
871
+ const diagnosticCollector = createXcodeDiagnosticCollector(root, sdk);
872
+ const tail = [];
873
+ let transcriptLines = 0;
718
874
  let compilationCacheActivity = COMPILATION_CACHE_UNAVAILABLE;
719
875
  let swiftTargetsWithoutExplicitModules = 0;
720
876
  const onLine = (line) => {
721
877
  const msg = cleanLine(line);
722
- transcript.push(msg);
878
+ transcriptLines += 1;
879
+ diagnosticCollector.push(msg);
723
880
  if (msg.trim() === "") return;
881
+ tail.push(msg);
882
+ if (tail.length > TAIL_LINES) tail.shift();
724
883
  logWriter.write({
725
884
  src: "build",
726
885
  level: "debug",
@@ -748,7 +907,7 @@ async function buildIos({ root, udid = null, logWriter, project = null, scheme =
748
907
  };
749
908
  let child;
750
909
  try {
751
- child = executor.spawn("xcodebuild", args, {
910
+ child = spawnDeclared(() => executor.spawn("xcodebuild", args, {
752
911
  cwd: resolvedTarget.dir || root,
753
912
  stdio: [
754
913
  "ignore",
@@ -761,7 +920,7 @@ async function buildIos({ root, udid = null, logWriter, project = null, scheme =
761
920
  NSUnbufferedIO: "YES",
762
921
  FORCE_COLOR: "0"
763
922
  }
764
- });
923
+ }));
765
924
  } catch (err) {
766
925
  const message = `Could not run xcodebuild: ${err.message}`;
767
926
  const remedy = "Install Xcode and select it with `sudo xcode-select -s /Applications/Xcode.app`.";
@@ -808,7 +967,6 @@ async function buildIos({ root, udid = null, logWriter, project = null, scheme =
808
967
  });
809
968
  stopHeartbeat();
810
969
  const durationMs = elapsed();
811
- const text = transcript.join("\n");
812
970
  if (outcome.error) {
813
971
  const message = `Could not run xcodebuild: ${outcome.error.message}`;
814
972
  const remedy = "Install Xcode and select it with `sudo xcode-select -s /Applications/Xcode.app`.";
@@ -820,13 +978,13 @@ async function buildIos({ root, udid = null, logWriter, project = null, scheme =
820
978
  remedy
821
979
  }],
822
980
  durationMs,
823
- transcriptLines: transcript.length,
824
- tail: tailLines(transcript),
981
+ transcriptLines,
982
+ tail,
825
983
  compilationCache: compilationCacheActivity
826
984
  });
827
985
  }
828
986
  if (outcome.code !== 0) {
829
- const diagnostics = extractXcodeDiagnostics(text, root, sdk);
987
+ const diagnostics = diagnosticCollector.diagnostics;
830
988
  const capped = capDiagnostics(diagnostics);
831
989
  for (const d of capped.diagnostics) reportError(describeDiagnostic(d), d.remedy);
832
990
  if (capped.diagnostics.length === 0) reportError(`xcodebuild exited ${outcome.code ?? `on ${outcome.signal}`} with no recognizable diagnostic; see the transcript above.`, null);
@@ -835,8 +993,8 @@ async function buildIos({ root, udid = null, logWriter, project = null, scheme =
835
993
  diagnostics,
836
994
  durationMs,
837
995
  exitCode: outcome.code,
838
- transcriptLines: transcript.length,
839
- tail: tailLines(transcript),
996
+ transcriptLines,
997
+ tail,
840
998
  compilationCache: compilationCacheActivity
841
999
  });
842
1000
  }
@@ -889,8 +1047,8 @@ async function buildIos({ root, udid = null, logWriter, project = null, scheme =
889
1047
  }],
890
1048
  durationMs,
891
1049
  exitCode: 0,
892
- transcriptLines: transcript.length,
893
- tail: tailLines(transcript),
1050
+ transcriptLines,
1051
+ tail,
894
1052
  compilationCache: compilationCacheActivity
895
1053
  });
896
1054
  }
@@ -907,8 +1065,8 @@ async function buildIos({ root, udid = null, logWriter, project = null, scheme =
907
1065
  }],
908
1066
  durationMs,
909
1067
  exitCode: 0,
910
- transcriptLines: transcript.length,
911
- tail: tailLines(transcript),
1068
+ transcriptLines,
1069
+ tail,
912
1070
  compilationCache: compilationCacheActivity
913
1071
  });
914
1072
  }
@@ -927,9 +1085,9 @@ async function buildIos({ root, udid = null, logWriter, project = null, scheme =
927
1085
  project: resolvedTarget,
928
1086
  derivedDataPath: dd,
929
1087
  productsDir: products,
930
- transcriptLines: transcript.length,
1088
+ transcriptLines,
931
1089
  compilationCache: compilationCacheActivity
932
1090
  };
933
1091
  }
934
1092
  //#endregion
935
- export { compilationCacheActivityLine as a, readBundleExecutable as c, startBuildHeartbeat as d, tailLines as f, podInstallCommand as h, ccacheEnabled as i, readBundleId as l, bundlerPin as m, HEARTBEAT_INTERVAL_MS as n, detectXcodeMajor as o, describeDiagnostic as p, buildIos as r, discoverXcodeProject as s, COMPILATION_CACHE_NOT_RUN as t, resolveScheme as u };
1093
+ export { listBuildSlots as _, compilationCacheActivityLine as a, readBundleExecutable as c, startBuildHeartbeat as d, tailLines as f, acquireBuildSlot as g, podInstallCommand as h, ccacheEnabled as i, readBundleId as l, bundlerPin as m, HEARTBEAT_INTERVAL_MS as n, detectXcodeMajor as o, describeDiagnostic as p, buildIos as r, discoverXcodeProject as s, COMPILATION_CACHE_NOT_RUN as t, resolveScheme as u, releaseBuildSlot as v };