stim 1.14.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.
- package/README.md +2 -0
- package/dist/{activity-CYVDcrt3.mjs → activity-B2-kb7k0.mjs} +281 -41
- package/dist/{android-XpvCrV7q.mjs → android-CYmq0jNi.mjs} +2 -2
- package/dist/{android-LbPFRWVL.mjs → android-Cu_L4RpL.mjs} +502 -407
- package/dist/{android-d6rGq1pA.mjs → android-Db2eEMaf.mjs} +152 -65
- package/dist/android-cas-PnKfAdB2.mjs +167 -0
- package/dist/android-cas-compiler.mjs +2 -2
- package/dist/{app-install-BpgSgkJj.mjs → app-install-CAAplXAt.mjs} +95 -161
- package/dist/{budget-CGDkhvv_.mjs → budget-CMiqghig.mjs} +1038 -73
- package/dist/{build-plan-Dcr-3ie0.mjs → build-plan-aeGMFX7e.mjs} +193 -653
- package/dist/build-progress-C2Jv1AtA.mjs +872 -0
- package/dist/build-slots-Df1ApSuH.mjs +127 -0
- package/dist/{cache-manifest-C68QQ0ua.mjs → cache-manifest-DnUHBJaU.mjs} +2 -2
- package/dist/cache-manifest.mjs +1 -1
- package/dist/{chrome-BPCiC3ZX.mjs → chrome-BrcVWQyD.mjs} +1 -1
- package/dist/cli-0cX5s4NM.mjs +45 -0
- package/dist/cli.mjs +6 -42
- package/dist/client-C2IS36Qf.mjs +1333 -0
- package/dist/collector-run.mjs +8 -7
- package/dist/{command-output-DVcpw6eE.mjs → command-output-CliHHn7a.mjs} +1 -1
- package/dist/{config-lvc7d3C2.mjs → config-D0DbpZtM.mjs} +28 -29
- package/dist/{created-devices-CllH_8eb.mjs → created-devices-BwVb3liH.mjs} +2 -2
- package/dist/{dependency-state-DsT7aKeo.mjs → dependency-state-BmsjKZ2H.mjs} +2 -2
- package/dist/{deps-D9IcKqXS.mjs → deps-BFlKbOIp.mjs} +3 -3
- package/dist/{detached-entry-DJHoca49.mjs → detached-entry-CiTEgMR1.mjs} +1 -1
- package/dist/dev-client-CjzLoNs0.mjs +80 -0
- package/dist/{device-CjuuAJjQ.mjs → device-BZ1IyS5A.mjs} +7 -7
- package/dist/device-capacity-DMuKC7nx.mjs +136 -0
- package/dist/device-host-worker.d.mts +1 -0
- package/dist/device-host-worker.mjs +769 -0
- package/dist/device-ios-DMQBUPus.mjs +381 -0
- package/dist/{device-lease-5c3WrVRm.mjs → device-lease-W5Pi1q38.mjs} +43 -36
- package/dist/{device-pool-BiyDyPP-.mjs → device-pool-B2r38YjS.mjs} +4 -4
- package/dist/{device-remote-D87zEzaq.mjs → device-remote-9ulPsdhY.mjs} +68 -162
- package/dist/{doctor-x9pnU9fq.mjs → doctor-BcE5xsBn.mjs} +191 -99
- package/dist/{error-diagnostics-C5HzcwCW.mjs → error-diagnostics-Cb0P1WLS.mjs} +12 -11
- package/dist/{exec-DK8KaVHK.mjs → exec-CaErVv7t.mjs} +5 -1
- package/dist/{gc-ko6mUAYD.mjs → gc-BuL1Genr.mjs} +429 -101
- package/dist/{android-cas-iZK3DOCr.mjs → gradle-CmdOBq1d.mjs} +8 -294
- package/dist/{guide-6R0VNFqI.mjs → guide-cCSGRFd3.mjs} +1959 -222
- package/dist/{guide-status-BZrFPIA_.mjs → guide-status-CIoWK6mG.mjs} +3 -3
- package/dist/hosted-macos-DwowNU7W.mjs +447 -0
- package/dist/idle-shutdown-B5LPaNyY.mjs +122 -0
- package/dist/in-use-CndPMilN.mjs +110 -0
- package/dist/{ios-BwDPrnYQ.mjs → ios-3CtCRhwr.mjs} +201 -10
- package/dist/{ios-P1f5nm2n.mjs → ios-Chob8ny7.mjs} +512 -263
- package/dist/{ios-device-DGglU3E_.mjs → ios-device-DERmMmk-.mjs} +12 -7
- package/dist/{ios-device-BiOsSVzd.mjs → ios-device-Ee7sDXfp.mjs} +1 -1
- package/dist/{launch-verify-Deg4tB3Z.mjs → launch-verify-CLlofxRt.mjs} +4 -4
- package/dist/{logs-Civ4gbBH.mjs → logs-h9h6MUvE.mjs} +95 -13
- package/dist/machine-usage-D_YYMzfR.mjs +283 -0
- package/dist/machines-CYtOCqcj.mjs +259 -0
- package/dist/macos-BwtbUtTN.mjs +468 -0
- package/dist/macos-run.d.mts +5 -0
- package/dist/macos-run.mjs +2 -0
- package/dist/{merge-state-ytFQCeh4.mjs → merge-state-BnxLNQiM.mjs} +22 -12
- package/dist/{metro-CcEo8Fl8.mjs → metro-Di8EN2Tb.mjs} +20 -120
- package/dist/{named-ports-Blg8IxOh.mjs → named-ports-Dj_XBNaL.mjs} +6 -59
- package/dist/native-run-DFDoIRbY.mjs +147 -0
- package/dist/{native-runtime-DjBHRg5m.mjs → native-runtime-i6OkVg-w.mjs} +9 -5
- package/dist/offload-worker.d.mts +121 -0
- package/dist/offload-worker.mjs +601 -0
- package/dist/{ownership-DyYDF5ww.mjs → ownership-BkYxUtuj.mjs} +3 -2
- package/dist/{page-BHUzTDFt.mjs → page-QF_Ke10s.mjs} +7 -2
- package/dist/{ports-BguS83ex.mjs → ports-DddwttKM.mjs} +3 -3
- package/dist/prebuild-BvxDlD6C.mjs +213 -0
- package/dist/process-identity-BmRm1qV0.mjs +12 -0
- package/dist/{project-CWlvEaeN.mjs → project-CV3tDFHl.mjs} +9 -18
- package/dist/pull-request-pxnDCQzn.mjs +322 -0
- package/dist/pull-requests.d.mts +120 -0
- package/dist/pull-requests.mjs +2 -0
- package/dist/recordings-GEXp4iBT.mjs +37 -0
- package/dist/{reload-BiniwsSZ.mjs → reload-ChPiwvot.mjs} +70 -22
- package/dist/{remote-cache-CcR6o6lI.mjs → remote-cache-CO2elm_b.mjs} +4 -4
- package/dist/run-B-xqRrsg.mjs +119 -0
- package/dist/{server-bare-DGfgyJ5J.mjs → server-bare-X7PUgizL.mjs} +33 -15
- package/dist/{server-expo-BVuwQ65v.mjs → server-expo-DhtX6bom.mjs} +6 -10
- package/dist/server-expo-KJeD109I.mjs +2 -0
- package/dist/{settings-D61iNOsB.mjs → settings-CduVcoxj.mjs} +77 -26
- package/dist/{settings-B-fsC-Om.mjs → settings-DbL6uhH8.mjs} +16 -10
- package/dist/settings.schema.json +357 -2
- package/dist/{simslim-DrDN4WuV.mjs → simslim-Ciluxlq4.mjs} +8 -132
- package/dist/{slot-launch-D5t8EiMf.mjs → slot-launch-BY0BrOLh.mjs} +6 -6
- package/dist/{spawn-claims-Drw_o-Fb.mjs → spawn-claims-D0XfSe8u.mjs} +1 -1
- package/dist/spawn-entry-DncpJvWL.mjs +16 -0
- package/dist/stage-CzaiwFsI.mjs +143 -0
- package/dist/start-CPO4wRg7.mjs +2 -0
- package/dist/{start-s4RXp1wy.mjs → start-CUD9nasA.mjs} +108 -61
- package/dist/state-B4PQFXMW.d.mts +12 -0
- package/dist/state-BxCgeTQu.mjs +41 -0
- package/dist/{state-DHqvsbqC.mjs → state-CaeaHbDA.mjs} +7 -4
- package/dist/{state-Dk9RVXI7.mjs → state-DFm77wRn.mjs} +67 -5
- package/dist/stats-DBLLFmKd.mjs +66 -0
- package/dist/{status-BsPAnfOI.mjs → status-DYXxNOT0.mjs} +423 -268
- package/dist/{status-BgAuxESg.mjs → status-l84Gbvp2.mjs} +116 -20
- package/dist/stdout-reader-C8yjre1x.mjs +23 -0
- package/dist/stim-desktop-Cnz8CPnl.mjs +63 -0
- package/dist/stim-footprint.swift +32 -0
- package/dist/{stim-installations-CINXy_su.mjs → stim-installations-QMEI32v1.mjs} +1 -1
- package/dist/{stop-BzreOWJp.mjs → stop-Bad61z_2.mjs} +38 -12
- package/dist/stop-Bvf1MflS.mjs +48 -0
- package/dist/{stop-BWIF_6BK.mjs → stop-CceYh1yG.mjs} +2 -2
- package/dist/stop-cause-CZ_Mr-v9.mjs +665 -0
- package/dist/supervisor-run.d.mts +43 -11
- package/dist/supervisor-run.mjs +227 -19
- package/dist/support-CDQbDkL5.mjs +315 -0
- package/dist/support-WLOzVKUv.mjs +176 -0
- package/dist/teardown-BYuQafiA.mjs +718 -0
- package/dist/{xcode-Cp5KvK9x.mjs → toolchain-DmeYjXD9.mjs} +363 -13
- package/dist/warm-progress-DVHLN4uC.mjs +463 -0
- package/dist/{idle-DXtx3RbX.mjs → watchman-Bx0wceoF.mjs} +55 -8
- package/dist/{web-BsQs1WlO.mjs → web-k6w8-HnE.mjs} +37 -20
- package/dist/web-run.mjs +309 -12
- package/dist/workspace-process-lock-BL7Q0HCO.mjs +57 -0
- package/dist/workspace-state-CL2V2Ons.mjs +371 -0
- package/dist/worktree-BT_uULFx.mjs +2 -0
- package/dist/{worktree-BVrHsYZG.mjs → worktree-C0c5Udwa.mjs} +55 -54
- package/dist/{worktree-UrQWXSeb.mjs → worktree-qXKzADSC.mjs} +154 -252
- package/package.json +10 -5
- package/shim/bundle-response.cjs +28 -12
- package/dist/build-progress-DxKpwnaS.mjs +0 -247
- package/dist/in-use-DlqBDKqY.mjs +0 -249
- package/dist/server-expo-DIHDhvGg.mjs +0 -2
- package/dist/start-Cetfvy8l.mjs +0 -2
- package/dist/stats-CFpFGlJV.mjs +0 -62
- package/dist/stats-CX1Z8Tca.mjs +0 -368
- package/dist/stim-desktop-CXt8-Z3J.mjs +0 -40
- package/dist/stop-cause-hjGBOZTG.mjs +0 -1231
- package/dist/worktree-CHWWvm5v.mjs +0 -2
- package/dist/{metro-store-DzeWkfqW.mjs → metro-store-CDDZHtPl.mjs} +1 -1
package/README.md
CHANGED
|
@@ -43,6 +43,8 @@ reference:
|
|
|
43
43
|
|
|
44
44
|
- [Getting started](https://stim.appandflow.com/docs/getting-started): terms,
|
|
45
45
|
the first run, parallel worktrees, and what to do when a run fails.
|
|
46
|
+
- [Native macOS prototype](https://stim.appandflow.com/docs/macos): Swift Package
|
|
47
|
+
Debug apps, workspace logs and local window viewing in Stim Desktop.
|
|
46
48
|
- [Web in an owned Chrome](https://stim.appandflow.com/docs/web): `stim web`
|
|
47
49
|
for Expo web and other web servers, page logs, and attaching browser tools.
|
|
48
50
|
- [Worktrees](https://stim.appandflow.com/docs/worktrees): `worktree warm`,
|
|
@@ -1,16 +1,130 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as
|
|
3
|
-
import {
|
|
4
|
-
import { i as listLeaseFiles, r as leaseIsExpired } from "./device-lease-
|
|
5
|
-
import { closeSync, fstatSync, openSync, readFileSync, readSync, readdirSync } from "node:fs";
|
|
1
|
+
import { t as getExecutor } from "./exec-CaErVv7t.mjs";
|
|
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";
|
|
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
8
|
import { homedir } from "node:os";
|
|
9
|
+
//#region src/devices/automation-tools.ts
|
|
10
|
+
const AUTOMATION_TOOLS = [
|
|
11
|
+
{
|
|
12
|
+
tool: "agent-browser",
|
|
13
|
+
platforms: ["web"],
|
|
14
|
+
cdp: /agent-browser/i
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
tool: "agent-device",
|
|
18
|
+
platforms: [
|
|
19
|
+
"ios",
|
|
20
|
+
"android",
|
|
21
|
+
"web"
|
|
22
|
+
],
|
|
23
|
+
host: /agent-device/i,
|
|
24
|
+
cdp: /agent-device/i
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
tool: "argent",
|
|
28
|
+
platforms: [
|
|
29
|
+
"ios",
|
|
30
|
+
"android",
|
|
31
|
+
"web"
|
|
32
|
+
],
|
|
33
|
+
host: /\/bin\/[\w-]+\/(simulator-server(\.exe)?\s+(ios|android|android_device)|ax-service)\b|com\.argent\.androiddevtools/,
|
|
34
|
+
instrumentation: /com\.argent\.androiddevtools/,
|
|
35
|
+
cdp: /@swmansion\/argent\/dist\/tool-server/
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
tool: "idb",
|
|
39
|
+
platforms: ["ios"],
|
|
40
|
+
host: /idb_companion/
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
tool: "maestro",
|
|
44
|
+
platforms: ["ios", "android"],
|
|
45
|
+
host: /maestro/i
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
tool: "appium",
|
|
49
|
+
platforms: ["ios", "android"],
|
|
50
|
+
host: /appium|WebDriverAgent/i
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
tool: "xcodebuild",
|
|
54
|
+
platforms: ["ios"],
|
|
55
|
+
host: /\bxcodebuild\b.*\btest(-without-building)?\b/
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
tool: "simctl",
|
|
59
|
+
platforms: ["ios"],
|
|
60
|
+
host: /\bsimctl\s+(io|spawn)\b/
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
tool: "uiautomator",
|
|
64
|
+
platforms: ["android"],
|
|
65
|
+
instrumentation: /uiautomator/
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
tool: "instrumentation",
|
|
69
|
+
platforms: ["android"],
|
|
70
|
+
instrumentation: /androidx\.test|\binstrument\b/
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
tool: "chrome-devtools-mcp",
|
|
74
|
+
platforms: ["web"],
|
|
75
|
+
cdp: /chrome-devtools-mcp/i
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
tool: "playwright",
|
|
79
|
+
platforms: ["web"],
|
|
80
|
+
cdp: /playwright/i
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
tool: "puppeteer",
|
|
84
|
+
platforms: ["web"],
|
|
85
|
+
cdp: /puppeteer/i
|
|
86
|
+
}
|
|
87
|
+
];
|
|
88
|
+
const HOST_EXCLUSIONS = [/\bsimctl\s+spawn\b.*\blog\s+stream\b/, /\blogcat\b/];
|
|
89
|
+
/** Stim's own DevTools clients: the browser supervisor, `stim-frames` in stim-server, Stim Desktop, the stim CLI. */
|
|
90
|
+
const CDP_EXCLUSIONS = [/\bstim-(frames|server|web)\b|StimDesktop|\/stim(-cli)?\/(dist|bin)\/|\/bin\/stim(\s|$)|^stim(\s|$)/];
|
|
91
|
+
function namesDevice(command, id) {
|
|
92
|
+
const escaped = id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
93
|
+
return new RegExp(`(^|[^A-Za-z0-9])${escaped}([^A-Za-z0-9]|$)`).test(command);
|
|
94
|
+
}
|
|
95
|
+
/** The tool a host process drives the device `id` with, or null when the command does not name the device. */
|
|
96
|
+
function hostDriverTool(command, platform, id) {
|
|
97
|
+
if (!namesDevice(command, id) || HOST_EXCLUSIONS.some((pattern) => pattern.test(command))) return null;
|
|
98
|
+
return AUTOMATION_TOOLS.find((entry) => entry.platforms.includes(platform) && entry.host?.test(command))?.tool ?? null;
|
|
99
|
+
}
|
|
100
|
+
/** The tool behind an on-device process's arguments, or null when it is not instrumentation. */
|
|
101
|
+
function instrumentationTool(args) {
|
|
102
|
+
return AUTOMATION_TOOLS.find((entry) => entry.platforms.includes("android") && entry.instrumentation?.test(args))?.tool ?? null;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* The tool a DevTools client's command line names, or null for Stim's own clients. A client no entry names is
|
|
106
|
+
* reported by its script or executable name.
|
|
107
|
+
*/
|
|
108
|
+
function cdpClientTool(command) {
|
|
109
|
+
if (CDP_EXCLUSIONS.some((pattern) => pattern.test(command))) return null;
|
|
110
|
+
const known = AUTOMATION_TOOLS.find((entry) => entry.platforms.includes("web") && entry.cdp?.test(command));
|
|
111
|
+
if (known) return known.tool;
|
|
112
|
+
const [executable = "", script] = command.trim().split(/\s+/);
|
|
113
|
+
const name = basename(executable);
|
|
114
|
+
return (/^(node|bun|deno|python3?)$/.test(name) && script ? basename(script) : name) || "unknown DevTools client";
|
|
115
|
+
}
|
|
116
|
+
//#endregion
|
|
8
117
|
//#region src/devices/activity.ts
|
|
9
118
|
const ACTIVE_WINDOW_MS = 6e5;
|
|
10
119
|
const LOG_TAIL_BYTES = 262144;
|
|
11
120
|
function classifyActivity(evidence, now) {
|
|
12
121
|
const last = evidence.recency.reduce((best, entry) => Number.isFinite(entry.at) && (!best || entry.at > best.at) ? entry : best, null);
|
|
13
|
-
const
|
|
122
|
+
const recent = {};
|
|
123
|
+
for (const { basis, at } of evidence.recency) if (Number.isFinite(at) && !(Date.parse(recent[basis] ?? "") >= at)) recent[basis] = new Date(at).toISOString();
|
|
124
|
+
const lastActivityAt = last ? {
|
|
125
|
+
lastActivityAt: new Date(last.at).toISOString(),
|
|
126
|
+
recent
|
|
127
|
+
} : {};
|
|
14
128
|
const [first] = evidence.drivers;
|
|
15
129
|
if (first) {
|
|
16
130
|
const { basis: _basis, ...driver } = first;
|
|
@@ -72,6 +186,7 @@ function parseAgentDeviceRecord(kind, path, raw) {
|
|
|
72
186
|
readable: Boolean(entry && deviceId),
|
|
73
187
|
owner: entry ? field(entry, "ownerPid", "ownerStartTime") : null,
|
|
74
188
|
runner: entry && kind === "runner-lease" ? field(entry, "runnerPid", "runnerStartTime") : null,
|
|
189
|
+
claimProtocol: kind === "runner-lease" && entry?.deviceClaimProtocol === 1,
|
|
75
190
|
createdAtMs
|
|
76
191
|
};
|
|
77
192
|
}
|
|
@@ -107,34 +222,15 @@ function parseProcessTable(output) {
|
|
|
107
222
|
}
|
|
108
223
|
return rows;
|
|
109
224
|
}
|
|
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
225
|
function parseAndroidInstrumentation(output) {
|
|
126
226
|
const found = [];
|
|
127
227
|
for (const line of output.split("\n")) {
|
|
128
228
|
const match = /^\s*(\d+)\s+(.*)$/.exec(line);
|
|
129
229
|
if (!match) continue;
|
|
130
|
-
const
|
|
131
|
-
if (
|
|
132
|
-
pid: Number(match[1]),
|
|
133
|
-
tool: "uiautomator"
|
|
134
|
-
});
|
|
135
|
-
else if (/androidx\.test|\binstrument\b/.test(args)) found.push({
|
|
230
|
+
const tool = instrumentationTool(match[2]);
|
|
231
|
+
if (tool) found.push({
|
|
136
232
|
pid: Number(match[1]),
|
|
137
|
-
tool
|
|
233
|
+
tool
|
|
138
234
|
});
|
|
139
235
|
}
|
|
140
236
|
return found;
|
|
@@ -159,6 +255,7 @@ function latestRecordAt(lines, matches) {
|
|
|
159
255
|
function isDeviceRecord(record) {
|
|
160
256
|
return !(typeof record.event === "string" && record.event.startsWith("collector_"));
|
|
161
257
|
}
|
|
258
|
+
/** The last lines of the file at `path`, at most its final 256 KiB, or null when it cannot be read. */
|
|
162
259
|
function tailLines(path) {
|
|
163
260
|
let fd;
|
|
164
261
|
try {
|
|
@@ -190,6 +287,18 @@ function agentDeviceDirs(home) {
|
|
|
190
287
|
dir
|
|
191
288
|
}))];
|
|
192
289
|
}
|
|
290
|
+
/**
|
|
291
|
+
* When an agent-device session last recorded an event: its events file's modification time. agent-device names a
|
|
292
|
+
* session's directory after the session with every character outside `[a-zA-Z0-9._-]` replaced by `_`.
|
|
293
|
+
*/
|
|
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
|
+
}
|
|
301
|
+
}
|
|
193
302
|
function readAgentDeviceRecords(home) {
|
|
194
303
|
return agentDeviceDirs(home).flatMap(({ kind, dir }) => {
|
|
195
304
|
let names;
|
|
@@ -208,18 +317,48 @@ function readAgentDeviceRecords(home) {
|
|
|
208
317
|
});
|
|
209
318
|
});
|
|
210
319
|
}
|
|
320
|
+
function isClaimProtocolRunner(record, row) {
|
|
321
|
+
if (!record.claimProtocol || record.runner?.pid !== row.pid || row.startedAt === null) return false;
|
|
322
|
+
const recorded = Date.parse(record.runner.startTime);
|
|
323
|
+
return Math.floor(recorded / 1e3) === Math.floor(Date.parse(row.startedAt) / 1e3);
|
|
324
|
+
}
|
|
325
|
+
function lsofArgs(port) {
|
|
326
|
+
return [
|
|
327
|
+
"-nP",
|
|
328
|
+
`-iTCP:${port}`,
|
|
329
|
+
"-sTCP:ESTABLISHED",
|
|
330
|
+
"-Fpn"
|
|
331
|
+
];
|
|
332
|
+
}
|
|
333
|
+
function lsofNoMatch(error) {
|
|
334
|
+
const stdout = error.stdout;
|
|
335
|
+
return error.status === 1 && (stdout === "" || stdout === void 0) ? "" : null;
|
|
336
|
+
}
|
|
211
337
|
function createDeviceProcessTables() {
|
|
212
338
|
const exec = getExecutor();
|
|
213
339
|
let host;
|
|
214
340
|
const android = /* @__PURE__ */ new Map();
|
|
341
|
+
const tcp = /* @__PURE__ */ new Map();
|
|
215
342
|
return {
|
|
343
|
+
tcpConnections(port) {
|
|
344
|
+
if (!tcp.has(port)) {
|
|
345
|
+
let output;
|
|
346
|
+
try {
|
|
347
|
+
output = exec.runFile("lsof", lsofArgs(port), { timeoutMs: 5e3 });
|
|
348
|
+
} catch (error) {
|
|
349
|
+
output = lsofNoMatch(error);
|
|
350
|
+
}
|
|
351
|
+
tcp.set(port, output);
|
|
352
|
+
}
|
|
353
|
+
return tcp.get(port) ?? null;
|
|
354
|
+
},
|
|
216
355
|
host() {
|
|
217
356
|
if (host === void 0) {
|
|
218
357
|
const output = exec.runFileQuiet("ps", [
|
|
219
358
|
"-axww",
|
|
220
359
|
"-o",
|
|
221
360
|
HOST_PROCESS_COLUMNS
|
|
222
|
-
], { timeoutMs:
|
|
361
|
+
], { timeoutMs: 1e4 });
|
|
223
362
|
host = output === null ? null : parseProcessTable(output);
|
|
224
363
|
}
|
|
225
364
|
return host;
|
|
@@ -238,9 +377,10 @@ function createDeviceProcessTables() {
|
|
|
238
377
|
}
|
|
239
378
|
};
|
|
240
379
|
}
|
|
241
|
-
function createActivityReader({ now = Date.now(), home = homedir(), startOf = process_identity_exports.inspectProcessStart, leaseFiles = listLeaseFiles, tables = createDeviceProcessTables() } = {}) {
|
|
380
|
+
function createActivityReader({ now = Date.now(), home = homedir(), startOf = process_identity_exports.inspectProcessStart, leaseFiles = listLeaseFiles, tables = createDeviceProcessTables(), viewers = readViewedDevices } = {}) {
|
|
242
381
|
let agentDevice;
|
|
243
382
|
let stimLeases;
|
|
383
|
+
let viewed;
|
|
244
384
|
const logs = /* @__PURE__ */ new Map();
|
|
245
385
|
const log = (path) => {
|
|
246
386
|
if (!logs.has(path)) logs.set(path, tailLines(path));
|
|
@@ -258,14 +398,21 @@ function createActivityReader({ now = Date.now(), home = homedir(), startOf = pr
|
|
|
258
398
|
}));
|
|
259
399
|
for (const { record, liveness } of agentDevice) {
|
|
260
400
|
if (record.deviceId !== null && record.deviceId !== target.id) continue;
|
|
401
|
+
if (record.claimProtocol) continue;
|
|
261
402
|
const basis = record.kind === "claim" ? "agent-device-claim" : "agent-device-lease";
|
|
262
|
-
if (liveness === "live")
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
403
|
+
if (liveness === "live") {
|
|
404
|
+
evidence.drivers.push({
|
|
405
|
+
basis,
|
|
406
|
+
tool: "agent-device",
|
|
407
|
+
pid: record.owner?.pid ?? null,
|
|
408
|
+
since: record.createdAtMs !== null ? new Date(record.createdAtMs).toISOString() : null
|
|
409
|
+
});
|
|
410
|
+
const actedAt = record.session ? agentSessionActedAt(home, record.session) : null;
|
|
411
|
+
if (actedAt !== null) evidence.recency.push({
|
|
412
|
+
basis: "agent-action",
|
|
413
|
+
at: actedAt
|
|
414
|
+
});
|
|
415
|
+
} else if (liveness === "unknown") evidence.unknown.push(basis);
|
|
269
416
|
}
|
|
270
417
|
try {
|
|
271
418
|
stimLeases ??= leaseFiles();
|
|
@@ -283,10 +430,16 @@ function createActivityReader({ now = Date.now(), home = homedir(), startOf = pr
|
|
|
283
430
|
since: entry.lease.grantedAt
|
|
284
431
|
});
|
|
285
432
|
}
|
|
433
|
+
viewed ??= viewers();
|
|
434
|
+
if (viewed.some((device) => device.platform === target.platform && device.id === target.id)) evidence.recency.push({
|
|
435
|
+
basis: "viewer",
|
|
436
|
+
at: now
|
|
437
|
+
});
|
|
286
438
|
const processes = tables.host();
|
|
287
439
|
if (processes === null) evidence.unknown.push("driver-process");
|
|
288
440
|
for (const row of processes ?? []) {
|
|
289
|
-
|
|
441
|
+
if (agentDevice.some(({ record }) => isClaimProtocolRunner(record, row))) continue;
|
|
442
|
+
const tool = hostDriverTool(row.command, target.platform, target.id);
|
|
290
443
|
if (tool) evidence.drivers.push({
|
|
291
444
|
basis: "driver-process",
|
|
292
445
|
tool,
|
|
@@ -316,7 +469,7 @@ function createActivityReader({ now = Date.now(), home = homedir(), startOf = pr
|
|
|
316
469
|
basis: "metro-bundle",
|
|
317
470
|
at: bundleAt
|
|
318
471
|
});
|
|
319
|
-
const usedAt = Date.parse(String(readWorkspaceState(target.workspace)?.lastUsedAt ?? ""));
|
|
472
|
+
const usedAt = Date.parse(String(readWorkspaceState$1(target.workspace)?.lastUsedAt ?? ""));
|
|
320
473
|
if (Number.isFinite(usedAt)) evidence.recency.push({
|
|
321
474
|
basis: "workspace-use",
|
|
322
475
|
at: usedAt
|
|
@@ -327,7 +480,7 @@ function createActivityReader({ now = Date.now(), home = homedir(), startOf = pr
|
|
|
327
480
|
}
|
|
328
481
|
function workspaceActivity(workspace, now = Date.now()) {
|
|
329
482
|
const dir = workspaceLogsDir(workspace);
|
|
330
|
-
const state = readWorkspaceState(workspace);
|
|
483
|
+
const state = readWorkspaceState$1(workspace);
|
|
331
484
|
const recency = [];
|
|
332
485
|
const deviceAt = latestRecordAt(tailLines(join(dir, "device.ndjson")) ?? [], (record) => record.src === "device" && isDeviceRecord(record));
|
|
333
486
|
if (deviceAt !== null) recency.push({
|
|
@@ -352,5 +505,92 @@ function workspaceActivity(workspace, now = Date.now()) {
|
|
|
352
505
|
recency
|
|
353
506
|
}, now);
|
|
354
507
|
}
|
|
508
|
+
/**
|
|
509
|
+
* The pids of processes connected to loopback `port` as clients, from `lsof -Fpn` output: a connection whose remote
|
|
510
|
+
* end is the port. Chrome's own accepted sockets have the port as their local end and are skipped.
|
|
511
|
+
*/
|
|
512
|
+
function parseTcpClients(output, port) {
|
|
513
|
+
const pids = /* @__PURE__ */ new Set();
|
|
514
|
+
let pid = null;
|
|
515
|
+
for (const line of output.split("\n")) if (line.startsWith("p")) pid = Number(line.slice(1));
|
|
516
|
+
else if (line.startsWith("n") && pid !== null && Number.isInteger(pid)) {
|
|
517
|
+
const remote = line.slice(1).split("->")[1];
|
|
518
|
+
if (remote && Number(remote.slice(remote.lastIndexOf(":") + 1)) === port) pids.add(pid);
|
|
519
|
+
}
|
|
520
|
+
return [...pids];
|
|
521
|
+
}
|
|
522
|
+
function webClientPids(connections, target) {
|
|
523
|
+
return parseTcpClients(connections, target.port).filter((pid) => !target.ownPids.includes(pid));
|
|
524
|
+
}
|
|
525
|
+
function webDrivers(clients, processes) {
|
|
526
|
+
return clients.flatMap((pid) => {
|
|
527
|
+
const row = processes.find((candidate) => candidate.pid === pid);
|
|
528
|
+
const tool = row ? cdpClientTool(row.command) : "unknown DevTools client";
|
|
529
|
+
return tool ? [{
|
|
530
|
+
tool,
|
|
531
|
+
pid,
|
|
532
|
+
since: row?.startedAt ?? null
|
|
533
|
+
}] : [];
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* The DevTools clients of the owned browser that are not Stim's, read without blocking the event loop; null when
|
|
538
|
+
* lsof or ps could not be read.
|
|
539
|
+
*/
|
|
540
|
+
async function probeWebDrivers(target) {
|
|
541
|
+
const exec = getExecutor();
|
|
542
|
+
let connections;
|
|
543
|
+
try {
|
|
544
|
+
connections = await exec.runFileAsync("lsof", lsofArgs(target.port), { timeoutMs: 5e3 });
|
|
545
|
+
} catch (error) {
|
|
546
|
+
connections = lsofNoMatch(error);
|
|
547
|
+
}
|
|
548
|
+
if (connections === null) return null;
|
|
549
|
+
const clients = webClientPids(connections, target);
|
|
550
|
+
if (!clients.length) return [];
|
|
551
|
+
let table;
|
|
552
|
+
try {
|
|
553
|
+
table = await exec.runFileAsync("ps", [
|
|
554
|
+
"-axww",
|
|
555
|
+
"-o",
|
|
556
|
+
HOST_PROCESS_COLUMNS
|
|
557
|
+
], { timeoutMs: 5e3 });
|
|
558
|
+
} catch {
|
|
559
|
+
return null;
|
|
560
|
+
}
|
|
561
|
+
return webDrivers(clients, parseProcessTable(table));
|
|
562
|
+
}
|
|
563
|
+
/**
|
|
564
|
+
* The owned browser's activity: a DevTools client other than Stim's is a driver (basis `cdp-client`). Recency comes
|
|
565
|
+
* from the newest agent action the browser supervisor recorded (basis `agent-action`) and the newest other page
|
|
566
|
+
* record (basis `page-log`).
|
|
567
|
+
*/
|
|
568
|
+
function readWebActivity(target, tables, now = Date.now()) {
|
|
569
|
+
const evidence = {
|
|
570
|
+
drivers: [],
|
|
571
|
+
unknown: [],
|
|
572
|
+
recency: []
|
|
573
|
+
};
|
|
574
|
+
const connections = tables.tcpConnections(target.port);
|
|
575
|
+
const clients = connections === null ? [] : webClientPids(connections, target);
|
|
576
|
+
const processes = clients.length ? (tables.clients ?? tables.host)() : [];
|
|
577
|
+
if (connections === null || processes === null) evidence.unknown.push("cdp-client");
|
|
578
|
+
else for (const driver of webDrivers(clients, processes)) evidence.drivers.push({
|
|
579
|
+
...driver,
|
|
580
|
+
basis: "cdp-client"
|
|
581
|
+
});
|
|
582
|
+
const lines = tailLines(join(workspaceLogsDir(target.workspace), "web.ndjson")) ?? [];
|
|
583
|
+
const actionAt = latestRecordAt(lines, (record) => record.src === "agent");
|
|
584
|
+
if (actionAt !== null) evidence.recency.push({
|
|
585
|
+
basis: "agent-action",
|
|
586
|
+
at: actionAt
|
|
587
|
+
});
|
|
588
|
+
const pageAt = latestRecordAt(lines, (record) => record.src !== "agent");
|
|
589
|
+
if (pageAt !== null) evidence.recency.push({
|
|
590
|
+
basis: "page-log",
|
|
591
|
+
at: pageAt
|
|
592
|
+
});
|
|
593
|
+
return classifyActivity(evidence, now);
|
|
594
|
+
}
|
|
355
595
|
//#endregion
|
|
356
|
-
export {
|
|
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 };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { t as getExecutor } from "./exec-
|
|
2
|
-
import {
|
|
1
|
+
import { t as getExecutor } from "./exec-CaErVv7t.mjs";
|
|
2
|
+
import { u as androidToolCwd } from "./android-Db2eEMaf.mjs";
|
|
3
3
|
//#region src/collector/android.ts
|
|
4
4
|
const LEVEL_BY_LETTER = {
|
|
5
5
|
V: "debug",
|