stim 1.8.0 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -155
- package/dist/{android-DWssqHQ8.mjs → android-787HhP5w.mjs} +168 -39
- package/dist/{android-BRgBYzU0.mjs → android-DBkeHwpm.mjs} +250 -200
- package/dist/{android-DeYdjjBP.mjs → android-DPov-fv2.mjs} +2 -2
- package/dist/{android-cas-SWIjzeuL.mjs → android-cas-CITmIeWY.mjs} +22 -14
- package/dist/android-cas-compiler.mjs +2 -2
- package/dist/app-install-CrJ6t_bv.mjs +819 -0
- package/dist/budget-BP4jZZL4.mjs +620 -0
- package/dist/build-progress-BXx-ZBe4.mjs +512 -0
- package/dist/{cache-manifest-CYJi8-Hx.mjs → cache-manifest-Pubfp8wV.mjs} +4 -4
- package/dist/cache-manifest.mjs +1 -1
- package/dist/cli.mjs +22 -19
- package/dist/collector-run.d.mts +7 -1
- package/dist/collector-run.mjs +10 -9
- package/dist/{command-output-B3ycpsYv.mjs → command-output-D5z2_bcF.mjs} +11 -2
- package/dist/{config-B7iyKWkL.mjs → config-BEq0Qw_a.mjs} +134 -121
- package/dist/{dependency-state-D0M7oKA7.mjs → dependency-state-r9UesA9a.mjs} +2 -2
- package/dist/{deps-oCXVNEqQ.mjs → deps-DP4eWqea.mjs} +8 -5
- package/dist/{detached-entry-Bpo6ZY_I.mjs → detached-entry-DJHoca49.mjs} +1 -1
- package/dist/{device-BUoZyMoR.mjs → dev-client-92U593KT.mjs} +1090 -660
- package/dist/{device-RdujjHLB.mjs → device-DhTZe7L4.mjs} +13 -20
- package/dist/{build-lock-CgtsB6TV.mjs → device-lease-CbEaIk7b.mjs} +481 -27
- package/dist/{device-pool-BxRwdufO.mjs → device-pool-Di8Q-9Ro.mjs} +7 -7
- package/dist/{device-remote-ByN8P6CB.mjs → device-remote-Dy1jGj3U.mjs} +645 -119
- package/dist/{doctor-BMt156qe.mjs → doctor-B-inAJmt.mjs} +137 -113
- package/dist/{error-diagnostics-D1Kaf-eT.mjs → error-diagnostics-xxWKNO3-.mjs} +22 -17
- package/dist/exec-DK8KaVHK.mjs +133 -0
- package/dist/{gc-v2YcoJvm.mjs → gc-ChPFZMRC.mjs} +836 -813
- package/dist/{guide-BTAlD1_y.mjs → guide-DwmaD_Wa.mjs} +953 -209
- package/dist/guide-status-CWvti2Lg.mjs +125 -0
- package/dist/idle-DpRLp5lj.mjs +157 -0
- package/dist/{build-slots-C-oAHRT1.mjs → in-use-CjUluqIX.mjs} +102 -8
- package/dist/{ios-BcCiI4TS.mjs → ios-B2Fpqvlb.mjs} +60 -71
- package/dist/{ios-BP04pY5f.mjs → ios-Dd-hYJBh.mjs} +241 -234
- package/dist/{ios-device-CFXA46ez.mjs → ios-device-BiOsSVzd.mjs} +3 -2
- package/dist/{ios-device-CauFJiYx.mjs → ios-device-CtRdEVjx.mjs} +149 -52
- package/dist/launch-verify-CQS_Kg-C.mjs +365 -0
- package/dist/{logs-CakdBTtm.mjs → logs-MARIy6m9.mjs} +20 -15
- package/dist/metro-BWLOJ_Wr.mjs +409 -0
- package/dist/{named-ports-DzMOlmaX.mjs → named-ports-DiTfSz6p.mjs} +4 -4
- package/dist/{ndjson-BZwI1NBv.mjs → ndjson-CDY9IkVC.mjs} +23 -43
- package/dist/{ndjson-DvHSsNU9.d.mts → ndjson-Du3j0AQ9.d.mts} +1 -0
- package/dist/{ownership-Cp52QAK1.mjs → ownership-D2xbLVoz.mjs} +2 -3
- package/dist/{teardown-BJqEzPo_.mjs → ownership-DZjdx8Gq.mjs} +664 -612
- package/dist/{ports-BIbJaKNW.mjs → ports-CLSSFOuz.mjs} +3 -3
- package/dist/{process-output-Dl5uKE03.mjs → process-output-Fpl8PYi8.mjs} +1 -1
- package/dist/{project-DDZWf_16.mjs → project-BXZzOABt.mjs} +8 -3
- package/dist/{reload-BdypPbjM.mjs → reload-DoETJ3rl.mjs} +11 -13
- package/dist/{server-bare-BQkyxa_b.mjs → server-bare-DVbb7kXH.mjs} +2 -2
- package/dist/{server-expo-BVAA1OqJ.mjs → server-expo-BvOCeakp.mjs} +8 -7
- package/dist/server-expo-DBVTdult.mjs +2 -0
- package/dist/{settings-BXXorVM2.mjs → settings-DOugPlPz.mjs} +59 -159
- package/dist/settings-EWYNwe72.mjs +378 -0
- package/dist/settings.schema.json +1064 -0
- package/dist/simslim-Ca3487H6.mjs +436 -0
- package/dist/{slot-launch-ZUUe0TQP.mjs → slot-launch-C5cg0fmk.mjs} +4 -4
- package/dist/spawn-claims-CUleC2cM.mjs +49 -0
- package/dist/{start-CmxtOKjU.mjs → start-F9gv5dcC.mjs} +159 -114
- package/dist/start-VoFsPSes.mjs +2 -0
- package/dist/{state-DZobzxLW.mjs → state-WGdIbi_d.mjs} +5 -3
- package/dist/{stats-BYcZ6n1h.mjs → stats-BAgJaLA4.mjs} +66 -5
- package/dist/{stats-CLg5RuDS.mjs → stats-DBYJ5bxL.mjs} +4 -4
- package/dist/status-DLCD-o3s.mjs +764 -0
- package/dist/status-Dr4fRkym.mjs +216 -0
- package/dist/{guide-status-Craq9z0t.mjs → stim-installations-CINXy_su.mjs} +3 -122
- package/dist/{stop-C8JW6kKp.mjs → stop-Dj4pfmyv.mjs} +111 -31
- package/dist/{stop-B1kOpYei.mjs → stop-ZvOeTmKb.mjs} +2 -2
- package/dist/supervisor-run.d.mts +11 -2
- package/dist/supervisor-run.mjs +228 -42
- package/dist/{workspace-process-lock-BYfZz_S-.mjs → workspace-process-lock-BXA96myD.mjs} +14 -9
- package/dist/workspaces-BTUyFfkv.mjs +1003 -0
- package/dist/worktree-4aZg2NnI.mjs +2 -0
- package/dist/{worktree-CFLA-6MX.mjs → worktree-BMS7GeqB.mjs} +523 -61
- package/dist/{worktree-FG_ZKKdb.mjs → worktree-BlizKDpZ.mjs} +200 -54
- package/dist/{xcode-B6YaWeg0.mjs → xcode-B2tBkf0m.mjs} +66 -57
- package/package.json +6 -6
- package/skill/SKILL.md +2 -0
- package/dist/app-install-BDbsSgCl.mjs +0 -1175
- package/dist/dev-client-BmT-ioFI.mjs +0 -797
- package/dist/device-lease-Ffz_M5t0.mjs +0 -359
- package/dist/exec-B29XNuhP.mjs +0 -77
- package/dist/logs-query-BGL-Pl12.mjs +0 -290
- package/dist/metro-C7bcJ0uo.mjs +0 -233
- package/dist/ownership-wolE-0Do.mjs +0 -470
- package/dist/public-http-probe-CyMDrQ3c.mjs +0 -61
- package/dist/reclaim-KJ-0uWEF.mjs +0 -361
- package/dist/server-expo-YDDG3eP7.mjs +0 -2
- package/dist/spawn-entry-nCE6m770.mjs +0 -14
- package/dist/status-DdkLki47.mjs +0 -426
- package/dist/workspace-state-BT5E3SA2.mjs +0 -146
- package/dist/{metro-store-CDDZHtPl.mjs → metro-store-DzeWkfqW.mjs} +1 -1
|
@@ -0,0 +1,512 @@
|
|
|
1
|
+
import { D as process_identity_exports, k as ownership_claim_exports } from "./config-BEq0Qw_a.mjs";
|
|
2
|
+
import { t as getExecutor } from "./exec-DK8KaVHK.mjs";
|
|
3
|
+
import { f as clearWorkspaceStateKey, g as updateWorkspaceState, j as workspaceLogsDir, m as readWorkspaceState$1 } from "./metro-BWLOJ_Wr.mjs";
|
|
4
|
+
import { a as formatElapsed, u as plural } from "./command-output-D5z2_bcF.mjs";
|
|
5
|
+
import { i as listLeaseFiles, r as leaseIsExpired } from "./device-lease-CbEaIk7b.mjs";
|
|
6
|
+
import { closeSync, fstatSync, openSync, readFileSync, readSync, readdirSync } from "node:fs";
|
|
7
|
+
import { BUILD_PHASES } from "@stim-cli/core/state";
|
|
8
|
+
import { basename, join, resolve } from "node:path";
|
|
9
|
+
import { homedir } from "node:os";
|
|
10
|
+
//#region src/devices/activity.ts
|
|
11
|
+
const ACTIVE_WINDOW_MS = 6e5;
|
|
12
|
+
const LOG_TAIL_BYTES = 262144;
|
|
13
|
+
function classifyActivity(evidence, now) {
|
|
14
|
+
const last = evidence.recency.reduce((best, entry) => Number.isFinite(entry.at) && (!best || entry.at > best.at) ? entry : best, null);
|
|
15
|
+
const lastActivityAt = last ? { lastActivityAt: new Date(last.at).toISOString() } : {};
|
|
16
|
+
const [first] = evidence.drivers;
|
|
17
|
+
if (first) {
|
|
18
|
+
const { basis: _basis, ...driver } = first;
|
|
19
|
+
return {
|
|
20
|
+
state: "driven",
|
|
21
|
+
driver,
|
|
22
|
+
...lastActivityAt,
|
|
23
|
+
basis: [...new Set(evidence.drivers.map((entry) => entry.basis))]
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
if (evidence.unknown.length) return {
|
|
27
|
+
state: "unknown",
|
|
28
|
+
...lastActivityAt,
|
|
29
|
+
basis: [...new Set(evidence.unknown)]
|
|
30
|
+
};
|
|
31
|
+
return {
|
|
32
|
+
state: Boolean(last && now - last.at < ACTIVE_WINDOW_MS) ? "active" : "idle",
|
|
33
|
+
...lastActivityAt,
|
|
34
|
+
basis: [...new Set(evidence.recency.map((entry) => entry.basis))]
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function idleForMs(activity, now) {
|
|
38
|
+
if (activity?.state !== "idle" || !activity.lastActivityAt) return null;
|
|
39
|
+
const at = Date.parse(activity.lastActivityAt);
|
|
40
|
+
return Number.isFinite(at) ? Math.max(0, now - at) : null;
|
|
41
|
+
}
|
|
42
|
+
function field(entry, pidKey, startKey) {
|
|
43
|
+
const pid = entry[pidKey];
|
|
44
|
+
const startTime = entry[startKey];
|
|
45
|
+
if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0 || typeof startTime !== "string") return null;
|
|
46
|
+
return {
|
|
47
|
+
pid,
|
|
48
|
+
startTime
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function parseObject(raw) {
|
|
52
|
+
if (raw === null) return null;
|
|
53
|
+
try {
|
|
54
|
+
const parsed = JSON.parse(raw);
|
|
55
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
56
|
+
} catch {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function parseAgentDeviceRecord(kind, path, raw) {
|
|
61
|
+
const entry = parseObject(raw);
|
|
62
|
+
const stem = basename(path, ".json");
|
|
63
|
+
const device = entry?.device && typeof entry.device === "object" ? entry.device : null;
|
|
64
|
+
const recordedId = kind === "claim" ? device?.id : entry?.deviceId;
|
|
65
|
+
const deviceId = typeof recordedId === "string" && recordedId ? recordedId : kind === "runner-lease" ? stem : null;
|
|
66
|
+
const createdAtMs = typeof entry?.createdAtMs === "number" ? entry.createdAtMs : null;
|
|
67
|
+
return {
|
|
68
|
+
path,
|
|
69
|
+
kind,
|
|
70
|
+
deviceId,
|
|
71
|
+
readable: Boolean(entry && deviceId),
|
|
72
|
+
owner: entry ? field(entry, "ownerPid", "ownerStartTime") : null,
|
|
73
|
+
runner: entry && kind === "runner-lease" ? field(entry, "runnerPid", "runnerStartTime") : null,
|
|
74
|
+
createdAtMs
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function processMatches(entry, startOf) {
|
|
78
|
+
const start = startOf(entry.pid);
|
|
79
|
+
if (start.status === "gone") return "dead";
|
|
80
|
+
if (start.status === "unknown") return "unknown";
|
|
81
|
+
const recorded = Date.parse(entry.startTime);
|
|
82
|
+
if (!Number.isFinite(recorded)) return "unknown";
|
|
83
|
+
return Math.floor(start.startedAtMs / 1e3) === Math.floor(recorded / 1e3) ? "live" : "dead";
|
|
84
|
+
}
|
|
85
|
+
function agentDeviceLiveness(record, startOf) {
|
|
86
|
+
const results = (record.kind === "claim" ? [record.owner] : [record.owner, record.runner]).map((entry) => entry ? processMatches(entry, startOf) : "unknown");
|
|
87
|
+
if (results.includes("dead")) return "dead";
|
|
88
|
+
if (!record.readable || results.includes("unknown")) return "unknown";
|
|
89
|
+
return "live";
|
|
90
|
+
}
|
|
91
|
+
function parseProcessTable(output) {
|
|
92
|
+
const rows = [];
|
|
93
|
+
for (const line of output.split("\n")) {
|
|
94
|
+
const match = /^\s*(\d+)\s+(\w{3}\s+\w{3}\s+\d+\s+\d{1,2}:\d{2}:\d{2}\s+\d{4})\s+(.*)$/.exec(line);
|
|
95
|
+
if (!match) continue;
|
|
96
|
+
const at = Date.parse(match[2]);
|
|
97
|
+
rows.push({
|
|
98
|
+
pid: Number(match[1]),
|
|
99
|
+
startedAt: Number.isFinite(at) ? new Date(at).toISOString() : null,
|
|
100
|
+
command: match[3]
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
return rows;
|
|
104
|
+
}
|
|
105
|
+
function namesDevice(command, id) {
|
|
106
|
+
const escaped = id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
107
|
+
return new RegExp(`(^|[^A-Za-z0-9])${escaped}([^A-Za-z0-9]|$)`).test(command);
|
|
108
|
+
}
|
|
109
|
+
function driverTool(command, id) {
|
|
110
|
+
if (!namesDevice(command, id)) return null;
|
|
111
|
+
if (/\bsimctl\s+spawn\b.*\blog\s+stream\b/.test(command) || /\blogcat\b/.test(command)) return null;
|
|
112
|
+
if (/agent-device/i.test(command)) return "agent-device";
|
|
113
|
+
if (/idb_companion/.test(command)) return "idb";
|
|
114
|
+
if (/maestro/i.test(command)) return "maestro";
|
|
115
|
+
if (/appium|WebDriverAgent/i.test(command)) return "appium";
|
|
116
|
+
if (/\bxcodebuild\b.*\btest(-without-building)?\b/.test(command)) return "xcodebuild";
|
|
117
|
+
if (/\bsimctl\s+(io|spawn)\b/.test(command)) return "simctl";
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
function parseAndroidInstrumentation(output) {
|
|
121
|
+
const found = [];
|
|
122
|
+
for (const line of output.split("\n")) {
|
|
123
|
+
const match = /^\s*(\d+)\s+(.*)$/.exec(line);
|
|
124
|
+
if (!match) continue;
|
|
125
|
+
const args = match[2];
|
|
126
|
+
if (/uiautomator/.test(args)) found.push({
|
|
127
|
+
pid: Number(match[1]),
|
|
128
|
+
tool: "uiautomator"
|
|
129
|
+
});
|
|
130
|
+
else if (/androidx\.test|\binstrument\b/.test(args)) found.push({
|
|
131
|
+
pid: Number(match[1]),
|
|
132
|
+
tool: "instrumentation"
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
return found;
|
|
136
|
+
}
|
|
137
|
+
function latestDeviceLogAt(lines, target) {
|
|
138
|
+
return latestRecordAt(lines, (record) => {
|
|
139
|
+
if (record.src !== "device" || record.platform !== target.platform || !isDeviceRecord(record)) return false;
|
|
140
|
+
if (target.slot === "default") return record.slot === void 0;
|
|
141
|
+
return record.slot === target.slot && (record.deviceId === void 0 || record.deviceId === target.id);
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
function latestBundleRequestAt(lines, platform) {
|
|
145
|
+
return latestRecordAt(lines, (record) => record.event === "bundle_response_started" && record.platform === platform);
|
|
146
|
+
}
|
|
147
|
+
function latestRecordAt(lines, matches) {
|
|
148
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
149
|
+
const record = parseObject(lines[i]);
|
|
150
|
+
if (record && typeof record.ts === "number" && matches(record)) return record.ts;
|
|
151
|
+
}
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
function isDeviceRecord(record) {
|
|
155
|
+
return !(typeof record.event === "string" && record.event.startsWith("collector_"));
|
|
156
|
+
}
|
|
157
|
+
function tailLines(path) {
|
|
158
|
+
let fd;
|
|
159
|
+
try {
|
|
160
|
+
fd = openSync(path, "r");
|
|
161
|
+
const size = fstatSync(fd).size;
|
|
162
|
+
const length = Math.min(size, LOG_TAIL_BYTES);
|
|
163
|
+
const buffer = Buffer.alloc(length);
|
|
164
|
+
readSync(fd, buffer, 0, length, size - length);
|
|
165
|
+
const lines = buffer.toString("utf8").split("\n");
|
|
166
|
+
return length < size ? lines.slice(1) : lines;
|
|
167
|
+
} catch {
|
|
168
|
+
return null;
|
|
169
|
+
} finally {
|
|
170
|
+
if (fd !== void 0) closeSync(fd);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
function envDir(name) {
|
|
174
|
+
const value = process.env[name]?.trim();
|
|
175
|
+
return value ? resolve(value) : null;
|
|
176
|
+
}
|
|
177
|
+
function agentDeviceDirs(home) {
|
|
178
|
+
const root = join(home, ".agent-device");
|
|
179
|
+
const leaseDirs = [envDir("AGENT_DEVICE_IOS_RUNNER_LEASE_DIR") ?? join(root, "apple-runner", "leases"), join(root, "ios-runner", "leases")];
|
|
180
|
+
return [{
|
|
181
|
+
kind: "claim",
|
|
182
|
+
dir: envDir("AGENT_DEVICE_CLAIMS_DIR") ?? join(root, "device-claims")
|
|
183
|
+
}, ...[...new Set(leaseDirs)].map((dir) => ({
|
|
184
|
+
kind: "runner-lease",
|
|
185
|
+
dir
|
|
186
|
+
}))];
|
|
187
|
+
}
|
|
188
|
+
function readAgentDeviceRecords(home) {
|
|
189
|
+
return agentDeviceDirs(home).flatMap(({ kind, dir }) => {
|
|
190
|
+
let names;
|
|
191
|
+
try {
|
|
192
|
+
names = readdirSync(dir).filter((name) => name.endsWith(".json"));
|
|
193
|
+
} catch {
|
|
194
|
+
return [];
|
|
195
|
+
}
|
|
196
|
+
return names.map((name) => {
|
|
197
|
+
const path = join(dir, name);
|
|
198
|
+
let raw = null;
|
|
199
|
+
try {
|
|
200
|
+
raw = readFileSync(path, "utf8");
|
|
201
|
+
} catch {}
|
|
202
|
+
return parseAgentDeviceRecord(kind, path, raw);
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
function createActivityReader({ now = Date.now(), home = homedir(), startOf = process_identity_exports.inspectProcessStart, leaseFiles = listLeaseFiles } = {}) {
|
|
207
|
+
const exec = getExecutor();
|
|
208
|
+
let agentDevice;
|
|
209
|
+
let stimLeases;
|
|
210
|
+
let processes;
|
|
211
|
+
const logs = /* @__PURE__ */ new Map();
|
|
212
|
+
const log = (path) => {
|
|
213
|
+
if (!logs.has(path)) logs.set(path, tailLines(path));
|
|
214
|
+
return logs.get(path) ?? null;
|
|
215
|
+
};
|
|
216
|
+
return (target) => {
|
|
217
|
+
const evidence = {
|
|
218
|
+
drivers: [],
|
|
219
|
+
unknown: [],
|
|
220
|
+
recency: []
|
|
221
|
+
};
|
|
222
|
+
agentDevice ??= readAgentDeviceRecords(home).map((record) => ({
|
|
223
|
+
record,
|
|
224
|
+
liveness: agentDeviceLiveness(record, startOf)
|
|
225
|
+
}));
|
|
226
|
+
for (const { record, liveness } of agentDevice) {
|
|
227
|
+
if (record.deviceId !== null && record.deviceId !== target.id) continue;
|
|
228
|
+
const basis = record.kind === "claim" ? "agent-device-claim" : "agent-device-lease";
|
|
229
|
+
if (liveness === "live") evidence.drivers.push({
|
|
230
|
+
basis,
|
|
231
|
+
tool: "agent-device",
|
|
232
|
+
pid: record.owner?.pid ?? null,
|
|
233
|
+
since: record.createdAtMs !== null ? new Date(record.createdAtMs).toISOString() : null
|
|
234
|
+
});
|
|
235
|
+
else if (liveness === "unknown") evidence.unknown.push(basis);
|
|
236
|
+
}
|
|
237
|
+
try {
|
|
238
|
+
stimLeases ??= leaseFiles();
|
|
239
|
+
} catch {
|
|
240
|
+
stimLeases = [];
|
|
241
|
+
evidence.unknown.push("device-lock");
|
|
242
|
+
}
|
|
243
|
+
for (const entry of stimLeases) {
|
|
244
|
+
if (entry.platform !== target.platform || entry.id !== target.id) continue;
|
|
245
|
+
if (!entry.lease) evidence.unknown.push("device-lock");
|
|
246
|
+
else if (!leaseIsExpired(entry.lease, now)) evidence.drivers.push({
|
|
247
|
+
basis: "device-lock",
|
|
248
|
+
tool: "stim device lock",
|
|
249
|
+
pid: null,
|
|
250
|
+
since: entry.lease.grantedAt
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
if (processes === void 0) {
|
|
254
|
+
const output = exec.runFileQuiet("ps", [
|
|
255
|
+
"-axww",
|
|
256
|
+
"-o",
|
|
257
|
+
"pid=,lstart=,command="
|
|
258
|
+
], { timeoutMs: 5e3 });
|
|
259
|
+
processes = output === null ? null : parseProcessTable(output);
|
|
260
|
+
}
|
|
261
|
+
if (processes === null) evidence.unknown.push("driver-process");
|
|
262
|
+
for (const row of processes ?? []) {
|
|
263
|
+
const tool = driverTool(row.command, target.id);
|
|
264
|
+
if (tool) evidence.drivers.push({
|
|
265
|
+
basis: "driver-process",
|
|
266
|
+
tool,
|
|
267
|
+
pid: row.pid,
|
|
268
|
+
since: row.startedAt
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
if (target.platform === "android") {
|
|
272
|
+
const output = exec.runFileQuiet("adb", [
|
|
273
|
+
"-s",
|
|
274
|
+
target.id,
|
|
275
|
+
"shell",
|
|
276
|
+
"ps",
|
|
277
|
+
"-A",
|
|
278
|
+
"-o",
|
|
279
|
+
"PID,ARGS"
|
|
280
|
+
], { timeoutMs: 5e3 });
|
|
281
|
+
if (output === null) evidence.unknown.push("instrumentation");
|
|
282
|
+
for (const { pid, tool } of parseAndroidInstrumentation(output ?? "")) evidence.drivers.push({
|
|
283
|
+
basis: "instrumentation",
|
|
284
|
+
tool,
|
|
285
|
+
pid,
|
|
286
|
+
since: null
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
if (target.workspace) {
|
|
290
|
+
const dir = workspaceLogsDir(target.workspace);
|
|
291
|
+
const deviceAt = latestDeviceLogAt(log(join(dir, "device.ndjson")) ?? [], target);
|
|
292
|
+
if (deviceAt !== null) evidence.recency.push({
|
|
293
|
+
basis: "device-log",
|
|
294
|
+
at: deviceAt
|
|
295
|
+
});
|
|
296
|
+
const bundleAt = latestBundleRequestAt(log(join(dir, "metro.ndjson")) ?? [], target.platform);
|
|
297
|
+
if (bundleAt !== null) evidence.recency.push({
|
|
298
|
+
basis: "metro-bundle",
|
|
299
|
+
at: bundleAt
|
|
300
|
+
});
|
|
301
|
+
const usedAt = Date.parse(String(readWorkspaceState$1(target.workspace)?.lastUsedAt ?? ""));
|
|
302
|
+
if (Number.isFinite(usedAt)) evidence.recency.push({
|
|
303
|
+
basis: "workspace-use",
|
|
304
|
+
at: usedAt
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
return classifyActivity(evidence, now);
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
function workspaceActivity(workspace, now = Date.now()) {
|
|
311
|
+
const dir = workspaceLogsDir(workspace);
|
|
312
|
+
const state = readWorkspaceState$1(workspace);
|
|
313
|
+
const recency = [];
|
|
314
|
+
const deviceAt = latestRecordAt(tailLines(join(dir, "device.ndjson")) ?? [], (record) => record.src === "device" && isDeviceRecord(record));
|
|
315
|
+
if (deviceAt !== null) recency.push({
|
|
316
|
+
basis: "device-log",
|
|
317
|
+
at: deviceAt
|
|
318
|
+
});
|
|
319
|
+
const bundleAt = latestRecordAt(tailLines(join(dir, "metro.ndjson")) ?? [], (record) => record.event === "bundle_response_started");
|
|
320
|
+
if (bundleAt !== null) recency.push({
|
|
321
|
+
basis: "metro-bundle",
|
|
322
|
+
at: bundleAt
|
|
323
|
+
});
|
|
324
|
+
for (const [basis, value] of [["workspace-use", state?.lastUsedAt], ["supervisor-start", state?.supervisor?.startedAt]]) {
|
|
325
|
+
const at = Date.parse(String(value ?? ""));
|
|
326
|
+
if (Number.isFinite(at)) recency.push({
|
|
327
|
+
basis,
|
|
328
|
+
at
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
return classifyActivity({
|
|
332
|
+
drivers: [],
|
|
333
|
+
unknown: [],
|
|
334
|
+
recency
|
|
335
|
+
}, now);
|
|
336
|
+
}
|
|
337
|
+
//#endregion
|
|
338
|
+
//#region src/engine/build-progress.ts
|
|
339
|
+
const ACTIVE_BUILD_KEY = "activeBuild";
|
|
340
|
+
const NO_BUILD_PROGRESS = {
|
|
341
|
+
step: () => {},
|
|
342
|
+
durations: () => ({}),
|
|
343
|
+
clear: () => {}
|
|
344
|
+
};
|
|
345
|
+
const COLD_PHASES = [
|
|
346
|
+
"prebuild",
|
|
347
|
+
"pods",
|
|
348
|
+
"compile"
|
|
349
|
+
];
|
|
350
|
+
const DEVICE_PHASES = ["install", "launch"];
|
|
351
|
+
function startBuildProgress({ root, platform, slot, claim, now = Date.now, note = () => {} }) {
|
|
352
|
+
const startedAt = new Date(now()).toISOString();
|
|
353
|
+
const record = {
|
|
354
|
+
platform,
|
|
355
|
+
slot,
|
|
356
|
+
startedAt,
|
|
357
|
+
phase: "prepare",
|
|
358
|
+
phaseStartedAt: startedAt,
|
|
359
|
+
phases: [{
|
|
360
|
+
phase: "prepare",
|
|
361
|
+
startedAt
|
|
362
|
+
}],
|
|
363
|
+
claim: {
|
|
364
|
+
root: claim.root,
|
|
365
|
+
path: claim.path,
|
|
366
|
+
claimId: claim.claimId,
|
|
367
|
+
pid: claim.owner.pid
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
let warned = false;
|
|
371
|
+
const guard = (fn) => {
|
|
372
|
+
try {
|
|
373
|
+
fn();
|
|
374
|
+
} catch (error) {
|
|
375
|
+
if (warned) return;
|
|
376
|
+
warned = true;
|
|
377
|
+
note(`Build progress could not be recorded in the workspace state: ${error?.message || error}`);
|
|
378
|
+
}
|
|
379
|
+
};
|
|
380
|
+
const write = () => guard(() => updateWorkspaceState(root, (state) => ({
|
|
381
|
+
...state,
|
|
382
|
+
[ACTIVE_BUILD_KEY]: record
|
|
383
|
+
})));
|
|
384
|
+
write();
|
|
385
|
+
return {
|
|
386
|
+
step(phase) {
|
|
387
|
+
if (phase === record.phase) return;
|
|
388
|
+
const at = new Date(now()).toISOString();
|
|
389
|
+
record.phase = phase;
|
|
390
|
+
record.phaseStartedAt = at;
|
|
391
|
+
record.phases.push({
|
|
392
|
+
phase,
|
|
393
|
+
startedAt: at
|
|
394
|
+
});
|
|
395
|
+
write();
|
|
396
|
+
},
|
|
397
|
+
durations() {
|
|
398
|
+
return phaseDurations(record.phases, now());
|
|
399
|
+
},
|
|
400
|
+
clear() {
|
|
401
|
+
guard(() => {
|
|
402
|
+
clearWorkspaceStateKey(root, ACTIVE_BUILD_KEY, (value) => parseActiveBuild(value)?.claim.claimId === record.claim.claimId);
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
function phaseDurations(phases, now) {
|
|
408
|
+
const out = {};
|
|
409
|
+
phases.forEach(({ phase, startedAt }, index) => {
|
|
410
|
+
const start = Date.parse(startedAt);
|
|
411
|
+
const next = phases[index + 1];
|
|
412
|
+
const end = next ? Date.parse(next.startedAt) : now;
|
|
413
|
+
if (!Number.isFinite(start) || !Number.isFinite(end)) return;
|
|
414
|
+
out[phase] = (out[phase] ?? 0) + Math.max(0, end - start);
|
|
415
|
+
});
|
|
416
|
+
return out;
|
|
417
|
+
}
|
|
418
|
+
function parseActiveBuild(value) {
|
|
419
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
420
|
+
const record = value;
|
|
421
|
+
const claim = record.claim;
|
|
422
|
+
if (record.platform !== "ios" && record.platform !== "android") return null;
|
|
423
|
+
if (!isPhase(record.phase)) return null;
|
|
424
|
+
if (typeof record.startedAt !== "string" || typeof record.phaseStartedAt !== "string") return null;
|
|
425
|
+
if (!claim || typeof claim.root !== "string" || typeof claim.claimId !== "string") return null;
|
|
426
|
+
const phases = Array.isArray(record.phases) ? record.phases.filter((entry) => isPhase(entry?.phase) && typeof entry?.startedAt === "string") : [];
|
|
427
|
+
return {
|
|
428
|
+
platform: record.platform,
|
|
429
|
+
slot: typeof record.slot === "string" ? record.slot : "default",
|
|
430
|
+
startedAt: record.startedAt,
|
|
431
|
+
phase: record.phase,
|
|
432
|
+
phaseStartedAt: record.phaseStartedAt,
|
|
433
|
+
phases,
|
|
434
|
+
claim: {
|
|
435
|
+
root: claim.root,
|
|
436
|
+
path: typeof claim.path === "string" ? claim.path : "",
|
|
437
|
+
claimId: claim.claimId,
|
|
438
|
+
pid: typeof claim.pid === "number" ? claim.pid : 0
|
|
439
|
+
}
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
function isPhase(value) {
|
|
443
|
+
return BUILD_PHASES.includes(value);
|
|
444
|
+
}
|
|
445
|
+
function activeBuildState(claim, survey = (0, ownership_claim_exports.readClaimSet)(claim.root)) {
|
|
446
|
+
if (survey.live.some((holder) => holder.claimId === claim.claimId)) return "running";
|
|
447
|
+
if (survey.dead.some((holder) => holder.claimId === claim.claimId)) return "stale";
|
|
448
|
+
return survey.unresolved.length ? "unknown" : "stale";
|
|
449
|
+
}
|
|
450
|
+
function liveOutcome(record) {
|
|
451
|
+
const seen = new Set(record.phases.map((entry) => entry.phase));
|
|
452
|
+
if (COLD_PHASES.some((phase) => seen.has(phase))) return "cold";
|
|
453
|
+
if (DEVICE_PHASES.some((phase) => seen.has(phase))) return "hit";
|
|
454
|
+
return null;
|
|
455
|
+
}
|
|
456
|
+
function median(values) {
|
|
457
|
+
if (!values.length) return null;
|
|
458
|
+
const sorted = values.toSorted((a, b) => a - b);
|
|
459
|
+
const mid = Math.floor(sorted.length / 2);
|
|
460
|
+
return sorted.length % 2 ? sorted[mid] : Math.round((sorted[mid - 1] + sorted[mid]) / 2);
|
|
461
|
+
}
|
|
462
|
+
function estimateBuild(history, platform, known, phase) {
|
|
463
|
+
const lists = history?.[platform];
|
|
464
|
+
const outcome = known ?? latestOutcome(lists);
|
|
465
|
+
const samples = outcome && lists?.[outcome] || [];
|
|
466
|
+
const phaseSamples = samples.flatMap((sample) => phase in sample.phases ? [sample.phases[phase]] : []);
|
|
467
|
+
return {
|
|
468
|
+
outcome,
|
|
469
|
+
expectedMs: median(samples.map((sample) => sample.durationMs)),
|
|
470
|
+
expectedPhaseMs: median(phaseSamples),
|
|
471
|
+
basis: samples.length
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
function latestOutcome(lists) {
|
|
475
|
+
let latest = null;
|
|
476
|
+
for (const outcome of ["hit", "cold"]) {
|
|
477
|
+
const last = lists?.[outcome]?.at(-1);
|
|
478
|
+
const at = last ? Date.parse(last.at) : NaN;
|
|
479
|
+
if (Number.isFinite(at) && (!latest || at > latest.at)) latest = {
|
|
480
|
+
outcome,
|
|
481
|
+
at
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
return latest?.outcome ?? null;
|
|
485
|
+
}
|
|
486
|
+
function buildReport(record, { state, history }) {
|
|
487
|
+
return {
|
|
488
|
+
platform: record.platform,
|
|
489
|
+
slot: record.slot,
|
|
490
|
+
state,
|
|
491
|
+
phase: record.phase,
|
|
492
|
+
startedAt: record.startedAt,
|
|
493
|
+
phaseStartedAt: record.phaseStartedAt,
|
|
494
|
+
...estimateBuild(history, record.platform, liveOutcome(record), record.phase)
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
function remainingText(remainingMs) {
|
|
498
|
+
return remainingMs < 6e4 ? "under a minute left" : `about ${Math.ceil(remainingMs / 6e4)} min left`;
|
|
499
|
+
}
|
|
500
|
+
function buildStatusLine(report, now) {
|
|
501
|
+
const slot = report.slot === "default" ? "" : ` [${report.slot}]`;
|
|
502
|
+
if (report.state === "stale") return `build: stale ${report.platform}${slot} record from ${report.startedAt} (its run is gone; the next run replaces it)`;
|
|
503
|
+
const elapsedMs = Math.max(0, now - Date.parse(report.startedAt));
|
|
504
|
+
const head = `build: ${report.platform}${slot} ${report.phase}, ${formatElapsed(elapsedMs)} elapsed`;
|
|
505
|
+
if (report.state === "unknown") return `${head} (its native-run claim cannot be resolved, so it may not be running)`;
|
|
506
|
+
if (report.expectedMs === null) return head;
|
|
507
|
+
const basis = `median of ${plural(report.basis, `${report.outcome} run`)}`;
|
|
508
|
+
const remainingMs = report.expectedMs - elapsedMs;
|
|
509
|
+
return remainingMs > 0 ? `${head} -- ${remainingText(remainingMs)} (${basis})` : `${head} (usually ~${formatElapsed(report.expectedMs)}, ${basis})`;
|
|
510
|
+
}
|
|
511
|
+
//#endregion
|
|
512
|
+
export { buildStatusLine as a, createActivityReader as c, buildReport as i, idleForMs as l, NO_BUILD_PROGRESS as n, parseActiveBuild as o, activeBuildState as r, startBuildProgress as s, ACTIVE_BUILD_KEY as t, workspaceActivity as u };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { u as getConfigDir } from "./config-
|
|
1
|
+
import { u as getConfigDir } from "./config-BEq0Qw_a.mjs";
|
|
2
2
|
import { existsSync } from "fs";
|
|
3
|
-
import {
|
|
4
|
-
import { join, resolve } from "path";
|
|
3
|
+
import { isAbsolute, join, resolve } from "path";
|
|
5
4
|
import { readCacheManifest, updateCacheManifest } from "@stim-cli/core";
|
|
5
|
+
import { homedir } from "os";
|
|
6
6
|
//#region src/cache/cache-manifest.ts
|
|
7
7
|
function manifestPath() {
|
|
8
8
|
return join(getConfigDir(), "caches.json");
|
|
@@ -53,7 +53,7 @@ function unregister(dir, path = manifestPath()) {
|
|
|
53
53
|
return removed;
|
|
54
54
|
}
|
|
55
55
|
function registeredCaches(path = manifestPath()) {
|
|
56
|
-
return readManifest(path).caches.filter((c) => c.dir && existsSync(c.dir)).map((c) => ({
|
|
56
|
+
return readManifest(path).caches.filter((c) => isAbsolute(c.dir) && existsSync(c.dir)).map((c) => ({
|
|
57
57
|
name: c.name,
|
|
58
58
|
dir: c.dir,
|
|
59
59
|
prune: c.prune === "atomic" ? "atomic" : "entries",
|
package/dist/cache-manifest.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as unregister, i as registeredCaches, n as readManifest, r as register, t as manifestPath } from "./cache-manifest-
|
|
1
|
+
import { a as unregister, i as registeredCaches, n as readManifest, r as register, t as manifestPath } from "./cache-manifest-Pubfp8wV.mjs";
|
|
2
2
|
export { manifestPath, readManifest, register, registeredCaches, unregister };
|
package/dist/cli.mjs
CHANGED
|
@@ -1,27 +1,30 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { _ as refuseRelativeStimPaths } from "./config-BEq0Qw_a.mjs";
|
|
2
3
|
import { readFileSync } from "node:fs";
|
|
3
4
|
import { Command } from "commander";
|
|
4
5
|
//#region bin/cli.ts
|
|
5
6
|
const commands = /* @__PURE__ */ new Map([
|
|
6
|
-
["doctor", () => import("./doctor-
|
|
7
|
-
["worktree", () => import("./worktree-
|
|
8
|
-
["start", () => import("./start-
|
|
9
|
-
["ports", () => import("./ports-
|
|
10
|
-
["stop", () => import("./stop-
|
|
11
|
-
["ios", () => import("./ios-
|
|
12
|
-
["android", () => import("./android-
|
|
13
|
-
["reload", () => import("./reload-
|
|
14
|
-
["device", () => import("./device-
|
|
15
|
-
["logs", () => import("./logs-
|
|
16
|
-
["
|
|
17
|
-
["
|
|
18
|
-
["
|
|
19
|
-
["
|
|
7
|
+
["doctor", () => import("./doctor-B-inAJmt.mjs")],
|
|
8
|
+
["worktree", () => import("./worktree-4aZg2NnI.mjs")],
|
|
9
|
+
["start", () => import("./start-VoFsPSes.mjs")],
|
|
10
|
+
["ports", () => import("./ports-CLSSFOuz.mjs")],
|
|
11
|
+
["stop", () => import("./stop-ZvOeTmKb.mjs")],
|
|
12
|
+
["ios", () => import("./ios-Dd-hYJBh.mjs")],
|
|
13
|
+
["android", () => import("./android-DBkeHwpm.mjs")],
|
|
14
|
+
["reload", () => import("./reload-DoETJ3rl.mjs")],
|
|
15
|
+
["device", () => import("./device-DhTZe7L4.mjs")],
|
|
16
|
+
["logs", () => import("./logs-MARIy6m9.mjs")],
|
|
17
|
+
["settings", () => import("./settings-EWYNwe72.mjs")],
|
|
18
|
+
["status", () => import("./status-DLCD-o3s.mjs")],
|
|
19
|
+
["stats", () => import("./stats-DBYJ5bxL.mjs")],
|
|
20
|
+
["gc", () => import("./gc-ChPFZMRC.mjs")],
|
|
21
|
+
["guide", () => import("./guide-DwmaD_Wa.mjs")]
|
|
20
22
|
]);
|
|
21
23
|
const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf-8"));
|
|
22
24
|
const program = new Command();
|
|
23
25
|
program.name("stim").description("Isolated React Native dev environments per project/worktree").version(pkg.version);
|
|
24
26
|
try {
|
|
27
|
+
refuseRelativeStimPaths();
|
|
25
28
|
const first = process.argv[2];
|
|
26
29
|
const loadCommand = commands.get((first === "help" ? process.argv[3] : first) ?? "");
|
|
27
30
|
if (loadCommand) (await loadCommand()).default(program, pkg.version);
|
|
@@ -31,11 +34,11 @@ try {
|
|
|
31
34
|
}
|
|
32
35
|
await program.parseAsync();
|
|
33
36
|
} catch (err) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
|
|
37
|
+
const code = err?.code;
|
|
38
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
39
|
+
if (typeof code === "string" && code.startsWith("STIM_")) console.error(`${code}: ${message}`);
|
|
40
|
+
else console.error(`Unexpected error: ${message.replace(/\.$/, "")}. Report it at ${pkg.bugs.url}`);
|
|
41
|
+
process.exit(1);
|
|
39
42
|
}
|
|
40
43
|
//#endregion
|
|
41
44
|
export {};
|
package/dist/collector-run.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as NdjsonWriter } from "./ndjson-
|
|
1
|
+
import { t as NdjsonWriter } from "./ndjson-Du3j0AQ9.mjs";
|
|
2
2
|
import { ChildProcess, SpawnOptions } from "child_process";
|
|
3
3
|
import { ChildProcess as ChildProcess$1 } from "node:child_process";
|
|
4
4
|
//#region src/exec.d.ts
|
|
@@ -8,10 +8,16 @@ interface ExecOptions {
|
|
|
8
8
|
cwd?: string;
|
|
9
9
|
env?: Record<string, string>;
|
|
10
10
|
omitEnv?: readonly string[];
|
|
11
|
+
/** Text written to the child's stdin; `runFile` only. */
|
|
12
|
+
input?: string;
|
|
13
|
+
/** `runFile` only: return stdout as written, without trimming surrounding whitespace. */
|
|
14
|
+
untrimmed?: boolean;
|
|
11
15
|
}
|
|
12
16
|
interface Executor {
|
|
13
17
|
run(cmd: string, opts?: ExecOptions): string;
|
|
14
18
|
runFile(file: string, args?: string[], opts?: ExecOptions): string;
|
|
19
|
+
/** `runFile` without blocking the event loop; rejects where `runFile` throws. */
|
|
20
|
+
runFileAsync(file: string, args?: string[], opts?: ExecOptions): Promise<string>;
|
|
15
21
|
runQuiet(cmd: string, opts?: ExecOptions): string | null;
|
|
16
22
|
runFileQuiet(file: string, args?: string[], opts?: ExecOptions): string | null;
|
|
17
23
|
spawn(cmd: string, args?: readonly string[], opts?: SpawnOptions): ChildProcess;
|
package/dist/collector-run.mjs
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { r as createNdjsonWriter } from "./ndjson-
|
|
4
|
-
import {
|
|
5
|
-
import { t as createLineReader } from "./process-output-
|
|
6
|
-
import {
|
|
7
|
-
import { a as unregisterCollector, i as registerCollector, r as readCollectors, t as collectorProcessTitle } from "./ownership-
|
|
8
|
-
import { a as watchAppPid, i as waitForAppPid, n as parseLogcatLine, r as startAndroidLogcat, t as androidClockOffset } from "./android-
|
|
1
|
+
import { D as process_identity_exports, R as deviceSlotKey, W as validateDeviceSlot } from "./config-BEq0Qw_a.mjs";
|
|
2
|
+
import { t as getExecutor } from "./exec-DK8KaVHK.mjs";
|
|
3
|
+
import { r as createNdjsonWriter } from "./ndjson-CDY9IkVC.mjs";
|
|
4
|
+
import { j as workspaceLogsDir } from "./metro-BWLOJ_Wr.mjs";
|
|
5
|
+
import { t as createLineReader } from "./process-output-Fpl8PYi8.mjs";
|
|
6
|
+
import { a as startIosDeviceConsole, i as parseDeviceConsoleLine } from "./ios-device-BiOsSVzd.mjs";
|
|
7
|
+
import { a as unregisterCollector, i as registerCollector, r as readCollectors, t as collectorProcessTitle } from "./ownership-D2xbLVoz.mjs";
|
|
8
|
+
import { a as watchAppPid, i as waitForAppPid, n as parseLogcatLine, r as startAndroidLogcat, t as androidClockOffset } from "./android-DPov-fv2.mjs";
|
|
9
9
|
import { existsSync } from "node:fs";
|
|
10
|
+
import { LOG_ROTATE_BYTES } from "@stim-cli/core";
|
|
10
11
|
import { isAbsolute, join, resolve } from "node:path";
|
|
11
12
|
import { fileURLToPath } from "node:url";
|
|
12
13
|
//#region src/collector/ios.ts
|
|
@@ -299,7 +300,7 @@ async function runCollector({ slot = "default", platform, root, udid = null, app
|
|
|
299
300
|
slot,
|
|
300
301
|
deviceId: udid ?? serial
|
|
301
302
|
};
|
|
302
|
-
const writer = createNdjsonWriter(join(workspaceLogsDir(root), "device.ndjson"));
|
|
303
|
+
const writer = createNdjsonWriter(join(workspaceLogsDir(root), "device.ndjson"), { maxBytes: LOG_ROTATE_BYTES });
|
|
303
304
|
const startedAt = new Date(now()).toISOString();
|
|
304
305
|
const processToken = (0, process_identity_exports.captureProcessToken)(process.pid);
|
|
305
306
|
if (!processToken) {
|