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,426 +0,0 @@
1
- import { t as getExecutor } from "./exec-B29XNuhP.mjs";
2
- import { M as volumeRootFor, R as projectDeviceSlots, g as loadConfig, u as getConfigDir } from "./config-B7iyKWkL.mjs";
3
- import { _ as workspaceLogsDir } from "./workspace-state-BT5E3SA2.mjs";
4
- import { v as resolveSourceCheckout } from "./worktree-FG_ZKKdb.mjs";
5
- import { a as findProjectRoot, c as projectShortcut } from "./project-DDZWf_16.mjs";
6
- import { a as resolveProjectMetro, s as isMetroRunning } from "./metro-C7bcJ0uo.mjs";
7
- import { I as resolveOwnedAvdSerial } from "./android-DWssqHQ8.mjs";
8
- import { a as formatElapsed, r as clockTime, u as plural } from "./command-output-B3ycpsYv.mjs";
9
- import { a as POOL_SETTING_REMEDY, l as parkedMaxSetting, u as readParked } from "./teardown-BJqEzPo_.mjs";
10
- import { u as listAllIosSims } from "./ios-BcCiI4TS.mjs";
11
- import { i as listLeaseFiles } from "./device-lease-Ffz_M5t0.mjs";
12
- import { c as queryLogs } from "./logs-query-BGL-Pl12.mjs";
13
- import { t as resolveSupervisorTarget } from "./ownership-wolE-0Do.mjs";
14
- import { i as readSupervisorState } from "./stop-C8JW6kKp.mjs";
15
- import { existsSync } from "fs";
16
- import { totalmem } from "os";
17
- import chalk from "chalk";
18
- //#region src/status.ts
19
- const IOS_SIM_MB = 1500;
20
- const ANDROID_EMULATOR_MB = 2500;
21
- const METRO_MB = 700;
22
- function poolLine({ platform, parked, max }) {
23
- if (parked <= 0) return null;
24
- const what = plural(parked, `parked ${platform === "ios" ? "iOS simulator" : "Android emulator"}`);
25
- return max > 0 ? `pool: ${what} (max ${max})` : `pool: ${what} (parking off; gc --delete removes them)`;
26
- }
27
- function environmentState(project, { simsByUdid = {}, metro = null, worktrees = [], simsAvailable = true, androidRuntime = null, androidRuntimes = {}, supervisor = null, logs = null } = {}) {
28
- const ios = project.platforms?.ios;
29
- const android = project.platforms?.android;
30
- const sim = ios ? simsByUdid[ios.deviceUdid] : null;
31
- const simBooted = Boolean(sim && sim.state === "Booted");
32
- const metroRunning = Boolean(metro?.metro);
33
- const androidDetected = androidRuntime ? Boolean(androidRuntime.serial) : Boolean(android?.serial);
34
- let live = simBooted || metroRunning || androidDetected;
35
- let memoryMb = 0;
36
- if (simBooted) memoryMb += IOS_SIM_MB;
37
- if (androidDetected) memoryMb += ANDROID_EMULATOR_MB;
38
- if (metroRunning) memoryMb += METRO_MB;
39
- const warnings = [];
40
- if (metro?.notOurs) warnings.push(`port ${project.metroPort}: ${metro.notOurs}`);
41
- if (ios && !sim && simsAvailable) warnings.push(`recorded sim ${ios.deviceUdid} no longer exists`);
42
- if (simBooted && project.metroPort && !metroRunning) warnings.push("simulator is booted with no Metro serving it");
43
- if (androidRuntime && android?.avdName) {
44
- const recordedSerial = android.consolePort ? `emulator-${android.consolePort}` : android.serial;
45
- if (androidRuntime.serial && recordedSerial && androidRuntime.serial !== recordedSerial) warnings.push(`owned AVD ${android.avdName} changed serial (${recordedSerial} -> ${androidRuntime.serial}); rerun your \`stim android\` command in this workspace to restore Metro forwarding, then reopen agent-device on ${androidRuntime.serial}`);
46
- if (androidRuntime.state === "missing") warnings.push(`recorded AVD ${android.avdName} no longer exists`);
47
- if (androidRuntime.state === "not-detected") warnings.push(`owned AVD ${android.avdName} is not detected by adb; rerun your \`stim android\` command in this workspace to reconnect`);
48
- if (androidRuntime.error) warnings.push(`could not check owned AVD ${android.avdName}: ${androidRuntime.error}`);
49
- }
50
- if (supervisor && supervisor.alive === false) warnings.push(`stale supervisor record for ${project.__path}`);
51
- const slots = [];
52
- for (const { slot, platforms } of projectDeviceSlots(project).slice(1)) {
53
- const deviceState = environmentState({
54
- ...project,
55
- platforms,
56
- deviceSlots: void 0
57
- }, {
58
- simsByUdid,
59
- simsAvailable,
60
- metro,
61
- androidRuntime: androidRuntimes[slot]
62
- });
63
- slots.push({
64
- slot,
65
- ios: deviceState.ios,
66
- android: deviceState.android
67
- });
68
- memoryMb += deviceState.memoryMb - (metroRunning ? METRO_MB : 0);
69
- live ||= deviceState.live;
70
- warnings.push(...deviceState.warnings.map((warning) => `${slot}: ${warning}`));
71
- }
72
- return {
73
- path: project.__path,
74
- ...slots.length ? { slots } : {},
75
- live,
76
- memoryMb,
77
- warnings,
78
- ios: ios ? {
79
- name: sim?.name ?? null,
80
- udid: ios.deviceUdid,
81
- owned: Boolean(ios.owned),
82
- state: sim?.state ?? (simsAvailable ? "missing" : "unknown")
83
- } : null,
84
- android: android ? {
85
- name: android.avdName ?? android.serial,
86
- owned: Boolean(android.owned),
87
- physical: Boolean(android.serial && !android.avdName),
88
- ...androidRuntime ? {
89
- serial: androidRuntime.serial,
90
- state: androidRuntime.state
91
- } : {}
92
- } : null,
93
- metro: project.metroPort ? {
94
- port: project.metroPort,
95
- running: metroRunning,
96
- pid: metro?.metro?.pid ?? null
97
- } : null,
98
- supervisor: supervisor ? {
99
- pid: supervisor.pid ?? null,
100
- mode: supervisor.mode ?? null,
101
- startedAt: supervisor.startedAt ?? null,
102
- healthy: Boolean(supervisor.healthy)
103
- } : null,
104
- logs: logs ? {
105
- dir: logs.dir,
106
- errorsSinceMarker: logs.errorsSinceMarker ?? 0
107
- } : null,
108
- worktree: worktrees.find((w) => w.path === project.__path) ?? null
109
- };
110
- }
111
- function capacity(states, totalMemoryMb) {
112
- const committedMb = states.reduce((n, s) => n + s.memoryMb, 0);
113
- return {
114
- liveCount: states.filter((s) => s.live).length,
115
- committedMb,
116
- totalMemoryMb,
117
- overCapacity: Boolean(totalMemoryMb && committedMb > totalMemoryMb * .6)
118
- };
119
- }
120
- function parseDfFree(output) {
121
- const lines = String(output || "").trim().split("\n");
122
- if (lines.length < 2) return null;
123
- const lastLine = lines[lines.length - 1];
124
- if (lastLine === void 0) return null;
125
- const m = /\s(\d+)\s+(\d+)\s+(\d+)\s+(\d+)%/.exec(lastLine);
126
- if (!m) return null;
127
- const totalKb = Number(m[1]);
128
- const availableKb = Number(m[3]);
129
- if (!Number.isFinite(totalKb) || !Number.isFinite(availableKb) || totalKb <= 0) return null;
130
- return {
131
- availableMb: Math.round(availableKb / 1024),
132
- totalMb: Math.round(totalKb / 1024)
133
- };
134
- }
135
- function diskIsTight(disk) {
136
- return Boolean(disk && disk.availableMb < 25600);
137
- }
138
- function formatSpace(mb) {
139
- if (!Number.isFinite(mb)) return "?";
140
- if (mb >= 1048576) return `${(mb / 1048576).toFixed(1)} TB`;
141
- if (mb >= 1024) return `${Math.round(mb / 1024)} GB`;
142
- return `${Math.round(mb)} MB`;
143
- }
144
- function diskLine(volumes) {
145
- const usable = (volumes || []).filter((v) => Boolean(v && v.disk));
146
- if (usable.length === 0) return null;
147
- if (usable.length === 1) {
148
- const first = usable[0];
149
- if (!first) return null;
150
- const { disk } = first;
151
- return `${formatSpace(disk.availableMb)} free of ${formatSpace(disk.totalMb)} on disk.`;
152
- }
153
- return `${usable.map((v) => `${formatSpace(v.disk.availableMb)} free on ${v.volume}`).join(", ")}.`;
154
- }
155
- function tightVolumes(volumes) {
156
- return (volumes || []).filter((v) => v && diskIsTight(v.disk));
157
- }
158
- function unprovisionedWorktrees(worktrees, projectPaths) {
159
- const known = new Set(projectPaths);
160
- return worktrees.filter((w) => !known.has(w.path));
161
- }
162
- function deviceLeaseStates(entries, { root, now }) {
163
- return entries.map((entry) => {
164
- const lease = entry.lease;
165
- return {
166
- path: entry.path,
167
- platform: entry.platform,
168
- ...lease?.slot ? { slot: lease.slot } : {},
169
- id: entry.id,
170
- deviceName: lease?.deviceName ?? null,
171
- holder: lease?.holder ?? null,
172
- grantedAt: lease?.grantedAt ?? null,
173
- expiresAt: lease?.expiresAt ?? null,
174
- mine: Boolean(lease && root && lease.holder === root),
175
- expired: Boolean(lease && Date.parse(lease.expiresAt) <= now),
176
- parsed: Boolean(lease)
177
- };
178
- });
179
- }
180
- function deviceLeaseLines(states, now) {
181
- if (states.length === 0) return [];
182
- const lines = [`Device leases (${states.length}):`];
183
- for (const state of states) {
184
- const device = `${state.platform}${state.slot ? ` [${state.slot}]` : ""} ${state.id ?? state.path}${state.deviceName ? ` (${state.deviceName})` : ""}`;
185
- if (!state.parsed || state.expiresAt === null) {
186
- lines.push(` ${device} -- unreadable lease file, so nothing may take the device: ${state.path}`);
187
- continue;
188
- }
189
- const when = clockTime(state.expiresAt);
190
- const remaining = Date.parse(state.expiresAt) - now;
191
- const expiry = state.expired ? `expired at ${when}` : `until ${when} (${formatElapsed(remaining)} left)`;
192
- lines.push(` ${device} -- ${state.holder} ${expiry}${state.mine ? " [this workspace]" : ""}`);
193
- }
194
- return lines;
195
- }
196
- //#endregion
197
- //#region src/commands/status.ts
198
- function formatGb(mb) {
199
- return `${(mb / 1024).toFixed(1)} GB`;
200
- }
201
- function statusCommand(program) {
202
- program.command("status").description("Show every environment on this machine: devices, ports, what is actually running, and anything stuck.").option("--json", "print the state as JSON").action(async (opts) => {
203
- const cfg = loadConfig();
204
- const projects = Object.entries(cfg?.projects || {});
205
- const cwdRoot = findProjectRoot(process.cwd());
206
- const simsByUdid = {};
207
- let simsAvailable = true;
208
- let simctlError = null;
209
- try {
210
- for (const sim of listAllIosSims()) simsByUdid[sim.udid] = sim;
211
- } catch (e) {
212
- simsAvailable = false;
213
- simctlError = String(e?.message || e).split("\n")[0] ?? "";
214
- }
215
- const source = resolveSourceCheckout(process.cwd());
216
- const sourcePath = "path" in source ? source.path : null;
217
- const worktrees = source.entries.filter((entry) => !entry.bare && entry.path !== sourcePath);
218
- const states = [];
219
- const labelOnlyRoots = [];
220
- for (const [path, proj] of projects) {
221
- let metro = null;
222
- if (proj.metroPort) metro = await resolveOnPort(proj.metroPort, path);
223
- const supervisor = await supervisorFacts(path, proj, metro);
224
- states.push(environmentState({
225
- ...proj,
226
- __path: path
227
- }, {
228
- simsByUdid,
229
- metro,
230
- worktrees,
231
- simsAvailable,
232
- androidRuntimes: Object.fromEntries(projectDeviceSlots(proj).slice(1).map(({ slot, platforms }) => [slot, platforms.android?.owned && platforms.android.avdName ? readAndroidRuntime(platforms.android.avdName) : null])),
233
- androidRuntime: proj.platforms?.android?.owned && proj.platforms.android.avdName ? readAndroidRuntime(proj.platforms.android.avdName) : null,
234
- supervisor,
235
- logs: logFacts(path)
236
- }));
237
- const state = states[states.length - 1];
238
- labelOnlyRoots.push(Boolean(proj.worktreeRoot && !proj.bundleId && !state?.metro && !state?.ios && !state?.android));
239
- }
240
- const leaseNow = Date.now();
241
- const leases = deviceLeaseStates(listLeaseFiles(), {
242
- root: cwdRoot,
243
- now: leaseNow
244
- });
245
- const cap = capacity(states, Math.round(totalmem() / 1048576));
246
- const orphanWorktrees = unprovisionedWorktrees(worktrees, projects.map(([p]) => p));
247
- const pools = ["ios", "android"].map((platform) => {
248
- const { max, error } = parkedMaxSetting(platform);
249
- return {
250
- error,
251
- line: poolLine({
252
- platform,
253
- parked: readParked(platform).length,
254
- max
255
- })
256
- };
257
- });
258
- if (opts.json) {
259
- console.log(JSON.stringify({
260
- environments: states.map((state, i) => labelOnlyRoots[i] ? {
261
- ...state,
262
- labelOnly: true
263
- } : state),
264
- capacity: cap,
265
- deviceLeases: leases,
266
- unprovisionedWorktrees: orphanWorktrees,
267
- simctlAvailable: simsAvailable
268
- }));
269
- return;
270
- }
271
- if (projects.length === 0 && orphanWorktrees.length === 0) {
272
- console.log(chalk.dim("No projects registered."));
273
- for (const pool of pools) {
274
- if (pool.error) console.log(chalk.yellow(`${pool.error} ${POOL_SETTING_REMEDY}`));
275
- if (pool.line) console.log(chalk.dim(pool.line));
276
- }
277
- for (const line of deviceLeaseLines(leases, leaseNow)) console.log(line);
278
- return;
279
- }
280
- if (!simsAvailable) console.log(chalk.yellow(`simctl could not be read (${simctlError}), so no iOS sim below could be checked.`));
281
- for (const [i, [path, proj]] of projects.entries()) {
282
- const state = states[i];
283
- if (!state) continue;
284
- const shortcut = projectShortcut(path, proj);
285
- const marker = path === cwdRoot ? chalk.bold.cyan(`* ${shortcut}`) : shortcut;
286
- const idle = state.live ? "" : chalk.dim(" [idle]");
287
- console.log(`\n${marker}${idle} ${chalk.dim(`(${path})`)}`);
288
- console.log(labelOnlyRoots[i] ? chalk.dim(" worktree root (holds the label; the app registers its own entry)") : chalk.dim(` app: ${proj.bundleId ?? "?"} (${proj.isExpo ? "expo" : "bare"})`));
289
- if (state.metro) {
290
- const label = state.metro.running ? chalk.green(`running (pid ${state.metro.pid})`) : chalk.dim("not running");
291
- console.log(` metro: port ${state.metro.port} ${label}`);
292
- }
293
- if (state.supervisor) {
294
- const health = state.supervisor.healthy ? chalk.green("healthy") : chalk.yellow("not answering");
295
- const mode = state.supervisor.mode ? chalk.dim(` (${state.supervisor.mode})`) : "";
296
- console.log(` supervisor: pid ${state.supervisor.pid}${mode} ${health}`);
297
- }
298
- if (state.logs) {
299
- const n = state.logs.errorsSinceMarker;
300
- const errs = n > 0 ? chalk.yellow(` (${n} error${n === 1 ? "" : "s"} since the last marker)`) : "";
301
- console.log(chalk.dim(` logs: ${state.logs.dir}`) + errs);
302
- }
303
- for (const deviceState of [{
304
- slot: "default",
305
- ios: state.ios,
306
- android: state.android
307
- }, ...state.slots ?? []]) {
308
- const slotLabel = deviceState.slot === "default" ? "" : ` [${deviceState.slot}]`;
309
- if (deviceState.ios) {
310
- const booted = deviceState.ios.state === "Booted" ? chalk.green("booted") : chalk.dim(deviceState.ios.state.toLowerCase());
311
- const owned = deviceState.ios.owned ? chalk.dim(" (owned)") : "";
312
- console.log(` ios${slotLabel}: ${chalk.cyan(deviceState.ios.name ?? deviceState.ios.udid)} ${booted}${owned}`);
313
- }
314
- if (deviceState.android) {
315
- const kind = deviceState.android.physical ? chalk.dim("(physical)") : chalk.dim("(emulator)");
316
- const observed = deviceState.android.state ? ` ${deviceState.android.state}${deviceState.android.serial ? ` (${deviceState.android.serial})` : ""}` : "";
317
- console.log(` android${slotLabel}: ${chalk.cyan(deviceState.android.name)} ${kind}${observed}${deviceState.android.owned ? chalk.dim(" (owned)") : ""}`);
318
- }
319
- }
320
- for (const w of state.warnings) console.log(chalk.yellow(` ! ${w}`));
321
- }
322
- for (const pool of pools) {
323
- if (pool.error) console.log(chalk.yellow(`\n${pool.error} ${POOL_SETTING_REMEDY}`));
324
- if (pool.line) console.log(chalk.dim(`\n${pool.line}`));
325
- }
326
- const leaseLines = deviceLeaseLines(leases, leaseNow);
327
- if (leaseLines.length) {
328
- console.log("");
329
- for (const line of leaseLines) console.log(line);
330
- }
331
- if (orphanWorktrees.length) {
332
- console.log(chalk.dim(`\nWorktrees with no environment (${orphanWorktrees.length}):`));
333
- for (const w of orphanWorktrees) console.log(chalk.dim(` ${w.path}${w.branch ? ` [${w.branch}]` : ""}`));
334
- }
335
- console.log(chalk.dim(`\n${cap.liveCount} live environment(s), roughly ${formatGb(cap.committedMb)} of ${formatGb(cap.totalMemoryMb)} committed.`));
336
- const volumes = readVolumes(cwdRoot || process.cwd());
337
- const line = diskLine(volumes);
338
- if (line) {
339
- const tight = tightVolumes(volumes);
340
- if (tight.length) {
341
- const which = tight.map((v) => v.volume).join(" and ");
342
- console.log(chalk.yellow(`${line} A single iOS build can exhaust ${which} -- run \`stim gc\` before starting another environment.`));
343
- } else console.log(chalk.dim(line));
344
- }
345
- if (cap.overCapacity) console.log(chalk.yellow("Over comfortable capacity. A machine that swaps is slower than one working in sequence -- release one before starting another."));
346
- });
347
- }
348
- function readAndroidRuntime(avdName) {
349
- try {
350
- const resolved = resolveOwnedAvdSerial(avdName, { timeoutMs: 5e3 });
351
- return {
352
- serial: resolved.serial ?? null,
353
- state: resolved.serial ? "detected" : resolved.missing ? "missing" : resolved.notRunning ? "not-detected" : "unknown"
354
- };
355
- } catch (error) {
356
- return {
357
- serial: null,
358
- state: "unknown",
359
- error: String(error?.message || error).split("\n")[0]
360
- };
361
- }
362
- }
363
- function readVolumes(projectPath) {
364
- const roots = [.../* @__PURE__ */ new Set([
365
- "/",
366
- volumeRootFor(getConfigDir()),
367
- volumeRootFor(projectPath)
368
- ])];
369
- const volumes = [];
370
- for (const volume of roots) {
371
- const quoted = `'${volume.replace(/'/g, "'\\''")}'`;
372
- const disk = parseDfFree(getExecutor().runQuiet(`df -k ${quoted}`, { timeoutMs: 5e3 }));
373
- if (disk) volumes.push({
374
- volume,
375
- disk
376
- });
377
- }
378
- return volumes;
379
- }
380
- async function resolveOnPort(port, path) {
381
- return await isMetroRunning(port) ? resolveProjectMetro(port, path) : { missing: true };
382
- }
383
- async function supervisorFacts(path, proj, metroResolution) {
384
- const state = readSupervisorState(path);
385
- const record = proj?.supervisor ?? null;
386
- const pid = state?.pid ?? record?.pid ?? null;
387
- if (!pid) return null;
388
- const port = state?.port ?? record?.port ?? null;
389
- const alive = resolveSupervisorTarget({
390
- state,
391
- record,
392
- reservedPort: proj?.metroPort
393
- }).status === "ours";
394
- let healthy = false;
395
- if (alive && port) {
396
- const resolution = port === proj?.metroPort && metroResolution ? metroResolution : await resolveOnPort(port, path);
397
- healthy = Boolean(resolution?.metro);
398
- }
399
- return {
400
- pid,
401
- mode: state?.mode ?? record?.mode ?? null,
402
- startedAt: state?.startedAt ?? record?.startedAt ?? null,
403
- alive,
404
- healthy
405
- };
406
- }
407
- function logFacts(path) {
408
- const dir = workspaceLogsDir(path);
409
- if (!existsSync(dir)) return null;
410
- try {
411
- return {
412
- dir,
413
- errorsSinceMarker: queryLogs({
414
- dir,
415
- errorsOnly: true
416
- }).length
417
- };
418
- } catch {
419
- return {
420
- dir,
421
- errorsSinceMarker: 0
422
- };
423
- }
424
- }
425
- //#endregion
426
- export { statusCommand as default, readVolumes };
@@ -1,6 +1,6 @@
1
1
  import { existsSync, readFileSync } from "node:fs";
2
- import { dirname, join, resolve } from "node:path";
3
2
  import { METRO_NAMED_CACHE_LAYOUT, metroCacheRoot, registerCache, sharedStoreRoot, tagSharedStore } from "@stim-cli/core";
3
+ import { dirname, join, resolve } from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
  //#region src/supervisor/metro-store.ts
6
6
  function metroStoreName(root) {