Files
VisionL/packages/cli/src/index.ts
T
AskaEth b843eea9ae feat(cli): implement CLI entry point with auto-daemon launcher (Task 18)
- Add auto-daemon.ts: ensureDaemonRunning() checks health, reads pidfile,
  spawns daemon if needed, polls health up to 3s
- Rewrite index.ts: commander-based CLI with all subcommands (health,
  open, list, get, kill, navigate, click, type, scroll, eval, wait,
  screenshot, text, html, profiles)
- Add daemon project reference to cli tsconfig
2026-08-12 21:24:07 +08:00

167 lines
5.0 KiB
JavaScript

#!/usr/bin/env node
import { Command } from 'commander';
import { ensureDaemonRunning } from './auto-daemon.js';
import { safeStringify, VisionLClient } from '@visionl/core';
const DEFAULT_PORT = 9527;
const program = new Command();
program.name('visionl').version('0.1.0');
program.option('-p, --port <n>', 'daemon port', String(DEFAULT_PORT));
program.option('--pretty', 'human-readable output');
function getClient(): Promise<VisionLClient> {
const opts = program.opts<{ port: string }>();
const port = parseInt(opts.port, 10) || DEFAULT_PORT;
return ensureDaemonRunning(port);
}
function print(result: unknown): void {
const opts = program.opts<{ pretty: boolean }>();
if (opts.pretty) {
console.log(JSON.stringify(result, null, 2));
} else {
console.log(safeStringify(result));
}
}
// health — check daemon health
program.command('health')
.description('Check daemon health')
.action(async () => {
const client = await getClient();
const ok = await client.health();
print({ ok });
});
// pages — page management
program.command('open <url>')
.description('Open a URL in a new page')
.option('-a, --alias <alias>', 'Page alias')
.option('-p, --profile <profile>', 'Browser profile')
.action(async (url: string, options: { alias?: string; profile?: string }) => {
const client = await getClient();
const result = await client.openPage(url, options.alias, options.profile);
print(result);
});
program.command('list')
.description('List all open pages')
.action(async () => {
const client = await getClient();
const result = await client.listPages();
print(result);
});
program.command('get <id>')
.description('Get page info by ID or alias')
.action(async (id: string) => {
const client = await getClient();
const result = await client.getPage(id);
print(result);
});
program.command('kill <id>')
.description('Close a page by ID or alias')
.action(async (id: string) => {
const client = await getClient();
const result = await client.killPage(id);
print(result);
});
// navigation — page interactions
program.command('navigate <id> <url>')
.description('Navigate an existing page to a URL')
.action(async (id: string, url: string) => {
const client = await getClient();
const result = await client.navigate(id, url);
print(result);
});
program.command('click <id> <selector>')
.description('Click an element on a page')
.action(async (id: string, selector: string) => {
const client = await getClient();
const result = await client.click(id, selector);
print(result);
});
program.command('type <id> <selector> <text>')
.description('Type text into an element')
.action(async (id: string, selector: string, text: string) => {
const client = await getClient();
const result = await client.type(id, selector, text);
print(result);
});
program.command('scroll <id>')
.description('Scroll on a page')
.option('-y, --delta-y <n>', 'Vertical scroll delta', '0')
.option('-b, --to-bottom', 'Scroll to bottom')
.action(async (id: string, options: { deltaY: string; toBottom?: boolean }) => {
const client = await getClient();
const result = await client.scroll(id, {
deltaY: parseInt(options.deltaY, 10) || undefined,
toBottom: options.toBottom,
});
print(result);
});
program.command('eval <id> <code>')
.description('Evaluate JavaScript on a page')
.action(async (id: string, code: string) => {
const client = await getClient();
const result = await client.eval(id, code);
print(result);
});
program.command('wait <id>')
.description('Wait for selector or duration on a page')
.option('-s, --selector <selector>', 'Wait for CSS selector')
.option('-m, --ms <ms>', 'Wait time in milliseconds')
.action(async (id: string, options: { selector?: string; ms?: string }) => {
const client = await getClient();
const result = await client.wait(id, {
selector: options.selector,
ms: options.ms ? parseInt(options.ms, 10) : undefined,
});
print(result);
});
// content — page content retrieval
program.command('screenshot <id>')
.description('Take a screenshot of a page (returns base64)')
.action(async (id: string) => {
const client = await getClient();
const result = await client.screenshot(id);
print(result);
});
program.command('text <id>')
.description('Get the text content of a page')
.action(async (id: string) => {
const client = await getClient();
const result = await client.text(id);
print(result);
});
program.command('html <id>')
.description('Get the HTML source of a page')
.action(async (id: string) => {
const client = await getClient();
const result = await client.html(id);
print(result);
});
// profiles
program.command('profiles')
.description('List available fingerprint profiles')
.action(async () => {
const client = await getClient();
const result = await client.getProfiles();
print(result);
});
program.parse();