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/action-result.ts
CHANGED
|
@@ -2,10 +2,12 @@ import fs from 'node:fs';
|
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { ConfigParser, type HtmlConfig, outputPath } from './config.ts';
|
|
4
4
|
import type { Link, WebPageState } from './state-manager.ts';
|
|
5
|
-
import { compactAriaSnapshot, diffAriaSnapshots } from './utils/aria.ts';
|
|
5
|
+
import { LARGE_ARIA_CHANGE_THRESHOLD, compactAriaSnapshot, diffAriaSnapshots } from './utils/aria.ts';
|
|
6
|
+
import { TTLCache } from './utils/cache.ts';
|
|
6
7
|
import { type HtmlDiffPart, type HtmlDiffResult, htmlDiff } from './utils/html-diff.ts';
|
|
7
8
|
import { extractHeadings, extractLinks, extractTargetedHtml, htmlCombinedSnapshot, htmlMinimalUISnapshot, htmlTextSnapshot, minifyHtml } from './utils/html.ts';
|
|
8
9
|
import { createDebug } from './utils/logger.ts';
|
|
10
|
+
import { slugify } from './utils/strings.ts';
|
|
9
11
|
import { extractStatePath, matchesUrl } from './utils/url-matcher.ts';
|
|
10
12
|
|
|
11
13
|
const debugLog = createDebug('explorbot:state');
|
|
@@ -37,6 +39,7 @@ export interface PageDiff {
|
|
|
37
39
|
previousUrl?: string;
|
|
38
40
|
currentUrl: string;
|
|
39
41
|
ariaChanges?: string | null;
|
|
42
|
+
ariaChangeCount?: number;
|
|
40
43
|
htmlParts?: HtmlDiffPart[];
|
|
41
44
|
iframes?: string;
|
|
42
45
|
}
|
|
@@ -68,12 +71,12 @@ export class ActionResult implements ActionResultData {
|
|
|
68
71
|
private _screenshot: Buffer | undefined = undefined;
|
|
69
72
|
readonly htmlFile: string | undefined = undefined;
|
|
70
73
|
private _html: string | undefined = undefined;
|
|
74
|
+
private snapshotCache = new TTLCache<string>();
|
|
71
75
|
readonly logFile: string | undefined = undefined;
|
|
72
|
-
private _browserLogs: any[] | undefined = undefined;
|
|
73
76
|
readonly ariaSnapshotFile: string | undefined = undefined;
|
|
74
77
|
private _ariaSnapshot: string | null | undefined = undefined;
|
|
75
78
|
private _lastExtractedHtml: string | undefined = undefined;
|
|
76
|
-
notes:
|
|
79
|
+
notes: string[] = [];
|
|
77
80
|
public links: Link[] = [];
|
|
78
81
|
public verifications?: Record<string, boolean>;
|
|
79
82
|
|
|
@@ -105,14 +108,11 @@ export class ActionResult implements ActionResultData {
|
|
|
105
108
|
this.ariaSnapshotFile = data.ariaSnapshotFile;
|
|
106
109
|
}
|
|
107
110
|
|
|
108
|
-
// Store HTML
|
|
111
|
+
// Store HTML in a private property if provided
|
|
109
112
|
if (data.html !== undefined) {
|
|
110
113
|
this._html = data.html;
|
|
111
114
|
}
|
|
112
115
|
|
|
113
|
-
if (data.browserLogs !== undefined) {
|
|
114
|
-
this._browserLogs = data.browserLogs;
|
|
115
|
-
}
|
|
116
116
|
if (data.screenshot !== undefined) {
|
|
117
117
|
this._screenshot = data.screenshot;
|
|
118
118
|
}
|
|
@@ -120,7 +120,7 @@ export class ActionResult implements ActionResultData {
|
|
|
120
120
|
this._ariaSnapshot = data.ariaSnapshot;
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
if (!this.fullUrl && this.url
|
|
123
|
+
if (!this.fullUrl && this.url) {
|
|
124
124
|
this.fullUrl = this.url;
|
|
125
125
|
}
|
|
126
126
|
|
|
@@ -132,7 +132,7 @@ export class ActionResult implements ActionResultData {
|
|
|
132
132
|
this.links = extractLinks(this._html);
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
-
if (this.url
|
|
135
|
+
if (this.url) {
|
|
136
136
|
this.url = extractStatePath(this.url);
|
|
137
137
|
}
|
|
138
138
|
}
|
|
@@ -151,6 +151,7 @@ export class ActionResult implements ActionResultData {
|
|
|
151
151
|
|
|
152
152
|
set html(value: string) {
|
|
153
153
|
this._html = value;
|
|
154
|
+
this.snapshotCache.clear();
|
|
154
155
|
}
|
|
155
156
|
|
|
156
157
|
get screenshot(): Buffer | undefined {
|
|
@@ -163,16 +164,6 @@ export class ActionResult implements ActionResultData {
|
|
|
163
164
|
return undefined;
|
|
164
165
|
}
|
|
165
166
|
|
|
166
|
-
get browserLogsContent(): any[] {
|
|
167
|
-
if (this._browserLogs !== undefined) {
|
|
168
|
-
return this._browserLogs;
|
|
169
|
-
}
|
|
170
|
-
if (this.logFile) {
|
|
171
|
-
return ActionResult.loadBrowserLogsFromFile(this.logFile);
|
|
172
|
-
}
|
|
173
|
-
return [];
|
|
174
|
-
}
|
|
175
|
-
|
|
176
167
|
get ariaSnapshot(): string | null {
|
|
177
168
|
if (this._ariaSnapshot !== undefined) {
|
|
178
169
|
return this._ariaSnapshot;
|
|
@@ -221,14 +212,14 @@ export class ActionResult implements ActionResultData {
|
|
|
221
212
|
}
|
|
222
213
|
|
|
223
214
|
isSameUrl(state: WebPageState): boolean {
|
|
224
|
-
if (!this.url
|
|
215
|
+
if (!this.url) {
|
|
225
216
|
return false;
|
|
226
217
|
}
|
|
227
218
|
return extractStatePath(state.url) === extractStatePath(this.url);
|
|
228
219
|
}
|
|
229
220
|
|
|
230
221
|
isMatchedBy(state: WebPageState): boolean {
|
|
231
|
-
if (!this.url
|
|
222
|
+
if (!this.url) {
|
|
232
223
|
return false;
|
|
233
224
|
}
|
|
234
225
|
|
|
@@ -263,21 +254,26 @@ export class ActionResult implements ActionResultData {
|
|
|
263
254
|
|
|
264
255
|
async simplifiedHtml(htmlConfig?: HtmlConfig): Promise<string> {
|
|
265
256
|
const normalizedConfig = this.normalizeHtmlConfig(htmlConfig);
|
|
266
|
-
|
|
257
|
+
const cacheKey = `simplified:${JSON.stringify(normalizedConfig?.minimal ?? null)}`;
|
|
258
|
+
return this.snapshotCache.fetch(cacheKey, () => minifyHtml(htmlMinimalUISnapshot(this.html ?? '', normalizedConfig?.minimal)));
|
|
267
259
|
}
|
|
268
260
|
|
|
269
261
|
async combinedHtml(htmlConfig?: HtmlConfig & { keepPositions?: boolean }): Promise<string> {
|
|
270
262
|
const normalizedConfig = this.normalizeHtmlConfig(htmlConfig);
|
|
271
|
-
const
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
263
|
+
const cacheKey = `combined:${!!htmlConfig?.keepPositions}:${JSON.stringify(normalizedConfig?.combined ?? null)}`;
|
|
264
|
+
return this.snapshotCache.fetch(cacheKey, async () => {
|
|
265
|
+
const combinedHtml = await minifyHtml(htmlCombinedSnapshot(this.html ?? '', normalizedConfig?.combined, { keepPositions: htmlConfig?.keepPositions }));
|
|
266
|
+
debugLog(`----${this.url}----`);
|
|
267
|
+
debugLog(`Combined HTML: \n${combinedHtml}`);
|
|
268
|
+
debugLog('----');
|
|
269
|
+
return combinedHtml;
|
|
270
|
+
});
|
|
276
271
|
}
|
|
277
272
|
|
|
278
273
|
async textHtml(htmlConfig?: HtmlConfig): Promise<string> {
|
|
279
274
|
const normalizedConfig = this.normalizeHtmlConfig(htmlConfig);
|
|
280
|
-
|
|
275
|
+
const cacheKey = `text:${JSON.stringify(normalizedConfig?.text ?? null)}`;
|
|
276
|
+
return this.snapshotCache.fetch(cacheKey, () => minifyHtml(htmlTextSnapshot(this.html ?? '', normalizedConfig?.text)));
|
|
281
277
|
}
|
|
282
278
|
|
|
283
279
|
getInteractiveARIA(): string {
|
|
@@ -341,26 +337,26 @@ export class ActionResult implements ActionResultData {
|
|
|
341
337
|
return new ActionResult(actionResultData);
|
|
342
338
|
}
|
|
343
339
|
|
|
344
|
-
private static
|
|
340
|
+
private static loadStateFile(file: string): string | null {
|
|
345
341
|
try {
|
|
346
|
-
const filePath = outputPath('states',
|
|
347
|
-
if (fs.existsSync(filePath))
|
|
348
|
-
|
|
349
|
-
}
|
|
350
|
-
return null;
|
|
342
|
+
const filePath = outputPath('states', file);
|
|
343
|
+
if (!fs.existsSync(filePath)) return null;
|
|
344
|
+
return fs.readFileSync(filePath, 'utf8');
|
|
351
345
|
} catch (error) {
|
|
352
|
-
console.error('Failed to load
|
|
346
|
+
console.error('Failed to load state file:', error);
|
|
353
347
|
return null;
|
|
354
348
|
}
|
|
355
349
|
}
|
|
356
350
|
|
|
351
|
+
private static loadHtmlFromFile(htmlFile: string): string | null {
|
|
352
|
+
return ActionResult.loadStateFile(htmlFile);
|
|
353
|
+
}
|
|
354
|
+
|
|
357
355
|
private static loadScreenshotFromFile(screenshotFile: string): Buffer | undefined {
|
|
358
356
|
try {
|
|
359
357
|
const filePath = outputPath('states', screenshotFile);
|
|
360
|
-
if (fs.existsSync(filePath))
|
|
361
|
-
|
|
362
|
-
}
|
|
363
|
-
return undefined;
|
|
358
|
+
if (!fs.existsSync(filePath)) return undefined;
|
|
359
|
+
return fs.readFileSync(filePath);
|
|
364
360
|
} catch (error) {
|
|
365
361
|
console.error('Failed to load screenshot from file:', error);
|
|
366
362
|
return undefined;
|
|
@@ -368,58 +364,38 @@ export class ActionResult implements ActionResultData {
|
|
|
368
364
|
}
|
|
369
365
|
|
|
370
366
|
private static loadBrowserLogsFromFile(logFile: string): any[] {
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
return { text: line, type: 'log', level: 'log', message: line };
|
|
390
|
-
});
|
|
391
|
-
}
|
|
392
|
-
return [];
|
|
393
|
-
} catch (error) {
|
|
394
|
-
console.error('Failed to load browser logs from file:', error);
|
|
395
|
-
return [];
|
|
396
|
-
}
|
|
367
|
+
const logContent = ActionResult.loadStateFile(logFile);
|
|
368
|
+
if (!logContent) return [];
|
|
369
|
+
return logContent
|
|
370
|
+
.split('\n')
|
|
371
|
+
.filter((line) => line.trim())
|
|
372
|
+
.map((line) => {
|
|
373
|
+
const match = line.match(/\[([^\]]+)\] (\w+): (.+)/);
|
|
374
|
+
if (match) {
|
|
375
|
+
return {
|
|
376
|
+
timestamp: match[1],
|
|
377
|
+
type: match[2].toLowerCase(),
|
|
378
|
+
text: match[3],
|
|
379
|
+
level: match[2].toLowerCase(),
|
|
380
|
+
message: match[3],
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
return { text: line, type: 'log', level: 'log', message: line };
|
|
384
|
+
});
|
|
397
385
|
}
|
|
398
386
|
|
|
399
387
|
private static loadAriaSnapshotFromFile(ariaSnapshotFile: string): string | null {
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
const content = fs.readFileSync(filePath, 'utf8');
|
|
407
|
-
const trimmed = content.trim();
|
|
408
|
-
if (!trimmed) {
|
|
409
|
-
return null;
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
return trimmed.endsWith('\n') ? trimmed : `${trimmed}\n`;
|
|
413
|
-
} catch (error) {
|
|
414
|
-
console.error('Failed to load aria snapshot from file:', error);
|
|
415
|
-
return null;
|
|
416
|
-
}
|
|
388
|
+
const content = ActionResult.loadStateFile(ariaSnapshotFile);
|
|
389
|
+
if (!content) return null;
|
|
390
|
+
const trimmed = content.trim();
|
|
391
|
+
if (!trimmed) return null;
|
|
392
|
+
return trimmed.endsWith('\n') ? trimmed : `${trimmed}\n`;
|
|
417
393
|
}
|
|
418
394
|
|
|
419
395
|
toAiContext(): string {
|
|
420
396
|
const parts: string[] = [];
|
|
421
397
|
|
|
422
|
-
if (this.url
|
|
398
|
+
if (this.url) {
|
|
423
399
|
parts.push(`<url>${this.url}</url>`);
|
|
424
400
|
}
|
|
425
401
|
|
|
@@ -465,7 +441,7 @@ export class ActionResult implements ActionResultData {
|
|
|
465
441
|
}
|
|
466
442
|
|
|
467
443
|
get relativeUrl(): string | null {
|
|
468
|
-
if (!this.url
|
|
444
|
+
if (!this.url) return null;
|
|
469
445
|
|
|
470
446
|
try {
|
|
471
447
|
const urlObj = new URL(this.url);
|
|
@@ -490,22 +466,10 @@ export class ActionResult implements ActionResultData {
|
|
|
490
466
|
|
|
491
467
|
this.extractHeadings(this.html);
|
|
492
468
|
|
|
493
|
-
|
|
469
|
+
if (this.h1) parts.push(`h1_${this.h1}`);
|
|
470
|
+
if (this.h2) parts.push(`h2_${this.h2}`);
|
|
494
471
|
|
|
495
|
-
|
|
496
|
-
const value = this[heading as keyof this] as string;
|
|
497
|
-
if (value) {
|
|
498
|
-
parts.push(`${heading}_${value}`);
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
let stateString = parts
|
|
503
|
-
.map((part) => part.substring(0, 100))
|
|
504
|
-
.join('_')
|
|
505
|
-
.replace(/[^a-zA-Z0-9_]/g, '_')
|
|
506
|
-
.replace(/_+/g, '_')
|
|
507
|
-
.replace(/^_|_$/g, '')
|
|
508
|
-
.toLowerCase();
|
|
472
|
+
let stateString = slugify(parts.map((part) => part.substring(0, 100)).join('_'));
|
|
509
473
|
|
|
510
474
|
if (stateString.length > 200) {
|
|
511
475
|
stateString = stateString.substring(0, 200);
|
|
@@ -517,60 +481,8 @@ export class ActionResult implements ActionResultData {
|
|
|
517
481
|
return stateString;
|
|
518
482
|
}
|
|
519
483
|
|
|
520
|
-
private saveBrowserLogs(): void {
|
|
521
|
-
const logs = this.browserLogsContent;
|
|
522
|
-
if (!logs || logs.length === 0) return;
|
|
523
|
-
|
|
524
|
-
try {
|
|
525
|
-
const outputDir = 'output';
|
|
526
|
-
const stateHash = this.getStateHash();
|
|
527
|
-
const filename = `${stateHash}.log`;
|
|
528
|
-
const filePath = join(outputDir, filename);
|
|
529
|
-
|
|
530
|
-
// Ensure output directory exists
|
|
531
|
-
if (!fs.existsSync(outputDir)) {
|
|
532
|
-
fs.mkdirSync(outputDir, { recursive: true });
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
// Format logs for saving
|
|
536
|
-
const formattedLogs = logs.map((log: any) => {
|
|
537
|
-
const timestamp = new Date().toISOString();
|
|
538
|
-
const level = (log.type || log.level || 'LOG').toUpperCase();
|
|
539
|
-
const message = log.text || log.message || String(log);
|
|
540
|
-
return `[${timestamp}] ${level}: ${message}`;
|
|
541
|
-
});
|
|
542
|
-
|
|
543
|
-
// Save log content to file
|
|
544
|
-
const logContent = `${formattedLogs.join('\n')}\n`;
|
|
545
|
-
fs.writeFileSync(filePath, logContent, 'utf8');
|
|
546
|
-
} catch (error) {
|
|
547
|
-
// Silently fail to avoid breaking the main flow
|
|
548
|
-
console.error('Failed to save browser logs:', error);
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
private saveHtmlOutput(): void {
|
|
553
|
-
try {
|
|
554
|
-
const outputDir = 'output';
|
|
555
|
-
const stateHash = this.getStateHash();
|
|
556
|
-
const filename = `${stateHash}.html`;
|
|
557
|
-
const filePath = join(outputDir, filename);
|
|
558
|
-
|
|
559
|
-
// Ensure output directory exists
|
|
560
|
-
if (!fs.existsSync(outputDir)) {
|
|
561
|
-
fs.mkdirSync(outputDir, { recursive: true });
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
// Save HTML content to file
|
|
565
|
-
fs.writeFileSync(filePath, this.html, 'utf8');
|
|
566
|
-
} catch (error) {
|
|
567
|
-
// Silently fail to avoid breaking the main flow
|
|
568
|
-
console.error('Failed to save HTML output:', error);
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
|
|
572
484
|
async diff(previousState: ActionResult | null): Promise<Diff> {
|
|
573
|
-
return
|
|
485
|
+
return Diff.create(this, previousState);
|
|
574
486
|
}
|
|
575
487
|
|
|
576
488
|
async toToolResult(previousState: ActionResult | null, locator: string): Promise<ToolResultMetadata> {
|
|
@@ -602,7 +514,6 @@ export class ActionResult implements ActionResultData {
|
|
|
602
514
|
}
|
|
603
515
|
|
|
604
516
|
const diff = await this.diff(previousState);
|
|
605
|
-
await diff.calculate();
|
|
606
517
|
|
|
607
518
|
const pageDiff: PageDiff = {
|
|
608
519
|
urlChanged,
|
|
@@ -612,6 +523,7 @@ export class ActionResult implements ActionResultData {
|
|
|
612
523
|
|
|
613
524
|
if (diff.ariaChanged) {
|
|
614
525
|
pageDiff.ariaChanges = diff.ariaChanged;
|
|
526
|
+
pageDiff.ariaChangeCount = diff.ariaChangeCount;
|
|
615
527
|
}
|
|
616
528
|
|
|
617
529
|
if (diff.htmlParts.length > 0) {
|
|
@@ -630,10 +542,7 @@ export class ActionResult implements ActionResultData {
|
|
|
630
542
|
}
|
|
631
543
|
|
|
632
544
|
if (pageDiff.ariaChanges && this.iframeSnapshots.length > 0) {
|
|
633
|
-
|
|
634
|
-
const removedMatch = pageDiff.ariaChanges.match(/removed: (\d+) interactive/);
|
|
635
|
-
const removedCount = removedMatch ? Number.parseInt(removedMatch[1]) : 0;
|
|
636
|
-
if (addedCount + removedCount >= 50) {
|
|
545
|
+
if (diff.ariaChangeCount >= LARGE_ARIA_CHANGE_THRESHOLD) {
|
|
637
546
|
pageDiff.iframes = this.iframeSnapshots.map((snap) => `iframe src="${snap.src}":\n${snap.html}`).join('\n\n');
|
|
638
547
|
}
|
|
639
548
|
}
|
|
@@ -671,20 +580,25 @@ function collapseHtmlParts(parts: HtmlDiffPart[]): HtmlDiffPart[] {
|
|
|
671
580
|
export class Diff {
|
|
672
581
|
private _htmlDiffResult: HtmlDiffResult | null = null;
|
|
673
582
|
private _ariaDiffResult: string | null = null;
|
|
583
|
+
private _ariaChangeCount = 0;
|
|
674
584
|
private _isSameUrl: boolean;
|
|
675
|
-
private _urlChanged: boolean;
|
|
676
585
|
|
|
677
586
|
constructor(
|
|
678
587
|
private current: ActionResult,
|
|
679
588
|
private previous: ActionResult | null
|
|
680
589
|
) {
|
|
681
590
|
this._isSameUrl = previous ? current.isSameUrl({ url: previous.url }) : false;
|
|
682
|
-
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
static async create(current: ActionResult, previous: ActionResult | null): Promise<Diff> {
|
|
594
|
+
const diff = new Diff(current, previous);
|
|
595
|
+
await diff.calculate();
|
|
596
|
+
return diff;
|
|
683
597
|
}
|
|
684
598
|
|
|
685
599
|
hasChanges(): boolean {
|
|
686
600
|
if (!this.previous) return false;
|
|
687
|
-
if (this.
|
|
601
|
+
if (!this._isSameUrl) return true;
|
|
688
602
|
|
|
689
603
|
const hasHtmlChanges = this._htmlDiffResult && (this._htmlDiffResult.parts.length > 0 || this._htmlDiffResult.added.length > 0 || this._htmlDiffResult.removed.length > 0);
|
|
690
604
|
const hasAriaChanges = this._ariaDiffResult !== null;
|
|
@@ -697,7 +611,7 @@ export class Diff {
|
|
|
697
611
|
}
|
|
698
612
|
|
|
699
613
|
urlHasChanged(): boolean {
|
|
700
|
-
return this.
|
|
614
|
+
return !this._isSameUrl;
|
|
701
615
|
}
|
|
702
616
|
|
|
703
617
|
get htmlParts(): HtmlDiffPart[] {
|
|
@@ -709,18 +623,14 @@ export class Diff {
|
|
|
709
623
|
return this._ariaDiffResult;
|
|
710
624
|
}
|
|
711
625
|
|
|
712
|
-
get
|
|
713
|
-
return this.
|
|
626
|
+
get ariaChangeCount(): number {
|
|
627
|
+
return this._ariaChangeCount;
|
|
714
628
|
}
|
|
715
629
|
|
|
716
630
|
get htmlDiff(): HtmlDiffResult | null {
|
|
717
631
|
return this._htmlDiffResult;
|
|
718
632
|
}
|
|
719
633
|
|
|
720
|
-
get ariaDiff(): string | null {
|
|
721
|
-
return this._ariaDiffResult;
|
|
722
|
-
}
|
|
723
|
-
|
|
724
634
|
async calculate(): Promise<void> {
|
|
725
635
|
if (!this.previous) return;
|
|
726
636
|
|
|
@@ -728,6 +638,8 @@ export class Diff {
|
|
|
728
638
|
this._htmlDiffResult = await htmlDiff(this.previous.html, this.current.html, ConfigParser.getInstance().getConfig().html);
|
|
729
639
|
}
|
|
730
640
|
|
|
731
|
-
|
|
641
|
+
const ariaDiff = diffAriaSnapshots(this.previous.ariaSnapshot, this.current.ariaSnapshot);
|
|
642
|
+
this._ariaDiffResult = ariaDiff.text;
|
|
643
|
+
this._ariaChangeCount = ariaDiff.count;
|
|
732
644
|
}
|
|
733
645
|
}
|