@pcamarajr/scout 0.1.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/LICENSE +21 -0
- package/README.md +188 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +157 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +31 -0
- package/dist/config.js +47 -0
- package/dist/config.js.map +1 -0
- package/dist/engine.d.ts +20 -0
- package/dist/engine.js +139 -0
- package/dist/engine.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/server.d.ts +8 -0
- package/dist/mcp/server.js +106 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/report.d.ts +5 -0
- package/dist/report.js +68 -0
- package/dist/report.js.map +1 -0
- package/dist/runner/ai-runner.d.ts +15 -0
- package/dist/runner/ai-runner.js +214 -0
- package/dist/runner/ai-runner.js.map +1 -0
- package/dist/runner/browser.d.ts +68 -0
- package/dist/runner/browser.js +307 -0
- package/dist/runner/browser.js.map +1 -0
- package/dist/runner/script-runner.d.ts +15 -0
- package/dist/runner/script-runner.js +51 -0
- package/dist/runner/script-runner.js.map +1 -0
- package/dist/store.d.ts +33 -0
- package/dist/store.js +119 -0
- package/dist/store.js.map +1 -0
- package/dist/types.d.ts +78 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +61 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP interface so coding agents (Claude Code local or cloud sessions) can
|
|
3
|
+
* create scenarios and run verifications as tool calls. Mirrors the CLI.
|
|
4
|
+
*
|
|
5
|
+
* Register in the target project's .mcp.json:
|
|
6
|
+
* { "mcpServers": { "scout": { "command": "npx", "args": ["scout", "mcp"] } } }
|
|
7
|
+
*/
|
|
8
|
+
export declare function startMcpServer(): Promise<void>;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
import { loadConfig } from "../config.js";
|
|
7
|
+
import { runScenario } from "../engine.js";
|
|
8
|
+
import { renderSummary } from "../report.js";
|
|
9
|
+
import { Store } from "../store.js";
|
|
10
|
+
/**
|
|
11
|
+
* MCP interface so coding agents (Claude Code local or cloud sessions) can
|
|
12
|
+
* create scenarios and run verifications as tool calls. Mirrors the CLI.
|
|
13
|
+
*
|
|
14
|
+
* Register in the target project's .mcp.json:
|
|
15
|
+
* { "mcpServers": { "scout": { "command": "npx", "args": ["scout", "mcp"] } } }
|
|
16
|
+
*/
|
|
17
|
+
export async function startMcpServer() {
|
|
18
|
+
const server = new McpServer({ name: "scout", version: "0.1.0" });
|
|
19
|
+
const store = new Store();
|
|
20
|
+
server.registerTool("scout_list_scenarios", {
|
|
21
|
+
description: "Lista os cenários de QA do projeto com status e se há script cacheado.",
|
|
22
|
+
inputSchema: {},
|
|
23
|
+
}, async () => {
|
|
24
|
+
const scenarios = store.listScenarios().map((s) => ({
|
|
25
|
+
...s,
|
|
26
|
+
hasCachedScript: Boolean(store.loadSteps(s.slug)),
|
|
27
|
+
}));
|
|
28
|
+
return { content: [{ type: "text", text: JSON.stringify(scenarios, null, 2) }] };
|
|
29
|
+
});
|
|
30
|
+
server.registerTool("scout_create_scenario", {
|
|
31
|
+
description: "Cria um cenário de verificação em linguagem natural. Descreva o FLUXO (passos do usuário) e o COMPORTAMENTO ESPERADO (o que deve/não deve acontecer). Não escreva seletores nem código — o runner descobre sozinho no browser.",
|
|
32
|
+
inputSchema: {
|
|
33
|
+
name: z.string().describe("Nome curto do cenário"),
|
|
34
|
+
scenario: z.string().describe("Fluxo + expectativas em linguagem natural"),
|
|
35
|
+
profile: z.string().optional().describe("Profile de auth de scout.config.json (omitir = anônimo)"),
|
|
36
|
+
notes: z.string().optional().describe("Contexto extra para o runner"),
|
|
37
|
+
},
|
|
38
|
+
}, async ({ name, scenario, profile, notes }) => {
|
|
39
|
+
store.init();
|
|
40
|
+
const created = store.addScenario({ name, scenario, profile, notes });
|
|
41
|
+
return {
|
|
42
|
+
content: [
|
|
43
|
+
{ type: "text", text: `Cenário #${created.id} (${created.slug}) criado. Rode com scout_run.` },
|
|
44
|
+
],
|
|
45
|
+
};
|
|
46
|
+
});
|
|
47
|
+
server.registerTool("scout_run", {
|
|
48
|
+
description: "Roda a verificação de um cenário (ou todos). Usa o script determinístico cacheado quando existe; cai pro agente AI no primeiro run ou quando o script quebra (self-healing). Retorna veredito + evidências.",
|
|
49
|
+
inputSchema: {
|
|
50
|
+
scenario: z.string().optional().describe("id ou slug; omitir = todos"),
|
|
51
|
+
forceAi: z.boolean().optional().describe("Ignora o cache e re-grava o script via AI"),
|
|
52
|
+
},
|
|
53
|
+
}, async ({ scenario, forceAi }) => {
|
|
54
|
+
const config = loadConfig();
|
|
55
|
+
const all = store.listScenarios();
|
|
56
|
+
const targets = scenario
|
|
57
|
+
? all.filter((s) => s.id === Number(scenario) || s.slug === scenario)
|
|
58
|
+
: all;
|
|
59
|
+
if (!targets.length) {
|
|
60
|
+
return { content: [{ type: "text", text: `Nenhum cenário encontrado para "${scenario}".` }], isError: true };
|
|
61
|
+
}
|
|
62
|
+
const results = [];
|
|
63
|
+
for (const s of targets) {
|
|
64
|
+
try {
|
|
65
|
+
const r = await runScenario(store, s, config, { forceAi });
|
|
66
|
+
results.push({
|
|
67
|
+
scenario: s.name,
|
|
68
|
+
verdict: r.verdict,
|
|
69
|
+
mode: r.mode,
|
|
70
|
+
healed: r.healed ?? false,
|
|
71
|
+
reason: r.reason,
|
|
72
|
+
runDir: r.runDir,
|
|
73
|
+
screenshots: r.screenshots,
|
|
74
|
+
trace: r.trace,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
results.push({
|
|
79
|
+
scenario: s.name,
|
|
80
|
+
verdict: "blocked",
|
|
81
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return { content: [{ type: "text", text: JSON.stringify(results, null, 2) }] };
|
|
86
|
+
});
|
|
87
|
+
server.registerTool("scout_report", {
|
|
88
|
+
description: "Resumo markdown da suíte (embedável em corpo de PR) — status por cenário + falhas.",
|
|
89
|
+
inputSchema: {},
|
|
90
|
+
}, async () => ({
|
|
91
|
+
content: [{ type: "text", text: renderSummary(store.listScenarios(), store.latestRuns()) }],
|
|
92
|
+
}));
|
|
93
|
+
server.registerTool("scout_get_run_report", {
|
|
94
|
+
description: "Lê o report.md de um run específico (passe o runDir retornado por scout_run).",
|
|
95
|
+
inputSchema: { runDir: z.string() },
|
|
96
|
+
}, async ({ runDir }) => {
|
|
97
|
+
const file = path.join(runDir, "report.md");
|
|
98
|
+
if (!fs.existsSync(file)) {
|
|
99
|
+
return { content: [{ type: "text", text: `Sem report em ${runDir}` }], isError: true };
|
|
100
|
+
}
|
|
101
|
+
return { content: [{ type: "text", text: fs.readFileSync(file, "utf8") }] };
|
|
102
|
+
});
|
|
103
|
+
const transport = new StdioServerTransport();
|
|
104
|
+
await server.connect(transport);
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"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"}
|
package/dist/report.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { RunResult, Scenario, Step } from "./types.js";
|
|
2
|
+
/** Per-run report saved at .scout/runs/<id>/report.md */
|
|
3
|
+
export declare function renderRunReport(result: RunResult, scenario: Scenario, steps?: Step[]): string;
|
|
4
|
+
/** Suite summary — PR-embeddable (the \`scout report\` output). */
|
|
5
|
+
export declare function renderSummary(scenarios: Scenario[], latest: Map<string, RunResult>): string;
|
package/dist/report.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { describeStep } from "./runner/script-runner.js";
|
|
3
|
+
const ICON = {
|
|
4
|
+
verified: "✅",
|
|
5
|
+
failed: "❌",
|
|
6
|
+
partial: "⚠️",
|
|
7
|
+
blocked: "🚫",
|
|
8
|
+
pending: "⏳",
|
|
9
|
+
};
|
|
10
|
+
/** Per-run report saved at .scout/runs/<id>/report.md */
|
|
11
|
+
export function renderRunReport(result, scenario, steps) {
|
|
12
|
+
const lines = [
|
|
13
|
+
`# ${ICON[result.verdict]} ${scenario.name}`,
|
|
14
|
+
``,
|
|
15
|
+
`| | |`,
|
|
16
|
+
`|---|---|`,
|
|
17
|
+
`| Verdict | **${result.verdict}** |`,
|
|
18
|
+
`| Mode | ${result.mode === "replay" ? "deterministic replay" : "AI-driven"}${result.healed ? " (healed)" : ""} |`,
|
|
19
|
+
`| Profile | ${scenario.profile ?? "anonymous"} |`,
|
|
20
|
+
`| Duration | ${(result.durationMs / 1000).toFixed(1)}s |`,
|
|
21
|
+
`| Started | ${result.startedAt} |`,
|
|
22
|
+
``,
|
|
23
|
+
`**Scenario:** ${scenario.scenario}`,
|
|
24
|
+
``,
|
|
25
|
+
`**Resultado:** ${result.reason}`,
|
|
26
|
+
];
|
|
27
|
+
if (steps?.length) {
|
|
28
|
+
lines.push(``, `## Recorded script (${steps.length} steps)`, ``);
|
|
29
|
+
steps.forEach((s, i) => lines.push(`${i + 1}. ${describeStep(s)}`));
|
|
30
|
+
}
|
|
31
|
+
if (result.screenshots.length) {
|
|
32
|
+
lines.push(``, `## Evidence`, ``);
|
|
33
|
+
for (const shot of result.screenshots) {
|
|
34
|
+
const name = path.basename(shot);
|
|
35
|
+
lines.push(``);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
if (result.trace) {
|
|
39
|
+
lines.push(``, `## Trace`, ``, `\`npx playwright show-trace ${result.trace}\` — screenshots, DOM snapshots, network and console for the entire session.`);
|
|
40
|
+
}
|
|
41
|
+
return lines.join("\n") + "\n";
|
|
42
|
+
}
|
|
43
|
+
/** Suite summary — PR-embeddable (the \`scout report\` output). */
|
|
44
|
+
export function renderSummary(scenarios, latest) {
|
|
45
|
+
const lines = [
|
|
46
|
+
`## 🔭 Scout — browser verification`,
|
|
47
|
+
``,
|
|
48
|
+
`| # | Scenario | Profile | Status | Mode | Last run |`,
|
|
49
|
+
`|---|---------|---------|--------|------|------------|`,
|
|
50
|
+
];
|
|
51
|
+
for (const s of scenarios) {
|
|
52
|
+
const run = latest.get(s.slug);
|
|
53
|
+
const mode = run ? (run.mode === "replay" ? "replay" : run.healed ? "AI (healed)" : "AI") : "—";
|
|
54
|
+
lines.push(`| ${s.id} | ${s.name} | ${s.profile ?? "anonymous"} | ${ICON[s.status]} ${s.status} | ${mode} | ${s.lastRun ? s.lastRun.slice(0, 16).replace("T", " ") : "never"} |`);
|
|
55
|
+
}
|
|
56
|
+
const failing = scenarios.filter((s) => s.status === "failed" || s.status === "blocked");
|
|
57
|
+
if (failing.length) {
|
|
58
|
+
lines.push(``, `### Failures`, ``);
|
|
59
|
+
for (const s of failing) {
|
|
60
|
+
const run = latest.get(s.slug);
|
|
61
|
+
lines.push(`- **${s.name}**: ${run?.reason ?? "no run recorded"}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const verified = scenarios.filter((s) => s.status === "verified").length;
|
|
65
|
+
lines.push(``, `**${verified}/${scenarios.length} verified.**`);
|
|
66
|
+
return lines.join("\n") + "\n";
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ScoutConfig } from "../config.js";
|
|
2
|
+
import type { Scenario, Step, Verdict } from "../types.js";
|
|
3
|
+
import { BrowserSession } from "./browser.js";
|
|
4
|
+
export interface AiRunOutcome {
|
|
5
|
+
verdict: Verdict;
|
|
6
|
+
reason: string;
|
|
7
|
+
steps: Step[];
|
|
8
|
+
transcript: string[];
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* AI-driven run: a Claude agent navigates the real browser to verify the
|
|
12
|
+
* scenario. Every successful action is recorded as a deterministic Step so
|
|
13
|
+
* subsequent runs can replay without the LLM.
|
|
14
|
+
*/
|
|
15
|
+
export declare function runWithAgent(session: BrowserSession, scenario: Scenario, config: ScoutConfig): Promise<AiRunOutcome>;
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { createSdkMcpServer, query, tool } from "@anthropic-ai/claude-agent-sdk";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { resolveEnvValue } from "./browser.js";
|
|
4
|
+
/**
|
|
5
|
+
* AI-driven run: a Claude agent navigates the real browser to verify the
|
|
6
|
+
* scenario. Every successful action is recorded as a deterministic Step so
|
|
7
|
+
* subsequent runs can replay without the LLM.
|
|
8
|
+
*/
|
|
9
|
+
export async function runWithAgent(session, scenario, config) {
|
|
10
|
+
const steps = [];
|
|
11
|
+
const transcript = [];
|
|
12
|
+
let verdict;
|
|
13
|
+
const record = (step) => steps.push(step);
|
|
14
|
+
const afterAction = async () => {
|
|
15
|
+
const snap = await session.snapshot();
|
|
16
|
+
return session.formatSnapshot(snap);
|
|
17
|
+
};
|
|
18
|
+
const ok = (text) => ({ content: [{ type: "text", text }] });
|
|
19
|
+
const fail = (error) => ({
|
|
20
|
+
content: [{ type: "text", text: `ERRO: ${error instanceof Error ? error.message : String(error)}` }],
|
|
21
|
+
isError: true,
|
|
22
|
+
});
|
|
23
|
+
const browserServer = createSdkMcpServer({
|
|
24
|
+
name: "browser",
|
|
25
|
+
version: "1.0.0",
|
|
26
|
+
tools: [
|
|
27
|
+
tool("browser_navigate", "Navega para uma URL (absoluta ou relativa ao baseUrl do app).", { url: z.string().describe("Ex: /login ou https://...") }, async ({ url }) => {
|
|
28
|
+
try {
|
|
29
|
+
await session.navigate(url);
|
|
30
|
+
// Grava relativo quando a URL está sob o baseUrl — o script
|
|
31
|
+
// precisa sobreviver a SCOUT_BASE_URL apontando pra outro ambiente.
|
|
32
|
+
const base = config.baseUrl.replace(/\/+$/, "");
|
|
33
|
+
const recorded = url.startsWith(base) ? url.slice(base.length) || "/" : url;
|
|
34
|
+
record({ kind: "navigate", url: recorded });
|
|
35
|
+
return ok(await afterAction());
|
|
36
|
+
}
|
|
37
|
+
catch (e) {
|
|
38
|
+
return fail(e);
|
|
39
|
+
}
|
|
40
|
+
}),
|
|
41
|
+
tool("browser_snapshot", "Retorna o estado atual da página: URL, título, elementos interativos numerados [ref] e texto visível. Use sempre que precisar decidir a próxima ação.", {}, async () => {
|
|
42
|
+
try {
|
|
43
|
+
return ok(await afterAction());
|
|
44
|
+
}
|
|
45
|
+
catch (e) {
|
|
46
|
+
return fail(e);
|
|
47
|
+
}
|
|
48
|
+
}),
|
|
49
|
+
tool("browser_click", "Clica no elemento identificado pelo [ref] do último snapshot.", { ref: z.number().int().describe("Ref numérico do snapshot") }, async ({ ref }) => {
|
|
50
|
+
try {
|
|
51
|
+
const target = await session.click(ref);
|
|
52
|
+
record({ kind: "click", target });
|
|
53
|
+
return ok(`Cliquei em ${target.description}.\n\n${await afterAction()}`);
|
|
54
|
+
}
|
|
55
|
+
catch (e) {
|
|
56
|
+
return fail(e);
|
|
57
|
+
}
|
|
58
|
+
}),
|
|
59
|
+
tool("browser_fill", "Preenche um campo. Para credenciais/segredos use placeholder $ENV:VAR_NAME — o valor real vem do ambiente e nunca passa por você.", {
|
|
60
|
+
ref: z.number().int(),
|
|
61
|
+
value: z.string().describe("Texto literal ou $ENV:VAR_NAME"),
|
|
62
|
+
}, async ({ ref, value }) => {
|
|
63
|
+
try {
|
|
64
|
+
const target = await session.fill(ref, resolveEnvValue(value));
|
|
65
|
+
record({ kind: "fill", target, value });
|
|
66
|
+
return ok(`Preenchi ${target.description}.`);
|
|
67
|
+
}
|
|
68
|
+
catch (e) {
|
|
69
|
+
return fail(e);
|
|
70
|
+
}
|
|
71
|
+
}),
|
|
72
|
+
tool("browser_select", "Seleciona uma opção em um <select> pelo value ou label.", { ref: z.number().int(), value: z.string() }, async ({ ref, value }) => {
|
|
73
|
+
try {
|
|
74
|
+
const target = await session.select(ref, resolveEnvValue(value));
|
|
75
|
+
record({ kind: "select", target, value });
|
|
76
|
+
return ok(`Selecionei "${value}" em ${target.description}.\n\n${await afterAction()}`);
|
|
77
|
+
}
|
|
78
|
+
catch (e) {
|
|
79
|
+
return fail(e);
|
|
80
|
+
}
|
|
81
|
+
}),
|
|
82
|
+
tool("browser_press", "Pressiona uma tecla (Enter, Escape, Tab, ArrowDown...).", { key: z.string() }, async ({ key }) => {
|
|
83
|
+
try {
|
|
84
|
+
await session.press(key);
|
|
85
|
+
record({ kind: "press", key });
|
|
86
|
+
return ok(await afterAction());
|
|
87
|
+
}
|
|
88
|
+
catch (e) {
|
|
89
|
+
return fail(e);
|
|
90
|
+
}
|
|
91
|
+
}),
|
|
92
|
+
tool("browser_wait_for", "Espera texto aparecer na página OU a URL conter um trecho. Use após ações que disparam carregamento.", {
|
|
93
|
+
text: z.string().optional().describe("Texto que deve ficar visível"),
|
|
94
|
+
urlContains: z.string().optional().describe("Trecho esperado na URL"),
|
|
95
|
+
}, async ({ text, urlContains }) => {
|
|
96
|
+
try {
|
|
97
|
+
if (text) {
|
|
98
|
+
await session.waitForText(text);
|
|
99
|
+
record({ kind: "waitForText", text });
|
|
100
|
+
}
|
|
101
|
+
if (urlContains) {
|
|
102
|
+
await session.waitForUrl(urlContains);
|
|
103
|
+
record({ kind: "waitForUrl", pattern: urlContains });
|
|
104
|
+
}
|
|
105
|
+
return ok(await afterAction());
|
|
106
|
+
}
|
|
107
|
+
catch (e) {
|
|
108
|
+
return fail(e);
|
|
109
|
+
}
|
|
110
|
+
}),
|
|
111
|
+
tool("browser_assert", "Registra uma verificação do comportamento esperado. Use para CADA expectativa do cenário — essas asserções viram o teste determinístico.", {
|
|
112
|
+
visibleText: z.string().optional().describe("Texto que DEVE estar visível"),
|
|
113
|
+
notVisibleText: z.string().optional().describe("Texto que NÃO deve estar visível"),
|
|
114
|
+
urlContains: z.string().optional().describe("Trecho que a URL deve conter"),
|
|
115
|
+
}, async ({ visibleText, notVisibleText, urlContains }) => {
|
|
116
|
+
try {
|
|
117
|
+
if (visibleText) {
|
|
118
|
+
await session.assertVisible(visibleText);
|
|
119
|
+
record({ kind: "assertVisible", text: visibleText });
|
|
120
|
+
}
|
|
121
|
+
if (notVisibleText) {
|
|
122
|
+
await session.assertNotVisible(notVisibleText);
|
|
123
|
+
record({ kind: "assertNotVisible", text: notVisibleText });
|
|
124
|
+
}
|
|
125
|
+
if (urlContains) {
|
|
126
|
+
await session.assertUrl(urlContains);
|
|
127
|
+
record({ kind: "assertUrl", pattern: urlContains });
|
|
128
|
+
}
|
|
129
|
+
return ok("Asserção passou.");
|
|
130
|
+
}
|
|
131
|
+
catch (e) {
|
|
132
|
+
return fail(e);
|
|
133
|
+
}
|
|
134
|
+
}),
|
|
135
|
+
tool("browser_screenshot", "Captura screenshot como evidência. Use em momentos-chave (estado final, paywall, erro encontrado).", { label: z.string().describe("Rótulo curto, ex: 'paywall-exibido'") }, async ({ label }) => {
|
|
136
|
+
try {
|
|
137
|
+
await session.screenshot(label);
|
|
138
|
+
record({ kind: "screenshot", label });
|
|
139
|
+
return ok(`Screenshot "${label}" salvo.`);
|
|
140
|
+
}
|
|
141
|
+
catch (e) {
|
|
142
|
+
return fail(e);
|
|
143
|
+
}
|
|
144
|
+
}),
|
|
145
|
+
tool("scout_verdict", "OBRIGATÓRIO ao final: registra o veredito da verificação. Após chamar, encerre.", {
|
|
146
|
+
verdict: z.enum(["verified", "failed", "partial", "blocked"]),
|
|
147
|
+
reason: z.string().describe("Justificativa objetiva, citando o que foi observado"),
|
|
148
|
+
}, async (args) => {
|
|
149
|
+
verdict = args;
|
|
150
|
+
return ok("Veredito registrado. Encerre agora.");
|
|
151
|
+
}),
|
|
152
|
+
],
|
|
153
|
+
});
|
|
154
|
+
const profileInfo = scenario.profile
|
|
155
|
+
? `Sessão autenticada com o profile "${scenario.profile}"${config.profiles[scenario.profile]?.description ? ` (${config.profiles[scenario.profile].description})` : ""}. Você JÁ está logado — não faça login de novo a menos que o cenário peça.`
|
|
156
|
+
: "Sessão anônima (logged-out).";
|
|
157
|
+
const envVars = scenario.profile ? (config.profiles[scenario.profile]?.env ?? []) : [];
|
|
158
|
+
const envInfo = envVars.length
|
|
159
|
+
? `Env vars disponíveis para browser_fill via placeholder: ${envVars.map((v) => `$ENV:${v}`).join(", ")}.`
|
|
160
|
+
: "";
|
|
161
|
+
const systemPrompt = `Você é Scout, um agente de QA que verifica cenários em um browser real.
|
|
162
|
+
|
|
163
|
+
App alvo: ${config.baseUrl}
|
|
164
|
+
${profileInfo}
|
|
165
|
+
${envInfo}
|
|
166
|
+
|
|
167
|
+
Método de trabalho:
|
|
168
|
+
1. Comece com browser_navigate para a página inicial do fluxo (ou browser_snapshot se já estiver lá).
|
|
169
|
+
2. Execute o fluxo descrito no cenário, passo a passo, sempre lendo o snapshot antes de agir.
|
|
170
|
+
3. Para CADA expectativa do cenário, use browser_assert — as asserções gravadas viram o teste determinístico que rodará em CI sem você.
|
|
171
|
+
4. Capture browser_screenshot como evidência nos momentos-chave.
|
|
172
|
+
5. Termine SEMPRE com scout_verdict:
|
|
173
|
+
- verified: todo o comportamento esperado foi confirmado por asserções
|
|
174
|
+
- failed: comportamento esperado está quebrado (descreva exatamente o quê)
|
|
175
|
+
- partial: parte funciona, parte não, ou não foi possível verificar tudo
|
|
176
|
+
- blocked: não conseguiu nem chegar ao fluxo (app fora do ar, login quebrado, etc.)
|
|
177
|
+
|
|
178
|
+
Regras:
|
|
179
|
+
- Aja como um usuário real: um passo de cada vez, espere carregamentos com browser_wait_for.
|
|
180
|
+
- Se um elemento não está no snapshot, tire novo snapshot ou role o fluxo de outro jeito — não invente refs.
|
|
181
|
+
- Nunca digite segredos literais: use $ENV:VAR_NAME.
|
|
182
|
+
- Seja econômico: não explore além do cenário.`;
|
|
183
|
+
const result = query({
|
|
184
|
+
prompt: `Verifique este cenário de QA:\n\n## ${scenario.name}\n\n${scenario.scenario}${scenario.notes ? `\n\nNotas: ${scenario.notes}` : ""}`,
|
|
185
|
+
options: {
|
|
186
|
+
model: config.model,
|
|
187
|
+
maxTurns: config.maxTurns,
|
|
188
|
+
systemPrompt,
|
|
189
|
+
mcpServers: { browser: browserServer },
|
|
190
|
+
allowedTools: ["mcp__browser__*"],
|
|
191
|
+
tools: [],
|
|
192
|
+
settingSources: [],
|
|
193
|
+
permissionMode: "bypassPermissions",
|
|
194
|
+
},
|
|
195
|
+
});
|
|
196
|
+
for await (const message of result) {
|
|
197
|
+
if (message.type === "assistant") {
|
|
198
|
+
for (const block of message.message.content) {
|
|
199
|
+
if (block.type === "text" && block.text.trim())
|
|
200
|
+
transcript.push(block.text.trim());
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
if (message.type === "result")
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
if (!verdict) {
|
|
207
|
+
verdict = {
|
|
208
|
+
verdict: "blocked",
|
|
209
|
+
reason: "Agente encerrou sem registrar veredito (scout_verdict não foi chamado).",
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
return { ...verdict, steps, transcript };
|
|
213
|
+
}
|
|
214
|
+
//# sourceMappingURL=ai-runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-runner.js","sourceRoot":"","sources":["../../src/runner/ai-runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { type Page } from "playwright";
|
|
2
|
+
import type { Step, Target } from "../types.js";
|
|
3
|
+
export interface ElementInfo {
|
|
4
|
+
ref: number;
|
|
5
|
+
role: string;
|
|
6
|
+
name: string;
|
|
7
|
+
css: string;
|
|
8
|
+
tag: string;
|
|
9
|
+
value?: string;
|
|
10
|
+
href?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface PageSnapshot {
|
|
13
|
+
url: string;
|
|
14
|
+
title: string;
|
|
15
|
+
text: string;
|
|
16
|
+
elements: ElementInfo[];
|
|
17
|
+
}
|
|
18
|
+
export interface LaunchOptions {
|
|
19
|
+
baseUrl: string;
|
|
20
|
+
headless: boolean;
|
|
21
|
+
storageState?: string;
|
|
22
|
+
locale?: string;
|
|
23
|
+
runDir: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Thin Playwright wrapper shared by the AI runner and the replay runner.
|
|
27
|
+
* Owns ref→element resolution, tracing and screenshots.
|
|
28
|
+
*/
|
|
29
|
+
export declare class BrowserSession {
|
|
30
|
+
private browser;
|
|
31
|
+
private context;
|
|
32
|
+
readonly page: Page;
|
|
33
|
+
private opts;
|
|
34
|
+
private refs;
|
|
35
|
+
private screenshotCount;
|
|
36
|
+
readonly screenshots: string[];
|
|
37
|
+
private constructor();
|
|
38
|
+
static launch(opts: LaunchOptions): Promise<BrowserSession>;
|
|
39
|
+
resolveUrl(url: string): string;
|
|
40
|
+
navigate(url: string): Promise<void>;
|
|
41
|
+
/**
|
|
42
|
+
* Builds a numbered map of visible interactive elements (role + accessible
|
|
43
|
+
* name + CSS path) plus a text excerpt. Refs are valid until the next snapshot.
|
|
44
|
+
*/
|
|
45
|
+
snapshot(): Promise<PageSnapshot>;
|
|
46
|
+
formatSnapshot(snap: PageSnapshot): string;
|
|
47
|
+
/**
|
|
48
|
+
* Resolves a snapshot ref to a locator AND the durable Target recorded for
|
|
49
|
+
* replay: role+name when unique on the page, CSS path otherwise.
|
|
50
|
+
*/
|
|
51
|
+
private resolveRef;
|
|
52
|
+
click(ref: number): Promise<Target>;
|
|
53
|
+
fill(ref: number, value: string): Promise<Target>;
|
|
54
|
+
select(ref: number, value: string): Promise<Target>;
|
|
55
|
+
press(key: string): Promise<void>;
|
|
56
|
+
waitForText(text: string): Promise<void>;
|
|
57
|
+
waitForUrl(pattern: string): Promise<void>;
|
|
58
|
+
assertVisible(text: string): Promise<void>;
|
|
59
|
+
assertNotVisible(text: string): Promise<void>;
|
|
60
|
+
assertUrl(pattern: string): Promise<void>;
|
|
61
|
+
screenshot(label: string): Promise<string>;
|
|
62
|
+
/** Replays one recorded step (used by the deterministic runner). */
|
|
63
|
+
executeStep(step: Step): Promise<void>;
|
|
64
|
+
private targetLocator;
|
|
65
|
+
close(): Promise<string | undefined>;
|
|
66
|
+
}
|
|
67
|
+
/** Replaces $ENV:VAR_NAME placeholders so secrets never live in committed scripts. */
|
|
68
|
+
export declare function resolveEnvValue(value: string): string;
|