stim 1.13.0 → 1.15.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 (133) hide show
  1. package/README.md +2 -0
  2. package/dist/{activity-DfVKZTEB.mjs → activity-B2-kb7k0.mjs} +281 -41
  3. package/dist/{android-BHfAu2mf.mjs → android-CYmq0jNi.mjs} +2 -2
  4. package/dist/{android-CD8pr8Q2.mjs → android-Cu_L4RpL.mjs} +524 -412
  5. package/dist/{android-qx_31wJo.mjs → android-Db2eEMaf.mjs} +152 -65
  6. package/dist/android-cas-PnKfAdB2.mjs +167 -0
  7. package/dist/android-cas-compiler.mjs +2 -2
  8. package/dist/{app-install-CDFY2nUL.mjs → app-install-CAAplXAt.mjs} +162 -173
  9. package/dist/{budget-E5o91NR1.mjs → budget-CMiqghig.mjs} +1048 -77
  10. package/dist/{build-plan-BTa93o_r.mjs → build-plan-aeGMFX7e.mjs} +193 -650
  11. package/dist/build-progress-C2Jv1AtA.mjs +872 -0
  12. package/dist/build-slots-Df1ApSuH.mjs +127 -0
  13. package/dist/{cache-manifest-DWPF4RKJ.mjs → cache-manifest-DnUHBJaU.mjs} +2 -2
  14. package/dist/cache-manifest.mjs +1 -1
  15. package/dist/{chrome-B5hWgQJS.mjs → chrome-BrcVWQyD.mjs} +4 -4
  16. package/dist/cli-0cX5s4NM.mjs +45 -0
  17. package/dist/cli.mjs +6 -42
  18. package/dist/client-C2IS36Qf.mjs +1333 -0
  19. package/dist/collector-run.mjs +8 -7
  20. package/dist/{command-output-BIL7rlX7.mjs → command-output-CliHHn7a.mjs} +1 -1
  21. package/dist/{config-0jCcMM0E.mjs → config-D0DbpZtM.mjs} +29 -29
  22. package/dist/{created-devices-Cd558h31.mjs → created-devices-BwVb3liH.mjs} +2 -2
  23. package/dist/{dependency-state-DNN0yfx6.mjs → dependency-state-BmsjKZ2H.mjs} +2 -2
  24. package/dist/{deps-2Wo81np9.mjs → deps-BFlKbOIp.mjs} +3 -3
  25. package/dist/{detached-entry-DJHoca49.mjs → detached-entry-CiTEgMR1.mjs} +1 -1
  26. package/dist/dev-client-CjzLoNs0.mjs +80 -0
  27. package/dist/{device-Cf7vl-so.mjs → device-BZ1IyS5A.mjs} +7 -7
  28. package/dist/device-capacity-DMuKC7nx.mjs +136 -0
  29. package/dist/device-host-worker.d.mts +1 -0
  30. package/dist/device-host-worker.mjs +769 -0
  31. package/dist/device-ios-DMQBUPus.mjs +381 -0
  32. package/dist/{device-lease-CL9dZf73.mjs → device-lease-W5Pi1q38.mjs} +43 -36
  33. package/dist/{device-pool-MuoiyRcu.mjs → device-pool-B2r38YjS.mjs} +4 -4
  34. package/dist/{device-remote-DT9ngZiD.mjs → device-remote-9ulPsdhY.mjs} +68 -162
  35. package/dist/{doctor-D2iysNNH.mjs → doctor-BcE5xsBn.mjs} +191 -99
  36. package/dist/{error-diagnostics-BoiOhck8.mjs → error-diagnostics-Cb0P1WLS.mjs} +12 -11
  37. package/dist/{exec-DK8KaVHK.mjs → exec-CaErVv7t.mjs} +5 -1
  38. package/dist/{gc-eA-PRBX8.mjs → gc-BuL1Genr.mjs} +430 -102
  39. package/dist/{android-cas-B9Kv9aIy.mjs → gradle-CmdOBq1d.mjs} +8 -294
  40. package/dist/{guide-BfZ0OmiP.mjs → guide-cCSGRFd3.mjs} +2052 -240
  41. package/dist/{guide-status-BaMYu55M.mjs → guide-status-CIoWK6mG.mjs} +3 -3
  42. package/dist/hosted-macos-DwowNU7W.mjs +447 -0
  43. package/dist/idle-shutdown-B5LPaNyY.mjs +122 -0
  44. package/dist/in-use-CndPMilN.mjs +110 -0
  45. package/dist/{ios-CmEzZiOj.mjs → ios-3CtCRhwr.mjs} +201 -10
  46. package/dist/{ios-CcfCeZ19.mjs → ios-Chob8ny7.mjs} +560 -259
  47. package/dist/{ios-device-BW-t_Lkv.mjs → ios-device-DERmMmk-.mjs} +12 -7
  48. package/dist/{ios-device-BiOsSVzd.mjs → ios-device-Ee7sDXfp.mjs} +1 -1
  49. package/dist/{launch-verify-CuJaBpEa.mjs → launch-verify-CLlofxRt.mjs} +42 -11
  50. package/dist/{logs-DcSRbC2t.mjs → logs-h9h6MUvE.mjs} +95 -13
  51. package/dist/machine-usage-D_YYMzfR.mjs +283 -0
  52. package/dist/machines-CYtOCqcj.mjs +259 -0
  53. package/dist/macos-BwtbUtTN.mjs +468 -0
  54. package/dist/macos-run.d.mts +5 -0
  55. package/dist/macos-run.mjs +2 -0
  56. package/dist/{merge-state-ytFQCeh4.mjs → merge-state-BnxLNQiM.mjs} +22 -12
  57. package/dist/{metro-BPeY1YMh.mjs → metro-Di8EN2Tb.mjs} +20 -120
  58. package/dist/{named-ports-n7tnJsI8.mjs → named-ports-Dj_XBNaL.mjs} +8 -64
  59. package/dist/native-run-DFDoIRbY.mjs +147 -0
  60. package/dist/{native-runtime-CiiFHt0e.mjs → native-runtime-i6OkVg-w.mjs} +11 -6
  61. package/dist/offload-worker.d.mts +121 -0
  62. package/dist/offload-worker.mjs +601 -0
  63. package/dist/{ownership-Bz2kETAS.mjs → ownership-BkYxUtuj.mjs} +3 -2
  64. package/dist/{page-BTu7I60K.mjs → page-QF_Ke10s.mjs} +7 -2
  65. package/dist/{ports-C5UutqpJ.mjs → ports-DddwttKM.mjs} +3 -3
  66. package/dist/prebuild-BvxDlD6C.mjs +213 -0
  67. package/dist/process-identity-BmRm1qV0.mjs +12 -0
  68. package/dist/{project-DGeclhvO.mjs → project-CV3tDFHl.mjs} +9 -18
  69. package/dist/pull-request-pxnDCQzn.mjs +322 -0
  70. package/dist/pull-requests.d.mts +120 -0
  71. package/dist/pull-requests.mjs +2 -0
  72. package/dist/recordings-GEXp4iBT.mjs +37 -0
  73. package/dist/{reload-C-cV5yy_.mjs → reload-ChPiwvot.mjs} +70 -22
  74. package/dist/{remote-cache--aehr4Pr.mjs → remote-cache-CO2elm_b.mjs} +4 -4
  75. package/dist/run-B-xqRrsg.mjs +119 -0
  76. package/dist/{server-bare-Bq8BpSIo.mjs → server-bare-X7PUgizL.mjs} +60 -13
  77. package/dist/{server-expo-DUCg52-K.mjs → server-expo-DhtX6bom.mjs} +6 -10
  78. package/dist/server-expo-KJeD109I.mjs +2 -0
  79. package/dist/{settings-xB0Frkg6.mjs → settings-CduVcoxj.mjs} +77 -26
  80. package/dist/{settings-ssVBToDR.mjs → settings-DbL6uhH8.mjs} +16 -10
  81. package/dist/settings.schema.json +357 -2
  82. package/dist/{simslim-Bbg9UWrt.mjs → simslim-Ciluxlq4.mjs} +8 -132
  83. package/dist/slot-launch-BY0BrOLh.mjs +65 -0
  84. package/dist/{spawn-claims-PT8aQpCa.mjs → spawn-claims-D0XfSe8u.mjs} +1 -1
  85. package/dist/spawn-entry-DncpJvWL.mjs +16 -0
  86. package/dist/stage-CzaiwFsI.mjs +143 -0
  87. package/dist/start-CPO4wRg7.mjs +2 -0
  88. package/dist/{start-BZhItkiL.mjs → start-CUD9nasA.mjs} +108 -61
  89. package/dist/state-B4PQFXMW.d.mts +12 -0
  90. package/dist/state-BxCgeTQu.mjs +41 -0
  91. package/dist/{state-DwAU2_-D.mjs → state-CaeaHbDA.mjs} +7 -4
  92. package/dist/{state-l1pYwOre.mjs → state-DFm77wRn.mjs} +67 -5
  93. package/dist/stats-DBLLFmKd.mjs +66 -0
  94. package/dist/{status-TJlL0HWo.mjs → status-DYXxNOT0.mjs} +425 -260
  95. package/dist/{status-sszP4XKU.mjs → status-l84Gbvp2.mjs} +122 -20
  96. package/dist/stdout-reader-C8yjre1x.mjs +23 -0
  97. package/dist/stim-desktop-Cnz8CPnl.mjs +63 -0
  98. package/dist/stim-footprint.swift +32 -0
  99. package/dist/{stim-installations-CINXy_su.mjs → stim-installations-QMEI32v1.mjs} +1 -1
  100. package/dist/{stop-BZ2qMiBP.mjs → stop-Bad61z_2.mjs} +128 -19
  101. package/dist/stop-Bvf1MflS.mjs +48 -0
  102. package/dist/{stop-DskDKNTU.mjs → stop-CceYh1yG.mjs} +2 -2
  103. package/dist/stop-cause-CZ_Mr-v9.mjs +665 -0
  104. package/dist/supervisor-run.d.mts +44 -12
  105. package/dist/supervisor-run.mjs +276 -33
  106. package/dist/support-CDQbDkL5.mjs +315 -0
  107. package/dist/support-WLOzVKUv.mjs +176 -0
  108. package/dist/teardown-BYuQafiA.mjs +718 -0
  109. package/dist/{xcode-LM6A8cNZ.mjs → toolchain-DmeYjXD9.mjs} +363 -13
  110. package/dist/warm-progress-DVHLN4uC.mjs +463 -0
  111. package/dist/{idle-DxLi5OkN.mjs → watchman-Bx0wceoF.mjs} +55 -8
  112. package/dist/{web-sWXd5iek.mjs → web-k6w8-HnE.mjs} +39 -22
  113. package/dist/web-run.mjs +326 -13
  114. package/dist/workspace-process-lock-BL7Q0HCO.mjs +57 -0
  115. package/dist/workspace-state-CL2V2Ons.mjs +371 -0
  116. package/dist/worktree-BT_uULFx.mjs +2 -0
  117. package/dist/{worktree-DeBQSVtR.mjs → worktree-C0c5Udwa.mjs} +55 -54
  118. package/dist/{worktree-D4Do_XiQ.mjs → worktree-qXKzADSC.mjs} +161 -255
  119. package/package.json +10 -5
  120. package/shim/bundle-response.cjs +55 -10
  121. package/shim/bundle-response.d.cts +1 -0
  122. package/shim/expo-metro-config.cjs +10 -2
  123. package/dist/build-progress-l7d-X-ZP.mjs +0 -247
  124. package/dist/in-use-wYEaKQM9.mjs +0 -249
  125. package/dist/ownership-PUP4Dwai.mjs +0 -1138
  126. package/dist/server-expo-BeYwP_Xy.mjs +0 -2
  127. package/dist/slot-launch-BIWEW-wI.mjs +0 -19
  128. package/dist/start-Rw2GD3nV.mjs +0 -2
  129. package/dist/stats-CaSwmsSZ.mjs +0 -62
  130. package/dist/stats-D8rUTPHd.mjs +0 -368
  131. package/dist/stim-desktop-CXt8-Z3J.mjs +0 -40
  132. package/dist/worktree-3cBa-bQt.mjs +0 -2
  133. package/dist/{metro-store-DzeWkfqW.mjs → metro-store-CDDZHtPl.mjs} +1 -1
@@ -1,6 +1,6 @@
1
- import { t as getExecutor } from "./exec-DK8KaVHK.mjs";
2
- import { n as TOOL_ERROR_PREFIX } from "./ios-device-BiOsSVzd.mjs";
3
- import { i as INSTALL_ERROR } from "./app-install-CDFY2nUL.mjs";
1
+ import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
+ import { n as TOOL_ERROR_PREFIX } from "./ios-device-Ee7sDXfp.mjs";
3
+ import { i as INSTALL_ERROR } from "./app-install-CAAplXAt.mjs";
4
4
  import { mkdtempSync, readFileSync, rmSync } from "node:fs";
5
5
  import { join } from "node:path";
6
6
  import { tmpdir } from "node:os";
@@ -38,12 +38,17 @@ function parseDevicectlDevices(payload) {
38
38
  transportType: text(connection.transportType),
39
39
  platform: text(hardware.platform),
40
40
  reality: text(hardware.reality),
41
- tunnelState: text(connection.tunnelState)
41
+ tunnelState: text(connection.tunnelState),
42
+ model: text(hardware.marketingName)
42
43
  });
43
44
  }
44
45
  return out;
45
46
  }
46
47
  function listIosDevices({ exec = null } = {}) {
48
+ return readIosDevices({ exec }) ?? [];
49
+ }
50
+ /** devicectl's device list, or null when devicectl failed or did not answer within `timeoutMs`. */
51
+ function readIosDevices({ exec = null, timeoutMs = DEVICECTL_TIMEOUT_MS } = {}) {
47
52
  const executor = exec || getExecutor();
48
53
  const dir = mkdtempSync(join(tmpdir(), "stim-devicectl-"));
49
54
  const out = join(dir, "devices.json");
@@ -54,10 +59,10 @@ function listIosDevices({ exec = null } = {}) {
54
59
  "devices",
55
60
  "-j",
56
61
  out
57
- ], { timeoutMs: DEVICECTL_TIMEOUT_MS });
62
+ ], { timeoutMs });
58
63
  return parseDevicectlDevices(readFileSync(out, "utf-8"));
59
64
  } catch {
60
- return [];
65
+ return null;
61
66
  } finally {
62
67
  rmSync(dir, {
63
68
  recursive: true,
@@ -507,4 +512,4 @@ function localNetworkPending(records, { since = 0, pid = null, lanOrigin }) {
507
512
  return false;
508
513
  }
509
514
  //#endregion
510
- export { iosPoolCandidates as a, listIosDevices as c, verifyIosDeviceReleaseLaunch as d, iosDeviceProcess as i, localNetworkPending as l, installIosDeviceApp as n, iosPoolNoCandidatesRefusal as o, iosDeviceBounds as r, isWirelessIosDevice as s, awaitIosDeviceLaunch as t, resolveIosPhysicalDevice as u };
515
+ export { iosPoolCandidates as a, isWirelessIosDevice as c, readIosDevices as d, resolveIosPhysicalDevice as f, iosDeviceProcess as i, listIosDevices as l, installIosDeviceApp as n, iosPoolNoCandidatesRefusal as o, verifyIosDeviceReleaseLaunch as p, iosDeviceBounds as r, isReachable as s, awaitIosDeviceLaunch as t, localNetworkPending as u };
@@ -1,4 +1,4 @@
1
- import { t as getExecutor } from "./exec-DK8KaVHK.mjs";
1
+ import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
2
  //#region src/collector/ios-device.ts
3
3
  const CONSOLE_ENV = { OS_ACTIVITY_DT_MODE: "enable" };
4
4
  const DEV_MENU_LAUNCH_ARGS = [
@@ -1,8 +1,8 @@
1
- import { t as getExecutor } from "./exec-DK8KaVHK.mjs";
1
+ import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
2
  import { o as readNdjsonGenerations } from "./ndjson-CDY9IkVC.mjs";
3
- import { s as isAppLaunchError } from "./command-output-BIL7rlX7.mjs";
4
- import { t as DEV_MENU_LAUNCH_ARGS } from "./ios-device-BiOsSVzd.mjs";
5
- import { d as deviceShellArg, n as ANDROID_DISABLE_AUTO_LAUNCH_EXTRA, o as androidAppProcess } from "./app-install-CDFY2nUL.mjs";
3
+ import { s as isAppLaunchError } from "./command-output-CliHHn7a.mjs";
4
+ import { t as DEV_MENU_LAUNCH_ARGS } from "./ios-device-Ee7sDXfp.mjs";
5
+ import { f as deviceShellArg, n as ANDROID_DISABLE_AUTO_LAUNCH_EXTRA, s as androidAppProcess } from "./app-install-CAAplXAt.mjs";
6
6
  import { join } from "node:path";
7
7
  //#region src/engine/app-readiness.ts
8
8
  const APP_READINESS_TIMEOUT_MS = 3e4;
@@ -121,7 +121,12 @@ function nativeLaunchFailure({ readCrashes, since, platform, processAlive, mode,
121
121
  waitedMs
122
122
  } : null;
123
123
  }
124
- async function verifyLaunch({ slot, logsDir, since, metroPort = null, platform = null, mode = null, requireBundleResponse, timeoutMs = VERIFY_TIMEOUT_MS, stabilityMs = STABILITY_WINDOW_MS, pollMs = VERIFY_POLL_MS, readRecords = null, readDeviceRecords = null, readClientRecords = null, readNativeCrashes = null, processAlive = null, onReadinessPending = null, now = Date.now, sleep = (ms) => new Promise((r) => setTimeout(r, ms)) } = {}) {
124
+ async function verifyLaunch({ slot, appPid, platformShared, logsDir, since, metroPort = null, platform = null, mode = null, requireBundleResponse, timeoutMs = VERIFY_TIMEOUT_MS, stabilityMs = STABILITY_WINDOW_MS, pollMs = VERIFY_POLL_MS, readRecords = null, readDeviceRecords = null, readClientRecords = null, readNativeCrashes = null, processAlive = null, onReadinessPending = null, now = Date.now, sleep = (ms) => new Promise((r) => setTimeout(r, ms)) } = {}) {
125
+ const delivered = (record) => deliveryOwner(record, {
126
+ slot,
127
+ appPid,
128
+ platformShared
129
+ }) === "this";
125
130
  const read = readRecords || (() => readMetroRecords(logsDir));
126
131
  const readDevice = readDeviceRecords || (() => readNdjson(logsDir, "device.ndjson"));
127
132
  const readClient = readClientRecords || (() => readNdjson(logsDir, "client.ndjson"));
@@ -136,10 +141,10 @@ async function verifyLaunch({ slot, logsDir, since, metroPort = null, platform =
136
141
  let runtimeStartedAt = null;
137
142
  let nextCrashCheck = startedAt + 1e3;
138
143
  while (true) {
139
- const metroRecords = read().filter((record) => after(record, since) && (slot === void 0 || record.slot === slot));
140
- const bundleRecords = metroRecords.filter((record) => !requireBundleResponse || String(record.event).startsWith("bundle_response_"));
144
+ const metroRecords = read().filter((record) => after(record, since));
145
+ const bundleRecords = metroRecords.filter((record) => (!requireBundleResponse || String(record.event).startsWith("bundle_response_")) && delivered(record));
141
146
  const deviceRecords = readDevice().filter((record) => after(record, since) && (slot === void 0 || (record.slot ?? "default") === slot));
142
- const clientRecords = readClient().filter((record) => after(record, since) && (slot === void 0 || record.slot === slot));
147
+ const clientRecords = readClient().filter((record) => after(record, since) && !platformShared);
143
148
  if (now() >= nextCrashCheck) {
144
149
  nextCrashCheck = now() + 2e3;
145
150
  const crash = nativeLaunchFailure({
@@ -238,6 +243,11 @@ async function verifyLaunch({ slot, logsDir, since, metroPort = null, platform =
238
243
  }
239
244
  if ((!proof || runtimeWaiting) && now() >= bundleDeadline) return bundleTimeoutOutcome({
240
245
  requested: findBundleRequest(deviceRecords, since, metroPort, platform) ?? activity,
246
+ unattributed: metroRecords.find((record) => record.event === "bundle_response_finished" && deliveryOwner(record, {
247
+ slot,
248
+ appPid,
249
+ platformShared
250
+ }) === "unknown" && isBundleProof(record, since, platform)) ?? null,
241
251
  processAlive,
242
252
  deviceRecords,
243
253
  platform,
@@ -247,7 +257,12 @@ async function verifyLaunch({ slot, logsDir, since, metroPort = null, platform =
247
257
  await sleep(Math.min(pollMs, Math.max(0, (runtimeWaiting ? bundleDeadline : proof && pendingAt !== null ? readinessDeadline : stabilityDeadline ?? bundleDeadline) - now())));
248
258
  }
249
259
  }
250
- function bundleTimeoutOutcome({ requested, processAlive, deviceRecords, platform, mode, waitedMs }) {
260
+ function deliveryOwner(record, { slot, appPid, platformShared }) {
261
+ if (slot === void 0) return "this";
262
+ if (typeof record.clientPid === "number" && appPid) return record.clientPid === appPid ? "this" : "other";
263
+ return platformShared ? "unknown" : "this";
264
+ }
265
+ function bundleTimeoutOutcome({ requested, unattributed, processAlive, deviceRecords, platform, mode, waitedMs }) {
251
266
  if (processAlive?.() === false) return {
252
267
  verified: false,
253
268
  fatal: true,
@@ -256,13 +271,21 @@ function bundleTimeoutOutcome({ requested, processAlive, deviceRecords, platform
256
271
  mode,
257
272
  waitedMs
258
273
  };
259
- return requested ? {
274
+ if (requested) return {
260
275
  verified: false,
261
276
  timedOut: true,
262
277
  requested: true,
263
278
  record: requested,
264
279
  mode,
265
280
  waitedMs
281
+ };
282
+ return unattributed ? {
283
+ verified: false,
284
+ timedOut: true,
285
+ unattributed: true,
286
+ record: unattributed,
287
+ mode,
288
+ waitedMs
266
289
  } : {
267
290
  verified: false,
268
291
  timedOut: true,
@@ -316,6 +339,14 @@ function readCollectorRecords(logsDir) {
316
339
  function readNdjson(logsDir, name) {
317
340
  return logsDir ? readNdjsonGenerations(join(logsDir, name)) : [];
318
341
  }
342
+ function unattributedLaunchLines({ platform, metroPort, slot = "default", siblings }) {
343
+ const others = siblings.length === 1 ? `Slot ${siblings[0]} also runs` : `Slots ${siblings.join(", ")} also run`;
344
+ return [
345
+ `UNVERIFIED: Metro delivered ${platform === "ios" ? "an iOS" : "an Android"} bundle on port ${metroPort}, but not provably to this device`,
346
+ `${others} ${platform} on this workspace's Metro, and this bundle delivery could not be matched to this device.`,
347
+ `Check this device directly: its screen through your device tool, or \`stim logs --slot ${slot} --source device\`. A slot stops counting as a sibling after \`stim stop --slot <name>\`.`
348
+ ];
349
+ }
319
350
  function unverifiedLaunchLines({ platform, metroPort, waitedMs = VERIFY_TIMEOUT_MS, bundleId = null, udid = null, serial = null, devClientUrl: url = null, mode = null, remote = false, physical = false, devClient = false, lanOrigin = null, metroOrigin = null, localNetworkPending = false, component = null }) {
320
351
  const seconds = Math.round(Number(waitedMs || 0) / 1e3);
321
352
  const localNetwork = localNetworkPending && platform === "ios" && physical;
@@ -375,4 +406,4 @@ function unverifiedLaunchLines({ platform, metroPort, waitedMs = VERIFY_TIMEOUT_
375
406
  return lines;
376
407
  }
377
408
  //#endregion
378
- export { VERIFY_TIMEOUT_MS as a, readMetroRecords as c, verifyLaunch as d, verifyReleaseLaunch as f, RELEASE_VERIFY_WAIT_MS as i, unverifiedLaunchLines as l, LAUNCH_FATAL as n, isBundleProof as o, APP_READINESS_TIMEOUT_MS as p, LAUNCH_UNVERIFIED as r, readCollectorRecords as s, LAUNCH_BUNDLING as t, verifyAndroidReleaseLaunch as u };
409
+ export { VERIFY_TIMEOUT_MS as a, readMetroRecords as c, verifyAndroidReleaseLaunch as d, verifyLaunch as f, RELEASE_VERIFY_WAIT_MS as i, unattributedLaunchLines as l, APP_READINESS_TIMEOUT_MS as m, LAUNCH_FATAL as n, isBundleProof as o, verifyReleaseLaunch as p, LAUNCH_UNVERIFIED as r, readCollectorRecords as s, LAUNCH_BUNDLING as t, unverifiedLaunchLines as u };
@@ -1,13 +1,16 @@
1
- import { H as parseDeviceSlotOption, O as process_identity_exports, U as projectDeviceSlots, _ as loadConfig$1, g as isPathPrefix } from "./config-0jCcMM0E.mjs";
1
+ import { H as projectDeviceSlots, V as parseDeviceSlotOption, _ as loadConfig$1, g as isPathPrefix } from "./config-D0DbpZtM.mjs";
2
+ import { t as process_identity_exports } from "./process-identity-BmRm1qV0.mjs";
2
3
  import { n as SOURCES, t as LEVELS } from "./ndjson-CDY9IkVC.mjs";
3
- import { M as workspaceLogsDir, h as readWorkspaceState$1 } from "./metro-BPeY1YMh.mjs";
4
- import { i as findCommandWorkspace } from "./project-DGeclhvO.mjs";
5
- import { f as refuseNoProject, v as launchErrorPreview } from "./command-output-BIL7rlX7.mjs";
6
- import { a as readAgentDeviceRecords, t as agentDeviceLiveness } from "./activity-DfVKZTEB.mjs";
7
- import { i as captureWorkspaceCrashes, t as errorDiagnostics } from "./error-diagnostics-BoiOhck8.mjs";
4
+ import { C as workspaceLogsDir, r as readWorkspaceState$1 } from "./workspace-state-CL2V2Ons.mjs";
5
+ import { i as findCommandWorkspace } from "./project-CV3tDFHl.mjs";
6
+ import { o as readAgentDeviceRecords, t as agentDeviceLiveness } from "./activity-B2-kb7k0.mjs";
7
+ import { f as refuseNoProject, l as phaseLine, v as launchErrorPreview } from "./command-output-CliHHn7a.mjs";
8
+ import { a as pullHostedMacosLogs, t as connectHost } from "./hosted-macos-DwowNU7W.mjs";
9
+ import { i as captureWorkspaceCrashes, t as errorDiagnostics } from "./error-diagnostics-Cb0P1WLS.mjs";
10
+ import { t as watchStdoutReader } from "./stdout-reader-C8yjre1x.mjs";
8
11
  import { closeSync, fstatSync, openSync, readFileSync, readSync, readdirSync, realpathSync, statSync } from "node:fs";
9
- import { attachExpoErrorContext, buildCriteria, compileGrep, fileSizes, followLogs, logFiles, parseSince, queryLogs, readLogRecords, recordMatches, sortByTs } from "@stim-cli/core/state";
10
12
  import { join, relative, resolve, sep } from "node:path";
13
+ import { attachExpoErrorContext, buildCriteria, compileGrep, fileSizes, followLogs, logFiles, parseSince, queryLogs, readLogRecords, readMacosRecord, recordMatches, sortByTs } from "@stim-cli/core/state";
11
14
  import { homedir } from "node:os";
12
15
  import chalk from "chalk";
13
16
  //#region src/devices/agent-actions.ts
@@ -17,6 +20,8 @@ function object(value) {
17
20
  }
18
21
  function parseAgentEvents(lines) {
19
22
  const events = [];
23
+ const started = [];
24
+ const finished = [];
20
25
  let session = null;
21
26
  let unknownVersion = null;
22
27
  for (const line of lines) {
@@ -37,9 +42,14 @@ function parseAgentEvents(lines) {
37
42
  }
38
43
  if (typeof entry.session === "string") session ??= entry.session;
39
44
  const { kind, command, summary } = entry;
45
+ const requestId = typeof entry.requestId === "string" ? entry.requestId : null;
46
+ if (kind === "request.started" && requestId && Number.isFinite(ts)) started.push([requestId, ts]);
40
47
  if (kind !== "action.recorded" && kind !== "request.finished") continue;
41
48
  const failed = kind === "request.finished" && entry.status === "error";
42
- if (kind === "request.finished" && !failed) continue;
49
+ if (kind === "request.finished" && !failed) {
50
+ if (requestId) finished.push(requestId);
51
+ continue;
52
+ }
43
53
  if (!Number.isFinite(ts) || typeof entry.session !== "string" || typeof command !== "string") continue;
44
54
  if (typeof summary !== "string") continue;
45
55
  events.push({
@@ -49,11 +59,14 @@ function parseAgentEvents(lines) {
49
59
  command,
50
60
  summary,
51
61
  failed,
52
- details: object(entry.details)
62
+ details: object(entry.details),
63
+ requestId
53
64
  });
54
65
  }
55
66
  return {
56
67
  events,
68
+ started,
69
+ finished,
57
70
  session,
58
71
  unknownVersion
59
72
  };
@@ -103,9 +116,10 @@ function spanDevice(spans, ts) {
103
116
  for (const span of spans) if (span.from <= ts) device = span.deviceId;
104
117
  return device;
105
118
  }
106
- function agentRecord(event, deviceId, target) {
119
+ function agentRecord(event, deviceId, target, startedAt) {
107
120
  return {
108
121
  ts: event.ts,
122
+ ...startedAt !== void 0 && startedAt <= event.ts ? { startedAt } : {},
109
123
  src: "agent",
110
124
  level: event.failed ? "error" : "info",
111
125
  msg: event.summary,
@@ -221,7 +235,8 @@ function createAgentActionReader({ targets, sinceTs = -Infinity, home = homedir(
221
235
  closes: [],
222
236
  runner: null,
223
237
  session: null,
224
- unknownReported: false
238
+ unknownReported: false,
239
+ started: /* @__PURE__ */ new Map()
225
240
  };
226
241
  cursors.set(name, cursor);
227
242
  try {
@@ -234,8 +249,13 @@ function createAgentActionReader({ targets, sinceTs = -Infinity, home = homedir(
234
249
  cursor.offset = chunk.next;
235
250
  lines.push(...chunk.text.split("\n"));
236
251
  const parsed = parseAgentEvents(lines);
252
+ for (const [requestId, at] of parsed.started) cursor.started.set(requestId, at);
237
253
  cursor.session ??= parsed.session;
238
254
  const session = cursor.session;
255
+ const matched = new Map(parsed.events.flatMap((event) => event.requestId && cursor.started.has(event.requestId) ? [[event.requestId, cursor.started.get(event.requestId)]] : []));
256
+ const failed = parsed.events.flatMap((event) => event.failed && event.requestId ? [event.requestId] : []);
257
+ for (const requestId of [...parsed.finished, ...failed]) cursor.started.delete(requestId);
258
+ for (const requestId of [...cursor.started.keys()].slice(0, -64)) cursor.started.delete(requestId);
239
259
  if (!parsed.events.length && !parsed.unknownVersion) continue;
240
260
  const runnerPath = join(dir, "runner.log");
241
261
  let runnerSize = -1;
@@ -267,13 +287,69 @@ function createAgentActionReader({ targets, sinceTs = -Infinity, home = homedir(
267
287
  if (event.ts < sinceTs) continue;
268
288
  const deviceId = deviceAt(event.ts);
269
289
  const target = deviceId ? byId.get(deviceId) : void 0;
270
- if (target && deviceId) out.push(agentRecord(event, deviceId, target));
290
+ const startedAt = event.requestId ? matched.get(event.requestId) : void 0;
291
+ if (target && deviceId) out.push(agentRecord(event, deviceId, target, startedAt));
271
292
  }
272
293
  }
273
294
  return out.toSorted((a, b) => a.ts - b.ts);
274
295
  };
275
296
  }
276
297
  //#endregion
298
+ //#region src/device-host/hosted-logs-sync.ts
299
+ function warning(machine, error) {
300
+ const message = error.message.replace(/\.$/, "");
301
+ const hint = message.includes("refused device-host.logs.query") ? ` If ${machine} runs an older stim-server, update it.` : "";
302
+ return phaseLine("device", `Could not read the app's logs from ${machine}: ${message}. Showing the logs already copied here.${hint}`);
303
+ }
304
+ async function syncHostedMacosLogs(root, placement) {
305
+ let host;
306
+ try {
307
+ host = await connectHost(placement.machine);
308
+ await pullHostedMacosLogs(root, placement, host);
309
+ return true;
310
+ } catch (error) {
311
+ process.stderr.write(`${warning(placement.machine, error)}\n`);
312
+ return false;
313
+ } finally {
314
+ host?.connection.close();
315
+ }
316
+ }
317
+ function followHostedMacosLogs(root, { failing = false, intervalMs = 500, retryMs = 5e3 } = {}) {
318
+ let host;
319
+ let retryAt = 0;
320
+ let stopped = false;
321
+ let running = false;
322
+ const tick = async () => {
323
+ if (running || stopped || Date.now() < retryAt) return;
324
+ running = true;
325
+ try {
326
+ const placement = readMacosRecord(root)?.host;
327
+ if (!placement) {
328
+ host?.connection.close();
329
+ host = void 0;
330
+ return;
331
+ }
332
+ host ??= await connectHost(placement.machine);
333
+ await pullHostedMacosLogs(root, placement, host);
334
+ failing = false;
335
+ } catch (error) {
336
+ host?.connection.close();
337
+ host = void 0;
338
+ if (!failing) process.stderr.write(`${warning(readMacosRecord(root)?.host?.machine ?? "the host", error)}\n`);
339
+ failing = true;
340
+ retryAt = Date.now() + retryMs;
341
+ } finally {
342
+ running = false;
343
+ }
344
+ };
345
+ const timer = setInterval(() => void tick(), intervalMs);
346
+ return () => {
347
+ stopped = true;
348
+ clearInterval(timer);
349
+ host?.connection.close();
350
+ };
351
+ }
352
+ //#endregion
277
353
  //#region src/commands/logs.ts
278
354
  const LEVEL_WIDTH = 5;
279
355
  const SRC_WIDTH = 6;
@@ -359,7 +435,7 @@ const LEVEL_COLOURS = {
359
435
  fatal: chalk.bgRed
360
436
  };
361
437
  function logsCommand(program) {
362
- program.command("logs").description("Query this workspace's merged NDJSON log timeline (bundler, client, device, build, agent-device actions). Prints and exits; an existing timeline with nothing matching is a successful, empty result. Use --follow to stream.").option("--slot <name>", "Only records attributed to this device slot", parseDeviceSlotOption).option("--source <s...>", "Only these sources: metro, client, device, build, agent, or all").option("--level <l>", `Minimum level: ${LEVELS.join(", ")}`).option("--since <d>", "Only records newer than this, e.g. 30s, 5m, 2h").option("--grep <re>", "Only records whose message matches this regular expression").option("--tail <n>", "Only the last n matching records").option("--errors", "Errors and fatals since the last marker, from metro, client and build, plus confirmed native app-crash reports and stim web browser errors. Add --source device or --source all for general device errors.").option("--follow", "Keep streaming new records until interrupted").option("--json", "Emit the raw records, one per line (valid NDJSON; zero matches is zero bytes, exit 0)").action(async (opts) => {
438
+ program.command("logs").description("Query this workspace's merged NDJSON log timeline (bundler, client, device, build, agent actions). Prints and exits; an existing timeline with nothing matching is a successful, empty result. Use --follow to stream.").option("--slot <name>", "Only this device slot's records, plus the shared Metro and app client records once it has launched", parseDeviceSlotOption).option("--source <s...>", "Only these sources: metro, client, device, build, agent, or all").option("--level <l>", `Minimum level: ${LEVELS.join(", ")}`).option("--since <d>", "Only records newer than this, e.g. 30s, 5m, 2h").option("--grep <re>", "Only records whose message matches this regular expression").option("--tail <n>", "Only the last n matching records").option("--errors", "Errors and fatals since the last marker, from metro, client and build, plus confirmed native app-crash reports and stim web browser errors. Add --source device or --source all for general device errors.").option("--follow", "Keep streaming new records until interrupted").option("--json", "Emit the raw records, one per line (valid NDJSON; zero matches is zero bytes, exit 0)").action(async (opts) => {
363
439
  const root = findCommandWorkspace(process.cwd());
364
440
  if (!root) {
365
441
  refuseNoProject({ json: false });
@@ -393,6 +469,8 @@ function logsCommand(program) {
393
469
  if (compiled.error) fail(compiled.error);
394
470
  }
395
471
  requireLogsWorkspace(root, dir);
472
+ const hosted = readMacosRecord(root)?.host;
473
+ const hostedReachable = hosted ? await syncHostedMacosLogs(root, hosted) : true;
396
474
  const query = {
397
475
  slot: opts.slot,
398
476
  dir,
@@ -469,11 +547,15 @@ function logsCommand(program) {
469
547
  const agentTimer = readAgent ? setInterval(() => {
470
548
  for (const record of readAgent()) if (recordMatches(record, criteria)) emit(record);
471
549
  }, 500) : null;
550
+ const stopHosted = hosted ? followHostedMacosLogs(root, { failing: !hostedReachable }) : null;
472
551
  const finish = () => {
473
552
  stop();
553
+ stopHosted?.();
554
+ stopReader();
474
555
  if (agentTimer) clearInterval(agentTimer);
475
556
  process.exit(0);
476
557
  };
558
+ const stopReader = watchStdoutReader(finish);
477
559
  process.on("SIGINT", finish);
478
560
  process.on("SIGTERM", finish);
479
561
  });
@@ -0,0 +1,283 @@
1
+ import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
+ import { basename, dirname, join } from "node:path";
3
+ import { compiledHelper, configDir } from "@stim-cli/core";
4
+ import { MACHINE_OWNER_KINDS } from "@stim-cli/core/state";
5
+ import { fileURLToPath } from "node:url";
6
+ //#region src/footprint.ts
7
+ const SOURCE = "stim-footprint.swift";
8
+ const COMPILE_TIMEOUT_MS = 12e4;
9
+ const READ_TIMEOUT_MS = 5e3;
10
+ function helperSource() {
11
+ const here = fileURLToPath(import.meta.url);
12
+ if (basename(dirname(here)) === "src") return fileURLToPath(new URL(`../helper/${SOURCE}`, import.meta.url));
13
+ return join(dirname(here), SOURCE);
14
+ }
15
+ async function buildHelper() {
16
+ const exec = getExecutor();
17
+ if (!exec.runFileQuiet("xcode-select", ["-p"], { timeoutMs: READ_TIMEOUT_MS })) return null;
18
+ const source = helperSource();
19
+ try {
20
+ return await compiledHelper({
21
+ dir: join(configDir(), "helpers"),
22
+ name: "stim-footprint",
23
+ inputs: [source],
24
+ version: "",
25
+ compile: async (output) => {
26
+ await exec.runFileAsync("xcrun", [
27
+ "swiftc",
28
+ "-O",
29
+ "-swift-version",
30
+ "5",
31
+ "-o",
32
+ output,
33
+ source
34
+ ], { timeoutMs: COMPILE_TIMEOUT_MS });
35
+ }
36
+ });
37
+ } catch {
38
+ return null;
39
+ }
40
+ }
41
+ const RETRY_BACKOFF_INITIAL_MS = 1e4;
42
+ const RETRY_BACKOFF_MAX_MS = 6e5;
43
+ let built = null;
44
+ let failure = null;
45
+ /** The `stim-footprint` helper, compiled on first use. A success is kept for the process's life; a failure retries after a doubling backoff instead of being pinned forever. */
46
+ function footprintHelper() {
47
+ const home = configDir();
48
+ if (built?.home === home) return built.promise;
49
+ if (failure?.home === home && Date.now() < failure.nextRetryAt) return Promise.resolve(null);
50
+ const promise = buildHelper().then((helper) => {
51
+ if (helper === null) {
52
+ const backoffMs = failure?.home === home ? Math.min(failure.backoffMs * 2, RETRY_BACKOFF_MAX_MS) : RETRY_BACKOFF_INITIAL_MS;
53
+ failure = {
54
+ home,
55
+ nextRetryAt: Date.now() + backoffMs,
56
+ backoffMs
57
+ };
58
+ if (built?.promise === promise) built = null;
59
+ return null;
60
+ }
61
+ return helper;
62
+ });
63
+ built = {
64
+ home,
65
+ promise
66
+ };
67
+ return promise;
68
+ }
69
+ /** Parses the helper's `<pid> <bytes>` lines into footprint bytes by pid. */
70
+ function parseFootprints(output) {
71
+ const footprints = /* @__PURE__ */ new Map();
72
+ for (const line of output.split("\n")) {
73
+ const match = /^(\d+) (\d+)$/.exec(line.trim());
74
+ if (match) footprints.set(Number(match[1]), Number(match[2]));
75
+ }
76
+ return footprints;
77
+ }
78
+ /** Every readable process's physical footprint in bytes, or null when the helper is unavailable or fails. */
79
+ async function readFootprints() {
80
+ const helper = await footprintHelper();
81
+ const output = helper ? getExecutor().runFileQuiet(helper, [], { timeoutMs: READ_TIMEOUT_MS }) : null;
82
+ return output === null ? null : parseFootprints(output);
83
+ }
84
+ //#endregion
85
+ //#region src/machine-usage.ts
86
+ const SHARED = [
87
+ {
88
+ name: "CoreSimulator services",
89
+ pattern: /\/CoreSimulator\.framework\/|\/Simulator\.app\/Contents\/MacOS\//
90
+ },
91
+ {
92
+ name: "adb server",
93
+ pattern: /(^|\/)adb\s.*\bfork-server\b/
94
+ },
95
+ {
96
+ name: "Android emulator services",
97
+ pattern: /\/emulator\/netsimd(\s|$)/
98
+ },
99
+ {
100
+ name: "Gradle daemon",
101
+ pattern: /\borg\.gradle\.launcher\.daemon\.bootstrap\.GradleDaemon\b/
102
+ },
103
+ {
104
+ name: "Kotlin daemon",
105
+ pattern: /\bKotlinCompileDaemon\b/
106
+ },
107
+ {
108
+ name: "Watchman",
109
+ pattern: /(^|\/)watchman(\s|$)/
110
+ }
111
+ ];
112
+ const STIM_SERVER_COMMAND = /(^|[\s/])stim-server(\.mjs)?(\s|$)/;
113
+ const LAUNCHD_SIM = /^launchd_sim\s.*\/Devices\/([^/\s]+)\//;
114
+ const MAX_DEPTH = 64;
115
+ const EMULATOR = /(?:^|\/)(?:qemu-system-[^\s/]+|emulator)(?:\s.*)?\s(?:-avd\s+|@)(\S+)/;
116
+ function emulatorAvd(command) {
117
+ return EMULATOR.exec(command)?.[1] ?? null;
118
+ }
119
+ function workspaceOwner(kind, env, name, id) {
120
+ return {
121
+ kind,
122
+ name,
123
+ workspace: env.path,
124
+ id,
125
+ owned: true
126
+ };
127
+ }
128
+ function rank(owner) {
129
+ return MACHINE_OWNER_KINDS.indexOf(owner.kind);
130
+ }
131
+ /**
132
+ * Attributes every process in `processes` to at most one owner: the owner of its nearest ancestor (itself included)
133
+ * that is a root. Roots are each booted simulator's `launchd_sim`, each emulator launcher and qemu process, and each
134
+ * workspace's verified supervisor and Metro, running build and Chrome. Only a process under none of those falls back
135
+ * to stim-server or a machine-wide service in `SHARED`, so a `simctl` a workspace's log collector runs stays with the
136
+ * workspace. An emulator counts in the workspace that records its AVD whenever its process runs, including while adb
137
+ * does not list it. A process with no root ancestor is left out. `footprints` holds physical footprint bytes by pid,
138
+ * or is null when they could not be read, and the owners' `memoryMb` then sums resident sizes. A process missing from
139
+ * `footprints`, another user's or one that exited between the two reads, counts its resident size.
140
+ */
141
+ function attributeMachineUsage({ processes, footprints, environments, roots, simNames }) {
142
+ const owners = /* @__PURE__ */ new Map();
143
+ const rootOf = /* @__PURE__ */ new Map();
144
+ const fallbackOf = /* @__PURE__ */ new Map();
145
+ const claim = (pid, key, owner, into = rootOf) => {
146
+ if (!owners.has(key)) owners.set(key, owner);
147
+ into.set(pid, key);
148
+ };
149
+ const sims = /* @__PURE__ */ new Map();
150
+ const emulators = /* @__PURE__ */ new Map();
151
+ for (const env of environments) for (const device of [{
152
+ slot: "default",
153
+ ios: env.ios,
154
+ android: env.android
155
+ }, ...env.slots ?? []]) {
156
+ const slot = device.slot === "default" ? {} : { slot: device.slot };
157
+ if (device.ios?.state === "Booted") sims.set(device.ios.udid.toUpperCase(), {
158
+ kind: "simulator",
159
+ name: device.ios.name ?? device.ios.udid,
160
+ workspace: env.path,
161
+ ...slot,
162
+ id: device.ios.udid,
163
+ owned: device.ios.owned
164
+ });
165
+ if (device.android?.name && !device.android.physical) emulators.set(device.android.name, {
166
+ kind: "emulator",
167
+ name: device.android.name,
168
+ workspace: env.path,
169
+ ...slot,
170
+ id: device.android.name,
171
+ owned: device.android.owned
172
+ });
173
+ }
174
+ for (const p of processes) {
175
+ const shared = SHARED.find(({ pattern }) => pattern.test(p.command));
176
+ if (shared) {
177
+ const owner = {
178
+ kind: "shared",
179
+ name: shared.name,
180
+ workspace: null,
181
+ id: null,
182
+ owned: false
183
+ };
184
+ claim(p.pid, `shared:${shared.name}`, owner, fallbackOf);
185
+ continue;
186
+ }
187
+ if (STIM_SERVER_COMMAND.test(p.command)) {
188
+ claim(p.pid, "server", {
189
+ kind: "server",
190
+ name: "stim-server",
191
+ workspace: null,
192
+ id: null,
193
+ owned: false
194
+ }, fallbackOf);
195
+ continue;
196
+ }
197
+ const udid = LAUNCHD_SIM.exec(p.command)?.[1]?.toUpperCase();
198
+ if (udid) {
199
+ const owner = sims.get(udid) ?? {
200
+ kind: "simulator",
201
+ name: simNames[udid] ?? udid,
202
+ workspace: null,
203
+ id: udid,
204
+ owned: false
205
+ };
206
+ claim(p.pid, `simulator:${udid}`, owner);
207
+ continue;
208
+ }
209
+ const avd = emulatorAvd(p.command);
210
+ if (avd) {
211
+ const owner = emulators.get(avd) ?? {
212
+ kind: "emulator",
213
+ name: avd,
214
+ workspace: null,
215
+ id: avd,
216
+ owned: false
217
+ };
218
+ claim(p.pid, `emulator:${avd}`, owner);
219
+ }
220
+ }
221
+ const present = new Set(processes.map((p) => p.pid));
222
+ const byPath = new Map(environments.map((env) => [env.path, env]));
223
+ for (const root of roots) {
224
+ const env = byPath.get(root.path);
225
+ if (!env) continue;
226
+ const metroPids = [root.supervisorPid, env.metro?.running ? env.metro.pid : null];
227
+ for (const pid of metroPids) if (pid && present.has(pid)) {
228
+ const port = env.metro?.port ?? null;
229
+ claim(pid, `metro:${env.path}`, workspaceOwner("metro", env, port ? `Metro :${port}` : "Metro", port ? String(port) : null));
230
+ }
231
+ if (root.build && present.has(root.build.pid)) {
232
+ const label = root.build.platform === "ios" ? "iOS build" : "Android build";
233
+ claim(root.build.pid, `build:${env.path}`, workspaceOwner("build", env, label, root.build.platform));
234
+ }
235
+ for (const pid of root.macosPids ?? []) if (present.has(pid)) claim(pid, `macos:${env.path}`, workspaceOwner("macos", env, env.macos?.product ?? "macOS app", null));
236
+ for (const pid of root.browserPids) if (present.has(pid)) claim(pid, `browser:${env.path}`, workspaceOwner("browser", env, "Chrome", null));
237
+ }
238
+ const parentOf = new Map(processes.map((p) => [p.pid, p.ppid]));
239
+ const totals = /* @__PURE__ */ new Map();
240
+ for (const p of processes) {
241
+ let pid = p.pid;
242
+ let key;
243
+ let fallback;
244
+ for (let depth = 0; pid !== void 0 && pid > 1 && depth < MAX_DEPTH; depth++) {
245
+ key = rootOf.get(pid);
246
+ if (key) break;
247
+ fallback ??= fallbackOf.get(pid);
248
+ pid = parentOf.get(pid);
249
+ }
250
+ key ??= fallback;
251
+ if (!key) continue;
252
+ const total = totals.get(key) ?? {
253
+ rssKb: 0,
254
+ memoryKb: 0,
255
+ cpu: 0,
256
+ processes: 0
257
+ };
258
+ total.rssKb += p.rssKb;
259
+ const footprint = footprints?.get(p.pid);
260
+ total.memoryKb += footprint === void 0 ? p.rssKb : footprint / 1024;
261
+ total.cpu += p.cpuPercent;
262
+ total.processes += 1;
263
+ totals.set(key, total);
264
+ }
265
+ const list = [];
266
+ for (const [key, owner] of owners) {
267
+ const total = totals.get(key);
268
+ if (total) list.push({
269
+ ...owner,
270
+ cpuPercent: Math.round(total.cpu),
271
+ residentMb: Math.round(total.rssKb / 1024),
272
+ memoryMb: Math.round(total.memoryKb / 1024),
273
+ processes: total.processes
274
+ });
275
+ }
276
+ list.sort((x, y) => rank(x) - rank(y) || (x.workspace ?? "~").localeCompare(y.workspace ?? "~") || x.name.localeCompare(y.name));
277
+ return {
278
+ memorySource: footprints ? "footprint" : "rss",
279
+ owners: list
280
+ };
281
+ }
282
+ //#endregion
283
+ export { attributeMachineUsage as n, readFootprints as r, STIM_SERVER_COMMAND as t };