@bonyadnouri/autoend 0.1.0 → 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 +138 -38
- package/dist/cli.js +5 -3
- package/dist/cli.js.map +1 -1
- package/dist/explore/explorer.d.ts +4 -1
- package/dist/explore/explorer.js +62 -23
- 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/setup/wizard.js +5 -3
- package/dist/setup/wizard.js.map +1 -1
- 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/README.md
CHANGED
|
@@ -1,80 +1,180 @@
|
|
|
1
1
|
# autoend
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/@bonyadnouri/autoend)
|
|
4
|
+
[](./LICENSE)
|
|
5
|
+
|
|
3
6
|
**Agent-powered end-to-end testing.** Point it at your app, walk away, get back a video-backed report.
|
|
4
7
|
|
|
5
8
|
```sh
|
|
6
|
-
npx autoend init # guided setup — takes a minute
|
|
7
|
-
npx autoend # agents test your app, a report opens
|
|
9
|
+
npx @bonyadnouri/autoend init # guided setup — takes a minute
|
|
10
|
+
npx @bonyadnouri/autoend # agents test your app, a report opens
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+

|
|
14
|
+
|
|
15
|
+
## What is autoend?
|
|
16
|
+
|
|
17
|
+
autoend is **not a test framework** — you never write a test. It's an autonomous testing fleet with a memory:
|
|
18
|
+
|
|
19
|
+
- **You initiate a Run** against any URL — your localhost dev server, a staging deploy, a preview URL.
|
|
20
|
+
- **AI agents explore your app** like users would: clicking, filling forms, navigating — discovering the flows that matter ("a visitor can sign up", "a shopper can complete checkout").
|
|
21
|
+
- **Every flow that works gets written down** — as an ordinary Playwright script, committed to your repo. This is the **Flow Map**: your app's living baseline of what demonstrably works.
|
|
22
|
+
- **Every subsequent Run replays the whole map first** — deterministic, parallel, headless, no AI involved, seconds not minutes — then spends its exploration budget on new surface only.
|
|
23
|
+
- **You get a report in your browser**: a one-glance verdict, findings sorted into tiers, and a WebM video behind every claim so you can *watch* what the agent saw instead of parsing a stack trace.
|
|
24
|
+
|
|
25
|
+
The result: e2e coverage that grows on its own, catches regressions run-over-run, and never asks you to write or maintain a selector.
|
|
26
|
+
|
|
27
|
+
## How is it different?
|
|
28
|
+
|
|
29
|
+
| | You write tests? | LLM cost per run | Regression baseline | Exit path |
|
|
30
|
+
|---|---|---|---|---|
|
|
31
|
+
| **autoend** | Never | Only for *new* surface — replays are LLM-free | Flow Map, committed to git | Eject anytime: the map **is** a plain Playwright suite |
|
|
32
|
+
| Playwright / Cypress | Yes, by hand | None | Your hand-written suite | — |
|
|
33
|
+
| Playwright Test Agents | You supervise agents in your IDE, per test | None at runtime | Generated suite you own | — |
|
|
34
|
+
| Stagehand & AI-automation libs | Yes — you code the automation | Every run (unless you manage caches) | DIY | Tied to their runtime |
|
|
35
|
+
|
|
36
|
+
The three ideas doing the work:
|
|
37
|
+
|
|
38
|
+
1. **Tests are discovered, not authored.** Agents generate flows on the fly — but a proposed flow only enters the map after it's been **verified by actually executing it**. No hallucinated tests in your baseline.
|
|
39
|
+
2. **The LLM pays once per flow, not once per run.** Discovery costs tokens; the resulting artifact is deterministic Playwright that replays for free, forever. This is why a Run with a warm map takes seconds, not minutes.
|
|
40
|
+
3. **Evidence over assertion.** Every finding — failure, regression, or judgment call — links to a video recorded headless during the Run. "It broke" comes with the footage.
|
|
41
|
+
|
|
42
|
+
And because the Flow Map is plain Playwright in your own repo: branches carry their own baseline, teammates share it through git, agent changes show up in PRs like any other diff, and **there is no lock-in** — delete autoend tomorrow and you still own a working test suite.
|
|
43
|
+
|
|
44
|
+
## Install
|
|
45
|
+
|
|
46
|
+
Requirements:
|
|
47
|
+
|
|
48
|
+
- **Node.js ≥ 23.6** (flow scripts run via native TypeScript type-stripping)
|
|
49
|
+
- **A Cursor account + API key** — exploring agents run on the [Cursor SDK](https://cursor.com/docs/sdk/typescript) and bill to your Cursor plan. Get a key at [cursor.com → Dashboard → API Keys](https://cursor.com/dashboard).
|
|
50
|
+
- macOS, Linux, or Windows
|
|
51
|
+
|
|
52
|
+
```sh
|
|
53
|
+
# one-off, always latest
|
|
54
|
+
npx @bonyadnouri/autoend init
|
|
55
|
+
|
|
56
|
+
# or install globally and get the plain `autoend` command
|
|
57
|
+
npm i -g @bonyadnouri/autoend
|
|
58
|
+
|
|
59
|
+
# or straight from GitHub
|
|
60
|
+
npx github:bonyadnouri/autoend init
|
|
61
|
+
|
|
62
|
+
# one-time: fetch the headless browser used for replays
|
|
63
|
+
npx playwright install chromium
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Usage
|
|
67
|
+
|
|
68
|
+
### 1. Set up once
|
|
69
|
+
|
|
8
70
|
```
|
|
71
|
+
$ npx @bonyadnouri/autoend init
|
|
9
72
|
|
|
10
|
-
|
|
73
|
+
◆ Where does your app run? http://localhost:3000
|
|
74
|
+
◆ How hard should a Run test by default? mid — everyday runs · ~2-3 min
|
|
75
|
+
◆ Cursor API key ✓ saved to .env
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
`init` writes `.autoend/config.json`, stores your API key in a gitignored `.env`, and updates your `.gitignore` so run artifacts and secrets never get committed.
|
|
11
79
|
|
|
12
|
-
|
|
80
|
+
### 2. Run
|
|
13
81
|
|
|
14
|
-
|
|
82
|
+
```
|
|
83
|
+
$ npx @bonyadnouri/autoend
|
|
15
84
|
|
|
16
|
-
|
|
85
|
+
Run starting http://localhost:3000 · effort mid
|
|
86
|
+
Run finished in 94.1s · 12 replayed · 2 discovered · all clear
|
|
87
|
+
Report: http://127.0.0.1:53211/
|
|
88
|
+
```
|
|
17
89
|
|
|
18
|
-
|
|
19
|
-
2. **Explore** — within the effort budget you chose, agents probe new surface: clicking, filling, navigating — looking for flows nobody wrote down and failures nobody noticed.
|
|
20
|
-
3. **Report** — a local page opens with a one-glance verdict. Click into any finding and *watch* what the agent saw.
|
|
90
|
+
Your **first Run is a discovery run** — the map is empty, so agents spend the whole budget exploring and the map gets its first flows. Every Run after that opens by replaying everything already known, so regressions surface even at the lowest effort.
|
|
21
91
|
|
|
22
|
-
|
|
92
|
+
### 3. Read the report
|
|
93
|
+
|
|
94
|
+
A browser tab opens with the verdict up top and findings below, sorted by how much you should care:
|
|
23
95
|
|
|
24
96
|
| Tier | Meaning | Your move |
|
|
25
97
|
|---|---|---|
|
|
26
98
|
| **Hard failure** | Objectively broken — 5xx, crashes, console errors | Fix it |
|
|
27
|
-
| **Regression** | Worked in a previous run, failed now | Fix it — or
|
|
28
|
-
| **Heal** | UI changed, goal still works;
|
|
99
|
+
| **Regression** | Worked in a previous run, failed now | Fix it — or remove the flow if the change was intentional |
|
|
100
|
+
| **Heal** | UI changed, goal still works; script was rewritten | Watch the video, confirm *(coming — see Status)* |
|
|
29
101
|
| **Advisory** | Agent judgment: UX, accessibility, speed | Your call |
|
|
30
102
|
|
|
31
|
-
|
|
103
|
+
Every finding carries a video. Watch it before you read another line of logs.
|
|
32
104
|
|
|
33
|
-
|
|
105
|
+

|
|
106
|
+
|
|
107
|
+
### 4. Choose your effort
|
|
34
108
|
|
|
35
|
-
|
|
109
|
+
Effort scales **exploration only** — replay of the full map always completes, at any level, so regression coverage is never sacrificed to a small budget.
|
|
36
110
|
|
|
111
|
+
```sh
|
|
112
|
+
npx @bonyadnouri/autoend -e low # quick pass ~1-2 min
|
|
113
|
+
npx @bonyadnouri/autoend -e mid # everyday runs ~2-3 min
|
|
114
|
+
npx @bonyadnouri/autoend -e high # thorough sweep ~5 min
|
|
115
|
+
npx @bonyadnouri/autoend -e xhigh # deep exploration ~12 min
|
|
116
|
+
npx @bonyadnouri/autoend -e ultra # leave it running ~35 min
|
|
37
117
|
```
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
118
|
+
|
|
119
|
+
### CLI reference
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
autoend init guided setup (target, effort, API key)
|
|
123
|
+
autoend [target-url] start a Run (falls back to your configured target)
|
|
124
|
+
autoend clean delete all local Run artifacts
|
|
125
|
+
|
|
126
|
+
-e, --effort <level> low | mid | high | xhigh | ultra
|
|
127
|
+
--no-open don't open the Report in a browser
|
|
128
|
+
--no-serve write the Run artifact and exit (CI-style)
|
|
129
|
+
--port <n> viewer port (default: random)
|
|
44
130
|
```
|
|
45
131
|
|
|
46
|
-
|
|
132
|
+
### Guardrails
|
|
47
133
|
|
|
48
|
-
|
|
134
|
+
Agents act on your app **for real**: they submit forms and click buttons. Two rails are built in — agents never navigate off the Target's origin, and they're instructed to avoid destructive or irreversible actions. The third rail is yours: **point autoend at an environment where real actions are safe** (localhost, staging with test data), never at production.
|
|
135
|
+
|
|
136
|
+
### Generated scripts run in-process (trust boundary)
|
|
137
|
+
|
|
138
|
+
Discovered and replayed Flow scripts are LLM-authored and executed in the Run's own Node process (verify-by-running, ADR-0002). That's a real trust boundary: a bad generation — or a prompt-injected Target page — could emit a script that reaches for the Node runtime. Two defense-in-depth mitigations are in place today: proposed scripts that reference disallowed APIs (`process.env`, `child_process`, dynamic `import()`/`require`, `eval`, …) are rejected before execution, and secret-looking environment variables (e.g. `CURSOR_API_KEY`) are stripped from `process.env` while any generated script runs. These are mitigations, not a sandbox — running scripts in a locked-down child process is tracked as follow-up work.
|
|
139
|
+
|
|
140
|
+
## The Flow Map is yours
|
|
49
141
|
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
142
|
+
```
|
|
143
|
+
.autoend/
|
|
144
|
+
├── flows/ # the Flow Map — commit this
|
|
145
|
+
│ └── choose-pro-plan/
|
|
146
|
+
│ ├── flow.json # metadata: title, discovered, last passed
|
|
147
|
+
│ └── flow.mts # an ordinary Playwright script
|
|
148
|
+
└── runs/ # gitignored — reports + evidence videos
|
|
55
149
|
```
|
|
56
150
|
|
|
57
|
-
|
|
151
|
+
A discovered flow is exactly this readable:
|
|
58
152
|
|
|
59
|
-
|
|
153
|
+
```ts
|
|
154
|
+
// .autoend/flows/choose-pro-plan/flow.mts
|
|
155
|
+
export default async function flow(page, target) {
|
|
156
|
+
await page.goto(new URL('/pricing', target).href);
|
|
157
|
+
await page.getByRole('button', { name: 'Choose Pro' }).click();
|
|
158
|
+
const status = await page.textContent('#plan');
|
|
159
|
+
if (status !== 'Pro plan selected') throw new Error('expected confirmation, got ' + status);
|
|
160
|
+
}
|
|
161
|
+
```
|
|
60
162
|
|
|
61
|
-
|
|
62
|
-
- **A Cursor API key** — exploring agents run on the [Cursor SDK](https://cursor.com/docs/sdk/typescript) ([get a key](https://cursor.com/dashboard))
|
|
63
|
-
- Browsers install on first use via Playwright; [agent-browser](https://github.com/vercel-labs/agent-browser) ships as a dependency
|
|
163
|
+
To retire a flow whose feature you intentionally removed, delete its folder — the in-report Dismiss button is on the roadmap.
|
|
64
164
|
|
|
65
165
|
## Status
|
|
66
166
|
|
|
67
|
-
Early. The architecture is settled, documented, and
|
|
167
|
+
Early and honest about it. The architecture is settled, documented, and verified end-to-end; discovery quality and speed are actively being tuned.
|
|
68
168
|
|
|
69
169
|
- [x] Run pipeline: replay → explore → report artifact
|
|
70
170
|
- [x] Replay engine — parallel headless Playwright with per-flow video
|
|
171
|
+
- [x] Explorer fleet — Cursor agents driving [agent-browser](https://github.com/vercel-labs/agent-browser) in isolated sessions, verify-before-map-entry
|
|
71
172
|
- [x] Report viewer — verdict, tiers, embedded evidence
|
|
72
173
|
- [x] Guided setup (`autoend init`)
|
|
73
|
-
- [x] Cursor SDK harness verified (agents driving agent-browser via shell)
|
|
74
|
-
- [ ] Explorer fleet — flow discovery, hard-failure detection, advisories
|
|
75
174
|
- [ ] Heal-and-notify on replay failures
|
|
76
175
|
- [ ] Report resolution actions (dismiss / reject / suppress)
|
|
77
|
-
- [ ] Fleet auth — login once, share session
|
|
176
|
+
- [ ] Fleet auth — login once with a test account, share session across agents
|
|
177
|
+
- [ ] CI mode — Run artifacts as build artifacts
|
|
78
178
|
|
|
79
179
|
## Under the hood
|
|
80
180
|
|
|
@@ -85,7 +185,7 @@ Every load-bearing decision is written down — start with [`CONTEXT.md`](./CONT
|
|
|
85
185
|
3. [Cursor SDK as the agent harness](./docs/adr/0003-cursor-sdk-as-agent-harness.md)
|
|
86
186
|
4. [Report as a static artifact + thin viewer](./docs/adr/0004-report-as-static-artifact.md)
|
|
87
187
|
|
|
88
|
-
The short version: exploration is LLM-latency-bound, so agents drive the browser through the most token-efficient hands
|
|
188
|
+
The short version: exploration is LLM-latency-bound, so agents drive the browser through the most token-efficient hands measured anywhere (agent-browser, ~200–400 tokens per snapshot). Replay is reliability-bound, so flows persist as plain Playwright with auto-waiting and native video. The report is static files served by a dumb local viewer — portable to CI by construction.
|
|
89
189
|
|
|
90
190
|
## Development
|
|
91
191
|
|
package/dist/cli.js
CHANGED
|
@@ -15,6 +15,8 @@ const USAGE = `Usage:
|
|
|
15
15
|
autoend [target-url] start a Run (falls back to your configured target)
|
|
16
16
|
autoend clean delete all local Run artifacts
|
|
17
17
|
|
|
18
|
+
(via npx, use the scoped name: npx @bonyadnouri/autoend <args>)
|
|
19
|
+
|
|
18
20
|
Options:
|
|
19
21
|
-e, --effort <level> ${EFFORT_LEVELS.join(' | ')} (default: from config, else mid)
|
|
20
22
|
--no-open don't open the Report in a browser
|
|
@@ -61,7 +63,7 @@ async function main() {
|
|
|
61
63
|
await runSetupWizard(repoRoot);
|
|
62
64
|
return;
|
|
63
65
|
}
|
|
64
|
-
console.error('error: no target configured — run `autoend init` or pass a URL');
|
|
66
|
+
console.error('error: no target configured — run `npx @bonyadnouri/autoend init` or pass a URL');
|
|
65
67
|
process.exitCode = 2;
|
|
66
68
|
return;
|
|
67
69
|
}
|
|
@@ -82,7 +84,7 @@ async function main() {
|
|
|
82
84
|
}
|
|
83
85
|
const effort = effortInput;
|
|
84
86
|
if (!process.env.CURSOR_API_KEY) {
|
|
85
|
-
console.warn(pc.yellow('warning: CURSOR_API_KEY not set — exploration will be skipped (run `autoend init`)'));
|
|
87
|
+
console.warn(pc.yellow('warning: CURSOR_API_KEY not set — exploration will be skipped (run `npx @bonyadnouri/autoend init`)'));
|
|
86
88
|
}
|
|
87
89
|
if (!(await handsAvailable())) {
|
|
88
90
|
console.warn(pc.yellow('warning: agent-browser not found on PATH — exploration will be skipped'));
|
|
@@ -102,7 +104,7 @@ async function main() {
|
|
|
102
104
|
console.log(pc.dim(`Artifact: ${artifactDir}`));
|
|
103
105
|
if (values['no-serve'])
|
|
104
106
|
return;
|
|
105
|
-
const viewer = await serveReport(artifactDir, Number(values.port));
|
|
107
|
+
const viewer = await serveReport(artifactDir, Number(values.port), repoRoot);
|
|
106
108
|
console.log(`Report: ${pc.underline(viewer.url)} ${pc.dim('(Ctrl+C to stop)')}`);
|
|
107
109
|
if (!values['no-open'])
|
|
108
110
|
openInBrowser(viewer.url);
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAe,MAAM,iBAAiB,CAAC;AACvE,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD,MAAM,KAAK,GAAG
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type FlowMeta } from '../map/flow-map.js';
|
|
2
2
|
import type { ExplorationBudget } from '../run/effort.js';
|
|
3
|
-
import type { Finding } from '../report/types.js';
|
|
3
|
+
import type { Finding, FlowSnapshot } from '../report/types.js';
|
|
4
4
|
export interface ExploreOptions {
|
|
5
5
|
repoRoot: string;
|
|
6
6
|
target: URL;
|
|
@@ -13,6 +13,8 @@ export interface ExploreOptions {
|
|
|
13
13
|
export interface ExplorationResult {
|
|
14
14
|
discovered: number;
|
|
15
15
|
findings: Finding[];
|
|
16
|
+
/** A snapshot per newly discovered Flow — the Report's receipts (CONTEXT.md: Report). */
|
|
17
|
+
flows: FlowSnapshot[];
|
|
16
18
|
}
|
|
17
19
|
export interface ProposedFlow {
|
|
18
20
|
id: string;
|
|
@@ -38,5 +40,6 @@ export declare function explore(opts: ExploreOptions): Promise<ExplorationResult
|
|
|
38
40
|
export declare function parseExplorerReport(text: string): ExplorerReport | undefined;
|
|
39
41
|
/** Dedupe proposed flows against the map and each other. Exported for tests. */
|
|
40
42
|
export declare function collectProposedFlows(reports: Array<ExplorerReport | undefined>, knownFlows: FlowMeta[]): ProposedFlow[];
|
|
43
|
+
export declare function looksDangerous(script: string): boolean;
|
|
41
44
|
/** Kebab-case a title into a flow id. Exported for tests. */
|
|
42
45
|
export declare function slugify(value: string): string | undefined;
|
package/dist/explore/explorer.js
CHANGED
|
@@ -4,6 +4,7 @@ import { Agent } from '@cursor/sdk';
|
|
|
4
4
|
import { chromium } from 'playwright';
|
|
5
5
|
import { addFlow } from '../map/flow-map.js';
|
|
6
6
|
import { runFlowScript } from '../replay/replay.js';
|
|
7
|
+
import { withoutSensitiveEnv } from '../run/sensitive-env.js';
|
|
7
8
|
import { closeSession } from './hands.js';
|
|
8
9
|
/** Each explorer gets a distinct lens so the fleet doesn't converge on one path. */
|
|
9
10
|
const LENSES = [
|
|
@@ -28,8 +29,8 @@ const GRACE_MS = 60_000;
|
|
|
28
29
|
export async function explore(opts) {
|
|
29
30
|
const apiKey = process.env.CURSOR_API_KEY;
|
|
30
31
|
if (!apiKey) {
|
|
31
|
-
console.warn('exploration skipped: CURSOR_API_KEY not set — run `autoend init`');
|
|
32
|
-
return { discovered: 0, findings: [] };
|
|
32
|
+
console.warn('exploration skipped: CURSOR_API_KEY not set — run `npx @bonyadnouri/autoend init`');
|
|
33
|
+
return { discovered: 0, findings: [], flows: [] };
|
|
33
34
|
}
|
|
34
35
|
const workDir = join(opts.runDir, 'explore');
|
|
35
36
|
await mkdir(workDir, { recursive: true });
|
|
@@ -51,29 +52,42 @@ export async function explore(opts) {
|
|
|
51
52
|
}
|
|
52
53
|
}
|
|
53
54
|
const proposed = collectProposedFlows(reports, opts.knownFlows);
|
|
54
|
-
|
|
55
|
+
const flowSnapshots = [];
|
|
55
56
|
if (proposed.length > 0) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
await
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
57
|
+
// Verify-by-running executes LLM-authored scripts in-process (ADR-0002).
|
|
58
|
+
// Hide secrets from them for the duration (issue #3).
|
|
59
|
+
await withoutSensitiveEnv(async () => {
|
|
60
|
+
const browser = await chromium.launch();
|
|
61
|
+
try {
|
|
62
|
+
for (const flow of proposed) {
|
|
63
|
+
const scriptPath = join(workDir, `${flow.id}.mts`);
|
|
64
|
+
await writeFile(scriptPath, flow.script);
|
|
65
|
+
const outcome = await runFlowScript(browser, scriptPath, opts.target, opts.evidenceDir, `discovered-${flow.id}`);
|
|
66
|
+
if (outcome.ok) {
|
|
67
|
+
const now = new Date().toISOString();
|
|
68
|
+
await addFlow(opts.repoRoot, { id: flow.id, title: flow.title, discoveredAt: now, lastPassedAt: now }, flow.script);
|
|
69
|
+
flowSnapshots.push({
|
|
70
|
+
id: flow.id,
|
|
71
|
+
title: flow.title,
|
|
72
|
+
status: 'discovered',
|
|
73
|
+
discoveredAt: now,
|
|
74
|
+
lastPassedAt: now,
|
|
75
|
+
timeline: outcome.timeline,
|
|
76
|
+
evidence: outcome.evidence,
|
|
77
|
+
durationMs: outcome.durationMs,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
console.warn(`proposed flow "${flow.id}" failed verification and was discarded: ${outcome.error}`);
|
|
82
|
+
}
|
|
69
83
|
}
|
|
70
84
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
85
|
+
finally {
|
|
86
|
+
await browser.close();
|
|
87
|
+
}
|
|
88
|
+
});
|
|
75
89
|
}
|
|
76
|
-
return { discovered, findings };
|
|
90
|
+
return { discovered: flowSnapshots.length, findings, flows: flowSnapshots };
|
|
77
91
|
}
|
|
78
92
|
async function runExplorer(index, apiKey, workDir, opts) {
|
|
79
93
|
const session = `autoend-x${index}`;
|
|
@@ -183,9 +197,13 @@ export function parseExplorerReport(text) {
|
|
|
183
197
|
for (const f of raw.flows) {
|
|
184
198
|
const id = slugify(String(f?.id ?? f?.title ?? ''));
|
|
185
199
|
const script = typeof f?.script === 'string' ? f.script : undefined;
|
|
186
|
-
if (id
|
|
187
|
-
|
|
200
|
+
if (!id || !script || !script.includes('export default'))
|
|
201
|
+
continue;
|
|
202
|
+
if (looksDangerous(script)) {
|
|
203
|
+
console.warn(`proposed flow "${id}" rejected: script uses a disallowed API (issue #3)`);
|
|
204
|
+
continue;
|
|
188
205
|
}
|
|
206
|
+
flows.push({ id, title: String(f.title ?? id), script });
|
|
189
207
|
}
|
|
190
208
|
}
|
|
191
209
|
const findings = [];
|
|
@@ -216,6 +234,27 @@ export function collectProposedFlows(reports, knownFlows) {
|
|
|
216
234
|
}
|
|
217
235
|
return out;
|
|
218
236
|
}
|
|
237
|
+
/**
|
|
238
|
+
* Reject a proposed script that reaches for capabilities a Flow never needs
|
|
239
|
+
* (issue #3). A Flow only drives the Playwright `page`; anything touching the
|
|
240
|
+
* Node runtime, the environment, or dynamic module loading is a red flag —
|
|
241
|
+
* either a bad generation or prompt-injected exfiltration. Cheap denylist,
|
|
242
|
+
* defense-in-depth alongside `withoutSensitiveEnv`; not a substitute for a
|
|
243
|
+
* real sandbox. Exported for tests.
|
|
244
|
+
*/
|
|
245
|
+
const DANGEROUS_SCRIPT_PATTERNS = [
|
|
246
|
+
/\bchild_process\b/,
|
|
247
|
+
/\bnode:/,
|
|
248
|
+
/\brequire\s*\(/,
|
|
249
|
+
/\bimport\s*\(/,
|
|
250
|
+
/\bprocess\s*\.\s*(env|exit|binding|kill|dlopen)/,
|
|
251
|
+
/\beval\s*\(/,
|
|
252
|
+
/\bglobalThis\b/,
|
|
253
|
+
/\bFunction\s*\(/,
|
|
254
|
+
];
|
|
255
|
+
export function looksDangerous(script) {
|
|
256
|
+
return DANGEROUS_SCRIPT_PATTERNS.some((re) => re.test(script));
|
|
257
|
+
}
|
|
219
258
|
/** Kebab-case a title into a flow id. Exported for tests. */
|
|
220
259
|
export function slugify(value) {
|
|
221
260
|
const slug = value
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"explorer.js","sourceRoot":"","sources":["../../src/explore/explorer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,OAAO,EAAiB,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGpD,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"explorer.js","sourceRoot":"","sources":["../../src/explore/explorer.ts"],"names":[],"mappings":"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"}
|
package/dist/map/flow-map.d.ts
CHANGED
|
@@ -10,17 +10,63 @@
|
|
|
10
10
|
* consuming repo's package.json declares, and it dodges test-runner globs
|
|
11
11
|
* (vitest/jest match *.spec.* / *.test.* by default).
|
|
12
12
|
*/
|
|
13
|
+
/** Bump when the persisted shape of flow.json changes; enables migrations. */
|
|
14
|
+
export declare const FLOW_SCHEMA_VERSION = 1;
|
|
15
|
+
/**
|
|
16
|
+
* The script a Flow had *before* its most recent Heal, retained so a Reject
|
|
17
|
+
* (CONTEXT.md) can revert the Heal and refile the Flow as a Regression. A Heal
|
|
18
|
+
* overwrites flow.mts in place, so without this the pre-heal version would
|
|
19
|
+
* survive only in git history — unreachable from the Report.
|
|
20
|
+
*/
|
|
21
|
+
export interface HealRecord {
|
|
22
|
+
/** flow.mts exactly as it was immediately before the Heal. */
|
|
23
|
+
previousScript: string;
|
|
24
|
+
/** ISO timestamp of the Heal. */
|
|
25
|
+
healedAt: string;
|
|
26
|
+
/** The Run that produced the Heal, if known. */
|
|
27
|
+
healedInRun?: string;
|
|
28
|
+
}
|
|
13
29
|
export interface FlowMeta {
|
|
30
|
+
/** Schema version of this flow.json (absent = pre-versioning, treat as 1). */
|
|
31
|
+
schemaVersion?: number;
|
|
14
32
|
id: string;
|
|
15
33
|
title: string;
|
|
16
34
|
discoveredAt: string;
|
|
17
35
|
lastPassedAt?: string;
|
|
18
36
|
/** Fingerprints of Suppressed Advisories attached to this Flow. */
|
|
19
37
|
suppressedAdvisories?: string[];
|
|
38
|
+
/** Present iff the Flow's script was Healed; consumed by Reject. */
|
|
39
|
+
heal?: HealRecord;
|
|
20
40
|
}
|
|
21
41
|
export declare function flowMapDir(repoRoot: string): string;
|
|
42
|
+
/**
|
|
43
|
+
* Read every Flow's metadata. A single malformed or incomplete flow directory
|
|
44
|
+
* (missing/partial flow.json — e.g. an interrupted write, a merge conflict, a
|
|
45
|
+
* manually-created folder) is skipped with a warning rather than aborting the
|
|
46
|
+
* whole Run: the Flow Map is human-editable and lives in the user's repo, so it
|
|
47
|
+
* must never fail closed on one bad entry.
|
|
48
|
+
*/
|
|
22
49
|
export declare function listFlows(repoRoot: string): Promise<FlowMeta[]>;
|
|
23
50
|
/** Update a Flow's metadata in place (e.g. lastPassedAt after a green replay). */
|
|
24
51
|
export declare function saveFlowMeta(repoRoot: string, meta: FlowMeta): Promise<void>;
|
|
52
|
+
/**
|
|
53
|
+
* Dismiss (CONTEXT.md): permanent map surgery — the Flow leaves the Map. A
|
|
54
|
+
* plain file edit performed by the viewer server (ADR-0004).
|
|
55
|
+
*/
|
|
56
|
+
export declare function removeFlow(repoRoot: string, flowId: string): Promise<void>;
|
|
25
57
|
/** Flows enter the map automatically on first successful execution (ADR-0001). */
|
|
26
58
|
export declare function addFlow(repoRoot: string, meta: FlowMeta, playwrightScript: string): Promise<void>;
|
|
59
|
+
/** Read a Flow's executable script (flow.mts). */
|
|
60
|
+
export declare function readFlowScript(repoRoot: string, id: string): Promise<string>;
|
|
61
|
+
/**
|
|
62
|
+
* Record a Heal (CONTEXT.md): overwrite flow.mts with the rewritten script
|
|
63
|
+
* while retaining the pre-heal version on the Flow's metadata, so a later
|
|
64
|
+
* Reject can revert. Returns the updated metadata.
|
|
65
|
+
*/
|
|
66
|
+
export declare function healFlow(repoRoot: string, meta: FlowMeta, healedScript: string, healedInRun?: string): Promise<FlowMeta>;
|
|
67
|
+
/**
|
|
68
|
+
* Reject a Heal (CONTEXT.md): restore the pre-heal script and clear the heal
|
|
69
|
+
* record. Returns the reverted metadata. Throws if the Flow has no Heal to
|
|
70
|
+
* revert, so callers can surface a clear error.
|
|
71
|
+
*/
|
|
72
|
+
export declare function revertHeal(repoRoot: string, meta: FlowMeta): Promise<FlowMeta>;
|