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.
Files changed (75) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +187 -0
  3. package/dist/adapters/base.d.ts +8 -0
  4. package/dist/adapters/base.d.ts.map +1 -0
  5. package/dist/adapters/base.js +2 -0
  6. package/dist/adapters/base.js.map +1 -0
  7. package/dist/adapters/web.d.ts +14 -0
  8. package/dist/adapters/web.d.ts.map +1 -0
  9. package/dist/adapters/web.js +120 -0
  10. package/dist/adapters/web.js.map +1 -0
  11. package/dist/cli/commands/agents.d.ts +3 -0
  12. package/dist/cli/commands/agents.d.ts.map +1 -0
  13. package/dist/cli/commands/agents.js +9 -0
  14. package/dist/cli/commands/agents.js.map +1 -0
  15. package/dist/cli/commands/run.d.ts +3 -0
  16. package/dist/cli/commands/run.d.ts.map +1 -0
  17. package/dist/cli/commands/run.js +49 -0
  18. package/dist/cli/commands/run.js.map +1 -0
  19. package/dist/cli/commands/setup.d.ts +3 -0
  20. package/dist/cli/commands/setup.d.ts.map +1 -0
  21. package/dist/cli/commands/setup.js +93 -0
  22. package/dist/cli/commands/setup.js.map +1 -0
  23. package/dist/cli/index.d.ts +2 -0
  24. package/dist/cli/index.d.ts.map +1 -0
  25. package/dist/cli/index.js +17 -0
  26. package/dist/cli/index.js.map +1 -0
  27. package/dist/core/director.d.ts +14 -0
  28. package/dist/core/director.d.ts.map +1 -0
  29. package/dist/core/director.js +112 -0
  30. package/dist/core/director.js.map +1 -0
  31. package/dist/core/extractor.d.ts +6 -0
  32. package/dist/core/extractor.d.ts.map +1 -0
  33. package/dist/core/extractor.js +42 -0
  34. package/dist/core/extractor.js.map +1 -0
  35. package/dist/core/orchestrator.d.ts +27 -0
  36. package/dist/core/orchestrator.d.ts.map +1 -0
  37. package/dist/core/orchestrator.js +62 -0
  38. package/dist/core/orchestrator.js.map +1 -0
  39. package/dist/core/reporter.d.ts +8 -0
  40. package/dist/core/reporter.d.ts.map +1 -0
  41. package/dist/core/reporter.js +132 -0
  42. package/dist/core/reporter.js.map +1 -0
  43. package/dist/core/runner.d.ts +13 -0
  44. package/dist/core/runner.d.ts.map +1 -0
  45. package/dist/core/runner.js +61 -0
  46. package/dist/core/runner.js.map +1 -0
  47. package/dist/lib/browser.d.ts +4 -0
  48. package/dist/lib/browser.d.ts.map +1 -0
  49. package/dist/lib/browser.js +24 -0
  50. package/dist/lib/browser.js.map +1 -0
  51. package/dist/lib/display.d.ts +7 -0
  52. package/dist/lib/display.d.ts.map +1 -0
  53. package/dist/lib/display.js +80 -0
  54. package/dist/lib/display.js.map +1 -0
  55. package/dist/lib/getConfigs.d.ts +3 -0
  56. package/dist/lib/getConfigs.d.ts.map +1 -0
  57. package/dist/lib/getConfigs.js +12 -0
  58. package/dist/lib/getConfigs.js.map +1 -0
  59. package/dist/llm/index.d.ts +15 -0
  60. package/dist/llm/index.d.ts.map +1 -0
  61. package/dist/llm/index.js +147 -0
  62. package/dist/llm/index.js.map +1 -0
  63. package/dist/personas/index.d.ts +5 -0
  64. package/dist/personas/index.d.ts.map +1 -0
  65. package/dist/personas/index.js +100 -0
  66. package/dist/personas/index.js.map +1 -0
  67. package/dist/personas/loader.d.ts +4 -0
  68. package/dist/personas/loader.d.ts.map +1 -0
  69. package/dist/personas/loader.js +72 -0
  70. package/dist/personas/loader.js.map +1 -0
  71. package/dist/types/index.d.ts +76 -0
  72. package/dist/types/index.d.ts.map +1 -0
  73. package/dist/types/index.js +3 -0
  74. package/dist/types/index.js.map +1 -0
  75. 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,6 @@
1
+ import type { Page } from 'playwright';
2
+ import type { PageState } from '../types/index.js';
3
+ export declare class Extractor {
4
+ extract(page: Page): Promise<PageState>;
5
+ }
6
+ //# sourceMappingURL=extractor.d.ts.map
@@ -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,4 @@
1
+ import { type Browser } from "playwright";
2
+ export declare function getBrowser(isheadless?: boolean): Promise<Browser>;
3
+ export declare function closeBrowser(): Promise<void>;
4
+ //# sourceMappingURL=browser.d.ts.map
@@ -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"}