@petukhovart/agent-view 0.9.2 → 0.10.1
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/.claude-plugin/plugin.json +21 -21
- package/LICENSE +21 -21
- package/README.md +26 -1
- package/dist/adapters/browser.d.ts.map +1 -1
- package/dist/adapters/browser.js +2 -2
- package/dist/adapters/browser.js.map +1 -1
- package/dist/adapters/electron.d.ts.map +1 -1
- package/dist/adapters/electron.js +2 -2
- package/dist/adapters/electron.js.map +1 -1
- package/dist/adapters/target-filter.d.ts +11 -0
- package/dist/adapters/target-filter.d.ts.map +1 -0
- package/dist/adapters/target-filter.js +19 -0
- package/dist/adapters/target-filter.js.map +1 -0
- package/dist/adapters/tauri.d.ts +2 -5
- package/dist/adapters/tauri.d.ts.map +1 -1
- package/dist/adapters/tauri.js +2 -13
- package/dist/adapters/tauri.js.map +1 -1
- package/dist/cdp/network-stream.d.ts +27 -0
- package/dist/cdp/network-stream.d.ts.map +1 -0
- package/dist/cdp/network-stream.js +336 -0
- package/dist/cdp/network-stream.js.map +1 -0
- package/dist/cdp/transport.d.ts.map +1 -1
- package/dist/cdp/transport.js +167 -8
- package/dist/cdp/transport.js.map +1 -1
- package/dist/cdp/types.d.ts +187 -0
- package/dist/cdp/types.d.ts.map +1 -1
- package/dist/cdp/types.js +55 -0
- package/dist/cdp/types.js.map +1 -1
- package/dist/cli/commands/network.d.ts +19 -0
- package/dist/cli/commands/network.d.ts.map +1 -0
- package/dist/cli/commands/network.js +55 -0
- package/dist/cli/commands/network.js.map +1 -0
- package/dist/cli/index.js +23 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/config/manager.d.ts.map +1 -1
- package/dist/config/manager.js +6 -0
- package/dist/config/manager.js.map +1 -1
- package/dist/config/types.d.ts +8 -0
- package/dist/config/types.d.ts.map +1 -1
- package/dist/inspectors/network/index.d.ts +19 -0
- package/dist/inspectors/network/index.d.ts.map +1 -0
- package/dist/inspectors/network/index.js +169 -0
- package/dist/inspectors/network/index.js.map +1 -0
- package/dist/server/server.d.ts +12 -0
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +193 -18
- package/dist/server/server.js.map +1 -1
- package/package.json +9 -8
- package/skills/verify/SKILL.md +319 -292
- package/skills/verify-recipe/SKILL.md +286 -286
- package/dist/cdp/console-stream.d.ts +0 -35
- package/dist/cdp/console-stream.d.ts.map +0 -1
- package/dist/cdp/console-stream.js +0 -103
- package/dist/cdp/console-stream.js.map +0 -1
- package/dist/inspectors/dom.d.ts +0 -17
- package/dist/inspectors/dom.d.ts.map +0 -1
- package/dist/inspectors/dom.js +0 -131
- package/dist/inspectors/dom.js.map +0 -1
- package/dist/inspectors/scene/pixi.d.ts +0 -3
- package/dist/inspectors/scene/pixi.d.ts.map +0 -1
- package/dist/inspectors/scene/pixi.js +0 -52
- package/dist/inspectors/scene/pixi.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/inspectors/network/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAA;AAuBtE,MAAM,MAAM,UAAU,GAAG;IACvB,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;CAClB,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,UAAU,EAAE,CAAA;IAClB,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AA8BD,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,mBAAmB,EAAE,EAC9B,IAAI,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5C,iBAAiB,CA+BnB;AAWD,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,mBAAmB,EAC1B,IAAI,GAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAA;CAAO,GAClC,MAAM,CAuFR"}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { NetworkEntryState, NetworkResourceType, WebSocketOpcode, WebSocketDirection } from '../../cdp/types.js';
|
|
2
|
+
const SENSITIVE_HEADERS = new Set([
|
|
3
|
+
'authorization',
|
|
4
|
+
'cookie',
|
|
5
|
+
'set-cookie',
|
|
6
|
+
'x-api-key',
|
|
7
|
+
'x-auth-token',
|
|
8
|
+
'proxy-authorization',
|
|
9
|
+
'x-csrf-token',
|
|
10
|
+
'x-xsrf-token',
|
|
11
|
+
]);
|
|
12
|
+
const OPCODE_NAMES = {
|
|
13
|
+
[WebSocketOpcode.Text]: 'text',
|
|
14
|
+
[WebSocketOpcode.Binary]: 'binary',
|
|
15
|
+
[WebSocketOpcode.Close]: 'close',
|
|
16
|
+
[WebSocketOpcode.Ping]: 'ping',
|
|
17
|
+
[WebSocketOpcode.Pong]: 'pong',
|
|
18
|
+
[WebSocketOpcode.Continuation]: 'continuation',
|
|
19
|
+
};
|
|
20
|
+
function entryMethod(entry) {
|
|
21
|
+
if (entry.isWebSocket || entry.resourceType === NetworkResourceType.WebSocket)
|
|
22
|
+
return 'WS';
|
|
23
|
+
if (entry.isEventSource || entry.resourceType === NetworkResourceType.EventSource)
|
|
24
|
+
return 'SSE';
|
|
25
|
+
return entry.method ?? 'GET';
|
|
26
|
+
}
|
|
27
|
+
function entryStatus(entry) {
|
|
28
|
+
if (entry.state === NetworkEntryState.Complete)
|
|
29
|
+
return String(entry.status ?? '');
|
|
30
|
+
if (entry.state === NetworkEntryState.Pending)
|
|
31
|
+
return 'pending';
|
|
32
|
+
return 'FAILED';
|
|
33
|
+
}
|
|
34
|
+
function entryTiming(entry) {
|
|
35
|
+
const isWs = entry.isWebSocket || entry.resourceType === NetworkResourceType.WebSocket;
|
|
36
|
+
const isSse = entry.isEventSource || entry.resourceType === NetworkResourceType.EventSource;
|
|
37
|
+
if (isWs || isSse) {
|
|
38
|
+
const count = entry.frames?.length ?? 0;
|
|
39
|
+
return `(${count} frames)`;
|
|
40
|
+
}
|
|
41
|
+
if (entry.state === NetworkEntryState.Complete && entry.totalMs !== undefined) {
|
|
42
|
+
return `${entry.totalMs}ms`;
|
|
43
|
+
}
|
|
44
|
+
return '';
|
|
45
|
+
}
|
|
46
|
+
export function formatNetworkList(entries, opts) {
|
|
47
|
+
if (entries.length === 0)
|
|
48
|
+
return { text: '(no network activity)', refs: [], nextRef: opts.startRef };
|
|
49
|
+
const refs = entries.map((entry, i) => ({
|
|
50
|
+
ref: opts.startRef + i,
|
|
51
|
+
targetId: entry.targetId,
|
|
52
|
+
requestId: entry.requestId,
|
|
53
|
+
}));
|
|
54
|
+
const nextRef = opts.startRef + entries.length;
|
|
55
|
+
const allLines = entries.map((entry, i) => {
|
|
56
|
+
const ref = opts.startRef + i;
|
|
57
|
+
const method = entryMethod(entry);
|
|
58
|
+
const status = entryStatus(entry);
|
|
59
|
+
const type = entry.resourceType;
|
|
60
|
+
const timing = entryTiming(entry);
|
|
61
|
+
const parts = [`[req=${ref}]`, method, status, type, entry.url];
|
|
62
|
+
if (timing)
|
|
63
|
+
parts.push(timing);
|
|
64
|
+
return parts.join(' ');
|
|
65
|
+
});
|
|
66
|
+
let text;
|
|
67
|
+
if (opts.maxLines !== undefined && allLines.length > opts.maxLines) {
|
|
68
|
+
const visible = allLines.slice(0, opts.maxLines - 1);
|
|
69
|
+
const hidden = allLines.length - visible.length;
|
|
70
|
+
text = [...visible, `… ${hidden} more requests`].join('\n');
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
text = allLines.join('\n');
|
|
74
|
+
}
|
|
75
|
+
return { text, refs, nextRef };
|
|
76
|
+
}
|
|
77
|
+
function redactHeaders(headers, rawHeaders) {
|
|
78
|
+
return Object.entries(headers)
|
|
79
|
+
.map(([k, v]) => {
|
|
80
|
+
const val = !rawHeaders && SENSITIVE_HEADERS.has(k.toLowerCase()) ? '[redacted]' : v;
|
|
81
|
+
return ` ${k}: ${val}`;
|
|
82
|
+
})
|
|
83
|
+
.join('\n');
|
|
84
|
+
}
|
|
85
|
+
export function formatNetworkDetail(entry, opts = {}) {
|
|
86
|
+
const rawHeaders = opts.rawHeaders ?? false;
|
|
87
|
+
const isWs = entry.isWebSocket || entry.resourceType === NetworkResourceType.WebSocket;
|
|
88
|
+
const isSse = entry.isEventSource || entry.resourceType === NetworkResourceType.EventSource;
|
|
89
|
+
const lines = [];
|
|
90
|
+
if (isWs || isSse) {
|
|
91
|
+
const proto = isWs ? 'WS' : 'SSE';
|
|
92
|
+
lines.push(`${proto} ${entry.url}`);
|
|
93
|
+
if (entry.state === NetworkEntryState.Failed) {
|
|
94
|
+
lines.push(`FAILED${entry.errorText ? ': ' + entry.errorText : ''}`);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
lines.push(entry.state);
|
|
98
|
+
}
|
|
99
|
+
if (entry.frames && entry.frames.length > 0) {
|
|
100
|
+
for (const frame of entry.frames) {
|
|
101
|
+
const arrow = frame.direction === WebSocketDirection.Sent ? '↑' : '↓';
|
|
102
|
+
const opName = OPCODE_NAMES[frame.opcode] ?? String(frame.opcode);
|
|
103
|
+
const time = new Date(frame.ts).toISOString().slice(11, 19);
|
|
104
|
+
let payload;
|
|
105
|
+
if (frame.terminal === 'closed') {
|
|
106
|
+
payload = 'closed';
|
|
107
|
+
}
|
|
108
|
+
else if (frame.terminal === 'error') {
|
|
109
|
+
payload = `error: ${frame.errorMessage ?? ''}`;
|
|
110
|
+
}
|
|
111
|
+
else if (frame.binary) {
|
|
112
|
+
payload = `[binary ${frame.size}B]`;
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
payload = frame.payload ?? '';
|
|
116
|
+
}
|
|
117
|
+
lines.push(`${arrow} ${opName} ${frame.size}B ${time} ${payload}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return lines.join('\n');
|
|
121
|
+
}
|
|
122
|
+
lines.push(`${entry.method ?? 'GET'} ${entry.url}`);
|
|
123
|
+
if (entry.state === NetworkEntryState.Pending) {
|
|
124
|
+
lines.push('pending');
|
|
125
|
+
}
|
|
126
|
+
else if (entry.state === NetworkEntryState.Failed) {
|
|
127
|
+
lines.push(`FAILED${entry.errorText ? ': ' + entry.errorText : ''}`);
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
lines.push(`→ ${entry.status ?? ''} ${entry.statusText ?? ''}`.trimEnd());
|
|
131
|
+
}
|
|
132
|
+
const timingParts = [];
|
|
133
|
+
if (entry.ttfbMs !== undefined)
|
|
134
|
+
timingParts.push(`ttfb ${entry.ttfbMs}ms`);
|
|
135
|
+
if (entry.totalMs !== undefined)
|
|
136
|
+
timingParts.push(`total ${entry.totalMs}ms`);
|
|
137
|
+
if (timingParts.length > 0)
|
|
138
|
+
lines.push(`timing: ${timingParts.join(' ')}`);
|
|
139
|
+
if (entry.requestHeaders && Object.keys(entry.requestHeaders).length > 0) {
|
|
140
|
+
lines.push('request headers:');
|
|
141
|
+
lines.push(redactHeaders(entry.requestHeaders, rawHeaders));
|
|
142
|
+
}
|
|
143
|
+
if (entry.responseHeaders && Object.keys(entry.responseHeaders).length > 0) {
|
|
144
|
+
lines.push('response headers:');
|
|
145
|
+
lines.push(redactHeaders(entry.responseHeaders, rawHeaders));
|
|
146
|
+
}
|
|
147
|
+
if (entry.requestBody !== undefined) {
|
|
148
|
+
lines.push('request body:');
|
|
149
|
+
const body = entry.requestBody;
|
|
150
|
+
if (body.length > 4096) {
|
|
151
|
+
lines.push(body.slice(0, 4096));
|
|
152
|
+
lines.push(`… ${body.length - 4096} more bytes`);
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
lines.push(body);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (entry.responseBody !== undefined) {
|
|
159
|
+
lines.push('response body:');
|
|
160
|
+
if (entry.responseBody.binary) {
|
|
161
|
+
lines.push(`[binary ${entry.responseBody.size} bytes]`);
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
lines.push(entry.responseBody.text);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return lines.join('\n');
|
|
168
|
+
}
|
|
169
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/inspectors/network/index.ts"],"names":[],"mappings":"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"}
|
package/dist/server/server.d.ts
CHANGED
|
@@ -19,6 +19,9 @@ export declare class AgentViewServer {
|
|
|
19
19
|
private domTextCache;
|
|
20
20
|
private axTreeCache;
|
|
21
21
|
private consoleStream;
|
|
22
|
+
private networkStream;
|
|
23
|
+
private networkRefs;
|
|
24
|
+
private networkNextRef;
|
|
22
25
|
private token;
|
|
23
26
|
private activeWatches;
|
|
24
27
|
private readonly handlers;
|
|
@@ -34,6 +37,12 @@ export declare class AgentViewServer {
|
|
|
34
37
|
private getRuntimeSession;
|
|
35
38
|
private handleDiscover;
|
|
36
39
|
private handleLaunch;
|
|
40
|
+
/**
|
|
41
|
+
* Connect+attach NetworkStream to every page/iframe target. Idempotent per target.
|
|
42
|
+
* Recreates the stream with config-tuned capacity/captureBody while it is still empty,
|
|
43
|
+
* mirroring the consoleStream pattern.
|
|
44
|
+
*/
|
|
45
|
+
private ensureNetworkAttached;
|
|
37
46
|
private handleDom;
|
|
38
47
|
private findByFilter;
|
|
39
48
|
private handleClick;
|
|
@@ -50,6 +59,9 @@ export declare class AgentViewServer {
|
|
|
50
59
|
private handleEval;
|
|
51
60
|
private handleWatchStreaming;
|
|
52
61
|
private handleConsole;
|
|
62
|
+
private handleNetwork;
|
|
63
|
+
private renderNetworkList;
|
|
64
|
+
private followNetwork;
|
|
53
65
|
private handleStop;
|
|
54
66
|
private shutdown;
|
|
55
67
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/server/server.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/server/server.ts"],"names":[],"mappings":"AAcA,OAAO,EAKL,KAAK,WAAW,EAEhB,KAAK,UAAU,EAGhB,MAAM,iBAAiB,CAAA;AAyDxB,KAAK,YAAY,GACb;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/C;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAA;AAEtC,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAIzG;AAED,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA6B5F;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,CAaxD;AAMD,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,WAAW,CAAmC;IACtD,OAAO,CAAC,QAAQ,CAAiB;IACjC,OAAO,CAAC,SAAS,CAA6C;IAC9D,OAAO,CAAC,UAAU,CAA+B;IACjD,OAAO,CAAC,YAAY,CAA4B;IAChD,OAAO,CAAC,WAAW,CAAoB;IACvC,OAAO,CAAC,aAAa,CAAsB;IAC3C,OAAO,CAAC,aAAa,CAAsB;IAC3C,OAAO,CAAC,WAAW,CAA6D;IAChF,OAAO,CAAC,cAAc,CAAI;IAC1B,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,aAAa,CAA0B;IAE/C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAgB2D;IAEpF,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAA0C;IAC5E,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA2F;IAEnH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAgB5B,OAAO,CAAC,cAAc;IAUtB,OAAO,CAAC,YAAY;YAmBN,cAAc;YAwCd,aAAa;YAMb,aAAa;YA0Bb,cAAc;YAad,iBAAiB;YAgBjB,cAAc;YASd,YAAY;IAmC1B;;;;OAIG;YACW,qBAAqB;YAiBrB,SAAS;YAmDT,YAAY;YAqGZ,WAAW;YA6CX,UAAU;YAwBV,gBAAgB;YA0BhB,UAAU;YAqCV,UAAU;YA+BV,uBAAuB;YAYvB,gBAAgB;YAuBhB,WAAW;YA2CX,UAAU;YAsCV,aAAa;YASb,aAAa;YA2Bb,UAAU;YAwCV,oBAAoB;YAsFpB,aAAa;YA2Hb,aAAa;IA0D3B,OAAO,CAAC,iBAAiB;YAQX,aAAa;YA2Cb,UAAU;YAKV,QAAQ;CAiBvB;AAID,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAOjG"}
|
package/dist/server/server.js
CHANGED
|
@@ -5,6 +5,7 @@ import { homedir, tmpdir } from 'node:os';
|
|
|
5
5
|
import { join, resolve } from 'node:path';
|
|
6
6
|
import { mkdirSync } from 'node:fs';
|
|
7
7
|
import { getAdapter } from '../adapters/registry.js';
|
|
8
|
+
import { isAppTarget } from '../adapters/target-filter.js';
|
|
8
9
|
import { formatAccessibilityTree, countAccessibilityNodes, diffDomText } from '../inspectors/dom/index.js';
|
|
9
10
|
import { getSceneGraph, getRawScene, diffScenes } from '../inspectors/scene/index.js';
|
|
10
11
|
import { RefStore } from './ref-store.js';
|
|
@@ -14,6 +15,9 @@ import { RuntimeType, WebGLEngine, ServerErrorCode } from '../types.js';
|
|
|
14
15
|
import { TargetType, ConsoleLevel, EvaluationError, MouseButton, } from '../cdp/types.js';
|
|
15
16
|
import { listSupportedTargets, connectToRuntime } from '../cdp/transport.js';
|
|
16
17
|
import { ConsoleStream } from '../cdp/_tests/console-stream.js';
|
|
18
|
+
import { NetworkStream } from '../cdp/network-stream.js';
|
|
19
|
+
import { formatNetworkList, formatNetworkDetail } from '../inspectors/network/index.js';
|
|
20
|
+
import { NetworkResourceType } from '../cdp/types.js';
|
|
17
21
|
import { AxTreeCache } from '../cdp/ax-cache.js';
|
|
18
22
|
import { WatchSession } from './watch-session.js';
|
|
19
23
|
import { StopReason, WATCH_MIN_INTERVAL_MS } from '../inspectors/watch/index.js';
|
|
@@ -27,6 +31,9 @@ const TOKEN_DIR = join(homedir(), '.agent-view');
|
|
|
27
31
|
const TOKEN_PATH = join(TOKEN_DIR, 'token');
|
|
28
32
|
const EVAL_OUTPUT_CAP = 64 * 1024;
|
|
29
33
|
const DEFAULT_CONSOLE_TARGETS = [TargetType.Page, TargetType.SharedWorker, TargetType.ServiceWorker];
|
|
34
|
+
const DEFAULT_NETWORK_BUFFER = 200;
|
|
35
|
+
const DEFAULT_NETWORK_MAX_LINES = 50;
|
|
36
|
+
const NETWORK_PAGE_TARGETS = new Set([TargetType.Page, TargetType.Iframe]);
|
|
30
37
|
const RUNTIME_ONLY_TARGETS = new Set([
|
|
31
38
|
TargetType.SharedWorker,
|
|
32
39
|
TargetType.ServiceWorker,
|
|
@@ -64,23 +71,23 @@ export function resolveDepth(filter, explicit) {
|
|
|
64
71
|
}
|
|
65
72
|
export async function textContentFallback(conn, filter) {
|
|
66
73
|
const safeFilter = JSON.stringify(filter);
|
|
67
|
-
const js = `(() => {
|
|
68
|
-
const q = ${safeFilter};
|
|
69
|
-
const results = [];
|
|
70
|
-
for (const el of document.querySelectorAll('body *')) {
|
|
71
|
-
if (results.length >= 5) break;
|
|
72
|
-
const directText = Array.from(el.childNodes)
|
|
73
|
-
.filter(n => n.nodeType === Node.TEXT_NODE)
|
|
74
|
-
.map(n => n.textContent.trim())
|
|
75
|
-
.join(' ');
|
|
76
|
-
if (directText.toLowerCase().includes(q.toLowerCase()) && el.offsetParent !== null) {
|
|
77
|
-
const id = el.id ? '#' + el.id : '';
|
|
78
|
-
const cls = el.className && typeof el.className === 'string' && el.className.trim()
|
|
79
|
-
? '.' + el.className.trim().split(/\\s+/)[0] : '';
|
|
80
|
-
results.push(el.tagName.toLowerCase() + id + cls);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
return results.length ? results.join(', ') : null;
|
|
74
|
+
const js = `(() => {
|
|
75
|
+
const q = ${safeFilter};
|
|
76
|
+
const results = [];
|
|
77
|
+
for (const el of document.querySelectorAll('body *')) {
|
|
78
|
+
if (results.length >= 5) break;
|
|
79
|
+
const directText = Array.from(el.childNodes)
|
|
80
|
+
.filter(n => n.nodeType === Node.TEXT_NODE)
|
|
81
|
+
.map(n => n.textContent.trim())
|
|
82
|
+
.join(' ');
|
|
83
|
+
if (directText.toLowerCase().includes(q.toLowerCase()) && el.offsetParent !== null) {
|
|
84
|
+
const id = el.id ? '#' + el.id : '';
|
|
85
|
+
const cls = el.className && typeof el.className === 'string' && el.className.trim()
|
|
86
|
+
? '.' + el.className.trim().split(/\\s+/)[0] : '';
|
|
87
|
+
results.push(el.tagName.toLowerCase() + id + cls);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return results.length ? results.join(', ') : null;
|
|
84
91
|
})()`;
|
|
85
92
|
const result = await conn.evaluate(js);
|
|
86
93
|
if (!result || typeof result !== 'string') {
|
|
@@ -113,6 +120,9 @@ export class AgentViewServer {
|
|
|
113
120
|
domTextCache = new Map();
|
|
114
121
|
axTreeCache = new AxTreeCache();
|
|
115
122
|
consoleStream = new ConsoleStream();
|
|
123
|
+
networkStream = new NetworkStream();
|
|
124
|
+
networkRefs = new Map();
|
|
125
|
+
networkNextRef = 1;
|
|
116
126
|
token = '';
|
|
117
127
|
activeWatches = new Set();
|
|
118
128
|
handlers = {
|
|
@@ -129,6 +139,7 @@ export class AgentViewServer {
|
|
|
129
139
|
targets: (req) => this.handleTargets(req),
|
|
130
140
|
eval: (req) => this.handleEval(req),
|
|
131
141
|
console: (req) => this.handleConsole(req),
|
|
142
|
+
network: (req) => this.handleNetwork(req),
|
|
132
143
|
stop: () => this.handleStop(),
|
|
133
144
|
};
|
|
134
145
|
streamingCommands = new Set(['watch']);
|
|
@@ -287,6 +298,7 @@ export class AgentViewServer {
|
|
|
287
298
|
return { ok: false, error: 'Launch command does not match project config' };
|
|
288
299
|
}
|
|
289
300
|
if (await isRunning(req.port)) {
|
|
301
|
+
await this.ensureNetworkAttached(req, config).catch(() => { });
|
|
290
302
|
return { ok: true, data: 'Application already running' };
|
|
291
303
|
}
|
|
292
304
|
try {
|
|
@@ -303,8 +315,34 @@ export class AgentViewServer {
|
|
|
303
315
|
}
|
|
304
316
|
throw err;
|
|
305
317
|
}
|
|
318
|
+
// Eager attach (ADR 0002): start capture the moment the app is ready, so page-load
|
|
319
|
+
// traffic is not missed. Best-effort — a launch must not fail on a capture hiccup.
|
|
320
|
+
await this.ensureNetworkAttached(req, config).catch(() => { });
|
|
306
321
|
return { ok: true, data: 'Application launched and ready' };
|
|
307
322
|
}
|
|
323
|
+
/**
|
|
324
|
+
* Connect+attach NetworkStream to every page/iframe target. Idempotent per target.
|
|
325
|
+
* Recreates the stream with config-tuned capacity/captureBody while it is still empty,
|
|
326
|
+
* mirroring the consoleStream pattern.
|
|
327
|
+
*/
|
|
328
|
+
async ensureNetworkAttached(req, config) {
|
|
329
|
+
if (this.networkStream.attachedCount === 0) {
|
|
330
|
+
this.networkStream = new NetworkStream({
|
|
331
|
+
capacity: config?.networkBufferSize ?? DEFAULT_NETWORK_BUFFER,
|
|
332
|
+
captureBody: config?.captureBody ?? false,
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
const all = await listSupportedTargets(req.port);
|
|
336
|
+
for (const t of all) {
|
|
337
|
+
if (!NETWORK_PAGE_TARGETS.has(t.type))
|
|
338
|
+
continue;
|
|
339
|
+
try {
|
|
340
|
+
const session = await this.getPageSession(req, t.id);
|
|
341
|
+
await this.networkStream.attach(session);
|
|
342
|
+
}
|
|
343
|
+
catch { /* a single unreachable target shouldn't abort capture */ }
|
|
344
|
+
}
|
|
345
|
+
}
|
|
308
346
|
async handleDom(req) {
|
|
309
347
|
const { targetId } = await this.resolveWindow(req);
|
|
310
348
|
const conn = await this.getPageSession(req, targetId);
|
|
@@ -689,7 +727,7 @@ export class AgentViewServer {
|
|
|
689
727
|
}
|
|
690
728
|
return found;
|
|
691
729
|
}
|
|
692
|
-
const firstPage = allTargets.find(t => t.type === TargetType.Page);
|
|
730
|
+
const firstPage = allTargets.find(isAppTarget) ?? allTargets.find(t => t.type === TargetType.Page);
|
|
693
731
|
if (!firstPage)
|
|
694
732
|
throw new Error('No page targets found.');
|
|
695
733
|
return firstPage;
|
|
@@ -926,6 +964,97 @@ export class AgentViewServer {
|
|
|
926
964
|
});
|
|
927
965
|
return { ok: true, data: formatConsoleMessages(messages) };
|
|
928
966
|
}
|
|
967
|
+
async handleNetwork(req) {
|
|
968
|
+
const cwd = argStr(req.args, 'cwd');
|
|
969
|
+
const config = cwd ? readConfig(resolve(cwd)) : null;
|
|
970
|
+
await this.ensureNetworkAttached(req, config);
|
|
971
|
+
let resolvedTargetId;
|
|
972
|
+
const targetQuery = argStr(req.args, 'target') ?? argStr(req.args, 'window');
|
|
973
|
+
if (targetQuery) {
|
|
974
|
+
const all = await listSupportedTargets(req.port);
|
|
975
|
+
const resolved = findTargetByIdOrSubstring(all, targetQuery);
|
|
976
|
+
if (!resolved) {
|
|
977
|
+
return { ok: false, error: `Target not found: "${targetQuery}". Run \`agent-view targets\` for the full list.` };
|
|
978
|
+
}
|
|
979
|
+
resolvedTargetId = resolved.id;
|
|
980
|
+
}
|
|
981
|
+
const reqN = argNum(req.args, 'req');
|
|
982
|
+
if (reqN !== undefined) {
|
|
983
|
+
const ref = this.networkRefs.get(reqN);
|
|
984
|
+
if (!ref) {
|
|
985
|
+
return { ok: false, error: `Invalid req: ${reqN}. Run \`agent-view network\` to get fresh handles.` };
|
|
986
|
+
}
|
|
987
|
+
const entry = this.networkStream.getEntry(ref.targetId, ref.requestId);
|
|
988
|
+
if (!entry) {
|
|
989
|
+
return { ok: false, error: `Request ${reqN} is no longer buffered (evicted or app restarted).` };
|
|
990
|
+
}
|
|
991
|
+
return { ok: true, data: formatNetworkDetail(entry, { rawHeaders: argBool(req.args, 'rawHeaders') ?? false }) };
|
|
992
|
+
}
|
|
993
|
+
if (argBool(req.args, 'clear')) {
|
|
994
|
+
this.networkStream.clear(resolvedTargetId);
|
|
995
|
+
return { ok: true, data: 'Network buffer cleared' };
|
|
996
|
+
}
|
|
997
|
+
const filter = {
|
|
998
|
+
targetId: resolvedTargetId,
|
|
999
|
+
since: argNum(req.args, 'since'),
|
|
1000
|
+
url: argStr(req.args, 'url'),
|
|
1001
|
+
method: parseMethodFilter(argStrArray(req.args, 'method')),
|
|
1002
|
+
type: parseTypeFilter(argStrArray(req.args, 'type')),
|
|
1003
|
+
...parseStatusFilter(argStrArray(req.args, 'status')),
|
|
1004
|
+
};
|
|
1005
|
+
const maxLines = argNum(req.args, 'maxLines') ?? DEFAULT_NETWORK_MAX_LINES;
|
|
1006
|
+
const follow = argBool(req.args, 'follow') ?? false;
|
|
1007
|
+
const untilPattern = argStr(req.args, 'until');
|
|
1008
|
+
if (untilPattern && !follow) {
|
|
1009
|
+
return { ok: false, error: '--until requires --follow' };
|
|
1010
|
+
}
|
|
1011
|
+
if (follow) {
|
|
1012
|
+
return this.followNetwork(req, filter, maxLines, untilPattern);
|
|
1013
|
+
}
|
|
1014
|
+
const entries = this.networkStream.drain(filter);
|
|
1015
|
+
return { ok: true, data: this.renderNetworkList(entries, maxLines) };
|
|
1016
|
+
}
|
|
1017
|
+
renderNetworkList(entries, maxLines) {
|
|
1018
|
+
const { text, refs, nextRef } = formatNetworkList(entries, { startRef: this.networkNextRef, maxLines });
|
|
1019
|
+
this.networkRefs.clear();
|
|
1020
|
+
for (const r of refs)
|
|
1021
|
+
this.networkRefs.set(r.ref, { targetId: r.targetId, requestId: r.requestId });
|
|
1022
|
+
this.networkNextRef = nextRef;
|
|
1023
|
+
return text;
|
|
1024
|
+
}
|
|
1025
|
+
async followNetwork(req, filter, maxLines, untilPattern) {
|
|
1026
|
+
const timeoutSec = argNum(req.args, 'timeout') ?? 10;
|
|
1027
|
+
const matcher = untilPattern ? buildMatcher(untilPattern) : null;
|
|
1028
|
+
const matchText = (e) => `${e.method ?? (e.isWebSocket ? 'WS' : e.isEventSource ? 'SSE' : '')} ${e.status ?? e.state} ${e.url}`;
|
|
1029
|
+
if (matcher) {
|
|
1030
|
+
const pre = this.networkStream.drain(filter);
|
|
1031
|
+
const hit = pre.findIndex(e => matcher(matchText(e)));
|
|
1032
|
+
if (hit !== -1)
|
|
1033
|
+
return { ok: true, data: this.renderNetworkList(pre.slice(0, hit + 1), maxLines) };
|
|
1034
|
+
}
|
|
1035
|
+
const matched = await new Promise((resolveFollow) => {
|
|
1036
|
+
const dispose = matcher
|
|
1037
|
+
? this.networkStream.subscribe(() => {
|
|
1038
|
+
const cur = this.networkStream.drain(filter);
|
|
1039
|
+
if (cur.some(e => matcher(matchText(e)))) {
|
|
1040
|
+
clearTimeout(timer);
|
|
1041
|
+
dispose();
|
|
1042
|
+
resolveFollow(true);
|
|
1043
|
+
}
|
|
1044
|
+
})
|
|
1045
|
+
: () => { };
|
|
1046
|
+
const timer = setTimeout(() => {
|
|
1047
|
+
dispose();
|
|
1048
|
+
resolveFollow(false);
|
|
1049
|
+
}, timeoutSec * 1000);
|
|
1050
|
+
timer.unref?.();
|
|
1051
|
+
});
|
|
1052
|
+
if (matcher && !matched) {
|
|
1053
|
+
return { ok: false, error: `Timeout: pattern not seen in ${timeoutSec}s` };
|
|
1054
|
+
}
|
|
1055
|
+
const entries = this.networkStream.drain(filter);
|
|
1056
|
+
return { ok: true, data: this.renderNetworkList(entries, maxLines) };
|
|
1057
|
+
}
|
|
929
1058
|
async handleStop() {
|
|
930
1059
|
setTimeout(() => this.shutdown(), 100);
|
|
931
1060
|
return { ok: true, data: 'Server stopping' };
|
|
@@ -938,6 +1067,7 @@ export class AgentViewServer {
|
|
|
938
1067
|
}
|
|
939
1068
|
await unlink(TOKEN_PATH).catch(() => { });
|
|
940
1069
|
this.consoleStream.detach();
|
|
1070
|
+
this.networkStream.detach();
|
|
941
1071
|
for (const cached of this.connections.values()) {
|
|
942
1072
|
try {
|
|
943
1073
|
await cached.session.close();
|
|
@@ -974,6 +1104,51 @@ function parseLevelFilter(levels) {
|
|
|
974
1104
|
}
|
|
975
1105
|
return set.size > 0 ? set : undefined;
|
|
976
1106
|
}
|
|
1107
|
+
function parseMethodFilter(methods) {
|
|
1108
|
+
if (!methods || methods.length === 0)
|
|
1109
|
+
return undefined;
|
|
1110
|
+
const set = new Set(methods.map(m => m.trim().toUpperCase()).filter(Boolean));
|
|
1111
|
+
return set.size > 0 ? set : undefined;
|
|
1112
|
+
}
|
|
1113
|
+
function parseTypeFilter(types) {
|
|
1114
|
+
if (!types || types.length === 0)
|
|
1115
|
+
return undefined;
|
|
1116
|
+
const valid = Object.values(NetworkResourceType);
|
|
1117
|
+
const set = new Set();
|
|
1118
|
+
for (const t of types) {
|
|
1119
|
+
const lower = t.trim().toLowerCase();
|
|
1120
|
+
if (valid.includes(lower))
|
|
1121
|
+
set.add(lower);
|
|
1122
|
+
}
|
|
1123
|
+
return set.size > 0 ? set : undefined;
|
|
1124
|
+
}
|
|
1125
|
+
function parseStatusFilter(tokens) {
|
|
1126
|
+
if (!tokens || tokens.length === 0)
|
|
1127
|
+
return {};
|
|
1128
|
+
const classes = new Set();
|
|
1129
|
+
const codes = new Set();
|
|
1130
|
+
let includeFailed = false;
|
|
1131
|
+
for (const tok of tokens) {
|
|
1132
|
+
const t = tok.trim().toLowerCase();
|
|
1133
|
+
if (t === 'failed' || t === 'fail') {
|
|
1134
|
+
includeFailed = true;
|
|
1135
|
+
continue;
|
|
1136
|
+
}
|
|
1137
|
+
const classMatch = /^([1-5])xx$/.exec(t);
|
|
1138
|
+
if (classMatch) {
|
|
1139
|
+
classes.add(Number(classMatch[1]));
|
|
1140
|
+
continue;
|
|
1141
|
+
}
|
|
1142
|
+
const n = Number(t);
|
|
1143
|
+
if (Number.isInteger(n) && n >= 100 && n <= 599)
|
|
1144
|
+
codes.add(n);
|
|
1145
|
+
}
|
|
1146
|
+
return {
|
|
1147
|
+
statusClasses: classes.size > 0 ? classes : undefined,
|
|
1148
|
+
statusCodes: codes.size > 0 ? codes : undefined,
|
|
1149
|
+
includeFailed: includeFailed ? true : undefined,
|
|
1150
|
+
};
|
|
1151
|
+
}
|
|
977
1152
|
function buildMatcher(pattern) {
|
|
978
1153
|
const regexMatch = /^\/(.+)\/([gimsuy]*)$/.exec(pattern);
|
|
979
1154
|
if (regexMatch) {
|