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,26 +1,24 @@
1
- import { K as projectDeviceSlots, g as loadConfig, u as getConfigDir, z as volumeRootFor } from "./config-Cp9lM3wX.mjs";
2
- import { t as getExecutor } from "./exec-jIAlgilk.mjs";
3
- import { a as parseNdjsonLine } from "./ndjson-9uFNRwfe.mjs";
4
- import { b as workspaceLogsDir, r as readWorkspaceState, y as workspaceLogErrorIndex } from "./workspace-state-DF2gCFhx.mjs";
5
- import { _ as repoRoot, s as gitCommonDir, y as resolveSourceCheckout } from "./worktree-R_cUlE7s.mjs";
6
- import { l as projectShortcut, o as findProjectRoot } from "./project-DS112DYB.mjs";
7
- import { l as readRemoteSession } from "./state-NcJzMEBF.mjs";
8
- import { c as resolveProjectMetro, i as listeningPidsByPort, o as processCwd, r as listeningPids, s as processCwds, u as isMetroRunning } from "./metro-DrLHaMn2.mjs";
9
- import { M as ownedAvdSerialResolver, c as androidToolPath } from "./android-_y4Ohi5z.mjs";
10
- import { a as formatElapsed, r as clockTime, u as plural } from "./command-output-gByVmIjy.mjs";
11
- import { o as parkedMaxSetting, s as readParked, t as POOL_SETTING_REMEDY } from "./sim-pool-KaIsp2Ft.mjs";
12
- import { a as buildStatusLine, i as buildReport, o as parseActiveBuild, r as activeBuildState, t as ACTIVE_BUILD_KEY } from "./build-progress-CVqkfPC6.mjs";
13
- import { d as listAllIosSimsAsync, v as parseSimctlList } from "./ios-BL2NFckr.mjs";
14
- import { i as listLeaseFiles } from "./device-lease-Crs-HkJY.mjs";
15
- import { a as statsProjectKey, r as readStats } from "./stats-DG9e66Rz.mjs";
16
- import { f as easMachineStateRoot, p as readEasSessionLedger } from "./device-remote-AQ5rQPqr.mjs";
17
- import { d as recordMatches, l as queryLogs, s as logFiles, t as ERROR_SOURCES } from "./logs-query-Cqma3WIu.mjs";
18
- import { t as resolveSupervisorTarget } from "./ownership-DCQPuWEc.mjs";
19
- import { i as readSupervisorState } from "./stop-BO28HGqF.mjs";
1
+ import { F as volumeRootFor, H as projectDeviceSlots, g as loadConfig$1, u as getConfigDir } from "./config-BEq0Qw_a.mjs";
2
+ import { t as getExecutor } from "./exec-DK8KaVHK.mjs";
3
+ import { a as parseNdjsonLine } from "./ndjson-CDY9IkVC.mjs";
4
+ import { A as workspaceLogErrorIndex, a as processCwd, j as workspaceLogsDir, l as isMetroRunning, m as readWorkspaceState$1, n as listeningPids, o as processCwds, r as listeningPidsByPort, s as resolveProjectMetro } from "./metro-BWLOJ_Wr.mjs";
5
+ import { c as gitCommonDirOnDisk, p as linkedWorktreesOnDisk, s as gitCommonDir, y as repoRoot } from "./worktree-BlizKDpZ.mjs";
6
+ import { l as projectShortcut, o as findProjectRoot } from "./project-BXZzOABt.mjs";
7
+ import { l as readRemoteSession } from "./state-WGdIbi_d.mjs";
8
+ import { i as listLeaseFiles } from "./device-lease-CbEaIk7b.mjs";
9
+ import { a as buildStatusLine, c as createActivityReader, i as buildReport, o as parseActiveBuild, r as activeBuildState, t as ACTIVE_BUILD_KEY } from "./build-progress-BXx-ZBe4.mjs";
10
+ import { N as ownedAvdSerialResolver, c as androidToolPath } from "./android-787HhP5w.mjs";
11
+ import { b as readParked, h as POOL_SETTING_REMEDY, t as resolveSupervisorTarget, y as parkedMaxSetting } from "./ownership-DZjdx8Gq.mjs";
12
+ import { _ as parseSimctlList, u as listAllIosSimsAsync } from "./ios-B2Fpqvlb.mjs";
13
+ import { m as readEasSessionLedger$1, p as easMachineStateRoot$1 } from "./device-remote-Dy1jGj3U.mjs";
14
+ import { a as diskLine, c as parseDfFree, d as remoteDeviceState, f as tightVolumes, i as deviceLeaseStates, l as poolLine, n as capacity, o as environmentState, p as unprovisionedWorktrees, r as deviceLeaseLines, t as activityLabel, u as remoteDeviceLine } from "./status-Dr4fRkym.mjs";
15
+ import { a as statsProjectKey, r as readStats } from "./stats-BAgJaLA4.mjs";
16
+ import { i as readSupervisorState } from "./stop-Dj4pfmyv.mjs";
20
17
  import { readdirSync, watch } from "node:fs";
21
- import { closeSync as closeSync$1, existsSync as existsSync$1, fstatSync, openSync as openSync$1, readFileSync as readFileSync$1, readSync as readSync$1, renameSync as renameSync$1, rmSync as rmSync$1, writeFileSync as writeFileSync$1 } from "fs";
22
- import { join } from "path";
18
+ import { closeSync as closeSync$1, existsSync as existsSync$1, fstatSync as fstatSync$1, openSync as openSync$1, readFileSync as readFileSync$1, readSync as readSync$1, renameSync as renameSync$1, rmSync as rmSync$1, writeFileSync as writeFileSync$1 } from "fs";
19
+ import { basename, dirname, join } from "path";
23
20
  import { rotatedLogPath } from "@stim-cli/core";
21
+ import { ERROR_SOURCES, logFiles, queryLogs, readIdleStop, recordMatches } from "@stim-cli/core/state";
24
22
  import { join as join$1 } from "node:path";
25
23
  import { totalmem } from "os";
26
24
  import chalk from "chalk";
@@ -106,7 +104,7 @@ function watchStatusSources({ home, onChange, platform = process.platform, simct
106
104
  [home, "home"],
107
105
  [join$1(home, "workspaces"), "workspaces"],
108
106
  [join$1(home, "device-locks"), "leases"],
109
- [easMachineStateRoot(), "eas"]
107
+ [easMachineStateRoot$1(), "eas"]
110
108
  ]);
111
109
  let names = [];
112
110
  try {
@@ -289,7 +287,7 @@ function summarize(path, known) {
289
287
  return null;
290
288
  }
291
289
  try {
292
- const { dev, ino, size } = fstatSync(fd);
290
+ const { dev, ino, size } = fstatSync$1(fd);
293
291
  const key = `${dev}:${ino}`;
294
292
  const cached = known[key];
295
293
  const base = cached && stillExtends(fd, cached, size) ? cached : null;
@@ -408,203 +406,6 @@ function writeIndex(file, index) {
408
406
  }
409
407
  }
410
408
  //#endregion
411
- //#region src/status.ts
412
- const IOS_SIM_MB = 1500;
413
- const ANDROID_EMULATOR_MB = 2500;
414
- const METRO_MB = 700;
415
- function remoteDeviceState(record, ledger, root) {
416
- if (!record) return null;
417
- const claim = ledger.claims.get(record.sessionId);
418
- return {
419
- platform: record.platform,
420
- backend: "eas",
421
- sessionId: record.sessionId,
422
- state: !ledger.safe ? "unknown" : claim?.workspaceRoot === root ? "claimed" : "unclaimed",
423
- startedAt: record.startedAt,
424
- webPreviewUrl: record.webPreviewUrl
425
- };
426
- }
427
- function poolLine({ platform, parked, max }) {
428
- if (parked <= 0) return null;
429
- const what = plural(parked, `parked ${platform === "ios" ? "iOS simulator" : "Android emulator"}`);
430
- return max > 0 ? `pool: ${what} (max ${max})` : `pool: ${what} (parking off; gc --delete removes them)`;
431
- }
432
- function environmentState(project, { simsByUdid = {}, metro = null, worktrees = [], simsAvailable = true, androidRuntime = null, androidRuntimes = {}, supervisor = null, logs = null, remote = null } = {}) {
433
- const ios = project.platforms?.ios;
434
- const android = project.platforms?.android;
435
- const sim = ios ? simsByUdid[ios.deviceUdid] : null;
436
- const simBooted = Boolean(sim && sim.state === "Booted");
437
- const metroRunning = Boolean(metro?.metro);
438
- const androidDetected = androidRuntime ? Boolean(androidRuntime.serial) : Boolean(android?.serial);
439
- let live = simBooted || metroRunning || androidDetected || Boolean(remote);
440
- let memoryMb = 0;
441
- if (simBooted) memoryMb += IOS_SIM_MB;
442
- if (androidDetected) memoryMb += ANDROID_EMULATOR_MB;
443
- if (metroRunning) memoryMb += METRO_MB;
444
- const warnings = [];
445
- if (metro?.notOurs) warnings.push(`port ${project.metroPort}: ${metro.notOurs}`);
446
- if (ios && !sim && simsAvailable) warnings.push(`recorded sim ${ios.deviceUdid} no longer exists`);
447
- if (simBooted && project.metroPort && !metroRunning) warnings.push("simulator is booted with no Metro serving it");
448
- if (androidRuntime && android?.avdName) {
449
- const recordedSerial = android.consolePort ? `emulator-${android.consolePort}` : android.serial;
450
- 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}`);
451
- if (androidRuntime.state === "missing") warnings.push(`recorded AVD ${android.avdName} no longer exists`);
452
- 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`);
453
- if (androidRuntime.error) warnings.push(`could not check owned AVD ${android.avdName}: ${androidRuntime.error}`);
454
- }
455
- if (supervisor && supervisor.alive === false) warnings.push(`stale supervisor record for ${project.__path}`);
456
- const slots = [];
457
- for (const { slot, platforms } of projectDeviceSlots(project).slice(1)) {
458
- const deviceState = environmentState({
459
- ...project,
460
- platforms,
461
- deviceSlots: void 0
462
- }, {
463
- simsByUdid,
464
- simsAvailable,
465
- metro,
466
- androidRuntime: androidRuntimes[slot]
467
- });
468
- slots.push({
469
- slot,
470
- ios: deviceState.ios,
471
- android: deviceState.android
472
- });
473
- memoryMb += deviceState.memoryMb - (metroRunning ? METRO_MB : 0);
474
- live ||= deviceState.live;
475
- warnings.push(...deviceState.warnings.map((warning) => `${slot}: ${warning}`));
476
- }
477
- return {
478
- path: project.__path,
479
- ...slots.length ? { slots } : {},
480
- live,
481
- memoryMb,
482
- warnings,
483
- ios: ios ? {
484
- name: sim?.name ?? null,
485
- udid: ios.deviceUdid,
486
- owned: Boolean(ios.owned),
487
- state: sim?.state ?? (simsAvailable ? "missing" : "unknown")
488
- } : null,
489
- android: android ? {
490
- name: android.avdName ?? android.serial,
491
- owned: Boolean(android.owned),
492
- physical: Boolean(android.serial && !android.avdName),
493
- ...androidRuntime ? {
494
- serial: androidRuntime.serial,
495
- state: androidRuntime.state
496
- } : {}
497
- } : null,
498
- metro: project.metroPort ? {
499
- port: project.metroPort,
500
- running: metroRunning,
501
- pid: metro?.metro?.pid ?? null
502
- } : null,
503
- supervisor: supervisor ? {
504
- pid: supervisor.pid ?? null,
505
- mode: supervisor.mode ?? null,
506
- startedAt: supervisor.startedAt ?? null,
507
- healthy: Boolean(supervisor.healthy)
508
- } : null,
509
- logs: logs ? {
510
- dir: logs.dir,
511
- errorsSinceMarker: logs.errorsSinceMarker ?? 0
512
- } : null,
513
- worktree: worktrees.find((w) => w.path === project.__path) ?? null,
514
- remoteDevices: remote ? [remote] : []
515
- };
516
- }
517
- function remoteDeviceLine(remote) {
518
- const claim = remote.state === "claimed" ? "" : ` (${remote.state})`;
519
- const watch = remote.webPreviewUrl ? ` -- watch: ${remote.webPreviewUrl}` : "";
520
- return `remote ${remote.platform ?? "?"}: EAS session ${remote.sessionId} billable${claim}${watch}`;
521
- }
522
- function capacity(states, totalMemoryMb) {
523
- const committedMb = states.reduce((n, s) => n + s.memoryMb, 0);
524
- return {
525
- liveCount: states.filter((s) => s.live).length,
526
- committedMb,
527
- totalMemoryMb,
528
- overCapacity: Boolean(totalMemoryMb && committedMb > totalMemoryMb * .6)
529
- };
530
- }
531
- function parseDfFree(output) {
532
- const lines = String(output || "").trim().split("\n");
533
- if (lines.length < 2) return null;
534
- const lastLine = lines[lines.length - 1];
535
- if (lastLine === void 0) return null;
536
- const m = /\s(\d+)\s+(\d+)\s+(\d+)\s+(\d+)%/.exec(lastLine);
537
- if (!m) return null;
538
- const totalKb = Number(m[1]);
539
- const availableKb = Number(m[3]);
540
- if (!Number.isFinite(totalKb) || !Number.isFinite(availableKb) || totalKb <= 0) return null;
541
- return {
542
- availableMb: Math.round(availableKb / 1024),
543
- totalMb: Math.round(totalKb / 1024)
544
- };
545
- }
546
- function diskIsTight(disk) {
547
- return Boolean(disk && disk.availableMb < 25600);
548
- }
549
- function formatSpace(mb) {
550
- if (!Number.isFinite(mb)) return "?";
551
- if (mb >= 1048576) return `${(mb / 1048576).toFixed(1)} TB`;
552
- if (mb >= 1024) return `${Math.round(mb / 1024)} GB`;
553
- return `${Math.round(mb)} MB`;
554
- }
555
- function diskLine(volumes) {
556
- const usable = (volumes || []).filter((v) => Boolean(v && v.disk));
557
- if (usable.length === 0) return null;
558
- if (usable.length === 1) {
559
- const first = usable[0];
560
- if (!first) return null;
561
- const { disk } = first;
562
- return `${formatSpace(disk.availableMb)} free of ${formatSpace(disk.totalMb)} on disk.`;
563
- }
564
- return `${usable.map((v) => `${formatSpace(v.disk.availableMb)} free on ${v.volume}`).join(", ")}.`;
565
- }
566
- function tightVolumes(volumes) {
567
- return (volumes || []).filter((v) => v && diskIsTight(v.disk));
568
- }
569
- function unprovisionedWorktrees(worktrees, projectPaths) {
570
- const known = new Set(projectPaths);
571
- return worktrees.filter((w) => !known.has(w.path));
572
- }
573
- function deviceLeaseStates(entries, { root, now }) {
574
- return entries.map((entry) => {
575
- const lease = entry.lease;
576
- return {
577
- path: entry.path,
578
- platform: entry.platform,
579
- ...lease?.slot ? { slot: lease.slot } : {},
580
- id: entry.id,
581
- deviceName: lease?.deviceName ?? null,
582
- holder: lease?.holder ?? null,
583
- grantedAt: lease?.grantedAt ?? null,
584
- expiresAt: lease?.expiresAt ?? null,
585
- mine: Boolean(lease && root && lease.holder === root),
586
- expired: Boolean(lease && Date.parse(lease.expiresAt) <= now),
587
- parsed: Boolean(lease)
588
- };
589
- });
590
- }
591
- function deviceLeaseLines(states, now) {
592
- if (states.length === 0) return [];
593
- const lines = [`Device leases (${states.length}):`];
594
- for (const state of states) {
595
- const device = `${state.platform}${state.slot ? ` [${state.slot}]` : ""} ${state.id ?? state.path}${state.deviceName ? ` (${state.deviceName})` : ""}`;
596
- if (!state.parsed || state.expiresAt === null) {
597
- lines.push(` ${device} -- unreadable lease file, so nothing may take the device: ${state.path}`);
598
- continue;
599
- }
600
- const when = clockTime(state.expiresAt);
601
- const remaining = Date.parse(state.expiresAt) - now;
602
- const expiry = state.expired ? `expired at ${when}` : `until ${when} (${formatElapsed(remaining)} left)`;
603
- lines.push(` ${device} -- ${state.holder} ${expiry}${state.mine ? " [this workspace]" : ""}`);
604
- }
605
- return lines;
606
- }
607
- //#endregion
608
409
  //#region src/commands/status.ts
609
410
  function formatGb(mb) {
610
411
  return `${(mb / 1024).toFixed(1)} GB`;
@@ -617,7 +418,7 @@ function statusCommand(program) {
617
418
  }
618
419
  async function statusLines(json) {
619
420
  const out = [];
620
- const cfg = loadConfig();
421
+ const cfg = loadConfig$1();
621
422
  const projects = Object.entries(cfg?.projects || {});
622
423
  const cwdRoot = findProjectRoot(process.cwd());
623
424
  const simsRead = listAllIosSimsAsync();
@@ -631,9 +432,6 @@ async function statusLines(json) {
631
432
  };
632
433
  }));
633
434
  processes.catch(() => {});
634
- const source = resolveSourceCheckout(process.cwd());
635
- const sourcePath = "path" in source ? source.path : null;
636
- const worktrees = source.entries.filter((entry) => !entry.bare && entry.path !== sourcePath);
637
435
  const androidRuntimeOf = androidRuntimeReader();
638
436
  const devices = projects.map(([, proj]) => ({
639
437
  androidRuntimes: Object.fromEntries(projectDeviceSlots(proj).slice(1).map(({ slot, platforms }) => [slot, platforms.android?.owned && platforms.android.avdName ? androidRuntimeOf(platforms.android.avdName) : null])),
@@ -650,10 +448,11 @@ async function statusLines(json) {
650
448
  simctlError = String(e?.message || e).split("\n")[0] ?? "";
651
449
  }
652
450
  const running = await processes;
451
+ const worktrees = linkedWorktrees([process.cwd(), ...projects.map(([path]) => path)]);
653
452
  const history = readStats().record?.history;
654
453
  const states = [];
655
454
  const labelOnlyRoots = [];
656
- const easLedger = readEasSessionLedger();
455
+ const easLedger = readEasSessionLedger$1();
657
456
  for (const [i, [path, proj]] of projects.entries()) {
658
457
  const { metro, supervisor } = running[i];
659
458
  states.push(environmentState({
@@ -667,18 +466,41 @@ async function statusLines(json) {
667
466
  ...devices[i],
668
467
  supervisor,
669
468
  logs: logs[i],
670
- remote: remoteDeviceState(readRemoteSession(path), easLedger, path)
469
+ remote: remoteDeviceState(readRemoteSession(path), easLedger, path),
470
+ idleStop: readIdleStop(readWorkspaceState$1(path))
671
471
  }));
672
472
  const state = states[states.length - 1];
673
473
  if (state) state.build = workspaceBuild(path, history);
674
474
  labelOnlyRoots.push(Boolean(proj.worktreeRoot && !proj.bundleId && !state?.metro && !state?.ios && !state?.android));
675
475
  }
476
+ const readActivity = createActivityReader();
477
+ for (const state of states) for (const device of [{
478
+ slot: "default",
479
+ ios: state.ios,
480
+ android: state.android
481
+ }, ...state.slots ?? []]) {
482
+ const base = {
483
+ slot: device.slot,
484
+ workspace: state.path
485
+ };
486
+ if (device.ios?.state === "Booted") device.ios.activity = readActivity({
487
+ ...base,
488
+ platform: "ios",
489
+ id: device.ios.udid
490
+ });
491
+ if (device.android && !device.android.physical && device.android.serial) device.android.activity = readActivity({
492
+ ...base,
493
+ platform: "android",
494
+ id: device.android.serial
495
+ });
496
+ }
676
497
  const leaseNow = Date.now();
677
498
  const leases = deviceLeaseStates(listLeaseFiles(), {
678
499
  root: cwdRoot,
679
500
  now: leaseNow
680
501
  });
681
- const cap = capacity(states, Math.round(totalmem() / 1048576));
502
+ const totalMemoryMb = Math.round(totalmem() / 1048576);
503
+ const cap = capacity(states, totalMemoryMb);
682
504
  const orphanWorktrees = unprovisionedWorktrees(worktrees, projects.map(([p]) => p));
683
505
  const pools = ["ios", "android"].map((platform) => {
684
506
  const { max, error } = parkedMaxSetting(platform);
@@ -723,7 +545,7 @@ async function statusLines(json) {
723
545
  out.push(`\n${marker}${idle} ${chalk.dim(`(${path})`)}`);
724
546
  out.push(labelOnlyRoots[i] ? chalk.dim(" worktree root (holds the label; the app registers its own entry)") : chalk.dim(` app: ${proj.bundleId ?? "?"} (${proj.isExpo ? "expo" : "bare"})`));
725
547
  if (state.metro) {
726
- const label = state.metro.running ? chalk.green(`running (pid ${state.metro.pid})`) : chalk.dim("not running");
548
+ const label = state.metro.running ? chalk.green(`running (pid ${state.metro.pid})`) : state.metro.idleStop ? chalk.dim(`stopped (idle) after ${state.metro.idleStop.idleMinutes}m; \`stim start\`, \`ios\` or \`android\` restarts it`) : chalk.dim("not running");
727
549
  out.push(` metro: port ${state.metro.port} ${label}`);
728
550
  }
729
551
  if (state.supervisor) {
@@ -749,12 +571,12 @@ async function statusLines(json) {
749
571
  if (deviceState.ios) {
750
572
  const booted = deviceState.ios.state === "Booted" ? chalk.green("booted") : chalk.dim(deviceState.ios.state.toLowerCase());
751
573
  const owned = deviceState.ios.owned ? chalk.dim(" (owned)") : "";
752
- out.push(` ios${slotLabel}: ${chalk.cyan(deviceState.ios.name ?? deviceState.ios.udid)} ${booted}${owned}`);
574
+ out.push(` ios${slotLabel}: ${chalk.cyan(deviceState.ios.name ?? deviceState.ios.udid)} ${booted}${owned}${activitySuffix(deviceState.ios.activity)}`);
753
575
  }
754
576
  if (deviceState.android) {
755
577
  const kind = deviceState.android.physical ? chalk.dim("(physical)") : chalk.dim("(emulator)");
756
578
  const observed = deviceState.android.state ? ` ${deviceState.android.state}${deviceState.android.serial ? ` (${deviceState.android.serial})` : ""}` : "";
757
- out.push(` android${slotLabel}: ${chalk.cyan(deviceState.android.name)} ${kind}${observed}${deviceState.android.owned ? chalk.dim(" (owned)") : ""}`);
579
+ out.push(` android${slotLabel}: ${chalk.cyan(deviceState.android.name)} ${kind}${observed}${deviceState.android.owned ? chalk.dim(" (owned)") : ""}${activitySuffix(deviceState.android.activity)}`);
758
580
  }
759
581
  }
760
582
  for (const remote of state.remoteDevices ?? []) out.push(` ${remoteDeviceLine(remote)}`);
@@ -823,8 +645,18 @@ async function watchStatus(json) {
823
645
  scheduler.trigger(0);
824
646
  await new Promise(() => {});
825
647
  }
648
+ function activitySuffix(activity) {
649
+ const label = activityLabel(activity, Date.now());
650
+ return label ? ` -- ${activity?.state === "driven" ? chalk.magenta(label) : chalk.dim(label)}` : "";
651
+ }
652
+ function linkedWorktrees(paths) {
653
+ return [...new Set(paths.flatMap((path) => gitCommonDirOnDisk(path) ?? []))].flatMap((common) => {
654
+ const repository = basename(common) === ".git" ? dirname(common) : common;
655
+ return linkedWorktreesOnDisk(common).map((entry) => Object.assign(entry, { repository }));
656
+ });
657
+ }
826
658
  function workspaceBuild(path, history) {
827
- const record = parseActiveBuild(readWorkspaceState(path)?.[ACTIVE_BUILD_KEY]);
659
+ const record = parseActiveBuild(readWorkspaceState$1(path)?.[ACTIVE_BUILD_KEY]);
828
660
  if (!record) return null;
829
661
  const projectKey = statsProjectKey({
830
662
  root: path,
@@ -0,0 +1,216 @@
1
+ import { H as projectDeviceSlots } from "./config-BEq0Qw_a.mjs";
2
+ import { a as formatElapsed, o as formatLongDuration, r as clockTime, u as plural } from "./command-output-D5z2_bcF.mjs";
3
+ import { sep } from "path";
4
+ //#region src/status.ts
5
+ const IOS_SIM_MB = 1500;
6
+ const ANDROID_EMULATOR_MB = 2500;
7
+ const METRO_MB = 700;
8
+ function remoteDeviceState(record, ledger, root) {
9
+ if (!record) return null;
10
+ const claim = ledger.claims.get(record.sessionId);
11
+ return {
12
+ platform: record.platform,
13
+ backend: "eas",
14
+ sessionId: record.sessionId,
15
+ state: !ledger.safe ? "unknown" : claim?.workspaceRoot === root ? "claimed" : "unclaimed",
16
+ startedAt: record.startedAt,
17
+ webPreviewUrl: record.webPreviewUrl
18
+ };
19
+ }
20
+ function poolLine({ platform, parked, max }) {
21
+ if (parked <= 0) return null;
22
+ const what = plural(parked, `parked ${platform === "ios" ? "iOS simulator" : "Android emulator"}`);
23
+ return max > 0 ? `pool: ${what} (max ${max})` : `pool: ${what} (parking off; gc --delete removes them)`;
24
+ }
25
+ function environmentState(project, { simsByUdid = {}, metro = null, worktrees = [], simsAvailable = true, androidRuntime = null, androidRuntimes = {}, supervisor = null, logs = null, remote = null, idleStop = null } = {}) {
26
+ const ios = project.platforms?.ios;
27
+ const android = project.platforms?.android;
28
+ const sim = ios ? simsByUdid[ios.deviceUdid] : null;
29
+ const simBooted = Boolean(sim && sim.state === "Booted");
30
+ const metroRunning = Boolean(metro?.metro);
31
+ const androidDetected = androidRuntime ? Boolean(androidRuntime.serial) : Boolean(android?.serial);
32
+ let live = simBooted || metroRunning || androidDetected || Boolean(remote);
33
+ let memoryMb = 0;
34
+ if (simBooted) memoryMb += IOS_SIM_MB;
35
+ if (androidDetected) memoryMb += ANDROID_EMULATOR_MB;
36
+ if (metroRunning) memoryMb += METRO_MB;
37
+ const warnings = [];
38
+ if (metro?.notOurs) warnings.push(`port ${project.metroPort}: ${metro.notOurs}`);
39
+ if (ios && !sim && simsAvailable) warnings.push(`recorded sim ${ios.deviceUdid} no longer exists`);
40
+ if (simBooted && project.metroPort && !metroRunning) warnings.push("simulator is booted with no Metro serving it");
41
+ if (androidRuntime && android?.avdName) {
42
+ const recordedSerial = android.consolePort ? `emulator-${android.consolePort}` : android.serial;
43
+ 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}`);
44
+ if (androidRuntime.state === "missing") warnings.push(`recorded AVD ${android.avdName} no longer exists`);
45
+ 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`);
46
+ if (androidRuntime.error) warnings.push(`could not check owned AVD ${android.avdName}: ${androidRuntime.error}`);
47
+ }
48
+ if (supervisor && supervisor.alive === false) warnings.push(`stale supervisor record for ${project.__path}`);
49
+ const slots = [];
50
+ for (const { slot, platforms } of projectDeviceSlots(project).slice(1)) {
51
+ const deviceState = environmentState({
52
+ ...project,
53
+ platforms,
54
+ deviceSlots: void 0
55
+ }, {
56
+ simsByUdid,
57
+ simsAvailable,
58
+ metro,
59
+ androidRuntime: androidRuntimes[slot]
60
+ });
61
+ slots.push({
62
+ slot,
63
+ ios: deviceState.ios,
64
+ android: deviceState.android
65
+ });
66
+ memoryMb += deviceState.memoryMb - (metroRunning ? METRO_MB : 0);
67
+ live ||= deviceState.live;
68
+ warnings.push(...deviceState.warnings.map((warning) => `${slot}: ${warning}`));
69
+ }
70
+ return {
71
+ path: project.__path,
72
+ ...slots.length ? { slots } : {},
73
+ live,
74
+ memoryMb,
75
+ warnings,
76
+ ios: ios ? {
77
+ name: sim?.name ?? null,
78
+ udid: ios.deviceUdid,
79
+ owned: Boolean(ios.owned),
80
+ state: sim?.state ?? (simsAvailable ? "missing" : "unknown")
81
+ } : null,
82
+ android: android ? {
83
+ name: android.avdName ?? android.serial,
84
+ owned: Boolean(android.owned),
85
+ physical: Boolean(android.serial && !android.avdName),
86
+ ...androidRuntime ? {
87
+ serial: androidRuntime.serial,
88
+ state: androidRuntime.state
89
+ } : {}
90
+ } : null,
91
+ metro: project.metroPort ? {
92
+ port: project.metroPort,
93
+ running: metroRunning,
94
+ pid: metro?.metro?.pid ?? null,
95
+ ...idleStop && !metroRunning ? { idleStop } : {}
96
+ } : null,
97
+ supervisor: supervisor ? {
98
+ pid: supervisor.pid ?? null,
99
+ mode: supervisor.mode ?? null,
100
+ startedAt: supervisor.startedAt ?? null,
101
+ healthy: Boolean(supervisor.healthy)
102
+ } : null,
103
+ logs: logs ? {
104
+ dir: logs.dir,
105
+ errorsSinceMarker: logs.errorsSinceMarker ?? 0
106
+ } : null,
107
+ worktree: worktrees.find((w) => w.path === project.__path) ?? null,
108
+ remoteDevices: remote ? [remote] : []
109
+ };
110
+ }
111
+ function activityLabel(activity, now) {
112
+ if (!activity) return null;
113
+ if (activity.state === "driven") {
114
+ const since = Date.parse(activity.driver?.since ?? "");
115
+ const duration = Number.isFinite(since) ? ` for ${formatLongDuration(Math.max(0, now - since))}` : "";
116
+ return `driven by ${activity.driver?.tool ?? "an unknown tool"}${duration}`;
117
+ }
118
+ if (activity.state === "unknown") return `activity unknown (${activity.basis.join(", ")})`;
119
+ if (activity.state === "active") return "active";
120
+ const last = Date.parse(activity.lastActivityAt ?? "");
121
+ return Number.isFinite(last) ? `idle ${formatLongDuration(Math.max(0, now - last))}` : "idle (no recorded activity)";
122
+ }
123
+ function remoteDeviceLine(remote) {
124
+ const claim = remote.state === "claimed" ? "" : ` (${remote.state})`;
125
+ const watch = remote.webPreviewUrl ? ` -- watch: ${remote.webPreviewUrl}` : "";
126
+ return `remote ${remote.platform ?? "?"}: EAS session ${remote.sessionId} billable${claim}${watch}`;
127
+ }
128
+ function capacity(states, totalMemoryMb) {
129
+ const committedMb = states.reduce((n, s) => n + s.memoryMb, 0);
130
+ return {
131
+ liveCount: states.filter((s) => s.live).length,
132
+ committedMb,
133
+ totalMemoryMb,
134
+ overCapacity: Boolean(totalMemoryMb && committedMb > totalMemoryMb * .6)
135
+ };
136
+ }
137
+ function parseDfFree(output) {
138
+ const lines = String(output || "").trim().split("\n");
139
+ if (lines.length < 2) return null;
140
+ const lastLine = lines[lines.length - 1];
141
+ if (lastLine === void 0) return null;
142
+ const m = /\s(\d+)\s+(\d+)\s+(\d+)\s+(\d+)%/.exec(lastLine);
143
+ if (!m) return null;
144
+ const totalKb = Number(m[1]);
145
+ const availableKb = Number(m[3]);
146
+ if (!Number.isFinite(totalKb) || !Number.isFinite(availableKb) || totalKb <= 0) return null;
147
+ return {
148
+ availableMb: Math.round(availableKb / 1024),
149
+ totalMb: Math.round(totalKb / 1024)
150
+ };
151
+ }
152
+ function diskIsTight(disk) {
153
+ return Boolean(disk && disk.availableMb < 25600);
154
+ }
155
+ function formatSpace(mb) {
156
+ if (!Number.isFinite(mb)) return "?";
157
+ if (mb >= 1048576) return `${(mb / 1048576).toFixed(1)} TB`;
158
+ if (mb >= 1024) return `${Math.round(mb / 1024)} GB`;
159
+ return `${Math.round(mb)} MB`;
160
+ }
161
+ function diskLine(volumes) {
162
+ const usable = (volumes || []).filter((v) => Boolean(v && v.disk));
163
+ if (usable.length === 0) return null;
164
+ if (usable.length === 1) {
165
+ const first = usable[0];
166
+ if (!first) return null;
167
+ const { disk } = first;
168
+ return `${formatSpace(disk.availableMb)} free of ${formatSpace(disk.totalMb)} on disk.`;
169
+ }
170
+ return `${usable.map((v) => `${formatSpace(v.disk.availableMb)} free on ${v.volume}`).join(", ")}.`;
171
+ }
172
+ function tightVolumes(volumes) {
173
+ return (volumes || []).filter((v) => v && diskIsTight(v.disk));
174
+ }
175
+ function unprovisionedWorktrees(worktrees, projectPaths) {
176
+ return worktrees.filter((w) => {
177
+ const inside = w.path.endsWith(sep) ? w.path : w.path + sep;
178
+ return !projectPaths.some((p) => p === w.path || p.startsWith(inside));
179
+ });
180
+ }
181
+ function deviceLeaseStates(entries, { root, now }) {
182
+ return entries.map((entry) => {
183
+ const lease = entry.lease;
184
+ return {
185
+ path: entry.path,
186
+ platform: entry.platform,
187
+ ...lease?.slot ? { slot: lease.slot } : {},
188
+ id: entry.id,
189
+ deviceName: lease?.deviceName ?? null,
190
+ holder: lease?.holder ?? null,
191
+ grantedAt: lease?.grantedAt ?? null,
192
+ expiresAt: lease?.expiresAt ?? null,
193
+ mine: Boolean(lease && root && lease.holder === root),
194
+ expired: Boolean(lease && Date.parse(lease.expiresAt) <= now),
195
+ parsed: Boolean(lease)
196
+ };
197
+ });
198
+ }
199
+ function deviceLeaseLines(states, now) {
200
+ if (states.length === 0) return [];
201
+ const lines = [`Device leases (${states.length}):`];
202
+ for (const state of states) {
203
+ const device = `${state.platform}${state.slot ? ` [${state.slot}]` : ""} ${state.id ?? state.path}${state.deviceName ? ` (${state.deviceName})` : ""}`;
204
+ if (!state.parsed || state.expiresAt === null) {
205
+ lines.push(` ${device} -- unreadable lease file, so nothing may take the device: ${state.path}`);
206
+ continue;
207
+ }
208
+ const when = clockTime(state.expiresAt);
209
+ const remaining = Date.parse(state.expiresAt) - now;
210
+ const expiry = state.expired ? `expired at ${when}` : `until ${when} (${formatElapsed(remaining)} left)`;
211
+ lines.push(` ${device} -- ${state.holder} ${expiry}${state.mine ? " [this workspace]" : ""}`);
212
+ }
213
+ return lines;
214
+ }
215
+ //#endregion
216
+ export { diskLine as a, parseDfFree as c, remoteDeviceState as d, tightVolumes as f, deviceLeaseStates as i, poolLine as l, capacity as n, environmentState as o, unprovisionedWorktrees as p, deviceLeaseLines as r, formatSpace as s, activityLabel as t, remoteDeviceLine as u };