explorbot 0.1.32-beta.1 → 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 +20 -3
- package/dist/src/action-result.d.ts +8 -10
- package/dist/src/action-result.js +72 -164
- package/dist/src/action.d.ts +3 -10
- package/dist/src/action.js +18 -111
- package/dist/src/ai/captain/web-mode.js +0 -7
- package/dist/src/ai/captain.d.ts +1 -3
- package/dist/src/ai/captain.js +4 -19
- package/dist/src/ai/conversation.d.ts +2 -5
- package/dist/src/ai/conversation.js +9 -28
- package/dist/src/ai/driller.d.ts +0 -4
- package/dist/src/ai/driller.js +3 -12
- package/dist/src/ai/experience-compactor.d.ts +2 -2
- package/dist/src/ai/experience-compactor.js +16 -33
- package/dist/src/ai/fisherman-tools.js +1 -1
- package/dist/src/ai/fisherman.d.ts +0 -1
- package/dist/src/ai/fisherman.js +0 -3
- package/dist/src/ai/historian/codeceptjs.d.ts +2 -0
- package/dist/src/ai/historian/codeceptjs.js +3 -2
- package/dist/src/ai/historian/playwright.d.ts +2 -0
- package/dist/src/ai/historian/playwright.js +3 -2
- package/dist/src/ai/historian/utils.d.ts +0 -1
- package/dist/src/ai/historian/utils.js +0 -1
- package/dist/src/ai/historian.d.ts +5 -4
- package/dist/src/ai/historian.js +1 -5
- package/dist/src/ai/navigator.d.ts +3 -5
- package/dist/src/ai/navigator.js +11 -46
- package/dist/src/ai/pilot.d.ts +1 -3
- package/dist/src/ai/pilot.js +6 -17
- package/dist/src/ai/planner.d.ts +3 -3
- package/dist/src/ai/planner.js +49 -37
- package/dist/src/ai/provider.d.ts +4 -1
- package/dist/src/ai/provider.js +77 -136
- package/dist/src/ai/quartermaster.d.ts +21 -22
- package/dist/src/ai/quartermaster.js +4 -10
- package/dist/src/ai/rerunner.d.ts +1 -2
- package/dist/src/ai/rerunner.js +14 -22
- package/dist/src/ai/researcher/cache.js +11 -18
- package/dist/src/ai/researcher/deep-analysis.js +3 -6
- package/dist/src/ai/researcher/parser.js +1 -1
- package/dist/src/ai/researcher/research-result.d.ts +0 -1
- package/dist/src/ai/researcher/research-result.js +0 -19
- package/dist/src/ai/researcher/sections.js +5 -1
- package/dist/src/ai/researcher.js +12 -53
- package/dist/src/ai/rules.d.ts +1 -0
- package/dist/src/ai/rules.js +22 -15
- package/dist/src/ai/session-analyst.js +8 -5
- package/dist/src/ai/task-agent.d.ts +1 -1
- package/dist/src/ai/task-agent.js +2 -27
- package/dist/src/ai/tester.d.ts +0 -2
- package/dist/src/ai/tester.js +10 -61
- package/dist/src/ai/tools.d.ts +18 -7
- package/dist/src/ai/tools.js +119 -101
- package/dist/src/commands/compact-command.d.ts +1 -1
- package/dist/src/commands/compact-command.js +6 -6
- package/dist/src/commands/context-command.js +1 -1
- package/dist/src/commands/context-knowledge-command.js +1 -1
- package/dist/src/commands/experience-command.js +1 -1
- package/dist/src/commands/knows-command.js +1 -1
- package/dist/src/commands/learn-command.js +3 -1
- package/dist/src/components/AddKnowledge.d.ts +2 -0
- package/dist/src/components/AddKnowledge.js +3 -7
- package/dist/src/components/App.js +2 -2
- package/dist/src/config.d.ts +2 -0
- package/dist/src/config.js +12 -0
- package/dist/src/experience-tracker.d.ts +11 -6
- package/dist/src/experience-tracker.js +81 -154
- package/dist/src/explorbot.d.ts +4 -5
- package/dist/src/explorbot.js +17 -51
- package/dist/src/explorer.d.ts +4 -15
- package/dist/src/explorer.js +18 -68
- package/dist/src/knowledge-tracker.d.ts +2 -2
- package/dist/src/knowledge-tracker.js +38 -45
- package/dist/src/state-manager.d.ts +6 -31
- package/dist/src/state-manager.js +10 -129
- package/dist/src/utils/aria.d.ts +6 -1
- package/dist/src/utils/aria.js +4 -1
- package/dist/src/utils/cache.d.ts +15 -0
- package/dist/src/utils/cache.js +34 -0
- package/dist/src/utils/context-formatter.d.ts +6 -0
- package/dist/src/utils/context-formatter.js +15 -27
- package/dist/src/utils/html.d.ts +5 -0
- package/dist/src/utils/html.js +71 -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-query.d.ts +6 -0
- package/dist/src/utils/markdown-query.js +14 -1
- package/dist/src/utils/page-readiness.d.ts +1 -0
- package/dist/src/utils/page-readiness.js +1 -1
- package/dist/src/utils/secrets.js +2 -2
- package/dist/src/utils/strings.d.ts +2 -0
- package/dist/src/utils/strings.js +10 -0
- package/dist/src/utils/web-element.d.ts +2 -1
- 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/package.json +20 -3
- package/src/action-result.ts +70 -173
- package/src/action.ts +16 -130
- 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 +85 -145
- 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 +88 -151
- package/src/explorbot.ts +17 -51
- package/src/explorer.ts +20 -72
- package/src/knowledge-tracker.ts +42 -49
- package/src/state-manager.ts +15 -156
- 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 +2 -3
- package/src/utils/strings.ts +12 -0
- package/src/utils/web-element.ts +4 -0
- package/src/utils/web-sandbox.ts +43 -0
- package/dist/boat/api-tester/example/apibot.config.js +0 -30
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
|
-
import { join } from 'node:path';
|
|
3
2
|
import { ConfigParser, outputPath } from "./config.js";
|
|
4
|
-
import { compactAriaSnapshot, diffAriaSnapshots } from "./utils/aria.js";
|
|
3
|
+
import { LARGE_ARIA_CHANGE_THRESHOLD, compactAriaSnapshot, diffAriaSnapshots } from "./utils/aria.js";
|
|
4
|
+
import { TTLCache } from "./utils/cache.js";
|
|
5
5
|
import { htmlDiff } from "./utils/html-diff.js";
|
|
6
6
|
import { extractHeadings, extractLinks, extractTargetedHtml, htmlCombinedSnapshot, htmlMinimalUISnapshot, htmlTextSnapshot, minifyHtml } from "./utils/html.js";
|
|
7
7
|
import { createDebug } from "./utils/logger.js";
|
|
8
|
+
import { slugify } from "./utils/strings.js";
|
|
8
9
|
import { extractStatePath, matchesUrl } from "./utils/url-matcher.js";
|
|
9
10
|
const debugLog = createDebug('explorbot:state');
|
|
10
11
|
export class ActionResult {
|
|
@@ -26,13 +27,12 @@ export class ActionResult {
|
|
|
26
27
|
_screenshot = undefined;
|
|
27
28
|
htmlFile = undefined;
|
|
28
29
|
_html = undefined;
|
|
29
|
-
snapshotCache = new
|
|
30
|
+
snapshotCache = new TTLCache();
|
|
30
31
|
logFile = undefined;
|
|
31
|
-
_browserLogs = undefined;
|
|
32
32
|
ariaSnapshotFile = undefined;
|
|
33
33
|
_ariaSnapshot = undefined;
|
|
34
34
|
_lastExtractedHtml = undefined;
|
|
35
|
-
notes;
|
|
35
|
+
notes = [];
|
|
36
36
|
links = [];
|
|
37
37
|
verifications;
|
|
38
38
|
constructor(data) {
|
|
@@ -61,20 +61,17 @@ export class ActionResult {
|
|
|
61
61
|
if (data.ariaSnapshotFile !== undefined) {
|
|
62
62
|
this.ariaSnapshotFile = data.ariaSnapshotFile;
|
|
63
63
|
}
|
|
64
|
-
// Store HTML
|
|
64
|
+
// Store HTML in a private property if provided
|
|
65
65
|
if (data.html !== undefined) {
|
|
66
66
|
this._html = data.html;
|
|
67
67
|
}
|
|
68
|
-
if (data.browserLogs !== undefined) {
|
|
69
|
-
this._browserLogs = data.browserLogs;
|
|
70
|
-
}
|
|
71
68
|
if (data.screenshot !== undefined) {
|
|
72
69
|
this._screenshot = data.screenshot;
|
|
73
70
|
}
|
|
74
71
|
if (data.ariaSnapshot !== undefined) {
|
|
75
72
|
this._ariaSnapshot = data.ariaSnapshot;
|
|
76
73
|
}
|
|
77
|
-
if (!this.fullUrl && this.url
|
|
74
|
+
if (!this.fullUrl && this.url) {
|
|
78
75
|
this.fullUrl = this.url;
|
|
79
76
|
}
|
|
80
77
|
this.extractHeadings(this.html);
|
|
@@ -84,7 +81,7 @@ export class ActionResult {
|
|
|
84
81
|
else if (this._html) {
|
|
85
82
|
this.links = extractLinks(this._html);
|
|
86
83
|
}
|
|
87
|
-
if (this.url
|
|
84
|
+
if (this.url) {
|
|
88
85
|
this.url = extractStatePath(this.url);
|
|
89
86
|
}
|
|
90
87
|
}
|
|
@@ -112,15 +109,6 @@ export class ActionResult {
|
|
|
112
109
|
}
|
|
113
110
|
return undefined;
|
|
114
111
|
}
|
|
115
|
-
get browserLogsContent() {
|
|
116
|
-
if (this._browserLogs !== undefined) {
|
|
117
|
-
return this._browserLogs;
|
|
118
|
-
}
|
|
119
|
-
if (this.logFile) {
|
|
120
|
-
return ActionResult.loadBrowserLogsFromFile(this.logFile);
|
|
121
|
-
}
|
|
122
|
-
return [];
|
|
123
|
-
}
|
|
124
112
|
get ariaSnapshot() {
|
|
125
113
|
if (this._ariaSnapshot !== undefined) {
|
|
126
114
|
return this._ariaSnapshot;
|
|
@@ -168,13 +156,13 @@ export class ActionResult {
|
|
|
168
156
|
return null;
|
|
169
157
|
}
|
|
170
158
|
isSameUrl(state) {
|
|
171
|
-
if (!this.url
|
|
159
|
+
if (!this.url) {
|
|
172
160
|
return false;
|
|
173
161
|
}
|
|
174
162
|
return extractStatePath(state.url) === extractStatePath(this.url);
|
|
175
163
|
}
|
|
176
164
|
isMatchedBy(state) {
|
|
177
|
-
if (!this.url
|
|
165
|
+
if (!this.url) {
|
|
178
166
|
return false;
|
|
179
167
|
}
|
|
180
168
|
const isRelevant = matchesUrl(extractStatePath(state.url), extractStatePath(this.url));
|
|
@@ -209,12 +197,12 @@ export class ActionResult {
|
|
|
209
197
|
async simplifiedHtml(htmlConfig) {
|
|
210
198
|
const normalizedConfig = this.normalizeHtmlConfig(htmlConfig);
|
|
211
199
|
const cacheKey = `simplified:${JSON.stringify(normalizedConfig?.minimal ?? null)}`;
|
|
212
|
-
return this.
|
|
200
|
+
return this.snapshotCache.fetch(cacheKey, () => minifyHtml(htmlMinimalUISnapshot(this.html ?? '', normalizedConfig?.minimal)));
|
|
213
201
|
}
|
|
214
202
|
async combinedHtml(htmlConfig) {
|
|
215
203
|
const normalizedConfig = this.normalizeHtmlConfig(htmlConfig);
|
|
216
204
|
const cacheKey = `combined:${!!htmlConfig?.keepPositions}:${JSON.stringify(normalizedConfig?.combined ?? null)}`;
|
|
217
|
-
return this.
|
|
205
|
+
return this.snapshotCache.fetch(cacheKey, async () => {
|
|
218
206
|
const combinedHtml = await minifyHtml(htmlCombinedSnapshot(this.html ?? '', normalizedConfig?.combined, { keepPositions: htmlConfig?.keepPositions }));
|
|
219
207
|
debugLog(`----${this.url}----`);
|
|
220
208
|
debugLog(`Combined HTML: \n${combinedHtml}`);
|
|
@@ -225,7 +213,7 @@ export class ActionResult {
|
|
|
225
213
|
async textHtml(htmlConfig) {
|
|
226
214
|
const normalizedConfig = this.normalizeHtmlConfig(htmlConfig);
|
|
227
215
|
const cacheKey = `text:${JSON.stringify(normalizedConfig?.text ?? null)}`;
|
|
228
|
-
return this.
|
|
216
|
+
return this.snapshotCache.fetch(cacheKey, () => minifyHtml(htmlTextSnapshot(this.html ?? '', normalizedConfig?.text)));
|
|
229
217
|
}
|
|
230
218
|
getInteractiveARIA() {
|
|
231
219
|
return compactAriaSnapshot(this.ariaSnapshot, false);
|
|
@@ -240,14 +228,6 @@ export class ActionResult {
|
|
|
240
228
|
const parser = ConfigParser.getInstance();
|
|
241
229
|
return parser.getConfig().html;
|
|
242
230
|
}
|
|
243
|
-
async cachedSnapshot(key, compute) {
|
|
244
|
-
const cached = this.snapshotCache.get(key);
|
|
245
|
-
if (cached !== undefined)
|
|
246
|
-
return cached;
|
|
247
|
-
const value = await compute();
|
|
248
|
-
this.snapshotCache.set(key, value);
|
|
249
|
-
return value;
|
|
250
|
-
}
|
|
251
231
|
static fromState(state) {
|
|
252
232
|
let html = undefined;
|
|
253
233
|
let screenshot = undefined;
|
|
@@ -285,26 +265,27 @@ export class ActionResult {
|
|
|
285
265
|
}
|
|
286
266
|
return new ActionResult(actionResultData);
|
|
287
267
|
}
|
|
288
|
-
static
|
|
268
|
+
static loadStateFile(file) {
|
|
289
269
|
try {
|
|
290
|
-
const filePath = outputPath('states',
|
|
291
|
-
if (fs.existsSync(filePath))
|
|
292
|
-
return
|
|
293
|
-
|
|
294
|
-
return null;
|
|
270
|
+
const filePath = outputPath('states', file);
|
|
271
|
+
if (!fs.existsSync(filePath))
|
|
272
|
+
return null;
|
|
273
|
+
return fs.readFileSync(filePath, 'utf8');
|
|
295
274
|
}
|
|
296
275
|
catch (error) {
|
|
297
|
-
console.error('Failed to load
|
|
276
|
+
console.error('Failed to load state file:', error);
|
|
298
277
|
return null;
|
|
299
278
|
}
|
|
300
279
|
}
|
|
280
|
+
static loadHtmlFromFile(htmlFile) {
|
|
281
|
+
return ActionResult.loadStateFile(htmlFile);
|
|
282
|
+
}
|
|
301
283
|
static loadScreenshotFromFile(screenshotFile) {
|
|
302
284
|
try {
|
|
303
285
|
const filePath = outputPath('states', screenshotFile);
|
|
304
|
-
if (fs.existsSync(filePath))
|
|
305
|
-
return
|
|
306
|
-
|
|
307
|
-
return undefined;
|
|
286
|
+
if (!fs.existsSync(filePath))
|
|
287
|
+
return undefined;
|
|
288
|
+
return fs.readFileSync(filePath);
|
|
308
289
|
}
|
|
309
290
|
catch (error) {
|
|
310
291
|
console.error('Failed to load screenshot from file:', error);
|
|
@@ -312,55 +293,38 @@ export class ActionResult {
|
|
|
312
293
|
}
|
|
313
294
|
}
|
|
314
295
|
static loadBrowserLogsFromFile(logFile) {
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
if (fs.existsSync(filePath)) {
|
|
318
|
-
const logContent = fs.readFileSync(filePath, 'utf8');
|
|
319
|
-
return logContent
|
|
320
|
-
.split('\n')
|
|
321
|
-
.filter((line) => line.trim())
|
|
322
|
-
.map((line) => {
|
|
323
|
-
const match = line.match(/\[([^\]]+)\] (\w+): (.+)/);
|
|
324
|
-
if (match) {
|
|
325
|
-
return {
|
|
326
|
-
timestamp: match[1],
|
|
327
|
-
type: match[2].toLowerCase(),
|
|
328
|
-
text: match[3],
|
|
329
|
-
level: match[2].toLowerCase(),
|
|
330
|
-
message: match[3],
|
|
331
|
-
};
|
|
332
|
-
}
|
|
333
|
-
return { text: line, type: 'log', level: 'log', message: line };
|
|
334
|
-
});
|
|
335
|
-
}
|
|
296
|
+
const logContent = ActionResult.loadStateFile(logFile);
|
|
297
|
+
if (!logContent)
|
|
336
298
|
return [];
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
299
|
+
return logContent
|
|
300
|
+
.split('\n')
|
|
301
|
+
.filter((line) => line.trim())
|
|
302
|
+
.map((line) => {
|
|
303
|
+
const match = line.match(/\[([^\]]+)\] (\w+): (.+)/);
|
|
304
|
+
if (match) {
|
|
305
|
+
return {
|
|
306
|
+
timestamp: match[1],
|
|
307
|
+
type: match[2].toLowerCase(),
|
|
308
|
+
text: match[3],
|
|
309
|
+
level: match[2].toLowerCase(),
|
|
310
|
+
message: match[3],
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
return { text: line, type: 'log', level: 'log', message: line };
|
|
314
|
+
});
|
|
342
315
|
}
|
|
343
316
|
static loadAriaSnapshotFromFile(ariaSnapshotFile) {
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
if (!fs.existsSync(filePath)) {
|
|
347
|
-
return null;
|
|
348
|
-
}
|
|
349
|
-
const content = fs.readFileSync(filePath, 'utf8');
|
|
350
|
-
const trimmed = content.trim();
|
|
351
|
-
if (!trimmed) {
|
|
352
|
-
return null;
|
|
353
|
-
}
|
|
354
|
-
return trimmed.endsWith('\n') ? trimmed : `${trimmed}\n`;
|
|
355
|
-
}
|
|
356
|
-
catch (error) {
|
|
357
|
-
console.error('Failed to load aria snapshot from file:', error);
|
|
317
|
+
const content = ActionResult.loadStateFile(ariaSnapshotFile);
|
|
318
|
+
if (!content)
|
|
358
319
|
return null;
|
|
359
|
-
|
|
320
|
+
const trimmed = content.trim();
|
|
321
|
+
if (!trimmed)
|
|
322
|
+
return null;
|
|
323
|
+
return trimmed.endsWith('\n') ? trimmed : `${trimmed}\n`;
|
|
360
324
|
}
|
|
361
325
|
toAiContext() {
|
|
362
326
|
const parts = [];
|
|
363
|
-
if (this.url
|
|
327
|
+
if (this.url) {
|
|
364
328
|
parts.push(`<url>${this.url}</url>`);
|
|
365
329
|
}
|
|
366
330
|
if (this.title) {
|
|
@@ -395,7 +359,7 @@ export class ActionResult {
|
|
|
395
359
|
return parts.join('\n');
|
|
396
360
|
}
|
|
397
361
|
get relativeUrl() {
|
|
398
|
-
if (!this.url
|
|
362
|
+
if (!this.url)
|
|
399
363
|
return null;
|
|
400
364
|
try {
|
|
401
365
|
const urlObj = new URL(this.url);
|
|
@@ -416,20 +380,11 @@ export class ActionResult {
|
|
|
416
380
|
const parts = [];
|
|
417
381
|
parts.push(this.relativeUrl || this.url || '/');
|
|
418
382
|
this.extractHeadings(this.html);
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
let stateString = parts
|
|
427
|
-
.map((part) => part.substring(0, 100))
|
|
428
|
-
.join('_')
|
|
429
|
-
.replace(/[^a-zA-Z0-9_]/g, '_')
|
|
430
|
-
.replace(/_+/g, '_')
|
|
431
|
-
.replace(/^_|_$/g, '')
|
|
432
|
-
.toLowerCase();
|
|
383
|
+
if (this.h1)
|
|
384
|
+
parts.push(`h1_${this.h1}`);
|
|
385
|
+
if (this.h2)
|
|
386
|
+
parts.push(`h2_${this.h2}`);
|
|
387
|
+
let stateString = slugify(parts.map((part) => part.substring(0, 100)).join('_'));
|
|
433
388
|
if (stateString.length > 200) {
|
|
434
389
|
stateString = stateString.substring(0, 200);
|
|
435
390
|
if (stateString.endsWith('_')) {
|
|
@@ -438,55 +393,8 @@ export class ActionResult {
|
|
|
438
393
|
}
|
|
439
394
|
return stateString;
|
|
440
395
|
}
|
|
441
|
-
saveBrowserLogs() {
|
|
442
|
-
const logs = this.browserLogsContent;
|
|
443
|
-
if (!logs || logs.length === 0)
|
|
444
|
-
return;
|
|
445
|
-
try {
|
|
446
|
-
const outputDir = 'output';
|
|
447
|
-
const stateHash = this.getStateHash();
|
|
448
|
-
const filename = `${stateHash}.log`;
|
|
449
|
-
const filePath = join(outputDir, filename);
|
|
450
|
-
// Ensure output directory exists
|
|
451
|
-
if (!fs.existsSync(outputDir)) {
|
|
452
|
-
fs.mkdirSync(outputDir, { recursive: true });
|
|
453
|
-
}
|
|
454
|
-
// Format logs for saving
|
|
455
|
-
const formattedLogs = logs.map((log) => {
|
|
456
|
-
const timestamp = new Date().toISOString();
|
|
457
|
-
const level = (log.type || log.level || 'LOG').toUpperCase();
|
|
458
|
-
const message = log.text || log.message || String(log);
|
|
459
|
-
return `[${timestamp}] ${level}: ${message}`;
|
|
460
|
-
});
|
|
461
|
-
// Save log content to file
|
|
462
|
-
const logContent = `${formattedLogs.join('\n')}\n`;
|
|
463
|
-
fs.writeFileSync(filePath, logContent, 'utf8');
|
|
464
|
-
}
|
|
465
|
-
catch (error) {
|
|
466
|
-
// Silently fail to avoid breaking the main flow
|
|
467
|
-
console.error('Failed to save browser logs:', error);
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
saveHtmlOutput() {
|
|
471
|
-
try {
|
|
472
|
-
const outputDir = 'output';
|
|
473
|
-
const stateHash = this.getStateHash();
|
|
474
|
-
const filename = `${stateHash}.html`;
|
|
475
|
-
const filePath = join(outputDir, filename);
|
|
476
|
-
// Ensure output directory exists
|
|
477
|
-
if (!fs.existsSync(outputDir)) {
|
|
478
|
-
fs.mkdirSync(outputDir, { recursive: true });
|
|
479
|
-
}
|
|
480
|
-
// Save HTML content to file
|
|
481
|
-
fs.writeFileSync(filePath, this.html, 'utf8');
|
|
482
|
-
}
|
|
483
|
-
catch (error) {
|
|
484
|
-
// Silently fail to avoid breaking the main flow
|
|
485
|
-
console.error('Failed to save HTML output:', error);
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
396
|
async diff(previousState) {
|
|
489
|
-
return
|
|
397
|
+
return Diff.create(this, previousState);
|
|
490
398
|
}
|
|
491
399
|
async toToolResult(previousState, locator) {
|
|
492
400
|
const result = {
|
|
@@ -512,7 +420,6 @@ export class ActionResult {
|
|
|
512
420
|
return result;
|
|
513
421
|
}
|
|
514
422
|
const diff = await this.diff(previousState);
|
|
515
|
-
await diff.calculate();
|
|
516
423
|
const pageDiff = {
|
|
517
424
|
urlChanged,
|
|
518
425
|
previousUrl: previousState.url,
|
|
@@ -520,6 +427,7 @@ export class ActionResult {
|
|
|
520
427
|
};
|
|
521
428
|
if (diff.ariaChanged) {
|
|
522
429
|
pageDiff.ariaChanges = diff.ariaChanged;
|
|
430
|
+
pageDiff.ariaChangeCount = diff.ariaChangeCount;
|
|
523
431
|
}
|
|
524
432
|
if (diff.htmlParts.length > 0) {
|
|
525
433
|
const htmlConfig = this.normalizeHtmlConfig();
|
|
@@ -536,10 +444,7 @@ export class ActionResult {
|
|
|
536
444
|
}
|
|
537
445
|
}
|
|
538
446
|
if (pageDiff.ariaChanges && this.iframeSnapshots.length > 0) {
|
|
539
|
-
|
|
540
|
-
const removedMatch = pageDiff.ariaChanges.match(/removed: (\d+) interactive/);
|
|
541
|
-
const removedCount = removedMatch ? Number.parseInt(removedMatch[1]) : 0;
|
|
542
|
-
if (addedCount + removedCount >= 50) {
|
|
447
|
+
if (diff.ariaChangeCount >= LARGE_ARIA_CHANGE_THRESHOLD) {
|
|
543
448
|
pageDiff.iframes = this.iframeSnapshots.map((snap) => `iframe src="${snap.src}":\n${snap.html}`).join('\n\n');
|
|
544
449
|
}
|
|
545
450
|
}
|
|
@@ -574,18 +479,22 @@ export class Diff {
|
|
|
574
479
|
previous;
|
|
575
480
|
_htmlDiffResult = null;
|
|
576
481
|
_ariaDiffResult = null;
|
|
482
|
+
_ariaChangeCount = 0;
|
|
577
483
|
_isSameUrl;
|
|
578
|
-
_urlChanged;
|
|
579
484
|
constructor(current, previous) {
|
|
580
485
|
this.current = current;
|
|
581
486
|
this.previous = previous;
|
|
582
487
|
this._isSameUrl = previous ? current.isSameUrl({ url: previous.url }) : false;
|
|
583
|
-
|
|
488
|
+
}
|
|
489
|
+
static async create(current, previous) {
|
|
490
|
+
const diff = new Diff(current, previous);
|
|
491
|
+
await diff.calculate();
|
|
492
|
+
return diff;
|
|
584
493
|
}
|
|
585
494
|
hasChanges() {
|
|
586
495
|
if (!this.previous)
|
|
587
496
|
return false;
|
|
588
|
-
if (this.
|
|
497
|
+
if (!this._isSameUrl)
|
|
589
498
|
return true;
|
|
590
499
|
const hasHtmlChanges = this._htmlDiffResult && (this._htmlDiffResult.parts.length > 0 || this._htmlDiffResult.added.length > 0 || this._htmlDiffResult.removed.length > 0);
|
|
591
500
|
const hasAriaChanges = this._ariaDiffResult !== null;
|
|
@@ -595,7 +504,7 @@ export class Diff {
|
|
|
595
504
|
return this._isSameUrl;
|
|
596
505
|
}
|
|
597
506
|
urlHasChanged() {
|
|
598
|
-
return this.
|
|
507
|
+
return !this._isSameUrl;
|
|
599
508
|
}
|
|
600
509
|
get htmlParts() {
|
|
601
510
|
if (!this._htmlDiffResult)
|
|
@@ -605,21 +514,20 @@ export class Diff {
|
|
|
605
514
|
get ariaChanged() {
|
|
606
515
|
return this._ariaDiffResult;
|
|
607
516
|
}
|
|
608
|
-
get
|
|
609
|
-
return this.
|
|
517
|
+
get ariaChangeCount() {
|
|
518
|
+
return this._ariaChangeCount;
|
|
610
519
|
}
|
|
611
520
|
get htmlDiff() {
|
|
612
521
|
return this._htmlDiffResult;
|
|
613
522
|
}
|
|
614
|
-
get ariaDiff() {
|
|
615
|
-
return this._ariaDiffResult;
|
|
616
|
-
}
|
|
617
523
|
async calculate() {
|
|
618
524
|
if (!this.previous)
|
|
619
525
|
return;
|
|
620
526
|
if (this._isSameUrl) {
|
|
621
527
|
this._htmlDiffResult = await htmlDiff(this.previous.html, this.current.html, ConfigParser.getInstance().getConfig().html);
|
|
622
528
|
}
|
|
623
|
-
|
|
529
|
+
const ariaDiff = diffAriaSnapshots(this.previous.ariaSnapshot, this.current.ariaSnapshot);
|
|
530
|
+
this._ariaDiffResult = ariaDiff.text;
|
|
531
|
+
this._ariaChangeCount = ariaDiff.count;
|
|
624
532
|
}
|
|
625
533
|
}
|
package/dist/src/action.d.ts
CHANGED
|
@@ -8,7 +8,6 @@ declare class Action {
|
|
|
8
8
|
actionResult: ActionResult | null;
|
|
9
9
|
config: ExplorbotConfig;
|
|
10
10
|
action: string | null;
|
|
11
|
-
expectation: string | null;
|
|
12
11
|
lastError: Error | null;
|
|
13
12
|
playwrightHelper: any;
|
|
14
13
|
playwrightGroupId: string | null;
|
|
@@ -19,10 +18,10 @@ declare class Action {
|
|
|
19
18
|
recorder?: PlaywrightRecorder;
|
|
20
19
|
mainDocumentStatus: number | undefined;
|
|
21
20
|
constructor(actor: CodeceptJS.I, stateManager: StateManager, recorder?: PlaywrightRecorder);
|
|
22
|
-
caputrePageWithScreenshot(): Promise<ActionResult>;
|
|
23
21
|
saveScreenshot(): Promise<string | undefined>;
|
|
24
|
-
capturePageState({ includeScreenshot }?: {
|
|
22
|
+
capturePageState({ includeScreenshot, codeBlock }?: {
|
|
25
23
|
includeScreenshot?: boolean;
|
|
24
|
+
codeBlock?: string;
|
|
26
25
|
}): Promise<ActionResult>;
|
|
27
26
|
captureMainDocumentStatus(): Promise<number | undefined>;
|
|
28
27
|
captureMainDocumentResponse(): () => void;
|
|
@@ -36,15 +35,9 @@ declare class Action {
|
|
|
36
35
|
}>>;
|
|
37
36
|
captureBrowserLogs(): Promise<any[]>;
|
|
38
37
|
execute(code: string): Promise<Action>;
|
|
39
|
-
|
|
40
|
-
waitForInteraction(): Promise<Action>;
|
|
41
|
-
attempt(codeBlock: string, originalMessage?: string, experience?: boolean): Promise<boolean>;
|
|
38
|
+
attempt(codeBlock: string, originalMessage?: string): Promise<boolean>;
|
|
42
39
|
getActor(): CodeceptJS.I;
|
|
43
|
-
setActor(actor: CodeceptJS.I): void;
|
|
44
|
-
getCurrentState(): ActionResult | null;
|
|
45
40
|
getActionResult(): ActionResult | null;
|
|
46
41
|
waitForPageReadiness(page: any): Promise<void>;
|
|
47
42
|
}
|
|
48
43
|
export default Action;
|
|
49
|
-
export declare function sanitizeCodeBlock(code: string): string;
|
|
50
|
-
export declare function createSandboxedFunction(argNames: string[], body: string): (...args: any[]) => any;
|