fast-replay 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +126 -0
- package/dist/api.d.ts +65 -0
- package/dist/api.js +78 -0
- package/dist/api.js.map +1 -0
- package/dist/browser.d.ts +67 -0
- package/dist/browser.js +77 -0
- package/dist/browser.js.map +1 -0
- package/dist/cli/format.d.ts +12 -0
- package/dist/cli/format.js +55 -0
- package/dist/cli/format.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +199 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/compiler/compile.d.ts +18 -0
- package/dist/compiler/compile.js +226 -0
- package/dist/compiler/compile.js.map +1 -0
- package/dist/compiler/normalize.d.ts +16 -0
- package/dist/compiler/normalize.js +75 -0
- package/dist/compiler/normalize.js.map +1 -0
- package/dist/compiler/waits.d.ts +31 -0
- package/dist/compiler/waits.js +69 -0
- package/dist/compiler/waits.js.map +1 -0
- package/dist/ir/io.d.ts +47 -0
- package/dist/ir/io.js +122 -0
- package/dist/ir/io.js.map +1 -0
- package/dist/ir/schema.d.ts +236 -0
- package/dist/ir/schema.js +153 -0
- package/dist/ir/schema.js.map +1 -0
- package/dist/mcp/index.d.ts +2 -0
- package/dist/mcp/index.js +22 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/server.d.ts +8 -0
- package/dist/mcp/server.js +268 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/noise.d.ts +20 -0
- package/dist/noise.js +70 -0
- package/dist/noise.js.map +1 -0
- package/dist/recorder/agent/capture.d.ts +15 -0
- package/dist/recorder/agent/capture.js +310 -0
- package/dist/recorder/agent/capture.js.map +1 -0
- package/dist/recorder/agent/config.d.ts +25 -0
- package/dist/recorder/agent/config.js +23 -0
- package/dist/recorder/agent/config.js.map +1 -0
- package/dist/recorder/agent/dom-reaction.d.ts +13 -0
- package/dist/recorder/agent/dom-reaction.js +166 -0
- package/dist/recorder/agent/dom-reaction.js.map +1 -0
- package/dist/recorder/agent/main.d.ts +1 -0
- package/dist/recorder/agent/main.js +14 -0
- package/dist/recorder/agent/main.js.map +1 -0
- package/dist/recorder/agent/page-agent.d.ts +12 -0
- package/dist/recorder/agent/page-agent.js +40 -0
- package/dist/recorder/agent/page-agent.js.map +1 -0
- package/dist/recorder/agent/reveal-tracker.d.ts +26 -0
- package/dist/recorder/agent/reveal-tracker.js +41 -0
- package/dist/recorder/agent/reveal-tracker.js.map +1 -0
- package/dist/recorder/agent/roles.d.ts +11 -0
- package/dist/recorder/agent/roles.js +148 -0
- package/dist/recorder/agent/roles.js.map +1 -0
- package/dist/recorder/agent/selectors.d.ts +26 -0
- package/dist/recorder/agent/selectors.js +256 -0
- package/dist/recorder/agent/selectors.js.map +1 -0
- package/dist/recorder/agent/text.d.ts +19 -0
- package/dist/recorder/agent/text.js +82 -0
- package/dist/recorder/agent/text.js.map +1 -0
- package/dist/recorder/agent/transport.d.ts +14 -0
- package/dist/recorder/agent/transport.js +43 -0
- package/dist/recorder/agent/transport.js.map +1 -0
- package/dist/recorder/agent/visibility.d.ts +8 -0
- package/dist/recorder/agent/visibility.js +20 -0
- package/dist/recorder/agent/visibility.js.map +1 -0
- package/dist/recorder/agent-bundle.generated.d.ts +2 -0
- package/dist/recorder/agent-bundle.generated.js +6 -0
- package/dist/recorder/agent-bundle.generated.js.map +1 -0
- package/dist/recorder/attach.d.ts +52 -0
- package/dist/recorder/attach.js +159 -0
- package/dist/recorder/attach.js.map +1 -0
- package/dist/recorder/instrument.d.ts +17 -0
- package/dist/recorder/instrument.js +24 -0
- package/dist/recorder/instrument.js.map +1 -0
- package/dist/recorder/launch.d.ts +38 -0
- package/dist/recorder/launch.js +72 -0
- package/dist/recorder/launch.js.map +1 -0
- package/dist/recorder/reaction.d.ts +13 -0
- package/dist/recorder/reaction.js +65 -0
- package/dist/recorder/reaction.js.map +1 -0
- package/dist/recorder/types.d.ts +62 -0
- package/dist/recorder/types.js +2 -0
- package/dist/recorder/types.js.map +1 -0
- package/dist/replayer/artifacts.d.ts +25 -0
- package/dist/replayer/artifacts.js +58 -0
- package/dist/replayer/artifacts.js.map +1 -0
- package/dist/replayer/invariants.d.ts +26 -0
- package/dist/replayer/invariants.js +109 -0
- package/dist/replayer/invariants.js.map +1 -0
- package/dist/replayer/resolve.d.ts +35 -0
- package/dist/replayer/resolve.js +50 -0
- package/dist/replayer/resolve.js.map +1 -0
- package/dist/replayer/run.d.ts +104 -0
- package/dist/replayer/run.js +467 -0
- package/dist/replayer/run.js.map +1 -0
- package/dist/replayer/values.d.ts +13 -0
- package/dist/replayer/values.js +76 -0
- package/dist/replayer/values.js.map +1 -0
- package/dist/replayer/waits.d.ts +25 -0
- package/dist/replayer/waits.js +90 -0
- package/dist/replayer/waits.js.map +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +10 -0
- package/dist/version.js.map +1 -0
- package/package.json +77 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
|
+
import { createReplayServer } from './server.js';
|
|
4
|
+
/**
|
|
5
|
+
* stdio entry point. Nothing may be written to stdout except protocol frames,
|
|
6
|
+
* so any diagnostics go to stderr.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Where `.repros/` lives. Defaults to the directory the client launched us in,
|
|
10
|
+
* which is wrong whenever the repros are kept outside the project being fixed.
|
|
11
|
+
*/
|
|
12
|
+
const root = process.env.REPLAY_ROOT ?? process.cwd();
|
|
13
|
+
const { server, dispose } = createReplayServer(root);
|
|
14
|
+
await server.connect(new StdioServerTransport());
|
|
15
|
+
// The pooled browsers outlive individual calls, so they need an explicit exit.
|
|
16
|
+
for (const signal of ['SIGINT', 'SIGTERM']) {
|
|
17
|
+
process.once(signal, () => {
|
|
18
|
+
void dispose().finally(() => process.exit(0));
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
process.stderr.write(`replay mcp server ready (repros: ${root})\n`);
|
|
22
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEjD;;;GAGG;AACH;;;GAGG;AACH,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;AACtD,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC;AACrD,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,oBAAoB,EAAE,CAAC,CAAC;AAEjD,+EAA+E;AAC/E,KAAK,MAAM,MAAM,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAU,EAAE,CAAC;IACpD,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE;QACxB,KAAK,OAAO,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;AACL,CAAC;AACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,oCAAoC,IAAI,KAAK,CAAC,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
export interface ReplayServer {
|
|
3
|
+
server: McpServer;
|
|
4
|
+
/** Closes the browsers held open across calls. */
|
|
5
|
+
dispose(): Promise<void>;
|
|
6
|
+
}
|
|
7
|
+
export declare function createServer(root?: string): McpServer;
|
|
8
|
+
export declare function createReplayServer(root?: string): ReplayServer;
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { list, readRepro, reproPaths, run } from '../api.js';
|
|
6
|
+
import { BrowserPool } from '../browser.js';
|
|
7
|
+
import { VERSION } from '../version.js';
|
|
8
|
+
/**
|
|
9
|
+
* Replay as an MCP server: a deterministic eye that a coding agent looks
|
|
10
|
+
* through.
|
|
11
|
+
*
|
|
12
|
+
* Nothing here calls a model. The agent already has one — this exists to hand
|
|
13
|
+
* it what the browser actually did, in one call, in a form it can read *and
|
|
14
|
+
* see*. That is the whole difference from driving a browser through an agent
|
|
15
|
+
* step by step: re-verifying a fix costs one round trip instead of one per
|
|
16
|
+
* action, every time.
|
|
17
|
+
*/
|
|
18
|
+
/** Screenshots are returned inline, so the model sees the page rather than a path. */
|
|
19
|
+
const MAX_INLINE_IMAGE_BYTES = 4 * 1024 * 1024;
|
|
20
|
+
async function imageContent(file) {
|
|
21
|
+
if (!file)
|
|
22
|
+
return [];
|
|
23
|
+
try {
|
|
24
|
+
const bytes = await readFile(file);
|
|
25
|
+
if (bytes.byteLength > MAX_INLINE_IMAGE_BYTES) {
|
|
26
|
+
return [
|
|
27
|
+
{
|
|
28
|
+
type: 'text',
|
|
29
|
+
text: `Screenshot too large to inline (${Math.round(bytes.byteLength / 1024)} kB): ${file}`,
|
|
30
|
+
},
|
|
31
|
+
];
|
|
32
|
+
}
|
|
33
|
+
return [{ type: 'image', data: bytes.toString('base64'), mimeType: 'image/png' }];
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
async function tail(file, lines) {
|
|
40
|
+
if (!file)
|
|
41
|
+
return '';
|
|
42
|
+
try {
|
|
43
|
+
const text = await readFile(file, 'utf8');
|
|
44
|
+
return text.split('\n').slice(-lines).join('\n').trim();
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return '';
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/** The one-screen summary an agent reads before deciding what to do next. */
|
|
51
|
+
function summarize(result, expectFixed) {
|
|
52
|
+
const mode = expectFixed ? 'expect-fixed' : 'expect-bug';
|
|
53
|
+
const head = result.passed
|
|
54
|
+
? `PASS (${mode}) — ${result.name}, ${result.timings.length}/${result.totalSteps} steps in ${(result.durationMs / 1000).toFixed(2)}s`
|
|
55
|
+
: `FAIL (${mode}) — ${result.name} after ${(result.durationMs / 1000).toFixed(2)}s`;
|
|
56
|
+
if (result.passed) {
|
|
57
|
+
return expectFixed
|
|
58
|
+
? `${head}\nThe flow completed and the recorded bug did not occur. The fix holds.`
|
|
59
|
+
: `${head}\nThe recorded outcome still occurs, so the repro itself is sound.`;
|
|
60
|
+
}
|
|
61
|
+
const f = result.failure;
|
|
62
|
+
if (!f)
|
|
63
|
+
return head;
|
|
64
|
+
if (f.kind === 'infrastructure') {
|
|
65
|
+
return [
|
|
66
|
+
`COULD NOT VERIFY — ${result.name}`,
|
|
67
|
+
'',
|
|
68
|
+
'The replay could not drive the app, so this says nothing about whether the bug is fixed.',
|
|
69
|
+
`Failing step: ${f.stepId} (step ${f.stepIndex + 1} of ${result.totalSteps})`,
|
|
70
|
+
`What it does: ${f.semantic}`,
|
|
71
|
+
`Observed: ${f.observed}`,
|
|
72
|
+
].join('\n');
|
|
73
|
+
}
|
|
74
|
+
return [
|
|
75
|
+
head,
|
|
76
|
+
``,
|
|
77
|
+
`Failing step: ${f.stepId} (step ${f.stepIndex + 1} of ${result.totalSteps})`,
|
|
78
|
+
`What it does: ${f.semantic}`,
|
|
79
|
+
`Expected: ${f.expected}`,
|
|
80
|
+
`Observed: ${f.observed}`,
|
|
81
|
+
].join('\n');
|
|
82
|
+
}
|
|
83
|
+
export function createServer(root = process.cwd()) {
|
|
84
|
+
return createReplayServer(root).server;
|
|
85
|
+
}
|
|
86
|
+
export function createReplayServer(root = process.cwd()) {
|
|
87
|
+
const server = new McpServer({ name: 'replay', version: VERSION });
|
|
88
|
+
/**
|
|
89
|
+
* One browser, held open for the life of the server.
|
|
90
|
+
*
|
|
91
|
+
* A CLI invocation is one-shot and has nothing to amortise, but this process
|
|
92
|
+
* is long-lived: without a pool every verification launched a fresh Chromium
|
|
93
|
+
* with an empty V8 code cache, so a large app paid its full cold boot on
|
|
94
|
+
* every single call. Each run still gets its own context, so replays stay
|
|
95
|
+
* isolated from one another.
|
|
96
|
+
*/
|
|
97
|
+
const pool = new BrowserPool();
|
|
98
|
+
server.registerTool('repro_run', {
|
|
99
|
+
title: 'Run a bug repro',
|
|
100
|
+
description: 'Replay a recorded bug reproduction against the running dev server and report what happened. ' +
|
|
101
|
+
'Deterministic and fast (a 10-step flow takes about 3 seconds) — call it after every code change to verify a fix. ' +
|
|
102
|
+
'Returns the pass/fail verdict, the failing step with a plain-language description of what it does, the console tail, ' +
|
|
103
|
+
'the network activity, and a screenshot of the resulting page. ' +
|
|
104
|
+
'Use expect_fixed=true while fixing a bug: it passes when the flow completes and the bug no longer occurs.',
|
|
105
|
+
inputSchema: {
|
|
106
|
+
name: z.string().describe('Name of the repro, as shown by repro_list.'),
|
|
107
|
+
expect_fixed: z
|
|
108
|
+
.boolean()
|
|
109
|
+
.optional()
|
|
110
|
+
.describe('True while verifying a fix: pass when the bug does NOT occur. False (default) asserts the bug still reproduces.'),
|
|
111
|
+
base_url: z.string().optional().describe('Override the recorded base URL.'),
|
|
112
|
+
headed: z
|
|
113
|
+
.boolean()
|
|
114
|
+
.optional()
|
|
115
|
+
.describe('Run in a visible browser. Required by apps that refuse headless sessions — without it those replays fail for reasons unrelated to the bug.'),
|
|
116
|
+
profile_dir: z
|
|
117
|
+
.string()
|
|
118
|
+
.optional()
|
|
119
|
+
.describe('Persistent Chromium profile directory, to reuse a login.'),
|
|
120
|
+
setup_command: z
|
|
121
|
+
.string()
|
|
122
|
+
.optional()
|
|
123
|
+
.describe('Shell command run before replay, to reset state the flow mutates.'),
|
|
124
|
+
timeout_scale: z
|
|
125
|
+
.number()
|
|
126
|
+
.optional()
|
|
127
|
+
.describe('Multiply every recorded wait. Raise it when replaying somewhere slower than the machine that recorded.'),
|
|
128
|
+
},
|
|
129
|
+
}, async ({ name, expect_fixed = false, base_url, headed, profile_dir, setup_command, timeout_scale }) => {
|
|
130
|
+
const result = await run({
|
|
131
|
+
name,
|
|
132
|
+
root,
|
|
133
|
+
expectFixed: expect_fixed,
|
|
134
|
+
captureFinalScreenshot: true,
|
|
135
|
+
// A persistent profile owns its own process and cannot share the pool.
|
|
136
|
+
...(profile_dir ? {} : { browser: await pool.acquire(!headed) }),
|
|
137
|
+
...(headed ? { headed: true } : {}),
|
|
138
|
+
...(profile_dir ? { profileDir: profile_dir } : {}),
|
|
139
|
+
...(setup_command ? { setupCommand: setup_command } : {}),
|
|
140
|
+
...(timeout_scale ? { timeoutScale: timeout_scale } : {}),
|
|
141
|
+
...(base_url ? { baseUrl: base_url } : {}),
|
|
142
|
+
});
|
|
143
|
+
const artifacts = result.failure?.artifacts ?? null;
|
|
144
|
+
const consoleTail = await tail(artifacts?.consoleLog ?? null, 50);
|
|
145
|
+
const networkLog = await tail(artifacts?.networkLog ?? null, 40);
|
|
146
|
+
const content = [{ type: 'text', text: summarize(result, expect_fixed) }];
|
|
147
|
+
// Notes carry the "this may be single-shot" warning, which is the only
|
|
148
|
+
// signal that a green verdict might mean nothing.
|
|
149
|
+
for (const note of result.notes)
|
|
150
|
+
content.push({ type: 'text', text: note });
|
|
151
|
+
if (consoleTail)
|
|
152
|
+
content.push({ type: 'text', text: `Console errors:\n${consoleTail}` });
|
|
153
|
+
if (networkLog)
|
|
154
|
+
content.push({ type: 'text', text: `Network since last step:\n${networkLog}` });
|
|
155
|
+
content.push(...(await imageContent(result.finalScreenshot)));
|
|
156
|
+
return {
|
|
157
|
+
content,
|
|
158
|
+
isError: !result.passed,
|
|
159
|
+
structuredContent: {
|
|
160
|
+
name: result.name,
|
|
161
|
+
passed: result.passed,
|
|
162
|
+
durationMs: result.durationMs,
|
|
163
|
+
totalSteps: result.totalSteps,
|
|
164
|
+
stepsRun: result.timings.length,
|
|
165
|
+
failure: result.failure
|
|
166
|
+
? {
|
|
167
|
+
stepId: result.failure.stepId,
|
|
168
|
+
stepIndex: result.failure.stepIndex,
|
|
169
|
+
semantic: result.failure.semantic,
|
|
170
|
+
expected: result.failure.expected,
|
|
171
|
+
observed: result.failure.observed,
|
|
172
|
+
}
|
|
173
|
+
: null,
|
|
174
|
+
failureKind: result.failure?.kind ?? null,
|
|
175
|
+
invariantViolations: result.invariantViolations,
|
|
176
|
+
screenshot: result.finalScreenshot,
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
});
|
|
180
|
+
server.registerTool('repro_list', {
|
|
181
|
+
title: 'List bug repros',
|
|
182
|
+
description: 'List the recorded bug reproductions available in this project, with step count, age, and how each one last did.',
|
|
183
|
+
inputSchema: {},
|
|
184
|
+
}, async () => {
|
|
185
|
+
const repros = await list(root);
|
|
186
|
+
if (!repros.length) {
|
|
187
|
+
return {
|
|
188
|
+
content: [
|
|
189
|
+
{
|
|
190
|
+
type: 'text',
|
|
191
|
+
text: 'No repros recorded. A developer creates one with: repro record <name> --url <dev server>',
|
|
192
|
+
},
|
|
193
|
+
],
|
|
194
|
+
structuredContent: { repros: [] },
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
const lines = repros.map((r) => {
|
|
198
|
+
const last = r.lastResult
|
|
199
|
+
? `${r.lastResult.status}${r.lastResult.failedStepId ? ` at ${r.lastResult.failedStepId}` : ''}`
|
|
200
|
+
: 'never run';
|
|
201
|
+
return `${r.name} — ${r.steps ?? '?'} steps, last run: ${last}${r.error ? ` (INVALID: ${r.error})` : ''}`;
|
|
202
|
+
});
|
|
203
|
+
return {
|
|
204
|
+
content: [{ type: 'text', text: lines.join('\n') }],
|
|
205
|
+
structuredContent: { repros },
|
|
206
|
+
};
|
|
207
|
+
});
|
|
208
|
+
server.registerTool('repro_artifacts', {
|
|
209
|
+
title: 'Inspect a repro',
|
|
210
|
+
description: 'Read the steps of a repro and the artifacts from its last failure: the failing step, its description, ' +
|
|
211
|
+
'the console tail, the network log, and the screenshot. Use this to understand a bug without re-running it.',
|
|
212
|
+
inputSchema: {
|
|
213
|
+
name: z.string().describe('Name of the repro.'),
|
|
214
|
+
},
|
|
215
|
+
}, async ({ name }) => {
|
|
216
|
+
const repro = await readRepro(name, root);
|
|
217
|
+
const paths = reproPaths(name, root);
|
|
218
|
+
const artifactsDir = paths.artifactsDir;
|
|
219
|
+
const steps = repro.steps
|
|
220
|
+
.map((s) => ` ${s.id} ${s.action.padEnd(8)} ${s.target?.semantic ?? s.value ?? ''}`)
|
|
221
|
+
.join('\n');
|
|
222
|
+
const summaryFile = path.join(artifactsDir, 'failure.json');
|
|
223
|
+
let failure = null;
|
|
224
|
+
try {
|
|
225
|
+
failure = JSON.parse(await readFile(summaryFile, 'utf8'));
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
/* no recorded failure */
|
|
229
|
+
}
|
|
230
|
+
const content = [
|
|
231
|
+
{
|
|
232
|
+
type: 'text',
|
|
233
|
+
text: [
|
|
234
|
+
`Repro: ${repro.name}`,
|
|
235
|
+
`Base URL: ${repro.baseUrl}${repro.startPath}`,
|
|
236
|
+
`Assertion mode: ${repro.assertion.mode}`,
|
|
237
|
+
``,
|
|
238
|
+
`Steps:`,
|
|
239
|
+
steps,
|
|
240
|
+
].join('\n'),
|
|
241
|
+
},
|
|
242
|
+
];
|
|
243
|
+
const observed = repro.assertion.observedAtRecord;
|
|
244
|
+
if (observed?.consoleErrors.length || observed?.failedRequests.length) {
|
|
245
|
+
content.push({
|
|
246
|
+
type: 'text',
|
|
247
|
+
text: [
|
|
248
|
+
'The bug, as observed when this repro was recorded:',
|
|
249
|
+
...observed.consoleErrors.map((e) => ` console: ${e}`),
|
|
250
|
+
...observed.failedRequests.map((r) => ` network: ${r.method} ${r.urlPattern} -> ${r.status ?? 'aborted'}`),
|
|
251
|
+
].join('\n'),
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
if (failure) {
|
|
255
|
+
content.push({ type: 'text', text: `Last failure:\n${JSON.stringify(failure, null, 2)}` });
|
|
256
|
+
const consoleTail = await tail(path.join(artifactsDir, 'console.log'), 50);
|
|
257
|
+
if (consoleTail)
|
|
258
|
+
content.push({ type: 'text', text: `Console:\n${consoleTail}` });
|
|
259
|
+
content.push(...(await imageContent(path.join(artifactsDir, 'screenshot.png'))));
|
|
260
|
+
}
|
|
261
|
+
return {
|
|
262
|
+
content,
|
|
263
|
+
structuredContent: { repro, failure, artifactsDir },
|
|
264
|
+
};
|
|
265
|
+
});
|
|
266
|
+
return { server, dispose: () => pool.dispose() };
|
|
267
|
+
}
|
|
268
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"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"}
|
package/dist/noise.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What counts as environment noise rather than evidence about the app.
|
|
3
|
+
*
|
|
4
|
+
* This lives in one module on purpose. Filtering only at record time produced a
|
|
5
|
+
* worse bug than not filtering at all: the compiler saw an empty error list,
|
|
6
|
+
* inferred "this app is clean", and switched the invariants to their strictest
|
|
7
|
+
* setting — while the replayer checked raw, unfiltered output. The same CORS
|
|
8
|
+
* error that was correctly ignored while recording became a hard failure on
|
|
9
|
+
* every replay. Both sides must agree on what noise is.
|
|
10
|
+
*/
|
|
11
|
+
export declare function isAmbientConsoleError(text: string): boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Requests that must never become a recorded wait.
|
|
14
|
+
*
|
|
15
|
+
* A content-hashed bundle breaks on the next deploy *and* on the very next
|
|
16
|
+
* replay, because the browser already has it cached and never re-requests it.
|
|
17
|
+
* Telemetry is unrelated to the app's behaviour by definition.
|
|
18
|
+
*/
|
|
19
|
+
export declare function isIncidentalRequest(url: string): boolean;
|
|
20
|
+
export declare function isTransientIndicator(role: string | null, name: string): boolean;
|
package/dist/noise.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What counts as environment noise rather than evidence about the app.
|
|
3
|
+
*
|
|
4
|
+
* This lives in one module on purpose. Filtering only at record time produced a
|
|
5
|
+
* worse bug than not filtering at all: the compiler saw an empty error list,
|
|
6
|
+
* inferred "this app is clean", and switched the invariants to their strictest
|
|
7
|
+
* setting — while the replayer checked raw, unfiltered output. The same CORS
|
|
8
|
+
* error that was correctly ignored while recording became a hard failure on
|
|
9
|
+
* every replay. Both sides must agree on what noise is.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Console output a production SPA emits regardless of what the user did:
|
|
13
|
+
* blocked third-party beacons, CORS preflights, DNS failures, CSP reports,
|
|
14
|
+
* extension chatter, framework advertising.
|
|
15
|
+
*/
|
|
16
|
+
const AMBIENT_CONSOLE_PATTERNS = [
|
|
17
|
+
/blocked by CORS policy/i,
|
|
18
|
+
/Access-Control-Allow-Origin/i,
|
|
19
|
+
/net::ERR_/i,
|
|
20
|
+
/ERR_NAME_NOT_RESOLVED/i,
|
|
21
|
+
/ERR_CONNECTION_REFUSED/i,
|
|
22
|
+
/Failed to load resource/i,
|
|
23
|
+
/chrome-extension:/i,
|
|
24
|
+
/Content Security Policy/i,
|
|
25
|
+
/favicon/i,
|
|
26
|
+
/ResizeObserver loop/i,
|
|
27
|
+
/Download the React DevTools/i,
|
|
28
|
+
];
|
|
29
|
+
export function isAmbientConsoleError(text) {
|
|
30
|
+
return AMBIENT_CONSOLE_PATTERNS.some((re) => re.test(text));
|
|
31
|
+
}
|
|
32
|
+
/** Telemetry and error-reporting hosts. Never evidence about the app's behaviour. */
|
|
33
|
+
const TELEMETRY_HOSTS = /(^|\.)(sentry\.io|segment\.(io|com)|google-analytics\.com|googletagmanager\.com|datadoghq\.com|amplitude\.com|mixpanel\.com|intercom\.io|hotjar\.com|fullstory\.com|launchdarkly\.com|bugsnag\.com|newrelic\.com|pusher\.com)$/i;
|
|
34
|
+
/** Build output and media. Cached on the second run, so never a reliable signal. */
|
|
35
|
+
const STATIC_ASSET = /\.(js|mjs|cjs|css|map|png|jpe?g|gif|svg|webp|avif|ico|woff2?|ttf|eot)$/i;
|
|
36
|
+
/**
|
|
37
|
+
* Requests that must never become a recorded wait.
|
|
38
|
+
*
|
|
39
|
+
* A content-hashed bundle breaks on the next deploy *and* on the very next
|
|
40
|
+
* replay, because the browser already has it cached and never re-requests it.
|
|
41
|
+
* Telemetry is unrelated to the app's behaviour by definition.
|
|
42
|
+
*/
|
|
43
|
+
export function isIncidentalRequest(url) {
|
|
44
|
+
try {
|
|
45
|
+
const parsed = new URL(url);
|
|
46
|
+
if (TELEMETRY_HOSTS.test(parsed.hostname))
|
|
47
|
+
return true;
|
|
48
|
+
if (STATIC_ASSET.test(parsed.pathname))
|
|
49
|
+
return true;
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Elements whose whole job is to be temporary.
|
|
58
|
+
*
|
|
59
|
+
* A spinner recorded as a required `domAppeared` fails on every warm replay:
|
|
60
|
+
* the data is cached, the spinner never renders, and a healthy app is reported
|
|
61
|
+
* broken. It survives an "is it still there next action?" check on a cold run,
|
|
62
|
+
* so it has to be excluded by what it is rather than how long it lasted.
|
|
63
|
+
*/
|
|
64
|
+
const TRANSIENT_NAME = /\b(loading|spinner|progress|skeleton|please wait|submitting|saving)\b/i;
|
|
65
|
+
export function isTransientIndicator(role, name) {
|
|
66
|
+
if (role === 'progressbar')
|
|
67
|
+
return true;
|
|
68
|
+
return TRANSIENT_NAME.test(name);
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=noise.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"noise.js","sourceRoot":"","sources":["../src/noise.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH;;;;GAIG;AACH,MAAM,wBAAwB,GAAG;IAC/B,yBAAyB;IACzB,8BAA8B;IAC9B,YAAY;IACZ,wBAAwB;IACxB,yBAAyB;IACzB,0BAA0B;IAC1B,oBAAoB;IACpB,0BAA0B;IAC1B,UAAU;IACV,sBAAsB;IACtB,8BAA8B;CAC/B,CAAC;AAEF,MAAM,UAAU,qBAAqB,CAAC,IAAY;IAChD,OAAO,wBAAwB,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;AAC9D,CAAC;AAED,qFAAqF;AACrF,MAAM,eAAe,GACnB,iOAAiO,CAAC;AAEpO,oFAAoF;AACpF,MAAM,YAAY,GAAG,yEAAyE,CAAC;AAE/F;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAW;IAC7C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC;YAAE,OAAO,IAAI,CAAC;QACvD,IAAI,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC;YAAE,OAAO,IAAI,CAAC;QACpD,OAAO,KAAK,CAAC;IACf,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,cAAc,GAAG,wEAAwE,CAAC;AAEhG,MAAM,UAAU,oBAAoB,CAAC,IAAmB,EAAE,IAAY;IACpE,IAAI,IAAI,KAAK,aAAa;QAAE,OAAO,IAAI,CAAC;IACxC,OAAO,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACnC,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { AgentConfig } from './config.js';
|
|
2
|
+
import type { RevealTracker } from './reveal-tracker.js';
|
|
3
|
+
import type { Transport } from './transport.js';
|
|
4
|
+
export interface CaptureContext {
|
|
5
|
+
config: AgentConfig;
|
|
6
|
+
transport: Transport;
|
|
7
|
+
reveals: RevealTracker;
|
|
8
|
+
/**
|
|
9
|
+
* Called just before each action is recorded, so the DOM observer can settle
|
|
10
|
+
* the previous action's reaction at the same moment replay would stop waiting
|
|
11
|
+
* for it.
|
|
12
|
+
*/
|
|
13
|
+
beforeAction?: () => void;
|
|
14
|
+
}
|
|
15
|
+
export declare function installCapture(ctx: CaptureContext): void;
|