explorbot 0.1.8 → 0.1.10
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/bin/explorbot-cli.ts +70 -8
- package/boat/api-tester/src/ai/curler-tools.ts +3 -3
- package/boat/api-tester/src/ai/curler.ts +1 -1
- package/boat/api-tester/src/apibot.ts +2 -2
- package/boat/api-tester/src/config.ts +1 -1
- package/dist/bin/explorbot-cli.js +70 -7
- package/dist/boat/api-tester/src/ai/curler-tools.js +2 -2
- package/dist/boat/api-tester/src/apibot.js +2 -2
- package/dist/package.json +1 -1
- package/dist/src/ai/bosun.js +5 -1
- package/dist/src/ai/experience-compactor.js +235 -50
- package/dist/src/ai/historian.js +13 -6
- package/dist/src/ai/navigator.js +62 -62
- package/dist/src/ai/pilot.js +22 -0
- package/dist/src/ai/planner/subpages.js +1 -30
- package/dist/src/ai/planner.js +4 -4
- package/dist/src/ai/provider.js +1 -1
- package/dist/src/ai/rerunner.js +3 -3
- package/dist/src/ai/researcher/deep-analysis.js +1 -1
- package/dist/src/ai/researcher/fingerprint-worker.js +1 -1
- package/dist/src/ai/researcher/locators.js +1 -1
- package/dist/src/ai/researcher/sections.js +8 -1
- package/dist/src/ai/researcher.js +4 -11
- package/dist/src/ai/tools.js +5 -3
- package/dist/src/api/request-store.js +20 -0
- package/dist/src/api/xhr-capture.js +19 -3
- package/dist/src/command-handler.js +1 -1
- package/dist/src/commands/add-rule-command.js +1 -1
- package/dist/src/commands/base-command.js +20 -0
- package/dist/src/commands/clean-command.js +1 -1
- package/dist/src/commands/compact-command.js +138 -0
- package/dist/src/commands/context-command.js +7 -1
- package/dist/src/commands/drill-command.js +4 -1
- package/dist/src/commands/experience-command.js +104 -0
- package/dist/src/commands/explore-command.js +33 -7
- package/dist/src/commands/freesail-command.js +2 -0
- package/dist/src/commands/index.js +7 -3
- package/dist/src/commands/init-command.js +2 -2
- package/dist/src/commands/learn-command.js +1 -1
- package/dist/src/commands/navigate-command.js +4 -1
- package/dist/src/commands/plan-clear-command.js +4 -1
- package/dist/src/commands/plan-command.js +11 -4
- package/dist/src/commands/plan-edit-command.js +1 -1
- package/dist/src/commands/plan-load-command.js +4 -1
- package/dist/src/commands/plan-reload-command.js +4 -1
- package/dist/src/commands/plan-save-command.js +1 -1
- package/dist/src/commands/research-command.js +5 -2
- package/dist/src/commands/start-command.js +5 -1
- package/dist/src/commands/test-command.js +7 -1
- package/dist/src/experience-tracker.js +191 -56
- package/dist/src/explorbot.js +26 -14
- package/dist/src/explorer.js +3 -3
- package/dist/src/reporter.js +17 -2
- package/dist/src/stats.js +2 -0
- package/dist/src/suite.js +1 -1
- package/dist/src/utils/error-page.js +10 -0
- package/dist/src/utils/logger.js +1 -1
- package/dist/src/utils/rules-loader.js +1 -1
- package/dist/src/utils/test-files.js +1 -1
- package/dist/src/utils/url-matcher.js +50 -0
- package/package.json +1 -1
- package/src/ai/bosun.ts +5 -1
- package/src/ai/experience-compactor.ts +270 -63
- package/src/ai/historian.ts +12 -7
- package/src/ai/navigator.ts +68 -66
- package/src/ai/pilot.ts +22 -0
- package/src/ai/planner/subpages.ts +1 -24
- package/src/ai/planner.ts +5 -5
- package/src/ai/provider.ts +1 -1
- package/src/ai/rerunner.ts +3 -3
- package/src/ai/researcher/deep-analysis.ts +1 -1
- package/src/ai/researcher/fingerprint-worker.ts +1 -1
- package/src/ai/researcher/locators.ts +2 -2
- package/src/ai/researcher/sections.ts +7 -1
- package/src/ai/researcher.ts +4 -11
- package/src/ai/task-agent.ts +1 -1
- package/src/ai/tools.ts +6 -4
- package/src/api/request-store.ts +22 -0
- package/src/api/xhr-capture.ts +21 -3
- package/src/command-handler.ts +1 -1
- package/src/commands/add-rule-command.ts +2 -2
- package/src/commands/base-command.ts +26 -1
- package/src/commands/clean-command.ts +2 -2
- package/src/commands/compact-command.ts +156 -0
- package/src/commands/context-command.ts +8 -2
- package/src/commands/drill-command.ts +5 -2
- package/src/commands/experience-command.ts +125 -0
- package/src/commands/explore-command.ts +35 -9
- package/src/commands/freesail-command.ts +2 -0
- package/src/commands/index.ts +7 -3
- package/src/commands/init-command.ts +2 -2
- package/src/commands/learn-command.ts +2 -2
- package/src/commands/navigate-command.ts +5 -2
- package/src/commands/plan-clear-command.ts +5 -2
- package/src/commands/plan-command.ts +12 -5
- package/src/commands/plan-edit-command.ts +2 -2
- package/src/commands/plan-load-command.ts +5 -2
- package/src/commands/plan-reload-command.ts +5 -2
- package/src/commands/plan-save-command.ts +2 -2
- package/src/commands/research-command.ts +6 -3
- package/src/commands/start-command.ts +6 -2
- package/src/commands/test-command.ts +8 -2
- package/src/experience-tracker.ts +220 -71
- package/src/explorbot.ts +28 -15
- package/src/explorer.ts +3 -3
- package/src/reporter.ts +17 -3
- package/src/stats.ts +4 -0
- package/src/suite.ts +1 -1
- package/src/utils/error-page.ts +10 -0
- package/src/utils/logger.ts +1 -1
- package/src/utils/rules-loader.ts +1 -1
- package/src/utils/test-files.ts +1 -1
- package/src/utils/url-matcher.ts +43 -0
package/src/reporter.ts
CHANGED
|
@@ -3,6 +3,8 @@ import { Client } from '@testomatio/reporter';
|
|
|
3
3
|
import type { Step } from '@testomatio/reporter/types/types.js';
|
|
4
4
|
import { ConfigParser, outputPath } from './config.js';
|
|
5
5
|
import type { ReporterConfig } from './config.js';
|
|
6
|
+
import type { StateManager } from './state-manager.js';
|
|
7
|
+
import { Stats } from './stats.js';
|
|
6
8
|
import { Test } from './test-plan.js';
|
|
7
9
|
import { createDebug } from './utils/logger.js';
|
|
8
10
|
|
|
@@ -18,22 +20,33 @@ export interface ReporterStep {
|
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
export class Reporter {
|
|
21
|
-
private client
|
|
23
|
+
private client!: Client;
|
|
22
24
|
private isRunStarted = false;
|
|
23
25
|
private reporterEnabled: boolean;
|
|
26
|
+
private stateManager?: StateManager;
|
|
24
27
|
|
|
25
|
-
constructor(config?: ReporterConfig) {
|
|
28
|
+
constructor(config?: ReporterConfig, stateManager?: StateManager) {
|
|
26
29
|
this.reporterEnabled = Reporter.resolveEnabled(config);
|
|
30
|
+
this.stateManager = stateManager;
|
|
27
31
|
|
|
28
32
|
if (this.reporterEnabled && (!process.env.TESTOMATIO || config?.html)) {
|
|
29
33
|
this.configureHtmlPipe();
|
|
30
34
|
}
|
|
31
35
|
|
|
32
|
-
this.client = new Client({ apiKey: process.env.TESTOMATIO || '' });
|
|
33
36
|
const pipe = process.env.TESTOMATIO && config?.html ? 'both' : process.env.TESTOMATIO ? 'testomatio' : 'html';
|
|
34
37
|
debugLog('Reporter initialized', { enabled: this.reporterEnabled, pipe });
|
|
35
38
|
}
|
|
36
39
|
|
|
40
|
+
private buildTitle(): string {
|
|
41
|
+
if (process.env.TESTOMATIO_TITLE) return process.env.TESTOMATIO_TITLE;
|
|
42
|
+
const url = this.stateManager?.getCurrentState()?.url;
|
|
43
|
+
const parts = ['Explorbot session'];
|
|
44
|
+
if (url) parts.push(url);
|
|
45
|
+
if (Stats.focus) parts.push(`focus: "${Stats.focus}"`);
|
|
46
|
+
parts.push(`at ${new Date().toISOString().slice(0, 16)}`);
|
|
47
|
+
return parts.join(' ');
|
|
48
|
+
}
|
|
49
|
+
|
|
37
50
|
static resolveEnabled(config?: ReporterConfig): boolean {
|
|
38
51
|
if (config?.enabled === true) return true;
|
|
39
52
|
if (config?.enabled === false) return false;
|
|
@@ -56,6 +69,7 @@ export class Reporter {
|
|
|
56
69
|
}
|
|
57
70
|
|
|
58
71
|
try {
|
|
72
|
+
this.client = new Client({ apiKey: process.env.TESTOMATIO || '', title: this.buildTitle() });
|
|
59
73
|
const timeoutMs = Number(process.env.TESTOMATIO_TIMEOUT_MS || '15000');
|
|
60
74
|
const timeoutPromise = new Promise<'timeout'>((resolve) => setTimeout(() => resolve('timeout'), timeoutMs));
|
|
61
75
|
|
package/src/stats.ts
CHANGED
|
@@ -4,11 +4,15 @@ interface TokenUsage {
|
|
|
4
4
|
total: number;
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
+
export type ExplorbotMode = 'explore' | 'test' | 'freesail' | 'tui';
|
|
8
|
+
|
|
7
9
|
export class Stats {
|
|
8
10
|
static startTime = Date.now();
|
|
9
11
|
static researches = 0;
|
|
10
12
|
static tests = 0;
|
|
11
13
|
static plans = 0;
|
|
14
|
+
static mode?: ExplorbotMode;
|
|
15
|
+
static focus?: string;
|
|
12
16
|
static models: Record<string, TokenUsage> = {};
|
|
13
17
|
|
|
14
18
|
static recordTokens(_agent: string, model: string, usage: TokenUsage): void {
|
package/src/suite.ts
CHANGED
|
@@ -3,8 +3,8 @@ import path from 'node:path';
|
|
|
3
3
|
import { Reflection } from '@codeceptjs/reflection';
|
|
4
4
|
import { ConfigParser } from './config.ts';
|
|
5
5
|
import { normalizeUrl } from './state-manager.ts';
|
|
6
|
-
import { parsePlanFromMarkdown } from './utils/test-plan-markdown.ts';
|
|
7
6
|
import { createDebug } from './utils/logger.ts';
|
|
7
|
+
import { parsePlanFromMarkdown } from './utils/test-plan-markdown.ts';
|
|
8
8
|
|
|
9
9
|
const debugLog = createDebug('explorbot:suite');
|
|
10
10
|
|
package/src/utils/error-page.ts
CHANGED
|
@@ -21,3 +21,13 @@ export function isErrorPage(actionResult: ActionResult): boolean {
|
|
|
21
21
|
|
|
22
22
|
return false;
|
|
23
23
|
}
|
|
24
|
+
|
|
25
|
+
export class ErrorPageError extends Error {
|
|
26
|
+
constructor(
|
|
27
|
+
public readonly url: string,
|
|
28
|
+
public readonly title?: string
|
|
29
|
+
) {
|
|
30
|
+
super(`Error page detected at ${url}${title ? ` (${title})` : ''}`);
|
|
31
|
+
this.name = 'ErrorPageError';
|
|
32
|
+
}
|
|
33
|
+
}
|
package/src/utils/logger.ts
CHANGED
|
@@ -5,9 +5,9 @@ import chalk from 'chalk';
|
|
|
5
5
|
import debug from 'debug';
|
|
6
6
|
import dedent from 'dedent';
|
|
7
7
|
import { marked } from 'marked';
|
|
8
|
+
import stripAnsi from 'strip-ansi';
|
|
8
9
|
import { ConfigParser } from '../config.js';
|
|
9
10
|
import { Observability } from '../observability.ts';
|
|
10
|
-
import stripAnsi from 'strip-ansi';
|
|
11
11
|
import { parseMarkdownToTerminal } from './markdown-terminal.ts';
|
|
12
12
|
|
|
13
13
|
export type LogType = 'info' | 'success' | 'error' | 'warning' | 'debug' | 'substep' | 'step' | 'multiline' | 'html' | 'input';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, mkdirSync,
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { basename, dirname, join, resolve } from 'node:path';
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
4
|
import { tag } from './logger.ts';
|
package/src/utils/test-files.ts
CHANGED
|
@@ -3,9 +3,9 @@ import path from 'node:path';
|
|
|
3
3
|
import chalk from 'chalk';
|
|
4
4
|
import { highlight } from 'cli-highlight';
|
|
5
5
|
import * as codeceptjs from 'codeceptjs';
|
|
6
|
-
import store from 'codeceptjs/lib/store';
|
|
7
6
|
import stepsListener from 'codeceptjs/lib/listener/steps';
|
|
8
7
|
import storeListener from 'codeceptjs/lib/listener/store';
|
|
8
|
+
import store from 'codeceptjs/lib/store';
|
|
9
9
|
import figureSet from 'figures';
|
|
10
10
|
import { ConfigParser } from '../config.ts';
|
|
11
11
|
|
package/src/utils/url-matcher.ts
CHANGED
|
@@ -1,4 +1,47 @@
|
|
|
1
1
|
import micromatch from 'micromatch';
|
|
2
|
+
import { ConfigParser } from '../config.js';
|
|
3
|
+
|
|
4
|
+
export function isDynamicSegment(segment: string): boolean {
|
|
5
|
+
try {
|
|
6
|
+
const configRegex = ConfigParser.getInstance().getConfig().dynamicPageRegex;
|
|
7
|
+
if (configRegex) return new RegExp(configRegex, 'i').test(segment);
|
|
8
|
+
} catch {
|
|
9
|
+
/* config not loaded yet */
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// numeric: /users/123
|
|
13
|
+
if (/^\d+$/.test(segment)) return true;
|
|
14
|
+
// UUID: /items/550e8400-e29b-41d4-a716-446655440000
|
|
15
|
+
if (/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i.test(segment)) return true;
|
|
16
|
+
// ULID: /items/01ARZ3NDEKTSV4RRFFQ69G5FAV
|
|
17
|
+
if (/^[0-9A-HJKMNP-TV-Z]{26}$/.test(segment)) return true;
|
|
18
|
+
// hex ID (4+ chars): /suite/70dae98a
|
|
19
|
+
if (/^[a-f0-9]{4,}$/i.test(segment)) return true;
|
|
20
|
+
// hex-prefixed slug (8+ hex before dash): /suite/95ef0c94-mobile
|
|
21
|
+
if (/^[a-f0-9]{8,}-/i.test(segment)) return true;
|
|
22
|
+
// short mixed alphanumeric (digits + letters, ≤8 chars, no dash): /item/x7f2
|
|
23
|
+
if (segment.length <= 8 && !segment.includes('-') && /\d/.test(segment) && /[a-z]/i.test(segment)) return true;
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function hasDynamicUrlSegment(url: string): boolean {
|
|
28
|
+
return url.split('/').some((seg) => seg.length > 0 && isDynamicSegment(seg));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function generalizeSegment(segment: string): string {
|
|
32
|
+
if (/^\d+$/.test(segment)) return '\\d+';
|
|
33
|
+
if (/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i.test(segment)) return '[a-f0-9-]+';
|
|
34
|
+
if (/^[0-9A-HJKMNP-TV-Z]{26}$/.test(segment)) return '[0-9A-HJKMNP-TV-Z]+';
|
|
35
|
+
if (/^[a-f0-9]+$/i.test(segment)) return '[a-f0-9]+';
|
|
36
|
+
return '[^/]+';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function generalizeUrl(url: string): string {
|
|
40
|
+
return url
|
|
41
|
+
.split('/')
|
|
42
|
+
.map((seg) => (seg.length > 0 && isDynamicSegment(seg) ? generalizeSegment(seg) : seg))
|
|
43
|
+
.join('/');
|
|
44
|
+
}
|
|
2
45
|
|
|
3
46
|
export function matchesUrl(pattern: string, path: string): boolean {
|
|
4
47
|
if (pattern === '*') return true;
|