explorbot 0.2.0 → 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 (141) hide show
  1. package/README.md +21 -5
  2. package/bin/explorbot-cli.ts +29 -7
  3. package/boat/api-tester/src/config.ts +45 -3
  4. package/boat/doc-collector/src/ai/documentarian.ts +5 -2
  5. package/boat/doc-collector/src/ai/tools.ts +12 -11
  6. package/boat/doc-collector/src/cli.ts +1 -6
  7. package/boat/doc-collector/src/docbot.ts +3 -3
  8. package/boat/doc-collector/src/screenshots.ts +2 -2
  9. package/dist/bin/explorbot-cli.js +26 -6
  10. package/dist/boat/api-tester/src/config.js +43 -4
  11. package/dist/boat/doc-collector/src/ai/documentarian.js +4 -2
  12. package/dist/boat/doc-collector/src/ai/tools.js +11 -11
  13. package/dist/boat/doc-collector/src/cli.js +1 -5
  14. package/dist/boat/doc-collector/src/docbot.js +3 -3
  15. package/dist/boat/doc-collector/src/screenshots.js +2 -2
  16. package/dist/models.json +30 -0
  17. package/dist/package.json +5 -2
  18. package/dist/src/action.d.ts +11 -3
  19. package/dist/src/action.js +22 -5
  20. package/dist/src/ai/agent.d.ts +17 -0
  21. package/dist/src/ai/captain/idle-mode.js +1 -1
  22. package/dist/src/ai/captain/test-mode.js +1 -1
  23. package/dist/src/ai/captain/web-mode.js +18 -22
  24. package/dist/src/ai/captain.js +13 -26
  25. package/dist/src/ai/driller.d.ts +2 -13
  26. package/dist/src/ai/driller.js +23 -38
  27. package/dist/src/ai/historian/codeceptjs.js +1 -1
  28. package/dist/src/ai/historian/playwright.d.ts +3 -2
  29. package/dist/src/ai/historian/playwright.js +1 -1
  30. package/dist/src/ai/historian/screencast.d.ts +2 -1
  31. package/dist/src/ai/historian/screencast.js +2 -2
  32. package/dist/src/ai/historian.d.ts +5 -4
  33. package/dist/src/ai/navigator.d.ts +6 -2
  34. package/dist/src/ai/navigator.js +26 -22
  35. package/dist/src/ai/pilot.d.ts +13 -6
  36. package/dist/src/ai/pilot.js +49 -44
  37. package/dist/src/ai/planner.d.ts +3 -2
  38. package/dist/src/ai/planner.js +19 -17
  39. package/dist/src/ai/provider.js +0 -1
  40. package/dist/src/ai/quartermaster.d.ts +3 -2
  41. package/dist/src/ai/quartermaster.js +4 -4
  42. package/dist/src/ai/rerunner.d.ts +2 -11
  43. package/dist/src/ai/rerunner.js +9 -22
  44. package/dist/src/ai/researcher/coordinates.js +4 -3
  45. package/dist/src/ai/researcher/deep-analysis.d.ts +2 -0
  46. package/dist/src/ai/researcher/deep-analysis.js +6 -6
  47. package/dist/src/ai/researcher/locators.d.ts +9 -2
  48. package/dist/src/ai/researcher/locators.js +65 -8
  49. package/dist/src/ai/researcher/sections.d.ts +2 -0
  50. package/dist/src/ai/researcher/sections.js +2 -2
  51. package/dist/src/ai/researcher.js +29 -54
  52. package/dist/src/ai/task-agent.d.ts +18 -5
  53. package/dist/src/ai/task-agent.js +35 -6
  54. package/dist/src/ai/tester.d.ts +6 -12
  55. package/dist/src/ai/tester.js +37 -51
  56. package/dist/src/ai/tools.d.ts +11 -10
  57. package/dist/src/ai/tools.js +24 -36
  58. package/dist/src/command-handler.js +1 -1
  59. package/dist/src/commands/context-aria-command.js +1 -1
  60. package/dist/src/commands/context-command.js +7 -5
  61. package/dist/src/commands/context-data-command.js +2 -2
  62. package/dist/src/commands/context-experience-command.js +2 -2
  63. package/dist/src/commands/context-html-command.js +2 -2
  64. package/dist/src/commands/context-knowledge-command.js +1 -1
  65. package/dist/src/commands/drill-command.js +1 -1
  66. package/dist/src/commands/explore-command.js +4 -3
  67. package/dist/src/commands/freesail-command.js +2 -2
  68. package/dist/src/commands/learn-command.js +4 -4
  69. package/dist/src/commands/path-command.js +1 -1
  70. package/dist/src/commands/research-command.js +1 -1
  71. package/dist/src/commands/test-command.js +1 -1
  72. package/dist/src/components/App.js +1 -1
  73. package/dist/src/config.d.ts +17 -0
  74. package/dist/src/config.js +144 -10
  75. package/dist/src/experience-tracker.js +1 -1
  76. package/dist/src/explorbot.d.ts +17 -8
  77. package/dist/src/explorbot.js +74 -36
  78. package/dist/src/explorer.d.ts +66 -91
  79. package/dist/src/explorer.js +326 -567
  80. package/dist/src/state-manager.d.ts +6 -1
  81. package/dist/src/state-manager.js +1 -0
  82. package/dist/src/utils/hooks-runner.js +2 -4
  83. package/dist/src/utils/test-files.js +1 -1
  84. package/dist/src/utils/web-annotate.d.ts +5 -0
  85. package/dist/src/utils/web-annotate.js +58 -0
  86. package/dist/src/utils/web-eidx.d.ts +2 -0
  87. package/dist/src/utils/web-eidx.js +20 -0
  88. package/dist/src/utils/web-element.d.ts +1 -0
  89. package/dist/src/utils/web-element.js +29 -0
  90. package/models.json +30 -0
  91. package/package.json +5 -2
  92. package/src/action.ts +25 -5
  93. package/src/ai/agent.ts +20 -0
  94. package/src/ai/captain/idle-mode.ts +1 -1
  95. package/src/ai/captain/test-mode.ts +1 -1
  96. package/src/ai/captain/web-mode.ts +18 -23
  97. package/src/ai/captain.ts +12 -25
  98. package/src/ai/driller.ts +50 -67
  99. package/src/ai/historian/codeceptjs.ts +1 -1
  100. package/src/ai/historian/playwright.ts +4 -3
  101. package/src/ai/historian/screencast.ts +4 -3
  102. package/src/ai/historian.ts +3 -2
  103. package/src/ai/navigator.ts +30 -25
  104. package/src/ai/pilot.ts +55 -47
  105. package/src/ai/planner.ts +22 -20
  106. package/src/ai/provider.ts +0 -1
  107. package/src/ai/quartermaster.ts +5 -4
  108. package/src/ai/rerunner.ts +10 -29
  109. package/src/ai/researcher/coordinates.ts +4 -3
  110. package/src/ai/researcher/deep-analysis.ts +8 -6
  111. package/src/ai/researcher/locators.ts +66 -9
  112. package/src/ai/researcher/sections.ts +4 -2
  113. package/src/ai/researcher.ts +32 -61
  114. package/src/ai/task-agent.ts +45 -11
  115. package/src/ai/tester.ts +40 -57
  116. package/src/ai/tools.ts +32 -53
  117. package/src/command-handler.ts +1 -1
  118. package/src/commands/context-aria-command.ts +1 -1
  119. package/src/commands/context-command.ts +7 -5
  120. package/src/commands/context-data-command.ts +2 -2
  121. package/src/commands/context-experience-command.ts +2 -2
  122. package/src/commands/context-html-command.ts +2 -2
  123. package/src/commands/context-knowledge-command.ts +1 -1
  124. package/src/commands/drill-command.ts +1 -1
  125. package/src/commands/explore-command.ts +4 -3
  126. package/src/commands/freesail-command.ts +2 -2
  127. package/src/commands/learn-command.ts +4 -4
  128. package/src/commands/path-command.ts +1 -1
  129. package/src/commands/research-command.ts +1 -1
  130. package/src/commands/test-command.ts +1 -1
  131. package/src/components/App.tsx +1 -1
  132. package/src/config.ts +173 -11
  133. package/src/experience-tracker.ts +1 -1
  134. package/src/explorbot.ts +78 -39
  135. package/src/explorer.ts +389 -628
  136. package/src/state-manager.ts +7 -1
  137. package/src/utils/hooks-runner.ts +2 -4
  138. package/src/utils/test-files.ts +1 -1
  139. package/src/utils/web-annotate.ts +64 -0
  140. package/src/utils/web-eidx.ts +21 -0
  141. package/src/utils/web-element.ts +30 -0
package/src/explorbot.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { existsSync, mkdirSync } from 'node:fs';
2
2
  import path from 'node:path';
3
+ import type { AgentDeps } from './ai/agent.ts';
3
4
  import { Captain } from './ai/captain.ts';
4
5
  import { Driller } from './ai/driller.ts';
5
6
  import { ExperienceCompactor } from './ai/experience-compactor.ts';
@@ -23,7 +24,9 @@ import { ConfigParser } from './config.ts';
23
24
  import { ExperienceTracker } from './experience-tracker.ts';
24
25
  import Explorer from './explorer.ts';
25
26
  import { KnowledgeTracker } from './knowledge-tracker.ts';
26
- import { WebPageState } from './state-manager.ts';
27
+ import { PlaywrightRecorder } from './playwright-recorder.ts';
28
+ import { Reporter } from './reporter.ts';
29
+ import { StateManager, WebPageState } from './state-manager.ts';
27
30
  import { Stats } from './stats.ts';
28
31
  import type { Suite } from './suite.ts';
29
32
  import { Plan, type Test } from './test-plan.ts';
@@ -42,7 +45,7 @@ export interface ExplorBotOptions {
42
45
  show?: boolean;
43
46
  headless?: boolean;
44
47
  incognito?: boolean;
45
- session?: string;
48
+ session?: string | boolean;
46
49
  }
47
50
 
48
51
  export type UserResolveFunction = (error?: Error, showWelcome?: boolean) => Promise<string | null>;
@@ -63,6 +66,10 @@ export class ExplorBot {
63
66
  private lastReportedTestCount = 0;
64
67
  private _experienceTracker?: ExperienceTracker;
65
68
  private _knowledgeTracker?: KnowledgeTracker;
69
+ private _stateManager?: StateManager;
70
+ private _reporter?: Reporter;
71
+ private _requestStore?: RequestStore;
72
+ private _playwrightRecorder?: PlaywrightRecorder;
66
73
 
67
74
  constructor(options: ExplorBotOptions = {}) {
68
75
  this.options = options;
@@ -74,7 +81,7 @@ export class ExplorBot {
74
81
  }
75
82
 
76
83
  get isExploring(): boolean {
77
- return this.explorer?.isStarted;
84
+ return !!this.explorer;
78
85
  }
79
86
 
80
87
  setUserResolve(fn: UserResolveFunction): void {
@@ -82,13 +89,20 @@ export class ExplorBot {
82
89
  }
83
90
 
84
91
  async start(): Promise<void> {
85
- if (this.explorer?.isStarted) {
92
+ if (this.explorer) {
86
93
  return;
87
94
  }
88
95
 
89
96
  try {
90
97
  await this.startProviderOnly();
91
- this.explorer = new Explorer(this.config, this.provider, this.options, this.experienceTracker(), this.knowledgeTracker());
98
+ const explorerOptions = { ...this.options, session: typeof this.options.session === 'string' ? this.options.session : undefined };
99
+ this.explorer = new Explorer(this.config, explorerOptions, {
100
+ stateManager: this.stateManager(),
101
+ knowledgeTracker: this.knowledgeTracker(),
102
+ reporter: this.reporter(),
103
+ requestStore: this.requestStore(),
104
+ playwrightRecorder: this.playwrightRecorder(),
105
+ });
92
106
  await this.explorer.start();
93
107
  if (!this.options.incognito) {
94
108
  await this.agentExperienceCompactor().autocompact();
@@ -103,6 +117,7 @@ export class ExplorBot {
103
117
  async startProviderOnly(): Promise<void> {
104
118
  if (this.provider) return;
105
119
  this.config = await this.configParser.loadConfig(this.options);
120
+ if (this.options.session === true) this.options.session = path.join(this.configParser.getOutputDir(), 'session.json');
106
121
  this.provider = new AIProvider(this.config.ai);
107
122
  await this.provider.validateConnection();
108
123
  }
@@ -121,12 +136,12 @@ export class ExplorBot {
121
136
  return this.agentNavigator().visit(url);
122
137
  }
123
138
 
124
- async openFreshTab(): Promise<void> {
125
- await this.explorer.openFreshTab();
139
+ async openTab(): Promise<void> {
140
+ await this.explorer.openTab();
126
141
  }
127
142
 
128
143
  getCurrentState(): WebPageState | null {
129
- return this.explorer?.getStateManager().getCurrentState() ?? null;
144
+ return this._stateManager?.getCurrentState() ?? null;
130
145
  }
131
146
 
132
147
  getExplorer(): Explorer {
@@ -141,6 +156,22 @@ export class ExplorBot {
141
156
  return (this._experienceTracker ||= new ExperienceTracker(this.knowledgeTracker()));
142
157
  }
143
158
 
159
+ stateManager(): StateManager {
160
+ return (this._stateManager ||= new StateManager(this.experienceTracker(), this.knowledgeTracker()));
161
+ }
162
+
163
+ reporter(): Reporter {
164
+ return (this._reporter ||= new Reporter(this.config.reporter, this.stateManager()));
165
+ }
166
+
167
+ requestStore(): RequestStore {
168
+ return (this._requestStore ||= new RequestStore(this.configParser.getOutputDir()));
169
+ }
170
+
171
+ playwrightRecorder(): PlaywrightRecorder {
172
+ return (this._playwrightRecorder ||= new PlaywrightRecorder());
173
+ }
174
+
144
175
  getConfig(): ExplorbotConfig {
145
176
  return this.config;
146
177
  }
@@ -153,11 +184,15 @@ export class ExplorBot {
153
184
  return this.provider;
154
185
  }
155
186
 
156
- createAgent<T>(factory: (deps: { explorer: Explorer; ai: AIProvider; config: ExplorbotConfig }) => T): T {
187
+ createAgent<T>(factory: (deps: AgentDeps) => T): T {
157
188
  const agent = factory({
158
189
  explorer: this.explorer,
159
190
  ai: this.provider,
160
191
  config: this.config,
192
+ stateManager: this.stateManager(),
193
+ knowledgeTracker: this.knowledgeTracker(),
194
+ requestStore: this.requestStore(),
195
+ playwrightRecorder: this.playwrightRecorder(),
161
196
  });
162
197
 
163
198
  const agentName = (agent as any).constructor.name.toLowerCase();
@@ -167,18 +202,16 @@ export class ExplorBot {
167
202
  }
168
203
 
169
204
  agentResearcher(): Researcher {
170
- return (this.agents.researcher ||= this.createAgent(({ ai, explorer }) => new Researcher(explorer, ai)));
205
+ return (this.agents.researcher ||= this.createAgent((deps) => new Researcher(deps)));
171
206
  }
172
207
 
173
208
  agentNavigator(): Navigator {
174
- return (this.agents.navigator ||= this.createAgent(({ ai, explorer }) => {
175
- return new Navigator(explorer, ai);
176
- }));
209
+ return (this.agents.navigator ||= this.createAgent((deps) => new Navigator(deps)));
177
210
  }
178
211
 
179
212
  agentPlanner(): Planner {
180
213
  if (!this.agents.planner) {
181
- this.agents.planner = this.createAgent(({ ai, explorer }) => new Planner(explorer, ai, this.agentResearcher()));
214
+ this.agents.planner = this.createAgent((deps) => new Planner(deps, this.agentResearcher()));
182
215
  const fisherman = this.agentFisherman();
183
216
  if (fisherman) this.agents.planner.setFisherman(fisherman);
184
217
  }
@@ -186,26 +219,26 @@ export class ExplorBot {
186
219
  }
187
220
 
188
221
  agentPilot(): Pilot {
189
- return (this.agents.pilot ||= this.createAgent(({ ai, explorer }) => {
222
+ return (this.agents.pilot ||= this.createAgent((deps) => {
190
223
  const researcher = this.agentResearcher();
191
224
  const navigator = this.agentNavigator();
192
- const tools = createAgentTools({ explorer, researcher, navigator, supervisor: true });
193
- return new Pilot(ai, tools, researcher, explorer);
225
+ const tools = createAgentTools({ ...deps, researcher, navigator, supervisor: true });
226
+ return new Pilot(deps, tools, researcher);
194
227
  }));
195
228
  }
196
229
 
197
230
  agentTester(): Tester {
198
231
  if (!this.agents.tester) {
199
- this.agents.tester = this.createAgent(({ ai, explorer }) => {
232
+ this.agents.tester = this.createAgent((deps) => {
200
233
  const researcher = this.agentResearcher();
201
234
  const navigator = this.agentNavigator();
202
- const tools = createAgentTools({ explorer, researcher, navigator });
203
- return new Tester(explorer, ai, researcher, navigator, tools);
235
+ const tools = createAgentTools({ ...deps, researcher, navigator });
236
+ return new Tester(deps, researcher, navigator, tools);
204
237
  });
205
238
 
206
239
  const qm = this.agentQuartermaster();
207
240
  if (qm) this.agents.tester.setQuartermaster(qm);
208
- this.agents.tester.setHistorian(this.agentHistorian());
241
+ if (this.isHistorianEnabled()) this.agents.tester.setHistorian(this.agentHistorian());
209
242
  this.agents.tester.setPilot(this.agentPilot());
210
243
  this.agents.tester.setCaptain(this.agentCaptain());
211
244
 
@@ -234,39 +267,37 @@ export class ExplorBot {
234
267
  this.agents.quartermaster = new Quartermaster(this.provider, {
235
268
  model: config?.model,
236
269
  });
237
- this.agents.quartermaster.start(this.explorer.playwrightHelper, this.explorer.getStateManager());
270
+ this.agents.quartermaster.start(this.explorer, this.stateManager());
238
271
  }
239
272
  return this.agents.quartermaster;
240
273
  }
241
274
 
242
275
  agentHistorian(): Historian {
243
- return (this.agents.historian ||= this.createAgent(({ ai, explorer, config }) => {
244
- const experienceTracker = explorer.getStateManager().getExperienceTracker();
245
- const reporter = explorer.getReporter();
246
- return new Historian(ai, experienceTracker, reporter, explorer.getStateManager(), config, {
247
- recorder: explorer.getPlaywrightRecorder(),
248
- helper: explorer.playwrightHelper,
276
+ return (this.agents.historian ||= this.createAgent(({ ai, explorer, config, stateManager, playwrightRecorder }) => {
277
+ return new Historian(ai, this.experienceTracker(), this.reporter(), stateManager, config, {
278
+ recorder: playwrightRecorder,
279
+ explorer,
249
280
  });
250
281
  }));
251
282
  }
252
283
 
253
284
  agentRerunner(): Rerunner {
254
285
  if (!this.agents.rerunner) {
255
- this.agents.rerunner = this.createAgent(({ ai, explorer }) => {
286
+ this.agents.rerunner = this.createAgent((deps) => {
256
287
  const researcher = this.agentResearcher();
257
288
  const navigator = this.agentNavigator();
258
- const tools = createAgentTools({ explorer, researcher, navigator, withExperience: false });
259
- return new Rerunner(explorer, ai, tools);
289
+ const tools = createAgentTools({ ...deps, researcher, navigator, withExperience: false });
290
+ return new Rerunner(deps, tools);
260
291
  });
261
- this.agents.rerunner.setHistorian(this.agentHistorian());
292
+ if (this.isHistorianEnabled()) this.agents.rerunner.setHistorian(this.agentHistorian());
262
293
  }
263
294
  return this.agents.rerunner;
264
295
  }
265
296
 
266
297
  agentDriller(): Driller {
267
- return (this.agents.driller ||= this.createAgent(({ ai, explorer }) => {
298
+ return (this.agents.driller ||= this.createAgent((deps) => {
268
299
  const navigator = this.agentNavigator();
269
- return new Driller(explorer, ai, navigator);
300
+ return new Driller(deps, navigator);
270
301
  }));
271
302
  }
272
303
 
@@ -283,7 +314,7 @@ export class ExplorBot {
283
314
  if (!this.agents.fisherman) {
284
315
  const apiConfig = this.config.api;
285
316
  const outputDir = this.configParser.getOutputDir();
286
- const requestStore = this.explorer.getRequestStore() || new RequestStore(outputDir);
317
+ const requestStore = this.requestStore();
287
318
  const baseEndpoint = apiConfig?.baseEndpoint || this.config.playwright.url;
288
319
  const configHeaders = apiConfig?.headers || {};
289
320
  const apiClient = new ApiClient(baseEndpoint);
@@ -298,7 +329,11 @@ export class ExplorBot {
298
329
  }
299
330
  };
300
331
 
301
- const cookieProvider = () => this.explorer.extractCookies();
332
+ const cookieProvider = async (): Promise<Record<string, string>> => {
333
+ const cookies = await this.explorer.withPage((page) => page.context().cookies()).catch(() => []);
334
+ if (!cookies.length) return {};
335
+ return { Cookie: cookies.map((c: any) => `${c.name}=${c.value}`).join('; ') };
336
+ };
302
337
 
303
338
  this.agents.fisherman = this.createAgent(({ ai }) => {
304
339
  return new Fisherman(ai, apiClient, requestStore, specLoader, baseEndpoint, cookieProvider, configHeaders, hasApiConfig);
@@ -332,7 +367,7 @@ export class ExplorBot {
332
367
  }
333
368
 
334
369
  if (!opts.extend && this.currentPlan?.url) {
335
- const currentUrl = this.explorer?.getStateManager().getCurrentState()?.url;
370
+ const currentUrl = this._stateManager?.getCurrentState()?.url;
336
371
  if (currentUrl && currentUrl !== this.currentPlan.url) {
337
372
  tag('info').log('Different page detected, clearing previous plan');
338
373
  this.clearPlan();
@@ -385,7 +420,7 @@ export class ExplorBot {
385
420
  }
386
421
 
387
422
  generatePlanFilename(feature?: string): string {
388
- const state = this.explorer?.getStateManager().getCurrentState();
423
+ const state = this._stateManager?.getCurrentState();
389
424
  const urlPath = state?.url || '/';
390
425
  const urlPart = sanitizeFilename(urlPath) || 'root';
391
426
  const suffix = '.md';
@@ -465,7 +500,7 @@ export class ExplorBot {
465
500
  const filePath = this.agentSessionAnalyst().writeReport(markdown);
466
501
  tag('info').log(`Session report saved: ${relativeToCwd(filePath)}`);
467
502
 
468
- const reporter = this.explorer?.getReporter();
503
+ const reporter = this._reporter;
469
504
  if (reporter?.isEnabled()) {
470
505
  let description = markdown;
471
506
  const modelsTable = Stats.modelsTable(this.provider.getConfiguredModels());
@@ -479,4 +514,8 @@ export class ExplorBot {
479
514
  tag('warning').log(`Session analysis failed: ${message}`);
480
515
  }
481
516
  }
517
+
518
+ private isHistorianEnabled(): boolean {
519
+ return this.config.ai?.agents?.historian?.enabled !== false;
520
+ }
482
521
  }