stim 1.9.0 → 1.10.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 (83) hide show
  1. package/dist/{android-_y4Ohi5z.mjs → android-787HhP5w.mjs} +119 -29
  2. package/dist/{android-BPGizezh.mjs → android-DBkeHwpm.mjs} +168 -95
  3. package/dist/{android-DEgo4oCo.mjs → android-DPov-fv2.mjs} +2 -2
  4. package/dist/{android-cas-eiExNLf7.mjs → android-cas-CITmIeWY.mjs} +9 -8
  5. package/dist/android-cas-compiler.mjs +1 -1
  6. package/dist/{app-install-Uf3a83Tv.mjs → app-install-CrJ6t_bv.mjs} +2 -2
  7. package/dist/budget-BP4jZZL4.mjs +620 -0
  8. package/dist/build-progress-BXx-ZBe4.mjs +512 -0
  9. package/dist/{cache-manifest-Dg7OnMrp.mjs → cache-manifest-Pubfp8wV.mjs} +1 -1
  10. package/dist/cache-manifest.mjs +1 -1
  11. package/dist/cli.mjs +16 -16
  12. package/dist/collector-run.d.mts +5 -1
  13. package/dist/collector-run.mjs +7 -7
  14. package/dist/{command-output-gByVmIjy.mjs → command-output-D5z2_bcF.mjs} +1 -1
  15. package/dist/{config-Cp9lM3wX.mjs → config-BEq0Qw_a.mjs} +14 -131
  16. package/dist/{dependency-state-Bq5DcLGn.mjs → dependency-state-r9UesA9a.mjs} +2 -2
  17. package/dist/{deps-Cip5vUm1.mjs → deps-DP4eWqea.mjs} +3 -3
  18. package/dist/{detached-entry-KEu0_AXL.mjs → detached-entry-DJHoca49.mjs} +1 -1
  19. package/dist/{dev-client-HyqISM0h.mjs → dev-client-92U593KT.mjs} +137 -250
  20. package/dist/{device-D4U-xLty.mjs → device-DhTZe7L4.mjs} +7 -7
  21. package/dist/{build-lock-DPXPLgTc.mjs → device-lease-CbEaIk7b.mjs} +370 -12
  22. package/dist/{device-pool-CnTAz8X1.mjs → device-pool-Di8Q-9Ro.mjs} +4 -4
  23. package/dist/{device-remote-AQ5rQPqr.mjs → device-remote-Dy1jGj3U.mjs} +547 -468
  24. package/dist/{doctor-B_Ei3bZu.mjs → doctor-B-inAJmt.mjs} +58 -22
  25. package/dist/{error-diagnostics-Vfz83GCF.mjs → error-diagnostics-xxWKNO3-.mjs} +11 -12
  26. package/dist/{exec-jIAlgilk.mjs → exec-DK8KaVHK.mjs} +3 -2
  27. package/dist/{gc-Ckw8RQHi.mjs → gc-ChPFZMRC.mjs} +500 -1114
  28. package/dist/{guide-BiAbhs1J.mjs → guide-DwmaD_Wa.mjs} +882 -528
  29. package/dist/guide-status-CWvti2Lg.mjs +125 -0
  30. package/dist/idle-DpRLp5lj.mjs +157 -0
  31. package/dist/in-use-CjUluqIX.mjs +245 -0
  32. package/dist/{ios-BL2NFckr.mjs → ios-B2Fpqvlb.mjs} +25 -59
  33. package/dist/{ios-BzUJWk1K.mjs → ios-Dd-hYJBh.mjs} +130 -89
  34. package/dist/{ios-device-DPsSm0v_.mjs → ios-device-BiOsSVzd.mjs} +1 -1
  35. package/dist/{ios-device-D972i9wf.mjs → ios-device-CtRdEVjx.mjs} +3 -3
  36. package/dist/{launch-verify-Q_OtW1Rf.mjs → launch-verify-CQS_Kg-C.mjs} +5 -5
  37. package/dist/{logs-CI9NQSK7.mjs → logs-MARIy6m9.mjs} +9 -9
  38. package/dist/{metro-DrLHaMn2.mjs → metro-BWLOJ_Wr.mjs} +116 -10
  39. package/dist/{named-ports-CmLQSIRj.mjs → named-ports-DiTfSz6p.mjs} +4 -4
  40. package/dist/{ndjson-9uFNRwfe.mjs → ndjson-CDY9IkVC.mjs} +3 -56
  41. package/dist/{ndjson-DvHSsNU9.d.mts → ndjson-Du3j0AQ9.d.mts} +1 -0
  42. package/dist/{ownership-D15Op8vN.mjs → ownership-D2xbLVoz.mjs} +2 -3
  43. package/dist/ownership-DZjdx8Gq.mjs +1180 -0
  44. package/dist/{ports-BNKoWVWZ.mjs → ports-CLSSFOuz.mjs} +3 -3
  45. package/dist/{project-DS112DYB.mjs → project-BXZzOABt.mjs} +2 -2
  46. package/dist/{reload-ZNJUo1pl.mjs → reload-DoETJ3rl.mjs} +9 -10
  47. package/dist/{server-bare-DWkkcIxV.mjs → server-bare-DVbb7kXH.mjs} +1 -1
  48. package/dist/{server-expo-B_lctBR3.mjs → server-expo-BvOCeakp.mjs} +4 -4
  49. package/dist/server-expo-DBVTdult.mjs +2 -0
  50. package/dist/{settings-D030k0t4.mjs → settings-DOugPlPz.mjs} +16 -428
  51. package/dist/{settings-BKNh5rl4.mjs → settings-EWYNwe72.mjs} +16 -14
  52. package/dist/settings.schema.json +75 -0
  53. package/dist/{simslim-B_LsGLuJ.mjs → simslim-Ca3487H6.mjs} +182 -5
  54. package/dist/{slot-launch-DU9A-gYw.mjs → slot-launch-C5cg0fmk.mjs} +4 -4
  55. package/dist/{spawn-claims-Dn3C_N6w.mjs → spawn-claims-CUleC2cM.mjs} +1 -1
  56. package/dist/{start-DYkkKF0s.mjs → start-F9gv5dcC.mjs} +147 -98
  57. package/dist/start-VoFsPSes.mjs +2 -0
  58. package/dist/{state-NcJzMEBF.mjs → state-WGdIbi_d.mjs} +2 -2
  59. package/dist/{stats-DG9e66Rz.mjs → stats-BAgJaLA4.mjs} +1 -1
  60. package/dist/{stats-B63-JVAY.mjs → stats-DBYJ5bxL.mjs} +4 -4
  61. package/dist/{status-S6ehL4Ui.mjs → status-DLCD-o3s.mjs} +63 -231
  62. package/dist/status-Dr4fRkym.mjs +216 -0
  63. package/dist/{guide-status-BAMVU0ZA.mjs → stim-installations-CINXy_su.mjs} +3 -124
  64. package/dist/{stop-BO28HGqF.mjs → stop-Dj4pfmyv.mjs} +22 -18
  65. package/dist/{stop-T1R-cZRz.mjs → stop-ZvOeTmKb.mjs} +2 -2
  66. package/dist/supervisor-run.d.mts +11 -2
  67. package/dist/supervisor-run.mjs +185 -17
  68. package/dist/{workspace-process-lock-DWaiRD0e.mjs → workspace-process-lock-BXA96myD.mjs} +2 -2
  69. package/dist/workspaces-BTUyFfkv.mjs +1003 -0
  70. package/dist/{worktree-DKhSF0UX.mjs → worktree-4aZg2NnI.mjs} +1 -1
  71. package/dist/{worktree-LGB-UwgG.mjs → worktree-BMS7GeqB.mjs} +40 -122
  72. package/dist/{worktree-R_cUlE7s.mjs → worktree-BlizKDpZ.mjs} +143 -18
  73. package/dist/{xcode-BZqyxh3e.mjs → xcode-B2tBkf0m.mjs} +9 -158
  74. package/package.json +6 -5
  75. package/dist/build-progress-CVqkfPC6.mjs +0 -188
  76. package/dist/device-lease-Crs-HkJY.mjs +0 -366
  77. package/dist/logs-query-Cqma3WIu.mjs +0 -298
  78. package/dist/ownership-DCQPuWEc.mjs +0 -489
  79. package/dist/public-http-probe-CyMDrQ3c.mjs +0 -61
  80. package/dist/server-expo-CeILEu86.mjs +0 -2
  81. package/dist/sim-pool-KaIsp2Ft.mjs +0 -723
  82. package/dist/spawn-entry-nCE6m770.mjs +0 -14
  83. package/dist/workspace-state-DF2gCFhx.mjs +0 -169
@@ -1,2 +1,2 @@
1
- import { a as reclaimKeys, c as removalBlockers, d as removeWorktreeTarget, f as worktreeCommand, i as porcelainPath, l as removalPath, n as excludePodChurn, o as registerRemove, r as matchWorktreeEntry, s as registerWarm, t as dependencyInstallCommand, u as removalRemedy } from "./worktree-LGB-UwgG.mjs";
1
+ import { a as reclaimKeys, c as removalBlockers, d as removeWorktreeTarget, f as worktreeCommand, i as porcelainPath, l as removalPath, n as excludePodChurn, o as registerRemove, r as matchWorktreeEntry, s as registerWarm, t as dependencyInstallCommand, u as removalRemedy } from "./worktree-BMS7GeqB.mjs";
2
2
  export { worktreeCommand as default, dependencyInstallCommand, excludePodChurn, matchWorktreeEntry, porcelainPath, reclaimKeys, registerRemove, registerWarm, removalBlockers, removalPath, removalRemedy, removeWorktreeTarget };
@@ -1,115 +1,27 @@
1
- import { D as process_identity_exports, K as projectDeviceSlots, O as acquireAvdClaim, b as removeProject, f as getProject, g as loadConfig, h as isPathPrefix, i as clearDevice, k as ownership_claim_exports, u as getConfigDir, w as upsertProject } from "./config-Cp9lM3wX.mjs";
2
- import { t as getExecutor } from "./exec-jIAlgilk.mjs";
3
- import { _ as workspaceDir, i as recordWorkspaceUse, r as readWorkspaceState } from "./workspace-state-DF2gCFhx.mjs";
4
- import { C as warmWorktreePaths, S as unpushedCommits, b as restoreFile, c as hasPopulatedSubmodules, d as isPodInstallChurn, f as listWorktrees, g as removeWorktree, h as readWorktreeExclude, i as depsOutOfSync, l as hasRemote, m as podsOutOfSync, n as cloneIgnoredEntries, o as dirtyPaths, p as locallyKnownUpstream, r as deleteBranch, s as gitCommonDir, t as branchExists, u as hasUncommittedWork, v as resolveFullRef, x as sourceCheckoutOf } from "./worktree-R_cUlE7s.mjs";
5
- import { o as findProjectRoot } from "./project-DS112DYB.mjs";
6
- import { a as clearRemoteSession, i as clearManagedMetroTunnel, l as readRemoteSession, s as readMetroTunnel, u as readRemoteSessionId } from "./state-NcJzMEBF.mjs";
7
- import { a as pidExists, c as resolveProjectMetro, l as signalProcessTree, n as killMetroTree } from "./metro-DrLHaMn2.mjs";
8
- import { n as verifyCollectorOwnership } from "./ownership-D15Op8vN.mjs";
9
- import { F as unknownSettingKeys, M as settingShapeErrors, i as SETTING_SHAPE_REMEDY, k as resolveSettings } from "./settings-D030k0t4.mjs";
10
- import { a as formatElapsed, h as shortUdid, i as formatDuration, l as phaseLine, p as releasedLeaseFact, u as plural } from "./command-output-gByVmIjy.mjs";
11
- import { n as listBuildLocks } from "./build-lock-DPXPLgTc.mjs";
12
- import { b as resolveEasCliBin, o as parkedMaxSetting, t as POOL_SETTING_REMEDY } from "./sim-pool-KaIsp2Ft.mjs";
13
- import { n as workspaceProcessLockError, r as workspaceProcessLockPath, t as withWorkspaceProcessLock } from "./workspace-process-lock-DWaiRD0e.mjs";
14
- import { l as releaseWorkspaceLeases } from "./device-lease-Crs-HkJY.mjs";
15
- import { _ as listBuildSlots, h as podInstallCommand, n as HEARTBEAT_INTERVAL_MS } from "./xcode-BZqyxh3e.mjs";
16
- import { E as teardownOwnedIosSim, T as teardownOwnedAvd, i as endRecordedSession } from "./device-remote-AQ5rQPqr.mjs";
17
- import { n as hasInstalledDependencies, r as installedNpmTreeIsValid, t as dependencyState } from "./dependency-state-Bq5DcLGn.mjs";
18
- import { t as clearNamedPorts } from "./named-ports-CmLQSIRj.mjs";
19
- import { i as stopTunnel, l as withManagedTunnelRemovalLock, n as managedLockHolders, s as withManagedRemoteWorktreeRemovalLock, t as resolveSupervisorTarget } from "./ownership-DCQPuWEc.mjs";
20
- import { a as runPodInstall, i as runCaptured, n as podsAreStale, r as readPodState, t as DEPS_ERROR } from "./deps-Cip5vUm1.mjs";
21
- import { existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, rmdirSync, unlinkSync, writeFileSync } from "node:fs";
1
+ import { D as process_identity_exports, H as projectDeviceSlots, O as acquireAvdClaim, b as removeProject, f as getProject, g as loadConfig, h as isPathPrefix, i as clearDevice, k as ownership_claim_exports, u as getConfigDir, w as upsertProject } from "./config-BEq0Qw_a.mjs";
2
+ import { t as getExecutor } from "./exec-DK8KaVHK.mjs";
3
+ import { O as workspaceDir, c as signalProcessTree, h as recordWorkspaceUse, i as pidExists, m as readWorkspaceState, s as resolveProjectMetro, t as killMetroTree } from "./metro-BWLOJ_Wr.mjs";
4
+ import { C as sourceCheckoutOf, S as restoreFile, T as warmWorktreePaths, _ as readWorktreeExclude, b as resolveFullRef, d as hasUncommittedWork, f as isPodInstallChurn, g as podsOutOfSync, h as locallyKnownUpstream, i as depsOutOfSync, l as hasPopulatedSubmodules, m as listWorktrees, n as cloneIgnoredEntries, o as dirtyPaths, r as deleteBranch, s as gitCommonDir, t as branchExists, u as hasRemote, v as removeWorktree, w as unpushedCommits } from "./worktree-BlizKDpZ.mjs";
5
+ import { o as findProjectRoot } from "./project-BXZzOABt.mjs";
6
+ import { a as clearRemoteSession, i as clearManagedMetroTunnel, l as readRemoteSession, s as readMetroTunnel, u as readRemoteSessionId } from "./state-WGdIbi_d.mjs";
7
+ import { a as formatElapsed, h as shortUdid, i as formatDuration, l as phaseLine, p as releasedLeaseFact, u as plural } from "./command-output-D5z2_bcF.mjs";
8
+ import { u as releaseWorkspaceLeases } from "./device-lease-CbEaIk7b.mjs";
9
+ import { L as unknownSettingKeys, P as settingShapeErrors, a as SETTING_SHAPE_REMEDY, j as resolveSettings } from "./settings-DOugPlPz.mjs";
10
+ import { d as teardownOwnedAvd, f as teardownOwnedIosSim, h as POOL_SETTING_REMEDY, i as stopTunnel, l as withManagedTunnelRemovalLock, s as withManagedRemoteWorktreeRemovalLock, t as resolveSupervisorTarget, y as parkedMaxSetting } from "./ownership-DZjdx8Gq.mjs";
11
+ import { n as withIdleWorkspace, t as emptyWorkspaceDir } from "./in-use-CjUluqIX.mjs";
12
+ import { n as verifyCollectorOwnership } from "./ownership-D2xbLVoz.mjs";
13
+ import { i as endRecordedSession, z as resolveEasCliBin } from "./device-remote-Dy1jGj3U.mjs";
14
+ import { h as podInstallCommand, n as HEARTBEAT_INTERVAL_MS } from "./xcode-B2tBkf0m.mjs";
15
+ import { n as hasInstalledDependencies, r as installedNpmTreeIsValid, t as dependencyState } from "./dependency-state-r9UesA9a.mjs";
16
+ import { t as clearNamedPorts } from "./named-ports-DiTfSz6p.mjs";
17
+ import { a as runPodInstall, i as runCaptured, n as podsAreStale, r as readPodState, t as DEPS_ERROR } from "./deps-DP4eWqea.mjs";
18
+ import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
22
19
  import { existsSync as existsSync$1, realpathSync as realpathSync$1 } from "fs";
23
20
  import { basename, dirname, isAbsolute, relative, resolve } from "path";
24
21
  import { workspaceName } from "@stim-cli/core";
25
22
  import { join as join$1, relative as relative$1 } from "node:path";
26
23
  import { createHash, randomUUID } from "node:crypto";
27
24
  import chalk from "chalk";
28
- //#region src/commands/gc/paths.ts
29
- function canonicalPath(p) {
30
- try {
31
- return realpathSync$1(p);
32
- } catch {
33
- return resolve(p);
34
- }
35
- }
36
- //#endregion
37
- //#region src/workspace/in-use.ts
38
- const NATIVE_RUN = "native-run";
39
- const NATIVE_RUN_HELD = "a stim ios, android or stop run holds its native-run.lock";
40
- function workspaceInUse(root, { supervisor = true, managedLocks = true, nativeRun = true } = {}) {
41
- const reasons = [];
42
- if (supervisor) {
43
- const project = getProject(root);
44
- const target = resolveSupervisorTarget({
45
- state: readWorkspaceState(root)?.supervisor,
46
- record: project?.supervisor,
47
- reservedPort: project?.metroPort
48
- });
49
- if (target.status === "ours") reasons.push(`its dev server supervisor (pid ${target.pid}) is running`);
50
- else if (target.status === "unverified") reasons.push(`its dev server supervisor cannot be verified: ${target.reason ?? "unknown identity"}`);
51
- }
52
- if (nativeRun) {
53
- const claims = (0, ownership_claim_exports.readClaimSet)(workspaceProcessLockPath(workspaceDir(root), NATIVE_RUN, true));
54
- if (claims.live.length) reasons.push(NATIVE_RUN_HELD);
55
- else if (claims.unresolved[0]) reasons.push(`its native-run.lock cannot be resolved: ${claims.unresolved[0].reason}`);
56
- }
57
- const self = canonicalPath(root);
58
- for (const [kind, entries] of [["build lock", listBuildLocks()], ["build slot", listBuildSlots()]]) for (const entry of entries) {
59
- if (!entry.alive && !entry.unresolved) continue;
60
- if (entry.projectRoot !== null && canonicalPath(entry.projectRoot) === self) reasons.push(`a ${entry.alive ? "live" : "unresolvable"} ${kind} at ${entry.path} names it`);
61
- }
62
- if (managedLocks) reasons.push(...managedLockHolders(root));
63
- return reasons;
64
- }
65
- function emptyWorkspaceDir(dir) {
66
- if (lstatSync(dir).isSymbolicLink()) {
67
- unlinkSync(dir);
68
- return;
69
- }
70
- for (const name of readdirSync(dir)) if (name !== `${NATIVE_RUN}.lock`) rmSync(join$1(dir, name), {
71
- recursive: true,
72
- force: true
73
- });
74
- }
75
- async function withIdleWorkspace(root, fn, { purpose, ...checks }) {
76
- try {
77
- return await withWorkspaceProcessLock(workspaceDir(root), NATIVE_RUN, async () => {
78
- const reasons = workspaceInUse(root, {
79
- ...checks,
80
- nativeRun: false
81
- });
82
- if (reasons.length) return {
83
- ran: false,
84
- reasons
85
- };
86
- return {
87
- ran: true,
88
- value: await fn()
89
- };
90
- }, {
91
- external: true,
92
- waitMs: 0,
93
- ownerPurpose: purpose
94
- });
95
- } catch (error) {
96
- if (workspaceProcessLockError(error)) return {
97
- ran: false,
98
- reasons: [NATIVE_RUN_HELD]
99
- };
100
- if ((0, ownership_claim_exports.isClaimRefusal)(error) || (0, ownership_claim_exports.isClaimUnavailable)(error)) return {
101
- ran: false,
102
- reasons: [`its native-run.lock cannot be taken: ${error.reason}`]
103
- };
104
- throw error;
105
- } finally {
106
- try {
107
- const dir = workspaceDir(root);
108
- if (!lstatSync(dir).isSymbolicLink()) rmdirSync(dir);
109
- } catch {}
110
- }
111
- }
112
- //#endregion
113
25
  //#region src/devices/reclaim.ts
114
26
  async function reapCollectors(root, { verify = verifyCollectorOwnership, collectors }) {
115
27
  const skippedDevices = [];
@@ -251,20 +163,12 @@ function describeDereferenced(project) {
251
163
  }
252
164
  return devices;
253
165
  }
254
- function parkedIosCacheKey(lastBuild) {
255
- if (lastBuild === null || typeof lastBuild !== "object" || Array.isArray(lastBuild)) return null;
256
- const build = lastBuild;
257
- return build.platform === "ios" && typeof build.cacheKey === "string" ? build.cacheKey : null;
258
- }
259
- function parkRequest(project, projectPath, slot, simslimManaged) {
166
+ function parkRequest(projectPath, slot, simslimManaged) {
260
167
  const { max, error } = parkedMaxSetting("ios");
261
168
  if (error || max <= 0) return void 0;
262
- const cacheKey = parkedIosCacheKey(readWorkspaceState(projectPath)?.lastBuild);
263
169
  return {
264
170
  projectPath,
265
171
  max,
266
- bundleId: typeof project?.bundleId === "string" ? project.bundleId : null,
267
- cacheKey,
268
172
  simslimManaged,
269
173
  slot
270
174
  };
@@ -284,7 +188,7 @@ function reclaimOwnedDevices(project, projectPath, { park = false } = {}) {
284
188
  const r = teardownOwnedIosSim(udid, {
285
189
  del: true,
286
190
  label,
287
- ...park ? { park: parkRequest(project, projectPath, slot, Boolean(ios.simslimManaged)) } : {}
191
+ ...park ? { park: parkRequest(projectPath, slot, Boolean(ios.simslimManaged)) } : {}
288
192
  });
289
193
  if (r.parkFallback) poolNotes.push(`could not park ${label}: ${r.parkFallback} -- deleted it instead`);
290
194
  for (const failure of r.evictionFailures ?? []) poolNotes.push(failure);
@@ -1235,7 +1139,13 @@ function dependencyInstallCommand(target, dir = ".") {
1235
1139
  return `cd '${installRoot.replaceAll("'", "'\\''")}' && ${command}`;
1236
1140
  }
1237
1141
  function reportCarriedStateHealth(root, target, copied) {
1238
- const staleDeps = depsOutOfSync(root, target, copied);
1142
+ const outOfSync = depsOutOfSync(root, target, copied);
1143
+ for (const d of outOfSync.filter((dependency) => dependency.reason === "installed")) {
1144
+ const where = d.dir === "." ? d.lockfile : `${d.dir}/${d.lockfile}`;
1145
+ const modules = d.dir === "." ? "node_modules" : `${d.dir}/node_modules`;
1146
+ console.error(chalk.yellow(phaseLine("carry", `carried ${modules} was installed from a different ${d.lockfile} than the ${where} on disk here. node_modules is gitignored and cloned from the source checkout; ${d.lockfile} is tracked, so the two can disagree. Run \`${dependencyInstallCommand(target, d.dir)}\` before building, or pod install and native builds see the wrong package versions.`)));
1147
+ }
1148
+ const staleDeps = outOfSync.filter((dependency) => dependency.reason === "lockfile");
1239
1149
  if (staleDeps.length) {
1240
1150
  const manifests = staleDeps.map((d) => d.dir === "." ? d.lockfile : `${d.dir}/${d.lockfile}`).join(", ");
1241
1151
  const remedies = [...new Set(staleDeps.map((dependency) => dependencyInstallCommand(target, dependency.dir)))];
@@ -1642,14 +1552,14 @@ function canonicalExistingPath(target) {
1642
1552
  }
1643
1553
  return resolve(realpathSync$1(existing), ...missing);
1644
1554
  }
1645
- function inspectRemoval(path) {
1555
+ function inspectRemoval(path, mergedHead) {
1646
1556
  const gitAnswered = hasUncommittedWork(path);
1647
1557
  const { lines: dirtyLines, restore: podChurn } = excludePodChurn(gitAnswered ? dirtyPaths(path, { limit: Infinity }) : []);
1648
1558
  const dirty = gitAnswered === null ? null : dirtyLines.length > 0;
1649
1559
  const unpushed = unpushedCommits(path);
1650
1560
  const blockers = removalBlockers({
1651
1561
  dirty,
1652
- unpushed
1562
+ unpushed: Boolean(mergedHead) && resolveFullRef(path, "HEAD") === mergedHead && unpushed ? [] : unpushed
1653
1563
  });
1654
1564
  if (hasPopulatedSubmodules(path)) blockers.push("initialized submodules, which git removes only with --force");
1655
1565
  return {
@@ -1797,7 +1707,7 @@ async function runRemove(target, opts, onRemoved) {
1797
1707
  const branch = entry.branch;
1798
1708
  const ownsBranch = Boolean(branch && project?.worktreeBranchOwned === true && project.worktreeBranch === branch);
1799
1709
  const approvedBranchSha = ownsBranch ? resolveFullRef(path, "HEAD") : null;
1800
- const inspection = inspectRemoval(path);
1710
+ const inspection = inspectRemoval(path, opts.mergedHead);
1801
1711
  if (inspection.blockers.length && !opts.force) {
1802
1712
  printRemovalRefusal(path, inspection);
1803
1713
  return;
@@ -1807,7 +1717,8 @@ async function runRemove(target, opts, onRemoved) {
1807
1717
  const branchDeleteCwd = source.path;
1808
1718
  await withManagedRemoteWorktreeRemovalLock(path, () => withReclaimLocks(path, async (lockedKeys) => {
1809
1719
  if (opts.guard?.(lockedKeys).length) return;
1810
- const current = inspectRemoval(path);
1720
+ const inspectedHead = resolveFullRef(path, "HEAD");
1721
+ const current = inspectRemoval(path, opts.mergedHead);
1811
1722
  if (current.blockers.length && !opts.force) {
1812
1723
  printRemovalRefusal(path, current);
1813
1724
  return;
@@ -1817,6 +1728,13 @@ async function runRemove(target, opts, onRemoved) {
1817
1728
  reportRetainedResources(path, result);
1818
1729
  return;
1819
1730
  }
1731
+ if (!opts.force && resolveFullRef(path, "HEAD") !== inspectedHead) {
1732
+ console.error(chalk.red(`Refusing to remove ${path}: its HEAD moved while its environment was reclaimed.`));
1733
+ console.error(chalk.dim(`The directory and Stim ownership record for ${path} were kept.`));
1734
+ printRemovalCleanup(result, true);
1735
+ process.exitCode = 1;
1736
+ return;
1737
+ }
1820
1738
  restorePodChurn(path, current.podChurn);
1821
1739
  try {
1822
1740
  removeWorktree(path, {
@@ -1882,4 +1800,4 @@ function worktreeCommand(program) {
1882
1800
  registerRemove(worktree);
1883
1801
  }
1884
1802
  //#endregion
1885
- export { workspaceInUse as _, reclaimKeys as a, removalBlockers as c, removeWorktreeTarget as d, worktreeCommand as f, withIdleWorkspace as g, emptyWorkspaceDir as h, porcelainPath as i, removalPath as l, reclaimProject as m, excludePodChurn as n, registerRemove as o, describeDereferenced as p, matchWorktreeEntry as r, registerWarm as s, dependencyInstallCommand as t, removalRemedy as u, canonicalPath as v };
1803
+ export { reclaimKeys as a, removalBlockers as c, removeWorktreeTarget as d, worktreeCommand as f, porcelainPath as i, removalPath as l, reclaimProject as m, excludePodChurn as n, registerRemove as o, describeDereferenced as p, matchWorktreeEntry as r, registerWarm as s, dependencyInstallCommand as t, removalRemedy as u };
@@ -1,7 +1,7 @@
1
- import { g as loadConfig, u as getConfigDir } from "./config-Cp9lM3wX.mjs";
2
- import { t as getExecutor } from "./exec-jIAlgilk.mjs";
1
+ import { g as loadConfig, u as getConfigDir } from "./config-BEq0Qw_a.mjs";
2
+ import { t as getExecutor } from "./exec-DK8KaVHK.mjs";
3
3
  import { accessSync, chmodSync, constants, lstatSync, mkdirSync, mkdtempSync, readdirSync, realpathSync, rmSync, statSync } from "node:fs";
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";
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, statSync as statSync$1, utimesSync } from "fs";
5
5
  import { dirname, isAbsolute, join, relative, resolve, sep } from "path";
6
6
  import { buildCacheRoot } from "@stim-cli/core";
7
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";
@@ -364,29 +364,99 @@ function podsOutOfSync(target, copiedEntries, { read = readFileSync$1 } = {}) {
364
364
  return problems;
365
365
  }
366
366
  const LOCKFILE_NAMES = [
367
- "package-lock.json",
368
- "yarn.lock",
369
367
  "pnpm-lock.yaml",
368
+ "yarn.lock",
370
369
  "bun.lock",
371
- "bun.lockb"
370
+ "bun.lockb",
371
+ "package-lock.json"
372
372
  ];
373
+ function npmInstallMatches(wanted, installed) {
374
+ if (!wanted.packages || !installed.packages) return null;
375
+ for (const [key, entry] of Object.entries(wanted.packages)) {
376
+ if (!key.includes("node_modules/") || entry.link) continue;
377
+ const present = installed.packages[key];
378
+ if (!present && (entry.optional || entry.devOptional)) continue;
379
+ if (present?.version !== entry.version) return false;
380
+ }
381
+ return true;
382
+ }
383
+ function lastYamlDocument(text) {
384
+ return (text.split(/^---$/m).at(-1) ?? "").trim();
385
+ }
386
+ function yarnClassicInstallMatches(lockfile, integrity) {
387
+ if (!integrity.lockfileEntries) return null;
388
+ const wanted = [...lockfile.matchAll(/^ {2}resolved "([^"]+)"$/gm)].flatMap((m) => m[1] ?? []);
389
+ if (wanted.length === 0) return null;
390
+ const installed = new Set(Object.values(integrity.lockfileEntries));
391
+ return wanted.every((url) => installed.has(url));
392
+ }
393
+ function yarnBerryInstallMatches(lockfile, state) {
394
+ const installed = new Set([...state.matchAll(/^"?([^\s"#][^"]*?)"?:$/gm)].flatMap((m) => m[1]?.replace(/@virtual:[^#]+#/, "@") ?? []));
395
+ let entries = 0;
396
+ for (const entry of lockfile.split(/\n(?=\S)/)) {
397
+ const resolution = /^ {2}resolution: "([^"]+)"$/m.exec(entry)?.[1];
398
+ if (!resolution) continue;
399
+ entries += 1;
400
+ if (installed.has(resolution) || /^ {2}conditions: /m.test(entry)) continue;
401
+ return false;
402
+ }
403
+ return entries === 0 ? null : true;
404
+ }
405
+ function installedMatchesLockfile(dir, lockfile, { read = readFileSync$1 } = {}) {
406
+ const text = (rel) => {
407
+ try {
408
+ return read(join(dir, rel), "utf-8").replaceAll("\r\n", "\n");
409
+ } catch {
410
+ return null;
411
+ }
412
+ };
413
+ const wanted = text(lockfile);
414
+ if (wanted === null) return null;
415
+ try {
416
+ if (lockfile === "package-lock.json") {
417
+ const installed = text("node_modules/.package-lock.json");
418
+ return installed === null ? null : npmInstallMatches(JSON.parse(wanted), JSON.parse(installed));
419
+ }
420
+ if (lockfile === "pnpm-lock.yaml") {
421
+ const installed = text("node_modules/.pnpm/lock.yaml");
422
+ return installed === null ? null : lastYamlDocument(wanted) === lastYamlDocument(installed);
423
+ }
424
+ if (lockfile === "yarn.lock") {
425
+ if (/^__metadata:$/m.test(wanted)) {
426
+ const berry = text("node_modules/.yarn-state.yml");
427
+ return berry === null ? null : yarnBerryInstallMatches(wanted, berry);
428
+ }
429
+ const classic = text("node_modules/.yarn-integrity");
430
+ return classic === null ? null : yarnClassicInstallMatches(wanted, JSON.parse(classic));
431
+ }
432
+ } catch {}
433
+ return null;
434
+ }
373
435
  function depsOutOfSync(root, target, copiedEntries, { read = readFileSync$1 } = {}) {
374
436
  const problems = [];
375
437
  for (const rel of copiedEntries || []) {
376
438
  if (rel !== "node_modules" && !rel.endsWith("/node_modules")) continue;
377
439
  const dir = rel === "node_modules" ? "" : rel.slice(0, -13);
378
- for (const name of LOCKFILE_NAMES) {
379
- const source = join(root, dir, name);
380
- const branch = join(target, dir, name);
381
- if (!existsSync$1(source) || !existsSync$1(branch)) continue;
382
- try {
383
- if (read(source, "utf-8") !== read(branch, "utf-8")) problems.push({
384
- dir: dir || ".",
385
- lockfile: name
386
- });
387
- } catch {}
388
- break;
440
+ const name = LOCKFILE_NAMES.find((candidate) => existsSync$1(join(target, dir, candidate)));
441
+ if (!name) continue;
442
+ const installed = installedMatchesLockfile(join(target, dir), name, { read });
443
+ if (installed === false) {
444
+ problems.push({
445
+ dir: dir || ".",
446
+ lockfile: name,
447
+ reason: "installed"
448
+ });
449
+ continue;
389
450
  }
451
+ const source = join(root, dir, name);
452
+ if (installed === true || !existsSync$1(source)) continue;
453
+ try {
454
+ if (read(source, "utf-8") !== read(join(target, dir, name), "utf-8")) problems.push({
455
+ dir: dir || ".",
456
+ lockfile: name,
457
+ reason: "lockfile"
458
+ });
459
+ } catch {}
390
460
  }
391
461
  return problems;
392
462
  }
@@ -581,6 +651,61 @@ function listWorktrees(cwd) {
581
651
  ]);
582
652
  return out ? parseWorktrees(out) : [];
583
653
  }
654
+ function branchOf(head) {
655
+ return head.startsWith("ref: refs/heads/") ? head.slice(16) : void 0;
656
+ }
657
+ /**
658
+ * The linked worktrees recorded under a git common dir, read from `worktrees/<id>/gitdir` and `HEAD` without running
659
+ * git or touching the worktree directories, sorted by path. The worktree with the branch the common dir's own `HEAD`
660
+ * names (the main checkout's branch, or a bare repository's source checkout) is left out.
661
+ */
662
+ function linkedWorktreesOnDisk(commonDir) {
663
+ const admin = join(commonDir, "worktrees");
664
+ let ids;
665
+ let sourceBranch;
666
+ try {
667
+ ids = readdirSync$1(admin);
668
+ sourceBranch = branchOf(readFileSync$1(join(commonDir, "HEAD"), "utf-8").trim());
669
+ } catch {
670
+ return [];
671
+ }
672
+ return ids.flatMap((id) => {
673
+ try {
674
+ const gitdir = readFileSync$1(join(admin, id, "gitdir"), "utf-8").trim();
675
+ const branch = branchOf(readFileSync$1(join(admin, id, "HEAD"), "utf-8").trim());
676
+ if (branch && branch === sourceBranch) return [];
677
+ const path = dirname(resolve(admin, id, nativePath(gitdir)));
678
+ return [branch ? {
679
+ path,
680
+ branch
681
+ } : { path }];
682
+ } catch {
683
+ return [];
684
+ }
685
+ }).toSorted((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
686
+ }
687
+ /**
688
+ * The git common dir of the repository containing `start`, read from `.git` and `commondir` files without running
689
+ * git. Returns null when `start` is missing, unreadable, or not inside a repository.
690
+ */
691
+ function gitCommonDirOnDisk(start) {
692
+ try {
693
+ for (let dir = realpathSync$1.native(start);; dir = dirname(dir)) {
694
+ const dotGit = join(dir, ".git");
695
+ if (existsSync$1(dotGit)) {
696
+ if (statSync$1(dotGit).isDirectory()) return realpathSync$1.native(dotGit);
697
+ const gitdir = /^gitdir: (.+)$/m.exec(readFileSync$1(dotGit, "utf-8"))?.[1]?.trim();
698
+ if (!gitdir) return null;
699
+ const linked = resolve(dir, gitdir);
700
+ const commondir = join(linked, "commondir");
701
+ return realpathSync$1.native(existsSync$1(commondir) ? resolve(linked, readFileSync$1(commondir, "utf-8").trim()) : linked);
702
+ }
703
+ if (dirname(dir) === dir) return null;
704
+ }
705
+ } catch {
706
+ return null;
707
+ }
708
+ }
584
709
  function selectSourceCheckout(entries, bareHead) {
585
710
  const first = entries[0];
586
711
  if (!first) return { refusal: "Git lists no worktrees for this repository." };
@@ -622,4 +747,4 @@ function resolveSourceCheckout(cwd) {
622
747
  };
623
748
  }
624
749
  //#endregion
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 };
750
+ export { sourceCheckoutOf as C, makeTemporaryDirectory as D, filesystemDevice as E, removeTemporaryEntry as O, restoreFile as S, warmWorktreePaths as T, readWorktreeExclude as _, dirtyFingerprintFiles as a, resolveFullRef as b, gitCommonDirOnDisk as c, hasUncommittedWork as d, isPodInstallChurn as f, podsOutOfSync as g, locallyKnownUpstream as h, depsOutOfSync as i, temporaryRoot as k, hasPopulatedSubmodules as l, listWorktrees as m, cloneIgnoredEntries as n, dirtyPaths as o, linkedWorktreesOnDisk as p, deleteBranch as r, gitCommonDir as s, branchExists as t, hasRemote as u, removeWorktree as v, unpushedCommits as w, resolveSourceCheckout as x, repoRoot as y };
@@ -1,165 +1,16 @@
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";
1
+ import { t as getExecutor } from "./exec-DK8KaVHK.mjs";
2
+ import { r as register } from "./cache-manifest-Pubfp8wV.mjs";
3
+ import { D as workspaceDerivedData, w as sharedCompilationCache } from "./metro-BWLOJ_Wr.mjs";
4
+ import { u as resolvePackageJson } from "./project-BXZzOABt.mjs";
5
+ import { n as spawnDeclared } from "./spawn-claims-CUleC2cM.mjs";
6
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";
7
+ import { a as formatElapsed, l as phaseLine } from "./command-output-D5z2_bcF.mjs";
8
+ import { W as resolveOptimizations } from "./settings-DOugPlPz.mjs";
9
+ import { t as cleanLine } from "./server-expo-BvOCeakp.mjs";
11
10
  import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
12
11
  import { basename, isAbsolute, join, relative, resolve, sep } from "node:path";
13
12
  import { createHash } from "node:crypto";
14
13
  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
163
14
  //#region src/engine/bundler.ts
164
15
  const COCOAPODS_SPEC = /^ {4}cocoapods \(/m;
165
16
  function bundlerPin(root) {
@@ -1090,4 +941,4 @@ async function buildIos({ root, udid = null, logWriter, project = null, scheme =
1090
941
  };
1091
942
  }
1092
943
  //#endregion
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 };
944
+ 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 };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stim",
3
- "version": "1.9.0",
3
+ "version": "1.10.0",
4
4
  "description": "Stim: fast, isolated React Native environments for coding agents",
5
5
  "keywords": [
6
6
  "agent",
@@ -34,6 +34,7 @@
34
34
  ],
35
35
  "type": "module",
36
36
  "exports": {
37
+ "./package.json": "./package.json",
37
38
  "./cache-manifest": {
38
39
  "types": "./dist/cache-manifest.d.mts",
39
40
  "module-sync": "./dist/cache-manifest.mjs",
@@ -42,9 +43,9 @@
42
43
  },
43
44
  "dependencies": {
44
45
  "@expo/fingerprint": "^0.20.10",
45
- "@stim-cli/cache": "^1.9.0",
46
- "@stim-cli/core": "^1.9.0",
47
- "@stim-cli/metro": "^1.9.0",
46
+ "@stim-cli/cache": "^1.10.0",
47
+ "@stim-cli/core": "^1.10.0",
48
+ "@stim-cli/metro": "^1.10.0",
48
49
  "chalk": "^6.0.0",
49
50
  "commander": "^15.0.0",
50
51
  "cross-spawn": "^7.0.6",
@@ -52,7 +53,7 @@
52
53
  "ws": "^8.21.3"
53
54
  },
54
55
  "devDependencies": {
55
- "@stim-cli/expo-build-cache": "1.9.0",
56
+ "@stim-cli/expo-build-cache": "1.10.0",
56
57
  "@types/cross-spawn": "^6.0.6",
57
58
  "@types/which": "^3.0.4",
58
59
  "@types/ws": "^8.18.1",