blastproof 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 +64 -0
- package/dist/cli.js +3 -3
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -189,6 +189,70 @@ Two workflows, split by what they cost:
|
|
|
189
189
|
- **Impact** — runs on every pull request, including forks. Deterministic and keyless: it reports the blast radius of the diff and which tests cover it, before anyone spends a token.
|
|
190
190
|
- **Dogfood** — runs daily and on demand. The agentic run needs an API key, so it stays out of the merge path: a non-deterministic model answer should never block a merge.
|
|
191
191
|
|
|
192
|
+
## GitHub Action
|
|
193
|
+
|
|
194
|
+
```yaml
|
|
195
|
+
name: blastproof
|
|
196
|
+
on: pull_request
|
|
197
|
+
|
|
198
|
+
jobs:
|
|
199
|
+
test:
|
|
200
|
+
runs-on: ubuntu-latest
|
|
201
|
+
steps:
|
|
202
|
+
- uses: actions/checkout@v4
|
|
203
|
+
with:
|
|
204
|
+
fetch-depth: 0 # required: the diff needs a merge-base
|
|
205
|
+
|
|
206
|
+
- run: npm start & # however your app boots
|
|
207
|
+
|
|
208
|
+
- uses: hamc/blastproof@v0.1.2
|
|
209
|
+
with:
|
|
210
|
+
api-key: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
211
|
+
base: ${{ github.event.pull_request.base.ref }}
|
|
212
|
+
min-score: '80'
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Exit non-zero blocks the merge. Use the score in a later step:
|
|
216
|
+
|
|
217
|
+
```yaml
|
|
218
|
+
- id: bp
|
|
219
|
+
uses: hamc/blastproof@v0.1.2
|
|
220
|
+
with:
|
|
221
|
+
api-key: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
222
|
+
base: ${{ github.event.pull_request.base.ref }}
|
|
223
|
+
min-score: '80'
|
|
224
|
+
fail-on-unmapped: 'true'
|
|
225
|
+
html: report.html
|
|
226
|
+
|
|
227
|
+
- if: always()
|
|
228
|
+
run: echo "scored ${{ steps.bp.outputs.score }}"
|
|
229
|
+
|
|
230
|
+
- if: always()
|
|
231
|
+
uses: actions/upload-artifact@v4
|
|
232
|
+
with: { name: blastproof-report, path: report.html }
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
| input | |
|
|
236
|
+
| --- | --- |
|
|
237
|
+
| `api-key` | Your provider key, from a secret. Not needed for ollama |
|
|
238
|
+
| `provider` · `model` · `llm-base-url` | Override the committed config without editing it |
|
|
239
|
+
| `command` | `test` (default), `run` or `plan` |
|
|
240
|
+
| `base` | Base ref for the diff |
|
|
241
|
+
| `url` | Base URL of the app under test |
|
|
242
|
+
| `min-score` | Weighted score threshold — replaces the all-must-pass rule |
|
|
243
|
+
| `fail-on-unmapped` | Fail on a changed file matching no `routes:` or `ignore:` glob |
|
|
244
|
+
| `write` | Persist generated drafts instead of previewing |
|
|
245
|
+
| `junit` · `html` | Where to write the reports |
|
|
246
|
+
| `version` | Which blastproof release to install (default `latest`) |
|
|
247
|
+
| `install-browser` | Set `false` if the workflow already installed Chromium |
|
|
248
|
+
| `working-directory` | Directory containing `.blastproof/` |
|
|
249
|
+
|
|
250
|
+
Output: **`score`** — 0–100, empty when no report was produced, so "no score" stays distinguishable from "scored zero".
|
|
251
|
+
|
|
252
|
+
**Pin both the tag and `version` when the result gates merges.** `latest` is convenient for trying it out, but a pipeline that blocks merges should not change behaviour without you changing something.
|
|
253
|
+
|
|
254
|
+
**`fetch-depth: 0` is not optional** for `test` and `plan`. The default checkout is shallow and has no merge-base; the action detects this and fails immediately rather than letting it surface as a git error mid-run.
|
|
255
|
+
|
|
192
256
|
## Testing behind a login
|
|
193
257
|
|
|
194
258
|
Most of a product lives behind authentication. Declare a recipe once and blastproof signs in a single time per run, then reuses that session for every test **and** for `plan` — so generated drafts describe the actual feature instead of the login wall.
|
package/dist/cli.js
CHANGED
|
@@ -826,8 +826,8 @@ Rules:
|
|
|
826
826
|
- Pick target elements exclusively from the current snapshot, using their exact role and accessible name. Never invent CSS selectors or guess elements not in the snapshot.
|
|
827
827
|
- Use "navigate" only with a URL or path as value. Use "fill" to type into textboxes/inputs. Use "press" for keyboard keys (value e.g. "Enter"). Use "select" for dropdowns (value = option label).
|
|
828
828
|
- Use "assert" with an expectation to verify page state (visible text, counts, URLs). Assertions never modify the page.
|
|
829
|
-
- Return "done"
|
|
830
|
-
- Return "fail"
|
|
829
|
+
- Return "done" when the current step's outcome holds \u2014 including when it already held before you acted, or was achieved by your previous action. "Already true" is done, never failure. Do not return "done" for work belonging to later steps.
|
|
830
|
+
- Return "fail" only when the step's outcome cannot be reached: the element is still absent after retries, the page cannot support the step, or an error blocks progress. Never return "fail" because the work appears to have been done already.
|
|
831
831
|
- If your previous action errored, re-read the fresh snapshot and choose an alternative element or approach. Do not repeat the exact same failing action.
|
|
832
832
|
- Keep reasoning to one short sentence.`;
|
|
833
833
|
}
|
|
@@ -1981,7 +1981,7 @@ function parseMinScore(value) {
|
|
|
1981
1981
|
return score;
|
|
1982
1982
|
}
|
|
1983
1983
|
var program = new Command();
|
|
1984
|
-
program.name("blastproof").description("Open-source AI testing agent: plain-English YAML tests executed agentically on a real browser.").version("0.
|
|
1984
|
+
program.name("blastproof").description("Open-source AI testing agent: plain-English YAML tests executed agentically on a real browser.").version("0.1.2");
|
|
1985
1985
|
program.command("init").description("Scaffold .blastproof/ (config, tests, sample tests) in the current directory").action(async () => {
|
|
1986
1986
|
try {
|
|
1987
1987
|
const result = await initProject(process.cwd());
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts","../src/commands/init.ts","../src/commands/plan.ts","../src/auth.ts","../src/runner/env.ts","../src/runner/executor.ts","../src/runner/actions.ts","../src/config.ts","../src/diff.ts","../src/impact.ts","../src/llm/brain.ts","../src/llm/prompts.ts","../src/llm/schemas.ts","../src/llm/provider.ts","../src/planner.ts","../src/runner/testfile.ts","../src/commands/run.ts","../src/report/html.ts","../src/report/junit.ts","../src/report/score.ts","../src/runner/selection.ts","../src/commands/test.ts"],"sourcesContent":["import { Command, InvalidArgumentError } from 'commander';\nimport { formatInitGuidance, initProject } from './commands/init.js';\nimport { planCommand } from './commands/plan.js';\nimport { EXIT_OK, EXIT_USAGE, runCommand } from './commands/run.js';\nimport { testCommand } from './commands/test.js';\nimport type { Priority } from './runner/testfile.js';\n\nfunction collect(value: string, previous: string[]): string[] {\n return [...previous, value];\n}\n\nfunction parsePriority(value: string): Priority {\n if (value !== 'P0' && value !== 'P1' && value !== 'P2') {\n throw new InvalidArgumentError('priority must be one of P0, P1, P2');\n }\n return value;\n}\n\nfunction parseMinScore(value: string): number {\n const score = Number(value);\n if (!Number.isInteger(score) || score < 0 || score > 100) {\n throw new InvalidArgumentError('min-score must be an integer between 0 and 100');\n }\n return score;\n}\n\nconst program = new Command();\n\nprogram\n .name('blastproof')\n .description('Open-source AI testing agent: plain-English YAML tests executed agentically on a real browser.')\n .version('0.0.1');\n\nprogram\n .command('init')\n .description('Scaffold .blastproof/ (config, tests, sample tests) in the current directory')\n .action(async () => {\n try {\n const result = await initProject(process.cwd());\n console.log(formatInitGuidance(result));\n process.exitCode = EXIT_OK;\n } catch (error) {\n console.error(`error: ${error instanceof Error ? error.message : String(error)}`);\n process.exitCode = EXIT_USAGE;\n }\n });\n\nprogram\n .command('run')\n .description('Discover and run all tests under .blastproof/tests/ agentically')\n .option('--tag <tag>', 'only run tests with this tag (repeatable)', collect, [])\n .option('--priority <priority>', 'only run tests with this priority (P0|P1|P2)', parsePriority)\n .option('--query <text>', 'only run tests whose summary contains <text> (case-insensitive)')\n .option('--impacted', 'run only tests impacted by the diff vs --base (uses routes: mappings)')\n .option('--base <ref>', 'base git ref for --impacted', 'main')\n .option('--url <url>', 'override config base_url for this run only (config file untouched)')\n .option('--dry-run', 'print the selection plan and exit without launching a browser or calling the LLM')\n .option(\n '--min-score <n>',\n 'require a weighted score of at least n (0-100); replaces the all-must-pass rule',\n parseMinScore,\n )\n .option('--junit [path]', 'write a JUnit XML report (default: .blastproof/reports/<session>/junit.xml)')\n .option('--html [path]', 'write a self-contained HTML report (default: .blastproof/reports/<session>/report.html)')\n .option('--fail-on-unmapped', 'fail when a changed file matches no routes: or ignore: glob')\n .action(\n async (options: {\n tag: string[];\n priority?: Priority;\n query?: string;\n impacted?: boolean;\n base: string;\n url?: string;\n dryRun?: boolean;\n minScore?: number;\n junit?: string | boolean;\n html?: string | boolean;\n failOnUnmapped?: boolean;\n }) => {\n try {\n process.exitCode = await runCommand({\n cwd: process.cwd(),\n tags: options.tag,\n priority: options.priority,\n query: options.query,\n impacted: options.impacted,\n base: options.base,\n url: options.url,\n dryRun: options.dryRun,\n minScore: options.minScore,\n junit: options.junit,\n html: options.html,\n failOnUnmapped: options.failOnUnmapped,\n });\n } catch (error) {\n console.error(`error: ${error instanceof Error ? error.message : String(error)}`);\n process.exitCode = EXIT_USAGE;\n }\n },\n );\n\nprogram\n .command('plan')\n .description('Generate plain-English YAML tests for affected routes no test covers yet')\n .option('--base <ref>', 'base git ref for the diff', 'main')\n .option('--url <url>', 'override config base_url for this run only (config file untouched)')\n .option('--route <route>', 'generate for this route, bypassing the diff (repeatable)', collect, [])\n .option('--write', 'persist drafts under .blastproof/tests/ instead of previewing them')\n .action(async (options: { base: string; url?: string; route: string[]; write?: boolean }) => {\n try {\n process.exitCode = await planCommand({\n cwd: process.cwd(),\n base: options.base,\n url: options.url,\n routes: options.route,\n write: options.write,\n });\n } catch (error) {\n console.error(`error: ${error instanceof Error ? error.message : String(error)}`);\n process.exitCode = EXIT_USAGE;\n }\n });\n\nprogram\n .command('test')\n .description('Full PR pipeline: run the tests covering the diff, then draft tests for the gaps')\n .option('--base <ref>', 'base git ref for the diff', 'main')\n .option('--url <url>', 'override config base_url for this run only (config file untouched)')\n .option(\n '--min-score <n>',\n 'require a weighted score of at least n (0-100); replaces the all-must-pass rule',\n parseMinScore,\n )\n .option('--junit [path]', 'write a JUnit XML report (default: .blastproof/reports/<session>/junit.xml)')\n .option('--html [path]', 'write a self-contained HTML report (default: .blastproof/reports/<session>/report.html)')\n .option('--write', 'persist generated drafts under .blastproof/tests/ instead of previewing them')\n .option('--fail-on-unmapped', 'fail when a changed file matches no routes: or ignore: glob')\n .action(\n async (options: {\n base: string;\n url?: string;\n minScore?: number;\n junit?: string | boolean;\n html?: string | boolean;\n write?: boolean;\n failOnUnmapped?: boolean;\n }) => {\n try {\n process.exitCode = await testCommand({\n cwd: process.cwd(),\n base: options.base,\n url: options.url,\n minScore: options.minScore,\n junit: options.junit,\n html: options.html,\n write: options.write,\n failOnUnmapped: options.failOnUnmapped,\n });\n } catch (error) {\n console.error(`error: ${error instanceof Error ? error.message : String(error)}`);\n process.exitCode = EXIT_USAGE;\n }\n },\n );\n\n// Commander exits 1 on a bad flag by default; usage errors are exit 2 here (spec:\n// cli-run-command). It writes the message itself, so we only map the outcome.\n// exitOverride is per-Command and is not inherited, so subcommands need it too.\nprogram.exitOverride();\nfor (const command of program.commands) command.exitOverride();\n\ntry {\n await program.parseAsync(process.argv);\n} catch (error) {\n const exitCode = (error as { exitCode?: number }).exitCode ?? EXIT_USAGE;\n process.exitCode = exitCode === 0 ? EXIT_OK : EXIT_USAGE;\n}\n","import { mkdir, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport { access } from 'node:fs/promises';\n\nconst DEFAULT_CONFIG = `# blastproof configuration\n# Docs: https://github.com/hamc/blastproof\n\n# Base URL of the app under test. Relative navigate paths resolve against it.\nbase_url: http://localhost:4173\n\nllm:\n # anthropic | openai | ollama\n provider: anthropic\n # Optional. Defaults: anthropic=claude-haiku-4-5, openai=gpt-4o-mini, ollama=qwen2.5\n # model: claude-haiku-4-5\n # Env var holding your API key (not needed for ollama).\n api_key_env: ANTHROPIC_API_KEY\n # OpenAI-compatible endpoint: required for ollama (http://localhost:11434/v1);\n # with provider \"openai\" it enables compatible gateways, e.g. OpenRouter:\n # base_url: https://openrouter.ai/api/v1 (model e.g. anthropic/claude-haiku-4.5)\n\nbrowser:\n headless: true\n timeout_ms: 30000\n\n# Budget of self-healing retries per step (element re-resolution via accessibility tree).\n# max_retries_per_step: 3\n\n# Impact hints mapping file globs to routes (used by \\`blastproof run --impacted\\`).\nroutes:\n \"src/auth/**\": [\"/login\"]\n \"src/cart/**\": [\"/cart\", \"/checkout\"]\n\n# Files knowingly irrelevant to any route. Nothing is ignored by default: a file\n# nobody has classified is exactly the risk \\`--fail-on-unmapped\\` exists to surface,\n# so the list is yours to state rather than ours to guess.\n# ignore:\n# - \"**/*.md\"\n# - \"docs/**\"\n# - \".github/**\"\n# - \"LICENSE\"\n\n# Optional: authenticate once per run so tests and \\`plan\\` reach pages behind a login.\n# Pick exactly ONE strategy. Credentials come from the environment via {{env.VAR}} —\n# never write them here. A test can opt out with \\`auth: false\\` (a login test must).\n#\n# 1) A plain-English login journey, executed once (form login, any clickable flow):\n# auth:\n# steps:\n# - navigate to /login\n# - fill the email field with {{env.TEST_EMAIL}}\n# - fill the password field with {{env.TEST_PASSWORD}}\n# - submit the login form\n# verify: a signed-in indicator is visible # optional, but turns N failures into 1\n#\n# 2) A session captured by hand — for SSO, MFA or magic links, which cannot be\n# automated honestly. The file holds live session cookies: NEVER commit it.\n# auth:\n# storage_state: .blastproof/auth.json\n#\n# 3) Static values, for token-based apps where driving a UI would be theatre:\n# auth:\n# headers:\n# Authorization: \"Bearer {{env.API_TOKEN}}\"\n\n# CI tip: these settings can be overridden from the environment, so you never have\n# to commit a provider choice just to configure a pipeline. Precedence is\n# CLI flag > environment > this file.\n# BLASTPROOF_BASE_URL the app under test\n# BLASTPROOF_LLM_PROVIDER anthropic | openai | ollama\n# BLASTPROOF_LLM_MODEL\n# BLASTPROOF_LLM_BASE_URL the provider endpoint (not the app)\n# BLASTPROOF_LLM_API_KEY_ENV name of the variable holding your key, never the key\n`;\n\nconst SAMPLE_TEST = `summary: App loads and shows the home page\npriority: P1\ntags: [smoke]\n# Routes this test covers; run --impacted selects tests by these routes.\nroutes: [\"/\"]\nsteps:\n - verify the home page loads and shows a heading\n`;\n\n/**\n * Keeps captured sessions and run artifacts out of git. A storage state holds live\n * session cookies: whoever has the file is signed in as that user (design D7).\n */\nconst GITIGNORE = `# blastproof runtime artifacts\nreports/\nsessions/\n\n# Captured sessions are live credentials — never commit them.\n.auth-state.json\nauth.json\n`;\n\nconst SAMPLE_LOGIN_TEST = `summary: Login with valid credentials succeeds\npriority: P0\ntags: [smoke, auth]\nroutes: [\"/login\"]\n# This test exercises the login itself, so it must start signed out even when an\n# auth recipe is configured.\nauth: false\nsteps:\n - navigate to /login\n - fill the email field with demo@blastproof.dev\n - fill the password field with demo123\n - submit the login form\n - verify a welcome message is shown\n# Secrets tip: use {{env.VAR_NAME}} placeholders in steps (e.g. {{env.TEST_PASSWORD}})\n# and export the variable before running. Values are masked as *** in all output.\n`;\n\nexport interface InitResult {\n created: string[];\n kept: string[];\n}\n\nasync function exists(file: string): Promise<boolean> {\n try {\n await access(file);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function writeIfAbsent(file: string, content: string, result: InitResult): Promise<void> {\n if (await exists(file)) {\n result.kept.push(file);\n return;\n }\n await mkdir(path.dirname(file), { recursive: true });\n await writeFile(file, content);\n result.created.push(file);\n}\n\n/** Scaffolds `.blastproof/` in `cwd`. Idempotent: never overwrites existing files. */\nexport async function initProject(cwd: string = process.cwd()): Promise<InitResult> {\n const result: InitResult = { created: [], kept: [] };\n const root = path.join(cwd, '.blastproof');\n\n await writeIfAbsent(path.join(root, '.gitignore'), GITIGNORE, result);\n await writeIfAbsent(path.join(root, 'config.yaml'), DEFAULT_CONFIG, result);\n await writeIfAbsent(path.join(root, 'tests', 'app-load.yaml'), SAMPLE_TEST, result);\n await writeIfAbsent(path.join(root, 'tests', 'login.yaml'), SAMPLE_LOGIN_TEST, result);\n\n return result;\n}\n\nexport function formatInitGuidance(result: InitResult): string {\n const lines: string[] = [];\n if (result.created.length > 0) {\n lines.push('Created:');\n for (const file of result.created) lines.push(` ${file}`);\n }\n if (result.kept.length > 0) {\n lines.push('Kept existing (not overwritten):');\n for (const file of result.kept) lines.push(` ${file}`);\n }\n lines.push(\n '',\n 'Next steps:',\n ' 1. Set your LLM API key: export ANTHROPIC_API_KEY=... (or OPENAI_API_KEY; ollama needs no key)',\n ' 2. Point base_url at your app in .blastproof/config.yaml',\n ' 3. Run your tests: blastproof run',\n '',\n 'First time with Playwright? Install the browser: npx playwright install chromium',\n );\n return lines.join('\\n');\n}\n","import path from 'node:path';\nimport { chromium } from 'playwright';\nimport { authenticate, AuthError, contextOptions, type AuthSession, type BrowserLike } from '../auth.js';\nimport { ConfigError, loadConfig, type BlastproofConfig } from '../config.js';\nimport { DiffError, getChangedFiles } from '../diff.js';\nimport { mapImpact } from '../impact.js';\nimport { createBrain, createPlanner } from '../llm/brain.js';\nimport { createModel, MissingApiKeyError } from '../llm/provider.js';\nimport {\n coveredRoutes,\n generateForRoute,\n PlannerError,\n renderTestYaml,\n writeDraft,\n type TestDraft,\n} from '../planner.js';\nimport type { PageLike } from '../runner/actions.js';\nimport type { ExecutorEvent } from '../runner/executor.js';\nimport {\n discoverTestFiles,\n parseTestFile,\n TESTS_RELATIVE_DIR,\n TestFileError,\n type TestFile,\n} from '../runner/testfile.js';\nimport { applyUrlOverride, EXIT_FAILED, EXIT_OK, EXIT_USAGE } from './run.js';\n\nexport interface PlanOptions {\n cwd: string;\n /** Base git ref for the diff (default \"main\"). Ignored when `routes` is given. */\n base?: string;\n /** Overrides config base_url for this run only; the config file is never mutated. */\n url?: string;\n /** Explicit routes (`--route`), bypassing diff and impact analysis entirely. */\n routes?: string[];\n /** Persist drafts under `.blastproof/tests/` instead of previewing them. */\n write?: boolean;\n}\n\n/** Prints login-journey progress, so a failing authentication is not a black box. */\nfunction printAuthEvent(event: ExecutorEvent): void {\n if (event.type === 'step-start') {\n console.log(` step ${event.index + 1}/${event.total}: ${event.step}`);\n } else if (event.type === 'action') {\n console.log(` -> ${event.action.action} :: ${event.result}`);\n } else if (event.status === 'failed') {\n console.log(` X step failed: ${event.reason ?? 'unknown reason'}`);\n }\n}\n\n/** Loads the suite, tolerating unreadable files (a broken test must not block planning). */\nasync function loadSuite(cwd: string): Promise<TestFile[]> {\n const testsDir = path.join(cwd, TESTS_RELATIVE_DIR);\n let files: string[];\n try {\n files = await discoverTestFiles(testsDir);\n } catch {\n return [];\n }\n\n const parsed: TestFile[] = [];\n for (const file of files) {\n try {\n parsed.push(await parseTestFile(file));\n } catch (error) {\n if (error instanceof TestFileError) continue;\n throw error;\n }\n }\n return parsed;\n}\n\ninterface RouteWork {\n route: string;\n /** Changed files that mapped to this route; empty under `--route`. */\n changedFiles: string[];\n}\n\n/** Resolves which routes to generate for, plus the routes skipped as already covered. */\nasync function resolveTargets(\n options: PlanOptions,\n config: BlastproofConfig,\n suite: TestFile[],\n): Promise<{ work: RouteWork[]; alreadyCovered: string[] }> {\n const covered = coveredRoutes(suite);\n\n if (options.routes && options.routes.length > 0) {\n return { work: options.routes.map((route) => ({ route, changedFiles: [] })), alreadyCovered: [] };\n }\n\n const base = options.base ?? 'main';\n const changedFiles = await getChangedFiles(base, options.cwd);\n const impact = mapImpact(changedFiles, config.routes ?? {}, config.ignore ?? []);\n\n // Re-run the glob match per route so each generation prompt sees only the files\n // that made *that* route impacted (design D3), not the whole changed set.\n const filesForRoute = (route: string): string[] =>\n changedFiles.filter((file) =>\n mapImpact([file], config.routes ?? {}, config.ignore ?? []).affectedRoutes.includes(route),\n );\n\n const work: RouteWork[] = [];\n const alreadyCovered: string[] = [];\n for (const route of impact.affectedRoutes) {\n if (covered.has(route)) {\n alreadyCovered.push(route);\n continue;\n }\n work.push({ route, changedFiles: filesForRoute(route) });\n }\n return { work, alreadyCovered };\n}\n\n/**\n * `blastproof plan`: diff → impact → uncovered routes → snapshot-grounded test drafts.\n * Preview by default; `--write` persists without ever overwriting (design D7).\n * Returns the exit code: 0 all generated (or nothing to do), 1 any route failed,\n * 2 usage/config/diff error.\n */\nexport async function planCommand(options: PlanOptions): Promise<number> {\n let config: BlastproofConfig;\n try {\n config = applyUrlOverride(await loadConfig(options.cwd), options.url);\n } catch (error) {\n if (error instanceof ConfigError) {\n console.error(`error: ${error.message}`);\n return EXIT_USAGE;\n }\n throw error;\n }\n\n const suite = await loadSuite(options.cwd);\n\n let work: RouteWork[];\n let alreadyCovered: string[];\n try {\n ({ work, alreadyCovered } = await resolveTargets(options, config, suite));\n } catch (error) {\n if (error instanceof DiffError) {\n console.error(`error: ${error.message}`);\n return EXIT_USAGE;\n }\n throw error;\n }\n\n if (alreadyCovered.length > 0) {\n console.log('Already covered (skipped):');\n for (const route of alreadyCovered) console.log(` ${route}`);\n }\n\n // Nothing to generate: no browser, no LLM key required.\n if (work.length === 0) {\n console.log('Nothing to generate: no affected route is missing coverage.');\n return EXIT_OK;\n }\n\n // Fail fast on a missing API key before launching any browser, as `run` does.\n try {\n createModel(config.llm);\n } catch (error) {\n if (error instanceof MissingApiKeyError) {\n console.error(`error: ${error.message}`);\n return EXIT_USAGE;\n }\n throw error;\n }\n\n const { model, provider, modelId } = createModel(config.llm);\n const brain = createPlanner(model);\n const base = options.routes && options.routes.length > 0 ? undefined : (options.base ?? 'main');\n console.log(\n `blastproof plan: ${work.length} route(s), provider=${provider} model=${modelId}, base_url=${config.base_url}`,\n );\n\n const generated: string[] = [];\n const written: string[] = [];\n const failed: { route: string; reason: string }[] = [];\n\n const browser = await chromium.launch({ headless: config.browser.headless });\n try {\n // The planner authenticates too (design D8): without a session it would snapshot\n // the login wall and draft a test for that instead of for the actual feature.\n let session: AuthSession | undefined;\n if (config.auth) {\n try {\n console.log('Authenticating...');\n session = await authenticate({\n auth: config.auth,\n cwd: options.cwd,\n baseUrl: config.base_url,\n browser: browser as unknown as BrowserLike,\n brain: createBrain(createModel(config.llm).model),\n maxRetries: config.max_retries_per_step,\n onEvent: printAuthEvent,\n });\n } catch (error) {\n if (error instanceof AuthError) {\n console.error(`error: ${error.message}`);\n return EXIT_USAGE;\n }\n throw error;\n }\n }\n\n for (const { route, changedFiles } of work) {\n console.log(`\\n> ${route}`);\n let draft: TestDraft;\n const context = await browser.newContext(contextOptions(session));\n try {\n const page = await context.newPage();\n page.setDefaultTimeout(config.browser.timeout_ms);\n draft = await generateForRoute(page as unknown as PageLike, {\n route,\n baseUrl: config.base_url,\n changedFiles,\n brain,\n });\n } catch (error) {\n // Per-route isolation (design D9): report and keep going.\n failed.push({ route, reason: error instanceof Error ? error.message : String(error) });\n console.log(` X failed: ${error instanceof Error ? error.message : String(error)}`);\n continue;\n } finally {\n await context.close();\n }\n\n generated.push(route);\n if (options.write) {\n try {\n const file = await writeDraft(options.cwd, draft, { route, base });\n written.push(path.relative(options.cwd, file));\n console.log(` wrote ${path.relative(options.cwd, file)}`);\n } catch (error) {\n if (error instanceof PlannerError) {\n generated.pop();\n failed.push({ route, reason: error.message });\n console.log(` X failed: ${error.message}`);\n continue;\n }\n throw error;\n }\n } else {\n console.log(`\\n${renderTestYaml(draft, { route, base })}`);\n }\n }\n } finally {\n await browser.close();\n }\n\n console.log('\\n--- Plan -------------------------------------------------------');\n console.log(`Generated: ${generated.length > 0 ? generated.join(', ') : 'none'}`);\n if (written.length > 0) {\n console.log('Written:');\n for (const file of written) console.log(` ${file}`);\n }\n if (!options.write && generated.length > 0) {\n console.log('Preview only: no files written. Re-run with --write to persist.');\n }\n if (failed.length > 0) {\n console.log('Failed:');\n for (const { route, reason } of failed) console.log(` ${route}: ${reason}`);\n }\n console.log('---------------------------------------------------------------');\n\n return failed.length > 0 ? EXIT_FAILED : EXIT_OK;\n}\n","import { readFile, writeFile, mkdir } from 'node:fs/promises';\nimport path from 'node:path';\nimport type { AuthConfig } from './config.js';\nimport type { AgentBrain } from './llm/brain.js';\nimport type { PageLike } from './runner/actions.js';\nimport { SecretsMask, substituteEnv } from './runner/env.js';\nimport { defaultSnapshot, executeTest, type ExecutorEvent } from './runner/executor.js';\n\n/** Where a captured session is cached when `auth.cache` is enabled (design D9). */\nexport const AUTH_STATE_RELATIVE_PATH = path.join('.blastproof', '.auth-state.json');\n\nexport class AuthError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'AuthError';\n }\n}\n\n/** Playwright storage state: cookies plus per-origin storage. */\nexport interface StorageState {\n cookies: unknown[];\n origins: unknown[];\n}\n\n/**\n * The single internal contract every strategy produces (design D1). Consumers seed\n * a browser context with it and never learn how the session was obtained.\n */\nexport interface AuthSession {\n storageState?: StorageState;\n extraHTTPHeaders?: Record<string, string>;\n}\n\n/** Narrow Playwright subset the authenticator needs; real objects are compatible. */\nexport interface ContextLike {\n newPage(): Promise<PageLike>;\n storageState(): Promise<StorageState>;\n close(): Promise<void>;\n}\n\nexport interface BrowserLike {\n newContext(options?: Record<string, unknown>): Promise<ContextLike>;\n}\n\nexport interface AuthenticateOptions {\n auth: AuthConfig;\n cwd: string;\n baseUrl: string;\n browser: BrowserLike;\n brain: AgentBrain;\n maxRetries?: number;\n /** Injectable snapshotter, mirroring the executor, so tests need no browser. */\n snapshot?: (page: PageLike) => Promise<string>;\n /**\n * Progress reporter for the login journey. Without it authentication is a black\n * box, and a failing login is the moment you most need to see what happened.\n */\n onEvent?: (event: ExecutorEvent) => void;\n}\n\nfunction isStorageState(value: unknown): value is StorageState {\n return (\n typeof value === 'object' &&\n value !== null &&\n Array.isArray((value as StorageState).cookies) &&\n Array.isArray((value as StorageState).origins)\n );\n}\n\n/** Reads a previously captured state, failing with the path when unusable. */\nasync function fromStorageState(file: string): Promise<AuthSession> {\n let raw: string;\n try {\n raw = await readFile(file, 'utf8');\n } catch {\n throw new AuthError(\n `Cannot read auth.storage_state at ${file}. Capture a session first, or switch to an auth.steps recipe.`,\n );\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n throw new AuthError(`Invalid JSON in auth.storage_state at ${file}.`);\n }\n if (!isStorageState(parsed)) {\n throw new AuthError(\n `Invalid storage state at ${file}: expected an object with \"cookies\" and \"origins\" arrays.`,\n );\n }\n return { storageState: parsed };\n}\n\n/**\n * Builds a session from static values (design D2). `{{env.*}}` placeholders are\n * substituted here, so a token never has to be written into the config file.\n */\nfunction fromStatic(auth: AuthConfig): { session: AuthSession; mask: SecretsMask } {\n const mask = new SecretsMask();\n const session: AuthSession = {};\n\n if (auth.headers) {\n const headers: Record<string, string> = {};\n for (const [name, value] of Object.entries(auth.headers)) {\n mask.registerFrom(value);\n headers[name] = substituteEnv(value);\n }\n session.extraHTTPHeaders = headers;\n }\n\n if (auth.cookies) {\n const cookies = auth.cookies.map((cookie) => {\n mask.registerFrom(cookie.value);\n return { ...cookie, value: substituteEnv(cookie.value) };\n });\n session.storageState = { cookies, origins: [] };\n }\n\n return { session, mask };\n}\n\n/** Runs the journey, converting any thrown error into an AuthError. */\nasync function runJourney(\n ...args: Parameters<typeof executeTest>\n): ReturnType<typeof executeTest> {\n try {\n return await executeTest(...args);\n } catch (error) {\n throw new AuthError(\n `Authentication could not run: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n}\n\n/**\n * Runs the login journey once in an empty context and captures the resulting\n * session (design D2/D3). Reuses the agentic executor, so a login is described in\n * exactly the same plain English as a test.\n */\nasync function fromSteps(options: AuthenticateOptions): Promise<AuthSession> {\n const { auth, baseUrl, browser, brain, maxRetries, snapshot = defaultSnapshot, onEvent } = options;\n const steps = auth.steps!;\n\n const mask = new SecretsMask();\n for (const step of steps) mask.registerFrom(step);\n\n const context = await browser.newContext();\n try {\n const page = await context.newPage();\n // The executor's opening navigation happens outside its per-step error handling,\n // so an unreachable app throws raw here. Every failure in this function must\n // surface as an AuthError with an actionable message (design D6).\n const result = await runJourney(\n page,\n {\n path: '<auth>',\n summary: 'authentication',\n steps: steps.map((step) => substituteEnv(step)),\n priority: 'P0',\n tags: [],\n routes: [],\n auth: false,\n },\n {\n brain,\n sessionDir: path.join(options.cwd, '.blastproof', 'reports', 'auth'),\n baseUrl,\n maxRetries,\n mask: (text) => mask.mask(text),\n snapshot,\n onEvent,\n },\n );\n\n if (result.status === 'failed') {\n throw new AuthError(\n `Authentication failed at step \"${result.failedStep ?? 'unknown'}\": ${result.reason ?? 'no reason given'}`,\n );\n }\n\n // Optional post-login check: turns N mysterious test failures into one message (D5).\n if (auth.verify) {\n const judgment = await brain.judge(auth.verify, await snapshot(page));\n if (!judgment.pass) {\n throw new AuthError(`Authentication could not be verified: ${mask.mask(judgment.reason)}`);\n }\n }\n\n return { storageState: await context.storageState() };\n } finally {\n await context.close();\n }\n}\n\n/** Reads the cached session, or undefined when absent or unusable. */\nasync function readCached(cwd: string): Promise<AuthSession | undefined> {\n try {\n return await fromStorageState(path.join(cwd, AUTH_STATE_RELATIVE_PATH));\n } catch {\n return undefined;\n }\n}\n\n/** Persists the captured session. Never logged: it is a live credential (design D7). */\nasync function writeCached(cwd: string, session: AuthSession): Promise<void> {\n if (!session.storageState) return;\n const file = path.join(cwd, AUTH_STATE_RELATIVE_PATH);\n await mkdir(path.dirname(file), { recursive: true });\n await writeFile(file, JSON.stringify(session.storageState), 'utf8');\n}\n\n/**\n * Produces the authenticated session for a run, at most once (design D3).\n * Throws {@link AuthError} with an actionable message; callers abort before\n * executing any test, since a failed login is a configuration problem and must\n * not be reported as failing tests (design D6).\n */\nexport async function authenticate(options: AuthenticateOptions): Promise<AuthSession> {\n const { auth, cwd } = options;\n\n if (auth.cache) {\n const cached = await readCached(cwd);\n if (cached) return cached;\n }\n\n if (auth.storage_state) {\n return fromStorageState(path.resolve(cwd, auth.storage_state));\n }\n\n if (auth.headers || auth.cookies) {\n return fromStatic(auth).session;\n }\n\n const session = await fromSteps(options);\n if (auth.cache) await writeCached(cwd, session);\n return session;\n}\n\n/** Context options that seed a browser context with the session, if any. */\nexport function contextOptions(session: AuthSession | undefined): Record<string, unknown> {\n if (!session) return {};\n return {\n ...(session.storageState ? { storageState: session.storageState } : {}),\n ...(session.extraHTTPHeaders ? { extraHTTPHeaders: session.extraHTTPHeaders } : {}),\n };\n}\n","const ENV_PLACEHOLDER = /\\{\\{\\s*env\\.([A-Za-z_][A-Za-z0-9_]*)\\s*\\}\\}/g;\n\nexport class MissingEnvError extends Error {\n constructor(variable: string) {\n super(\n `Environment variable ${variable} is referenced via {{env.${variable}}} but is not set. ` +\n `Export it before running, e.g. \\`export ${variable}=...\\`.`,\n );\n this.name = 'MissingEnvError';\n }\n}\n\ntype EnvSource = Record<string, string | undefined>;\n\n/** Substitutes `{{env.VAR}}` placeholders from the environment. Throws MissingEnvError on unset vars. */\nexport function substituteEnv(text: string, env: EnvSource = process.env): string {\n return text.replace(ENV_PLACEHOLDER, (_match, name: string) => {\n const value = env[name];\n if (value === undefined) {\n throw new MissingEnvError(name);\n }\n return value;\n });\n}\n\n/** Returns the names of all env vars referenced by `{{env.VAR}}` placeholders in `text`. */\nexport function referencedEnvVars(text: string): string[] {\n const names = new Set<string>();\n for (const match of text.matchAll(ENV_PLACEHOLDER)) {\n if (match[1]) names.add(match[1]);\n }\n return [...names];\n}\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\n/** Replaces every occurrence of each secret value with `***`. Empty values are ignored. */\nexport function maskSecrets(text: string, secrets: Iterable<string>): string {\n // Longest first so a short secret never masks inside a longer one (e.g. `demo` inside `demo123`).\n const ordered = [...secrets].filter(Boolean).sort((a, b) => b.length - a.length);\n let masked = text;\n for (const secret of ordered) {\n masked = masked.replace(new RegExp(escapeRegExp(secret), 'g'), '***');\n }\n return masked;\n}\n\n/**\n * Stateful masker: register secret values once (e.g. per test), mask all output channels.\n */\nexport class SecretsMask {\n private readonly secrets = new Set<string>();\n\n /** Registers the current values of the env vars referenced in `text`. Throws MissingEnvError on unset vars. */\n registerFrom(text: string, env: EnvSource = process.env): void {\n for (const name of referencedEnvVars(text)) {\n const value = env[name];\n if (value === undefined) {\n throw new MissingEnvError(name);\n }\n this.secrets.add(value);\n }\n }\n\n mask(text: string): string {\n return maskSecrets(text, this.secrets);\n }\n}\n","import { mkdir } from 'node:fs/promises';\nimport path from 'node:path';\nimport type { AgentBrain } from '../llm/brain.js';\nimport type { AgentAction } from '../llm/schemas.js';\nimport type { TestFile } from './testfile.js';\nimport { performAction, type PageLike } from './actions.js';\n\nexport interface StepResult {\n step: string;\n setup: boolean;\n status: 'passed' | 'failed';\n iterations: number;\n failedAttempts: number;\n reason?: string;\n durationMs: number;\n}\n\nexport interface TestResult {\n file: string;\n summary: string;\n priority: string;\n tags: string[];\n status: 'passed' | 'failed';\n steps: StepResult[];\n /** Failing step text + reason, for summary reporting. */\n failedStep?: string;\n reason?: string;\n screenshot?: string;\n durationMs: number;\n}\n\nexport type ExecutorEvent =\n | { type: 'step-start'; index: number; total: number; step: string; setup: boolean }\n | { type: 'action'; index: number; action: AgentAction; result: string }\n | { type: 'step-end'; index: number; status: 'passed' | 'failed'; reason?: string };\n\nexport interface ExecutorOptions {\n brain: AgentBrain;\n /** Session directory for failure screenshots, e.g. `.blastproof/reports/<session>`. */\n sessionDir: string;\n baseUrl: string;\n /** Budget of failed attempts per step (self-healing retries). Default 3. */\n maxRetries?: number;\n /** Hard cap on LLM actions per step. Default 15. */\n maxIterationsPerStep?: number;\n /** Masks secrets from every emitted/logged string. */\n mask?: (text: string) => string;\n /** Injectable snapshotter (defaults to live ariaSnapshot via {@link defaultSnapshot}). */\n snapshot?: (page: PageLike) => Promise<string>;\n onEvent?: (event: ExecutorEvent) => void;\n}\n\n/** Live snapshot via Playwright ariaSnapshot; kept here so tests can inject a fake. */\nexport async function defaultSnapshot(page: PageLike): Promise<string> {\n const { captureSnapshot } = await import('./snapshot.js');\n return captureSnapshot(page as never);\n}\n\nfunction slugify(text: string): string {\n const slug = text\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 60);\n return slug || 'test';\n}\n\nclass StepFailure extends Error {}\n\n/**\n * Executes one test against a page from a fresh browser context.\n * The loop per step: snapshot → LLM action → perform → repeat, until\n * done/fail or budget exhaustion (design D3, spec agentic-execution).\n */\nexport async function executeTest(page: PageLike, test: TestFile, options: ExecutorOptions): Promise<TestResult> {\n const {\n brain,\n sessionDir,\n baseUrl,\n maxRetries = 3,\n maxIterationsPerStep = 15,\n mask = (s: string) => s,\n snapshot = defaultSnapshot,\n onEvent = () => {},\n } = options;\n\n const startedAt = Date.now();\n const stepResults: StepResult[] = [];\n const allSteps = [\n ...(test.setup ?? []).map((step) => ({ step, setup: true })),\n ...test.steps.map((step) => ({ step, setup: false })),\n ];\n\n const emitAction = (index: number, action: AgentAction, result: string): void => {\n // The payload may contain substituted secrets — mask before it reaches any output channel.\n const maskedAction: AgentAction = {\n ...action,\n value: action.value === undefined ? undefined : mask(action.value),\n reasoning: mask(action.reasoning),\n expectation: action.expectation === undefined ? undefined : mask(action.expectation),\n };\n onEvent({ type: 'action', index, action: maskedAction, result: mask(result) });\n };\n\n let failure: { step: string; reason: string } | undefined;\n\n // Every test starts from the configured base_url (spec: browser lifecycle).\n await page.goto(new URL(baseUrl).toString());\n\n for (let index = 0; index < allSteps.length; index++) {\n const { step, setup } = allSteps[index]!;\n const stepStartedAt = Date.now();\n onEvent({ type: 'step-start', index, total: allSteps.length, step: mask(step), setup });\n\n let iterations = 0;\n let failedAttempts = 0;\n let lastResult: string | undefined;\n let stepFailedReason: string | undefined;\n\n try {\n while (true) {\n if (iterations >= maxIterationsPerStep) {\n throw new StepFailure(`step exceeded ${maxIterationsPerStep} actions without completing`);\n }\n\n const snap = await snapshot(page);\n\n let action: AgentAction;\n try {\n action = await brain.nextAction({\n step,\n isSetup: setup,\n snapshot: snap,\n lastResult,\n retriesLeft: maxRetries - failedAttempts,\n iterationsLeft: maxIterationsPerStep - iterations,\n });\n } catch (error) {\n // Malformed model output counts as a failed attempt (spec: structured output).\n failedAttempts++;\n lastResult = `error: ${error instanceof Error ? error.message : String(error)}`;\n if (failedAttempts >= maxRetries) {\n throw new StepFailure(`retry budget exhausted (${maxRetries}): ${lastResult}`);\n }\n continue;\n }\n iterations++;\n\n if (action.action === 'done') {\n emitAction(index, action, action.reasoning);\n break;\n }\n\n if (action.action === 'fail') {\n emitAction(index, action, action.reasoning);\n throw new StepFailure(action.reasoning);\n }\n\n if (action.action === 'assert') {\n const expectation = action.expectation ?? action.reasoning;\n const judgment = await brain.judge(expectation, snap);\n const result = judgment.pass\n ? `ok: assertion passed: ${judgment.reason}`\n : `assertion failed: ${judgment.reason}`;\n emitAction(index, action, result);\n if (judgment.pass) {\n lastResult = result;\n continue;\n }\n // A failed judgment may just mean the page hasn't settled: retry within budget.\n failedAttempts++;\n lastResult = result;\n if (failedAttempts >= maxRetries) {\n throw new StepFailure(judgment.reason);\n }\n continue;\n }\n\n try {\n const result = await performAction(page, action, { baseUrl });\n lastResult = result;\n emitAction(index, action, result);\n } catch (error) {\n // Self-healing: fresh snapshot next iteration, up to the retry budget.\n failedAttempts++;\n lastResult = `error: ${error instanceof Error ? error.message : String(error)}`;\n emitAction(index, action, lastResult);\n if (failedAttempts >= maxRetries) {\n throw new StepFailure(lastResult);\n }\n }\n }\n } catch (error) {\n // Mask everything that reaches logs/reports, regardless of throw site.\n stepFailedReason = mask(error instanceof Error ? error.message : String(error));\n }\n\n const status = stepFailedReason ? ('failed' as const) : ('passed' as const);\n stepResults.push({\n step: mask(step), // results are a report channel — never store substituted secrets\n setup,\n status,\n iterations,\n failedAttempts,\n reason: stepFailedReason,\n durationMs: Date.now() - stepStartedAt,\n });\n onEvent({ type: 'step-end', index, status, reason: stepFailedReason });\n\n if (stepFailedReason) {\n failure = { step: mask(step), reason: stepFailedReason };\n break; // stop at the first failing step; remaining steps are not executed\n }\n }\n\n let screenshot: string | undefined;\n if (failure) {\n try {\n await mkdir(sessionDir, { recursive: true });\n const file = path.join(sessionDir, `${slugify(test.summary)}.png`);\n await page.screenshot({ path: file, fullPage: true });\n screenshot = file;\n } catch {\n // never let a screenshot failure mask the real test failure\n }\n }\n\n return {\n file: test.path,\n summary: test.summary,\n priority: test.priority,\n tags: test.tags,\n status: failure ? 'failed' : 'passed',\n steps: stepResults,\n failedStep: failure?.step,\n reason: failure?.reason,\n screenshot,\n durationMs: Date.now() - startedAt,\n };\n}\n","import type { AgentAction, AgentTarget } from '../llm/schemas.js';\n\n/**\n * Narrow Playwright subset used by the runner. Real `Page`/`Locator` objects are\n * structurally compatible (cast once at the boundary) and tests use fakes.\n */\nexport interface LocatorLike {\n click(): Promise<void>;\n fill(value: string): Promise<void>;\n press(key: string): Promise<void>;\n selectOption(option: { label: string }): Promise<unknown>;\n waitFor(options?: { state?: 'attached' | 'visible'; timeout?: number }): Promise<void>;\n first(): LocatorLike;\n}\n\nexport interface PageLike {\n goto(url: string, options?: { timeout?: number }): Promise<unknown>;\n getByRole(role: string, options?: { name?: string }): LocatorLike;\n getByLabel(text: string): LocatorLike;\n getByText(text: string): LocatorLike;\n keyboard: { press(key: string): Promise<void> };\n screenshot(options: { path: string; fullPage?: boolean }): Promise<unknown>;\n url(): string;\n}\n\nexport class ActionError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ActionError';\n }\n}\n\nexport interface ActionContext {\n baseUrl: string;\n /** Per-resolution wait budget in ms (each fallback candidate gets a short wait). */\n resolveTimeoutMs?: number;\n}\n\nfunction describeTarget(target: AgentTarget): string {\n const parts: string[] = [];\n if (target.role) parts.push(`role=${target.role}`);\n if (target.name) parts.push(`name=\"${target.name}\"`);\n if (target.text) parts.push(`text=\"${target.text}\"`);\n return parts.join(' ') || '(no target)';\n}\n\n/**\n * Resolves an element from the live accessibility tree only (self-healing, design D4):\n * getByRole → getByLabel → getByText, each with a short visibility wait.\n */\nexport async function resolveTarget(\n page: PageLike,\n target: AgentTarget,\n resolveTimeoutMs = 2_000,\n): Promise<LocatorLike> {\n const candidates: LocatorLike[] = [];\n if (target.role) {\n candidates.push(page.getByRole(target.role, target.name ? { name: target.name } : {}));\n }\n if (target.name) {\n candidates.push(page.getByLabel(target.name));\n }\n const text = target.text ?? target.name;\n if (text) {\n candidates.push(page.getByText(text));\n }\n\n for (const candidate of candidates) {\n const locator = candidate.first();\n try {\n await locator.waitFor({ state: 'visible', timeout: resolveTimeoutMs });\n return locator;\n } catch {\n // try the next resolution strategy\n }\n }\n throw new ActionError(`Element not found: ${describeTarget(target)}`);\n}\n\nfunction requireTarget(action: AgentAction): AgentTarget {\n if (!action.target || (!action.target.role && !action.target.name && !action.target.text)) {\n throw new ActionError(`Action \"${action.action}\" requires a target (role/name/text)`);\n }\n return action.target;\n}\n\nfunction requireValue(action: AgentAction): string {\n if (action.value === undefined || action.value === '') {\n throw new ActionError(`Action \"${action.action}\" requires a value`);\n }\n return action.value;\n}\n\n/**\n * Performs one agent action on the page. `assert`/`done`/`fail` are control\n * actions handled by the executor and rejected here.\n * Returns a short human-readable result string fed back into the next prompt.\n */\nexport async function performAction(\n page: PageLike,\n action: AgentAction,\n ctx: ActionContext,\n): Promise<string> {\n switch (action.action) {\n case 'navigate': {\n const value = requireValue(action);\n const url = new URL(value, ctx.baseUrl).toString();\n await page.goto(url, { timeout: 30_000 });\n return `ok: navigated to ${url}`;\n }\n case 'click': {\n const target = requireTarget(action);\n const locator = await resolveTarget(page, target, ctx.resolveTimeoutMs);\n await locator.click();\n return `ok: clicked ${describeTarget(target)}`;\n }\n case 'fill': {\n const target = requireTarget(action);\n const value = requireValue(action);\n const locator = await resolveTarget(page, target, ctx.resolveTimeoutMs);\n await locator.fill(value);\n return `ok: filled ${describeTarget(target)}`;\n }\n case 'press': {\n const key = action.value ?? 'Enter';\n if (action.target && (action.target.role || action.target.name || action.target.text)) {\n const locator = await resolveTarget(page, action.target, ctx.resolveTimeoutMs);\n await locator.press(key);\n return `ok: pressed ${key} on ${describeTarget(action.target)}`;\n }\n await page.keyboard.press(key);\n return `ok: pressed ${key}`;\n }\n case 'select': {\n const target = requireTarget(action);\n const value = requireValue(action);\n const locator = await resolveTarget(page, target, ctx.resolveTimeoutMs);\n await locator.selectOption({ label: value });\n return `ok: selected \"${value}\" in ${describeTarget(target)}`;\n }\n case 'assert':\n case 'done':\n case 'fail':\n throw new ActionError(`Action \"${action.action}\" is handled by the executor, not the page`);\n }\n}\n","import { readFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport { parse as parseYaml } from 'yaml';\nimport { z } from 'zod';\n\nexport const CONFIG_RELATIVE_PATH = path.join('.blastproof', 'config.yaml');\n\nconst llmSchema = z.object({\n provider: z.enum(['anthropic', 'openai', 'ollama']).default('anthropic'),\n model: z.string().min(1).optional(),\n api_key_env: z.string().min(1).optional(),\n base_url: z.string().url().optional(),\n});\n\nconst browserSchema = z.object({\n headless: z.boolean().default(true),\n timeout_ms: z.number().int().positive().default(30_000),\n});\n\nconst cookieSchema = z.object({\n name: z.string().min(1),\n value: z.string(),\n domain: z.string().optional(),\n path: z.string().optional(),\n url: z.string().optional(),\n});\n\n/**\n * Authentication recipe. Exactly one strategy, chosen by which field is present\n * (design D2): a plain-English login journey, a previously captured storage state,\n * or static headers/cookies. All three converge on one authenticated session (D1).\n */\nconst authSchema = z\n .object({\n steps: z.array(z.string().min(1)).min(1).optional(),\n storage_state: z.string().min(1).optional(),\n headers: z.record(z.string()).optional(),\n cookies: z.array(cookieSchema).min(1).optional(),\n verify: z.string().min(1).optional(),\n cache: z.boolean().default(false),\n })\n .superRefine((value, ctx) => {\n const present: string[] = [];\n if (value.steps) present.push('steps');\n if (value.storage_state) present.push('storage_state');\n if (value.headers || value.cookies) {\n present.push([value.headers && 'headers', value.cookies && 'cookies'].filter(Boolean).join('+'));\n }\n\n if (present.length === 0) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message:\n 'configure one auth strategy: steps (login journey), storage_state (captured session), or headers/cookies',\n });\n return;\n }\n if (present.length > 1) {\n // Silently preferring one would make a typo look like it worked.\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `configure exactly one auth strategy, found ${present.join(' and ')}`,\n });\n }\n });\n\nconst configSchema = z.object({\n base_url: z.string().url(),\n llm: llmSchema.default({}),\n browser: browserSchema.default({}),\n routes: z.record(z.array(z.string())).optional(),\n /** Globs for files knowingly irrelevant to any route (docs, CI config, licences). */\n ignore: z.array(z.string()).optional(),\n auth: authSchema.optional(),\n max_retries_per_step: z.number().int().min(1).default(3),\n});\n\nexport type BlastproofConfig = z.infer<typeof configSchema>;\nexport type LlmConfig = BlastproofConfig['llm'];\nexport type BrowserConfig = BlastproofConfig['browser'];\nexport type AuthConfig = NonNullable<BlastproofConfig['auth']>;\n\n/**\n * Environment overrides, one entry per settable field (design D2). `base_url` (the\n * app under test) and `llm.base_url` (the provider endpoint) are deliberately kept\n * apart by name: silently overriding the wrong one would point the browser at an\n * LLM gateway, and that failure is baffling to debug.\n */\nexport const ENV_OVERRIDES: Record<string, readonly [string] | readonly [string, string]> = {\n BLASTPROOF_BASE_URL: ['base_url'],\n BLASTPROOF_LLM_PROVIDER: ['llm', 'provider'],\n BLASTPROOF_LLM_MODEL: ['llm', 'model'],\n BLASTPROOF_LLM_BASE_URL: ['llm', 'base_url'],\n BLASTPROOF_LLM_API_KEY_ENV: ['llm', 'api_key_env'],\n};\n\nexport interface OverrideResult {\n /** Parsed config with environment values merged in, before validation. */\n data: Record<string, unknown>;\n /** Names of the variables that actually contributed, for error attribution. */\n applied: string[];\n}\n\n/**\n * Merges environment overrides into the parsed YAML *before* zod runs (design D4),\n * so an invalid override fails with the same actionable error as an invalid file\n * and per-field defaulting still applies. Pure; `env` is injectable for tests.\n * An empty string counts as unset, so `FOO=` in a CI matrix never blanks a value.\n */\nexport function applyEnvOverrides(\n data: unknown,\n env: Record<string, string | undefined> = process.env,\n): OverrideResult {\n const merged: Record<string, unknown> =\n data && typeof data === 'object' ? { ...(data as Record<string, unknown>) } : {};\n const applied: string[] = [];\n\n for (const [variable, keyPath] of Object.entries(ENV_OVERRIDES)) {\n const value = env[variable];\n if (value === undefined || value === '') continue;\n\n if (keyPath.length === 1) {\n merged[keyPath[0]!] = value;\n } else {\n const [section, field] = keyPath as [string, string];\n const current = merged[section];\n merged[section] = {\n ...(current && typeof current === 'object' ? (current as Record<string, unknown>) : {}),\n [field]: value,\n };\n }\n applied.push(variable);\n }\n\n return { data: merged, applied };\n}\n\nexport class ConfigError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ConfigError';\n }\n}\n\nfunction formatIssues(error: z.ZodError): string {\n return error.issues\n .map((issue) => ` - ${issue.path.join('.') || '(root)'}: ${issue.message}`)\n .join('\\n');\n}\n\n/**\n * Loads and validates `.blastproof/config.yaml` from `cwd`.\n * Throws ConfigError with an actionable message on any problem.\n */\nexport async function loadConfig(\n cwd: string = process.cwd(),\n env: Record<string, string | undefined> = process.env,\n): Promise<BlastproofConfig> {\n const configPath = path.join(cwd, CONFIG_RELATIVE_PATH);\n\n let raw: string;\n try {\n raw = await readFile(configPath, 'utf8');\n } catch {\n throw new ConfigError(\n `No config found at ${CONFIG_RELATIVE_PATH}. Run \\`blastproof init\\` to scaffold one.`,\n );\n }\n\n let data: unknown;\n try {\n data = parseYaml(raw);\n } catch (error) {\n throw new ConfigError(\n `Invalid YAML in ${CONFIG_RELATIVE_PATH}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n\n const { data: merged, applied } = applyEnvOverrides(data ?? {}, env);\n\n const result = configSchema.safeParse(merged);\n if (!result.success) {\n // The file may be perfectly valid and an override the culprit; saying only\n // \"invalid config.yaml\" would send the user hunting through a correct file.\n const source =\n applied.length > 0\n ? `${CONFIG_RELATIVE_PATH} (with overrides from ${applied.join(', ')})`\n : CONFIG_RELATIVE_PATH;\n throw new ConfigError(`Invalid ${source}:\\n${formatIssues(result.error)}`);\n }\n return result.data;\n}\n","import { simpleGit } from 'simple-git';\n\nexport class DiffError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'DiffError';\n }\n}\n\nfunction toDiffError(error: unknown, baseRef: string): DiffError {\n const message = error instanceof Error ? error.message : String(error);\n if (/not a git repository/i.test(message)) {\n return new DiffError(\n 'Cannot compute diff: not inside a git repository. Run blastproof from within your project repository.',\n );\n }\n if (/no merge base/i.test(message)) {\n return new DiffError(\n `Cannot compute diff against '${baseRef}': no common merge-base with HEAD (shallow clone?). Fetch more history, e.g. \\`git fetch --deepen\\` or set fetch-depth: 0 in actions/checkout.`,\n );\n }\n if (/unknown revision|ambiguous argument|bad revision|invalid object name/i.test(message)) {\n return new DiffError(\n `Cannot compute diff: base ref '${baseRef}' does not exist in this repository. Fetch it first (e.g. \\`git fetch origin ${baseRef}\\`) or pass a valid --base ref.`,\n );\n }\n return new DiffError(`Cannot compute diff against '${baseRef}': ${message}`);\n}\n\n/**\n * Returns the sorted repo-relative paths of files changed on the current branch\n * relative to `baseRef` (`git diff <baseRef>...HEAD`, three-dot merge-base\n * semantics): added, modified, deleted and renamed files, paths only.\n * Throws DiffError with an actionable message on invalid refs, non-repo cwd\n * or a missing merge-base (e.g. shallow CI clone).\n */\nexport async function getChangedFiles(\n baseRef: string,\n cwd: string = process.cwd(),\n): Promise<string[]> {\n try {\n const summary = await simpleGit(cwd).diffSummary(['--name-only', `${baseRef}...HEAD`]);\n return summary.files.map((file) => file.file).sort();\n } catch (error) {\n throw toDiffError(error, baseRef);\n }\n}\n","import picomatch from 'picomatch';\n\nexport interface ImpactResult {\n /** Sorted, de-duplicated routes affected by the changed files. */\n affectedRoutes: string[];\n /**\n * Sorted changed files matching neither a `routes:` glob nor an `ignore:` glob —\n * files whose blast radius nobody has declared (design D1).\n */\n unmappedFiles: string[];\n /** Sorted changed files declared irrelevant via `ignore:`. */\n ignoredFiles: string[];\n}\n\n/**\n * Maps changed files to affected routes using the config `routes:` glob→URL-list\n * entries (pure, no I/O). Paths are normalized to forward slashes before matching.\n *\n * Each file lands in exactly one of three buckets (design D1): mapped to routes,\n * ignored, or unclassified. Only the third is reported as unmapped, which is what\n * makes that report — and the gate built on it — worth acting on.\n */\nexport function mapImpact(\n changedFiles: string[],\n routes: Record<string, string[]>,\n ignore: string[] = [],\n): ImpactResult {\n const matchers = Object.entries(routes).map(([glob, urls]) => ({\n isMatch: picomatch(glob, { dot: true }),\n urls,\n }));\n const isIgnored = ignore.map((glob) => picomatch(glob, { dot: true }));\n\n const affected = new Set<string>();\n const unmapped: string[] = [];\n const ignored: string[] = [];\n\n for (const file of changedFiles) {\n const normalized = file.replace(/\\\\/g, '/');\n\n // Ignore wins: a file declared irrelevant contributes nothing, even if some\n // glob would also match it. Otherwise \"irrelevant\" would not mean irrelevant.\n if (isIgnored.some((matches) => matches(normalized))) {\n ignored.push(normalized);\n continue;\n }\n\n let matched = false;\n for (const { isMatch, urls } of matchers) {\n if (isMatch(normalized)) {\n matched = true;\n for (const url of urls) {\n affected.add(url);\n }\n }\n }\n if (!matched) {\n unmapped.push(normalized);\n }\n }\n\n return {\n affectedRoutes: [...affected].sort(),\n unmappedFiles: unmapped.sort(),\n ignoredFiles: ignored.sort(),\n };\n}\n","import { generateObject, type LanguageModel } from 'ai';\nimport type { z } from 'zod';\nimport {\n agentSystemPrompt,\n agentUserPrompt,\n assertSystemPrompt,\n assertUserPrompt,\n plannerSystemPrompt,\n plannerUserPrompt,\n type AgentIterationInput,\n type PlannerInput,\n} from './prompts.js';\nimport {\n agentActionSchema,\n assertJudgmentSchema,\n generatedTestSchema,\n type AgentAction,\n type AssertJudgment,\n type GeneratedTest,\n} from './schemas.js';\n\n/**\n * The LLM decision-maker used by the executor. Mocked in unit tests.\n */\nexport interface AgentBrain {\n /** Decides the single next action for the current step. Throws on malformed model output. */\n nextAction(input: AgentIterationInput): Promise<AgentAction>;\n /** Judges whether the snapshot satisfies an assert expectation. */\n judge(expectation: string, snapshot: string): Promise<AssertJudgment>;\n}\n\n/** Narrowed signature of `generateObject` so tests can inject a stub. */\nexport type GenerateObjectFn = (options: {\n model: LanguageModel;\n schema: z.ZodTypeAny;\n system?: string;\n prompt?: string;\n}) => Promise<{ object: unknown }>;\n\nexport class MalformedModelOutputError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'MalformedModelOutputError';\n }\n}\n\nexport function createBrain(\n model: LanguageModel,\n generate: GenerateObjectFn = generateObject as unknown as GenerateObjectFn,\n): AgentBrain {\n return {\n async nextAction(input) {\n const result = await generate({\n model,\n schema: agentActionSchema,\n system: agentSystemPrompt(),\n prompt: agentUserPrompt(input),\n });\n const parsed = agentActionSchema.safeParse(result.object);\n if (!parsed.success) {\n throw new MalformedModelOutputError(\n `Model returned an invalid action: ${parsed.error.issues[0]?.message ?? 'unknown'}`,\n );\n }\n return parsed.data;\n },\n\n async judge(expectation, snapshot) {\n const result = await generate({\n model,\n schema: assertJudgmentSchema,\n system: assertSystemPrompt(),\n prompt: assertUserPrompt(expectation, snapshot),\n });\n const parsed = assertJudgmentSchema.safeParse(result.object);\n if (!parsed.success) {\n throw new MalformedModelOutputError(\n `Model returned an invalid assert judgment: ${parsed.error.issues[0]?.message ?? 'unknown'}`,\n );\n }\n return parsed.data;\n },\n };\n}\n\n/**\n * The LLM test writer used by the planner (design D5). Mocked in unit tests.\n */\nexport interface PlannerBrain {\n /** Generates one test draft for a route. Throws on malformed model output. */\n planTest(input: PlannerInput): Promise<GeneratedTest>;\n}\n\nexport function createPlanner(\n model: LanguageModel,\n generate: GenerateObjectFn = generateObject as unknown as GenerateObjectFn,\n): PlannerBrain {\n return {\n async planTest(input) {\n const result = await generate({\n model,\n schema: generatedTestSchema,\n system: plannerSystemPrompt(),\n prompt: plannerUserPrompt(input),\n });\n const parsed = generatedTestSchema.safeParse(result.object);\n if (!parsed.success) {\n throw new MalformedModelOutputError(\n `Model returned an invalid test draft: ${parsed.error.issues[0]?.message ?? 'unknown'}`,\n );\n }\n return parsed.data;\n },\n };\n}\n","/**\n * Prompts for the agentic execution loop (design D3: one structured action per iteration).\n */\n\nexport function agentSystemPrompt(): string {\n return `You are a meticulous QA agent executing one plain-English test step at a time in a real browser.\n\nYou receive the current page as a YAML accessibility snapshot (roles and accessible names, exactly what a user perceives) and decide the single next action to move the current step forward.\n\nRules:\n- Return exactly ONE action per response. Never batch actions.\n- Pick target elements exclusively from the current snapshot, using their exact role and accessible name. Never invent CSS selectors or guess elements not in the snapshot.\n- Use \"navigate\" only with a URL or path as value. Use \"fill\" to type into textboxes/inputs. Use \"press\" for keyboard keys (value e.g. \"Enter\"). Use \"select\" for dropdowns (value = option label).\n- Use \"assert\" with an expectation to verify page state (visible text, counts, URLs). Assertions never modify the page.\n- Return \"done\" only when the current step is fully accomplished. Do not return \"done\" for work belonging to later steps.\n- Return \"fail\" with a clear reason when the step is impossible to accomplish (element missing after retries, unexpected page state, blocking error).\n- If your previous action errored, re-read the fresh snapshot and choose an alternative element or approach. Do not repeat the exact same failing action.\n- Keep reasoning to one short sentence.`;\n}\n\nexport interface AgentIterationInput {\n /** The plain-English step being executed. */\n step: string;\n /** Whether the step is a setup step (context for the model). */\n isSetup?: boolean;\n /** Current accessibility snapshot of the page. */\n snapshot: string;\n /** Result of the previous action attempt, if any (\"ok: ...\" or \"error: ...\"). */\n lastResult?: string;\n /** Remaining retry budget for failed attempts. */\n retriesLeft: number;\n /** Remaining action iterations before the step is aborted. */\n iterationsLeft: number;\n}\n\nexport function agentUserPrompt(input: AgentIterationInput): string {\n const parts = [\n `Current ${input.isSetup ? 'setup ' : ''}step: ${input.step}`,\n '',\n 'Page accessibility snapshot:',\n input.snapshot,\n ];\n if (input.lastResult) {\n parts.push('', `Previous action result: ${input.lastResult}`);\n }\n parts.push(\n '',\n `Budget: ${input.retriesLeft} failed attempts left, ${input.iterationsLeft} actions left for this step.`,\n 'What is the single next action?',\n );\n return parts.join('\\n');\n}\n\nexport function assertSystemPrompt(): string {\n return `You are a QA judge. You receive a page accessibility snapshot and an expectation. Decide whether the snapshot satisfies the expectation. Be strict: only pass when the expectation is clearly met by the snapshot content. Answer with pass=true/false and a one-sentence reason.`;\n}\n\nexport function assertUserPrompt(expectation: string, snapshot: string): string {\n return [\n `Expectation: ${expectation}`,\n '',\n 'Page accessibility snapshot:',\n snapshot,\n '',\n 'Does the snapshot satisfy the expectation?',\n ].join('\\n');\n}\n\nexport function plannerSystemPrompt(): string {\n return `You are a QA engineer writing one end-to-end test for a web page, in plain English.\n\nYou receive a YAML accessibility snapshot of the page (roles and accessible names, exactly what a user perceives) and the list of source files a pull request changed in the area this page covers.\n\nRules:\n- Write steps a human tester could follow without looking at the code. One action or check per step.\n- Refer to controls by the accessible name shown in the snapshot, spelled exactly. Never invent buttons, fields or links that are not in the snapshot.\n- Never write CSS selectors, XPath, IDs or any code — the runner resolves elements live from the accessibility tree.\n- Prefer the journey the changed files touch over a generic tour of the page. The changed files tell you which part of the page matters.\n- End with at least one step that verifies an observable outcome (visible text, a count, a state change).\n- If a step needs a credential or any secret, write it as a placeholder like {{env.TEST_PASSWORD}}. Never write a real or invented password, token or key.\n- Keep the whole test to a handful of steps: one journey, not an exhaustive suite.`;\n}\n\nexport interface PlannerInput {\n /** Route the test is being generated for, e.g. `/cart`. */\n route: string;\n /** Accessibility snapshot captured after loading the route. */\n snapshot: string;\n /** Repo-relative paths of the changed files that mapped to this route (design D3). */\n changedFiles: string[];\n}\n\nexport function plannerUserPrompt(input: PlannerInput): string {\n const parts = [`Route under test: ${input.route}`, '', 'Page accessibility snapshot:', input.snapshot];\n if (input.changedFiles.length > 0) {\n parts.push(\n '',\n 'Files this pull request changed in the area covering this route:',\n ...input.changedFiles.map((file) => `- ${file}`),\n );\n } else {\n parts.push('', 'No specific changed files for this route: cover its main user journey.');\n }\n parts.push('', 'Write the test for this route.');\n return parts.join('\\n');\n}\n","import { z } from 'zod';\n\n/**\n * The single structured decision the LLM returns on every loop iteration (design D3).\n */\nexport const agentActionSchema = z.object({\n action: z\n .enum(['navigate', 'click', 'fill', 'press', 'select', 'assert', 'done', 'fail'])\n .describe('The next browser action to perform for the current step.'),\n target: z\n .object({\n role: z\n .string()\n .optional()\n .describe('ARIA role of the target element, e.g. \"button\", \"link\", \"textbox\".'),\n name: z\n .string()\n .optional()\n .describe('Accessible name of the target element, exactly as shown in the snapshot.'),\n text: z\n .string()\n .optional()\n .describe('Visible text of the target element, used as a fallback when no role matches.'),\n })\n .optional()\n .describe('Element to act on, resolved from the accessibility snapshot. Omit for navigate/done/fail.'),\n value: z\n .string()\n .optional()\n .describe(\n 'Action payload: URL/path for navigate, text for fill, key for press (e.g. \"Enter\"), option label for select.',\n ),\n reasoning: z.string().describe('One sentence explaining why this action moves the step forward.'),\n expectation: z\n .string()\n .optional()\n .describe('For assert: the condition the current page snapshot must satisfy.'),\n});\n\nexport type AgentAction = z.infer<typeof agentActionSchema>;\nexport type AgentActionName = AgentAction['action'];\nexport type AgentTarget = NonNullable<AgentAction['target']>;\n\n/** Judgment returned by the LLM when evaluating an `assert` expectation against a snapshot. */\nexport const assertJudgmentSchema = z.object({\n pass: z.boolean().describe('Whether the snapshot satisfies the expectation.'),\n reason: z.string().describe('One sentence explaining the judgment.'),\n});\n\nexport type AssertJudgment = z.infer<typeof assertJudgmentSchema>;\n\n/**\n * A test draft returned by the planner (design D5). `routes` is deliberately absent:\n * it is set by code to the route the draft was generated for, never by the model (D6).\n */\nexport const generatedTestSchema = z.object({\n summary: z\n .string()\n .min(1)\n .describe('One line naming the user journey this test covers, e.g. \"Applying a discount updates the cart total\".'),\n steps: z\n .array(z.string().min(1))\n .min(1)\n .describe(\n 'Plain-English steps a QA engineer would follow, each naming controls exactly as they appear in the snapshot.',\n ),\n priority: z\n .enum(['P0', 'P1', 'P2'])\n .describe('P0 for revenue- or auth-critical journeys, P1 for main flows, P2 for edge cases.'),\n tags: z\n .array(z.string())\n .describe('Short lowercase tags grouping this test, e.g. [\"cart\", \"checkout\"].'),\n});\n\nexport type GeneratedTest = z.infer<typeof generatedTestSchema>;\n","import { createAnthropic } from '@ai-sdk/anthropic';\nimport { createOpenAI } from '@ai-sdk/openai';\nimport type { LanguageModel } from 'ai';\nimport type { LlmConfig } from '../config.js';\n\nexport const DEFAULT_MODELS: Record<LlmConfig['provider'], string> = {\n anthropic: 'claude-haiku-4-5',\n openai: 'gpt-4o-mini',\n ollama: 'qwen2.5',\n};\n\nexport const DEFAULT_API_KEY_ENVS: Record<Exclude<LlmConfig['provider'], 'ollama'>, string> = {\n anthropic: 'ANTHROPIC_API_KEY',\n openai: 'OPENAI_API_KEY',\n};\n\nexport const DEFAULT_OLLAMA_BASE_URL = 'http://localhost:11434/v1';\n\nexport class MissingApiKeyError extends Error {\n constructor(variable: string, provider: string) {\n super(\n `Missing API key: environment variable ${variable} is not set (required for provider \"${provider}\"). ` +\n `Export it before running, e.g. \\`export ${variable}=...\\`, or change llm.provider / llm.api_key_env in .blastproof/config.yaml.`,\n );\n this.name = 'MissingApiKeyError';\n }\n}\n\nexport interface ResolvedModel {\n model: LanguageModel;\n provider: LlmConfig['provider'];\n modelId: string;\n}\n\n/**\n * Resolves the configured LLM provider to a model instance.\n * Fails fast (before any browser launch) when the required API key env var is unset.\n */\nexport function createModel(\n llm: LlmConfig,\n env: Record<string, string | undefined> = process.env,\n): ResolvedModel {\n const modelId = llm.model ?? DEFAULT_MODELS[llm.provider];\n\n switch (llm.provider) {\n case 'anthropic':\n case 'openai': {\n const keyEnv = llm.api_key_env ?? DEFAULT_API_KEY_ENVS[llm.provider];\n const apiKey = env[keyEnv];\n if (!apiKey) {\n throw new MissingApiKeyError(keyEnv, llm.provider);\n }\n const model =\n llm.provider === 'anthropic'\n ? createAnthropic({ apiKey })(modelId)\n : // `.chat` = Chat Completions: works with official OpenAI and any\n // OpenAI-compatible endpoint (OpenRouter, LiteLLM, vLLM) via base_url.\n createOpenAI({ apiKey, ...(llm.base_url ? { baseURL: llm.base_url } : {}) }).chat(modelId);\n return { model, provider: llm.provider, modelId };\n }\n case 'ollama': {\n const baseURL = llm.base_url ?? DEFAULT_OLLAMA_BASE_URL;\n // Ollama exposes an OpenAI-compatible Chat Completions endpoint; a dummy key satisfies the client.\n const model = createOpenAI({ baseURL, apiKey: 'ollama' }).chat(modelId);\n return { model, provider: 'ollama', modelId };\n }\n }\n}\n","import { writeFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport { stringify } from 'yaml';\nimport type { PlannerBrain } from './llm/brain.js';\nimport type { GeneratedTest } from './llm/schemas.js';\nimport type { PageLike } from './runner/actions.js';\nimport { defaultSnapshot } from './runner/executor.js';\nimport { TESTS_RELATIVE_DIR, type TestFile } from './runner/testfile.js';\n\nexport class PlannerError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'PlannerError';\n }\n}\n\n/** A generated test plus the route coverage the planner assigns to it (design D6). */\nexport type TestDraft = GeneratedTest & { routes: string[] };\n\nexport interface GenerateOptions {\n route: string;\n baseUrl: string;\n /** Changed files that mapped to this route; empty for `--route` generation. */\n changedFiles: string[];\n brain: PlannerBrain;\n /** Injectable snapshotter (defaults to live ariaSnapshot), mirroring the executor. */\n snapshot?: (page: PageLike) => Promise<string>;\n}\n\n/** Steps naming a credential alongside a quoted literal, with no `{{env.*}}` placeholder. */\nconst CREDENTIAL_WORD = /\\b(password|passwd|api[ _-]?key|token|secret|credential)\\b/i;\nconst QUOTED_LITERAL = /[\"'][^\"']+[\"']/;\n\n/**\n * Returns the steps that appear to carry a literal secret instead of a placeholder\n * (design D8). A credential word alone is not enough — \"check the password field is\n * visible\" is fine; it is the quoted literal without `{{env.*}}` that is a violation.\n */\nexport function findSecretLiterals(steps: string[]): string[] {\n return steps.filter(\n (step) => CREDENTIAL_WORD.test(step) && QUOTED_LITERAL.test(step) && !step.includes('{{env.'),\n );\n}\n\n/**\n * Derives the test filename stem from a route: `/` → `home`, everything else\n * lowercased with non-alphanumerics collapsed to `-` (design D7).\n */\nexport function routeToSlug(route: string): string {\n const slug = route\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 60);\n return slug || 'home';\n}\n\nexport interface ProvenanceMeta {\n route: string;\n /** Base ref the diff came from; omitted when the route was named with `--route`. */\n base?: string;\n /** ISO date (YYYY-MM-DD); injectable so tests are not clock-dependent. */\n date?: string;\n}\n\n/** Renders a draft as YAML with the provenance comment header (design D7). */\nexport function renderTestYaml(draft: TestDraft, meta: ProvenanceMeta): string {\n const header = [\n '# Generated by `blastproof plan` — review before committing.',\n `# route: ${meta.route}`,\n `# base: ${meta.base ?? '(explicit --route)'}`,\n `# generated: ${meta.date ?? new Date().toISOString().slice(0, 10)}`,\n '',\n ].join('\\n');\n return (\n header +\n stringify({\n summary: draft.summary,\n priority: draft.priority,\n tags: draft.tags,\n routes: draft.routes,\n steps: draft.steps,\n })\n );\n}\n\n/**\n * Generates one test draft for a route: load it, snapshot it, one LLM call (design D2/D4).\n * `routes` is set here, never taken from the model, so the draft provably closes the\n * coverage gap that triggered it (design D6).\n */\nexport async function generateForRoute(page: PageLike, options: GenerateOptions): Promise<TestDraft> {\n const { route, baseUrl, changedFiles, brain, snapshot = defaultSnapshot } = options;\n\n const url = new URL(route, baseUrl).toString();\n try {\n await page.goto(url, { timeout: 30_000 });\n } catch (error) {\n throw new PlannerError(\n `Cannot load ${url}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n\n const generated = await brain.planTest({ route, snapshot: await snapshot(page), changedFiles });\n\n const leaked = findSecretLiterals(generated.steps);\n if (leaked.length > 0) {\n throw new PlannerError(\n `Generated steps contain literal secrets instead of {{env.VAR}} placeholders: ${leaked.join(' | ')}`,\n );\n }\n\n return { ...generated, routes: [route] };\n}\n\n/**\n * Writes a draft under `.blastproof/tests/`, never overwriting: an existing target\n * fails this route with the conflicting path (design D7). Returns the path written.\n */\nexport async function writeDraft(\n cwd: string,\n draft: TestDraft,\n meta: ProvenanceMeta,\n): Promise<string> {\n const file = path.join(cwd, TESTS_RELATIVE_DIR, `${routeToSlug(meta.route)}.yaml`);\n try {\n // 'wx' fails if the file exists — atomic, so a concurrent write cannot slip through.\n await writeFile(file, renderTestYaml(draft, meta), { flag: 'wx' });\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'EEXIST') {\n throw new PlannerError(\n `Refusing to overwrite ${path.relative(cwd, file)} (route ${meta.route}). ` +\n 'Delete or rename it to regenerate.',\n );\n }\n throw error;\n }\n return file;\n}\n\n/** Routes already covered by at least one test in the suite. */\nexport function coveredRoutes(tests: TestFile[]): Set<string> {\n return new Set(tests.flatMap((test) => test.routes));\n}\n","import { readFile, readdir } from 'node:fs/promises';\nimport path from 'node:path';\nimport { parse as parseYaml } from 'yaml';\nimport { z } from 'zod';\n\nexport const TESTS_RELATIVE_DIR = path.join('.blastproof', 'tests');\n\nconst prioritySchema = z.enum(['P0', 'P1', 'P2']);\nexport type Priority = z.infer<typeof prioritySchema>;\n\nconst testFileSchema = z.object({\n summary: z.string().min(1),\n steps: z.array(z.string().min(1)).min(1),\n priority: prioritySchema.default('P1'),\n tags: z.array(z.string()).default([]),\n setup: z.array(z.string().min(1)).optional(),\n routes: z.array(z.string()).default([]),\n auth: z.boolean().default(true),\n});\n\nexport interface TestFile {\n /** Path relative to the project root, e.g. `.blastproof/tests/app-load.yaml`. */\n path: string;\n summary: string;\n steps: string[];\n priority: Priority;\n tags: string[];\n setup?: string[];\n /** Routes/URLs this test covers, used by `run --impacted` for impact matching. */\n routes: string[];\n /**\n * Whether this test runs with the configured authenticated session (default true).\n * A login test must set `false`, or it would start already signed in.\n */\n auth: boolean;\n}\n\nexport class TestFileError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'TestFileError';\n }\n}\n\nfunction formatIssues(file: string, error: z.ZodError): string {\n return error.issues\n .map((issue) => ` - ${issue.path.join('.') || '(root)'}: ${issue.message}`)\n .join('\\n');\n}\n\n/** Parses and validates a single YAML test file. Throws TestFileError naming file and field. */\nexport async function parseTestFile(filePath: string): Promise<TestFile> {\n let raw: string;\n try {\n raw = await readFile(filePath, 'utf8');\n } catch {\n throw new TestFileError(`Cannot read test file: ${filePath}`);\n }\n\n let data: unknown;\n try {\n data = parseYaml(raw);\n } catch (error) {\n throw new TestFileError(\n `Invalid YAML in ${filePath}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n\n const result = testFileSchema.safeParse(data ?? {});\n if (!result.success) {\n throw new TestFileError(`Invalid test file ${filePath}:\\n${formatIssues(filePath, result.error)}`);\n }\n\n return { path: filePath, ...result.data };\n}\n\n/** Recursively discovers `.yaml`/`.yml` test files under `dir`, in sorted path order. */\nexport async function discoverTestFiles(root: string): Promise<string[]> {\n const found: string[] = [];\n\n async function walk(dir: string): Promise<void> {\n const entries = await readdir(dir, { withFileTypes: true });\n for (const entry of entries) {\n const full = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n await walk(full);\n } else if (/\\.ya?ml$/i.test(entry.name)) {\n found.push(full);\n }\n }\n }\n\n await walk(root);\n return found.sort();\n}\n","import path from 'node:path';\nimport { chromium } from 'playwright';\nimport { authenticate, AuthError, contextOptions, type AuthSession, type BrowserLike } from '../auth.js';\nimport { ConfigError, loadConfig, type BlastproofConfig } from '../config.js';\nimport { DiffError, getChangedFiles } from '../diff.js';\nimport { mapImpact, type ImpactResult } from '../impact.js';\nimport { createBrain } from '../llm/brain.js';\nimport { createModel, MissingApiKeyError } from '../llm/provider.js';\nimport { renderHtml, writeHtml } from '../report/html.js';\nimport { renderJUnit, writeJUnit, type SkippedCase } from '../report/junit.js';\nimport { computeScore, formatScoreLine } from '../report/score.js';\nimport type { PageLike } from '../runner/actions.js';\nimport { MissingEnvError, SecretsMask, substituteEnv } from '../runner/env.js';\nimport { executeTest, type ExecutorEvent, type TestResult } from '../runner/executor.js';\nimport {\n matchesFilters,\n selectImpactedTests,\n type ImpactedSelection,\n type TestFilters,\n} from '../runner/selection.js';\nimport {\n discoverTestFiles,\n parseTestFile,\n TESTS_RELATIVE_DIR,\n TestFileError,\n type TestFile,\n} from '../runner/testfile.js';\n\nexport const EXIT_OK = 0;\nexport const EXIT_FAILED = 1;\nexport const EXIT_USAGE = 2;\n\nexport interface RunOptions extends TestFilters {\n cwd: string;\n /** Run only tests impacted by the diff vs `base` (uses routes: mappings). */\n impacted?: boolean;\n /** Base git ref for --impacted (default \"main\"). */\n base?: string;\n /** Overrides config base_url for this run only; the config file is never mutated. */\n url?: string;\n /** Print the selection plan and exit without launching a browser or calling the LLM. */\n dryRun?: boolean;\n /**\n * Minimum weighted score (0–100). When set it replaces the all-must-pass rule:\n * the run succeeds when the score reaches it, tolerating lower-priority failures\n * within the margin (design D4).\n */\n minScore?: number;\n /** Write a JUnit report: `true` for the session-directory default, or an explicit path. */\n junit?: string | boolean;\n /** Write an HTML report: `true` for the session-directory default, or an explicit path. */\n html?: string | boolean;\n /**\n * Fail the run when the diff contains a file matching neither a `routes:` glob\n * nor an `ignore:` glob. Additive to `--min-score`: a run can verify what it\n * selected and still be blocked by a change nobody has classified (design D4).\n */\n failOnUnmapped?: boolean;\n}\n\nfunction sessionId(): string {\n return new Date().toISOString().replace(/[:.]/g, '-');\n}\n\n/**\n * Returns a copy of `config` with `base_url` overridden by the --url flag\n * (CLI flag > config file, design D6). Throws ConfigError on an invalid URL.\n */\nexport function applyUrlOverride(\n config: BlastproofConfig,\n url: string | undefined,\n): BlastproofConfig {\n if (url === undefined) return config;\n try {\n new URL(url);\n } catch {\n throw new ConfigError(\n `Invalid --url '${url}': expected an absolute URL like http://localhost:4173.`,\n );\n }\n return { ...config, base_url: url };\n}\n\n/** Substitutes env placeholders across setup+steps, registering every secret for masking. */\nfunction resolveSecretsAndSteps(test: TestFile): { test: TestFile; mask: SecretsMask } {\n const mask = new SecretsMask();\n for (const step of [...(test.setup ?? []), ...test.steps]) {\n mask.registerFrom(step);\n }\n return {\n test: {\n ...test,\n setup: test.setup?.map((step) => substituteEnv(step)),\n steps: test.steps.map((step) => substituteEnv(step)),\n },\n mask,\n };\n}\n\nfunction printEvent(event: ExecutorEvent): void {\n switch (event.type) {\n case 'step-start':\n console.log(` ${event.setup ? '(setup) ' : ''}step ${event.index + 1}/${event.total}: ${event.step}`);\n break;\n case 'action': {\n const { action, result } = event;\n const target = action.target ? ` ${action.target.role ?? ''} \"${action.target.name ?? action.target.text ?? ''}\"` : '';\n const value = action.value ? ` [${action.value}]` : '';\n console.log(` -> ${action.action}${target}${value} :: ${result}`);\n break;\n }\n case 'step-end':\n if (event.status === 'failed') {\n console.log(` X step failed: ${event.reason ?? 'unknown reason'}`);\n }\n break;\n }\n}\n\nfunction printSummary(results: TestResult[]): void {\n const passed = results.filter((r) => r.status === 'passed');\n const failed = results.filter((r) => r.status === 'failed');\n\n console.log('\\n--- Summary ---------------------------------------------------');\n const rows = results.map((r) => ({\n status: r.status === 'passed' ? 'PASS' : 'FAIL',\n priority: r.priority,\n summary: r.summary,\n duration: `${(r.durationMs / 1000).toFixed(1)}s`,\n }));\n const width = (key: keyof (typeof rows)[number]) =>\n Math.max(key.length, ...rows.map((r) => String(r[key]).length));\n const [ws, wp, wsum, wd] = [width('status'), width('priority'), width('summary'), width('duration')];\n for (const row of rows) {\n console.log(\n `${row.status.padEnd(ws)} ${row.priority.padEnd(wp)} ${row.summary.padEnd(wsum)} ${row.duration.padStart(wd)}`,\n );\n }\n console.log('---------------------------------------------------------------');\n console.log(`${passed.length} passed, ${failed.length} failed, ${results.length} total`);\n\n for (const r of failed) {\n console.log(`\\nX ${r.summary} (${r.file})`);\n if (r.failedStep) console.log(` failing step: ${r.failedStep}`);\n if (r.reason) console.log(` reason: ${r.reason}`);\n if (r.screenshot) console.log(` screenshot: ${r.screenshot}`);\n }\n}\n\nasync function runOne(\n browser: import('playwright').Browser,\n test: TestFile,\n config: BlastproofConfig,\n sessionDir: string,\n session: AuthSession | undefined,\n): Promise<TestResult> {\n const brain = createBrain(createModel(config.llm).model);\n\n let resolved: TestFile;\n let mask: SecretsMask;\n try {\n ({ test: resolved, mask } = resolveSecretsAndSteps(test));\n } catch (error) {\n if (error instanceof MissingEnvError) {\n // Spec: missing env var fails the test before any page is opened.\n return {\n file: test.path,\n summary: test.summary,\n priority: test.priority,\n tags: test.tags,\n status: 'failed',\n steps: [],\n reason: error.message,\n durationMs: 0,\n };\n }\n throw error;\n }\n\n // Fresh context per test: no cookies, storage or history leakage (m1 design D6).\n // With auth configured the context merely starts from the shared session instead\n // of empty, so isolation is preserved (auth design D3); `auth: false` opts out.\n const context = await browser.newContext(test.auth ? contextOptions(session) : {});\n try {\n const page = await context.newPage();\n page.setDefaultTimeout(config.browser.timeout_ms);\n return await executeTest(page as unknown as PageLike, resolved, {\n brain,\n sessionDir,\n baseUrl: config.base_url,\n maxRetries: config.max_retries_per_step,\n mask: (text) => mask.mask(text),\n onEvent: printEvent,\n });\n } finally {\n await context.close();\n }\n}\n\n/** Prints the --impacted report sections (affected/unmapped/unrouted-skipped/uncovered). */\nfunction printImpactReport(\n impact: ImpactResult,\n selection: ImpactedSelection,\n cwd: string,\n): void {\n console.log('\\n--- Impact -----------------------------------------------------');\n console.log(\n impact.affectedRoutes.length > 0 ? 'Affected routes:' : 'Affected routes: none',\n );\n for (const route of impact.affectedRoutes) console.log(` ${route}`);\n if (impact.unmappedFiles.length > 0) {\n console.log('Unclassified files (matched by no routes: or ignore: glob):');\n for (const file of impact.unmappedFiles) console.log(` ${file}`);\n }\n if (impact.ignoredFiles.length > 0) {\n console.log(`Ignored files: ${impact.ignoredFiles.length}`);\n }\n if (selection.unroutedSkipped.length > 0) {\n console.log('Unrouted tests (skipped):');\n for (const test of selection.unroutedSkipped) {\n console.log(` ${test.summary} (${path.relative(cwd, test.path)})`);\n }\n }\n if (selection.uncoveredRoutes.length > 0) {\n console.log('Affected but uncovered routes:');\n for (const route of selection.uncoveredRoutes) console.log(` ${route}`);\n }\n console.log('---------------------------------------------------------------');\n}\n\n/**\n * Prints the summary tail shared by every terminating path: score line, optional\n * JUnit report, and the exit code. With `--min-score` the threshold decides the\n * outcome instead of the all-must-pass rule (design D4).\n */\nasync function finalize(\n results: TestResult[],\n skipped: SkippedCase[],\n options: RunOptions,\n sessionDir: string,\n durationMs: number,\n impact?: ImpactResult,\n): Promise<number> {\n if (results.length > 0) printSummary(results);\n\n const score = computeScore(results);\n console.log(formatScoreLine(score, results, options.minScore));\n\n if (options.junit) {\n const target =\n typeof options.junit === 'string'\n ? path.resolve(options.cwd, options.junit)\n : path.join(sessionDir, 'junit.xml');\n const xml = renderJUnit(results, skipped, { score, durationMs, cwd: options.cwd });\n await writeJUnit(target, xml);\n console.log(`JUnit report: ${path.relative(options.cwd, target)}`);\n }\n\n if (options.html) {\n const target =\n typeof options.html === 'string'\n ? path.resolve(options.cwd, options.html)\n : path.join(sessionDir, 'report.html');\n const html = await renderHtml(results, skipped, {\n score,\n durationMs,\n minScore: options.minScore,\n cwd: options.cwd,\n });\n await writeHtml(target, html);\n console.log(`HTML report: ${path.relative(options.cwd, target)}`);\n }\n\n // Additive, unlike --min-score (design D4): \"the tests I ran passed\" and\n // \"something changed that nobody has classified\" are different claims, and the\n // second is exactly the silent false negative this flag exists to remove.\n const unclassified = options.failOnUnmapped ? (impact?.unmappedFiles ?? []) : [];\n if (unclassified.length > 0) {\n console.error(\n `error: ${unclassified.length} changed file(s) match no routes: or ignore: glob, ` +\n 'so their blast radius is unknown:',\n );\n for (const file of unclassified) console.error(` ${file}`);\n console.error(\n 'Map them in routes: if they can affect a page, or list them in ignore: if they cannot.',\n );\n return EXIT_FAILED;\n }\n\n if (options.minScore !== undefined) {\n return score >= options.minScore ? EXIT_OK : EXIT_FAILED;\n }\n return results.some((result) => result.status === 'failed') ? EXIT_FAILED : EXIT_OK;\n}\n\n/** Prints the --dry-run selection plan; no browser is launched and no LLM is called. */\nfunction printDryRun(selected: TestFile[], baseUrl: string, cwd: string): void {\n console.log(`\\nDry run: ${selected.length} test(s) selected, base_url=${baseUrl}`);\n for (const test of selected) {\n console.log(` ${test.summary} [${test.priority}] (${path.relative(cwd, test.path)})`);\n }\n console.log('Dry run: no browser launched, no LLM calls made.');\n}\n\n/**\n * `blastproof run`: discovery → filters → sequential agentic execution → summary.\n * With --impacted, the diff vs `base` selects only tests covering affected routes.\n * Returns the process exit code: 0 all pass, 1 any failure, 2 usage/config error.\n */\nexport async function runCommand(options: RunOptions): Promise<number> {\n let config: BlastproofConfig;\n try {\n config = applyUrlOverride(await loadConfig(options.cwd), options.url);\n } catch (error) {\n if (error instanceof ConfigError) {\n console.error(`error: ${error.message}`);\n return EXIT_USAGE;\n }\n throw error;\n }\n\n const impacted = options.impacted ?? false;\n const base = options.base ?? 'main';\n\n // Impact analysis fails fast (usage error) before any browser launch.\n let impact: ImpactResult | undefined;\n if (impacted) {\n let changedFiles: string[];\n try {\n changedFiles = await getChangedFiles(base, options.cwd);\n } catch (error) {\n if (error instanceof DiffError) {\n console.error(`error: ${error.message}`);\n return EXIT_USAGE;\n }\n throw error;\n }\n impact = mapImpact(changedFiles, config.routes ?? {}, config.ignore ?? []);\n }\n\n const testsDir = path.join(options.cwd, TESTS_RELATIVE_DIR);\n let files: string[];\n try {\n files = await discoverTestFiles(testsDir);\n } catch {\n console.error(`error: no tests directory at ${TESTS_RELATIVE_DIR}. Run \\`blastproof init\\` first.`);\n return EXIT_USAGE;\n }\n\n const results: TestResult[] = [];\n const parsed: TestFile[] = [];\n for (const file of files) {\n try {\n parsed.push(await parseTestFile(file));\n } catch (error) {\n if (error instanceof TestFileError) {\n // A broken test file is a failure, not a crash: report it and keep going.\n results.push({\n file: path.relative(options.cwd, file),\n summary: path.basename(file),\n priority: 'P1',\n tags: [],\n status: 'failed',\n steps: [],\n reason: error.message,\n durationMs: 0,\n });\n continue;\n }\n throw error;\n }\n }\n\n // Impacted selection first, tag/priority/query filters applied within it (D3/D4).\n const selection: ImpactedSelection = impact\n ? selectImpactedTests(parsed, impact.affectedRoutes, options)\n : {\n selected: parsed.filter((test) => matchesFilters(test, options)),\n unroutedSkipped: [],\n uncoveredRoutes: [],\n };\n const selected = selection.selected;\n\n if (impact) {\n printImpactReport(impact, selection, options.cwd);\n }\n\n if (options.dryRun) {\n printDryRun(selected, config.base_url, options.cwd);\n return EXIT_OK;\n }\n\n const sessionDir = path.join(options.cwd, '.blastproof', 'reports', sessionId());\n const startedAt = Date.now();\n\n // Empty selection short-circuits before the LLM key check and browser launch (D5).\n if (selected.length === 0) {\n console.log(\n impact ? 'No impacted tests to run.' : 'No tests matched the given filters.',\n );\n return finalize(results, selection.unroutedSkipped, options, sessionDir, Date.now() - startedAt, impact);\n }\n\n // Fail fast on a missing API key before launching any browser (spec: llm-providers).\n try {\n createModel(config.llm);\n } catch (error) {\n if (error instanceof MissingApiKeyError) {\n console.error(`error: ${error.message}`);\n return EXIT_USAGE;\n }\n throw error;\n }\n\n const { provider, modelId } = createModel(config.llm);\n console.log(\n `blastproof run: ${selected.length} test(s), provider=${provider} model=${modelId}, base_url=${config.base_url}`,\n );\n\n const browser = await chromium.launch({ headless: config.browser.headless });\n try {\n // Authenticate once, before the first test (design D3). A failed login is a\n // configuration problem, not a product defect, so it aborts with exit 2 rather\n // than surfacing as N failing tests and a meaningless score (design D6).\n let session: AuthSession | undefined;\n if (config.auth) {\n try {\n console.log('Authenticating...');\n session = await authenticate({\n auth: config.auth,\n cwd: options.cwd,\n baseUrl: config.base_url,\n browser: browser as unknown as BrowserLike,\n brain: createBrain(createModel(config.llm).model),\n maxRetries: config.max_retries_per_step,\n onEvent: printEvent,\n });\n } catch (error) {\n if (error instanceof AuthError) {\n console.error(`error: ${error.message}`);\n return EXIT_USAGE;\n }\n throw error;\n }\n }\n\n for (const test of selected) {\n console.log(`\\n> ${test.summary} [${test.priority}] (${path.relative(options.cwd, test.path)})`);\n results.push(await runOne(browser, test, config, sessionDir, session));\n }\n } finally {\n await browser.close();\n }\n\n return finalize(results, selection.unroutedSkipped, options, sessionDir, Date.now() - startedAt, impact);\n}\n","import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport type { StepResult, TestResult } from '../runner/executor.js';\nimport type { SkippedCase } from './junit.js';\n\nconst ESCAPES: Record<string, string> = {\n '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n \"'\": ''',\n};\n\n/**\n * Single escaping choke point (design D4). Summaries, steps and failure reasons are\n * user- and model-authored; one missed interpolation turns the report into an\n * injection vector the moment someone opens it in a browser.\n */\nexport function escapeHtml(text: string): string {\n return text.replace(/[&<>\"']/g, (char) => ESCAPES[char]!);\n}\n\n/**\n * Reads a screenshot as a base64 `data:` URI so the report stays self-contained\n * (design D3). Returns undefined when unreadable: a report exists to explain a\n * failure and must not fail while doing so.\n */\nexport async function embedScreenshot(file: string): Promise<string | undefined> {\n try {\n const bytes = await readFile(file);\n return `data:image/png;base64,${bytes.toString('base64')}`;\n } catch {\n return undefined;\n }\n}\n\nexport interface HtmlMeta {\n score: number;\n durationMs: number;\n /** Threshold, when one was given, so the gate verdict can be shown. */\n minScore?: number;\n /** Repo root, so paths render relative rather than absolute. */\n cwd?: string;\n generatedAt?: Date;\n}\n\nconst STYLES = `\n:root { color-scheme: light dark; --bg:#fff; --fg:#111; --muted:#666; --line:#e3e3e3;\n --pass:#137333; --fail:#c5221f; --card:#fafafa; }\n@media (prefers-color-scheme: dark) {\n :root { --bg:#15171a; --fg:#e8e8e8; --muted:#9aa0a6; --line:#2c2f34;\n --pass:#5bb974; --fail:#f28b82; --card:#1c1f23; }\n}\n* { box-sizing: border-box; }\nbody { margin:0; padding:2rem 1.25rem; background:var(--bg); color:var(--fg);\n font:15px/1.55 ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", sans-serif; }\nmain { max-width: 62rem; margin: 0 auto; }\nh1 { font-size:1.35rem; margin:0 0 .25rem; }\n.sub { color:var(--muted); font-size:.85rem; margin-bottom:1.5rem; }\n.score { display:flex; align-items:baseline; gap:.75rem; flex-wrap:wrap;\n padding:1.25rem; border:1px solid var(--line); border-radius:10px;\n background:var(--card); margin-bottom:1.5rem; }\n.score b { font-size:2.5rem; line-height:1; font-variant-numeric:tabular-nums; }\n.verdict { font-weight:600; }\n.verdict.pass { color:var(--pass); } .verdict.fail { color:var(--fail); }\n.counts { color:var(--muted); font-size:.9rem; }\ntable { width:100%; border-collapse:collapse; margin-bottom:1.5rem; }\nth, td { text-align:left; padding:.5rem .6rem; border-bottom:1px solid var(--line);\n font-size:.9rem; vertical-align:top; }\nth { color:var(--muted); font-weight:600; }\ntd.num { text-align:right; font-variant-numeric:tabular-nums; white-space:nowrap; }\n.tag { font-weight:700; font-size:.78rem; letter-spacing:.03em; }\n.tag.pass { color:var(--pass); } .tag.fail { color:var(--fail); }\n.tag.skip { color:var(--muted); }\ndetails { border:1px solid var(--line); border-radius:10px; padding:.75rem 1rem;\n margin-bottom:.75rem; background:var(--card); }\ndetails > summary { cursor:pointer; font-weight:600; }\n.reason { border-left:3px solid var(--fail); padding:.5rem .75rem; margin:.75rem 0;\n color:var(--fg); background:color-mix(in srgb, var(--fail) 8%, transparent);\n border-radius:0 6px 6px 0; }\nol.steps { margin:.5rem 0 0; padding-left:1.25rem; }\nol.steps li { margin:.15rem 0; }\nol.steps li.failed { color:var(--fail); font-weight:600; }\nimg.shot { max-width:100%; border:1px solid var(--line); border-radius:8px; margin-top:.75rem; }\n.note { color:var(--muted); font-style:italic; font-size:.85rem; }\nfooter { color:var(--muted); font-size:.8rem; border-top:1px solid var(--line);\n padding-top:1rem; margin-top:2rem; }\n`;\n\nfunction seconds(ms: number): string {\n return `${(ms / 1000).toFixed(1)}s`;\n}\n\nfunction renderSteps(steps: StepResult[]): string {\n if (steps.length === 0) return '';\n const items = steps\n .map(\n (step) =>\n `<li class=\"${step.status === 'failed' ? 'failed' : ''}\">${escapeHtml(step.step)}` +\n `${step.setup ? ' <span class=\"note\">(setup)</span>' : ''}</li>`,\n )\n .join('\\n ');\n return `<ol class=\"steps\">\\n ${items}\\n </ol>`;\n}\n\nasync function renderTest(result: TestResult, cwd?: string): Promise<string> {\n const failed = result.status === 'failed';\n const file = cwd ? path.relative(cwd, result.file) : result.file;\n const parts = [\n ` <details${failed ? ' open' : ''}>`,\n ` <summary><span class=\"tag ${failed ? 'fail' : 'pass'}\">${failed ? 'FAIL' : 'PASS'}</span> ` +\n `${escapeHtml(result.summary)} <span class=\"note\">${escapeHtml(result.priority)} · ${escapeHtml(file)}</span></summary>`,\n ];\n\n if (failed) {\n if (result.failedStep) {\n parts.push(` <p><strong>Failing step:</strong> ${escapeHtml(result.failedStep)}</p>`);\n }\n if (result.reason) {\n parts.push(` <p class=\"reason\">${escapeHtml(result.reason)}</p>`);\n }\n }\n\n parts.push(` ${renderSteps(result.steps)}`);\n\n if (failed && result.screenshot) {\n const embedded = await embedScreenshot(result.screenshot);\n parts.push(\n embedded\n ? ` <img class=\"shot\" alt=\"Screenshot at failure\" src=\"${embedded}\">`\n : ' <p class=\"note\">Screenshot unavailable.</p>',\n );\n }\n\n parts.push(' </details>');\n return parts.join('\\n');\n}\n\n/**\n * Renders the run as one self-contained HTML document (design D3): inline CSS,\n * screenshots as data URIs, no external references and no scripts. Score and gate\n * verdict lead, failures sort above passes, passing tests collapse (design D5).\n */\nexport async function renderHtml(\n results: TestResult[],\n skipped: SkippedCase[],\n meta: HtmlMeta,\n): Promise<string> {\n const passed = results.filter((r) => r.status === 'passed').length;\n const failures = results.filter((r) => r.status === 'failed');\n const generatedAt = (meta.generatedAt ?? new Date()).toISOString();\n\n // Failures first: the reader came for those.\n const ordered = [...failures, ...results.filter((r) => r.status === 'passed')];\n const detail = (await Promise.all(ordered.map((r) => renderTest(r, meta.cwd)))).join('\\n');\n\n const verdict =\n meta.minScore === undefined\n ? ''\n : meta.score >= meta.minScore\n ? `<span class=\"verdict pass\">min-score ${meta.minScore}: pass</span>`\n : `<span class=\"verdict fail\">min-score ${meta.minScore}: FAIL</span>`;\n\n const rows = results\n .map((r) => {\n const file = meta.cwd ? path.relative(meta.cwd, r.file) : r.file;\n return (\n ` <tr><td><span class=\"tag ${r.status === 'failed' ? 'fail' : 'pass'}\">` +\n `${r.status === 'failed' ? 'FAIL' : 'PASS'}</span></td>` +\n `<td>${escapeHtml(r.priority)}</td><td>${escapeHtml(r.summary)}</td>` +\n `<td>${escapeHtml(file)}</td><td class=\"num\">${seconds(r.durationMs)}</td></tr>`\n );\n })\n .join('\\n');\n\n const skippedRows = skipped\n .map((s) => {\n const file = meta.cwd ? path.relative(meta.cwd, s.path) : s.path;\n return (\n ' <tr><td><span class=\"tag skip\">SKIP</span></td><td>—</td>' +\n `<td>${escapeHtml(s.summary)}</td><td>${escapeHtml(file)}</td>` +\n '<td class=\"num\">—</td></tr>'\n );\n })\n .join('\\n');\n\n return `<!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>blastproof report</title>\n<style>${STYLES}</style>\n</head>\n<body>\n<main>\n <h1>blastproof report</h1>\n <p class=\"sub\">${escapeHtml(generatedAt)} · ${seconds(meta.durationMs)}</p>\n\n <section class=\"score\">\n <b>${meta.score}</b>\n ${verdict}\n <span class=\"counts\">${passed} passed · ${failures.length} failed${\n skipped.length > 0 ? ` · ${skipped.length} skipped` : ''\n }</span>\n </section>\n\n <table>\n <thead><tr><th>Status</th><th>Priority</th><th>Test</th><th>File</th><th class=\"num\">Time</th></tr></thead>\n <tbody>\n${rows}${rows && skippedRows ? '\\n' : ''}${skippedRows}\n </tbody>\n </table>\n\n${detail}\n\n <footer>Generated by blastproof. Screenshots are embedded, so this file works offline.</footer>\n</main>\n</body>\n</html>\n`;\n}\n\n/** Writes the report, creating missing parent directories. Returns the path written. */\nexport async function writeHtml(file: string, html: string): Promise<string> {\n await mkdir(path.dirname(file), { recursive: true });\n await writeFile(file, html, 'utf8');\n return file;\n}\n","import { mkdir, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport type { TestResult } from '../runner/executor.js';\n\nconst ESCAPES: Record<string, string> = {\n '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n \"'\": ''',\n};\n\n/**\n * Single escaping choke point (design D6). Summaries, steps and failure reasons\n * are model- and user-authored, so any of them can carry XML-significant characters.\n */\nexport function escapeXml(text: string): string {\n return text.replace(/[&<>\"']/g, (char) => ESCAPES[char]!);\n}\n\n/** Minimal shape of a test skipped as unrouted under `--impacted`. */\nexport interface SkippedCase {\n path: string;\n summary: string;\n}\n\nexport interface JUnitMeta {\n score: number;\n durationMs: number;\n /** Repo root, so `classname` is repo-relative rather than absolute. */\n cwd?: string;\n}\n\nfunction seconds(ms: number): string {\n return (ms / 1000).toFixed(3);\n}\n\n/**\n * Renders the run as a JUnit `testsuite` (design D7). Unrouted skipped tests are\n * emitted as `<skipped/>` cases so the coverage gap shows up in CI instead of\n * being absent from the report. Pure — returns the XML, writes nothing.\n */\nexport function renderJUnit(\n results: TestResult[],\n skipped: SkippedCase[],\n meta: JUnitMeta,\n): string {\n const relative = (file: string): string =>\n meta.cwd ? path.relative(meta.cwd, file) : file;\n\n const failures = results.filter((result) => result.status === 'failed').length;\n const lines = [\n '<?xml version=\"1.0\" encoding=\"UTF-8\"?>',\n `<testsuite name=\"blastproof\" tests=\"${results.length + skipped.length}\" failures=\"${failures}\" skipped=\"${skipped.length}\" time=\"${seconds(meta.durationMs)}\">`,\n ' <properties>',\n ` <property name=\"score\" value=\"${meta.score}\"/>`,\n ' </properties>',\n ];\n\n for (const result of results) {\n const open = ` <testcase classname=\"${escapeXml(relative(result.file))}\" name=\"${escapeXml(result.summary)}\" time=\"${seconds(result.durationMs)}\"`;\n if (result.status === 'passed') {\n lines.push(`${open}/>`);\n continue;\n }\n const reason = result.reason ?? 'test failed';\n const body = result.failedStep\n ? `failing step: ${result.failedStep}\\n${reason}`\n : reason;\n lines.push(`${open}>`);\n lines.push(` <failure message=\"${escapeXml(reason)}\">${escapeXml(body)}</failure>`);\n lines.push(' </testcase>');\n }\n\n for (const test of skipped) {\n lines.push(\n ` <testcase classname=\"${escapeXml(relative(test.path))}\" name=\"${escapeXml(test.summary)}\" time=\"0.000\">`,\n ' <skipped message=\"no routes: declared, skipped by --impacted\"/>',\n ' </testcase>',\n );\n }\n\n lines.push('</testsuite>', '');\n return lines.join('\\n');\n}\n\n/** Writes the report, creating missing parent directories. Returns the path written. */\nexport async function writeJUnit(file: string, xml: string): Promise<string> {\n await mkdir(path.dirname(file), { recursive: true });\n await writeFile(file, xml, 'utf8');\n return file;\n}\n","import type { TestResult } from '../runner/executor.js';\n\n/** Priority weights: a failing P0 costs three times a failing P2 (design D1). */\nexport const WEIGHTS: Record<string, number> = { P0: 3, P1: 2, P2: 1 };\n\nconst DEFAULT_WEIGHT = WEIGHTS.P1!;\n\n/**\n * Weighted pass rate over executed tests, rounded to an integer 0–100.\n * An empty result list scores 100: nothing ran, so nothing is at risk (design D2).\n * Pure — the caller owns all I/O.\n */\nexport function computeScore(results: TestResult[]): number {\n let total = 0;\n let passed = 0;\n for (const result of results) {\n const weight = WEIGHTS[result.priority] ?? DEFAULT_WEIGHT;\n total += weight;\n if (result.status === 'passed') passed += weight;\n }\n if (total === 0) return 100;\n return Math.round((100 * passed) / total);\n}\n\n/**\n * The summary score line. Names the threshold when one is set so the gate\n * decision is visible without recomputing it, and flags the empty case so a\n * score of 100 is never mistaken for verified quality (design D2).\n */\nexport function formatScoreLine(\n score: number,\n results: TestResult[],\n threshold?: number,\n): string {\n if (results.length === 0) {\n const base = 'Score: 100 (no tests executed)';\n return threshold === undefined ? base : `${base} — min-score ${threshold}: pass`;\n }\n if (threshold === undefined) return `Score: ${score}`;\n return score >= threshold\n ? `Score: ${score} — min-score ${threshold}: pass`\n : `Score: ${score} — min-score ${threshold}: FAIL (below threshold)`;\n}\n","import type { Priority, TestFile } from './testfile.js';\n\nexport interface TestFilters {\n tags: string[];\n priority?: Priority;\n query?: string;\n}\n\n/** Returns true when the test passes the tag/priority/query filters. */\nexport function matchesFilters(test: TestFile, filters: TestFilters): boolean {\n if (filters.tags.length > 0 && !filters.tags.some((tag) => test.tags.includes(tag))) {\n return false;\n }\n if (filters.priority && test.priority !== filters.priority) {\n return false;\n }\n if (filters.query && !test.summary.toLowerCase().includes(filters.query.toLowerCase())) {\n return false;\n }\n return true;\n}\n\nexport interface ImpactedSelection {\n /** Tests covering at least one affected route, after tag/priority/query filters. */\n selected: TestFile[];\n /** Tests with no `routes:` declaration — never executed by `--impacted` (design D4). */\n unroutedSkipped: TestFile[];\n /** Affected routes covered by no selected test — reported, never failing (design D5). */\n uncoveredRoutes: string[];\n}\n\n/**\n * Pure impacted selection (design D3): tests whose `routes:` intersect the\n * affected route set, further reduced by the tag/priority/query filters.\n * Route strings compare by exact equality. Tests without `routes:` are skipped\n * and reported; affected routes no selected test covers are reported.\n */\nexport function selectImpactedTests(\n tests: TestFile[],\n affectedRoutes: string[],\n filters: TestFilters = { tags: [] },\n): ImpactedSelection {\n const affected = new Set(affectedRoutes);\n const selected: TestFile[] = [];\n const unroutedSkipped: TestFile[] = [];\n\n for (const test of tests) {\n if (test.routes.length === 0) {\n unroutedSkipped.push(test);\n continue;\n }\n if (!test.routes.some((route) => affected.has(route))) continue;\n if (!matchesFilters(test, filters)) continue;\n selected.push(test);\n }\n\n const covered = new Set(selected.flatMap((test) => test.routes));\n return {\n selected,\n unroutedSkipped,\n uncoveredRoutes: affectedRoutes.filter((route) => !covered.has(route)),\n };\n}\n","import { planCommand } from './plan.js';\nimport { EXIT_FAILED, EXIT_OK, EXIT_USAGE, runCommand } from './run.js';\n\nexport interface TestOptions {\n cwd: string;\n /** Base git ref for the diff (default \"main\"). */\n base?: string;\n /** Overrides config base_url for this run only. */\n url?: string;\n /** Minimum weighted score; replaces the all-must-pass rule when given. */\n minScore?: number;\n junit?: string | boolean;\n html?: string | boolean;\n /** Persist generated drafts instead of previewing them. */\n write?: boolean;\n /** Fail when the diff contains a file matching neither routes: nor ignore:. */\n failOnUnmapped?: boolean;\n}\n\nfunction section(title: string): void {\n console.log(`\\n=== ${title} ${'='.repeat(Math.max(0, 58 - title.length))}`);\n}\n\n/**\n * `blastproof test`: the diff-driven one-shot — execute the tests covering the\n * affected routes, then draft tests for the affected routes nothing covers.\n *\n * Drafts are reported, never executed (design D1). A model-written test that has\n * not been reviewed must not decide a merge: a hallucinated expectation would block\n * a correct change, and a credulous one would wave a broken change through while\n * looking like coverage. So `test` makes a gap visible; it does not close it.\n *\n * Exit codes: 2 usage/config/diff, 1 gate failure or draft generation failure,\n * 0 otherwise (design D2).\n */\nexport async function testCommand(options: TestOptions): Promise<number> {\n section('Verify: tests covering the affected routes');\n const runCode = await runCommand({\n cwd: options.cwd,\n tags: [],\n impacted: true,\n base: options.base,\n url: options.url,\n minScore: options.minScore,\n junit: options.junit,\n html: options.html,\n failOnUnmapped: options.failOnUnmapped,\n });\n if (runCode === EXIT_USAGE) return EXIT_USAGE;\n\n section('Draft: affected routes no test covers');\n const planCode = await planCommand({\n cwd: options.cwd,\n base: options.base,\n url: options.url,\n write: options.write,\n });\n if (planCode === EXIT_USAGE) return EXIT_USAGE;\n\n console.log(\n '\\nDrafts are not executed and do not affect the score — review them before ' +\n 'they join the suite.',\n );\n\n return runCode === EXIT_OK && planCode === EXIT_OK ? EXIT_OK : EXIT_FAILED;\n}\n"],"mappings":"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","names":["path","chromium","writeFile","mkdir","path","mkdir","path","mkdir","path","path","mkdir","writeFile","readFile","path","section","z","writeFile","path","readFile","path","parseYaml","z","formatIssues","path","writeFile","path","mkdir","readFile","writeFile","path","mkdir","writeFile","path","ESCAPES","seconds","path","path","chromium"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/commands/init.ts","../src/commands/plan.ts","../src/auth.ts","../src/runner/env.ts","../src/runner/executor.ts","../src/runner/actions.ts","../src/config.ts","../src/diff.ts","../src/impact.ts","../src/llm/brain.ts","../src/llm/prompts.ts","../src/llm/schemas.ts","../src/llm/provider.ts","../src/planner.ts","../src/runner/testfile.ts","../src/commands/run.ts","../src/report/html.ts","../src/report/junit.ts","../src/report/score.ts","../src/runner/selection.ts","../src/commands/test.ts"],"sourcesContent":["import { Command, InvalidArgumentError } from 'commander';\nimport { formatInitGuidance, initProject } from './commands/init.js';\nimport { planCommand } from './commands/plan.js';\nimport { EXIT_OK, EXIT_USAGE, runCommand } from './commands/run.js';\nimport { testCommand } from './commands/test.js';\nimport type { Priority } from './runner/testfile.js';\n\n/** Injected from package.json at build time (see tsup.config.ts). */\ndeclare const __BLASTPROOF_VERSION__: string;\n\nfunction collect(value: string, previous: string[]): string[] {\n return [...previous, value];\n}\n\nfunction parsePriority(value: string): Priority {\n if (value !== 'P0' && value !== 'P1' && value !== 'P2') {\n throw new InvalidArgumentError('priority must be one of P0, P1, P2');\n }\n return value;\n}\n\nfunction parseMinScore(value: string): number {\n const score = Number(value);\n if (!Number.isInteger(score) || score < 0 || score > 100) {\n throw new InvalidArgumentError('min-score must be an integer between 0 and 100');\n }\n return score;\n}\n\nconst program = new Command();\n\nprogram\n .name('blastproof')\n .description('Open-source AI testing agent: plain-English YAML tests executed agentically on a real browser.')\n .version(__BLASTPROOF_VERSION__);\n\nprogram\n .command('init')\n .description('Scaffold .blastproof/ (config, tests, sample tests) in the current directory')\n .action(async () => {\n try {\n const result = await initProject(process.cwd());\n console.log(formatInitGuidance(result));\n process.exitCode = EXIT_OK;\n } catch (error) {\n console.error(`error: ${error instanceof Error ? error.message : String(error)}`);\n process.exitCode = EXIT_USAGE;\n }\n });\n\nprogram\n .command('run')\n .description('Discover and run all tests under .blastproof/tests/ agentically')\n .option('--tag <tag>', 'only run tests with this tag (repeatable)', collect, [])\n .option('--priority <priority>', 'only run tests with this priority (P0|P1|P2)', parsePriority)\n .option('--query <text>', 'only run tests whose summary contains <text> (case-insensitive)')\n .option('--impacted', 'run only tests impacted by the diff vs --base (uses routes: mappings)')\n .option('--base <ref>', 'base git ref for --impacted', 'main')\n .option('--url <url>', 'override config base_url for this run only (config file untouched)')\n .option('--dry-run', 'print the selection plan and exit without launching a browser or calling the LLM')\n .option(\n '--min-score <n>',\n 'require a weighted score of at least n (0-100); replaces the all-must-pass rule',\n parseMinScore,\n )\n .option('--junit [path]', 'write a JUnit XML report (default: .blastproof/reports/<session>/junit.xml)')\n .option('--html [path]', 'write a self-contained HTML report (default: .blastproof/reports/<session>/report.html)')\n .option('--fail-on-unmapped', 'fail when a changed file matches no routes: or ignore: glob')\n .action(\n async (options: {\n tag: string[];\n priority?: Priority;\n query?: string;\n impacted?: boolean;\n base: string;\n url?: string;\n dryRun?: boolean;\n minScore?: number;\n junit?: string | boolean;\n html?: string | boolean;\n failOnUnmapped?: boolean;\n }) => {\n try {\n process.exitCode = await runCommand({\n cwd: process.cwd(),\n tags: options.tag,\n priority: options.priority,\n query: options.query,\n impacted: options.impacted,\n base: options.base,\n url: options.url,\n dryRun: options.dryRun,\n minScore: options.minScore,\n junit: options.junit,\n html: options.html,\n failOnUnmapped: options.failOnUnmapped,\n });\n } catch (error) {\n console.error(`error: ${error instanceof Error ? error.message : String(error)}`);\n process.exitCode = EXIT_USAGE;\n }\n },\n );\n\nprogram\n .command('plan')\n .description('Generate plain-English YAML tests for affected routes no test covers yet')\n .option('--base <ref>', 'base git ref for the diff', 'main')\n .option('--url <url>', 'override config base_url for this run only (config file untouched)')\n .option('--route <route>', 'generate for this route, bypassing the diff (repeatable)', collect, [])\n .option('--write', 'persist drafts under .blastproof/tests/ instead of previewing them')\n .action(async (options: { base: string; url?: string; route: string[]; write?: boolean }) => {\n try {\n process.exitCode = await planCommand({\n cwd: process.cwd(),\n base: options.base,\n url: options.url,\n routes: options.route,\n write: options.write,\n });\n } catch (error) {\n console.error(`error: ${error instanceof Error ? error.message : String(error)}`);\n process.exitCode = EXIT_USAGE;\n }\n });\n\nprogram\n .command('test')\n .description('Full PR pipeline: run the tests covering the diff, then draft tests for the gaps')\n .option('--base <ref>', 'base git ref for the diff', 'main')\n .option('--url <url>', 'override config base_url for this run only (config file untouched)')\n .option(\n '--min-score <n>',\n 'require a weighted score of at least n (0-100); replaces the all-must-pass rule',\n parseMinScore,\n )\n .option('--junit [path]', 'write a JUnit XML report (default: .blastproof/reports/<session>/junit.xml)')\n .option('--html [path]', 'write a self-contained HTML report (default: .blastproof/reports/<session>/report.html)')\n .option('--write', 'persist generated drafts under .blastproof/tests/ instead of previewing them')\n .option('--fail-on-unmapped', 'fail when a changed file matches no routes: or ignore: glob')\n .action(\n async (options: {\n base: string;\n url?: string;\n minScore?: number;\n junit?: string | boolean;\n html?: string | boolean;\n write?: boolean;\n failOnUnmapped?: boolean;\n }) => {\n try {\n process.exitCode = await testCommand({\n cwd: process.cwd(),\n base: options.base,\n url: options.url,\n minScore: options.minScore,\n junit: options.junit,\n html: options.html,\n write: options.write,\n failOnUnmapped: options.failOnUnmapped,\n });\n } catch (error) {\n console.error(`error: ${error instanceof Error ? error.message : String(error)}`);\n process.exitCode = EXIT_USAGE;\n }\n },\n );\n\n// Commander exits 1 on a bad flag by default; usage errors are exit 2 here (spec:\n// cli-run-command). It writes the message itself, so we only map the outcome.\n// exitOverride is per-Command and is not inherited, so subcommands need it too.\nprogram.exitOverride();\nfor (const command of program.commands) command.exitOverride();\n\ntry {\n await program.parseAsync(process.argv);\n} catch (error) {\n const exitCode = (error as { exitCode?: number }).exitCode ?? EXIT_USAGE;\n process.exitCode = exitCode === 0 ? EXIT_OK : EXIT_USAGE;\n}\n","import { mkdir, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport { access } from 'node:fs/promises';\n\nconst DEFAULT_CONFIG = `# blastproof configuration\n# Docs: https://github.com/hamc/blastproof\n\n# Base URL of the app under test. Relative navigate paths resolve against it.\nbase_url: http://localhost:4173\n\nllm:\n # anthropic | openai | ollama\n provider: anthropic\n # Optional. Defaults: anthropic=claude-haiku-4-5, openai=gpt-4o-mini, ollama=qwen2.5\n # model: claude-haiku-4-5\n # Env var holding your API key (not needed for ollama).\n api_key_env: ANTHROPIC_API_KEY\n # OpenAI-compatible endpoint: required for ollama (http://localhost:11434/v1);\n # with provider \"openai\" it enables compatible gateways, e.g. OpenRouter:\n # base_url: https://openrouter.ai/api/v1 (model e.g. anthropic/claude-haiku-4.5)\n\nbrowser:\n headless: true\n timeout_ms: 30000\n\n# Budget of self-healing retries per step (element re-resolution via accessibility tree).\n# max_retries_per_step: 3\n\n# Impact hints mapping file globs to routes (used by \\`blastproof run --impacted\\`).\nroutes:\n \"src/auth/**\": [\"/login\"]\n \"src/cart/**\": [\"/cart\", \"/checkout\"]\n\n# Files knowingly irrelevant to any route. Nothing is ignored by default: a file\n# nobody has classified is exactly the risk \\`--fail-on-unmapped\\` exists to surface,\n# so the list is yours to state rather than ours to guess.\n# ignore:\n# - \"**/*.md\"\n# - \"docs/**\"\n# - \".github/**\"\n# - \"LICENSE\"\n\n# Optional: authenticate once per run so tests and \\`plan\\` reach pages behind a login.\n# Pick exactly ONE strategy. Credentials come from the environment via {{env.VAR}} —\n# never write them here. A test can opt out with \\`auth: false\\` (a login test must).\n#\n# 1) A plain-English login journey, executed once (form login, any clickable flow):\n# auth:\n# steps:\n# - navigate to /login\n# - fill the email field with {{env.TEST_EMAIL}}\n# - fill the password field with {{env.TEST_PASSWORD}}\n# - submit the login form\n# verify: a signed-in indicator is visible # optional, but turns N failures into 1\n#\n# 2) A session captured by hand — for SSO, MFA or magic links, which cannot be\n# automated honestly. The file holds live session cookies: NEVER commit it.\n# auth:\n# storage_state: .blastproof/auth.json\n#\n# 3) Static values, for token-based apps where driving a UI would be theatre:\n# auth:\n# headers:\n# Authorization: \"Bearer {{env.API_TOKEN}}\"\n\n# CI tip: these settings can be overridden from the environment, so you never have\n# to commit a provider choice just to configure a pipeline. Precedence is\n# CLI flag > environment > this file.\n# BLASTPROOF_BASE_URL the app under test\n# BLASTPROOF_LLM_PROVIDER anthropic | openai | ollama\n# BLASTPROOF_LLM_MODEL\n# BLASTPROOF_LLM_BASE_URL the provider endpoint (not the app)\n# BLASTPROOF_LLM_API_KEY_ENV name of the variable holding your key, never the key\n`;\n\nconst SAMPLE_TEST = `summary: App loads and shows the home page\npriority: P1\ntags: [smoke]\n# Routes this test covers; run --impacted selects tests by these routes.\nroutes: [\"/\"]\nsteps:\n - verify the home page loads and shows a heading\n`;\n\n/**\n * Keeps captured sessions and run artifacts out of git. A storage state holds live\n * session cookies: whoever has the file is signed in as that user (design D7).\n */\nconst GITIGNORE = `# blastproof runtime artifacts\nreports/\nsessions/\n\n# Captured sessions are live credentials — never commit them.\n.auth-state.json\nauth.json\n`;\n\nconst SAMPLE_LOGIN_TEST = `summary: Login with valid credentials succeeds\npriority: P0\ntags: [smoke, auth]\nroutes: [\"/login\"]\n# This test exercises the login itself, so it must start signed out even when an\n# auth recipe is configured.\nauth: false\nsteps:\n - navigate to /login\n - fill the email field with demo@blastproof.dev\n - fill the password field with demo123\n - submit the login form\n - verify a welcome message is shown\n# Secrets tip: use {{env.VAR_NAME}} placeholders in steps (e.g. {{env.TEST_PASSWORD}})\n# and export the variable before running. Values are masked as *** in all output.\n`;\n\nexport interface InitResult {\n created: string[];\n kept: string[];\n}\n\nasync function exists(file: string): Promise<boolean> {\n try {\n await access(file);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function writeIfAbsent(file: string, content: string, result: InitResult): Promise<void> {\n if (await exists(file)) {\n result.kept.push(file);\n return;\n }\n await mkdir(path.dirname(file), { recursive: true });\n await writeFile(file, content);\n result.created.push(file);\n}\n\n/** Scaffolds `.blastproof/` in `cwd`. Idempotent: never overwrites existing files. */\nexport async function initProject(cwd: string = process.cwd()): Promise<InitResult> {\n const result: InitResult = { created: [], kept: [] };\n const root = path.join(cwd, '.blastproof');\n\n await writeIfAbsent(path.join(root, '.gitignore'), GITIGNORE, result);\n await writeIfAbsent(path.join(root, 'config.yaml'), DEFAULT_CONFIG, result);\n await writeIfAbsent(path.join(root, 'tests', 'app-load.yaml'), SAMPLE_TEST, result);\n await writeIfAbsent(path.join(root, 'tests', 'login.yaml'), SAMPLE_LOGIN_TEST, result);\n\n return result;\n}\n\nexport function formatInitGuidance(result: InitResult): string {\n const lines: string[] = [];\n if (result.created.length > 0) {\n lines.push('Created:');\n for (const file of result.created) lines.push(` ${file}`);\n }\n if (result.kept.length > 0) {\n lines.push('Kept existing (not overwritten):');\n for (const file of result.kept) lines.push(` ${file}`);\n }\n lines.push(\n '',\n 'Next steps:',\n ' 1. Set your LLM API key: export ANTHROPIC_API_KEY=... (or OPENAI_API_KEY; ollama needs no key)',\n ' 2. Point base_url at your app in .blastproof/config.yaml',\n ' 3. Run your tests: blastproof run',\n '',\n 'First time with Playwright? Install the browser: npx playwright install chromium',\n );\n return lines.join('\\n');\n}\n","import path from 'node:path';\nimport { chromium } from 'playwright';\nimport { authenticate, AuthError, contextOptions, type AuthSession, type BrowserLike } from '../auth.js';\nimport { ConfigError, loadConfig, type BlastproofConfig } from '../config.js';\nimport { DiffError, getChangedFiles } from '../diff.js';\nimport { mapImpact } from '../impact.js';\nimport { createBrain, createPlanner } from '../llm/brain.js';\nimport { createModel, MissingApiKeyError } from '../llm/provider.js';\nimport {\n coveredRoutes,\n generateForRoute,\n PlannerError,\n renderTestYaml,\n writeDraft,\n type TestDraft,\n} from '../planner.js';\nimport type { PageLike } from '../runner/actions.js';\nimport type { ExecutorEvent } from '../runner/executor.js';\nimport {\n discoverTestFiles,\n parseTestFile,\n TESTS_RELATIVE_DIR,\n TestFileError,\n type TestFile,\n} from '../runner/testfile.js';\nimport { applyUrlOverride, EXIT_FAILED, EXIT_OK, EXIT_USAGE } from './run.js';\n\nexport interface PlanOptions {\n cwd: string;\n /** Base git ref for the diff (default \"main\"). Ignored when `routes` is given. */\n base?: string;\n /** Overrides config base_url for this run only; the config file is never mutated. */\n url?: string;\n /** Explicit routes (`--route`), bypassing diff and impact analysis entirely. */\n routes?: string[];\n /** Persist drafts under `.blastproof/tests/` instead of previewing them. */\n write?: boolean;\n}\n\n/** Prints login-journey progress, so a failing authentication is not a black box. */\nfunction printAuthEvent(event: ExecutorEvent): void {\n if (event.type === 'step-start') {\n console.log(` step ${event.index + 1}/${event.total}: ${event.step}`);\n } else if (event.type === 'action') {\n console.log(` -> ${event.action.action} :: ${event.result}`);\n } else if (event.status === 'failed') {\n console.log(` X step failed: ${event.reason ?? 'unknown reason'}`);\n }\n}\n\n/** Loads the suite, tolerating unreadable files (a broken test must not block planning). */\nasync function loadSuite(cwd: string): Promise<TestFile[]> {\n const testsDir = path.join(cwd, TESTS_RELATIVE_DIR);\n let files: string[];\n try {\n files = await discoverTestFiles(testsDir);\n } catch {\n return [];\n }\n\n const parsed: TestFile[] = [];\n for (const file of files) {\n try {\n parsed.push(await parseTestFile(file));\n } catch (error) {\n if (error instanceof TestFileError) continue;\n throw error;\n }\n }\n return parsed;\n}\n\ninterface RouteWork {\n route: string;\n /** Changed files that mapped to this route; empty under `--route`. */\n changedFiles: string[];\n}\n\n/** Resolves which routes to generate for, plus the routes skipped as already covered. */\nasync function resolveTargets(\n options: PlanOptions,\n config: BlastproofConfig,\n suite: TestFile[],\n): Promise<{ work: RouteWork[]; alreadyCovered: string[] }> {\n const covered = coveredRoutes(suite);\n\n if (options.routes && options.routes.length > 0) {\n return { work: options.routes.map((route) => ({ route, changedFiles: [] })), alreadyCovered: [] };\n }\n\n const base = options.base ?? 'main';\n const changedFiles = await getChangedFiles(base, options.cwd);\n const impact = mapImpact(changedFiles, config.routes ?? {}, config.ignore ?? []);\n\n // Re-run the glob match per route so each generation prompt sees only the files\n // that made *that* route impacted (design D3), not the whole changed set.\n const filesForRoute = (route: string): string[] =>\n changedFiles.filter((file) =>\n mapImpact([file], config.routes ?? {}, config.ignore ?? []).affectedRoutes.includes(route),\n );\n\n const work: RouteWork[] = [];\n const alreadyCovered: string[] = [];\n for (const route of impact.affectedRoutes) {\n if (covered.has(route)) {\n alreadyCovered.push(route);\n continue;\n }\n work.push({ route, changedFiles: filesForRoute(route) });\n }\n return { work, alreadyCovered };\n}\n\n/**\n * `blastproof plan`: diff → impact → uncovered routes → snapshot-grounded test drafts.\n * Preview by default; `--write` persists without ever overwriting (design D7).\n * Returns the exit code: 0 all generated (or nothing to do), 1 any route failed,\n * 2 usage/config/diff error.\n */\nexport async function planCommand(options: PlanOptions): Promise<number> {\n let config: BlastproofConfig;\n try {\n config = applyUrlOverride(await loadConfig(options.cwd), options.url);\n } catch (error) {\n if (error instanceof ConfigError) {\n console.error(`error: ${error.message}`);\n return EXIT_USAGE;\n }\n throw error;\n }\n\n const suite = await loadSuite(options.cwd);\n\n let work: RouteWork[];\n let alreadyCovered: string[];\n try {\n ({ work, alreadyCovered } = await resolveTargets(options, config, suite));\n } catch (error) {\n if (error instanceof DiffError) {\n console.error(`error: ${error.message}`);\n return EXIT_USAGE;\n }\n throw error;\n }\n\n if (alreadyCovered.length > 0) {\n console.log('Already covered (skipped):');\n for (const route of alreadyCovered) console.log(` ${route}`);\n }\n\n // Nothing to generate: no browser, no LLM key required.\n if (work.length === 0) {\n console.log('Nothing to generate: no affected route is missing coverage.');\n return EXIT_OK;\n }\n\n // Fail fast on a missing API key before launching any browser, as `run` does.\n try {\n createModel(config.llm);\n } catch (error) {\n if (error instanceof MissingApiKeyError) {\n console.error(`error: ${error.message}`);\n return EXIT_USAGE;\n }\n throw error;\n }\n\n const { model, provider, modelId } = createModel(config.llm);\n const brain = createPlanner(model);\n const base = options.routes && options.routes.length > 0 ? undefined : (options.base ?? 'main');\n console.log(\n `blastproof plan: ${work.length} route(s), provider=${provider} model=${modelId}, base_url=${config.base_url}`,\n );\n\n const generated: string[] = [];\n const written: string[] = [];\n const failed: { route: string; reason: string }[] = [];\n\n const browser = await chromium.launch({ headless: config.browser.headless });\n try {\n // The planner authenticates too (design D8): without a session it would snapshot\n // the login wall and draft a test for that instead of for the actual feature.\n let session: AuthSession | undefined;\n if (config.auth) {\n try {\n console.log('Authenticating...');\n session = await authenticate({\n auth: config.auth,\n cwd: options.cwd,\n baseUrl: config.base_url,\n browser: browser as unknown as BrowserLike,\n brain: createBrain(createModel(config.llm).model),\n maxRetries: config.max_retries_per_step,\n onEvent: printAuthEvent,\n });\n } catch (error) {\n if (error instanceof AuthError) {\n console.error(`error: ${error.message}`);\n return EXIT_USAGE;\n }\n throw error;\n }\n }\n\n for (const { route, changedFiles } of work) {\n console.log(`\\n> ${route}`);\n let draft: TestDraft;\n const context = await browser.newContext(contextOptions(session));\n try {\n const page = await context.newPage();\n page.setDefaultTimeout(config.browser.timeout_ms);\n draft = await generateForRoute(page as unknown as PageLike, {\n route,\n baseUrl: config.base_url,\n changedFiles,\n brain,\n });\n } catch (error) {\n // Per-route isolation (design D9): report and keep going.\n failed.push({ route, reason: error instanceof Error ? error.message : String(error) });\n console.log(` X failed: ${error instanceof Error ? error.message : String(error)}`);\n continue;\n } finally {\n await context.close();\n }\n\n generated.push(route);\n if (options.write) {\n try {\n const file = await writeDraft(options.cwd, draft, { route, base });\n written.push(path.relative(options.cwd, file));\n console.log(` wrote ${path.relative(options.cwd, file)}`);\n } catch (error) {\n if (error instanceof PlannerError) {\n generated.pop();\n failed.push({ route, reason: error.message });\n console.log(` X failed: ${error.message}`);\n continue;\n }\n throw error;\n }\n } else {\n console.log(`\\n${renderTestYaml(draft, { route, base })}`);\n }\n }\n } finally {\n await browser.close();\n }\n\n console.log('\\n--- Plan -------------------------------------------------------');\n console.log(`Generated: ${generated.length > 0 ? generated.join(', ') : 'none'}`);\n if (written.length > 0) {\n console.log('Written:');\n for (const file of written) console.log(` ${file}`);\n }\n if (!options.write && generated.length > 0) {\n console.log('Preview only: no files written. Re-run with --write to persist.');\n }\n if (failed.length > 0) {\n console.log('Failed:');\n for (const { route, reason } of failed) console.log(` ${route}: ${reason}`);\n }\n console.log('---------------------------------------------------------------');\n\n return failed.length > 0 ? EXIT_FAILED : EXIT_OK;\n}\n","import { readFile, writeFile, mkdir } from 'node:fs/promises';\nimport path from 'node:path';\nimport type { AuthConfig } from './config.js';\nimport type { AgentBrain } from './llm/brain.js';\nimport type { PageLike } from './runner/actions.js';\nimport { SecretsMask, substituteEnv } from './runner/env.js';\nimport { defaultSnapshot, executeTest, type ExecutorEvent } from './runner/executor.js';\n\n/** Where a captured session is cached when `auth.cache` is enabled (design D9). */\nexport const AUTH_STATE_RELATIVE_PATH = path.join('.blastproof', '.auth-state.json');\n\nexport class AuthError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'AuthError';\n }\n}\n\n/** Playwright storage state: cookies plus per-origin storage. */\nexport interface StorageState {\n cookies: unknown[];\n origins: unknown[];\n}\n\n/**\n * The single internal contract every strategy produces (design D1). Consumers seed\n * a browser context with it and never learn how the session was obtained.\n */\nexport interface AuthSession {\n storageState?: StorageState;\n extraHTTPHeaders?: Record<string, string>;\n}\n\n/** Narrow Playwright subset the authenticator needs; real objects are compatible. */\nexport interface ContextLike {\n newPage(): Promise<PageLike>;\n storageState(): Promise<StorageState>;\n close(): Promise<void>;\n}\n\nexport interface BrowserLike {\n newContext(options?: Record<string, unknown>): Promise<ContextLike>;\n}\n\nexport interface AuthenticateOptions {\n auth: AuthConfig;\n cwd: string;\n baseUrl: string;\n browser: BrowserLike;\n brain: AgentBrain;\n maxRetries?: number;\n /** Injectable snapshotter, mirroring the executor, so tests need no browser. */\n snapshot?: (page: PageLike) => Promise<string>;\n /**\n * Progress reporter for the login journey. Without it authentication is a black\n * box, and a failing login is the moment you most need to see what happened.\n */\n onEvent?: (event: ExecutorEvent) => void;\n}\n\nfunction isStorageState(value: unknown): value is StorageState {\n return (\n typeof value === 'object' &&\n value !== null &&\n Array.isArray((value as StorageState).cookies) &&\n Array.isArray((value as StorageState).origins)\n );\n}\n\n/** Reads a previously captured state, failing with the path when unusable. */\nasync function fromStorageState(file: string): Promise<AuthSession> {\n let raw: string;\n try {\n raw = await readFile(file, 'utf8');\n } catch {\n throw new AuthError(\n `Cannot read auth.storage_state at ${file}. Capture a session first, or switch to an auth.steps recipe.`,\n );\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n throw new AuthError(`Invalid JSON in auth.storage_state at ${file}.`);\n }\n if (!isStorageState(parsed)) {\n throw new AuthError(\n `Invalid storage state at ${file}: expected an object with \"cookies\" and \"origins\" arrays.`,\n );\n }\n return { storageState: parsed };\n}\n\n/**\n * Builds a session from static values (design D2). `{{env.*}}` placeholders are\n * substituted here, so a token never has to be written into the config file.\n */\nfunction fromStatic(auth: AuthConfig): { session: AuthSession; mask: SecretsMask } {\n const mask = new SecretsMask();\n const session: AuthSession = {};\n\n if (auth.headers) {\n const headers: Record<string, string> = {};\n for (const [name, value] of Object.entries(auth.headers)) {\n mask.registerFrom(value);\n headers[name] = substituteEnv(value);\n }\n session.extraHTTPHeaders = headers;\n }\n\n if (auth.cookies) {\n const cookies = auth.cookies.map((cookie) => {\n mask.registerFrom(cookie.value);\n return { ...cookie, value: substituteEnv(cookie.value) };\n });\n session.storageState = { cookies, origins: [] };\n }\n\n return { session, mask };\n}\n\n/** Runs the journey, converting any thrown error into an AuthError. */\nasync function runJourney(\n ...args: Parameters<typeof executeTest>\n): ReturnType<typeof executeTest> {\n try {\n return await executeTest(...args);\n } catch (error) {\n throw new AuthError(\n `Authentication could not run: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n}\n\n/**\n * Runs the login journey once in an empty context and captures the resulting\n * session (design D2/D3). Reuses the agentic executor, so a login is described in\n * exactly the same plain English as a test.\n */\nasync function fromSteps(options: AuthenticateOptions): Promise<AuthSession> {\n const { auth, baseUrl, browser, brain, maxRetries, snapshot = defaultSnapshot, onEvent } = options;\n const steps = auth.steps!;\n\n const mask = new SecretsMask();\n for (const step of steps) mask.registerFrom(step);\n\n const context = await browser.newContext();\n try {\n const page = await context.newPage();\n // The executor's opening navigation happens outside its per-step error handling,\n // so an unreachable app throws raw here. Every failure in this function must\n // surface as an AuthError with an actionable message (design D6).\n const result = await runJourney(\n page,\n {\n path: '<auth>',\n summary: 'authentication',\n steps: steps.map((step) => substituteEnv(step)),\n priority: 'P0',\n tags: [],\n routes: [],\n auth: false,\n },\n {\n brain,\n sessionDir: path.join(options.cwd, '.blastproof', 'reports', 'auth'),\n baseUrl,\n maxRetries,\n mask: (text) => mask.mask(text),\n snapshot,\n onEvent,\n },\n );\n\n if (result.status === 'failed') {\n throw new AuthError(\n `Authentication failed at step \"${result.failedStep ?? 'unknown'}\": ${result.reason ?? 'no reason given'}`,\n );\n }\n\n // Optional post-login check: turns N mysterious test failures into one message (D5).\n if (auth.verify) {\n const judgment = await brain.judge(auth.verify, await snapshot(page));\n if (!judgment.pass) {\n throw new AuthError(`Authentication could not be verified: ${mask.mask(judgment.reason)}`);\n }\n }\n\n return { storageState: await context.storageState() };\n } finally {\n await context.close();\n }\n}\n\n/** Reads the cached session, or undefined when absent or unusable. */\nasync function readCached(cwd: string): Promise<AuthSession | undefined> {\n try {\n return await fromStorageState(path.join(cwd, AUTH_STATE_RELATIVE_PATH));\n } catch {\n return undefined;\n }\n}\n\n/** Persists the captured session. Never logged: it is a live credential (design D7). */\nasync function writeCached(cwd: string, session: AuthSession): Promise<void> {\n if (!session.storageState) return;\n const file = path.join(cwd, AUTH_STATE_RELATIVE_PATH);\n await mkdir(path.dirname(file), { recursive: true });\n await writeFile(file, JSON.stringify(session.storageState), 'utf8');\n}\n\n/**\n * Produces the authenticated session for a run, at most once (design D3).\n * Throws {@link AuthError} with an actionable message; callers abort before\n * executing any test, since a failed login is a configuration problem and must\n * not be reported as failing tests (design D6).\n */\nexport async function authenticate(options: AuthenticateOptions): Promise<AuthSession> {\n const { auth, cwd } = options;\n\n if (auth.cache) {\n const cached = await readCached(cwd);\n if (cached) return cached;\n }\n\n if (auth.storage_state) {\n return fromStorageState(path.resolve(cwd, auth.storage_state));\n }\n\n if (auth.headers || auth.cookies) {\n return fromStatic(auth).session;\n }\n\n const session = await fromSteps(options);\n if (auth.cache) await writeCached(cwd, session);\n return session;\n}\n\n/** Context options that seed a browser context with the session, if any. */\nexport function contextOptions(session: AuthSession | undefined): Record<string, unknown> {\n if (!session) return {};\n return {\n ...(session.storageState ? { storageState: session.storageState } : {}),\n ...(session.extraHTTPHeaders ? { extraHTTPHeaders: session.extraHTTPHeaders } : {}),\n };\n}\n","const ENV_PLACEHOLDER = /\\{\\{\\s*env\\.([A-Za-z_][A-Za-z0-9_]*)\\s*\\}\\}/g;\n\nexport class MissingEnvError extends Error {\n constructor(variable: string) {\n super(\n `Environment variable ${variable} is referenced via {{env.${variable}}} but is not set. ` +\n `Export it before running, e.g. \\`export ${variable}=...\\`.`,\n );\n this.name = 'MissingEnvError';\n }\n}\n\ntype EnvSource = Record<string, string | undefined>;\n\n/** Substitutes `{{env.VAR}}` placeholders from the environment. Throws MissingEnvError on unset vars. */\nexport function substituteEnv(text: string, env: EnvSource = process.env): string {\n return text.replace(ENV_PLACEHOLDER, (_match, name: string) => {\n const value = env[name];\n if (value === undefined) {\n throw new MissingEnvError(name);\n }\n return value;\n });\n}\n\n/** Returns the names of all env vars referenced by `{{env.VAR}}` placeholders in `text`. */\nexport function referencedEnvVars(text: string): string[] {\n const names = new Set<string>();\n for (const match of text.matchAll(ENV_PLACEHOLDER)) {\n if (match[1]) names.add(match[1]);\n }\n return [...names];\n}\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\n/** Replaces every occurrence of each secret value with `***`. Empty values are ignored. */\nexport function maskSecrets(text: string, secrets: Iterable<string>): string {\n // Longest first so a short secret never masks inside a longer one (e.g. `demo` inside `demo123`).\n const ordered = [...secrets].filter(Boolean).sort((a, b) => b.length - a.length);\n let masked = text;\n for (const secret of ordered) {\n masked = masked.replace(new RegExp(escapeRegExp(secret), 'g'), '***');\n }\n return masked;\n}\n\n/**\n * Stateful masker: register secret values once (e.g. per test), mask all output channels.\n */\nexport class SecretsMask {\n private readonly secrets = new Set<string>();\n\n /** Registers the current values of the env vars referenced in `text`. Throws MissingEnvError on unset vars. */\n registerFrom(text: string, env: EnvSource = process.env): void {\n for (const name of referencedEnvVars(text)) {\n const value = env[name];\n if (value === undefined) {\n throw new MissingEnvError(name);\n }\n this.secrets.add(value);\n }\n }\n\n mask(text: string): string {\n return maskSecrets(text, this.secrets);\n }\n}\n","import { mkdir } from 'node:fs/promises';\nimport path from 'node:path';\nimport type { AgentBrain } from '../llm/brain.js';\nimport type { AgentAction } from '../llm/schemas.js';\nimport type { TestFile } from './testfile.js';\nimport { performAction, type PageLike } from './actions.js';\n\nexport interface StepResult {\n step: string;\n setup: boolean;\n status: 'passed' | 'failed';\n iterations: number;\n failedAttempts: number;\n reason?: string;\n durationMs: number;\n}\n\nexport interface TestResult {\n file: string;\n summary: string;\n priority: string;\n tags: string[];\n status: 'passed' | 'failed';\n steps: StepResult[];\n /** Failing step text + reason, for summary reporting. */\n failedStep?: string;\n reason?: string;\n screenshot?: string;\n durationMs: number;\n}\n\nexport type ExecutorEvent =\n | { type: 'step-start'; index: number; total: number; step: string; setup: boolean }\n | { type: 'action'; index: number; action: AgentAction; result: string }\n | { type: 'step-end'; index: number; status: 'passed' | 'failed'; reason?: string };\n\nexport interface ExecutorOptions {\n brain: AgentBrain;\n /** Session directory for failure screenshots, e.g. `.blastproof/reports/<session>`. */\n sessionDir: string;\n baseUrl: string;\n /** Budget of failed attempts per step (self-healing retries). Default 3. */\n maxRetries?: number;\n /** Hard cap on LLM actions per step. Default 15. */\n maxIterationsPerStep?: number;\n /** Masks secrets from every emitted/logged string. */\n mask?: (text: string) => string;\n /** Injectable snapshotter (defaults to live ariaSnapshot via {@link defaultSnapshot}). */\n snapshot?: (page: PageLike) => Promise<string>;\n onEvent?: (event: ExecutorEvent) => void;\n}\n\n/** Live snapshot via Playwright ariaSnapshot; kept here so tests can inject a fake. */\nexport async function defaultSnapshot(page: PageLike): Promise<string> {\n const { captureSnapshot } = await import('./snapshot.js');\n return captureSnapshot(page as never);\n}\n\nfunction slugify(text: string): string {\n const slug = text\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 60);\n return slug || 'test';\n}\n\nclass StepFailure extends Error {}\n\n/**\n * Executes one test against a page from a fresh browser context.\n * The loop per step: snapshot → LLM action → perform → repeat, until\n * done/fail or budget exhaustion (design D3, spec agentic-execution).\n */\nexport async function executeTest(page: PageLike, test: TestFile, options: ExecutorOptions): Promise<TestResult> {\n const {\n brain,\n sessionDir,\n baseUrl,\n maxRetries = 3,\n maxIterationsPerStep = 15,\n mask = (s: string) => s,\n snapshot = defaultSnapshot,\n onEvent = () => {},\n } = options;\n\n const startedAt = Date.now();\n const stepResults: StepResult[] = [];\n const allSteps = [\n ...(test.setup ?? []).map((step) => ({ step, setup: true })),\n ...test.steps.map((step) => ({ step, setup: false })),\n ];\n\n const emitAction = (index: number, action: AgentAction, result: string): void => {\n // The payload may contain substituted secrets — mask before it reaches any output channel.\n const maskedAction: AgentAction = {\n ...action,\n value: action.value === undefined ? undefined : mask(action.value),\n reasoning: mask(action.reasoning),\n expectation: action.expectation === undefined ? undefined : mask(action.expectation),\n };\n onEvent({ type: 'action', index, action: maskedAction, result: mask(result) });\n };\n\n let failure: { step: string; reason: string } | undefined;\n\n // Every test starts from the configured base_url (spec: browser lifecycle).\n await page.goto(new URL(baseUrl).toString());\n\n for (let index = 0; index < allSteps.length; index++) {\n const { step, setup } = allSteps[index]!;\n const stepStartedAt = Date.now();\n onEvent({ type: 'step-start', index, total: allSteps.length, step: mask(step), setup });\n\n let iterations = 0;\n let failedAttempts = 0;\n let lastResult: string | undefined;\n let stepFailedReason: string | undefined;\n\n try {\n while (true) {\n if (iterations >= maxIterationsPerStep) {\n throw new StepFailure(`step exceeded ${maxIterationsPerStep} actions without completing`);\n }\n\n const snap = await snapshot(page);\n\n let action: AgentAction;\n try {\n action = await brain.nextAction({\n step,\n isSetup: setup,\n snapshot: snap,\n lastResult,\n retriesLeft: maxRetries - failedAttempts,\n iterationsLeft: maxIterationsPerStep - iterations,\n });\n } catch (error) {\n // Malformed model output counts as a failed attempt (spec: structured output).\n failedAttempts++;\n lastResult = `error: ${error instanceof Error ? error.message : String(error)}`;\n if (failedAttempts >= maxRetries) {\n throw new StepFailure(`retry budget exhausted (${maxRetries}): ${lastResult}`);\n }\n continue;\n }\n iterations++;\n\n if (action.action === 'done') {\n emitAction(index, action, action.reasoning);\n break;\n }\n\n if (action.action === 'fail') {\n emitAction(index, action, action.reasoning);\n throw new StepFailure(action.reasoning);\n }\n\n if (action.action === 'assert') {\n const expectation = action.expectation ?? action.reasoning;\n const judgment = await brain.judge(expectation, snap);\n const result = judgment.pass\n ? `ok: assertion passed: ${judgment.reason}`\n : `assertion failed: ${judgment.reason}`;\n emitAction(index, action, result);\n if (judgment.pass) {\n lastResult = result;\n continue;\n }\n // A failed judgment may just mean the page hasn't settled: retry within budget.\n failedAttempts++;\n lastResult = result;\n if (failedAttempts >= maxRetries) {\n throw new StepFailure(judgment.reason);\n }\n continue;\n }\n\n try {\n const result = await performAction(page, action, { baseUrl });\n lastResult = result;\n emitAction(index, action, result);\n } catch (error) {\n // Self-healing: fresh snapshot next iteration, up to the retry budget.\n failedAttempts++;\n lastResult = `error: ${error instanceof Error ? error.message : String(error)}`;\n emitAction(index, action, lastResult);\n if (failedAttempts >= maxRetries) {\n throw new StepFailure(lastResult);\n }\n }\n }\n } catch (error) {\n // Mask everything that reaches logs/reports, regardless of throw site.\n stepFailedReason = mask(error instanceof Error ? error.message : String(error));\n }\n\n const status = stepFailedReason ? ('failed' as const) : ('passed' as const);\n stepResults.push({\n step: mask(step), // results are a report channel — never store substituted secrets\n setup,\n status,\n iterations,\n failedAttempts,\n reason: stepFailedReason,\n durationMs: Date.now() - stepStartedAt,\n });\n onEvent({ type: 'step-end', index, status, reason: stepFailedReason });\n\n if (stepFailedReason) {\n failure = { step: mask(step), reason: stepFailedReason };\n break; // stop at the first failing step; remaining steps are not executed\n }\n }\n\n let screenshot: string | undefined;\n if (failure) {\n try {\n await mkdir(sessionDir, { recursive: true });\n const file = path.join(sessionDir, `${slugify(test.summary)}.png`);\n await page.screenshot({ path: file, fullPage: true });\n screenshot = file;\n } catch {\n // never let a screenshot failure mask the real test failure\n }\n }\n\n return {\n file: test.path,\n summary: test.summary,\n priority: test.priority,\n tags: test.tags,\n status: failure ? 'failed' : 'passed',\n steps: stepResults,\n failedStep: failure?.step,\n reason: failure?.reason,\n screenshot,\n durationMs: Date.now() - startedAt,\n };\n}\n","import type { AgentAction, AgentTarget } from '../llm/schemas.js';\n\n/**\n * Narrow Playwright subset used by the runner. Real `Page`/`Locator` objects are\n * structurally compatible (cast once at the boundary) and tests use fakes.\n */\nexport interface LocatorLike {\n click(): Promise<void>;\n fill(value: string): Promise<void>;\n press(key: string): Promise<void>;\n selectOption(option: { label: string }): Promise<unknown>;\n waitFor(options?: { state?: 'attached' | 'visible'; timeout?: number }): Promise<void>;\n first(): LocatorLike;\n}\n\nexport interface PageLike {\n goto(url: string, options?: { timeout?: number }): Promise<unknown>;\n getByRole(role: string, options?: { name?: string }): LocatorLike;\n getByLabel(text: string): LocatorLike;\n getByText(text: string): LocatorLike;\n keyboard: { press(key: string): Promise<void> };\n screenshot(options: { path: string; fullPage?: boolean }): Promise<unknown>;\n url(): string;\n}\n\nexport class ActionError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ActionError';\n }\n}\n\nexport interface ActionContext {\n baseUrl: string;\n /** Per-resolution wait budget in ms (each fallback candidate gets a short wait). */\n resolveTimeoutMs?: number;\n}\n\nfunction describeTarget(target: AgentTarget): string {\n const parts: string[] = [];\n if (target.role) parts.push(`role=${target.role}`);\n if (target.name) parts.push(`name=\"${target.name}\"`);\n if (target.text) parts.push(`text=\"${target.text}\"`);\n return parts.join(' ') || '(no target)';\n}\n\n/**\n * Resolves an element from the live accessibility tree only (self-healing, design D4):\n * getByRole → getByLabel → getByText, each with a short visibility wait.\n */\nexport async function resolveTarget(\n page: PageLike,\n target: AgentTarget,\n resolveTimeoutMs = 2_000,\n): Promise<LocatorLike> {\n const candidates: LocatorLike[] = [];\n if (target.role) {\n candidates.push(page.getByRole(target.role, target.name ? { name: target.name } : {}));\n }\n if (target.name) {\n candidates.push(page.getByLabel(target.name));\n }\n const text = target.text ?? target.name;\n if (text) {\n candidates.push(page.getByText(text));\n }\n\n for (const candidate of candidates) {\n const locator = candidate.first();\n try {\n await locator.waitFor({ state: 'visible', timeout: resolveTimeoutMs });\n return locator;\n } catch {\n // try the next resolution strategy\n }\n }\n throw new ActionError(`Element not found: ${describeTarget(target)}`);\n}\n\nfunction requireTarget(action: AgentAction): AgentTarget {\n if (!action.target || (!action.target.role && !action.target.name && !action.target.text)) {\n throw new ActionError(`Action \"${action.action}\" requires a target (role/name/text)`);\n }\n return action.target;\n}\n\nfunction requireValue(action: AgentAction): string {\n if (action.value === undefined || action.value === '') {\n throw new ActionError(`Action \"${action.action}\" requires a value`);\n }\n return action.value;\n}\n\n/**\n * Performs one agent action on the page. `assert`/`done`/`fail` are control\n * actions handled by the executor and rejected here.\n * Returns a short human-readable result string fed back into the next prompt.\n */\nexport async function performAction(\n page: PageLike,\n action: AgentAction,\n ctx: ActionContext,\n): Promise<string> {\n switch (action.action) {\n case 'navigate': {\n const value = requireValue(action);\n const url = new URL(value, ctx.baseUrl).toString();\n await page.goto(url, { timeout: 30_000 });\n return `ok: navigated to ${url}`;\n }\n case 'click': {\n const target = requireTarget(action);\n const locator = await resolveTarget(page, target, ctx.resolveTimeoutMs);\n await locator.click();\n return `ok: clicked ${describeTarget(target)}`;\n }\n case 'fill': {\n const target = requireTarget(action);\n const value = requireValue(action);\n const locator = await resolveTarget(page, target, ctx.resolveTimeoutMs);\n await locator.fill(value);\n return `ok: filled ${describeTarget(target)}`;\n }\n case 'press': {\n const key = action.value ?? 'Enter';\n if (action.target && (action.target.role || action.target.name || action.target.text)) {\n const locator = await resolveTarget(page, action.target, ctx.resolveTimeoutMs);\n await locator.press(key);\n return `ok: pressed ${key} on ${describeTarget(action.target)}`;\n }\n await page.keyboard.press(key);\n return `ok: pressed ${key}`;\n }\n case 'select': {\n const target = requireTarget(action);\n const value = requireValue(action);\n const locator = await resolveTarget(page, target, ctx.resolveTimeoutMs);\n await locator.selectOption({ label: value });\n return `ok: selected \"${value}\" in ${describeTarget(target)}`;\n }\n case 'assert':\n case 'done':\n case 'fail':\n throw new ActionError(`Action \"${action.action}\" is handled by the executor, not the page`);\n }\n}\n","import { readFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport { parse as parseYaml } from 'yaml';\nimport { z } from 'zod';\n\nexport const CONFIG_RELATIVE_PATH = path.join('.blastproof', 'config.yaml');\n\nconst llmSchema = z.object({\n provider: z.enum(['anthropic', 'openai', 'ollama']).default('anthropic'),\n model: z.string().min(1).optional(),\n api_key_env: z.string().min(1).optional(),\n base_url: z.string().url().optional(),\n});\n\nconst browserSchema = z.object({\n headless: z.boolean().default(true),\n timeout_ms: z.number().int().positive().default(30_000),\n});\n\nconst cookieSchema = z.object({\n name: z.string().min(1),\n value: z.string(),\n domain: z.string().optional(),\n path: z.string().optional(),\n url: z.string().optional(),\n});\n\n/**\n * Authentication recipe. Exactly one strategy, chosen by which field is present\n * (design D2): a plain-English login journey, a previously captured storage state,\n * or static headers/cookies. All three converge on one authenticated session (D1).\n */\nconst authSchema = z\n .object({\n steps: z.array(z.string().min(1)).min(1).optional(),\n storage_state: z.string().min(1).optional(),\n headers: z.record(z.string()).optional(),\n cookies: z.array(cookieSchema).min(1).optional(),\n verify: z.string().min(1).optional(),\n cache: z.boolean().default(false),\n })\n .superRefine((value, ctx) => {\n const present: string[] = [];\n if (value.steps) present.push('steps');\n if (value.storage_state) present.push('storage_state');\n if (value.headers || value.cookies) {\n present.push([value.headers && 'headers', value.cookies && 'cookies'].filter(Boolean).join('+'));\n }\n\n if (present.length === 0) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message:\n 'configure one auth strategy: steps (login journey), storage_state (captured session), or headers/cookies',\n });\n return;\n }\n if (present.length > 1) {\n // Silently preferring one would make a typo look like it worked.\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `configure exactly one auth strategy, found ${present.join(' and ')}`,\n });\n }\n });\n\nconst configSchema = z.object({\n base_url: z.string().url(),\n llm: llmSchema.default({}),\n browser: browserSchema.default({}),\n routes: z.record(z.array(z.string())).optional(),\n /** Globs for files knowingly irrelevant to any route (docs, CI config, licences). */\n ignore: z.array(z.string()).optional(),\n auth: authSchema.optional(),\n max_retries_per_step: z.number().int().min(1).default(3),\n});\n\nexport type BlastproofConfig = z.infer<typeof configSchema>;\nexport type LlmConfig = BlastproofConfig['llm'];\nexport type BrowserConfig = BlastproofConfig['browser'];\nexport type AuthConfig = NonNullable<BlastproofConfig['auth']>;\n\n/**\n * Environment overrides, one entry per settable field (design D2). `base_url` (the\n * app under test) and `llm.base_url` (the provider endpoint) are deliberately kept\n * apart by name: silently overriding the wrong one would point the browser at an\n * LLM gateway, and that failure is baffling to debug.\n */\nexport const ENV_OVERRIDES: Record<string, readonly [string] | readonly [string, string]> = {\n BLASTPROOF_BASE_URL: ['base_url'],\n BLASTPROOF_LLM_PROVIDER: ['llm', 'provider'],\n BLASTPROOF_LLM_MODEL: ['llm', 'model'],\n BLASTPROOF_LLM_BASE_URL: ['llm', 'base_url'],\n BLASTPROOF_LLM_API_KEY_ENV: ['llm', 'api_key_env'],\n};\n\nexport interface OverrideResult {\n /** Parsed config with environment values merged in, before validation. */\n data: Record<string, unknown>;\n /** Names of the variables that actually contributed, for error attribution. */\n applied: string[];\n}\n\n/**\n * Merges environment overrides into the parsed YAML *before* zod runs (design D4),\n * so an invalid override fails with the same actionable error as an invalid file\n * and per-field defaulting still applies. Pure; `env` is injectable for tests.\n * An empty string counts as unset, so `FOO=` in a CI matrix never blanks a value.\n */\nexport function applyEnvOverrides(\n data: unknown,\n env: Record<string, string | undefined> = process.env,\n): OverrideResult {\n const merged: Record<string, unknown> =\n data && typeof data === 'object' ? { ...(data as Record<string, unknown>) } : {};\n const applied: string[] = [];\n\n for (const [variable, keyPath] of Object.entries(ENV_OVERRIDES)) {\n const value = env[variable];\n if (value === undefined || value === '') continue;\n\n if (keyPath.length === 1) {\n merged[keyPath[0]!] = value;\n } else {\n const [section, field] = keyPath as [string, string];\n const current = merged[section];\n merged[section] = {\n ...(current && typeof current === 'object' ? (current as Record<string, unknown>) : {}),\n [field]: value,\n };\n }\n applied.push(variable);\n }\n\n return { data: merged, applied };\n}\n\nexport class ConfigError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ConfigError';\n }\n}\n\nfunction formatIssues(error: z.ZodError): string {\n return error.issues\n .map((issue) => ` - ${issue.path.join('.') || '(root)'}: ${issue.message}`)\n .join('\\n');\n}\n\n/**\n * Loads and validates `.blastproof/config.yaml` from `cwd`.\n * Throws ConfigError with an actionable message on any problem.\n */\nexport async function loadConfig(\n cwd: string = process.cwd(),\n env: Record<string, string | undefined> = process.env,\n): Promise<BlastproofConfig> {\n const configPath = path.join(cwd, CONFIG_RELATIVE_PATH);\n\n let raw: string;\n try {\n raw = await readFile(configPath, 'utf8');\n } catch {\n throw new ConfigError(\n `No config found at ${CONFIG_RELATIVE_PATH}. Run \\`blastproof init\\` to scaffold one.`,\n );\n }\n\n let data: unknown;\n try {\n data = parseYaml(raw);\n } catch (error) {\n throw new ConfigError(\n `Invalid YAML in ${CONFIG_RELATIVE_PATH}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n\n const { data: merged, applied } = applyEnvOverrides(data ?? {}, env);\n\n const result = configSchema.safeParse(merged);\n if (!result.success) {\n // The file may be perfectly valid and an override the culprit; saying only\n // \"invalid config.yaml\" would send the user hunting through a correct file.\n const source =\n applied.length > 0\n ? `${CONFIG_RELATIVE_PATH} (with overrides from ${applied.join(', ')})`\n : CONFIG_RELATIVE_PATH;\n throw new ConfigError(`Invalid ${source}:\\n${formatIssues(result.error)}`);\n }\n return result.data;\n}\n","import { simpleGit } from 'simple-git';\n\nexport class DiffError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'DiffError';\n }\n}\n\nfunction toDiffError(error: unknown, baseRef: string): DiffError {\n const message = error instanceof Error ? error.message : String(error);\n if (/not a git repository/i.test(message)) {\n return new DiffError(\n 'Cannot compute diff: not inside a git repository. Run blastproof from within your project repository.',\n );\n }\n if (/no merge base/i.test(message)) {\n return new DiffError(\n `Cannot compute diff against '${baseRef}': no common merge-base with HEAD (shallow clone?). Fetch more history, e.g. \\`git fetch --deepen\\` or set fetch-depth: 0 in actions/checkout.`,\n );\n }\n if (/unknown revision|ambiguous argument|bad revision|invalid object name/i.test(message)) {\n return new DiffError(\n `Cannot compute diff: base ref '${baseRef}' does not exist in this repository. Fetch it first (e.g. \\`git fetch origin ${baseRef}\\`) or pass a valid --base ref.`,\n );\n }\n return new DiffError(`Cannot compute diff against '${baseRef}': ${message}`);\n}\n\n/**\n * Returns the sorted repo-relative paths of files changed on the current branch\n * relative to `baseRef` (`git diff <baseRef>...HEAD`, three-dot merge-base\n * semantics): added, modified, deleted and renamed files, paths only.\n * Throws DiffError with an actionable message on invalid refs, non-repo cwd\n * or a missing merge-base (e.g. shallow CI clone).\n */\nexport async function getChangedFiles(\n baseRef: string,\n cwd: string = process.cwd(),\n): Promise<string[]> {\n try {\n const summary = await simpleGit(cwd).diffSummary(['--name-only', `${baseRef}...HEAD`]);\n return summary.files.map((file) => file.file).sort();\n } catch (error) {\n throw toDiffError(error, baseRef);\n }\n}\n","import picomatch from 'picomatch';\n\nexport interface ImpactResult {\n /** Sorted, de-duplicated routes affected by the changed files. */\n affectedRoutes: string[];\n /**\n * Sorted changed files matching neither a `routes:` glob nor an `ignore:` glob —\n * files whose blast radius nobody has declared (design D1).\n */\n unmappedFiles: string[];\n /** Sorted changed files declared irrelevant via `ignore:`. */\n ignoredFiles: string[];\n}\n\n/**\n * Maps changed files to affected routes using the config `routes:` glob→URL-list\n * entries (pure, no I/O). Paths are normalized to forward slashes before matching.\n *\n * Each file lands in exactly one of three buckets (design D1): mapped to routes,\n * ignored, or unclassified. Only the third is reported as unmapped, which is what\n * makes that report — and the gate built on it — worth acting on.\n */\nexport function mapImpact(\n changedFiles: string[],\n routes: Record<string, string[]>,\n ignore: string[] = [],\n): ImpactResult {\n const matchers = Object.entries(routes).map(([glob, urls]) => ({\n isMatch: picomatch(glob, { dot: true }),\n urls,\n }));\n const isIgnored = ignore.map((glob) => picomatch(glob, { dot: true }));\n\n const affected = new Set<string>();\n const unmapped: string[] = [];\n const ignored: string[] = [];\n\n for (const file of changedFiles) {\n const normalized = file.replace(/\\\\/g, '/');\n\n // Ignore wins: a file declared irrelevant contributes nothing, even if some\n // glob would also match it. Otherwise \"irrelevant\" would not mean irrelevant.\n if (isIgnored.some((matches) => matches(normalized))) {\n ignored.push(normalized);\n continue;\n }\n\n let matched = false;\n for (const { isMatch, urls } of matchers) {\n if (isMatch(normalized)) {\n matched = true;\n for (const url of urls) {\n affected.add(url);\n }\n }\n }\n if (!matched) {\n unmapped.push(normalized);\n }\n }\n\n return {\n affectedRoutes: [...affected].sort(),\n unmappedFiles: unmapped.sort(),\n ignoredFiles: ignored.sort(),\n };\n}\n","import { generateObject, type LanguageModel } from 'ai';\nimport type { z } from 'zod';\nimport {\n agentSystemPrompt,\n agentUserPrompt,\n assertSystemPrompt,\n assertUserPrompt,\n plannerSystemPrompt,\n plannerUserPrompt,\n type AgentIterationInput,\n type PlannerInput,\n} from './prompts.js';\nimport {\n agentActionSchema,\n assertJudgmentSchema,\n generatedTestSchema,\n type AgentAction,\n type AssertJudgment,\n type GeneratedTest,\n} from './schemas.js';\n\n/**\n * The LLM decision-maker used by the executor. Mocked in unit tests.\n */\nexport interface AgentBrain {\n /** Decides the single next action for the current step. Throws on malformed model output. */\n nextAction(input: AgentIterationInput): Promise<AgentAction>;\n /** Judges whether the snapshot satisfies an assert expectation. */\n judge(expectation: string, snapshot: string): Promise<AssertJudgment>;\n}\n\n/** Narrowed signature of `generateObject` so tests can inject a stub. */\nexport type GenerateObjectFn = (options: {\n model: LanguageModel;\n schema: z.ZodTypeAny;\n system?: string;\n prompt?: string;\n}) => Promise<{ object: unknown }>;\n\nexport class MalformedModelOutputError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'MalformedModelOutputError';\n }\n}\n\nexport function createBrain(\n model: LanguageModel,\n generate: GenerateObjectFn = generateObject as unknown as GenerateObjectFn,\n): AgentBrain {\n return {\n async nextAction(input) {\n const result = await generate({\n model,\n schema: agentActionSchema,\n system: agentSystemPrompt(),\n prompt: agentUserPrompt(input),\n });\n const parsed = agentActionSchema.safeParse(result.object);\n if (!parsed.success) {\n throw new MalformedModelOutputError(\n `Model returned an invalid action: ${parsed.error.issues[0]?.message ?? 'unknown'}`,\n );\n }\n return parsed.data;\n },\n\n async judge(expectation, snapshot) {\n const result = await generate({\n model,\n schema: assertJudgmentSchema,\n system: assertSystemPrompt(),\n prompt: assertUserPrompt(expectation, snapshot),\n });\n const parsed = assertJudgmentSchema.safeParse(result.object);\n if (!parsed.success) {\n throw new MalformedModelOutputError(\n `Model returned an invalid assert judgment: ${parsed.error.issues[0]?.message ?? 'unknown'}`,\n );\n }\n return parsed.data;\n },\n };\n}\n\n/**\n * The LLM test writer used by the planner (design D5). Mocked in unit tests.\n */\nexport interface PlannerBrain {\n /** Generates one test draft for a route. Throws on malformed model output. */\n planTest(input: PlannerInput): Promise<GeneratedTest>;\n}\n\nexport function createPlanner(\n model: LanguageModel,\n generate: GenerateObjectFn = generateObject as unknown as GenerateObjectFn,\n): PlannerBrain {\n return {\n async planTest(input) {\n const result = await generate({\n model,\n schema: generatedTestSchema,\n system: plannerSystemPrompt(),\n prompt: plannerUserPrompt(input),\n });\n const parsed = generatedTestSchema.safeParse(result.object);\n if (!parsed.success) {\n throw new MalformedModelOutputError(\n `Model returned an invalid test draft: ${parsed.error.issues[0]?.message ?? 'unknown'}`,\n );\n }\n return parsed.data;\n },\n };\n}\n","/**\n * Prompts for the agentic execution loop (design D3: one structured action per iteration).\n */\n\nexport function agentSystemPrompt(): string {\n return `You are a meticulous QA agent executing one plain-English test step at a time in a real browser.\n\nYou receive the current page as a YAML accessibility snapshot (roles and accessible names, exactly what a user perceives) and decide the single next action to move the current step forward.\n\nRules:\n- Return exactly ONE action per response. Never batch actions.\n- Pick target elements exclusively from the current snapshot, using their exact role and accessible name. Never invent CSS selectors or guess elements not in the snapshot.\n- Use \"navigate\" only with a URL or path as value. Use \"fill\" to type into textboxes/inputs. Use \"press\" for keyboard keys (value e.g. \"Enter\"). Use \"select\" for dropdowns (value = option label).\n- Use \"assert\" with an expectation to verify page state (visible text, counts, URLs). Assertions never modify the page.\n- Return \"done\" when the current step's outcome holds — including when it already held before you acted, or was achieved by your previous action. \"Already true\" is done, never failure. Do not return \"done\" for work belonging to later steps.\n- Return \"fail\" only when the step's outcome cannot be reached: the element is still absent after retries, the page cannot support the step, or an error blocks progress. Never return \"fail\" because the work appears to have been done already.\n- If your previous action errored, re-read the fresh snapshot and choose an alternative element or approach. Do not repeat the exact same failing action.\n- Keep reasoning to one short sentence.`;\n}\n\nexport interface AgentIterationInput {\n /** The plain-English step being executed. */\n step: string;\n /** Whether the step is a setup step (context for the model). */\n isSetup?: boolean;\n /** Current accessibility snapshot of the page. */\n snapshot: string;\n /** Result of the previous action attempt, if any (\"ok: ...\" or \"error: ...\"). */\n lastResult?: string;\n /** Remaining retry budget for failed attempts. */\n retriesLeft: number;\n /** Remaining action iterations before the step is aborted. */\n iterationsLeft: number;\n}\n\nexport function agentUserPrompt(input: AgentIterationInput): string {\n const parts = [\n `Current ${input.isSetup ? 'setup ' : ''}step: ${input.step}`,\n '',\n 'Page accessibility snapshot:',\n input.snapshot,\n ];\n if (input.lastResult) {\n parts.push('', `Previous action result: ${input.lastResult}`);\n }\n parts.push(\n '',\n `Budget: ${input.retriesLeft} failed attempts left, ${input.iterationsLeft} actions left for this step.`,\n 'What is the single next action?',\n );\n return parts.join('\\n');\n}\n\nexport function assertSystemPrompt(): string {\n return `You are a QA judge. You receive a page accessibility snapshot and an expectation. Decide whether the snapshot satisfies the expectation. Be strict: only pass when the expectation is clearly met by the snapshot content. Answer with pass=true/false and a one-sentence reason.`;\n}\n\nexport function assertUserPrompt(expectation: string, snapshot: string): string {\n return [\n `Expectation: ${expectation}`,\n '',\n 'Page accessibility snapshot:',\n snapshot,\n '',\n 'Does the snapshot satisfy the expectation?',\n ].join('\\n');\n}\n\nexport function plannerSystemPrompt(): string {\n return `You are a QA engineer writing one end-to-end test for a web page, in plain English.\n\nYou receive a YAML accessibility snapshot of the page (roles and accessible names, exactly what a user perceives) and the list of source files a pull request changed in the area this page covers.\n\nRules:\n- Write steps a human tester could follow without looking at the code. One action or check per step.\n- Refer to controls by the accessible name shown in the snapshot, spelled exactly. Never invent buttons, fields or links that are not in the snapshot.\n- Never write CSS selectors, XPath, IDs or any code — the runner resolves elements live from the accessibility tree.\n- Prefer the journey the changed files touch over a generic tour of the page. The changed files tell you which part of the page matters.\n- End with at least one step that verifies an observable outcome (visible text, a count, a state change).\n- If a step needs a credential or any secret, write it as a placeholder like {{env.TEST_PASSWORD}}. Never write a real or invented password, token or key.\n- Keep the whole test to a handful of steps: one journey, not an exhaustive suite.`;\n}\n\nexport interface PlannerInput {\n /** Route the test is being generated for, e.g. `/cart`. */\n route: string;\n /** Accessibility snapshot captured after loading the route. */\n snapshot: string;\n /** Repo-relative paths of the changed files that mapped to this route (design D3). */\n changedFiles: string[];\n}\n\nexport function plannerUserPrompt(input: PlannerInput): string {\n const parts = [`Route under test: ${input.route}`, '', 'Page accessibility snapshot:', input.snapshot];\n if (input.changedFiles.length > 0) {\n parts.push(\n '',\n 'Files this pull request changed in the area covering this route:',\n ...input.changedFiles.map((file) => `- ${file}`),\n );\n } else {\n parts.push('', 'No specific changed files for this route: cover its main user journey.');\n }\n parts.push('', 'Write the test for this route.');\n return parts.join('\\n');\n}\n","import { z } from 'zod';\n\n/**\n * The single structured decision the LLM returns on every loop iteration (design D3).\n */\nexport const agentActionSchema = z.object({\n action: z\n .enum(['navigate', 'click', 'fill', 'press', 'select', 'assert', 'done', 'fail'])\n .describe('The next browser action to perform for the current step.'),\n target: z\n .object({\n role: z\n .string()\n .optional()\n .describe('ARIA role of the target element, e.g. \"button\", \"link\", \"textbox\".'),\n name: z\n .string()\n .optional()\n .describe('Accessible name of the target element, exactly as shown in the snapshot.'),\n text: z\n .string()\n .optional()\n .describe('Visible text of the target element, used as a fallback when no role matches.'),\n })\n .optional()\n .describe('Element to act on, resolved from the accessibility snapshot. Omit for navigate/done/fail.'),\n value: z\n .string()\n .optional()\n .describe(\n 'Action payload: URL/path for navigate, text for fill, key for press (e.g. \"Enter\"), option label for select.',\n ),\n reasoning: z.string().describe('One sentence explaining why this action moves the step forward.'),\n expectation: z\n .string()\n .optional()\n .describe('For assert: the condition the current page snapshot must satisfy.'),\n});\n\nexport type AgentAction = z.infer<typeof agentActionSchema>;\nexport type AgentActionName = AgentAction['action'];\nexport type AgentTarget = NonNullable<AgentAction['target']>;\n\n/** Judgment returned by the LLM when evaluating an `assert` expectation against a snapshot. */\nexport const assertJudgmentSchema = z.object({\n pass: z.boolean().describe('Whether the snapshot satisfies the expectation.'),\n reason: z.string().describe('One sentence explaining the judgment.'),\n});\n\nexport type AssertJudgment = z.infer<typeof assertJudgmentSchema>;\n\n/**\n * A test draft returned by the planner (design D5). `routes` is deliberately absent:\n * it is set by code to the route the draft was generated for, never by the model (D6).\n */\nexport const generatedTestSchema = z.object({\n summary: z\n .string()\n .min(1)\n .describe('One line naming the user journey this test covers, e.g. \"Applying a discount updates the cart total\".'),\n steps: z\n .array(z.string().min(1))\n .min(1)\n .describe(\n 'Plain-English steps a QA engineer would follow, each naming controls exactly as they appear in the snapshot.',\n ),\n priority: z\n .enum(['P0', 'P1', 'P2'])\n .describe('P0 for revenue- or auth-critical journeys, P1 for main flows, P2 for edge cases.'),\n tags: z\n .array(z.string())\n .describe('Short lowercase tags grouping this test, e.g. [\"cart\", \"checkout\"].'),\n});\n\nexport type GeneratedTest = z.infer<typeof generatedTestSchema>;\n","import { createAnthropic } from '@ai-sdk/anthropic';\nimport { createOpenAI } from '@ai-sdk/openai';\nimport type { LanguageModel } from 'ai';\nimport type { LlmConfig } from '../config.js';\n\nexport const DEFAULT_MODELS: Record<LlmConfig['provider'], string> = {\n anthropic: 'claude-haiku-4-5',\n openai: 'gpt-4o-mini',\n ollama: 'qwen2.5',\n};\n\nexport const DEFAULT_API_KEY_ENVS: Record<Exclude<LlmConfig['provider'], 'ollama'>, string> = {\n anthropic: 'ANTHROPIC_API_KEY',\n openai: 'OPENAI_API_KEY',\n};\n\nexport const DEFAULT_OLLAMA_BASE_URL = 'http://localhost:11434/v1';\n\nexport class MissingApiKeyError extends Error {\n constructor(variable: string, provider: string) {\n super(\n `Missing API key: environment variable ${variable} is not set (required for provider \"${provider}\"). ` +\n `Export it before running, e.g. \\`export ${variable}=...\\`, or change llm.provider / llm.api_key_env in .blastproof/config.yaml.`,\n );\n this.name = 'MissingApiKeyError';\n }\n}\n\nexport interface ResolvedModel {\n model: LanguageModel;\n provider: LlmConfig['provider'];\n modelId: string;\n}\n\n/**\n * Resolves the configured LLM provider to a model instance.\n * Fails fast (before any browser launch) when the required API key env var is unset.\n */\nexport function createModel(\n llm: LlmConfig,\n env: Record<string, string | undefined> = process.env,\n): ResolvedModel {\n const modelId = llm.model ?? DEFAULT_MODELS[llm.provider];\n\n switch (llm.provider) {\n case 'anthropic':\n case 'openai': {\n const keyEnv = llm.api_key_env ?? DEFAULT_API_KEY_ENVS[llm.provider];\n const apiKey = env[keyEnv];\n if (!apiKey) {\n throw new MissingApiKeyError(keyEnv, llm.provider);\n }\n const model =\n llm.provider === 'anthropic'\n ? createAnthropic({ apiKey })(modelId)\n : // `.chat` = Chat Completions: works with official OpenAI and any\n // OpenAI-compatible endpoint (OpenRouter, LiteLLM, vLLM) via base_url.\n createOpenAI({ apiKey, ...(llm.base_url ? { baseURL: llm.base_url } : {}) }).chat(modelId);\n return { model, provider: llm.provider, modelId };\n }\n case 'ollama': {\n const baseURL = llm.base_url ?? DEFAULT_OLLAMA_BASE_URL;\n // Ollama exposes an OpenAI-compatible Chat Completions endpoint; a dummy key satisfies the client.\n const model = createOpenAI({ baseURL, apiKey: 'ollama' }).chat(modelId);\n return { model, provider: 'ollama', modelId };\n }\n }\n}\n","import { writeFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport { stringify } from 'yaml';\nimport type { PlannerBrain } from './llm/brain.js';\nimport type { GeneratedTest } from './llm/schemas.js';\nimport type { PageLike } from './runner/actions.js';\nimport { defaultSnapshot } from './runner/executor.js';\nimport { TESTS_RELATIVE_DIR, type TestFile } from './runner/testfile.js';\n\nexport class PlannerError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'PlannerError';\n }\n}\n\n/** A generated test plus the route coverage the planner assigns to it (design D6). */\nexport type TestDraft = GeneratedTest & { routes: string[] };\n\nexport interface GenerateOptions {\n route: string;\n baseUrl: string;\n /** Changed files that mapped to this route; empty for `--route` generation. */\n changedFiles: string[];\n brain: PlannerBrain;\n /** Injectable snapshotter (defaults to live ariaSnapshot), mirroring the executor. */\n snapshot?: (page: PageLike) => Promise<string>;\n}\n\n/** Steps naming a credential alongside a quoted literal, with no `{{env.*}}` placeholder. */\nconst CREDENTIAL_WORD = /\\b(password|passwd|api[ _-]?key|token|secret|credential)\\b/i;\nconst QUOTED_LITERAL = /[\"'][^\"']+[\"']/;\n\n/**\n * Returns the steps that appear to carry a literal secret instead of a placeholder\n * (design D8). A credential word alone is not enough — \"check the password field is\n * visible\" is fine; it is the quoted literal without `{{env.*}}` that is a violation.\n */\nexport function findSecretLiterals(steps: string[]): string[] {\n return steps.filter(\n (step) => CREDENTIAL_WORD.test(step) && QUOTED_LITERAL.test(step) && !step.includes('{{env.'),\n );\n}\n\n/**\n * Derives the test filename stem from a route: `/` → `home`, everything else\n * lowercased with non-alphanumerics collapsed to `-` (design D7).\n */\nexport function routeToSlug(route: string): string {\n const slug = route\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 60);\n return slug || 'home';\n}\n\nexport interface ProvenanceMeta {\n route: string;\n /** Base ref the diff came from; omitted when the route was named with `--route`. */\n base?: string;\n /** ISO date (YYYY-MM-DD); injectable so tests are not clock-dependent. */\n date?: string;\n}\n\n/** Renders a draft as YAML with the provenance comment header (design D7). */\nexport function renderTestYaml(draft: TestDraft, meta: ProvenanceMeta): string {\n const header = [\n '# Generated by `blastproof plan` — review before committing.',\n `# route: ${meta.route}`,\n `# base: ${meta.base ?? '(explicit --route)'}`,\n `# generated: ${meta.date ?? new Date().toISOString().slice(0, 10)}`,\n '',\n ].join('\\n');\n return (\n header +\n stringify({\n summary: draft.summary,\n priority: draft.priority,\n tags: draft.tags,\n routes: draft.routes,\n steps: draft.steps,\n })\n );\n}\n\n/**\n * Generates one test draft for a route: load it, snapshot it, one LLM call (design D2/D4).\n * `routes` is set here, never taken from the model, so the draft provably closes the\n * coverage gap that triggered it (design D6).\n */\nexport async function generateForRoute(page: PageLike, options: GenerateOptions): Promise<TestDraft> {\n const { route, baseUrl, changedFiles, brain, snapshot = defaultSnapshot } = options;\n\n const url = new URL(route, baseUrl).toString();\n try {\n await page.goto(url, { timeout: 30_000 });\n } catch (error) {\n throw new PlannerError(\n `Cannot load ${url}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n\n const generated = await brain.planTest({ route, snapshot: await snapshot(page), changedFiles });\n\n const leaked = findSecretLiterals(generated.steps);\n if (leaked.length > 0) {\n throw new PlannerError(\n `Generated steps contain literal secrets instead of {{env.VAR}} placeholders: ${leaked.join(' | ')}`,\n );\n }\n\n return { ...generated, routes: [route] };\n}\n\n/**\n * Writes a draft under `.blastproof/tests/`, never overwriting: an existing target\n * fails this route with the conflicting path (design D7). Returns the path written.\n */\nexport async function writeDraft(\n cwd: string,\n draft: TestDraft,\n meta: ProvenanceMeta,\n): Promise<string> {\n const file = path.join(cwd, TESTS_RELATIVE_DIR, `${routeToSlug(meta.route)}.yaml`);\n try {\n // 'wx' fails if the file exists — atomic, so a concurrent write cannot slip through.\n await writeFile(file, renderTestYaml(draft, meta), { flag: 'wx' });\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'EEXIST') {\n throw new PlannerError(\n `Refusing to overwrite ${path.relative(cwd, file)} (route ${meta.route}). ` +\n 'Delete or rename it to regenerate.',\n );\n }\n throw error;\n }\n return file;\n}\n\n/** Routes already covered by at least one test in the suite. */\nexport function coveredRoutes(tests: TestFile[]): Set<string> {\n return new Set(tests.flatMap((test) => test.routes));\n}\n","import { readFile, readdir } from 'node:fs/promises';\nimport path from 'node:path';\nimport { parse as parseYaml } from 'yaml';\nimport { z } from 'zod';\n\nexport const TESTS_RELATIVE_DIR = path.join('.blastproof', 'tests');\n\nconst prioritySchema = z.enum(['P0', 'P1', 'P2']);\nexport type Priority = z.infer<typeof prioritySchema>;\n\nconst testFileSchema = z.object({\n summary: z.string().min(1),\n steps: z.array(z.string().min(1)).min(1),\n priority: prioritySchema.default('P1'),\n tags: z.array(z.string()).default([]),\n setup: z.array(z.string().min(1)).optional(),\n routes: z.array(z.string()).default([]),\n auth: z.boolean().default(true),\n});\n\nexport interface TestFile {\n /** Path relative to the project root, e.g. `.blastproof/tests/app-load.yaml`. */\n path: string;\n summary: string;\n steps: string[];\n priority: Priority;\n tags: string[];\n setup?: string[];\n /** Routes/URLs this test covers, used by `run --impacted` for impact matching. */\n routes: string[];\n /**\n * Whether this test runs with the configured authenticated session (default true).\n * A login test must set `false`, or it would start already signed in.\n */\n auth: boolean;\n}\n\nexport class TestFileError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'TestFileError';\n }\n}\n\nfunction formatIssues(file: string, error: z.ZodError): string {\n return error.issues\n .map((issue) => ` - ${issue.path.join('.') || '(root)'}: ${issue.message}`)\n .join('\\n');\n}\n\n/** Parses and validates a single YAML test file. Throws TestFileError naming file and field. */\nexport async function parseTestFile(filePath: string): Promise<TestFile> {\n let raw: string;\n try {\n raw = await readFile(filePath, 'utf8');\n } catch {\n throw new TestFileError(`Cannot read test file: ${filePath}`);\n }\n\n let data: unknown;\n try {\n data = parseYaml(raw);\n } catch (error) {\n throw new TestFileError(\n `Invalid YAML in ${filePath}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n\n const result = testFileSchema.safeParse(data ?? {});\n if (!result.success) {\n throw new TestFileError(`Invalid test file ${filePath}:\\n${formatIssues(filePath, result.error)}`);\n }\n\n return { path: filePath, ...result.data };\n}\n\n/** Recursively discovers `.yaml`/`.yml` test files under `dir`, in sorted path order. */\nexport async function discoverTestFiles(root: string): Promise<string[]> {\n const found: string[] = [];\n\n async function walk(dir: string): Promise<void> {\n const entries = await readdir(dir, { withFileTypes: true });\n for (const entry of entries) {\n const full = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n await walk(full);\n } else if (/\\.ya?ml$/i.test(entry.name)) {\n found.push(full);\n }\n }\n }\n\n await walk(root);\n return found.sort();\n}\n","import path from 'node:path';\nimport { chromium } from 'playwright';\nimport { authenticate, AuthError, contextOptions, type AuthSession, type BrowserLike } from '../auth.js';\nimport { ConfigError, loadConfig, type BlastproofConfig } from '../config.js';\nimport { DiffError, getChangedFiles } from '../diff.js';\nimport { mapImpact, type ImpactResult } from '../impact.js';\nimport { createBrain } from '../llm/brain.js';\nimport { createModel, MissingApiKeyError } from '../llm/provider.js';\nimport { renderHtml, writeHtml } from '../report/html.js';\nimport { renderJUnit, writeJUnit, type SkippedCase } from '../report/junit.js';\nimport { computeScore, formatScoreLine } from '../report/score.js';\nimport type { PageLike } from '../runner/actions.js';\nimport { MissingEnvError, SecretsMask, substituteEnv } from '../runner/env.js';\nimport { executeTest, type ExecutorEvent, type TestResult } from '../runner/executor.js';\nimport {\n matchesFilters,\n selectImpactedTests,\n type ImpactedSelection,\n type TestFilters,\n} from '../runner/selection.js';\nimport {\n discoverTestFiles,\n parseTestFile,\n TESTS_RELATIVE_DIR,\n TestFileError,\n type TestFile,\n} from '../runner/testfile.js';\n\nexport const EXIT_OK = 0;\nexport const EXIT_FAILED = 1;\nexport const EXIT_USAGE = 2;\n\nexport interface RunOptions extends TestFilters {\n cwd: string;\n /** Run only tests impacted by the diff vs `base` (uses routes: mappings). */\n impacted?: boolean;\n /** Base git ref for --impacted (default \"main\"). */\n base?: string;\n /** Overrides config base_url for this run only; the config file is never mutated. */\n url?: string;\n /** Print the selection plan and exit without launching a browser or calling the LLM. */\n dryRun?: boolean;\n /**\n * Minimum weighted score (0–100). When set it replaces the all-must-pass rule:\n * the run succeeds when the score reaches it, tolerating lower-priority failures\n * within the margin (design D4).\n */\n minScore?: number;\n /** Write a JUnit report: `true` for the session-directory default, or an explicit path. */\n junit?: string | boolean;\n /** Write an HTML report: `true` for the session-directory default, or an explicit path. */\n html?: string | boolean;\n /**\n * Fail the run when the diff contains a file matching neither a `routes:` glob\n * nor an `ignore:` glob. Additive to `--min-score`: a run can verify what it\n * selected and still be blocked by a change nobody has classified (design D4).\n */\n failOnUnmapped?: boolean;\n}\n\nfunction sessionId(): string {\n return new Date().toISOString().replace(/[:.]/g, '-');\n}\n\n/**\n * Returns a copy of `config` with `base_url` overridden by the --url flag\n * (CLI flag > config file, design D6). Throws ConfigError on an invalid URL.\n */\nexport function applyUrlOverride(\n config: BlastproofConfig,\n url: string | undefined,\n): BlastproofConfig {\n if (url === undefined) return config;\n try {\n new URL(url);\n } catch {\n throw new ConfigError(\n `Invalid --url '${url}': expected an absolute URL like http://localhost:4173.`,\n );\n }\n return { ...config, base_url: url };\n}\n\n/** Substitutes env placeholders across setup+steps, registering every secret for masking. */\nfunction resolveSecretsAndSteps(test: TestFile): { test: TestFile; mask: SecretsMask } {\n const mask = new SecretsMask();\n for (const step of [...(test.setup ?? []), ...test.steps]) {\n mask.registerFrom(step);\n }\n return {\n test: {\n ...test,\n setup: test.setup?.map((step) => substituteEnv(step)),\n steps: test.steps.map((step) => substituteEnv(step)),\n },\n mask,\n };\n}\n\nfunction printEvent(event: ExecutorEvent): void {\n switch (event.type) {\n case 'step-start':\n console.log(` ${event.setup ? '(setup) ' : ''}step ${event.index + 1}/${event.total}: ${event.step}`);\n break;\n case 'action': {\n const { action, result } = event;\n const target = action.target ? ` ${action.target.role ?? ''} \"${action.target.name ?? action.target.text ?? ''}\"` : '';\n const value = action.value ? ` [${action.value}]` : '';\n console.log(` -> ${action.action}${target}${value} :: ${result}`);\n break;\n }\n case 'step-end':\n if (event.status === 'failed') {\n console.log(` X step failed: ${event.reason ?? 'unknown reason'}`);\n }\n break;\n }\n}\n\nfunction printSummary(results: TestResult[]): void {\n const passed = results.filter((r) => r.status === 'passed');\n const failed = results.filter((r) => r.status === 'failed');\n\n console.log('\\n--- Summary ---------------------------------------------------');\n const rows = results.map((r) => ({\n status: r.status === 'passed' ? 'PASS' : 'FAIL',\n priority: r.priority,\n summary: r.summary,\n duration: `${(r.durationMs / 1000).toFixed(1)}s`,\n }));\n const width = (key: keyof (typeof rows)[number]) =>\n Math.max(key.length, ...rows.map((r) => String(r[key]).length));\n const [ws, wp, wsum, wd] = [width('status'), width('priority'), width('summary'), width('duration')];\n for (const row of rows) {\n console.log(\n `${row.status.padEnd(ws)} ${row.priority.padEnd(wp)} ${row.summary.padEnd(wsum)} ${row.duration.padStart(wd)}`,\n );\n }\n console.log('---------------------------------------------------------------');\n console.log(`${passed.length} passed, ${failed.length} failed, ${results.length} total`);\n\n for (const r of failed) {\n console.log(`\\nX ${r.summary} (${r.file})`);\n if (r.failedStep) console.log(` failing step: ${r.failedStep}`);\n if (r.reason) console.log(` reason: ${r.reason}`);\n if (r.screenshot) console.log(` screenshot: ${r.screenshot}`);\n }\n}\n\nasync function runOne(\n browser: import('playwright').Browser,\n test: TestFile,\n config: BlastproofConfig,\n sessionDir: string,\n session: AuthSession | undefined,\n): Promise<TestResult> {\n const brain = createBrain(createModel(config.llm).model);\n\n let resolved: TestFile;\n let mask: SecretsMask;\n try {\n ({ test: resolved, mask } = resolveSecretsAndSteps(test));\n } catch (error) {\n if (error instanceof MissingEnvError) {\n // Spec: missing env var fails the test before any page is opened.\n return {\n file: test.path,\n summary: test.summary,\n priority: test.priority,\n tags: test.tags,\n status: 'failed',\n steps: [],\n reason: error.message,\n durationMs: 0,\n };\n }\n throw error;\n }\n\n // Fresh context per test: no cookies, storage or history leakage (m1 design D6).\n // With auth configured the context merely starts from the shared session instead\n // of empty, so isolation is preserved (auth design D3); `auth: false` opts out.\n const context = await browser.newContext(test.auth ? contextOptions(session) : {});\n try {\n const page = await context.newPage();\n page.setDefaultTimeout(config.browser.timeout_ms);\n return await executeTest(page as unknown as PageLike, resolved, {\n brain,\n sessionDir,\n baseUrl: config.base_url,\n maxRetries: config.max_retries_per_step,\n mask: (text) => mask.mask(text),\n onEvent: printEvent,\n });\n } finally {\n await context.close();\n }\n}\n\n/** Prints the --impacted report sections (affected/unmapped/unrouted-skipped/uncovered). */\nfunction printImpactReport(\n impact: ImpactResult,\n selection: ImpactedSelection,\n cwd: string,\n): void {\n console.log('\\n--- Impact -----------------------------------------------------');\n console.log(\n impact.affectedRoutes.length > 0 ? 'Affected routes:' : 'Affected routes: none',\n );\n for (const route of impact.affectedRoutes) console.log(` ${route}`);\n if (impact.unmappedFiles.length > 0) {\n console.log('Unclassified files (matched by no routes: or ignore: glob):');\n for (const file of impact.unmappedFiles) console.log(` ${file}`);\n }\n if (impact.ignoredFiles.length > 0) {\n console.log(`Ignored files: ${impact.ignoredFiles.length}`);\n }\n if (selection.unroutedSkipped.length > 0) {\n console.log('Unrouted tests (skipped):');\n for (const test of selection.unroutedSkipped) {\n console.log(` ${test.summary} (${path.relative(cwd, test.path)})`);\n }\n }\n if (selection.uncoveredRoutes.length > 0) {\n console.log('Affected but uncovered routes:');\n for (const route of selection.uncoveredRoutes) console.log(` ${route}`);\n }\n console.log('---------------------------------------------------------------');\n}\n\n/**\n * Prints the summary tail shared by every terminating path: score line, optional\n * JUnit report, and the exit code. With `--min-score` the threshold decides the\n * outcome instead of the all-must-pass rule (design D4).\n */\nasync function finalize(\n results: TestResult[],\n skipped: SkippedCase[],\n options: RunOptions,\n sessionDir: string,\n durationMs: number,\n impact?: ImpactResult,\n): Promise<number> {\n if (results.length > 0) printSummary(results);\n\n const score = computeScore(results);\n console.log(formatScoreLine(score, results, options.minScore));\n\n if (options.junit) {\n const target =\n typeof options.junit === 'string'\n ? path.resolve(options.cwd, options.junit)\n : path.join(sessionDir, 'junit.xml');\n const xml = renderJUnit(results, skipped, { score, durationMs, cwd: options.cwd });\n await writeJUnit(target, xml);\n console.log(`JUnit report: ${path.relative(options.cwd, target)}`);\n }\n\n if (options.html) {\n const target =\n typeof options.html === 'string'\n ? path.resolve(options.cwd, options.html)\n : path.join(sessionDir, 'report.html');\n const html = await renderHtml(results, skipped, {\n score,\n durationMs,\n minScore: options.minScore,\n cwd: options.cwd,\n });\n await writeHtml(target, html);\n console.log(`HTML report: ${path.relative(options.cwd, target)}`);\n }\n\n // Additive, unlike --min-score (design D4): \"the tests I ran passed\" and\n // \"something changed that nobody has classified\" are different claims, and the\n // second is exactly the silent false negative this flag exists to remove.\n const unclassified = options.failOnUnmapped ? (impact?.unmappedFiles ?? []) : [];\n if (unclassified.length > 0) {\n console.error(\n `error: ${unclassified.length} changed file(s) match no routes: or ignore: glob, ` +\n 'so their blast radius is unknown:',\n );\n for (const file of unclassified) console.error(` ${file}`);\n console.error(\n 'Map them in routes: if they can affect a page, or list them in ignore: if they cannot.',\n );\n return EXIT_FAILED;\n }\n\n if (options.minScore !== undefined) {\n return score >= options.minScore ? EXIT_OK : EXIT_FAILED;\n }\n return results.some((result) => result.status === 'failed') ? EXIT_FAILED : EXIT_OK;\n}\n\n/** Prints the --dry-run selection plan; no browser is launched and no LLM is called. */\nfunction printDryRun(selected: TestFile[], baseUrl: string, cwd: string): void {\n console.log(`\\nDry run: ${selected.length} test(s) selected, base_url=${baseUrl}`);\n for (const test of selected) {\n console.log(` ${test.summary} [${test.priority}] (${path.relative(cwd, test.path)})`);\n }\n console.log('Dry run: no browser launched, no LLM calls made.');\n}\n\n/**\n * `blastproof run`: discovery → filters → sequential agentic execution → summary.\n * With --impacted, the diff vs `base` selects only tests covering affected routes.\n * Returns the process exit code: 0 all pass, 1 any failure, 2 usage/config error.\n */\nexport async function runCommand(options: RunOptions): Promise<number> {\n let config: BlastproofConfig;\n try {\n config = applyUrlOverride(await loadConfig(options.cwd), options.url);\n } catch (error) {\n if (error instanceof ConfigError) {\n console.error(`error: ${error.message}`);\n return EXIT_USAGE;\n }\n throw error;\n }\n\n const impacted = options.impacted ?? false;\n const base = options.base ?? 'main';\n\n // Impact analysis fails fast (usage error) before any browser launch.\n let impact: ImpactResult | undefined;\n if (impacted) {\n let changedFiles: string[];\n try {\n changedFiles = await getChangedFiles(base, options.cwd);\n } catch (error) {\n if (error instanceof DiffError) {\n console.error(`error: ${error.message}`);\n return EXIT_USAGE;\n }\n throw error;\n }\n impact = mapImpact(changedFiles, config.routes ?? {}, config.ignore ?? []);\n }\n\n const testsDir = path.join(options.cwd, TESTS_RELATIVE_DIR);\n let files: string[];\n try {\n files = await discoverTestFiles(testsDir);\n } catch {\n console.error(`error: no tests directory at ${TESTS_RELATIVE_DIR}. Run \\`blastproof init\\` first.`);\n return EXIT_USAGE;\n }\n\n const results: TestResult[] = [];\n const parsed: TestFile[] = [];\n for (const file of files) {\n try {\n parsed.push(await parseTestFile(file));\n } catch (error) {\n if (error instanceof TestFileError) {\n // A broken test file is a failure, not a crash: report it and keep going.\n results.push({\n file: path.relative(options.cwd, file),\n summary: path.basename(file),\n priority: 'P1',\n tags: [],\n status: 'failed',\n steps: [],\n reason: error.message,\n durationMs: 0,\n });\n continue;\n }\n throw error;\n }\n }\n\n // Impacted selection first, tag/priority/query filters applied within it (D3/D4).\n const selection: ImpactedSelection = impact\n ? selectImpactedTests(parsed, impact.affectedRoutes, options)\n : {\n selected: parsed.filter((test) => matchesFilters(test, options)),\n unroutedSkipped: [],\n uncoveredRoutes: [],\n };\n const selected = selection.selected;\n\n if (impact) {\n printImpactReport(impact, selection, options.cwd);\n }\n\n if (options.dryRun) {\n printDryRun(selected, config.base_url, options.cwd);\n return EXIT_OK;\n }\n\n const sessionDir = path.join(options.cwd, '.blastproof', 'reports', sessionId());\n const startedAt = Date.now();\n\n // Empty selection short-circuits before the LLM key check and browser launch (D5).\n if (selected.length === 0) {\n console.log(\n impact ? 'No impacted tests to run.' : 'No tests matched the given filters.',\n );\n return finalize(results, selection.unroutedSkipped, options, sessionDir, Date.now() - startedAt, impact);\n }\n\n // Fail fast on a missing API key before launching any browser (spec: llm-providers).\n try {\n createModel(config.llm);\n } catch (error) {\n if (error instanceof MissingApiKeyError) {\n console.error(`error: ${error.message}`);\n return EXIT_USAGE;\n }\n throw error;\n }\n\n const { provider, modelId } = createModel(config.llm);\n console.log(\n `blastproof run: ${selected.length} test(s), provider=${provider} model=${modelId}, base_url=${config.base_url}`,\n );\n\n const browser = await chromium.launch({ headless: config.browser.headless });\n try {\n // Authenticate once, before the first test (design D3). A failed login is a\n // configuration problem, not a product defect, so it aborts with exit 2 rather\n // than surfacing as N failing tests and a meaningless score (design D6).\n let session: AuthSession | undefined;\n if (config.auth) {\n try {\n console.log('Authenticating...');\n session = await authenticate({\n auth: config.auth,\n cwd: options.cwd,\n baseUrl: config.base_url,\n browser: browser as unknown as BrowserLike,\n brain: createBrain(createModel(config.llm).model),\n maxRetries: config.max_retries_per_step,\n onEvent: printEvent,\n });\n } catch (error) {\n if (error instanceof AuthError) {\n console.error(`error: ${error.message}`);\n return EXIT_USAGE;\n }\n throw error;\n }\n }\n\n for (const test of selected) {\n console.log(`\\n> ${test.summary} [${test.priority}] (${path.relative(options.cwd, test.path)})`);\n results.push(await runOne(browser, test, config, sessionDir, session));\n }\n } finally {\n await browser.close();\n }\n\n return finalize(results, selection.unroutedSkipped, options, sessionDir, Date.now() - startedAt, impact);\n}\n","import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport type { StepResult, TestResult } from '../runner/executor.js';\nimport type { SkippedCase } from './junit.js';\n\nconst ESCAPES: Record<string, string> = {\n '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n \"'\": ''',\n};\n\n/**\n * Single escaping choke point (design D4). Summaries, steps and failure reasons are\n * user- and model-authored; one missed interpolation turns the report into an\n * injection vector the moment someone opens it in a browser.\n */\nexport function escapeHtml(text: string): string {\n return text.replace(/[&<>\"']/g, (char) => ESCAPES[char]!);\n}\n\n/**\n * Reads a screenshot as a base64 `data:` URI so the report stays self-contained\n * (design D3). Returns undefined when unreadable: a report exists to explain a\n * failure and must not fail while doing so.\n */\nexport async function embedScreenshot(file: string): Promise<string | undefined> {\n try {\n const bytes = await readFile(file);\n return `data:image/png;base64,${bytes.toString('base64')}`;\n } catch {\n return undefined;\n }\n}\n\nexport interface HtmlMeta {\n score: number;\n durationMs: number;\n /** Threshold, when one was given, so the gate verdict can be shown. */\n minScore?: number;\n /** Repo root, so paths render relative rather than absolute. */\n cwd?: string;\n generatedAt?: Date;\n}\n\nconst STYLES = `\n:root { color-scheme: light dark; --bg:#fff; --fg:#111; --muted:#666; --line:#e3e3e3;\n --pass:#137333; --fail:#c5221f; --card:#fafafa; }\n@media (prefers-color-scheme: dark) {\n :root { --bg:#15171a; --fg:#e8e8e8; --muted:#9aa0a6; --line:#2c2f34;\n --pass:#5bb974; --fail:#f28b82; --card:#1c1f23; }\n}\n* { box-sizing: border-box; }\nbody { margin:0; padding:2rem 1.25rem; background:var(--bg); color:var(--fg);\n font:15px/1.55 ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", sans-serif; }\nmain { max-width: 62rem; margin: 0 auto; }\nh1 { font-size:1.35rem; margin:0 0 .25rem; }\n.sub { color:var(--muted); font-size:.85rem; margin-bottom:1.5rem; }\n.score { display:flex; align-items:baseline; gap:.75rem; flex-wrap:wrap;\n padding:1.25rem; border:1px solid var(--line); border-radius:10px;\n background:var(--card); margin-bottom:1.5rem; }\n.score b { font-size:2.5rem; line-height:1; font-variant-numeric:tabular-nums; }\n.verdict { font-weight:600; }\n.verdict.pass { color:var(--pass); } .verdict.fail { color:var(--fail); }\n.counts { color:var(--muted); font-size:.9rem; }\ntable { width:100%; border-collapse:collapse; margin-bottom:1.5rem; }\nth, td { text-align:left; padding:.5rem .6rem; border-bottom:1px solid var(--line);\n font-size:.9rem; vertical-align:top; }\nth { color:var(--muted); font-weight:600; }\ntd.num { text-align:right; font-variant-numeric:tabular-nums; white-space:nowrap; }\n.tag { font-weight:700; font-size:.78rem; letter-spacing:.03em; }\n.tag.pass { color:var(--pass); } .tag.fail { color:var(--fail); }\n.tag.skip { color:var(--muted); }\ndetails { border:1px solid var(--line); border-radius:10px; padding:.75rem 1rem;\n margin-bottom:.75rem; background:var(--card); }\ndetails > summary { cursor:pointer; font-weight:600; }\n.reason { border-left:3px solid var(--fail); padding:.5rem .75rem; margin:.75rem 0;\n color:var(--fg); background:color-mix(in srgb, var(--fail) 8%, transparent);\n border-radius:0 6px 6px 0; }\nol.steps { margin:.5rem 0 0; padding-left:1.25rem; }\nol.steps li { margin:.15rem 0; }\nol.steps li.failed { color:var(--fail); font-weight:600; }\nimg.shot { max-width:100%; border:1px solid var(--line); border-radius:8px; margin-top:.75rem; }\n.note { color:var(--muted); font-style:italic; font-size:.85rem; }\nfooter { color:var(--muted); font-size:.8rem; border-top:1px solid var(--line);\n padding-top:1rem; margin-top:2rem; }\n`;\n\nfunction seconds(ms: number): string {\n return `${(ms / 1000).toFixed(1)}s`;\n}\n\nfunction renderSteps(steps: StepResult[]): string {\n if (steps.length === 0) return '';\n const items = steps\n .map(\n (step) =>\n `<li class=\"${step.status === 'failed' ? 'failed' : ''}\">${escapeHtml(step.step)}` +\n `${step.setup ? ' <span class=\"note\">(setup)</span>' : ''}</li>`,\n )\n .join('\\n ');\n return `<ol class=\"steps\">\\n ${items}\\n </ol>`;\n}\n\nasync function renderTest(result: TestResult, cwd?: string): Promise<string> {\n const failed = result.status === 'failed';\n const file = cwd ? path.relative(cwd, result.file) : result.file;\n const parts = [\n ` <details${failed ? ' open' : ''}>`,\n ` <summary><span class=\"tag ${failed ? 'fail' : 'pass'}\">${failed ? 'FAIL' : 'PASS'}</span> ` +\n `${escapeHtml(result.summary)} <span class=\"note\">${escapeHtml(result.priority)} · ${escapeHtml(file)}</span></summary>`,\n ];\n\n if (failed) {\n if (result.failedStep) {\n parts.push(` <p><strong>Failing step:</strong> ${escapeHtml(result.failedStep)}</p>`);\n }\n if (result.reason) {\n parts.push(` <p class=\"reason\">${escapeHtml(result.reason)}</p>`);\n }\n }\n\n parts.push(` ${renderSteps(result.steps)}`);\n\n if (failed && result.screenshot) {\n const embedded = await embedScreenshot(result.screenshot);\n parts.push(\n embedded\n ? ` <img class=\"shot\" alt=\"Screenshot at failure\" src=\"${embedded}\">`\n : ' <p class=\"note\">Screenshot unavailable.</p>',\n );\n }\n\n parts.push(' </details>');\n return parts.join('\\n');\n}\n\n/**\n * Renders the run as one self-contained HTML document (design D3): inline CSS,\n * screenshots as data URIs, no external references and no scripts. Score and gate\n * verdict lead, failures sort above passes, passing tests collapse (design D5).\n */\nexport async function renderHtml(\n results: TestResult[],\n skipped: SkippedCase[],\n meta: HtmlMeta,\n): Promise<string> {\n const passed = results.filter((r) => r.status === 'passed').length;\n const failures = results.filter((r) => r.status === 'failed');\n const generatedAt = (meta.generatedAt ?? new Date()).toISOString();\n\n // Failures first: the reader came for those.\n const ordered = [...failures, ...results.filter((r) => r.status === 'passed')];\n const detail = (await Promise.all(ordered.map((r) => renderTest(r, meta.cwd)))).join('\\n');\n\n const verdict =\n meta.minScore === undefined\n ? ''\n : meta.score >= meta.minScore\n ? `<span class=\"verdict pass\">min-score ${meta.minScore}: pass</span>`\n : `<span class=\"verdict fail\">min-score ${meta.minScore}: FAIL</span>`;\n\n const rows = results\n .map((r) => {\n const file = meta.cwd ? path.relative(meta.cwd, r.file) : r.file;\n return (\n ` <tr><td><span class=\"tag ${r.status === 'failed' ? 'fail' : 'pass'}\">` +\n `${r.status === 'failed' ? 'FAIL' : 'PASS'}</span></td>` +\n `<td>${escapeHtml(r.priority)}</td><td>${escapeHtml(r.summary)}</td>` +\n `<td>${escapeHtml(file)}</td><td class=\"num\">${seconds(r.durationMs)}</td></tr>`\n );\n })\n .join('\\n');\n\n const skippedRows = skipped\n .map((s) => {\n const file = meta.cwd ? path.relative(meta.cwd, s.path) : s.path;\n return (\n ' <tr><td><span class=\"tag skip\">SKIP</span></td><td>—</td>' +\n `<td>${escapeHtml(s.summary)}</td><td>${escapeHtml(file)}</td>` +\n '<td class=\"num\">—</td></tr>'\n );\n })\n .join('\\n');\n\n return `<!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>blastproof report</title>\n<style>${STYLES}</style>\n</head>\n<body>\n<main>\n <h1>blastproof report</h1>\n <p class=\"sub\">${escapeHtml(generatedAt)} · ${seconds(meta.durationMs)}</p>\n\n <section class=\"score\">\n <b>${meta.score}</b>\n ${verdict}\n <span class=\"counts\">${passed} passed · ${failures.length} failed${\n skipped.length > 0 ? ` · ${skipped.length} skipped` : ''\n }</span>\n </section>\n\n <table>\n <thead><tr><th>Status</th><th>Priority</th><th>Test</th><th>File</th><th class=\"num\">Time</th></tr></thead>\n <tbody>\n${rows}${rows && skippedRows ? '\\n' : ''}${skippedRows}\n </tbody>\n </table>\n\n${detail}\n\n <footer>Generated by blastproof. Screenshots are embedded, so this file works offline.</footer>\n</main>\n</body>\n</html>\n`;\n}\n\n/** Writes the report, creating missing parent directories. Returns the path written. */\nexport async function writeHtml(file: string, html: string): Promise<string> {\n await mkdir(path.dirname(file), { recursive: true });\n await writeFile(file, html, 'utf8');\n return file;\n}\n","import { mkdir, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport type { TestResult } from '../runner/executor.js';\n\nconst ESCAPES: Record<string, string> = {\n '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n \"'\": ''',\n};\n\n/**\n * Single escaping choke point (design D6). Summaries, steps and failure reasons\n * are model- and user-authored, so any of them can carry XML-significant characters.\n */\nexport function escapeXml(text: string): string {\n return text.replace(/[&<>\"']/g, (char) => ESCAPES[char]!);\n}\n\n/** Minimal shape of a test skipped as unrouted under `--impacted`. */\nexport interface SkippedCase {\n path: string;\n summary: string;\n}\n\nexport interface JUnitMeta {\n score: number;\n durationMs: number;\n /** Repo root, so `classname` is repo-relative rather than absolute. */\n cwd?: string;\n}\n\nfunction seconds(ms: number): string {\n return (ms / 1000).toFixed(3);\n}\n\n/**\n * Renders the run as a JUnit `testsuite` (design D7). Unrouted skipped tests are\n * emitted as `<skipped/>` cases so the coverage gap shows up in CI instead of\n * being absent from the report. Pure — returns the XML, writes nothing.\n */\nexport function renderJUnit(\n results: TestResult[],\n skipped: SkippedCase[],\n meta: JUnitMeta,\n): string {\n const relative = (file: string): string =>\n meta.cwd ? path.relative(meta.cwd, file) : file;\n\n const failures = results.filter((result) => result.status === 'failed').length;\n const lines = [\n '<?xml version=\"1.0\" encoding=\"UTF-8\"?>',\n `<testsuite name=\"blastproof\" tests=\"${results.length + skipped.length}\" failures=\"${failures}\" skipped=\"${skipped.length}\" time=\"${seconds(meta.durationMs)}\">`,\n ' <properties>',\n ` <property name=\"score\" value=\"${meta.score}\"/>`,\n ' </properties>',\n ];\n\n for (const result of results) {\n const open = ` <testcase classname=\"${escapeXml(relative(result.file))}\" name=\"${escapeXml(result.summary)}\" time=\"${seconds(result.durationMs)}\"`;\n if (result.status === 'passed') {\n lines.push(`${open}/>`);\n continue;\n }\n const reason = result.reason ?? 'test failed';\n const body = result.failedStep\n ? `failing step: ${result.failedStep}\\n${reason}`\n : reason;\n lines.push(`${open}>`);\n lines.push(` <failure message=\"${escapeXml(reason)}\">${escapeXml(body)}</failure>`);\n lines.push(' </testcase>');\n }\n\n for (const test of skipped) {\n lines.push(\n ` <testcase classname=\"${escapeXml(relative(test.path))}\" name=\"${escapeXml(test.summary)}\" time=\"0.000\">`,\n ' <skipped message=\"no routes: declared, skipped by --impacted\"/>',\n ' </testcase>',\n );\n }\n\n lines.push('</testsuite>', '');\n return lines.join('\\n');\n}\n\n/** Writes the report, creating missing parent directories. Returns the path written. */\nexport async function writeJUnit(file: string, xml: string): Promise<string> {\n await mkdir(path.dirname(file), { recursive: true });\n await writeFile(file, xml, 'utf8');\n return file;\n}\n","import type { TestResult } from '../runner/executor.js';\n\n/** Priority weights: a failing P0 costs three times a failing P2 (design D1). */\nexport const WEIGHTS: Record<string, number> = { P0: 3, P1: 2, P2: 1 };\n\nconst DEFAULT_WEIGHT = WEIGHTS.P1!;\n\n/**\n * Weighted pass rate over executed tests, rounded to an integer 0–100.\n * An empty result list scores 100: nothing ran, so nothing is at risk (design D2).\n * Pure — the caller owns all I/O.\n */\nexport function computeScore(results: TestResult[]): number {\n let total = 0;\n let passed = 0;\n for (const result of results) {\n const weight = WEIGHTS[result.priority] ?? DEFAULT_WEIGHT;\n total += weight;\n if (result.status === 'passed') passed += weight;\n }\n if (total === 0) return 100;\n return Math.round((100 * passed) / total);\n}\n\n/**\n * The summary score line. Names the threshold when one is set so the gate\n * decision is visible without recomputing it, and flags the empty case so a\n * score of 100 is never mistaken for verified quality (design D2).\n */\nexport function formatScoreLine(\n score: number,\n results: TestResult[],\n threshold?: number,\n): string {\n if (results.length === 0) {\n const base = 'Score: 100 (no tests executed)';\n return threshold === undefined ? base : `${base} — min-score ${threshold}: pass`;\n }\n if (threshold === undefined) return `Score: ${score}`;\n return score >= threshold\n ? `Score: ${score} — min-score ${threshold}: pass`\n : `Score: ${score} — min-score ${threshold}: FAIL (below threshold)`;\n}\n","import type { Priority, TestFile } from './testfile.js';\n\nexport interface TestFilters {\n tags: string[];\n priority?: Priority;\n query?: string;\n}\n\n/** Returns true when the test passes the tag/priority/query filters. */\nexport function matchesFilters(test: TestFile, filters: TestFilters): boolean {\n if (filters.tags.length > 0 && !filters.tags.some((tag) => test.tags.includes(tag))) {\n return false;\n }\n if (filters.priority && test.priority !== filters.priority) {\n return false;\n }\n if (filters.query && !test.summary.toLowerCase().includes(filters.query.toLowerCase())) {\n return false;\n }\n return true;\n}\n\nexport interface ImpactedSelection {\n /** Tests covering at least one affected route, after tag/priority/query filters. */\n selected: TestFile[];\n /** Tests with no `routes:` declaration — never executed by `--impacted` (design D4). */\n unroutedSkipped: TestFile[];\n /** Affected routes covered by no selected test — reported, never failing (design D5). */\n uncoveredRoutes: string[];\n}\n\n/**\n * Pure impacted selection (design D3): tests whose `routes:` intersect the\n * affected route set, further reduced by the tag/priority/query filters.\n * Route strings compare by exact equality. Tests without `routes:` are skipped\n * and reported; affected routes no selected test covers are reported.\n */\nexport function selectImpactedTests(\n tests: TestFile[],\n affectedRoutes: string[],\n filters: TestFilters = { tags: [] },\n): ImpactedSelection {\n const affected = new Set(affectedRoutes);\n const selected: TestFile[] = [];\n const unroutedSkipped: TestFile[] = [];\n\n for (const test of tests) {\n if (test.routes.length === 0) {\n unroutedSkipped.push(test);\n continue;\n }\n if (!test.routes.some((route) => affected.has(route))) continue;\n if (!matchesFilters(test, filters)) continue;\n selected.push(test);\n }\n\n const covered = new Set(selected.flatMap((test) => test.routes));\n return {\n selected,\n unroutedSkipped,\n uncoveredRoutes: affectedRoutes.filter((route) => !covered.has(route)),\n };\n}\n","import { planCommand } from './plan.js';\nimport { EXIT_FAILED, EXIT_OK, EXIT_USAGE, runCommand } from './run.js';\n\nexport interface TestOptions {\n cwd: string;\n /** Base git ref for the diff (default \"main\"). */\n base?: string;\n /** Overrides config base_url for this run only. */\n url?: string;\n /** Minimum weighted score; replaces the all-must-pass rule when given. */\n minScore?: number;\n junit?: string | boolean;\n html?: string | boolean;\n /** Persist generated drafts instead of previewing them. */\n write?: boolean;\n /** Fail when the diff contains a file matching neither routes: nor ignore:. */\n failOnUnmapped?: boolean;\n}\n\nfunction section(title: string): void {\n console.log(`\\n=== ${title} ${'='.repeat(Math.max(0, 58 - title.length))}`);\n}\n\n/**\n * `blastproof test`: the diff-driven one-shot — execute the tests covering the\n * affected routes, then draft tests for the affected routes nothing covers.\n *\n * Drafts are reported, never executed (design D1). A model-written test that has\n * not been reviewed must not decide a merge: a hallucinated expectation would block\n * a correct change, and a credulous one would wave a broken change through while\n * looking like coverage. So `test` makes a gap visible; it does not close it.\n *\n * Exit codes: 2 usage/config/diff, 1 gate failure or draft generation failure,\n * 0 otherwise (design D2).\n */\nexport async function testCommand(options: TestOptions): Promise<number> {\n section('Verify: tests covering the affected routes');\n const runCode = await runCommand({\n cwd: options.cwd,\n tags: [],\n impacted: true,\n base: options.base,\n url: options.url,\n minScore: options.minScore,\n junit: options.junit,\n html: options.html,\n failOnUnmapped: options.failOnUnmapped,\n });\n if (runCode === EXIT_USAGE) return EXIT_USAGE;\n\n section('Draft: affected routes no test covers');\n const planCode = await planCommand({\n cwd: options.cwd,\n base: options.base,\n url: options.url,\n write: options.write,\n });\n if (planCode === EXIT_USAGE) return EXIT_USAGE;\n\n console.log(\n '\\nDrafts are not executed and do not affect the score — review them before ' +\n 'they join the suite.',\n );\n\n return runCode === EXIT_OK && planCode === EXIT_OK ? EXIT_OK : EXIT_FAILED;\n}\n"],"mappings":"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","names":["path","chromium","writeFile","mkdir","path","mkdir","path","mkdir","path","path","mkdir","writeFile","readFile","path","section","z","writeFile","path","readFile","path","parseYaml","z","formatIssues","path","writeFile","path","mkdir","readFile","writeFile","path","mkdir","writeFile","path","ESCAPES","seconds","path","path","chromium"]}
|
package/package.json
CHANGED