@agentxjs/devtools 1.9.5-dev → 2.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/README.md +284 -0
- package/dist/bdd/cli.d.ts +1 -0
- package/dist/bdd/cli.js +117 -0
- package/dist/bdd/cli.js.map +1 -0
- package/dist/bdd/index.d.ts +202 -0
- package/dist/bdd/index.js +381 -0
- package/dist/bdd/index.js.map +1 -0
- package/dist/chunk-6OHXS7LW.js +297 -0
- package/dist/chunk-6OHXS7LW.js.map +1 -0
- package/dist/chunk-DGUM43GV.js +11 -0
- package/dist/chunk-DGUM43GV.js.map +1 -0
- package/dist/chunk-DR45HEV4.js +152 -0
- package/dist/chunk-DR45HEV4.js.map +1 -0
- package/dist/chunk-J6L73HM5.js +301 -0
- package/dist/chunk-J6L73HM5.js.map +1 -0
- package/dist/chunk-S7J75AXG.js +64 -0
- package/dist/chunk-S7J75AXG.js.map +1 -0
- package/dist/fixtures/index.d.ts +49 -0
- package/dist/fixtures/index.js +22 -0
- package/dist/fixtures/index.js.map +1 -0
- package/dist/index.d.ts +240 -0
- package/dist/index.js +269 -0
- package/dist/index.js.map +1 -0
- package/dist/mock/index.d.ts +115 -0
- package/dist/mock/index.js +11 -0
- package/dist/mock/index.js.map +1 -0
- package/dist/recorder/index.d.ts +120 -0
- package/dist/recorder/index.js +10 -0
- package/dist/recorder/index.js.map +1 -0
- package/dist/types-C6Lf3vz2.d.ts +78 -0
- package/package.json +63 -8
- package/src/Devtools.ts +11 -14
- package/src/bdd/agent-doc-tester.ts +130 -0
- package/src/bdd/agent-ui-tester.ts +88 -0
- package/src/bdd/cli.ts +166 -0
- package/src/bdd/cucumber.config.ts +40 -0
- package/src/bdd/dev-server.ts +82 -0
- package/src/bdd/index.ts +41 -0
- package/src/bdd/paths.ts +140 -0
- package/src/bdd/playwright.ts +110 -0
- package/src/env.ts +97 -0
- package/src/index.ts +6 -1
- package/src/mock/MockDriver.ts +21 -12
- package/src/recorder/RecordingDriver.ts +1 -5
- package/scripts/record-fixture.ts +0 -148
- package/tsconfig.json +0 -10
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
import {
|
|
2
|
+
env
|
|
3
|
+
} from "../chunk-S7J75AXG.js";
|
|
4
|
+
import {
|
|
5
|
+
__require
|
|
6
|
+
} from "../chunk-DGUM43GV.js";
|
|
7
|
+
|
|
8
|
+
// src/bdd/cucumber.config.ts
|
|
9
|
+
function createCucumberConfig(options) {
|
|
10
|
+
return {
|
|
11
|
+
format: options.format ?? ["progress"],
|
|
12
|
+
formatOptions: { snippetInterface: "async-await" },
|
|
13
|
+
import: options.import,
|
|
14
|
+
paths: options.paths,
|
|
15
|
+
tags: options.tags ?? "not @pending and not @skip",
|
|
16
|
+
worldParameters: {
|
|
17
|
+
defaultTimeout: options.timeout ?? 3e4
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// src/bdd/playwright.ts
|
|
23
|
+
import { chromium } from "@playwright/test";
|
|
24
|
+
var browser = null;
|
|
25
|
+
var page = null;
|
|
26
|
+
async function launchBrowser(options = {}) {
|
|
27
|
+
if (browser) return browser;
|
|
28
|
+
const headless = options.headless ?? process.env.CI === "true";
|
|
29
|
+
browser = await chromium.launch({
|
|
30
|
+
channel: "chrome",
|
|
31
|
+
headless,
|
|
32
|
+
slowMo: options.slowMo
|
|
33
|
+
});
|
|
34
|
+
return browser;
|
|
35
|
+
}
|
|
36
|
+
async function getPage() {
|
|
37
|
+
if (page && !page.isClosed()) return page;
|
|
38
|
+
const b = await launchBrowser();
|
|
39
|
+
page = await b.newPage();
|
|
40
|
+
return page;
|
|
41
|
+
}
|
|
42
|
+
async function resetPage() {
|
|
43
|
+
try {
|
|
44
|
+
if (page && !page.isClosed()) {
|
|
45
|
+
const context = page.context();
|
|
46
|
+
await context.clearCookies();
|
|
47
|
+
await page.goto("about:blank");
|
|
48
|
+
}
|
|
49
|
+
} catch {
|
|
50
|
+
page = null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async function closePage() {
|
|
54
|
+
if (page) {
|
|
55
|
+
await page.close();
|
|
56
|
+
page = null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
async function closeBrowser() {
|
|
60
|
+
if (page && !page.isClosed()) {
|
|
61
|
+
await page.close();
|
|
62
|
+
page = null;
|
|
63
|
+
}
|
|
64
|
+
if (browser) {
|
|
65
|
+
await browser.close();
|
|
66
|
+
browser = null;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async function waitForUrl(url, timeout = 3e4) {
|
|
70
|
+
const start = Date.now();
|
|
71
|
+
while (Date.now() - start < timeout) {
|
|
72
|
+
try {
|
|
73
|
+
const response = await fetch(url);
|
|
74
|
+
if (response.ok) {
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
} catch {
|
|
78
|
+
}
|
|
79
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
80
|
+
}
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// src/bdd/dev-server.ts
|
|
85
|
+
import { spawn } from "child_process";
|
|
86
|
+
var devServer = null;
|
|
87
|
+
async function startDevServer(options) {
|
|
88
|
+
if (devServer) return;
|
|
89
|
+
const {
|
|
90
|
+
cwd,
|
|
91
|
+
command = "bun",
|
|
92
|
+
args = ["run", "dev"],
|
|
93
|
+
port,
|
|
94
|
+
timeout = 3e4,
|
|
95
|
+
debug = !!process.env.DEBUG
|
|
96
|
+
} = options;
|
|
97
|
+
devServer = spawn(command, args, {
|
|
98
|
+
cwd,
|
|
99
|
+
stdio: "pipe",
|
|
100
|
+
detached: false
|
|
101
|
+
});
|
|
102
|
+
if (debug) {
|
|
103
|
+
devServer.stdout?.on("data", (data) => {
|
|
104
|
+
console.log("[dev-server]", data.toString());
|
|
105
|
+
});
|
|
106
|
+
devServer.stderr?.on("data", (data) => {
|
|
107
|
+
console.error("[dev-server error]", data.toString());
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
const url = `http://localhost:${port}`;
|
|
111
|
+
const ready = await waitForUrl(url, timeout);
|
|
112
|
+
if (!ready) {
|
|
113
|
+
stopDevServer();
|
|
114
|
+
throw new Error(`Dev server failed to start on port ${port}`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
function stopDevServer() {
|
|
118
|
+
if (devServer) {
|
|
119
|
+
devServer.kill("SIGTERM");
|
|
120
|
+
devServer = null;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
function getDevServer() {
|
|
124
|
+
return devServer;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// src/bdd/paths.ts
|
|
128
|
+
import { resolve, dirname } from "path";
|
|
129
|
+
import { existsSync, mkdtempSync, mkdirSync } from "fs";
|
|
130
|
+
import { tmpdir } from "os";
|
|
131
|
+
function findMonorepoRoot(startDir = process.cwd()) {
|
|
132
|
+
let dir = startDir;
|
|
133
|
+
while (dir !== "/") {
|
|
134
|
+
const pkgPath = resolve(dir, "package.json");
|
|
135
|
+
if (existsSync(pkgPath)) {
|
|
136
|
+
try {
|
|
137
|
+
const pkg = __require(pkgPath);
|
|
138
|
+
if (pkg.workspaces || pkg.private === true) {
|
|
139
|
+
const hasPackages = existsSync(resolve(dir, "packages"));
|
|
140
|
+
const hasApps = existsSync(resolve(dir, "apps"));
|
|
141
|
+
if (hasPackages || hasApps) {
|
|
142
|
+
return dir;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
} catch {
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
dir = dirname(dir);
|
|
149
|
+
}
|
|
150
|
+
return startDir;
|
|
151
|
+
}
|
|
152
|
+
function getPackageRoot(startDir = process.cwd()) {
|
|
153
|
+
let dir = startDir;
|
|
154
|
+
while (dir !== "/") {
|
|
155
|
+
if (existsSync(resolve(dir, "package.json"))) {
|
|
156
|
+
return dir;
|
|
157
|
+
}
|
|
158
|
+
dir = dirname(dir);
|
|
159
|
+
}
|
|
160
|
+
return startDir;
|
|
161
|
+
}
|
|
162
|
+
var _monorepoRoot = null;
|
|
163
|
+
var _packageRoot = null;
|
|
164
|
+
var _tempDir = null;
|
|
165
|
+
function getMonorepoPath() {
|
|
166
|
+
if (!_monorepoRoot) {
|
|
167
|
+
_monorepoRoot = findMonorepoRoot();
|
|
168
|
+
}
|
|
169
|
+
return _monorepoRoot;
|
|
170
|
+
}
|
|
171
|
+
function getPackagePath() {
|
|
172
|
+
if (!_packageRoot) {
|
|
173
|
+
_packageRoot = getPackageRoot();
|
|
174
|
+
}
|
|
175
|
+
return _packageRoot;
|
|
176
|
+
}
|
|
177
|
+
function getBddPath() {
|
|
178
|
+
return resolve(getPackagePath(), "bdd");
|
|
179
|
+
}
|
|
180
|
+
function getFixturesPath(subdir) {
|
|
181
|
+
const base = resolve(getBddPath(), "fixtures");
|
|
182
|
+
return subdir ? resolve(base, subdir) : base;
|
|
183
|
+
}
|
|
184
|
+
function getTempPath(prefix = "bdd-") {
|
|
185
|
+
if (!_tempDir) {
|
|
186
|
+
_tempDir = mkdtempSync(resolve(tmpdir(), prefix));
|
|
187
|
+
}
|
|
188
|
+
return _tempDir;
|
|
189
|
+
}
|
|
190
|
+
function ensureDir(path) {
|
|
191
|
+
if (!existsSync(path)) {
|
|
192
|
+
mkdirSync(path, { recursive: true });
|
|
193
|
+
}
|
|
194
|
+
return path;
|
|
195
|
+
}
|
|
196
|
+
function resetPaths() {
|
|
197
|
+
_monorepoRoot = null;
|
|
198
|
+
_packageRoot = null;
|
|
199
|
+
_tempDir = null;
|
|
200
|
+
}
|
|
201
|
+
var paths = {
|
|
202
|
+
monorepo: getMonorepoPath,
|
|
203
|
+
package: getPackagePath,
|
|
204
|
+
bdd: getBddPath,
|
|
205
|
+
fixtures: getFixturesPath,
|
|
206
|
+
temp: getTempPath,
|
|
207
|
+
ensure: ensureDir,
|
|
208
|
+
reset: resetPaths
|
|
209
|
+
};
|
|
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
|
+
export {
|
|
360
|
+
agentDocTester,
|
|
361
|
+
agentUiTester,
|
|
362
|
+
closeBrowser,
|
|
363
|
+
closePage,
|
|
364
|
+
createCucumberConfig,
|
|
365
|
+
ensureDir,
|
|
366
|
+
getBddPath,
|
|
367
|
+
getDevServer,
|
|
368
|
+
getFixturesPath,
|
|
369
|
+
getMonorepoPath,
|
|
370
|
+
getPackagePath,
|
|
371
|
+
getPage,
|
|
372
|
+
getTempPath,
|
|
373
|
+
launchBrowser,
|
|
374
|
+
paths,
|
|
375
|
+
resetPage,
|
|
376
|
+
resetPaths,
|
|
377
|
+
startDevServer,
|
|
378
|
+
stopDevServer,
|
|
379
|
+
waitForUrl
|
|
380
|
+
};
|
|
381
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/bdd/cucumber.config.ts","../../src/bdd/playwright.ts","../../src/bdd/dev-server.ts","../../src/bdd/paths.ts","../../src/bdd/agent-ui-tester.ts","../../src/bdd/agent-doc-tester.ts"],"sourcesContent":["/**\n * Shared Cucumber configuration for BDD tests\n *\n * Usage in project's cucumber.js:\n *\n * ```js\n * import { createCucumberConfig } from \"@agentxjs/devtools/bdd\";\n *\n * export default createCucumberConfig({\n * paths: [\"bdd/journeys/** /*.feature\"],\n * import: [\"bdd/steps/** /*.ts\"],\n * });\n * ```\n */\n\nexport interface CucumberConfigOptions {\n /** Feature file paths */\n paths: string[];\n /** Step definition paths */\n import: string[];\n /** Tags to filter (default: exclude @pending and @skip) */\n tags?: string;\n /** Default timeout in ms (default: 30000) */\n timeout?: number;\n /** Format output (default: progress) */\n format?: string[];\n}\n\nexport function createCucumberConfig(options: CucumberConfigOptions) {\n return {\n format: options.format ?? [\"progress\"],\n formatOptions: { snippetInterface: \"async-await\" },\n import: options.import,\n paths: options.paths,\n tags: options.tags ?? \"not @pending and not @skip\",\n worldParameters: {\n defaultTimeout: options.timeout ?? 30000,\n },\n };\n}\n","/**\n * Playwright utilities for BDD testing\n *\n * Uses system Chrome to avoid downloading Chromium.\n * Install: PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 bun add -d @playwright/test\n *\n * Browser lifecycle:\n * - Single browser instance for all tests\n * - Single page (tab) reused across scenarios\n * - resetPage() clears state between scenarios\n */\n\nimport { chromium, Browser, Page } from \"@playwright/test\";\n\nexport interface BrowserOptions {\n headless?: boolean;\n slowMo?: number;\n}\n\nlet browser: Browser | null = null;\nlet page: Page | null = null;\n\n/**\n * Launch browser using system Chrome (singleton)\n */\nexport async function launchBrowser(options: BrowserOptions = {}): Promise<Browser> {\n if (browser) return browser;\n\n const headless = options.headless ?? process.env.CI === \"true\";\n\n browser = await chromium.launch({\n channel: \"chrome\",\n headless,\n slowMo: options.slowMo,\n });\n\n return browser;\n}\n\n/**\n * Get or create a page (singleton, reused across scenarios)\n */\nexport async function getPage(): Promise<Page> {\n if (page && !page.isClosed()) return page;\n\n const b = await launchBrowser();\n page = await b.newPage();\n return page;\n}\n\n/**\n * Reset page state between scenarios (without closing)\n * Use this instead of closePage() for faster tests\n */\nexport async function resetPage(): Promise<void> {\n try {\n if (page && !page.isClosed()) {\n const context = page.context();\n await context.clearCookies();\n await page.goto(\"about:blank\");\n }\n } catch {\n // Browser may have crashed, will recreate on next getPage()\n page = null;\n }\n}\n\n/**\n * Close current page\n * @deprecated Use resetPage() for faster tests. Only use closePage() if you need full isolation.\n */\nexport async function closePage(): Promise<void> {\n if (page) {\n await page.close();\n page = null;\n }\n}\n\n/**\n * Close browser and cleanup\n */\nexport async function closeBrowser(): Promise<void> {\n if (page && !page.isClosed()) {\n await page.close();\n page = null;\n }\n if (browser) {\n await browser.close();\n browser = null;\n }\n}\n\n/**\n * Wait for a URL to be accessible\n */\nexport async function waitForUrl(url: string, timeout = 30000): Promise<boolean> {\n const start = Date.now();\n while (Date.now() - start < timeout) {\n try {\n const response = await fetch(url);\n if (response.ok) {\n return true;\n }\n } catch {\n // Not ready yet\n }\n await new Promise((r) => setTimeout(r, 500));\n }\n return false;\n}\n","/**\n * Dev server utilities for BDD testing\n *\n * Start and stop dev servers during test runs.\n */\n\nimport { spawn, ChildProcess } from \"node:child_process\";\nimport { waitForUrl } from \"./playwright\";\n\nexport interface DevServerOptions {\n /** Working directory */\n cwd: string;\n /** Command to run (default: \"bun\") */\n command?: string;\n /** Command arguments (default: [\"run\", \"dev\"]) */\n args?: string[];\n /** Port to wait for */\n port: number;\n /** Startup timeout in ms (default: 30000) */\n timeout?: number;\n /** Show server output (default: false, or true if DEBUG env is set) */\n debug?: boolean;\n}\n\nlet devServer: ChildProcess | null = null;\n\n/**\n * Start a dev server and wait for it to be ready\n */\nexport async function startDevServer(options: DevServerOptions): Promise<void> {\n if (devServer) return;\n\n const {\n cwd,\n command = \"bun\",\n args = [\"run\", \"dev\"],\n port,\n timeout = 30000,\n debug = !!process.env.DEBUG,\n } = options;\n\n devServer = spawn(command, args, {\n cwd,\n stdio: \"pipe\",\n detached: false,\n });\n\n if (debug) {\n devServer.stdout?.on(\"data\", (data) => {\n console.log(\"[dev-server]\", data.toString());\n });\n\n devServer.stderr?.on(\"data\", (data) => {\n console.error(\"[dev-server error]\", data.toString());\n });\n }\n\n const url = `http://localhost:${port}`;\n const ready = await waitForUrl(url, timeout);\n\n if (!ready) {\n stopDevServer();\n throw new Error(`Dev server failed to start on port ${port}`);\n }\n}\n\n/**\n * Stop the dev server\n */\nexport function stopDevServer(): void {\n if (devServer) {\n devServer.kill(\"SIGTERM\");\n devServer = null;\n }\n}\n\n/**\n * Get the dev server process (for advanced use)\n */\nexport function getDevServer(): ChildProcess | null {\n return devServer;\n}\n","/**\n * Unified path utilities for BDD testing\n *\n * Provides consistent path resolution across all packages.\n */\n\nimport { resolve, dirname } from \"node:path\";\nimport { existsSync, mkdtempSync, mkdirSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\n\n// ============================================================================\n// Path Resolution\n// ============================================================================\n\n/**\n * Find the monorepo root by looking for root package.json with workspaces\n */\nexport function findMonorepoRoot(startDir: string = process.cwd()): string {\n let dir = startDir;\n while (dir !== \"/\") {\n const pkgPath = resolve(dir, \"package.json\");\n if (existsSync(pkgPath)) {\n try {\n const pkg = require(pkgPath);\n if (pkg.workspaces || pkg.private === true) {\n // Check if it looks like a monorepo root\n const hasPackages = existsSync(resolve(dir, \"packages\"));\n const hasApps = existsSync(resolve(dir, \"apps\"));\n if (hasPackages || hasApps) {\n return dir;\n }\n }\n } catch {\n // Ignore errors\n }\n }\n dir = dirname(dir);\n }\n return startDir;\n}\n\n/**\n * Get the current package root (where package.json is)\n */\nexport function getPackageRoot(startDir: string = process.cwd()): string {\n let dir = startDir;\n while (dir !== \"/\") {\n if (existsSync(resolve(dir, \"package.json\"))) {\n return dir;\n }\n dir = dirname(dir);\n }\n return startDir;\n}\n\n// ============================================================================\n// Standard Paths\n// ============================================================================\n\nlet _monorepoRoot: string | null = null;\nlet _packageRoot: string | null = null;\nlet _tempDir: string | null = null;\n\n/**\n * Monorepo root directory\n */\nexport function getMonorepoPath(): string {\n if (!_monorepoRoot) {\n _monorepoRoot = findMonorepoRoot();\n }\n return _monorepoRoot;\n}\n\n/**\n * Current package root directory\n */\nexport function getPackagePath(): string {\n if (!_packageRoot) {\n _packageRoot = getPackageRoot();\n }\n return _packageRoot;\n}\n\n/**\n * BDD directory for current package\n */\nexport function getBddPath(): string {\n return resolve(getPackagePath(), \"bdd\");\n}\n\n/**\n * Fixtures directory for current package's BDD tests\n */\nexport function getFixturesPath(subdir?: string): string {\n const base = resolve(getBddPath(), \"fixtures\");\n return subdir ? resolve(base, subdir) : base;\n}\n\n/**\n * Get or create a temporary directory for tests\n */\nexport function getTempPath(prefix: string = \"bdd-\"): string {\n if (!_tempDir) {\n _tempDir = mkdtempSync(resolve(tmpdir(), prefix));\n }\n return _tempDir;\n}\n\n/**\n * Ensure a directory exists, creating it if necessary\n */\nexport function ensureDir(path: string): string {\n if (!existsSync(path)) {\n mkdirSync(path, { recursive: true });\n }\n return path;\n}\n\n/**\n * Reset cached paths (useful for testing)\n */\nexport function resetPaths(): void {\n _monorepoRoot = null;\n _packageRoot = null;\n _tempDir = null;\n}\n\n// ============================================================================\n// Convenience exports\n// ============================================================================\n\nexport const paths = {\n monorepo: getMonorepoPath,\n package: getPackagePath,\n bdd: getBddPath,\n fixtures: getFixturesPath,\n temp: getTempPath,\n ensure: ensureDir,\n reset: resetPaths,\n};\n","import { execFileSync } from \"node:child_process\";\nimport { readFileSync } from \"node:fs\";\nimport { resolve, dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\nconst SKILL_PATH = resolve(__dirname, \"../../../../.claude/skills/agent-browser/SKILL.md\");\n\nfunction loadSystemPrompt(headed = false): string {\n let skillContent = \"\";\n try {\n skillContent = readFileSync(SKILL_PATH, \"utf-8\");\n } catch {\n // Skill file not found, continue without it\n }\n\n return `You are a UI tester. You test web application scenarios using the agent-browser CLI.\n\nRULES:\n- ONLY use agent-browser commands via Bash tool\n- Use ${headed ? \"--headed \" : \"\"}--executable-path \"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome\" for all commands\n- After each navigation or click, run: agent-browser snapshot -i\n- Refs (@e1, @e2) are invalidated after page changes — always re-snapshot\n- At the end, close the browser with: agent-browser close\n- Output your result as a single line: PASS or FAIL followed by a brief reason\n\n${skillContent ? `AGENT-BROWSER REFERENCE:\\n${skillContent}` : \"\"}`;\n}\n\nexport interface UiTestResult {\n passed: boolean;\n output: string;\n}\n\nexport interface UiTesterOptions {\n model?: string;\n baseUrl?: string;\n timeout?: number;\n /** Show browser window (default: false) */\n headed?: boolean;\n}\n\n/**\n * Run a UI test scenario using Claude Code CLI + agent-browser.\n *\n * BDD scripts must run under Node.js (not Bun) to avoid claude CLI auth bug.\n */\nexport function agentUiTester(prompt: string, options: UiTesterOptions = {}): UiTestResult {\n const { model = \"haiku\", baseUrl, timeout = 300_000, headed = false } = options;\n\n const fullPrompt = baseUrl ? `Base URL: ${baseUrl}\\n\\n${prompt}` : prompt;\n\n const systemPrompt = loadSystemPrompt(headed);\n\n // Filter out CLAUDE* env vars to avoid auth conflicts when spawned from Claude Code\n const cleanEnv = Object.fromEntries(\n Object.entries(process.env).filter(([k]) => !k.startsWith(\"CLAUDE\"))\n );\n\n try {\n const output = execFileSync(\n \"claude\",\n [\n \"-p\",\n fullPrompt,\n \"--model\",\n model,\n \"--append-system-prompt\",\n systemPrompt,\n \"--allowedTools\",\n \"Bash(agent-browser:*)\",\n ],\n {\n encoding: \"utf-8\",\n timeout,\n env: cleanEnv,\n maxBuffer: 10 * 1024 * 1024,\n }\n ).trim();\n\n const passed = /\\*{0,2}PASS\\*{0,2}\\b/m.test(output);\n return { passed, output };\n } catch (error: any) {\n const output = error.stdout || error.stderr || error.message || \"Unknown error\";\n return { passed: false, output: output.trim() };\n }\n}\n","import { readFileSync, existsSync } from \"node:fs\";\nimport { env } from \"../env\";\n\nconst SYSTEM_PROMPT = `You are a documentation reviewer evaluating documents from the reader's experience.\n\nEVALUATION DIMENSIONS:\n1. Completeness — All required information is present. Nothing critical is missing.\n2. Logic — Structure flows naturally. Concepts build on each other without jumps.\n3. Readability — A newcomer can follow without confusion. No unexplained jargon.\n\nRULES:\n- Read the provided document carefully\n- Evaluate each requirement listed in the prompt against ALL three dimensions\n- Be strict but fair — the document should genuinely help the reader achieve the stated goal\n- Output your result as a single line: PASS or FAIL followed by a brief reason\n- If FAIL, list which specific requirements are not met and which dimension they violate`;\n\nexport interface DocTestResult {\n passed: boolean;\n output: string;\n}\n\nexport interface DocTesterOptions {\n /** LLM provider (default: \"anthropic\") */\n provider?: string;\n /** Model name */\n model?: string;\n /** API key (reads from env if not provided) */\n apiKey?: string;\n /** Base URL (reads from env if not provided) */\n baseUrl?: string;\n /** Timeout in ms */\n timeout?: number;\n}\n\n/**\n * Evaluate a document against requirements using AgentX.\n *\n * Uses agentxjs local mode — no subprocess, no CLI, no auth issues.\n * Requires `agentxjs` as a peer dependency.\n */\nexport async function agentDocTester(\n options: {\n files: string[];\n requirements: string;\n },\n testerOptions: DocTesterOptions = {}\n): Promise<DocTestResult> {\n const {\n provider = process.env.AGENTX_PROVIDER || \"anthropic\",\n model = env.model,\n apiKey = env.apiKey || \"\",\n baseUrl = env.baseUrl,\n timeout = 120_000,\n } = testerOptions;\n\n const docContents = options.files\n .map((filePath) => {\n if (!existsSync(filePath)) {\n return `--- ${filePath} ---\\n[FILE NOT FOUND]`;\n }\n return `--- ${filePath} ---\\n${readFileSync(filePath, \"utf-8\")}`;\n })\n .join(\"\\n\\n\");\n\n const userPrompt = [\n \"Evaluate the following document(s) against the requirements below.\",\n \"\",\n \"DOCUMENTS:\",\n docContents,\n \"\",\n \"REQUIREMENTS:\",\n options.requirements,\n \"\",\n \"Evaluate each requirement. Output PASS if all are met, FAIL if any are not.\",\n ].join(\"\\n\");\n\n // Dynamic import to avoid circular dependency (devtools ↔ agentxjs)\n // Use variable to prevent TypeScript DTS from resolving the module\n const moduleName = \"agentxjs\";\n const agentxjs: any = await import(/* @vite-ignore */ moduleName);\n const createAgentX: (...args: any[]) => Promise<any> = agentxjs.createAgentX;\n\n let agentx: any = null;\n\n try {\n agentx = await createAgentX({\n apiKey,\n provider,\n model,\n baseUrl,\n logLevel: \"silent\",\n });\n\n await agentx.containers.create(\"doc-tester\");\n\n const { record: image } = await agentx.images.create({\n containerId: \"doc-tester\",\n systemPrompt: SYSTEM_PROMPT,\n });\n\n const { agentId } = await agentx.agents.create({ imageId: image.imageId });\n\n // Collect response text\n let output = \"\";\n agentx.on(\"text_delta\", (e: any) => {\n output += e.data.text;\n });\n\n // Send prompt and wait for completion\n await Promise.race([\n agentx.sessions.send(agentId, userPrompt),\n new Promise((_, reject) => setTimeout(() => reject(new Error(\"Timeout\")), timeout)),\n ]);\n\n output = output.trim();\n const passed = /\\*{0,2}PASS\\*{0,2}\\b/m.test(output);\n return { passed, output };\n } catch (error: any) {\n return { passed: false, output: error.message || \"Unknown error\" };\n } finally {\n if (agentx) {\n try {\n await agentx.shutdown();\n } catch {\n // ignore shutdown errors\n }\n }\n }\n}\n"],"mappings":"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","names":["resolve","dirname","readFileSync","existsSync","existsSync","readFileSync"]}
|