@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.
Files changed (75) hide show
  1. package/README.md +2 -2
  2. package/dist/brain/index.d.ts +24 -7
  3. package/dist/brain/index.d.ts.map +1 -1
  4. package/dist/brain/index.js +227 -93
  5. package/dist/brain/index.js.map +1 -1
  6. package/dist/captcha.d.ts +55 -0
  7. package/dist/captcha.d.ts.map +1 -0
  8. package/dist/captcha.js +403 -0
  9. package/dist/captcha.js.map +1 -0
  10. package/dist/cli-design-audit.d.ts.map +1 -1
  11. package/dist/cli-design-audit.js +81 -59
  12. package/dist/cli-design-audit.js.map +1 -1
  13. package/dist/cli-ui.d.ts +49 -0
  14. package/dist/cli-ui.d.ts.map +1 -0
  15. package/dist/cli-ui.js +395 -0
  16. package/dist/cli-ui.js.map +1 -0
  17. package/dist/cli.js +144 -191
  18. package/dist/cli.js.map +1 -1
  19. package/dist/config.d.ts +1 -1
  20. package/dist/config.d.ts.map +1 -1
  21. package/dist/config.js +1 -1
  22. package/dist/config.js.map +1 -1
  23. package/dist/design-audit.js +2 -2
  24. package/dist/design-audit.js.map +1 -1
  25. package/dist/drivers/cdp-snapshot.d.ts.map +1 -1
  26. package/dist/drivers/cdp-snapshot.js +2 -1
  27. package/dist/drivers/cdp-snapshot.js.map +1 -1
  28. package/dist/drivers/playwright.d.ts +6 -0
  29. package/dist/drivers/playwright.d.ts.map +1 -1
  30. package/dist/drivers/playwright.js +34 -9
  31. package/dist/drivers/playwright.js.map +1 -1
  32. package/dist/drivers/snapshot.d.ts +1 -0
  33. package/dist/drivers/snapshot.d.ts.map +1 -1
  34. package/dist/drivers/snapshot.js +12 -5
  35. package/dist/drivers/snapshot.js.map +1 -1
  36. package/dist/drivers/types.d.ts +2 -0
  37. package/dist/drivers/types.d.ts.map +1 -1
  38. package/dist/index.d.ts +7 -3
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +4 -1
  41. package/dist/index.js.map +1 -1
  42. package/dist/memory/knowledge.d.ts +25 -9
  43. package/dist/memory/knowledge.d.ts.map +1 -1
  44. package/dist/memory/knowledge.js +94 -28
  45. package/dist/memory/knowledge.js.map +1 -1
  46. package/dist/memory/run-registry.d.ts +85 -0
  47. package/dist/memory/run-registry.d.ts.map +1 -0
  48. package/dist/memory/run-registry.js +144 -0
  49. package/dist/memory/run-registry.js.map +1 -0
  50. package/dist/multi-actor.d.ts +3 -3
  51. package/dist/multi-actor.d.ts.map +1 -1
  52. package/dist/multi-actor.js +3 -3
  53. package/dist/multi-actor.js.map +1 -1
  54. package/dist/recovery.d.ts.map +1 -1
  55. package/dist/recovery.js +16 -35
  56. package/dist/recovery.js.map +1 -1
  57. package/dist/runner/index.d.ts +2 -2
  58. package/dist/runner/index.d.ts.map +1 -1
  59. package/dist/runner/index.js +1 -1
  60. package/dist/runner/index.js.map +1 -1
  61. package/dist/runner/runner.d.ts +10 -5
  62. package/dist/runner/runner.d.ts.map +1 -1
  63. package/dist/runner/runner.js +204 -120
  64. package/dist/runner/runner.js.map +1 -1
  65. package/dist/runner.d.ts +2 -2
  66. package/dist/runner.d.ts.map +1 -1
  67. package/dist/runner.js +1 -1
  68. package/dist/runner.js.map +1 -1
  69. package/dist/test-runner.d.ts +1 -0
  70. package/dist/test-runner.d.ts.map +1 -1
  71. package/dist/test-runner.js +26 -2
  72. package/dist/test-runner.js.map +1 -1
  73. package/dist/types.d.ts +19 -0
  74. package/dist/types.d.ts.map +1 -1
  75. 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
- printHelp();
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
- console.error('Error: --url is required for design-audit.');
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
- console.error(`Unknown command: ${command}. Use "run" or "design-audit".`);
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
- console.error('Error: provide --goal "..." --url "..." for a single task, or --cases ./cases.json for a suite.');
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
- console.error(`Error: unknown mode "${mode}". Valid modes: ${RUN_MODES.join(', ')}`);
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
- console.error(`Error: unknown profile "${profile}". Valid profiles: ${DRIVER_PROFILES.join(', ')}`);
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
- console.error(`Error: prompt file not found: ${promptPath}`);
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
- console.error(`Error: prompt file is empty: ${promptPath}`);
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
- console.warn(`Warning: ${warning}`);
498
+ cliWarn(warning);
461
499
  }
462
500
  }
463
501
  if (launchPlan.errors.length > 0) {
464
502
  for (const error of launchPlan.errors) {
465
- console.error(`Error: ${error}`);
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.cases) {
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
- console.error(`Error: unknown persona "${persona}". ` +
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
- if (!quiet && !values.json) {
568
- console.log(`bad v${JSON.parse(fs.readFileSync(new URL('../package.json', import.meta.url), 'utf-8')).version}`);
569
- console.log(`Model: ${config.provider}/${config.model} | Browser: ${browserName} | Tests: ${cases.length} | Concurrency: ${concurrency}`);
570
- if (mode)
571
- console.log(`Mode: ${mode}`);
572
- if (driverConfig.profile && driverConfig.profile !== 'default') {
573
- console.log(`Profile: ${driverConfig.profile}`);
574
- }
575
- if (config.adaptiveModelRouting) {
576
- const effectiveNavModel = config.navModel || 'gpt-4.1-mini';
577
- console.log(`Adaptive routing: ON (nav=${config.navProvider || config.provider}/${effectiveNavModel})`);
578
- }
579
- console.log(`Output: ${sinkDir}`);
580
- console.log('');
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
- console.error(`Error: storage state file not found: ${storageStatePath}`);
677
+ cliError(`storage state file not found: ${storageStatePath}`);
591
678
  process.exit(1);
592
679
  }
593
680
  if (launchPlan.walletMode && browserName !== 'chromium') {
594
- console.error('Error: wallet mode currently supports Chromium only. Set --browser chromium.');
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) => console.log(`[wallet] ${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: driverConfig.memory?.enabled === true,
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 (quiet)
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
- console.log(` ▶ ${event.testName}`);
1009
+ renderer.testStart(event.testId, event.testName);
912
1010
  break;
913
1011
  case 'test:turn':
914
- if (debug) {
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
- const costStr = event.estimatedCostUsd ? `, $${event.estimatedCostUsd.toFixed(3)}` : '';
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
- case 'suite:complete': {
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
- if (!quiet && !values.json) {
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
- await syncLocalBenchmarkRun(path.resolve(sinkDir), values.cases
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
- console.warn(`abd-app benchmark import skipped after non-zero exit for ${outPath}`);
1113
+ cliWarn(`abd-app benchmark import skipped after non-zero exit for ${outPath}`);
1161
1114
  }
1162
1115
  }
1163
1116
  main().catch((err) => {
1164
- console.error(err instanceof Error ? err.message : err);
1117
+ cliError(err instanceof Error ? err.message : String(err));
1165
1118
  process.exit(1);
1166
1119
  });
1167
1120
  //# sourceMappingURL=cli.js.map