crawlix 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 +187 -0
- package/dist/adapters/base.d.ts +8 -0
- package/dist/adapters/base.d.ts.map +1 -0
- package/dist/adapters/base.js +2 -0
- package/dist/adapters/base.js.map +1 -0
- package/dist/adapters/web.d.ts +14 -0
- package/dist/adapters/web.d.ts.map +1 -0
- package/dist/adapters/web.js +120 -0
- package/dist/adapters/web.js.map +1 -0
- package/dist/cli/commands/agents.d.ts +3 -0
- package/dist/cli/commands/agents.d.ts.map +1 -0
- package/dist/cli/commands/agents.js +9 -0
- package/dist/cli/commands/agents.js.map +1 -0
- package/dist/cli/commands/run.d.ts +3 -0
- package/dist/cli/commands/run.d.ts.map +1 -0
- package/dist/cli/commands/run.js +49 -0
- package/dist/cli/commands/run.js.map +1 -0
- package/dist/cli/commands/setup.d.ts +3 -0
- package/dist/cli/commands/setup.d.ts.map +1 -0
- package/dist/cli/commands/setup.js +93 -0
- package/dist/cli/commands/setup.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +17 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/core/director.d.ts +14 -0
- package/dist/core/director.d.ts.map +1 -0
- package/dist/core/director.js +112 -0
- package/dist/core/director.js.map +1 -0
- package/dist/core/extractor.d.ts +6 -0
- package/dist/core/extractor.d.ts.map +1 -0
- package/dist/core/extractor.js +42 -0
- package/dist/core/extractor.js.map +1 -0
- package/dist/core/orchestrator.d.ts +27 -0
- package/dist/core/orchestrator.d.ts.map +1 -0
- package/dist/core/orchestrator.js +62 -0
- package/dist/core/orchestrator.js.map +1 -0
- package/dist/core/reporter.d.ts +8 -0
- package/dist/core/reporter.d.ts.map +1 -0
- package/dist/core/reporter.js +132 -0
- package/dist/core/reporter.js.map +1 -0
- package/dist/core/runner.d.ts +13 -0
- package/dist/core/runner.d.ts.map +1 -0
- package/dist/core/runner.js +61 -0
- package/dist/core/runner.js.map +1 -0
- package/dist/lib/browser.d.ts +4 -0
- package/dist/lib/browser.d.ts.map +1 -0
- package/dist/lib/browser.js +24 -0
- package/dist/lib/browser.js.map +1 -0
- package/dist/lib/display.d.ts +7 -0
- package/dist/lib/display.d.ts.map +1 -0
- package/dist/lib/display.js +80 -0
- package/dist/lib/display.js.map +1 -0
- package/dist/lib/getConfigs.d.ts +3 -0
- package/dist/lib/getConfigs.d.ts.map +1 -0
- package/dist/lib/getConfigs.js +12 -0
- package/dist/lib/getConfigs.js.map +1 -0
- package/dist/llm/index.d.ts +15 -0
- package/dist/llm/index.d.ts.map +1 -0
- package/dist/llm/index.js +147 -0
- package/dist/llm/index.js.map +1 -0
- package/dist/personas/index.d.ts +5 -0
- package/dist/personas/index.d.ts.map +1 -0
- package/dist/personas/index.js +100 -0
- package/dist/personas/index.js.map +1 -0
- package/dist/personas/loader.d.ts +4 -0
- package/dist/personas/loader.d.ts.map +1 -0
- package/dist/personas/loader.js +72 -0
- package/dist/personas/loader.js.map +1 -0
- package/dist/types/index.d.ts +76 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +57 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { LLM } from "../llm/index.js";
|
|
2
|
+
export class Director {
|
|
3
|
+
persona;
|
|
4
|
+
llm;
|
|
5
|
+
goal;
|
|
6
|
+
constructor(persona, llm, goal) {
|
|
7
|
+
this.persona = persona;
|
|
8
|
+
this.llm = llm;
|
|
9
|
+
this.goal = goal;
|
|
10
|
+
}
|
|
11
|
+
get personaName() {
|
|
12
|
+
return this.persona.name;
|
|
13
|
+
}
|
|
14
|
+
// build system prompt
|
|
15
|
+
buildSystemPrompt() {
|
|
16
|
+
return `You are an autonomous QA agent embodying the following user persona.
|
|
17
|
+
|
|
18
|
+
PERSONA: ${this.persona.name}
|
|
19
|
+
DESCRIPTION: ${this.persona.description}
|
|
20
|
+
GOAL: ${this.goal}
|
|
21
|
+
|
|
22
|
+
BEHAVIOR:
|
|
23
|
+
${this.persona.systemPrompt}
|
|
24
|
+
|
|
25
|
+
BEHAVIORAL MODIFIERS:
|
|
26
|
+
- Patience level: ${this.persona.patience}/10 — ${this.persona.patience <= 3 ? 'give up quickly if stuck' : this.persona.patience >= 8 ? 'persist through difficulties' : 'try a few times before giving up'}
|
|
27
|
+
- Aggression level: ${this.persona.aggression}/10 — ${this.persona.aggression >= 8 ? 'actively try to break things' : this.persona.aggression <= 2 ? 'interact gently and carefully' : 'interact normally'}
|
|
28
|
+
- Reading behavior: ${this.persona.readingBehavior === 'thorough' ? 'read everything on the page before acting' : this.persona.readingBehavior === 'skim' ? 'skim text quickly, catch main points only' : 'skip all text, act on visual cues only'}
|
|
29
|
+
|
|
30
|
+
RESPONSE RULES:
|
|
31
|
+
"target": "use ONLY the visible label text of the element, nothing else.
|
|
32
|
+
Not the index number, not the role. Just the text.
|
|
33
|
+
Example: 'Sign Up' not '[01] button Sign Up'"
|
|
34
|
+
You must always respond with valid JSON only. No explanation outside JSON.
|
|
35
|
+
|
|
36
|
+
{
|
|
37
|
+
"type": "open" | "click" | "type" | "scroll" | "select" | "hover" | "press" | "wait" | "done" | "stuck",
|
|
38
|
+
"target": "element description",
|
|
39
|
+
"value": "text to type / key to press / scroll direction / url to open",
|
|
40
|
+
"reasoning": "one sentence why",
|
|
41
|
+
"finding": {
|
|
42
|
+
"severity": "critical" | "warning" | "info",
|
|
43
|
+
"description": "what you observed",
|
|
44
|
+
"element": "which element"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
Set "finding" to null if nothing notable this step.
|
|
49
|
+
Set "type" to "done" only when goal is fully complete.
|
|
50
|
+
Set "type" to "stuck" only after ${this.persona.patience <= 3 ? '2' : this.persona.patience <= 6 ? '3' : '4'} failed attempts on the same step.`;
|
|
51
|
+
}
|
|
52
|
+
// build user message with history and page Elementa
|
|
53
|
+
buildUserMessage(pageState, history) {
|
|
54
|
+
const header = [
|
|
55
|
+
pageState.url ? `Current URL: ${pageState.url}` : null,
|
|
56
|
+
pageState.title ? `Current Title: ${pageState.title}` : null,
|
|
57
|
+
].filter(Boolean).join('\n');
|
|
58
|
+
// build history text to pass to the LLM in user message
|
|
59
|
+
const historyText = history.length === 0
|
|
60
|
+
? 'No actions yet — this is your first step.'
|
|
61
|
+
: history.slice(-10).map(e => `[${String(e.step).padStart(2, '0')}] ${e.action.type.toUpperCase().padEnd(8)} ` +
|
|
62
|
+
`${e.action.target ?? '—'} ` +
|
|
63
|
+
`${e.action.value ? `"${e.action.value}"` : ''} ` +
|
|
64
|
+
`→ ${e.action.reasoning}` +
|
|
65
|
+
(e.action.finding ? ` ⚠ ${e.action.finding.severity.toUpperCase()}: ${e.action.finding.description}` : '')).join('\n');
|
|
66
|
+
return [
|
|
67
|
+
header,
|
|
68
|
+
'',
|
|
69
|
+
'── PAGE ELEMENTS ──────────────────────────',
|
|
70
|
+
pageState.tree,
|
|
71
|
+
'',
|
|
72
|
+
'── HISTORY ────────────────────────────────',
|
|
73
|
+
historyText,
|
|
74
|
+
'',
|
|
75
|
+
'What is your next action?',
|
|
76
|
+
].join('\n');
|
|
77
|
+
}
|
|
78
|
+
// parse LLM response for next action
|
|
79
|
+
parseAction(raw) {
|
|
80
|
+
const cleaned = raw
|
|
81
|
+
.replace(/^```(?:json)?\n?/m, '')
|
|
82
|
+
.replace(/\n?```$/m, '')
|
|
83
|
+
.trim();
|
|
84
|
+
let parsed;
|
|
85
|
+
try {
|
|
86
|
+
parsed = JSON.parse(cleaned);
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
throw new Error(`Crawlix: received non-JSON response:\n${raw}`);
|
|
90
|
+
}
|
|
91
|
+
if (Array.isArray(parsed)) {
|
|
92
|
+
parsed = parsed[0];
|
|
93
|
+
}
|
|
94
|
+
if (!parsed['type'] || typeof parsed['type'] !== 'string') {
|
|
95
|
+
throw new Error(`Crawlix: missing "type" in response:\n${raw}`);
|
|
96
|
+
}
|
|
97
|
+
if (!parsed['reasoning'] || typeof parsed['reasoning'] !== 'string') {
|
|
98
|
+
throw new Error(`Crawlix: missing "reasoning" in response:\n${raw}`);
|
|
99
|
+
}
|
|
100
|
+
return parsed;
|
|
101
|
+
}
|
|
102
|
+
// call LLM for Next Action
|
|
103
|
+
async decide(pageState, history) {
|
|
104
|
+
const input = {
|
|
105
|
+
system: this.buildSystemPrompt(),
|
|
106
|
+
messages: [{ role: 'user', content: this.buildUserMessage(pageState, history) }]
|
|
107
|
+
};
|
|
108
|
+
const response = await this.llm.complete(input);
|
|
109
|
+
return this.parseAction(response.content);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=director.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"director.js","sourceRoot":"","sources":["../../src/core/director.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extractor.d.ts","sourceRoot":"","sources":["../../src/core/extractor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AACtC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAElD,qBAAa,SAAS;IACd,OAAO,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,SAAS,CAAC;CA6C9C"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export class Extractor {
|
|
2
|
+
async extract(page) {
|
|
3
|
+
const url = page.url();
|
|
4
|
+
const title = await page.title().catch(() => undefined);
|
|
5
|
+
// runs inside the browser - queries DOM directly
|
|
6
|
+
const tree = await page.evaluate(() => {
|
|
7
|
+
const selector = [
|
|
8
|
+
'button', 'a[href]', 'input', 'select', 'textarea',
|
|
9
|
+
'[role="button"]', '[role="link"]', '[role="tab"]',
|
|
10
|
+
'[role="menuitem"]', '[role="checkbox"]', '[role="radio"]',
|
|
11
|
+
'[tabindex]:not([tabindex="-1"])',
|
|
12
|
+
].join(', ');
|
|
13
|
+
return Array.from(document.querySelectorAll(selector))
|
|
14
|
+
.filter(el => {
|
|
15
|
+
// visible only
|
|
16
|
+
const rect = el.getBoundingClientRect();
|
|
17
|
+
return rect.width > 0 && rect.height > 0;
|
|
18
|
+
})
|
|
19
|
+
.slice(0, 60) // cap at 60
|
|
20
|
+
.map((el, i) => {
|
|
21
|
+
const tag = el.tagName.toLowerCase();
|
|
22
|
+
const role = el.getAttribute('role') ?? (tag === 'a' ? 'link' : tag === 'button' ? 'button' : tag);
|
|
23
|
+
const label = (el.getAttribute('aria-label') ??
|
|
24
|
+
el.getAttribute('placeholder') ??
|
|
25
|
+
el.getAttribute('title') ??
|
|
26
|
+
el.textContent?.trim().slice(0, 60) ??
|
|
27
|
+
'').trim();
|
|
28
|
+
const type = el.getAttribute('type') ?? '';
|
|
29
|
+
const name = el.getAttribute('name') ?? '';
|
|
30
|
+
return `[${String(i + 1).padStart(2, '0')}] ${role}: "${label}"${type ? ` (${type})` : ''}${name ? ` name="${name}"` : ''}`;
|
|
31
|
+
})
|
|
32
|
+
.join('\n');
|
|
33
|
+
});
|
|
34
|
+
return {
|
|
35
|
+
url,
|
|
36
|
+
title,
|
|
37
|
+
tree: tree || '(no interactive elements found)',
|
|
38
|
+
timestamp: Date.now(),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=extractor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extractor.js","sourceRoot":"","sources":["../../src/core/extractor.ts"],"names":[],"mappings":"AAGA,MAAM,OAAO,SAAS;IACpB,KAAK,CAAC,OAAO,CAAC,IAAU;QACtB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QACtB,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;QAEvD,iDAAiD;QACjD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE;YACpC,MAAM,QAAQ,GAAG;gBACf,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU;gBAClD,iBAAiB,EAAE,eAAe,EAAE,cAAc;gBAClD,mBAAmB,EAAE,mBAAmB,EAAE,gBAAgB;gBAC1D,iCAAiC;aAClC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YAEZ,OAAO,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;iBACnD,MAAM,CAAC,EAAE,CAAC,EAAE;gBACX,eAAe;gBACf,MAAM,IAAI,GAAG,EAAE,CAAC,qBAAqB,EAAE,CAAA;gBACvC,OAAO,IAAI,CAAC,KAAK,GAAG,CAAC,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,CAAA;YAC1C,CAAC,CAAC;iBACD,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAE,YAAY;iBAC1B,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE;gBACb,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,WAAW,EAAE,CAAA;gBACpC,MAAM,IAAI,GAAG,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;gBAClG,MAAM,KAAK,GAAG,CACZ,EAAE,CAAC,YAAY,CAAC,YAAY,CAAC;oBAC7B,EAAE,CAAC,YAAY,CAAC,aAAa,CAAC;oBAC9B,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC;oBACxB,EAAE,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;oBACnC,EAAE,CACH,CAAC,IAAI,EAAE,CAAA;gBACR,MAAM,IAAI,GAAG,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,EAAE,CAAA;gBAC1C,MAAM,IAAI,GAAG,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,EAAE,CAAA;gBAE1C,OAAO,IAAI,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,KAAK,IAAI,MAAM,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,UAAU,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAA;YAC7H,CAAC,CAAC;iBACD,IAAI,CAAC,IAAI,CAAC,CAAA;QACf,CAAC,CAAC,CAAA;QAEF,OAAO;YACL,GAAG;YACH,KAAK;YACL,IAAI,EAAE,IAAI,IAAI,iCAAiC;YAC/C,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;SACtB,CAAA;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { LLM } from "../llm/index.js";
|
|
2
|
+
import type { PersonaConfig, RunResult } from "../types/index.js";
|
|
3
|
+
export interface OrchestratorConfig {
|
|
4
|
+
url: string;
|
|
5
|
+
goal: string;
|
|
6
|
+
llm: LLM;
|
|
7
|
+
personas: PersonaConfig[];
|
|
8
|
+
maxSteps?: number;
|
|
9
|
+
concurrency?: number;
|
|
10
|
+
headless?: boolean;
|
|
11
|
+
onAgentStart?: (personaName: string) => void;
|
|
12
|
+
onAgentDone?: (result: RunResult) => void;
|
|
13
|
+
}
|
|
14
|
+
export declare class Orchestrator {
|
|
15
|
+
private url;
|
|
16
|
+
private goal;
|
|
17
|
+
private llm;
|
|
18
|
+
private personas;
|
|
19
|
+
private maxSteps;
|
|
20
|
+
private concurrency;
|
|
21
|
+
private headless;
|
|
22
|
+
private onAgentStart?;
|
|
23
|
+
private onAgentDone?;
|
|
24
|
+
constructor(config: OrchestratorConfig);
|
|
25
|
+
run(): Promise<RunResult[]>;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=orchestrator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../../src/core/orchestrator.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAElE,MAAM,WAAW,kBAAkB;IAC/B,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,GAAG,CAAA;IACR,QAAQ,EAAE,aAAa,EAAE,CAAA;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,YAAY,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAA;IAC5C,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,KAAK,IAAI,CAAA;CAC5C;AAED,qBAAa,YAAY;IACrB,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,GAAG,CAAM;IACjB,OAAO,CAAC,QAAQ,CAAkB;IAClC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,QAAQ,CAAU;IAC1B,OAAO,CAAC,YAAY,CAAC,CAA8C;IACnE,OAAO,CAAC,WAAW,CAAC,CAA4C;gBAEpD,MAAM,EAAE,kBAAkB;IAYhC,GAAG,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;CAwCpC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { getBrowser, closeBrowser } from "../lib/browser.js";
|
|
2
|
+
import { Director } from "./director.js";
|
|
3
|
+
import { Runner } from "./runner.js";
|
|
4
|
+
import { WebAdapter } from "../adapters/web.js";
|
|
5
|
+
export class Orchestrator {
|
|
6
|
+
url;
|
|
7
|
+
goal;
|
|
8
|
+
llm;
|
|
9
|
+
personas;
|
|
10
|
+
maxSteps;
|
|
11
|
+
concurrency;
|
|
12
|
+
headless;
|
|
13
|
+
onAgentStart;
|
|
14
|
+
onAgentDone;
|
|
15
|
+
constructor(config) {
|
|
16
|
+
this.url = config.url;
|
|
17
|
+
this.goal = config.goal;
|
|
18
|
+
this.llm = config.llm;
|
|
19
|
+
this.personas = config.personas;
|
|
20
|
+
this.maxSteps = config.maxSteps || 10;
|
|
21
|
+
this.concurrency = config.concurrency || 2;
|
|
22
|
+
this.headless = config.headless ?? true;
|
|
23
|
+
this.onAgentStart = config.onAgentStart;
|
|
24
|
+
this.onAgentDone = config.onAgentDone;
|
|
25
|
+
}
|
|
26
|
+
async run() {
|
|
27
|
+
const browser = await getBrowser(this.headless);
|
|
28
|
+
const allResults = [];
|
|
29
|
+
try {
|
|
30
|
+
for (let i = 0; i < this.personas.length; i += this.concurrency) {
|
|
31
|
+
const batch = this.personas.slice(i, i + this.concurrency);
|
|
32
|
+
const results = await Promise.all(batch.map(async (persona) => {
|
|
33
|
+
this.onAgentStart?.(persona.name);
|
|
34
|
+
const context = await browser.newContext();
|
|
35
|
+
const page = await context.newPage();
|
|
36
|
+
try {
|
|
37
|
+
const adapter = new WebAdapter(page);
|
|
38
|
+
const director = new Director(persona, this.llm, this.goal);
|
|
39
|
+
const runner = new Runner(adapter, director, this.url, this.goal, this.maxSteps);
|
|
40
|
+
const result = await runner.run();
|
|
41
|
+
this.onAgentDone?.(result);
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
finally {
|
|
45
|
+
await page.close();
|
|
46
|
+
await context.close();
|
|
47
|
+
}
|
|
48
|
+
}));
|
|
49
|
+
allResults.push(...results);
|
|
50
|
+
}
|
|
51
|
+
return allResults;
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
console.error(`Error during orchestration: ${err}`);
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
finally {
|
|
58
|
+
await closeBrowser();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=orchestrator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orchestrator.js","sourceRoot":"","sources":["../../src/core/orchestrator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { RunResult } from '../types/index.js';
|
|
2
|
+
import { LLM } from '../llm/index.js';
|
|
3
|
+
export declare function generateReport(results: RunResult[], url: string, goal: string, llm: LLM): Promise<{
|
|
4
|
+
title: string;
|
|
5
|
+
report: string;
|
|
6
|
+
}>;
|
|
7
|
+
export declare function saveReport(title: string, content: string): string;
|
|
8
|
+
//# sourceMappingURL=reporter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reporter.d.ts","sourceRoot":"","sources":["../../src/core/reporter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAY,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAC5D,OAAO,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAA;AA+GrC,wBAAsB,cAAc,CAChC,OAAO,EAAE,SAAS,EAAE,EACpB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,GAAG,GACT,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAa5C;AAGD,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAajE"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { LLM } from '../llm/index.js';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
const REPORT_SYSTEM_PROMPT = `You are a senior QA analyst reviewing results from an autonomous AI testing session.
|
|
5
|
+
|
|
6
|
+
Your job is to turn raw test findings into a clear, actionable markdown report that a developer can read and immediately understand what to fix.
|
|
7
|
+
|
|
8
|
+
REPORT STRUCTURE — follow this exactly:
|
|
9
|
+
|
|
10
|
+
# Crawlix Report
|
|
11
|
+
|
|
12
|
+
## Overview
|
|
13
|
+
- URL tested
|
|
14
|
+
- Goal
|
|
15
|
+
- Agents run
|
|
16
|
+
- Total findings
|
|
17
|
+
- Time taken
|
|
18
|
+
|
|
19
|
+
## Critical Issues
|
|
20
|
+
List every critical finding. For each one:
|
|
21
|
+
- What broke
|
|
22
|
+
- Which agent found it
|
|
23
|
+
- Why it matters
|
|
24
|
+
- Suggested fix
|
|
25
|
+
|
|
26
|
+
## Warnings
|
|
27
|
+
Group similar warnings together. For each group:
|
|
28
|
+
- What the pattern is
|
|
29
|
+
- How many agents hit it
|
|
30
|
+
- Suggested fix
|
|
31
|
+
|
|
32
|
+
## Patterns
|
|
33
|
+
Findings that multiple agents experienced — these are the most important UX problems because real users of different types all hit them.
|
|
34
|
+
|
|
35
|
+
## Agent Performance
|
|
36
|
+
One line per agent — goal reached, steps taken, findings count, notable observations.
|
|
37
|
+
|
|
38
|
+
## Recommendations
|
|
39
|
+
Top 3-5 things to fix first, ordered by impact.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
RULES:
|
|
44
|
+
- Be specific — name the element, describe the exact problem
|
|
45
|
+
- Be actionable — every finding should have a suggested fix
|
|
46
|
+
- Be concise — no filler, no generic advice
|
|
47
|
+
- Use proper markdown — headers, bullet points, code blocks where relevant
|
|
48
|
+
- If no critical issues found — say so clearly
|
|
49
|
+
- Write for a developer who will act on this immediately
|
|
50
|
+
|
|
51
|
+
RESPONSE RULES:
|
|
52
|
+
You must always respond with valid JSON only. No explanation outside JSON.
|
|
53
|
+
|
|
54
|
+
{
|
|
55
|
+
"title": "Short report title for report filename",
|
|
56
|
+
"report": "Complete markdown report content following the structure and rules above",
|
|
57
|
+
}
|
|
58
|
+
}`;
|
|
59
|
+
// formats all results into structured text for the LLM
|
|
60
|
+
function buildReportPrompt(results, url, goal) {
|
|
61
|
+
const totalFindings = results.flatMap(r => r.findings).length;
|
|
62
|
+
const totalDuration = results.reduce((acc, r) => acc + r.duration, 0);
|
|
63
|
+
// summary context as first message
|
|
64
|
+
const summary = {
|
|
65
|
+
role: 'user',
|
|
66
|
+
content: [
|
|
67
|
+
`URL: ${url}`,
|
|
68
|
+
`Goal: ${goal}`,
|
|
69
|
+
`Agents run: ${results.length}`,
|
|
70
|
+
`Total findings: ${totalFindings}`,
|
|
71
|
+
`Total duration: ${(totalDuration / 1000).toFixed(1)}s`,
|
|
72
|
+
].join('\n')
|
|
73
|
+
};
|
|
74
|
+
// one message per agent
|
|
75
|
+
const agentMessages = results.map(result => {
|
|
76
|
+
const findingsText = result.findings.length === 0
|
|
77
|
+
? 'No findings.'
|
|
78
|
+
: result.findings
|
|
79
|
+
.map(f => `- [${f.severity}] ${f.description}${f.element ? ` — element: ${f.element}` : ''}`)
|
|
80
|
+
.join('\n');
|
|
81
|
+
return {
|
|
82
|
+
role: 'user',
|
|
83
|
+
content: [
|
|
84
|
+
`Agent: ${result.persona}`,
|
|
85
|
+
`Goal reached: ${result.goalReached}`,
|
|
86
|
+
`Stuck: ${result.stuck}`,
|
|
87
|
+
`Steps taken: ${result.steps}`,
|
|
88
|
+
`Duration: ${(result.duration / 1000).toFixed(1)}s`,
|
|
89
|
+
`Findings:\n${findingsText}`,
|
|
90
|
+
].join('\n')
|
|
91
|
+
};
|
|
92
|
+
});
|
|
93
|
+
// final instruction
|
|
94
|
+
const instruction = {
|
|
95
|
+
role: 'user',
|
|
96
|
+
content: 'Generate the full QA report based on the above results.'
|
|
97
|
+
};
|
|
98
|
+
return {
|
|
99
|
+
system: REPORT_SYSTEM_PROMPT,
|
|
100
|
+
messages: [summary, ...agentMessages, instruction]
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
// generates the report using LLM
|
|
104
|
+
export async function generateReport(results, url, goal, llm) {
|
|
105
|
+
const prompt = buildReportPrompt(results, url, goal);
|
|
106
|
+
const output = await llm.complete(prompt);
|
|
107
|
+
const cleaned = output.content
|
|
108
|
+
.replace(/^```(?:json)?\n?/m, '')
|
|
109
|
+
.replace(/\n?```$/m, '')
|
|
110
|
+
.trim();
|
|
111
|
+
try {
|
|
112
|
+
const parsed = JSON.parse(cleaned);
|
|
113
|
+
return parsed;
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
return { title: 'crawlix-report', report: output.content };
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// saves report to ./crawlix-reports/
|
|
120
|
+
export function saveReport(title, content) {
|
|
121
|
+
const reportsDir = path.join(process.cwd(), 'crawlix-reports');
|
|
122
|
+
if (!fs.existsSync(reportsDir)) {
|
|
123
|
+
fs.mkdirSync(reportsDir, { recursive: true });
|
|
124
|
+
}
|
|
125
|
+
const safeTitle = title.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9\-]/g, '');
|
|
126
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
127
|
+
const filename = `report-${safeTitle}-${timestamp}.md`;
|
|
128
|
+
const filepath = path.join(reportsDir, filename);
|
|
129
|
+
fs.writeFileSync(filepath, content);
|
|
130
|
+
return filepath;
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=reporter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reporter.js","sourceRoot":"","sources":["../../src/core/reporter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Adapter } from "../adapters/base.js";
|
|
2
|
+
import type { RunResult } from "../types/index.js";
|
|
3
|
+
import type { Director } from "./director.js";
|
|
4
|
+
export declare class Runner {
|
|
5
|
+
private adapter;
|
|
6
|
+
private director;
|
|
7
|
+
private url;
|
|
8
|
+
private goal;
|
|
9
|
+
private maxSteps;
|
|
10
|
+
constructor(adapter: Adapter, director: Director, url: string, goal: string, maxSteps?: number);
|
|
11
|
+
run(): Promise<RunResult>;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/core/runner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,KAAK,EAAyB,SAAS,EAAE,MAAM,mBAAmB,CAAA;AACzE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAE7C,qBAAa,MAAM;IACjB,OAAO,CAAC,OAAO,CAAW;IAC1B,OAAO,CAAC,QAAQ,CAAW;IAC3B,OAAO,CAAC,GAAG,CAAc;IACzB,OAAO,CAAC,IAAI,CAAa;IACzB,OAAO,CAAC,QAAQ,CAAS;gBAEb,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,SAAK;IAQpF,GAAG,IAAI,OAAO,CAAC,SAAS,CAAC;CAuDhC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export class Runner {
|
|
2
|
+
adapter;
|
|
3
|
+
director;
|
|
4
|
+
url;
|
|
5
|
+
goal;
|
|
6
|
+
maxSteps;
|
|
7
|
+
constructor(adapter, director, url, goal, maxSteps = 25) {
|
|
8
|
+
this.adapter = adapter;
|
|
9
|
+
this.director = director;
|
|
10
|
+
this.url = url;
|
|
11
|
+
this.goal = goal;
|
|
12
|
+
this.maxSteps = maxSteps;
|
|
13
|
+
}
|
|
14
|
+
async run() {
|
|
15
|
+
// 1. open the url
|
|
16
|
+
await this.adapter.open(this.url);
|
|
17
|
+
const history = [];
|
|
18
|
+
const findings = [];
|
|
19
|
+
const startedAt = Date.now();
|
|
20
|
+
// 2. loop
|
|
21
|
+
for (let step = 1; step <= this.maxSteps; step++) {
|
|
22
|
+
// get current state
|
|
23
|
+
const pageState = await this.adapter.getState();
|
|
24
|
+
// ask director what to do
|
|
25
|
+
const action = await this.director.decide(pageState, history);
|
|
26
|
+
// record finding if any
|
|
27
|
+
if (action.finding)
|
|
28
|
+
findings.push({ ...action.finding, step });
|
|
29
|
+
// stop if done or stuck
|
|
30
|
+
if (action.type === 'done' || action.type === 'stuck')
|
|
31
|
+
break;
|
|
32
|
+
// execute the action
|
|
33
|
+
const result = await this.adapter.execute(action);
|
|
34
|
+
// record in history
|
|
35
|
+
history.push({ step, pageState, action });
|
|
36
|
+
// if action failed record it
|
|
37
|
+
if (!result.success) {
|
|
38
|
+
findings.push({
|
|
39
|
+
severity: 'warning',
|
|
40
|
+
description: `Action failed: ${result.error}`,
|
|
41
|
+
element: action.target ?? undefined,
|
|
42
|
+
step,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// 3. close and return
|
|
47
|
+
await this.adapter.close();
|
|
48
|
+
return {
|
|
49
|
+
persona: this.director.personaName,
|
|
50
|
+
url: this.url,
|
|
51
|
+
goal: this.goal,
|
|
52
|
+
steps: history.length,
|
|
53
|
+
findings,
|
|
54
|
+
goalReached: history.at(-1)?.action.type === 'done',
|
|
55
|
+
stuck: history.at(-1)?.action.type === 'stuck',
|
|
56
|
+
duration: Date.now() - startedAt,
|
|
57
|
+
history,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../../src/core/runner.ts"],"names":[],"mappings":"AAIA,MAAM,OAAO,MAAM;IACT,OAAO,CAAW;IAClB,QAAQ,CAAW;IACnB,GAAG,CAAc;IACjB,IAAI,CAAa;IACjB,QAAQ,CAAS;IAEzB,YAAY,OAAgB,EAAE,QAAkB,EAAE,GAAW,EAAE,IAAY,EAAE,QAAQ,GAAG,EAAE;QACxF,IAAI,CAAC,OAAO,GAAI,OAAO,CAAC;QACxB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,GAAG,GAAQ,GAAG,CAAC;QACpB,IAAI,CAAC,IAAI,GAAO,IAAI,CAAC;QACrB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;IAED,KAAK,CAAC,GAAG;QACP,kBAAkB;QAClB,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QAEjC,MAAM,OAAO,GAAoB,EAAE,CAAA;QACnC,MAAM,QAAQ,GAAmB,EAAE,CAAA;QACnC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QAE5B,UAAU;QACV,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,IAAI,IAAI,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE,CAAC;YAEjD,oBAAoB;YACpB,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAA;YAE/C,0BAA0B;YAC1B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;YAE7D,wBAAwB;YACxB,IAAI,MAAM,CAAC,OAAO;gBAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,GAAG,MAAM,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAA;YAE9D,wBAAwB;YACxB,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO;gBAAE,MAAK;YAE5D,qBAAqB;YACrB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;YAEjD,oBAAoB;YACpB,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAA;YAEzC,6BAA6B;YAC7B,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBACpB,QAAQ,CAAC,IAAI,CAAC;oBACZ,QAAQ,EAAK,SAAS;oBACtB,WAAW,EAAE,kBAAkB,MAAM,CAAC,KAAK,EAAE;oBAC7C,OAAO,EAAM,MAAM,CAAC,MAAM,IAAI,SAAS;oBACvC,IAAI;iBACL,CAAC,CAAA;YACJ,CAAC;QACH,CAAC;QAED,sBAAsB;QACtB,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAA;QAE1B,OAAO;YACL,OAAO,EAAM,IAAI,CAAC,QAAQ,CAAC,WAAW;YACtC,GAAG,EAAU,IAAI,CAAC,GAAG;YACrB,IAAI,EAAS,IAAI,CAAC,IAAI;YACtB,KAAK,EAAQ,OAAO,CAAC,MAAM;YAC3B,QAAQ;YACR,WAAW,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,IAAI,KAAK,MAAM;YACnD,KAAK,EAAQ,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,IAAI,KAAK,OAAO;YACpD,QAAQ,EAAK,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS;YACnC,OAAO;SACR,CAAA;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../src/lib/browser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,OAAO,EAAE,MAAM,YAAY,CAAA;AAInD,wBAAsB,UAAU,CAAC,UAAU,GAAE,OAAc,GAAG,OAAO,CAAC,OAAO,CAAC,CAc7E;AAED,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAKlD"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { chromium } from "playwright";
|
|
2
|
+
let browser = null;
|
|
3
|
+
export async function getBrowser(isheadless = true) {
|
|
4
|
+
if (!browser || !browser.isConnected()) {
|
|
5
|
+
browser = await chromium.launch({
|
|
6
|
+
headless: isheadless,
|
|
7
|
+
timeout: 30_000,
|
|
8
|
+
args: [
|
|
9
|
+
"--no-sandbox",
|
|
10
|
+
"--disable-setuid-sandbox",
|
|
11
|
+
"--disable-dev-shm-usage",
|
|
12
|
+
"--disable-gpu",
|
|
13
|
+
]
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
return browser;
|
|
17
|
+
}
|
|
18
|
+
export async function closeBrowser() {
|
|
19
|
+
if (browser) {
|
|
20
|
+
await browser.close();
|
|
21
|
+
browser = null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=browser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser.js","sourceRoot":"","sources":["../../src/lib/browser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAgB,MAAM,YAAY,CAAA;AAEnD,IAAI,OAAO,GAAmB,IAAI,CAAA;AAElC,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,aAAsB,IAAI;IACvD,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC;QACrC,OAAO,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC;YAC5B,QAAQ,EAAE,UAAU;YACpB,OAAO,EAAE,MAAM;YACf,IAAI,EAAE;gBACF,cAAc;gBACd,0BAA0B;gBAC1B,yBAAyB;gBACzB,eAAe;aAClB;SACJ,CAAC,CAAA;IACN,CAAC;IACD,OAAO,OAAO,CAAA;AAClB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY;IAC9B,IAAI,OAAO,EAAE,CAAC;QACV,MAAM,OAAO,CAAC,KAAK,EAAE,CAAA;QACrB,OAAO,GAAG,IAAI,CAAA;IAClB,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { RunResult, Finding } from '../types/index.js';
|
|
2
|
+
export declare function agentStarted(personaName: string): void;
|
|
3
|
+
export declare function agentDone(result: RunResult): void;
|
|
4
|
+
export declare function printFinding(finding: Finding, persona: string): void;
|
|
5
|
+
export declare function printSummary(results: RunResult[]): void;
|
|
6
|
+
export declare function printHeader(url: string, goal: string, personas: string[]): void;
|
|
7
|
+
//# sourceMappingURL=display.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"display.d.ts","sourceRoot":"","sources":["../../src/lib/display.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAI3D,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAMtD;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA6BjD;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAWpE;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,IAAI,CAyBvD;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,CAM/E"}
|