@hasna/testers 0.0.3 → 0.0.5
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 +1 -1
- package/dist/cli/index.js +183 -10
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +104 -4
- package/dist/lib/ai-client.d.ts +9 -0
- package/dist/lib/ai-client.d.ts.map +1 -1
- package/dist/lib/runner.d.ts +18 -1
- package/dist/lib/runner.d.ts.map +1 -1
- package/dist/mcp/index.js +113 -17
- package/dist/server/index.js +30 -4
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -228,7 +228,7 @@ Screenshots are saved to `~/.testers/screenshots/` organized by:
|
|
|
228
228
|
Install for Claude Code:
|
|
229
229
|
|
|
230
230
|
```bash
|
|
231
|
-
claude mcp add --transport stdio --scope user testers
|
|
231
|
+
claude mcp add --transport stdio --scope user testers -- testers-mcp
|
|
232
232
|
```
|
|
233
233
|
|
|
234
234
|
Available tools: `create_scenario`, `list_scenarios`, `run_scenarios`, `get_results`, `get_screenshots`, and more.
|
package/dist/cli/index.js
CHANGED
|
@@ -3641,7 +3641,8 @@ async function runAgentLoop(options) {
|
|
|
3641
3641
|
screenshotter,
|
|
3642
3642
|
model,
|
|
3643
3643
|
runId,
|
|
3644
|
-
maxTurns = 30
|
|
3644
|
+
maxTurns = 30,
|
|
3645
|
+
onStep
|
|
3645
3646
|
} = options;
|
|
3646
3647
|
const systemPrompt = [
|
|
3647
3648
|
"You are an expert QA testing agent. Your job is to thoroughly test web application scenarios.",
|
|
@@ -3700,8 +3701,8 @@ async function runAgentLoop(options) {
|
|
|
3700
3701
|
}
|
|
3701
3702
|
const toolUseBlocks = response.content.filter((block) => block.type === "tool_use");
|
|
3702
3703
|
if (toolUseBlocks.length === 0 && response.stop_reason === "end_turn") {
|
|
3703
|
-
const
|
|
3704
|
-
const textReasoning =
|
|
3704
|
+
const textBlocks2 = response.content.filter((block) => block.type === "text");
|
|
3705
|
+
const textReasoning = textBlocks2.map((b) => b.text).join(`
|
|
3705
3706
|
`);
|
|
3706
3707
|
return {
|
|
3707
3708
|
status: "error",
|
|
@@ -3712,10 +3713,22 @@ async function runAgentLoop(options) {
|
|
|
3712
3713
|
};
|
|
3713
3714
|
}
|
|
3714
3715
|
const toolResults = [];
|
|
3716
|
+
const textBlocks = response.content.filter((block) => block.type === "text");
|
|
3717
|
+
if (textBlocks.length > 0 && onStep) {
|
|
3718
|
+
const thinking = textBlocks.map((b) => b.text).join(`
|
|
3719
|
+
`);
|
|
3720
|
+
onStep({ type: "thinking", thinking, stepNumber });
|
|
3721
|
+
}
|
|
3715
3722
|
for (const toolBlock of toolUseBlocks) {
|
|
3716
3723
|
stepNumber++;
|
|
3717
3724
|
const toolInput = toolBlock.input;
|
|
3725
|
+
if (onStep) {
|
|
3726
|
+
onStep({ type: "tool_call", toolName: toolBlock.name, toolInput, stepNumber });
|
|
3727
|
+
}
|
|
3718
3728
|
const execResult = await executeTool(page, screenshotter, toolBlock.name, toolInput, { runId, scenarioSlug, stepNumber });
|
|
3729
|
+
if (onStep) {
|
|
3730
|
+
onStep({ type: "tool_result", toolName: toolBlock.name, toolResult: execResult.result, stepNumber });
|
|
3731
|
+
}
|
|
3719
3732
|
if (execResult.screenshot) {
|
|
3720
3733
|
screenshots.push({
|
|
3721
3734
|
...execResult.screenshot,
|
|
@@ -3824,6 +3837,9 @@ function loadConfig() {
|
|
|
3824
3837
|
|
|
3825
3838
|
// src/lib/runner.ts
|
|
3826
3839
|
var eventHandler = null;
|
|
3840
|
+
function onRunEvent(handler) {
|
|
3841
|
+
eventHandler = handler;
|
|
3842
|
+
}
|
|
3827
3843
|
function emit(event) {
|
|
3828
3844
|
if (eventHandler)
|
|
3829
3845
|
eventHandler(event);
|
|
@@ -3858,7 +3874,20 @@ async function runSingleScenario(scenario, runId, options) {
|
|
|
3858
3874
|
screenshotter,
|
|
3859
3875
|
model,
|
|
3860
3876
|
runId,
|
|
3861
|
-
maxTurns: 30
|
|
3877
|
+
maxTurns: 30,
|
|
3878
|
+
onStep: (stepEvent) => {
|
|
3879
|
+
emit({
|
|
3880
|
+
type: `step:${stepEvent.type}`,
|
|
3881
|
+
scenarioId: scenario.id,
|
|
3882
|
+
scenarioName: scenario.name,
|
|
3883
|
+
runId,
|
|
3884
|
+
toolName: stepEvent.toolName,
|
|
3885
|
+
toolInput: stepEvent.toolInput,
|
|
3886
|
+
toolResult: stepEvent.toolResult,
|
|
3887
|
+
thinking: stepEvent.thinking,
|
|
3888
|
+
stepNumber: stepEvent.stepNumber
|
|
3889
|
+
});
|
|
3890
|
+
}
|
|
3862
3891
|
});
|
|
3863
3892
|
for (const ss of agentResult.screenshots) {
|
|
3864
3893
|
createScreenshot({
|
|
@@ -3968,6 +3997,79 @@ async function runByFilter(options) {
|
|
|
3968
3997
|
}
|
|
3969
3998
|
return runBatch(scenarios, options);
|
|
3970
3999
|
}
|
|
4000
|
+
function startRunAsync(options) {
|
|
4001
|
+
const config = loadConfig();
|
|
4002
|
+
const model = resolveModel(options.model ?? config.defaultModel);
|
|
4003
|
+
let scenarios;
|
|
4004
|
+
if (options.scenarioIds && options.scenarioIds.length > 0) {
|
|
4005
|
+
const all = listScenarios({ projectId: options.projectId });
|
|
4006
|
+
scenarios = all.filter((s) => options.scenarioIds.includes(s.id) || options.scenarioIds.includes(s.shortId));
|
|
4007
|
+
} else {
|
|
4008
|
+
scenarios = listScenarios({
|
|
4009
|
+
projectId: options.projectId,
|
|
4010
|
+
tags: options.tags,
|
|
4011
|
+
priority: options.priority
|
|
4012
|
+
});
|
|
4013
|
+
}
|
|
4014
|
+
const parallel = options.parallel ?? 1;
|
|
4015
|
+
const run = createRun({
|
|
4016
|
+
url: options.url,
|
|
4017
|
+
model,
|
|
4018
|
+
headed: options.headed,
|
|
4019
|
+
parallel,
|
|
4020
|
+
projectId: options.projectId
|
|
4021
|
+
});
|
|
4022
|
+
if (scenarios.length === 0) {
|
|
4023
|
+
updateRun(run.id, { status: "passed", total: 0, finished_at: new Date().toISOString() });
|
|
4024
|
+
return { runId: run.id, scenarioCount: 0 };
|
|
4025
|
+
}
|
|
4026
|
+
updateRun(run.id, { status: "running", total: scenarios.length });
|
|
4027
|
+
(async () => {
|
|
4028
|
+
const results = [];
|
|
4029
|
+
try {
|
|
4030
|
+
if (parallel <= 1) {
|
|
4031
|
+
for (const scenario of scenarios) {
|
|
4032
|
+
const result = await runSingleScenario(scenario, run.id, options);
|
|
4033
|
+
results.push(result);
|
|
4034
|
+
}
|
|
4035
|
+
} else {
|
|
4036
|
+
const queue = [...scenarios];
|
|
4037
|
+
const running = [];
|
|
4038
|
+
const processNext = async () => {
|
|
4039
|
+
const scenario = queue.shift();
|
|
4040
|
+
if (!scenario)
|
|
4041
|
+
return;
|
|
4042
|
+
const result = await runSingleScenario(scenario, run.id, options);
|
|
4043
|
+
results.push(result);
|
|
4044
|
+
await processNext();
|
|
4045
|
+
};
|
|
4046
|
+
const workers = Math.min(parallel, scenarios.length);
|
|
4047
|
+
for (let i = 0;i < workers; i++) {
|
|
4048
|
+
running.push(processNext());
|
|
4049
|
+
}
|
|
4050
|
+
await Promise.all(running);
|
|
4051
|
+
}
|
|
4052
|
+
const passed = results.filter((r) => r.status === "passed").length;
|
|
4053
|
+
const failed = results.filter((r) => r.status === "failed" || r.status === "error").length;
|
|
4054
|
+
updateRun(run.id, {
|
|
4055
|
+
status: failed > 0 ? "failed" : "passed",
|
|
4056
|
+
passed,
|
|
4057
|
+
failed,
|
|
4058
|
+
total: scenarios.length,
|
|
4059
|
+
finished_at: new Date().toISOString()
|
|
4060
|
+
});
|
|
4061
|
+
emit({ type: "run:complete", runId: run.id });
|
|
4062
|
+
} catch (error) {
|
|
4063
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
4064
|
+
updateRun(run.id, {
|
|
4065
|
+
status: "failed",
|
|
4066
|
+
finished_at: new Date().toISOString()
|
|
4067
|
+
});
|
|
4068
|
+
emit({ type: "run:complete", runId: run.id, error: errorMsg });
|
|
4069
|
+
}
|
|
4070
|
+
})();
|
|
4071
|
+
return { runId: run.id, scenarioCount: scenarios.length };
|
|
4072
|
+
}
|
|
3971
4073
|
function estimateCost(model, tokens) {
|
|
3972
4074
|
const costs = {
|
|
3973
4075
|
"claude-haiku-4-5-20251001": 0.1,
|
|
@@ -5289,8 +5391,17 @@ function deleteAuthPreset(name) {
|
|
|
5289
5391
|
|
|
5290
5392
|
// src/cli/index.tsx
|
|
5291
5393
|
import { existsSync as existsSync7, mkdirSync as mkdirSync4 } from "fs";
|
|
5394
|
+
function formatToolInput(input) {
|
|
5395
|
+
const parts = [];
|
|
5396
|
+
for (const [key, value] of Object.entries(input)) {
|
|
5397
|
+
const str = typeof value === "string" ? value : JSON.stringify(value);
|
|
5398
|
+
const truncated = str.length > 60 ? str.slice(0, 60) + "..." : str;
|
|
5399
|
+
parts.push(`${key}="${truncated}"`);
|
|
5400
|
+
}
|
|
5401
|
+
return parts.join(" ");
|
|
5402
|
+
}
|
|
5292
5403
|
var program2 = new Command;
|
|
5293
|
-
program2.name("testers").version("0.0.
|
|
5404
|
+
program2.name("testers").version("0.0.4").description("AI-powered browser testing CLI");
|
|
5294
5405
|
var CONFIG_DIR2 = join6(process.env["HOME"] ?? "~", ".testers");
|
|
5295
5406
|
var CONFIG_PATH2 = join6(CONFIG_DIR2, "config.json");
|
|
5296
5407
|
function getActiveProject() {
|
|
@@ -5442,9 +5553,75 @@ program2.command("delete <id>").description("Delete a scenario").action((id) =>
|
|
|
5442
5553
|
program2.command("run <url> [description]").description("Run test scenarios against a URL").option("-t, --tag <tag>", "Filter by tag (repeatable)", (val, acc) => {
|
|
5443
5554
|
acc.push(val);
|
|
5444
5555
|
return acc;
|
|
5445
|
-
}, []).option("-s, --scenario <id>", "Run specific scenario ID").option("-p, --priority <level>", "Filter by priority").option("--headed", "Run browser in headed mode", false).option("-m, --model <model>", "AI model to use").option("--parallel <n>", "Number of parallel browsers", "1").option("--json", "Output results as JSON", false).option("-o, --output <filepath>", "Write JSON results to file").option("--timeout <ms>", "Timeout in milliseconds").option("--from-todos", "Import scenarios from todos before running", false).option("--project <id>", "Project ID").action(async (url, description, opts) => {
|
|
5556
|
+
}, []).option("-s, --scenario <id>", "Run specific scenario ID").option("-p, --priority <level>", "Filter by priority").option("--headed", "Run browser in headed mode", false).option("-m, --model <model>", "AI model to use").option("--parallel <n>", "Number of parallel browsers", "1").option("--json", "Output results as JSON", false).option("-o, --output <filepath>", "Write JSON results to file").option("--timeout <ms>", "Timeout in milliseconds").option("--from-todos", "Import scenarios from todos before running", false).option("--project <id>", "Project ID").option("-b, --background", "Start run in background and return immediately", false).action(async (url, description, opts) => {
|
|
5446
5557
|
try {
|
|
5447
5558
|
const projectId = resolveProject(opts.project);
|
|
5559
|
+
if (opts.fromTodos) {
|
|
5560
|
+
const result = importFromTodos({ projectId });
|
|
5561
|
+
console.log(chalk4.blue(`Imported ${result.imported} scenarios from todos (${result.skipped} skipped)`));
|
|
5562
|
+
}
|
|
5563
|
+
if (opts.background) {
|
|
5564
|
+
if (description) {
|
|
5565
|
+
createScenario({ name: description, description, tags: ["ad-hoc"], projectId });
|
|
5566
|
+
}
|
|
5567
|
+
const { runId, scenarioCount } = startRunAsync({
|
|
5568
|
+
url,
|
|
5569
|
+
tags: opts.tag.length > 0 ? opts.tag : undefined,
|
|
5570
|
+
scenarioIds: opts.scenario ? [opts.scenario] : undefined,
|
|
5571
|
+
priority: opts.priority,
|
|
5572
|
+
model: opts.model,
|
|
5573
|
+
headed: opts.headed,
|
|
5574
|
+
parallel: parseInt(opts.parallel, 10),
|
|
5575
|
+
timeout: opts.timeout ? parseInt(opts.timeout, 10) : undefined,
|
|
5576
|
+
projectId
|
|
5577
|
+
});
|
|
5578
|
+
console.log(chalk4.green(`Run started in background: ${chalk4.bold(runId.slice(0, 8))}`));
|
|
5579
|
+
console.log(chalk4.dim(` Scenarios: ${scenarioCount}`));
|
|
5580
|
+
console.log(chalk4.dim(` URL: ${url}`));
|
|
5581
|
+
console.log(chalk4.dim(` Check progress: testers results ${runId.slice(0, 8)}`));
|
|
5582
|
+
process.exit(0);
|
|
5583
|
+
}
|
|
5584
|
+
if (!opts.json && !opts.output) {
|
|
5585
|
+
onRunEvent((event) => {
|
|
5586
|
+
switch (event.type) {
|
|
5587
|
+
case "scenario:start":
|
|
5588
|
+
console.log(chalk4.blue(` [start] ${event.scenarioName ?? event.scenarioId}`));
|
|
5589
|
+
break;
|
|
5590
|
+
case "step:thinking":
|
|
5591
|
+
if (event.thinking) {
|
|
5592
|
+
const preview = event.thinking.length > 120 ? event.thinking.slice(0, 120) + "..." : event.thinking;
|
|
5593
|
+
console.log(chalk4.dim(` [think] ${preview}`));
|
|
5594
|
+
}
|
|
5595
|
+
break;
|
|
5596
|
+
case "step:tool_call":
|
|
5597
|
+
console.log(chalk4.cyan(` [step ${event.stepNumber}] ${event.toolName}${event.toolInput ? ` ${formatToolInput(event.toolInput)}` : ""}`));
|
|
5598
|
+
break;
|
|
5599
|
+
case "step:tool_result":
|
|
5600
|
+
if (event.toolName === "report_result") {
|
|
5601
|
+
console.log(chalk4.bold(` [result] ${event.toolResult}`));
|
|
5602
|
+
} else {
|
|
5603
|
+
const resultPreview = (event.toolResult ?? "").length > 100 ? (event.toolResult ?? "").slice(0, 100) + "..." : event.toolResult ?? "";
|
|
5604
|
+
console.log(chalk4.dim(` [done] ${resultPreview}`));
|
|
5605
|
+
}
|
|
5606
|
+
break;
|
|
5607
|
+
case "screenshot:captured":
|
|
5608
|
+
console.log(chalk4.dim(` [screenshot] ${event.screenshotPath}`));
|
|
5609
|
+
break;
|
|
5610
|
+
case "scenario:pass":
|
|
5611
|
+
console.log(chalk4.green(` [PASS] ${event.scenarioName}`));
|
|
5612
|
+
break;
|
|
5613
|
+
case "scenario:fail":
|
|
5614
|
+
console.log(chalk4.red(` [FAIL] ${event.scenarioName}`));
|
|
5615
|
+
break;
|
|
5616
|
+
case "scenario:error":
|
|
5617
|
+
console.log(chalk4.yellow(` [ERR] ${event.scenarioName}: ${event.error}`));
|
|
5618
|
+
break;
|
|
5619
|
+
}
|
|
5620
|
+
});
|
|
5621
|
+
console.log("");
|
|
5622
|
+
console.log(chalk4.bold(` Running tests against ${url}`));
|
|
5623
|
+
console.log("");
|
|
5624
|
+
}
|
|
5448
5625
|
if (description) {
|
|
5449
5626
|
const scenario = createScenario({
|
|
5450
5627
|
name: description,
|
|
@@ -5475,10 +5652,6 @@ program2.command("run <url> [description]").description("Run test scenarios agai
|
|
|
5475
5652
|
}
|
|
5476
5653
|
process.exit(getExitCode(run2));
|
|
5477
5654
|
}
|
|
5478
|
-
if (opts.fromTodos) {
|
|
5479
|
-
const result = importFromTodos({ projectId });
|
|
5480
|
-
console.log(chalk4.blue(`Imported ${result.imported} scenarios from todos (${result.skipped} skipped)`));
|
|
5481
|
-
}
|
|
5482
5655
|
const { run, results } = await runByFilter({
|
|
5483
5656
|
url,
|
|
5484
5657
|
tags: opts.tag.length > 0 ? opts.tag : undefined,
|
package/dist/index.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export { loadConfig, resolveModel as resolveModelConfig, getDefaultConfig, } fro
|
|
|
12
12
|
export { launchBrowser, getPage, closeBrowser, BrowserPool, installBrowser, } from "./lib/browser.js";
|
|
13
13
|
export { Screenshotter, slugify, generateFilename, getScreenshotDir, ensureDir, } from "./lib/screenshotter.js";
|
|
14
14
|
export { createClient, resolveModel, runAgentLoop, executeTool, BROWSER_TOOLS, } from "./lib/ai-client.js";
|
|
15
|
-
export { runSingleScenario, runBatch, runByFilter, onRunEvent, } from "./lib/runner.js";
|
|
15
|
+
export { runSingleScenario, runBatch, runByFilter, startRunAsync, onRunEvent, } from "./lib/runner.js";
|
|
16
16
|
export type { RunOptions, RunEvent, RunEventHandler } from "./lib/runner.js";
|
|
17
17
|
export { formatTerminal, formatJSON, formatSummary, getExitCode, formatRunList, formatScenarioList, formatResultDetail, } from "./lib/reporter.js";
|
|
18
18
|
export { connectToTodos, pullTasks, taskToScenarioInput, importFromTodos, markTodoDone, } from "./lib/todos-connector.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1951,7 +1951,8 @@ async function runAgentLoop(options) {
|
|
|
1951
1951
|
screenshotter,
|
|
1952
1952
|
model,
|
|
1953
1953
|
runId,
|
|
1954
|
-
maxTurns = 30
|
|
1954
|
+
maxTurns = 30,
|
|
1955
|
+
onStep
|
|
1955
1956
|
} = options;
|
|
1956
1957
|
const systemPrompt = [
|
|
1957
1958
|
"You are an expert QA testing agent. Your job is to thoroughly test web application scenarios.",
|
|
@@ -2010,8 +2011,8 @@ async function runAgentLoop(options) {
|
|
|
2010
2011
|
}
|
|
2011
2012
|
const toolUseBlocks = response.content.filter((block) => block.type === "tool_use");
|
|
2012
2013
|
if (toolUseBlocks.length === 0 && response.stop_reason === "end_turn") {
|
|
2013
|
-
const
|
|
2014
|
-
const textReasoning =
|
|
2014
|
+
const textBlocks2 = response.content.filter((block) => block.type === "text");
|
|
2015
|
+
const textReasoning = textBlocks2.map((b) => b.text).join(`
|
|
2015
2016
|
`);
|
|
2016
2017
|
return {
|
|
2017
2018
|
status: "error",
|
|
@@ -2022,10 +2023,22 @@ async function runAgentLoop(options) {
|
|
|
2022
2023
|
};
|
|
2023
2024
|
}
|
|
2024
2025
|
const toolResults = [];
|
|
2026
|
+
const textBlocks = response.content.filter((block) => block.type === "text");
|
|
2027
|
+
if (textBlocks.length > 0 && onStep) {
|
|
2028
|
+
const thinking = textBlocks.map((b) => b.text).join(`
|
|
2029
|
+
`);
|
|
2030
|
+
onStep({ type: "thinking", thinking, stepNumber });
|
|
2031
|
+
}
|
|
2025
2032
|
for (const toolBlock of toolUseBlocks) {
|
|
2026
2033
|
stepNumber++;
|
|
2027
2034
|
const toolInput = toolBlock.input;
|
|
2035
|
+
if (onStep) {
|
|
2036
|
+
onStep({ type: "tool_call", toolName: toolBlock.name, toolInput, stepNumber });
|
|
2037
|
+
}
|
|
2028
2038
|
const execResult = await executeTool(page, screenshotter, toolBlock.name, toolInput, { runId, scenarioSlug, stepNumber });
|
|
2039
|
+
if (onStep) {
|
|
2040
|
+
onStep({ type: "tool_result", toolName: toolBlock.name, toolResult: execResult.result, stepNumber });
|
|
2041
|
+
}
|
|
2029
2042
|
if (execResult.screenshot) {
|
|
2030
2043
|
screenshots.push({
|
|
2031
2044
|
...execResult.screenshot,
|
|
@@ -2115,7 +2128,20 @@ async function runSingleScenario(scenario, runId, options) {
|
|
|
2115
2128
|
screenshotter,
|
|
2116
2129
|
model,
|
|
2117
2130
|
runId,
|
|
2118
|
-
maxTurns: 30
|
|
2131
|
+
maxTurns: 30,
|
|
2132
|
+
onStep: (stepEvent) => {
|
|
2133
|
+
emit({
|
|
2134
|
+
type: `step:${stepEvent.type}`,
|
|
2135
|
+
scenarioId: scenario.id,
|
|
2136
|
+
scenarioName: scenario.name,
|
|
2137
|
+
runId,
|
|
2138
|
+
toolName: stepEvent.toolName,
|
|
2139
|
+
toolInput: stepEvent.toolInput,
|
|
2140
|
+
toolResult: stepEvent.toolResult,
|
|
2141
|
+
thinking: stepEvent.thinking,
|
|
2142
|
+
stepNumber: stepEvent.stepNumber
|
|
2143
|
+
});
|
|
2144
|
+
}
|
|
2119
2145
|
});
|
|
2120
2146
|
for (const ss of agentResult.screenshots) {
|
|
2121
2147
|
createScreenshot({
|
|
@@ -2225,6 +2251,79 @@ async function runByFilter(options) {
|
|
|
2225
2251
|
}
|
|
2226
2252
|
return runBatch(scenarios, options);
|
|
2227
2253
|
}
|
|
2254
|
+
function startRunAsync(options) {
|
|
2255
|
+
const config = loadConfig();
|
|
2256
|
+
const model = resolveModel2(options.model ?? config.defaultModel);
|
|
2257
|
+
let scenarios;
|
|
2258
|
+
if (options.scenarioIds && options.scenarioIds.length > 0) {
|
|
2259
|
+
const all = listScenarios({ projectId: options.projectId });
|
|
2260
|
+
scenarios = all.filter((s) => options.scenarioIds.includes(s.id) || options.scenarioIds.includes(s.shortId));
|
|
2261
|
+
} else {
|
|
2262
|
+
scenarios = listScenarios({
|
|
2263
|
+
projectId: options.projectId,
|
|
2264
|
+
tags: options.tags,
|
|
2265
|
+
priority: options.priority
|
|
2266
|
+
});
|
|
2267
|
+
}
|
|
2268
|
+
const parallel = options.parallel ?? 1;
|
|
2269
|
+
const run = createRun({
|
|
2270
|
+
url: options.url,
|
|
2271
|
+
model,
|
|
2272
|
+
headed: options.headed,
|
|
2273
|
+
parallel,
|
|
2274
|
+
projectId: options.projectId
|
|
2275
|
+
});
|
|
2276
|
+
if (scenarios.length === 0) {
|
|
2277
|
+
updateRun(run.id, { status: "passed", total: 0, finished_at: new Date().toISOString() });
|
|
2278
|
+
return { runId: run.id, scenarioCount: 0 };
|
|
2279
|
+
}
|
|
2280
|
+
updateRun(run.id, { status: "running", total: scenarios.length });
|
|
2281
|
+
(async () => {
|
|
2282
|
+
const results = [];
|
|
2283
|
+
try {
|
|
2284
|
+
if (parallel <= 1) {
|
|
2285
|
+
for (const scenario of scenarios) {
|
|
2286
|
+
const result = await runSingleScenario(scenario, run.id, options);
|
|
2287
|
+
results.push(result);
|
|
2288
|
+
}
|
|
2289
|
+
} else {
|
|
2290
|
+
const queue = [...scenarios];
|
|
2291
|
+
const running = [];
|
|
2292
|
+
const processNext = async () => {
|
|
2293
|
+
const scenario = queue.shift();
|
|
2294
|
+
if (!scenario)
|
|
2295
|
+
return;
|
|
2296
|
+
const result = await runSingleScenario(scenario, run.id, options);
|
|
2297
|
+
results.push(result);
|
|
2298
|
+
await processNext();
|
|
2299
|
+
};
|
|
2300
|
+
const workers = Math.min(parallel, scenarios.length);
|
|
2301
|
+
for (let i = 0;i < workers; i++) {
|
|
2302
|
+
running.push(processNext());
|
|
2303
|
+
}
|
|
2304
|
+
await Promise.all(running);
|
|
2305
|
+
}
|
|
2306
|
+
const passed = results.filter((r) => r.status === "passed").length;
|
|
2307
|
+
const failed = results.filter((r) => r.status === "failed" || r.status === "error").length;
|
|
2308
|
+
updateRun(run.id, {
|
|
2309
|
+
status: failed > 0 ? "failed" : "passed",
|
|
2310
|
+
passed,
|
|
2311
|
+
failed,
|
|
2312
|
+
total: scenarios.length,
|
|
2313
|
+
finished_at: new Date().toISOString()
|
|
2314
|
+
});
|
|
2315
|
+
emit({ type: "run:complete", runId: run.id });
|
|
2316
|
+
} catch (error) {
|
|
2317
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
2318
|
+
updateRun(run.id, {
|
|
2319
|
+
status: "failed",
|
|
2320
|
+
finished_at: new Date().toISOString()
|
|
2321
|
+
});
|
|
2322
|
+
emit({ type: "run:complete", runId: run.id, error: errorMsg });
|
|
2323
|
+
}
|
|
2324
|
+
})();
|
|
2325
|
+
return { runId: run.id, scenarioCount: scenarios.length };
|
|
2326
|
+
}
|
|
2228
2327
|
function estimateCost(model, tokens) {
|
|
2229
2328
|
const costs = {
|
|
2230
2329
|
"claude-haiku-4-5-20251001": 0.1,
|
|
@@ -4418,6 +4517,7 @@ export {
|
|
|
4418
4517
|
testWebhook,
|
|
4419
4518
|
taskToScenarioInput,
|
|
4420
4519
|
startWatcher,
|
|
4520
|
+
startRunAsync,
|
|
4421
4521
|
slugify,
|
|
4422
4522
|
shouldRunAt,
|
|
4423
4523
|
shortUuid,
|
package/dist/lib/ai-client.d.ts
CHANGED
|
@@ -31,6 +31,14 @@ interface ToolExecutionResult {
|
|
|
31
31
|
* Returns the result string and an optional screenshot capture.
|
|
32
32
|
*/
|
|
33
33
|
export declare function executeTool(page: Page, screenshotter: Screenshotter, toolName: string, toolInput: Record<string, unknown>, context: ToolContext): Promise<ToolExecutionResult>;
|
|
34
|
+
export type StepEventHandler = (event: {
|
|
35
|
+
type: "tool_call" | "tool_result" | "thinking";
|
|
36
|
+
toolName?: string;
|
|
37
|
+
toolInput?: Record<string, unknown>;
|
|
38
|
+
toolResult?: string;
|
|
39
|
+
thinking?: string;
|
|
40
|
+
stepNumber: number;
|
|
41
|
+
}) => void;
|
|
34
42
|
interface AgentLoopOptions {
|
|
35
43
|
client: Anthropic;
|
|
36
44
|
page: Page;
|
|
@@ -39,6 +47,7 @@ interface AgentLoopOptions {
|
|
|
39
47
|
model: string;
|
|
40
48
|
runId: string;
|
|
41
49
|
maxTurns?: number;
|
|
50
|
+
onStep?: StepEventHandler;
|
|
42
51
|
}
|
|
43
52
|
interface AgentLoopResult {
|
|
44
53
|
status: "passed" | "failed" | "error";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-client.d.ts","sourceRoot":"","sources":["../../src/lib/ai-client.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,mBAAmB,CAAC;AAC1C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD,OAAO,KAAK,EAAe,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAI/D;;;GAGG;AACH,wBAAgB,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAKzD;AAID,eAAO,MAAM,aAAa,EAAE,SAAS,CAAC,IAAI,EAyTzC,CAAC;AAIF,UAAU,WAAW;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,UAAU,gBAAgB;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,UAAU,mBAAmB;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,gBAAgB,CAAC;CAC/B;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,IAAI,EACV,aAAa,EAAE,aAAa,EAC5B,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAClC,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,mBAAmB,CAAC,CA8P9B;AAID,UAAU,gBAAgB;IACxB,MAAM,EAAE,SAAS,CAAC;IAClB,IAAI,EAAE,IAAI,CAAC;IACX,QAAQ,EAAE,QAAQ,CAAC;IACnB,aAAa,EAAE,aAAa,CAAC;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"ai-client.d.ts","sourceRoot":"","sources":["../../src/lib/ai-client.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/runner.d.ts
CHANGED
|
@@ -10,13 +10,18 @@ export interface RunOptions {
|
|
|
10
10
|
screenshotDir?: string;
|
|
11
11
|
}
|
|
12
12
|
export interface RunEvent {
|
|
13
|
-
type: "scenario:start" | "scenario:pass" | "scenario:fail" | "scenario:error" | "screenshot:captured" | "run:complete";
|
|
13
|
+
type: "scenario:start" | "scenario:pass" | "scenario:fail" | "scenario:error" | "screenshot:captured" | "run:complete" | "step:tool_call" | "step:tool_result" | "step:thinking";
|
|
14
14
|
scenarioId?: string;
|
|
15
15
|
scenarioName?: string;
|
|
16
16
|
resultId?: string;
|
|
17
17
|
runId?: string;
|
|
18
18
|
error?: string;
|
|
19
19
|
screenshotPath?: string;
|
|
20
|
+
toolName?: string;
|
|
21
|
+
toolInput?: Record<string, unknown>;
|
|
22
|
+
toolResult?: string;
|
|
23
|
+
thinking?: string;
|
|
24
|
+
stepNumber?: number;
|
|
20
25
|
}
|
|
21
26
|
export type RunEventHandler = (event: RunEvent) => void;
|
|
22
27
|
export declare function onRunEvent(handler: RunEventHandler): void;
|
|
@@ -33,4 +38,16 @@ export declare function runByFilter(options: RunOptions & {
|
|
|
33
38
|
run: Run;
|
|
34
39
|
results: Result[];
|
|
35
40
|
}>;
|
|
41
|
+
/**
|
|
42
|
+
* Start a run asynchronously — creates the run record immediately and returns it,
|
|
43
|
+
* then executes scenarios in the background. Poll getRun(id) to check progress.
|
|
44
|
+
*/
|
|
45
|
+
export declare function startRunAsync(options: RunOptions & {
|
|
46
|
+
tags?: string[];
|
|
47
|
+
priority?: string;
|
|
48
|
+
scenarioIds?: string[];
|
|
49
|
+
}): {
|
|
50
|
+
runId: string;
|
|
51
|
+
scenarioCount: number;
|
|
52
|
+
};
|
|
36
53
|
//# sourceMappingURL=runner.d.ts.map
|
package/dist/lib/runner.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/lib/runner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAW/D,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/lib/runner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAW/D,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EACA,gBAAgB,GAChB,eAAe,GACf,eAAe,GACf,gBAAgB,GAChB,qBAAqB,GACrB,cAAc,GACd,gBAAgB,GAChB,kBAAkB,GAClB,eAAe,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,eAAe,GAAG,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAC;AAIxD,wBAAgB,UAAU,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI,CAEzD;AAMD,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,MAAM,CAAC,CAiGjB;AAED,wBAAsB,QAAQ,CAC5B,SAAS,EAAE,QAAQ,EAAE,EACrB,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC;IAAE,GAAG,EAAE,GAAG,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CA4D1C;AAED,wBAAsB,WAAW,CAC/B,OAAO,EAAE,UAAU,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,GACnF,OAAO,CAAC;IAAE,GAAG,EAAE,GAAG,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAuB1C;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,UAAU,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,GACnF;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,CA+E1C"}
|
package/dist/mcp/index.js
CHANGED
|
@@ -5565,7 +5565,8 @@ async function runAgentLoop(options) {
|
|
|
5565
5565
|
screenshotter,
|
|
5566
5566
|
model,
|
|
5567
5567
|
runId,
|
|
5568
|
-
maxTurns = 30
|
|
5568
|
+
maxTurns = 30,
|
|
5569
|
+
onStep
|
|
5569
5570
|
} = options;
|
|
5570
5571
|
const systemPrompt = [
|
|
5571
5572
|
"You are an expert QA testing agent. Your job is to thoroughly test web application scenarios.",
|
|
@@ -5624,8 +5625,8 @@ async function runAgentLoop(options) {
|
|
|
5624
5625
|
}
|
|
5625
5626
|
const toolUseBlocks = response.content.filter((block) => block.type === "tool_use");
|
|
5626
5627
|
if (toolUseBlocks.length === 0 && response.stop_reason === "end_turn") {
|
|
5627
|
-
const
|
|
5628
|
-
const textReasoning =
|
|
5628
|
+
const textBlocks2 = response.content.filter((block) => block.type === "text");
|
|
5629
|
+
const textReasoning = textBlocks2.map((b) => b.text).join(`
|
|
5629
5630
|
`);
|
|
5630
5631
|
return {
|
|
5631
5632
|
status: "error",
|
|
@@ -5636,10 +5637,22 @@ async function runAgentLoop(options) {
|
|
|
5636
5637
|
};
|
|
5637
5638
|
}
|
|
5638
5639
|
const toolResults = [];
|
|
5640
|
+
const textBlocks = response.content.filter((block) => block.type === "text");
|
|
5641
|
+
if (textBlocks.length > 0 && onStep) {
|
|
5642
|
+
const thinking = textBlocks.map((b) => b.text).join(`
|
|
5643
|
+
`);
|
|
5644
|
+
onStep({ type: "thinking", thinking, stepNumber });
|
|
5645
|
+
}
|
|
5639
5646
|
for (const toolBlock of toolUseBlocks) {
|
|
5640
5647
|
stepNumber++;
|
|
5641
5648
|
const toolInput = toolBlock.input;
|
|
5649
|
+
if (onStep) {
|
|
5650
|
+
onStep({ type: "tool_call", toolName: toolBlock.name, toolInput, stepNumber });
|
|
5651
|
+
}
|
|
5642
5652
|
const execResult = await executeTool(page, screenshotter, toolBlock.name, toolInput, { runId, scenarioSlug, stepNumber });
|
|
5653
|
+
if (onStep) {
|
|
5654
|
+
onStep({ type: "tool_result", toolName: toolBlock.name, toolResult: execResult.result, stepNumber });
|
|
5655
|
+
}
|
|
5643
5656
|
if (execResult.screenshot) {
|
|
5644
5657
|
screenshots.push({
|
|
5645
5658
|
...execResult.screenshot,
|
|
@@ -5781,7 +5794,20 @@ async function runSingleScenario(scenario, runId, options) {
|
|
|
5781
5794
|
screenshotter,
|
|
5782
5795
|
model,
|
|
5783
5796
|
runId,
|
|
5784
|
-
maxTurns: 30
|
|
5797
|
+
maxTurns: 30,
|
|
5798
|
+
onStep: (stepEvent) => {
|
|
5799
|
+
emit({
|
|
5800
|
+
type: `step:${stepEvent.type}`,
|
|
5801
|
+
scenarioId: scenario.id,
|
|
5802
|
+
scenarioName: scenario.name,
|
|
5803
|
+
runId,
|
|
5804
|
+
toolName: stepEvent.toolName,
|
|
5805
|
+
toolInput: stepEvent.toolInput,
|
|
5806
|
+
toolResult: stepEvent.toolResult,
|
|
5807
|
+
thinking: stepEvent.thinking,
|
|
5808
|
+
stepNumber: stepEvent.stepNumber
|
|
5809
|
+
});
|
|
5810
|
+
}
|
|
5785
5811
|
});
|
|
5786
5812
|
for (const ss of agentResult.screenshots) {
|
|
5787
5813
|
createScreenshot({
|
|
@@ -5891,6 +5917,79 @@ async function runByFilter(options) {
|
|
|
5891
5917
|
}
|
|
5892
5918
|
return runBatch(scenarios, options);
|
|
5893
5919
|
}
|
|
5920
|
+
function startRunAsync(options) {
|
|
5921
|
+
const config = loadConfig();
|
|
5922
|
+
const model = resolveModel(options.model ?? config.defaultModel);
|
|
5923
|
+
let scenarios;
|
|
5924
|
+
if (options.scenarioIds && options.scenarioIds.length > 0) {
|
|
5925
|
+
const all = listScenarios({ projectId: options.projectId });
|
|
5926
|
+
scenarios = all.filter((s) => options.scenarioIds.includes(s.id) || options.scenarioIds.includes(s.shortId));
|
|
5927
|
+
} else {
|
|
5928
|
+
scenarios = listScenarios({
|
|
5929
|
+
projectId: options.projectId,
|
|
5930
|
+
tags: options.tags,
|
|
5931
|
+
priority: options.priority
|
|
5932
|
+
});
|
|
5933
|
+
}
|
|
5934
|
+
const parallel = options.parallel ?? 1;
|
|
5935
|
+
const run = createRun({
|
|
5936
|
+
url: options.url,
|
|
5937
|
+
model,
|
|
5938
|
+
headed: options.headed,
|
|
5939
|
+
parallel,
|
|
5940
|
+
projectId: options.projectId
|
|
5941
|
+
});
|
|
5942
|
+
if (scenarios.length === 0) {
|
|
5943
|
+
updateRun(run.id, { status: "passed", total: 0, finished_at: new Date().toISOString() });
|
|
5944
|
+
return { runId: run.id, scenarioCount: 0 };
|
|
5945
|
+
}
|
|
5946
|
+
updateRun(run.id, { status: "running", total: scenarios.length });
|
|
5947
|
+
(async () => {
|
|
5948
|
+
const results = [];
|
|
5949
|
+
try {
|
|
5950
|
+
if (parallel <= 1) {
|
|
5951
|
+
for (const scenario of scenarios) {
|
|
5952
|
+
const result = await runSingleScenario(scenario, run.id, options);
|
|
5953
|
+
results.push(result);
|
|
5954
|
+
}
|
|
5955
|
+
} else {
|
|
5956
|
+
const queue = [...scenarios];
|
|
5957
|
+
const running = [];
|
|
5958
|
+
const processNext = async () => {
|
|
5959
|
+
const scenario = queue.shift();
|
|
5960
|
+
if (!scenario)
|
|
5961
|
+
return;
|
|
5962
|
+
const result = await runSingleScenario(scenario, run.id, options);
|
|
5963
|
+
results.push(result);
|
|
5964
|
+
await processNext();
|
|
5965
|
+
};
|
|
5966
|
+
const workers = Math.min(parallel, scenarios.length);
|
|
5967
|
+
for (let i = 0;i < workers; i++) {
|
|
5968
|
+
running.push(processNext());
|
|
5969
|
+
}
|
|
5970
|
+
await Promise.all(running);
|
|
5971
|
+
}
|
|
5972
|
+
const passed = results.filter((r) => r.status === "passed").length;
|
|
5973
|
+
const failed = results.filter((r) => r.status === "failed" || r.status === "error").length;
|
|
5974
|
+
updateRun(run.id, {
|
|
5975
|
+
status: failed > 0 ? "failed" : "passed",
|
|
5976
|
+
passed,
|
|
5977
|
+
failed,
|
|
5978
|
+
total: scenarios.length,
|
|
5979
|
+
finished_at: new Date().toISOString()
|
|
5980
|
+
});
|
|
5981
|
+
emit({ type: "run:complete", runId: run.id });
|
|
5982
|
+
} catch (error) {
|
|
5983
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
5984
|
+
updateRun(run.id, {
|
|
5985
|
+
status: "failed",
|
|
5986
|
+
finished_at: new Date().toISOString()
|
|
5987
|
+
});
|
|
5988
|
+
emit({ type: "run:complete", runId: run.id, error: errorMsg });
|
|
5989
|
+
}
|
|
5990
|
+
})();
|
|
5991
|
+
return { runId: run.id, scenarioCount: scenarios.length };
|
|
5992
|
+
}
|
|
5894
5993
|
function estimateCost(model, tokens) {
|
|
5895
5994
|
const costs = {
|
|
5896
5995
|
"claude-haiku-4-5-20251001": 0.1,
|
|
@@ -6343,7 +6442,7 @@ class Scheduler {
|
|
|
6343
6442
|
|
|
6344
6443
|
// src/mcp/index.ts
|
|
6345
6444
|
var server = new McpServer({
|
|
6346
|
-
name: "testers
|
|
6445
|
+
name: "testers",
|
|
6347
6446
|
version: "0.0.1"
|
|
6348
6447
|
});
|
|
6349
6448
|
server.tool("create_scenario", "Create a new test scenario", {
|
|
@@ -6457,18 +6556,15 @@ server.tool("run_scenarios", "Run test scenarios against a URL", {
|
|
|
6457
6556
|
parallel: exports_external.number().optional().describe("Number of parallel workers")
|
|
6458
6557
|
}, async ({ url, tags, scenarioIds, priority, model, headed, parallel }) => {
|
|
6459
6558
|
try {
|
|
6460
|
-
const {
|
|
6461
|
-
const passed = results.filter((r) => r.status === "passed").length;
|
|
6462
|
-
const failed = results.filter((r) => r.status === "failed" || r.status === "error").length;
|
|
6463
|
-
const skipped = results.filter((r) => r.status === "skipped").length;
|
|
6559
|
+
const { runId, scenarioCount } = startRunAsync({ url, tags, scenarioIds, priority, model, headed, parallel });
|
|
6464
6560
|
const text = [
|
|
6465
|
-
`Run
|
|
6466
|
-
`
|
|
6467
|
-
`
|
|
6468
|
-
`
|
|
6469
|
-
|
|
6470
|
-
|
|
6471
|
-
].
|
|
6561
|
+
`Run started: ${runId}`,
|
|
6562
|
+
`Scenarios: ${scenarioCount}`,
|
|
6563
|
+
`URL: ${url}`,
|
|
6564
|
+
`Status: running (async)`,
|
|
6565
|
+
``,
|
|
6566
|
+
`Poll with get_run to check progress.`
|
|
6567
|
+
].join(`
|
|
6472
6568
|
`);
|
|
6473
6569
|
return { content: [{ type: "text", text }] };
|
|
6474
6570
|
} catch (error) {
|
|
@@ -6712,6 +6808,6 @@ async function main() {
|
|
|
6712
6808
|
await server.connect(transport);
|
|
6713
6809
|
}
|
|
6714
6810
|
main().catch((error) => {
|
|
6715
|
-
console.error("Failed to start testers
|
|
6811
|
+
console.error("Failed to start testers:", error);
|
|
6716
6812
|
process.exit(1);
|
|
6717
6813
|
});
|
package/dist/server/index.js
CHANGED
|
@@ -1499,7 +1499,8 @@ async function runAgentLoop(options) {
|
|
|
1499
1499
|
screenshotter,
|
|
1500
1500
|
model,
|
|
1501
1501
|
runId,
|
|
1502
|
-
maxTurns = 30
|
|
1502
|
+
maxTurns = 30,
|
|
1503
|
+
onStep
|
|
1503
1504
|
} = options;
|
|
1504
1505
|
const systemPrompt = [
|
|
1505
1506
|
"You are an expert QA testing agent. Your job is to thoroughly test web application scenarios.",
|
|
@@ -1558,8 +1559,8 @@ async function runAgentLoop(options) {
|
|
|
1558
1559
|
}
|
|
1559
1560
|
const toolUseBlocks = response.content.filter((block) => block.type === "tool_use");
|
|
1560
1561
|
if (toolUseBlocks.length === 0 && response.stop_reason === "end_turn") {
|
|
1561
|
-
const
|
|
1562
|
-
const textReasoning =
|
|
1562
|
+
const textBlocks2 = response.content.filter((block) => block.type === "text");
|
|
1563
|
+
const textReasoning = textBlocks2.map((b) => b.text).join(`
|
|
1563
1564
|
`);
|
|
1564
1565
|
return {
|
|
1565
1566
|
status: "error",
|
|
@@ -1570,10 +1571,22 @@ async function runAgentLoop(options) {
|
|
|
1570
1571
|
};
|
|
1571
1572
|
}
|
|
1572
1573
|
const toolResults = [];
|
|
1574
|
+
const textBlocks = response.content.filter((block) => block.type === "text");
|
|
1575
|
+
if (textBlocks.length > 0 && onStep) {
|
|
1576
|
+
const thinking = textBlocks.map((b) => b.text).join(`
|
|
1577
|
+
`);
|
|
1578
|
+
onStep({ type: "thinking", thinking, stepNumber });
|
|
1579
|
+
}
|
|
1573
1580
|
for (const toolBlock of toolUseBlocks) {
|
|
1574
1581
|
stepNumber++;
|
|
1575
1582
|
const toolInput = toolBlock.input;
|
|
1583
|
+
if (onStep) {
|
|
1584
|
+
onStep({ type: "tool_call", toolName: toolBlock.name, toolInput, stepNumber });
|
|
1585
|
+
}
|
|
1576
1586
|
const execResult = await executeTool(page, screenshotter, toolBlock.name, toolInput, { runId, scenarioSlug, stepNumber });
|
|
1587
|
+
if (onStep) {
|
|
1588
|
+
onStep({ type: "tool_result", toolName: toolBlock.name, toolResult: execResult.result, stepNumber });
|
|
1589
|
+
}
|
|
1577
1590
|
if (execResult.screenshot) {
|
|
1578
1591
|
screenshots.push({
|
|
1579
1592
|
...execResult.screenshot,
|
|
@@ -1715,7 +1728,20 @@ async function runSingleScenario(scenario, runId, options) {
|
|
|
1715
1728
|
screenshotter,
|
|
1716
1729
|
model,
|
|
1717
1730
|
runId,
|
|
1718
|
-
maxTurns: 30
|
|
1731
|
+
maxTurns: 30,
|
|
1732
|
+
onStep: (stepEvent) => {
|
|
1733
|
+
emit({
|
|
1734
|
+
type: `step:${stepEvent.type}`,
|
|
1735
|
+
scenarioId: scenario.id,
|
|
1736
|
+
scenarioName: scenario.name,
|
|
1737
|
+
runId,
|
|
1738
|
+
toolName: stepEvent.toolName,
|
|
1739
|
+
toolInput: stepEvent.toolInput,
|
|
1740
|
+
toolResult: stepEvent.toolResult,
|
|
1741
|
+
thinking: stepEvent.thinking,
|
|
1742
|
+
stepNumber: stepEvent.stepNumber
|
|
1743
|
+
});
|
|
1744
|
+
}
|
|
1719
1745
|
});
|
|
1720
1746
|
for (const ss of agentResult.screenshots) {
|
|
1721
1747
|
createScreenshot({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hasna/testers",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5",
|
|
4
4
|
"description": "AI-powered QA testing CLI — spawns cheap AI agents to test web apps with headless browsers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"build:mcp": "bun build src/mcp/index.ts --outdir dist/mcp --target bun --external @modelcontextprotocol/sdk --external @anthropic-ai/sdk --external playwright",
|
|
29
29
|
"build:server": "bun build src/server/index.ts --outdir dist/server --target bun --external @anthropic-ai/sdk --external playwright",
|
|
30
30
|
"build:lib": "bun build src/index.ts --outdir dist --target bun --external playwright --external @anthropic-ai/sdk --external @modelcontextprotocol/sdk",
|
|
31
|
-
"build:types": "tsc --emitDeclarationOnly --outDir dist",
|
|
31
|
+
"build:types": "NODE_OPTIONS='--max-old-space-size=8192' tsc --emitDeclarationOnly --outDir dist --skipLibCheck",
|
|
32
32
|
"build:dashboard": "cd dashboard && bun run build",
|
|
33
33
|
"typecheck": "tsc --noEmit",
|
|
34
34
|
"test": "bun test",
|