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
@@ -1,486 +0,0 @@
1
- import { H as parseDeviceSlotOption, O as process_identity_exports, U as projectDeviceSlots, _ as loadConfig$1, g as isPathPrefix } from "./config-lvc7d3C2.mjs";
2
- import { n as SOURCES, t as LEVELS } from "./ndjson-CDY9IkVC.mjs";
3
- import { M as workspaceLogsDir, h as readWorkspaceState$1 } from "./metro-CcEo8Fl8.mjs";
4
- import { i as findCommandWorkspace } from "./project-CWlvEaeN.mjs";
5
- import { f as refuseNoProject, v as launchErrorPreview } from "./command-output-DVcpw6eE.mjs";
6
- import { a as readAgentDeviceRecords, t as agentDeviceLiveness } from "./activity-CYVDcrt3.mjs";
7
- import { i as captureWorkspaceCrashes, t as errorDiagnostics } from "./error-diagnostics-C5HzcwCW.mjs";
8
- import { closeSync, fstatSync, openSync, readFileSync, readSync, readdirSync, realpathSync, statSync } from "node:fs";
9
- import { attachExpoErrorContext, buildCriteria, compileGrep, fileSizes, followLogs, logFiles, parseSince, queryLogs, readLogRecords, recordMatches, sortByTs } from "@stim-cli/core/state";
10
- import { join, relative, resolve, sep } from "node:path";
11
- import { homedir } from "node:os";
12
- import chalk from "chalk";
13
- //#region src/devices/agent-actions.ts
14
- const EVENTS_VERSION = 1;
15
- function object(value) {
16
- return value && typeof value === "object" && !Array.isArray(value) ? value : null;
17
- }
18
- function parseAgentEvents(lines) {
19
- const events = [];
20
- let session = null;
21
- let unknownVersion = null;
22
- for (const line of lines) {
23
- let entry;
24
- try {
25
- entry = object(JSON.parse(line));
26
- } catch {
27
- continue;
28
- }
29
- if (!entry) continue;
30
- const ts = typeof entry.ts === "string" ? Date.parse(entry.ts) : NaN;
31
- if (entry.version !== EVENTS_VERSION) {
32
- unknownVersion ??= {
33
- version: entry.version,
34
- ts: Number.isFinite(ts) ? ts : null
35
- };
36
- continue;
37
- }
38
- if (typeof entry.session === "string") session ??= entry.session;
39
- const { kind, command, summary } = entry;
40
- if (kind !== "action.recorded" && kind !== "request.finished") continue;
41
- const failed = kind === "request.finished" && entry.status === "error";
42
- if (kind === "request.finished" && !failed) continue;
43
- if (!Number.isFinite(ts) || typeof entry.session !== "string" || typeof command !== "string") continue;
44
- if (typeof summary !== "string") continue;
45
- events.push({
46
- ts,
47
- session: entry.session,
48
- kind,
49
- command,
50
- summary,
51
- failed,
52
- details: object(entry.details)
53
- });
54
- }
55
- return {
56
- events,
57
- session,
58
- unknownVersion
59
- };
60
- }
61
- const RUNNER_DEVICE = /-destination\s+"?platform=iOS Simulator,id=([^",\s]+)|AgentDeviceRunner\.env\.session-(.+?)-owner-/;
62
- const LOG_TIME = /^(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2})(\.\d+)?([+-]\d{2})?(\d{2})?\b/;
63
- function logTime(line) {
64
- const match = LOG_TIME.exec(line);
65
- if (!match) return null;
66
- const [, date, time, fraction = "", offsetHours, offsetMinutes] = match;
67
- const offset = offsetHours && offsetMinutes ? `${offsetHours}:${offsetMinutes}` : "";
68
- const at = Date.parse(`${date}T${time}${fraction.slice(0, 4)}${offset}`);
69
- return Number.isFinite(at) ? at : null;
70
- }
71
- function parseRunnerSpans(text) {
72
- const spans = [];
73
- let pending = null;
74
- for (const line of text.split("\n")) {
75
- const device = RUNNER_DEVICE.exec(line);
76
- if (device) {
77
- pending = device[1] ?? device[2] ?? null;
78
- continue;
79
- }
80
- if (pending === null) continue;
81
- const at = logTime(line);
82
- if (at === null) continue;
83
- if (spans.at(-1)?.deviceId !== pending) spans.push({
84
- deviceId: pending,
85
- from: spans.length ? at : -Infinity
86
- });
87
- pending = null;
88
- }
89
- return spans;
90
- }
91
- function sessionSpans(runner, closes) {
92
- return runner.map((span, i) => {
93
- const previous = runner[i - 1]?.from ?? -Infinity;
94
- const close = closes.findLast((at) => at > previous && at < span.from);
95
- return close === void 0 ? span : {
96
- ...span,
97
- from: close + 1
98
- };
99
- });
100
- }
101
- function spanDevice(spans, ts) {
102
- let device = null;
103
- for (const span of spans) if (span.from <= ts) device = span.deviceId;
104
- return device;
105
- }
106
- function agentRecord(event, deviceId, target) {
107
- return {
108
- ts: event.ts,
109
- src: "agent",
110
- level: event.failed ? "error" : "info",
111
- msg: event.summary,
112
- event: event.failed ? "agent_failed" : "agent_action",
113
- command: event.command,
114
- session: event.session,
115
- platform: target.platform,
116
- deviceId,
117
- ...target.slot === "default" ? {} : { slot: target.slot },
118
- ...event.details ? { details: event.details } : {}
119
- };
120
- }
121
- function formatUnknownRecord(session, unknown, target, now) {
122
- return {
123
- ts: unknown.ts ?? now,
124
- src: "agent",
125
- level: "warn",
126
- msg: `agent-device session ${session} records events in an unrecognized format (version ${JSON.stringify(unknown.version) ?? "missing"}); its actions are not shown.`,
127
- event: "agent_format_unknown",
128
- session,
129
- platform: target.platform,
130
- ...target.slot === "default" ? {} : { slot: target.slot }
131
- };
132
- }
133
- function workspaceAgentTargets(project) {
134
- const targets = [];
135
- let slots;
136
- try {
137
- slots = projectDeviceSlots(project);
138
- } catch {
139
- return [];
140
- }
141
- for (const { slot, platforms } of slots) {
142
- const { ios, android } = platforms;
143
- if (ios?.owned && typeof ios.deviceUdid === "string") targets.push({
144
- platform: "ios",
145
- id: ios.deviceUdid,
146
- slot
147
- });
148
- if (android?.owned && typeof android.consolePort === "number" && typeof android.avdName === "string") targets.push({
149
- platform: "android",
150
- id: `emulator-${android.consolePort}`,
151
- slot,
152
- name: android.avdName
153
- });
154
- }
155
- return targets;
156
- }
157
- function envDir(name) {
158
- const value = process.env[name]?.trim();
159
- return value ? resolve(value) : null;
160
- }
161
- function readCompleteLines(path, start) {
162
- let fd;
163
- try {
164
- fd = openSync(path, "r");
165
- const size = fstatSync(fd).size;
166
- if (size <= start) return {
167
- text: "",
168
- next: start,
169
- size
170
- };
171
- const buffer = Buffer.alloc(size - start);
172
- const read = readSync(fd, buffer, 0, buffer.length, start);
173
- const end = buffer.subarray(0, read).lastIndexOf(10);
174
- return {
175
- text: end < 0 ? "" : buffer.toString("utf8", 0, end + 1),
176
- next: start + end + 1,
177
- size
178
- };
179
- } catch {
180
- return null;
181
- } finally {
182
- if (fd !== void 0) closeSync(fd);
183
- }
184
- }
185
- /**
186
- * Returns a reader of agent-device actions on the given devices. Each call returns the records written
187
- * since the previous call; the first call returns the retained history newer than `sinceTs`. It only reads
188
- * agent-device's state directory, and any file it cannot read or recognize yields no records.
189
- */
190
- function createAgentActionReader({ targets, sinceTs = -Infinity, home = homedir(), now = Date.now, startOf = process_identity_exports.inspectProcessStart }) {
191
- const sessionsDir = join(envDir("AGENT_DEVICE_STATE_DIR") ?? join(home, ".agent-device"), "sessions");
192
- const cursors = /* @__PURE__ */ new Map();
193
- const byId = new Map(targets.map((target) => [target.id, target]));
194
- return () => {
195
- if (!targets.length) return [];
196
- let names;
197
- try {
198
- names = readdirSync(sessionsDir);
199
- } catch {
200
- return [];
201
- }
202
- let claimed;
203
- const claimedDevice = (session) => (claimed ??= new Map(readAgentDeviceRecords(home).filter((record) => record.kind === "claim" && record.session && record.deviceId).filter((record) => {
204
- const target = byId.get(record.deviceId);
205
- return target?.name === void 0 || target.name === record.deviceName;
206
- }).filter((record) => agentDeviceLiveness(record, startOf) === "live").map((record) => [record.session, record.deviceId]))).get(session) ?? null;
207
- const out = [];
208
- for (const name of names) {
209
- const dir = join(sessionsDir, name);
210
- const eventsPath = join(dir, "events.ndjson");
211
- const lines = [];
212
- let cursor = cursors.get(name);
213
- if (!cursor) {
214
- try {
215
- if (statSync(eventsPath).mtimeMs < sinceTs) continue;
216
- } catch {
217
- continue;
218
- }
219
- cursor = {
220
- offset: 0,
221
- closes: [],
222
- runner: null,
223
- session: null,
224
- unknownReported: false
225
- };
226
- cursors.set(name, cursor);
227
- try {
228
- lines.push(...readFileSync(`${eventsPath}.1`, "utf8").split("\n"));
229
- } catch {}
230
- }
231
- let chunk = readCompleteLines(eventsPath, cursor.offset);
232
- if (chunk && chunk.size < cursor.offset) chunk = readCompleteLines(eventsPath, 0);
233
- if (!chunk) continue;
234
- cursor.offset = chunk.next;
235
- lines.push(...chunk.text.split("\n"));
236
- const parsed = parseAgentEvents(lines);
237
- cursor.session ??= parsed.session;
238
- const session = cursor.session;
239
- if (!parsed.events.length && !parsed.unknownVersion) continue;
240
- const runnerPath = join(dir, "runner.log");
241
- let runnerSize = -1;
242
- try {
243
- runnerSize = statSync(runnerPath).size;
244
- } catch {}
245
- if (cursor.runner?.size !== runnerSize) {
246
- let text = "";
247
- try {
248
- text = readFileSync(runnerPath, "utf8");
249
- } catch {}
250
- cursor.runner = {
251
- size: runnerSize,
252
- spans: parseRunnerSpans(text)
253
- };
254
- }
255
- for (const event of parsed.events) if (event.command === "close" && !event.failed) cursor.closes.push(event.ts);
256
- const spans = sessionSpans(cursor.runner.spans, cursor.closes);
257
- const deviceAt = (ts) => spans.length ? spanDevice(spans, ts) : session && claimedDevice(session);
258
- if (parsed.unknownVersion && !cursor.unknownReported) {
259
- const deviceId = spans.at(-1)?.deviceId ?? (session && claimedDevice(session));
260
- const target = deviceId ? byId.get(deviceId) : void 0;
261
- if (target) {
262
- cursor.unknownReported = true;
263
- out.push(formatUnknownRecord(session ?? name, parsed.unknownVersion, target, now()));
264
- }
265
- }
266
- for (const event of parsed.events) {
267
- if (event.ts < sinceTs) continue;
268
- const deviceId = deviceAt(event.ts);
269
- const target = deviceId ? byId.get(deviceId) : void 0;
270
- if (target && deviceId) out.push(agentRecord(event, deviceId, target));
271
- }
272
- }
273
- return out.toSorted((a, b) => a.ts - b.ts);
274
- };
275
- }
276
- //#endregion
277
- //#region src/commands/logs.ts
278
- const LEVEL_WIDTH = 5;
279
- const SRC_WIDTH = 6;
280
- const INDENT = " ";
281
- function padTimePart(value, width = 2) {
282
- return String(value).padStart(width, "0");
283
- }
284
- function formatTime(ts) {
285
- if (typeof ts !== "number" || !Number.isFinite(ts)) return "--:--:--.---";
286
- const d = new Date(ts);
287
- return `${padTimePart(d.getHours())}:${padTimePart(d.getMinutes())}:${padTimePart(d.getSeconds())}.${padTimePart(d.getMilliseconds(), 3)}`;
288
- }
289
- function formatStackFrame(frame) {
290
- if (!frame || typeof frame !== "object") return null;
291
- const where = [
292
- frame.file,
293
- frame.line,
294
- frame.column
295
- ].filter((p) => p !== void 0 && p !== null && p !== "").join(":");
296
- if (frame.fn && where) return `at ${frame.fn} (${where})`;
297
- if (frame.fn) return `at ${frame.fn}`;
298
- if (where) return `at ${where}`;
299
- return null;
300
- }
301
- function formatRecord(record, { paint } = {}) {
302
- const colour = paint || ((t) => t);
303
- const level = String(record?.level ?? "").padEnd(LEVEL_WIDTH);
304
- const src = String(record?.src ?? "").padEnd(SRC_WIDTH);
305
- const [first, ...rest] = (record?.msg === void 0 || record?.msg === null ? "" : String(record.msg)).split("\n");
306
- const lines = [`${formatTime(record?.ts)} ${colour(level)} ${src} ${first}`];
307
- for (const line of rest) lines.push(`${INDENT}${line}`);
308
- for (const frame of Array.isArray(record?.stack) ? record.stack : []) {
309
- const rendered = formatStackFrame(frame);
310
- if (rendered) lines.push(`${INDENT}${rendered}`);
311
- }
312
- return lines.join("\n");
313
- }
314
- function parseTail(value) {
315
- if (typeof value === "number" && Number.isInteger(value) && value >= 0) return { n: value };
316
- if (typeof value !== "string" || !/^\d+$/.test(value.trim())) return { error: `Invalid --tail value ${JSON.stringify(value)}. Use a non-negative whole number, e.g. --tail 50.` };
317
- return { n: parseInt(value.trim(), 10) };
318
- }
319
- function validateSources(sources) {
320
- if (sources === void 0 || sources === null) return { sources: void 0 };
321
- const list = Array.isArray(sources) ? sources : [sources];
322
- if (list.includes("all")) return { sources: [...SOURCES] };
323
- const unknown = list.filter((s) => !SOURCES.includes(s));
324
- if (unknown.length > 0) return { error: `Unknown --source value(s): ${unknown.join(", ")}. Use one or more of: ${SOURCES.join(", ")}, or all.` };
325
- return { sources: list };
326
- }
327
- function validateLevel(level) {
328
- if (LEVELS.includes(level)) return { level };
329
- return { error: `Invalid --level value ${JSON.stringify(level)}. Use one of: ${LEVELS.join(", ")}.` };
330
- }
331
- const ERRORS_PRINT_CAP = 20;
332
- function nearestRegisteredLogsProject(root, candidates) {
333
- return candidates.filter((candidate) => candidate !== root && isPathPrefix(root, candidate)).toSorted((a, b) => {
334
- const depth = (path) => relative(root, path).split(sep).length;
335
- return depth(a) - depth(b) || a.length - b.length || a.localeCompare(b);
336
- })[0] ?? null;
337
- }
338
- function registeredDescendantWithLogs(root) {
339
- const candidates = Object.keys(loadConfig$1()?.projects ?? {}).flatMap((path) => {
340
- try {
341
- const canonical = realpathSync(path);
342
- return logFiles(workspaceLogsDir(canonical)).length > 0 ? [canonical] : [];
343
- } catch {
344
- return [];
345
- }
346
- });
347
- return nearestRegisteredLogsProject(root, [...new Set(candidates)]);
348
- }
349
- function requireLogsWorkspace(root, dir) {
350
- if (logFiles(dir).length > 0) return;
351
- const descendant = registeredDescendantWithLogs(root);
352
- fail(`STIM_NO_PROJECT: No Stim workspace has run in ${root}.${descendant ? ` The nearest registered app with logs is ${descendant}; run this command from there.` : " Run `stim start`, `stim ios`, or `stim android` in the app first."}`);
353
- }
354
- const LEVEL_COLOURS = {
355
- debug: chalk.dim,
356
- info: chalk.reset,
357
- warn: chalk.yellow,
358
- error: chalk.red,
359
- fatal: chalk.bgRed
360
- };
361
- function logsCommand(program) {
362
- program.command("logs").description("Query this workspace's merged NDJSON log timeline (bundler, client, device, build, agent-device actions). Prints and exits; an existing timeline with nothing matching is a successful, empty result. Use --follow to stream.").option("--slot <name>", "Only records attributed to this device slot", parseDeviceSlotOption).option("--source <s...>", "Only these sources: metro, client, device, build, agent, or all").option("--level <l>", `Minimum level: ${LEVELS.join(", ")}`).option("--since <d>", "Only records newer than this, e.g. 30s, 5m, 2h").option("--grep <re>", "Only records whose message matches this regular expression").option("--tail <n>", "Only the last n matching records").option("--errors", "Errors and fatals since the last marker, from metro, client and build, plus confirmed native app-crash reports and stim web browser errors. Add --source device or --source all for general device errors.").option("--follow", "Keep streaming new records until interrupted").option("--json", "Emit the raw records, one per line (valid NDJSON; zero matches is zero bytes, exit 0)").action(async (opts) => {
363
- const root = findCommandWorkspace(process.cwd());
364
- if (!root) {
365
- refuseNoProject({ json: false });
366
- return;
367
- }
368
- const dir = workspaceLogsDir(root);
369
- let sources;
370
- if (opts.source !== void 0) {
371
- const checked = validateSources(opts.source);
372
- if (checked.error) fail(checked.error);
373
- sources = checked.sources;
374
- }
375
- let minLevel;
376
- if (opts.level !== void 0) {
377
- const checked = validateLevel(opts.level);
378
- if (checked.error) fail(checked.error);
379
- minLevel = checked.level;
380
- }
381
- if (opts.since !== void 0) {
382
- const parsed = parseSince(opts.since);
383
- if (parsed.error) fail(parsed.error);
384
- }
385
- let tail;
386
- if (opts.tail !== void 0) {
387
- const parsed = parseTail(opts.tail);
388
- if (parsed.error) fail(parsed.error);
389
- tail = parsed.n;
390
- }
391
- if (opts.grep !== void 0) {
392
- const compiled = compileGrep(opts.grep);
393
- if (compiled.error) fail(compiled.error);
394
- }
395
- requireLogsWorkspace(root, dir);
396
- const query = {
397
- slot: opts.slot,
398
- dir,
399
- sources,
400
- minLevel,
401
- since: opts.since,
402
- grep: opts.grep,
403
- tail,
404
- errorsOnly: Boolean(opts.errors),
405
- errorContext: Boolean(opts.errors && !opts.json && !opts.follow)
406
- };
407
- const emit = (record) => {
408
- if (opts.json) {
409
- console.log(JSON.stringify(record));
410
- return;
411
- }
412
- const rendered = {
413
- ...record,
414
- msg: launchErrorPreview([record], root, { full: true }).join("\n"),
415
- stack: void 0
416
- };
417
- console.log(formatRecord(rendered, { paint: record?.level ? LEVEL_COLOURS[record.level] : void 0 }));
418
- };
419
- const captured = captureWorkspaceCrashes(root, dir);
420
- const offsets = opts.follow ? fileSizes(dir) : null;
421
- const workspaceTimeline = opts.follow ? readLogRecords(dir) : captured;
422
- const agentSince = workspaceTimeline[0]?.ts ?? (opts.follow ? Date.now() : void 0);
423
- const readAgent = (sources ? sources.includes("agent") : !opts.errors) && agentSince !== void 0 ? createAgentActionReader({
424
- targets: workspaceAgentTargets(loadConfig$1()?.projects?.[root]),
425
- sinceTs: agentSince
426
- }) : null;
427
- const timeline = readAgent ? sortByTs([...workspaceTimeline, ...readAgent()]) : workspaceTimeline;
428
- const rawRecords = queryLogs({
429
- ...query,
430
- records: timeline
431
- });
432
- const supervisorPort = readWorkspaceState$1(root)?.supervisor?.port;
433
- const records = opts.json ? opts.errors ? attachExpoErrorContext(timeline, rawRecords, "field") : rawRecords : await errorDiagnostics(rawRecords, {
434
- root,
435
- logsDir: dir,
436
- port: typeof supervisorPort === "number" ? supervisorPort : null,
437
- allowRequest: true,
438
- timeline
439
- });
440
- const errorCount = records.filter((record) => record.errorContext !== true).length;
441
- let seenErrors = 0;
442
- const capped = opts.errors && !opts.json && tail === void 0 && errorCount > 20 ? records.filter((record) => {
443
- if (record.errorContext === true) return seenErrors <= 20;
444
- seenErrors += 1;
445
- return seenErrors <= 20;
446
- }) : records;
447
- for (const record of capped) emit(record);
448
- const hidden = errorCount - capped.filter((record) => record.errorContext !== true).length;
449
- if (hidden > 0) console.log(chalk.dim(`Showing 20 of ${errorCount} matching error records; ${hidden} not shown. Add --tail ${rawRecords.length} to include all captured records before grouping, or --json for raw records. Stacks are shown in full.`));
450
- if (!opts.follow) {
451
- if (records.length === 0 && !opts.json) console.error(chalk.dim(`No matching log records in ${dir}`));
452
- return;
453
- }
454
- const criteria = buildCriteria({
455
- slot: opts.slot,
456
- sources,
457
- minLevel,
458
- since: opts.since,
459
- grep: opts.grep,
460
- errorsOnly: Boolean(opts.errors)
461
- });
462
- const stop = followLogs({
463
- dir,
464
- offsets,
465
- criteria,
466
- onRecord: emit,
467
- errorContext: opts.errors ? opts.json ? "field" : "msg" : void 0
468
- });
469
- const agentTimer = readAgent ? setInterval(() => {
470
- for (const record of readAgent()) if (recordMatches(record, criteria)) emit(record);
471
- }, 500) : null;
472
- const finish = () => {
473
- stop();
474
- if (agentTimer) clearInterval(agentTimer);
475
- process.exit(0);
476
- };
477
- process.on("SIGINT", finish);
478
- process.on("SIGTERM", finish);
479
- });
480
- }
481
- function fail(message) {
482
- console.error(chalk.red(message));
483
- process.exit(1);
484
- }
485
- //#endregion
486
- export { ERRORS_PRINT_CAP, logsCommand as default, formatRecord, formatStackFrame, formatTime, parseTail, validateLevel, validateSources };