stim 1.14.0 → 1.16.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 (152) hide show
  1. package/README.md +2 -0
  2. package/dist/activity-BxwdD81S.mjs +609 -0
  3. package/dist/agent-device-usage-output-Dpih1Tfa.mjs +411 -0
  4. package/dist/{android-d6rGq1pA.mjs → android-B_NumVyQ.mjs} +169 -81
  5. package/dist/{android-LbPFRWVL.mjs → android-Ift4kVvt.mjs} +517 -413
  6. package/dist/android-cas-8U4DPqeR.mjs +167 -0
  7. package/dist/android-cas-compiler.mjs +2 -2
  8. package/dist/{android-XpvCrV7q.mjs → android-r2rBL8kV.mjs} +2 -2
  9. package/dist/{app-install-BpgSgkJj.mjs → app-install-CKWdbXIX.mjs} +128 -151
  10. package/dist/attempt-Bk2ouQVL.mjs +39 -0
  11. package/dist/budget-DjrURfQn.mjs +2700 -0
  12. package/dist/{build-plan-Dcr-3ie0.mjs → build-plan-DjeSb2Xl.mjs} +192 -653
  13. package/dist/build-slots-rUk77xqh.mjs +132 -0
  14. package/dist/{cache-manifest-C68QQ0ua.mjs → cache-manifest-DnUHBJaU.mjs} +2 -2
  15. package/dist/cache-manifest.mjs +1 -1
  16. package/dist/{chrome-BPCiC3ZX.mjs → chrome-BrcVWQyD.mjs} +1 -1
  17. package/dist/cli-BkYZ_6hy.mjs +51 -0
  18. package/dist/cli.mjs +6 -42
  19. package/dist/client-BTY686R6.mjs +1368 -0
  20. package/dist/collector-run.mjs +9 -195
  21. package/dist/{command-output-DVcpw6eE.mjs → command-output-DnJMdEY-.mjs} +1 -1
  22. package/dist/{config-lvc7d3C2.mjs → config-D0DbpZtM.mjs} +28 -29
  23. package/dist/{created-devices-CllH_8eb.mjs → created-devices-D5owjN4C.mjs} +2 -2
  24. package/dist/{dependency-state-DsT7aKeo.mjs → dependency-state-B6cMXZab.mjs} +2 -2
  25. package/dist/{deps-D9IcKqXS.mjs → deps-urbmfXig.mjs} +3 -3
  26. package/dist/{detached-entry-DJHoca49.mjs → detached-entry-CiTEgMR1.mjs} +1 -1
  27. package/dist/dev-client-CroAIjjj.mjs +43 -0
  28. package/dist/{device-CjuuAJjQ.mjs → device-DZQjF7H5.mjs} +8 -7
  29. package/dist/device-capacity-BHwxNBlu.mjs +136 -0
  30. package/dist/device-host-worker.d.mts +1 -0
  31. package/dist/device-host-worker.mjs +964 -0
  32. package/dist/device-ios-BadmeL2V.mjs +381 -0
  33. package/dist/{device-lease-5c3WrVRm.mjs → device-lease-D-hLqx1b.mjs} +43 -36
  34. package/dist/{device-pool-BiyDyPP-.mjs → device-lease-run-CqjShqWW.mjs} +6 -126
  35. package/dist/device-pool-CUaEWMMm.mjs +125 -0
  36. package/dist/{device-remote-D87zEzaq.mjs → device-remote-D5WVWqOq.mjs} +69 -163
  37. package/dist/{doctor-x9pnU9fq.mjs → doctor-Cimf-gGB.mjs} +241 -103
  38. package/dist/{error-diagnostics-C5HzcwCW.mjs → error-diagnostics-B9L4Ae0x.mjs} +15 -18
  39. package/dist/{exec-DK8KaVHK.mjs → exec-CaErVv7t.mjs} +5 -1
  40. package/dist/{gc-ko6mUAYD.mjs → gc-Bqy4bjEu.mjs} +599 -108
  41. package/dist/{android-cas-iZK3DOCr.mjs → gradle-BsO1MdsH.mjs} +8 -294
  42. package/dist/{guide-6R0VNFqI.mjs → guide-B_uyKTxW.mjs} +2511 -239
  43. package/dist/{guide-status-BZrFPIA_.mjs → guide-status-CjEZMAD4.mjs} +3 -3
  44. package/dist/hosted-client-a-WmqE2L.mjs +197 -0
  45. package/dist/hosted-ios-CIWelIqf.mjs +220 -0
  46. package/dist/hosted-logs-D5g041Q3.mjs +187 -0
  47. package/dist/hosted-macos-BSZyIb3y.mjs +135 -0
  48. package/dist/idle-DYVymG92.mjs +1099 -0
  49. package/dist/idle-shutdown-CTsDnAde.mjs +122 -0
  50. package/dist/in-use-DvBw0Kxo.mjs +130 -0
  51. package/dist/{ios-BwDPrnYQ.mjs → ios-BaInn3sW.mjs} +201 -10
  52. package/dist/ios-Cr1h5gnO.mjs +189 -0
  53. package/dist/{ios-P1f5nm2n.mjs → ios-aq3QoGI3.mjs} +1159 -633
  54. package/dist/{ios-device-BiOsSVzd.mjs → ios-device-Ee7sDXfp.mjs} +1 -1
  55. package/dist/{ios-device-DGglU3E_.mjs → ios-device-FfCN_C8p.mjs} +12 -7
  56. package/dist/ios-state-DS5UpYTH.mjs +48 -0
  57. package/dist/{launch-verify-Deg4tB3Z.mjs → launch-verify-2DBRizjo.mjs} +5 -5
  58. package/dist/logs-CHEVK8zD.mjs +371 -0
  59. package/dist/machines-BIBUdhpj.mjs +265 -0
  60. package/dist/macos-CSYEAFmF.mjs +473 -0
  61. package/dist/macos-run.d.mts +5 -0
  62. package/dist/macos-run.mjs +2 -0
  63. package/dist/maintenance-run.d.mts +4 -0
  64. package/dist/maintenance-run.mjs +254 -0
  65. package/dist/{metro-CcEo8Fl8.mjs → metro-BeK1ZPz1.mjs} +20 -120
  66. package/dist/metro-gateway-BJ-rxQAB.mjs +84 -0
  67. package/dist/{named-ports-Blg8IxOh.mjs → named-ports-Bk_YXEmE.mjs} +6 -59
  68. package/dist/native-run-NaBrRFtQ.mjs +147 -0
  69. package/dist/{native-runtime-DjBHRg5m.mjs → native-runtime-DBzRnEKS.mjs} +9 -5
  70. package/dist/{ndjson-CDY9IkVC.mjs → ndjson-PclOIwVY.mjs} +2 -2
  71. package/dist/offload-worker.d.mts +122 -0
  72. package/dist/offload-worker.mjs +606 -0
  73. package/dist/{ownership-DyYDF5ww.mjs → ownership-DiA16AsN.mjs} +3 -2
  74. package/dist/ownership-hCkPzGmr.mjs +571 -0
  75. package/dist/{page-BHUzTDFt.mjs → page-QF_Ke10s.mjs} +7 -2
  76. package/dist/{ports-BguS83ex.mjs → ports-R2k4Knz3.mjs} +3 -3
  77. package/dist/prebuild-D2fsca_Z.mjs +213 -0
  78. package/dist/preview-DhVbvPAZ.mjs +318 -0
  79. package/dist/process-identity-BmRm1qV0.mjs +12 -0
  80. package/dist/{project-CWlvEaeN.mjs → project-Du91Ilvc.mjs} +74 -19
  81. package/dist/pull-request-pxnDCQzn.mjs +322 -0
  82. package/dist/pull-requests.d.mts +120 -0
  83. package/dist/pull-requests.mjs +2 -0
  84. package/dist/recordings-BIXOzVnr.mjs +37 -0
  85. package/dist/{reload-BiniwsSZ.mjs → reload-rM9jV1fz.mjs} +109 -24
  86. package/dist/{remote-cache-CcR6o6lI.mjs → remote-cache-C8-V13XN.mjs} +5 -5
  87. package/dist/run-f4vI6EnK.mjs +119 -0
  88. package/dist/{server-bare-DGfgyJ5J.mjs → server-bare-CpPGmnmO.mjs} +33 -15
  89. package/dist/server-expo-CVqO2c35.mjs +2 -0
  90. package/dist/{server-expo-BVuwQ65v.mjs → server-expo-MC0rHlOC.mjs} +8 -12
  91. package/dist/settings-BBpMcDbD.mjs +25 -0
  92. package/dist/{settings-B-fsC-Om.mjs → settings-ZkDzkzvB.mjs} +22 -10
  93. package/dist/settings-oTSkkNrM.mjs +1439 -0
  94. package/dist/settings.schema.json +845 -97
  95. package/dist/{simslim-DrDN4WuV.mjs → simslim-B8-7AoTI.mjs} +8 -132
  96. package/dist/{slot-launch-D5t8EiMf.mjs → slot-launch-B8VrHxn1.mjs} +11 -8
  97. package/dist/{spawn-claims-Drw_o-Fb.mjs → spawn-claims-D0XfSe8u.mjs} +1 -1
  98. package/dist/spawn-entry-jw19m8Nl.mjs +17 -0
  99. package/dist/stage-CzaiwFsI.mjs +143 -0
  100. package/dist/start-CX8Ig-U2.mjs +2 -0
  101. package/dist/{start-s4RXp1wy.mjs → start-ClDfwXa4.mjs} +116 -65
  102. package/dist/state-3DwSQIBK.mjs +41 -0
  103. package/dist/state-B4PQFXMW.d.mts +12 -0
  104. package/dist/{state-Dk9RVXI7.mjs → state-C1jB7Q8q.mjs} +70 -8
  105. package/dist/{state-DHqvsbqC.mjs → state-DcW6vOd_.mjs} +7 -4
  106. package/dist/stats-CEZ_i5je.mjs +77 -0
  107. package/dist/{status-BgAuxESg.mjs → status-BuvBFeII.mjs} +118 -20
  108. package/dist/{status-BsPAnfOI.mjs → status-Cs7VLg9D.mjs} +542 -283
  109. package/dist/status-DlKvVJBE.mjs +49 -0
  110. package/dist/stdout-reader-C8yjre1x.mjs +23 -0
  111. package/dist/stim-desktop-Cnz8CPnl.mjs +63 -0
  112. package/dist/stim-footprint.swift +32 -0
  113. package/dist/{stim-installations-CINXy_su.mjs → stim-installations-QMEI32v1.mjs} +1 -1
  114. package/dist/stop-B0Yxtmiy.mjs +48 -0
  115. package/dist/{stop-BzreOWJp.mjs → stop-Bffozsac.mjs} +83 -16
  116. package/dist/{stop-BWIF_6BK.mjs → stop-Dt_dM16A.mjs} +2 -2
  117. package/dist/stop-cause-CidxIqup.mjs +97 -0
  118. package/dist/supervisor-run.d.mts +43 -11
  119. package/dist/supervisor-run.mjs +363 -24
  120. package/dist/support-CAP8qDUF.mjs +316 -0
  121. package/dist/support-D4aYJniO.mjs +201 -0
  122. package/dist/teardown-CL6vDs2Y.mjs +727 -0
  123. package/dist/{xcode-Cp5KvK9x.mjs → toolchain-DkUflduf.mjs} +364 -14
  124. package/dist/trigger-Bemu8ytw.mjs +65 -0
  125. package/dist/trigger-CXts6qt1.mjs +2 -0
  126. package/dist/warm-progress-CpGv3Wb6.mjs +732 -0
  127. package/dist/{web-BsQs1WlO.mjs → web-DWUaMp8P.mjs} +37 -21
  128. package/dist/web-run.mjs +310 -13
  129. package/dist/workspace-process-lock-BL7Q0HCO.mjs +57 -0
  130. package/dist/workspace-state-B8-aJ1_5.mjs +371 -0
  131. package/dist/workspaces-BV4pdHFz.mjs +306 -0
  132. package/dist/{worktree-UrQWXSeb.mjs → worktree-K-xj9Db0.mjs} +625 -256
  133. package/dist/worktree-jnG8Iph5.mjs +2 -0
  134. package/package.json +10 -5
  135. package/shim/bundle-response.cjs +28 -12
  136. package/dist/activity-CYVDcrt3.mjs +0 -356
  137. package/dist/budget-CGDkhvv_.mjs +0 -1745
  138. package/dist/build-progress-DxKpwnaS.mjs +0 -247
  139. package/dist/idle-DXtx3RbX.mjs +0 -191
  140. package/dist/in-use-DlqBDKqY.mjs +0 -249
  141. package/dist/logs-Civ4gbBH.mjs +0 -486
  142. package/dist/merge-state-ytFQCeh4.mjs +0 -262
  143. package/dist/server-expo-DIHDhvGg.mjs +0 -2
  144. package/dist/settings-D61iNOsB.mjs +0 -629
  145. package/dist/start-Cetfvy8l.mjs +0 -2
  146. package/dist/stats-CFpFGlJV.mjs +0 -62
  147. package/dist/stats-CX1Z8Tca.mjs +0 -368
  148. package/dist/stim-desktop-CXt8-Z3J.mjs +0 -40
  149. package/dist/stop-cause-hjGBOZTG.mjs +0 -1231
  150. package/dist/worktree-BVrHsYZG.mjs +0 -752
  151. package/dist/worktree-CHWWvm5v.mjs +0 -2
  152. package/dist/{metro-store-DzeWkfqW.mjs → metro-store-CDDZHtPl.mjs} +1 -1
@@ -0,0 +1,2 @@
1
+ import { a as porcelainPath, c as registerWarm, d as removalRemedy, f as removeWorktreeTarget, i as matchWorktreeEntry, l as removalBlockers, m as worktreeCommand, n as excludePodChurn, o as reclaimKeys, p as warmedWorkspace, r as excludeWatchmanCookies, s as registerRemove, t as dependencyInstallCommand, u as removalPath } from "./worktree-K-xj9Db0.mjs";
2
+ export { worktreeCommand as default, dependencyInstallCommand, excludePodChurn, excludeWatchmanCookies, matchWorktreeEntry, porcelainPath, reclaimKeys, registerRemove, registerWarm, removalBlockers, removalPath, removalRemedy, removeWorktreeTarget, warmedWorkspace };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stim",
3
- "version": "1.14.0",
3
+ "version": "1.16.0",
4
4
  "description": "Stim: fast, isolated React Native environments for coding agents",
5
5
  "keywords": [
6
6
  "agent",
@@ -39,13 +39,18 @@
39
39
  "types": "./dist/cache-manifest.d.mts",
40
40
  "module-sync": "./dist/cache-manifest.mjs",
41
41
  "default": "./dist/cache-manifest.mjs"
42
+ },
43
+ "./pull-requests": {
44
+ "types": "./dist/pull-requests.d.mts",
45
+ "module-sync": "./dist/pull-requests.mjs",
46
+ "default": "./dist/pull-requests.mjs"
42
47
  }
43
48
  },
44
49
  "dependencies": {
45
50
  "@expo/fingerprint": "^0.20.10",
46
- "@stim-cli/cache": "^1.14.0",
47
- "@stim-cli/core": "^1.14.0",
48
- "@stim-cli/metro": "^1.14.0",
51
+ "@stim-cli/cache": "^1.16.0",
52
+ "@stim-cli/core": "^1.16.0",
53
+ "@stim-cli/metro": "^1.16.0",
49
54
  "chalk": "^6.0.0",
50
55
  "commander": "^15.0.0",
51
56
  "cross-spawn": "^7.0.6",
@@ -53,7 +58,7 @@
53
58
  "ws": "^8.21.3"
54
59
  },
55
60
  "devDependencies": {
56
- "@stim-cli/expo-build-cache": "1.14.0",
61
+ "@stim-cli/expo-build-cache": "1.16.0",
57
62
  "@types/cross-spawn": "^6.0.6",
58
63
  "@types/which": "^3.0.4",
59
64
  "@types/ws": "^8.18.1",
@@ -3,6 +3,10 @@
3
3
  const { randomUUID } = require('node:crypto');
4
4
 
5
5
  const LOOPBACK = new Set(['127.0.0.1', '::1', '::ffff:127.0.0.1']);
6
+ // Metro writes each progress update of a multipart bundle response as its own JSON part:
7
+ // https://github.com/facebook/metro/blob/main/packages/metro/src/Server.js (`writeChunk` with done, total, percent).
8
+ const PROGRESS_PART = /^\{"done":(\d+),"total":(\d+),"percent":(\d+)\}$/;
9
+ const PROGRESS_INTERVAL_MS = 1000;
6
10
 
7
11
  function clientPidFromLsof(output, clientPort, serverPort) {
8
12
  const connection = new RegExp(`:${clientPort}->\\S*:${serverPort}$`);
@@ -30,7 +34,7 @@ function bundleResponseMiddleware(write, { runLsof } = {}) {
30
34
  res.end('ready');
31
35
  return;
32
36
  }
33
- if (req.headers['x-stim-metro-warmup'] === '1') return next();
37
+ const prefetch = req.headers['x-stim-metro-warmup'] === '1';
34
38
  let url;
35
39
  try {
36
40
  url = new URL(req.url, 'http://localhost');
@@ -42,34 +46,46 @@ function bundleResponseMiddleware(write, { runLsof } = {}) {
42
46
  return next();
43
47
  }
44
48
  const requestId = randomUUID();
45
- const clientPid = platform === 'ios' ? lookupClientPid(req, runLsof) : null;
46
- const send = (ts, event, statusCode, pid) => {
49
+ const kind = prefetch ? 'prefetch' : 'response';
50
+ const clientPid = platform === 'ios' && !prefetch ? lookupClientPid(req, runLsof) : null;
51
+ const send = (ts, stage, statusCode, pid, progress) => {
47
52
  try {
48
53
  write({
49
54
  ts,
50
55
  src: 'metro',
51
- level: event === 'bundle_response_failed' ? 'error' : 'debug',
52
- event,
56
+ level: stage === 'failed' && !prefetch ? 'error' : 'debug',
57
+ event: `bundle_${kind}_${stage}`,
53
58
  platform,
54
59
  requestId,
55
- statusCode,
56
- msg: `${platform} bundle response ${event.slice('bundle_response_'.length)}`,
60
+ ...(progress ?? { statusCode }),
61
+ msg: progress ? `${platform} bundle ${progress.percent}%` : `${platform} bundle ${kind} ${stage}`,
57
62
  ...(pid ? { clientPid: pid } : {}),
58
63
  });
59
64
  } catch {}
60
65
  };
61
- const emit = (event, statusCode) => {
66
+ const emit = (stage, statusCode, progress) => {
62
67
  const ts = Date.now();
63
- if (clientPid) clientPid.then((pid) => send(ts, event, statusCode, pid));
64
- else send(ts, event, statusCode, null);
68
+ if (clientPid) clientPid.then((pid) => send(ts, stage, statusCode, pid, progress));
69
+ else send(ts, stage, statusCode, null, progress);
70
+ };
71
+ emit('started');
72
+ const writeChunk = res.write;
73
+ let progressAt = 0;
74
+ res.write = function (chunk, ...rest) {
75
+ const part = typeof chunk === 'string' && chunk.length < 100 ? PROGRESS_PART.exec(chunk) : null;
76
+ const now = part ? Date.now() : 0;
77
+ if (part && now - progressAt >= PROGRESS_INTERVAL_MS) {
78
+ progressAt = now;
79
+ emit('progress', undefined, { done: Number(part[1]), total: Number(part[2]), percent: Number(part[3]) });
80
+ }
81
+ return writeChunk.call(this, chunk, ...rest);
65
82
  };
66
- emit('bundle_response_started');
67
83
  let ended = false;
68
84
  const finish = (complete) => {
69
85
  if (ended) return;
70
86
  ended = true;
71
87
  const success = complete && (res.statusCode === 200 || res.statusCode === 304);
72
- emit(success ? 'bundle_response_finished' : 'bundle_response_failed', res.statusCode);
88
+ emit(success ? 'finished' : 'failed', res.statusCode);
73
89
  };
74
90
  res.once('finish', () => finish(true));
75
91
  res.once('close', () => finish(false));
@@ -1,356 +0,0 @@
1
- import { O as process_identity_exports } from "./config-lvc7d3C2.mjs";
2
- import { t as getExecutor } from "./exec-DK8KaVHK.mjs";
3
- import { M as workspaceLogsDir, h as readWorkspaceState } from "./metro-CcEo8Fl8.mjs";
4
- import { i as listLeaseFiles, r as leaseIsExpired } from "./device-lease-5c3WrVRm.mjs";
5
- import { closeSync, fstatSync, openSync, readFileSync, readSync, readdirSync } from "node:fs";
6
- import { basename, join, resolve } from "node:path";
7
- import { homedir } from "node:os";
8
- //#region src/devices/activity.ts
9
- const ACTIVE_WINDOW_MS = 6e5;
10
- const LOG_TAIL_BYTES = 262144;
11
- function classifyActivity(evidence, now) {
12
- const last = evidence.recency.reduce((best, entry) => Number.isFinite(entry.at) && (!best || entry.at > best.at) ? entry : best, null);
13
- const lastActivityAt = last ? { lastActivityAt: new Date(last.at).toISOString() } : {};
14
- const [first] = evidence.drivers;
15
- if (first) {
16
- const { basis: _basis, ...driver } = first;
17
- return {
18
- state: "driven",
19
- driver,
20
- ...lastActivityAt,
21
- basis: [...new Set(evidence.drivers.map((entry) => entry.basis))]
22
- };
23
- }
24
- if (evidence.unknown.length) return {
25
- state: "unknown",
26
- ...lastActivityAt,
27
- basis: [...new Set(evidence.unknown)]
28
- };
29
- return {
30
- state: Boolean(last && now - last.at < ACTIVE_WINDOW_MS) ? "active" : "idle",
31
- ...lastActivityAt,
32
- basis: [...new Set(evidence.recency.map((entry) => entry.basis))]
33
- };
34
- }
35
- function idleForMs(activity, now) {
36
- if (activity?.state !== "idle" || !activity.lastActivityAt) return null;
37
- const at = Date.parse(activity.lastActivityAt);
38
- return Number.isFinite(at) ? Math.max(0, now - at) : null;
39
- }
40
- function field(entry, pidKey, startKey) {
41
- const pid = entry[pidKey];
42
- const startTime = entry[startKey];
43
- if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0 || typeof startTime !== "string") return null;
44
- return {
45
- pid,
46
- startTime
47
- };
48
- }
49
- function parseObject(raw) {
50
- if (raw === null) return null;
51
- try {
52
- const parsed = JSON.parse(raw);
53
- return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
54
- } catch {
55
- return null;
56
- }
57
- }
58
- function parseAgentDeviceRecord(kind, path, raw) {
59
- const entry = parseObject(raw);
60
- const stem = basename(path, ".json");
61
- const device = entry?.device && typeof entry.device === "object" ? entry.device : null;
62
- const recordedId = kind === "claim" ? device?.id : entry?.deviceId;
63
- const deviceId = typeof recordedId === "string" && recordedId ? recordedId : kind === "runner-lease" ? stem : null;
64
- const createdAtMs = typeof entry?.createdAtMs === "number" ? entry.createdAtMs : null;
65
- return {
66
- path,
67
- kind,
68
- deviceId,
69
- session: kind === "claim" && typeof entry?.session === "string" && entry.session ? entry.session : null,
70
- workspace: kind === "claim" && typeof entry?.workspace === "string" && entry.workspace ? entry.workspace : null,
71
- deviceName: kind === "claim" && typeof device?.name === "string" ? device.name : null,
72
- readable: Boolean(entry && deviceId),
73
- owner: entry ? field(entry, "ownerPid", "ownerStartTime") : null,
74
- runner: entry && kind === "runner-lease" ? field(entry, "runnerPid", "runnerStartTime") : null,
75
- createdAtMs
76
- };
77
- }
78
- function processMatches(entry, startOf) {
79
- const start = startOf(entry.pid);
80
- if (start.status === "gone") return "dead";
81
- if (start.status === "unknown") return "unknown";
82
- const recorded = Date.parse(entry.startTime);
83
- if (!Number.isFinite(recorded)) return "unknown";
84
- return Math.floor(start.startedAtMs / 1e3) === Math.floor(recorded / 1e3) ? "live" : "dead";
85
- }
86
- function agentDeviceLiveness(record, startOf) {
87
- const results = (record.kind === "claim" ? [record.owner] : [record.owner, record.runner]).map((entry) => entry ? processMatches(entry, startOf) : "unknown");
88
- if (results.includes("dead")) return "dead";
89
- if (!record.readable || results.includes("unknown")) return "unknown";
90
- return "live";
91
- }
92
- const HOST_PROCESS_COLUMNS = "pid=,ppid=,rss=,%cpu=,lstart=,command=";
93
- function parseProcessTable(output) {
94
- const rows = [];
95
- for (const line of output.split("\n")) {
96
- const match = /^\s*(\d+)\s+(\d+)\s+(\d+)\s+(\d+(?:[.,]\d+)?)\s+(\w{3}\s+\w{3}\s+\d+\s+\d{1,2}:\d{2}:\d{2}\s+\d{4})\s+(.*)$/.exec(line);
97
- if (!match) continue;
98
- const at = Date.parse(match[5]);
99
- rows.push({
100
- pid: Number(match[1]),
101
- ppid: Number(match[2]),
102
- rssKb: Number(match[3]),
103
- cpuPercent: Number(match[4].replace(",", ".")),
104
- startedAt: Number.isFinite(at) ? new Date(at).toISOString() : null,
105
- command: match[6]
106
- });
107
- }
108
- return rows;
109
- }
110
- function namesDevice(command, id) {
111
- const escaped = id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
112
- return new RegExp(`(^|[^A-Za-z0-9])${escaped}([^A-Za-z0-9]|$)`).test(command);
113
- }
114
- function driverTool(command, id) {
115
- if (!namesDevice(command, id)) return null;
116
- if (/\bsimctl\s+spawn\b.*\blog\s+stream\b/.test(command) || /\blogcat\b/.test(command)) return null;
117
- if (/agent-device/i.test(command)) return "agent-device";
118
- if (/idb_companion/.test(command)) return "idb";
119
- if (/maestro/i.test(command)) return "maestro";
120
- if (/appium|WebDriverAgent/i.test(command)) return "appium";
121
- if (/\bxcodebuild\b.*\btest(-without-building)?\b/.test(command)) return "xcodebuild";
122
- if (/\bsimctl\s+(io|spawn)\b/.test(command)) return "simctl";
123
- return null;
124
- }
125
- function parseAndroidInstrumentation(output) {
126
- const found = [];
127
- for (const line of output.split("\n")) {
128
- const match = /^\s*(\d+)\s+(.*)$/.exec(line);
129
- if (!match) continue;
130
- const args = match[2];
131
- if (/uiautomator/.test(args)) found.push({
132
- pid: Number(match[1]),
133
- tool: "uiautomator"
134
- });
135
- else if (/androidx\.test|\binstrument\b/.test(args)) found.push({
136
- pid: Number(match[1]),
137
- tool: "instrumentation"
138
- });
139
- }
140
- return found;
141
- }
142
- function latestDeviceLogAt(lines, target) {
143
- return latestRecordAt(lines, (record) => {
144
- if (record.src !== "device" || record.platform !== target.platform || !isDeviceRecord(record)) return false;
145
- if (target.slot === "default") return record.slot === void 0;
146
- return record.slot === target.slot && (record.deviceId === void 0 || record.deviceId === target.id);
147
- });
148
- }
149
- function latestBundleRequestAt(lines, platform) {
150
- return latestRecordAt(lines, (record) => record.event === "bundle_response_started" && record.platform === platform);
151
- }
152
- function latestRecordAt(lines, matches) {
153
- for (let i = lines.length - 1; i >= 0; i--) {
154
- const record = parseObject(lines[i]);
155
- if (record && typeof record.ts === "number" && matches(record)) return record.ts;
156
- }
157
- return null;
158
- }
159
- function isDeviceRecord(record) {
160
- return !(typeof record.event === "string" && record.event.startsWith("collector_"));
161
- }
162
- function tailLines(path) {
163
- let fd;
164
- try {
165
- fd = openSync(path, "r");
166
- const size = fstatSync(fd).size;
167
- const length = Math.min(size, LOG_TAIL_BYTES);
168
- const buffer = Buffer.alloc(length);
169
- readSync(fd, buffer, 0, length, size - length);
170
- const lines = buffer.toString("utf8").split("\n");
171
- return length < size ? lines.slice(1) : lines;
172
- } catch {
173
- return null;
174
- } finally {
175
- if (fd !== void 0) closeSync(fd);
176
- }
177
- }
178
- function envDir(name) {
179
- const value = process.env[name]?.trim();
180
- return value ? resolve(value) : null;
181
- }
182
- function agentDeviceDirs(home) {
183
- const root = join(home, ".agent-device");
184
- const leaseDirs = [envDir("AGENT_DEVICE_IOS_RUNNER_LEASE_DIR") ?? join(root, "apple-runner", "leases"), join(root, "ios-runner", "leases")];
185
- return [{
186
- kind: "claim",
187
- dir: envDir("AGENT_DEVICE_CLAIMS_DIR") ?? join(root, "device-claims")
188
- }, ...[...new Set(leaseDirs)].map((dir) => ({
189
- kind: "runner-lease",
190
- dir
191
- }))];
192
- }
193
- function readAgentDeviceRecords(home) {
194
- return agentDeviceDirs(home).flatMap(({ kind, dir }) => {
195
- let names;
196
- try {
197
- names = readdirSync(dir).filter((name) => name.endsWith(".json"));
198
- } catch {
199
- return [];
200
- }
201
- return names.map((name) => {
202
- const path = join(dir, name);
203
- let raw = null;
204
- try {
205
- raw = readFileSync(path, "utf8");
206
- } catch {}
207
- return parseAgentDeviceRecord(kind, path, raw);
208
- });
209
- });
210
- }
211
- function createDeviceProcessTables() {
212
- const exec = getExecutor();
213
- let host;
214
- const android = /* @__PURE__ */ new Map();
215
- return {
216
- host() {
217
- if (host === void 0) {
218
- const output = exec.runFileQuiet("ps", [
219
- "-axww",
220
- "-o",
221
- HOST_PROCESS_COLUMNS
222
- ], { timeoutMs: 5e3 });
223
- host = output === null ? null : parseProcessTable(output);
224
- }
225
- return host;
226
- },
227
- android(serial) {
228
- if (!android.has(serial)) android.set(serial, exec.runFileQuiet("adb", [
229
- "-s",
230
- serial,
231
- "shell",
232
- "ps",
233
- "-A",
234
- "-o",
235
- "PID,ARGS"
236
- ], { timeoutMs: 5e3 }));
237
- return android.get(serial) ?? null;
238
- }
239
- };
240
- }
241
- function createActivityReader({ now = Date.now(), home = homedir(), startOf = process_identity_exports.inspectProcessStart, leaseFiles = listLeaseFiles, tables = createDeviceProcessTables() } = {}) {
242
- let agentDevice;
243
- let stimLeases;
244
- const logs = /* @__PURE__ */ new Map();
245
- const log = (path) => {
246
- if (!logs.has(path)) logs.set(path, tailLines(path));
247
- return logs.get(path) ?? null;
248
- };
249
- return (target) => {
250
- const evidence = {
251
- drivers: [],
252
- unknown: [],
253
- recency: []
254
- };
255
- agentDevice ??= readAgentDeviceRecords(home).map((record) => ({
256
- record,
257
- liveness: agentDeviceLiveness(record, startOf)
258
- }));
259
- for (const { record, liveness } of agentDevice) {
260
- if (record.deviceId !== null && record.deviceId !== target.id) continue;
261
- const basis = record.kind === "claim" ? "agent-device-claim" : "agent-device-lease";
262
- if (liveness === "live") evidence.drivers.push({
263
- basis,
264
- tool: "agent-device",
265
- pid: record.owner?.pid ?? null,
266
- since: record.createdAtMs !== null ? new Date(record.createdAtMs).toISOString() : null
267
- });
268
- else if (liveness === "unknown") evidence.unknown.push(basis);
269
- }
270
- try {
271
- stimLeases ??= leaseFiles();
272
- } catch {
273
- stimLeases = [];
274
- evidence.unknown.push("device-lock");
275
- }
276
- for (const entry of stimLeases) {
277
- if (entry.platform !== target.platform || entry.id !== target.id) continue;
278
- if (!entry.lease) evidence.unknown.push("device-lock");
279
- else if (!leaseIsExpired(entry.lease, now)) evidence.drivers.push({
280
- basis: "device-lock",
281
- tool: "stim device lock",
282
- pid: null,
283
- since: entry.lease.grantedAt
284
- });
285
- }
286
- const processes = tables.host();
287
- if (processes === null) evidence.unknown.push("driver-process");
288
- for (const row of processes ?? []) {
289
- const tool = driverTool(row.command, target.id);
290
- if (tool) evidence.drivers.push({
291
- basis: "driver-process",
292
- tool,
293
- pid: row.pid,
294
- since: row.startedAt
295
- });
296
- }
297
- if (target.platform === "android") {
298
- const output = tables.android(target.id);
299
- if (output === null) evidence.unknown.push("instrumentation");
300
- for (const { pid, tool } of parseAndroidInstrumentation(output ?? "")) evidence.drivers.push({
301
- basis: "instrumentation",
302
- tool,
303
- pid,
304
- since: null
305
- });
306
- }
307
- if (target.workspace) {
308
- const dir = workspaceLogsDir(target.workspace);
309
- const deviceAt = latestDeviceLogAt(log(join(dir, "device.ndjson")) ?? [], target);
310
- if (deviceAt !== null) evidence.recency.push({
311
- basis: "device-log",
312
- at: deviceAt
313
- });
314
- const bundleAt = latestBundleRequestAt(log(join(dir, "metro.ndjson")) ?? [], target.platform);
315
- if (bundleAt !== null) evidence.recency.push({
316
- basis: "metro-bundle",
317
- at: bundleAt
318
- });
319
- const usedAt = Date.parse(String(readWorkspaceState(target.workspace)?.lastUsedAt ?? ""));
320
- if (Number.isFinite(usedAt)) evidence.recency.push({
321
- basis: "workspace-use",
322
- at: usedAt
323
- });
324
- }
325
- return classifyActivity(evidence, now);
326
- };
327
- }
328
- function workspaceActivity(workspace, now = Date.now()) {
329
- const dir = workspaceLogsDir(workspace);
330
- const state = readWorkspaceState(workspace);
331
- const recency = [];
332
- const deviceAt = latestRecordAt(tailLines(join(dir, "device.ndjson")) ?? [], (record) => record.src === "device" && isDeviceRecord(record));
333
- if (deviceAt !== null) recency.push({
334
- basis: "device-log",
335
- at: deviceAt
336
- });
337
- const bundleAt = latestRecordAt(tailLines(join(dir, "metro.ndjson")) ?? [], (record) => record.event === "bundle_response_started");
338
- if (bundleAt !== null) recency.push({
339
- basis: "metro-bundle",
340
- at: bundleAt
341
- });
342
- for (const [basis, value] of [["workspace-use", state?.lastUsedAt], ["supervisor-start", state?.supervisor?.startedAt]]) {
343
- const at = Date.parse(String(value ?? ""));
344
- if (Number.isFinite(at)) recency.push({
345
- basis,
346
- at
347
- });
348
- }
349
- return classifyActivity({
350
- drivers: [],
351
- unknown: [],
352
- recency
353
- }, now);
354
- }
355
- //#endregion
356
- export { readAgentDeviceRecords as a, idleForMs as i, createActivityReader as n, workspaceActivity as o, createDeviceProcessTables as r, agentDeviceLiveness as t };