@bonyadnouri/autoend 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 +101 -0
- package/dist/auth/session.d.ts +16 -0
- package/dist/auth/session.js +14 -0
- package/dist/auth/session.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +118 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +14 -0
- package/dist/config.js +65 -0
- package/dist/config.js.map +1 -0
- package/dist/explore/explorer.d.ts +42 -0
- package/dist/explore/explorer.js +232 -0
- package/dist/explore/explorer.js.map +1 -0
- package/dist/explore/hands.d.ts +13 -0
- package/dist/explore/hands.js +35 -0
- package/dist/explore/hands.js.map +1 -0
- package/dist/map/flow-map.d.ts +26 -0
- package/dist/map/flow-map.js +34 -0
- package/dist/map/flow-map.js.map +1 -0
- package/dist/replay/replay.d.ts +36 -0
- package/dist/replay/replay.js +110 -0
- package/dist/replay/replay.js.map +1 -0
- package/dist/report/artifact.d.ts +10 -0
- package/dist/report/artifact.js +20 -0
- package/dist/report/artifact.js.map +1 -0
- package/dist/report/types.d.ts +39 -0
- package/dist/report/types.js +2 -0
- package/dist/report/types.js.map +1 -0
- package/dist/run/effort.d.ts +21 -0
- package/dist/run/effort.js +23 -0
- package/dist/run/effort.js.map +1 -0
- package/dist/run/run.d.ts +17 -0
- package/dist/run/run.js +39 -0
- package/dist/run/run.js.map +1 -0
- package/dist/setup/wizard.d.ts +2 -0
- package/dist/setup/wizard.js +81 -0
- package/dist/setup/wizard.js.map +1 -0
- package/dist/viewer/html.d.ts +7 -0
- package/dist/viewer/html.js +240 -0
- package/dist/viewer/html.js.map +1 -0
- package/dist/viewer/server.d.ts +13 -0
- package/dist/viewer/server.js +54 -0
- package/dist/viewer/server.js.map +1 -0
- package/package.json +50 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"explorer.js","sourceRoot":"","sources":["../../src/explore/explorer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The exploring agents' hands (ADR-0002): the agent-browser CLI.
|
|
3
|
+
* Agents drive it via snapshot-with-refs then act-by-ref; Evidence for
|
|
4
|
+
* exploration comes from `record start <path.webm>` / `record stop`.
|
|
5
|
+
*
|
|
6
|
+
* agent-browser is a direct dependency, so its binary ships with autoend.
|
|
7
|
+
* TODO: resolve the dependency's bin path explicitly instead of relying on
|
|
8
|
+
* PATH, so `autoend` works outside npm scripts.
|
|
9
|
+
*/
|
|
10
|
+
export declare function agentBrowser(args: string[]): Promise<string>;
|
|
11
|
+
export declare function handsAvailable(): Promise<boolean>;
|
|
12
|
+
/** Close one explorer's isolated browser session; a no-op if it never opened. */
|
|
13
|
+
export declare function closeSession(session: string): Promise<void>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { promisify } from 'node:util';
|
|
3
|
+
const exec = promisify(execFile);
|
|
4
|
+
/**
|
|
5
|
+
* The exploring agents' hands (ADR-0002): the agent-browser CLI.
|
|
6
|
+
* Agents drive it via snapshot-with-refs then act-by-ref; Evidence for
|
|
7
|
+
* exploration comes from `record start <path.webm>` / `record stop`.
|
|
8
|
+
*
|
|
9
|
+
* agent-browser is a direct dependency, so its binary ships with autoend.
|
|
10
|
+
* TODO: resolve the dependency's bin path explicitly instead of relying on
|
|
11
|
+
* PATH, so `autoend` works outside npm scripts.
|
|
12
|
+
*/
|
|
13
|
+
export async function agentBrowser(args) {
|
|
14
|
+
const { stdout } = await exec('agent-browser', args);
|
|
15
|
+
return stdout.trim();
|
|
16
|
+
}
|
|
17
|
+
export async function handsAvailable() {
|
|
18
|
+
try {
|
|
19
|
+
await agentBrowser(['--version']);
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/** Close one explorer's isolated browser session; a no-op if it never opened. */
|
|
27
|
+
export async function closeSession(session) {
|
|
28
|
+
try {
|
|
29
|
+
await agentBrowser(['--session', session, 'close']);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
// session never opened or already closed — nothing to clean up
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=hands.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hands.js","sourceRoot":"","sources":["../../src/explore/hands.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,MAAM,IAAI,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAEjC;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAc;IAC/C,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,eAAe,EAAE,IAAI,CAAC,CAAC;IACrD,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC;AACvB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc;IAClC,IAAI,CAAC;QACH,MAAM,YAAY,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC;QAClC,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,iFAAiF;AACjF,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,OAAe;IAChD,IAAI,CAAC;QACH,MAAM,YAAY,CAAC,CAAC,WAAW,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;IACtD,CAAC;IAAC,MAAM,CAAC;QACP,+DAA+D;IACjE,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Flow Map (CONTEXT.md): the persistent record of every Flow agents have
|
|
3
|
+
* discovered and successfully executed. Committed to the user's repo like a
|
|
4
|
+
* lockfile — a branch carries its own baseline (ADR-0001).
|
|
5
|
+
*
|
|
6
|
+
* Layout: .autoend/flows/<flowId>/flow.json (metadata, this shape)
|
|
7
|
+
* .autoend/flows/<flowId>/flow.mts (the Playwright script, ADR-0002)
|
|
8
|
+
*
|
|
9
|
+
* .mts, deliberately: it is unambiguously ESM no matter what "type" the
|
|
10
|
+
* consuming repo's package.json declares, and it dodges test-runner globs
|
|
11
|
+
* (vitest/jest match *.spec.* / *.test.* by default).
|
|
12
|
+
*/
|
|
13
|
+
export interface FlowMeta {
|
|
14
|
+
id: string;
|
|
15
|
+
title: string;
|
|
16
|
+
discoveredAt: string;
|
|
17
|
+
lastPassedAt?: string;
|
|
18
|
+
/** Fingerprints of Suppressed Advisories attached to this Flow. */
|
|
19
|
+
suppressedAdvisories?: string[];
|
|
20
|
+
}
|
|
21
|
+
export declare function flowMapDir(repoRoot: string): string;
|
|
22
|
+
export declare function listFlows(repoRoot: string): Promise<FlowMeta[]>;
|
|
23
|
+
/** Update a Flow's metadata in place (e.g. lastPassedAt after a green replay). */
|
|
24
|
+
export declare function saveFlowMeta(repoRoot: string, meta: FlowMeta): Promise<void>;
|
|
25
|
+
/** Flows enter the map automatically on first successful execution (ADR-0001). */
|
|
26
|
+
export declare function addFlow(repoRoot: string, meta: FlowMeta, playwrightScript: string): Promise<void>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { mkdir, readFile, readdir, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
export function flowMapDir(repoRoot) {
|
|
4
|
+
return join(repoRoot, '.autoend', 'flows');
|
|
5
|
+
}
|
|
6
|
+
export async function listFlows(repoRoot) {
|
|
7
|
+
let entries;
|
|
8
|
+
try {
|
|
9
|
+
entries = await readdir(flowMapDir(repoRoot), { withFileTypes: true });
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return []; // no map yet — first Run on this repo
|
|
13
|
+
}
|
|
14
|
+
const flows = [];
|
|
15
|
+
for (const entry of entries) {
|
|
16
|
+
if (!entry.isDirectory())
|
|
17
|
+
continue;
|
|
18
|
+
const raw = await readFile(join(flowMapDir(repoRoot), entry.name, 'flow.json'), 'utf8');
|
|
19
|
+
flows.push(JSON.parse(raw));
|
|
20
|
+
}
|
|
21
|
+
return flows;
|
|
22
|
+
}
|
|
23
|
+
/** Update a Flow's metadata in place (e.g. lastPassedAt after a green replay). */
|
|
24
|
+
export async function saveFlowMeta(repoRoot, meta) {
|
|
25
|
+
await writeFile(join(flowMapDir(repoRoot), meta.id, 'flow.json'), JSON.stringify(meta, null, 2));
|
|
26
|
+
}
|
|
27
|
+
/** Flows enter the map automatically on first successful execution (ADR-0001). */
|
|
28
|
+
export async function addFlow(repoRoot, meta, playwrightScript) {
|
|
29
|
+
const dir = join(flowMapDir(repoRoot), meta.id);
|
|
30
|
+
await mkdir(dir, { recursive: true });
|
|
31
|
+
await writeFile(join(dir, 'flow.json'), JSON.stringify(meta, null, 2));
|
|
32
|
+
await writeFile(join(dir, 'flow.mts'), playwrightScript);
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=flow-map.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flow-map.js","sourceRoot":"","sources":["../../src/map/flow-map.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACvE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAuBjC,MAAM,UAAU,UAAU,CAAC,QAAgB;IACzC,OAAO,IAAI,CAAC,QAAQ,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;AAC7C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,QAAgB;IAC9C,IAAI,OAAO,CAAC;IACZ,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IACzE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC,CAAC,sCAAsC;IACnD,CAAC;IACD,MAAM,KAAK,GAAe,EAAE,CAAC;IAC7B,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE;YAAE,SAAS;QACnC,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,KAAK,CAAC,IAAI,EAAE,WAAW,CAAC,EAAE,MAAM,CAAC,CAAC;QACxF,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAa,CAAC,CAAC;IAC1C,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,kFAAkF;AAClF,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,QAAgB,EAAE,IAAc;IACjE,MAAM,SAAS,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC,EAAE,EAAE,WAAW,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AACnG,CAAC;AAED,kFAAkF;AAClF,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,QAAgB,EAAE,IAAc,EAAE,gBAAwB;IACtF,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;IAChD,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtC,MAAM,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACvE,MAAM,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,EAAE,gBAAgB,CAAC,CAAC;AAC3D,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type Browser, type Page } from 'playwright';
|
|
2
|
+
import { type FlowMeta } from '../map/flow-map.js';
|
|
3
|
+
import type { Finding, Heal } from '../report/types.js';
|
|
4
|
+
export interface ReplayResult {
|
|
5
|
+
replayed: number;
|
|
6
|
+
findings: Finding[];
|
|
7
|
+
heals: Heal[];
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* A Flow's executable steps (ADR-0002): a plain Playwright script, default-
|
|
11
|
+
* exporting an async function. Scripts are Target-relative — they receive the
|
|
12
|
+
* Run's Target and navigate via `new URL(path, target)` — so the same Flow
|
|
13
|
+
* replays against localhost today and staging tomorrow. A Flow asserts by
|
|
14
|
+
* throwing; returning normally means the Flow's goal was achieved.
|
|
15
|
+
*
|
|
16
|
+
* Scripts are .mts executed via native Node type stripping (Node >= 23.6).
|
|
17
|
+
*/
|
|
18
|
+
export type FlowScript = (page: Page, target: URL) => Promise<void>;
|
|
19
|
+
export interface ScriptOutcome {
|
|
20
|
+
ok: boolean;
|
|
21
|
+
error?: string;
|
|
22
|
+
/** Evidence filename within evidenceDir (WebM). */
|
|
23
|
+
evidence?: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Execute one Flow script in a fresh recording browser context. Shared by
|
|
27
|
+
* replay (map scripts) and exploration (verify-by-running a proposed script
|
|
28
|
+
* before it may enter the Flow Map, ADR-0002).
|
|
29
|
+
*/
|
|
30
|
+
export declare function runFlowScript(browser: Browser, scriptPath: string, target: URL, evidenceDir: string, videoBase: string): Promise<ScriptOutcome>;
|
|
31
|
+
/**
|
|
32
|
+
* Phase 1 of every Run: replay the whole Flow Map — headless, parallel,
|
|
33
|
+
* recording WebM Evidence per Flow via Playwright's recordVideo (ADR-0002).
|
|
34
|
+
* Always completes at any Effort (CONTEXT.md: Effort).
|
|
35
|
+
*/
|
|
36
|
+
export declare function replayFlowMap(repoRoot: string, target: URL, flows: FlowMeta[], evidenceDir: string): Promise<ReplayResult>;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { rename } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { pathToFileURL } from 'node:url';
|
|
4
|
+
import { chromium } from 'playwright';
|
|
5
|
+
import { flowMapDir, saveFlowMeta } from '../map/flow-map.js';
|
|
6
|
+
const FLOW_TIMEOUT_MS = 60_000;
|
|
7
|
+
const REPLAY_WORKERS = 4;
|
|
8
|
+
/**
|
|
9
|
+
* Execute one Flow script in a fresh recording browser context. Shared by
|
|
10
|
+
* replay (map scripts) and exploration (verify-by-running a proposed script
|
|
11
|
+
* before it may enter the Flow Map, ADR-0002).
|
|
12
|
+
*/
|
|
13
|
+
export async function runFlowScript(browser, scriptPath, target, evidenceDir, videoBase) {
|
|
14
|
+
const context = await browser.newContext({ recordVideo: { dir: evidenceDir } });
|
|
15
|
+
// TODO: inject shared storage state (src/auth/session.ts) once fleet auth exists.
|
|
16
|
+
const page = await context.newPage();
|
|
17
|
+
let failure;
|
|
18
|
+
try {
|
|
19
|
+
const script = await loadFlowScript(scriptPath);
|
|
20
|
+
await withTimeout(script(page, target), FLOW_TIMEOUT_MS, videoBase);
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
failure = error;
|
|
24
|
+
}
|
|
25
|
+
const video = page.video();
|
|
26
|
+
await context.close(); // finalizes the recording
|
|
27
|
+
let evidence;
|
|
28
|
+
if (video) {
|
|
29
|
+
evidence = `${videoBase}.webm`;
|
|
30
|
+
await rename(await video.path(), join(evidenceDir, evidence));
|
|
31
|
+
}
|
|
32
|
+
if (failure !== undefined) {
|
|
33
|
+
return { ok: false, error: failure instanceof Error ? failure.message : String(failure), evidence };
|
|
34
|
+
}
|
|
35
|
+
return { ok: true, evidence };
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Phase 1 of every Run: replay the whole Flow Map — headless, parallel,
|
|
39
|
+
* recording WebM Evidence per Flow via Playwright's recordVideo (ADR-0002).
|
|
40
|
+
* Always completes at any Effort (CONTEXT.md: Effort).
|
|
41
|
+
*/
|
|
42
|
+
export async function replayFlowMap(repoRoot, target, flows, evidenceDir) {
|
|
43
|
+
if (flows.length === 0) {
|
|
44
|
+
return { replayed: 0, findings: [], heals: [] };
|
|
45
|
+
}
|
|
46
|
+
const browser = await chromium.launch();
|
|
47
|
+
try {
|
|
48
|
+
const outcomes = await withPool(flows, REPLAY_WORKERS, async (flow) => {
|
|
49
|
+
const scriptPath = join(flowMapDir(repoRoot), flow.id, 'flow.mts');
|
|
50
|
+
const outcome = await runFlowScript(browser, scriptPath, target, evidenceDir, flow.id);
|
|
51
|
+
if (!outcome.ok) {
|
|
52
|
+
// TODO(ADR-0001): attempt a Heal (re-achieve the Flow's goal via an agent)
|
|
53
|
+
// before reporting. Until healing exists, every failure is a Regression.
|
|
54
|
+
const finding = {
|
|
55
|
+
id: `regression-${flow.id}`,
|
|
56
|
+
kind: 'regression',
|
|
57
|
+
flowId: flow.id,
|
|
58
|
+
title: `Flow "${flow.title}" failed on replay`,
|
|
59
|
+
detail: outcome.error ?? 'unknown failure',
|
|
60
|
+
evidence: outcome.evidence,
|
|
61
|
+
};
|
|
62
|
+
return finding;
|
|
63
|
+
}
|
|
64
|
+
await saveFlowMeta(repoRoot, { ...flow, lastPassedAt: new Date().toISOString() });
|
|
65
|
+
return undefined;
|
|
66
|
+
});
|
|
67
|
+
return {
|
|
68
|
+
replayed: flows.length,
|
|
69
|
+
findings: outcomes.filter((f) => f !== undefined),
|
|
70
|
+
heals: [],
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
finally {
|
|
74
|
+
await browser.close();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
async function loadFlowScript(scriptPath) {
|
|
78
|
+
// Query param busts the ESM cache so a Healed script reloads within one process.
|
|
79
|
+
const module = (await import(`${pathToFileURL(scriptPath).href}?v=${Date.now()}`));
|
|
80
|
+
if (typeof module.default !== 'function') {
|
|
81
|
+
throw new Error(`${scriptPath} must default-export an async (page, target) function`);
|
|
82
|
+
}
|
|
83
|
+
return module.default;
|
|
84
|
+
}
|
|
85
|
+
async function withTimeout(work, ms, label) {
|
|
86
|
+
let timer;
|
|
87
|
+
try {
|
|
88
|
+
await Promise.race([
|
|
89
|
+
work,
|
|
90
|
+
new Promise((_, reject) => {
|
|
91
|
+
timer = setTimeout(() => reject(new Error(`flow "${label}" timed out after ${ms / 1000}s`)), ms);
|
|
92
|
+
}),
|
|
93
|
+
]);
|
|
94
|
+
}
|
|
95
|
+
finally {
|
|
96
|
+
clearTimeout(timer);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
async function withPool(items, size, work) {
|
|
100
|
+
const results = new Array(items.length);
|
|
101
|
+
let next = 0;
|
|
102
|
+
await Promise.all(Array.from({ length: Math.min(size, items.length) }, async () => {
|
|
103
|
+
while (next < items.length) {
|
|
104
|
+
const i = next++;
|
|
105
|
+
results[i] = await work(items[i]);
|
|
106
|
+
}
|
|
107
|
+
}));
|
|
108
|
+
return results;
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=replay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"replay.js","sourceRoot":"","sources":["../../src/replay/replay.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { RunArtifact } from './types.js';
|
|
2
|
+
/** Run artifacts live under .autoend/runs/<runId>/ — gitignored, kept until `autoend clean`. */
|
|
3
|
+
export declare function runsDir(repoRoot: string): string;
|
|
4
|
+
export interface RunDir {
|
|
5
|
+
dir: string;
|
|
6
|
+
evidenceDir: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function prepareRunDir(repoRoot: string, runId: string): Promise<RunDir>;
|
|
9
|
+
export declare function writeReport(runDir: string, artifact: RunArtifact): Promise<void>;
|
|
10
|
+
export declare function readRunArtifact(runDir: string): Promise<RunArtifact>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
/** Run artifacts live under .autoend/runs/<runId>/ — gitignored, kept until `autoend clean`. */
|
|
4
|
+
export function runsDir(repoRoot) {
|
|
5
|
+
return join(repoRoot, '.autoend', 'runs');
|
|
6
|
+
}
|
|
7
|
+
export async function prepareRunDir(repoRoot, runId) {
|
|
8
|
+
const dir = join(runsDir(repoRoot), runId);
|
|
9
|
+
const evidenceDir = join(dir, 'evidence');
|
|
10
|
+
await mkdir(evidenceDir, { recursive: true });
|
|
11
|
+
return { dir, evidenceDir };
|
|
12
|
+
}
|
|
13
|
+
export async function writeReport(runDir, artifact) {
|
|
14
|
+
await writeFile(join(runDir, 'report.json'), JSON.stringify(artifact, null, 2));
|
|
15
|
+
}
|
|
16
|
+
export async function readRunArtifact(runDir) {
|
|
17
|
+
const raw = await readFile(join(runDir, 'report.json'), 'utf8');
|
|
18
|
+
return JSON.parse(raw);
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=artifact.js.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { Effort } from '../run/effort.js';
|
|
2
|
+
/**
|
|
3
|
+
* Data model for a Run's Report. Mirrors CONTEXT.md — the glossary is
|
|
4
|
+
* authoritative; if a name here drifts from the glossary, the glossary wins.
|
|
5
|
+
*/
|
|
6
|
+
/** The three Finding tiers (CONTEXT.md: Finding). */
|
|
7
|
+
export type FindingKind = 'hard-failure' | 'regression' | 'advisory';
|
|
8
|
+
export interface Finding {
|
|
9
|
+
id: string;
|
|
10
|
+
kind: FindingKind;
|
|
11
|
+
/** The Flow this Finding is attached to; Regressions always have one. */
|
|
12
|
+
flowId?: string;
|
|
13
|
+
title: string;
|
|
14
|
+
detail: string;
|
|
15
|
+
/** Path to WebM Evidence, relative to the Run artifact's evidence/ dir. */
|
|
16
|
+
evidence?: string;
|
|
17
|
+
}
|
|
18
|
+
/** A Heal is reported alongside Findings but is not one (CONTEXT.md: Heal). */
|
|
19
|
+
export interface Heal {
|
|
20
|
+
flowId: string;
|
|
21
|
+
summary: string;
|
|
22
|
+
evidence?: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The self-contained record of one Run (ADR-0004): serialized as report.json
|
|
26
|
+
* next to an evidence/ dir of WebM files. Portable — viewable anywhere;
|
|
27
|
+
* resolution actions only work where the Flow Map lives.
|
|
28
|
+
*/
|
|
29
|
+
export interface RunArtifact {
|
|
30
|
+
runId: string;
|
|
31
|
+
target: string;
|
|
32
|
+
effort: Effort;
|
|
33
|
+
startedAt: string;
|
|
34
|
+
finishedAt?: string;
|
|
35
|
+
flowsReplayed: number;
|
|
36
|
+
flowsDiscovered: number;
|
|
37
|
+
findings: Finding[];
|
|
38
|
+
heals: Heal[];
|
|
39
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/report/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Effort — the user-chosen depth preset for a Run (CONTEXT.md).
|
|
3
|
+
* Effort bounds exploration only: replaying the whole Flow Map always
|
|
4
|
+
* completes at any Effort, so Regression claims are never sacrificed.
|
|
5
|
+
*/
|
|
6
|
+
export declare const EFFORT_LEVELS: readonly ["low", "mid", "high", "xhigh", "ultra"];
|
|
7
|
+
export type Effort = (typeof EFFORT_LEVELS)[number];
|
|
8
|
+
export interface ExplorationBudget {
|
|
9
|
+
/** Wall-clock cap for the exploration phase, in seconds. */
|
|
10
|
+
seconds: number;
|
|
11
|
+
/** Maximum concurrently exploring agents. */
|
|
12
|
+
explorers: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Replay stays under a few seconds; exploration is what these budgets
|
|
16
|
+
* time-box. Live-run data (2026-07-04): one agent turn costs 5-15s of LLM
|
|
17
|
+
* latency and ≈ 21.5k input tokens; a useful exploration needs 4-8 turns, so
|
|
18
|
+
* sub-45s soft budgets starve explorers before they can report.
|
|
19
|
+
*/
|
|
20
|
+
export declare const EFFORT_BUDGETS: Record<Effort, ExplorationBudget>;
|
|
21
|
+
export declare function isEffort(value: string): value is Effort;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Effort — the user-chosen depth preset for a Run (CONTEXT.md).
|
|
3
|
+
* Effort bounds exploration only: replaying the whole Flow Map always
|
|
4
|
+
* completes at any Effort, so Regression claims are never sacrificed.
|
|
5
|
+
*/
|
|
6
|
+
export const EFFORT_LEVELS = ['low', 'mid', 'high', 'xhigh', 'ultra'];
|
|
7
|
+
/**
|
|
8
|
+
* Replay stays under a few seconds; exploration is what these budgets
|
|
9
|
+
* time-box. Live-run data (2026-07-04): one agent turn costs 5-15s of LLM
|
|
10
|
+
* latency and ≈ 21.5k input tokens; a useful exploration needs 4-8 turns, so
|
|
11
|
+
* sub-45s soft budgets starve explorers before they can report.
|
|
12
|
+
*/
|
|
13
|
+
export const EFFORT_BUDGETS = {
|
|
14
|
+
low: { seconds: 45, explorers: 2 },
|
|
15
|
+
mid: { seconds: 90, explorers: 3 },
|
|
16
|
+
high: { seconds: 240, explorers: 4 },
|
|
17
|
+
xhigh: { seconds: 600, explorers: 8 },
|
|
18
|
+
ultra: { seconds: 1800, explorers: 12 },
|
|
19
|
+
};
|
|
20
|
+
export function isEffort(value) {
|
|
21
|
+
return EFFORT_LEVELS.includes(value);
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=effort.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"effort.js","sourceRoot":"","sources":["../../src/run/effort.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAU,CAAC;AAW/E;;;;;GAKG;AACH,MAAM,CAAC,MAAM,cAAc,GAAsC;IAC/D,GAAG,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE;IAClC,GAAG,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE;IAClC,IAAI,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC,EAAE;IACpC,KAAK,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC,EAAE;IACrC,KAAK,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE;CACxC,CAAC;AAEF,MAAM,UAAU,QAAQ,CAAC,KAAa;IACpC,OAAQ,aAAmC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AAC9D,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { RunArtifact } from '../report/types.js';
|
|
2
|
+
import { type Effort } from './effort.js';
|
|
3
|
+
export interface RunOptions {
|
|
4
|
+
target: URL;
|
|
5
|
+
effort: Effort;
|
|
6
|
+
repoRoot: string;
|
|
7
|
+
}
|
|
8
|
+
export interface RunOutcome {
|
|
9
|
+
artifactDir: string;
|
|
10
|
+
artifact: RunArtifact;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* A Run (CONTEXT.md): two ordered phases. Phase 1 replays the whole Flow Map
|
|
14
|
+
* (always completes); phase 2 explores new surface within the Effort budget.
|
|
15
|
+
* Output is a self-contained Run artifact (ADR-0004).
|
|
16
|
+
*/
|
|
17
|
+
export declare function executeRun(opts: RunOptions): Promise<RunOutcome>;
|
package/dist/run/run.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { listFlows } from '../map/flow-map.js';
|
|
2
|
+
import { explore } from '../explore/explorer.js';
|
|
3
|
+
import { replayFlowMap } from '../replay/replay.js';
|
|
4
|
+
import { prepareRunDir, writeReport } from '../report/artifact.js';
|
|
5
|
+
import { EFFORT_BUDGETS } from './effort.js';
|
|
6
|
+
/**
|
|
7
|
+
* A Run (CONTEXT.md): two ordered phases. Phase 1 replays the whole Flow Map
|
|
8
|
+
* (always completes); phase 2 explores new surface within the Effort budget.
|
|
9
|
+
* Output is a self-contained Run artifact (ADR-0004).
|
|
10
|
+
*/
|
|
11
|
+
export async function executeRun(opts) {
|
|
12
|
+
const startedAt = new Date().toISOString();
|
|
13
|
+
const runId = startedAt.replace(/[:.]/g, '-');
|
|
14
|
+
const { dir, evidenceDir } = await prepareRunDir(opts.repoRoot, runId);
|
|
15
|
+
const flows = await listFlows(opts.repoRoot);
|
|
16
|
+
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
|
+
});
|
|
25
|
+
const artifact = {
|
|
26
|
+
runId,
|
|
27
|
+
target: opts.target.href,
|
|
28
|
+
effort: opts.effort,
|
|
29
|
+
startedAt,
|
|
30
|
+
finishedAt: new Date().toISOString(),
|
|
31
|
+
flowsReplayed: replay.replayed,
|
|
32
|
+
flowsDiscovered: exploration.discovered,
|
|
33
|
+
findings: [...replay.findings, ...exploration.findings],
|
|
34
|
+
heals: replay.heals,
|
|
35
|
+
};
|
|
36
|
+
await writeReport(dir, artifact);
|
|
37
|
+
return { artifactDir: dir, artifact };
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=run.js.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import * as p from '@clack/prompts';
|
|
2
|
+
import pc from 'picocolors';
|
|
3
|
+
import { appendDotEnv, ensureGitignore, loadDotEnv, saveConfig } from '../config.js';
|
|
4
|
+
import { EFFORT_BUDGETS } from '../run/effort.js';
|
|
5
|
+
const EFFORT_CHOICES = [
|
|
6
|
+
{ value: 'low', label: 'low', hint: 'quick pass · ~1-2 min' },
|
|
7
|
+
{ value: 'mid', label: 'mid', hint: 'everyday runs · ~2-3 min' },
|
|
8
|
+
{ value: 'high', label: 'high', hint: 'thorough sweep · ~5 min' },
|
|
9
|
+
{ value: 'xhigh', label: 'xhigh', hint: 'deep exploration · ~12 min' },
|
|
10
|
+
{ value: 'ultra', label: 'ultra', hint: 'leave it running · ~35 min' },
|
|
11
|
+
];
|
|
12
|
+
/** `autoend init` — the guided setup. Writes .autoend/config.json and .env. */
|
|
13
|
+
export async function runSetupWizard(repoRoot) {
|
|
14
|
+
console.clear();
|
|
15
|
+
p.intro(`${pc.bgCyan(pc.black(' autoend '))} ${pc.dim('agent-powered end-to-end testing')}`);
|
|
16
|
+
p.note([
|
|
17
|
+
'Agents test your app and hand you a video-backed report.',
|
|
18
|
+
'',
|
|
19
|
+
`${pc.dim('1.')} Known flows are replayed — fast, deterministic`,
|
|
20
|
+
`${pc.dim('2.')} Agents explore new surface within your effort budget`,
|
|
21
|
+
`${pc.dim('3.')} A report opens: watch what broke, dismiss what didn't`,
|
|
22
|
+
].join('\n'), 'How it works');
|
|
23
|
+
const target = await p.text({
|
|
24
|
+
message: 'Where does your app run?',
|
|
25
|
+
placeholder: 'http://localhost:3000',
|
|
26
|
+
initialValue: 'http://localhost:3000',
|
|
27
|
+
validate: (value) => {
|
|
28
|
+
try {
|
|
29
|
+
new URL(value ?? '');
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return 'Enter a full URL, e.g. http://localhost:3000';
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
bail(target);
|
|
38
|
+
const effort = await p.select({
|
|
39
|
+
message: 'How hard should a Run test by default?',
|
|
40
|
+
options: EFFORT_CHOICES,
|
|
41
|
+
initialValue: 'mid',
|
|
42
|
+
});
|
|
43
|
+
bail(effort);
|
|
44
|
+
await loadDotEnv(repoRoot);
|
|
45
|
+
if (process.env.CURSOR_API_KEY) {
|
|
46
|
+
p.log.success('Cursor API key found — agents are ready to think.');
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
const key = await p.password({
|
|
50
|
+
message: `Cursor API key ${pc.dim('(cursor.com → Dashboard → API Keys)')}`,
|
|
51
|
+
validate: (value) => ((value ?? '').trim().length > 0 ? undefined : 'Required — agents run on your Cursor account'),
|
|
52
|
+
});
|
|
53
|
+
bail(key);
|
|
54
|
+
await appendDotEnv(repoRoot, 'CURSOR_API_KEY', key.trim());
|
|
55
|
+
p.log.success('Saved to .env');
|
|
56
|
+
}
|
|
57
|
+
const spinner = p.spinner();
|
|
58
|
+
spinner.start('Writing configuration');
|
|
59
|
+
await saveConfig(repoRoot, { target: target, effort: effort });
|
|
60
|
+
const added = await ensureGitignore(repoRoot, ['.autoend/runs/', '.env']);
|
|
61
|
+
spinner.stop(added.length > 0
|
|
62
|
+
? `Config written · .gitignore now covers ${added.join(', ')}`
|
|
63
|
+
: 'Config written');
|
|
64
|
+
const budget = EFFORT_BUDGETS[effort];
|
|
65
|
+
p.note([
|
|
66
|
+
`${pc.cyan('npx autoend')} run with your defaults`,
|
|
67
|
+
`${pc.cyan('npx autoend <url>')} run against another target`,
|
|
68
|
+
`${pc.cyan('npx autoend -e high')} push harder for one run`,
|
|
69
|
+
'',
|
|
70
|
+
pc.dim(`Defaults: ${target} · effort ${effort} (${budget.explorers} explorers, ${budget.seconds}s exploration)`),
|
|
71
|
+
pc.dim(`Commit ${pc.reset(pc.dim('.autoend/flows/'))} — it is your team's shared baseline.`),
|
|
72
|
+
].join('\n'), 'You are set');
|
|
73
|
+
p.outro('First run discovers your flows. Every run after that guards them.');
|
|
74
|
+
}
|
|
75
|
+
function bail(value) {
|
|
76
|
+
if (p.isCancel(value)) {
|
|
77
|
+
p.cancel('Setup aborted — nothing was written.');
|
|
78
|
+
process.exit(0);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=wizard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wizard.js","sourceRoot":"","sources":["../../src/setup/wizard.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Report page (ADR-0004: thin viewer over the static Run artifact).
|
|
3
|
+
* Single file, zero external assets — must render offline. Design direction:
|
|
4
|
+
* forensic terminal dossier. The page answers "did anything break?" in the
|
|
5
|
+
* first glance (the verdict), then presents Evidence per tier.
|
|
6
|
+
*/
|
|
7
|
+
export declare const VIEWER_HTML = "<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>autoend Report</title>\n<style>\n :root {\n --bg: #0a0d12;\n --surface: #10141c;\n --line: #1d2432;\n --ink: #dce3ef;\n --dim: #6d7890;\n --hard: #ff5c5c;\n --regression: #f2a53f;\n --heal: #38cfc0;\n --advisory: #8f7ff0;\n --ok: #56d68b;\n --mono: ui-monospace, \"SFMono-Regular\", \"Cascadia Code\", \"JetBrains Mono\", Menlo, Consolas, monospace;\n }\n * { box-sizing: border-box; margin: 0; }\n html { color-scheme: dark; }\n body {\n background:\n radial-gradient(1200px 500px at 50% -10%, #131a26 0%, transparent 60%),\n repeating-linear-gradient(0deg, transparent 0 47px, #ffffff05 47px 48px),\n repeating-linear-gradient(90deg, transparent 0 47px, #ffffff05 47px 48px),\n var(--bg);\n color: var(--ink);\n font-family: var(--mono);\n font-size: 14px;\n line-height: 1.55;\n min-height: 100vh;\n }\n main { max-width: 66rem; margin: 0 auto; padding: 0 1.5rem 6rem; }\n\n header {\n display: flex; justify-content: space-between; align-items: baseline;\n padding: 1.1rem 0; border-bottom: 1px solid var(--line);\n font-size: 11px; letter-spacing: .18em; text-transform: uppercase; color: var(--dim);\n }\n header .brand b { color: var(--ink); font-weight: 700; }\n\n .verdict { padding: 3.2rem 0 0; animation: rise .5s ease-out both; }\n .verdict h1 {\n font-size: clamp(2rem, 5.4vw, 3.4rem);\n font-weight: 800; letter-spacing: .01em; line-height: 1.1;\n display: flex; align-items: center; gap: 1rem; flex-wrap: wrap;\n }\n .lamp {\n width: .6em; height: .6em; border-radius: 50%;\n background: var(--verdict-color, var(--ok));\n box-shadow: 0 0 18px var(--verdict-color, var(--ok));\n animation: pulse 2.4s ease-in-out infinite;\n }\n .verdict .sub { margin-top: .9rem; color: var(--dim); font-size: 13px; }\n .verdict .sub b { color: var(--ink); font-weight: 600; }\n\n .stats {\n display: grid; grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr));\n gap: 1px; background: var(--line); border: 1px solid var(--line);\n margin-top: 2.6rem; animation: rise .5s .08s ease-out both;\n }\n .stat { background: var(--surface); padding: .9rem 1rem .8rem; position: relative; }\n .stat::before {\n content: \"\"; position: absolute; inset: 0 auto auto 0; height: 2px; width: 100%;\n background: var(--stat-color, transparent);\n }\n .stat .n { font-size: 1.7rem; font-weight: 800; }\n .stat .l { font-size: 10px; letter-spacing: .14em; text-transform: uppercase; color: var(--dim); }\n .stat.zero .n { color: var(--dim); }\n\n section { margin-top: 3.4rem; animation: rise .5s ease-out both; }\n section:nth-of-type(1) { animation-delay: .14s }\n section:nth-of-type(2) { animation-delay: .20s }\n section:nth-of-type(3) { animation-delay: .26s }\n section:nth-of-type(4) { animation-delay: .32s }\n section h2 {\n font-size: 12px; letter-spacing: .2em; text-transform: uppercase;\n display: flex; align-items: baseline; gap: .7rem;\n }\n section h2::before { content: \"\u25AE\"; color: var(--tier); }\n section h2 .count { color: var(--tier); }\n section .explain { color: var(--dim); font-size: 12.5px; margin: .45rem 0 1.2rem; max-width: 44rem; }\n\n .card {\n border: 1px solid var(--line); border-left: 3px solid var(--tier);\n background: var(--surface); padding: 1.1rem 1.25rem 1.25rem; margin-bottom: 1rem;\n }\n .card h3 { font-size: 15px; font-weight: 700; }\n .card .detail {\n color: var(--dim); font-size: 12.5px; margin-top: .4rem;\n white-space: pre-wrap; word-break: break-word;\n }\n .card video {\n width: 100%; margin-top: 1rem; border: 1px solid var(--line);\n background: #000; display: block;\n }\n .card .flowid { color: var(--dim); font-size: 11px; margin-top: .55rem; letter-spacing: .06em; }\n\n .allclear { text-align: center; padding: 5rem 0 2rem; animation: rise .5s .14s ease-out both; }\n .allclear .mark { font-size: 3rem; color: var(--ok); }\n .allclear h2 { font-size: 1.5rem; letter-spacing: .3em; text-transform: uppercase; margin-top: 1rem; }\n .allclear p { color: var(--dim); margin-top: .8rem; }\n\n footer {\n margin-top: 5rem; padding-top: 1rem; border-top: 1px solid var(--line);\n color: var(--dim); font-size: 11px; letter-spacing: .08em;\n display: flex; justify-content: space-between; flex-wrap: wrap; gap: .5rem;\n }\n\n @keyframes rise { from { opacity: 0; transform: translateY(10px) } to { opacity: 1; transform: none } }\n @keyframes pulse { 0%,100% { opacity: 1 } 50% { opacity: .55 } }\n @media (prefers-reduced-motion: reduce) { * { animation: none !important } }\n</style>\n</head>\n<body>\n<main>\n <header>\n <span class=\"brand\"><b>autoend</b> \u2044 run report</span>\n <span id=\"runid\"></span>\n </header>\n <div class=\"verdict\" id=\"verdict\"></div>\n <div class=\"stats\" id=\"stats\"></div>\n <div id=\"body\"></div>\n <footer>\n <span>evidence recorded headless during the run</span>\n <span id=\"stamp\"></span>\n </footer>\n</main>\n<script>\nconst TIERS = {\n 'hard-failure': {\n label: 'Hard failures', color: 'var(--hard)',\n explain: 'Objectively broken \u2014 errors, crashes, failed requests. Fix these first.',\n },\n 'regression': {\n label: 'Regressions', color: 'var(--regression)',\n explain: 'Worked in a previous run, failed in this one. If the removal was intentional, dismiss it to update the baseline.',\n },\n 'heal': {\n label: 'Heals', color: 'var(--heal)',\n explain: 'The UI changed but the goal still works \u2014 the flow script was rewritten. Watch each video to confirm the heal is right.',\n },\n 'advisory': {\n label: 'Advisories', color: 'var(--advisory)',\n explain: 'Agent judgment on UX, accessibility, and speed. Signals worth a look, not verdicts.',\n },\n};\n\nfunction el(tag, attrs, children) {\n const node = document.createElement(tag);\n for (const [k, v] of Object.entries(attrs || {})) {\n if (k === 'style') node.style.cssText = v; else if (k === 'text') node.textContent = v; else node.setAttribute(k, v);\n }\n for (const child of children || []) node.appendChild(child);\n return node;\n}\n\nfetch('/api/report').then(r => r.json()).then(report => {\n const byTier = { 'hard-failure': [], 'regression': [], 'heal': [], 'advisory': [] };\n for (const f of report.findings) (byTier[f.kind] || byTier['advisory']).push(f);\n for (const h of report.heals) byTier['heal'].push({ title: 'Flow \"' + h.flowId + '\" healed', detail: h.summary, flowId: h.flowId, evidence: h.evidence });\n\n const nHard = byTier['hard-failure'].length, nReg = byTier['regression'].length;\n const nHeal = byTier['heal'].length, nAdv = byTier['advisory'].length;\n\n document.getElementById('runid').textContent = report.runId;\n document.getElementById('stamp').textContent = 'effort ' + report.effort + ' \u00B7 ' + report.startedAt;\n\n const durationS = report.finishedAt\n ? ((new Date(report.finishedAt) - new Date(report.startedAt)) / 1000).toFixed(1) + 's'\n : '\u2014';\n\n let text, color;\n if (nHard > 0) { text = nHard + ' hard failure' + (nHard > 1 ? 's' : ''); color = 'var(--hard)'; }\n else if (nReg > 0) { text = nReg + ' regression' + (nReg > 1 ? 's' : ''); color = 'var(--regression)'; }\n else if (nHeal > 0) { text = 'passing, ' + nHeal + ' heal' + (nHeal > 1 ? 's' : '') + ' to verify'; color = 'var(--heal)'; }\n else { text = 'all clear'; color = 'var(--ok)'; }\n\n const verdict = document.getElementById('verdict');\n verdict.style.setProperty('--verdict-color', color);\n verdict.appendChild(el('h1', {}, [el('span', { class: 'lamp' }), el('span', { text })]));\n const sub = el('div', { class: 'sub' });\n sub.innerHTML = 'target <b></b> \u00B7 <b>' + report.flowsReplayed + '</b> flows replayed \u00B7 <b>'\n + report.flowsDiscovered + '</b> discovered \u00B7 <b>' + durationS + '</b>';\n sub.querySelector('b').textContent = report.target;\n verdict.appendChild(sub);\n\n const stats = document.getElementById('stats');\n for (const [kind, tier] of Object.entries(TIERS)) {\n const n = byTier[kind].length;\n stats.appendChild(el('div', { class: 'stat' + (n === 0 ? ' zero' : ''), style: '--stat-color:' + (n > 0 ? tier.color : 'transparent') }, [\n el('div', { class: 'n', text: String(n) }),\n el('div', { class: 'l', text: tier.label }),\n ]));\n }\n\n const body = document.getElementById('body');\n const total = nHard + nReg + nHeal + nAdv;\n if (total === 0) {\n body.appendChild(el('div', { class: 'allclear' }, [\n el('div', { class: 'mark', text: '\u2713' }),\n el('h2', { text: 'all clear' }),\n el('p', { text: report.flowsReplayed > 0\n ? 'Every known flow replayed successfully. Nothing to act on.'\n : 'No flows in the map yet \u2014 the first exploration run will build your baseline.' }),\n ]));\n return;\n }\n\n for (const [kind, tier] of Object.entries(TIERS)) {\n const items = byTier[kind];\n if (items.length === 0) continue;\n const section = el('section', { style: '--tier:' + tier.color });\n section.appendChild(el('h2', {}, [\n el('span', { text: tier.label + ' ' }),\n el('span', { class: 'count', text: String(items.length) }),\n ]));\n section.appendChild(el('div', { class: 'explain', text: tier.explain }));\n for (const f of items) {\n const card = el('div', { class: 'card' }, [el('h3', { text: f.title })]);\n if (f.detail) card.appendChild(el('div', { class: 'detail', text: f.detail }));\n if (f.evidence) card.appendChild(el('video', { controls: '', preload: 'metadata', src: '/evidence/' + f.evidence }));\n if (f.flowId) card.appendChild(el('div', { class: 'flowid', text: 'flow: ' + f.flowId }));\n section.appendChild(card);\n }\n body.appendChild(section);\n }\n});\n</script>\n</body>\n</html>";
|