argus-reviewer-e2e 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.
Files changed (67) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +80 -0
  3. package/action/action.yml +147 -0
  4. package/action/sticky-comment.mjs +376 -0
  5. package/dist/api.d.ts +121 -0
  6. package/dist/api.js +256 -0
  7. package/dist/cache/fingerprint.d.ts +59 -0
  8. package/dist/cache/fingerprint.js +67 -0
  9. package/dist/cache/store.d.ts +17 -0
  10. package/dist/cache/store.js +41 -0
  11. package/dist/cli.d.ts +25 -0
  12. package/dist/cli.js +1355 -0
  13. package/dist/config.d.ts +130 -0
  14. package/dist/config.js +163 -0
  15. package/dist/debug.d.ts +1 -0
  16. package/dist/debug.js +30 -0
  17. package/dist/detect.d.ts +50 -0
  18. package/dist/detect.js +105 -0
  19. package/dist/driver/browser.d.ts +64 -0
  20. package/dist/driver/browser.js +200 -0
  21. package/dist/driver/target.d.ts +23 -0
  22. package/dist/driver/target.js +97 -0
  23. package/dist/engine/actions.d.ts +26 -0
  24. package/dist/engine/actions.js +47 -0
  25. package/dist/engine/loop.d.ts +118 -0
  26. package/dist/engine/loop.js +649 -0
  27. package/dist/engine/prompts.d.ts +22 -0
  28. package/dist/engine/prompts.js +112 -0
  29. package/dist/evidence/ci.d.ts +18 -0
  30. package/dist/evidence/ci.js +61 -0
  31. package/dist/evidence/link.d.ts +35 -0
  32. package/dist/evidence/link.js +90 -0
  33. package/dist/executor/a0.d.ts +29 -0
  34. package/dist/executor/a0.js +38 -0
  35. package/dist/fsutil.d.ts +5 -0
  36. package/dist/fsutil.js +12 -0
  37. package/dist/index/context.d.ts +17 -0
  38. package/dist/index/context.js +88 -0
  39. package/dist/index/diff.d.ts +1 -0
  40. package/dist/index/diff.js +33 -0
  41. package/dist/index/invalidate.d.ts +28 -0
  42. package/dist/index/invalidate.js +56 -0
  43. package/dist/index/scan.d.ts +26 -0
  44. package/dist/index/scan.js +209 -0
  45. package/dist/journal/build.d.ts +15 -0
  46. package/dist/journal/build.js +47 -0
  47. package/dist/journal/schema.d.ts +59 -0
  48. package/dist/journal/schema.js +6 -0
  49. package/dist/journal/store.d.ts +10 -0
  50. package/dist/journal/store.js +26 -0
  51. package/dist/live.d.ts +2 -0
  52. package/dist/live.js +46 -0
  53. package/dist/log.d.ts +17 -0
  54. package/dist/log.js +24 -0
  55. package/dist/report/comment.d.ts +13 -0
  56. package/dist/report/comment.js +135 -0
  57. package/dist/report/junit.d.ts +10 -0
  58. package/dist/report/junit.js +46 -0
  59. package/dist/report/run.d.ts +51 -0
  60. package/dist/report/run.js +36 -0
  61. package/dist/vision/cost.d.ts +36 -0
  62. package/dist/vision/cost.js +16 -0
  63. package/dist/vision/ledger.d.ts +29 -0
  64. package/dist/vision/ledger.js +65 -0
  65. package/dist/vision/openrouter.d.ts +70 -0
  66. package/dist/vision/openrouter.js +134 -0
  67. package/package.json +65 -0
@@ -0,0 +1,130 @@
1
+ export interface ProviderRules {
2
+ only?: string[];
3
+ ignore?: string[];
4
+ order?: string[];
5
+ allow_fallbacks?: boolean;
6
+ require_parameters?: boolean;
7
+ }
8
+ export interface Target {
9
+ command: string;
10
+ url: string;
11
+ readyTimeoutMs: number;
12
+ }
13
+ export interface Config {
14
+ model: string;
15
+ escalation_model: string;
16
+ /**
17
+ * Optional specialist model for grounding-correction retries (e.g. a
18
+ * ui-tars-class model that returns bare coordinates). Used only when the
19
+ * primary model's proposed point resolves to the wrong element.
20
+ */
21
+ grounding_model: string | undefined;
22
+ /**
23
+ * Optional code review model. Used by `argus-reviewer code-review` to review
24
+ * PR diffs and post findings. Defaults to the primary `model` if not set.
25
+ */
26
+ code_model: string | undefined;
27
+ /**
28
+ * Hard budget for the `argus-reviewer code-review` lane. When set, the
29
+ * review stops early if the cumulative OpenRouter cost exceeds this cap.
30
+ */
31
+ codeReviewBudgetUsd: number | undefined;
32
+ provider: ProviderRules;
33
+ budgetUsd: number | undefined;
34
+ target: Target | undefined;
35
+ cacheDir: string | undefined;
36
+ /** Directory scanned by `argus-reviewer run` for *.test.* files. */
37
+ testsDir: string | undefined;
38
+ /** Directory for JUnit XML + JSON run report output. */
39
+ reportDir: string | undefined;
40
+ /**
41
+ * Named secrets for `td.type(name, { secret: true })`. The value is typed
42
+ * locally and never sent to the model — the model only resolves the field.
43
+ */
44
+ secrets: Record<string, string> | undefined;
45
+ /**
46
+ * Optional module path (resolved from cwd) whose default export is invoked
47
+ * with the Playwright `Page` after the driver launches and before any
48
+ * navigation — the seam for `page.route` mocks, tenant seeding, and other
49
+ * pre-navigation setup.
50
+ */
51
+ pageSetup: string | undefined;
52
+ /**
53
+ * OpenRouter request metadata. `trace` is sent in the request body and
54
+ * can be used to attribute spend by repo, PR, or run. `headers` are
55
+ * sent verbatim with every OpenRouter request (e.g. HTTP-Referer, X-Title).
56
+ */
57
+ openrouter: {
58
+ trace?: Record<string, string>;
59
+ headers?: Record<string, string>;
60
+ } | undefined;
61
+ /**
62
+ * Browser engine for Playwright: `chromium`, `firefox`, or `webkit`.
63
+ * Defaults to `chromium`.
64
+ */
65
+ browser: 'chromium' | 'firefox' | 'webkit' | undefined;
66
+ /**
67
+ * Hard limit in milliseconds for Playwright cleanup (context + browser close).
68
+ * Prevents a hung browser from keeping the runner or test suite alive.
69
+ * Defaults to 30 seconds.
70
+ */
71
+ browserTimeoutMs: number | undefined;
72
+ /**
73
+ * Severity levels that block a pre-merge status. Defaults to `['bug']` so
74
+ * `risk`/`nit`/`q` findings are surfaced but do not fail the status.
75
+ */
76
+ severity: string[] | undefined;
77
+ /**
78
+ * Log verbosity — 'debug'|'info'|'warn'|'error'. ARGUS_DEBUG=1 forces
79
+ * 'debug'. Default 'warn'.
80
+ */
81
+ logLevel: 'debug' | 'info' | 'warn' | 'error' | undefined;
82
+ /**
83
+ * Repo globs naming the app surface the tests exercise (e.g. 'ui/src/**').
84
+ * Diff-aware invalidation marks flow caches stale when the diff touches
85
+ * files in this surface's dependency cone.
86
+ */
87
+ sourceGlobs: string[] | undefined;
88
+ /** Path (repo-relative) for the generated repo index. Default 'argus.index.json'. */
89
+ indexPath: string | undefined;
90
+ /** Base ref for diff invalidation (e.g. 'origin/main'); unset = working tree. */
91
+ diffBase: string | undefined;
92
+ /**
93
+ * Max actions `argus-reviewer record` will take before giving up on `done`.
94
+ * Real multi-action flows need headroom — defaults to 40; `record
95
+ * --max-steps <n>` overrides.
96
+ */
97
+ recordStepCap: number | undefined;
98
+ /**
99
+ * Agent Zero instance for delegated tasks (`argus-reviewer delegate`,
100
+ * `heal: 'a0'`). `url` is the instance base URL — leave unset to let the
101
+ * `a0` CLI resolve it (saved host, AGENT_ZERO_HOST, Docker discovery).
102
+ * Least-privilege scoping (browser vs full desktop) is configured on the
103
+ * instance's gateway, not here.
104
+ */
105
+ a0: {
106
+ url: string | undefined;
107
+ } | undefined;
108
+ /**
109
+ * Failure escalation for `run`. 'local' (default) heals via the vision
110
+ * model only. 'a0' additionally sends each failed test to Agent Zero for an
111
+ * autonomous second opinion — it clicks through the app and reports whether
112
+ * the app or the expectation is wrong.
113
+ */
114
+ heal: 'local' | 'a0' | undefined;
115
+ }
116
+ export type ConfigInput = Partial<Omit<Config, 'provider'>> & {
117
+ provider?: Partial<ProviderRules>;
118
+ };
119
+ export declare const DEFAULT_RECORD_STEP_CAP = 40;
120
+ export declare function defineConfig(input: ConfigInput): ConfigInput;
121
+ export declare function resolveConfig(input?: ConfigInput): Config;
122
+ export declare function loadConfig(cwd: string): Promise<Config>;
123
+ /**
124
+ * Provider slugs the harness recognizes for `provider.only/ignore/order`
125
+ * (KTD4). Unknown slugs warn but do not fail — OpenRouter's catalog changes
126
+ * faster than this list, so validation is fail-open by design.
127
+ */
128
+ export declare const KNOWN_PROVIDER_SLUGS: ReadonlySet<string>;
129
+ /** Slugs in the provider rules that are not recognized; callers warn, not fail. */
130
+ export declare function unknownProviderSlugs(provider: ProviderRules): string[];
package/dist/config.js ADDED
@@ -0,0 +1,163 @@
1
+ import { pathToFileURL } from 'node:url';
2
+ export const DEFAULT_RECORD_STEP_CAP = 40;
3
+ const defaults = {
4
+ model: 'google/gemini-2.5-flash-lite',
5
+ escalation_model: 'moonshotai/kimi-k2.5',
6
+ grounding_model: undefined,
7
+ code_model: 'deepseek/deepseek-v4.1-flash',
8
+ codeReviewBudgetUsd: undefined,
9
+ provider: {
10
+ ignore: ['siliconflow', 'novitaai', 'atlascloud', 'streamlake', 'chutes'],
11
+ },
12
+ budgetUsd: undefined,
13
+ target: undefined,
14
+ cacheDir: undefined,
15
+ testsDir: undefined,
16
+ reportDir: undefined,
17
+ secrets: undefined,
18
+ pageSetup: undefined,
19
+ openrouter: undefined,
20
+ browser: 'chromium',
21
+ browserTimeoutMs: 30_000,
22
+ severity: ['bug'],
23
+ logLevel: undefined,
24
+ sourceGlobs: undefined,
25
+ indexPath: undefined,
26
+ diffBase: undefined,
27
+ recordStepCap: DEFAULT_RECORD_STEP_CAP,
28
+ a0: undefined,
29
+ heal: 'local',
30
+ };
31
+ export function defineConfig(input) {
32
+ return input;
33
+ }
34
+ export function resolveConfig(input = {}) {
35
+ const provider = { ...defaults.provider, ...(input.provider ?? {}) };
36
+ const resolved = { ...defaults, ...input, provider };
37
+ const cap = resolved.recordStepCap;
38
+ resolved.recordStepCap =
39
+ cap !== undefined && Number.isFinite(cap) && cap >= 1
40
+ ? Math.floor(cap)
41
+ : DEFAULT_RECORD_STEP_CAP;
42
+ if (resolved.heal !== 'a0')
43
+ resolved.heal = 'local';
44
+ return resolved;
45
+ }
46
+ export async function loadConfig(cwd) {
47
+ const fs = await import('node:fs/promises');
48
+ const path = await import('node:path');
49
+ const names = ['argus-reviewer.config', 'vision-e2e.config'];
50
+ for (const name of names) {
51
+ for (const ext of ['.ts', '.json']) {
52
+ const file = path.join(cwd, `${name}${ext}`);
53
+ try {
54
+ const stat = await fs.stat(file);
55
+ if (!stat.isFile())
56
+ continue;
57
+ if (ext === '.json') {
58
+ const raw = await fs.readFile(file, 'utf8');
59
+ return resolveConfig(JSON.parse(raw));
60
+ }
61
+ // Always transpile .ts to a temp .mjs rather than importing natively:
62
+ // Node's built-in type stripping resolves the module type from the
63
+ // *consumer's* package.json, so a CommonJS consumer makes ESM config
64
+ // fail with 'Cannot use import statement'. The transpiled file is
65
+ // written next to the config (removed after import) so relative
66
+ // imports and node_modules resolution behave like the original file;
67
+ // the package self-import is rewritten to this module's own index so
68
+ // global/npx installs resolve it too.
69
+ const ts = await import('typescript');
70
+ const { readFile, writeFile, rm } = await import('node:fs/promises');
71
+ const { join, dirname } = await import('node:path');
72
+ const source = await readFile(file, 'utf8');
73
+ const js = ts
74
+ .transpileModule(source, {
75
+ compilerOptions: { module: ts.ModuleKind.ESNext, target: ts.ScriptTarget.ES2022 },
76
+ })
77
+ .outputText.replace(/(['"])argus-reviewer-e2e\1/g,
78
+ // package.json exports '.' → dist/api.js (sibling of this file)
79
+ JSON.stringify(new URL('./api.js', import.meta.url).href));
80
+ const out = join(dirname(file), `.argus-config-${process.pid}-${Date.now()}.mjs`);
81
+ let mod;
82
+ try {
83
+ await writeFile(out, js, 'utf8');
84
+ mod = (await import(pathToFileURL(out).href));
85
+ }
86
+ finally {
87
+ await rm(out, { force: true }).catch(() => undefined);
88
+ }
89
+ const exported = mod.default ?? mod;
90
+ return resolveConfig(exported);
91
+ }
92
+ catch (e) {
93
+ const code = e.code;
94
+ if (code === 'ENOENT')
95
+ continue;
96
+ throw e;
97
+ }
98
+ }
99
+ }
100
+ return resolveConfig();
101
+ }
102
+ /**
103
+ * Provider slugs the harness recognizes for `provider.only/ignore/order`
104
+ * (KTD4). Unknown slugs warn but do not fail — OpenRouter's catalog changes
105
+ * faster than this list, so validation is fail-open by design.
106
+ */
107
+ export const KNOWN_PROVIDER_SLUGS = new Set([
108
+ 'ai21',
109
+ 'aion-labs',
110
+ 'alibaba',
111
+ 'amazon-bedrock',
112
+ 'anthropic',
113
+ 'atlascloud',
114
+ 'azure',
115
+ 'bedrock',
116
+ 'cerebras',
117
+ 'chutes',
118
+ 'cloudflare',
119
+ 'cohere',
120
+ 'coreweave',
121
+ 'crusoe',
122
+ 'deepinfra',
123
+ 'deepseek',
124
+ 'featherless',
125
+ 'fireworks',
126
+ 'friendli',
127
+ 'gmicloud',
128
+ 'google',
129
+ 'google-ai-studio',
130
+ 'groq',
131
+ 'hyperbolic',
132
+ 'inception',
133
+ 'inference-net',
134
+ 'lambda',
135
+ 'mistral',
136
+ 'moonshotai',
137
+ 'ncompass',
138
+ 'nebius',
139
+ 'nineteen',
140
+ 'novitaai',
141
+ 'open-inference',
142
+ 'openai',
143
+ 'openrouter',
144
+ 'parasail',
145
+ 'perplexity',
146
+ 'phala',
147
+ 'relace',
148
+ 'sambanova',
149
+ 'siliconflow',
150
+ 'streamlake',
151
+ 'targon',
152
+ 'together',
153
+ 'ubicloud',
154
+ 'venice',
155
+ 'wandb',
156
+ 'xai',
157
+ 'zai',
158
+ ]);
159
+ /** Slugs in the provider rules that are not recognized; callers warn, not fail. */
160
+ export function unknownProviderSlugs(provider) {
161
+ const slugs = [...(provider.only ?? []), ...(provider.ignore ?? []), ...(provider.order ?? [])];
162
+ return slugs.filter((slug) => !KNOWN_PROVIDER_SLUGS.has(slug.toLowerCase()));
163
+ }
@@ -0,0 +1 @@
1
+ export declare function debug(kind: string, ...args: unknown[]): void;
package/dist/debug.js ADDED
@@ -0,0 +1,30 @@
1
+ import { join } from 'node:path';
2
+ import { liveLog } from './live.js';
3
+ const DEBUG = process.env.ARGUS_DEBUG === '1' || process.env.ARGUS_DEBUG === 'true';
4
+ // debug() has no config access — it lives under the default cache dir.
5
+ const LIVE_DIR = join(process.cwd(), '.argus-reviewer-cache');
6
+ function toMsg(arg) {
7
+ if (typeof arg === 'string')
8
+ return arg;
9
+ try {
10
+ const s = JSON.stringify(arg);
11
+ return s === undefined ? String(arg) : s; // JSON.stringify(undefined) -> undefined
12
+ }
13
+ catch {
14
+ return String(arg); // BigInt, cyclic refs — never throw from a debug call
15
+ }
16
+ }
17
+ export function debug(kind, ...args) {
18
+ if (!DEBUG)
19
+ return; // keep debug() free of fs work on the hot path
20
+ for (const arg of args) {
21
+ liveLog(LIVE_DIR, kind, 'debug', toMsg(arg));
22
+ const prefix = `[argus-reviewer:${kind}]`;
23
+ if (typeof arg === 'string') {
24
+ console.error(`${prefix} ${arg}`);
25
+ }
26
+ else {
27
+ console.error(prefix, arg);
28
+ }
29
+ }
30
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Environment detection for `init` readiness reporting and Agent Zero
3
+ * resolution. Everything here is best-effort — a missing tool degrades a
4
+ * feature flag, it never breaks the command.
5
+ */
6
+ export interface ExecResult {
7
+ code: number;
8
+ stdout: string;
9
+ stderr: string;
10
+ }
11
+ export type ExecFn = (cmd: string, args: string[], timeoutMs: number) => Promise<ExecResult>;
12
+ export declare const defaultExec: ExecFn;
13
+ export type ProbeFn = (url: string, timeoutMs: number) => Promise<boolean>;
14
+ /**
15
+ * Any HTTP response — including a login redirect — means *something* is up,
16
+ * but port 5080 could be an unrelated service. Require an Agent Zero marker
17
+ * in the served HTML before trusting the probe result.
18
+ */
19
+ export declare const defaultProbe: ProbeFn;
20
+ export interface A0Info {
21
+ /** `a0` CLI version string when the binary is on PATH. */
22
+ version: string | undefined;
23
+ /** Resolved instance base URL, if one could be found. */
24
+ host: string | undefined;
25
+ hostSource: 'env' | 'dotfile' | 'probe' | undefined;
26
+ }
27
+ export interface EnvironmentReport {
28
+ /** OPENROUTER_API_KEY is set and non-empty. */
29
+ openrouterKey: boolean;
30
+ /** gh CLI authenticated; undefined when gh is not installed at all. */
31
+ ghAuth: boolean | undefined;
32
+ /** Playwright engines with a downloaded executable (e.g. 'chromium'). */
33
+ playwrightBrowsers: string[];
34
+ a0: A0Info;
35
+ }
36
+ export interface DetectOptions {
37
+ exec?: ExecFn;
38
+ home?: string;
39
+ probe?: ProbeFn;
40
+ }
41
+ /**
42
+ * Resolve the Agent Zero instance URL the same way the `a0` CLI does:
43
+ * explicit env var, the launcher-managed ~/.agent-zero/.env, then a probe of
44
+ * the default local port (http://localhost:5080).
45
+ */
46
+ export declare function resolveA0Host(env: NodeJS.ProcessEnv, opts?: DetectOptions): Promise<{
47
+ host: string | undefined;
48
+ source: A0Info['hostSource'];
49
+ }>;
50
+ export declare function detectEnvironment(env: NodeJS.ProcessEnv, opts?: DetectOptions): Promise<EnvironmentReport>;
package/dist/detect.js ADDED
@@ -0,0 +1,105 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { homedir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { readdir, readFile } from 'node:fs/promises';
5
+ export const defaultExec = (cmd, args, timeoutMs) => new Promise((resolve) => {
6
+ execFile(cmd, args, { timeout: timeoutMs }, (err, stdout, stderr) => {
7
+ if (err) {
8
+ // stderr is '' (not undefined) on spawn ENOENT — fall back to the
9
+ // error message so callers can distinguish "missing" from "failed".
10
+ resolve({ code: 1, stdout: String(stdout), stderr: String(stderr) || err.message });
11
+ }
12
+ else {
13
+ resolve({ code: 0, stdout: String(stdout), stderr: String(stderr) });
14
+ }
15
+ });
16
+ });
17
+ /**
18
+ * Any HTTP response — including a login redirect — means *something* is up,
19
+ * but port 5080 could be an unrelated service. Require an Agent Zero marker
20
+ * in the served HTML before trusting the probe result.
21
+ */
22
+ export const defaultProbe = async (url, timeoutMs) => {
23
+ try {
24
+ const res = await fetch(url, {
25
+ signal: AbortSignal.timeout(timeoutMs),
26
+ redirect: 'manual',
27
+ });
28
+ if (res.status >= 500)
29
+ return false;
30
+ const body = (await res.text()).slice(0, 65_536);
31
+ return /agent.?zero/i.test(body);
32
+ }
33
+ catch {
34
+ return false;
35
+ }
36
+ };
37
+ /** Read `AGENT_ZERO_HOST` (and friends) out of ~/.agent-zero/.env. */
38
+ async function a0EnvFileHost(home) {
39
+ try {
40
+ const raw = await readFile(join(home, '.agent-zero', '.env'), 'utf8');
41
+ for (const line of raw.split('\n')) {
42
+ const m = /^AGENT_ZERO_HOST=(\S+)\s*$/.exec(line.trim());
43
+ if (m?.[1] !== undefined && m[1] !== '')
44
+ return m[1];
45
+ }
46
+ }
47
+ catch {
48
+ // no dotfile — fall through
49
+ }
50
+ return undefined;
51
+ }
52
+ /**
53
+ * Resolve the Agent Zero instance URL the same way the `a0` CLI does:
54
+ * explicit env var, the launcher-managed ~/.agent-zero/.env, then a probe of
55
+ * the default local port (http://localhost:5080).
56
+ */
57
+ export async function resolveA0Host(env, opts = {}) {
58
+ const home = opts.home ?? homedir();
59
+ const probe = opts.probe ?? defaultProbe;
60
+ const fromEnv = env.AGENT_ZERO_HOST;
61
+ if (fromEnv !== undefined && fromEnv !== '')
62
+ return { host: fromEnv, source: 'env' };
63
+ const fromFile = await a0EnvFileHost(home);
64
+ if (fromFile !== undefined)
65
+ return { host: fromFile, source: 'dotfile' };
66
+ const local = 'http://localhost:5080';
67
+ if (await probe(local, 2_000))
68
+ return { host: local, source: 'probe' };
69
+ return { host: undefined, source: undefined };
70
+ }
71
+ /** Playwright engines with a downloaded browser under ~/.cache/ms-playwright. */
72
+ async function playwrightBrowsers(home) {
73
+ try {
74
+ const entries = await readdir(join(home, '.cache', 'ms-playwright'));
75
+ const found = new Set();
76
+ for (const entry of entries) {
77
+ for (const engine of ['chromium', 'firefox', 'webkit']) {
78
+ if (entry.startsWith(engine))
79
+ found.add(engine);
80
+ }
81
+ }
82
+ return [...found];
83
+ }
84
+ catch {
85
+ return [];
86
+ }
87
+ }
88
+ export async function detectEnvironment(env, opts = {}) {
89
+ const exec = opts.exec ?? defaultExec;
90
+ const home = opts.home ?? homedir();
91
+ const [a0Version, gh, browsers, a0Host] = await Promise.all([
92
+ exec('a0', ['--version'], 5_000),
93
+ exec('gh', ['auth', 'status'], 5_000),
94
+ playwrightBrowsers(home),
95
+ resolveA0Host(env, opts),
96
+ ]);
97
+ const version = a0Version.code === 0 ? a0Version.stdout.trim() : undefined;
98
+ const ghMissing = gh.code !== 0 && /ENOENT|not found|no such file/i.test(gh.stderr);
99
+ return {
100
+ openrouterKey: env.OPENROUTER_API_KEY !== undefined && env.OPENROUTER_API_KEY !== '',
101
+ ghAuth: gh.code === 0 ? true : ghMissing ? undefined : false,
102
+ playwrightBrowsers: browsers,
103
+ a0: { version, host: a0Host.host, hostSource: a0Host.source },
104
+ };
105
+ }
@@ -0,0 +1,64 @@
1
+ import { type Page } from 'playwright';
2
+ export interface Viewport {
3
+ width: number;
4
+ height: number;
5
+ }
6
+ export interface BrowserDriverOptions {
7
+ /** Pinned viewport in CSS pixels. Model coordinates map 1:1 onto this. */
8
+ viewport?: Viewport;
9
+ /** Directory for the per-run webm recording. Defaults to a fresh temp dir. */
10
+ videoDir?: string;
11
+ /** JPEG quality 0-100 for observation screenshots. */
12
+ screenshotQuality?: number;
13
+ /** Optional scale factor for the observation screenshot (<=1 downscales). */
14
+ screenshotScale?: number;
15
+ /** Playwright browser engine: `chromium` (default), `firefox`, or `webkit`. */
16
+ browser?: 'chromium' | 'firefox' | 'webkit' | undefined;
17
+ /** Hard limit in ms for Playwright cleanup. */
18
+ browserTimeoutMs?: number | undefined;
19
+ }
20
+ export interface Observation {
21
+ screenshotJpeg: Buffer;
22
+ a11yYaml: string;
23
+ /** Viewport (CSS pixels) the screenshot was taken at — model coords map 1:1. */
24
+ width: number;
25
+ height: number;
26
+ }
27
+ /**
28
+ * One Playwright context per run. Viewport and deviceScaleFactor are pinned so
29
+ * vision-model pixel coordinates map 1:1 to viewport pixels (KTD2).
30
+ */
31
+ export declare class BrowserDriver {
32
+ private readonly browser;
33
+ private readonly context;
34
+ private readonly page;
35
+ private readonly quality;
36
+ private readonly videoDir;
37
+ private readonly viewport;
38
+ private readonly browserTimeoutMs;
39
+ private video;
40
+ private closed;
41
+ private constructor();
42
+ static launch(options?: BrowserDriverOptions): Promise<BrowserDriver>;
43
+ get rawPage(): Page;
44
+ get recordingDir(): string;
45
+ goto(url: string): Promise<void>;
46
+ /**
47
+ * Capture the current observation: a bounded JPEG screenshot plus the page's
48
+ * a11y tree as YAML via ariaSnapshot (not the deprecated accessibility API).
49
+ *
50
+ * `grid: true` paints a temporary coordinate overlay (lines + axis labels
51
+ * every 100px) before the screenshot and removes it immediately after — the
52
+ * set-of-marks trick that measurably improves vision-model pixel grounding.
53
+ */
54
+ observe(options?: {
55
+ grid?: boolean;
56
+ }): Promise<Observation>;
57
+ private _paintGrid;
58
+ private _removeGrid;
59
+ /** Path of the recorded webm, available after close(). */
60
+ videoPath(): string | undefined;
61
+ /** Close the context and browser; resolves the video artifact path. Idempotent. */
62
+ close(): Promise<string | undefined>;
63
+ private _withTimeout;
64
+ }