explorbot 0.1.30 → 0.1.32-beta.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 (179) hide show
  1. package/dist/boat/api-tester/example/apibot.config.js +30 -0
  2. package/dist/package.json +11 -26
  3. package/dist/src/action-result.d.ts +133 -0
  4. package/dist/src/action-result.js +22 -7
  5. package/dist/src/action.d.ts +50 -0
  6. package/dist/src/action.js +10 -5
  7. package/dist/src/activity.d.ts +26 -0
  8. package/dist/src/ai/agent.d.ts +3 -0
  9. package/dist/src/ai/captain/file-tools.d.ts +28 -0
  10. package/dist/src/ai/captain/idle-mode.d.ts +11 -0
  11. package/dist/src/ai/captain/mixin.d.ts +10 -0
  12. package/dist/src/ai/captain/test-mode.d.ts +13 -0
  13. package/dist/src/ai/captain/web-mode.d.ts +13 -0
  14. package/dist/src/ai/captain.d.ts +143 -0
  15. package/dist/src/ai/conversation.d.ts +31 -0
  16. package/dist/src/ai/conversation.js +1 -1
  17. package/dist/src/ai/driller.d.ts +161 -0
  18. package/dist/src/ai/experience-compactor.d.ts +53 -0
  19. package/dist/src/ai/fisherman-tools.d.ts +90 -0
  20. package/dist/src/ai/fisherman.d.ts +30 -0
  21. package/dist/src/ai/historian/codeceptjs.d.ts +15 -0
  22. package/dist/src/ai/historian/experience.d.ts +40 -0
  23. package/dist/src/ai/historian/mixin.d.ts +2 -0
  24. package/dist/src/ai/historian/playwright.d.ts +20 -0
  25. package/dist/src/ai/historian/screencast.d.ts +32 -0
  26. package/dist/src/ai/historian/utils.d.ts +4 -0
  27. package/dist/src/ai/historian.d.ts +108 -0
  28. package/dist/src/ai/navigator.d.ts +56 -0
  29. package/dist/src/ai/pilot.d.ts +83 -0
  30. package/dist/src/ai/planner/session-dedup.d.ts +12 -0
  31. package/dist/src/ai/planner/styles.d.ts +6 -0
  32. package/dist/src/ai/planner/subpages.d.ts +35 -0
  33. package/dist/src/ai/planner.d.ts +79 -0
  34. package/dist/src/ai/provider.d.ts +50 -0
  35. package/dist/src/ai/provider.js +13 -5
  36. package/dist/src/ai/quartermaster.d.ts +57 -0
  37. package/dist/src/ai/rerunner.d.ts +49 -0
  38. package/dist/src/ai/researcher/cache.d.ts +6 -0
  39. package/dist/src/ai/researcher/coordinates.d.ts +56 -0
  40. package/dist/src/ai/researcher/deep-analysis.d.ts +74 -0
  41. package/dist/src/ai/researcher/fingerprint-worker.d.ts +1 -0
  42. package/dist/src/ai/researcher/focus.d.ts +8 -0
  43. package/dist/src/ai/researcher/locators.d.ts +36 -0
  44. package/dist/src/ai/researcher/mixin.d.ts +2 -0
  45. package/dist/src/ai/researcher/parser.d.ts +35 -0
  46. package/dist/src/ai/researcher/research-result.d.ts +16 -0
  47. package/dist/src/ai/researcher/sections.d.ts +19 -0
  48. package/dist/src/ai/rules.d.ts +15 -0
  49. package/dist/src/ai/session-analyst.d.ts +11 -0
  50. package/dist/src/ai/task-agent.d.ts +28 -0
  51. package/dist/src/ai/tester.d.ts +103 -0
  52. package/dist/src/ai/tools.d.ts +41 -0
  53. package/dist/src/api/api-client.d.ts +17 -0
  54. package/dist/src/api/request-result.d.ts +44 -0
  55. package/dist/src/api/request-store.d.ts +29 -0
  56. package/dist/src/api/spec-reader.d.ts +5 -0
  57. package/dist/src/api/xhr-capture.d.ts +10 -0
  58. package/dist/src/browser-server.d.ts +10 -0
  59. package/dist/src/command-handler.d.ts +68 -0
  60. package/dist/src/commands/add-rule-command.d.ts +11 -0
  61. package/dist/src/commands/base-command.d.ts +26 -0
  62. package/dist/src/commands/clean-command.d.ts +12 -0
  63. package/dist/src/commands/compact-command.d.ts +20 -0
  64. package/dist/src/commands/context-aria-command.d.ts +6 -0
  65. package/dist/src/commands/context-command.d.ts +11 -0
  66. package/dist/src/commands/context-data-command.d.ts +6 -0
  67. package/dist/src/commands/context-experience-command.d.ts +6 -0
  68. package/dist/src/commands/context-html-command.d.ts +6 -0
  69. package/dist/src/commands/context-knowledge-command.d.ts +6 -0
  70. package/dist/src/commands/debug-command.d.ts +6 -0
  71. package/dist/src/commands/drill-command.d.ts +10 -0
  72. package/dist/src/commands/exit-command.d.ts +7 -0
  73. package/dist/src/commands/experience-command.d.ts +15 -0
  74. package/dist/src/commands/explore-command.d.ts +51 -0
  75. package/dist/src/commands/freesail-command.d.ts +12 -0
  76. package/dist/src/commands/help-command.d.ts +6 -0
  77. package/dist/src/commands/index.d.ts +4 -0
  78. package/dist/src/commands/init-command.d.ts +7 -0
  79. package/dist/src/commands/knows-command.d.ts +10 -0
  80. package/dist/src/commands/learn-command.d.ts +7 -0
  81. package/dist/src/commands/navigate-command.d.ts +7 -0
  82. package/dist/src/commands/path-command.d.ts +10 -0
  83. package/dist/src/commands/plan-clear-command.d.ts +7 -0
  84. package/dist/src/commands/plan-command.d.ts +14 -0
  85. package/dist/src/commands/plan-edit-command.d.ts +7 -0
  86. package/dist/src/commands/plan-load-command.d.ts +7 -0
  87. package/dist/src/commands/plan-reload-command.d.ts +7 -0
  88. package/dist/src/commands/plan-save-command.d.ts +7 -0
  89. package/dist/src/commands/plans-command.d.ts +21 -0
  90. package/dist/src/commands/rerun-command.d.ts +7 -0
  91. package/dist/src/commands/research-command.d.ts +11 -0
  92. package/dist/src/commands/runs-command.d.ts +7 -0
  93. package/dist/src/commands/start-command.d.ts +7 -0
  94. package/dist/src/commands/status-command.d.ts +6 -0
  95. package/dist/src/commands/test-command.d.ts +11 -0
  96. package/dist/src/components/ActivityPane.d.ts +6 -0
  97. package/dist/src/components/AddKnowledge.d.ts +8 -0
  98. package/dist/src/components/AddRule.d.ts +9 -0
  99. package/dist/src/components/App.d.ts +9 -0
  100. package/dist/src/components/Autocomplete.d.ts +3 -0
  101. package/dist/src/components/InputPane.d.ts +13 -0
  102. package/dist/src/components/InputReadline.d.ts +17 -0
  103. package/dist/src/components/LogPane.d.ts +6 -0
  104. package/dist/src/components/PlanEditor.d.ts +9 -0
  105. package/dist/src/components/PlanPane.d.ts +13 -0
  106. package/dist/src/components/SessionTimer.d.ts +6 -0
  107. package/dist/src/components/StateTransitionPane.d.ts +8 -0
  108. package/dist/src/components/StatusPane.d.ts +4 -0
  109. package/dist/src/components/TaskPane.d.ts +9 -0
  110. package/dist/src/components/Welcome.d.ts +3 -0
  111. package/dist/src/components/WelcomeChecklist.d.ts +9 -0
  112. package/dist/src/components/WelcomeCommands.d.ts +6 -0
  113. package/dist/src/components/autocomplete-store.d.ts +10 -0
  114. package/dist/src/components/parse-keypress.d.ts +12 -0
  115. package/dist/src/config.d.ts +251 -0
  116. package/dist/src/execution-controller.d.ts +25 -0
  117. package/dist/src/experience-tracker.d.ts +107 -0
  118. package/dist/src/experience-tracker.js +2 -1
  119. package/dist/src/explorbot.d.ts +107 -0
  120. package/dist/src/explorer.d.ts +140 -0
  121. package/dist/src/explorer.js +13 -9
  122. package/dist/src/index.d.ts +6 -0
  123. package/dist/src/index.js +2 -55
  124. package/dist/src/knowledge-tracker.d.ts +33 -0
  125. package/dist/src/knowledge-tracker.js +9 -2
  126. package/dist/src/observability.d.ts +13 -0
  127. package/dist/src/observability.js +2 -1
  128. package/dist/src/playwright-recorder.d.ts +31 -0
  129. package/dist/src/reporter.d.ts +34 -0
  130. package/dist/src/state-manager.d.ts +185 -0
  131. package/dist/src/state-manager.js +0 -7
  132. package/dist/src/stats.d.ts +24 -0
  133. package/dist/src/suite.d.ts +24 -0
  134. package/dist/src/test-plan.d.ts +166 -0
  135. package/dist/src/utils/aria.d.ts +20 -0
  136. package/dist/src/utils/browser-errors.d.ts +8 -0
  137. package/dist/src/utils/cli-name.d.ts +1 -0
  138. package/dist/src/utils/code-extractor.d.ts +1 -0
  139. package/dist/src/utils/context-formatter.d.ts +25 -0
  140. package/dist/src/utils/error-page.d.ts +12 -0
  141. package/dist/src/utils/expandable.d.ts +4 -0
  142. package/dist/src/utils/hooks-runner.d.ts +15 -0
  143. package/dist/src/utils/html-diff.d.ts +19 -0
  144. package/dist/src/utils/html.d.ts +124 -0
  145. package/dist/src/utils/log-filters.d.ts +6 -0
  146. package/dist/src/utils/logger.d.ts +35 -0
  147. package/dist/src/utils/loop.d.ts +35 -0
  148. package/dist/src/utils/markdown-parser.d.ts +17 -0
  149. package/dist/src/utils/markdown-query.d.ts +40 -0
  150. package/dist/src/utils/markdown-terminal.d.ts +1 -0
  151. package/dist/src/utils/next-steps.d.ts +11 -0
  152. package/dist/src/utils/page-readiness.d.ts +6 -0
  153. package/dist/src/utils/research-parser.d.ts +1 -0
  154. package/dist/src/utils/retry.d.ts +8 -0
  155. package/dist/src/utils/rules-loader.d.ts +12 -0
  156. package/dist/src/utils/secrets.d.ts +4 -0
  157. package/dist/src/utils/secrets.js +28 -0
  158. package/dist/src/utils/step-analyzer.d.ts +11 -0
  159. package/dist/src/utils/strings.d.ts +3 -0
  160. package/dist/src/utils/test-files.d.ts +3 -0
  161. package/dist/src/utils/test-plan-markdown.d.ts +9 -0
  162. package/dist/src/utils/throttle.d.ts +2 -0
  163. package/dist/src/utils/unique-names.d.ts +2 -0
  164. package/dist/src/utils/url-matcher.d.ts +6 -0
  165. package/dist/src/utils/web-element.d.ts +44 -0
  166. package/dist/src/utils/xpath.d.ts +17 -0
  167. package/package.json +11 -26
  168. package/src/action-result.ts +22 -7
  169. package/src/action.ts +12 -5
  170. package/src/ai/conversation.ts +1 -1
  171. package/src/ai/provider.ts +13 -5
  172. package/src/experience-tracker.ts +2 -1
  173. package/src/explorer.ts +14 -9
  174. package/src/index.ts +9 -0
  175. package/src/knowledge-tracker.ts +7 -1
  176. package/src/observability.ts +2 -1
  177. package/src/state-manager.ts +0 -8
  178. package/src/utils/secrets.ts +29 -0
  179. package/src/index.tsx +0 -62
@@ -68,6 +68,7 @@ export class ActionResult implements ActionResultData {
68
68
  private _screenshot: Buffer | undefined = undefined;
69
69
  readonly htmlFile: string | undefined = undefined;
70
70
  private _html: string | undefined = undefined;
71
+ private snapshotCache = new Map<string, string>();
71
72
  readonly logFile: string | undefined = undefined;
72
73
  private _browserLogs: any[] | undefined = undefined;
73
74
  readonly ariaSnapshotFile: string | undefined = undefined;
@@ -151,6 +152,7 @@ export class ActionResult implements ActionResultData {
151
152
 
152
153
  set html(value: string) {
153
154
  this._html = value;
155
+ this.snapshotCache.clear();
154
156
  }
155
157
 
156
158
  get screenshot(): Buffer | undefined {
@@ -263,21 +265,26 @@ export class ActionResult implements ActionResultData {
263
265
 
264
266
  async simplifiedHtml(htmlConfig?: HtmlConfig): Promise<string> {
265
267
  const normalizedConfig = this.normalizeHtmlConfig(htmlConfig);
266
- return await minifyHtml(htmlMinimalUISnapshot(this.html ?? '', normalizedConfig?.minimal));
268
+ const cacheKey = `simplified:${JSON.stringify(normalizedConfig?.minimal ?? null)}`;
269
+ return this.cachedSnapshot(cacheKey, () => minifyHtml(htmlMinimalUISnapshot(this.html ?? '', normalizedConfig?.minimal)));
267
270
  }
268
271
 
269
272
  async combinedHtml(htmlConfig?: HtmlConfig & { keepPositions?: boolean }): Promise<string> {
270
273
  const normalizedConfig = this.normalizeHtmlConfig(htmlConfig);
271
- const combinedHtml = await minifyHtml(htmlCombinedSnapshot(this.html ?? '', normalizedConfig?.combined, { keepPositions: htmlConfig?.keepPositions }));
272
- debugLog(`----${this.url}----`);
273
- debugLog(`Combined HTML: \n${combinedHtml}`);
274
- debugLog('----');
275
- return combinedHtml;
274
+ const cacheKey = `combined:${!!htmlConfig?.keepPositions}:${JSON.stringify(normalizedConfig?.combined ?? null)}`;
275
+ return this.cachedSnapshot(cacheKey, async () => {
276
+ const combinedHtml = await minifyHtml(htmlCombinedSnapshot(this.html ?? '', normalizedConfig?.combined, { keepPositions: htmlConfig?.keepPositions }));
277
+ debugLog(`----${this.url}----`);
278
+ debugLog(`Combined HTML: \n${combinedHtml}`);
279
+ debugLog('----');
280
+ return combinedHtml;
281
+ });
276
282
  }
277
283
 
278
284
  async textHtml(htmlConfig?: HtmlConfig): Promise<string> {
279
285
  const normalizedConfig = this.normalizeHtmlConfig(htmlConfig);
280
- return await minifyHtml(htmlTextSnapshot(this.html ?? '', normalizedConfig?.text));
286
+ const cacheKey = `text:${JSON.stringify(normalizedConfig?.text ?? null)}`;
287
+ return this.cachedSnapshot(cacheKey, () => minifyHtml(htmlTextSnapshot(this.html ?? '', normalizedConfig?.text)));
281
288
  }
282
289
 
283
290
  getInteractiveARIA(): string {
@@ -296,6 +303,14 @@ export class ActionResult implements ActionResultData {
296
303
  return parser.getConfig().html;
297
304
  }
298
305
 
306
+ private async cachedSnapshot(key: string, compute: () => Promise<string>): Promise<string> {
307
+ const cached = this.snapshotCache.get(key);
308
+ if (cached !== undefined) return cached;
309
+ const value = await compute();
310
+ this.snapshotCache.set(key, value);
311
+ return value;
312
+ }
313
+
299
314
  static fromState(state: WebPageState): ActionResult {
300
315
  let html: string | undefined = undefined;
301
316
  let screenshot: Buffer | undefined = undefined;
package/src/action.ts CHANGED
@@ -303,11 +303,11 @@ class Action {
303
303
 
304
304
  if (isPlaywright) {
305
305
  const page = this.playwrightHelper.page;
306
- const asyncFn = new Function('page', `return (async () => { ${sanitizedCode} })()`);
306
+ const asyncFn = createSandboxedFunction(['page'], `return (async () => { ${sanitizedCode} })()`);
307
307
  await asyncFn(page);
308
308
  await sleep(this.config.action?.delay || 500);
309
309
  } else {
310
- const codeFunction = new Function('I', 'tryTo', 'retryTo', 'within', 'hopeThat', 'step', 'faker', sanitizedCode);
310
+ const codeFunction = createSandboxedFunction(['I', 'tryTo', 'retryTo', 'within', 'hopeThat', 'step', 'faker'], sanitizedCode);
311
311
  codeFunction(this.actor, tryTo, retryTo, within, hopeThat, step, faker);
312
312
  await recorder.add(() => sleep(this.config.action?.delay || 500));
313
313
  await recorder.promise();
@@ -366,7 +366,7 @@ class Action {
366
366
  if (!sanitizedCode) {
367
367
  throw new Error('No valid I.* commands found in code block');
368
368
  }
369
- codeFunction = new Function('I', 'tryTo', 'retryTo', 'within', 'hopeThat', 'step', sanitizedCode);
369
+ codeFunction = createSandboxedFunction(['I', 'tryTo', 'retryTo', 'within', 'hopeThat', 'step'], sanitizedCode);
370
370
  }
371
371
  codeFunction(this.actor, tryTo, retryTo, within, hopeThat, step);
372
372
  await recorder.promise();
@@ -480,14 +480,21 @@ async function captureTitle(page: any, actor: any): Promise<string> {
480
480
  return '';
481
481
  }
482
482
 
483
- function sanitizeCodeBlock(code: string): string {
483
+ const SHADOWED_GLOBALS = ['process', 'global', 'globalThis', 'fetch', 'Bun', 'require', 'module', 'exports'];
484
+
485
+ export function sanitizeCodeBlock(code: string): string {
484
486
  return code
485
487
  .split('\n')
486
488
  .map((line) => line.trim())
487
- .filter((line) => line.startsWith('I.') || line.startsWith('page.') || line.startsWith('await '))
489
+ .filter((line) => line.startsWith('I.') || line.startsWith('page.') || line.startsWith('await page.') || line.startsWith('await I.'))
488
490
  .join('\n');
489
491
  }
490
492
 
493
+ export function createSandboxedFunction(argNames: string[], body: string): (...args: any[]) => any {
494
+ const fn = new Function(...argNames, ...SHADOWED_GLOBALS, `'use strict';\n${body}`);
495
+ return (...args: any[]) => fn(...args);
496
+ }
497
+
491
498
  function hasPlaywrightCommands(code: string): boolean {
492
499
  return code.split('\n').some((line) => {
493
500
  const trimmed = line.trim();
@@ -51,7 +51,7 @@ export class Conversation {
51
51
 
52
52
  this.messages.push({
53
53
  role: 'user',
54
- content: [{ type: 'image', image: imageData }],
54
+ content: [{ type: 'file', mediaType: 'image/png', data: imageData }],
55
55
  });
56
56
  }
57
57
 
@@ -31,7 +31,7 @@ function extractCachedTokens(usage: any): number {
31
31
  return typeof fromRaw === 'number' ? fromRaw : 0;
32
32
  }
33
33
 
34
- function rejectAfterIdle(ms: number, signal: { cancelled: boolean }): Promise<never> {
34
+ function rejectAfterIdle(ms: number, signal: { cancelled: boolean }, controller: AbortController): Promise<never> {
35
35
  return new Promise((_, reject) => {
36
36
  const tick = () => {
37
37
  if (signal.cancelled) return;
@@ -39,6 +39,7 @@ function rejectAfterIdle(ms: number, signal: { cancelled: boolean }): Promise<ne
39
39
  setTimeout(tick, ms);
40
40
  return;
41
41
  }
42
+ controller.abort();
42
43
  reject(new Error('AI request timeout'));
43
44
  };
44
45
  setTimeout(tick, ms);
@@ -367,13 +368,16 @@ export class Provider {
367
368
  const response = await withRetry(async () => {
368
369
  const timeout = config.timeout || 30000;
369
370
  const cancel = { cancelled: false };
371
+ const controller = new AbortController();
372
+ const combinedSignal = AbortSignal.any([controller.signal, executionController.getAbortSignal()].filter(Boolean) as AbortSignal[]);
370
373
  try {
371
374
  const result = (await Promise.race([
372
375
  generateText({
373
376
  messages,
374
377
  ...config,
378
+ abortSignal: combinedSignal,
375
379
  }),
376
- rejectAfterIdle(timeout, cancel),
380
+ rejectAfterIdle(timeout, cancel, controller),
377
381
  ])) as any;
378
382
  const hasToolCall = (result.toolCalls?.length || 0) > 0;
379
383
  if (!result.text && !hasToolCall && result.finishReason === 'length') {
@@ -455,13 +459,16 @@ export class Provider {
455
459
  const response = await withRetry(async () => {
456
460
  const timeout = config.timeout || 30000;
457
461
  const cancel = { cancelled: false };
462
+ const controller = new AbortController();
463
+ const combinedSignal = AbortSignal.any([controller.signal, executionController.getAbortSignal()].filter(Boolean) as AbortSignal[]);
458
464
  try {
459
465
  return (await Promise.race([
460
466
  generateObject({
461
467
  messages,
462
468
  ...config,
469
+ abortSignal: combinedSignal,
463
470
  }),
464
- rejectAfterIdle(timeout, cancel),
471
+ rejectAfterIdle(timeout, cancel, controller),
465
472
  ])) as any;
466
473
  } finally {
467
474
  cancel.cancelled = true;
@@ -641,8 +648,9 @@ export class Provider {
641
648
  text: prompt,
642
649
  },
643
650
  {
644
- type: 'image',
645
- image: imageData,
651
+ type: 'file',
652
+ mediaType: 'image/png',
653
+ data: imageData,
646
654
  },
647
655
  ],
648
656
  },
@@ -8,6 +8,7 @@ import { KnowledgeTracker } from './knowledge-tracker.js';
8
8
  import type { WebPageState } from './state-manager.js';
9
9
  import { createDebug, tag } from './utils/logger.js';
10
10
  import { mdq } from './utils/markdown-query.js';
11
+ import { redactSecrets } from './utils/secrets.js';
11
12
  import { isNonReusableCode } from './utils/step-analyzer.ts';
12
13
  import { extractStatePath } from './utils/url-matcher.js';
13
14
 
@@ -109,7 +110,7 @@ export class ExperienceTracker {
109
110
  return;
110
111
  }
111
112
  const filePath = this.getExperienceFilePath(stateHash);
112
- const fileContent = matter.stringify(content, frontmatter || {});
113
+ const fileContent = matter.stringify(redactSecrets(content), frontmatter || {});
113
114
  writeFileSync(filePath, fileContent, 'utf8');
114
115
  }
115
116
 
package/src/explorer.ts CHANGED
@@ -727,17 +727,22 @@ class Explorer {
727
727
 
728
728
  this.watchActiveTestPage();
729
729
 
730
- codeceptjs.event.dispatcher.emit('test.before', codeceptjsTest);
731
- codeceptjs.event.dispatcher.emit('test.start', codeceptjsTest);
732
- codeceptjs.event.dispatcher.on('step.passed', (step: any) => stepHandler(step, 'passed'));
733
- codeceptjs.event.dispatcher.on('step.failed', (step: any, error: any) => {
730
+ const onStepPassed = (step: any) => stepHandler(step, 'passed');
731
+ const onStepFailed = (step: any, error: any) => {
734
732
  stepHandler(step, 'failed', error?.message || String(error), error?.stack);
735
- });
736
- codeceptjs.event.dispatcher.on('test.after', () => {
737
- codeceptjs.event.dispatcher.off('step.passed', stepHandler);
738
- codeceptjs.event.dispatcher.off('step.failed', stepHandler);
733
+ };
734
+ const onTestAfter = () => {
735
+ codeceptjs.event.dispatcher.off('step.passed', onStepPassed);
736
+ codeceptjs.event.dispatcher.off('step.failed', onStepFailed);
737
+ codeceptjs.event.dispatcher.off('test.after', onTestAfter);
739
738
  this.unwatchActiveTestPages();
740
- });
739
+ };
740
+
741
+ codeceptjs.event.dispatcher.emit('test.before', codeceptjsTest);
742
+ codeceptjs.event.dispatcher.emit('test.start', codeceptjsTest);
743
+ codeceptjs.event.dispatcher.on('step.passed', onStepPassed);
744
+ codeceptjs.event.dispatcher.on('step.failed', onStepFailed);
745
+ codeceptjs.event.dispatcher.on('test.after', onTestAfter);
741
746
 
742
747
  return true;
743
748
  }
package/src/index.ts ADDED
@@ -0,0 +1,9 @@
1
+ export { ExplorBot } from './explorbot.ts';
2
+ export type { ExplorBotOptions, UserResolveFunction } from './explorbot.ts';
3
+
4
+ export { Plan, Test, TestResult, TestStatus } from './test-plan.ts';
5
+ export type { Note, StepData, TestResultType, TestStatusType } from './test-plan.ts';
6
+
7
+ export type { Link, StateTransition, WebPageState } from './state-manager.ts';
8
+
9
+ export type { AIConfig, ExplorbotConfig, PlaywrightConfig } from './config.ts';
@@ -5,6 +5,7 @@ import { ActionResult } from './action-result.js';
5
5
  import { ConfigParser } from './config.js';
6
6
  import { getCliName } from './utils/cli-name.ts';
7
7
  import { createDebug } from './utils/logger.js';
8
+ import { isSecretName, registerSecret } from './utils/secrets.js';
8
9
 
9
10
  const debugLog = createDebug('explorbot:knowledge-tracker');
10
11
 
@@ -138,9 +139,14 @@ export class KnowledgeTracker {
138
139
  const namespace = expr.slice(0, dotIndex);
139
140
  const key = expr.slice(dotIndex + 1);
140
141
 
141
- if (namespace === 'env') return process.env[key] ?? '';
142
+ if (namespace === 'env') {
143
+ const value = process.env[key] ?? '';
144
+ if (isSecretName(key)) registerSecret(value);
145
+ return value;
146
+ }
142
147
 
143
148
  if (namespace === 'config') {
149
+ if (isSecretName(key)) return '';
144
150
  const config = ConfigParser.getInstance().getConfig();
145
151
  const value = key.split('.').reduce((obj: any, k) => obj?.[k], config);
146
152
  if (value !== undefined && typeof value !== 'object') return String(value);
@@ -1,4 +1,5 @@
1
1
  import { type Span, trace } from '@opentelemetry/api';
2
+ import { redactSecrets } from './utils/secrets.js';
2
3
 
3
4
  type TelemetryMetadata = Record<string, unknown>;
4
5
 
@@ -84,7 +85,7 @@ function buildRootSpanAttributes(name: string, metadata: TelemetryMetadata): Rec
84
85
  attributes['langfuse.trace.tags'] = metadata.tags as string[];
85
86
  }
86
87
  if (metadata.input !== undefined) {
87
- attributes['langfuse.trace.input'] = JSON.stringify(metadata.input);
88
+ attributes['langfuse.trace.input'] = redactSecrets(JSON.stringify(metadata.input));
88
89
  }
89
90
 
90
91
  return attributes;
@@ -5,7 +5,6 @@ import { ActionResult } from './action-result.js';
5
5
  import { ConfigParser, outputPath } from './config.js';
6
6
  import { ExperienceTracker } from './experience-tracker.js';
7
7
  import { detectFocusArea } from './utils/aria.js';
8
- import { htmlTextSnapshot } from './utils/html.js';
9
8
  import { createDebug, tag } from './utils/logger.js';
10
9
  import { extractStatePath } from './utils/url-matcher.js';
11
10
 
@@ -126,13 +125,6 @@ export class StateManager {
126
125
  * Emit state change event to all listeners
127
126
  */
128
127
  private emitStateChange(event: StateTransition): void {
129
- // Log HTML content when state changes
130
- if (event.toState.html && event.toState.html !== event.fromState?.html) {
131
- let htmlContent = event?.toState?.html ?? '';
132
- htmlContent = htmlTextSnapshot(htmlContent);
133
- // tag('html').log(`Page HTML for ${event.toState.url}:\n${htmlContent}`);
134
- }
135
-
136
128
  this.stateChangeListeners.forEach((listener) => {
137
129
  try {
138
130
  listener(event);
@@ -0,0 +1,29 @@
1
+ const MIN_SECRET_LENGTH = 4;
2
+ const secretValues = new Set<string>();
3
+
4
+ export function registerSecret(value: string): void {
5
+ if (!value) return;
6
+ if (value.length < MIN_SECRET_LENGTH) return;
7
+ secretValues.add(value);
8
+ }
9
+
10
+ export function redactSecrets(text: string): string {
11
+ if (!text) return text;
12
+ let result = text;
13
+ for (const value of secretValues) {
14
+ if (!result.includes(value)) continue;
15
+ result = result.split(value).join('***REDACTED***');
16
+ }
17
+ return result;
18
+ }
19
+
20
+ export function clearRegisteredSecrets(): void {
21
+ secretValues.clear();
22
+ }
23
+
24
+ const SECRET_NAME_TOKENS = ['password', 'passwd', 'secret', 'token', 'apikey', 'api_key', 'credential', 'private_key', 'privatekey', 'access_key'];
25
+
26
+ export function isSecretName(name: string): boolean {
27
+ const lower = name.toLowerCase();
28
+ return SECRET_NAME_TOKENS.some((token) => lower.includes(token));
29
+ }
package/src/index.tsx DELETED
@@ -1,62 +0,0 @@
1
- #!/usr/bin/env node
2
- import { Command } from 'commander';
3
- import { addKnowledgeCommand } from './commands/add-knowledge.js';
4
- import { cleanCommand } from './commands/clean.js';
5
- import { exploreCommand } from './commands/explore.js';
6
- import { initCommand } from './commands/init.js';
7
-
8
- const program = new Command();
9
-
10
- program
11
- .name('explorbot')
12
- .description('AI-powered web exploration tool')
13
- .option('-f, --from <url>', 'Start exploration from a specific URL')
14
- .option('-v, --verbose', 'Enable verbose logging')
15
- .option('--debug', 'Enable debug logging (same as --verbose)')
16
- .option('-c, --config <path>', 'Path to configuration file')
17
- .option('-p, --path <path>', 'Working directory path')
18
- .helpOption('-h, --help', 'Show this help message');
19
-
20
- program
21
- .command('clean')
22
- .description('Clean up artifacts or experience folders')
23
- .option('-t, --type <type>', 'Type of cleanup (artifacts|experience|all)', 'artifacts')
24
- .option('-p, --path <path>', 'Custom path to clean')
25
- .action(async (options, command) => {
26
- const globalOptions = command.parent.opts();
27
- const allOptions = { ...globalOptions, ...options };
28
- await cleanCommand(allOptions);
29
- });
30
-
31
- program
32
- .command('init')
33
- .description('Initialize a new project with configuration')
34
- .option('-c, --config-path <path>', 'Path for the config file', './explorbot.config.js')
35
- .option('-f, --force', 'Overwrite existing config file', false)
36
- .option('-p, --path <path>', 'Working directory for initialization')
37
- .action(async (options, command) => {
38
- const globalOptions = command.parent.opts();
39
- const allOptions = { ...globalOptions, ...options };
40
- await initCommand(allOptions);
41
- });
42
-
43
- program
44
- .command('add-knowledge')
45
- .alias('learn')
46
- .description('Add knowledge for specific URLs')
47
- .option('-p, --path <path>', 'Knowledge directory path')
48
- .action(async (options) => {
49
- await addKnowledgeCommand(options);
50
- });
51
-
52
- program.parse();
53
-
54
- const options = program.opts();
55
- const command = program.args[0];
56
-
57
- if (command === 'clean' || command === 'init' || command === 'add-knowledge' || command === 'learn') {
58
- // These commands are handled by their respective actions
59
- } else {
60
- // Default to explore command
61
- exploreCommand(options);
62
- }