@siteverb/runner 0.1.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,34 @@
1
+ # @siteverb/runner
2
+
3
+ Deterministic real-browser execution for `siteverb.webmcp.json` contracts.
4
+
5
+ ```sh
6
+ npx @siteverb/runner \
7
+ --contract siteverb.webmcp.json \
8
+ --url http://127.0.0.1:3000 \
9
+ --output .siteverb/report.json
10
+ ```
11
+
12
+ The runner launches a named Chrome channel with WebMCP enabled, opens a fresh page for each journey,
13
+ refreshes the native tool inventory before every step, executes through Puppeteer's official
14
+ `page.webmcp` API, checks bounded result/URL/DOM postconditions, attempts cleanup, and writes a
15
+ versioned evidence report.
16
+
17
+ Targets must be absolute HTTP(S) URLs. Per-navigation, tool, and postcondition timeouts are bounded
18
+ to 1-600,000 milliseconds and validated with journey/profile filters before browser acquisition.
19
+
20
+ Journey steps fail fast. Cleanup is best-effort: every declared cleanup step is attempted even when
21
+ an earlier one fails, and any cleanup failure still fails the journey.
22
+
23
+ Add `--profile chatgpt-site-tools-2026-08-26` to include a dated documented compatibility
24
+ assessment. This never upgrades documented behavior to real-client evidence.
25
+
26
+ Mutating tools do not run unless `--allow-mutations` is present. A stable tool ID listed in a
27
+ journey's `requireHumanBefore` policy also requires `--approve <stable-id>`. Use these flags only
28
+ against an explicitly approved local, preview, or seeded test target.
29
+
30
+ The default report excludes prompts, inputs, tool outputs, DOM snapshots, cookies, and credentials.
31
+ It records browser provenance, step status, duration, and Siteverb-generated failure categories.
32
+ The persisted target is reduced to its origin; path, query, fragment, and URL credentials are omitted.
33
+ `--include-error-details` opts a local run into bounded page-provided error messages; do not use it
34
+ for shared CI artifacts unless those messages are independently redacted.
@@ -0,0 +1,4 @@
1
+ import type { StepExpectation } from '@siteverb/contracts';
2
+ import type { BrowserPageAdapter } from './types.js';
3
+ export declare function assertStepExpectation(page: BrowserPageAdapter, output: unknown, expectation: StepExpectation | undefined): Promise<string | undefined>;
4
+ //# sourceMappingURL=assertions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assertions.d.ts","sourceRoot":"","sources":["../src/assertions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAGV,eAAe,EAEhB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAiFrD,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,kBAAkB,EACxB,MAAM,EAAE,OAAO,EACf,WAAW,EAAE,eAAe,GAAG,SAAS,GACvC,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAe7B"}
@@ -0,0 +1,92 @@
1
+ function isRecord(value) {
2
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
3
+ }
4
+ function deepEqual(actual, expected) {
5
+ if (Object.is(actual, expected))
6
+ return true;
7
+ if (Array.isArray(actual) && Array.isArray(expected)) {
8
+ return (actual.length === expected.length &&
9
+ actual.every((entry, index) => deepEqual(entry, expected[index])));
10
+ }
11
+ if (isRecord(actual) && isRecord(expected)) {
12
+ const actualKeys = Object.keys(actual).sort();
13
+ const expectedKeys = Object.keys(expected).sort();
14
+ return (deepEqual(actualKeys, expectedKeys) &&
15
+ expectedKeys.every((key) => deepEqual(actual[key], expected[key])));
16
+ }
17
+ return false;
18
+ }
19
+ function deepContains(actual, expected) {
20
+ if (isRecord(expected)) {
21
+ if (!isRecord(actual))
22
+ return false;
23
+ return Object.entries(expected).every(([key, value]) => deepContains(actual[key], value));
24
+ }
25
+ if (Array.isArray(expected)) {
26
+ if (!Array.isArray(actual) || actual.length < expected.length)
27
+ return false;
28
+ return expected.every((entry, index) => deepContains(actual[index], entry));
29
+ }
30
+ return Object.is(actual, expected);
31
+ }
32
+ function normalizeOutput(output) {
33
+ if (typeof output !== 'string')
34
+ return output;
35
+ try {
36
+ return JSON.parse(output);
37
+ }
38
+ catch {
39
+ return output;
40
+ }
41
+ }
42
+ function assertResult(output, expectation) {
43
+ const normalized = normalizeOutput(output);
44
+ if (expectation.equals !== undefined && !deepEqual(normalized, expectation.equals)) {
45
+ return 'Tool result did not exactly match the configured expectation.';
46
+ }
47
+ if (expectation.contains !== undefined && !deepContains(normalized, expectation.contains)) {
48
+ return 'Tool result did not contain the configured expectation.';
49
+ }
50
+ return undefined;
51
+ }
52
+ function assertUrl(actual, expectation) {
53
+ const url = new URL(actual);
54
+ if (expectation.pathname !== undefined && url.pathname !== expectation.pathname) {
55
+ return `Browser pathname did not match the configured value "${expectation.pathname}".`;
56
+ }
57
+ for (const [name, value] of Object.entries(expectation.searchParams ?? {})) {
58
+ if (url.searchParams.get(name) !== value) {
59
+ return `Browser URL parameter "${name}" did not match the configured value.`;
60
+ }
61
+ }
62
+ return undefined;
63
+ }
64
+ async function assertDom(page, expectations) {
65
+ for (const expectation of expectations) {
66
+ const failure = await page.inspectDom(expectation);
67
+ if (failure)
68
+ return failure;
69
+ }
70
+ return undefined;
71
+ }
72
+ export async function assertStepExpectation(page, output, expectation) {
73
+ if (!expectation)
74
+ return undefined;
75
+ if (expectation.result) {
76
+ const failure = assertResult(output, expectation.result);
77
+ if (failure)
78
+ return failure;
79
+ }
80
+ if (expectation.url) {
81
+ const failure = assertUrl(page.currentUrl(), expectation.url);
82
+ if (failure)
83
+ return failure;
84
+ }
85
+ if (expectation.dom) {
86
+ const failure = await assertDom(page, expectation.dom);
87
+ if (failure)
88
+ return failure;
89
+ }
90
+ return undefined;
91
+ }
92
+ //# sourceMappingURL=assertions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assertions.js","sourceRoot":"","sources":["../src/assertions.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,97 @@
1
+ #!/usr/bin/env node
2
+ import { readFile, mkdir, writeFile } from 'node:fs/promises';
3
+ import { dirname, resolve } from 'node:path';
4
+ import { parseArgs } from 'node:util';
5
+ import { parseProjectContract } from '@siteverb/contracts';
6
+ import { runProject } from './run.js';
7
+ const HELP = `Usage: siteverb-run --contract <path> --url <url> [options]
8
+
9
+ Options:
10
+ --output <path> Report path (default: .siteverb/report.json)
11
+ --journey <ids> Comma-separated journey IDs
12
+ --profile <ids> Comma-separated versioned client profile IDs
13
+ --chrome-channel <name> chrome, chrome-beta, chrome-canary, or chrome-dev
14
+ --executable-path <path> Explicit Chrome executable
15
+ --timeout <ms> Navigation and tool timeout (default: 30000)
16
+ --allow-mutations Permit reversible/state-changing tools on this target
17
+ --approve <tool-ids> Comma-separated stable IDs approved for guarded steps
18
+ --include-error-details Include page-provided error messages in the local report
19
+ --headed Show the browser window
20
+ --help Show this message
21
+ `;
22
+ function commaList(value) {
23
+ if (!value)
24
+ return undefined;
25
+ const entries = value
26
+ .split(',')
27
+ .map((entry) => entry.trim())
28
+ .filter(Boolean);
29
+ return entries.length > 0 ? entries : undefined;
30
+ }
31
+ function positiveInteger(value, fallback) {
32
+ if (value === undefined)
33
+ return fallback;
34
+ const parsed = Number(value);
35
+ if (!Number.isSafeInteger(parsed) || parsed <= 0)
36
+ throw new TypeError('timeout must be positive.');
37
+ return parsed;
38
+ }
39
+ const { values } = parseArgs({
40
+ options: {
41
+ contract: { type: 'string', short: 'c' },
42
+ url: { type: 'string', short: 'u' },
43
+ output: { type: 'string', short: 'o', default: '.siteverb/report.json' },
44
+ journey: { type: 'string' },
45
+ profile: { type: 'string' },
46
+ 'chrome-channel': { type: 'string', default: 'chrome' },
47
+ 'executable-path': { type: 'string' },
48
+ timeout: { type: 'string', default: '30000' },
49
+ 'allow-mutations': { type: 'boolean', default: false },
50
+ approve: { type: 'string' },
51
+ 'include-error-details': { type: 'boolean', default: false },
52
+ headed: { type: 'boolean', default: false },
53
+ help: { type: 'boolean', short: 'h', default: false },
54
+ },
55
+ strict: true,
56
+ allowPositionals: false,
57
+ });
58
+ if (values.help) {
59
+ process.stdout.write(HELP);
60
+ process.exit(0);
61
+ }
62
+ if (!values.contract || !values.url) {
63
+ process.stderr.write(`${HELP}\nBoth --contract and --url are required.\n`);
64
+ process.exit(2);
65
+ }
66
+ try {
67
+ const contractPath = resolve(values.contract);
68
+ const outputPath = resolve(values.output);
69
+ const contract = parseProjectContract(JSON.parse(await readFile(contractPath, 'utf8')));
70
+ const journeyIds = commaList(values.journey);
71
+ const profileIds = commaList(values.profile);
72
+ const approvedTools = commaList(values.approve);
73
+ const report = await runProject({
74
+ contract,
75
+ targetUrl: values.url,
76
+ channel: values['chrome-channel'],
77
+ headless: !values.headed,
78
+ timeoutMs: positiveInteger(values.timeout, 30_000),
79
+ allowMutations: values['allow-mutations'],
80
+ includeErrorDetails: values['include-error-details'],
81
+ ...(values['executable-path'] === undefined
82
+ ? {}
83
+ : { executablePath: values['executable-path'] }),
84
+ ...(journeyIds === undefined ? {} : { journeyIds }),
85
+ ...(profileIds === undefined ? {} : { profileIds }),
86
+ ...(approvedTools === undefined ? {} : { approvedTools }),
87
+ });
88
+ await mkdir(dirname(outputPath), { recursive: true });
89
+ await writeFile(outputPath, `${JSON.stringify(report, null, 2)}\n`);
90
+ process.stdout.write(`Siteverb: ${report.summary.passed} passed, ${report.summary.failed} failed, ${report.summary.skipped} skipped\nReport: ${outputPath}\n`);
91
+ process.exitCode = report.summary.failed > 0 ? 1 : 0;
92
+ }
93
+ catch (error) {
94
+ process.stderr.write(`Siteverb runner failed: ${error instanceof Error ? error.message : String(error)}\n`);
95
+ process.exitCode = 2;
96
+ }
97
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export { assertStepExpectation } from './assertions.js';
2
+ export { launchBrowser } from './puppeteer.js';
3
+ export { runProject } from './run.js';
4
+ export { RUNNER_VERSION } from './version.js';
5
+ export type { BrowserAdapter, BrowserPageAdapter, BrowserTool, BrowserToolResult, LaunchBrowserOptions, RunProjectOptions, RunReport, } from './types.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,YAAY,EACV,cAAc,EACd,kBAAkB,EAClB,WAAW,EACX,iBAAiB,EACjB,oBAAoB,EACpB,iBAAiB,EACjB,SAAS,GACV,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export { assertStepExpectation } from './assertions.js';
2
+ export { launchBrowser } from './puppeteer.js';
3
+ export { runProject } from './run.js';
4
+ export { RUNNER_VERSION } from './version.js';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { BrowserAdapter, LaunchBrowserOptions } from './types.js';
2
+ export declare function launchBrowser(options?: LaunchBrowserOptions): Promise<BrowserAdapter>;
3
+ //# sourceMappingURL=puppeteer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"puppeteer.d.ts","sourceRoot":"","sources":["../src/puppeteer.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,cAAc,EAGd,oBAAoB,EACrB,MAAM,YAAY,CAAC;AAsHpB,wBAAsB,aAAa,CAAC,OAAO,GAAE,oBAAyB,GAAG,OAAO,CAAC,cAAc,CAAC,CAE/F"}
@@ -0,0 +1,101 @@
1
+ import puppeteer, {} from 'puppeteer-core';
2
+ function browserName(version) {
3
+ return version.split('/')[0] || 'Chrome';
4
+ }
5
+ function browserVersion(version) {
6
+ return version.split('/')[1] || version;
7
+ }
8
+ function executionError(result) {
9
+ if (result.errorText?.trim())
10
+ return result.errorText.trim();
11
+ if (result.exception?.description?.trim())
12
+ return result.exception.description.trim();
13
+ return 'The browser reported a WebMCP execution error without details.';
14
+ }
15
+ class PuppeteerPageAdapter {
16
+ #page;
17
+ constructor(page) {
18
+ this.#page = page;
19
+ }
20
+ async close() {
21
+ await this.#page.close();
22
+ }
23
+ currentUrl() {
24
+ return this.#page.url();
25
+ }
26
+ async executeTool(name, input, signal) {
27
+ const tool = this.#page.webmcp.tools().find((candidate) => candidate.name === name);
28
+ if (!tool)
29
+ return { status: 'error', error: `WebMCP tool "${name}" is unavailable.` };
30
+ const result = await tool.execute(input, signal ? { signal } : undefined);
31
+ if (result.status === 'Completed')
32
+ return { status: 'completed', output: result.output };
33
+ return { status: 'error', error: executionError(result) };
34
+ }
35
+ async goto(url, timeoutMs) {
36
+ await this.#page.goto(url, { timeout: timeoutMs, waitUntil: 'networkidle2' });
37
+ }
38
+ async inspectDom(expectation) {
39
+ const element = await this.#page.$(expectation.selector);
40
+ if (expectation.state === 'detached') {
41
+ return element ? `Expected "${expectation.selector}" to be detached.` : undefined;
42
+ }
43
+ if (!element)
44
+ return `Expected "${expectation.selector}" to be attached.`;
45
+ if (expectation.state === 'visible' && !(await element.isVisible())) {
46
+ return `Expected "${expectation.selector}" to be visible.`;
47
+ }
48
+ if (expectation.state === 'hidden' && !(await element.isHidden())) {
49
+ return `Expected "${expectation.selector}" to be hidden.`;
50
+ }
51
+ if (expectation.textContains !== undefined) {
52
+ const text = await element.evaluate((node) => node.textContent ?? '');
53
+ if (!text.includes(expectation.textContains)) {
54
+ return `Expected "${expectation.selector}" text to contain the configured value.`;
55
+ }
56
+ }
57
+ if (expectation.attribute) {
58
+ const actual = await element.evaluate((node, name) => node.getAttribute(name), expectation.attribute.name);
59
+ if (actual !== expectation.attribute.equals) {
60
+ return `Expected "${expectation.selector}" attribute "${expectation.attribute.name}" to equal the configured value.`;
61
+ }
62
+ }
63
+ return undefined;
64
+ }
65
+ async listTools() {
66
+ return this.#page.webmcp.tools().map((tool) => ({ name: tool.name }));
67
+ }
68
+ }
69
+ class PuppeteerBrowserAdapter {
70
+ #browser;
71
+ channel;
72
+ name;
73
+ version;
74
+ constructor(browser, channel, version) {
75
+ this.#browser = browser;
76
+ this.channel = channel;
77
+ this.name = browserName(version);
78
+ this.version = browserVersion(version);
79
+ }
80
+ static async launch(options = {}) {
81
+ const channel = options.channel ?? 'chrome';
82
+ const browser = await puppeteer.launch({
83
+ browser: 'chrome',
84
+ channel,
85
+ headless: options.headless ?? true,
86
+ ...(options.executablePath === undefined ? {} : { executablePath: options.executablePath }),
87
+ args: ['--enable-features=WebMCP'],
88
+ });
89
+ return new PuppeteerBrowserAdapter(browser, channel, await browser.version());
90
+ }
91
+ async close() {
92
+ await this.#browser.close();
93
+ }
94
+ async newPage() {
95
+ return new PuppeteerPageAdapter(await this.#browser.newPage());
96
+ }
97
+ }
98
+ export async function launchBrowser(options = {}) {
99
+ return PuppeteerBrowserAdapter.launch(options);
100
+ }
101
+ //# sourceMappingURL=puppeteer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"puppeteer.js","sourceRoot":"","sources":["../src/puppeteer.ts"],"names":[],"mappings":"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"}
package/dist/run.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ import type { RunReport } from '@siteverb/contracts';
2
+ import type { RunProjectOptions } from './types.js';
3
+ export declare function runProject(options: RunProjectOptions): Promise<RunReport>;
4
+ //# sourceMappingURL=run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAIV,SAAS,EAEV,MAAM,qBAAqB,CAAC;AAK7B,OAAO,KAAK,EAAyC,iBAAiB,EAAE,MAAM,YAAY,CAAC;AA6Q3F,wBAAsB,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,SAAS,CAAC,CAmD/E"}
package/dist/run.js ADDED
@@ -0,0 +1,287 @@
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ import { parseRunReport } from '@siteverb/contracts';
3
+ import { assessCompatibility, getClientProfile } from '@siteverb/profiles';
4
+ import { assertStepExpectation } from './assertions.js';
5
+ import { launchBrowser } from './puppeteer.js';
6
+ import { RUNNER_VERSION } from './version.js';
7
+ const DEFAULT_TIMEOUT_MS = 30_000;
8
+ const MAX_TIMEOUT_MS = 600_000;
9
+ class RunnerTimeoutError extends Error {
10
+ name = 'RunnerTimeoutError';
11
+ }
12
+ function reportError(error, includeDetails, fallback) {
13
+ if (error instanceof RunnerTimeoutError)
14
+ return error.message;
15
+ if (!includeDetails)
16
+ return fallback;
17
+ const message = error instanceof Error
18
+ ? error.message || error.name
19
+ : typeof error === 'string'
20
+ ? error
21
+ : fallback;
22
+ return message.replaceAll(/[\u0000-\u001f\u007f]+/g, ' ').slice(0, 4_000) || fallback;
23
+ }
24
+ async function withTimeout(label, timeoutMs, operation) {
25
+ const controller = new AbortController();
26
+ const timeoutError = new RunnerTimeoutError(`${label} timed out after ${timeoutMs} ms.`);
27
+ const timer = setTimeout(() => controller.abort(timeoutError), timeoutMs);
28
+ try {
29
+ return await Promise.race([
30
+ operation(controller.signal),
31
+ new Promise((_, reject) => {
32
+ controller.signal.addEventListener('abort', () => reject(controller.signal.reason), {
33
+ once: true,
34
+ });
35
+ }),
36
+ ]);
37
+ }
38
+ finally {
39
+ clearTimeout(timer);
40
+ }
41
+ }
42
+ function stableJson(value) {
43
+ if (value === null || typeof value !== 'object')
44
+ return JSON.stringify(value);
45
+ if (Array.isArray(value))
46
+ return `[${value.map(stableJson).join(',')}]`;
47
+ const record = value;
48
+ return `{${Object.keys(record)
49
+ .sort()
50
+ .map((key) => `${JSON.stringify(key)}:${stableJson(record[key])}`)
51
+ .join(',')}}`;
52
+ }
53
+ function contractHash(contract) {
54
+ return `sha256:${createHash('sha256').update(stableJson(contract)).digest('hex')}`;
55
+ }
56
+ function reportTargetUrl(targetUrl) {
57
+ const url = new URL(targetUrl);
58
+ return `${url.origin}/`;
59
+ }
60
+ function validateRunOptions(options) {
61
+ let target;
62
+ try {
63
+ target = new URL(options.targetUrl);
64
+ }
65
+ catch {
66
+ throw new TypeError('targetUrl must be an absolute HTTP or HTTPS URL.');
67
+ }
68
+ if (target.protocol !== 'http:' && target.protocol !== 'https:') {
69
+ throw new TypeError('targetUrl must be an absolute HTTP or HTTPS URL.');
70
+ }
71
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
72
+ if (!Number.isSafeInteger(timeoutMs) || timeoutMs <= 0 || timeoutMs > MAX_TIMEOUT_MS) {
73
+ throw new TypeError(`timeoutMs must be an integer from 1 to ${MAX_TIMEOUT_MS}.`);
74
+ }
75
+ if ((options.profileIds?.length ?? 0) > 20) {
76
+ throw new TypeError('At most 20 client profiles may be assessed in one run.');
77
+ }
78
+ if (options.profileIds && new Set(options.profileIds).size !== options.profileIds.length) {
79
+ throw new TypeError('Client profile IDs must be unique.');
80
+ }
81
+ }
82
+ async function waitForTool(page, name, timeoutMs) {
83
+ const startedAt = Date.now();
84
+ while (Date.now() - startedAt < Math.min(timeoutMs, 5_000)) {
85
+ if ((await page.listTools()).some((tool) => tool.name === name))
86
+ return true;
87
+ await new Promise((resolve) => setTimeout(resolve, 100));
88
+ }
89
+ return false;
90
+ }
91
+ async function runStep(context, step, index) {
92
+ const startedAt = Date.now();
93
+ const tool = context.contract.tools.find((candidate) => candidate.id === step.tool);
94
+ if (!tool) {
95
+ return {
96
+ index,
97
+ toolId: step.tool,
98
+ toolName: 'unknown',
99
+ status: 'failed',
100
+ durationMs: Date.now() - startedAt,
101
+ error: `Contract tool "${step.tool}" is unavailable.`,
102
+ };
103
+ }
104
+ if (tool.risk !== 'read-only' && !context.allowMutations) {
105
+ return {
106
+ index,
107
+ toolId: tool.id,
108
+ toolName: tool.name,
109
+ status: 'failed',
110
+ durationMs: Date.now() - startedAt,
111
+ error: `Mutation denied for "${tool.id}". Enable mutations only for an approved test target.`,
112
+ };
113
+ }
114
+ if (context.journey.policy.requireHumanBefore.includes(tool.id) &&
115
+ !context.approvedTools.has(tool.id)) {
116
+ return {
117
+ index,
118
+ toolId: tool.id,
119
+ toolName: tool.name,
120
+ status: 'failed',
121
+ durationMs: Date.now() - startedAt,
122
+ error: `Explicit approval is required before invoking "${tool.id}".`,
123
+ };
124
+ }
125
+ if (!(await waitForTool(context.page, tool.name, context.timeoutMs))) {
126
+ return {
127
+ index,
128
+ toolId: tool.id,
129
+ toolName: tool.name,
130
+ status: 'failed',
131
+ durationMs: Date.now() - startedAt,
132
+ error: `WebMCP tool "${tool.name}" is not registered in the current page state.`,
133
+ };
134
+ }
135
+ try {
136
+ const result = await withTimeout(`Tool "${tool.name}"`, context.timeoutMs, (signal) => context.page.executeTool(tool.name, step.input, signal));
137
+ if (result.status === 'error') {
138
+ return {
139
+ index,
140
+ toolId: tool.id,
141
+ toolName: tool.name,
142
+ status: 'failed',
143
+ durationMs: Date.now() - startedAt,
144
+ error: reportError(result.error, context.includeErrorDetails, 'The browser reported a WebMCP execution failure.'),
145
+ };
146
+ }
147
+ const assertionFailure = await withTimeout(`Postconditions for "${tool.name}"`, context.timeoutMs, () => assertStepExpectation(context.page, result.output, step.expect));
148
+ return {
149
+ index,
150
+ toolId: tool.id,
151
+ toolName: tool.name,
152
+ status: assertionFailure ? 'failed' : 'passed',
153
+ durationMs: Date.now() - startedAt,
154
+ ...(assertionFailure ? { error: assertionFailure } : {}),
155
+ };
156
+ }
157
+ catch (error) {
158
+ return {
159
+ index,
160
+ toolId: tool.id,
161
+ toolName: tool.name,
162
+ status: 'failed',
163
+ durationMs: Date.now() - startedAt,
164
+ error: reportError(error, context.includeErrorDetails, 'WebMCP tool execution failed.'),
165
+ };
166
+ }
167
+ }
168
+ async function runSteps(context, steps, stopOnFailure = true) {
169
+ const reports = [];
170
+ for (const [offset, step] of steps.entries()) {
171
+ const report = await runStep(context, step, offset + 1);
172
+ reports.push(report);
173
+ if (stopOnFailure && report.status === 'failed')
174
+ break;
175
+ }
176
+ return reports;
177
+ }
178
+ async function runJourney(contract, journey, page, options) {
179
+ const startedAt = Date.now();
180
+ const context = {
181
+ allowMutations: options.allowMutations ?? false,
182
+ approvedTools: new Set(options.approvedTools ?? []),
183
+ contract,
184
+ journey,
185
+ page,
186
+ includeErrorDetails: options.includeErrorDetails ?? false,
187
+ targetUrl: options.targetUrl,
188
+ timeoutMs: options.timeoutMs ?? DEFAULT_TIMEOUT_MS,
189
+ };
190
+ const steps = [];
191
+ const cleanup = [];
192
+ let setupError;
193
+ try {
194
+ await page.goto(new URL(journey.start, options.targetUrl).href, context.timeoutMs);
195
+ steps.push(...(await runSteps(context, journey.steps)));
196
+ }
197
+ catch (error) {
198
+ setupError = reportError(error, context.includeErrorDetails, 'Journey setup failed.');
199
+ }
200
+ finally {
201
+ if (journey.cleanup)
202
+ cleanup.push(...(await runSteps(context, journey.cleanup, false)));
203
+ }
204
+ const failed = Boolean(setupError ||
205
+ steps.some((step) => step.status === 'failed') ||
206
+ cleanup.some((step) => step.status === 'failed'));
207
+ return {
208
+ id: journey.id,
209
+ name: journey.name,
210
+ status: failed ? 'failed' : 'passed',
211
+ durationMs: Date.now() - startedAt,
212
+ steps,
213
+ cleanup,
214
+ ...(setupError ? { error: setupError } : {}),
215
+ };
216
+ }
217
+ function selectJourneys(contract, ids) {
218
+ if (!ids?.length)
219
+ return contract.journeys;
220
+ const requested = new Set(ids);
221
+ const unknown = ids.filter((id) => !contract.journeys.some((journey) => journey.id === id));
222
+ if (unknown.length > 0)
223
+ throw new TypeError(`Unknown journey ids: ${unknown.join(', ')}`);
224
+ return contract.journeys.filter((journey) => requested.has(journey.id));
225
+ }
226
+ function assessProfiles(contract, ids) {
227
+ if (!ids?.length)
228
+ return [];
229
+ return ids.map((id) => {
230
+ const profile = getClientProfile(id);
231
+ if (!profile)
232
+ throw new TypeError(`Unknown client profile id: ${id}`);
233
+ return assessCompatibility(contract, profile);
234
+ });
235
+ }
236
+ export async function runProject(options) {
237
+ const startedAt = new Date();
238
+ validateRunOptions(options);
239
+ const selectedJourneys = selectJourneys(options.contract, options.journeyIds);
240
+ const compatibility = assessProfiles(options.contract, options.profileIds);
241
+ const browser = options.browser ??
242
+ (await launchBrowser({
243
+ ...(options.channel === undefined ? {} : { channel: options.channel }),
244
+ ...(options.executablePath === undefined ? {} : { executablePath: options.executablePath }),
245
+ ...(options.headless === undefined ? {} : { headless: options.headless }),
246
+ }));
247
+ const journeys = [];
248
+ try {
249
+ for (const journey of selectedJourneys) {
250
+ const page = await browser.newPage();
251
+ try {
252
+ journeys.push(await runJourney(options.contract, journey, page, options));
253
+ }
254
+ finally {
255
+ await page.close();
256
+ }
257
+ }
258
+ }
259
+ finally {
260
+ await browser.close();
261
+ }
262
+ const report = {
263
+ version: 1,
264
+ runId: randomUUID(),
265
+ startedAt: startedAt.toISOString(),
266
+ finishedAt: new Date().toISOString(),
267
+ targetUrl: reportTargetUrl(options.targetUrl),
268
+ project: options.contract.project,
269
+ contractHash: contractHash(options.contract),
270
+ runner: { name: '@siteverb/runner', version: RUNNER_VERSION },
271
+ browser: {
272
+ name: browser.name,
273
+ version: browser.version,
274
+ channel: browser.channel,
275
+ evidence: 'real-browser',
276
+ },
277
+ summary: {
278
+ passed: journeys.filter((journey) => journey.status === 'passed').length,
279
+ failed: journeys.filter((journey) => journey.status === 'failed').length,
280
+ skipped: 0,
281
+ },
282
+ compatibility,
283
+ journeys,
284
+ };
285
+ return parseRunReport(report);
286
+ }
287
+ //# sourceMappingURL=run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.js","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ import type { DomExpectation, JourneyContract, ProjectContract, RunReport } from '@siteverb/contracts';
2
+ import type { ChromeReleaseChannel } from 'puppeteer-core';
3
+ export interface BrowserTool {
4
+ readonly name: string;
5
+ }
6
+ export interface BrowserToolResult {
7
+ readonly error?: string;
8
+ readonly output?: unknown;
9
+ readonly status: 'completed' | 'error';
10
+ }
11
+ export interface BrowserPageAdapter {
12
+ close(): Promise<void>;
13
+ currentUrl(): string;
14
+ executeTool(name: string, input: Record<string, unknown>, signal?: AbortSignal): Promise<BrowserToolResult>;
15
+ goto(url: string, timeoutMs: number): Promise<void>;
16
+ inspectDom(expectation: DomExpectation): Promise<string | undefined>;
17
+ listTools(): Promise<readonly BrowserTool[]>;
18
+ }
19
+ export interface BrowserAdapter {
20
+ readonly channel: string;
21
+ readonly name: string;
22
+ readonly version: string;
23
+ close(): Promise<void>;
24
+ newPage(): Promise<BrowserPageAdapter>;
25
+ }
26
+ export interface LaunchBrowserOptions {
27
+ readonly channel?: ChromeReleaseChannel;
28
+ readonly executablePath?: string;
29
+ readonly headless?: boolean;
30
+ readonly includeErrorDetails?: boolean;
31
+ }
32
+ export interface RunProjectOptions extends LaunchBrowserOptions {
33
+ readonly allowMutations?: boolean;
34
+ readonly approvedTools?: readonly string[];
35
+ readonly browser?: BrowserAdapter;
36
+ readonly contract: ProjectContract;
37
+ readonly journeyIds?: readonly string[];
38
+ readonly profileIds?: readonly string[];
39
+ readonly targetUrl: string;
40
+ readonly timeoutMs?: number;
41
+ }
42
+ export interface RunJourneyContext {
43
+ readonly allowMutations: boolean;
44
+ readonly approvedTools: ReadonlySet<string>;
45
+ readonly contract: ProjectContract;
46
+ readonly journey: JourneyContract;
47
+ readonly page: BrowserPageAdapter;
48
+ readonly includeErrorDetails: boolean;
49
+ readonly targetUrl: string;
50
+ readonly timeoutMs: number;
51
+ }
52
+ export type { RunReport };
53
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,eAAe,EACf,eAAe,EACf,SAAS,EACV,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAE3D,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC;CACxC;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,UAAU,IAAI,MAAM,CAAC;IACrB,WAAW,CACT,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAC9B,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,UAAU,CAAC,WAAW,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACrE,SAAS,IAAI,OAAO,CAAC,SAAS,WAAW,EAAE,CAAC,CAAC;CAC9C;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,OAAO,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAAC;CACxC;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,OAAO,CAAC,EAAE,oBAAoB,CAAC;IACxC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC;CACxC;AAED,MAAM,WAAW,iBAAkB,SAAQ,oBAAoB;IAC7D,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;IAClC,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,QAAQ,CAAC,OAAO,CAAC,EAAE,cAAc,CAAC;IAClC,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;IACnC,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC,QAAQ,CAAC,aAAa,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC5C,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAClC,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAC;IAClC,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAC;IACtC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,YAAY,EAAE,SAAS,EAAE,CAAC"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export declare const RUNNER_VERSION = "0.1.0-rc.0";
2
+ //# sourceMappingURL=version.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,cAAc,eAAe,CAAC"}
@@ -0,0 +1,2 @@
1
+ export const RUNNER_VERSION = '0.1.0-rc.0';
2
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,cAAc,GAAG,YAAY,CAAC"}
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@siteverb/runner",
3
+ "version": "0.1.0-rc.0",
4
+ "description": "Deterministic real-browser runner for Siteverb WebMCP journey contracts.",
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "files": [
8
+ "dist",
9
+ "README.md"
10
+ ],
11
+ "bin": {
12
+ "siteverb-run": "dist/cli.js"
13
+ },
14
+ "exports": {
15
+ ".": {
16
+ "types": "./dist/index.d.ts",
17
+ "import": "./dist/index.js"
18
+ },
19
+ "./package.json": "./package.json"
20
+ },
21
+ "main": "./dist/index.js",
22
+ "types": "./dist/index.d.ts",
23
+ "engines": {
24
+ "node": ">=22"
25
+ },
26
+ "scripts": {
27
+ "build": "tsc -p tsconfig.build.json",
28
+ "package:check": "publint && attw --pack . --profile esm-only",
29
+ "test": "vitest run",
30
+ "typecheck": "tsc -p tsconfig.json --noEmit"
31
+ },
32
+ "keywords": [
33
+ "webmcp",
34
+ "browser-testing",
35
+ "puppeteer",
36
+ "github-actions"
37
+ ],
38
+ "license": "Apache-2.0",
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "git+https://github.com/siteverb/siteverb.git",
42
+ "directory": "packages/runner"
43
+ },
44
+ "publishConfig": {
45
+ "access": "public",
46
+ "provenance": true
47
+ },
48
+ "dependencies": {
49
+ "@siteverb/contracts": "^0.1.0-rc.0",
50
+ "@siteverb/profiles": "^0.1.0-rc.0",
51
+ "puppeteer-core": "25.9.0"
52
+ }
53
+ }