explorbot 0.1.31 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -14
- package/bin/explorbot-cli.ts +4 -3
- package/boat/doc-collector/src/ai/documentarian.ts +22 -27
- package/boat/doc-collector/src/ai/tools.ts +67 -10
- package/boat/doc-collector/src/docbot.ts +28 -7
- package/boat/doc-collector/src/docs-renderer.ts +19 -24
- package/boat/doc-collector/src/screenshots.ts +33 -1
- package/boat/doc-collector/src/state-diagram.ts +281 -0
- package/dist/bin/explorbot-cli.js +3 -3
- package/dist/boat/doc-collector/src/ai/documentarian.js +16 -6
- package/dist/boat/doc-collector/src/ai/tools.js +47 -10
- package/dist/boat/doc-collector/src/docbot.js +25 -6
- package/dist/boat/doc-collector/src/docs-renderer.js +18 -3
- package/dist/boat/doc-collector/src/screenshots.js +29 -0
- package/dist/boat/doc-collector/src/state-diagram.js +200 -0
- package/dist/package.json +8 -6
- package/dist/src/action-result.d.ts +131 -0
- package/dist/src/action-result.js +82 -159
- package/dist/src/action.d.ts +43 -0
- package/dist/src/action.js +18 -106
- package/dist/src/activity.d.ts +26 -0
- package/dist/src/ai/agent.d.ts +3 -0
- package/dist/src/ai/captain/file-tools.d.ts +28 -0
- package/dist/src/ai/captain/idle-mode.d.ts +11 -0
- package/dist/src/ai/captain/mixin.d.ts +10 -0
- package/dist/src/ai/captain/test-mode.d.ts +13 -0
- package/dist/src/ai/captain/web-mode.d.ts +13 -0
- package/dist/src/ai/captain/web-mode.js +0 -7
- package/dist/src/ai/captain.d.ts +141 -0
- package/dist/src/ai/captain.js +4 -19
- package/dist/src/ai/conversation.d.ts +28 -0
- package/dist/src/ai/conversation.js +9 -28
- package/dist/src/ai/driller.d.ts +157 -0
- package/dist/src/ai/driller.js +3 -12
- package/dist/src/ai/experience-compactor.d.ts +53 -0
- package/dist/src/ai/experience-compactor.js +16 -33
- package/dist/src/ai/fisherman-tools.d.ts +90 -0
- package/dist/src/ai/fisherman-tools.js +1 -1
- package/dist/src/ai/fisherman.d.ts +29 -0
- package/dist/src/ai/fisherman.js +0 -3
- package/dist/src/ai/historian/codeceptjs.d.ts +17 -0
- package/dist/src/ai/historian/codeceptjs.js +3 -2
- package/dist/src/ai/historian/experience.d.ts +40 -0
- package/dist/src/ai/historian/mixin.d.ts +2 -0
- package/dist/src/ai/historian/playwright.d.ts +22 -0
- package/dist/src/ai/historian/playwright.js +3 -2
- package/dist/src/ai/historian/screencast.d.ts +32 -0
- package/dist/src/ai/historian/utils.d.ts +3 -0
- package/dist/src/ai/historian/utils.js +0 -1
- package/dist/src/ai/historian.d.ts +109 -0
- package/dist/src/ai/historian.js +1 -5
- package/dist/src/ai/navigator.d.ts +54 -0
- package/dist/src/ai/navigator.js +11 -46
- package/dist/src/ai/pilot.d.ts +81 -0
- package/dist/src/ai/pilot.js +6 -17
- package/dist/src/ai/planner/session-dedup.d.ts +12 -0
- package/dist/src/ai/planner/styles.d.ts +6 -0
- package/dist/src/ai/planner/subpages.d.ts +35 -0
- package/dist/src/ai/planner.d.ts +79 -0
- package/dist/src/ai/planner.js +49 -37
- package/dist/src/ai/provider.d.ts +53 -0
- package/dist/src/ai/provider.js +78 -130
- package/dist/src/ai/quartermaster.d.ts +56 -0
- package/dist/src/ai/quartermaster.js +4 -10
- package/dist/src/ai/rerunner.d.ts +48 -0
- package/dist/src/ai/rerunner.js +14 -22
- package/dist/src/ai/researcher/cache.d.ts +6 -0
- package/dist/src/ai/researcher/cache.js +11 -18
- package/dist/src/ai/researcher/coordinates.d.ts +56 -0
- package/dist/src/ai/researcher/deep-analysis.d.ts +74 -0
- package/dist/src/ai/researcher/deep-analysis.js +3 -6
- package/dist/src/ai/researcher/fingerprint-worker.d.ts +1 -0
- package/dist/src/ai/researcher/focus.d.ts +8 -0
- package/dist/src/ai/researcher/locators.d.ts +36 -0
- package/dist/src/ai/researcher/mixin.d.ts +2 -0
- package/dist/src/ai/researcher/parser.d.ts +35 -0
- package/dist/src/ai/researcher/parser.js +1 -1
- package/dist/src/ai/researcher/research-result.d.ts +15 -0
- package/dist/src/ai/researcher/research-result.js +0 -19
- package/dist/src/ai/researcher/sections.d.ts +19 -0
- package/dist/src/ai/researcher/sections.js +5 -1
- package/dist/src/ai/researcher.js +12 -53
- package/dist/src/ai/rules.d.ts +16 -0
- package/dist/src/ai/rules.js +22 -15
- package/dist/src/ai/session-analyst.d.ts +11 -0
- package/dist/src/ai/session-analyst.js +8 -5
- package/dist/src/ai/task-agent.d.ts +28 -0
- package/dist/src/ai/task-agent.js +2 -27
- package/dist/src/ai/tester.d.ts +101 -0
- package/dist/src/ai/tester.js +10 -61
- package/dist/src/ai/tools.d.ts +52 -0
- package/dist/src/ai/tools.js +119 -101
- package/dist/src/api/api-client.d.ts +17 -0
- package/dist/src/api/request-result.d.ts +44 -0
- package/dist/src/api/request-store.d.ts +29 -0
- package/dist/src/api/spec-reader.d.ts +5 -0
- package/dist/src/api/xhr-capture.d.ts +10 -0
- package/dist/src/browser-server.d.ts +10 -0
- package/dist/src/command-handler.d.ts +68 -0
- package/dist/src/commands/add-rule-command.d.ts +11 -0
- package/dist/src/commands/base-command.d.ts +26 -0
- package/dist/src/commands/clean-command.d.ts +12 -0
- package/dist/src/commands/compact-command.d.ts +20 -0
- package/dist/src/commands/compact-command.js +6 -6
- package/dist/src/commands/context-aria-command.d.ts +6 -0
- package/dist/src/commands/context-command.d.ts +11 -0
- package/dist/src/commands/context-command.js +1 -1
- package/dist/src/commands/context-data-command.d.ts +6 -0
- package/dist/src/commands/context-experience-command.d.ts +6 -0
- package/dist/src/commands/context-html-command.d.ts +6 -0
- package/dist/src/commands/context-knowledge-command.d.ts +6 -0
- package/dist/src/commands/context-knowledge-command.js +1 -1
- package/dist/src/commands/debug-command.d.ts +6 -0
- package/dist/src/commands/drill-command.d.ts +10 -0
- package/dist/src/commands/exit-command.d.ts +7 -0
- package/dist/src/commands/experience-command.d.ts +15 -0
- package/dist/src/commands/experience-command.js +1 -1
- package/dist/src/commands/explore-command.d.ts +51 -0
- package/dist/src/commands/freesail-command.d.ts +12 -0
- package/dist/src/commands/help-command.d.ts +6 -0
- package/dist/src/commands/index.d.ts +4 -0
- package/dist/src/commands/init-command.d.ts +7 -0
- package/dist/src/commands/knows-command.d.ts +10 -0
- package/dist/src/commands/knows-command.js +1 -1
- package/dist/src/commands/learn-command.d.ts +7 -0
- package/dist/src/commands/learn-command.js +3 -1
- package/dist/src/commands/navigate-command.d.ts +7 -0
- package/dist/src/commands/path-command.d.ts +10 -0
- package/dist/src/commands/plan-clear-command.d.ts +7 -0
- package/dist/src/commands/plan-command.d.ts +14 -0
- package/dist/src/commands/plan-edit-command.d.ts +7 -0
- package/dist/src/commands/plan-load-command.d.ts +7 -0
- package/dist/src/commands/plan-reload-command.d.ts +7 -0
- package/dist/src/commands/plan-save-command.d.ts +7 -0
- package/dist/src/commands/plans-command.d.ts +21 -0
- package/dist/src/commands/rerun-command.d.ts +7 -0
- package/dist/src/commands/research-command.d.ts +11 -0
- package/dist/src/commands/runs-command.d.ts +7 -0
- package/dist/src/commands/start-command.d.ts +7 -0
- package/dist/src/commands/status-command.d.ts +6 -0
- package/dist/src/commands/test-command.d.ts +11 -0
- package/dist/src/components/ActivityPane.d.ts +6 -0
- package/dist/src/components/AddKnowledge.d.ts +10 -0
- package/dist/src/components/AddKnowledge.js +3 -7
- package/dist/src/components/AddRule.d.ts +9 -0
- package/dist/src/components/App.d.ts +9 -0
- package/dist/src/components/App.js +2 -2
- package/dist/src/components/Autocomplete.d.ts +3 -0
- package/dist/src/components/InputPane.d.ts +13 -0
- package/dist/src/components/InputReadline.d.ts +17 -0
- package/dist/src/components/LogPane.d.ts +6 -0
- package/dist/src/components/PlanEditor.d.ts +9 -0
- package/dist/src/components/PlanPane.d.ts +13 -0
- package/dist/src/components/SessionTimer.d.ts +6 -0
- package/dist/src/components/StateTransitionPane.d.ts +8 -0
- package/dist/src/components/StatusPane.d.ts +4 -0
- package/dist/src/components/TaskPane.d.ts +9 -0
- package/dist/src/components/Welcome.d.ts +3 -0
- package/dist/src/components/WelcomeChecklist.d.ts +9 -0
- package/dist/src/components/WelcomeCommands.d.ts +6 -0
- package/dist/src/components/autocomplete-store.d.ts +10 -0
- package/dist/src/components/parse-keypress.d.ts +12 -0
- package/dist/src/config.d.ts +253 -0
- package/dist/src/config.js +12 -0
- package/dist/src/execution-controller.d.ts +25 -0
- package/dist/src/experience-tracker.d.ts +112 -0
- package/dist/src/experience-tracker.js +83 -155
- package/dist/src/explorbot.d.ts +106 -0
- package/dist/src/explorbot.js +17 -51
- package/dist/src/explorer.d.ts +129 -0
- package/dist/src/explorer.js +31 -77
- package/dist/src/index.d.ts +6 -0
- package/dist/src/index.js +2 -55
- package/dist/src/knowledge-tracker.d.ts +33 -0
- package/dist/src/knowledge-tracker.js +47 -47
- package/dist/src/observability.d.ts +13 -0
- package/dist/src/observability.js +2 -1
- package/dist/src/playwright-recorder.d.ts +31 -0
- package/dist/src/reporter.d.ts +34 -0
- package/dist/src/state-manager.d.ts +160 -0
- package/dist/src/state-manager.js +10 -136
- package/dist/src/stats.d.ts +24 -0
- package/dist/src/suite.d.ts +24 -0
- package/dist/src/test-plan.d.ts +166 -0
- package/dist/src/utils/aria.d.ts +25 -0
- package/dist/src/utils/aria.js +4 -1
- package/dist/src/utils/browser-errors.d.ts +8 -0
- package/dist/src/utils/cache.d.ts +15 -0
- package/dist/src/utils/cache.js +34 -0
- package/dist/src/utils/cli-name.d.ts +1 -0
- package/dist/src/utils/code-extractor.d.ts +1 -0
- package/dist/src/utils/context-formatter.d.ts +31 -0
- package/dist/src/utils/context-formatter.js +15 -27
- package/dist/src/utils/error-page.d.ts +12 -0
- package/dist/src/utils/expandable.d.ts +4 -0
- package/dist/src/utils/hooks-runner.d.ts +15 -0
- package/dist/src/utils/html-diff.d.ts +19 -0
- package/dist/src/utils/html.d.ts +129 -0
- package/dist/src/utils/html.js +71 -0
- package/dist/src/utils/log-filters.d.ts +6 -0
- package/dist/src/utils/logger.d.ts +35 -0
- package/dist/src/utils/loop.d.ts +35 -0
- package/dist/src/utils/markdown-files.d.ts +10 -0
- package/dist/src/utils/markdown-files.js +21 -0
- package/dist/src/utils/markdown-parser.d.ts +17 -0
- package/dist/src/utils/markdown-query.d.ts +46 -0
- package/dist/src/utils/markdown-query.js +14 -1
- package/dist/src/utils/markdown-terminal.d.ts +1 -0
- package/dist/src/utils/next-steps.d.ts +11 -0
- package/dist/src/utils/page-readiness.d.ts +7 -0
- package/dist/src/utils/page-readiness.js +1 -1
- package/dist/src/utils/research-parser.d.ts +1 -0
- package/dist/src/utils/retry.d.ts +8 -0
- package/dist/src/utils/rules-loader.d.ts +12 -0
- package/dist/src/utils/secrets.d.ts +4 -0
- package/dist/src/utils/secrets.js +28 -0
- package/dist/src/utils/step-analyzer.d.ts +11 -0
- package/dist/src/utils/strings.d.ts +5 -0
- package/dist/src/utils/strings.js +10 -0
- package/dist/src/utils/test-files.d.ts +3 -0
- package/dist/src/utils/test-plan-markdown.d.ts +9 -0
- package/dist/src/utils/throttle.d.ts +2 -0
- package/dist/src/utils/unique-names.d.ts +2 -0
- package/dist/src/utils/url-matcher.d.ts +6 -0
- package/dist/src/utils/web-element.d.ts +45 -0
- package/dist/src/utils/web-element.js +3 -0
- package/dist/src/utils/web-sandbox.d.ts +4 -0
- package/dist/src/utils/web-sandbox.js +37 -0
- package/dist/src/utils/xpath.d.ts +17 -0
- package/package.json +8 -6
- package/src/action-result.ts +80 -168
- package/src/action.ts +16 -123
- package/src/ai/captain/web-mode.ts +0 -7
- package/src/ai/captain.ts +5 -17
- package/src/ai/conversation.ts +9 -32
- package/src/ai/driller.ts +3 -16
- package/src/ai/experience-compactor.ts +16 -35
- package/src/ai/fisherman-tools.ts +1 -1
- package/src/ai/fisherman.ts +0 -4
- package/src/ai/historian/codeceptjs.ts +4 -2
- package/src/ai/historian/playwright.ts +4 -2
- package/src/ai/historian/utils.ts +0 -1
- package/src/ai/historian.ts +4 -7
- package/src/ai/navigator.ts +13 -49
- package/src/ai/pilot.ts +6 -16
- package/src/ai/planner.ts +56 -44
- package/src/ai/provider.ts +86 -139
- package/src/ai/quartermaster.ts +34 -41
- package/src/ai/rerunner.ts +17 -21
- package/src/ai/researcher/cache.ts +10 -16
- package/src/ai/researcher/deep-analysis.ts +3 -6
- package/src/ai/researcher/parser.ts +1 -1
- package/src/ai/researcher/research-result.ts +0 -18
- package/src/ai/researcher/sections.ts +5 -1
- package/src/ai/researcher.ts +11 -59
- package/src/ai/rules.ts +24 -13
- package/src/ai/session-analyst.ts +8 -5
- package/src/ai/task-agent.ts +3 -30
- package/src/ai/tester.ts +12 -73
- package/src/ai/tools.ts +119 -106
- package/src/commands/compact-command.ts +7 -8
- package/src/commands/context-command.ts +1 -1
- package/src/commands/context-knowledge-command.ts +1 -1
- package/src/commands/experience-command.ts +1 -1
- package/src/commands/knows-command.ts +1 -1
- package/src/commands/learn-command.ts +3 -1
- package/src/components/AddKnowledge.tsx +5 -7
- package/src/components/App.tsx +2 -2
- package/src/config.ts +12 -0
- package/src/experience-tracker.ts +90 -152
- package/src/explorbot.ts +17 -51
- package/src/explorer.ts +34 -81
- package/src/index.ts +9 -0
- package/src/knowledge-tracker.ts +49 -50
- package/src/observability.ts +2 -1
- package/src/state-manager.ts +15 -164
- package/src/utils/aria.ts +11 -2
- package/src/utils/cache.ts +40 -0
- package/src/utils/context-formatter.ts +12 -15
- package/src/utils/html.ts +79 -0
- package/src/utils/markdown-files.ts +30 -0
- package/src/utils/markdown-query.ts +15 -1
- package/src/utils/page-readiness.ts +1 -1
- package/src/utils/secrets.ts +28 -0
- package/src/utils/strings.ts +12 -0
- package/src/utils/web-element.ts +4 -0
- package/src/utils/web-sandbox.ts +43 -0
- package/src/index.tsx +0 -62
package/src/explorer.ts
CHANGED
|
@@ -14,17 +14,17 @@ import { visuallyAnnotateContainers } from './ai/researcher/coordinates.ts';
|
|
|
14
14
|
import { RequestStore } from './api/request-store.ts';
|
|
15
15
|
import { XhrCapture } from './api/xhr-capture.ts';
|
|
16
16
|
import type { ExplorbotConfig } from './config.js';
|
|
17
|
-
import { ConfigParser
|
|
18
|
-
import type {
|
|
17
|
+
import { ConfigParser } from './config.js';
|
|
18
|
+
import type { ExperienceTracker } from './experience-tracker.js';
|
|
19
19
|
import { KnowledgeTracker } from './knowledge-tracker.js';
|
|
20
20
|
import { PlaywrightRecorder } from './playwright-recorder.ts';
|
|
21
21
|
import { Reporter } from './reporter.ts';
|
|
22
22
|
import { StateManager } from './state-manager.js';
|
|
23
23
|
import { Test, TestResult } from './test-plan.ts';
|
|
24
|
-
import { BrowserRecoveryError, isFatalBrowserError, isNavigationTransitionError } from './utils/browser-errors.ts';
|
|
24
|
+
import { BrowserRecoveryError, browserErrorMessage, isFatalBrowserError, isNavigationTransitionError } from './utils/browser-errors.ts';
|
|
25
25
|
import { ELEMENT_EXTRACTION_CONFIG, getElementDataExtractorSource } from './utils/html.ts';
|
|
26
26
|
import { createDebug, log, tag } from './utils/logger.js';
|
|
27
|
-
import { waitForPageReadiness } from './utils/page-readiness.ts';
|
|
27
|
+
import { sleep, waitForPageReadiness } from './utils/page-readiness.ts';
|
|
28
28
|
import { WebElement } from './utils/web-element.ts';
|
|
29
29
|
|
|
30
30
|
declare global {
|
|
@@ -64,7 +64,6 @@ class Explorer {
|
|
|
64
64
|
private stateManager!: StateManager;
|
|
65
65
|
private knowledgeTracker!: KnowledgeTracker;
|
|
66
66
|
config: ExplorbotConfig;
|
|
67
|
-
private userResolveFn: UserResolveFunction | null = null;
|
|
68
67
|
private options?: { show?: boolean; headless?: boolean; incognito?: boolean; session?: string };
|
|
69
68
|
private reporter!: Reporter;
|
|
70
69
|
private otherTabs: TabInfo[] = [];
|
|
@@ -77,13 +76,13 @@ class Explorer {
|
|
|
77
76
|
private testConsoleHandler: ((message: any) => void) | null = null;
|
|
78
77
|
private testDialogHandler: ((dialog: any) => void) | null = null;
|
|
79
78
|
|
|
80
|
-
constructor(config: ExplorbotConfig, aiProvider: AIProvider, options
|
|
79
|
+
constructor(config: ExplorbotConfig, aiProvider: AIProvider, options: { show?: boolean; headless?: boolean; incognito?: boolean; session?: string } | undefined, experienceTracker: ExperienceTracker, knowledgeTracker: KnowledgeTracker) {
|
|
81
80
|
this.config = config;
|
|
82
81
|
this.aiProvider = aiProvider;
|
|
83
82
|
this.options = options;
|
|
84
83
|
this.initializeContainer();
|
|
85
|
-
this.
|
|
86
|
-
this.
|
|
84
|
+
this.knowledgeTracker = knowledgeTracker;
|
|
85
|
+
this.stateManager = new StateManager(experienceTracker, knowledgeTracker);
|
|
87
86
|
this.reporter = new Reporter(config.reporter, this.stateManager);
|
|
88
87
|
}
|
|
89
88
|
|
|
@@ -91,8 +90,7 @@ class Explorer {
|
|
|
91
90
|
try {
|
|
92
91
|
// Use project root for output directory, not current working directory
|
|
93
92
|
const configParser = ConfigParser.getInstance();
|
|
94
|
-
const
|
|
95
|
-
const projectRoot = configPath ? path.dirname(configPath) : process.cwd();
|
|
93
|
+
const projectRoot = configParser.getProjectRoot();
|
|
96
94
|
(global as any).output_dir = path.join(projectRoot, 'output', 'states');
|
|
97
95
|
(global as any).codecept_dir = projectRoot;
|
|
98
96
|
|
|
@@ -155,8 +153,7 @@ class Explorer {
|
|
|
155
153
|
};
|
|
156
154
|
|
|
157
155
|
if (this.config.stepsFile) {
|
|
158
|
-
const
|
|
159
|
-
const projectRoot = configPath ? path.dirname(configPath) : process.cwd();
|
|
156
|
+
const projectRoot = ConfigParser.getInstance().getProjectRoot();
|
|
160
157
|
codeceptConfig.include = { I: path.resolve(projectRoot, this.config.stepsFile) };
|
|
161
158
|
}
|
|
162
159
|
|
|
@@ -164,17 +161,9 @@ class Explorer {
|
|
|
164
161
|
}
|
|
165
162
|
|
|
166
163
|
public getConfig(): ExplorbotConfig {
|
|
167
|
-
if (!this.config) {
|
|
168
|
-
throw new Error('Configuration not loaded. Call run() first.');
|
|
169
|
-
}
|
|
170
164
|
return this.config;
|
|
171
165
|
}
|
|
172
166
|
|
|
173
|
-
public getConfigPath(): string | null {
|
|
174
|
-
const configParser = ConfigParser.getInstance();
|
|
175
|
-
return configParser.getConfigPath();
|
|
176
|
-
}
|
|
177
|
-
|
|
178
167
|
public getAIProvider(): AIProvider {
|
|
179
168
|
return this.aiProvider;
|
|
180
169
|
}
|
|
@@ -183,10 +172,6 @@ class Explorer {
|
|
|
183
172
|
return this.stateManager;
|
|
184
173
|
}
|
|
185
174
|
|
|
186
|
-
public getCurrentUrl(): string {
|
|
187
|
-
return this.stateManager.getCurrentState()!.url || '?';
|
|
188
|
-
}
|
|
189
|
-
|
|
190
175
|
public getKnowledgeTracker(): KnowledgeTracker {
|
|
191
176
|
return this.knowledgeTracker;
|
|
192
177
|
}
|
|
@@ -227,10 +212,6 @@ class Explorer {
|
|
|
227
212
|
return;
|
|
228
213
|
}
|
|
229
214
|
|
|
230
|
-
if (!this.config) {
|
|
231
|
-
await this.initializeContainer();
|
|
232
|
-
}
|
|
233
|
-
|
|
234
215
|
await codeceptjs.recorder.start();
|
|
235
216
|
await codeceptjs.container.started(null);
|
|
236
217
|
storeListener();
|
|
@@ -359,8 +340,8 @@ class Explorer {
|
|
|
359
340
|
return this.runWithBrowserRecovery('executeAction', () => this.createAction().execute(code));
|
|
360
341
|
}
|
|
361
342
|
|
|
362
|
-
async attemptAction(code: string, originalMessage?: string
|
|
363
|
-
return this.runWithBrowserRecovery('attemptAction', () => this.createAction().attempt(code, originalMessage
|
|
343
|
+
async attemptAction(code: string, originalMessage?: string): Promise<boolean> {
|
|
344
|
+
return this.runWithBrowserRecovery('attemptAction', () => this.createAction().attempt(code, originalMessage));
|
|
364
345
|
}
|
|
365
346
|
|
|
366
347
|
getPlaywrightRecorder(): PlaywrightRecorder {
|
|
@@ -388,7 +369,7 @@ class Explorer {
|
|
|
388
369
|
try {
|
|
389
370
|
await action.execute(`I.amOnPage(${serializedUrl})`);
|
|
390
371
|
} catch (err) {
|
|
391
|
-
const msg =
|
|
372
|
+
const msg = browserErrorMessage(err);
|
|
392
373
|
if (!RECOVERABLE_NAVIGATION_ERRORS.test(msg)) throw err;
|
|
393
374
|
tag('warning').log(`Navigation warning (continuing after load): ${msg.split('\n')[0]}`);
|
|
394
375
|
await this.waitForPageReadiness();
|
|
@@ -437,7 +418,7 @@ class Explorer {
|
|
|
437
418
|
return result;
|
|
438
419
|
} catch (error) {
|
|
439
420
|
if (this.isFatalBrowserError(error)) {
|
|
440
|
-
tag('warning').log(`getEidxInContainer: ${error
|
|
421
|
+
tag('warning').log(`getEidxInContainer: ${browserErrorMessage(error)}`);
|
|
441
422
|
await this.recoverFromBrowserError();
|
|
442
423
|
}
|
|
443
424
|
return [];
|
|
@@ -452,18 +433,13 @@ class Explorer {
|
|
|
452
433
|
return await el.first().getAttribute(ELEMENT_EXTRACTION_CONFIG.attrs.eidx);
|
|
453
434
|
} catch (error) {
|
|
454
435
|
if (this.isFatalBrowserError(error)) {
|
|
455
|
-
tag('warning').log(`getEidxByLocator: ${error
|
|
436
|
+
tag('warning').log(`getEidxByLocator: ${browserErrorMessage(error)}`);
|
|
456
437
|
await this.recoverFromBrowserError();
|
|
457
438
|
}
|
|
458
439
|
return null;
|
|
459
440
|
}
|
|
460
441
|
}
|
|
461
442
|
|
|
462
|
-
async reload() {
|
|
463
|
-
await this.closeOtherTabs();
|
|
464
|
-
await this.playwrightHelper.page.reload();
|
|
465
|
-
}
|
|
466
|
-
|
|
467
443
|
private resolveBrowserUrl(url?: string): string | null {
|
|
468
444
|
if (!url) return null;
|
|
469
445
|
try {
|
|
@@ -508,7 +484,7 @@ class Explorer {
|
|
|
508
484
|
await this.playwrightHelper.page.reload({ waitUntil: 'domcontentloaded', timeout: 10000 });
|
|
509
485
|
return this.waitForPageReadiness();
|
|
510
486
|
} catch (err) {
|
|
511
|
-
tag('error').log(`Browser recovery failed: ${err
|
|
487
|
+
tag('error').log(`Browser recovery failed: ${browserErrorMessage(err)}`);
|
|
512
488
|
return false;
|
|
513
489
|
}
|
|
514
490
|
}
|
|
@@ -552,7 +528,7 @@ class Explorer {
|
|
|
552
528
|
tag('success').log('Browser restarted');
|
|
553
529
|
return true;
|
|
554
530
|
} catch (err) {
|
|
555
|
-
tag('error').log(`Browser restart failed: ${err
|
|
531
|
+
tag('error').log(`Browser restart failed: ${browserErrorMessage(err)}`);
|
|
556
532
|
return false;
|
|
557
533
|
}
|
|
558
534
|
}
|
|
@@ -584,7 +560,7 @@ class Explorer {
|
|
|
584
560
|
return await page.evaluate(() => window.top !== window.self);
|
|
585
561
|
} catch (error) {
|
|
586
562
|
if (this.isFatalBrowserError(error)) {
|
|
587
|
-
tag('warning').log(`isInsideIframe: ${error
|
|
563
|
+
tag('warning').log(`isInsideIframe: ${browserErrorMessage(error)}`);
|
|
588
564
|
await this.recoverFromBrowserError();
|
|
589
565
|
}
|
|
590
566
|
return false;
|
|
@@ -608,10 +584,6 @@ class Explorer {
|
|
|
608
584
|
this.otherTabs = [];
|
|
609
585
|
}
|
|
610
586
|
|
|
611
|
-
setUserResolve(userResolveFn: UserResolveFunction): void {
|
|
612
|
-
this.userResolveFn = userResolveFn;
|
|
613
|
-
}
|
|
614
|
-
|
|
615
587
|
private listenToStateChanged(): void {
|
|
616
588
|
if (!this.playwrightHelper) {
|
|
617
589
|
debugLog('Playwright helper not available for state monitoring');
|
|
@@ -727,17 +699,22 @@ class Explorer {
|
|
|
727
699
|
|
|
728
700
|
this.watchActiveTestPage();
|
|
729
701
|
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
codeceptjs.event.dispatcher.on('step.passed', (step: any) => stepHandler(step, 'passed'));
|
|
733
|
-
codeceptjs.event.dispatcher.on('step.failed', (step: any, error: any) => {
|
|
702
|
+
const onStepPassed = (step: any) => stepHandler(step, 'passed');
|
|
703
|
+
const onStepFailed = (step: any, error: any) => {
|
|
734
704
|
stepHandler(step, 'failed', error?.message || String(error), error?.stack);
|
|
735
|
-
}
|
|
736
|
-
|
|
737
|
-
codeceptjs.event.dispatcher.off('step.passed',
|
|
738
|
-
codeceptjs.event.dispatcher.off('step.failed',
|
|
705
|
+
};
|
|
706
|
+
const onTestAfter = () => {
|
|
707
|
+
codeceptjs.event.dispatcher.off('step.passed', onStepPassed);
|
|
708
|
+
codeceptjs.event.dispatcher.off('step.failed', onStepFailed);
|
|
709
|
+
codeceptjs.event.dispatcher.off('test.after', onTestAfter);
|
|
739
710
|
this.unwatchActiveTestPages();
|
|
740
|
-
}
|
|
711
|
+
};
|
|
712
|
+
|
|
713
|
+
codeceptjs.event.dispatcher.emit('test.before', codeceptjsTest);
|
|
714
|
+
codeceptjs.event.dispatcher.emit('test.start', codeceptjsTest);
|
|
715
|
+
codeceptjs.event.dispatcher.on('step.passed', onStepPassed);
|
|
716
|
+
codeceptjs.event.dispatcher.on('step.failed', onStepFailed);
|
|
717
|
+
codeceptjs.event.dispatcher.on('test.after', onTestAfter);
|
|
741
718
|
|
|
742
719
|
return true;
|
|
743
720
|
}
|
|
@@ -755,12 +732,8 @@ class Explorer {
|
|
|
755
732
|
return true;
|
|
756
733
|
}
|
|
757
734
|
|
|
758
|
-
async ensureActiveTestPageAvailable(): Promise<boolean> {
|
|
759
|
-
return this.ensurePageAvailable();
|
|
760
|
-
}
|
|
761
|
-
|
|
762
735
|
async handleExecutionError(error: unknown): Promise<BrowserExecutionErrorResult> {
|
|
763
|
-
const message =
|
|
736
|
+
const message = browserErrorMessage(error);
|
|
764
737
|
tag('error').log(`Browser execution error: ${message}`);
|
|
765
738
|
|
|
766
739
|
if (error instanceof Error && error.name === 'AbortError') {
|
|
@@ -868,33 +841,13 @@ class Explorer {
|
|
|
868
841
|
this.observedTestPages.clear();
|
|
869
842
|
}
|
|
870
843
|
|
|
871
|
-
async hasPlaywrightLocator(locatorFn: (page: any) => any, opts: { multiple?: boolean; contents?: boolean; success?: (locator: any) => Promise<void> | void } = {}): Promise<boolean> {
|
|
872
|
-
try {
|
|
873
|
-
const pwLocator = locatorFn(this.playwrightHelper.page);
|
|
874
|
-
const count = await pwLocator.count();
|
|
875
|
-
if (opts.multiple ? count === 0 : count !== 1) return false;
|
|
876
|
-
if (opts.contents) {
|
|
877
|
-
const html = await pwLocator.first().innerHTML();
|
|
878
|
-
if (!html?.trim()) return false;
|
|
879
|
-
}
|
|
880
|
-
if (opts.success) await opts.success(pwLocator);
|
|
881
|
-
return true;
|
|
882
|
-
} catch (error) {
|
|
883
|
-
if (this.isFatalBrowserError(error)) {
|
|
884
|
-
tag('warning').log(`hasPlaywrightLocator: ${error instanceof Error ? error.message : error}`);
|
|
885
|
-
await this.recoverFromBrowserError();
|
|
886
|
-
}
|
|
887
|
-
return false;
|
|
888
|
-
}
|
|
889
|
-
}
|
|
890
|
-
|
|
891
844
|
async playwrightLocatorCount(locatorFn: (page: any) => any): Promise<number> {
|
|
892
845
|
try {
|
|
893
846
|
const pwLocator = locatorFn(this.playwrightHelper.page);
|
|
894
847
|
return await pwLocator.count();
|
|
895
848
|
} catch (error) {
|
|
896
849
|
if (this.isFatalBrowserError(error)) {
|
|
897
|
-
tag('warning').log(`playwrightLocatorCount: ${error
|
|
850
|
+
tag('warning').log(`playwrightLocatorCount: ${browserErrorMessage(error)}`);
|
|
898
851
|
await this.recoverFromBrowserError();
|
|
899
852
|
}
|
|
900
853
|
throw error;
|
|
@@ -916,7 +869,7 @@ class Explorer {
|
|
|
916
869
|
|
|
917
870
|
this.stateManager.updateStateFromBasic(newUrl, newTitle, 'navigation');
|
|
918
871
|
|
|
919
|
-
await
|
|
872
|
+
await sleep(500);
|
|
920
873
|
});
|
|
921
874
|
}
|
|
922
875
|
|
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';
|
package/src/knowledge-tracker.ts
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync,
|
|
2
|
-
import {
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import dedent from 'dedent';
|
|
3
4
|
import matter from 'gray-matter';
|
|
4
5
|
import { ActionResult } from './action-result.js';
|
|
5
6
|
import { ConfigParser } from './config.js';
|
|
6
7
|
import { getCliName } from './utils/cli-name.ts';
|
|
7
|
-
import { createDebug } from './utils/logger.js';
|
|
8
|
+
import { createDebug, pluralize, tag } from './utils/logger.js';
|
|
9
|
+
import { loadMarkdownFiles } from './utils/markdown-files.js';
|
|
10
|
+
import { mdq } from './utils/markdown-query.js';
|
|
11
|
+
import { isSecretName, registerSecret } from './utils/secrets.js';
|
|
12
|
+
import { slugify } from './utils/strings.js';
|
|
8
13
|
|
|
9
14
|
const debugLog = createDebug('explorbot:knowledge-tracker');
|
|
10
15
|
|
|
11
|
-
interface Knowledge {
|
|
16
|
+
export interface Knowledge {
|
|
12
17
|
filePath: string;
|
|
13
18
|
url: string;
|
|
14
19
|
content: string;
|
|
@@ -23,14 +28,7 @@ export class KnowledgeTracker {
|
|
|
23
28
|
constructor() {
|
|
24
29
|
const configParser = ConfigParser.getInstance();
|
|
25
30
|
const config = configParser.getConfig();
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
if (configPath) {
|
|
29
|
-
const projectRoot = dirname(configPath);
|
|
30
|
-
this.knowledgeDir = join(projectRoot, config.dirs?.knowledge || 'knowledge');
|
|
31
|
-
} else {
|
|
32
|
-
this.knowledgeDir = config.dirs?.knowledge || 'knowledge';
|
|
33
|
-
}
|
|
31
|
+
this.knowledgeDir = configParser.resolveProjectDir(config.dirs?.knowledge || 'knowledge');
|
|
34
32
|
|
|
35
33
|
if (!existsSync(this.knowledgeDir)) {
|
|
36
34
|
mkdirSync(this.knowledgeDir, { recursive: true });
|
|
@@ -42,29 +40,13 @@ export class KnowledgeTracker {
|
|
|
42
40
|
|
|
43
41
|
this.knowledgeFiles = [];
|
|
44
42
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
for (const filePath of files) {
|
|
54
|
-
try {
|
|
55
|
-
const fileContent = readFileSync(filePath, 'utf8');
|
|
56
|
-
const parsed = matter(fileContent);
|
|
57
|
-
const urlPattern = parsed.data.url || parsed.data.path || '*';
|
|
58
|
-
|
|
59
|
-
this.knowledgeFiles.push({
|
|
60
|
-
filePath,
|
|
61
|
-
url: urlPattern,
|
|
62
|
-
content: this.interpolateVars(parsed.content),
|
|
63
|
-
...parsed.data,
|
|
64
|
-
});
|
|
65
|
-
} catch (error) {
|
|
66
|
-
// Skip invalid files
|
|
67
|
-
}
|
|
43
|
+
for (const entry of loadMarkdownFiles(this.knowledgeDir, { recursive: true })) {
|
|
44
|
+
this.knowledgeFiles.push({
|
|
45
|
+
filePath: entry.filePath,
|
|
46
|
+
url: entry.data.url || entry.data.path || '*',
|
|
47
|
+
content: this.interpolateVars(entry.content),
|
|
48
|
+
...entry.data,
|
|
49
|
+
});
|
|
68
50
|
}
|
|
69
51
|
|
|
70
52
|
this.isLoaded = true;
|
|
@@ -78,25 +60,40 @@ export class KnowledgeTracker {
|
|
|
78
60
|
});
|
|
79
61
|
}
|
|
80
62
|
|
|
63
|
+
renderRelevantKnowledge(state: ActionResult): string {
|
|
64
|
+
const knowledgeFiles = this.getRelevantKnowledge(state);
|
|
65
|
+
if (knowledgeFiles.length === 0) return '';
|
|
66
|
+
|
|
67
|
+
const knowledgeContent = knowledgeFiles
|
|
68
|
+
.map((k) => k.content)
|
|
69
|
+
.filter((k) => !!k)
|
|
70
|
+
.join('\n\n');
|
|
71
|
+
|
|
72
|
+
tag('operation').log(`Found ${knowledgeFiles.length} relevant knowledge ${pluralize(knowledgeFiles.length, 'file')}`);
|
|
73
|
+
return dedent`
|
|
74
|
+
<knowledge>
|
|
75
|
+
Here is relevant knowledge for this page:
|
|
76
|
+
|
|
77
|
+
${knowledgeContent}
|
|
78
|
+
</knowledge>
|
|
79
|
+
`;
|
|
80
|
+
}
|
|
81
|
+
|
|
81
82
|
addKnowledge(urlPattern: string, description: string): { filename: string; filePath: string; isNewFile: boolean } {
|
|
82
83
|
const configParser = ConfigParser.getInstance();
|
|
83
|
-
const config = configParser.getConfig();
|
|
84
84
|
const configPath = configParser.getConfigPath();
|
|
85
85
|
|
|
86
86
|
if (!configPath) {
|
|
87
87
|
throw new Error(`No explorbot configuration found. Please run "${getCliName()} init" first.`);
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if (!existsSync(knowledgeDir)) {
|
|
94
|
-
mkdirSync(knowledgeDir, { recursive: true });
|
|
90
|
+
if (!existsSync(this.knowledgeDir)) {
|
|
91
|
+
mkdirSync(this.knowledgeDir, { recursive: true });
|
|
95
92
|
}
|
|
96
93
|
|
|
97
94
|
const normalizedUrl = this.normalizeUrl(urlPattern);
|
|
98
95
|
const filename = this.generateFilename(normalizedUrl);
|
|
99
|
-
const filePath = join(knowledgeDir, filename);
|
|
96
|
+
const filePath = join(this.knowledgeDir, filename);
|
|
100
97
|
|
|
101
98
|
const isNewFile = !existsSync(filePath);
|
|
102
99
|
|
|
@@ -127,6 +124,8 @@ export class KnowledgeTracker {
|
|
|
127
124
|
writeFileSync(filePath, fileContent, 'utf8');
|
|
128
125
|
}
|
|
129
126
|
|
|
127
|
+
this.isLoaded = false;
|
|
128
|
+
|
|
130
129
|
return { filename, filePath, isNewFile };
|
|
131
130
|
}
|
|
132
131
|
|
|
@@ -138,9 +137,14 @@ export class KnowledgeTracker {
|
|
|
138
137
|
const namespace = expr.slice(0, dotIndex);
|
|
139
138
|
const key = expr.slice(dotIndex + 1);
|
|
140
139
|
|
|
141
|
-
if (namespace === 'env')
|
|
140
|
+
if (namespace === 'env') {
|
|
141
|
+
const value = process.env[key] ?? '';
|
|
142
|
+
if (isSecretName(key)) registerSecret(value);
|
|
143
|
+
return value;
|
|
144
|
+
}
|
|
142
145
|
|
|
143
146
|
if (namespace === 'config') {
|
|
147
|
+
if (isSecretName(key)) return '';
|
|
144
148
|
const config = ConfigParser.getInstance().getConfig();
|
|
145
149
|
const value = key.split('.').reduce((obj: any, k) => obj?.[k], config);
|
|
146
150
|
if (value !== undefined && typeof value !== 'object') return String(value);
|
|
@@ -152,12 +156,7 @@ export class KnowledgeTracker {
|
|
|
152
156
|
}
|
|
153
157
|
|
|
154
158
|
private generateFilename(url: string): string {
|
|
155
|
-
let filename = url
|
|
156
|
-
.replace(/https?:\/\//g, '')
|
|
157
|
-
.replace(/[^a-zA-Z0-9_]/g, '_')
|
|
158
|
-
.replace(/_+/g, '_')
|
|
159
|
-
.replace(/^_|_$/g, '')
|
|
160
|
-
.toLowerCase();
|
|
159
|
+
let filename = slugify(url.replace(/https?:\/\//g, ''));
|
|
161
160
|
|
|
162
161
|
if (!filename || filename === '*') {
|
|
163
162
|
filename = 'general';
|
|
@@ -188,7 +187,7 @@ export class KnowledgeTracker {
|
|
|
188
187
|
|
|
189
188
|
return this.knowledgeFiles.map((knowledge) => {
|
|
190
189
|
const content = knowledge.content.trim();
|
|
191
|
-
const firstLine = content.split('\n')[0]?.trim() || '';
|
|
190
|
+
const firstLine = mdq(content).meta()[0]?.text.split('\n')[0]?.trim() || '';
|
|
192
191
|
return {
|
|
193
192
|
url: knowledge.url,
|
|
194
193
|
firstLine,
|
package/src/observability.ts
CHANGED
|
@@ -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;
|