explorbot 0.2.3 → 0.2.5
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 +26 -8
- package/boat/api-tester/src/cli.ts +17 -0
- 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/cli.ts +14 -1
- 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 +127 -86
- package/boat/prima/src/envelope.ts +102 -52
- package/boat/prima/src/prima.ts +567 -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 +26 -8
- package/dist/boat/api-tester/bin/apibot-cli.js +2 -0
- package/dist/boat/api-tester/src/cli.js +17 -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/cli.js +14 -1
- 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 +109 -77
- package/dist/boat/prima/src/envelope.js +94 -44
- package/dist/boat/prima/src/prima.js +533 -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 +41 -2
- 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 +34 -0
- package/dist/src/ai/navigator.js +237 -181
- package/dist/src/ai/pilot.d.ts +7 -0
- package/dist/src/ai/pilot.js +90 -2
- package/dist/src/ai/provider.d.ts +2 -2
- package/dist/src/ai/provider.js +14 -23
- package/dist/src/ai/rerunner.js +2 -1
- package/dist/src/ai/researcher/cache.d.ts +2 -0
- package/dist/src/ai/researcher/cache.js +10 -2
- package/dist/src/ai/researcher.js +3 -2
- package/dist/src/ai/rules.js +17 -10
- package/dist/src/ai/session-analyst.js +2 -0
- package/dist/src/ai/task-agent.js +4 -1
- package/dist/src/ai/tester.d.ts +6 -3
- package/dist/src/ai/tester.js +50 -46
- package/dist/src/ai/tools.d.ts +14 -0
- package/dist/src/ai/tools.js +117 -37
- package/dist/src/commands/config-command.d.ts +51 -0
- package/dist/src/commands/config-command.js +117 -0
- package/dist/src/commands/index.js +2 -0
- package/dist/src/config.d.ts +9 -1
- package/dist/src/config.js +53 -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 +7 -2
- package/dist/src/explorer.js +2 -3
- package/dist/src/playwright-recorder.js +30 -0
- package/dist/src/remote.d.ts +55 -0
- package/dist/src/remote.js +235 -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/state-manager.js +3 -1
- package/dist/src/stats.d.ts +1 -0
- package/dist/src/stats.js +1 -0
- package/dist/src/test-plan.d.ts +3 -0
- package/dist/src/test-plan.js +26 -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 +7 -1
- package/dist/src/utils/logger.js +32 -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 +87 -0
- package/docs/reference/commands.md +997 -0
- package/docs/reference/configuration.md +569 -0
- package/docs/reference/scripting.md +303 -0
- package/docs/reference/websocket.md +50 -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/superpowers/specs/2026-08-18-prima-false-verdicts.md +159 -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 +43 -3
- package/src/ai/captain/web-mode.ts +6 -3
- package/src/ai/captain.ts +3 -0
- package/src/ai/navigator.ts +255 -186
- package/src/ai/pilot.ts +104 -2
- package/src/ai/provider.ts +14 -24
- package/src/ai/rerunner.ts +2 -1
- package/src/ai/researcher/cache.ts +12 -2
- package/src/ai/researcher.ts +3 -2
- package/src/ai/rules.ts +17 -10
- package/src/ai/session-analyst.ts +2 -0
- package/src/ai/task-agent.ts +3 -1
- package/src/ai/tester.ts +52 -45
- package/src/ai/tools.ts +136 -37
- package/src/commands/config-command.ts +146 -0
- package/src/commands/index.ts +2 -0
- package/src/config.ts +60 -5
- package/src/execution-controller.ts +8 -0
- package/src/explorbot.ts +7 -3
- package/src/explorer.ts +2 -2
- package/src/playwright-recorder.ts +23 -0
- package/src/remote.ts +244 -0
- package/src/reporter.ts +7 -1
- package/src/state-manager.ts +6 -2
- package/src/stats.ts +1 -0
- package/src/test-plan.ts +29 -0
- package/src/utils/aria.ts +65 -45
- package/src/utils/html.ts +1 -0
- package/src/utils/logger.ts +33 -2
- 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
package/README.md
CHANGED
|
@@ -138,7 +138,7 @@ EXPLORBOT_KNOWLEDGE="Log in as admin@example.com / secret123" \
|
|
|
138
138
|
npx explorbot explore /admin/users --max-tests 3
|
|
139
139
|
```
|
|
140
140
|
|
|
141
|
-
Output lands in a per-host
|
|
141
|
+
Output lands in a per-host site directory, `~/.explorbot/sites/<host>/`, so runs against the same app collect in one place and nothing is written to your project. Set `EXPLORBOT_EPHEMERAL=1` to keep nothing between runs. See [Agentic Usage](docs/workflow/agentic-usage.md).
|
|
142
142
|
|
|
143
143
|
## Teaching Explorbot
|
|
144
144
|
|
package/bin/explorbot-cli.ts
CHANGED
|
@@ -11,10 +11,11 @@ import { App } from '../src/components/App.js';
|
|
|
11
11
|
import { StatusPane } from '../src/components/StatusPane.js';
|
|
12
12
|
import { ConfigParser, EXPLORBOT_ENV_VARS, PROVIDERS } from '../src/config.js';
|
|
13
13
|
import { ExplorBot, type ExplorBotOptions } from '../src/explorbot.js';
|
|
14
|
+
import { remote } from '../src/remote.js';
|
|
14
15
|
import { Stats } from '../src/stats.js';
|
|
15
16
|
import { Plan } from '../src/test-plan.js';
|
|
16
17
|
import { getCliName } from '../src/utils/cli-name.ts';
|
|
17
|
-
import { log, setPreserveConsoleLogs } from '../src/utils/logger.js';
|
|
18
|
+
import { isVerboseMode, log, setPreserveConsoleLogs, setQuietMode } from '../src/utils/logger.js';
|
|
18
19
|
import { jsonToTable } from '../src/utils/markdown-parser.js';
|
|
19
20
|
import { parseMarkdownToTerminal } from '../src/utils/markdown-terminal.js';
|
|
20
21
|
import { type NextStepSection, printNextSteps, relativeToCwd } from '../src/utils/next-steps.ts';
|
|
@@ -26,8 +27,9 @@ const pkgPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../p
|
|
|
26
27
|
const pkgVersion = JSON.parse(fs.readFileSync(pkgPath, 'utf-8')).version as string;
|
|
27
28
|
|
|
28
29
|
program.name(cli).description('AI-powered web exploration tool').version(pkgVersion, '-V, --version');
|
|
30
|
+
remote.registerOption(program);
|
|
29
31
|
|
|
30
|
-
if (!process.env.EXPLORBOT_NO_BANNER) {
|
|
32
|
+
if (!process.env.EXPLORBOT_NO_BANNER && !process.argv.includes('prima')) {
|
|
31
33
|
console.log(`⛵ ${chalk.yellow.bold(`Explorbot v${pkgVersion}`)} ${chalk.dim('Autonomous Testing Agent')}`);
|
|
32
34
|
}
|
|
33
35
|
|
|
@@ -99,6 +101,10 @@ async function startTUI(explorBot: ExplorBot): Promise<void> {
|
|
|
99
101
|
}
|
|
100
102
|
|
|
101
103
|
async function showStatsAndExit(code: number): Promise<never> {
|
|
104
|
+
if (remote.isAttached()) {
|
|
105
|
+
await remote.close(code);
|
|
106
|
+
process.exit(code);
|
|
107
|
+
}
|
|
102
108
|
if (Stats.hasActivity()) {
|
|
103
109
|
await new Promise<void>((resolve) => {
|
|
104
110
|
const { unmount } = render(
|
|
@@ -430,6 +436,23 @@ addCommonOptions(
|
|
|
430
436
|
await cmd.execute(args);
|
|
431
437
|
});
|
|
432
438
|
|
|
439
|
+
program
|
|
440
|
+
.command('config [url]')
|
|
441
|
+
.description('Show models, config file and paths used by this run')
|
|
442
|
+
.option('-c, --config <path>', 'Path to configuration file')
|
|
443
|
+
.option('-p, --path <path>', 'Working directory path')
|
|
444
|
+
.option('--json', 'Print the resolved config as JSON')
|
|
445
|
+
.action(async (url, options) => {
|
|
446
|
+
setQuietMode(!isVerboseMode());
|
|
447
|
+
const { ConfigCommand } = await import('../src/commands/config-command.js');
|
|
448
|
+
try {
|
|
449
|
+
console.log(await ConfigCommand.summary({ config: options.config, path: options.path, url, json: options.json }));
|
|
450
|
+
} catch (error) {
|
|
451
|
+
console.error(error instanceof Error ? error.message : 'Unknown error');
|
|
452
|
+
process.exit(1);
|
|
453
|
+
}
|
|
454
|
+
});
|
|
455
|
+
|
|
433
456
|
program
|
|
434
457
|
.command('init')
|
|
435
458
|
.description('Initialize configuration for a project or for this machine')
|
|
@@ -906,11 +929,6 @@ ${rows}
|
|
|
906
929
|
`;
|
|
907
930
|
};
|
|
908
931
|
|
|
909
|
-
|
|
910
|
-
cmd.addHelpText('after', envHelp);
|
|
911
|
-
for (const sub of cmd.commands) addEnvHelp(sub);
|
|
912
|
-
};
|
|
913
|
-
|
|
914
|
-
addEnvHelp(program);
|
|
932
|
+
program.addHelpText('after', envHelp);
|
|
915
933
|
|
|
916
934
|
program.parse();
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { Command } from 'commander';
|
|
4
|
+
import { ConfigCommand } from '../../../src/commands/config-command.ts';
|
|
5
|
+
import { listSites } from '../../../src/global-config.ts';
|
|
4
6
|
import { setPreserveConsoleLogs } from '../../../src/utils/logger.ts';
|
|
5
7
|
import { getStyles } from './ai/chief/styles.ts';
|
|
6
8
|
import { ApiBot, type ApibotOptions } from './apibot.ts';
|
|
9
|
+
import { ApibotConfigParser } from './config.ts';
|
|
7
10
|
|
|
8
11
|
function buildOptions(options: any): ApibotOptions {
|
|
9
12
|
return {
|
|
@@ -82,6 +85,20 @@ export function createApiCommands(name = 'api'): Command {
|
|
|
82
85
|
}
|
|
83
86
|
});
|
|
84
87
|
|
|
88
|
+
addCommonOptions(cmd.command('config [endpoint]').description('Show models, config file and paths used by this run'))
|
|
89
|
+
.option('--json', 'Print the resolved config as JSON')
|
|
90
|
+
.action(async (endpoint, options) => {
|
|
91
|
+
const parser = ApibotConfigParser.getInstance();
|
|
92
|
+
const [site] = listSites();
|
|
93
|
+
try {
|
|
94
|
+
const config = await parser.loadConfig({ config: options.config, path: options.path, endpoint: endpoint || site?.url });
|
|
95
|
+
console.log(ConfigCommand.render(config, { configPath: parser.getConfigPath(), root: parser.getProjectRoot(), json: options.json }));
|
|
96
|
+
} catch (error) {
|
|
97
|
+
console.error(error instanceof Error ? error.message : 'Unknown error');
|
|
98
|
+
process.exit(1);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
85
102
|
addCommonOptions(cmd.command('test <planfile> [index]').description('Execute tests from a plan file. Index: 1, 1-3, *')).action(async (planfile, index, options) => {
|
|
86
103
|
setPreserveConsoleLogs(true);
|
|
87
104
|
try {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync } from 'node:fs';
|
|
2
2
|
import path, { resolve } from 'node:path';
|
|
3
|
+
import { pathToFileURL } from 'node:url';
|
|
3
4
|
import { parseEnv } from 'node:util';
|
|
4
5
|
import { type AIConfig, type ApiHookFn, type ApiConfig as BaseApiConfig, ConfigMissingError, EXPLORBOT_CONFIG_PATHS, createModel, envConfigRequested, materializeKnowledge, missingConfigMessage, resolveConfigModels, resolveModel, resolveOutputRoot } from '../../../src/config.ts';
|
|
5
6
|
import { type SiteRecord, findGlobalConfig, globalEnvPath, isGlobalConfigPath, registerSite, resolveSiteTarget } from '../../../src/global-config.ts';
|
|
@@ -213,10 +214,11 @@ export class ApibotConfigParser {
|
|
|
213
214
|
|
|
214
215
|
private async loadConfigModule(configPath: string): Promise<any> {
|
|
215
216
|
const ext = configPath.split('.').pop();
|
|
217
|
+
const moduleUrl = pathToFileURL(resolve(configPath)).href;
|
|
216
218
|
|
|
217
219
|
if (ext === 'ts') {
|
|
218
220
|
try {
|
|
219
|
-
return await import(
|
|
221
|
+
return await import(moduleUrl);
|
|
220
222
|
} catch {
|
|
221
223
|
const require = (await import('node:module')).createRequire(import.meta.url);
|
|
222
224
|
return require(configPath);
|
|
@@ -224,7 +226,7 @@ export class ApibotConfigParser {
|
|
|
224
226
|
}
|
|
225
227
|
|
|
226
228
|
if (ext === 'js' || ext === 'mjs') {
|
|
227
|
-
return await import(
|
|
229
|
+
return await import(moduleUrl);
|
|
228
230
|
}
|
|
229
231
|
|
|
230
232
|
const content = readFileSync(configPath, 'utf8');
|
|
@@ -67,17 +67,32 @@ class Documentarian {
|
|
|
67
67
|
const message = error instanceof Error ? error.message : String(error);
|
|
68
68
|
tag('warning').log(`Interactive documentation failed: ${message}.`);
|
|
69
69
|
if (meaningfulInteractions.length > 0) {
|
|
70
|
-
tag('info').log(`
|
|
71
|
-
return this.
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
70
|
+
tag('info').log(`Retrying static documentation while preserving ${meaningfulInteractions.length} observed interaction(s).`);
|
|
71
|
+
return this.documentStatic(state, research)
|
|
72
|
+
.then((documentation) =>
|
|
73
|
+
this.normalizeDocumentation(
|
|
74
|
+
{
|
|
75
|
+
...documentation,
|
|
76
|
+
interactions: meaningfulInteractions,
|
|
77
|
+
},
|
|
78
|
+
state,
|
|
79
|
+
research
|
|
80
|
+
)
|
|
81
|
+
)
|
|
82
|
+
.catch((fallbackError) => {
|
|
83
|
+
const fallbackMessage = fallbackError instanceof Error ? fallbackError.message : String(fallbackError);
|
|
84
|
+
tag('warning').log(`Static documentation fallback failed: ${fallbackMessage}. Preserving observed interactions without AI summary.`);
|
|
85
|
+
return this.normalizeDocumentation(
|
|
86
|
+
{
|
|
87
|
+
summary: `Observed ${meaningfulInteractions.length} interaction(s); AI-generated summary was unavailable.`,
|
|
88
|
+
can: [],
|
|
89
|
+
might: [],
|
|
90
|
+
interactions: meaningfulInteractions,
|
|
91
|
+
},
|
|
92
|
+
state,
|
|
93
|
+
research
|
|
94
|
+
);
|
|
95
|
+
});
|
|
81
96
|
}
|
|
82
97
|
return this.documentStatic(state, research);
|
|
83
98
|
}
|
|
@@ -113,13 +128,13 @@ class Documentarian {
|
|
|
113
128
|
},
|
|
114
129
|
];
|
|
115
130
|
|
|
116
|
-
const response = await this.provider.generateObject(messages,
|
|
131
|
+
const response = await this.provider.generateObject(messages, generatedPageDocumentationSchema, undefined, {
|
|
117
132
|
agentName: 'documentarian',
|
|
118
133
|
});
|
|
119
134
|
|
|
120
135
|
return this.normalizeDocumentation(
|
|
121
136
|
{
|
|
122
|
-
...(response.object as
|
|
137
|
+
...(response.object as GeneratedPageDocumentation),
|
|
123
138
|
interactions,
|
|
124
139
|
},
|
|
125
140
|
state,
|
|
@@ -139,11 +154,11 @@ class Documentarian {
|
|
|
139
154
|
},
|
|
140
155
|
];
|
|
141
156
|
|
|
142
|
-
const response = await this.provider.generateObject(messages,
|
|
157
|
+
const response = await this.provider.generateObject(messages, generatedPageDocumentationSchema, undefined, {
|
|
143
158
|
agentName: 'documentarian',
|
|
144
159
|
});
|
|
145
160
|
|
|
146
|
-
return this.normalizeDocumentation(response.object as
|
|
161
|
+
return this.normalizeDocumentation(response.object as GeneratedPageDocumentation, state, research);
|
|
147
162
|
}
|
|
148
163
|
|
|
149
164
|
private getSystemPrompt(): string {
|
|
@@ -259,7 +274,7 @@ class Documentarian {
|
|
|
259
274
|
return message.includes('Failed to generate JSON') || message.includes('Failed to validate JSON') || message.includes('failed_generation') || message.includes('No object generated') || message.includes('response did not match schema');
|
|
260
275
|
}
|
|
261
276
|
|
|
262
|
-
private normalizeDocumentation(documentation: PageDocumentation, _state: WebPageState, _research: string): PageDocumentation {
|
|
277
|
+
private normalizeDocumentation(documentation: GeneratedPageDocumentation & Partial<Pick<PageDocumentation, 'interactions'>>, _state: WebPageState, _research: string): PageDocumentation {
|
|
263
278
|
const normalized = { ...documentation };
|
|
264
279
|
if (!normalized.interactions) {
|
|
265
280
|
normalized.interactions = undefined;
|
|
@@ -267,10 +282,10 @@ class Documentarian {
|
|
|
267
282
|
|
|
268
283
|
const qualityNotes = this.evaluateDocumentationQuality(normalized);
|
|
269
284
|
|
|
270
|
-
return {
|
|
285
|
+
return pageDocumentationSchema.parse({
|
|
271
286
|
...normalized,
|
|
272
287
|
qualityNotes,
|
|
273
|
-
};
|
|
288
|
+
});
|
|
274
289
|
}
|
|
275
290
|
|
|
276
291
|
private evaluateDocumentationQuality(documentation: PageDocumentation): string[] {
|
|
@@ -346,39 +361,54 @@ const stateTransitionSchema = z.object({
|
|
|
346
361
|
action: z.string(),
|
|
347
362
|
before: z.string(),
|
|
348
363
|
after: z.string(),
|
|
349
|
-
targetUrl: z.string().
|
|
350
|
-
discoveredUrls: z.array(z.string()).
|
|
351
|
-
newCapabilities: z.array(z.string()).
|
|
364
|
+
targetUrl: z.string().optional(),
|
|
365
|
+
discoveredUrls: z.array(z.string()).optional(),
|
|
366
|
+
newCapabilities: z.array(z.string()).optional(),
|
|
352
367
|
element: z
|
|
353
368
|
.object({
|
|
354
369
|
role: z.string(),
|
|
355
370
|
name: z.string(),
|
|
356
371
|
section: z.string(),
|
|
357
|
-
container: z.string().
|
|
358
|
-
locator: z.string().
|
|
372
|
+
container: z.string().optional(),
|
|
373
|
+
locator: z.string().optional(),
|
|
359
374
|
})
|
|
360
|
-
.
|
|
375
|
+
.optional(),
|
|
361
376
|
changes: z
|
|
362
377
|
.object({
|
|
363
378
|
urlChanged: z.boolean(),
|
|
364
379
|
newElements: z.number(),
|
|
365
380
|
removedElements: z.number(),
|
|
366
381
|
})
|
|
367
|
-
.
|
|
382
|
+
.optional(),
|
|
383
|
+
targetState: z
|
|
384
|
+
.object({
|
|
385
|
+
kind: z.enum(['page', 'dialog', 'modal', 'section']),
|
|
386
|
+
label: z.string(),
|
|
387
|
+
url: z.string(),
|
|
388
|
+
})
|
|
389
|
+
.optional(),
|
|
390
|
+
screenshot: z
|
|
391
|
+
.object({
|
|
392
|
+
title: z.string(),
|
|
393
|
+
relativePath: z.string(),
|
|
394
|
+
})
|
|
395
|
+
.optional(),
|
|
368
396
|
});
|
|
369
397
|
|
|
370
|
-
const
|
|
398
|
+
const generatedPageDocumentationSchema = z.object({
|
|
371
399
|
summary: z.string(),
|
|
372
400
|
can: z.array(capabilitySchema),
|
|
373
401
|
might: z.array(capabilitySchema),
|
|
374
|
-
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
const pageDocumentationSchema = generatedPageDocumentationSchema.extend({
|
|
405
|
+
interactions: z.array(stateTransitionSchema).optional(),
|
|
406
|
+
qualityNotes: z.array(z.string()).optional(),
|
|
375
407
|
});
|
|
376
408
|
|
|
377
409
|
type StateTransition = DocStateTransition;
|
|
378
|
-
type
|
|
379
|
-
|
|
380
|
-
qualityNotes?: string[];
|
|
381
|
-
};
|
|
410
|
+
type GeneratedPageDocumentation = z.infer<typeof generatedPageDocumentationSchema>;
|
|
411
|
+
type PageDocumentation = z.infer<typeof pageDocumentationSchema>;
|
|
382
412
|
|
|
383
413
|
export { Documentarian };
|
|
384
414
|
export type { PageDocumentation, StateTransition };
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { Command } from 'commander';
|
|
4
|
-
import {
|
|
4
|
+
import { ConfigCommand } from '../../../src/commands/config-command.ts';
|
|
5
|
+
import { isVerboseMode, setPreserveConsoleLogs, setQuietMode } from '../../../src/utils/logger.ts';
|
|
5
6
|
import { DocBot, type DocbotOptions } from './docbot.ts';
|
|
6
7
|
|
|
7
8
|
function buildOptions(options: any): DocbotOptions {
|
|
@@ -65,6 +66,18 @@ export function createDocsCommands(name = 'docs'): Command {
|
|
|
65
66
|
}
|
|
66
67
|
});
|
|
67
68
|
|
|
69
|
+
addCommonOptions(cmd.command('config [url]').description('Show models, config file and paths used by this run'))
|
|
70
|
+
.option('--json', 'Print the resolved config as JSON')
|
|
71
|
+
.action(async (url, options) => {
|
|
72
|
+
setQuietMode(!isVerboseMode());
|
|
73
|
+
try {
|
|
74
|
+
console.log(await ConfigCommand.summary({ config: options.config, path: options.path, url, json: options.json }));
|
|
75
|
+
} catch (error) {
|
|
76
|
+
console.error(error instanceof Error ? error.message : 'Unknown error');
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
68
81
|
cmd
|
|
69
82
|
.command('init')
|
|
70
83
|
.description('Initialize doc collector configuration')
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'node:fs';
|
|
2
2
|
import path, { resolve } from 'node:path';
|
|
3
|
+
import { pathToFileURL } from 'node:url';
|
|
3
4
|
import { parseEnv } from 'node:util';
|
|
4
5
|
import { ConfigParser } from '../../../src/config.ts';
|
|
5
6
|
|
|
@@ -90,10 +91,11 @@ class DocbotConfigParser {
|
|
|
90
91
|
|
|
91
92
|
private async loadConfigModule(configPath: string): Promise<any> {
|
|
92
93
|
const ext = configPath.split('.').pop();
|
|
94
|
+
const moduleUrl = pathToFileURL(resolve(configPath)).href;
|
|
93
95
|
|
|
94
96
|
if (ext === 'ts') {
|
|
95
97
|
try {
|
|
96
|
-
return await import(
|
|
98
|
+
return await import(moduleUrl);
|
|
97
99
|
} catch {
|
|
98
100
|
const require = (await import('node:module')).createRequire(import.meta.url);
|
|
99
101
|
return require(configPath);
|
|
@@ -101,7 +103,7 @@ class DocbotConfigParser {
|
|
|
101
103
|
}
|
|
102
104
|
|
|
103
105
|
if (ext === 'js' || ext === 'mjs') {
|
|
104
|
-
return await import(
|
|
106
|
+
return await import(moduleUrl);
|
|
105
107
|
}
|
|
106
108
|
|
|
107
109
|
return JSON.parse(readFileSync(configPath, 'utf8'));
|
|
File without changes
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { type ActivityEntry, addActivityListener, removeActivityListener } from '../../../src/activity.ts';
|
|
3
|
+
import { isVerboseMode } from '../../../src/utils/logger.ts';
|
|
4
|
+
|
|
5
|
+
const RESET_LINE = '\r\u001b[2K';
|
|
6
|
+
|
|
7
|
+
const stream = process.stderr;
|
|
8
|
+
let tracking = false;
|
|
9
|
+
|
|
10
|
+
export function trackActivityLine(): void {
|
|
11
|
+
if (tracking) return;
|
|
12
|
+
if (!stream.isTTY) return;
|
|
13
|
+
if (isVerboseMode()) return;
|
|
14
|
+
|
|
15
|
+
tracking = true;
|
|
16
|
+
addActivityListener(writeActivityLine);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function clearActivityLine(): void {
|
|
20
|
+
if (!tracking) return;
|
|
21
|
+
|
|
22
|
+
tracking = false;
|
|
23
|
+
removeActivityListener(writeActivityLine);
|
|
24
|
+
stream.write(RESET_LINE);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function writeActivityLine(activity: ActivityEntry | null): void {
|
|
28
|
+
if (!activity) return;
|
|
29
|
+
|
|
30
|
+
const width = (stream.columns || 80) - 2;
|
|
31
|
+
const message = Array.from(activity.message.replace(/\s+/g, ' ').trim()).slice(0, width).join('');
|
|
32
|
+
stream.write(`${RESET_LINE}${chalk.gray(message)}`);
|
|
33
|
+
}
|