agent-browser
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
# veb
|
# agent-browser
|
||||||
|
|
||||||
Headless browser automation CLI for agents and humans.
|
Headless browser automation CLI for AI agents.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
@@ -13,12 +13,12 @@ pnpm build
|
|||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
veb open example.com
|
agent-browser open example.com
|
||||||
veb click "#submit"
|
agent-browser click "#submit"
|
||||||
veb fill "#email" "test@example.com"
|
agent-browser fill "#email" "test@example.com"
|
||||||
veb get text "h1"
|
agent-browser get text "h1"
|
||||||
veb screenshot page.png
|
agent-browser screenshot page.png
|
||||||
veb close
|
agent-browser close
|
||||||
```
|
```
|
||||||
|
|
||||||
## Commands
|
## Commands
|
||||||
@@ -26,87 +26,87 @@ veb close
|
|||||||
### Core Commands
|
### Core Commands
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
veb open <url> # Navigate to URL
|
agent-browser open <url> # Navigate to URL
|
||||||
veb click <sel> # Click element
|
agent-browser click <sel> # Click element
|
||||||
veb type <sel> <text> # Type into element
|
agent-browser type <sel> <text> # Type into element
|
||||||
veb fill <sel> <text> # Clear and fill
|
agent-browser fill <sel> <text> # Clear and fill
|
||||||
veb press <key> # Press key (Enter, Tab, Control+a)
|
agent-browser press <key> # Press key (Enter, Tab, Control+a)
|
||||||
veb keydown <key> # Hold key down
|
agent-browser keydown <key> # Hold key down
|
||||||
veb keyup <key> # Release key
|
agent-browser keyup <key> # Release key
|
||||||
veb insert <text> # Insert text (no key events)
|
agent-browser insert <text> # Insert text (no key events)
|
||||||
veb hover <sel> # Hover element
|
agent-browser hover <sel> # Hover element
|
||||||
veb select <sel> <val> # Select dropdown option
|
agent-browser select <sel> <val> # Select dropdown option
|
||||||
veb multiselect <sel> <v1> <v2> # Multi-select
|
agent-browser multiselect <sel> <v1> <v2> # Multi-select
|
||||||
veb check <sel> # Check checkbox
|
agent-browser check <sel> # Check checkbox
|
||||||
veb uncheck <sel> # Uncheck checkbox
|
agent-browser uncheck <sel> # Uncheck checkbox
|
||||||
veb scroll <dir> [px] # Scroll (up/down/left/right)
|
agent-browser scroll <dir> [px] # Scroll (up/down/left/right)
|
||||||
veb scrollinto <sel> # Scroll element into view
|
agent-browser scrollinto <sel> # Scroll element into view
|
||||||
veb drag <src> <tgt> # Drag and drop
|
agent-browser drag <src> <tgt> # Drag and drop
|
||||||
veb upload <sel> <files> # Upload files
|
agent-browser upload <sel> <files> # Upload files
|
||||||
veb download [path] # Wait for download
|
agent-browser download [path] # Wait for download
|
||||||
veb screenshot [path] # Take screenshot (--full for full page)
|
agent-browser screenshot [path] # Take screenshot (--full for full page)
|
||||||
veb pdf <path> # Save as PDF
|
agent-browser pdf <path> # Save as PDF
|
||||||
veb snapshot # Accessibility tree (best for AI)
|
agent-browser snapshot # Accessibility tree (best for AI)
|
||||||
veb eval <js> # Run JavaScript
|
agent-browser eval <js> # Run JavaScript
|
||||||
veb close # Close browser
|
agent-browser close # Close browser
|
||||||
```
|
```
|
||||||
|
|
||||||
### Get Info
|
### Get Info
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
veb get text <sel> # Get text content
|
agent-browser get text <sel> # Get text content
|
||||||
veb get html <sel> # Get innerHTML
|
agent-browser get html <sel> # Get innerHTML
|
||||||
veb get value <sel> # Get input value
|
agent-browser get value <sel> # Get input value
|
||||||
veb get attr <sel> <attr> # Get attribute
|
agent-browser get attr <sel> <attr> # Get attribute
|
||||||
veb get title # Get page title
|
agent-browser get title # Get page title
|
||||||
veb get url # Get current URL
|
agent-browser get url # Get current URL
|
||||||
veb get count <sel> # Count matching elements
|
agent-browser get count <sel> # Count matching elements
|
||||||
veb get box <sel> # Get bounding box
|
agent-browser get box <sel> # Get bounding box
|
||||||
```
|
```
|
||||||
|
|
||||||
### Check State
|
### Check State
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
veb is visible <sel> # Check if visible
|
agent-browser is visible <sel> # Check if visible
|
||||||
veb is enabled <sel> # Check if enabled
|
agent-browser is enabled <sel> # Check if enabled
|
||||||
veb is checked <sel> # Check if checked
|
agent-browser is checked <sel> # Check if checked
|
||||||
```
|
```
|
||||||
|
|
||||||
### Find Elements (Semantic Locators)
|
### Find Elements (Semantic Locators)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
veb find role <role> <action> [value] # By ARIA role
|
agent-browser find role <role> <action> [value] # By ARIA role
|
||||||
veb find text <text> <action> # By text content
|
agent-browser find text <text> <action> # By text content
|
||||||
veb find label <label> <action> [value] # By label
|
agent-browser find label <label> <action> [value] # By label
|
||||||
veb find placeholder <ph> <action> [value] # By placeholder
|
agent-browser find placeholder <ph> <action> [value] # By placeholder
|
||||||
veb find alt <text> <action> # By alt text
|
agent-browser find alt <text> <action> # By alt text
|
||||||
veb find title <text> <action> # By title attr
|
agent-browser find title <text> <action> # By title attr
|
||||||
veb find testid <id> <action> [value] # By data-testid
|
agent-browser find testid <id> <action> [value] # By data-testid
|
||||||
veb find first <sel> <action> [value] # First match
|
agent-browser find first <sel> <action> [value] # First match
|
||||||
veb find last <sel> <action> [value] # Last match
|
agent-browser find last <sel> <action> [value] # Last match
|
||||||
veb find nth <n> <sel> <action> [value] # Nth match
|
agent-browser find nth <n> <sel> <action> [value] # Nth match
|
||||||
```
|
```
|
||||||
|
|
||||||
**Actions:** `click`, `fill`, `check`, `hover`, `text`
|
**Actions:** `click`, `fill`, `check`, `hover`, `text`
|
||||||
|
|
||||||
**Examples:**
|
**Examples:**
|
||||||
```bash
|
```bash
|
||||||
veb find role button click --name "Submit"
|
agent-browser find role button click --name "Submit"
|
||||||
veb find text "Sign In" click
|
agent-browser find text "Sign In" click
|
||||||
veb find label "Email" fill "test@test.com"
|
agent-browser find label "Email" fill "test@test.com"
|
||||||
veb find first ".item" click
|
agent-browser find first ".item" click
|
||||||
veb find nth 2 "a" text
|
agent-browser find nth 2 "a" text
|
||||||
```
|
```
|
||||||
|
|
||||||
### Wait
|
### Wait
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
veb wait <selector> # Wait for element
|
agent-browser wait <selector> # Wait for element
|
||||||
veb wait <ms> # Wait for time
|
agent-browser wait <ms> # Wait for time
|
||||||
veb wait --text "Welcome" # Wait for text
|
agent-browser wait --text "Welcome" # Wait for text
|
||||||
veb wait --url "**/dash" # Wait for URL pattern
|
agent-browser wait --url "**/dash" # Wait for URL pattern
|
||||||
veb wait --load networkidle # Wait for load state
|
agent-browser wait --load networkidle # Wait for load state
|
||||||
veb wait --fn "window.ready === true" # Wait for JS condition
|
agent-browser wait --fn "window.ready === true" # Wait for JS condition
|
||||||
```
|
```
|
||||||
|
|
||||||
**Load states:** `load`, `domcontentloaded`, `networkidle`
|
**Load states:** `load`, `domcontentloaded`, `networkidle`
|
||||||
@@ -114,109 +114,109 @@ veb wait --fn "window.ready === true" # Wait for JS condition
|
|||||||
### Mouse Control
|
### Mouse Control
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
veb mouse move <x> <y> # Move mouse
|
agent-browser mouse move <x> <y> # Move mouse
|
||||||
veb mouse down [button] # Press button (left/right/middle)
|
agent-browser mouse down [button] # Press button (left/right/middle)
|
||||||
veb mouse up [button] # Release button
|
agent-browser mouse up [button] # Release button
|
||||||
veb mouse wheel <dy> [dx] # Scroll wheel
|
agent-browser mouse wheel <dy> [dx] # Scroll wheel
|
||||||
```
|
```
|
||||||
|
|
||||||
### Browser Settings
|
### Browser Settings
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
veb set viewport <w> <h> # Set viewport size
|
agent-browser set viewport <w> <h> # Set viewport size
|
||||||
veb set device <name> # Emulate device ("iPhone 14")
|
agent-browser set device <name> # Emulate device ("iPhone 14")
|
||||||
veb set geo <lat> <lng> # Set geolocation
|
agent-browser set geo <lat> <lng> # Set geolocation
|
||||||
veb set offline [on|off] # Toggle offline mode
|
agent-browser set offline [on|off] # Toggle offline mode
|
||||||
veb set headers <json> # Extra HTTP headers
|
agent-browser set headers <json> # Extra HTTP headers
|
||||||
veb set credentials <u> <p> # HTTP basic auth
|
agent-browser set credentials <u> <p> # HTTP basic auth
|
||||||
veb set media [dark|light|print] # Emulate media
|
agent-browser set media [dark|light|print] # Emulate media
|
||||||
```
|
```
|
||||||
|
|
||||||
### Cookies & Storage
|
### Cookies & Storage
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
veb cookies # Get all cookies
|
agent-browser cookies # Get all cookies
|
||||||
veb cookies set <json> # Set cookies
|
agent-browser cookies set <json> # Set cookies
|
||||||
veb cookies clear # Clear cookies
|
agent-browser cookies clear # Clear cookies
|
||||||
|
|
||||||
veb storage local # Get all localStorage
|
agent-browser storage local # Get all localStorage
|
||||||
veb storage local <key> # Get specific key
|
agent-browser storage local <key> # Get specific key
|
||||||
veb storage local set <k> <v> # Set value
|
agent-browser storage local set <k> <v> # Set value
|
||||||
veb storage local clear # Clear all
|
agent-browser storage local clear # Clear all
|
||||||
|
|
||||||
veb storage session # Same for sessionStorage
|
agent-browser storage session # Same for sessionStorage
|
||||||
```
|
```
|
||||||
|
|
||||||
### Network
|
### Network
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
veb network route <url> # Intercept requests
|
agent-browser network route <url> # Intercept requests
|
||||||
veb network route <url> --abort # Block requests
|
agent-browser network route <url> --abort # Block requests
|
||||||
veb network route <url> --body <json> # Mock response
|
agent-browser network route <url> --body <json> # Mock response
|
||||||
veb network unroute [url] # Remove routes
|
agent-browser network unroute [url] # Remove routes
|
||||||
veb network requests # View tracked requests
|
agent-browser network requests # View tracked requests
|
||||||
veb network requests --filter api # Filter requests
|
agent-browser network requests --filter api # Filter requests
|
||||||
veb response <url> # Get response body (waits for matching request)
|
agent-browser response <url> # Get response body (waits for matching request)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Tabs & Windows
|
### Tabs & Windows
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
veb tab # List tabs
|
agent-browser tab # List tabs
|
||||||
veb tab new # New tab
|
agent-browser tab new # New tab
|
||||||
veb tab <n> # Switch to tab n
|
agent-browser tab <n> # Switch to tab n
|
||||||
veb tab close [n] # Close tab
|
agent-browser tab close [n] # Close tab
|
||||||
veb window new # New window
|
agent-browser window new # New window
|
||||||
```
|
```
|
||||||
|
|
||||||
### Frames
|
### Frames
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
veb frame <sel> # Switch to iframe
|
agent-browser frame <sel> # Switch to iframe
|
||||||
veb frame main # Back to main frame
|
agent-browser frame main # Back to main frame
|
||||||
```
|
```
|
||||||
|
|
||||||
### Dialogs
|
### Dialogs
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
veb dialog accept [text] # Accept (with optional prompt text)
|
agent-browser dialog accept [text] # Accept (with optional prompt text)
|
||||||
veb dialog dismiss # Dismiss
|
agent-browser dialog dismiss # Dismiss
|
||||||
```
|
```
|
||||||
|
|
||||||
### Debug
|
### Debug
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
veb trace start # Start recording trace
|
agent-browser trace start # Start recording trace
|
||||||
veb trace stop <path> # Stop and save trace
|
agent-browser trace stop <path> # Stop and save trace
|
||||||
veb console # View console messages
|
agent-browser console # View console messages
|
||||||
veb console --clear # Clear console
|
agent-browser console --clear # Clear console
|
||||||
veb errors # View page errors
|
agent-browser errors # View page errors
|
||||||
veb highlight <sel> # Highlight element
|
agent-browser highlight <sel> # Highlight element
|
||||||
veb state save <path> # Save auth state
|
agent-browser state save <path> # Save auth state
|
||||||
veb state load <path> # Load auth state
|
agent-browser state load <path> # Load auth state
|
||||||
veb initscript <js> # Run JS on every page load
|
agent-browser initscript <js> # Run JS on every page load
|
||||||
```
|
```
|
||||||
|
|
||||||
### Navigation
|
### Navigation
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
veb back # Go back
|
agent-browser back # Go back
|
||||||
veb forward # Go forward
|
agent-browser forward # Go forward
|
||||||
veb reload # Reload page
|
agent-browser reload # Reload page
|
||||||
```
|
```
|
||||||
|
|
||||||
### Sessions
|
### Sessions
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
veb session # Show current session
|
agent-browser session # Show current session
|
||||||
veb session list # List active sessions
|
agent-browser session list # List active sessions
|
||||||
```
|
```
|
||||||
|
|
||||||
## Options
|
## Options
|
||||||
|
|
||||||
| Option | Description |
|
| Option | Description |
|
||||||
|--------|-------------|
|
|--------|-------------|
|
||||||
| `--session <name>` | Use isolated session (or `VEB_SESSION` env) |
|
| `--session <name>` | Use isolated session (or `AGENT_BROWSER_SESSION` env) |
|
||||||
| `--json` | JSON output (for agents) |
|
| `--json` | JSON output (for agents) |
|
||||||
| `--full, -f` | Full page screenshot |
|
| `--full, -f` | Full page screenshot |
|
||||||
| `--name, -n` | Locator name filter |
|
| `--name, -n` | Locator name filter |
|
||||||
@@ -229,33 +229,33 @@ Run multiple isolated browser instances:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Different sessions
|
# Different sessions
|
||||||
veb --session agent1 open site-a.com
|
agent-browser --session agent1 open site-a.com
|
||||||
veb --session agent2 open site-b.com
|
agent-browser --session agent2 open site-b.com
|
||||||
|
|
||||||
# Or via environment
|
# Or via environment
|
||||||
VEB_SESSION=agent1 veb click "#btn"
|
AGENT_BROWSER_SESSION=agent1 agent-browser click "#btn"
|
||||||
|
|
||||||
# List all
|
# List all
|
||||||
veb session list
|
agent-browser session list
|
||||||
```
|
```
|
||||||
|
|
||||||
## Selectors
|
## Selectors
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# CSS
|
# CSS
|
||||||
veb click "#id"
|
agent-browser click "#id"
|
||||||
veb click ".class"
|
agent-browser click ".class"
|
||||||
veb click "div > button"
|
agent-browser click "div > button"
|
||||||
|
|
||||||
# Text
|
# Text
|
||||||
veb click "text=Submit"
|
agent-browser click "text=Submit"
|
||||||
|
|
||||||
# XPath
|
# XPath
|
||||||
veb click "xpath=//button"
|
agent-browser click "xpath=//button"
|
||||||
|
|
||||||
# Semantic (recommended)
|
# Semantic (recommended)
|
||||||
veb find role button click --name "Submit"
|
agent-browser find role button click --name "Submit"
|
||||||
veb find label "Email" fill "test@test.com"
|
agent-browser find label "Email" fill "test@test.com"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Agent Mode
|
## Agent Mode
|
||||||
@@ -263,9 +263,9 @@ veb find label "Email" fill "test@test.com"
|
|||||||
Use `--json` for machine-readable output:
|
Use `--json` for machine-readable output:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
veb snapshot --json
|
agent-browser snapshot --json
|
||||||
veb get text "h1" --json
|
agent-browser get text "h1" --json
|
||||||
veb is visible ".modal" --json
|
agent-browser is visible ".modal" --json
|
||||||
```
|
```
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|||||||
+3
-3
@@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "veb",
|
"name": "agent-browser",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "Headless browser automation CLI for agents and humans",
|
"description": "Headless browser automation CLI for AI agents",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"bin": {
|
"bin": {
|
||||||
"veb": "./bin/veb"
|
"agent-browser": "./bin/agent-browser"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
|
|||||||
+1
-1
@@ -55,7 +55,7 @@ export async function ensureDaemon(): Promise<void> {
|
|||||||
const child = spawn(process.execPath, [daemonPath], {
|
const child = spawn(process.execPath, [daemonPath], {
|
||||||
detached: true,
|
detached: true,
|
||||||
stdio: 'ignore',
|
stdio: 'ignore',
|
||||||
env: { ...process.env, VEB_DAEMON: '1', VEB_SESSION: session },
|
env: { ...process.env, AGENT_BROWSER_DAEMON: '1', AGENT_BROWSER_SESSION: session },
|
||||||
});
|
});
|
||||||
child.unref();
|
child.unref();
|
||||||
|
|
||||||
|
|||||||
+4
-4
@@ -7,7 +7,7 @@ import { parseCommand, serializeResponse, errorResponse } from './protocol.js';
|
|||||||
import { executeCommand } from './actions.js';
|
import { executeCommand } from './actions.js';
|
||||||
|
|
||||||
// Session support - each session gets its own socket/pid
|
// Session support - each session gets its own socket/pid
|
||||||
let currentSession = process.env.VEB_SESSION || 'default';
|
let currentSession = process.env.AGENT_BROWSER_SESSION || 'default';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the current session
|
* Set the current session
|
||||||
@@ -28,7 +28,7 @@ export function getSession(): string {
|
|||||||
*/
|
*/
|
||||||
export function getSocketPath(session?: string): string {
|
export function getSocketPath(session?: string): string {
|
||||||
const sess = session ?? currentSession;
|
const sess = session ?? currentSession;
|
||||||
return path.join(os.tmpdir(), `veb-${sess}.sock`);
|
return path.join(os.tmpdir(), `agent-browser-${sess}.sock`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -36,7 +36,7 @@ export function getSocketPath(session?: string): string {
|
|||||||
*/
|
*/
|
||||||
export function getPidFile(session?: string): string {
|
export function getPidFile(session?: string): string {
|
||||||
const sess = session ?? currentSession;
|
const sess = session ?? currentSession;
|
||||||
return path.join(os.tmpdir(), `veb-${sess}.pid`);
|
return path.join(os.tmpdir(), `agent-browser-${sess}.pid`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -178,7 +178,7 @@ export async function startDaemon(): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Run daemon if this is the entry point
|
// Run daemon if this is the entry point
|
||||||
if (process.argv[1]?.endsWith('daemon.js') || process.env.VEB_DAEMON === '1') {
|
if (process.argv[1]?.endsWith('daemon.js') || process.env.AGENT_BROWSER_DAEMON === '1') {
|
||||||
startDaemon().catch((err) => {
|
startDaemon().catch((err) => {
|
||||||
console.error('Daemon error:', err);
|
console.error('Daemon error:', err);
|
||||||
cleanupSocket();
|
cleanupSocket();
|
||||||
|
|||||||
+59
-59
@@ -15,7 +15,7 @@ function listSessions(): string[] {
|
|||||||
const files = fs.readdirSync(tmpDir);
|
const files = fs.readdirSync(tmpDir);
|
||||||
const sessions: string[] = [];
|
const sessions: string[] = [];
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
const match = file.match(/^veb-(.+)\.pid$/);
|
const match = file.match(/^agent-browser-(.+)\.pid$/);
|
||||||
if (match) {
|
if (match) {
|
||||||
const pidFile = path.join(tmpDir, file);
|
const pidFile = path.join(tmpDir, file);
|
||||||
try {
|
try {
|
||||||
@@ -60,9 +60,9 @@ function err(msg: string): never {
|
|||||||
|
|
||||||
function printHelp(): void {
|
function printHelp(): void {
|
||||||
console.log(`
|
console.log(`
|
||||||
${c('bold', 'veb')} - headless browser automation
|
${c('bold', 'agent-browser')} - headless browser automation for AI agents
|
||||||
|
|
||||||
${c('yellow', 'Usage:')} veb <command> [options]
|
${c('yellow', 'Usage:')} agent-browser <command> [options]
|
||||||
|
|
||||||
${c('yellow', 'Core Commands:')}
|
${c('yellow', 'Core Commands:')}
|
||||||
${c('cyan', 'open')} <url> Navigate to URL
|
${c('cyan', 'open')} <url> Navigate to URL
|
||||||
@@ -79,26 +79,26 @@ ${c('yellow', 'Core Commands:')}
|
|||||||
${c('cyan', 'eval')} <js> Run JavaScript
|
${c('cyan', 'eval')} <js> Run JavaScript
|
||||||
${c('cyan', 'close')} Close browser
|
${c('cyan', 'close')} Close browser
|
||||||
|
|
||||||
${c('yellow', 'Get Info:')} veb get <what> [selector]
|
${c('yellow', 'Get Info:')} agent-browser get <what> [selector]
|
||||||
text, html, value, attr, title, url, count, box
|
text, html, value, attr, title, url, count, box
|
||||||
|
|
||||||
${c('yellow', 'Check State:')} veb is <what> <selector>
|
${c('yellow', 'Check State:')} agent-browser is <what> <selector>
|
||||||
visible, enabled, checked
|
visible, enabled, checked
|
||||||
|
|
||||||
${c('yellow', 'Find Elements:')} veb find <locator> <action> [value]
|
${c('yellow', 'Find Elements:')} agent-browser find <locator> <action> [value]
|
||||||
role, text, label, placeholder, alt, title, testid, first, last, nth
|
role, text, label, placeholder, alt, title, testid, first, last, nth
|
||||||
|
|
||||||
${c('yellow', 'Mouse:')} veb mouse <action> [args]
|
${c('yellow', 'Mouse:')} agent-browser mouse <action> [args]
|
||||||
move <x> <y>, down, up, wheel <dy>
|
move <x> <y>, down, up, wheel <dy>
|
||||||
|
|
||||||
${c('yellow', 'Storage:')}
|
${c('yellow', 'Storage:')}
|
||||||
${c('cyan', 'cookies')} [get|set|clear] Manage cookies
|
${c('cyan', 'cookies')} [get|set|clear] Manage cookies
|
||||||
${c('cyan', 'storage')} <local|session> Manage web storage
|
${c('cyan', 'storage')} <local|session> Manage web storage
|
||||||
|
|
||||||
${c('yellow', 'Browser:')} veb set <setting> [value]
|
${c('yellow', 'Browser:')} agent-browser set <setting> [value]
|
||||||
viewport, device, geo, offline, headers, credentials
|
viewport, device, geo, offline, headers, credentials
|
||||||
|
|
||||||
${c('yellow', 'Network:')} veb network <action>
|
${c('yellow', 'Network:')} agent-browser network <action>
|
||||||
route, unroute, requests
|
route, unroute, requests
|
||||||
|
|
||||||
${c('yellow', 'Tabs:')}
|
${c('yellow', 'Tabs:')}
|
||||||
@@ -110,21 +110,21 @@ ${c('yellow', 'Debug:')}
|
|||||||
${c('cyan', 'errors')} View page errors
|
${c('cyan', 'errors')} View page errors
|
||||||
|
|
||||||
${c('yellow', 'Options:')}
|
${c('yellow', 'Options:')}
|
||||||
--session <name> Isolated session (or VEB_SESSION env)
|
--session <name> Isolated session (or AGENT_BROWSER_SESSION env)
|
||||||
--json JSON output
|
--json JSON output
|
||||||
--full, -f Full page screenshot
|
--full, -f Full page screenshot
|
||||||
--headed Show browser window (not headless)
|
--headed Show browser window (not headless)
|
||||||
--debug Debug output
|
--debug Debug output
|
||||||
|
|
||||||
${c('yellow', 'Examples:')}
|
${c('yellow', 'Examples:')}
|
||||||
veb open example.com
|
agent-browser open example.com
|
||||||
veb click "#submit"
|
agent-browser click "#submit"
|
||||||
veb fill "#email" "test@example.com"
|
agent-browser fill "#email" "test@example.com"
|
||||||
veb get text "h1"
|
agent-browser get text "h1"
|
||||||
veb is visible ".modal"
|
agent-browser is visible ".modal"
|
||||||
veb find role button click --name Submit
|
agent-browser find role button click --name Submit
|
||||||
veb wait 2000
|
agent-browser wait 2000
|
||||||
veb wait --load networkidle
|
agent-browser wait --load networkidle
|
||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,29 +267,29 @@ async function handleGet(args: string[], id: string): Promise<Record<string, unk
|
|||||||
|
|
||||||
switch (what) {
|
switch (what) {
|
||||||
case 'text':
|
case 'text':
|
||||||
if (!selector) err('Selector required: veb get text <selector>');
|
if (!selector) err('Selector required: agent-browser get text <selector>');
|
||||||
return { id, action: 'gettext', selector };
|
return { id, action: 'gettext', selector };
|
||||||
case 'html':
|
case 'html':
|
||||||
if (!selector) err('Selector required: veb get html <selector>');
|
if (!selector) err('Selector required: agent-browser get html <selector>');
|
||||||
return { id, action: 'innerhtml', selector };
|
return { id, action: 'innerhtml', selector };
|
||||||
case 'value':
|
case 'value':
|
||||||
if (!selector) err('Selector required: veb get value <selector>');
|
if (!selector) err('Selector required: agent-browser get value <selector>');
|
||||||
return { id, action: 'inputvalue', selector };
|
return { id, action: 'inputvalue', selector };
|
||||||
case 'attr':
|
case 'attr':
|
||||||
if (!selector || !args[2]) err('Usage: veb get attr <selector> <attribute>');
|
if (!selector || !args[2]) err('Usage: agent-browser get attr <selector> <attribute>');
|
||||||
return { id, action: 'getattribute', selector, attribute: args[2] };
|
return { id, action: 'getattribute', selector, attribute: args[2] };
|
||||||
case 'title':
|
case 'title':
|
||||||
return { id, action: 'title' };
|
return { id, action: 'title' };
|
||||||
case 'url':
|
case 'url':
|
||||||
return { id, action: 'url' };
|
return { id, action: 'url' };
|
||||||
case 'count':
|
case 'count':
|
||||||
if (!selector) err('Selector required: veb get count <selector>');
|
if (!selector) err('Selector required: agent-browser get count <selector>');
|
||||||
return { id, action: 'count', selector };
|
return { id, action: 'count', selector };
|
||||||
case 'box':
|
case 'box':
|
||||||
if (!selector) err('Selector required: veb get box <selector>');
|
if (!selector) err('Selector required: agent-browser get box <selector>');
|
||||||
return { id, action: 'boundingbox', selector };
|
return { id, action: 'boundingbox', selector };
|
||||||
default:
|
default:
|
||||||
err(`Unknown: veb get ${what}. Options: text, html, value, attr, title, url, count, box`);
|
err(`Unknown: agent-browser get ${what}. Options: text, html, value, attr, title, url, count, box`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -297,7 +297,7 @@ async function handleIs(args: string[], id: string): Promise<Record<string, unkn
|
|||||||
const what = args[0];
|
const what = args[0];
|
||||||
const selector = args[1];
|
const selector = args[1];
|
||||||
|
|
||||||
if (!selector) err(`Selector required: veb is ${what} <selector>`);
|
if (!selector) err(`Selector required: agent-browser is ${what} <selector>`);
|
||||||
|
|
||||||
switch (what) {
|
switch (what) {
|
||||||
case 'visible':
|
case 'visible':
|
||||||
@@ -307,7 +307,7 @@ async function handleIs(args: string[], id: string): Promise<Record<string, unkn
|
|||||||
case 'checked':
|
case 'checked':
|
||||||
return { id, action: 'ischecked', selector };
|
return { id, action: 'ischecked', selector };
|
||||||
default:
|
default:
|
||||||
err(`Unknown: veb is ${what}. Options: visible, enabled, checked`);
|
err(`Unknown: agent-browser is ${what}. Options: visible, enabled, checked`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,7 +321,7 @@ async function handleFind(
|
|||||||
const subaction = args[2] || 'click';
|
const subaction = args[2] || 'click';
|
||||||
const fillValue = args[3];
|
const fillValue = args[3];
|
||||||
|
|
||||||
if (!value) err(`Value required: veb find ${locator} <value> <action>`);
|
if (!value) err(`Value required: agent-browser find ${locator} <value> <action>`);
|
||||||
|
|
||||||
const exact = flags.exact;
|
const exact = flags.exact;
|
||||||
const name = flags.name;
|
const name = flags.name;
|
||||||
@@ -357,7 +357,7 @@ async function handleFind(
|
|||||||
const sel = args[2];
|
const sel = args[2];
|
||||||
const act = args[3] || 'click';
|
const act = args[3] || 'click';
|
||||||
const val = args[4];
|
const val = args[4];
|
||||||
if (isNaN(idx) || !sel) err('Usage: veb find nth <index> <selector> <action>');
|
if (isNaN(idx) || !sel) err('Usage: agent-browser find nth <index> <selector> <action>');
|
||||||
return { id, action: 'nth', selector: sel, index: idx, subaction: act, value: val };
|
return { id, action: 'nth', selector: sel, index: idx, subaction: act, value: val };
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@@ -374,7 +374,7 @@ async function handleMouse(args: string[], id: string): Promise<Record<string, u
|
|||||||
case 'move': {
|
case 'move': {
|
||||||
const x = parseInt(args[1], 10);
|
const x = parseInt(args[1], 10);
|
||||||
const y = parseInt(args[2], 10);
|
const y = parseInt(args[2], 10);
|
||||||
if (isNaN(x) || isNaN(y)) err('Usage: veb mouse move <x> <y>');
|
if (isNaN(x) || isNaN(y)) err('Usage: agent-browser mouse move <x> <y>');
|
||||||
return { id, action: 'mousemove', x, y };
|
return { id, action: 'mousemove', x, y };
|
||||||
}
|
}
|
||||||
case 'down':
|
case 'down':
|
||||||
@@ -387,7 +387,7 @@ async function handleMouse(args: string[], id: string): Promise<Record<string, u
|
|||||||
return { id, action: 'wheel', deltaY: dy, deltaX: dx };
|
return { id, action: 'wheel', deltaY: dy, deltaX: dx };
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
err(`Unknown: veb mouse ${action}. Options: move, down, up, wheel`);
|
err(`Unknown: agent-browser mouse ${action}. Options: move, down, up, wheel`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -398,23 +398,23 @@ async function handleSet(args: string[], id: string): Promise<Record<string, unk
|
|||||||
case 'viewport': {
|
case 'viewport': {
|
||||||
const w = parseInt(args[1], 10);
|
const w = parseInt(args[1], 10);
|
||||||
const h = parseInt(args[2], 10);
|
const h = parseInt(args[2], 10);
|
||||||
if (isNaN(w) || isNaN(h)) err('Usage: veb set viewport <width> <height>');
|
if (isNaN(w) || isNaN(h)) err('Usage: agent-browser set viewport <width> <height>');
|
||||||
return { id, action: 'viewport', width: w, height: h };
|
return { id, action: 'viewport', width: w, height: h };
|
||||||
}
|
}
|
||||||
case 'device':
|
case 'device':
|
||||||
if (!args[1]) err('Usage: veb set device <name>');
|
if (!args[1]) err('Usage: agent-browser set device <name>');
|
||||||
return { id, action: 'device', device: args[1] };
|
return { id, action: 'device', device: args[1] };
|
||||||
case 'geo':
|
case 'geo':
|
||||||
case 'geolocation': {
|
case 'geolocation': {
|
||||||
const lat = parseFloat(args[1]);
|
const lat = parseFloat(args[1]);
|
||||||
const lng = parseFloat(args[2]);
|
const lng = parseFloat(args[2]);
|
||||||
if (isNaN(lat) || isNaN(lng)) err('Usage: veb set geo <lat> <lng>');
|
if (isNaN(lat) || isNaN(lng)) err('Usage: agent-browser set geo <lat> <lng>');
|
||||||
return { id, action: 'geolocation', latitude: lat, longitude: lng };
|
return { id, action: 'geolocation', latitude: lat, longitude: lng };
|
||||||
}
|
}
|
||||||
case 'offline':
|
case 'offline':
|
||||||
return { id, action: 'offline', offline: args[1] !== 'off' && args[1] !== 'false' };
|
return { id, action: 'offline', offline: args[1] !== 'off' && args[1] !== 'false' };
|
||||||
case 'headers':
|
case 'headers':
|
||||||
if (!args[1]) err('Usage: veb set headers <json>');
|
if (!args[1]) err('Usage: agent-browser set headers <json>');
|
||||||
try {
|
try {
|
||||||
return { id, action: 'headers', headers: JSON.parse(args[1]) };
|
return { id, action: 'headers', headers: JSON.parse(args[1]) };
|
||||||
} catch {
|
} catch {
|
||||||
@@ -423,7 +423,7 @@ async function handleSet(args: string[], id: string): Promise<Record<string, unk
|
|||||||
break;
|
break;
|
||||||
case 'credentials':
|
case 'credentials':
|
||||||
case 'auth':
|
case 'auth':
|
||||||
if (!args[1] || !args[2]) err('Usage: veb set credentials <user> <pass>');
|
if (!args[1] || !args[2]) err('Usage: agent-browser set credentials <user> <pass>');
|
||||||
return { id, action: 'credentials', username: args[1], password: args[2] };
|
return { id, action: 'credentials', username: args[1], password: args[2] };
|
||||||
case 'media': {
|
case 'media': {
|
||||||
const colorScheme = args.includes('dark')
|
const colorScheme = args.includes('dark')
|
||||||
@@ -440,7 +440,7 @@ async function handleSet(args: string[], id: string): Promise<Record<string, unk
|
|||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
err(
|
err(
|
||||||
`Unknown: veb set ${setting}. Options: viewport, device, geo, offline, headers, credentials, media`
|
`Unknown: agent-browser set ${setting}. Options: viewport, device, geo, offline, headers, credentials, media`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
@@ -456,7 +456,7 @@ async function handleNetwork(
|
|||||||
switch (action) {
|
switch (action) {
|
||||||
case 'route': {
|
case 'route': {
|
||||||
const url = args[1];
|
const url = args[1];
|
||||||
if (!url) err('Usage: veb network route <url> [--abort|--body <json>]');
|
if (!url) err('Usage: agent-browser network route <url> [--abort|--body <json>]');
|
||||||
const abort = allArgs.includes('--abort');
|
const abort = allArgs.includes('--abort');
|
||||||
const bodyIdx = allArgs.indexOf('--body');
|
const bodyIdx = allArgs.indexOf('--body');
|
||||||
const body = bodyIdx !== -1 ? allArgs[bodyIdx + 1] : undefined;
|
const body = bodyIdx !== -1 ? allArgs[bodyIdx + 1] : undefined;
|
||||||
@@ -477,7 +477,7 @@ async function handleNetwork(
|
|||||||
return { id, action: 'requests', clear, filter };
|
return { id, action: 'requests', clear, filter };
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
err(`Unknown: veb network ${action}. Options: route, unroute, requests`);
|
err(`Unknown: agent-browser network ${action}. Options: route, unroute, requests`);
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@@ -487,11 +487,11 @@ async function handleStorage(args: string[], id: string): Promise<Record<string,
|
|||||||
const sub = args[1];
|
const sub = args[1];
|
||||||
|
|
||||||
if (type !== 'local' && type !== 'session') {
|
if (type !== 'local' && type !== 'session') {
|
||||||
err('Usage: veb storage <local|session> [get|set|clear] [key] [value]');
|
err('Usage: agent-browser storage <local|session> [get|set|clear] [key] [value]');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sub === 'set') {
|
if (sub === 'set') {
|
||||||
if (!args[2] || !args[3]) err(`Usage: veb storage ${type} set <key> <value>`);
|
if (!args[2] || !args[3]) err(`Usage: agent-browser storage ${type} set <key> <value>`);
|
||||||
return { id, action: 'storage_set', type, key: args[2], value: args[3] };
|
return { id, action: 'storage_set', type, key: args[2], value: args[3] };
|
||||||
} else if (sub === 'clear') {
|
} else if (sub === 'clear') {
|
||||||
return { id, action: 'storage_clear', type };
|
return { id, action: 'storage_clear', type };
|
||||||
@@ -505,7 +505,7 @@ async function handleCookies(args: string[], id: string): Promise<Record<string,
|
|||||||
const sub = args[0];
|
const sub = args[0];
|
||||||
|
|
||||||
if (sub === 'set') {
|
if (sub === 'set') {
|
||||||
if (!args[1]) err('Usage: veb cookies set <json>');
|
if (!args[1]) err('Usage: agent-browser cookies set <json>');
|
||||||
try {
|
try {
|
||||||
return { id, action: 'cookies_set', cookies: JSON.parse(args[1]) };
|
return { id, action: 'cookies_set', cookies: JSON.parse(args[1]) };
|
||||||
} catch {
|
} catch {
|
||||||
@@ -531,7 +531,7 @@ async function handleTab(args: string[], id: string): Promise<Record<string, unk
|
|||||||
return { id, action: 'tab_close', index: idx };
|
return { id, action: 'tab_close', index: idx };
|
||||||
} else {
|
} else {
|
||||||
const idx = parseInt(sub, 10);
|
const idx = parseInt(sub, 10);
|
||||||
if (isNaN(idx)) err(`Unknown: veb tab ${sub}. Options: new, list, close, <index>`);
|
if (isNaN(idx)) err(`Unknown: agent-browser tab ${sub}. Options: new, list, close, <index>`);
|
||||||
return { id, action: 'tab_switch', index: idx };
|
return { id, action: 'tab_switch', index: idx };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -542,10 +542,10 @@ async function handleTrace(args: string[], id: string): Promise<Record<string, u
|
|||||||
if (sub === 'start') {
|
if (sub === 'start') {
|
||||||
return { id, action: 'trace_start', screenshots: true, snapshots: true };
|
return { id, action: 'trace_start', screenshots: true, snapshots: true };
|
||||||
} else if (sub === 'stop') {
|
} else if (sub === 'stop') {
|
||||||
if (!args[1]) err('Usage: veb trace stop <path>');
|
if (!args[1]) err('Usage: agent-browser trace stop <path>');
|
||||||
return { id, action: 'trace_stop', path: args[1] };
|
return { id, action: 'trace_stop', path: args[1] };
|
||||||
} else {
|
} else {
|
||||||
err('Usage: veb trace start|stop');
|
err('Usage: agent-browser trace start|stop');
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@@ -555,13 +555,13 @@ async function handleState(args: string[], id: string): Promise<Record<string, u
|
|||||||
const path = args[1];
|
const path = args[1];
|
||||||
|
|
||||||
if (sub === 'save') {
|
if (sub === 'save') {
|
||||||
if (!path) err('Usage: veb state save <path>');
|
if (!path) err('Usage: agent-browser state save <path>');
|
||||||
return { id, action: 'state_save', path };
|
return { id, action: 'state_save', path };
|
||||||
} else if (sub === 'load') {
|
} else if (sub === 'load') {
|
||||||
if (!path) err('Usage: veb state load <path>');
|
if (!path) err('Usage: agent-browser state load <path>');
|
||||||
return { id, action: 'state_load', path };
|
return { id, action: 'state_load', path };
|
||||||
} else {
|
} else {
|
||||||
err('Usage: veb state save|load <path>');
|
err('Usage: agent-browser state save|load <path>');
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@@ -592,7 +592,7 @@ function parseFlags(args: string[]): { flags: Flags; cleanArgs: string[] } {
|
|||||||
text: false,
|
text: false,
|
||||||
debug: false,
|
debug: false,
|
||||||
headed: false,
|
headed: false,
|
||||||
session: process.env.VEB_SESSION || 'default',
|
session: process.env.AGENT_BROWSER_SESSION || 'default',
|
||||||
exact: false,
|
exact: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -683,12 +683,12 @@ async function main(): Promise<void> {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'type':
|
case 'type':
|
||||||
if (!args[0] || !args[1]) err('Usage: veb type <selector> <text>');
|
if (!args[0] || !args[1]) err('Usage: agent-browser type <selector> <text>');
|
||||||
cmd = { id, action: 'type', selector: args[0], text: args.slice(1).join(' ') };
|
cmd = { id, action: 'type', selector: args[0], text: args.slice(1).join(' ') };
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'fill':
|
case 'fill':
|
||||||
if (!args[0] || !args[1]) err('Usage: veb fill <selector> <text>');
|
if (!args[0] || !args[1]) err('Usage: agent-browser fill <selector> <text>');
|
||||||
cmd = { id, action: 'fill', selector: args[0], value: args.slice(1).join(' ') };
|
cmd = { id, action: 'fill', selector: args[0], value: args.slice(1).join(' ') };
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -729,17 +729,17 @@ async function main(): Promise<void> {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'select':
|
case 'select':
|
||||||
if (!args[0] || !args[1]) err('Usage: veb select <selector> <value>');
|
if (!args[0] || !args[1]) err('Usage: agent-browser select <selector> <value>');
|
||||||
cmd = { id, action: 'select', selector: args[0], value: args[1] };
|
cmd = { id, action: 'select', selector: args[0], value: args[1] };
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'drag':
|
case 'drag':
|
||||||
if (!args[0] || !args[1]) err('Usage: veb drag <source> <target>');
|
if (!args[0] || !args[1]) err('Usage: agent-browser drag <source> <target>');
|
||||||
cmd = { id, action: 'drag', source: args[0], target: args[1] };
|
cmd = { id, action: 'drag', source: args[0], target: args[1] };
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'upload':
|
case 'upload':
|
||||||
if (!args[0] || !args[1]) err('Usage: veb upload <selector> <files...>');
|
if (!args[0] || !args[1]) err('Usage: agent-browser upload <selector> <files...>');
|
||||||
cmd = { id, action: 'upload', selector: args[0], files: args.slice(1) };
|
cmd = { id, action: 'upload', selector: args[0], files: args.slice(1) };
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -767,7 +767,7 @@ async function main(): Promise<void> {
|
|||||||
} else if (target) {
|
} else if (target) {
|
||||||
cmd = { id, action: 'wait', selector: target };
|
cmd = { id, action: 'wait', selector: target };
|
||||||
} else {
|
} else {
|
||||||
err('Usage: veb wait <selector|ms|--text|--url|--load|--fn>');
|
err('Usage: agent-browser wait <selector|ms|--text|--url|--load|--fn>');
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -852,7 +852,7 @@ async function main(): Promise<void> {
|
|||||||
if (args[0] === 'new') {
|
if (args[0] === 'new') {
|
||||||
cmd = { id, action: 'window_new' };
|
cmd = { id, action: 'window_new' };
|
||||||
} else {
|
} else {
|
||||||
err('Usage: veb window new');
|
err('Usage: agent-browser window new');
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -871,7 +871,7 @@ async function main(): Promise<void> {
|
|||||||
} else if (args[0] === 'dismiss') {
|
} else if (args[0] === 'dismiss') {
|
||||||
cmd = { id, action: 'dialog', response: 'dismiss' };
|
cmd = { id, action: 'dialog', response: 'dismiss' };
|
||||||
} else {
|
} else {
|
||||||
err('Usage: veb dialog accept|dismiss');
|
err('Usage: agent-browser dialog accept|dismiss');
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -915,7 +915,7 @@ async function main(): Promise<void> {
|
|||||||
|
|
||||||
case 'multiselect':
|
case 'multiselect':
|
||||||
if (!args[0] || args.length < 2)
|
if (!args[0] || args.length < 2)
|
||||||
err('Usage: veb multiselect <selector> <value1> [value2...]');
|
err('Usage: agent-browser multiselect <selector> <value1> [value2...]');
|
||||||
cmd = { id, action: 'multiselect', selector: args[0], values: args.slice(1) };
|
cmd = { id, action: 'multiselect', selector: args[0], values: args.slice(1) };
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -962,7 +962,7 @@ async function main(): Promise<void> {
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
console.error(c('red', 'Unknown command:'), command);
|
console.error(c('red', 'Unknown command:'), command);
|
||||||
console.error(c('dim', 'Run: veb --help'));
|
console.error(c('dim', 'Run: agent-browser --help'));
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user