@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.
@@ -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), evidence };
92
+ return { ok: false, error: failure instanceof Error ? failure.message : String(failure), ...capture };
34
93
  }
35
- return { ok: true, evidence };
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 outcomes = await withPool(flows, REPLAY_WORKERS, async (flow) => {
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
- await saveFlowMeta(repoRoot, { ...flow, lastPassedAt: new Date().toISOString() });
65
- return undefined;
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: outcomes.filter((f) => f !== undefined),
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":"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"}
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>;
@@ -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"}
@@ -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
- const exploration = await explore({
18
- repoRoot: opts.repoRoot,
19
- target: opts.target,
20
- budget: EFFORT_BUDGETS[opts.effort],
21
- runDir: dir,
22
- evidenceDir,
23
- knownFlows: flows,
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
- findings: [...replay.findings, ...exploration.findings],
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);
@@ -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,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEnE,OAAO,EAAE,cAAc,EAAe,MAAM,aAAa,CAAC;AAa1D;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAgB;IAC/C,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC3C,MAAM,KAAK,GAAG,SAAS,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IAC9C,MAAM,EAAE,GAAG,EAAE,WAAW,EAAE,GAAG,MAAM,aAAa,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAEvE,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC7C,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC;IACnF,MAAM,WAAW,GAAG,MAAM,OAAO,CAAC;QAChC,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,MAAM,EAAE,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC;QACnC,MAAM,EAAE,GAAG;QACX,WAAW;QACX,UAAU,EAAE,KAAK;KAClB,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAgB;QAC5B,KAAK;QACL,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI;QACxB,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,SAAS;QACT,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACpC,aAAa,EAAE,MAAM,CAAC,QAAQ;QAC9B,eAAe,EAAE,WAAW,CAAC,UAAU;QACvC,QAAQ,EAAE,CAAC,GAAG,MAAM,CAAC,QAAQ,EAAE,GAAG,WAAW,CAAC,QAAQ,CAAC;QACvD,KAAK,EAAE,MAAM,CAAC,KAAK;KACpB,CAAC;IACF,MAAM,WAAW,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IACjC,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC;AACxC,CAAC"}
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"}