@agentxjs/devtools 2.0.0 → 3.0.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/dist/bdd/cli.js +2 -2
- package/dist/bdd/cli.js.map +1 -1
- package/dist/bdd/index.d.ts +81 -81
- package/dist/bdd/index.js +167 -165
- package/dist/bdd/index.js.map +1 -1
- package/dist/{chunk-J6L73HM5.js → chunk-SVPPRUN5.js} +1 -1
- package/dist/{chunk-J6L73HM5.js.map → chunk-SVPPRUN5.js.map} +1 -1
- package/dist/{chunk-S7J75AXG.js → chunk-Y6RXZINS.js} +2 -2
- package/dist/{chunk-S7J75AXG.js.map → chunk-Y6RXZINS.js.map} +1 -1
- package/dist/{chunk-DR45HEV4.js → chunk-YFONF7SD.js} +1 -1
- package/dist/{chunk-DR45HEV4.js.map → chunk-YFONF7SD.js.map} +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +6 -6
- package/dist/index.js.map +1 -1
- package/dist/mock/index.d.ts +1 -1
- package/dist/mock/index.js +1 -1
- package/dist/recorder/index.d.ts +1 -1
- package/dist/recorder/index.js +1 -1
- package/package.json +6 -6
- package/src/Devtools.ts +6 -6
- package/src/bdd/agent-doc-tester.ts +1 -1
- package/src/bdd/agent-ui-tester.ts +1 -1
- package/src/bdd/cli.ts +2 -2
- package/src/bdd/dev-server.ts +1 -1
- package/src/bdd/index.ts +17 -22
- package/src/bdd/paths.ts +2 -2
- package/src/bdd/playwright.ts +1 -1
- package/src/env.ts +1 -1
- package/src/index.ts +16 -21
- package/src/mock/MockDriver.ts +3 -3
- package/src/mock/index.ts +1 -1
- package/src/recorder/RecordingDriver.ts +2 -2
- package/src/recorder/index.ts +1 -1
package/dist/bdd/cli.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
// src/bdd/cli.ts
|
|
4
4
|
import { spawn } from "child_process";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { existsSync, readFileSync, unlinkSync, writeFileSync } from "fs";
|
|
6
|
+
import { dirname, relative, resolve } from "path";
|
|
7
7
|
import { fileURLToPath } from "url";
|
|
8
8
|
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
9
9
|
function loadEnvFile(filePath) {
|
package/dist/bdd/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/bdd/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * BDD CLI wrapper for cucumber-js\n *\n * Usage:\n * bdd # Run all tests\n * bdd path/to/file.feature # Run specific feature file\n * bdd path/to/file.feature:10 # Run specific scenario by line\n * bdd --tags @contributor # Run specific tags\n * bdd --tags \"@dev and not @slow\" # Tag expression\n * bdd --name \"token usage\" # Filter by scenario name (regex)\n * bdd --dry-run # Validate without executing\n * bdd --config path # Custom config (default: bdd/cucumber.js)\n */\n\nimport { spawn } from \"node:child_process\";\nimport {
|
|
1
|
+
{"version":3,"sources":["../../src/bdd/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * BDD CLI wrapper for cucumber-js\n *\n * Usage:\n * bdd # Run all tests\n * bdd path/to/file.feature # Run specific feature file\n * bdd path/to/file.feature:10 # Run specific scenario by line\n * bdd --tags @contributor # Run specific tags\n * bdd --tags \"@dev and not @slow\" # Tag expression\n * bdd --name \"token usage\" # Filter by scenario name (regex)\n * bdd --dry-run # Validate without executing\n * bdd --config path # Custom config (default: bdd/cucumber.js)\n */\n\nimport { spawn } from \"node:child_process\";\nimport { existsSync, readFileSync, unlinkSync, writeFileSync } from \"node:fs\";\nimport { dirname, relative, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\n// Load .env files (like dotenv but zero dependencies)\nfunction loadEnvFile(filePath: string) {\n if (!existsSync(filePath)) return;\n const content = readFileSync(filePath, \"utf-8\");\n for (const line of content.split(\"\\n\")) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith(\"#\")) continue;\n const eqIndex = trimmed.indexOf(\"=\");\n if (eqIndex === -1) continue;\n const key = trimmed.slice(0, eqIndex).trim();\n let value = trimmed.slice(eqIndex + 1).trim();\n if (\n (value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\"))\n ) {\n value = value.slice(1, -1);\n }\n if (process.env[key] === undefined) {\n process.env[key] = value;\n }\n }\n}\n\n// Find monorepo root by walking up to find the root package.json with workspaces\nfunction findMonorepoRoot(startDir: string): string | null {\n let dir = startDir;\n while (true) {\n const pkgPath = resolve(dir, \"package.json\");\n if (existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\"));\n if (pkg.workspaces) return dir;\n } catch {\n // ignore parse errors\n }\n }\n const parent = dirname(dir);\n if (parent === dir) return null;\n dir = parent;\n }\n}\n\nconst cwd = process.cwd();\n\n// Load .env files from cwd first, then monorepo root\nloadEnvFile(resolve(cwd, \".env\"));\nloadEnvFile(resolve(cwd, \".env.local\"));\n\nconst monorepoRoot = findMonorepoRoot(cwd);\nif (monorepoRoot && monorepoRoot !== cwd) {\n loadEnvFile(resolve(monorepoRoot, \".env\"));\n loadEnvFile(resolve(monorepoRoot, \".env.local\"));\n}\n\nconst args = process.argv.slice(2);\n\n// Extract --config\nlet configPath = \"bdd/cucumber.js\";\nconst configIndex = args.indexOf(\"--config\");\nif (configIndex !== -1 && args[configIndex + 1]) {\n configPath = args[configIndex + 1];\n args.splice(configIndex, 2);\n}\n\n// Check if config exists\nconst fullConfigPath = resolve(cwd, configPath);\nif (!existsSync(fullConfigPath)) {\n console.error(`Config not found: ${fullConfigPath}`);\n console.error(\"Create bdd/cucumber.js or specify --config path\");\n process.exit(1);\n}\n\n// Separate positional args (feature files/lines) from flags\nconst featurePaths: string[] = [];\nconst flags: string[] = [];\n\nfor (const arg of args) {\n if (arg.startsWith(\"-\")) {\n flags.push(arg);\n } else if (arg.endsWith(\".feature\") || arg.includes(\".feature:\")) {\n featurePaths.push(arg);\n } else {\n // Could be a flag value (e.g. after --tags), keep as-is\n flags.push(arg);\n }\n}\n\n// Find cucumber-js binary\nconst cucumberPaths = [\n resolve(cwd, \"node_modules/.bin/cucumber-js\"),\n resolve(__dirname, \"../../../.bin/cucumber-js\"),\n \"cucumber-js\",\n];\nconst cucumberBin =\n cucumberPaths.find((p) => p === \"cucumber-js\" || existsSync(p)) || \"cucumber-js\";\n\nconst rootNodeModules = resolve(cwd, \"node_modules\");\n\n// When feature paths are specified, generate a temp config that overrides\n// the original config's `paths` — cucumber-js config.paths takes precedence\n// over positional args, so we must override it in the config itself.\nlet effectiveConfig = configPath;\nlet tempConfigPath: string | null = null;\n\nif (featurePaths.length > 0) {\n const configRelPath = relative(\n dirname(resolve(cwd, \"bdd/.tmp-cucumber.js\")),\n fullConfigPath\n ).replace(/\\\\/g, \"/\");\n const pathsJson = JSON.stringify(featurePaths);\n const tempContent = [\n `import config from \"./${configRelPath}\";`,\n `export default { ...config.default ?? config, paths: ${pathsJson} };`,\n \"\",\n ].join(\"\\n\");\n\n tempConfigPath = resolve(cwd, \"bdd/.tmp-cucumber.js\");\n writeFileSync(tempConfigPath, tempContent);\n effectiveConfig = \"bdd/.tmp-cucumber.js\";\n}\n\n// Build cucumber args\nconst cucumberArgs = [\"--config\", effectiveConfig, ...flags];\n\nconst child = spawn(cucumberBin, cucumberArgs, {\n stdio: \"inherit\",\n env: {\n ...process.env,\n NODE_OPTIONS: \"--import tsx\",\n NODE_PATH: rootNodeModules,\n },\n});\n\nchild.on(\"close\", (code) => {\n // Clean up temp config\n if (tempConfigPath && existsSync(tempConfigPath)) {\n try {\n unlinkSync(tempConfigPath);\n } catch {\n // ignore cleanup errors\n }\n }\n process.exit(code ?? 0);\n});\n"],"mappings":"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","names":[]}
|
package/dist/bdd/index.d.ts
CHANGED
|
@@ -1,5 +1,50 @@
|
|
|
1
|
-
import { Browser, Page } from '@playwright/test';
|
|
2
1
|
import { ChildProcess } from 'node:child_process';
|
|
2
|
+
import { Page, Browser } from '@playwright/test';
|
|
3
|
+
|
|
4
|
+
interface DocTestResult {
|
|
5
|
+
passed: boolean;
|
|
6
|
+
output: string;
|
|
7
|
+
}
|
|
8
|
+
interface DocTesterOptions {
|
|
9
|
+
/** LLM provider (default: "anthropic") */
|
|
10
|
+
provider?: string;
|
|
11
|
+
/** Model name */
|
|
12
|
+
model?: string;
|
|
13
|
+
/** API key (reads from env if not provided) */
|
|
14
|
+
apiKey?: string;
|
|
15
|
+
/** Base URL (reads from env if not provided) */
|
|
16
|
+
baseUrl?: string;
|
|
17
|
+
/** Timeout in ms */
|
|
18
|
+
timeout?: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Evaluate a document against requirements using AgentX.
|
|
22
|
+
*
|
|
23
|
+
* Uses agentxjs local mode — no subprocess, no CLI, no auth issues.
|
|
24
|
+
* Requires `agentxjs` as a peer dependency.
|
|
25
|
+
*/
|
|
26
|
+
declare function agentDocTester(options: {
|
|
27
|
+
files: string[];
|
|
28
|
+
requirements: string;
|
|
29
|
+
}, testerOptions?: DocTesterOptions): Promise<DocTestResult>;
|
|
30
|
+
|
|
31
|
+
interface UiTestResult {
|
|
32
|
+
passed: boolean;
|
|
33
|
+
output: string;
|
|
34
|
+
}
|
|
35
|
+
interface UiTesterOptions {
|
|
36
|
+
model?: string;
|
|
37
|
+
baseUrl?: string;
|
|
38
|
+
timeout?: number;
|
|
39
|
+
/** Show browser window (default: false) */
|
|
40
|
+
headed?: boolean;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Run a UI test scenario using Claude Code CLI + agent-browser.
|
|
44
|
+
*
|
|
45
|
+
* BDD scripts must run under Node.js (not Bun) to avoid claude CLI auth bug.
|
|
46
|
+
*/
|
|
47
|
+
declare function agentUiTester(prompt: string, options?: UiTesterOptions): UiTestResult;
|
|
3
48
|
|
|
4
49
|
/**
|
|
5
50
|
* Shared Cucumber configuration for BDD tests
|
|
@@ -40,49 +85,6 @@ declare function createCucumberConfig(options: CucumberConfigOptions): {
|
|
|
40
85
|
};
|
|
41
86
|
};
|
|
42
87
|
|
|
43
|
-
/**
|
|
44
|
-
* Playwright utilities for BDD testing
|
|
45
|
-
*
|
|
46
|
-
* Uses system Chrome to avoid downloading Chromium.
|
|
47
|
-
* Install: PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 bun add -d @playwright/test
|
|
48
|
-
*
|
|
49
|
-
* Browser lifecycle:
|
|
50
|
-
* - Single browser instance for all tests
|
|
51
|
-
* - Single page (tab) reused across scenarios
|
|
52
|
-
* - resetPage() clears state between scenarios
|
|
53
|
-
*/
|
|
54
|
-
|
|
55
|
-
interface BrowserOptions {
|
|
56
|
-
headless?: boolean;
|
|
57
|
-
slowMo?: number;
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Launch browser using system Chrome (singleton)
|
|
61
|
-
*/
|
|
62
|
-
declare function launchBrowser(options?: BrowserOptions): Promise<Browser>;
|
|
63
|
-
/**
|
|
64
|
-
* Get or create a page (singleton, reused across scenarios)
|
|
65
|
-
*/
|
|
66
|
-
declare function getPage(): Promise<Page>;
|
|
67
|
-
/**
|
|
68
|
-
* Reset page state between scenarios (without closing)
|
|
69
|
-
* Use this instead of closePage() for faster tests
|
|
70
|
-
*/
|
|
71
|
-
declare function resetPage(): Promise<void>;
|
|
72
|
-
/**
|
|
73
|
-
* Close current page
|
|
74
|
-
* @deprecated Use resetPage() for faster tests. Only use closePage() if you need full isolation.
|
|
75
|
-
*/
|
|
76
|
-
declare function closePage(): Promise<void>;
|
|
77
|
-
/**
|
|
78
|
-
* Close browser and cleanup
|
|
79
|
-
*/
|
|
80
|
-
declare function closeBrowser(): Promise<void>;
|
|
81
|
-
/**
|
|
82
|
-
* Wait for a URL to be accessible
|
|
83
|
-
*/
|
|
84
|
-
declare function waitForUrl(url: string, timeout?: number): Promise<boolean>;
|
|
85
|
-
|
|
86
88
|
/**
|
|
87
89
|
* Dev server utilities for BDD testing
|
|
88
90
|
*
|
|
@@ -154,49 +156,47 @@ declare const paths: {
|
|
|
154
156
|
reset: typeof resetPaths;
|
|
155
157
|
};
|
|
156
158
|
|
|
157
|
-
interface UiTestResult {
|
|
158
|
-
passed: boolean;
|
|
159
|
-
output: string;
|
|
160
|
-
}
|
|
161
|
-
interface UiTesterOptions {
|
|
162
|
-
model?: string;
|
|
163
|
-
baseUrl?: string;
|
|
164
|
-
timeout?: number;
|
|
165
|
-
/** Show browser window (default: false) */
|
|
166
|
-
headed?: boolean;
|
|
167
|
-
}
|
|
168
159
|
/**
|
|
169
|
-
*
|
|
160
|
+
* Playwright utilities for BDD testing
|
|
170
161
|
*
|
|
171
|
-
*
|
|
162
|
+
* Uses system Chrome to avoid downloading Chromium.
|
|
163
|
+
* Install: PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 bun add -d @playwright/test
|
|
164
|
+
*
|
|
165
|
+
* Browser lifecycle:
|
|
166
|
+
* - Single browser instance for all tests
|
|
167
|
+
* - Single page (tab) reused across scenarios
|
|
168
|
+
* - resetPage() clears state between scenarios
|
|
172
169
|
*/
|
|
173
|
-
declare function agentUiTester(prompt: string, options?: UiTesterOptions): UiTestResult;
|
|
174
170
|
|
|
175
|
-
interface
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
}
|
|
179
|
-
interface DocTesterOptions {
|
|
180
|
-
/** LLM provider (default: "anthropic") */
|
|
181
|
-
provider?: string;
|
|
182
|
-
/** Model name */
|
|
183
|
-
model?: string;
|
|
184
|
-
/** API key (reads from env if not provided) */
|
|
185
|
-
apiKey?: string;
|
|
186
|
-
/** Base URL (reads from env if not provided) */
|
|
187
|
-
baseUrl?: string;
|
|
188
|
-
/** Timeout in ms */
|
|
189
|
-
timeout?: number;
|
|
171
|
+
interface BrowserOptions {
|
|
172
|
+
headless?: boolean;
|
|
173
|
+
slowMo?: number;
|
|
190
174
|
}
|
|
191
175
|
/**
|
|
192
|
-
*
|
|
193
|
-
*
|
|
194
|
-
* Uses agentxjs local mode — no subprocess, no CLI, no auth issues.
|
|
195
|
-
* Requires `agentxjs` as a peer dependency.
|
|
176
|
+
* Launch browser using system Chrome (singleton)
|
|
196
177
|
*/
|
|
197
|
-
declare function
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
178
|
+
declare function launchBrowser(options?: BrowserOptions): Promise<Browser>;
|
|
179
|
+
/**
|
|
180
|
+
* Get or create a page (singleton, reused across scenarios)
|
|
181
|
+
*/
|
|
182
|
+
declare function getPage(): Promise<Page>;
|
|
183
|
+
/**
|
|
184
|
+
* Reset page state between scenarios (without closing)
|
|
185
|
+
* Use this instead of closePage() for faster tests
|
|
186
|
+
*/
|
|
187
|
+
declare function resetPage(): Promise<void>;
|
|
188
|
+
/**
|
|
189
|
+
* Close current page
|
|
190
|
+
* @deprecated Use resetPage() for faster tests. Only use closePage() if you need full isolation.
|
|
191
|
+
*/
|
|
192
|
+
declare function closePage(): Promise<void>;
|
|
193
|
+
/**
|
|
194
|
+
* Close browser and cleanup
|
|
195
|
+
*/
|
|
196
|
+
declare function closeBrowser(): Promise<void>;
|
|
197
|
+
/**
|
|
198
|
+
* Wait for a URL to be accessible
|
|
199
|
+
*/
|
|
200
|
+
declare function waitForUrl(url: string, timeout?: number): Promise<boolean>;
|
|
201
201
|
|
|
202
202
|
export { type BrowserOptions, type CucumberConfigOptions, type DevServerOptions, type DocTestResult, type DocTesterOptions, type UiTestResult, type UiTesterOptions, agentDocTester, agentUiTester, closeBrowser, closePage, createCucumberConfig, ensureDir, getBddPath, getDevServer, getFixturesPath, getMonorepoPath, getPackagePath, getPage, getTempPath, launchBrowser, paths, resetPage, resetPaths, startDevServer, stopDevServer, waitForUrl };
|
package/dist/bdd/index.js
CHANGED
|
@@ -1,10 +1,159 @@
|
|
|
1
1
|
import {
|
|
2
2
|
env
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-Y6RXZINS.js";
|
|
4
4
|
import {
|
|
5
5
|
__require
|
|
6
6
|
} from "../chunk-DGUM43GV.js";
|
|
7
7
|
|
|
8
|
+
// src/bdd/agent-doc-tester.ts
|
|
9
|
+
import { existsSync, readFileSync } from "fs";
|
|
10
|
+
var SYSTEM_PROMPT = `You are a documentation reviewer evaluating documents from the reader's experience.
|
|
11
|
+
|
|
12
|
+
EVALUATION DIMENSIONS:
|
|
13
|
+
1. Completeness \u2014 All required information is present. Nothing critical is missing.
|
|
14
|
+
2. Logic \u2014 Structure flows naturally. Concepts build on each other without jumps.
|
|
15
|
+
3. Readability \u2014 A newcomer can follow without confusion. No unexplained jargon.
|
|
16
|
+
|
|
17
|
+
RULES:
|
|
18
|
+
- Read the provided document carefully
|
|
19
|
+
- Evaluate each requirement listed in the prompt against ALL three dimensions
|
|
20
|
+
- Be strict but fair \u2014 the document should genuinely help the reader achieve the stated goal
|
|
21
|
+
- Output your result as a single line: PASS or FAIL followed by a brief reason
|
|
22
|
+
- If FAIL, list which specific requirements are not met and which dimension they violate`;
|
|
23
|
+
async function agentDocTester(options, testerOptions = {}) {
|
|
24
|
+
const {
|
|
25
|
+
provider = process.env.AGENTX_PROVIDER || "anthropic",
|
|
26
|
+
model = env.model,
|
|
27
|
+
apiKey = env.apiKey || "",
|
|
28
|
+
baseUrl = env.baseUrl,
|
|
29
|
+
timeout = 12e4
|
|
30
|
+
} = testerOptions;
|
|
31
|
+
const docContents = options.files.map((filePath) => {
|
|
32
|
+
if (!existsSync(filePath)) {
|
|
33
|
+
return `--- ${filePath} ---
|
|
34
|
+
[FILE NOT FOUND]`;
|
|
35
|
+
}
|
|
36
|
+
return `--- ${filePath} ---
|
|
37
|
+
${readFileSync(filePath, "utf-8")}`;
|
|
38
|
+
}).join("\n\n");
|
|
39
|
+
const userPrompt = [
|
|
40
|
+
"Evaluate the following document(s) against the requirements below.",
|
|
41
|
+
"",
|
|
42
|
+
"DOCUMENTS:",
|
|
43
|
+
docContents,
|
|
44
|
+
"",
|
|
45
|
+
"REQUIREMENTS:",
|
|
46
|
+
options.requirements,
|
|
47
|
+
"",
|
|
48
|
+
"Evaluate each requirement. Output PASS if all are met, FAIL if any are not."
|
|
49
|
+
].join("\n");
|
|
50
|
+
const moduleName = "agentxjs";
|
|
51
|
+
const agentxjs = await import(
|
|
52
|
+
/* @vite-ignore */
|
|
53
|
+
moduleName
|
|
54
|
+
);
|
|
55
|
+
const createAgentX = agentxjs.createAgentX;
|
|
56
|
+
let agentx = null;
|
|
57
|
+
try {
|
|
58
|
+
agentx = await createAgentX({
|
|
59
|
+
apiKey,
|
|
60
|
+
provider,
|
|
61
|
+
model,
|
|
62
|
+
baseUrl,
|
|
63
|
+
logLevel: "silent"
|
|
64
|
+
});
|
|
65
|
+
await agentx.containers.create("doc-tester");
|
|
66
|
+
const { record: image } = await agentx.images.create({
|
|
67
|
+
containerId: "doc-tester",
|
|
68
|
+
systemPrompt: SYSTEM_PROMPT
|
|
69
|
+
});
|
|
70
|
+
const { agentId } = await agentx.agents.create({ imageId: image.imageId });
|
|
71
|
+
let output = "";
|
|
72
|
+
agentx.on("text_delta", (e) => {
|
|
73
|
+
output += e.data.text;
|
|
74
|
+
});
|
|
75
|
+
await Promise.race([
|
|
76
|
+
agentx.sessions.send(agentId, userPrompt),
|
|
77
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error("Timeout")), timeout))
|
|
78
|
+
]);
|
|
79
|
+
output = output.trim();
|
|
80
|
+
const passed = /\*{0,2}PASS\*{0,2}\b/m.test(output);
|
|
81
|
+
return { passed, output };
|
|
82
|
+
} catch (error) {
|
|
83
|
+
return { passed: false, output: error.message || "Unknown error" };
|
|
84
|
+
} finally {
|
|
85
|
+
if (agentx) {
|
|
86
|
+
try {
|
|
87
|
+
await agentx.shutdown();
|
|
88
|
+
} catch {
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// src/bdd/agent-ui-tester.ts
|
|
95
|
+
import { execFileSync } from "child_process";
|
|
96
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
97
|
+
import { dirname, resolve } from "path";
|
|
98
|
+
import { fileURLToPath } from "url";
|
|
99
|
+
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
100
|
+
var SKILL_PATH = resolve(__dirname, "../../../../.claude/skills/agent-browser/SKILL.md");
|
|
101
|
+
function loadSystemPrompt(headed = false) {
|
|
102
|
+
let skillContent = "";
|
|
103
|
+
try {
|
|
104
|
+
skillContent = readFileSync2(SKILL_PATH, "utf-8");
|
|
105
|
+
} catch {
|
|
106
|
+
}
|
|
107
|
+
return `You are a UI tester. You test web application scenarios using the agent-browser CLI.
|
|
108
|
+
|
|
109
|
+
RULES:
|
|
110
|
+
- ONLY use agent-browser commands via Bash tool
|
|
111
|
+
- Use ${headed ? "--headed " : ""}--executable-path "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" for all commands
|
|
112
|
+
- After each navigation or click, run: agent-browser snapshot -i
|
|
113
|
+
- Refs (@e1, @e2) are invalidated after page changes \u2014 always re-snapshot
|
|
114
|
+
- At the end, close the browser with: agent-browser close
|
|
115
|
+
- Output your result as a single line: PASS or FAIL followed by a brief reason
|
|
116
|
+
|
|
117
|
+
${skillContent ? `AGENT-BROWSER REFERENCE:
|
|
118
|
+
${skillContent}` : ""}`;
|
|
119
|
+
}
|
|
120
|
+
function agentUiTester(prompt, options = {}) {
|
|
121
|
+
const { model = "haiku", baseUrl, timeout = 3e5, headed = false } = options;
|
|
122
|
+
const fullPrompt = baseUrl ? `Base URL: ${baseUrl}
|
|
123
|
+
|
|
124
|
+
${prompt}` : prompt;
|
|
125
|
+
const systemPrompt = loadSystemPrompt(headed);
|
|
126
|
+
const cleanEnv = Object.fromEntries(
|
|
127
|
+
Object.entries(process.env).filter(([k]) => !k.startsWith("CLAUDE"))
|
|
128
|
+
);
|
|
129
|
+
try {
|
|
130
|
+
const output = execFileSync(
|
|
131
|
+
"claude",
|
|
132
|
+
[
|
|
133
|
+
"-p",
|
|
134
|
+
fullPrompt,
|
|
135
|
+
"--model",
|
|
136
|
+
model,
|
|
137
|
+
"--append-system-prompt",
|
|
138
|
+
systemPrompt,
|
|
139
|
+
"--allowedTools",
|
|
140
|
+
"Bash(agent-browser:*)"
|
|
141
|
+
],
|
|
142
|
+
{
|
|
143
|
+
encoding: "utf-8",
|
|
144
|
+
timeout,
|
|
145
|
+
env: cleanEnv,
|
|
146
|
+
maxBuffer: 10 * 1024 * 1024
|
|
147
|
+
}
|
|
148
|
+
).trim();
|
|
149
|
+
const passed = /\*{0,2}PASS\*{0,2}\b/m.test(output);
|
|
150
|
+
return { passed, output };
|
|
151
|
+
} catch (error) {
|
|
152
|
+
const output = error.stdout || error.stderr || error.message || "Unknown error";
|
|
153
|
+
return { passed: false, output: output.trim() };
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
8
157
|
// src/bdd/cucumber.config.ts
|
|
9
158
|
function createCucumberConfig(options) {
|
|
10
159
|
return {
|
|
@@ -19,6 +168,9 @@ function createCucumberConfig(options) {
|
|
|
19
168
|
};
|
|
20
169
|
}
|
|
21
170
|
|
|
171
|
+
// src/bdd/dev-server.ts
|
|
172
|
+
import { spawn } from "child_process";
|
|
173
|
+
|
|
22
174
|
// src/bdd/playwright.ts
|
|
23
175
|
import { chromium } from "@playwright/test";
|
|
24
176
|
var browser = null;
|
|
@@ -82,7 +234,6 @@ async function waitForUrl(url, timeout = 3e4) {
|
|
|
82
234
|
}
|
|
83
235
|
|
|
84
236
|
// src/bdd/dev-server.ts
|
|
85
|
-
import { spawn } from "child_process";
|
|
86
237
|
var devServer = null;
|
|
87
238
|
async function startDevServer(options) {
|
|
88
239
|
if (devServer) return;
|
|
@@ -125,19 +276,19 @@ function getDevServer() {
|
|
|
125
276
|
}
|
|
126
277
|
|
|
127
278
|
// src/bdd/paths.ts
|
|
128
|
-
import {
|
|
129
|
-
import { existsSync, mkdtempSync, mkdirSync } from "fs";
|
|
279
|
+
import { existsSync as existsSync2, mkdirSync, mkdtempSync } from "fs";
|
|
130
280
|
import { tmpdir } from "os";
|
|
281
|
+
import { dirname as dirname2, resolve as resolve2 } from "path";
|
|
131
282
|
function findMonorepoRoot(startDir = process.cwd()) {
|
|
132
283
|
let dir = startDir;
|
|
133
284
|
while (dir !== "/") {
|
|
134
|
-
const pkgPath =
|
|
135
|
-
if (
|
|
285
|
+
const pkgPath = resolve2(dir, "package.json");
|
|
286
|
+
if (existsSync2(pkgPath)) {
|
|
136
287
|
try {
|
|
137
288
|
const pkg = __require(pkgPath);
|
|
138
289
|
if (pkg.workspaces || pkg.private === true) {
|
|
139
|
-
const hasPackages =
|
|
140
|
-
const hasApps =
|
|
290
|
+
const hasPackages = existsSync2(resolve2(dir, "packages"));
|
|
291
|
+
const hasApps = existsSync2(resolve2(dir, "apps"));
|
|
141
292
|
if (hasPackages || hasApps) {
|
|
142
293
|
return dir;
|
|
143
294
|
}
|
|
@@ -145,17 +296,17 @@ function findMonorepoRoot(startDir = process.cwd()) {
|
|
|
145
296
|
} catch {
|
|
146
297
|
}
|
|
147
298
|
}
|
|
148
|
-
dir =
|
|
299
|
+
dir = dirname2(dir);
|
|
149
300
|
}
|
|
150
301
|
return startDir;
|
|
151
302
|
}
|
|
152
303
|
function getPackageRoot(startDir = process.cwd()) {
|
|
153
304
|
let dir = startDir;
|
|
154
305
|
while (dir !== "/") {
|
|
155
|
-
if (
|
|
306
|
+
if (existsSync2(resolve2(dir, "package.json"))) {
|
|
156
307
|
return dir;
|
|
157
308
|
}
|
|
158
|
-
dir =
|
|
309
|
+
dir = dirname2(dir);
|
|
159
310
|
}
|
|
160
311
|
return startDir;
|
|
161
312
|
}
|
|
@@ -175,20 +326,20 @@ function getPackagePath() {
|
|
|
175
326
|
return _packageRoot;
|
|
176
327
|
}
|
|
177
328
|
function getBddPath() {
|
|
178
|
-
return
|
|
329
|
+
return resolve2(getPackagePath(), "bdd");
|
|
179
330
|
}
|
|
180
331
|
function getFixturesPath(subdir) {
|
|
181
|
-
const base =
|
|
182
|
-
return subdir ?
|
|
332
|
+
const base = resolve2(getBddPath(), "fixtures");
|
|
333
|
+
return subdir ? resolve2(base, subdir) : base;
|
|
183
334
|
}
|
|
184
335
|
function getTempPath(prefix = "bdd-") {
|
|
185
336
|
if (!_tempDir) {
|
|
186
|
-
_tempDir = mkdtempSync(
|
|
337
|
+
_tempDir = mkdtempSync(resolve2(tmpdir(), prefix));
|
|
187
338
|
}
|
|
188
339
|
return _tempDir;
|
|
189
340
|
}
|
|
190
341
|
function ensureDir(path) {
|
|
191
|
-
if (!
|
|
342
|
+
if (!existsSync2(path)) {
|
|
192
343
|
mkdirSync(path, { recursive: true });
|
|
193
344
|
}
|
|
194
345
|
return path;
|
|
@@ -207,155 +358,6 @@ var paths = {
|
|
|
207
358
|
ensure: ensureDir,
|
|
208
359
|
reset: resetPaths
|
|
209
360
|
};
|
|
210
|
-
|
|
211
|
-
// src/bdd/agent-ui-tester.ts
|
|
212
|
-
import { execFileSync } from "child_process";
|
|
213
|
-
import { readFileSync } from "fs";
|
|
214
|
-
import { resolve as resolve2, dirname as dirname2 } from "path";
|
|
215
|
-
import { fileURLToPath } from "url";
|
|
216
|
-
var __dirname = dirname2(fileURLToPath(import.meta.url));
|
|
217
|
-
var SKILL_PATH = resolve2(__dirname, "../../../../.claude/skills/agent-browser/SKILL.md");
|
|
218
|
-
function loadSystemPrompt(headed = false) {
|
|
219
|
-
let skillContent = "";
|
|
220
|
-
try {
|
|
221
|
-
skillContent = readFileSync(SKILL_PATH, "utf-8");
|
|
222
|
-
} catch {
|
|
223
|
-
}
|
|
224
|
-
return `You are a UI tester. You test web application scenarios using the agent-browser CLI.
|
|
225
|
-
|
|
226
|
-
RULES:
|
|
227
|
-
- ONLY use agent-browser commands via Bash tool
|
|
228
|
-
- Use ${headed ? "--headed " : ""}--executable-path "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" for all commands
|
|
229
|
-
- After each navigation or click, run: agent-browser snapshot -i
|
|
230
|
-
- Refs (@e1, @e2) are invalidated after page changes \u2014 always re-snapshot
|
|
231
|
-
- At the end, close the browser with: agent-browser close
|
|
232
|
-
- Output your result as a single line: PASS or FAIL followed by a brief reason
|
|
233
|
-
|
|
234
|
-
${skillContent ? `AGENT-BROWSER REFERENCE:
|
|
235
|
-
${skillContent}` : ""}`;
|
|
236
|
-
}
|
|
237
|
-
function agentUiTester(prompt, options = {}) {
|
|
238
|
-
const { model = "haiku", baseUrl, timeout = 3e5, headed = false } = options;
|
|
239
|
-
const fullPrompt = baseUrl ? `Base URL: ${baseUrl}
|
|
240
|
-
|
|
241
|
-
${prompt}` : prompt;
|
|
242
|
-
const systemPrompt = loadSystemPrompt(headed);
|
|
243
|
-
const cleanEnv = Object.fromEntries(
|
|
244
|
-
Object.entries(process.env).filter(([k]) => !k.startsWith("CLAUDE"))
|
|
245
|
-
);
|
|
246
|
-
try {
|
|
247
|
-
const output = execFileSync(
|
|
248
|
-
"claude",
|
|
249
|
-
[
|
|
250
|
-
"-p",
|
|
251
|
-
fullPrompt,
|
|
252
|
-
"--model",
|
|
253
|
-
model,
|
|
254
|
-
"--append-system-prompt",
|
|
255
|
-
systemPrompt,
|
|
256
|
-
"--allowedTools",
|
|
257
|
-
"Bash(agent-browser:*)"
|
|
258
|
-
],
|
|
259
|
-
{
|
|
260
|
-
encoding: "utf-8",
|
|
261
|
-
timeout,
|
|
262
|
-
env: cleanEnv,
|
|
263
|
-
maxBuffer: 10 * 1024 * 1024
|
|
264
|
-
}
|
|
265
|
-
).trim();
|
|
266
|
-
const passed = /\*{0,2}PASS\*{0,2}\b/m.test(output);
|
|
267
|
-
return { passed, output };
|
|
268
|
-
} catch (error) {
|
|
269
|
-
const output = error.stdout || error.stderr || error.message || "Unknown error";
|
|
270
|
-
return { passed: false, output: output.trim() };
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
// src/bdd/agent-doc-tester.ts
|
|
275
|
-
import { readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
|
|
276
|
-
var SYSTEM_PROMPT = `You are a documentation reviewer evaluating documents from the reader's experience.
|
|
277
|
-
|
|
278
|
-
EVALUATION DIMENSIONS:
|
|
279
|
-
1. Completeness \u2014 All required information is present. Nothing critical is missing.
|
|
280
|
-
2. Logic \u2014 Structure flows naturally. Concepts build on each other without jumps.
|
|
281
|
-
3. Readability \u2014 A newcomer can follow without confusion. No unexplained jargon.
|
|
282
|
-
|
|
283
|
-
RULES:
|
|
284
|
-
- Read the provided document carefully
|
|
285
|
-
- Evaluate each requirement listed in the prompt against ALL three dimensions
|
|
286
|
-
- Be strict but fair \u2014 the document should genuinely help the reader achieve the stated goal
|
|
287
|
-
- Output your result as a single line: PASS or FAIL followed by a brief reason
|
|
288
|
-
- If FAIL, list which specific requirements are not met and which dimension they violate`;
|
|
289
|
-
async function agentDocTester(options, testerOptions = {}) {
|
|
290
|
-
const {
|
|
291
|
-
provider = process.env.AGENTX_PROVIDER || "anthropic",
|
|
292
|
-
model = env.model,
|
|
293
|
-
apiKey = env.apiKey || "",
|
|
294
|
-
baseUrl = env.baseUrl,
|
|
295
|
-
timeout = 12e4
|
|
296
|
-
} = testerOptions;
|
|
297
|
-
const docContents = options.files.map((filePath) => {
|
|
298
|
-
if (!existsSync2(filePath)) {
|
|
299
|
-
return `--- ${filePath} ---
|
|
300
|
-
[FILE NOT FOUND]`;
|
|
301
|
-
}
|
|
302
|
-
return `--- ${filePath} ---
|
|
303
|
-
${readFileSync2(filePath, "utf-8")}`;
|
|
304
|
-
}).join("\n\n");
|
|
305
|
-
const userPrompt = [
|
|
306
|
-
"Evaluate the following document(s) against the requirements below.",
|
|
307
|
-
"",
|
|
308
|
-
"DOCUMENTS:",
|
|
309
|
-
docContents,
|
|
310
|
-
"",
|
|
311
|
-
"REQUIREMENTS:",
|
|
312
|
-
options.requirements,
|
|
313
|
-
"",
|
|
314
|
-
"Evaluate each requirement. Output PASS if all are met, FAIL if any are not."
|
|
315
|
-
].join("\n");
|
|
316
|
-
const moduleName = "agentxjs";
|
|
317
|
-
const agentxjs = await import(
|
|
318
|
-
/* @vite-ignore */
|
|
319
|
-
moduleName
|
|
320
|
-
);
|
|
321
|
-
const createAgentX = agentxjs.createAgentX;
|
|
322
|
-
let agentx = null;
|
|
323
|
-
try {
|
|
324
|
-
agentx = await createAgentX({
|
|
325
|
-
apiKey,
|
|
326
|
-
provider,
|
|
327
|
-
model,
|
|
328
|
-
baseUrl,
|
|
329
|
-
logLevel: "silent"
|
|
330
|
-
});
|
|
331
|
-
await agentx.containers.create("doc-tester");
|
|
332
|
-
const { record: image } = await agentx.images.create({
|
|
333
|
-
containerId: "doc-tester",
|
|
334
|
-
systemPrompt: SYSTEM_PROMPT
|
|
335
|
-
});
|
|
336
|
-
const { agentId } = await agentx.agents.create({ imageId: image.imageId });
|
|
337
|
-
let output = "";
|
|
338
|
-
agentx.on("text_delta", (e) => {
|
|
339
|
-
output += e.data.text;
|
|
340
|
-
});
|
|
341
|
-
await Promise.race([
|
|
342
|
-
agentx.sessions.send(agentId, userPrompt),
|
|
343
|
-
new Promise((_, reject) => setTimeout(() => reject(new Error("Timeout")), timeout))
|
|
344
|
-
]);
|
|
345
|
-
output = output.trim();
|
|
346
|
-
const passed = /\*{0,2}PASS\*{0,2}\b/m.test(output);
|
|
347
|
-
return { passed, output };
|
|
348
|
-
} catch (error) {
|
|
349
|
-
return { passed: false, output: error.message || "Unknown error" };
|
|
350
|
-
} finally {
|
|
351
|
-
if (agentx) {
|
|
352
|
-
try {
|
|
353
|
-
await agentx.shutdown();
|
|
354
|
-
} catch {
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
361
|
export {
|
|
360
362
|
agentDocTester,
|
|
361
363
|
agentUiTester,
|