@tangle-network/browser-agent-driver 0.8.0 → 0.10.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 +2 -2
- package/dist/brain/index.d.ts +24 -7
- package/dist/brain/index.d.ts.map +1 -1
- package/dist/brain/index.js +227 -93
- package/dist/brain/index.js.map +1 -1
- package/dist/captcha.d.ts +55 -0
- package/dist/captcha.d.ts.map +1 -0
- package/dist/captcha.js +403 -0
- package/dist/captcha.js.map +1 -0
- package/dist/cli-design-audit.d.ts.map +1 -1
- package/dist/cli-design-audit.js +81 -59
- package/dist/cli-design-audit.js.map +1 -1
- package/dist/cli-ui.d.ts +49 -0
- package/dist/cli-ui.d.ts.map +1 -0
- package/dist/cli-ui.js +395 -0
- package/dist/cli-ui.js.map +1 -0
- package/dist/cli.js +144 -191
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -1
- package/dist/config.js.map +1 -1
- package/dist/design-audit.js +2 -2
- package/dist/design-audit.js.map +1 -1
- package/dist/drivers/cdp-snapshot.d.ts.map +1 -1
- package/dist/drivers/cdp-snapshot.js +2 -1
- package/dist/drivers/cdp-snapshot.js.map +1 -1
- package/dist/drivers/playwright.d.ts +6 -0
- package/dist/drivers/playwright.d.ts.map +1 -1
- package/dist/drivers/playwright.js +34 -9
- package/dist/drivers/playwright.js.map +1 -1
- package/dist/drivers/snapshot.d.ts +1 -0
- package/dist/drivers/snapshot.d.ts.map +1 -1
- package/dist/drivers/snapshot.js +12 -5
- package/dist/drivers/snapshot.js.map +1 -1
- package/dist/drivers/types.d.ts +2 -0
- package/dist/drivers/types.d.ts.map +1 -1
- package/dist/index.d.ts +7 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/memory/knowledge.d.ts +25 -9
- package/dist/memory/knowledge.d.ts.map +1 -1
- package/dist/memory/knowledge.js +94 -28
- package/dist/memory/knowledge.js.map +1 -1
- package/dist/memory/run-registry.d.ts +85 -0
- package/dist/memory/run-registry.d.ts.map +1 -0
- package/dist/memory/run-registry.js +144 -0
- package/dist/memory/run-registry.js.map +1 -0
- package/dist/multi-actor.d.ts +3 -3
- package/dist/multi-actor.d.ts.map +1 -1
- package/dist/multi-actor.js +3 -3
- package/dist/multi-actor.js.map +1 -1
- package/dist/recovery.d.ts.map +1 -1
- package/dist/recovery.js +16 -35
- package/dist/recovery.js.map +1 -1
- package/dist/runner/index.d.ts +2 -2
- package/dist/runner/index.d.ts.map +1 -1
- package/dist/runner/index.js +1 -1
- package/dist/runner/index.js.map +1 -1
- package/dist/runner/runner.d.ts +10 -5
- package/dist/runner/runner.d.ts.map +1 -1
- package/dist/runner/runner.js +204 -120
- package/dist/runner/runner.js.map +1 -1
- package/dist/runner.d.ts +2 -2
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +1 -1
- package/dist/runner.js.map +1 -1
- package/dist/test-runner.d.ts +1 -0
- package/dist/test-runner.d.ts.map +1 -1
- package/dist/test-runner.js +26 -2
- package/dist/test-runner.js.map +1 -1
- package/dist/types.d.ts +19 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -2
package/dist/cli.js
CHANGED
|
@@ -20,6 +20,9 @@ import { runWalletPreflight, startWalletAutoApprover } from './wallet/automation
|
|
|
20
20
|
import { isPersonaId, listPersonaIds, withPersonaDirective } from './personas.js';
|
|
21
21
|
import { resolveProviderApiKey, resolveProviderModelName } from './provider-defaults.js';
|
|
22
22
|
import { loadLocalEnvFiles } from './env-loader.js';
|
|
23
|
+
import { CliRenderer, cliError, cliWarn, cliLog, printStyledHelp } from './cli-ui.js';
|
|
24
|
+
import { ProjectStore } from './memory/project-store.js';
|
|
25
|
+
import { RunRegistry } from './memory/run-registry.js';
|
|
23
26
|
const RUN_MODES = ['fast-explore', 'full-evidence'];
|
|
24
27
|
const DRIVER_PROFILES = ['default', 'stealth', 'benchmark-webbench', 'benchmark-webbench-stealth', 'benchmark-webvoyager'];
|
|
25
28
|
async function applyStorageStateToPersistentContext(context, storageStatePath) {
|
|
@@ -93,6 +96,9 @@ async function main() {
|
|
|
93
96
|
'storage-state': { type: 'string' },
|
|
94
97
|
concurrency: { type: 'string' },
|
|
95
98
|
'max-turns': { type: 'string' },
|
|
99
|
+
'session-id': { type: 'string' },
|
|
100
|
+
'resume-run': { type: 'string' },
|
|
101
|
+
'fork-run': { type: 'string' },
|
|
96
102
|
pages: { type: 'string' },
|
|
97
103
|
'extract-tokens': { type: 'boolean' },
|
|
98
104
|
'llm-timeout': { type: 'string' },
|
|
@@ -145,13 +151,13 @@ async function main() {
|
|
|
145
151
|
process.exit(0);
|
|
146
152
|
}
|
|
147
153
|
if (values.help || positionals.length === 0) {
|
|
148
|
-
|
|
154
|
+
printStyledHelp(RUN_MODES, DRIVER_PROFILES, listPersonaIds());
|
|
149
155
|
process.exit(0);
|
|
150
156
|
}
|
|
151
157
|
const command = positionals[0];
|
|
152
158
|
if (command === 'design-audit') {
|
|
153
159
|
if (!values.url) {
|
|
154
|
-
|
|
160
|
+
cliError('--url is required for design-audit.');
|
|
155
161
|
process.exit(1);
|
|
156
162
|
}
|
|
157
163
|
const { runDesignAudit } = await import('./cli-design-audit.js');
|
|
@@ -170,25 +176,57 @@ async function main() {
|
|
|
170
176
|
});
|
|
171
177
|
process.exit(0);
|
|
172
178
|
}
|
|
179
|
+
if (command === 'runs') {
|
|
180
|
+
const store = new ProjectStore(values['memory-dir']);
|
|
181
|
+
const registry = new RunRegistry(store.getRoot());
|
|
182
|
+
const runs = registry.listRuns({
|
|
183
|
+
domain: values.url ? new URL(values.url).hostname : undefined,
|
|
184
|
+
sessionId: values['session-id'],
|
|
185
|
+
limit: 20,
|
|
186
|
+
});
|
|
187
|
+
if (runs.length === 0) {
|
|
188
|
+
console.log(' No runs found.');
|
|
189
|
+
}
|
|
190
|
+
else if (values.json) {
|
|
191
|
+
console.log(JSON.stringify(runs, null, 2));
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
for (const r of runs) {
|
|
195
|
+
const icon = r.status === 'completed' ? (r.success ? '\u2713' : '\u2717') : '\u25cb';
|
|
196
|
+
const ts = r.startedAt.slice(0, 16).replace('T', ' ');
|
|
197
|
+
const dur = r.completedAt
|
|
198
|
+
? `${Math.round((new Date(r.completedAt).getTime() - new Date(r.startedAt).getTime()) / 1000)}s`
|
|
199
|
+
: 'running';
|
|
200
|
+
const session = r.sessionId ? ` [${r.sessionId}]` : '';
|
|
201
|
+
const parent = r.parentRunId ? ` \u2190 ${r.parentRunId.slice(0, 20)}` : '';
|
|
202
|
+
console.log(` ${icon} ${r.runId.slice(0, 30)} ${ts} ${dur} ${r.goal.slice(0, 50)}${session}${parent}`);
|
|
203
|
+
if (r.summary)
|
|
204
|
+
console.log(` ${r.summary.slice(0, 80)}`);
|
|
205
|
+
if (r.finalUrl)
|
|
206
|
+
console.log(` ${r.finalUrl}`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
process.exit(0);
|
|
210
|
+
}
|
|
173
211
|
if (command !== 'run') {
|
|
174
|
-
|
|
212
|
+
cliError(`Unknown command: ${command}. Use "run", "runs", or "design-audit".`);
|
|
175
213
|
process.exit(1);
|
|
176
214
|
}
|
|
177
215
|
// Validate inputs
|
|
178
|
-
if (!values.goal && !values.cases) {
|
|
179
|
-
|
|
216
|
+
if (!values.goal && !values.cases && !values['resume-run'] && !values['fork-run']) {
|
|
217
|
+
cliError('provide --goal "..." --url "..." for a single task, --cases ./cases.json for a suite, or --resume-run / --fork-run <runId>.');
|
|
180
218
|
process.exit(1);
|
|
181
219
|
}
|
|
182
220
|
// Load config file, then overlay CLI flags
|
|
183
221
|
const fileConfig = await loadConfig(values.config);
|
|
184
222
|
const mode = values.mode;
|
|
185
223
|
if (mode && !RUN_MODES.includes(mode)) {
|
|
186
|
-
|
|
224
|
+
cliError(`unknown mode "${mode}". Valid modes: ${RUN_MODES.join(', ')}`);
|
|
187
225
|
process.exit(1);
|
|
188
226
|
}
|
|
189
227
|
const profile = values.profile;
|
|
190
228
|
if (profile && !DRIVER_PROFILES.includes(profile)) {
|
|
191
|
-
|
|
229
|
+
cliError(`unknown profile "${profile}". Valid profiles: ${DRIVER_PROFILES.join(', ')}`);
|
|
192
230
|
process.exit(1);
|
|
193
231
|
}
|
|
194
232
|
// Build CLI overrides (only set values that were explicitly passed)
|
|
@@ -216,12 +254,12 @@ async function main() {
|
|
|
216
254
|
if (values['prompt-file']) {
|
|
217
255
|
const promptPath = path.resolve(values['prompt-file']);
|
|
218
256
|
if (!fs.existsSync(promptPath)) {
|
|
219
|
-
|
|
257
|
+
cliError(`prompt file not found: ${promptPath}`);
|
|
220
258
|
process.exit(1);
|
|
221
259
|
}
|
|
222
260
|
cliOverrides.systemPrompt = fs.readFileSync(promptPath, 'utf-8').trim();
|
|
223
261
|
if (!cliOverrides.systemPrompt) {
|
|
224
|
-
|
|
262
|
+
cliError(`prompt file is empty: ${promptPath}`);
|
|
225
263
|
process.exit(1);
|
|
226
264
|
}
|
|
227
265
|
}
|
|
@@ -457,12 +495,12 @@ async function main() {
|
|
|
457
495
|
const quiet = values.quiet;
|
|
458
496
|
for (const warning of launchPlan.warnings) {
|
|
459
497
|
if (!quiet) {
|
|
460
|
-
|
|
498
|
+
cliWarn(warning);
|
|
461
499
|
}
|
|
462
500
|
}
|
|
463
501
|
if (launchPlan.errors.length > 0) {
|
|
464
502
|
for (const error of launchPlan.errors) {
|
|
465
|
-
|
|
503
|
+
cliError(error);
|
|
466
504
|
}
|
|
467
505
|
process.exit(1);
|
|
468
506
|
}
|
|
@@ -516,9 +554,49 @@ async function main() {
|
|
|
516
554
|
walletMode: Boolean(driverConfig.wallet?.enabled),
|
|
517
555
|
walletAddress: driverConfig.wallet?.address,
|
|
518
556
|
};
|
|
557
|
+
// Create project store for memory + run registry
|
|
558
|
+
const memoryEnabled = driverConfig.memory?.enabled === true;
|
|
559
|
+
const projectStore = memoryEnabled
|
|
560
|
+
? new ProjectStore(driverConfig.memory?.dir)
|
|
561
|
+
: undefined;
|
|
562
|
+
const runRegistry = projectStore
|
|
563
|
+
? new RunRegistry(projectStore.getRoot())
|
|
564
|
+
: undefined;
|
|
519
565
|
// Build test cases
|
|
520
566
|
let cases;
|
|
521
|
-
if (values
|
|
567
|
+
if (values['resume-run'] || values['fork-run']) {
|
|
568
|
+
// Resume or fork from a previous run
|
|
569
|
+
if (!runRegistry) {
|
|
570
|
+
cliError('--resume-run and --fork-run require memory to be enabled');
|
|
571
|
+
process.exit(1);
|
|
572
|
+
}
|
|
573
|
+
const isResume = Boolean(values['resume-run']);
|
|
574
|
+
const sourceRunId = (values['resume-run'] || values['fork-run']);
|
|
575
|
+
const scenario = isResume
|
|
576
|
+
? runRegistry.buildResumeScenario(sourceRunId, values.goal)
|
|
577
|
+
: runRegistry.buildForkScenario(sourceRunId, values.goal || '');
|
|
578
|
+
if (!scenario) {
|
|
579
|
+
cliError(`run "${sourceRunId}" not found in registry`);
|
|
580
|
+
process.exit(1);
|
|
581
|
+
}
|
|
582
|
+
if (!isResume && !values.goal) {
|
|
583
|
+
cliError('--fork-run requires --goal');
|
|
584
|
+
process.exit(1);
|
|
585
|
+
}
|
|
586
|
+
cases = [{
|
|
587
|
+
id: `${isResume ? 'resume' : 'fork'}-${sourceRunId.slice(0, 20)}`,
|
|
588
|
+
name: scenario.goal.slice(0, 60),
|
|
589
|
+
startUrl: values.url || scenario.startUrl,
|
|
590
|
+
goal: scenario.goal,
|
|
591
|
+
allowedDomains: parseAllowedDomains(values['allowed-domains']),
|
|
592
|
+
maxTurns,
|
|
593
|
+
timeoutMs,
|
|
594
|
+
priority: 0,
|
|
595
|
+
sessionId: scenario.sessionId,
|
|
596
|
+
parentRunId: scenario.parentRunId,
|
|
597
|
+
}];
|
|
598
|
+
}
|
|
599
|
+
else if (values.cases) {
|
|
522
600
|
const raw = fs.readFileSync(path.resolve(values.cases), 'utf-8');
|
|
523
601
|
const parsed = JSON.parse(raw);
|
|
524
602
|
const rawCases = Array.isArray(parsed) ? parsed : [parsed];
|
|
@@ -546,13 +624,18 @@ async function main() {
|
|
|
546
624
|
maxTurns,
|
|
547
625
|
timeoutMs,
|
|
548
626
|
priority: 0,
|
|
627
|
+
sessionId: values['session-id'],
|
|
549
628
|
}];
|
|
550
629
|
}
|
|
630
|
+
// Apply --session-id to all cases from file too
|
|
631
|
+
if (values['session-id'] && cases.length > 0 && cases[0].id !== 'cli-task') {
|
|
632
|
+
const sid = values['session-id'];
|
|
633
|
+
cases = cases.map(c => ({ ...c, sessionId: c.sessionId || sid }));
|
|
634
|
+
}
|
|
551
635
|
const persona = values.persona;
|
|
552
636
|
if (persona) {
|
|
553
637
|
if (!isPersonaId(persona)) {
|
|
554
|
-
|
|
555
|
-
`Valid personas: ${listPersonaIds().join(', ')}`);
|
|
638
|
+
cliError(`unknown persona "${persona}". Valid personas: ${listPersonaIds().join(', ')}`);
|
|
556
639
|
process.exit(1);
|
|
557
640
|
}
|
|
558
641
|
cases = cases.map((c) => ({
|
|
@@ -564,20 +647,24 @@ async function main() {
|
|
|
564
647
|
}),
|
|
565
648
|
}));
|
|
566
649
|
}
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
650
|
+
const renderer = (!quiet && !values.json) ? new CliRenderer({ debug }) : null;
|
|
651
|
+
if (renderer) {
|
|
652
|
+
const version = JSON.parse(fs.readFileSync(new URL('../package.json', import.meta.url), 'utf-8')).version;
|
|
653
|
+
renderer.banner({
|
|
654
|
+
version,
|
|
655
|
+
provider: config.provider || 'openai',
|
|
656
|
+
model: config.model,
|
|
657
|
+
browser: browserName,
|
|
658
|
+
testCount: cases.length,
|
|
659
|
+
concurrency,
|
|
660
|
+
mode: mode || undefined,
|
|
661
|
+
profile: driverConfig.profile,
|
|
662
|
+
adaptiveRouting: config.adaptiveModelRouting ? {
|
|
663
|
+
navProvider: config.navProvider || config.provider || 'openai',
|
|
664
|
+
navModel: config.navModel || 'gpt-4.1-mini',
|
|
665
|
+
} : undefined,
|
|
666
|
+
outputDir: sinkDir,
|
|
667
|
+
});
|
|
581
668
|
}
|
|
582
669
|
// Set up artifact sink
|
|
583
670
|
const sink = new FilesystemSink(path.resolve(sinkDir));
|
|
@@ -587,13 +674,17 @@ async function main() {
|
|
|
587
674
|
? path.resolve(driverConfig.storageState)
|
|
588
675
|
: undefined;
|
|
589
676
|
if (storageStatePath && !fs.existsSync(storageStatePath)) {
|
|
590
|
-
|
|
677
|
+
cliError(`storage state file not found: ${storageStatePath}`);
|
|
591
678
|
process.exit(1);
|
|
592
679
|
}
|
|
593
680
|
if (launchPlan.walletMode && browserName !== 'chromium') {
|
|
594
|
-
|
|
681
|
+
cliError('wallet mode currently supports Chromium only. Set --browser chromium.');
|
|
595
682
|
process.exit(1);
|
|
596
683
|
}
|
|
684
|
+
// Ensure clean exit on interrupt
|
|
685
|
+
process.on('SIGINT', () => { renderer?.destroy(); process.exit(130); });
|
|
686
|
+
process.on('SIGTERM', () => { renderer?.destroy(); process.exit(143); });
|
|
687
|
+
renderer?.launchStart(browserName);
|
|
597
688
|
// Set up browser
|
|
598
689
|
let browser;
|
|
599
690
|
let persistentContext;
|
|
@@ -732,7 +823,7 @@ async function main() {
|
|
|
732
823
|
accountsTimeoutMs: walletConfig.preflight?.accountsTimeoutMs,
|
|
733
824
|
maxChainSwitchAttempts: walletConfig.preflight?.maxChainSwitchAttempts,
|
|
734
825
|
chain: walletConfig.preflight?.chain,
|
|
735
|
-
log: quiet ? undefined : (message) =>
|
|
826
|
+
log: quiet ? undefined : (message) => cliLog('wallet', message),
|
|
736
827
|
});
|
|
737
828
|
if (!preflight.ok) {
|
|
738
829
|
const failed = preflight.results.find((resultEntry) => !resultEntry.ready);
|
|
@@ -847,6 +938,8 @@ async function main() {
|
|
|
847
938
|
screenshotQuality: 50,
|
|
848
939
|
disableCdp: driverConfig.disableCdp,
|
|
849
940
|
timeout: actionTimeout,
|
|
941
|
+
visionStrategy: config.visionStrategy,
|
|
942
|
+
screenshotInterval,
|
|
850
943
|
});
|
|
851
944
|
// Apply resource blocking if configured
|
|
852
945
|
const resourceBlockingStartedAt = Date.now();
|
|
@@ -889,13 +982,15 @@ async function main() {
|
|
|
889
982
|
if (concurrency <= 1) {
|
|
890
983
|
singleDriver = await driverFactory();
|
|
891
984
|
}
|
|
985
|
+
renderer?.launchDone();
|
|
892
986
|
const runner = new TestRunner({
|
|
893
987
|
config,
|
|
894
988
|
defaultTimeoutMs: timeoutMs,
|
|
895
989
|
driver: singleDriver,
|
|
896
990
|
driverFactory: concurrency > 1 ? driverFactory : undefined,
|
|
897
|
-
enableMemory:
|
|
991
|
+
enableMemory: memoryEnabled,
|
|
898
992
|
trajectoryStorePath: driverConfig.memory?.dir,
|
|
993
|
+
projectStore,
|
|
899
994
|
concurrency,
|
|
900
995
|
screenshotInterval,
|
|
901
996
|
artifactSink: sink,
|
|
@@ -904,32 +999,24 @@ async function main() {
|
|
|
904
999
|
console.log(JSON.stringify(event));
|
|
905
1000
|
return;
|
|
906
1001
|
}
|
|
907
|
-
if (
|
|
1002
|
+
if (!renderer)
|
|
908
1003
|
return;
|
|
909
1004
|
switch (event.type) {
|
|
1005
|
+
case 'suite:start':
|
|
1006
|
+
renderer.suiteStart(event.totalTests);
|
|
1007
|
+
break;
|
|
910
1008
|
case 'test:start':
|
|
911
|
-
|
|
1009
|
+
renderer.testStart(event.testId, event.testName);
|
|
912
1010
|
break;
|
|
913
1011
|
case 'test:turn':
|
|
914
|
-
|
|
915
|
-
const modelTag = event.modelUsed ? ` [${event.modelUsed}]` : '';
|
|
916
|
-
console.log(` turn ${event.turn}: ${event.action} (${event.durationMs}ms)${modelTag}`);
|
|
917
|
-
}
|
|
1012
|
+
renderer.testTurn(event.testId, event.turn, event.action, event.durationMs, event.modelUsed);
|
|
918
1013
|
break;
|
|
919
|
-
case 'test:complete':
|
|
920
|
-
|
|
921
|
-
console.log(` ${event.passed ? '✓' : '✗'} ${event.testId} — ${event.verdict.slice(0, 80)} (${event.turnsUsed} turns, ${Math.round(event.durationMs / 1000)}s${costStr})`);
|
|
1014
|
+
case 'test:complete':
|
|
1015
|
+
renderer.testComplete(event.testId, event.passed, event.verdict, event.turnsUsed, event.durationMs, event.estimatedCostUsd);
|
|
922
1016
|
break;
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
console.log('');
|
|
926
|
-
const suiteCostStr = event.totalCostUsd ? ` | $${event.totalCostUsd.toFixed(2)}` : '';
|
|
927
|
-
console.log(`Done: ${event.passed} passed, ${event.failed} failed, ${event.skipped} skipped (${Math.round(event.totalMs / 1000)}s${suiteCostStr})`);
|
|
928
|
-
if (event.manifestUri) {
|
|
929
|
-
console.log(`Artifacts: ${event.manifestUri}`);
|
|
930
|
-
}
|
|
1017
|
+
case 'suite:complete':
|
|
1018
|
+
renderer.suiteComplete(event.passed, event.failed, event.skipped, event.totalMs, event.totalCostUsd, event.manifestUri);
|
|
931
1019
|
break;
|
|
932
|
-
}
|
|
933
1020
|
}
|
|
934
1021
|
},
|
|
935
1022
|
});
|
|
@@ -956,9 +1043,7 @@ async function main() {
|
|
|
956
1043
|
const report = generateReport(result, { format, includeTurns: format === 'markdown' });
|
|
957
1044
|
const reportPath = path.join(reportDir, `report.${meta.ext}`);
|
|
958
1045
|
fs.writeFileSync(reportPath, report);
|
|
959
|
-
|
|
960
|
-
console.log(`Report: ${reportPath}`);
|
|
961
|
-
}
|
|
1046
|
+
renderer?.report(reportPath);
|
|
962
1047
|
}
|
|
963
1048
|
catch {
|
|
964
1049
|
// Report generation is best-effort
|
|
@@ -973,6 +1058,7 @@ async function main() {
|
|
|
973
1058
|
runError = err;
|
|
974
1059
|
}
|
|
975
1060
|
finally {
|
|
1061
|
+
renderer?.destroy();
|
|
976
1062
|
await singleDriver?.close?.().catch(() => { });
|
|
977
1063
|
stopWalletAutoApprover?.();
|
|
978
1064
|
await persistentContext?.close().catch(() => { });
|
|
@@ -981,145 +1067,12 @@ async function main() {
|
|
|
981
1067
|
if (runError) {
|
|
982
1068
|
throw runError;
|
|
983
1069
|
}
|
|
984
|
-
|
|
1070
|
+
const runLabel = values.cases
|
|
985
1071
|
? `${path.basename(values.cases)} · cli run`
|
|
986
|
-
: `${values.goal.slice(0, 80)} · cli run
|
|
1072
|
+
: `${(values.goal || values['resume-run'] || values['fork-run'] || 'run').slice(0, 80)} · cli run`;
|
|
1073
|
+
await syncLocalBenchmarkRun(path.resolve(sinkDir), runLabel);
|
|
987
1074
|
process.exit((result?.summary.failed ?? 1) > 0 ? 1 : 0);
|
|
988
1075
|
}
|
|
989
|
-
function printHelp() {
|
|
990
|
-
console.log(`
|
|
991
|
-
bad — LLM-driven browser automation CLI
|
|
992
|
-
|
|
993
|
-
USAGE:
|
|
994
|
-
bad run [options]
|
|
995
|
-
|
|
996
|
-
SINGLE TASK:
|
|
997
|
-
bad run --goal "Sign up for account" --url http://localhost:3000
|
|
998
|
-
bad run -g "Build a todo app" -u http://localhost:5173 -m claude-sonnet-4-20250514
|
|
999
|
-
bad run --goal "Create Coinbase blueprint and verify preview" --url https://ai.tangle.tools --persona alice-blueprint-builder
|
|
1000
|
-
bad run --goal "Create partner project and verify preview works" --url https://ai.tangle.tools --persona auto
|
|
1001
|
-
bad run --goal "Explore key routes quickly" --url https://example.com --mode fast-explore
|
|
1002
|
-
|
|
1003
|
-
TEST SUITE:
|
|
1004
|
-
bad run --cases ./cases.json --concurrency 4
|
|
1005
|
-
bad run --cases ./cases.json --sink ./results/ --model gpt-5.4
|
|
1006
|
-
|
|
1007
|
-
DESIGN AUDIT:
|
|
1008
|
-
bad design-audit --url https://stripe.com
|
|
1009
|
-
bad design-audit --url https://app.uniswap.org --profile defi
|
|
1010
|
-
bad design-audit --url http://localhost:3000 --profile saas --pages 10
|
|
1011
|
-
bad design-audit --url https://example.com --profile marketing --json
|
|
1012
|
-
|
|
1013
|
-
Profiles: general, saas, defi, marketing
|
|
1014
|
-
|
|
1015
|
-
DESIGN TOKEN EXTRACTION:
|
|
1016
|
-
bad design-audit --url https://stripe.com --extract-tokens
|
|
1017
|
-
bad design-audit --url https://app.example.com --extract-tokens --json
|
|
1018
|
-
|
|
1019
|
-
Extracts colors, typography, spacing, components, logos, icons,
|
|
1020
|
-
CSS custom properties, and brand assets at mobile/tablet/desktop viewports.
|
|
1021
|
-
Outputs tokens.json — no LLM calls, pure DOM extraction.
|
|
1022
|
-
|
|
1023
|
-
DOCKER:
|
|
1024
|
-
docker run -v ./cases.json:/data/cases.json -v ./out:/output \\
|
|
1025
|
-
bad run --cases /data/cases.json --sink /output/
|
|
1026
|
-
|
|
1027
|
-
OPTIONS:
|
|
1028
|
-
--config <path> Path to config file (default: auto-detect)
|
|
1029
|
-
-g, --goal <text> Natural language goal for single task
|
|
1030
|
-
-u, --url <url> Starting URL
|
|
1031
|
-
-c, --cases <file> JSON file with test cases array
|
|
1032
|
-
--allowed-domains <csv> Comma-separated host allowlist (e.g. www.nih.gov,docs.foo.com)
|
|
1033
|
-
--vision-strategy <m> Vision policy: always, never, auto
|
|
1034
|
-
-m, --model <name> LLM model (default: gpt-5.4)
|
|
1035
|
-
--provider <name> LLM provider: openai, anthropic, google, codex-cli, claude-code, sandbox-backend (default: openai)
|
|
1036
|
-
--model-adaptive Enable adaptive model routing for decide() turns
|
|
1037
|
-
--nav-model <name> Fast navigation model for adaptive routing
|
|
1038
|
-
--nav-provider <name> Provider for nav model (default: same as --provider)
|
|
1039
|
-
--persona <id> Append persona directive (${listPersonaIds().join(', ')})
|
|
1040
|
-
--mode <name> Mode preset: ${RUN_MODES.join(', ')}
|
|
1041
|
-
--profile <name> Execution profile: ${DRIVER_PROFILES.join(', ')}
|
|
1042
|
-
--prompt-file <path> Load system prompt from file for experimentable prompt variants
|
|
1043
|
-
--sandbox-backend-type <type> Native sidecar backend type for --provider sandbox-backend (e.g. claude-code, codex, opencode:with-web-search)
|
|
1044
|
-
--sandbox-backend-profile <id> Optional native backend profile/preset ID
|
|
1045
|
-
--sandbox-backend-provider <id> Optional native backend model provider override (mainly for opencode)
|
|
1046
|
-
--api-key <key> API key (or set OPENAI_API_KEY / ANTHROPIC_API_KEY; codex-cli can use \`codex login\`; claude-code can use \`claude login\`)
|
|
1047
|
-
--base-url <url> Custom LLM endpoint (e.g., LiteLLM proxy)
|
|
1048
|
-
--browser <name> Browser: chromium, firefox, webkit (default: chromium)
|
|
1049
|
-
--storage-state <file> Playwright storage state JSON for pre-authenticated session
|
|
1050
|
-
--concurrency <n> Parallel workers (default: 1)
|
|
1051
|
-
--max-turns <n> Max turns per test (default: 30)
|
|
1052
|
-
--llm-timeout <ms> Timeout per LLM call in milliseconds
|
|
1053
|
-
--retries <n> Retries for observe/LLM/action transient failures
|
|
1054
|
-
--retry-delay-ms <ms> Base retry backoff in milliseconds
|
|
1055
|
-
--screenshot-interval <n> Capture every N turns (default: 5)
|
|
1056
|
-
--scout Enable scout/ranker recommendations on ambiguous pages
|
|
1057
|
-
--scout-model <name> Model override for scout recommendations
|
|
1058
|
-
--scout-provider <name> Provider override for scout recommendations
|
|
1059
|
-
--scout-vision Let scout inspect screenshots when available
|
|
1060
|
-
--scout-max-candidates <n> Max visible link candidates sent to the scout
|
|
1061
|
-
--scout-min-top-score <n> Skip scout when the top deterministic score is already strong
|
|
1062
|
-
--scout-max-score-gap <n> Skip scout when the top-vs-second score gap is already wide
|
|
1063
|
-
--headless Run browser headless (default: true)
|
|
1064
|
-
--no-headless Show browser window
|
|
1065
|
-
--wallet Enable wallet mode (persistent Chromium profile)
|
|
1066
|
-
--extension <path> Load unpacked wallet/browser extension (repeatable)
|
|
1067
|
-
--user-data-dir <dir> Persistent profile directory for wallet sessions
|
|
1068
|
-
--wallet-auto-approve Enable extension prompt auto-approval (default: true in wallet mode)
|
|
1069
|
-
--wallet-password <v> Wallet unlock password for auto-approval/preflight
|
|
1070
|
-
--wallet-preflight Run wallet origin preflight before tests (default: true in wallet mode)
|
|
1071
|
-
--wallet-seed-url <u> Preflight URL to authorize/switch-chain (repeatable)
|
|
1072
|
-
--wallet-chain-id <n> Target chain ID for preflight switch/add
|
|
1073
|
-
--wallet-chain-rpc-url <u> RPC URL for preflight wallet_addEthereumChain
|
|
1074
|
-
--memory Enable trajectory memory reuse (default: on)
|
|
1075
|
-
--no-memory Disable trajectory memory
|
|
1076
|
-
--memory-dir <dir> Memory directory (default: .agent-memory)
|
|
1077
|
-
--timeout <ms> Per-test timeout in ms (default: 600000)
|
|
1078
|
-
-s, --sink <dir> Output directory for artifacts (default: ./agent-results)
|
|
1079
|
-
--json Output progress as JSON lines (for piping)
|
|
1080
|
-
-q, --quiet Suppress all output
|
|
1081
|
-
--quality-threshold <n> Min quality score 1-10 (default: 0 = skip)
|
|
1082
|
-
--trace-scoring Enable trajectory scoring for reference trace selection
|
|
1083
|
-
--trace-ttl-days <n> Retention window for scored traces (default: 30)
|
|
1084
|
-
--goal-verification Verify goal completion (default: true)
|
|
1085
|
-
--no-goal-verification Skip goal verification
|
|
1086
|
-
--vision Enable vision/screenshots (default: true)
|
|
1087
|
-
--no-vision Disable vision
|
|
1088
|
-
--block-analytics Block analytics/tracking scripts
|
|
1089
|
-
--block-images Block image loading
|
|
1090
|
-
--block-media Block media loading (video, audio)
|
|
1091
|
-
-d, --debug Enable debug logging
|
|
1092
|
-
-h, --help Show this help
|
|
1093
|
-
-v, --version Show version
|
|
1094
|
-
|
|
1095
|
-
TEST CASES JSON FORMAT:
|
|
1096
|
-
[
|
|
1097
|
-
{
|
|
1098
|
-
"id": "signup",
|
|
1099
|
-
"name": "User signup flow",
|
|
1100
|
-
"goal": "Create a new account with email test@example.com",
|
|
1101
|
-
"startUrl": "http://localhost:3000/signup",
|
|
1102
|
-
"maxTurns": 20,
|
|
1103
|
-
"timeoutMs": 300000,
|
|
1104
|
-
"successDescription": "Account created and redirected to dashboard"
|
|
1105
|
-
}
|
|
1106
|
-
]
|
|
1107
|
-
|
|
1108
|
-
ENVIRONMENT VARIABLES:
|
|
1109
|
-
OPENAI_API_KEY OpenAI API key
|
|
1110
|
-
ANTHROPIC_API_KEY Anthropic API key
|
|
1111
|
-
LLM_BASE_URL Custom LLM endpoint URL
|
|
1112
|
-
CODEX_CLI_PATH Optional Codex CLI binary path for --provider codex-cli
|
|
1113
|
-
CODEX_ALLOW_NPX Set to 0 to disable npx fallback for --provider codex-cli
|
|
1114
|
-
CLAUDE_CODE_CLI_PATH Optional Claude CLI binary path for --provider claude-code
|
|
1115
|
-
SANDBOX_BACKEND_TYPE Native backend type for --provider sandbox-backend
|
|
1116
|
-
SANDBOX_BACKEND_PROFILE Native backend profile/preset for --provider sandbox-backend
|
|
1117
|
-
SANDBOX_BACKEND_PROFILE_ID Legacy alias for SANDBOX_BACKEND_PROFILE
|
|
1118
|
-
SANDBOX_BACKEND_MODEL_PROVIDER Native backend provider override for --provider sandbox-backend
|
|
1119
|
-
SANDBOX_SIDECAR_URL Sidecar API URL for --provider sandbox-backend (default: http://127.0.0.1:$SIDECAR_PORT)
|
|
1120
|
-
SANDBOX_SIDECAR_AUTH_TOKEN Sidecar API bearer token for --provider sandbox-backend
|
|
1121
|
-
`);
|
|
1122
|
-
}
|
|
1123
1076
|
function parseAllowedDomains(value) {
|
|
1124
1077
|
if (!value)
|
|
1125
1078
|
return undefined;
|
|
@@ -1157,11 +1110,11 @@ async function syncLocalBenchmarkRun(outPath, label) {
|
|
|
1157
1110
|
if (process.env.ABD_BENCHMARK_SYNC_STRICT === '1') {
|
|
1158
1111
|
throw new Error(`abd-app benchmark import failed for ${outPath}`);
|
|
1159
1112
|
}
|
|
1160
|
-
|
|
1113
|
+
cliWarn(`abd-app benchmark import skipped after non-zero exit for ${outPath}`);
|
|
1161
1114
|
}
|
|
1162
1115
|
}
|
|
1163
1116
|
main().catch((err) => {
|
|
1164
|
-
|
|
1117
|
+
cliError(err instanceof Error ? err.message : String(err));
|
|
1165
1118
|
process.exit(1);
|
|
1166
1119
|
});
|
|
1167
1120
|
//# sourceMappingURL=cli.js.map
|