@pcamarajr/scout 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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Pedro Camara Junior
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,188 @@
1
+ # 🔭 Scout
2
+
3
+ Self-healing browser QA. Scenarios written in **natural language**, verified by an **AI agent** in a real browser (Playwright), and recorded as a **deterministic script** that runs cheaply and quickly in CI — AI only kicks in when the script breaks.
4
+
5
+ > Status: functional POC.
6
+
7
+ ## Why not just Playwright? Why not just AI?
8
+
9
+ | | Pure Playwright | Pure AI | Scout (hybrid) |
10
+ |---|---|---|---|
11
+ | Authoring | expensive (code + selectors) | cheap (1 sentence) | cheap (1 sentence) |
12
+ | Cost per CI run | ~zero | $$ + slow | ~zero (replay) |
13
+ | Resilience to UI changes | breaks | adapts | breaks → AI re-verifies and re-records |
14
+ | Judges behavior ("paywall MUST NOT appear") | only what was coded | yes | yes |
15
+
16
+ **Scenario lifecycle:**
17
+
18
+ ```
19
+ scout create "Paywall free" -c "Open ep 3 of series X without login; paywall should appear with signup CTA"
20
+
21
+
22
+ scout go ──── 1st run: AI agent runs in browser, judges (verified/failed/partial/blocked)
23
+ │ and records .scout/scripts/paywall-free.json (deterministic steps + assertions)
24
+
25
+ CI / subsequent runs: pure Playwright replay, no LLM, seconds per scenario
26
+
27
+
28
+ UI changed and replay broke? ── AI re-runs, re-judges, re-records the script (self-healing)
29
+ (`--no-heal` disables this, e.g. in CI without API key)
30
+ ```
31
+
32
+ ## Quickstart
33
+
34
+ ```bash
35
+ npm install @pcamarajr/scout # or npm link during POC
36
+ npx playwright install chromium # browser engine
37
+
38
+ scout init # creates scout.config.json + .scout/
39
+ scout create "Login with Google" \
40
+ -c "On logged-out home, click Sign In; login page should show Google and email/password options" \
41
+ -p anon
42
+ scout go # 1st run = AI (requires Anthropic credentials)
43
+ scout go # subsequent runs = deterministic replay
44
+ scout report # markdown ready to embed in PR body
45
+ ```
46
+
47
+ ### AI runner credentials
48
+
49
+ - **Local:** if you use Claude Code, the Agent SDK reuses your machine credentials — zero config.
50
+ - **CI/headless:** export `ANTHROPIC_API_KEY`. The SDK is self-contained (Claude Code CLI not required).
51
+ - Deterministic replay **does not use LLM** — in CI without a key, use `scout go --no-heal` (failure becomes ❌ in the report instead of healing).
52
+
53
+ ## Auth profiles (storageState)
54
+
55
+ Authenticated flows use sessions captured once per environment:
56
+
57
+ ```jsonc
58
+ // scout.config.json
59
+ {
60
+ "baseUrl": "http://localhost:3000",
61
+ "model": "claude-sonnet-4-6",
62
+ "profiles": {
63
+ "anon": { "description": "Logged-out session" },
64
+ "subscriber": { "description": "User with active subscription", "env": ["QA_SUB_EMAIL", "QA_SUB_PASSWORD"] },
65
+ "free-no-coins": { "description": "Free user with no coin balance" }
66
+ }
67
+ }
68
+ ```
69
+
70
+ ```bash
71
+ scout login subscriber # opens headed browser, log in, press Enter → saves .scout/state/subscriber.json (gitignored)
72
+ ```
73
+
74
+ In CI, generate the storageState in a setup step (login via script) or let the agent log in using `$ENV:QA_SUB_EMAIL` / `$ENV:QA_SUB_PASSWORD` — placeholders are resolved from the environment at runtime; **secrets never enter the committed script or pass through the LLM**.
75
+
76
+ ## Worktrees and environments
77
+
78
+ Everything is relative to the project directory and the target comes from env — two worktrees run in parallel without colliding:
79
+
80
+ ```bash
81
+ SCOUT_BASE_URL=http://localhost:3001 scout go # worktree B pointing to a different port
82
+ SCOUT_BASE_URL=https://staging.myapp.com scout go --no-heal # against staging
83
+ ```
84
+
85
+ - `.scout/scenarios.json` and `.scout/scripts/` are **committed** — the suite travels with the branch.
86
+ - `.scout/runs/` and `.scout/state/` are **gitignored** — artifacts and sessions are per-machine.
87
+
88
+ ## Per-run artifacts
89
+
90
+ Each run records in `.scout/runs/<timestamp>-<slug>/`:
91
+
92
+ | File | What |
93
+ |---|---|
94
+ | `trace.zip` | Playwright trace — screenshots, DOM snapshots, network, console (`npx playwright show-trace trace.zip`) |
95
+ | `*.png` | Evidence screenshots (captured by the agent or at the end of replay/failure) |
96
+ | `report.md` | Verdict + reason + recorded script + evidence |
97
+ | `result.json` | Structured result (consumable by automation) |
98
+ | `transcript.md` | Agent reasoning (AI runs only) |
99
+
100
+ ## MCP — usage by coding agents (Claude Code, cloud sessions)
101
+
102
+ Scout's main flow is to be called **by the agent that developed the feature**: the agent writes the scenario (in NL — never the script; the script is born from the verified execution) and triggers the verification.
103
+
104
+ ```jsonc
105
+ // .mcp.json of the target project
106
+ {
107
+ "mcpServers": {
108
+ "scout": { "command": "npx", "args": ["scout", "mcp"] }
109
+ }
110
+ }
111
+ ```
112
+
113
+ Exposed tools: `scout_list_scenarios`, `scout_create_scenario`, `scout_run`, `scout_report`, `scout_get_run_report`.
114
+
115
+ ## CI (GitHub Actions)
116
+
117
+ ```yaml
118
+ qa-browser:
119
+ runs-on: ubuntu-latest
120
+ steps:
121
+ - uses: actions/checkout@v4
122
+ - uses: actions/setup-node@v4
123
+ with: { node-version: 24 }
124
+ - run: npm ci && npx playwright install --with-deps chromium
125
+ - run: npm run start:test-server & # app running
126
+ - run: npx scout go --no-heal # pure replay, no LLM, exit 1 on failure
127
+ env: { SCOUT_BASE_URL: "http://localhost:3000" }
128
+ - run: npx scout report >> "$GITHUB_STEP_SUMMARY"
129
+ if: always()
130
+ - uses: actions/upload-artifact@v4 # traces + screenshots in the action run
131
+ if: always()
132
+ with: { name: scout-runs, path: .scout/runs/ }
133
+ ```
134
+
135
+ With heal in CI: add `ANTHROPIC_API_KEY` and switch to `npx scout go` — when the UI changes legitimately, the job re-records the script and the `.scout/scripts/` diff shows up for commit (e.g. via PR bot).
136
+
137
+ ## Full CLI
138
+
139
+ ```
140
+ scout init # bootstrap in the project
141
+ scout create <name> -c <scenario> [-p profile] [-n notes]
142
+ scout list # scenarios + status + 📜 if cached script exists
143
+ scout go [-s id|slug] [--ai] [--no-heal] [--headed]
144
+ scout report # markdown suite summary
145
+ scout login <profile> # capture storageState in headed browser
146
+ scout mcp # MCP server stdio
147
+ ```
148
+
149
+ ## Verdicts
150
+
151
+ | | Meaning |
152
+ |---|---|
153
+ | ✅ `verified` | All expected behavior confirmed by assertions |
154
+ | ❌ `failed` | Broken behavior (the reason says exactly what) |
155
+ | ⚠️ `partial` | Partially verified |
156
+ | 🚫 `blocked` | Couldn't reach the flow (app down, login broken) |
157
+
158
+ ## Architecture
159
+
160
+ ```
161
+ src/
162
+ ├── cli.ts # commander CLI
163
+ ├── engine.ts # orchestrates: replay → (failed?) → AI heal → re-record
164
+ ├── config.ts # scout.config.json + env overrides
165
+ ├── store.ts # .scout/ (scenarios, scripts, runs)
166
+ ├── report.ts # per-run markdown + suite summary
167
+ ├── runner/
168
+ │ ├── browser.ts # Playwright wrapper: snapshot with refs, trace, screenshots,
169
+ │ │ # ref→locator resolution (getByRole when unique, CSS fallback)
170
+ │ ├── ai-runner.ts # Claude Agent SDK + in-process browser tools; records steps
171
+ │ └── script-runner.ts # deterministic step replay
172
+ └── mcp/server.ts # MCP interface (stdio)
173
+ ```
174
+
175
+ Design decisions:
176
+
177
+ - **The agent never writes test code.** It acts in the browser; the script is recorded from actions that actually worked (`getByRole` + accessible name when unique on the page, CSS path as fallback). Eliminates hallucinated selectors.
178
+ - **Assertions are tools.** The agent registers each expectation via `browser_assert` — that's what makes the replay a real test, not just a click macro.
179
+ - **Trace > video.** Playwright's trace.zip gives per-action screenshots, DOM, network, and console in a single navigable artifact. Raw video stays as an optional enhancement.
180
+ - **No server/dashboard.** State is the filesystem in the target repo; report is markdown. Pluggable into any project with `npm i` + 2 files.
181
+
182
+ ## Known POC limitations
183
+
184
+ - Replay runs sequentially (no sharding/parallelism).
185
+ - Snapshot covers interactive elements + text; canvas/video are verified indirectly (element presence, surrounding UI state).
186
+ - Flows that depend on reading email are not verifiable — covers UI + redirects.
187
+ - Heal re-records the script locally; committing the updated script is manual (intentional: script diff is reviewable).
188
+ - Fixed mobile viewport (390×844) — multi-viewport is a simple enhancement (per-scenario variant).
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,157 @@
1
+ #!/usr/bin/env node
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import readline from "node:readline";
5
+ import { Command } from "commander";
6
+ import { chromium } from "playwright";
7
+ import { CONFIG_FILE, SCOUT_DIR, loadConfig } from "./config.js";
8
+ import { runScenario } from "./engine.js";
9
+ import { renderSummary } from "./report.js";
10
+ import { Store } from "./store.js";
11
+ // Rejeições fora da cadeia de await (SDK/Playwright em subprocesso) não podem
12
+ // derrubar a suíte inteira — logar e deixar o cenário corrente falhar sozinho.
13
+ process.on("unhandledRejection", (reason) => {
14
+ console.error(`\n⚠ unhandled rejection: ${reason instanceof Error ? reason.message : String(reason)}`);
15
+ });
16
+ const program = new Command();
17
+ program
18
+ .name("scout")
19
+ .description("Self-healing browser QA — natural-language scenarios, deterministic replay in CI")
20
+ .version("0.1.0");
21
+ program
22
+ .command("init")
23
+ .description("Creates scout.config.json and .scout/ in the current project")
24
+ .action(() => {
25
+ const store = new Store();
26
+ store.init();
27
+ const configPath = path.join(process.cwd(), CONFIG_FILE);
28
+ if (!fs.existsSync(configPath)) {
29
+ fs.writeFileSync(configPath, JSON.stringify({
30
+ baseUrl: "http://localhost:3000",
31
+ model: "claude-sonnet-4-6",
32
+ headless: true,
33
+ maxTurns: 40,
34
+ locale: "pt-BR",
35
+ profiles: {
36
+ anon: { description: "Logged-out session" },
37
+ },
38
+ }, null, 2) + "\n");
39
+ console.log(`✓ ${CONFIG_FILE} created — adjust baseUrl and profiles.`);
40
+ }
41
+ console.log(`✓ ${SCOUT_DIR}/ initialized.`);
42
+ });
43
+ program
44
+ .command("create <name>")
45
+ .description("Creates a verification scenario")
46
+ .requiredOption("-c, --scenario <text>", "Scenario in natural language: flow + expected behavior")
47
+ .option("-p, --profile <profile>", "Auth profile (from scout.config.json)")
48
+ .option("-n, --notes <notes>", "Extra notes for the agent")
49
+ .action((name, opts) => {
50
+ const store = new Store();
51
+ if (!store.exists()) {
52
+ console.error("Run `scout init` first.");
53
+ process.exit(1);
54
+ }
55
+ const scenario = store.addScenario({
56
+ name,
57
+ scenario: opts.scenario,
58
+ profile: opts.profile,
59
+ notes: opts.notes,
60
+ });
61
+ console.log(`✓ Scenario #${scenario.id} created: ${scenario.slug}`);
62
+ });
63
+ program
64
+ .command("list")
65
+ .description("Lists scenarios and status")
66
+ .action(() => {
67
+ const store = new Store();
68
+ for (const s of store.listScenarios()) {
69
+ const script = store.loadSteps(s.slug) ? "📜" : " ";
70
+ console.log(`#${s.id} ${script} [${s.status.padEnd(8)}] ${s.name} (${s.profile ?? "anonymous"})`);
71
+ }
72
+ });
73
+ program
74
+ .command("go")
75
+ .description("Runs scenarios: cached script replay; AI on first run or when the script breaks")
76
+ .option("-s, --scenario <idOrSlug>", "Run a single scenario")
77
+ .option("--ai", "Force AI-driven run (re-records the script)", false)
78
+ .option("--no-heal", "Do not fall back to AI when replay fails (cheap CI)")
79
+ .option("--headed", "Visible browser (local debug)", false)
80
+ .action(async (opts) => {
81
+ const store = new Store();
82
+ const config = loadConfig();
83
+ const all = store.listScenarios();
84
+ const targets = opts.scenario
85
+ ? all.filter((s) => s.id === Number(opts.scenario) || s.slug === opts.scenario)
86
+ : all;
87
+ if (!targets.length) {
88
+ console.error(opts.scenario ? `Scenario "${opts.scenario}" not found.` : "No scenarios. Use `scout create`.");
89
+ process.exit(1);
90
+ }
91
+ let failed = 0;
92
+ for (const scenario of targets) {
93
+ process.stdout.write(`▶ #${scenario.id} ${scenario.name} ... `);
94
+ try {
95
+ const result = await runScenario(store, scenario, config, {
96
+ forceAi: opts.ai,
97
+ heal: opts.heal,
98
+ headed: opts.headed,
99
+ });
100
+ const icon = result.verdict === "verified" ? "✅" : result.verdict === "partial" ? "⚠️" : "❌";
101
+ console.log(`${icon} ${result.verdict} [${result.mode}${result.healed ? "+heal" : ""}] ${(result.durationMs / 1000).toFixed(1)}s`);
102
+ if (result.verdict !== "verified") {
103
+ console.log(` ↳ ${result.reason}`);
104
+ failed++;
105
+ }
106
+ console.log(` ↳ artifacts: ${path.relative(process.cwd(), result.runDir)}`);
107
+ }
108
+ catch (error) {
109
+ console.log(`💥 error: ${error instanceof Error ? error.message : error}`);
110
+ failed++;
111
+ }
112
+ }
113
+ process.exit(failed ? 1 : 0);
114
+ });
115
+ program
116
+ .command("report")
117
+ .description("Prints the suite summary in markdown (embeddable in PR)")
118
+ .action(() => {
119
+ const store = new Store();
120
+ console.log(renderSummary(store.listScenarios(), store.latestRuns()));
121
+ });
122
+ program
123
+ .command("login <profile>")
124
+ .description("Opens headed browser to capture a profile session (storageState)")
125
+ .action(async (profileName) => {
126
+ const config = loadConfig();
127
+ if (!config.profiles[profileName]) {
128
+ console.error(`Profile "${profileName}" does not exist in ${CONFIG_FILE}. Add it first.`);
129
+ process.exit(1);
130
+ }
131
+ const statePath = path.resolve(config.profiles[profileName].storageState ?? path.join(SCOUT_DIR, "state", `${profileName}.json`));
132
+ fs.mkdirSync(path.dirname(statePath), { recursive: true });
133
+ const browser = await chromium.launch({ headless: false });
134
+ const context = await browser.newContext({ locale: config.locale ?? "pt-BR" });
135
+ const page = await context.newPage();
136
+ await page.goto(config.baseUrl);
137
+ console.log(`\nLog in as "${profileName}" in the opened browser.`);
138
+ await new Promise((resolve) => {
139
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
140
+ rl.question("Press Enter when done... ", () => {
141
+ rl.close();
142
+ resolve();
143
+ });
144
+ });
145
+ await context.storageState({ path: statePath });
146
+ await browser.close();
147
+ console.log(`✓ Session saved to ${statePath} (gitignored).`);
148
+ });
149
+ program
150
+ .command("mcp")
151
+ .description("Starts the MCP server (stdio) — for Claude Code and cloud coding sessions")
152
+ .action(async () => {
153
+ const { startMcpServer } = await import("./mcp/server.js");
154
+ await startMcpServer();
155
+ });
156
+ program.parseAsync();
157
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ export interface ScoutProfile {
2
+ /** Shown to the AI agent so it knows what kind of session this is */
3
+ description?: string;
4
+ /**
5
+ * Playwright storageState JSON path. Defaults to .scout/state/<name>.json
6
+ * (created by `scout login <name>`). Gitignored — each env captures its own.
7
+ */
8
+ storageState?: string;
9
+ /**
10
+ * Env var names the agent may reference as $ENV:NAME when filling forms
11
+ * (e.g. credentials for login flows). Values never reach the LLM.
12
+ */
13
+ env?: string[];
14
+ }
15
+ export interface ScoutConfig {
16
+ /** Target app. Override per worktree/CI with SCOUT_BASE_URL. */
17
+ baseUrl: string;
18
+ /** Model for AI runs. Override with SCOUT_MODEL. */
19
+ model: string;
20
+ /** Headless by default; SCOUT_HEADED=1 or --headed for local debugging. */
21
+ headless: boolean;
22
+ /** Max agent turns per AI run */
23
+ maxTurns: number;
24
+ /** Locale forced on the browser context */
25
+ locale?: string;
26
+ profiles: Record<string, ScoutProfile>;
27
+ }
28
+ export declare const CONFIG_FILE = "scout.config.json";
29
+ export declare const SCOUT_DIR = ".scout";
30
+ export declare function loadConfig(cwd?: string): ScoutConfig;
31
+ export declare function resolveStorageState(profileName: string | undefined, config: ScoutConfig, cwd?: string): string | undefined;
package/dist/config.js ADDED
@@ -0,0 +1,47 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ export const CONFIG_FILE = "scout.config.json";
4
+ export const SCOUT_DIR = ".scout";
5
+ const DEFAULTS = {
6
+ baseUrl: "http://localhost:3000",
7
+ model: "claude-sonnet-4-6",
8
+ headless: true,
9
+ maxTurns: 40,
10
+ locale: "pt-BR",
11
+ profiles: {},
12
+ };
13
+ export function loadConfig(cwd = process.cwd()) {
14
+ const file = path.join(cwd, CONFIG_FILE);
15
+ let fromFile = {};
16
+ if (fs.existsSync(file)) {
17
+ fromFile = JSON.parse(fs.readFileSync(file, "utf8"));
18
+ }
19
+ const merged = { ...DEFAULTS, ...fromFile };
20
+ if (process.env.SCOUT_BASE_URL)
21
+ merged.baseUrl = process.env.SCOUT_BASE_URL;
22
+ if (process.env.SCOUT_MODEL)
23
+ merged.model = process.env.SCOUT_MODEL;
24
+ if (process.env.SCOUT_HEADED === "1")
25
+ merged.headless = false;
26
+ return merged;
27
+ }
28
+ export function resolveStorageState(profileName, config, cwd = process.cwd()) {
29
+ if (!profileName)
30
+ return undefined;
31
+ const profile = config.profiles[profileName];
32
+ if (!profile) {
33
+ throw new Error(`Profile "${profileName}" não existe no ${CONFIG_FILE}. Profiles: ${Object.keys(config.profiles).join(", ") || "(nenhum)"}`);
34
+ }
35
+ const statePath = path.resolve(cwd, profile.storageState ?? path.join(SCOUT_DIR, "state", `${profileName}.json`));
36
+ if (!fs.existsSync(statePath)) {
37
+ // Caminho explícito configurado e ausente = erro do usuário.
38
+ // Caminho default ausente = contexto fresh — cobre profiles logged-out
39
+ // e profiles que logam dentro do cenário via $ENV.
40
+ if (profile.storageState) {
41
+ throw new Error(`storageState do profile "${profileName}" não encontrado em ${statePath} (caminho configurado em ${CONFIG_FILE}). Rode: scout login ${profileName}`);
42
+ }
43
+ return undefined;
44
+ }
45
+ return statePath;
46
+ }
47
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AA+B7B,MAAM,CAAC,MAAM,WAAW,GAAG,mBAAmB,CAAC;AAC/C,MAAM,CAAC,MAAM,SAAS,GAAG,QAAQ,CAAC;AAElC,MAAM,QAAQ,GAAgB;IAC5B,OAAO,EAAE,uBAAuB;IAChC,KAAK,EAAE,mBAAmB;IAC1B,QAAQ,EAAE,IAAI;IACd,QAAQ,EAAE,EAAE;IACZ,MAAM,EAAE,OAAO;IACf,QAAQ,EAAE,EAAE;CACb,CAAC;AAEF,MAAM,UAAU,UAAU,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE;IAC5C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;IACzC,IAAI,QAAQ,GAAyB,EAAE,CAAC;IACxC,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACxB,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;IACvD,CAAC;IACD,MAAM,MAAM,GAAgB,EAAE,GAAG,QAAQ,EAAE,GAAG,QAAQ,EAAE,CAAC;IACzD,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc;QAAE,MAAM,CAAC,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;IAC5E,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW;QAAE,MAAM,CAAC,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC;IACpE,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,KAAK,GAAG;QAAE,MAAM,CAAC,QAAQ,GAAG,KAAK,CAAC;IAC9D,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,mBAAmB,CACjC,WAA+B,EAC/B,MAAmB,EACnB,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE;IAEnB,IAAI,CAAC,WAAW;QAAE,OAAO,SAAS,CAAC;IACnC,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IAC7C,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CACb,YAAY,WAAW,mBAAmB,WAAW,eAAe,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,UAAU,EAAE,CAC5H,CAAC;IACJ,CAAC;IACD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAC5B,GAAG,EACH,OAAO,CAAC,YAAY,IAAI,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,EAAE,GAAG,WAAW,OAAO,CAAC,CAC7E,CAAC;IACF,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC9B,6DAA6D;QAC7D,uEAAuE;QACvE,mDAAmD;QACnD,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CACb,4BAA4B,WAAW,uBAAuB,SAAS,4BAA4B,WAAW,wBAAwB,WAAW,EAAE,CACpJ,CAAC;QACJ,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1,20 @@
1
+ import { type ScoutConfig } from "./config.js";
2
+ import { Store } from "./store.js";
3
+ import { describeStep } from "./runner/script-runner.js";
4
+ import type { RunResult, Scenario } from "./types.js";
5
+ export interface RunOptions {
6
+ /** Skip the cached script and force an AI run (re-record) */
7
+ forceAi?: boolean;
8
+ /** When the cached script fails, re-verify with the AI agent (default true) */
9
+ heal?: boolean;
10
+ headed?: boolean;
11
+ }
12
+ /**
13
+ * Core run logic shared by CLI and MCP server:
14
+ * cached script exists? → replay (no LLM)
15
+ * → pass → verified
16
+ * → fail + heal → AI run; if verified, re-record the script
17
+ * no script / --ai → AI run; if verified, record the script
18
+ */
19
+ export declare function runScenario(store: Store, scenario: Scenario, config?: ScoutConfig, opts?: RunOptions): Promise<RunResult>;
20
+ export { describeStep };
package/dist/engine.js ADDED
@@ -0,0 +1,139 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { loadConfig, resolveStorageState } from "./config.js";
4
+ import { renderRunReport } from "./report.js";
5
+ import { BrowserSession } from "./runner/browser.js";
6
+ import { runWithAgent } from "./runner/ai-runner.js";
7
+ import { describeStep, replaySteps } from "./runner/script-runner.js";
8
+ function aiAvailable() {
9
+ return Boolean(process.env.ANTHROPIC_API_KEY ||
10
+ process.env.CLAUDE_CODE_OAUTH_TOKEN ||
11
+ fs.existsSync(path.join(process.env.HOME ?? "", ".claude")));
12
+ }
13
+ /**
14
+ * Core run logic shared by CLI and MCP server:
15
+ * cached script exists? → replay (no LLM)
16
+ * → pass → verified
17
+ * → fail + heal → AI run; if verified, re-record the script
18
+ * no script / --ai → AI run; if verified, record the script
19
+ */
20
+ export async function runScenario(store, scenario, config = loadConfig(), opts = {}) {
21
+ const heal = opts.heal ?? true;
22
+ const startedAt = new Date().toISOString();
23
+ const start = Date.now();
24
+ const runDir = store.newRunDir(scenario.slug);
25
+ const storageState = resolveStorageState(scenario.profile, config);
26
+ const cached = opts.forceAi ? undefined : store.loadSteps(scenario.slug);
27
+ const launch = () => BrowserSession.launch({
28
+ baseUrl: config.baseUrl,
29
+ headless: opts.headed ? false : config.headless,
30
+ storageState,
31
+ locale: config.locale,
32
+ runDir,
33
+ });
34
+ let result;
35
+ if (cached?.length) {
36
+ const session = await launch();
37
+ const replay = await replaySteps(session, cached);
38
+ const trace = await session.close();
39
+ if (replay.passed) {
40
+ result = {
41
+ scenarioId: scenario.id,
42
+ slug: scenario.slug,
43
+ mode: "replay",
44
+ verdict: "verified",
45
+ reason: `Deterministic script passed (${cached.length} steps).`,
46
+ stepCount: cached.length,
47
+ runDir,
48
+ startedAt,
49
+ durationMs: Date.now() - start,
50
+ screenshots: session.screenshots,
51
+ trace,
52
+ };
53
+ }
54
+ else if (heal && aiAvailable()) {
55
+ // cached script broke — re-verify with the agent and re-record
56
+ const aiResult = await runAi(scenario, config, store, runDir, storageState, opts);
57
+ result = {
58
+ ...aiResult,
59
+ healed: true,
60
+ reason: `Cached script broke at step ${replay.failedIndex + 1} (${replay.failedStep}: ${replay.error}). Re-verified by AI: ${aiResult.reason}`,
61
+ startedAt,
62
+ durationMs: Date.now() - start,
63
+ };
64
+ }
65
+ else {
66
+ result = {
67
+ scenarioId: scenario.id,
68
+ slug: scenario.slug,
69
+ mode: "replay",
70
+ verdict: "failed",
71
+ reason: `Step ${replay.failedIndex + 1} failed: ${replay.failedStep} — ${replay.error}${heal ? " (AI heal unavailable: no Anthropic credentials)" : " (heal disabled)"}`,
72
+ stepCount: cached.length,
73
+ failedStep: replay.failedStep,
74
+ runDir,
75
+ startedAt,
76
+ durationMs: Date.now() - start,
77
+ screenshots: session.screenshots,
78
+ trace,
79
+ };
80
+ }
81
+ }
82
+ else {
83
+ if (!aiAvailable()) {
84
+ throw new Error(`Scenario "${scenario.slug}" has no recorded script and no Anthropic credentials (ANTHROPIC_API_KEY) for the initial AI run.`);
85
+ }
86
+ const aiResult = await runAi(scenario, config, store, runDir, storageState, opts);
87
+ result = { ...aiResult, startedAt, durationMs: Date.now() - start };
88
+ }
89
+ store.saveRunResult(result);
90
+ store.updateScenario(scenario.id, { status: result.verdict, lastRun: result.startedAt });
91
+ fs.writeFileSync(path.join(runDir, "report.md"), renderRunReport(result, scenario, store.loadSteps(scenario.slug)));
92
+ return result;
93
+ }
94
+ async function runAi(scenario, config, store, runDir, storageState, opts) {
95
+ const startedAt = new Date().toISOString();
96
+ const start = Date.now();
97
+ const session = await BrowserSession.launch({
98
+ baseUrl: config.baseUrl,
99
+ headless: opts.headed ? false : config.headless,
100
+ storageState,
101
+ locale: config.locale,
102
+ runDir,
103
+ });
104
+ let outcome;
105
+ try {
106
+ outcome = await runWithAgent(session, scenario, config);
107
+ }
108
+ finally {
109
+ await session.screenshot("final-state").catch(() => { });
110
+ }
111
+ const trace = await session.close();
112
+ fs.writeFileSync(path.join(runDir, "transcript.md"), outcome.transcript.join("\n\n---\n\n"));
113
+ if (outcome.verdict === "verified" && outcome.steps.length) {
114
+ store.saveSteps(scenario.slug, pruneSteps(outcome.steps));
115
+ }
116
+ return {
117
+ scenarioId: scenario.id,
118
+ slug: scenario.slug,
119
+ mode: "ai",
120
+ verdict: outcome.verdict,
121
+ reason: outcome.reason,
122
+ stepCount: outcome.steps.length,
123
+ runDir,
124
+ startedAt,
125
+ durationMs: Date.now() - start,
126
+ screenshots: session.screenshots,
127
+ trace,
128
+ };
129
+ }
130
+ /**
131
+ * Cleans the recorded trace before caching: drops exploratory snapshots the
132
+ * agent took that produced no action (they're not steps) — currently steps
133
+ * are only recorded on success, so this is a hook for future pruning rules.
134
+ */
135
+ function pruneSteps(steps) {
136
+ return steps;
137
+ }
138
+ export { describeStep };
139
+ //# sourceMappingURL=engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAoB,MAAM,aAAa,CAAC;AAChF,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAWtE,SAAS,WAAW;IAClB,OAAO,OAAO,CACZ,OAAO,CAAC,GAAG,CAAC,iBAAiB;QAC3B,OAAO,CAAC,GAAG,CAAC,uBAAuB;QACnC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,EAAE,SAAS,CAAC,CAAC,CAC9D,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,KAAY,EACZ,QAAkB,EAClB,SAAsB,UAAU,EAAE,EAClC,OAAmB,EAAE;IAErB,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC;IAC/B,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC3C,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACzB,MAAM,MAAM,GAAG,KAAK,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC9C,MAAM,YAAY,GAAG,mBAAmB,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IACnE,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IAEzE,MAAM,MAAM,GAAG,GAAG,EAAE,CAClB,cAAc,CAAC,MAAM,CAAC;QACpB,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ;QAC/C,YAAY;QACZ,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,MAAM;KACP,CAAC,CAAC;IAEL,IAAI,MAAiB,CAAC;IAEtB,IAAI,MAAM,EAAE,MAAM,EAAE,CAAC;QACnB,MAAM,OAAO,GAAG,MAAM,MAAM,EAAE,CAAC;QAC/B,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAClD,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;QAEpC,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YAClB,MAAM,GAAG;gBACP,UAAU,EAAE,QAAQ,CAAC,EAAE;gBACvB,IAAI,EAAE,QAAQ,CAAC,IAAI;gBACnB,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,UAAU;gBACnB,MAAM,EAAE,gCAAgC,MAAM,CAAC,MAAM,UAAU;gBAC/D,SAAS,EAAE,MAAM,CAAC,MAAM;gBACxB,MAAM;gBACN,SAAS;gBACT,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK;gBAC9B,WAAW,EAAE,OAAO,CAAC,WAAW;gBAChC,KAAK;aACN,CAAC;QACJ,CAAC;aAAM,IAAI,IAAI,IAAI,WAAW,EAAE,EAAE,CAAC;YACjC,+DAA+D;YAC/D,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,IAAI,CAAC,CAAC;YAClF,MAAM,GAAG;gBACP,GAAG,QAAQ;gBACX,MAAM,EAAE,IAAI;gBACZ,MAAM,EAAE,+BAA+B,MAAM,CAAC,WAAY,GAAG,CAAC,KAAK,MAAM,CAAC,UAAU,KAAK,MAAM,CAAC,KAAK,yBAAyB,QAAQ,CAAC,MAAM,EAAE;gBAC/I,SAAS;gBACT,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK;aAC/B,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,MAAM,GAAG;gBACP,UAAU,EAAE,QAAQ,CAAC,EAAE;gBACvB,IAAI,EAAE,QAAQ,CAAC,IAAI;gBACnB,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,QAAQ;gBACjB,MAAM,EAAE,QAAQ,MAAM,CAAC,WAAY,GAAG,CAAC,YAAY,MAAM,CAAC,UAAU,MAAM,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,kDAAkD,CAAC,CAAC,CAAC,kBAAkB,EAAE;gBACzK,SAAS,EAAE,MAAM,CAAC,MAAM;gBACxB,UAAU,EAAE,MAAM,CAAC,UAAU;gBAC7B,MAAM;gBACN,SAAS;gBACT,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK;gBAC9B,WAAW,EAAE,OAAO,CAAC,WAAW;gBAChC,KAAK;aACN,CAAC;QACJ,CAAC;IACH,CAAC;SAAM,CAAC;QACN,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;YACnB,MAAM,IAAI,KAAK,CACb,aAAa,QAAQ,CAAC,IAAI,mGAAmG,CAC9H,CAAC;QACJ,CAAC;QACD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,IAAI,CAAC,CAAC;QAClF,MAAM,GAAG,EAAE,GAAG,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,CAAC;IACtE,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;IAC5B,KAAK,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC;IACzF,EAAE,CAAC,aAAa,CACd,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,EAC9B,eAAe,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAClE,CAAC;IACF,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,KAAK,UAAU,KAAK,CAClB,QAAkB,EAClB,MAAmB,EACnB,KAAY,EACZ,MAAc,EACd,YAAgC,EAChC,IAAgB;IAEhB,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC3C,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACzB,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,MAAM,CAAC;QAC1C,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ;QAC/C,YAAY;QACZ,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,MAAM;KACP,CAAC,CAAC;IAEH,IAAI,OAAO,CAAC;IACZ,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC1D,CAAC;YAAS,CAAC;QACT,MAAM,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAC1D,CAAC;IACD,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;IAEpC,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,eAAe,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC;IAE7F,IAAI,OAAO,CAAC,OAAO,KAAK,UAAU,IAAI,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;QAC3D,KAAK,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;IAC5D,CAAC;IAED,OAAO;QACL,UAAU,EAAE,QAAQ,CAAC,EAAE;QACvB,IAAI,EAAE,QAAQ,CAAC,IAAI;QACnB,IAAI,EAAE,IAAI;QACV,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,SAAS,EAAE,OAAO,CAAC,KAAK,CAAC,MAAM;QAC/B,MAAM;QACN,SAAS;QACT,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK;QAC9B,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,KAAK;KACN,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAS,UAAU,CAAC,KAAa;IAC/B,OAAO,KAAK,CAAC;AACf,CAAC;AAED,OAAO,EAAE,YAAY,EAAE,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { loadConfig, type ScoutConfig, type ScoutProfile } from "./config.js";
2
+ export { runScenario, type RunOptions } from "./engine.js";
3
+ export { renderRunReport, renderSummary } from "./report.js";
4
+ export { Store } from "./store.js";
5
+ export type { RunResult, Scenario, Step, Target, Verdict } from "./types.js";
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export { loadConfig } from "./config.js";
2
+ export { runScenario } from "./engine.js";
3
+ export { renderRunReport, renderSummary } from "./report.js";
4
+ export { Store } from "./store.js";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAuC,MAAM,aAAa,CAAC;AAC9E,OAAO,EAAE,WAAW,EAAmB,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC"}