explorbot 0.1.32-beta.1 → 0.2.1

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 (222) hide show
  1. package/README.md +35 -19
  2. package/bin/explorbot-cli.ts +33 -10
  3. package/boat/api-tester/src/config.ts +45 -3
  4. package/boat/doc-collector/src/ai/documentarian.ts +26 -28
  5. package/boat/doc-collector/src/ai/tools.ts +74 -16
  6. package/boat/doc-collector/src/cli.ts +1 -6
  7. package/boat/doc-collector/src/docbot.ts +30 -9
  8. package/boat/doc-collector/src/docs-renderer.ts +19 -24
  9. package/boat/doc-collector/src/screenshots.ts +34 -2
  10. package/boat/doc-collector/src/state-diagram.ts +281 -0
  11. package/dist/bin/explorbot-cli.js +29 -9
  12. package/dist/boat/api-tester/src/config.js +43 -4
  13. package/dist/boat/doc-collector/src/ai/documentarian.js +19 -7
  14. package/dist/boat/doc-collector/src/ai/tools.js +53 -16
  15. package/dist/boat/doc-collector/src/cli.js +1 -5
  16. package/dist/boat/doc-collector/src/docbot.js +27 -8
  17. package/dist/boat/doc-collector/src/docs-renderer.js +18 -3
  18. package/dist/boat/doc-collector/src/screenshots.js +30 -1
  19. package/dist/boat/doc-collector/src/state-diagram.js +200 -0
  20. package/dist/models.json +30 -0
  21. package/dist/package.json +23 -3
  22. package/dist/src/action-result.d.ts +8 -10
  23. package/dist/src/action-result.js +72 -164
  24. package/dist/src/action.d.ts +13 -12
  25. package/dist/src/action.js +37 -113
  26. package/dist/src/ai/agent.d.ts +17 -0
  27. package/dist/src/ai/captain/idle-mode.js +1 -1
  28. package/dist/src/ai/captain/test-mode.js +1 -1
  29. package/dist/src/ai/captain/web-mode.js +18 -29
  30. package/dist/src/ai/captain.d.ts +1 -3
  31. package/dist/src/ai/captain.js +17 -45
  32. package/dist/src/ai/conversation.d.ts +2 -5
  33. package/dist/src/ai/conversation.js +9 -28
  34. package/dist/src/ai/driller.d.ts +2 -17
  35. package/dist/src/ai/driller.js +26 -50
  36. package/dist/src/ai/experience-compactor.d.ts +2 -2
  37. package/dist/src/ai/experience-compactor.js +16 -33
  38. package/dist/src/ai/fisherman-tools.js +1 -1
  39. package/dist/src/ai/fisherman.d.ts +0 -1
  40. package/dist/src/ai/fisherman.js +0 -3
  41. package/dist/src/ai/historian/codeceptjs.d.ts +2 -0
  42. package/dist/src/ai/historian/codeceptjs.js +4 -3
  43. package/dist/src/ai/historian/playwright.d.ts +4 -1
  44. package/dist/src/ai/historian/playwright.js +4 -3
  45. package/dist/src/ai/historian/screencast.d.ts +2 -1
  46. package/dist/src/ai/historian/screencast.js +2 -2
  47. package/dist/src/ai/historian/utils.d.ts +0 -1
  48. package/dist/src/ai/historian/utils.js +0 -1
  49. package/dist/src/ai/historian.d.ts +10 -8
  50. package/dist/src/ai/historian.js +1 -5
  51. package/dist/src/ai/navigator.d.ts +8 -6
  52. package/dist/src/ai/navigator.js +34 -65
  53. package/dist/src/ai/pilot.d.ts +13 -8
  54. package/dist/src/ai/pilot.js +53 -59
  55. package/dist/src/ai/planner.d.ts +5 -4
  56. package/dist/src/ai/planner.js +66 -52
  57. package/dist/src/ai/provider.d.ts +4 -1
  58. package/dist/src/ai/provider.js +77 -137
  59. package/dist/src/ai/quartermaster.d.ts +22 -22
  60. package/dist/src/ai/quartermaster.js +8 -14
  61. package/dist/src/ai/rerunner.d.ts +3 -13
  62. package/dist/src/ai/rerunner.js +23 -44
  63. package/dist/src/ai/researcher/cache.js +11 -18
  64. package/dist/src/ai/researcher/coordinates.js +4 -3
  65. package/dist/src/ai/researcher/deep-analysis.d.ts +2 -0
  66. package/dist/src/ai/researcher/deep-analysis.js +8 -11
  67. package/dist/src/ai/researcher/locators.d.ts +9 -2
  68. package/dist/src/ai/researcher/locators.js +65 -8
  69. package/dist/src/ai/researcher/parser.js +1 -1
  70. package/dist/src/ai/researcher/research-result.d.ts +0 -1
  71. package/dist/src/ai/researcher/research-result.js +0 -19
  72. package/dist/src/ai/researcher/sections.d.ts +2 -0
  73. package/dist/src/ai/researcher/sections.js +7 -3
  74. package/dist/src/ai/researcher.js +40 -106
  75. package/dist/src/ai/rules.d.ts +1 -0
  76. package/dist/src/ai/rules.js +22 -15
  77. package/dist/src/ai/session-analyst.js +8 -5
  78. package/dist/src/ai/task-agent.d.ts +19 -6
  79. package/dist/src/ai/task-agent.js +37 -33
  80. package/dist/src/ai/tester.d.ts +6 -14
  81. package/dist/src/ai/tester.js +46 -111
  82. package/dist/src/ai/tools.d.ts +26 -14
  83. package/dist/src/ai/tools.js +138 -132
  84. package/dist/src/command-handler.js +1 -1
  85. package/dist/src/commands/compact-command.d.ts +1 -1
  86. package/dist/src/commands/compact-command.js +6 -6
  87. package/dist/src/commands/context-aria-command.js +1 -1
  88. package/dist/src/commands/context-command.js +8 -6
  89. package/dist/src/commands/context-data-command.js +2 -2
  90. package/dist/src/commands/context-experience-command.js +2 -2
  91. package/dist/src/commands/context-html-command.js +2 -2
  92. package/dist/src/commands/context-knowledge-command.js +2 -2
  93. package/dist/src/commands/drill-command.js +1 -1
  94. package/dist/src/commands/experience-command.js +1 -1
  95. package/dist/src/commands/explore-command.js +4 -3
  96. package/dist/src/commands/freesail-command.js +2 -2
  97. package/dist/src/commands/knows-command.js +1 -1
  98. package/dist/src/commands/learn-command.js +5 -3
  99. package/dist/src/commands/path-command.js +1 -1
  100. package/dist/src/commands/research-command.js +1 -1
  101. package/dist/src/commands/test-command.js +1 -1
  102. package/dist/src/components/AddKnowledge.d.ts +2 -0
  103. package/dist/src/components/AddKnowledge.js +3 -7
  104. package/dist/src/components/App.js +3 -3
  105. package/dist/src/config.d.ts +19 -0
  106. package/dist/src/config.js +156 -10
  107. package/dist/src/experience-tracker.d.ts +11 -6
  108. package/dist/src/experience-tracker.js +82 -155
  109. package/dist/src/explorbot.d.ts +21 -13
  110. package/dist/src/explorbot.js +84 -80
  111. package/dist/src/explorer.d.ts +66 -102
  112. package/dist/src/explorer.js +332 -623
  113. package/dist/src/knowledge-tracker.d.ts +2 -2
  114. package/dist/src/knowledge-tracker.js +38 -45
  115. package/dist/src/state-manager.d.ts +10 -30
  116. package/dist/src/state-manager.js +11 -129
  117. package/dist/src/utils/aria.d.ts +6 -1
  118. package/dist/src/utils/aria.js +4 -1
  119. package/dist/src/utils/cache.d.ts +15 -0
  120. package/dist/src/utils/cache.js +34 -0
  121. package/dist/src/utils/context-formatter.d.ts +6 -0
  122. package/dist/src/utils/context-formatter.js +15 -27
  123. package/dist/src/utils/hooks-runner.js +2 -4
  124. package/dist/src/utils/html.d.ts +5 -0
  125. package/dist/src/utils/html.js +71 -0
  126. package/dist/src/utils/markdown-files.d.ts +10 -0
  127. package/dist/src/utils/markdown-files.js +21 -0
  128. package/dist/src/utils/markdown-query.d.ts +6 -0
  129. package/dist/src/utils/markdown-query.js +14 -1
  130. package/dist/src/utils/page-readiness.d.ts +1 -0
  131. package/dist/src/utils/page-readiness.js +1 -1
  132. package/dist/src/utils/secrets.js +2 -2
  133. package/dist/src/utils/strings.d.ts +2 -0
  134. package/dist/src/utils/strings.js +10 -0
  135. package/dist/src/utils/test-files.js +1 -1
  136. package/dist/src/utils/web-annotate.d.ts +5 -0
  137. package/dist/src/utils/web-annotate.js +58 -0
  138. package/dist/src/utils/web-eidx.d.ts +2 -0
  139. package/dist/src/utils/web-eidx.js +20 -0
  140. package/dist/src/utils/web-element.d.ts +3 -1
  141. package/dist/src/utils/web-element.js +32 -0
  142. package/dist/src/utils/web-sandbox.d.ts +4 -0
  143. package/dist/src/utils/web-sandbox.js +37 -0
  144. package/models.json +30 -0
  145. package/package.json +23 -3
  146. package/src/action-result.ts +70 -173
  147. package/src/action.ts +38 -132
  148. package/src/ai/agent.ts +20 -0
  149. package/src/ai/captain/idle-mode.ts +1 -1
  150. package/src/ai/captain/test-mode.ts +1 -1
  151. package/src/ai/captain/web-mode.ts +18 -30
  152. package/src/ai/captain.ts +17 -42
  153. package/src/ai/conversation.ts +9 -32
  154. package/src/ai/driller.ts +53 -83
  155. package/src/ai/experience-compactor.ts +16 -35
  156. package/src/ai/fisherman-tools.ts +1 -1
  157. package/src/ai/fisherman.ts +0 -4
  158. package/src/ai/historian/codeceptjs.ts +5 -3
  159. package/src/ai/historian/playwright.ts +7 -4
  160. package/src/ai/historian/screencast.ts +4 -3
  161. package/src/ai/historian/utils.ts +0 -1
  162. package/src/ai/historian.ts +6 -8
  163. package/src/ai/navigator.ts +39 -70
  164. package/src/ai/pilot.ts +59 -61
  165. package/src/ai/planner.ts +76 -62
  166. package/src/ai/provider.ts +85 -146
  167. package/src/ai/quartermaster.ts +39 -45
  168. package/src/ai/rerunner.ts +27 -50
  169. package/src/ai/researcher/cache.ts +10 -16
  170. package/src/ai/researcher/coordinates.ts +4 -3
  171. package/src/ai/researcher/deep-analysis.ts +10 -11
  172. package/src/ai/researcher/locators.ts +66 -9
  173. package/src/ai/researcher/parser.ts +1 -1
  174. package/src/ai/researcher/research-result.ts +0 -18
  175. package/src/ai/researcher/sections.ts +9 -3
  176. package/src/ai/researcher.ts +42 -119
  177. package/src/ai/rules.ts +24 -13
  178. package/src/ai/session-analyst.ts +8 -5
  179. package/src/ai/task-agent.ts +45 -38
  180. package/src/ai/tester.ts +51 -129
  181. package/src/ai/tools.ts +145 -153
  182. package/src/command-handler.ts +1 -1
  183. package/src/commands/compact-command.ts +7 -8
  184. package/src/commands/context-aria-command.ts +1 -1
  185. package/src/commands/context-command.ts +8 -6
  186. package/src/commands/context-data-command.ts +2 -2
  187. package/src/commands/context-experience-command.ts +2 -2
  188. package/src/commands/context-html-command.ts +2 -2
  189. package/src/commands/context-knowledge-command.ts +2 -2
  190. package/src/commands/drill-command.ts +1 -1
  191. package/src/commands/experience-command.ts +1 -1
  192. package/src/commands/explore-command.ts +4 -3
  193. package/src/commands/freesail-command.ts +2 -2
  194. package/src/commands/knows-command.ts +1 -1
  195. package/src/commands/learn-command.ts +5 -3
  196. package/src/commands/path-command.ts +1 -1
  197. package/src/commands/research-command.ts +1 -1
  198. package/src/commands/test-command.ts +1 -1
  199. package/src/components/AddKnowledge.tsx +5 -7
  200. package/src/components/App.tsx +3 -3
  201. package/src/config.ts +185 -11
  202. package/src/experience-tracker.ts +89 -152
  203. package/src/explorbot.ts +88 -83
  204. package/src/explorer.ts +402 -693
  205. package/src/knowledge-tracker.ts +42 -49
  206. package/src/state-manager.ts +20 -155
  207. package/src/utils/aria.ts +11 -2
  208. package/src/utils/cache.ts +40 -0
  209. package/src/utils/context-formatter.ts +12 -15
  210. package/src/utils/hooks-runner.ts +2 -4
  211. package/src/utils/html.ts +79 -0
  212. package/src/utils/markdown-files.ts +30 -0
  213. package/src/utils/markdown-query.ts +15 -1
  214. package/src/utils/page-readiness.ts +1 -1
  215. package/src/utils/secrets.ts +2 -3
  216. package/src/utils/strings.ts +12 -0
  217. package/src/utils/test-files.ts +1 -1
  218. package/src/utils/web-annotate.ts +64 -0
  219. package/src/utils/web-eidx.ts +21 -0
  220. package/src/utils/web-element.ts +34 -0
  221. package/src/utils/web-sandbox.ts +43 -0
  222. package/dist/boat/api-tester/example/apibot.config.js +0 -30
@@ -1,8 +1,8 @@
1
1
  import { basename } from 'node:path';
2
2
  import chalk from 'chalk';
3
+ import { COMPACT_MAX_LENGTH } from '../ai/experience-compactor.js';
3
4
  import { tag } from '../utils/logger.js';
4
5
  import { BaseCommand } from './base-command.js';
5
- const COMPACT_THRESHOLD = 5000;
6
6
  export class CompactCommand extends BaseCommand {
7
7
  name = 'compact';
8
8
  description = 'Compact stored experience files; optionally filtered by filename or URL substring';
@@ -16,7 +16,7 @@ export class CompactCommand extends BaseCommand {
16
16
  const target = remaining[0];
17
17
  const dryRun = !!opts.dryRun;
18
18
  const merge = opts.merge !== false;
19
- const tracker = this.explorBot.getExperienceTracker();
19
+ const tracker = this.explorBot.experienceTracker();
20
20
  const files = this.resolveTarget(tracker, target);
21
21
  if (files === null) {
22
22
  tag('info').log('No experience files found.');
@@ -51,7 +51,7 @@ export class CompactCommand extends BaseCommand {
51
51
  buildDryRunSuggestions(compactor, files, target) {
52
52
  const wouldTouch = files.filter((f) => {
53
53
  const stripped = compactor.stripNonUsefulEntries(f.content);
54
- return stripped !== f.content || stripped.length >= COMPACT_THRESHOLD || compactor.isRecent(f);
54
+ return stripped !== f.content || stripped.length >= COMPACT_MAX_LENGTH || compactor.isRecent(f);
55
55
  });
56
56
  if (wouldTouch.length === 0)
57
57
  return [];
@@ -62,7 +62,7 @@ export class CompactCommand extends BaseCommand {
62
62
  async runFullSweep(compactor, merge) {
63
63
  if (!merge) {
64
64
  tag('info').log('Compacting all experience files (merge skipped)…');
65
- const compacted = await compactor.compactFiles(this.explorBot.getExperienceTracker().getAllExperience());
65
+ const compacted = await compactor.compactFiles(this.explorBot.experienceTracker().getAllExperience());
66
66
  return { merged: 0, compacted };
67
67
  }
68
68
  tag('info').log('Merging experience files with similar URLs, then compacting all…');
@@ -101,7 +101,7 @@ export class CompactCommand extends BaseCommand {
101
101
  const charsRemoved = chars - strippedChars;
102
102
  const willStrip = stripped !== file.content;
103
103
  const willReview = compactor.isRecent(file);
104
- const willAi = strippedChars >= COMPACT_THRESHOLD;
104
+ const willAi = strippedChars >= COMPACT_MAX_LENGTH;
105
105
  if (willStrip)
106
106
  wouldStrip++;
107
107
  if (willReview)
@@ -131,7 +131,7 @@ export class CompactCommand extends BaseCommand {
131
131
  lines.push(` ${chalk.dim('Files matched:')} ${chalk.bold(String(files.length))}`);
132
132
  lines.push(` ${chalk.dim('Would strip:')} ${chalk.bold(String(wouldStrip))} ${chalk.dim('(remove legacy / FAILED / Visual click, rename to FLOW/ACTION)')}`);
133
133
  lines.push(` ${chalk.dim('Would ai-review:')} ${chalk.bold(String(wouldAiReview))} ${chalk.dim('(recent files ≤30d — quality/reusability check)')}`);
134
- lines.push(` ${chalk.dim('Would ai-compact:')} ${chalk.bold(String(wouldAiCompact))} ${chalk.dim(`(over ${COMPACT_THRESHOLD} chars after strip)`)}`);
134
+ lines.push(` ${chalk.dim('Would ai-compact:')} ${chalk.bold(String(wouldAiCompact))} ${chalk.dim(`(over ${COMPACT_MAX_LENGTH} chars after strip)`)}`);
135
135
  lines.push(` ${chalk.dim('Total chars:')} ${chalk.bold(String(totalChars))}`);
136
136
  tag('info').log(lines.join('\n'));
137
137
  }
@@ -4,7 +4,7 @@ export class ContextAriaCommand extends BaseCommand {
4
4
  name = 'context:aria';
5
5
  description = 'Print full ARIA snapshot for current page';
6
6
  async execute(_args) {
7
- const state = this.explorBot.getExplorer().getStateManager().getCurrentState();
7
+ const state = this.explorBot.stateManager().getCurrentState();
8
8
  if (!state) {
9
9
  throw new Error('No active page to snapshot');
10
10
  }
@@ -1,8 +1,10 @@
1
1
  import { Researcher } from '../ai/researcher.js';
2
+ import { visuallyAnnotateContainers } from '../ai/researcher/coordinates.js';
2
3
  import { outputPath } from '../config.js';
3
4
  import { formatContextSummary } from '../utils/context-formatter.js';
4
5
  import { tag } from '../utils/logger.js';
5
6
  import { extractValidContainers } from '../utils/research-parser.js';
7
+ import { annotatePageElements } from '../utils/web-annotate.js';
6
8
  import { BaseCommand } from './base-command.js';
7
9
  export class ContextCommand extends BaseCommand {
8
10
  name = 'context';
@@ -22,21 +24,21 @@ export class ContextCommand extends BaseCommand {
22
24
  ];
23
25
  async execute(args) {
24
26
  const explorer = this.explorBot.getExplorer();
25
- const state = explorer.getStateManager().getCurrentState();
27
+ const state = this.explorBot.stateManager().getCurrentState();
26
28
  if (!state) {
27
29
  throw new Error('No active page to show context for');
28
30
  }
29
31
  const { opts } = this.parseArgs(args);
30
32
  const isVisual = !!(opts.visual || opts.screenshot);
31
- await explorer.annotateElements();
33
+ await explorer.withPage(annotatePageElements);
32
34
  if (isVisual) {
33
35
  const cachedResearch = Researcher.getCachedResearch(state);
34
36
  const containers = cachedResearch ? extractValidContainers(cachedResearch) : [];
35
- await explorer.visuallyAnnotateElements({ containers });
37
+ await explorer.withPage((page) => visuallyAnnotateContainers(page, containers));
36
38
  }
37
- const actionResult = await explorer.createAction().capturePageState({ includeScreenshot: isVisual });
38
- const experienceTracker = explorer.getStateManager().getExperienceTracker();
39
- const knowledgeTracker = this.explorBot.getKnowledgeTracker();
39
+ const actionResult = await explorer.capture({ screenshot: isVisual });
40
+ const experienceTracker = this.explorBot.experienceTracker();
41
+ const knowledgeTracker = this.explorBot.knowledgeTracker();
40
42
  let mode = 'compact';
41
43
  if (opts.full) {
42
44
  mode = 'full';
@@ -6,14 +6,14 @@ export class ContextDataCommand extends BaseCommand {
6
6
  description = 'Extract structured data from current page';
7
7
  async execute(_args) {
8
8
  const explorer = this.explorBot.getExplorer();
9
- const state = explorer.getStateManager().getCurrentState();
9
+ const state = this.explorBot.stateManager().getCurrentState();
10
10
  if (!state) {
11
11
  throw new Error('No active page to extract data from');
12
12
  }
13
13
  const actionResult = ActionResult.fromState(state);
14
14
  if (!actionResult.html || actionResult.html.trim().length < 100) {
15
15
  tag('info').log('Capturing fresh page content...');
16
- const freshResult = await explorer.createAction().capturePageState();
16
+ const freshResult = await explorer.capture();
17
17
  const table = await this.explorBot.agentResearcher().extractData(freshResult);
18
18
  tag('multiline').log(table);
19
19
  return;
@@ -8,12 +8,12 @@ export class ContextExperienceCommand extends BaseCommand {
8
8
  description = 'Print all matching experience for current page';
9
9
  async execute(_args) {
10
10
  const explorer = this.explorBot.getExplorer();
11
- const state = explorer.getStateManager().getCurrentState();
11
+ const state = this.explorBot.stateManager().getCurrentState();
12
12
  if (!state) {
13
13
  throw new Error('No active page');
14
14
  }
15
15
  const actionResult = ActionResult.fromState(state);
16
- const experienceTracker = explorer.getStateManager().getExperienceTracker();
16
+ const experienceTracker = this.explorBot.experienceTracker();
17
17
  const experience = experienceTracker.getRelevantExperience(actionResult);
18
18
  if (experience.length === 0) {
19
19
  tag('info').log(`No experience found for: ${actionResult.url}`);
@@ -6,7 +6,7 @@ export class ContextHtmlCommand extends BaseCommand {
6
6
  description = 'Print combined HTML snapshot for current page';
7
7
  async execute(_args) {
8
8
  const explorer = this.explorBot.getExplorer();
9
- const manager = explorer.getStateManager();
9
+ const manager = this.explorBot.stateManager();
10
10
  const state = manager.getCurrentState();
11
11
  if (!state) {
12
12
  throw new Error('No active page to snapshot');
@@ -14,7 +14,7 @@ export class ContextHtmlCommand extends BaseCommand {
14
14
  let actionResult = ActionResult.fromState(state);
15
15
  if (!actionResult.html || actionResult.html.trim().length < 100) {
16
16
  tag('info').log('Capturing fresh page content...');
17
- actionResult = await explorer.createAction().capturePageState();
17
+ actionResult = await explorer.capture();
18
18
  }
19
19
  const html = await actionResult.combinedHtml();
20
20
  if (!html) {
@@ -8,12 +8,12 @@ export class ContextKnowledgeCommand extends BaseCommand {
8
8
  description = 'Print all matching knowledge for current page';
9
9
  async execute(_args) {
10
10
  const explorer = this.explorBot.getExplorer();
11
- const state = explorer.getStateManager().getCurrentState();
11
+ const state = this.explorBot.stateManager().getCurrentState();
12
12
  if (!state) {
13
13
  throw new Error('No active page');
14
14
  }
15
15
  const actionResult = ActionResult.fromState(state);
16
- const knowledgeTracker = this.explorBot.getKnowledgeTracker();
16
+ const knowledgeTracker = this.explorBot.knowledgeTracker();
17
17
  const knowledge = knowledgeTracker.getRelevantKnowledge(actionResult);
18
18
  if (knowledge.length === 0) {
19
19
  tag('info').log(`No knowledge found for: ${actionResult.url}`);
@@ -10,7 +10,7 @@ export class DrillCommand extends BaseCommand {
10
10
  async execute(args) {
11
11
  const knowledgePath = this.parseKnowledgeArg(args);
12
12
  const maxComponents = this.parseMaxArg(args);
13
- const state = this.explorBot.getExplorer().getStateManager().getCurrentState();
13
+ const state = this.explorBot.stateManager().getCurrentState();
14
14
  if (!state) {
15
15
  throw new Error('No active page to drill');
16
16
  }
@@ -16,7 +16,7 @@ export class ExperienceCommand extends BaseCommand {
16
16
  return;
17
17
  }
18
18
  const recency = opts.recent ? 'recent' : opts.old ? 'old' : undefined;
19
- const tracker = this.explorBot.getExperienceTracker();
19
+ const tracker = this.explorBot.experienceTracker();
20
20
  const [first, second] = remaining;
21
21
  const combinedRef = first?.match(/^([A-Z]+)[.\-]?(\d+)$/i);
22
22
  if (combinedRef) {
@@ -4,6 +4,7 @@ import { outputPath } from '../config.js';
4
4
  import { normalizeUrl } from '../state-manager.js';
5
5
  import { Stats } from '../stats.js';
6
6
  import { TestResult } from '../test-plan.js';
7
+ import { browserErrorMessage } from "../utils/browser-errors.js";
7
8
  import { getCliName } from "../utils/cli-name.js";
8
9
  import { ErrorPageError, getStateErrorPageError } from "../utils/error-page.js";
9
10
  import { tag } from '../utils/logger.js';
@@ -34,7 +35,7 @@ export class ExploreCommand extends BaseCommand {
34
35
  oldTestRefs = new Set();
35
36
  priorityFilter;
36
37
  getCurrentPageUrl() {
37
- const state = this.explorBot.getExplorer().getStateManager().getCurrentState();
38
+ const state = this.explorBot.stateManager().getCurrentState();
38
39
  return state?.fullUrl || state?.url;
39
40
  }
40
41
  async execute(args) {
@@ -53,7 +54,7 @@ export class ExploreCommand extends BaseCommand {
53
54
  Stats.mode ??= 'explore';
54
55
  Stats.focus ??= feature;
55
56
  const mainUrl = this.getCurrentPageUrl();
56
- const error = getStateErrorPageError(this.explorBot.getExplorer().getStateManager().getCurrentState());
57
+ const error = getStateErrorPageError(this.explorBot.stateManager().getCurrentState());
57
58
  if (error) {
58
59
  tag('warning').log(error.message);
59
60
  return;
@@ -72,7 +73,7 @@ export class ExploreCommand extends BaseCommand {
72
73
  return;
73
74
  }
74
75
  if (mainUrl)
75
- await this.explorBot.visit(mainUrl);
76
+ await this.explorBot.visit(mainUrl).catch((err) => tag('warning').log(`Could not return to ${mainUrl}: ${browserErrorMessage(err)}`));
76
77
  const savedPath = this.explorBot.savePlans(this.completedPlans);
77
78
  this.printResults();
78
79
  await this.explorBot.printSessionAnalysis();
@@ -31,7 +31,7 @@ export class FreesailCommand extends BaseCommand {
31
31
  await loop(async (ctx) => {
32
32
  if (maxTests != null && testsRun >= maxTests)
33
33
  ctx.stop();
34
- const stateManager = this.explorBot.getExplorer().getStateManager();
34
+ const stateManager = this.explorBot.stateManager();
35
35
  const state = stateManager.getCurrentState();
36
36
  if (state && !Researcher.getCachedResearch(state)) {
37
37
  await this.explorBot.agentResearcher().research(state, { deep: true, screenshot: true });
@@ -63,7 +63,7 @@ export class FreesailCommand extends BaseCommand {
63
63
  return;
64
64
  }
65
65
  tag('info').log(`Navigating to: ${suggestion.target} - ${suggestion.reason}`);
66
- await this.explorBot.openFreshTab();
66
+ await this.explorBot.openTab();
67
67
  await this.explorBot.visit(suggestion.target);
68
68
  this.explorBot.clearPlan();
69
69
  }, { maxAttempts: Number.POSITIVE_INFINITY });
@@ -17,7 +17,7 @@ export class KnowsCommand extends BaseCommand {
17
17
  description = 'List all knowledge URLs or show matching knowledge for a URL';
18
18
  async execute(args, limit = 0) {
19
19
  const url = args.trim();
20
- const tracker = this.explorBot.getKnowledgeTracker();
20
+ const tracker = this.explorBot.knowledgeTracker();
21
21
  if (!url) {
22
22
  const allKnowledge = tracker.listAllKnowledge();
23
23
  if (allKnowledge.length === 0) {
@@ -10,10 +10,12 @@ export class LearnCommand extends BaseCommand {
10
10
  const note = args.trim();
11
11
  if (!note) {
12
12
  const AddKnowledge = (await import('../components/AddKnowledge.js')).default;
13
- const state = this.explorBot.getExplorer().getStateManager().getCurrentState();
13
+ const explorer = this.explorBot.getExplorer();
14
+ const state = this.explorBot.stateManager().getCurrentState();
14
15
  const initialUrl = state?.url || '';
15
16
  const { unmount } = render(React.createElement(AddKnowledge, {
16
17
  initialUrl,
18
+ knowledgeTracker: this.explorBot.knowledgeTracker(),
17
19
  onComplete: () => unmount(),
18
20
  onCancel: () => unmount(),
19
21
  }), {
@@ -23,12 +25,12 @@ export class LearnCommand extends BaseCommand {
23
25
  return;
24
26
  }
25
27
  const explorer = this.explorBot.getExplorer();
26
- const state = explorer.getStateManager().getCurrentState();
28
+ const state = this.explorBot.stateManager().getCurrentState();
27
29
  if (!state) {
28
30
  throw new Error('No active page to attach knowledge');
29
31
  }
30
32
  const targetUrl = state.url || state.fullUrl || '/';
31
- explorer.getKnowledgeTracker().addKnowledge(targetUrl, note);
33
+ this.explorBot.knowledgeTracker().addKnowledge(targetUrl, note);
32
34
  tag('success').log('Knowledge saved for current page');
33
35
  }
34
36
  }
@@ -7,7 +7,7 @@ export class PathCommand extends BaseCommand {
7
7
  async execute(args) {
8
8
  const { opts } = this.parseArgs(args);
9
9
  const showLinks = !!opts.links;
10
- const stateManager = this.explorBot.getExplorer().getStateManager();
10
+ const stateManager = this.explorBot.stateManager();
11
11
  const history = stateManager.getStateHistory();
12
12
  if (history.length === 0) {
13
13
  tag('multiline').log('No navigation history yet.');
@@ -23,7 +23,7 @@ export class ResearchCommand extends BaseCommand {
23
23
  if (target) {
24
24
  await this.explorBot.agentNavigator().visit(target);
25
25
  }
26
- const state = this.explorBot.getExplorer().getStateManager().getCurrentState();
26
+ const state = this.explorBot.stateManager().getCurrentState();
27
27
  if (!state) {
28
28
  throw new Error('No active page to research');
29
29
  }
@@ -48,7 +48,7 @@ export class TestCommand extends BaseCommand {
48
48
  toExecute.push(...matching);
49
49
  }
50
50
  else {
51
- const state = this.explorBot.getExplorer().getStateManager().getCurrentState();
51
+ const state = this.explorBot.stateManager().getCurrentState();
52
52
  if (!state) {
53
53
  throw new Error('No page loaded. Please navigate to a page first.');
54
54
  }
@@ -1,6 +1,8 @@
1
1
  import React from 'react';
2
+ import type { KnowledgeTracker } from '../knowledge-tracker.js';
2
3
  interface AddKnowledgeProps {
3
4
  initialUrl?: string;
5
+ knowledgeTracker: KnowledgeTracker;
4
6
  onComplete?: () => void;
5
7
  onCancel?: () => void;
6
8
  }
@@ -1,8 +1,7 @@
1
1
  import { Box, Text, useInput } from 'ink';
2
2
  import React, { useEffect, useState } from 'react';
3
- import { KnowledgeTracker } from '../knowledge-tracker.js';
4
3
  import InputReadline from './InputReadline.js';
5
- const AddKnowledge = ({ initialUrl = '', onComplete, onCancel }) => {
4
+ const AddKnowledge = ({ initialUrl = '', knowledgeTracker, onComplete, onCancel }) => {
6
5
  const [urlPattern, setUrlPattern] = useState(initialUrl);
7
6
  const [description, setDescription] = useState('');
8
7
  const [activeField, setActiveField] = useState(initialUrl ? 'description' : 'url');
@@ -11,18 +10,16 @@ const AddKnowledge = ({ initialUrl = '', onComplete, onCancel }) => {
11
10
  const isStandalone = !onComplete;
12
11
  useEffect(() => {
13
12
  try {
14
- const knowledgeTracker = new KnowledgeTracker();
15
13
  const urls = knowledgeTracker.getExistingUrls();
16
14
  setSuggestedUrls(urls);
17
15
  }
18
16
  catch (error) {
19
17
  console.error('Failed to load suggestions:', error);
20
18
  }
21
- }, []);
19
+ }, [knowledgeTracker]);
22
20
  useEffect(() => {
23
21
  if (urlPattern.trim()) {
24
22
  try {
25
- const knowledgeTracker = new KnowledgeTracker();
26
23
  const knowledge = knowledgeTracker.getKnowledgeForUrl(urlPattern);
27
24
  setExistingKnowledge(knowledge);
28
25
  }
@@ -34,13 +31,12 @@ const AddKnowledge = ({ initialUrl = '', onComplete, onCancel }) => {
34
31
  else {
35
32
  setExistingKnowledge([]);
36
33
  }
37
- }, [urlPattern]);
34
+ }, [urlPattern, knowledgeTracker]);
38
35
  const handleSave = () => {
39
36
  if (!urlPattern.trim() || !description.trim()) {
40
37
  return;
41
38
  }
42
39
  try {
43
- const knowledgeTracker = new KnowledgeTracker();
44
40
  const result = knowledgeTracker.addKnowledge(urlPattern.trim(), description.trim());
45
41
  const action = result.isNewFile ? 'Created' : 'Updated';
46
42
  console.log(`\nKnowledge ${action} in: ${result.filename}`);
@@ -47,7 +47,7 @@ export function App({ explorBot, initialShowInput = false, exitOnEmptyInput = fa
47
47
  userInputPromiseRef.current = { resolve, reject };
48
48
  });
49
49
  });
50
- const manager = explorBot.getExplorer().getStateManager();
50
+ const manager = explorBot.stateManager();
51
51
  unsubscribe = manager.onStateChange((transition) => {
52
52
  if (mounted) {
53
53
  setLastTransition(transition);
@@ -111,7 +111,7 @@ export function App({ explorBot, initialShowInput = false, exitOnEmptyInput = fa
111
111
  return;
112
112
  setChecklistData({
113
113
  config: explorBot.getConfig(),
114
- knowledgeTracker: explorBot.getKnowledgeTracker(),
114
+ knowledgeTracker: explorBot.knowledgeTracker(),
115
115
  });
116
116
  setShowWelcome(true);
117
117
  setShowInput(true);
@@ -283,7 +283,7 @@ export function App({ explorBot, initialShowInput = false, exitOnEmptyInput = fa
283
283
  React.createElement(Box, { height: 3, flexDirection: "row", justifyContent: "space-between", alignItems: "center", paddingX: 1 },
284
284
  React.createElement(ActivityPane, { isInputVisible: showInput }),
285
285
  showSessionTimer && React.createElement(SessionTimer, { startedAt: sessionStartedAtRef.current })),
286
- showWelcome && React.createElement(WelcomeCommands, { hasKnowledge: explorBot.getKnowledgeTracker().listAllKnowledge().length > 0 }),
286
+ showWelcome && React.createElement(WelcomeCommands, { hasKnowledge: explorBot.knowledgeTracker().listAllKnowledge().length > 0 }),
287
287
  showInput && React.createElement(Box, { height: 1 }),
288
288
  interruptPrompt && showInput && (React.createElement(Box, { paddingX: 1 },
289
289
  React.createElement(Text, { color: "yellow" }, interruptPrompt))),
@@ -1,3 +1,4 @@
1
+ export declare const PROVIDERS: Record<string, () => Promise<(modelId: string) => any>>;
1
2
  interface PlaywrightConfig {
2
3
  browser: 'chromium' | 'firefox' | 'webkit';
3
4
  url: string;
@@ -204,6 +205,7 @@ interface ExplorbotConfig {
204
205
  };
205
206
  experience?: {
206
207
  maxReadLines?: number;
208
+ disabled?: boolean;
207
209
  };
208
210
  reporter?: ReporterConfig;
209
211
  api?: ApiConfig;
@@ -213,14 +215,17 @@ interface ExplorbotConfig {
213
215
  }
214
216
  type RuleEntry = string | Record<string, string>;
215
217
  export declare const EXPLORBOT_CONFIG_PATHS: string[];
218
+ export declare const EXPLORBOT_ENV_VARS: EnvVar[];
216
219
  export type { ExplorbotConfig, PlaywrightConfig, AIConfig, HtmlConfig, ActionConfig, AgentConfig, AgentsConfig, HistorianAgentConfig, ResearcherAgentConfig, NavigatorAgentConfig, PlannerAgentConfig, RerunnerAgentConfig, HealRecipe, Hook, HookConfig, HooksConfig, PlaywrightHook, CodeceptJSHook, HookPatternMap, RuleEntry, ReporterConfig, ApiConfig, WebConfig, ApiHookFn, };
217
220
  export declare class ConfigParser {
218
221
  static instance: ConfigParser;
222
+ static recommended: Record<string, Record<string, string>> | null;
219
223
  config: ExplorbotConfig | null;
220
224
  configPath: string | null;
221
225
  runtimeBaseUrlOverride: string | null;
222
226
  constructor();
223
227
  static loadEnv(filePath: string): void;
228
+ static recommendedModels(): Record<string, Record<string, string>>;
224
229
  static getInstance(): ConfigParser;
225
230
  loadConfig(options?: {
226
231
  config?: string;
@@ -230,6 +235,8 @@ export declare class ConfigParser {
230
235
  getConfig(): ExplorbotConfig;
231
236
  getConfigPath(): string | null;
232
237
  getOutputDir(): string;
238
+ getProjectRoot(): string;
239
+ resolveProjectDir(relativeDir: string): string;
233
240
  getStatesDir(): string;
234
241
  getPlansDir(): string;
235
242
  getTestsDir(): string;
@@ -237,6 +244,7 @@ export declare class ConfigParser {
237
244
  static setupTestConfig(): void;
238
245
  static getTestDirectories(): string[];
239
246
  static cleanupAllTestDirectories(): void;
247
+ buildEnvConfig(baseUrl: string | undefined, outputRoot: string): Promise<ExplorbotConfig>;
240
248
  findConfigFile(): string | null;
241
249
  loadConfigModule(configPath: string): Promise<any>;
242
250
  resolveConfig(config: ExplorbotConfig, options?: {
@@ -249,3 +257,14 @@ export declare class ConfigParser {
249
257
  ensureDirectory(path: string): void;
250
258
  }
251
259
  export declare function outputPath(...segments: string[]): string;
260
+ export declare function resolveModel(spec: string, role?: ModelRole): Promise<any>;
261
+ export declare function resolveOutputRoot(): string;
262
+ export declare function materializeKnowledge(outputRoot: string): void;
263
+ export declare function createModel(provider: string, modelId: string): Promise<any>;
264
+ type ModelRole = 'model' | 'visionModel' | 'agenticModel';
265
+ interface EnvVar {
266
+ name: string;
267
+ description: string;
268
+ required?: boolean;
269
+ }
270
+ export type { ModelRole, EnvVar };