💬 Commit message: Update 2026-02-06 21:32:05, 5 files, 447 lines
📁 Files changed: 5 📝 Lines changed: 447 • settings.local.json • cli.ts • logger.ts • mcp.ts • server.ts
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"enabledMcpjsonServers": [
|
||||
"claude-browse"
|
||||
],
|
||||
"enableAllProjectMcpServers": true
|
||||
}
|
||||
+18
-20
@@ -19,7 +19,6 @@ const { values, positionals } = parseArgs({
|
||||
json: { type: 'boolean', short: 'j', default: false },
|
||||
click: { type: 'string', short: 'c', multiple: true },
|
||||
type: { type: 'string', short: 't', multiple: true },
|
||||
serve: { type: 'string', short: 's' },
|
||||
help: { type: 'boolean', default: false },
|
||||
version: { type: 'boolean', short: 'v', default: false },
|
||||
},
|
||||
@@ -40,7 +39,6 @@ Options:
|
||||
-j, --json Output query results as JSON
|
||||
-c, --click <selector> Click on element (can be repeated for multiple clicks)
|
||||
-t, --type <sel>=<text> Type text into input (can be repeated)
|
||||
-s, --serve <port> Start browser server on port (default: 3000)
|
||||
-v, --version Show version
|
||||
--help Show this help
|
||||
|
||||
@@ -54,19 +52,19 @@ Examples:
|
||||
claude-browse -t "input[name=q]=hello" -c "button[type=submit]" https://google.com
|
||||
claude-browse -c ".cookie-accept" -c "a.nav-link" -q "h1" https://example.com
|
||||
|
||||
Server mode:
|
||||
claude-browse -s 3000 # Start server on port 3000
|
||||
claude-browse -s 3000 --headed # Start with visible browser
|
||||
Server mode (default):
|
||||
claude-browse # Start server on port 13373
|
||||
claude-browse --headed # Start with visible browser
|
||||
|
||||
# Send commands via curl:
|
||||
curl -X POST http://localhost:3000 -d '{"cmd":"goto","url":"https://example.com"}'
|
||||
curl -X POST http://localhost:3000 -d '{"cmd":"click","selector":"button"}'
|
||||
curl -X POST http://localhost:3000 -d '{"cmd":"type","selector":"input","text":"hello"}'
|
||||
curl -X POST http://localhost:3000 -d '{"cmd":"query","selector":"a[href]"}'
|
||||
curl -X POST http://localhost:3000 -d '{"cmd":"screenshot","path":"shot.png"}'
|
||||
curl -X POST http://localhost:3000 -d '{"cmd":"url"}'
|
||||
curl -X POST http://localhost:3000 -d '{"cmd":"html"}'
|
||||
curl -X POST http://localhost:3000 -d '{"cmd":"close"}'
|
||||
curl -X POST http://localhost:13373 -d '{"cmd":"goto","url":"https://example.com"}'
|
||||
curl -X POST http://localhost:13373 -d '{"cmd":"click","selector":"button"}'
|
||||
curl -X POST http://localhost:13373 -d '{"cmd":"type","selector":"input","text":"hello"}'
|
||||
curl -X POST http://localhost:13373 -d '{"cmd":"query","selector":"a[href]"}'
|
||||
curl -X POST http://localhost:13373 -d '{"cmd":"screenshot","path":"shot.png"}'
|
||||
curl -X POST http://localhost:13373 -d '{"cmd":"url"}'
|
||||
curl -X POST http://localhost:13373 -d '{"cmd":"html"}'
|
||||
curl -X POST http://localhost:13373 -d '{"cmd":"close"}'
|
||||
`;
|
||||
|
||||
function getViewportConfig() {
|
||||
@@ -78,7 +76,7 @@ function getViewportConfig() {
|
||||
}
|
||||
|
||||
async function runServerMode(): Promise<void> {
|
||||
const port = Number.parseInt(values.serve as string) || 3000;
|
||||
const port = 13373;
|
||||
const server = await startServer({ port, ...getViewportConfig() });
|
||||
|
||||
process.on('SIGINT', async () => {
|
||||
@@ -197,16 +195,16 @@ async function main(): Promise<void> {
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (values.serve) {
|
||||
await runServerMode();
|
||||
return;
|
||||
}
|
||||
|
||||
if (values.help || positionals.length === 0) {
|
||||
if (values.help) {
|
||||
console.log(HELP);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (positionals.length === 0) {
|
||||
await runServerMode();
|
||||
return;
|
||||
}
|
||||
|
||||
await runBrowserMode();
|
||||
}
|
||||
|
||||
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
import chalk from 'chalk';
|
||||
import logSymbols from 'log-symbols';
|
||||
|
||||
// Icons for commands
|
||||
export const icons: Record<string, string> = {
|
||||
goto: '→',
|
||||
click: '◉',
|
||||
type: '⌨',
|
||||
query: '?',
|
||||
screenshot: '📷',
|
||||
url: '🔗',
|
||||
html: '<>',
|
||||
back: '←',
|
||||
forward: '→',
|
||||
reload: '↻',
|
||||
wait: '⏳',
|
||||
newpage: '+',
|
||||
close: '✕',
|
||||
eval: '⚡',
|
||||
};
|
||||
|
||||
// Colors for command types
|
||||
export const cmdColor: Record<string, (s: string) => string> = {
|
||||
goto: chalk.cyan,
|
||||
click: chalk.yellow,
|
||||
type: chalk.magenta,
|
||||
query: chalk.blue,
|
||||
screenshot: chalk.green,
|
||||
url: chalk.cyan,
|
||||
html: chalk.blue,
|
||||
back: chalk.yellow,
|
||||
forward: chalk.yellow,
|
||||
reload: chalk.yellow,
|
||||
wait: chalk.gray,
|
||||
newpage: chalk.green,
|
||||
close: chalk.red,
|
||||
eval: chalk.magenta,
|
||||
};
|
||||
|
||||
export function ts(): string {
|
||||
return chalk.gray(`[${new Date().toISOString()}]`);
|
||||
}
|
||||
|
||||
export function truncate(str: string, max: number): string {
|
||||
return str.length > max ? `${str.slice(0, max)}...` : str;
|
||||
}
|
||||
|
||||
export interface CommandLike {
|
||||
cmd: string;
|
||||
url?: string;
|
||||
selector?: string;
|
||||
text?: string;
|
||||
path?: string;
|
||||
full?: boolean;
|
||||
ms?: number;
|
||||
script?: string;
|
||||
}
|
||||
|
||||
export function getCommandDetail(cmd: CommandLike): string | undefined {
|
||||
switch (cmd.cmd) {
|
||||
case 'goto':
|
||||
return chalk.white(cmd.url);
|
||||
case 'click':
|
||||
case 'query':
|
||||
return chalk.white(cmd.selector);
|
||||
case 'type':
|
||||
return `${chalk.white(cmd.selector)} ${chalk.dim(`="${cmd.text}"`)}`;
|
||||
case 'screenshot':
|
||||
return chalk.dim(cmd.path || 'screenshot.png');
|
||||
case 'html':
|
||||
return cmd.full ? chalk.dim('(full)') : undefined;
|
||||
case 'wait':
|
||||
return chalk.dim(`${cmd.ms || 1000}ms`);
|
||||
case 'eval':
|
||||
return chalk.dim(truncate(cmd.script || '', 50));
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export function formatCommand(cmd: CommandLike): string {
|
||||
const color = cmdColor[cmd.cmd] || chalk.white;
|
||||
const icon = icons[cmd.cmd] || '•';
|
||||
const detail = getCommandDetail(cmd);
|
||||
const suffix = detail ? ` ${detail}` : '';
|
||||
return `${ts()} ${chalk.bold(color(icon))} ${color(cmd.cmd.toUpperCase())}${suffix}`;
|
||||
}
|
||||
|
||||
export interface ResultLike {
|
||||
ok: boolean;
|
||||
error?: string;
|
||||
title?: string;
|
||||
url?: string;
|
||||
count?: number;
|
||||
path?: string;
|
||||
html?: string;
|
||||
result?: unknown;
|
||||
}
|
||||
|
||||
const resultFormatters: Record<string, (r: ResultLike) => string | undefined> = {
|
||||
goto: (r) => r.title,
|
||||
click: (r) => (r.url ? `→ ${r.url}` : undefined),
|
||||
query: (r) => (r.count !== undefined ? `Found ${r.count} element(s)` : undefined),
|
||||
screenshot: (r) => (r.path ? `Saved to ${r.path}` : undefined),
|
||||
url: (r) => r.url,
|
||||
html: (r) => (r.html !== undefined ? `${r.html.length} chars` : undefined),
|
||||
eval: (r) => (r.result !== undefined ? truncate(JSON.stringify(r.result), 80) : undefined),
|
||||
};
|
||||
|
||||
export function formatResult(cmd: CommandLike, result: ResultLike): string {
|
||||
if (!result.ok) {
|
||||
return `${ts()} ${logSymbols.error} ${chalk.red(result.error)}`;
|
||||
}
|
||||
const formatter = resultFormatters[cmd.cmd];
|
||||
const msg = formatter ? formatter(result) : undefined;
|
||||
const suffix = msg ? ` ${chalk.dim(msg)}` : '';
|
||||
return `${ts()} ${logSymbols.success}${suffix}`;
|
||||
}
|
||||
|
||||
export type LogFn = (msg: string) => void;
|
||||
|
||||
export function createLogger(logFn: LogFn = console.log) {
|
||||
return {
|
||||
command(cmd: CommandLike): void {
|
||||
logFn(formatCommand(cmd));
|
||||
},
|
||||
result(cmd: CommandLike, result: ResultLike): void {
|
||||
logFn(formatResult(cmd, result));
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Default logger to stdout
|
||||
export const logger = createLogger();
|
||||
|
||||
// Logger to stderr (for MCP)
|
||||
export const stderrLogger = createLogger((msg) => process.stderr.write(`${msg}\n`));
|
||||
+93
-36
@@ -3,6 +3,7 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
||||
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
||||
import { z } from 'zod';
|
||||
import { ClaudeBrowser } from './browser.js';
|
||||
import { type CommandLike, type ResultLike, stderrLogger as log } from './logger.js';
|
||||
|
||||
const browser = new ClaudeBrowser({ headless: true, width: 1280, height: 800 });
|
||||
let launched = false;
|
||||
@@ -14,52 +15,103 @@ async function ensureLaunched(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
type ToolResult = { content: [{ type: 'text'; text: string }] };
|
||||
|
||||
function textResult(text: string): ToolResult {
|
||||
return { content: [{ type: 'text' as const, text }] };
|
||||
}
|
||||
|
||||
function withLogging<T extends Record<string, unknown>>(
|
||||
cmd: string,
|
||||
fn: (args: T) => Promise<ToolResult>
|
||||
): (args: T) => Promise<ToolResult> {
|
||||
return async (args: T) => {
|
||||
const cmdLike: CommandLike = { cmd, ...args };
|
||||
log.command(cmdLike);
|
||||
try {
|
||||
const result = await fn(args);
|
||||
const parsed = JSON.parse(result.content[0]?.text || '{}');
|
||||
const resultLike: ResultLike = { ok: true, ...parsed };
|
||||
log.result(cmdLike, resultLike);
|
||||
return result;
|
||||
} catch (err) {
|
||||
log.result(cmdLike, { ok: false, error: (err as Error).message });
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const server = new McpServer({
|
||||
name: 'claude-browse',
|
||||
version: '0.1.0',
|
||||
});
|
||||
|
||||
// Navigation
|
||||
server.tool('goto', 'Navigate to a URL', { url: z.string().url() }, async ({ url }) => {
|
||||
server.tool(
|
||||
'goto',
|
||||
'Navigate to a URL',
|
||||
{ url: z.string().url() },
|
||||
withLogging('goto', async ({ url }) => {
|
||||
await ensureLaunched();
|
||||
const result = await browser.goto(url);
|
||||
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
||||
});
|
||||
return textResult(JSON.stringify(result));
|
||||
})
|
||||
);
|
||||
|
||||
server.tool('back', 'Go back in browser history', {}, async () => {
|
||||
server.tool(
|
||||
'back',
|
||||
'Go back in browser history',
|
||||
{},
|
||||
withLogging('back', async () => {
|
||||
await ensureLaunched();
|
||||
const result = await browser.back();
|
||||
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
||||
});
|
||||
return textResult(JSON.stringify(result));
|
||||
})
|
||||
);
|
||||
|
||||
server.tool('forward', 'Go forward in browser history', {}, async () => {
|
||||
server.tool(
|
||||
'forward',
|
||||
'Go forward in browser history',
|
||||
{},
|
||||
withLogging('forward', async () => {
|
||||
await ensureLaunched();
|
||||
const result = await browser.forward();
|
||||
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
||||
});
|
||||
return textResult(JSON.stringify(result));
|
||||
})
|
||||
);
|
||||
|
||||
server.tool('reload', 'Reload the current page', {}, async () => {
|
||||
server.tool(
|
||||
'reload',
|
||||
'Reload the current page',
|
||||
{},
|
||||
withLogging('reload', async () => {
|
||||
await ensureLaunched();
|
||||
const result = await browser.reload();
|
||||
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
||||
});
|
||||
return textResult(JSON.stringify(result));
|
||||
})
|
||||
);
|
||||
|
||||
// Interaction
|
||||
server.tool('click', 'Click on an element', { selector: z.string() }, async ({ selector }) => {
|
||||
server.tool(
|
||||
'click',
|
||||
'Click on an element',
|
||||
{ selector: z.string() },
|
||||
withLogging('click', async ({ selector }) => {
|
||||
await ensureLaunched();
|
||||
const result = await browser.click(selector);
|
||||
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
||||
});
|
||||
return textResult(JSON.stringify(result));
|
||||
})
|
||||
);
|
||||
|
||||
server.tool(
|
||||
'type',
|
||||
'Type text into an input field',
|
||||
{ selector: z.string(), text: z.string() },
|
||||
async ({ selector, text }) => {
|
||||
withLogging('type', async ({ selector, text }) => {
|
||||
await ensureLaunched();
|
||||
await browser.type(selector, text);
|
||||
return { content: [{ type: 'text', text: 'ok' }] };
|
||||
}
|
||||
return textResult(JSON.stringify({ ok: true }));
|
||||
})
|
||||
);
|
||||
|
||||
// Query
|
||||
@@ -67,28 +119,33 @@ server.tool(
|
||||
'query',
|
||||
'Query elements by CSS selector, returns tag, text, and attributes',
|
||||
{ selector: z.string() },
|
||||
async ({ selector }) => {
|
||||
withLogging('query', async ({ selector }) => {
|
||||
await ensureLaunched();
|
||||
const elements = await browser.query(selector);
|
||||
return { content: [{ type: 'text', text: JSON.stringify(elements, null, 2) }] };
|
||||
}
|
||||
return textResult(JSON.stringify({ ok: true, count: elements.length, elements }));
|
||||
})
|
||||
);
|
||||
|
||||
server.tool('url', 'Get current URL and page title', {}, async () => {
|
||||
server.tool(
|
||||
'url',
|
||||
'Get current URL and page title',
|
||||
{},
|
||||
withLogging('url', async () => {
|
||||
await ensureLaunched();
|
||||
const result = await browser.getUrl();
|
||||
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
||||
});
|
||||
return textResult(JSON.stringify(result));
|
||||
})
|
||||
);
|
||||
|
||||
server.tool(
|
||||
'html',
|
||||
'Get page HTML content',
|
||||
{ full: z.boolean().optional().default(false) },
|
||||
async ({ full }) => {
|
||||
withLogging('html', async ({ full }) => {
|
||||
await ensureLaunched();
|
||||
const html = await browser.getHtml(full);
|
||||
return { content: [{ type: 'text', text: html }] };
|
||||
}
|
||||
return textResult(JSON.stringify({ ok: true, html }));
|
||||
})
|
||||
);
|
||||
|
||||
// Screenshot
|
||||
@@ -99,11 +156,11 @@ server.tool(
|
||||
path: z.string().optional().default('screenshots/screenshot.png'),
|
||||
fullPage: z.boolean().optional().default(false),
|
||||
},
|
||||
async ({ path, fullPage }) => {
|
||||
withLogging('screenshot', async ({ path, fullPage }) => {
|
||||
await ensureLaunched();
|
||||
const result = await browser.screenshot(path, fullPage);
|
||||
return { content: [{ type: 'text', text: `Screenshot saved to ${result.path}` }] };
|
||||
}
|
||||
return textResult(JSON.stringify({ ok: true, path: result.path }));
|
||||
})
|
||||
);
|
||||
|
||||
// Eval
|
||||
@@ -111,11 +168,11 @@ server.tool(
|
||||
'eval',
|
||||
'Execute JavaScript in the browser context',
|
||||
{ script: z.string() },
|
||||
async ({ script }) => {
|
||||
withLogging('eval', async ({ script }) => {
|
||||
await ensureLaunched();
|
||||
const result = await browser.eval(script);
|
||||
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
||||
}
|
||||
return textResult(JSON.stringify({ ok: true, result }));
|
||||
})
|
||||
);
|
||||
|
||||
// Utility
|
||||
@@ -123,11 +180,11 @@ server.tool(
|
||||
'wait',
|
||||
'Wait for a specified time in milliseconds',
|
||||
{ ms: z.number().optional().default(1000) },
|
||||
async ({ ms }) => {
|
||||
withLogging('wait', async ({ ms }) => {
|
||||
await ensureLaunched();
|
||||
await browser.wait(ms);
|
||||
return { content: [{ type: 'text', text: 'ok' }] };
|
||||
}
|
||||
return textResult(JSON.stringify({ ok: true }));
|
||||
})
|
||||
);
|
||||
|
||||
// Start server
|
||||
|
||||
+6
-107
@@ -2,114 +2,13 @@ import chalk from 'chalk';
|
||||
import express, { type Request, type Response } from 'express';
|
||||
import logSymbols from 'log-symbols';
|
||||
import { ClaudeBrowser } from './browser.js';
|
||||
import type { BrowserCommand, BrowserOptions, CommandResponse } from './types.js';
|
||||
import { logger, ts } from './logger.js';
|
||||
import type { BrowserCommand, BrowserOptions } from './types.js';
|
||||
|
||||
export interface ServerOptions extends BrowserOptions {
|
||||
port?: number;
|
||||
}
|
||||
|
||||
// Icons for commands
|
||||
const icons = {
|
||||
goto: '→',
|
||||
click: '◉',
|
||||
type: '⌨',
|
||||
query: '?',
|
||||
screenshot: '📷',
|
||||
url: '🔗',
|
||||
html: '<>',
|
||||
back: '←',
|
||||
forward: '→',
|
||||
reload: '↻',
|
||||
wait: '⏳',
|
||||
newpage: '+',
|
||||
close: '✕',
|
||||
eval: '⚡',
|
||||
};
|
||||
|
||||
// Colors for command types
|
||||
const cmdColor: Record<string, (s: string) => string> = {
|
||||
goto: chalk.cyan,
|
||||
click: chalk.yellow,
|
||||
type: chalk.magenta,
|
||||
query: chalk.blue,
|
||||
screenshot: chalk.green,
|
||||
url: chalk.cyan,
|
||||
html: chalk.blue,
|
||||
back: chalk.yellow,
|
||||
forward: chalk.yellow,
|
||||
reload: chalk.yellow,
|
||||
wait: chalk.gray,
|
||||
newpage: chalk.green,
|
||||
close: chalk.red,
|
||||
eval: chalk.magenta,
|
||||
};
|
||||
|
||||
function ts(): string {
|
||||
return chalk.gray(`[${new Date().toISOString()}]`);
|
||||
}
|
||||
|
||||
function truncate(str: string, max: number): string {
|
||||
return str.length > max ? `${str.slice(0, max)}...` : str;
|
||||
}
|
||||
|
||||
function getCommandDetail(cmd: BrowserCommand): string | undefined {
|
||||
switch (cmd.cmd) {
|
||||
case 'goto':
|
||||
return chalk.white(cmd.url);
|
||||
case 'click':
|
||||
case 'query':
|
||||
return chalk.white(cmd.selector);
|
||||
case 'type':
|
||||
return `${chalk.white(cmd.selector)} ${chalk.dim(`="${cmd.text}"`)}`;
|
||||
case 'screenshot':
|
||||
return chalk.dim(cmd.path || 'screenshot.png');
|
||||
case 'html':
|
||||
return cmd.full ? chalk.dim('(full)') : undefined;
|
||||
case 'wait':
|
||||
return chalk.dim(`${cmd.ms || 1000}ms`);
|
||||
case 'eval':
|
||||
return chalk.dim(truncate(cmd.script, 50));
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function logCommand(cmd: BrowserCommand): void {
|
||||
const color = cmdColor[cmd.cmd] || chalk.white;
|
||||
const icon = icons[cmd.cmd as keyof typeof icons] || '•';
|
||||
const detail = getCommandDetail(cmd);
|
||||
const suffix = detail ? ` ${detail}` : '';
|
||||
console.log(`${ts()} ${chalk.bold(color(icon))} ${color(cmd.cmd.toUpperCase())}${suffix}`);
|
||||
}
|
||||
|
||||
type ResultFormatter = (result: CommandResponse) => string | undefined;
|
||||
|
||||
const resultFormatters: Record<string, ResultFormatter> = {
|
||||
goto: (r) => ('title' in r ? r.title : undefined),
|
||||
click: (r) => ('url' in r ? `→ ${r.url}` : undefined),
|
||||
query: (r) => ('count' in r ? `Found ${r.count} element(s)` : undefined),
|
||||
screenshot: (r) => ('path' in r ? `Saved to ${r.path}` : undefined),
|
||||
url: (r) => ('url' in r ? r.url : undefined),
|
||||
html: (r) => ('html' in r ? `${r.html?.length || 0} chars` : undefined),
|
||||
eval: (r) => ('result' in r ? truncate(JSON.stringify(r.result), 80) : undefined),
|
||||
};
|
||||
|
||||
function getResultMessage(cmd: BrowserCommand, result: CommandResponse): string | undefined {
|
||||
if (!result.ok) return undefined;
|
||||
const formatter = resultFormatters[cmd.cmd];
|
||||
return formatter ? formatter(result) : undefined;
|
||||
}
|
||||
|
||||
function logResult(cmd: BrowserCommand, result: CommandResponse): void {
|
||||
if (!result.ok) {
|
||||
console.log(`${ts()} ${logSymbols.error} ${chalk.red(result.error)}`);
|
||||
return;
|
||||
}
|
||||
const msg = getResultMessage(cmd, result);
|
||||
const suffix = msg ? ` ${chalk.dim(msg)}` : '';
|
||||
console.log(`${ts()} ${logSymbols.success}${suffix}`);
|
||||
}
|
||||
|
||||
function printBanner(port: number): void {
|
||||
console.log();
|
||||
console.log(chalk.cyan.bold(' 🌐 Claude Browse Server'));
|
||||
@@ -139,7 +38,7 @@ export class BrowserServer {
|
||||
|
||||
constructor(options: ServerOptions = {}) {
|
||||
this.browser = new ClaudeBrowser(options);
|
||||
this.port = options.port ?? 3000;
|
||||
this.port = options.port ?? 13373;
|
||||
this.setupMiddleware();
|
||||
this.setupRoutes();
|
||||
}
|
||||
@@ -156,17 +55,17 @@ export class BrowserServer {
|
||||
private async handleCommand(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const cmd: BrowserCommand = typeof req.body === 'string' ? JSON.parse(req.body) : req.body;
|
||||
logCommand(cmd);
|
||||
logger.command(cmd);
|
||||
|
||||
if (cmd.cmd === 'close') {
|
||||
logResult(cmd, { ok: true });
|
||||
logger.result(cmd, { ok: true });
|
||||
res.json({ ok: true });
|
||||
await this.stop();
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const result = await this.browser.executeCommand(cmd);
|
||||
logResult(cmd, result);
|
||||
logger.result(cmd, result);
|
||||
res.json(result);
|
||||
} catch (err) {
|
||||
const error = (err as Error).message;
|
||||
|
||||
Reference in New Issue
Block a user