@bonyadnouri/autoend 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -2
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/explore/explorer.d.ts +4 -1
- package/dist/explore/explorer.js +61 -22
- package/dist/explore/explorer.js.map +1 -1
- package/dist/map/flow-map.d.ts +46 -0
- package/dist/map/flow-map.js +99 -6
- package/dist/map/flow-map.js.map +1 -1
- package/dist/replay/replay.d.ts +13 -2
- package/dist/replay/replay.js +92 -11
- package/dist/replay/replay.js.map +1 -1
- package/dist/report/artifact.d.ts +7 -1
- package/dist/report/artifact.js +10 -0
- package/dist/report/artifact.js.map +1 -1
- package/dist/report/types.d.ts +62 -0
- package/dist/run/run.d.ts +6 -1
- package/dist/run/run.js +64 -9
- package/dist/run/run.js.map +1 -1
- package/dist/run/sensitive-env.d.ts +24 -0
- package/dist/run/sensitive-env.js +45 -0
- package/dist/run/sensitive-env.js.map +1 -0
- package/dist/spa/assets/index-BYZk0rZl.js +264 -0
- package/dist/spa/assets/index-D8cAgArG.css +1 -0
- package/dist/spa/index.html +14 -0
- package/dist/spa/logo.svg +7 -0
- package/dist/viewer/server.d.ts +7 -6
- package/dist/viewer/server.js +168 -26
- package/dist/viewer/server.js.map +1 -1
- package/package.json +17 -4
- package/dist/viewer/html.d.ts +0 -7
- package/dist/viewer/html.js +0 -240
- package/dist/viewer/html.js.map +0 -1
package/dist/replay/replay.js
CHANGED
|
@@ -3,17 +3,64 @@ import { join } from 'node:path';
|
|
|
3
3
|
import { pathToFileURL } from 'node:url';
|
|
4
4
|
import { chromium } from 'playwright';
|
|
5
5
|
import { flowMapDir, saveFlowMeta } from '../map/flow-map.js';
|
|
6
|
+
import { withoutSensitiveEnv } from '../run/sensitive-env.js';
|
|
6
7
|
const FLOW_TIMEOUT_MS = 60_000;
|
|
7
8
|
const REPLAY_WORKERS = 4;
|
|
9
|
+
const VIEWPORT = { width: 1280, height: 720 };
|
|
10
|
+
const CAPTURE_CAP = 50; // per stream; drop beyond, note nothing — caps keep report.json bounded
|
|
8
11
|
/**
|
|
9
12
|
* Execute one Flow script in a fresh recording browser context. Shared by
|
|
10
13
|
* replay (map scripts) and exploration (verify-by-running a proposed script
|
|
11
|
-
* before it may enter the Flow Map, ADR-0002).
|
|
14
|
+
* before it may enter the Flow Map, ADR-0002). Alongside the WebM Evidence it
|
|
15
|
+
* captures the panels the viewer renders (ADR-0005): console/page errors,
|
|
16
|
+
* failed network, a navigation timeline, and before/after screenshots.
|
|
12
17
|
*/
|
|
13
18
|
export async function runFlowScript(browser, scriptPath, target, evidenceDir, videoBase) {
|
|
14
|
-
const context = await browser.newContext({ recordVideo: { dir: evidenceDir } });
|
|
19
|
+
const context = await browser.newContext({ recordVideo: { dir: evidenceDir }, viewport: VIEWPORT });
|
|
15
20
|
// TODO: inject shared storage state (src/auth/session.ts) once fleet auth exists.
|
|
16
21
|
const page = await context.newPage();
|
|
22
|
+
const startedMs = Date.now();
|
|
23
|
+
const consoleEntries = [];
|
|
24
|
+
const network = [];
|
|
25
|
+
const timeline = [];
|
|
26
|
+
const screenshots = [];
|
|
27
|
+
const screenshot = async (label, file) => {
|
|
28
|
+
try {
|
|
29
|
+
await page.screenshot({ path: join(evidenceDir, file) });
|
|
30
|
+
screenshots.push({ file, label, tMs: Date.now() - startedMs });
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// A crashed or closed page must not mask the Flow's own error.
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
page.on('console', (msg) => {
|
|
37
|
+
const level = msg.type() === 'error' ? 'error' : msg.type() === 'warning' ? 'warning' : undefined;
|
|
38
|
+
if (level && consoleEntries.length < CAPTURE_CAP)
|
|
39
|
+
consoleEntries.push({ level, text: msg.text(), tMs: Date.now() - startedMs });
|
|
40
|
+
});
|
|
41
|
+
page.on('pageerror', (err) => {
|
|
42
|
+
if (consoleEntries.length < CAPTURE_CAP)
|
|
43
|
+
consoleEntries.push({ level: 'error', text: String(err), tMs: Date.now() - startedMs });
|
|
44
|
+
});
|
|
45
|
+
page.on('response', (res) => {
|
|
46
|
+
if (res.status() >= 400 && network.length < CAPTURE_CAP)
|
|
47
|
+
network.push({ method: res.request().method(), url: res.url(), status: res.status(), tMs: Date.now() - startedMs });
|
|
48
|
+
});
|
|
49
|
+
page.on('requestfailed', (req) => {
|
|
50
|
+
if (network.length < CAPTURE_CAP)
|
|
51
|
+
network.push({ method: req.method(), url: req.url(), status: 0, tMs: Date.now() - startedMs });
|
|
52
|
+
});
|
|
53
|
+
page.on('framenavigated', (frame) => {
|
|
54
|
+
if (frame !== page.mainFrame() || frame.url() === 'about:blank' || timeline.length >= CAPTURE_CAP)
|
|
55
|
+
return;
|
|
56
|
+
const path = new URL(frame.url()).pathname;
|
|
57
|
+
timeline.push({ label: `goto ${path}`, status: 'passed', tMs: Date.now() - startedMs });
|
|
58
|
+
});
|
|
59
|
+
// First main-frame load → the "before" screenshot; awaited below so it always precedes "after".
|
|
60
|
+
let beforeShot;
|
|
61
|
+
page.once('load', () => {
|
|
62
|
+
beforeShot = screenshot('before', `${videoBase}-before.png`);
|
|
63
|
+
});
|
|
17
64
|
let failure;
|
|
18
65
|
try {
|
|
19
66
|
const script = await loadFlowScript(scriptPath);
|
|
@@ -22,6 +69,16 @@ export async function runFlowScript(browser, scriptPath, target, evidenceDir, vi
|
|
|
22
69
|
catch (error) {
|
|
23
70
|
failure = error;
|
|
24
71
|
}
|
|
72
|
+
if (beforeShot)
|
|
73
|
+
await beforeShot;
|
|
74
|
+
if (failure !== undefined) {
|
|
75
|
+
const message = failure instanceof Error ? failure.message : String(failure);
|
|
76
|
+
timeline.push({ label: message.slice(0, 80), status: 'failed', tMs: Date.now() - startedMs });
|
|
77
|
+
await screenshot('at-failure', `${videoBase}-at-failure.png`);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
await screenshot('after', `${videoBase}-after.png`);
|
|
81
|
+
}
|
|
25
82
|
const video = page.video();
|
|
26
83
|
await context.close(); // finalizes the recording
|
|
27
84
|
let evidence;
|
|
@@ -29,10 +86,12 @@ export async function runFlowScript(browser, scriptPath, target, evidenceDir, vi
|
|
|
29
86
|
evidence = `${videoBase}.webm`;
|
|
30
87
|
await rename(await video.path(), join(evidenceDir, evidence));
|
|
31
88
|
}
|
|
89
|
+
const durationMs = Date.now() - startedMs;
|
|
90
|
+
const capture = { console: consoleEntries, network, timeline, screenshots, durationMs, evidence };
|
|
32
91
|
if (failure !== undefined) {
|
|
33
|
-
return { ok: false, error: failure instanceof Error ? failure.message : String(failure),
|
|
92
|
+
return { ok: false, error: failure instanceof Error ? failure.message : String(failure), ...capture };
|
|
34
93
|
}
|
|
35
|
-
return { ok: true,
|
|
94
|
+
return { ok: true, ...capture };
|
|
36
95
|
}
|
|
37
96
|
/**
|
|
38
97
|
* Phase 1 of every Run: replay the whole Flow Map — headless, parallel,
|
|
@@ -41,13 +100,27 @@ export async function runFlowScript(browser, scriptPath, target, evidenceDir, vi
|
|
|
41
100
|
*/
|
|
42
101
|
export async function replayFlowMap(repoRoot, target, flows, evidenceDir) {
|
|
43
102
|
if (flows.length === 0) {
|
|
44
|
-
return { replayed: 0, findings: [], heals: [] };
|
|
103
|
+
return { replayed: 0, findings: [], heals: [], flows: [] };
|
|
45
104
|
}
|
|
46
105
|
const browser = await chromium.launch();
|
|
47
106
|
try {
|
|
48
|
-
const
|
|
107
|
+
const browserVersion = browser.version();
|
|
108
|
+
// Map scripts are LLM-authored and imported in-process (ADR-0002); hide
|
|
109
|
+
// secrets from them while the whole pool runs (issue #3). Scrubbing wraps
|
|
110
|
+
// the batch, not each script, because process.env is process-global.
|
|
111
|
+
const results = await withoutSensitiveEnv(() => withPool(flows, REPLAY_WORKERS, async (flow) => {
|
|
49
112
|
const scriptPath = join(flowMapDir(repoRoot), flow.id, 'flow.mts');
|
|
50
113
|
const outcome = await runFlowScript(browser, scriptPath, target, evidenceDir, flow.id);
|
|
114
|
+
const snapshot = {
|
|
115
|
+
id: flow.id,
|
|
116
|
+
title: flow.title,
|
|
117
|
+
status: outcome.ok ? 'passed' : 'failed',
|
|
118
|
+
discoveredAt: flow.discoveredAt,
|
|
119
|
+
lastPassedAt: flow.lastPassedAt,
|
|
120
|
+
timeline: outcome.timeline,
|
|
121
|
+
evidence: outcome.evidence,
|
|
122
|
+
durationMs: outcome.durationMs,
|
|
123
|
+
};
|
|
51
124
|
if (!outcome.ok) {
|
|
52
125
|
// TODO(ADR-0001): attempt a Heal (re-achieve the Flow's goal via an agent)
|
|
53
126
|
// before reporting. Until healing exists, every failure is a Regression.
|
|
@@ -58,16 +131,24 @@ export async function replayFlowMap(repoRoot, target, flows, evidenceDir) {
|
|
|
58
131
|
title: `Flow "${flow.title}" failed on replay`,
|
|
59
132
|
detail: outcome.error ?? 'unknown failure',
|
|
60
133
|
evidence: outcome.evidence,
|
|
134
|
+
console: outcome.console,
|
|
135
|
+
network: outcome.network,
|
|
136
|
+
timeline: outcome.timeline,
|
|
137
|
+
screenshots: outcome.screenshots,
|
|
61
138
|
};
|
|
62
|
-
return finding;
|
|
139
|
+
return { snapshot, finding };
|
|
63
140
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
141
|
+
const lastPassedAt = new Date().toISOString();
|
|
142
|
+
await saveFlowMeta(repoRoot, { ...flow, lastPassedAt });
|
|
143
|
+
snapshot.lastPassedAt = lastPassedAt;
|
|
144
|
+
return { snapshot };
|
|
145
|
+
}));
|
|
67
146
|
return {
|
|
68
147
|
replayed: flows.length,
|
|
69
|
-
findings:
|
|
148
|
+
findings: results.map((r) => r.finding).filter((f) => f !== undefined),
|
|
70
149
|
heals: [],
|
|
150
|
+
flows: results.map((r) => r.snapshot),
|
|
151
|
+
browserVersion,
|
|
71
152
|
};
|
|
72
153
|
}
|
|
73
154
|
finally {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replay.js","sourceRoot":"","sources":["../../src/replay/replay.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,QAAQ,EAA2B,MAAM,YAAY,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,YAAY,EAAiB,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"replay.js","sourceRoot":"","sources":["../../src/replay/replay.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { RunArtifact } from './types.js';
|
|
1
|
+
import type { Finding, RunArtifact } from './types.js';
|
|
2
2
|
/** Run artifacts live under .autoend/runs/<runId>/ — gitignored, kept until `autoend clean`. */
|
|
3
3
|
export declare function runsDir(repoRoot: string): string;
|
|
4
4
|
export interface RunDir {
|
|
@@ -8,3 +8,9 @@ export interface RunDir {
|
|
|
8
8
|
export declare function prepareRunDir(repoRoot: string, runId: string): Promise<RunDir>;
|
|
9
9
|
export declare function writeReport(runDir: string, artifact: RunArtifact): Promise<void>;
|
|
10
10
|
export declare function readRunArtifact(runDir: string): Promise<RunArtifact>;
|
|
11
|
+
/**
|
|
12
|
+
* Patch one Finding in report.json in place — how resolution actions
|
|
13
|
+
* (Dismiss/Suppress) become visible to a viewer reload (ADR-0004: the
|
|
14
|
+
* artifact is the state; the server just edits files).
|
|
15
|
+
*/
|
|
16
|
+
export declare function updateFinding(runDir: string, findingId: string, patch: Partial<Finding>): Promise<void>;
|
package/dist/report/artifact.js
CHANGED
|
@@ -17,4 +17,14 @@ export async function readRunArtifact(runDir) {
|
|
|
17
17
|
const raw = await readFile(join(runDir, 'report.json'), 'utf8');
|
|
18
18
|
return JSON.parse(raw);
|
|
19
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Patch one Finding in report.json in place — how resolution actions
|
|
22
|
+
* (Dismiss/Suppress) become visible to a viewer reload (ADR-0004: the
|
|
23
|
+
* artifact is the state; the server just edits files).
|
|
24
|
+
*/
|
|
25
|
+
export async function updateFinding(runDir, findingId, patch) {
|
|
26
|
+
const artifact = await readRunArtifact(runDir);
|
|
27
|
+
artifact.findings = artifact.findings.map((finding) => finding.id === findingId ? { ...finding, ...patch } : finding);
|
|
28
|
+
await writeReport(runDir, artifact);
|
|
29
|
+
}
|
|
20
30
|
//# sourceMappingURL=artifact.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"artifact.js","sourceRoot":"","sources":["../../src/report/artifact.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC,gGAAgG;AAChG,MAAM,UAAU,OAAO,CAAC,QAAgB;IACtC,OAAO,IAAI,CAAC,QAAQ,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC;AAC5C,CAAC;AAOD,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,QAAgB,EAAE,KAAa;IACjE,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,KAAK,CAAC,CAAC;IAC3C,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IAC1C,MAAM,KAAK,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,OAAO,EAAE,GAAG,EAAE,WAAW,EAAE,CAAC;AAC9B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,MAAc,EAAE,QAAqB;IACrE,MAAM,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,aAAa,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AAClF,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,MAAc;IAClD,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,CAAC,CAAC;IAChE,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAgB,CAAC;AACxC,CAAC"}
|
|
1
|
+
{"version":3,"file":"artifact.js","sourceRoot":"","sources":["../../src/report/artifact.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC,gGAAgG;AAChG,MAAM,UAAU,OAAO,CAAC,QAAgB;IACtC,OAAO,IAAI,CAAC,QAAQ,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC;AAC5C,CAAC;AAOD,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,QAAgB,EAAE,KAAa;IACjE,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,KAAK,CAAC,CAAC;IAC3C,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IAC1C,MAAM,KAAK,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,OAAO,EAAE,GAAG,EAAE,WAAW,EAAE,CAAC;AAC9B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,MAAc,EAAE,QAAqB;IACrE,MAAM,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,aAAa,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AAClF,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,MAAc;IAClD,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,CAAC,CAAC;IAChE,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAgB,CAAC;AACxC,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,MAAc,EACd,SAAiB,EACjB,KAAuB;IAEvB,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC,MAAM,CAAC,CAAC;IAC/C,QAAQ,CAAC,QAAQ,GAAG,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CACpD,OAAO,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,OAAO,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,OAAO,CAC9D,CAAC;IACF,MAAM,WAAW,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;AACtC,CAAC"}
|
package/dist/report/types.d.ts
CHANGED
|
@@ -5,6 +5,41 @@ import type { Effort } from '../run/effort.js';
|
|
|
5
5
|
*/
|
|
6
6
|
/** The three Finding tiers (CONTEXT.md: Finding). */
|
|
7
7
|
export type FindingKind = 'hard-failure' | 'regression' | 'advisory';
|
|
8
|
+
/** Where a Finding's fault lives (CONTEXT.md: Diagnosis). A wrong Flow script is 'flow', never 'test'. */
|
|
9
|
+
export type FaultDomain = 'app' | 'flow' | 'environment';
|
|
10
|
+
/** CONTEXT.md: Diagnosis — the filing agent's judgment. Producers land per ADR-0006; schema-only for now. */
|
|
11
|
+
export interface Diagnosis {
|
|
12
|
+
rootCause: string;
|
|
13
|
+
faultDomain: FaultDomain;
|
|
14
|
+
/** 0–100. */
|
|
15
|
+
confidence: number;
|
|
16
|
+
}
|
|
17
|
+
export interface ConsoleEntry {
|
|
18
|
+
level: 'error' | 'warning';
|
|
19
|
+
text: string;
|
|
20
|
+
/** Milliseconds since the Flow's execution started. */
|
|
21
|
+
tMs: number;
|
|
22
|
+
}
|
|
23
|
+
export interface NetworkEntry {
|
|
24
|
+
method: string;
|
|
25
|
+
url: string;
|
|
26
|
+
/** HTTP status; 0 = request failed/aborted before a response. */
|
|
27
|
+
status: number;
|
|
28
|
+
tMs: number;
|
|
29
|
+
}
|
|
30
|
+
export interface StepResult {
|
|
31
|
+
/** Navigation milestone or terminal outcome, e.g. "goto /pricing". */
|
|
32
|
+
label: string;
|
|
33
|
+
status: 'passed' | 'failed';
|
|
34
|
+
tMs: number;
|
|
35
|
+
}
|
|
36
|
+
export interface Screenshot {
|
|
37
|
+
/** Filename within the artifact's evidence/ dir. */
|
|
38
|
+
file: string;
|
|
39
|
+
label: 'before' | 'after' | 'at-failure';
|
|
40
|
+
tMs: number;
|
|
41
|
+
}
|
|
42
|
+
export type Resolution = 'dismissed' | 'rejected' | 'suppressed';
|
|
8
43
|
export interface Finding {
|
|
9
44
|
id: string;
|
|
10
45
|
kind: FindingKind;
|
|
@@ -14,6 +49,13 @@ export interface Finding {
|
|
|
14
49
|
detail: string;
|
|
15
50
|
/** Path to WebM Evidence, relative to the Run artifact's evidence/ dir. */
|
|
16
51
|
evidence?: string;
|
|
52
|
+
diagnosis?: Diagnosis;
|
|
53
|
+
console?: ConsoleEntry[];
|
|
54
|
+
network?: NetworkEntry[];
|
|
55
|
+
timeline?: StepResult[];
|
|
56
|
+
screenshots?: Screenshot[];
|
|
57
|
+
/** Written by the viewer server when the user resolves the Finding. */
|
|
58
|
+
resolution?: Resolution;
|
|
17
59
|
}
|
|
18
60
|
/** A Heal is reported alongside Findings but is not one (CONTEXT.md: Heal). */
|
|
19
61
|
export interface Heal {
|
|
@@ -21,6 +63,24 @@ export interface Heal {
|
|
|
21
63
|
summary: string;
|
|
22
64
|
evidence?: string;
|
|
23
65
|
}
|
|
66
|
+
/** One Flow as this Run touched it — the Report's receipts (CONTEXT.md: Report). */
|
|
67
|
+
export interface FlowSnapshot {
|
|
68
|
+
id: string;
|
|
69
|
+
title: string;
|
|
70
|
+
status: 'passed' | 'failed' | 'discovered';
|
|
71
|
+
discoveredAt: string;
|
|
72
|
+
lastPassedAt?: string;
|
|
73
|
+
timeline?: StepResult[];
|
|
74
|
+
evidence?: string;
|
|
75
|
+
durationMs?: number;
|
|
76
|
+
}
|
|
77
|
+
export interface Environment {
|
|
78
|
+
browser: string;
|
|
79
|
+
viewport: string;
|
|
80
|
+
os: string;
|
|
81
|
+
node: string;
|
|
82
|
+
autoendVersion: string;
|
|
83
|
+
}
|
|
24
84
|
/**
|
|
25
85
|
* The self-contained record of one Run (ADR-0004): serialized as report.json
|
|
26
86
|
* next to an evidence/ dir of WebM files. Portable — viewable anywhere;
|
|
@@ -34,6 +94,8 @@ export interface RunArtifact {
|
|
|
34
94
|
finishedAt?: string;
|
|
35
95
|
flowsReplayed: number;
|
|
36
96
|
flowsDiscovered: number;
|
|
97
|
+
flows: FlowSnapshot[];
|
|
98
|
+
environment: Environment;
|
|
37
99
|
findings: Finding[];
|
|
38
100
|
heals: Heal[];
|
|
39
101
|
}
|
package/dist/run/run.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { RunArtifact } from '../report/types.js';
|
|
1
|
+
import type { Finding, RunArtifact } from '../report/types.js';
|
|
2
2
|
import { type Effort } from './effort.js';
|
|
3
3
|
export interface RunOptions {
|
|
4
4
|
target: URL;
|
|
@@ -9,6 +9,11 @@ export interface RunOutcome {
|
|
|
9
9
|
artifactDir: string;
|
|
10
10
|
artifact: RunArtifact;
|
|
11
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* Suppress (CONTEXT.md): "future Runs stop re-reporting" — drop Advisories
|
|
14
|
+
* whose title the user suppressed. Other Finding kinds always pass through.
|
|
15
|
+
*/
|
|
16
|
+
export declare function filterSuppressed(findings: Finding[], suppressed: string[]): Finding[];
|
|
12
17
|
/**
|
|
13
18
|
* A Run (CONTEXT.md): two ordered phases. Phase 1 replays the whole Flow Map
|
|
14
19
|
* (always completes); phase 2 explores new surface within the Effort budget.
|
package/dist/run/run.js
CHANGED
|
@@ -1,8 +1,31 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { release } from 'node:os';
|
|
1
3
|
import { listFlows } from '../map/flow-map.js';
|
|
2
4
|
import { explore } from '../explore/explorer.js';
|
|
3
5
|
import { replayFlowMap } from '../replay/replay.js';
|
|
6
|
+
import { join } from 'node:path';
|
|
4
7
|
import { prepareRunDir, writeReport } from '../report/artifact.js';
|
|
5
8
|
import { EFFORT_BUDGETS } from './effort.js';
|
|
9
|
+
/**
|
|
10
|
+
* Suppress (CONTEXT.md): "future Runs stop re-reporting" — drop Advisories
|
|
11
|
+
* whose title the user suppressed. Other Finding kinds always pass through.
|
|
12
|
+
*/
|
|
13
|
+
export function filterSuppressed(findings, suppressed) {
|
|
14
|
+
return findings.filter((f) => f.kind !== 'advisory' || !suppressed.includes(f.title));
|
|
15
|
+
}
|
|
16
|
+
/** Titles from .autoend/suppressed.json; missing or corrupt file means nothing suppressed. */
|
|
17
|
+
async function readSuppressedTitles(repoRoot) {
|
|
18
|
+
try {
|
|
19
|
+
const raw = await readFile(join(repoRoot, '.autoend', 'suppressed.json'), 'utf8');
|
|
20
|
+
const parsed = JSON.parse(raw);
|
|
21
|
+
return Array.isArray(parsed.advisories)
|
|
22
|
+
? parsed.advisories.filter((t) => typeof t === 'string')
|
|
23
|
+
: [];
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
}
|
|
6
29
|
/**
|
|
7
30
|
* A Run (CONTEXT.md): two ordered phases. Phase 1 replays the whole Flow Map
|
|
8
31
|
* (always completes); phase 2 explores new surface within the Effort budget.
|
|
@@ -14,14 +37,44 @@ export async function executeRun(opts) {
|
|
|
14
37
|
const { dir, evidenceDir } = await prepareRunDir(opts.repoRoot, runId);
|
|
15
38
|
const flows = await listFlows(opts.repoRoot);
|
|
16
39
|
const replay = await replayFlowMap(opts.repoRoot, opts.target, flows, evidenceDir);
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
40
|
+
// Exploration is best-effort (issue #1): replaying the whole map always
|
|
41
|
+
// completes, so an exploration crash must never swallow replay's regressions
|
|
42
|
+
// or skip the report. Degrade to an empty result plus an advisory instead.
|
|
43
|
+
let exploration;
|
|
44
|
+
try {
|
|
45
|
+
exploration = await explore({
|
|
46
|
+
repoRoot: opts.repoRoot,
|
|
47
|
+
target: opts.target,
|
|
48
|
+
budget: EFFORT_BUDGETS[opts.effort],
|
|
49
|
+
runDir: dir,
|
|
50
|
+
evidenceDir,
|
|
51
|
+
knownFlows: flows,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
56
|
+
console.warn(`exploration failed; reporting replay results only: ${message}`);
|
|
57
|
+
exploration = {
|
|
58
|
+
discovered: 0,
|
|
59
|
+
findings: [
|
|
60
|
+
{
|
|
61
|
+
id: 'exploration-failed',
|
|
62
|
+
kind: 'advisory',
|
|
63
|
+
title: 'Exploration phase did not complete',
|
|
64
|
+
detail: `Exploration failed and was skipped: ${message}. Replay results below are still complete.`,
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
flows: [],
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
const pkg = JSON.parse(await readFile(new URL('../../package.json', import.meta.url), 'utf8'));
|
|
71
|
+
const environment = {
|
|
72
|
+
browser: replay.browserVersion ? `Chromium ${replay.browserVersion}` : 'Chromium (not launched)',
|
|
73
|
+
viewport: '1280×720',
|
|
74
|
+
os: `${process.platform} ${release()}`,
|
|
75
|
+
node: process.version,
|
|
76
|
+
autoendVersion: pkg.version,
|
|
77
|
+
};
|
|
25
78
|
const artifact = {
|
|
26
79
|
runId,
|
|
27
80
|
target: opts.target.href,
|
|
@@ -30,7 +83,9 @@ export async function executeRun(opts) {
|
|
|
30
83
|
finishedAt: new Date().toISOString(),
|
|
31
84
|
flowsReplayed: replay.replayed,
|
|
32
85
|
flowsDiscovered: exploration.discovered,
|
|
33
|
-
|
|
86
|
+
flows: [...replay.flows, ...exploration.flows],
|
|
87
|
+
environment,
|
|
88
|
+
findings: filterSuppressed([...replay.findings, ...exploration.findings], await readSuppressedTitles(opts.repoRoot)),
|
|
34
89
|
heals: replay.heals,
|
|
35
90
|
};
|
|
36
91
|
await writeReport(dir, artifact);
|
package/dist/run/run.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/run/run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/run/run.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Guardrail for the verify-by-running trust boundary (ADR-0002, issue #3).
|
|
3
|
+
*
|
|
4
|
+
* Discovered and replayed Flow scripts are LLM-authored and run in-process via
|
|
5
|
+
* dynamic import(), so they inherit the Run's full Node authority — including
|
|
6
|
+
* whatever secrets `.env` loaded into `process.env` (notably CURSOR_API_KEY).
|
|
7
|
+
* A flow script never needs those secrets: it only drives a Playwright `page`.
|
|
8
|
+
*
|
|
9
|
+
* `withoutSensitiveEnv` removes secret-looking variables from `process.env`
|
|
10
|
+
* for the duration of a callback and restores them afterward, so a malicious or
|
|
11
|
+
* buggy generated script can't read them. It is a defense-in-depth mitigation,
|
|
12
|
+
* not a sandbox — full isolation (running scripts in a locked-down child
|
|
13
|
+
* process) is tracked separately.
|
|
14
|
+
*
|
|
15
|
+
* IMPORTANT: `process.env` is process-global, so wrap a whole batch of script
|
|
16
|
+
* executions in one call rather than each script — never run agent creation
|
|
17
|
+
* (which needs the key) concurrently inside the callback.
|
|
18
|
+
*/
|
|
19
|
+
export declare function isSensitiveEnvName(name: string): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Run `fn` with secret-looking `process.env` entries temporarily removed,
|
|
22
|
+
* restoring the exact prior environment in a `finally` (even on throw).
|
|
23
|
+
*/
|
|
24
|
+
export declare function withoutSensitiveEnv<T>(fn: () => Promise<T>): Promise<T>;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Guardrail for the verify-by-running trust boundary (ADR-0002, issue #3).
|
|
3
|
+
*
|
|
4
|
+
* Discovered and replayed Flow scripts are LLM-authored and run in-process via
|
|
5
|
+
* dynamic import(), so they inherit the Run's full Node authority — including
|
|
6
|
+
* whatever secrets `.env` loaded into `process.env` (notably CURSOR_API_KEY).
|
|
7
|
+
* A flow script never needs those secrets: it only drives a Playwright `page`.
|
|
8
|
+
*
|
|
9
|
+
* `withoutSensitiveEnv` removes secret-looking variables from `process.env`
|
|
10
|
+
* for the duration of a callback and restores them afterward, so a malicious or
|
|
11
|
+
* buggy generated script can't read them. It is a defense-in-depth mitigation,
|
|
12
|
+
* not a sandbox — full isolation (running scripts in a locked-down child
|
|
13
|
+
* process) is tracked separately.
|
|
14
|
+
*
|
|
15
|
+
* IMPORTANT: `process.env` is process-global, so wrap a whole batch of script
|
|
16
|
+
* executions in one call rather than each script — never run agent creation
|
|
17
|
+
* (which needs the key) concurrently inside the callback.
|
|
18
|
+
*/
|
|
19
|
+
/** A variable is treated as a secret when its name matches this pattern. */
|
|
20
|
+
const SENSITIVE_ENV_PATTERN = /(KEY|TOKEN|SECRET|PASSWORD|PASSWD|CREDENTIAL|AUTH|PRIVATE)/i;
|
|
21
|
+
export function isSensitiveEnvName(name) {
|
|
22
|
+
return SENSITIVE_ENV_PATTERN.test(name);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Run `fn` with secret-looking `process.env` entries temporarily removed,
|
|
26
|
+
* restoring the exact prior environment in a `finally` (even on throw).
|
|
27
|
+
*/
|
|
28
|
+
export async function withoutSensitiveEnv(fn) {
|
|
29
|
+
const removed = {};
|
|
30
|
+
for (const [name, value] of Object.entries(process.env)) {
|
|
31
|
+
if (value !== undefined && isSensitiveEnvName(name)) {
|
|
32
|
+
removed[name] = value;
|
|
33
|
+
delete process.env[name];
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
return await fn();
|
|
38
|
+
}
|
|
39
|
+
finally {
|
|
40
|
+
for (const [name, value] of Object.entries(removed)) {
|
|
41
|
+
process.env[name] = value;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=sensitive-env.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sensitive-env.js","sourceRoot":"","sources":["../../src/run/sensitive-env.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,4EAA4E;AAC5E,MAAM,qBAAqB,GAAG,6DAA6D,CAAC;AAE5F,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,OAAO,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1C,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAI,EAAoB;IAC/D,MAAM,OAAO,GAA2B,EAAE,CAAC;IAC3C,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACxD,IAAI,KAAK,KAAK,SAAS,IAAI,kBAAkB,CAAC,IAAI,CAAC,EAAE,CAAC;YACpD,OAAO,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC;YACtB,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC3B,CAAC;IACH,CAAC;IACD,IAAI,CAAC;QACH,OAAO,MAAM,EAAE,EAAE,CAAC;IACpB,CAAC;YAAS,CAAC;QACT,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YACpD,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC;QAC5B,CAAC;IACH,CAAC;AACH,CAAC"}
|