explorbot 0.2.3 → 0.2.4
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 +1 -1
- package/bin/explorbot-cli.ts +7 -1
- package/boat/api-tester/src/config.ts +4 -2
- package/boat/doc-collector/bin/doc-collector-cli.ts +2 -0
- package/boat/doc-collector/src/ai/documentarian.ts +61 -31
- package/boat/doc-collector/src/config.ts +4 -2
- package/boat/prima/bin/prima-cli.ts +0 -0
- package/boat/prima/src/activity-line.ts +33 -0
- package/boat/prima/src/cli.ts +115 -86
- package/boat/prima/src/envelope.ts +80 -52
- package/boat/prima/src/prima.ts +547 -128
- package/boat/prima/src/pw-parser.ts +11 -1
- package/boat/prima/src/pw-registry.ts +4 -5
- package/boat/prima/src/session-log.ts +126 -0
- package/dist/bin/explorbot-cli.js +7 -1
- package/dist/boat/api-tester/bin/apibot-cli.js +2 -0
- package/dist/boat/api-tester/src/config.js +4 -2
- package/dist/boat/doc-collector/bin/doc-collector-cli.js +2 -0
- package/dist/boat/doc-collector/src/ai/documentarian.js +44 -19
- package/dist/boat/doc-collector/src/config.js +4 -2
- package/dist/boat/prima/src/activity-line.js +30 -0
- package/dist/boat/prima/src/cli.js +97 -77
- package/dist/boat/prima/src/envelope.js +70 -44
- package/dist/boat/prima/src/prima.js +515 -119
- package/dist/boat/prima/src/pw-parser.js +13 -1
- package/dist/boat/prima/src/pw-registry.js +4 -5
- package/dist/boat/prima/src/session-log.js +108 -0
- package/dist/package.json +3 -2
- package/dist/rules/navigator/verification-actions.md +20 -0
- package/dist/src/action-result.d.ts +7 -0
- package/dist/src/action-result.js +4 -0
- package/dist/src/action.d.ts +2 -0
- package/dist/src/action.js +36 -1
- package/dist/src/ai/captain/web-mode.js +6 -3
- package/dist/src/ai/captain.js +2 -0
- package/dist/src/ai/navigator.d.ts +7 -0
- package/dist/src/ai/navigator.js +10 -6
- package/dist/src/ai/pilot.d.ts +4 -0
- package/dist/src/ai/pilot.js +48 -2
- package/dist/src/ai/provider.js +2 -2
- package/dist/src/ai/rerunner.js +2 -1
- package/dist/src/ai/researcher.js +1 -1
- package/dist/src/ai/rules.js +17 -10
- package/dist/src/ai/task-agent.js +4 -1
- package/dist/src/ai/tester.d.ts +1 -1
- package/dist/src/ai/tester.js +33 -33
- package/dist/src/ai/tools.d.ts +14 -0
- package/dist/src/ai/tools.js +113 -36
- package/dist/src/config.d.ts +1 -0
- package/dist/src/config.js +13 -4
- package/dist/src/execution-controller.d.ts +2 -0
- package/dist/src/execution-controller.js +6 -0
- package/dist/src/explorbot.d.ts +2 -1
- package/dist/src/explorbot.js +3 -1
- package/dist/src/explorer.js +2 -3
- package/dist/src/playwright-recorder.js +30 -0
- package/dist/src/remote.d.ts +54 -0
- package/dist/src/remote.js +229 -0
- package/dist/src/reporter.d.ts +1 -0
- package/dist/src/reporter.js +7 -1
- package/dist/src/state-manager.d.ts +2 -1
- package/dist/src/stats.d.ts +1 -0
- package/dist/src/stats.js +1 -0
- package/dist/src/test-plan.d.ts +2 -0
- package/dist/src/test-plan.js +7 -0
- package/dist/src/utils/aria.d.ts +2 -8
- package/dist/src/utils/aria.js +69 -40
- package/dist/src/utils/html.js +1 -0
- package/dist/src/utils/logger.d.ts +6 -0
- package/dist/src/utils/logger.js +24 -0
- package/dist/src/utils/page-readiness.js +18 -1
- package/dist/src/utils/url-matcher.js +3 -0
- package/dist/src/utils/web-element.d.ts +2 -0
- package/dist/src/utils/web-element.js +8 -0
- package/dist/src/utils/web-sandbox.d.ts +1 -1
- package/dist/src/utils/web-sandbox.js +2 -3
- package/docs/api-testing/basics.md +90 -0
- package/docs/api-testing/planning.md +57 -0
- package/docs/api-testing/running-tests.md +55 -0
- package/docs/assets/cloud-report.png +0 -0
- package/docs/assets/html-report.png +0 -0
- package/docs/assets/langfuse-trace.png +0 -0
- package/docs/assets/successful-explore-run.png +0 -0
- package/docs/basics/getting-started.md +140 -0
- package/docs/basics/prerequisites.md +63 -0
- package/docs/basics/providers.md +362 -0
- package/docs/basics/running.md +78 -0
- package/docs/contributing/ai-integration-tests.md +57 -0
- package/docs/contributing/contributing.md +90 -0
- package/docs/contributing/demo-videos.md +36 -0
- package/docs/contributing/npm-package.md +138 -0
- package/docs/contributing/observability.md +227 -0
- package/docs/contributing/regression-tests.md +103 -0
- package/docs/contributing/testing.md +95 -0
- package/docs/doc-collection/basics.md +128 -0
- package/docs/doc-collection/crawling.md +67 -0
- package/docs/doc-collection/interactive-mode.md +99 -0
- package/docs/index.json +86 -0
- package/docs/reference/commands.md +994 -0
- package/docs/reference/configuration.md +569 -0
- package/docs/reference/scripting.md +303 -0
- package/docs/superpowers/plans/2026-08-01-actor-boat.md +925 -0
- package/docs/superpowers/plans/2026-08-01-prima-boat.md +1120 -0
- package/docs/superpowers/reviews/2026-08-06-prima-vs-playwright-cli.md +268 -0
- package/docs/superpowers/specs/2026-08-01-actor-boat-design.md +204 -0
- package/docs/superpowers/specs/2026-08-01-prima-boat-design.md +242 -0
- package/docs/superpowers/specs/2026-08-03-global-config-design.md +138 -0
- package/docs/superpowers/specs/2026-08-07-prima-fixes-design.md +394 -0
- package/docs/web-testing/agents.md +158 -0
- package/docs/web-testing/automated-tests.md +134 -0
- package/docs/web-testing/basics.md +91 -0
- package/docs/web-testing/customization.md +131 -0
- package/docs/web-testing/hooks.md +238 -0
- package/docs/web-testing/page-interaction.md +84 -0
- package/docs/web-testing/planner.md +122 -0
- package/docs/web-testing/rerun.md +164 -0
- package/docs/web-testing/researcher.md +380 -0
- package/docs/workflow/agentic-usage.md +233 -0
- package/docs/workflow/application-spec.md +73 -0
- package/docs/workflow/ci.md +202 -0
- package/docs/workflow/knowledge.md +310 -0
- package/docs/workflow/planning-styles.md +67 -0
- package/docs/workflow/reporting.md +133 -0
- package/docs/workflow/test-plans.md +90 -0
- package/package.json +3 -2
- package/rules/navigator/verification-actions.md +20 -0
- package/src/action-result.ts +11 -0
- package/src/action.ts +38 -2
- package/src/ai/captain/web-mode.ts +6 -3
- package/src/ai/captain.ts +3 -0
- package/src/ai/navigator.ts +14 -8
- package/src/ai/pilot.ts +53 -2
- package/src/ai/provider.ts +2 -4
- package/src/ai/rerunner.ts +2 -1
- package/src/ai/researcher.ts +1 -1
- package/src/ai/rules.ts +17 -10
- package/src/ai/task-agent.ts +3 -1
- package/src/ai/tester.ts +32 -33
- package/src/ai/tools.ts +132 -36
- package/src/config.ts +15 -4
- package/src/execution-controller.ts +8 -0
- package/src/explorbot.ts +4 -2
- package/src/explorer.ts +2 -2
- package/src/playwright-recorder.ts +23 -0
- package/src/remote.ts +238 -0
- package/src/reporter.ts +7 -1
- package/src/state-manager.ts +2 -1
- package/src/stats.ts +1 -0
- package/src/test-plan.ts +9 -0
- package/src/utils/aria.ts +65 -45
- package/src/utils/html.ts +1 -0
- package/src/utils/logger.ts +24 -1
- package/src/utils/page-readiness.ts +24 -1
- package/src/utils/url-matcher.ts +3 -0
- package/src/utils/web-element.ts +9 -0
- package/src/utils/web-sandbox.ts +3 -4
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
|
|
4
|
+
const EXPECTATION_LABELS = {
|
|
5
|
+
passed: 'PASSED ',
|
|
6
|
+
failed: 'FAILED ',
|
|
7
|
+
unverified: 'not verified',
|
|
8
|
+
};
|
|
9
|
+
|
|
4
10
|
export interface InstanceInfo {
|
|
5
11
|
name: string;
|
|
6
12
|
tabs: number;
|
|
@@ -9,59 +15,52 @@ export interface InstanceInfo {
|
|
|
9
15
|
others: Array<{ name: string; tabs: number }>;
|
|
10
16
|
}
|
|
11
17
|
|
|
12
|
-
export interface HealAttempt {
|
|
13
|
-
code: string;
|
|
14
|
-
outcome: string;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
18
|
export interface EnvelopeData {
|
|
18
19
|
ok: boolean;
|
|
19
20
|
command: string;
|
|
20
|
-
healed?: boolean;
|
|
21
|
-
healNote?: string;
|
|
22
21
|
used?: string[];
|
|
23
22
|
page: { url: string; previousUrl?: string; title: string; state: string; visits: number };
|
|
24
23
|
changes?: string | null;
|
|
24
|
+
steps?: Array<{ label: string; ok: boolean; proof: string }>;
|
|
25
|
+
expectations?: Array<{ text: string; status: 'passed' | 'failed' | 'unverified' }>;
|
|
26
|
+
stepFiles?: string;
|
|
27
|
+
value?: string;
|
|
25
28
|
answer?: string;
|
|
26
29
|
research?: string;
|
|
27
|
-
|
|
28
|
-
failure?: { error: string;
|
|
30
|
+
assertions?: Array<{ code: string; passed: boolean; proof: string[] }>;
|
|
31
|
+
failure?: { error: string; compactAria?: string };
|
|
29
32
|
instance: InstanceInfo;
|
|
30
|
-
|
|
33
|
+
status?: string;
|
|
34
|
+
artifacts?: { aria: string; html: string; network?: string };
|
|
31
35
|
}
|
|
32
36
|
|
|
33
37
|
export function renderEnvelope(data: EnvelopeData): string {
|
|
34
|
-
const sections = [renderResult(data), renderPage(data), renderOutcome(data), ...renderFailure(data), renderInstance(data
|
|
38
|
+
const sections = [renderResult(data), renderPage(data), renderValue(data), renderChanges(data), renderSteps(data), renderExpectations(data), renderOutcome(data), ...renderFailure(data), renderInstance(data), renderArtifacts(data)];
|
|
35
39
|
return sections.filter((section) => section).join('\n\n');
|
|
36
40
|
}
|
|
37
41
|
|
|
38
|
-
export function writeArtifacts(dir: string, snapshot: { aria: string | null; html: string | null; requests: unknown[] }): { aria: string; html: string; network
|
|
42
|
+
export function writeArtifacts(dir: string, snapshot: { aria: string | null; html: string | null; requests: unknown[] }): { aria: string; html: string; network?: string } {
|
|
39
43
|
mkdirSync(dir, { recursive: true });
|
|
40
|
-
const paths = {
|
|
44
|
+
const paths: { aria: string; html: string; network?: string } = {
|
|
41
45
|
aria: path.resolve(dir, 'aria.yml'),
|
|
42
46
|
html: path.resolve(dir, 'page.html'),
|
|
43
|
-
network: path.resolve(dir, 'network.jsonl'),
|
|
44
47
|
};
|
|
45
48
|
writeFileSync(paths.aria, snapshot.aria ?? '', 'utf-8');
|
|
46
49
|
writeFileSync(paths.html, snapshot.html ?? '', 'utf-8');
|
|
50
|
+
|
|
51
|
+
if (!snapshot.requests.length) return paths;
|
|
52
|
+
|
|
53
|
+
paths.network = path.resolve(dir, 'network.jsonl');
|
|
47
54
|
writeFileSync(paths.network, snapshot.requests.map((request) => `${JSON.stringify(request)}\n`).join(''), 'utf-8');
|
|
48
55
|
return paths;
|
|
49
56
|
}
|
|
50
57
|
|
|
51
58
|
function renderResult(data: EnvelopeData): string {
|
|
52
59
|
const lines = [`ok: ${data.ok}`, `command: ${data.command}`];
|
|
53
|
-
const healed = renderHealed(data);
|
|
54
|
-
if (healed) lines.push(healed);
|
|
55
60
|
if (data.used?.length) lines.push(`used: ${data.used.join('; ')}`);
|
|
56
61
|
return section('Result', lines.join('\n'));
|
|
57
62
|
}
|
|
58
63
|
|
|
59
|
-
function renderHealed(data: EnvelopeData): string | null {
|
|
60
|
-
if (data.healed === undefined) return null;
|
|
61
|
-
if (data.healNote) return `healed: ${data.healed} (${data.healNote})`;
|
|
62
|
-
return `healed: ${data.healed}`;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
64
|
function renderPage(data: EnvelopeData): string {
|
|
66
65
|
const { url, previousUrl, title, state, visits } = data.page;
|
|
67
66
|
const urlLabel = `url: ${url}`;
|
|
@@ -73,28 +72,59 @@ function renderPage(data: EnvelopeData): string {
|
|
|
73
72
|
return section('Page', lines.join('\n'));
|
|
74
73
|
}
|
|
75
74
|
|
|
75
|
+
function renderValue(data: EnvelopeData): string | null {
|
|
76
|
+
if (data.value === undefined) return null;
|
|
77
|
+
return section('Value', data.value);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function renderChanges(data: EnvelopeData): string | null {
|
|
81
|
+
if (data.changes === undefined || data.changes === null) return null;
|
|
82
|
+
return section('Changes', data.changes);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function renderSteps(data: EnvelopeData): string | null {
|
|
86
|
+
if (!data.steps?.length) return null;
|
|
87
|
+
|
|
88
|
+
const lines: string[] = [];
|
|
89
|
+
data.steps.forEach((step, index) => {
|
|
90
|
+
lines.push(`${index + 1}. ${step.ok ? 'ok ' : 'FAIL'} ${step.label}`);
|
|
91
|
+
for (const line of (step.proof || '').split('\n').filter(Boolean)) lines.push(` ${line}`);
|
|
92
|
+
});
|
|
93
|
+
if (data.stepFiles) lines.push('', `page after each step: ${data.stepFiles}`);
|
|
94
|
+
return section('Steps', lines.join('\n'));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function renderExpectations(data: EnvelopeData): string | null {
|
|
98
|
+
if (!data.expectations?.length) return null;
|
|
99
|
+
const lines = data.expectations.map((expectation, index) => `${index + 1}. ${EXPECTATION_LABELS[expectation.status]} ${expectation.text}`);
|
|
100
|
+
return section('Expected outcomes', lines.join('\n'));
|
|
101
|
+
}
|
|
102
|
+
|
|
76
103
|
function renderOutcome(data: EnvelopeData): string | null {
|
|
77
|
-
if (data.changes) return section('Changes', data.changes);
|
|
78
104
|
if (data.answer) return section('Answer', data.answer);
|
|
79
105
|
if (data.research) return section('Research', data.research);
|
|
80
|
-
if (!data.
|
|
81
|
-
|
|
82
|
-
return section('
|
|
106
|
+
if (!data.assertions) return null;
|
|
107
|
+
|
|
108
|
+
if (!data.assertions.length) return section('Assertions', 'none ran — no assertion could express this claim, so nothing was checked against the page');
|
|
109
|
+
|
|
110
|
+
const lines = data.assertions.map((assertion) => {
|
|
111
|
+
const code = assertion.code
|
|
112
|
+
.split('\n')
|
|
113
|
+
.map((line) => line.trim())
|
|
114
|
+
.filter((line) => line && !line.startsWith('//'))
|
|
115
|
+
.join(' ');
|
|
116
|
+
return `${code} => ${assertion.passed ? 'PASSED' : 'FAILED'}`;
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
const proof = data.assertions.flatMap((assertion) => assertion.proof);
|
|
120
|
+
if (proof.length) lines.push('', 'playwright:', ...proof);
|
|
121
|
+
|
|
122
|
+
return section('Assertions', lines.join('\n'));
|
|
83
123
|
}
|
|
84
124
|
|
|
85
125
|
function renderFailure(data: EnvelopeData): Array<string | null> {
|
|
86
126
|
if (!data.failure) return [];
|
|
87
|
-
|
|
88
|
-
if (data.failure.reasoning) lines.push(`reasoning: ${data.failure.reasoning}`);
|
|
89
|
-
return [section('Failure', lines.join('\n')), renderAttempts(data.failure.attempts), renderCompactAria(data.failure.compactAria)];
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function renderAttempts(attempts: HealAttempt[]): string | null {
|
|
93
|
-
if (!attempts?.length) return null;
|
|
94
|
-
const labels = attempts.map((attempt, index) => `${index + 1}. ${attempt.code}`);
|
|
95
|
-
const width = Math.max(...labels.map((label) => label.length)) + 3;
|
|
96
|
-
const lines = labels.map((label, index) => align(label, `→ ${attempts[index].outcome}`, width));
|
|
97
|
-
return section(`Healing attempts (${attempts.length})`, lines.join('\n'));
|
|
127
|
+
return [section('Failure', `error: ${data.failure.error}`), renderCompactAria(data.failure.compactAria)];
|
|
98
128
|
}
|
|
99
129
|
|
|
100
130
|
function renderCompactAria(compactAria?: string): string | null {
|
|
@@ -102,22 +132,19 @@ function renderCompactAria(compactAria?: string): string | null {
|
|
|
102
132
|
return section('Current page (compact ARIA)', compactAria);
|
|
103
133
|
}
|
|
104
134
|
|
|
105
|
-
function renderInstance(
|
|
106
|
-
const
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
function otherInstances(others: string[]): string {
|
|
112
|
-
if (!others.length) return 'none';
|
|
113
|
-
return others.join(', ');
|
|
135
|
+
function renderInstance(data: EnvelopeData): string | null {
|
|
136
|
+
const instance = data.instance;
|
|
137
|
+
const parts = [`${instance.name} (${tabsLabel(instance.tabs)})`, browserLine(instance)];
|
|
138
|
+
if (instance.others.length) parts.push(`other instances: ${instance.others.map((other) => `${other.name} (${tabsLabel(other.tabs)})`).join(', ')}`);
|
|
139
|
+
if (data.status) parts.push(`details: prima status ${data.status}`);
|
|
140
|
+
return section('Instance', parts.join(' | '));
|
|
114
141
|
}
|
|
115
142
|
|
|
116
143
|
function browserLine(instance: InstanceInfo): string {
|
|
117
|
-
if (instance.attached) return `
|
|
118
|
-
if (instance.startedAgo) return `
|
|
119
|
-
if (instance.tabs > 0) return '
|
|
120
|
-
return '
|
|
144
|
+
if (instance.attached) return `attached to ${instance.attached}`;
|
|
145
|
+
if (instance.startedAgo) return `running, started ${instance.startedAgo} ago`;
|
|
146
|
+
if (instance.tabs > 0) return 'running';
|
|
147
|
+
return 'not running';
|
|
121
148
|
}
|
|
122
149
|
|
|
123
150
|
function tabsLabel(tabs: number): string {
|
|
@@ -125,9 +152,10 @@ function tabsLabel(tabs: number): string {
|
|
|
125
152
|
return `${tabs} tabs`;
|
|
126
153
|
}
|
|
127
154
|
|
|
128
|
-
function renderArtifacts(data: EnvelopeData): string | null {
|
|
155
|
+
export function renderArtifacts(data: EnvelopeData): string | null {
|
|
129
156
|
if (!data.artifacts) return null;
|
|
130
|
-
const lines = [`aria: ${data.artifacts.aria}`, `html: ${data.artifacts.html}
|
|
157
|
+
const lines = [`aria: ${data.artifacts.aria}`, `html: ${data.artifacts.html}`];
|
|
158
|
+
if (data.artifacts.network) lines.push(`network: ${data.artifacts.network}`);
|
|
131
159
|
return section('Artifacts', lines.join('\n'));
|
|
132
160
|
}
|
|
133
161
|
|