afterburn-cli 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +281 -0
- package/dist/ai/gemini-client.d.ts +21 -0
- package/dist/ai/gemini-client.js +105 -0
- package/dist/ai/gemini-client.js.map +1 -0
- package/dist/ai/index.d.ts +1 -0
- package/dist/ai/index.js +3 -0
- package/dist/ai/index.js.map +1 -0
- package/dist/analysis/diagnosis-schema.d.ts +106 -0
- package/dist/analysis/diagnosis-schema.js +54 -0
- package/dist/analysis/diagnosis-schema.js.map +1 -0
- package/dist/analysis/error-analyzer.d.ts +9 -0
- package/dist/analysis/error-analyzer.js +573 -0
- package/dist/analysis/error-analyzer.js.map +1 -0
- package/dist/analysis/index.d.ts +4 -0
- package/dist/analysis/index.js +6 -0
- package/dist/analysis/index.js.map +1 -0
- package/dist/analysis/source-mapper.d.ts +19 -0
- package/dist/analysis/source-mapper.js +329 -0
- package/dist/analysis/source-mapper.js.map +1 -0
- package/dist/analysis/ui-auditor.d.ts +9 -0
- package/dist/analysis/ui-auditor.js +104 -0
- package/dist/analysis/ui-auditor.js.map +1 -0
- package/dist/artifacts/artifact-storage.d.ts +44 -0
- package/dist/artifacts/artifact-storage.js +99 -0
- package/dist/artifacts/artifact-storage.js.map +1 -0
- package/dist/artifacts/index.d.ts +1 -0
- package/dist/artifacts/index.js +3 -0
- package/dist/artifacts/index.js.map +1 -0
- package/dist/browser/browser-manager.d.ts +45 -0
- package/dist/browser/browser-manager.js +88 -0
- package/dist/browser/browser-manager.js.map +1 -0
- package/dist/browser/challenge-detector.d.ts +10 -0
- package/dist/browser/challenge-detector.js +58 -0
- package/dist/browser/challenge-detector.js.map +1 -0
- package/dist/browser/cookie-dismisser.d.ts +18 -0
- package/dist/browser/cookie-dismisser.js +76 -0
- package/dist/browser/cookie-dismisser.js.map +1 -0
- package/dist/browser/index.d.ts +4 -0
- package/dist/browser/index.js +6 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/browser/stealth-browser.d.ts +13 -0
- package/dist/browser/stealth-browser.js +59 -0
- package/dist/browser/stealth-browser.js.map +1 -0
- package/dist/cli/commander-cli.d.ts +2 -0
- package/dist/cli/commander-cli.js +150 -0
- package/dist/cli/commander-cli.js.map +1 -0
- package/dist/cli/doctor.d.ts +34 -0
- package/dist/cli/doctor.js +124 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/first-run.d.ts +6 -0
- package/dist/cli/first-run.js +58 -0
- package/dist/cli/first-run.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.js +5 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/progress.d.ts +11 -0
- package/dist/cli/progress.js +30 -0
- package/dist/cli/progress.js.map +1 -0
- package/dist/core/engine.d.ts +33 -0
- package/dist/core/engine.js +269 -0
- package/dist/core/engine.js.map +1 -0
- package/dist/core/index.d.ts +3 -0
- package/dist/core/index.js +4 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/validation.d.ts +52 -0
- package/dist/core/validation.js +228 -0
- package/dist/core/validation.js.map +1 -0
- package/dist/discovery/crawler.d.ts +58 -0
- package/dist/discovery/crawler.js +240 -0
- package/dist/discovery/crawler.js.map +1 -0
- package/dist/discovery/discovery-pipeline.d.ts +22 -0
- package/dist/discovery/discovery-pipeline.js +256 -0
- package/dist/discovery/discovery-pipeline.js.map +1 -0
- package/dist/discovery/element-mapper.d.ts +21 -0
- package/dist/discovery/element-mapper.js +422 -0
- package/dist/discovery/element-mapper.js.map +1 -0
- package/dist/discovery/index.d.ts +8 -0
- package/dist/discovery/index.js +8 -0
- package/dist/discovery/index.js.map +1 -0
- package/dist/discovery/link-validator.d.ts +15 -0
- package/dist/discovery/link-validator.js +137 -0
- package/dist/discovery/link-validator.js.map +1 -0
- package/dist/discovery/sitemap-builder.d.ts +19 -0
- package/dist/discovery/sitemap-builder.js +166 -0
- package/dist/discovery/sitemap-builder.js.map +1 -0
- package/dist/discovery/spa-detector.d.ts +12 -0
- package/dist/discovery/spa-detector.js +271 -0
- package/dist/discovery/spa-detector.js.map +1 -0
- package/dist/execution/error-detector.d.ts +10 -0
- package/dist/execution/error-detector.js +87 -0
- package/dist/execution/error-detector.js.map +1 -0
- package/dist/execution/evidence-capture.d.ts +8 -0
- package/dist/execution/evidence-capture.js +37 -0
- package/dist/execution/evidence-capture.js.map +1 -0
- package/dist/execution/index.d.ts +5 -0
- package/dist/execution/index.js +7 -0
- package/dist/execution/index.js.map +1 -0
- package/dist/execution/step-handlers.d.ts +48 -0
- package/dist/execution/step-handlers.js +349 -0
- package/dist/execution/step-handlers.js.map +1 -0
- package/dist/execution/test-data.d.ts +50 -0
- package/dist/execution/test-data.js +160 -0
- package/dist/execution/test-data.js.map +1 -0
- package/dist/execution/workflow-executor.d.ts +56 -0
- package/dist/execution/workflow-executor.js +331 -0
- package/dist/execution/workflow-executor.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/entry.d.ts +2 -0
- package/dist/mcp/entry.js +5 -0
- package/dist/mcp/entry.js.map +1 -0
- package/dist/mcp/index.d.ts +2 -0
- package/dist/mcp/index.js +4 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/server.d.ts +3 -0
- package/dist/mcp/server.js +19 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tools.d.ts +2 -0
- package/dist/mcp/tools.js +162 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/planning/heuristic-planner.d.ts +7 -0
- package/dist/planning/heuristic-planner.js +238 -0
- package/dist/planning/heuristic-planner.js.map +1 -0
- package/dist/planning/index.d.ts +3 -0
- package/dist/planning/index.js +5 -0
- package/dist/planning/index.js.map +1 -0
- package/dist/planning/plan-schema.d.ts +74 -0
- package/dist/planning/plan-schema.js +39 -0
- package/dist/planning/plan-schema.js.map +1 -0
- package/dist/planning/workflow-planner.d.ts +39 -0
- package/dist/planning/workflow-planner.js +211 -0
- package/dist/planning/workflow-planner.js.map +1 -0
- package/dist/reports/health-scorer.d.ts +14 -0
- package/dist/reports/health-scorer.js +88 -0
- package/dist/reports/health-scorer.js.map +1 -0
- package/dist/reports/html-generator.d.ts +10 -0
- package/dist/reports/html-generator.js +155 -0
- package/dist/reports/html-generator.js.map +1 -0
- package/dist/reports/index.d.ts +4 -0
- package/dist/reports/index.js +6 -0
- package/dist/reports/index.js.map +1 -0
- package/dist/reports/markdown-generator.d.ts +10 -0
- package/dist/reports/markdown-generator.js +334 -0
- package/dist/reports/markdown-generator.js.map +1 -0
- package/dist/reports/priority-ranker.d.ts +22 -0
- package/dist/reports/priority-ranker.js +608 -0
- package/dist/reports/priority-ranker.js.map +1 -0
- package/dist/screenshots/dual-format.d.ts +14 -0
- package/dist/screenshots/dual-format.js +59 -0
- package/dist/screenshots/dual-format.js.map +1 -0
- package/dist/screenshots/index.d.ts +2 -0
- package/dist/screenshots/index.js +4 -0
- package/dist/screenshots/index.js.map +1 -0
- package/dist/screenshots/screenshot-manager.d.ts +33 -0
- package/dist/screenshots/screenshot-manager.js +86 -0
- package/dist/screenshots/screenshot-manager.js.map +1 -0
- package/dist/testing/accessibility-auditor.d.ts +23 -0
- package/dist/testing/accessibility-auditor.js +44 -0
- package/dist/testing/accessibility-auditor.js.map +1 -0
- package/dist/testing/index.d.ts +4 -0
- package/dist/testing/index.js +5 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/meta-auditor.d.ts +16 -0
- package/dist/testing/meta-auditor.js +268 -0
- package/dist/testing/meta-auditor.js.map +1 -0
- package/dist/testing/performance-monitor.d.ts +15 -0
- package/dist/testing/performance-monitor.js +64 -0
- package/dist/testing/performance-monitor.js.map +1 -0
- package/dist/types/artifacts.d.ts +58 -0
- package/dist/types/artifacts.js +3 -0
- package/dist/types/artifacts.js.map +1 -0
- package/dist/types/discovery.d.ts +124 -0
- package/dist/types/discovery.js +3 -0
- package/dist/types/discovery.js.map +1 -0
- package/dist/types/execution.d.ts +154 -0
- package/dist/types/execution.js +3 -0
- package/dist/types/execution.js.map +1 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.js +4 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/sanitizer.d.ts +25 -0
- package/dist/utils/sanitizer.js +98 -0
- package/dist/utils/sanitizer.js.map +1 -0
- package/package.json +86 -0
- package/templates/report.hbs +202 -0
- package/templates/styles/report.css +607 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { BrowserContext, Page } from 'playwright';
|
|
2
|
+
import type { BrowserConfig } from '../types/artifacts.js';
|
|
3
|
+
import type { ChallengeDetectionResult } from './challenge-detector.js';
|
|
4
|
+
/**
|
|
5
|
+
* Manages browser lifecycle: launch, navigation, and cleanup.
|
|
6
|
+
* Automatically dismisses cookie banners after navigation.
|
|
7
|
+
*/
|
|
8
|
+
export declare class BrowserManager {
|
|
9
|
+
private browser;
|
|
10
|
+
private context;
|
|
11
|
+
private config?;
|
|
12
|
+
constructor(config?: Partial<BrowserConfig>);
|
|
13
|
+
/**
|
|
14
|
+
* Launches stealth-enabled browser and creates context
|
|
15
|
+
*/
|
|
16
|
+
launch(): Promise<void>;
|
|
17
|
+
private _lastChallengeResult;
|
|
18
|
+
/**
|
|
19
|
+
* Returns the challenge detection result from the most recent newPage() navigation.
|
|
20
|
+
*/
|
|
21
|
+
get lastChallengeResult(): ChallengeDetectionResult;
|
|
22
|
+
/**
|
|
23
|
+
* Creates a new page and optionally navigates to a URL.
|
|
24
|
+
* Automatically dismisses cookie banners and detects bot-challenge pages after navigation.
|
|
25
|
+
*
|
|
26
|
+
* @param url - Optional URL to navigate to
|
|
27
|
+
* @returns Playwright page instance
|
|
28
|
+
*/
|
|
29
|
+
newPage(url?: string, options?: {
|
|
30
|
+
networkIdleTimeout?: number;
|
|
31
|
+
}): Promise<Page>;
|
|
32
|
+
/**
|
|
33
|
+
* Closes browser and cleans up resources.
|
|
34
|
+
* Safe to call multiple times.
|
|
35
|
+
*/
|
|
36
|
+
close(): Promise<void>;
|
|
37
|
+
/**
|
|
38
|
+
* Returns the browser context for route interception (e.g., blocking resources).
|
|
39
|
+
*/
|
|
40
|
+
getContext(): BrowserContext | null;
|
|
41
|
+
/**
|
|
42
|
+
* Checks if browser is currently launched
|
|
43
|
+
*/
|
|
44
|
+
get isLaunched(): boolean;
|
|
45
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// Browser lifecycle management with stealth and cookie dismissal
|
|
2
|
+
import { createStealthBrowser } from './stealth-browser.js';
|
|
3
|
+
import { dismissCookieBanner } from './cookie-dismisser.js';
|
|
4
|
+
import { detectChallengePage } from './challenge-detector.js';
|
|
5
|
+
/**
|
|
6
|
+
* Manages browser lifecycle: launch, navigation, and cleanup.
|
|
7
|
+
* Automatically dismisses cookie banners after navigation.
|
|
8
|
+
*/
|
|
9
|
+
export class BrowserManager {
|
|
10
|
+
browser = null;
|
|
11
|
+
context = null;
|
|
12
|
+
config;
|
|
13
|
+
constructor(config) {
|
|
14
|
+
this.config = config;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Launches stealth-enabled browser and creates context
|
|
18
|
+
*/
|
|
19
|
+
async launch() {
|
|
20
|
+
const { browser, context } = await createStealthBrowser(this.config);
|
|
21
|
+
this.browser = browser;
|
|
22
|
+
this.context = context;
|
|
23
|
+
}
|
|
24
|
+
// Tracks whether the last navigated page was a bot-challenge page
|
|
25
|
+
_lastChallengeResult = { isChallengePage: false, provider: null };
|
|
26
|
+
/**
|
|
27
|
+
* Returns the challenge detection result from the most recent newPage() navigation.
|
|
28
|
+
*/
|
|
29
|
+
get lastChallengeResult() {
|
|
30
|
+
return this._lastChallengeResult;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Creates a new page and optionally navigates to a URL.
|
|
34
|
+
* Automatically dismisses cookie banners and detects bot-challenge pages after navigation.
|
|
35
|
+
*
|
|
36
|
+
* @param url - Optional URL to navigate to
|
|
37
|
+
* @returns Playwright page instance
|
|
38
|
+
*/
|
|
39
|
+
async newPage(url, options) {
|
|
40
|
+
if (!this.context) {
|
|
41
|
+
throw new Error('Browser not launched. Call launch() first.');
|
|
42
|
+
}
|
|
43
|
+
const page = await this.context.newPage();
|
|
44
|
+
if (url) {
|
|
45
|
+
// Navigate with DOM ready first
|
|
46
|
+
await page.goto(url, { waitUntil: 'domcontentloaded' });
|
|
47
|
+
// Detect anti-bot challenge pages before processing content
|
|
48
|
+
this._lastChallengeResult = await detectChallengePage(page);
|
|
49
|
+
if (this._lastChallengeResult.isChallengePage) {
|
|
50
|
+
console.warn(`Anti-bot challenge detected (${this._lastChallengeResult.provider}) on ${url}. Results may be incomplete.`);
|
|
51
|
+
}
|
|
52
|
+
// Try to dismiss cookie banner
|
|
53
|
+
await dismissCookieBanner(page);
|
|
54
|
+
// Wait for network idle with timeout (some SPAs never reach idle)
|
|
55
|
+
try {
|
|
56
|
+
await page.waitForLoadState('networkidle', { timeout: options?.networkIdleTimeout ?? 2000 });
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// Timeout is acceptable - page may never reach networkidle
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return page;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Closes browser and cleans up resources.
|
|
66
|
+
* Safe to call multiple times.
|
|
67
|
+
*/
|
|
68
|
+
async close() {
|
|
69
|
+
if (this.browser) {
|
|
70
|
+
await this.browser.close();
|
|
71
|
+
this.browser = null;
|
|
72
|
+
this.context = null;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Returns the browser context for route interception (e.g., blocking resources).
|
|
77
|
+
*/
|
|
78
|
+
getContext() {
|
|
79
|
+
return this.context;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Checks if browser is currently launched
|
|
83
|
+
*/
|
|
84
|
+
get isLaunched() {
|
|
85
|
+
return this.browser !== null && this.context !== null;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=browser-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-manager.js","sourceRoot":"","sources":["../../src/browser/browser-manager.ts"],"names":[],"mappings":"AAAA,iEAAiE;AAIjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAG9D;;;GAGG;AACH,MAAM,OAAO,cAAc;IACjB,OAAO,GAAmB,IAAI,CAAC;IAC/B,OAAO,GAA0B,IAAI,CAAC;IACtC,MAAM,CAA0B;IAExC,YAAY,MAA+B;QACzC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,MAAM;QACV,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,MAAM,oBAAoB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACrE,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED,kEAAkE;IAC1D,oBAAoB,GAA6B,EAAE,eAAe,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAEpG;;OAEG;IACH,IAAI,mBAAmB;QACrB,OAAO,IAAI,CAAC,oBAAoB,CAAC;IACnC,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,OAAO,CAAC,GAAY,EAAE,OAAyC;QACnE,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;QAChE,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QAE1C,IAAI,GAAG,EAAE,CAAC;YACR,gCAAgC;YAChC,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,kBAAkB,EAAE,CAAC,CAAC;YAExD,4DAA4D;YAC5D,IAAI,CAAC,oBAAoB,GAAG,MAAM,mBAAmB,CAAC,IAAI,CAAC,CAAC;YAC5D,IAAI,IAAI,CAAC,oBAAoB,CAAC,eAAe,EAAE,CAAC;gBAC9C,OAAO,CAAC,IAAI,CAAC,gCAAgC,IAAI,CAAC,oBAAoB,CAAC,QAAQ,QAAQ,GAAG,8BAA8B,CAAC,CAAC;YAC5H,CAAC;YAED,+BAA+B;YAC/B,MAAM,mBAAmB,CAAC,IAAI,CAAC,CAAC;YAEhC,kEAAkE;YAClE,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,gBAAgB,CAAC,aAAa,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,kBAAkB,IAAI,IAAI,EAAE,CAAC,CAAC;YAC/F,CAAC;YAAC,MAAM,CAAC;gBACP,2DAA2D;YAC7D,CAAC;QACH,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,KAAK;QACT,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;YAC3B,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;YACpB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACtB,CAAC;IACH,CAAC;IAED;;OAEG;IACH,UAAU;QACR,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED;;OAEG;IACH,IAAI,UAAU;QACZ,OAAO,IAAI,CAAC,OAAO,KAAK,IAAI,IAAI,IAAI,CAAC,OAAO,KAAK,IAAI,CAAC;IACxD,CAAC;CACF"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Page } from 'playwright';
|
|
2
|
+
export interface ChallengeDetectionResult {
|
|
3
|
+
isChallengePage: boolean;
|
|
4
|
+
provider: string | null;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Checks if the current page is an anti-bot challenge page rather than real site content.
|
|
8
|
+
* Should be called after page.goto() to prevent parsing challenge HTML as real content.
|
|
9
|
+
*/
|
|
10
|
+
export declare function detectChallengePage(page: Page): Promise<ChallengeDetectionResult>;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Detect anti-bot challenge pages (Cloudflare, Akamai, etc.) that block automated testing
|
|
2
|
+
/**
|
|
3
|
+
* Checks if the current page is an anti-bot challenge page rather than real site content.
|
|
4
|
+
* Should be called after page.goto() to prevent parsing challenge HTML as real content.
|
|
5
|
+
*/
|
|
6
|
+
export async function detectChallengePage(page) {
|
|
7
|
+
try {
|
|
8
|
+
const title = await page.title();
|
|
9
|
+
const titleLower = title.toLowerCase();
|
|
10
|
+
// Cloudflare challenge: title is "Just a moment..." or "Attention Required!"
|
|
11
|
+
if (titleLower.includes('just a moment') || titleLower.includes('attention required')) {
|
|
12
|
+
return { isChallengePage: true, provider: 'Cloudflare' };
|
|
13
|
+
}
|
|
14
|
+
// Check for known challenge page indicators in the DOM
|
|
15
|
+
const challengeIndicators = await page.evaluate(() => {
|
|
16
|
+
const body = document.body?.textContent?.toLowerCase() || '';
|
|
17
|
+
const html = document.documentElement?.innerHTML?.toLowerCase() || '';
|
|
18
|
+
return {
|
|
19
|
+
// Cloudflare-specific
|
|
20
|
+
hasCfChallenge: !!document.querySelector('#cf-challenge-running, #challenge-running, .cf-browser-verification'),
|
|
21
|
+
hasCfScript: html.includes('__cf_chl_opt') || html.includes('cf-browser-verification'),
|
|
22
|
+
hasCheckingBrowser: body.includes('checking your browser') || body.includes('verify you are human'),
|
|
23
|
+
// Akamai Bot Manager
|
|
24
|
+
hasAkamai: html.includes('akamai') && (body.includes('access denied') || body.includes('reference #')),
|
|
25
|
+
// Generic bot detection
|
|
26
|
+
hasAccessDenied: (body.includes('access denied') || body.includes('forbidden')) && body.length < 2000,
|
|
27
|
+
hasCaptcha: !!document.querySelector('.g-recaptcha, .h-captcha, [data-sitekey]'),
|
|
28
|
+
// PerimeterX
|
|
29
|
+
hasPerimeterX: html.includes('perimeterx') || html.includes('_pxhd'),
|
|
30
|
+
bodyLength: body.length,
|
|
31
|
+
};
|
|
32
|
+
});
|
|
33
|
+
if (challengeIndicators.hasCfChallenge || challengeIndicators.hasCfScript) {
|
|
34
|
+
return { isChallengePage: true, provider: 'Cloudflare' };
|
|
35
|
+
}
|
|
36
|
+
if (challengeIndicators.hasCheckingBrowser && challengeIndicators.bodyLength < 5000) {
|
|
37
|
+
return { isChallengePage: true, provider: 'Anti-bot protection' };
|
|
38
|
+
}
|
|
39
|
+
if (challengeIndicators.hasAkamai) {
|
|
40
|
+
return { isChallengePage: true, provider: 'Akamai' };
|
|
41
|
+
}
|
|
42
|
+
if (challengeIndicators.hasPerimeterX) {
|
|
43
|
+
return { isChallengePage: true, provider: 'PerimeterX' };
|
|
44
|
+
}
|
|
45
|
+
if (challengeIndicators.hasAccessDenied) {
|
|
46
|
+
return { isChallengePage: true, provider: 'Access control' };
|
|
47
|
+
}
|
|
48
|
+
if (challengeIndicators.hasCaptcha && challengeIndicators.bodyLength < 5000) {
|
|
49
|
+
return { isChallengePage: true, provider: 'CAPTCHA' };
|
|
50
|
+
}
|
|
51
|
+
return { isChallengePage: false, provider: null };
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
// If detection fails, assume it's not a challenge page
|
|
55
|
+
return { isChallengePage: false, provider: null };
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=challenge-detector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"challenge-detector.js","sourceRoot":"","sources":["../../src/browser/challenge-detector.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Page } from 'playwright';
|
|
2
|
+
import type { CookieBannerSelector } from '../types/artifacts.js';
|
|
3
|
+
/**
|
|
4
|
+
* Known cookie consent platforms with their selectors
|
|
5
|
+
* Ordered by prevalence (OneTrust is most common)
|
|
6
|
+
*/
|
|
7
|
+
export declare const COOKIE_SELECTORS: CookieBannerSelector[];
|
|
8
|
+
/**
|
|
9
|
+
* Attempts to detect and dismiss cookie consent banners.
|
|
10
|
+
* Tries known platforms in order, clicking the first visible accept button.
|
|
11
|
+
*
|
|
12
|
+
* @param page - Playwright page instance
|
|
13
|
+
* @returns Object indicating if banner was dismissed and which platform
|
|
14
|
+
*/
|
|
15
|
+
export declare function dismissCookieBanner(page: Page): Promise<{
|
|
16
|
+
dismissed: boolean;
|
|
17
|
+
platform: string | null;
|
|
18
|
+
}>;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// Auto-detection and dismissal of cookie consent banners
|
|
2
|
+
/**
|
|
3
|
+
* Known cookie consent platforms with their selectors
|
|
4
|
+
* Ordered by prevalence (OneTrust is most common)
|
|
5
|
+
*/
|
|
6
|
+
export const COOKIE_SELECTORS = [
|
|
7
|
+
{
|
|
8
|
+
name: 'OneTrust',
|
|
9
|
+
acceptButton: '#onetrust-accept-btn-handler',
|
|
10
|
+
modal: '#onetrust-banner-sdk',
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
name: 'Cookiebot',
|
|
14
|
+
acceptButton: '#CybotCookiebotDialogBodyLevelButtonLevelOptinAllowAll',
|
|
15
|
+
modal: '#CybotCookiebotDialog',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
name: 'CookieYes',
|
|
19
|
+
acceptButton: '.cky-btn-accept',
|
|
20
|
+
modal: '.cky-consent-container',
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: 'Generic',
|
|
24
|
+
acceptButton: [
|
|
25
|
+
'button:has-text("Accept all")',
|
|
26
|
+
'button:has-text("Accept cookies")',
|
|
27
|
+
'button:has-text("Allow all")',
|
|
28
|
+
'button:has-text("I agree")',
|
|
29
|
+
'[id*="accept"]',
|
|
30
|
+
'[class*="accept"]',
|
|
31
|
+
].join(', '),
|
|
32
|
+
},
|
|
33
|
+
];
|
|
34
|
+
/**
|
|
35
|
+
* Attempts to detect and dismiss cookie consent banners.
|
|
36
|
+
* Tries known platforms in order, clicking the first visible accept button.
|
|
37
|
+
*
|
|
38
|
+
* @param page - Playwright page instance
|
|
39
|
+
* @returns Object indicating if banner was dismissed and which platform
|
|
40
|
+
*/
|
|
41
|
+
export async function dismissCookieBanner(page) {
|
|
42
|
+
for (const selector of COOKIE_SELECTORS) {
|
|
43
|
+
try {
|
|
44
|
+
// Wait for accept button with short timeout (banner may not exist)
|
|
45
|
+
const button = page.locator(selector.acceptButton).first();
|
|
46
|
+
const isVisible = await button.isVisible({ timeout: 800 });
|
|
47
|
+
if (!isVisible) {
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
// Click the accept button
|
|
51
|
+
await button.click();
|
|
52
|
+
// If modal selector exists, wait for it to become hidden
|
|
53
|
+
if (selector.modal) {
|
|
54
|
+
try {
|
|
55
|
+
await page.locator(selector.modal).waitFor({
|
|
56
|
+
state: 'hidden',
|
|
57
|
+
timeout: 5000,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// Modal might not disappear or might not exist - non-critical
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// Wait for animations to complete
|
|
65
|
+
await page.waitForTimeout(500);
|
|
66
|
+
return { dismissed: true, platform: selector.name };
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
// This selector didn't work, try next one
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// No cookie banner found
|
|
74
|
+
return { dismissed: false, platform: null };
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=cookie-dismisser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cookie-dismisser.js","sourceRoot":"","sources":["../../src/browser/cookie-dismisser.ts"],"names":[],"mappings":"AAAA,yDAAyD;AAKzD;;;GAGG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAA2B;IACtD;QACE,IAAI,EAAE,UAAU;QAChB,YAAY,EAAE,8BAA8B;QAC5C,KAAK,EAAE,sBAAsB;KAC9B;IACD;QACE,IAAI,EAAE,WAAW;QACjB,YAAY,EAAE,wDAAwD;QACtE,KAAK,EAAE,uBAAuB;KAC/B;IACD;QACE,IAAI,EAAE,WAAW;QACjB,YAAY,EAAE,iBAAiB;QAC/B,KAAK,EAAE,wBAAwB;KAChC;IACD;QACE,IAAI,EAAE,SAAS;QACf,YAAY,EAAE;YACZ,+BAA+B;YAC/B,mCAAmC;YACnC,8BAA8B;YAC9B,4BAA4B;YAC5B,gBAAgB;YAChB,mBAAmB;SACpB,CAAC,IAAI,CAAC,IAAI,CAAC;KACb;CACF,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,IAAU;IAEV,KAAK,MAAM,QAAQ,IAAI,gBAAgB,EAAE,CAAC;QACxC,IAAI,CAAC;YACH,mEAAmE;YACnE,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,KAAK,EAAE,CAAC;YAC3D,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC;YAE3D,IAAI,CAAC,SAAS,EAAE,CAAC;gBACf,SAAS;YACX,CAAC;YAED,0BAA0B;YAC1B,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;YAErB,yDAAyD;YACzD,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;gBACnB,IAAI,CAAC;oBACH,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC;wBACzC,KAAK,EAAE,QAAQ;wBACf,OAAO,EAAE,IAAI;qBACd,CAAC,CAAC;gBACL,CAAC;gBAAC,MAAM,CAAC;oBACP,8DAA8D;gBAChE,CAAC;YACH,CAAC;YAED,kCAAkC;YAClC,MAAM,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;YAE/B,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC;QACtD,CAAC;QAAC,MAAM,CAAC;YACP,0CAA0C;YAC1C,SAAS;QACX,CAAC;IACH,CAAC;IAED,yBAAyB;IACzB,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;AAC9C,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/browser/index.ts"],"names":[],"mappings":"AAAA,mCAAmC;AAEnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Browser, BrowserContext } from 'playwright';
|
|
2
|
+
import type { BrowserConfig } from '../types/artifacts.js';
|
|
3
|
+
/**
|
|
4
|
+
* Creates a stealth-enabled Playwright browser with anti-bot evasion.
|
|
5
|
+
* Uses playwright-extra with stealth plugin to bypass detection on real websites.
|
|
6
|
+
*
|
|
7
|
+
* @param config - Optional browser configuration overrides
|
|
8
|
+
* @returns Object containing browser and context references
|
|
9
|
+
*/
|
|
10
|
+
export declare function createStealthBrowser(config?: Partial<BrowserConfig>): Promise<{
|
|
11
|
+
browser: Browser;
|
|
12
|
+
context: BrowserContext;
|
|
13
|
+
}>;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// Stealth-enabled Playwright browser creation with anti-bot evasion
|
|
2
|
+
import { chromium } from 'playwright-extra';
|
|
3
|
+
import StealthPlugin from 'puppeteer-extra-plugin-stealth';
|
|
4
|
+
// Prevent duplicate plugin registration
|
|
5
|
+
let pluginApplied = false;
|
|
6
|
+
/**
|
|
7
|
+
* Default browser configuration with realistic fingerprint
|
|
8
|
+
*/
|
|
9
|
+
const DEFAULT_CONFIG = {
|
|
10
|
+
headless: true,
|
|
11
|
+
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36',
|
|
12
|
+
viewport: {
|
|
13
|
+
width: 1920,
|
|
14
|
+
height: 1080,
|
|
15
|
+
},
|
|
16
|
+
locale: 'en-US',
|
|
17
|
+
timezoneId: 'America/New_York',
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Creates a stealth-enabled Playwright browser with anti-bot evasion.
|
|
21
|
+
* Uses playwright-extra with stealth plugin to bypass detection on real websites.
|
|
22
|
+
*
|
|
23
|
+
* @param config - Optional browser configuration overrides
|
|
24
|
+
* @returns Object containing browser and context references
|
|
25
|
+
*/
|
|
26
|
+
export async function createStealthBrowser(config) {
|
|
27
|
+
// Apply stealth plugin once
|
|
28
|
+
if (!pluginApplied) {
|
|
29
|
+
chromium.use(StealthPlugin());
|
|
30
|
+
pluginApplied = true;
|
|
31
|
+
}
|
|
32
|
+
// Merge config with defaults
|
|
33
|
+
const finalConfig = {
|
|
34
|
+
...DEFAULT_CONFIG,
|
|
35
|
+
...config,
|
|
36
|
+
viewport: {
|
|
37
|
+
...DEFAULT_CONFIG.viewport,
|
|
38
|
+
...config?.viewport,
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
// Launch browser with anti-bot evasion arguments
|
|
42
|
+
const browser = await chromium.launch({
|
|
43
|
+
headless: finalConfig.headless,
|
|
44
|
+
args: [
|
|
45
|
+
'--disable-blink-features=AutomationControlled',
|
|
46
|
+
'--disable-features=IsolateOrigins,site-per-process',
|
|
47
|
+
'--disable-site-isolation-trials',
|
|
48
|
+
],
|
|
49
|
+
});
|
|
50
|
+
// Create context with realistic fingerprint
|
|
51
|
+
const context = await browser.newContext({
|
|
52
|
+
userAgent: finalConfig.userAgent,
|
|
53
|
+
viewport: finalConfig.viewport,
|
|
54
|
+
locale: finalConfig.locale,
|
|
55
|
+
timezoneId: finalConfig.timezoneId,
|
|
56
|
+
});
|
|
57
|
+
return { browser, context };
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=stealth-browser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stealth-browser.js","sourceRoot":"","sources":["../../src/browser/stealth-browser.ts"],"names":[],"mappings":"AAAA,oEAAoE;AAEpE,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,aAAa,MAAM,gCAAgC,CAAC;AAI3D,wCAAwC;AACxC,IAAI,aAAa,GAAG,KAAK,CAAC;AAE1B;;GAEG;AACH,MAAM,cAAc,GAAkB;IACpC,QAAQ,EAAE,IAAI;IACd,SAAS,EAAE,iHAAiH;IAC5H,QAAQ,EAAE;QACR,KAAK,EAAE,IAAI;QACX,MAAM,EAAE,IAAI;KACb;IACD,MAAM,EAAE,OAAO;IACf,UAAU,EAAE,kBAAkB;CAC/B,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,MAA+B;IAE/B,4BAA4B;IAC5B,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,QAAQ,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC,CAAC;QAC9B,aAAa,GAAG,IAAI,CAAC;IACvB,CAAC;IAED,6BAA6B;IAC7B,MAAM,WAAW,GAAkB;QACjC,GAAG,cAAc;QACjB,GAAG,MAAM;QACT,QAAQ,EAAE;YACR,GAAG,cAAc,CAAC,QAAQ;YAC1B,GAAG,MAAM,EAAE,QAAQ;SACpB;KACF,CAAC;IAEF,iDAAiD;IACjD,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC;QACpC,QAAQ,EAAE,WAAW,CAAC,QAAQ;QAC9B,IAAI,EAAE;YACJ,+CAA+C;YAC/C,oDAAoD;YACpD,iCAAiC;SAClC;KACF,CAAC,CAAC;IAEH,4CAA4C;IAC5C,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC;QACvC,SAAS,EAAE,WAAW,CAAC,SAAS;QAChC,QAAQ,EAAE,WAAW,CAAC,QAAQ;QAC9B,MAAM,EAAE,WAAW,CAAC,MAAM;QAC1B,UAAU,EAAE,WAAW,CAAC,UAAU;KACnC,CAAC,CAAC;IAEH,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AAC9B,CAAC"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
// Commander.js CLI program definition with all flags and action handler
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { runAfterburn } from '../core/index.js';
|
|
4
|
+
import { ensureBrowserInstalled, createSpinner } from './index.js';
|
|
5
|
+
import { runDoctor, printDoctorResults } from './doctor.js';
|
|
6
|
+
export const program = new Command();
|
|
7
|
+
program
|
|
8
|
+
.name('afterburn')
|
|
9
|
+
.description('Automated testing for vibe-coded websites')
|
|
10
|
+
.version('1.0.0')
|
|
11
|
+
.showHelpAfterError(true)
|
|
12
|
+
.argument('<url>', 'URL to test')
|
|
13
|
+
.option('--source <path>', 'Source code directory for pinpointing bugs')
|
|
14
|
+
.option('--email <email>', 'Login email (or set AFTERBURN_EMAIL env var)')
|
|
15
|
+
.option('--password <password>', 'Login password (tip: use AFTERBURN_PASSWORD env var to avoid shell history exposure)')
|
|
16
|
+
.option('--output-dir <path>', 'Custom output directory (default: ./afterburn-reports/{timestamp})')
|
|
17
|
+
.option('--flows <hints>', 'Comma-separated workflow hints (e.g., "signup, checkout")')
|
|
18
|
+
.option('--max-pages <n>', 'Max pages to crawl (default: 50, max: 500)', '50')
|
|
19
|
+
.option('--no-headless', 'Show browser window (useful for debugging)')
|
|
20
|
+
.option('--verbose', 'Show detailed progress output')
|
|
21
|
+
.action(async (url, opts) => {
|
|
22
|
+
// Resolve credentials: CLI flags take priority, env vars are fallback
|
|
23
|
+
const email = opts.email || process.env.AFTERBURN_EMAIL;
|
|
24
|
+
const password = opts.password || process.env.AFTERBURN_PASSWORD;
|
|
25
|
+
// First-run browser check
|
|
26
|
+
await ensureBrowserInstalled();
|
|
27
|
+
// Print banner
|
|
28
|
+
console.log('Afterburn v1.0.0\n');
|
|
29
|
+
// In verbose mode, show credential source without exposing actual values
|
|
30
|
+
if (opts.verbose) {
|
|
31
|
+
if (email) {
|
|
32
|
+
const atIndex = email.indexOf('@');
|
|
33
|
+
const masked = atIndex > 3
|
|
34
|
+
? email.substring(0, 3) + '***' + email.substring(atIndex)
|
|
35
|
+
: '***' + email.substring(atIndex);
|
|
36
|
+
console.log(` Auth email: ${masked}`);
|
|
37
|
+
}
|
|
38
|
+
if (password)
|
|
39
|
+
console.log(` Auth password: ***`);
|
|
40
|
+
}
|
|
41
|
+
// Create spinner for progress tracking
|
|
42
|
+
let spinner;
|
|
43
|
+
let currentStage = '';
|
|
44
|
+
// Parse --flows flag
|
|
45
|
+
const flowHints = opts.flows
|
|
46
|
+
? opts.flows.split(',').map(s => s.trim()).filter(Boolean)
|
|
47
|
+
: undefined;
|
|
48
|
+
// Parse --max-pages flag (Commander passes string, convert to number)
|
|
49
|
+
// Validation happens in core/engine.ts via validateMaxPages
|
|
50
|
+
const parsedMaxPages = parseInt(opts.maxPages, 10);
|
|
51
|
+
const maxPages = isNaN(parsedMaxPages) ? undefined : parsedMaxPages;
|
|
52
|
+
try {
|
|
53
|
+
const result = await runAfterburn({
|
|
54
|
+
targetUrl: url,
|
|
55
|
+
sourcePath: opts.source,
|
|
56
|
+
email,
|
|
57
|
+
password,
|
|
58
|
+
outputDir: opts.outputDir,
|
|
59
|
+
flowHints,
|
|
60
|
+
maxPages,
|
|
61
|
+
headless: opts.headless,
|
|
62
|
+
onProgress: (stage, message) => {
|
|
63
|
+
// Detect stage change before updating currentStage
|
|
64
|
+
const stageChanged = stage !== currentStage;
|
|
65
|
+
// Map stage names to ora spinner text
|
|
66
|
+
if (stageChanged) {
|
|
67
|
+
currentStage = stage;
|
|
68
|
+
// Stop previous spinner if exists
|
|
69
|
+
if (spinner) {
|
|
70
|
+
spinner.succeed();
|
|
71
|
+
spinner = undefined;
|
|
72
|
+
}
|
|
73
|
+
// Start new spinner for new stage (except 'complete')
|
|
74
|
+
if (stage !== 'complete') {
|
|
75
|
+
const spinnerText = {
|
|
76
|
+
browser: 'Checking browser...',
|
|
77
|
+
discovery: 'Crawling site...',
|
|
78
|
+
execution: 'Testing workflows...',
|
|
79
|
+
analysis: 'Analyzing results...',
|
|
80
|
+
reporting: 'Generating reports...',
|
|
81
|
+
}[stage] || message;
|
|
82
|
+
spinner = createSpinner(spinnerText).start();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// Verbose mode: show detailed messages within same stage
|
|
86
|
+
if (opts.verbose && !stageChanged) {
|
|
87
|
+
if (spinner) {
|
|
88
|
+
spinner.text = message;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
// Complete spinner
|
|
94
|
+
if (spinner) {
|
|
95
|
+
spinner.succeed();
|
|
96
|
+
}
|
|
97
|
+
// Print one-liner summary (ASCII-safe for Windows terminals)
|
|
98
|
+
console.log(`\nHealth: ${result.healthScore.overall}/100 - ${result.totalIssues} issues found (${result.highPriorityCount} high, ${result.mediumPriorityCount} medium, ${result.lowPriorityCount} low)`);
|
|
99
|
+
// Print top 3 issues in terminal so the user sees value immediately
|
|
100
|
+
if (result.prioritizedIssues.length > 0) {
|
|
101
|
+
console.log('\nTop issues:');
|
|
102
|
+
const topIssues = result.prioritizedIssues.slice(0, 3);
|
|
103
|
+
for (let i = 0; i < topIssues.length; i++) {
|
|
104
|
+
const issue = topIssues[i];
|
|
105
|
+
const priorityTag = issue.priority === 'high' ? '[HIGH]' : issue.priority === 'medium' ? '[MED]' : '[LOW]';
|
|
106
|
+
// Truncate summary for terminal readability
|
|
107
|
+
const summary = issue.summary.length > 80 ? issue.summary.slice(0, 77) + '...' : issue.summary;
|
|
108
|
+
console.log(` ${i + 1}. ${priorityTag} ${summary}`);
|
|
109
|
+
}
|
|
110
|
+
if (result.prioritizedIssues.length > 3) {
|
|
111
|
+
console.log(` ... and ${result.prioritizedIssues.length - 3} more (see report)`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// Print report paths if they exist
|
|
115
|
+
if (result.htmlReportPath || result.markdownReportPath) {
|
|
116
|
+
console.log('\nReports saved:');
|
|
117
|
+
if (result.htmlReportPath) {
|
|
118
|
+
console.log(` HTML: ${result.htmlReportPath}`);
|
|
119
|
+
}
|
|
120
|
+
if (result.markdownReportPath) {
|
|
121
|
+
console.log(` Markdown: ${result.markdownReportPath}`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
console.log('');
|
|
125
|
+
// Exit with code based on execution results
|
|
126
|
+
process.exit(result.exitCode);
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
// Fail spinner if active
|
|
130
|
+
if (spinner) {
|
|
131
|
+
spinner.fail();
|
|
132
|
+
}
|
|
133
|
+
console.error('\n[x] Error:', error instanceof Error ? error.message : String(error));
|
|
134
|
+
if (opts.verbose && error instanceof Error && error.stack) {
|
|
135
|
+
console.error('\nStack trace:');
|
|
136
|
+
console.error(error.stack);
|
|
137
|
+
}
|
|
138
|
+
process.exit(1);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
// Doctor subcommand: pre-flight environment checks
|
|
142
|
+
program
|
|
143
|
+
.command('doctor')
|
|
144
|
+
.description('Check if your environment is ready to run Afterburn')
|
|
145
|
+
.action(async () => {
|
|
146
|
+
const { results, exitCode } = await runDoctor();
|
|
147
|
+
printDoctorResults(results, exitCode);
|
|
148
|
+
process.exit(exitCode);
|
|
149
|
+
});
|
|
150
|
+
//# sourceMappingURL=commander-cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commander-cli.js","sourceRoot":"","sources":["../../src/cli/commander-cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export type CheckStatus = 'pass' | 'warn' | 'fail';
|
|
2
|
+
export interface CheckResult {
|
|
3
|
+
name: string;
|
|
4
|
+
status: CheckStatus;
|
|
5
|
+
message: string;
|
|
6
|
+
required: boolean;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Check Node.js version >= 18
|
|
10
|
+
*/
|
|
11
|
+
export declare function checkNodeVersion(): CheckResult;
|
|
12
|
+
/**
|
|
13
|
+
* Check if Playwright Chromium browser is installed
|
|
14
|
+
*/
|
|
15
|
+
export declare function checkBrowserInstalled(): CheckResult;
|
|
16
|
+
/**
|
|
17
|
+
* Check if GEMINI_API_KEY is set (optional, warn only)
|
|
18
|
+
*/
|
|
19
|
+
export declare function checkApiKey(): CheckResult;
|
|
20
|
+
/**
|
|
21
|
+
* Check network connectivity by resolving a known host
|
|
22
|
+
*/
|
|
23
|
+
export declare function checkNetwork(): Promise<CheckResult>;
|
|
24
|
+
/**
|
|
25
|
+
* Run all doctor checks and return results + exit code
|
|
26
|
+
*/
|
|
27
|
+
export declare function runDoctor(): Promise<{
|
|
28
|
+
results: CheckResult[];
|
|
29
|
+
exitCode: number;
|
|
30
|
+
}>;
|
|
31
|
+
/**
|
|
32
|
+
* Format and print doctor results to console
|
|
33
|
+
*/
|
|
34
|
+
export declare function printDoctorResults(results: CheckResult[], exitCode: number): void;
|