@rallycry/conveyor-agent 6.0.2 → 6.0.4

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/dist/index.js CHANGED
@@ -5,14 +5,1273 @@ import {
5
5
  ProjectConnection,
6
6
  ProjectRunner,
7
7
  ensureWorktree,
8
+ injectTelemetry,
8
9
  loadConveyorConfig,
9
10
  removeWorktree,
10
11
  runSetupCommand,
11
12
  runStartCommand
12
- } from "./chunk-HYWZJYPW.js";
13
+ } from "./chunk-NKZSUGND.js";
14
+
15
+ // src/debug/cdp-client.ts
16
+ var MAX_VARIABLE_VALUE_BYTES = 2048;
17
+ var MAX_VARIABLES_PER_SCOPE = 50;
18
+ var AUTO_RESUME_TIMEOUT_MS = 3e4;
19
+ var PROBE_BUFFER_MAX_SIZE = 1e3;
20
+ var CdpDebugClient = class {
21
+ ws = null;
22
+ requestId = 0;
23
+ pendingRequests = /* @__PURE__ */ new Map();
24
+ breakpoints = /* @__PURE__ */ new Map();
25
+ probes = /* @__PURE__ */ new Map();
26
+ probeCounter = 0;
27
+ probeBufferInjected = false;
28
+ pausedState = null;
29
+ autoResumeTimer = null;
30
+ protocol = "cdp";
31
+ onPausedCallback = null;
32
+ onResumedCallback = null;
33
+ onAutoResumedCallback = null;
34
+ // ── Connection ─────────────────────────────────────────────────────────
35
+ // oxlint-disable-next-line require-await
36
+ async connect(wsUrl, protocol) {
37
+ if (this.ws) {
38
+ throw new Error("Already connected to CDP");
39
+ }
40
+ this.protocol = protocol ?? "cdp";
41
+ return new Promise((resolve, reject) => {
42
+ const ws = new WebSocket(wsUrl);
43
+ ws.addEventListener("open", async () => {
44
+ this.ws = ws;
45
+ try {
46
+ await this.send("Debugger.enable", {});
47
+ await this.send("Runtime.enable", {});
48
+ resolve();
49
+ } catch (err) {
50
+ reject(err);
51
+ }
52
+ });
53
+ ws.addEventListener("message", (event) => {
54
+ const data = typeof event.data === "string" ? event.data : String(event.data);
55
+ this.handleMessage(data);
56
+ });
57
+ ws.addEventListener("error", () => {
58
+ reject(new Error(`CDP WebSocket connection failed: ${wsUrl}`));
59
+ });
60
+ ws.addEventListener("close", () => {
61
+ this.cleanup();
62
+ });
63
+ });
64
+ }
65
+ disconnect() {
66
+ if (this.ws) {
67
+ this.clearAutoResumeTimer();
68
+ this.ws.close();
69
+ this.cleanup();
70
+ }
71
+ }
72
+ isConnected() {
73
+ return this.ws !== null && this.ws.readyState === WebSocket.OPEN;
74
+ }
75
+ getProtocol() {
76
+ return this.protocol;
77
+ }
78
+ // ── Breakpoints ────────────────────────────────────────────────────────
79
+ async setBreakpoint(file, line, condition) {
80
+ const params = {
81
+ urlRegex: this.fileToUrlRegex(file),
82
+ // CDP uses 0-based lines
83
+ lineNumber: line - 1
84
+ };
85
+ if (condition) {
86
+ params.condition = condition;
87
+ }
88
+ const result = await this.send("Debugger.setBreakpointByUrl", params);
89
+ const breakpointId = result.breakpointId;
90
+ this.breakpoints.set(breakpointId, { breakpointId, file, line, condition });
91
+ return breakpointId;
92
+ }
93
+ async removeBreakpoint(breakpointId) {
94
+ await this.send("Debugger.removeBreakpoint", { breakpointId });
95
+ this.breakpoints.delete(breakpointId);
96
+ }
97
+ async removeAllBreakpoints() {
98
+ const ids = [...this.breakpoints.keys()];
99
+ for (const id of ids) {
100
+ try {
101
+ await this.removeBreakpoint(id);
102
+ } catch {
103
+ this.breakpoints.delete(id);
104
+ }
105
+ }
106
+ }
107
+ listBreakpoints() {
108
+ return [...this.breakpoints.values()];
109
+ }
110
+ // ── Probes (Logpoints) ────────────────────────────────────────────────
111
+ async setLogpoint(file, line, expressions, label) {
112
+ await this.ensureProbeBuffer();
113
+ const probeId = `probe-${++this.probeCounter}`;
114
+ const probeLabel = label ?? `${file}:${line}`;
115
+ const dataEntries = expressions.map((expr) => `${JSON.stringify(expr)}: (${expr})`).join(", ");
116
+ const condition = `(globalThis.__conveyorProbes?.probe(${JSON.stringify(probeLabel)}, {${dataEntries}}), false)`;
117
+ const breakpointId = await this.setBreakpoint(file, line, condition);
118
+ const probe = { probeId, breakpointId, file, line, expressions, label: probeLabel };
119
+ this.probes.set(probeId, probe);
120
+ return probeId;
121
+ }
122
+ async removeProbe(probeId) {
123
+ const probe = this.probes.get(probeId);
124
+ if (!probe) {
125
+ throw new Error(`Probe ${probeId} not found`);
126
+ }
127
+ await this.removeBreakpoint(probe.breakpointId);
128
+ this.probes.delete(probeId);
129
+ }
130
+ listProbes() {
131
+ return [...this.probes.values()];
132
+ }
133
+ async getProbeResults(label, limit) {
134
+ const args = [
135
+ label === void 0 ? "undefined" : JSON.stringify(label),
136
+ limit === void 0 ? "undefined" : String(limit)
137
+ ].join(", ");
138
+ const result = await this.evaluate(
139
+ `JSON.stringify(globalThis.__conveyorProbes?.read(${args}) ?? [])`
140
+ );
141
+ if (result.type === "error") {
142
+ return [];
143
+ }
144
+ try {
145
+ const raw = result.value;
146
+ const parsed = JSON.parse(raw.startsWith('"') ? JSON.parse(raw) : raw);
147
+ return parsed;
148
+ } catch {
149
+ return [];
150
+ }
151
+ }
152
+ async clearProbeResults(label) {
153
+ const arg = label === void 0 ? "undefined" : JSON.stringify(label);
154
+ await this.evaluate(`(globalThis.__conveyorProbes?.clear(${arg}), undefined)`);
155
+ }
156
+ // ── Paused State ───────────────────────────────────────────────────────
157
+ isPaused() {
158
+ return this.pausedState !== null;
159
+ }
160
+ getPausedState() {
161
+ return this.pausedState;
162
+ }
163
+ getCallStack() {
164
+ return this.pausedState?.callFrames ?? [];
165
+ }
166
+ async getScopeVariables(callFrameId) {
167
+ if (!this.pausedState) {
168
+ throw new Error("Debugger is not paused");
169
+ }
170
+ const frame = this.pausedState.callFrames.find((f) => f.callFrameId === callFrameId);
171
+ if (!frame) {
172
+ throw new Error(`Call frame ${callFrameId} not found`);
173
+ }
174
+ const result = await this.send("Debugger.evaluateOnCallFrame", {
175
+ callFrameId,
176
+ expression: "this",
177
+ returnByValue: false
178
+ });
179
+ const variables = [];
180
+ try {
181
+ const scopeResult = await this.send("Runtime.getProperties", {
182
+ objectId: result.result?.objectId,
183
+ ownProperties: true,
184
+ generatePreview: false
185
+ });
186
+ const descriptors = scopeResult.result ?? [];
187
+ for (const desc of descriptors.slice(0, MAX_VARIABLES_PER_SCOPE)) {
188
+ if (desc.value) {
189
+ variables.push({
190
+ name: desc.name,
191
+ type: desc.value.type,
192
+ value: this.truncateValue(this.formatRemoteObject(desc.value))
193
+ });
194
+ }
195
+ }
196
+ } catch {
197
+ }
198
+ return variables;
199
+ }
200
+ // ── Execution Control ──────────────────────────────────────────────────
201
+ async resume() {
202
+ this.clearAutoResumeTimer();
203
+ await this.send("Debugger.resume", {});
204
+ this.pausedState = null;
205
+ }
206
+ async stepOver() {
207
+ this.clearAutoResumeTimer();
208
+ await this.send("Debugger.stepOver", {});
209
+ }
210
+ async stepInto() {
211
+ this.clearAutoResumeTimer();
212
+ await this.send("Debugger.stepInto", {});
213
+ }
214
+ // ── Evaluation ─────────────────────────────────────────────────────────
215
+ async evaluate(expression, callFrameId) {
216
+ let result;
217
+ if (callFrameId && this.pausedState) {
218
+ result = await this.send("Debugger.evaluateOnCallFrame", {
219
+ callFrameId,
220
+ expression,
221
+ returnByValue: false,
222
+ generatePreview: true
223
+ });
224
+ } else {
225
+ result = await this.send("Runtime.evaluate", {
226
+ expression,
227
+ returnByValue: false,
228
+ generatePreview: true
229
+ });
230
+ }
231
+ const remoteObj = result.result;
232
+ const errorValue = this.extractEvalError(result, remoteObj);
233
+ if (errorValue !== null) {
234
+ return { type: "error", value: errorValue };
235
+ }
236
+ return {
237
+ type: remoteObj?.type ?? "undefined",
238
+ value: this.truncateValue(this.formatRemoteObject(remoteObj))
239
+ };
240
+ }
241
+ // ── Event Callbacks ────────────────────────────────────────────────────
242
+ onPaused(callback) {
243
+ this.onPausedCallback = callback;
244
+ }
245
+ onResumed(callback) {
246
+ this.onResumedCallback = callback;
247
+ }
248
+ onAutoResumed(callback) {
249
+ this.onAutoResumedCallback = callback;
250
+ }
251
+ // ── Private: CDP Communication ─────────────────────────────────────────
252
+ send(method, params) {
253
+ return new Promise((resolve, reject) => {
254
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
255
+ reject(new Error("CDP WebSocket is not connected"));
256
+ return;
257
+ }
258
+ const id = ++this.requestId;
259
+ const request = { id, method, params };
260
+ this.pendingRequests.set(id, { resolve, reject });
261
+ this.ws.send(JSON.stringify(request));
262
+ setTimeout(() => {
263
+ if (this.pendingRequests.has(id)) {
264
+ this.pendingRequests.delete(id);
265
+ reject(new Error(`CDP request timed out: ${method}`));
266
+ }
267
+ }, 1e4);
268
+ });
269
+ }
270
+ handleMessage(raw) {
271
+ let msg;
272
+ try {
273
+ msg = JSON.parse(raw);
274
+ } catch {
275
+ return;
276
+ }
277
+ if ("id" in msg && typeof msg.id === "number") {
278
+ const pending = this.pendingRequests.get(msg.id);
279
+ if (pending) {
280
+ this.pendingRequests.delete(msg.id);
281
+ const response = msg;
282
+ if (response.error) {
283
+ pending.reject(new Error(`CDP error: ${response.error.message}`));
284
+ } else {
285
+ pending.resolve(response.result ?? {});
286
+ }
287
+ }
288
+ return;
289
+ }
290
+ const event = msg;
291
+ if (event.method === "Debugger.paused") {
292
+ this.handlePaused(event.params ?? {});
293
+ } else if (event.method === "Debugger.resumed") {
294
+ this.pausedState = null;
295
+ this.clearAutoResumeTimer();
296
+ this.onResumedCallback?.();
297
+ }
298
+ }
299
+ handlePaused(params) {
300
+ const rawFrames = params.callFrames ?? [];
301
+ const reason = params.reason ?? "unknown";
302
+ const hitBreakpoints = params.hitBreakpoints;
303
+ const callFrames = rawFrames.map((frame, index) => ({
304
+ index,
305
+ functionName: frame.functionName || "(anonymous)",
306
+ file: frame.url,
307
+ // Convert to 1-based
308
+ line: frame.location.lineNumber + 1,
309
+ column: frame.location.columnNumber + 1,
310
+ callFrameId: frame.callFrameId
311
+ }));
312
+ this.pausedState = { callFrames, reason, hitBreakpoints };
313
+ this.clearAutoResumeTimer();
314
+ this.autoResumeTimer = setTimeout(() => {
315
+ if (this.pausedState) {
316
+ this.resume().then(() => {
317
+ this.onAutoResumedCallback?.();
318
+ }).catch(() => {
319
+ });
320
+ }
321
+ }, AUTO_RESUME_TIMEOUT_MS);
322
+ this.onPausedCallback?.(this.pausedState);
323
+ }
324
+ // ── Private: Probe Buffer ──────────────────────────────────────────────
325
+ async ensureProbeBuffer() {
326
+ if (this.probeBufferInjected) return;
327
+ await this.send("Runtime.evaluate", {
328
+ expression: `
329
+ globalThis.__conveyorProbes = globalThis.__conveyorProbes || {
330
+ _buffer: [],
331
+ _maxSize: ${PROBE_BUFFER_MAX_SIZE},
332
+ probe(label, data) {
333
+ this._buffer.push({ label, data, timestamp: Date.now() });
334
+ if (this._buffer.length > this._maxSize) this._buffer.shift();
335
+ },
336
+ read(label, limit) {
337
+ const items = label !== undefined
338
+ ? this._buffer.filter(function(e) { return e.label === label; })
339
+ : this._buffer;
340
+ return limit !== undefined ? items.slice(-limit) : items;
341
+ },
342
+ clear(label) {
343
+ if (label !== undefined) {
344
+ this._buffer = this._buffer.filter(function(e) { return e.label !== label; });
345
+ } else {
346
+ this._buffer = [];
347
+ }
348
+ }
349
+ };
350
+ "ok"
351
+ `,
352
+ returnByValue: true
353
+ });
354
+ this.probeBufferInjected = true;
355
+ }
356
+ // ── Private: Helpers ───────────────────────────────────────────────────
357
+ extractEvalError(result, remoteObj) {
358
+ if (this.protocol === "webkit") {
359
+ if (result.wasThrown) {
360
+ return remoteObj?.description ?? remoteObj?.value?.toString() ?? "Unknown error";
361
+ }
362
+ return null;
363
+ }
364
+ const exceptionDetails = result.exceptionDetails;
365
+ if (exceptionDetails) {
366
+ return exceptionDetails.exception?.description ?? exceptionDetails.text ?? "Unknown error";
367
+ }
368
+ return null;
369
+ }
370
+ fileToUrlRegex(file) {
371
+ const escaped = file.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
372
+ return `.*${escaped}$`;
373
+ }
374
+ formatRemoteObject(obj) {
375
+ if (!obj) return "undefined";
376
+ if (obj.type === "undefined") return "undefined";
377
+ if (obj.type === "boolean" || obj.type === "number") return String(obj.value);
378
+ if (obj.type === "string") return JSON.stringify(obj.value);
379
+ if (obj.value === null) return "null";
380
+ if (obj.description) return obj.description;
381
+ if (obj.subtype) return `[${obj.type}:${obj.subtype}]`;
382
+ return `[${obj.type}]`;
383
+ }
384
+ truncateValue(value) {
385
+ if (value.length <= MAX_VARIABLE_VALUE_BYTES) return value;
386
+ return value.slice(0, MAX_VARIABLE_VALUE_BYTES - 3) + "...";
387
+ }
388
+ clearAutoResumeTimer() {
389
+ if (this.autoResumeTimer) {
390
+ clearTimeout(this.autoResumeTimer);
391
+ this.autoResumeTimer = null;
392
+ }
393
+ }
394
+ cleanup() {
395
+ this.clearAutoResumeTimer();
396
+ this.ws = null;
397
+ this.pausedState = null;
398
+ this.probes.clear();
399
+ this.probeBufferInjected = false;
400
+ for (const [, pending] of this.pendingRequests) {
401
+ pending.reject(new Error("CDP connection closed"));
402
+ }
403
+ this.pendingRequests.clear();
404
+ }
405
+ };
406
+
407
+ // src/debug/playwright-types.ts
408
+ var MAX_VARIABLE_VALUE_BYTES2 = 2048;
409
+ function formatRemoteObject(obj) {
410
+ if (!obj) return "undefined";
411
+ if (obj.type === "undefined") return "undefined";
412
+ if (obj.type === "boolean" || obj.type === "number") return String(obj.value);
413
+ if (obj.type === "string") return JSON.stringify(obj.value);
414
+ if (obj.value === null) return "null";
415
+ if (obj.description) return obj.description;
416
+ if (obj.subtype) return `[${obj.type}:${obj.subtype}]`;
417
+ return `[${obj.type}]`;
418
+ }
419
+ function truncateValue(value) {
420
+ if (value.length <= MAX_VARIABLE_VALUE_BYTES2) return value;
421
+ return value.slice(0, MAX_VARIABLE_VALUE_BYTES2 - 3) + "...";
422
+ }
423
+
424
+ // src/debug/playwright-client.ts
425
+ var MAX_VARIABLES_PER_SCOPE2 = 50;
426
+ var AUTO_RESUME_TIMEOUT_MS2 = 3e4;
427
+ var PROBE_BUFFER_MAX_SIZE2 = 1e3;
428
+ var BUFFER_MAX = 500;
429
+ var ERROR_BUFFER_MAX = 200;
430
+ var INACTIVITY_TIMEOUT_MS = 6e5;
431
+ var PlaywrightDebugClient = class {
432
+ browser = null;
433
+ page = null;
434
+ cdpSession = null;
435
+ breakpoints = /* @__PURE__ */ new Map();
436
+ probes = /* @__PURE__ */ new Map();
437
+ probeCounter = 0;
438
+ probeBufferInjected = false;
439
+ pausedState = null;
440
+ autoResumeTimer = null;
441
+ inactivityTimer = null;
442
+ onPausedCallback = null;
443
+ onResumedCallback = null;
444
+ onAutoResumedCallback = null;
445
+ consoleMessages = [];
446
+ networkRequests = [];
447
+ pageErrors = [];
448
+ sourceMapDetected = null;
449
+ async launch(previewUrl) {
450
+ if (this.browser) throw new Error("Playwright browser is already running");
451
+ const { chromium } = await import("playwright-core");
452
+ this.browser = await chromium.launch({
453
+ headless: true,
454
+ args: [
455
+ "--no-sandbox",
456
+ "--disable-setuid-sandbox",
457
+ "--disable-dev-shm-usage",
458
+ "--disable-gpu"
459
+ ]
460
+ });
461
+ const context = this.browser.contexts()[0] ?? await this.browser.newContext();
462
+ this.page = await context.newPage();
463
+ this.setupPassiveCapture();
464
+ await this.page.goto(previewUrl, { waitUntil: "domcontentloaded" });
465
+ this.cdpSession = await context.newCDPSession(this.page);
466
+ await this.cdpSession.send("Debugger.enable", {});
467
+ await this.cdpSession.send("Runtime.enable", {});
468
+ this.setupCdpEvents();
469
+ await this.checkSourceMaps();
470
+ this.resetInactivityTimer();
471
+ }
472
+ async close() {
473
+ this.clearAutoResumeTimer();
474
+ this.clearInactivityTimer();
475
+ if (this.cdpSession) {
476
+ try {
477
+ await this.cdpSession.detach();
478
+ } catch {
479
+ }
480
+ this.cdpSession = null;
481
+ }
482
+ if (this.browser) {
483
+ try {
484
+ await this.browser.close();
485
+ } catch {
486
+ }
487
+ this.browser = null;
488
+ }
489
+ this.page = null;
490
+ this.pausedState = null;
491
+ this.breakpoints.clear();
492
+ this.probes.clear();
493
+ this.probeBufferInjected = false;
494
+ this.consoleMessages = [];
495
+ this.networkRequests = [];
496
+ this.pageErrors = [];
497
+ }
498
+ isConnected() {
499
+ return this.browser !== null && this.cdpSession !== null;
500
+ }
501
+ hasSourceMaps() {
502
+ return this.sourceMapDetected;
503
+ }
504
+ async setBreakpoint(file, line, condition) {
505
+ const session = this.requireSession();
506
+ this.resetInactivityTimer();
507
+ const params = {
508
+ urlRegex: this.fileToUrlRegex(file),
509
+ // CDP uses 0-based lines
510
+ lineNumber: line - 1
511
+ };
512
+ if (condition) params.condition = condition;
513
+ const result = await session.send("Debugger.setBreakpointByUrl", params);
514
+ const breakpointId = result.breakpointId;
515
+ this.breakpoints.set(breakpointId, { breakpointId, file, line, condition });
516
+ return breakpointId;
517
+ }
518
+ async removeBreakpoint(breakpointId) {
519
+ const session = this.requireSession();
520
+ await session.send("Debugger.removeBreakpoint", { breakpointId });
521
+ this.breakpoints.delete(breakpointId);
522
+ }
523
+ async removeAllBreakpoints() {
524
+ for (const id of [...this.breakpoints.keys()]) {
525
+ try {
526
+ await this.removeBreakpoint(id);
527
+ } catch {
528
+ this.breakpoints.delete(id);
529
+ }
530
+ }
531
+ }
532
+ listBreakpoints() {
533
+ return [...this.breakpoints.values()];
534
+ }
535
+ async setLogpoint(file, line, expressions, label) {
536
+ await this.ensureProbeBuffer();
537
+ const probeId = `client-probe-${++this.probeCounter}`;
538
+ const probeLabel = label ?? `${file}:${line}`;
539
+ const dataEntries = expressions.map((expr) => `${JSON.stringify(expr)}: (${expr})`).join(", ");
540
+ const condition = `(globalThis.__conveyorProbes?.probe(${JSON.stringify(probeLabel)}, {${dataEntries}}), false)`;
541
+ const breakpointId = await this.setBreakpoint(file, line, condition);
542
+ this.probes.set(probeId, { probeId, breakpointId, file, line, expressions, label: probeLabel });
543
+ return probeId;
544
+ }
545
+ async removeProbe(probeId) {
546
+ const probe = this.probes.get(probeId);
547
+ if (!probe) throw new Error(`Probe ${probeId} not found`);
548
+ await this.removeBreakpoint(probe.breakpointId);
549
+ this.probes.delete(probeId);
550
+ }
551
+ listProbes() {
552
+ return [...this.probes.values()];
553
+ }
554
+ async getProbeResults(label, limit) {
555
+ const args = [
556
+ label === void 0 ? "undefined" : JSON.stringify(label),
557
+ limit === void 0 ? "undefined" : String(limit)
558
+ ].join(", ");
559
+ const result = await this.evaluate(
560
+ `JSON.stringify(globalThis.__conveyorProbes?.read(${args}) ?? [])`
561
+ );
562
+ if (result.type === "error") return [];
563
+ try {
564
+ const raw = result.value;
565
+ return JSON.parse(raw.startsWith('"') ? JSON.parse(raw) : raw);
566
+ } catch {
567
+ return [];
568
+ }
569
+ }
570
+ async clearProbeResults(label) {
571
+ const arg = label === void 0 ? "undefined" : JSON.stringify(label);
572
+ await this.evaluate(`(globalThis.__conveyorProbes?.clear(${arg}), undefined)`);
573
+ }
574
+ isPaused() {
575
+ return this.pausedState !== null;
576
+ }
577
+ getPausedState() {
578
+ return this.pausedState;
579
+ }
580
+ getCallStack() {
581
+ return this.pausedState?.callFrames ?? [];
582
+ }
583
+ async getScopeVariables(callFrameId) {
584
+ if (!this.pausedState) throw new Error("Debugger is not paused");
585
+ const session = this.requireSession();
586
+ const frame = this.pausedState.callFrames.find((f) => f.callFrameId === callFrameId);
587
+ if (!frame) throw new Error(`Call frame ${callFrameId} not found`);
588
+ const result = await session.send("Debugger.evaluateOnCallFrame", {
589
+ callFrameId,
590
+ expression: "this",
591
+ returnByValue: false
592
+ });
593
+ const variables = [];
594
+ try {
595
+ const scopeResult = await session.send("Runtime.getProperties", {
596
+ objectId: result.result?.objectId,
597
+ ownProperties: true,
598
+ generatePreview: false
599
+ });
600
+ for (const desc of (scopeResult.result ?? []).slice(
601
+ 0,
602
+ MAX_VARIABLES_PER_SCOPE2
603
+ )) {
604
+ if (desc.value) {
605
+ variables.push({
606
+ name: desc.name,
607
+ type: desc.value.type,
608
+ value: truncateValue(formatRemoteObject(desc.value))
609
+ });
610
+ }
611
+ }
612
+ } catch {
613
+ }
614
+ return variables;
615
+ }
616
+ async resume() {
617
+ this.clearAutoResumeTimer();
618
+ await this.requireSession().send("Debugger.resume", {});
619
+ this.pausedState = null;
620
+ }
621
+ async stepOver() {
622
+ this.clearAutoResumeTimer();
623
+ await this.requireSession().send("Debugger.stepOver", {});
624
+ }
625
+ async stepInto() {
626
+ this.clearAutoResumeTimer();
627
+ await this.requireSession().send("Debugger.stepInto", {});
628
+ }
629
+ async evaluate(expression, callFrameId) {
630
+ const session = this.requireSession();
631
+ this.resetInactivityTimer();
632
+ let result;
633
+ if (callFrameId && this.pausedState) {
634
+ result = await session.send("Debugger.evaluateOnCallFrame", {
635
+ callFrameId,
636
+ expression,
637
+ returnByValue: false,
638
+ generatePreview: true
639
+ });
640
+ } else {
641
+ result = await session.send("Runtime.evaluate", {
642
+ expression,
643
+ returnByValue: false,
644
+ generatePreview: true
645
+ });
646
+ }
647
+ const remoteObj = result.result;
648
+ const exDetails = result.exceptionDetails;
649
+ if (exDetails) {
650
+ return {
651
+ type: "error",
652
+ value: exDetails.exception?.description ?? exDetails.text ?? "Unknown error"
653
+ };
654
+ }
655
+ return {
656
+ type: remoteObj?.type ?? "undefined",
657
+ value: truncateValue(formatRemoteObject(remoteObj))
658
+ };
659
+ }
660
+ async navigate(url) {
661
+ this.requirePage();
662
+ this.resetInactivityTimer();
663
+ await this.page?.goto(url, { waitUntil: "domcontentloaded" });
664
+ }
665
+ async click(selector) {
666
+ this.requirePage();
667
+ this.resetInactivityTimer();
668
+ await this.page?.click(selector, { timeout: 1e4 });
669
+ }
670
+ async screenshot() {
671
+ this.requirePage();
672
+ this.resetInactivityTimer();
673
+ const buffer = await this.page?.screenshot({ type: "png", encoding: "base64" });
674
+ return typeof buffer === "string" ? buffer : Buffer.from(buffer).toString("base64");
675
+ }
676
+ getCurrentUrl() {
677
+ this.requirePage();
678
+ return this.page?.url() ?? "";
679
+ }
680
+ getConsoleMessages(level, limit) {
681
+ let messages = this.consoleMessages;
682
+ if (level) messages = messages.filter((m) => m.level === level);
683
+ if (limit) messages = messages.slice(-limit);
684
+ return messages;
685
+ }
686
+ getNetworkRequests(filter, limit) {
687
+ let requests = this.networkRequests;
688
+ if (filter) {
689
+ const regex = new RegExp(filter, "i");
690
+ requests = requests.filter((r) => regex.test(r.url));
691
+ }
692
+ if (limit) requests = requests.slice(-limit);
693
+ return requests;
694
+ }
695
+ getPageErrors(limit) {
696
+ return limit ? this.pageErrors.slice(-limit) : [...this.pageErrors];
697
+ }
698
+ onPaused(callback) {
699
+ this.onPausedCallback = callback;
700
+ }
701
+ onResumed(callback) {
702
+ this.onResumedCallback = callback;
703
+ }
704
+ onAutoResumed(callback) {
705
+ this.onAutoResumedCallback = callback;
706
+ }
707
+ // ── Private ───────────────────────────────────────────────────────────
708
+ setupCdpEvents() {
709
+ if (!this.cdpSession) return;
710
+ this.cdpSession.on("Debugger.paused", (params) => this.handlePaused(params));
711
+ this.cdpSession.on("Debugger.resumed", () => {
712
+ this.pausedState = null;
713
+ this.clearAutoResumeTimer();
714
+ this.onResumedCallback?.();
715
+ });
716
+ }
717
+ handlePaused(params) {
718
+ const rawFrames = params.callFrames ?? [];
719
+ const callFrames = rawFrames.map((frame, index) => ({
720
+ index,
721
+ functionName: frame.functionName || "(anonymous)",
722
+ file: frame.url,
723
+ line: frame.location.lineNumber + 1,
724
+ column: frame.location.columnNumber + 1,
725
+ callFrameId: frame.callFrameId
726
+ }));
727
+ this.pausedState = {
728
+ callFrames,
729
+ reason: params.reason ?? "unknown",
730
+ hitBreakpoints: params.hitBreakpoints
731
+ };
732
+ this.clearAutoResumeTimer();
733
+ this.autoResumeTimer = setTimeout(() => {
734
+ if (this.pausedState) {
735
+ this.resume().then(() => this.onAutoResumedCallback?.()).catch(() => {
736
+ });
737
+ }
738
+ }, AUTO_RESUME_TIMEOUT_MS2);
739
+ this.onPausedCallback?.(this.pausedState);
740
+ }
741
+ setupPassiveCapture() {
742
+ if (!this.page) return;
743
+ this.page.on("console", (msg) => {
744
+ const m = msg;
745
+ this.consoleMessages.push({
746
+ level: m.type(),
747
+ text: m.text(),
748
+ timestamp: Date.now(),
749
+ url: m.location()?.url,
750
+ line: m.location()?.lineNumber
751
+ });
752
+ if (this.consoleMessages.length > BUFFER_MAX) this.consoleMessages.shift();
753
+ });
754
+ this.page.on("pageerror", (error) => {
755
+ const e = error;
756
+ this.pageErrors.push({ message: e.message, stack: e.stack, timestamp: Date.now() });
757
+ if (this.pageErrors.length > ERROR_BUFFER_MAX) this.pageErrors.shift();
758
+ });
759
+ this.page.on("request", (request) => {
760
+ const r = request;
761
+ this.networkRequests.push({
762
+ url: r.url(),
763
+ method: r.method(),
764
+ resourceType: r.resourceType(),
765
+ timestamp: Date.now()
766
+ });
767
+ if (this.networkRequests.length > BUFFER_MAX) this.networkRequests.shift();
768
+ });
769
+ this.page.on("response", (response) => {
770
+ const resp = response;
771
+ const url = resp.url();
772
+ for (let i = this.networkRequests.length - 1; i >= 0; i--) {
773
+ const req = this.networkRequests[i];
774
+ if (req.url === url && req.status === void 0) {
775
+ req.status = resp.status();
776
+ req.duration = Date.now() - req.timestamp;
777
+ break;
778
+ }
779
+ }
780
+ });
781
+ }
782
+ async checkSourceMaps() {
783
+ const hasMapFiles = this.networkRequests.some(
784
+ (r) => r.url.endsWith(".map") || r.url.includes("sourceMappingURL")
785
+ );
786
+ if (hasMapFiles) {
787
+ this.sourceMapDetected = true;
788
+ return;
789
+ }
790
+ try {
791
+ const result = await this.requireSession().send("Runtime.evaluate", {
792
+ expression: "true",
793
+ returnByValue: true
794
+ });
795
+ this.sourceMapDetected = result.result ? null : false;
796
+ } catch {
797
+ this.sourceMapDetected = null;
798
+ }
799
+ }
800
+ async ensureProbeBuffer() {
801
+ if (this.probeBufferInjected) return;
802
+ const session = this.requireSession();
803
+ await session.send("Runtime.evaluate", {
804
+ expression: `globalThis.__conveyorProbes=globalThis.__conveyorProbes||{_buffer:[],_maxSize:${PROBE_BUFFER_MAX_SIZE2},probe(l,d){this._buffer.push({label:l,data:d,timestamp:Date.now()});if(this._buffer.length>this._maxSize)this._buffer.shift()},read(l,n){var i=l!==void 0?this._buffer.filter(function(e){return e.label===l}):this._buffer;return n!==void 0?i.slice(-n):i},clear(l){if(l!==void 0){this._buffer=this._buffer.filter(function(e){return e.label!==l})}else{this._buffer=[]}}};"ok"`,
805
+ returnByValue: true
806
+ });
807
+ this.probeBufferInjected = true;
808
+ }
809
+ requireSession() {
810
+ if (!this.cdpSession) throw new Error("Playwright CDP session is not connected");
811
+ return this.cdpSession;
812
+ }
813
+ requirePage() {
814
+ if (!this.page) throw new Error("No page available");
815
+ }
816
+ fileToUrlRegex(file) {
817
+ return `.*${file.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`;
818
+ }
819
+ clearAutoResumeTimer() {
820
+ if (this.autoResumeTimer) {
821
+ clearTimeout(this.autoResumeTimer);
822
+ this.autoResumeTimer = null;
823
+ }
824
+ }
825
+ resetInactivityTimer() {
826
+ this.clearInactivityTimer();
827
+ this.inactivityTimer = setTimeout(() => {
828
+ this.close().catch(() => {
829
+ });
830
+ }, INACTIVITY_TIMEOUT_MS);
831
+ }
832
+ clearInactivityTimer() {
833
+ if (this.inactivityTimer) {
834
+ clearTimeout(this.inactivityTimer);
835
+ this.inactivityTimer = null;
836
+ }
837
+ }
838
+ };
839
+
840
+ // src/debug/debug-manager.ts
841
+ import { spawn } from "child_process";
842
+ import { execSync } from "child_process";
843
+ var NODE_INSPECTOR_REGEX = /Debugger listening on (ws:\/\/127\.0\.0\.1:\d+\/[^\s]+)/;
844
+ var BUN_INSPECTOR_REGEX = /(ws:\/\/[\w.-]+:\d+\/[\w-]+)/;
845
+ var CONNECTION_TIMEOUT_MS = 1e4;
846
+ var PLAYWRIGHT_INSTALL_TIMEOUT_MS = 12e4;
847
+ var DebugManager = class {
848
+ cdpClient = null;
849
+ playwrightClient = null;
850
+ debugProcess = null;
851
+ _isDebugMode = false;
852
+ _isClientDebugMode = false;
853
+ startCommand;
854
+ workingDir;
855
+ eventHandler;
856
+ breakpointHitQueue = [];
857
+ clientBreakpointHitQueue = [];
858
+ constructor(startCommand, workingDir, eventHandler) {
859
+ this.startCommand = startCommand;
860
+ this.workingDir = workingDir;
861
+ this.eventHandler = eventHandler;
862
+ }
863
+ // ── Public API ─────────────────────────────────────────────────────────
864
+ isDebugMode() {
865
+ return this._isDebugMode;
866
+ }
867
+ isClientDebugMode() {
868
+ return this._isClientDebugMode;
869
+ }
870
+ getClient() {
871
+ return this.cdpClient;
872
+ }
873
+ getPlaywrightClient() {
874
+ return this.playwrightClient;
875
+ }
876
+ drainBreakpointHitQueue() {
877
+ const queued = [...this.breakpointHitQueue];
878
+ this.breakpointHitQueue = [];
879
+ return queued;
880
+ }
881
+ drainClientBreakpointHitQueue() {
882
+ const queued = [...this.clientBreakpointHitQueue];
883
+ this.clientBreakpointHitQueue = [];
884
+ return queued;
885
+ }
886
+ async enterDebugMode(killCurrentProcess, options) {
887
+ const serverSide = options?.serverSide ?? !options?.clientSide;
888
+ const clientSide = options?.clientSide ?? false;
889
+ if (serverSide && this._isDebugMode) {
890
+ throw new Error("Already in server debug mode");
891
+ }
892
+ if (clientSide && this._isClientDebugMode) {
893
+ throw new Error("Already in client debug mode");
894
+ }
895
+ if (serverSide && !this._isDebugMode) {
896
+ await this.activateServerDebug(killCurrentProcess);
897
+ }
898
+ if (clientSide && !this._isClientDebugMode) {
899
+ await this.activateClientDebug(options?.previewUrl);
900
+ }
901
+ this.eventHandler.onDebugModeEntered();
902
+ }
903
+ async exitDebugMode(restartNormalServer) {
904
+ if (!this._isDebugMode && !this._isClientDebugMode) {
905
+ throw new Error("Not in debug mode");
906
+ }
907
+ if (this.cdpClient) {
908
+ try {
909
+ await this.cdpClient.removeAllBreakpoints();
910
+ } catch {
911
+ }
912
+ this.cdpClient.disconnect();
913
+ this.cdpClient = null;
914
+ }
915
+ if (this.playwrightClient) {
916
+ try {
917
+ await this.playwrightClient.removeAllBreakpoints();
918
+ } catch {
919
+ }
920
+ try {
921
+ await this.playwrightClient.close();
922
+ } catch {
923
+ }
924
+ this.playwrightClient = null;
925
+ }
926
+ if (this.debugProcess && !this.debugProcess.killed) {
927
+ this.debugProcess.kill("SIGTERM");
928
+ this.debugProcess = null;
929
+ }
930
+ this._isDebugMode = false;
931
+ this._isClientDebugMode = false;
932
+ this.breakpointHitQueue = [];
933
+ this.clientBreakpointHitQueue = [];
934
+ restartNormalServer?.();
935
+ this.eventHandler.onDebugModeExited();
936
+ }
937
+ // ── Debug Mode Activation ────────────────────────────────────────────
938
+ async activateServerDebug(killCurrentProcess) {
939
+ killCurrentProcess?.();
940
+ const runtime = detectRuntime(this.startCommand);
941
+ const inspectorUrl = await this.startWithInspector();
942
+ const protocol = runtime === "bun" ? "webkit" : "cdp";
943
+ this.cdpClient = new CdpDebugClient();
944
+ this.cdpClient.onPaused((state) => {
945
+ this.breakpointHitQueue.push(state);
946
+ this.eventHandler.onBreakpointHit(state);
947
+ });
948
+ this.cdpClient.onAutoResumed(() => {
949
+ this.eventHandler.onAutoResumed();
950
+ });
951
+ await this.cdpClient.connect(inspectorUrl, protocol);
952
+ this._isDebugMode = true;
953
+ try {
954
+ await injectTelemetry(this.cdpClient);
955
+ } catch {
956
+ }
957
+ }
958
+ async activateClientDebug(previewUrl) {
959
+ if (!previewUrl) {
960
+ throw new Error("previewUrl is required for client-side debug mode");
961
+ }
962
+ await this.ensurePlaywrightInstalled();
963
+ this.playwrightClient = new PlaywrightDebugClient();
964
+ this.playwrightClient.onPaused((state) => {
965
+ this.clientBreakpointHitQueue.push(state);
966
+ this.eventHandler.onClientBreakpointHit(state);
967
+ });
968
+ this.playwrightClient.onAutoResumed(() => {
969
+ this.eventHandler.onAutoResumed();
970
+ });
971
+ await this.playwrightClient.launch(previewUrl);
972
+ this._isClientDebugMode = true;
973
+ }
974
+ // ── Playwright Installation ───────────────────────────────────────────
975
+ async ensurePlaywrightInstalled() {
976
+ try {
977
+ await import("playwright-core");
978
+ const pw = await import("playwright-core");
979
+ const executablePath = pw.chromium.executablePath();
980
+ if (executablePath) return;
981
+ } catch {
982
+ }
983
+ this.eventHandler.onOutput(
984
+ "stderr",
985
+ "Installing Playwright Chromium (first-time setup, ~250MB)...\n"
986
+ );
987
+ try {
988
+ execSync("npx playwright install chromium --with-deps", {
989
+ cwd: this.workingDir,
990
+ timeout: PLAYWRIGHT_INSTALL_TIMEOUT_MS,
991
+ stdio: "pipe"
992
+ });
993
+ this.eventHandler.onOutput("stderr", "Playwright Chromium installed successfully.\n");
994
+ } catch (error) {
995
+ throw new Error(
996
+ `Failed to install Playwright Chromium: ${error instanceof Error ? error.message : "Unknown error"}`,
997
+ { cause: error }
998
+ );
999
+ }
1000
+ }
1001
+ // ── Private ────────────────────────────────────────────────────────────
1002
+ // oxlint-disable-next-line require-await
1003
+ async startWithInspector() {
1004
+ const runtime = detectRuntime(this.startCommand);
1005
+ return new Promise((resolve, reject) => {
1006
+ let resolved = false;
1007
+ const timeout = setTimeout(() => {
1008
+ if (!resolved) {
1009
+ resolved = true;
1010
+ reject(new Error(getTimeoutMessage(runtime)));
1011
+ }
1012
+ }, CONNECTION_TIMEOUT_MS);
1013
+ let cmd = this.startCommand;
1014
+ let env = { ...process.env };
1015
+ if (runtime === "bun") {
1016
+ cmd = injectBunInspectFlag(this.startCommand);
1017
+ } else {
1018
+ env = { ...env, NODE_OPTIONS: "--inspect=0" };
1019
+ }
1020
+ const inspectorRegex = runtime === "bun" ? BUN_INSPECTOR_REGEX : NODE_INSPECTOR_REGEX;
1021
+ const child = runStartCommandWithEnv(cmd, this.workingDir, env, (stream, data) => {
1022
+ this.eventHandler.onOutput(stream, data);
1023
+ if (!resolved && stream === "stderr") {
1024
+ const match = data.match(inspectorRegex);
1025
+ if (match?.[1]) {
1026
+ resolved = true;
1027
+ clearTimeout(timeout);
1028
+ resolve(match[1]);
1029
+ }
1030
+ }
1031
+ });
1032
+ this.debugProcess = child;
1033
+ child.on("error", (err) => {
1034
+ if (!resolved) {
1035
+ resolved = true;
1036
+ clearTimeout(timeout);
1037
+ reject(err);
1038
+ }
1039
+ });
1040
+ child.on("exit", (code) => {
1041
+ if (!resolved) {
1042
+ resolved = true;
1043
+ clearTimeout(timeout);
1044
+ reject(new Error(`Debug process exited with code ${code} before inspector started`));
1045
+ }
1046
+ });
1047
+ });
1048
+ }
1049
+ };
1050
+ var BUN_COMMAND_REGEX = /\b(bun|bunx)\b/;
1051
+ var NODE_COMMAND_REGEX = /\b(node|npx|tsx|ts-node)\b/;
1052
+ function detectRuntime(startCommand) {
1053
+ if (BUN_COMMAND_REGEX.test(startCommand)) return "bun";
1054
+ if (NODE_COMMAND_REGEX.test(startCommand)) return "node";
1055
+ if (process.env.BUN_INSTALL) return "bun";
1056
+ return "node";
1057
+ }
1058
+ function injectBunInspectFlag(command) {
1059
+ return command.replace(/\b(bunx?)\b/, "$1 --inspect");
1060
+ }
1061
+ function getTimeoutMessage(runtime) {
1062
+ switch (runtime) {
1063
+ case "bun":
1064
+ return "Timed out waiting for Bun inspector (--inspect flag). Ensure your start command begins with 'bun'";
1065
+ case "node":
1066
+ return "Timed out waiting for Node.js inspector (NODE_OPTIONS='--inspect=0')";
1067
+ default:
1068
+ return "Timed out waiting for debugger inspector. Your runtime may not support CDP.";
1069
+ }
1070
+ }
1071
+ function runStartCommandWithEnv(cmd, cwd, env, onOutput) {
1072
+ const child = spawn("sh", ["-c", cmd], {
1073
+ cwd,
1074
+ stdio: ["ignore", "pipe", "pipe"],
1075
+ detached: true,
1076
+ env: { ...env }
1077
+ });
1078
+ child.stdout?.on("data", (chunk) => {
1079
+ onOutput("stdout", chunk.toString());
1080
+ });
1081
+ child.stderr?.on("data", (chunk) => {
1082
+ onOutput("stderr", chunk.toString());
1083
+ });
1084
+ child.unref();
1085
+ return child;
1086
+ }
1087
+
1088
+ // src/debug/lifecycle-manager.ts
1089
+ var DebugLifecycleManager = class {
1090
+ connection;
1091
+ debugManager = null;
1092
+ hypothesis;
1093
+ sessionStartedAt;
1094
+ breakpointsHitCount = 0;
1095
+ telemetryEvents = [];
1096
+ reproduceRequested = false;
1097
+ keyFindings = [];
1098
+ _isActive = false;
1099
+ constructor(connection) {
1100
+ this.connection = connection;
1101
+ }
1102
+ get isActive() {
1103
+ return this._isActive;
1104
+ }
1105
+ setDebugManager(manager) {
1106
+ this.debugManager = manager;
1107
+ }
1108
+ /**
1109
+ * Creates a DebugEventHandler that bridges DebugManager events
1110
+ * to the lifecycle manager and broadcasts state to the web UI.
1111
+ */
1112
+ createEventHandler(baseOnOutput) {
1113
+ return {
1114
+ onDebugModeEntered: () => {
1115
+ this._isActive = true;
1116
+ this.sessionStartedAt = (/* @__PURE__ */ new Date()).toISOString();
1117
+ this.breakpointsHitCount = 0;
1118
+ this.telemetryEvents = [];
1119
+ this.keyFindings = [];
1120
+ this.broadcastState();
1121
+ },
1122
+ onDebugModeExited: () => {
1123
+ this.broadcastState();
1124
+ },
1125
+ onBreakpointHit: (state) => {
1126
+ this.breakpointsHitCount++;
1127
+ const file = state.callFrames[0]?.file ?? "unknown";
1128
+ const line = state.callFrames[0]?.line ?? 0;
1129
+ this.keyFindings.push(`Breakpoint hit at ${file}:${line} (${state.reason})`);
1130
+ this.broadcastState();
1131
+ },
1132
+ onClientBreakpointHit: (state) => {
1133
+ this.breakpointsHitCount++;
1134
+ const file = state.callFrames[0]?.file ?? "unknown";
1135
+ const line = state.callFrames[0]?.line ?? 0;
1136
+ this.keyFindings.push(`Client breakpoint hit at ${file}:${line}`);
1137
+ this.broadcastState();
1138
+ },
1139
+ onAutoResumed: () => {
1140
+ this.broadcastState();
1141
+ },
1142
+ onDebugError: (message) => {
1143
+ this.keyFindings.push(`Debug error: ${message}`);
1144
+ this.broadcastState();
1145
+ },
1146
+ onOutput: baseOnOutput
1147
+ };
1148
+ }
1149
+ /** Mark the hypothesis for the current debug session */
1150
+ setHypothesis(hypothesis) {
1151
+ this.hypothesis = hypothesis;
1152
+ this.broadcastState();
1153
+ }
1154
+ /** Signal that the user has been asked to reproduce the bug */
1155
+ requestReproduce(hypothesis) {
1156
+ this.reproduceRequested = true;
1157
+ if (hypothesis) this.hypothesis = hypothesis;
1158
+ this.connection.emitDebugReproduceRequested(hypothesis ?? this.hypothesis);
1159
+ this.broadcastState();
1160
+ }
1161
+ /** Add a telemetry event to the debug session */
1162
+ addTelemetryEvent(event) {
1163
+ this.telemetryEvents.push(event);
1164
+ if (this.telemetryEvents.length > 100) {
1165
+ this.telemetryEvents = this.telemetryEvents.slice(-100);
1166
+ }
1167
+ this.broadcastState();
1168
+ }
1169
+ /** Generate and broadcast a debug session summary, then reset state */
1170
+ completeSession() {
1171
+ if (!this._isActive && !this.sessionStartedAt) return null;
1172
+ const endedAt = (/* @__PURE__ */ new Date()).toISOString();
1173
+ const startedAt = this.sessionStartedAt ?? endedAt;
1174
+ const durationMs = new Date(endedAt).getTime() - new Date(startedAt).getTime();
1175
+ const breakpoints = this.getBreakpoints();
1176
+ const probes = this.getProbes();
1177
+ const summary = {
1178
+ hypothesis: this.hypothesis,
1179
+ breakpointsSet: breakpoints.length,
1180
+ breakpointsHit: this.breakpointsHitCount,
1181
+ probesSet: probes.length,
1182
+ telemetryEventsCaptured: this.telemetryEvents.length,
1183
+ keyFindings: this.keyFindings.slice(0, 20),
1184
+ durationMs,
1185
+ startedAt,
1186
+ endedAt
1187
+ };
1188
+ this.connection.emitDebugSessionComplete(summary);
1189
+ this._isActive = false;
1190
+ this.hypothesis = void 0;
1191
+ this.sessionStartedAt = void 0;
1192
+ this.breakpointsHitCount = 0;
1193
+ this.telemetryEvents = [];
1194
+ this.reproduceRequested = false;
1195
+ this.keyFindings = [];
1196
+ this.broadcastState();
1197
+ return summary;
1198
+ }
1199
+ /** Auto-exit cleanup — called when agent session ends */
1200
+ autoCleanup() {
1201
+ if (this._isActive || this.sessionStartedAt) {
1202
+ this.completeSession();
1203
+ }
1204
+ }
1205
+ // ── Private ────────────────────────────────────────────────────────
1206
+ getBreakpoints() {
1207
+ if (!this.debugManager) return [];
1208
+ const client = this.debugManager.getClient();
1209
+ if (!client?.isConnected()) return [];
1210
+ try {
1211
+ return client.listBreakpoints().map((bp) => ({
1212
+ id: bp.breakpointId,
1213
+ file: bp.file,
1214
+ line: bp.line,
1215
+ condition: bp.condition,
1216
+ hitCount: 0
1217
+ }));
1218
+ } catch {
1219
+ return [];
1220
+ }
1221
+ }
1222
+ getProbes() {
1223
+ if (!this.debugManager) return [];
1224
+ const client = this.debugManager.getClient();
1225
+ if (!client?.isConnected()) return [];
1226
+ try {
1227
+ return client.listProbes().map((p) => ({
1228
+ id: p.probeId,
1229
+ file: p.file,
1230
+ line: p.line,
1231
+ expressions: p.expressions,
1232
+ label: p.label
1233
+ }));
1234
+ } catch {
1235
+ return [];
1236
+ }
1237
+ }
1238
+ buildState() {
1239
+ const serverDebug = this.debugManager?.isDebugMode() ?? false;
1240
+ const clientDebug = this.debugManager?.isClientDebugMode() ?? false;
1241
+ const active = this._isActive && (serverDebug || clientDebug);
1242
+ let pausedAt;
1243
+ const client = this.debugManager?.getClient();
1244
+ if (client?.isPaused()) {
1245
+ const callStack = client.getCallStack();
1246
+ if (callStack[0]) {
1247
+ pausedAt = {
1248
+ file: callStack[0].file,
1249
+ line: callStack[0].line
1250
+ };
1251
+ }
1252
+ }
1253
+ return {
1254
+ active,
1255
+ hypothesis: this.hypothesis,
1256
+ serverDebug,
1257
+ clientDebug,
1258
+ breakpoints: this.getBreakpoints(),
1259
+ probes: this.getProbes(),
1260
+ telemetryEvents: this.telemetryEvents.slice(-50),
1261
+ pausedAt,
1262
+ reproduceRequested: this.reproduceRequested
1263
+ };
1264
+ }
1265
+ broadcastState() {
1266
+ this.connection.emitDebugStateChanged(this.buildState());
1267
+ }
1268
+ };
13
1269
  export {
14
1270
  AgentRunner,
1271
+ CdpDebugClient,
15
1272
  ConveyorConnection,
1273
+ DebugLifecycleManager,
1274
+ DebugManager,
16
1275
  FileCache,
17
1276
  ProjectConnection,
18
1277
  ProjectRunner,