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.
Files changed (188) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +281 -0
  3. package/dist/ai/gemini-client.d.ts +21 -0
  4. package/dist/ai/gemini-client.js +105 -0
  5. package/dist/ai/gemini-client.js.map +1 -0
  6. package/dist/ai/index.d.ts +1 -0
  7. package/dist/ai/index.js +3 -0
  8. package/dist/ai/index.js.map +1 -0
  9. package/dist/analysis/diagnosis-schema.d.ts +106 -0
  10. package/dist/analysis/diagnosis-schema.js +54 -0
  11. package/dist/analysis/diagnosis-schema.js.map +1 -0
  12. package/dist/analysis/error-analyzer.d.ts +9 -0
  13. package/dist/analysis/error-analyzer.js +573 -0
  14. package/dist/analysis/error-analyzer.js.map +1 -0
  15. package/dist/analysis/index.d.ts +4 -0
  16. package/dist/analysis/index.js +6 -0
  17. package/dist/analysis/index.js.map +1 -0
  18. package/dist/analysis/source-mapper.d.ts +19 -0
  19. package/dist/analysis/source-mapper.js +329 -0
  20. package/dist/analysis/source-mapper.js.map +1 -0
  21. package/dist/analysis/ui-auditor.d.ts +9 -0
  22. package/dist/analysis/ui-auditor.js +104 -0
  23. package/dist/analysis/ui-auditor.js.map +1 -0
  24. package/dist/artifacts/artifact-storage.d.ts +44 -0
  25. package/dist/artifacts/artifact-storage.js +99 -0
  26. package/dist/artifacts/artifact-storage.js.map +1 -0
  27. package/dist/artifacts/index.d.ts +1 -0
  28. package/dist/artifacts/index.js +3 -0
  29. package/dist/artifacts/index.js.map +1 -0
  30. package/dist/browser/browser-manager.d.ts +45 -0
  31. package/dist/browser/browser-manager.js +88 -0
  32. package/dist/browser/browser-manager.js.map +1 -0
  33. package/dist/browser/challenge-detector.d.ts +10 -0
  34. package/dist/browser/challenge-detector.js +58 -0
  35. package/dist/browser/challenge-detector.js.map +1 -0
  36. package/dist/browser/cookie-dismisser.d.ts +18 -0
  37. package/dist/browser/cookie-dismisser.js +76 -0
  38. package/dist/browser/cookie-dismisser.js.map +1 -0
  39. package/dist/browser/index.d.ts +4 -0
  40. package/dist/browser/index.js +6 -0
  41. package/dist/browser/index.js.map +1 -0
  42. package/dist/browser/stealth-browser.d.ts +13 -0
  43. package/dist/browser/stealth-browser.js +59 -0
  44. package/dist/browser/stealth-browser.js.map +1 -0
  45. package/dist/cli/commander-cli.d.ts +2 -0
  46. package/dist/cli/commander-cli.js +150 -0
  47. package/dist/cli/commander-cli.js.map +1 -0
  48. package/dist/cli/doctor.d.ts +34 -0
  49. package/dist/cli/doctor.js +124 -0
  50. package/dist/cli/doctor.js.map +1 -0
  51. package/dist/cli/first-run.d.ts +6 -0
  52. package/dist/cli/first-run.js +58 -0
  53. package/dist/cli/first-run.js.map +1 -0
  54. package/dist/cli/index.d.ts +3 -0
  55. package/dist/cli/index.js +5 -0
  56. package/dist/cli/index.js.map +1 -0
  57. package/dist/cli/progress.d.ts +11 -0
  58. package/dist/cli/progress.js +30 -0
  59. package/dist/cli/progress.js.map +1 -0
  60. package/dist/core/engine.d.ts +33 -0
  61. package/dist/core/engine.js +269 -0
  62. package/dist/core/engine.js.map +1 -0
  63. package/dist/core/index.d.ts +3 -0
  64. package/dist/core/index.js +4 -0
  65. package/dist/core/index.js.map +1 -0
  66. package/dist/core/validation.d.ts +52 -0
  67. package/dist/core/validation.js +228 -0
  68. package/dist/core/validation.js.map +1 -0
  69. package/dist/discovery/crawler.d.ts +58 -0
  70. package/dist/discovery/crawler.js +240 -0
  71. package/dist/discovery/crawler.js.map +1 -0
  72. package/dist/discovery/discovery-pipeline.d.ts +22 -0
  73. package/dist/discovery/discovery-pipeline.js +256 -0
  74. package/dist/discovery/discovery-pipeline.js.map +1 -0
  75. package/dist/discovery/element-mapper.d.ts +21 -0
  76. package/dist/discovery/element-mapper.js +422 -0
  77. package/dist/discovery/element-mapper.js.map +1 -0
  78. package/dist/discovery/index.d.ts +8 -0
  79. package/dist/discovery/index.js +8 -0
  80. package/dist/discovery/index.js.map +1 -0
  81. package/dist/discovery/link-validator.d.ts +15 -0
  82. package/dist/discovery/link-validator.js +137 -0
  83. package/dist/discovery/link-validator.js.map +1 -0
  84. package/dist/discovery/sitemap-builder.d.ts +19 -0
  85. package/dist/discovery/sitemap-builder.js +166 -0
  86. package/dist/discovery/sitemap-builder.js.map +1 -0
  87. package/dist/discovery/spa-detector.d.ts +12 -0
  88. package/dist/discovery/spa-detector.js +271 -0
  89. package/dist/discovery/spa-detector.js.map +1 -0
  90. package/dist/execution/error-detector.d.ts +10 -0
  91. package/dist/execution/error-detector.js +87 -0
  92. package/dist/execution/error-detector.js.map +1 -0
  93. package/dist/execution/evidence-capture.d.ts +8 -0
  94. package/dist/execution/evidence-capture.js +37 -0
  95. package/dist/execution/evidence-capture.js.map +1 -0
  96. package/dist/execution/index.d.ts +5 -0
  97. package/dist/execution/index.js +7 -0
  98. package/dist/execution/index.js.map +1 -0
  99. package/dist/execution/step-handlers.d.ts +48 -0
  100. package/dist/execution/step-handlers.js +349 -0
  101. package/dist/execution/step-handlers.js.map +1 -0
  102. package/dist/execution/test-data.d.ts +50 -0
  103. package/dist/execution/test-data.js +160 -0
  104. package/dist/execution/test-data.js.map +1 -0
  105. package/dist/execution/workflow-executor.d.ts +56 -0
  106. package/dist/execution/workflow-executor.js +331 -0
  107. package/dist/execution/workflow-executor.js.map +1 -0
  108. package/dist/index.d.ts +2 -0
  109. package/dist/index.js +5 -0
  110. package/dist/index.js.map +1 -0
  111. package/dist/mcp/entry.d.ts +2 -0
  112. package/dist/mcp/entry.js +5 -0
  113. package/dist/mcp/entry.js.map +1 -0
  114. package/dist/mcp/index.d.ts +2 -0
  115. package/dist/mcp/index.js +4 -0
  116. package/dist/mcp/index.js.map +1 -0
  117. package/dist/mcp/server.d.ts +3 -0
  118. package/dist/mcp/server.js +19 -0
  119. package/dist/mcp/server.js.map +1 -0
  120. package/dist/mcp/tools.d.ts +2 -0
  121. package/dist/mcp/tools.js +162 -0
  122. package/dist/mcp/tools.js.map +1 -0
  123. package/dist/planning/heuristic-planner.d.ts +7 -0
  124. package/dist/planning/heuristic-planner.js +238 -0
  125. package/dist/planning/heuristic-planner.js.map +1 -0
  126. package/dist/planning/index.d.ts +3 -0
  127. package/dist/planning/index.js +5 -0
  128. package/dist/planning/index.js.map +1 -0
  129. package/dist/planning/plan-schema.d.ts +74 -0
  130. package/dist/planning/plan-schema.js +39 -0
  131. package/dist/planning/plan-schema.js.map +1 -0
  132. package/dist/planning/workflow-planner.d.ts +39 -0
  133. package/dist/planning/workflow-planner.js +211 -0
  134. package/dist/planning/workflow-planner.js.map +1 -0
  135. package/dist/reports/health-scorer.d.ts +14 -0
  136. package/dist/reports/health-scorer.js +88 -0
  137. package/dist/reports/health-scorer.js.map +1 -0
  138. package/dist/reports/html-generator.d.ts +10 -0
  139. package/dist/reports/html-generator.js +155 -0
  140. package/dist/reports/html-generator.js.map +1 -0
  141. package/dist/reports/index.d.ts +4 -0
  142. package/dist/reports/index.js +6 -0
  143. package/dist/reports/index.js.map +1 -0
  144. package/dist/reports/markdown-generator.d.ts +10 -0
  145. package/dist/reports/markdown-generator.js +334 -0
  146. package/dist/reports/markdown-generator.js.map +1 -0
  147. package/dist/reports/priority-ranker.d.ts +22 -0
  148. package/dist/reports/priority-ranker.js +608 -0
  149. package/dist/reports/priority-ranker.js.map +1 -0
  150. package/dist/screenshots/dual-format.d.ts +14 -0
  151. package/dist/screenshots/dual-format.js +59 -0
  152. package/dist/screenshots/dual-format.js.map +1 -0
  153. package/dist/screenshots/index.d.ts +2 -0
  154. package/dist/screenshots/index.js +4 -0
  155. package/dist/screenshots/index.js.map +1 -0
  156. package/dist/screenshots/screenshot-manager.d.ts +33 -0
  157. package/dist/screenshots/screenshot-manager.js +86 -0
  158. package/dist/screenshots/screenshot-manager.js.map +1 -0
  159. package/dist/testing/accessibility-auditor.d.ts +23 -0
  160. package/dist/testing/accessibility-auditor.js +44 -0
  161. package/dist/testing/accessibility-auditor.js.map +1 -0
  162. package/dist/testing/index.d.ts +4 -0
  163. package/dist/testing/index.js +5 -0
  164. package/dist/testing/index.js.map +1 -0
  165. package/dist/testing/meta-auditor.d.ts +16 -0
  166. package/dist/testing/meta-auditor.js +268 -0
  167. package/dist/testing/meta-auditor.js.map +1 -0
  168. package/dist/testing/performance-monitor.d.ts +15 -0
  169. package/dist/testing/performance-monitor.js +64 -0
  170. package/dist/testing/performance-monitor.js.map +1 -0
  171. package/dist/types/artifacts.d.ts +58 -0
  172. package/dist/types/artifacts.js +3 -0
  173. package/dist/types/artifacts.js.map +1 -0
  174. package/dist/types/discovery.d.ts +124 -0
  175. package/dist/types/discovery.js +3 -0
  176. package/dist/types/discovery.js.map +1 -0
  177. package/dist/types/execution.d.ts +154 -0
  178. package/dist/types/execution.js +3 -0
  179. package/dist/types/execution.js.map +1 -0
  180. package/dist/types/index.d.ts +2 -0
  181. package/dist/types/index.js +4 -0
  182. package/dist/types/index.js.map +1 -0
  183. package/dist/utils/sanitizer.d.ts +25 -0
  184. package/dist/utils/sanitizer.js +98 -0
  185. package/dist/utils/sanitizer.js.map +1 -0
  186. package/package.json +86 -0
  187. package/templates/report.hbs +202 -0
  188. 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":"AAAA,0FAA0F;AAS1F;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,IAAU;IAClD,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;QACjC,MAAM,UAAU,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;QAEvC,6EAA6E;QAC7E,IAAI,UAAU,CAAC,QAAQ,CAAC,eAAe,CAAC,IAAI,UAAU,CAAC,QAAQ,CAAC,oBAAoB,CAAC,EAAE,CAAC;YACtF,OAAO,EAAE,eAAe,EAAE,IAAI,EAAE,QAAQ,EAAE,YAAY,EAAE,CAAC;QAC3D,CAAC;QAED,uDAAuD;QACvD,MAAM,mBAAmB,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE;YACnD,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;YAC7D,MAAM,IAAI,GAAG,QAAQ,CAAC,eAAe,EAAE,SAAS,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;YAEtE,OAAO;gBACL,sBAAsB;gBACtB,cAAc,EAAE,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC,qEAAqE,CAAC;gBAC/G,WAAW,EAAE,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,yBAAyB,CAAC;gBACtF,kBAAkB,EAAE,IAAI,CAAC,QAAQ,CAAC,uBAAuB,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,sBAAsB,CAAC;gBAEnG,qBAAqB;gBACrB,SAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;gBAEtG,wBAAwB;gBACxB,eAAe,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,IAAI,IAAI,CAAC,MAAM,GAAG,IAAI;gBACrG,UAAU,EAAE,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC,0CAA0C,CAAC;gBAEhF,aAAa;gBACb,aAAa,EAAE,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;gBAEpE,UAAU,EAAE,IAAI,CAAC,MAAM;aACxB,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,IAAI,mBAAmB,CAAC,cAAc,IAAI,mBAAmB,CAAC,WAAW,EAAE,CAAC;YAC1E,OAAO,EAAE,eAAe,EAAE,IAAI,EAAE,QAAQ,EAAE,YAAY,EAAE,CAAC;QAC3D,CAAC;QAED,IAAI,mBAAmB,CAAC,kBAAkB,IAAI,mBAAmB,CAAC,UAAU,GAAG,IAAI,EAAE,CAAC;YACpF,OAAO,EAAE,eAAe,EAAE,IAAI,EAAE,QAAQ,EAAE,qBAAqB,EAAE,CAAC;QACpE,CAAC;QAED,IAAI,mBAAmB,CAAC,SAAS,EAAE,CAAC;YAClC,OAAO,EAAE,eAAe,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;QACvD,CAAC;QAED,IAAI,mBAAmB,CAAC,aAAa,EAAE,CAAC;YACtC,OAAO,EAAE,eAAe,EAAE,IAAI,EAAE,QAAQ,EAAE,YAAY,EAAE,CAAC;QAC3D,CAAC;QAED,IAAI,mBAAmB,CAAC,eAAe,EAAE,CAAC;YACxC,OAAO,EAAE,eAAe,EAAE,IAAI,EAAE,QAAQ,EAAE,gBAAgB,EAAE,CAAC;QAC/D,CAAC;QAED,IAAI,mBAAmB,CAAC,UAAU,IAAI,mBAAmB,CAAC,UAAU,GAAG,IAAI,EAAE,CAAC;YAC5E,OAAO,EAAE,eAAe,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;QACxD,CAAC;QAED,OAAO,EAAE,eAAe,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IACpD,CAAC;IAAC,MAAM,CAAC;QACP,uDAAuD;QACvD,OAAO,EAAE,eAAe,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IACpD,CAAC;AACH,CAAC"}
@@ -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,4 @@
1
+ export * from './stealth-browser.js';
2
+ export * from './cookie-dismisser.js';
3
+ export * from './challenge-detector.js';
4
+ export * from './browser-manager.js';
@@ -0,0 +1,6 @@
1
+ // Barrel export for browser module
2
+ export * from './stealth-browser.js';
3
+ export * from './cookie-dismisser.js';
4
+ export * from './challenge-detector.js';
5
+ export * from './browser-manager.js';
6
+ //# sourceMappingURL=index.js.map
@@ -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,2 @@
1
+ import { Command } from 'commander';
2
+ export declare const program: Command;
@@ -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;