stim 1.15.0 → 1.17.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 (123) hide show
  1. package/dist/{activity-B2-kb7k0.mjs → activity-IRvJY_ru.mjs} +49 -16
  2. package/dist/agent-device-usage-output-BK41vnzN.mjs +411 -0
  3. package/dist/{android-Cu_L4RpL.mjs → android--GorydsK.mjs} +692 -321
  4. package/dist/{android-Db2eEMaf.mjs → android-B_YNZy61.mjs} +25 -60
  5. package/dist/{android-CYmq0jNi.mjs → android-DE4v59Mb.mjs} +12 -3
  6. package/dist/{android-cas-PnKfAdB2.mjs → android-cas-ZFpjJBL_.mjs} +6 -5
  7. package/dist/{app-install-CAAplXAt.mjs → app-install-VPkI5WEg.mjs} +52 -8
  8. package/dist/attempt-Bk2ouQVL.mjs +39 -0
  9. package/dist/{budget-CMiqghig.mjs → budget-DM984mnd.mjs} +372 -366
  10. package/dist/{build-plan-aeGMFX7e.mjs → build-plan-CmQCoyT-.mjs} +346 -88
  11. package/dist/{build-slots-Df1ApSuH.mjs → build-slots-41v_d2x9.mjs} +36 -21
  12. package/dist/{cli-0cX5s4NM.mjs → cli-Daxug5-K.mjs} +23 -17
  13. package/dist/cli.mjs +1 -1
  14. package/dist/collector-run.mjs +5 -192
  15. package/dist/{command-output-CliHHn7a.mjs → command-output-CDAIFD-8.mjs} +3 -3
  16. package/dist/{created-devices-BwVb3liH.mjs → created-devices-CpvzBL25.mjs} +1 -1
  17. package/dist/{dependency-state-BmsjKZ2H.mjs → dependency-state-CLcBHH2M.mjs} +1 -1
  18. package/dist/{deps-BFlKbOIp.mjs → deps-DNWaQs4V.mjs} +2 -2
  19. package/dist/{dev-client-CjzLoNs0.mjs → dev-client-B-XC4uRH.mjs} +3 -40
  20. package/dist/{device-BZ1IyS5A.mjs → device-C8IOoJPv.mjs} +7 -6
  21. package/dist/device-capacity-D0GWHnTM.mjs +580 -0
  22. package/dist/device-host-worker.mjs +587 -110
  23. package/dist/{device-ios-DMQBUPus.mjs → device-ios-Bc7vbGwl.mjs} +75 -44
  24. package/dist/{device-lease-W5Pi1q38.mjs → device-lease-DpoS36_u.mjs} +2 -2
  25. package/dist/{device-pool-B2r38YjS.mjs → device-lease-run-B8YltIH_.mjs} +5 -125
  26. package/dist/device-pool-3o7s5lo4.mjs +125 -0
  27. package/dist/{device-remote-9ulPsdhY.mjs → device-remote-hFSkpfQu.mjs} +13 -13
  28. package/dist/{doctor-BcE5xsBn.mjs → doctor-Cnk9d7Hm.mjs} +86 -59
  29. package/dist/{error-diagnostics-Cb0P1WLS.mjs → error-diagnostics-C-NMnhrk.mjs} +11 -15
  30. package/dist/{gc-BuL1Genr.mjs → gc-DxCL2A46.mjs} +478 -33
  31. package/dist/{gradle-CmdOBq1d.mjs → gradle-D7-L2Th3.mjs} +4 -4
  32. package/dist/{guide-cCSGRFd3.mjs → guide-DjrTQGik.mjs} +2198 -566
  33. package/dist/{guide-status-CIoWK6mG.mjs → guide-status-Du-ZQ3FD.mjs} +1 -1
  34. package/dist/hosted-android-DwLS4Mse.mjs +32 -0
  35. package/dist/hosted-client-wk1W_CNW.mjs +196 -0
  36. package/dist/hosted-ios-aMo7Cljf.mjs +15 -0
  37. package/dist/hosted-logs-DqYmWtXc.mjs +187 -0
  38. package/dist/hosted-macos-dlHXEeF5.mjs +135 -0
  39. package/dist/hosted-native-C9xJ9rpA.mjs +255 -0
  40. package/dist/{build-progress-C2Jv1AtA.mjs → idle-B2m-Ovod.mjs} +300 -9
  41. package/dist/{idle-shutdown-B5LPaNyY.mjs → idle-shutdown-_3oOV1V_.mjs} +25 -17
  42. package/dist/{in-use-CndPMilN.mjs → in-use-CvGOUn-x.mjs} +30 -10
  43. package/dist/ios-Cr1h5gnO.mjs +189 -0
  44. package/dist/{ios-Chob8ny7.mjs → ios-DE6duQoz.mjs} +819 -460
  45. package/dist/{ios-3CtCRhwr.mjs → ios-DSfnPfse.mjs} +4 -4
  46. package/dist/{ios-device-DERmMmk-.mjs → ios-device-pAN4t54-.mjs} +1 -1
  47. package/dist/ios-state-8L3lgum6.mjs +89 -0
  48. package/dist/{launch-verify-CLlofxRt.mjs → launch-verify-lTnWZikR.mjs} +3 -3
  49. package/dist/logs-DUoKlDoS.mjs +384 -0
  50. package/dist/{client-C2IS36Qf.mjs → machines-C2XiNwh9.mjs} +330 -37
  51. package/dist/{macos-BwtbUtTN.mjs → macos-32po3kUq.mjs} +23 -18
  52. package/dist/macos-run.mjs +1 -1
  53. package/dist/maintenance-run.d.mts +4 -0
  54. package/dist/maintenance-run.mjs +254 -0
  55. package/dist/{metro-Di8EN2Tb.mjs → metro-g8NqiJp-.mjs} +1 -1
  56. package/dist/metro-gateway-DmslUM30.mjs +103 -0
  57. package/dist/{named-ports-Dj_XBNaL.mjs → named-ports-C8m8EQs2.mjs} +2 -2
  58. package/dist/{native-run-DFDoIRbY.mjs → native-run-D1Q56rAD.mjs} +17 -6
  59. package/dist/{native-runtime-i6OkVg-w.mjs → native-runtime-DhJI3IVQ.mjs} +6 -6
  60. package/dist/{ndjson-CDY9IkVC.mjs → ndjson-PclOIwVY.mjs} +2 -2
  61. package/dist/offload-worker.d.mts +1 -0
  62. package/dist/offload-worker.mjs +13 -8
  63. package/dist/{teardown-BYuQafiA.mjs → ownership-BMLqyBgJ.mjs} +639 -53
  64. package/dist/{ownership-BkYxUtuj.mjs → ownership-DyRQru0q.mjs} +2 -2
  65. package/dist/{ports-DddwttKM.mjs → ports-4nYqxPWC.mjs} +2 -2
  66. package/dist/{prebuild-BvxDlD6C.mjs → prebuild-D7RUMkwN.mjs} +4 -4
  67. package/dist/preview-CxPRIEht.mjs +318 -0
  68. package/dist/{project-CV3tDFHl.mjs → project-D47Rh0ly.mjs} +72 -3
  69. package/dist/{recordings-GEXp4iBT.mjs → recordings-BNgsD1Tr.mjs} +1 -1
  70. package/dist/{reload-ChPiwvot.mjs → reload-qZei6iWn.mjs} +77 -13
  71. package/dist/{remote-cache-CO2elm_b.mjs → remote-cache-Ci4DQYYj.mjs} +4 -4
  72. package/dist/{run-B-xqRrsg.mjs → run-CkE9y0nc.mjs} +2 -2
  73. package/dist/{server-bare-X7PUgizL.mjs → server-bare-BkrSxSsZ.mjs} +1 -1
  74. package/dist/{server-expo-DhtX6bom.mjs → server-expo-CHTyXD0G.mjs} +4 -4
  75. package/dist/server-expo-CeakhI8P.mjs +2 -0
  76. package/dist/settings-BBpMcDbD.mjs +25 -0
  77. package/dist/settings-Cfd-0r2d.mjs +1440 -0
  78. package/dist/{settings-DbL6uhH8.mjs → settings-SR9OmW9h.mjs} +10 -4
  79. package/dist/settings.schema.json +472 -50
  80. package/dist/{simslim-Ciluxlq4.mjs → simslim-BWNrc3M3.mjs} +3 -3
  81. package/dist/{slot-launch-BY0BrOLh.mjs → slot-launch-DmjBmpQV.mjs} +10 -7
  82. package/dist/{spawn-entry-DncpJvWL.mjs → spawn-entry-jw19m8Nl.mjs} +1 -0
  83. package/dist/{start-CPO4wRg7.mjs → start-BopZcr4n.mjs} +1 -1
  84. package/dist/{start-CUD9nasA.mjs → start-uee1wWjY.mjs} +21 -17
  85. package/dist/{state-CaeaHbDA.mjs → state-BKa8wNN5.mjs} +1 -1
  86. package/dist/{state-BxCgeTQu.mjs → state-DyaNTgfR.mjs} +2 -2
  87. package/dist/{state-DFm77wRn.mjs → state-PeQiJuIu.mjs} +5 -5
  88. package/dist/{stats-DBLLFmKd.mjs → stats-Bq4fJOdB.mjs} +19 -5
  89. package/dist/{status-l84Gbvp2.mjs → status-CzS4bPO9.mjs} +5 -3
  90. package/dist/status-DlKvVJBE.mjs +49 -0
  91. package/dist/{status-DYXxNOT0.mjs → status-dXX2JX1C.mjs} +244 -50
  92. package/dist/{stop-CceYh1yG.mjs → stop-BCwvvs7F.mjs} +2 -2
  93. package/dist/{stop-Bad61z_2.mjs → stop-BjizrQwv.mjs} +83 -21
  94. package/dist/{stop-Bvf1MflS.mjs → stop-Cr1d8DSE.mjs} +4 -4
  95. package/dist/stop-cause-rhmGfwvt.mjs +97 -0
  96. package/dist/supervisor-run.d.mts +7 -4
  97. package/dist/supervisor-run.mjs +156 -27
  98. package/dist/{support-WLOzVKUv.mjs → support-BJPgYCu-.mjs} +61 -11
  99. package/dist/{support-CDQbDkL5.mjs → support-F9-sr0Oi.mjs} +6 -5
  100. package/dist/{toolchain-DmeYjXD9.mjs → toolchain-CNIBTIOv.mjs} +11 -11
  101. package/dist/trigger-Bemu8ytw.mjs +65 -0
  102. package/dist/trigger-CXts6qt1.mjs +2 -0
  103. package/dist/{warm-progress-DVHLN4uC.mjs → warm-progress-DF4JRvYI.mjs} +274 -5
  104. package/dist/watchman-APYjXSQa.mjs +48 -0
  105. package/dist/{web-k6w8-HnE.mjs → web-exQtGkoc.mjs} +14 -16
  106. package/dist/web-run.mjs +5 -5
  107. package/dist/{workspace-process-lock-BL7Q0HCO.mjs → workspace-process-lock-2mNzOlaB.mjs} +3 -2
  108. package/dist/{workspace-state-CL2V2Ons.mjs → workspace-state-DqcCbpE7.mjs} +34 -34
  109. package/dist/workspaces-Bcib97-6.mjs +306 -0
  110. package/dist/{worktree-BT_uULFx.mjs → worktree-Dc98cE6_.mjs} +1 -1
  111. package/dist/{worktree-qXKzADSC.mjs → worktree-ZpZR3S1k.mjs} +601 -33
  112. package/package.json +5 -5
  113. package/dist/device-capacity-DMuKC7nx.mjs +0 -136
  114. package/dist/hosted-macos-DwowNU7W.mjs +0 -447
  115. package/dist/logs-h9h6MUvE.mjs +0 -568
  116. package/dist/machine-usage-D_YYMzfR.mjs +0 -283
  117. package/dist/machines-CYtOCqcj.mjs +0 -259
  118. package/dist/merge-state-BnxLNQiM.mjs +0 -272
  119. package/dist/server-expo-KJeD109I.mjs +0 -2
  120. package/dist/settings-CduVcoxj.mjs +0 -680
  121. package/dist/stop-cause-CZ_Mr-v9.mjs +0 -665
  122. package/dist/watchman-Bx0wceoF.mjs +0 -238
  123. package/dist/worktree-C0c5Udwa.mjs +0 -753
@@ -1,10 +1,10 @@
1
1
  import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
2
  import { t as process_identity_exports } from "./process-identity-BmRm1qV0.mjs";
3
- import { C as workspaceLogsDir, r as readWorkspaceState$1 } from "./workspace-state-CL2V2Ons.mjs";
4
- import { i as listLeaseFiles, r as leaseIsExpired } from "./device-lease-W5Pi1q38.mjs";
3
+ import { r as readWorkspaceState$1, x as workspaceLogsDir$1 } from "./workspace-state-DqcCbpE7.mjs";
4
+ import { i as listLeaseFiles, r as leaseIsExpired } from "./device-lease-DpoS36_u.mjs";
5
5
  import { closeSync, fstatSync, openSync, readFileSync, readSync, readdirSync, statSync } from "node:fs";
6
6
  import { basename, join, resolve } from "node:path";
7
- import { readViewedDevices } from "@stim-cli/core/state";
7
+ import { agentDeviceStateDirs, readViewedDevices } from "@stim-cli/core/state";
8
8
  import { homedir } from "node:os";
9
9
  //#region src/devices/automation-tools.ts
10
10
  const AUTOMATION_TOOLS = [
@@ -182,12 +182,14 @@ function parseAgentDeviceRecord(kind, path, raw) {
182
182
  deviceId,
183
183
  session: kind === "claim" && typeof entry?.session === "string" && entry.session ? entry.session : null,
184
184
  workspace: kind === "claim" && typeof entry?.workspace === "string" && entry.workspace ? entry.workspace : null,
185
+ stateDir: kind === "claim" && typeof entry?.stateDir === "string" && entry.stateDir ? entry.stateDir : null,
185
186
  deviceName: kind === "claim" && typeof device?.name === "string" ? device.name : null,
186
187
  readable: Boolean(entry && deviceId),
187
188
  owner: entry ? field(entry, "ownerPid", "ownerStartTime") : null,
188
189
  runner: entry && kind === "runner-lease" ? field(entry, "runnerPid", "runnerStartTime") : null,
189
190
  claimProtocol: kind === "runner-lease" && entry?.deviceClaimProtocol === 1,
190
- createdAtMs
191
+ createdAtMs,
192
+ xctestrunPath: typeof entry?.xctestrunPath === "string" ? entry.xctestrunPath : null
191
193
  };
192
194
  }
193
195
  function processMatches(entry, startOf) {
@@ -198,6 +200,17 @@ function processMatches(entry, startOf) {
198
200
  if (!Number.isFinite(recorded)) return "unknown";
199
201
  return Math.floor(start.startedAtMs / 1e3) === Math.floor(recorded / 1e3) ? "live" : "dead";
200
202
  }
203
+ function agentDeviceDaemon(stateDir, startOf = process_identity_exports.inspectProcessStart) {
204
+ let raw;
205
+ try {
206
+ raw = readFileSync(join(stateDir, "daemon.json"), "utf8");
207
+ } catch (error) {
208
+ return error.code === "ENOENT" ? "none" : "unknown";
209
+ }
210
+ const entry = parseObject(raw);
211
+ const owner = entry && field(entry, "pid", "processStartTime");
212
+ return owner ? processMatches(owner, startOf) : "unknown";
213
+ }
201
214
  function agentDeviceLiveness(record, startOf) {
202
215
  const results = (record.kind === "claim" ? [record.owner] : [record.owner, record.runner]).map((entry) => entry ? processMatches(entry, startOf) : "unknown");
203
216
  if (results.includes("dead")) return "dead";
@@ -291,13 +304,13 @@ function agentDeviceDirs(home) {
291
304
  * When an agent-device session last recorded an event: its events file's modification time. agent-device names a
292
305
  * session's directory after the session with every character outside `[a-zA-Z0-9._-]` replaced by `_`.
293
306
  */
294
- function agentSessionActedAt(home, session) {
295
- const root = envDir("AGENT_DEVICE_STATE_DIR") ?? join(home, ".agent-device");
296
- try {
297
- return statSync(join(root, "sessions", session.replaceAll(/[^a-zA-Z0-9._-]/g, "_"), "events.ndjson")).mtimeMs;
298
- } catch {
299
- return null;
300
- }
307
+ function agentSessionActedAt(home, session, stateDir) {
308
+ let latest = null;
309
+ for (const root of stateDir ? [stateDir] : agentDeviceStateDirs(home)) try {
310
+ const at = statSync(join(root, "sessions", session.replaceAll(/[^a-zA-Z0-9._-]/g, "_"), "events.ndjson")).mtimeMs;
311
+ latest = latest === null ? at : Math.max(latest, at);
312
+ } catch {}
313
+ return latest;
301
314
  }
302
315
  function readAgentDeviceRecords(home) {
303
316
  return agentDeviceDirs(home).flatMap(({ kind, dir }) => {
@@ -377,6 +390,26 @@ function createDeviceProcessTables() {
377
390
  }
378
391
  };
379
392
  }
393
+ function workspaceDeviceRecency(root) {
394
+ const recency = [];
395
+ const usedAt = Date.parse(String(readWorkspaceState$1(root)?.lastUsedAt ?? ""));
396
+ if (Number.isFinite(usedAt)) recency.push({
397
+ basis: "workspace-use",
398
+ at: usedAt
399
+ });
400
+ for (const file of ["client.ndjson", "device.ndjson"]) try {
401
+ recency.push({
402
+ basis: "device-log",
403
+ at: statSync(join(workspaceLogsDir$1(root), file)).mtimeMs
404
+ });
405
+ } catch {}
406
+ return recency;
407
+ }
408
+ /** The newest workspace use or change to its in-app or native device logs. */
409
+ function workspaceDeviceLastUseAt(root) {
410
+ const times = workspaceDeviceRecency(root).map(({ at }) => at).filter(Number.isFinite);
411
+ return times.length ? Math.max(...times) : NaN;
412
+ }
380
413
  function createActivityReader({ now = Date.now(), home = homedir(), startOf = process_identity_exports.inspectProcessStart, leaseFiles = listLeaseFiles, tables = createDeviceProcessTables(), viewers = readViewedDevices } = {}) {
381
414
  let agentDevice;
382
415
  let stimLeases;
@@ -407,7 +440,7 @@ function createActivityReader({ now = Date.now(), home = homedir(), startOf = pr
407
440
  pid: record.owner?.pid ?? null,
408
441
  since: record.createdAtMs !== null ? new Date(record.createdAtMs).toISOString() : null
409
442
  });
410
- const actedAt = record.session ? agentSessionActedAt(home, record.session) : null;
443
+ const actedAt = record.session ? agentSessionActedAt(home, record.session, record.stateDir) : null;
411
444
  if (actedAt !== null) evidence.recency.push({
412
445
  basis: "agent-action",
413
446
  at: actedAt
@@ -458,7 +491,7 @@ function createActivityReader({ now = Date.now(), home = homedir(), startOf = pr
458
491
  });
459
492
  }
460
493
  if (target.workspace) {
461
- const dir = workspaceLogsDir(target.workspace);
494
+ const dir = workspaceLogsDir$1(target.workspace);
462
495
  const deviceAt = latestDeviceLogAt(log(join(dir, "device.ndjson")) ?? [], target);
463
496
  if (deviceAt !== null) evidence.recency.push({
464
497
  basis: "device-log",
@@ -479,7 +512,7 @@ function createActivityReader({ now = Date.now(), home = homedir(), startOf = pr
479
512
  };
480
513
  }
481
514
  function workspaceActivity(workspace, now = Date.now()) {
482
- const dir = workspaceLogsDir(workspace);
515
+ const dir = workspaceLogsDir$1(workspace);
483
516
  const state = readWorkspaceState$1(workspace);
484
517
  const recency = [];
485
518
  const deviceAt = latestRecordAt(tailLines(join(dir, "device.ndjson")) ?? [], (record) => record.src === "device" && isDeviceRecord(record));
@@ -579,7 +612,7 @@ function readWebActivity(target, tables, now = Date.now()) {
579
612
  ...driver,
580
613
  basis: "cdp-client"
581
614
  });
582
- const lines = tailLines(join(workspaceLogsDir(target.workspace), "web.ndjson")) ?? [];
615
+ const lines = tailLines(join(workspaceLogsDir$1(target.workspace), "web.ndjson")) ?? [];
583
616
  const actionAt = latestRecordAt(lines, (record) => record.src === "agent");
584
617
  if (actionAt !== null) evidence.recency.push({
585
618
  basis: "agent-action",
@@ -593,4 +626,4 @@ function readWebActivity(target, tables, now = Date.now()) {
593
626
  return classifyActivity(evidence, now);
594
627
  }
595
628
  //#endregion
596
- export { probeWebDrivers as a, tailLines as c, idleForMs as i, workspaceActivity as l, createActivityReader as n, readAgentDeviceRecords as o, createDeviceProcessTables as r, readWebActivity as s, agentDeviceLiveness as t };
629
+ export { envDir as a, probeWebDrivers as c, readWebActivity as d, tailLines as f, createDeviceProcessTables as i, processMatches as l, workspaceDeviceLastUseAt as m, agentDeviceLiveness as n, idleForMs as o, workspaceActivity as p, createActivityReader as r, parseAgentDeviceRecord as s, agentDeviceDaemon as t, readAgentDeviceRecords as u };
@@ -0,0 +1,411 @@
1
+ import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
+ import { j as formatBytes } from "./config-D0DbpZtM.mjs";
3
+ import { t as process_identity_exports } from "./process-identity-BmRm1qV0.mjs";
4
+ import { o as formatLongDuration } from "./command-output-CDAIFD-8.mjs";
5
+ import { a as envDir, l as processMatches, s as parseAgentDeviceRecord } from "./activity-IRvJY_ru.mjs";
6
+ import { c as listWorkspaceDirs } from "./workspaces-Bcib97-6.mjs";
7
+ import { randomUUID } from "node:crypto";
8
+ import { existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
9
+ import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
10
+ import { configDir } from "@stim-cli/core";
11
+ import { agentDeviceUsageFile, readAgentDeviceUsageCache, readSwiftpmCacheUsage, swiftpmCacheUsageFile } from "@stim-cli/core/state";
12
+ import { homedir } from "node:os";
13
+ import chalk from "chalk";
14
+ //#region src/devices/report-only-usage.ts
15
+ function canonical(path) {
16
+ try {
17
+ return realpathSync.native(path);
18
+ } catch {
19
+ return resolve(path);
20
+ }
21
+ }
22
+ function parseDu(output) {
23
+ const sizes = /* @__PURE__ */ new Map();
24
+ for (const line of output.split("\n")) {
25
+ const match = /^(\d+)\t(.+?)\r?$/.exec(line);
26
+ if (match) sizes.set(resolve(match[2]), Number(match[1]) * 1024);
27
+ }
28
+ return sizes;
29
+ }
30
+ async function measureDu(args) {
31
+ let output = "";
32
+ let complete = true;
33
+ try {
34
+ output = await getExecutor().runFileAsync("du", args, { timeoutMs: 2e4 });
35
+ } catch (error) {
36
+ complete = false;
37
+ const failure = error;
38
+ if (failure.status === 1 && !failure.code && typeof failure.stdout === "string") output = failure.stdout;
39
+ }
40
+ return {
41
+ sizes: parseDu(output),
42
+ complete
43
+ };
44
+ }
45
+ function writeUsageCache(file, value) {
46
+ const temporary = `${file}.${randomUUID()}.tmp`;
47
+ try {
48
+ mkdirSync(dirname(file), { recursive: true });
49
+ writeFileSync(temporary, JSON.stringify(value));
50
+ renameSync(temporary, file);
51
+ } catch {
52
+ try {
53
+ rmSync(temporary, { force: true });
54
+ } catch {}
55
+ }
56
+ }
57
+ //#endregion
58
+ //#region src/devices/swiftpm-cache-usage.ts
59
+ async function getSwiftpmCacheUsage({ maxAgeMs = 6e5 } = {}) {
60
+ const dir = canonical(process.platform === "darwin" ? join(homedir(), "Library", "Caches", "org.swift.swiftpm") : join(process.env.XDG_CACHE_HOME || join(homedir(), ".cache"), "org.swift.swiftpm"));
61
+ const cached = readSwiftpmCacheUsage();
62
+ const age = cached ? Date.now() - Date.parse(cached.measuredAt) : Infinity;
63
+ if (cached && cached.dir === dir && age >= 0 && age < maxAgeMs) return cached;
64
+ const present = existsSync(dir);
65
+ const result = present ? await measureDu(["-sk", dir]) : null;
66
+ const bytes = present ? result?.complete ? result.sizes.get(dir) ?? null : null : 0;
67
+ const usage = {
68
+ version: 1,
69
+ measuredAt: (/* @__PURE__ */ new Date()).toISOString(),
70
+ dir,
71
+ present,
72
+ bytes,
73
+ complete: bytes !== null
74
+ };
75
+ writeUsageCache(swiftpmCacheUsageFile(), usage);
76
+ return usage;
77
+ }
78
+ //#endregion
79
+ //#region src/devices/agent-device-usage.ts
80
+ const MAX_AGE_MS = 6e5;
81
+ function contains(parent, path) {
82
+ const rel = relative(parent, path);
83
+ return rel === "" || rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute(rel);
84
+ }
85
+ function parseRunnerMetadata(raw) {
86
+ let value = null;
87
+ try {
88
+ value = raw === null ? null : JSON.parse(raw);
89
+ } catch {}
90
+ return {
91
+ packageVersion: typeof value?.packageVersion === "string" ? value.packageVersion : null,
92
+ xcodeBuildVersion: typeof value?.xcodeBuildVersion === "string" ? value.xcodeBuildVersion : null
93
+ };
94
+ }
95
+ function runnerLastUsedAt(plist, mtimeMs) {
96
+ const match = plist?.match(/<key>LastAccessedDate<\/key>\s*<date>([^<]+)<\/date>/);
97
+ const date = match ? Date.parse(match[1]) : NaN;
98
+ const at = Number.isFinite(date) ? date : mtimeMs;
99
+ return at !== null && Number.isFinite(at) ? new Date(at).toISOString() : null;
100
+ }
101
+ function runnerInUse(dir, leases, unreadable, lock, startOf) {
102
+ if (unreadable || leases.some((lease) => !lease.readable)) return {
103
+ inUse: true,
104
+ inUseReason: "unreadable"
105
+ };
106
+ for (const lease of leases) {
107
+ if (!lease.xctestrunPath || !contains(dir, lease.xctestrunPath)) continue;
108
+ const liveness = [lease.owner, lease.runner].flatMap((process) => process ? [processMatches(process, startOf)] : []);
109
+ if (liveness.includes("live")) return {
110
+ inUse: true,
111
+ inUseReason: "lease"
112
+ };
113
+ if (liveness.includes("unknown")) return {
114
+ inUse: true,
115
+ inUseReason: "unreadable"
116
+ };
117
+ }
118
+ if (lock === "unreadable") return {
119
+ inUse: true,
120
+ inUseReason: "unreadable"
121
+ };
122
+ if (lock && processMatches(lock, startOf) !== "dead") return {
123
+ inUse: true,
124
+ inUseReason: "lock"
125
+ };
126
+ return {
127
+ inUse: false,
128
+ inUseReason: null
129
+ };
130
+ }
131
+ function subtract(total, parts) {
132
+ return total === null || parts.some((part) => part === null) ? null : Math.max(0, total - parts.reduce((sum, part) => sum + part, 0));
133
+ }
134
+ function largest(entries) {
135
+ return entries.toSorted((a, b) => (b.bytes ?? -1) - (a.bytes ?? -1));
136
+ }
137
+ function assembleAgentDeviceUsage(tree, sizes, complete, measuredAt) {
138
+ const size = (dir) => tree.absent.has(dir) ? 0 : sizes.get(dir) ?? null;
139
+ const { stateDir, runnerRoot } = tree.roots;
140
+ const sessions = join(stateDir, "sessions");
141
+ const logs = join(stateDir, "logs");
142
+ const runnerBytes = tree.runnerPresent ? size(runnerRoot) : 0;
143
+ const platforms = largest(tree.platforms.map((platform) => ({
144
+ ...platform,
145
+ bytes: size(platform.dir),
146
+ entries: largest(platform.entries.map((entry) => ({
147
+ ...entry,
148
+ bytes: size(entry.dir)
149
+ })))
150
+ })));
151
+ const stateBytes = tree.statePresent ? size(stateDir) : 0;
152
+ const sessionsBytes = tree.statePresent ? size(sessions) : 0;
153
+ const logsBytes = tree.statePresent ? size(logs) : 0;
154
+ const workspaces = largest(tree.workspaces.map((entry) => ({
155
+ ...entry,
156
+ bytes: size(entry.dir)
157
+ })));
158
+ const hosted = tree.hosted ? {
159
+ ...tree.hosted,
160
+ bytes: size(tree.hosted.dir)
161
+ } : null;
162
+ return {
163
+ version: 1,
164
+ measuredAt,
165
+ complete,
166
+ bytes: (stateBytes ?? 0) + (contains(stateDir, runnerRoot) ? 0 : runnerBytes ?? 0) + workspaces.reduce((sum, entry) => sum + (entry.bytes ?? 0), 0) + (hosted?.bytes ?? 0),
167
+ stateDir: {
168
+ dir: stateDir,
169
+ present: tree.statePresent,
170
+ bytes: stateBytes,
171
+ sessions: {
172
+ dir: sessions,
173
+ bytes: sessionsBytes,
174
+ count: tree.sessionsCount
175
+ },
176
+ logs: {
177
+ dir: logs,
178
+ bytes: logsBytes
179
+ },
180
+ other: {
181
+ bytes: subtract(stateBytes, [
182
+ sessionsBytes,
183
+ logsBytes,
184
+ ...contains(stateDir, runnerRoot) ? [runnerBytes] : []
185
+ ]),
186
+ largest: largest(tree.otherChildren.map((entry) => ({
187
+ ...entry,
188
+ bytes: sizes.get(join(stateDir, entry.name)) ?? entry.bytes
189
+ }))).slice(0, 5)
190
+ }
191
+ },
192
+ runnerBuilds: {
193
+ dir: runnerRoot,
194
+ present: tree.runnerPresent,
195
+ bytes: runnerBytes,
196
+ sharedBytes: tree.runnerPresent ? subtract(size(join(runnerRoot, "derived")), platforms.map((entry) => entry.bytes)) : 0,
197
+ platforms
198
+ },
199
+ workspaces,
200
+ hosted
201
+ };
202
+ }
203
+ function readText(path) {
204
+ try {
205
+ return readFileSync(path, "utf8");
206
+ } catch {
207
+ return null;
208
+ }
209
+ }
210
+ function children(dir) {
211
+ try {
212
+ return readdirSync(dir);
213
+ } catch {
214
+ return [];
215
+ }
216
+ }
217
+ function directories(dir) {
218
+ try {
219
+ return readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
220
+ } catch {
221
+ return [];
222
+ }
223
+ }
224
+ function workspaceAgentDeviceDir(dir) {
225
+ return join(dir, "agent-device");
226
+ }
227
+ function readRunnerLock(dir) {
228
+ if (!existsSync(dir)) return null;
229
+ try {
230
+ const owner = JSON.parse(readFileSync(join(dir, "owner.json"), "utf8"));
231
+ if (owner && typeof owner === "object" && !Array.isArray(owner) && "pid" in owner && typeof owner.pid === "number" && Number.isInteger(owner.pid) && owner.pid > 0 && "startTime" in owner && typeof owner.startTime === "string" && Number.isFinite(Date.parse(owner.startTime))) return {
232
+ pid: owner.pid,
233
+ startTime: owner.startTime
234
+ };
235
+ } catch {}
236
+ return "unreadable";
237
+ }
238
+ function discover() {
239
+ const runnerRoot = canonical(join(homedir(), ".agent-device", "apple-runner"));
240
+ const hostedDir = canonical(join(configDir(), "server", "agent-device"));
241
+ const workspaceDirs = listWorkspaceDirs().map(({ dir, projectRoot }) => ({
242
+ dir: canonical(workspaceAgentDeviceDir(dir)),
243
+ projectRoot,
244
+ bytes: null
245
+ }));
246
+ const override = envDir("AGENT_DEVICE_STATE_DIR");
247
+ const candidate = override ? canonical(override) : null;
248
+ const overlaps = candidate && [...workspaceDirs.map(({ dir }) => dir), hostedDir].some((dir) => contains(dir, candidate) || contains(candidate, dir));
249
+ const stateDir = candidate && !overlaps ? candidate : canonical(join(homedir(), ".agent-device"));
250
+ const workspaces = workspaceDirs.filter(({ dir }) => existsSync(dir));
251
+ const roots = {
252
+ stateDir,
253
+ runnerRoot,
254
+ hostedDir,
255
+ workspaceDirs: workspaces.map(({ dir }) => dir).toSorted()
256
+ };
257
+ const leaseDir = envDir("AGENT_DEVICE_IOS_RUNNER_LEASE_DIR") ?? join(runnerRoot, "leases");
258
+ let leases = [];
259
+ let unreadable = false;
260
+ try {
261
+ leases = readdirSync(leaseDir).filter((name) => name.endsWith(".json")).map((name) => {
262
+ const path = join(leaseDir, name);
263
+ const record = parseAgentDeviceRecord("runner-lease", path, readText(path));
264
+ if (record.xctestrunPath) record.xctestrunPath = canonical(record.xctestrunPath);
265
+ return record;
266
+ });
267
+ } catch (error) {
268
+ unreadable = error.code !== "ENOENT";
269
+ }
270
+ const starts = /* @__PURE__ */ new Map();
271
+ const startOf = (pid) => {
272
+ if (!starts.has(pid)) starts.set(pid, (0, process_identity_exports.inspectProcessStart)(pid));
273
+ return starts.get(pid);
274
+ };
275
+ const derived = join(runnerRoot, "derived");
276
+ const platforms = directories(derived).flatMap((platform) => {
277
+ const dir = join(derived, platform);
278
+ const entries = directories(dir).filter((name) => name.startsWith("cache-") && !name.endsWith(".lock")).map((name) => {
279
+ const entryDir = join(dir, name);
280
+ let mtime = null;
281
+ try {
282
+ mtime = statSync(entryDir).mtimeMs;
283
+ } catch {}
284
+ return Object.assign({
285
+ name,
286
+ dir: entryDir,
287
+ bytes: null,
288
+ lastUsedAt: runnerLastUsedAt(readText(join(entryDir, "info.plist")), mtime)
289
+ }, parseRunnerMetadata(readText(join(entryDir, ".agent-device-runner-cache.json"))), runnerInUse(canonical(entryDir), leases, unreadable, readRunnerLock(`${entryDir}.lock`), startOf));
290
+ });
291
+ return entries.length ? [{
292
+ platform,
293
+ dir,
294
+ bytes: null,
295
+ entries
296
+ }] : [];
297
+ });
298
+ const statePresent = existsSync(stateDir);
299
+ const runnerPresent = existsSync(runnerRoot);
300
+ const absent = new Set([
301
+ join(stateDir, "sessions"),
302
+ join(stateDir, "logs"),
303
+ derived
304
+ ].filter((dir) => !existsSync(dir)));
305
+ const otherChildren = children(stateDir).filter((name) => ![
306
+ "sessions",
307
+ "logs",
308
+ "apple-runner"
309
+ ].includes(name)).map((name) => {
310
+ let bytes = null;
311
+ try {
312
+ const stat = statSync(join(stateDir, name));
313
+ if (stat.isFile() && typeof stat.blocks === "number") bytes = stat.blocks * 512;
314
+ } catch {}
315
+ return {
316
+ name,
317
+ bytes
318
+ };
319
+ });
320
+ return {
321
+ roots,
322
+ statePresent,
323
+ runnerPresent,
324
+ sessionsCount: directories(join(stateDir, "sessions")).length,
325
+ otherChildren,
326
+ platforms,
327
+ workspaces,
328
+ hosted: existsSync(hostedDir) ? {
329
+ dir: hostedDir,
330
+ bytes: null,
331
+ sessions: directories(hostedDir).length
332
+ } : null,
333
+ absent
334
+ };
335
+ }
336
+ async function getAgentDeviceUsage({ maxAgeMs = MAX_AGE_MS } = {}) {
337
+ const tree = discover();
338
+ const cached = readAgentDeviceUsageCache();
339
+ const age = cached ? Date.now() - Date.parse(cached.usage.measuredAt) : Infinity;
340
+ if (cached && age >= 0 && age < maxAgeMs && JSON.stringify(cached.roots) === JSON.stringify(tree.roots)) return cached.usage;
341
+ const sizes = /* @__PURE__ */ new Map();
342
+ let complete = true;
343
+ const measure = async (args) => {
344
+ const result = await measureDu(args);
345
+ complete &&= result.complete;
346
+ for (const [dir, bytes] of result.sizes) sizes.set(dir, bytes);
347
+ };
348
+ if (tree.statePresent) await measure([
349
+ "-k",
350
+ "-d",
351
+ "4",
352
+ tree.roots.stateDir
353
+ ]);
354
+ if (tree.runnerPresent && !contains(tree.roots.stateDir, tree.roots.runnerRoot)) await measure([
355
+ "-k",
356
+ "-d",
357
+ "4",
358
+ tree.roots.runnerRoot
359
+ ]);
360
+ const extra = [...tree.roots.workspaceDirs, ...tree.hosted ? [tree.hosted.dir] : []];
361
+ if (extra.length) await measure(["-sk", ...extra]);
362
+ const usage = assembleAgentDeviceUsage(tree, sizes, complete, (/* @__PURE__ */ new Date()).toISOString());
363
+ writeUsageCache(agentDeviceUsageFile(), {
364
+ ...usage,
365
+ roots: tree.roots
366
+ });
367
+ return usage;
368
+ }
369
+ //#endregion
370
+ //#region src/devices/swiftpm-cache-usage-output.ts
371
+ function swiftpmCacheLines(usage, now = Date.now()) {
372
+ if (!usage?.present) return [];
373
+ return [
374
+ `SwiftPM cache (${usage.bytes === null ? "unknown" : formatBytes(usage.bytes).replace(/([KMG])$/, " $1B")}${usage.complete ? "" : ", incomplete"}) - measured ${formatLongDuration(Math.max(0, now - Date.parse(usage.measuredAt)))} ago`,
375
+ ` ${usage.dir}`,
376
+ chalk.dim(" shared by every SwiftPM build on this machine; Stim reports it and never deletes it")
377
+ ];
378
+ }
379
+ //#endregion
380
+ //#region src/devices/agent-device-usage-output.ts
381
+ const size = (bytes) => bytes === null ? "unknown" : formatBytes(bytes);
382
+ function agentDeviceLines(usage, detailed = false, now = Date.now()) {
383
+ if (!usage || !usage.stateDir.present && !usage.runnerBuilds.present && !usage.workspaces.length && !usage.hosted) return [];
384
+ const lines = [`agent-device (${formatBytes(usage.bytes)}${usage.complete ? "" : ", incomplete"}) - measured ${formatLongDuration(Math.max(0, now - Date.parse(usage.measuredAt)))} ago`];
385
+ lines.push(` runner builds: ${size(usage.runnerBuilds.bytes)}`);
386
+ for (const platform of usage.runnerBuilds.platforms) {
387
+ lines.push(` ${platform.platform}: ${size(platform.bytes)}, ${platform.entries.length} entries, ${platform.entries.filter((entry) => entry.inUse).length} in use`);
388
+ if (detailed) for (const entry of platform.entries) {
389
+ const details = [
390
+ entry.lastUsedAt ? `last use ${entry.lastUsedAt.slice(0, 10)}` : "last use unknown",
391
+ entry.packageVersion ? `agent-device ${entry.packageVersion}` : null,
392
+ entry.xcodeBuildVersion ? `Xcode ${entry.xcodeBuildVersion}` : null,
393
+ entry.inUse ? `in use (${entry.inUseReason})` : null
394
+ ].filter(Boolean);
395
+ lines.push(` ${entry.name}: ${size(entry.bytes)}, ${details.join(", ")}`, ` ${entry.dir}`);
396
+ }
397
+ }
398
+ const { sessions, logs, other } = usage.stateDir;
399
+ lines.push(` sessions: ${size(sessions.bytes)}, ${sessions.count} entries`, ` logs: ${size(logs.bytes)}`, ` other: ${size(other.bytes)}`);
400
+ if (detailed) for (const entry of other.largest) lines.push(` ${entry.name}: ${size(entry.bytes)}`);
401
+ const knownWorkspaceBytes = usage.workspaces.reduce((sum, entry) => sum + (entry.bytes ?? 0), 0);
402
+ if (usage.workspaces.length) {
403
+ lines.push(` workspaces: ${usage.workspaces.length}, ${usage.workspaces.some((entry) => entry.bytes === null) ? "at least " : ""}${formatBytes(knownWorkspaceBytes)}`);
404
+ if (detailed) for (const entry of usage.workspaces) lines.push(` ${entry.dir}: ${size(entry.bytes)}`);
405
+ }
406
+ if (usage.hosted) lines.push(` stim-server hosted: ${size(usage.hosted.bytes)}${detailed ? `, ${usage.hosted.sessions} entries, ${usage.hosted.dir}` : ""}`);
407
+ if (detailed) lines.push(" Stim never trims or deletes the shared runner builds, sessions, logs and other state or the hosted driver dir; a workspace's own agent-device dir goes only with its workspace.", " A lease flag identifies a live owner or runner; a lock flag identifies a live or unverified lock owner, and unreadable flags conservatively keep entries in use.", " Clear the rest with agent-device's own tooling or by removing the directories yourself.");
408
+ return lines;
409
+ }
410
+ //#endregion
411
+ export { getSwiftpmCacheUsage as i, swiftpmCacheLines as n, getAgentDeviceUsage as r, agentDeviceLines as t };