fastbrowser_cli 1.0.2 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +118 -0
- package/dist/fastbrowser_cli/fastbrowser_cli.d.ts +3 -0
- package/dist/fastbrowser_cli/fastbrowser_cli.d.ts.map +1 -0
- package/dist/fastbrowser_cli/fastbrowser_cli.js +288 -0
- package/dist/fastbrowser_cli/fastbrowser_cli.js.map +1 -0
- package/dist/fastbrowser_cli/libs/http-client.d.ts +7 -0
- package/dist/fastbrowser_cli/libs/http-client.d.ts.map +1 -0
- package/dist/fastbrowser_cli/libs/http-client.js +51 -0
- package/dist/fastbrowser_cli/libs/http-client.js.map +1 -0
- package/dist/fastbrowser_cli/libs/server-manager.d.ts +12 -0
- package/dist/fastbrowser_cli/libs/server-manager.d.ts.map +1 -0
- package/dist/fastbrowser_cli/libs/server-manager.js +194 -0
- package/dist/fastbrowser_cli/libs/server-manager.js.map +1 -0
- package/dist/fastbrowser_httpd/fastbrowser_httpd.d.ts +3 -0
- package/dist/fastbrowser_httpd/fastbrowser_httpd.d.ts.map +1 -0
- package/dist/fastbrowser_httpd/fastbrowser_httpd.js +82 -0
- package/dist/fastbrowser_httpd/fastbrowser_httpd.js.map +1 -0
- package/dist/fastbrowser_httpd/libs/routes.d.ts +6 -0
- package/dist/fastbrowser_httpd/libs/routes.d.ts.map +1 -0
- package/dist/fastbrowser_httpd/libs/routes.js +41 -0
- package/dist/fastbrowser_httpd/libs/routes.js.map +1 -0
- package/dist/fastbrowser_httpd/libs/tool-schemas.d.ts +72 -0
- package/dist/fastbrowser_httpd/libs/tool-schemas.d.ts.map +1 -0
- package/dist/fastbrowser_httpd/libs/tool-schemas.js +65 -0
- package/dist/fastbrowser_httpd/libs/tool-schemas.js.map +1 -0
- package/dist/fastbrowser_mcp/fastbrowser_mcp.d.ts +4 -0
- package/dist/fastbrowser_mcp/fastbrowser_mcp.d.ts.map +1 -0
- package/dist/fastbrowser_mcp/fastbrowser_mcp.js +417 -0
- package/dist/fastbrowser_mcp/fastbrowser_mcp.js.map +1 -0
- package/dist/fastbrowser_mcp/libs/mcp_client.d.ts +120 -0
- package/dist/fastbrowser_mcp/libs/mcp_client.d.ts.map +1 -0
- package/dist/fastbrowser_mcp/libs/mcp_client.js +83 -0
- package/dist/fastbrowser_mcp/libs/mcp_client.js.map +1 -0
- package/dist/fastbrowser_mcp/libs/mcp_proxy.d.ts +10 -0
- package/dist/fastbrowser_mcp/libs/mcp_proxy.d.ts.map +1 -0
- package/dist/fastbrowser_mcp/libs/mcp_proxy.js +45 -0
- package/dist/fastbrowser_mcp/libs/mcp_proxy.js.map +1 -0
- package/dist/fastbrowser_mcp/libs/schemas.d.ts +28 -0
- package/dist/fastbrowser_mcp/libs/schemas.d.ts.map +1 -0
- package/dist/fastbrowser_mcp/libs/schemas.js +38 -0
- package/dist/fastbrowser_mcp/libs/schemas.js.map +1 -0
- package/docs/brainstorm_scrap_by_ai.md +1 -1
- package/docs/feature_support_cli.md +27 -27
- package/package.json +7 -7
- package/{tmp/.claude/skills/fastweb → skills/fastbrowser}/SKILL.md +22 -22
- package/src/{fastweb_cli/fastweb_cli.ts → fastbrowser_cli/fastbrowser_cli.ts} +49 -15
- package/src/{fastweb_cli → fastbrowser_cli}/libs/http-client.ts +2 -2
- package/src/{fastweb_cli → fastbrowser_cli}/libs/server-manager.ts +8 -8
- package/src/{fastweb_http_server/fastweb_http_server.ts → fastbrowser_httpd/fastbrowser_httpd.ts} +10 -10
- package/src/{fastweb_http_server → fastbrowser_httpd}/libs/routes.ts +1 -1
- package/src/{fastweb_http_server → fastbrowser_httpd}/libs/tool-schemas.ts +4 -4
- package/src/{fastweb_mcp → fastbrowser_mcp}/libs/mcp_proxy.ts +1 -1
- package/src/{fastweb_mcp → fastbrowser_mcp}/libs/schemas.ts +1 -1
- package/tmp/.claude/.claude/settings.local.json +7 -0
- package/tmp/.claude/skills/fastbrowser/SKILL.md +214 -0
- /package/src/{fastweb_mcp/fastweb_mcp.ts → fastbrowser_mcp/fastbrowser_mcp.ts} +0 -0
- /package/src/{fastweb_mcp → fastbrowser_mcp}/libs/mcp_client.ts +0 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.McpClient = void 0;
|
|
4
|
+
// node import
|
|
5
|
+
const index_js_1 = require("@modelcontextprotocol/sdk/client/index.js");
|
|
6
|
+
const stdio_js_1 = require("@modelcontextprotocol/sdk/client/stdio.js");
|
|
7
|
+
const streamableHttp_js_1 = require("@modelcontextprotocol/sdk/client/streamableHttp.js");
|
|
8
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
9
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
10
|
+
//
|
|
11
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
12
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
13
|
+
class McpClient {
|
|
14
|
+
constructor(options) {
|
|
15
|
+
this.options = options;
|
|
16
|
+
this.connected = false;
|
|
17
|
+
this.client = new index_js_1.Client({
|
|
18
|
+
name: options.name,
|
|
19
|
+
version: options.version,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
async connect() {
|
|
23
|
+
if (this.connected)
|
|
24
|
+
return;
|
|
25
|
+
this.transport = this.createTransport(this.options.transport);
|
|
26
|
+
await this.client.connect(this.transport);
|
|
27
|
+
this.connected = true;
|
|
28
|
+
}
|
|
29
|
+
async close() {
|
|
30
|
+
if (!this.connected)
|
|
31
|
+
return;
|
|
32
|
+
await this.client.close();
|
|
33
|
+
this.connected = false;
|
|
34
|
+
}
|
|
35
|
+
async listTools() {
|
|
36
|
+
this.assertConnected();
|
|
37
|
+
const { tools } = await this.client.listTools();
|
|
38
|
+
return tools;
|
|
39
|
+
}
|
|
40
|
+
async callTool(name, args = {}) {
|
|
41
|
+
this.assertConnected();
|
|
42
|
+
return this.client.callTool({ name, arguments: args });
|
|
43
|
+
}
|
|
44
|
+
async listResources() {
|
|
45
|
+
this.assertConnected();
|
|
46
|
+
const { resources } = await this.client.listResources();
|
|
47
|
+
return resources;
|
|
48
|
+
}
|
|
49
|
+
async readResource(uri) {
|
|
50
|
+
this.assertConnected();
|
|
51
|
+
return this.client.readResource({ uri });
|
|
52
|
+
}
|
|
53
|
+
async listPrompts() {
|
|
54
|
+
this.assertConnected();
|
|
55
|
+
const { prompts } = await this.client.listPrompts();
|
|
56
|
+
return prompts;
|
|
57
|
+
}
|
|
58
|
+
async getPrompt(name, args = {}) {
|
|
59
|
+
this.assertConnected();
|
|
60
|
+
return this.client.getPrompt({ name, arguments: args });
|
|
61
|
+
}
|
|
62
|
+
createTransport(config) {
|
|
63
|
+
switch (config.type) {
|
|
64
|
+
case "stdio":
|
|
65
|
+
return new stdio_js_1.StdioClientTransport({
|
|
66
|
+
command: config.command,
|
|
67
|
+
args: config.args ?? [],
|
|
68
|
+
env: config.env,
|
|
69
|
+
});
|
|
70
|
+
case "http":
|
|
71
|
+
return new streamableHttp_js_1.StreamableHTTPClientTransport(new URL(config.url), {
|
|
72
|
+
requestInit: { headers: config.headers },
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
assertConnected() {
|
|
77
|
+
if (!this.connected) {
|
|
78
|
+
throw new Error("McpClient is not connected. Call connect() first.");
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
exports.McpClient = McpClient;
|
|
83
|
+
//# sourceMappingURL=mcp_client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp_client.js","sourceRoot":"","sources":["../../../src/fastbrowser_mcp/libs/mcp_client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { McpClient } from "./mcp_client.js";
|
|
3
|
+
export declare class McpProxy {
|
|
4
|
+
private _mcpServer;
|
|
5
|
+
constructor();
|
|
6
|
+
connect(): Promise<void>;
|
|
7
|
+
getMcpServer(): Promise<McpServer>;
|
|
8
|
+
proxyToolCall(mcpClient: McpClient, toolName: string): Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=mcp_proxy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp_proxy.d.ts","sourceRoot":"","sources":["../../../src/fastbrowser_mcp/libs/mcp_proxy.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAKpE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,qBAAa,QAAQ;IACpB,OAAO,CAAC,UAAU,CAAY;;IASxB,OAAO;IAKP,YAAY,IAAI,OAAO,CAAC,SAAS,CAAC;IAOlC,aAAa,CAAC,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM;CAc1D"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.McpProxy = void 0;
|
|
7
|
+
// node imports
|
|
8
|
+
const node_assert_1 = __importDefault(require("node:assert"));
|
|
9
|
+
// npm imports
|
|
10
|
+
const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
11
|
+
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
12
|
+
const zod_from_json_schema_1 = require("zod-from-json-schema");
|
|
13
|
+
class McpProxy {
|
|
14
|
+
constructor() {
|
|
15
|
+
this._mcpServer = new mcp_js_1.McpServer({
|
|
16
|
+
name: "fastbrowser_mcp",
|
|
17
|
+
version: "1.0.0",
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
async connect() {
|
|
21
|
+
const transport = new stdio_js_1.StdioServerTransport();
|
|
22
|
+
await this._mcpServer.connect(transport);
|
|
23
|
+
}
|
|
24
|
+
async getMcpServer() {
|
|
25
|
+
if (this._mcpServer === null) {
|
|
26
|
+
throw new Error("MCP server is not initialized");
|
|
27
|
+
}
|
|
28
|
+
return this._mcpServer;
|
|
29
|
+
}
|
|
30
|
+
async proxyToolCall(mcpClient, toolName) {
|
|
31
|
+
const mcpClientTools = await mcpClient.listTools();
|
|
32
|
+
const mcpClientTool = mcpClientTools.find((tool) => tool.name === toolName);
|
|
33
|
+
node_assert_1.default.ok(mcpClientTool !== undefined, `Tool ${toolName} not found in mcp client tools`);
|
|
34
|
+
const inputSchema = (0, zod_from_json_schema_1.convertJsonSchemaToZod)(mcpClientTool.inputSchema);
|
|
35
|
+
this._mcpServer.registerTool(toolName, {
|
|
36
|
+
description: mcpClientTool.description,
|
|
37
|
+
inputSchema: inputSchema,
|
|
38
|
+
}, async (...args) => {
|
|
39
|
+
const callResult = await mcpClient.callTool(toolName, args[0]);
|
|
40
|
+
return callResult;
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
exports.McpProxy = McpProxy;
|
|
45
|
+
//# sourceMappingURL=mcp_proxy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp_proxy.js","sourceRoot":"","sources":["../../../src/fastbrowser_mcp/libs/mcp_proxy.ts"],"names":[],"mappings":";;;;;;AAAA,eAAe;AACf,8DAAiC;AAEjC,cAAc;AACd,oEAAoE;AACpE,wEAAiF;AACjF,+DAA8D;AAK9D,MAAa,QAAQ;IAGpB;QACC,IAAI,CAAC,UAAU,GAAG,IAAI,kBAAS,CAAC;YAC/B,IAAI,EAAE,iBAAiB;YACvB,OAAO,EAAE,OAAO;SAChB,CAAC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,OAAO;QACZ,MAAM,SAAS,GAAG,IAAI,+BAAoB,EAAE,CAAC;QAC7C,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAC1C,CAAC;IAED,KAAK,CAAC,YAAY;QACjB,IAAI,IAAI,CAAC,UAAU,KAAK,IAAI,EAAE,CAAC;YAC9B,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;QAClD,CAAC;QACD,OAAO,IAAI,CAAC,UAAU,CAAC;IACxB,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,SAAoB,EAAE,QAAgB;QACzD,MAAM,cAAc,GAAG,MAAM,SAAS,CAAC,SAAS,EAAE,CAAA;QAClD,MAAM,aAAa,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC;QAC5E,qBAAM,CAAC,EAAE,CAAC,aAAa,KAAK,SAAS,EAAE,QAAQ,QAAQ,gCAAgC,CAAC,CAAA;QAExF,MAAM,WAAW,GAAG,IAAA,6CAAsB,EAAC,aAAa,CAAC,WAAkB,CAAC,CAAC;QAC7E,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,QAAQ,EAAE;YACtC,WAAW,EAAE,aAAa,CAAC,WAAW;YACtC,WAAW,EAAE,WAAW;SACxB,EAAE,KAAK,EAAE,GAAG,IAAW,EAAE,EAAE;YAC3B,MAAM,UAAU,GAAG,MAAM,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAA;YAC9D,OAAO,UAAU,CAAA;QAClB,CAAC,CAAC,CAAC;IACJ,CAAC;CACD;AApCD,4BAoCC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const QuerySelectorInputSchema: z.ZodObject<{
|
|
3
|
+
selector: z.ZodString;
|
|
4
|
+
limit: z.ZodDefault<z.ZodNumber>;
|
|
5
|
+
withAncestors: z.ZodDefault<z.ZodBoolean>;
|
|
6
|
+
}, z.core.$strip>;
|
|
7
|
+
export declare const QuerySelectorsInputSchema: z.ZodObject<{
|
|
8
|
+
selectors: z.ZodArray<z.ZodObject<{
|
|
9
|
+
selector: z.ZodString;
|
|
10
|
+
limit: z.ZodDefault<z.ZodNumber>;
|
|
11
|
+
withAncestors: z.ZodDefault<z.ZodBoolean>;
|
|
12
|
+
}, z.core.$strip>>;
|
|
13
|
+
}, z.core.$strip>;
|
|
14
|
+
export declare const QuerySelectorFirstInputSchema: z.ZodObject<{
|
|
15
|
+
selector: z.ZodString;
|
|
16
|
+
withAncestors: z.ZodDefault<z.ZodBoolean>;
|
|
17
|
+
}, z.core.$strip>;
|
|
18
|
+
export declare const QuerySelectorsFirstInputSchema: z.ZodObject<{
|
|
19
|
+
selectors: z.ZodArray<z.ZodObject<{
|
|
20
|
+
selector: z.ZodString;
|
|
21
|
+
withAncestors: z.ZodDefault<z.ZodBoolean>;
|
|
22
|
+
}, z.core.$strip>>;
|
|
23
|
+
}, z.core.$strip>;
|
|
24
|
+
export type QuerySelectorInput = z.infer<typeof QuerySelectorInputSchema>;
|
|
25
|
+
export type QuerySelectorsInput = z.infer<typeof QuerySelectorsInputSchema>;
|
|
26
|
+
export type QuerySelectorFirstInput = z.infer<typeof QuerySelectorFirstInputSchema>;
|
|
27
|
+
export type QuerySelectorsFirstInput = z.infer<typeof QuerySelectorsFirstInputSchema>;
|
|
28
|
+
//# sourceMappingURL=schemas.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../../../src/fastbrowser_mcp/libs/schemas.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAUxB,eAAO,MAAM,wBAAwB;;;;iBASnC,CAAC;AAEH,eAAO,MAAM,yBAAyB;;;;;;iBAGpC,CAAC;AAEH,eAAO,MAAM,6BAA6B;;;iBAMxC,CAAC;AAEH,eAAO,MAAM,8BAA8B;;;;;iBAGzC,CAAC;AAEH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAC1E,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAC5E,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,CAAC;AACpF,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,8BAA8B,CAAC,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.QuerySelectorsFirstInputSchema = exports.QuerySelectorFirstInputSchema = exports.QuerySelectorsInputSchema = exports.QuerySelectorInputSchema = void 0;
|
|
4
|
+
// npm imports
|
|
5
|
+
const zod_1 = require("zod");
|
|
6
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
7
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
8
|
+
// Authoritative query selector schemas — shared between the MCP server
|
|
9
|
+
// and the HTTP/CLI layer. Kept out of the fastbrowser_mcp.ts entrypoint so
|
|
10
|
+
// importing the schemas does not execute the CLI's top-level `main()`.
|
|
11
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
12
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
13
|
+
exports.QuerySelectorInputSchema = zod_1.z.object({
|
|
14
|
+
selector: zod_1.z.string()
|
|
15
|
+
.describe("CSS-like selector to query the accessibility tree"),
|
|
16
|
+
limit: zod_1.z.number()
|
|
17
|
+
.describe("Maximum number of nodes to return (excluding ancestors). Set to 0 for no limit.")
|
|
18
|
+
.default(0),
|
|
19
|
+
withAncestors: zod_1.z.boolean()
|
|
20
|
+
.describe("Whether to include ancestor nodes in the result")
|
|
21
|
+
.default(true),
|
|
22
|
+
});
|
|
23
|
+
exports.QuerySelectorsInputSchema = zod_1.z.object({
|
|
24
|
+
selectors: zod_1.z.array(exports.QuerySelectorInputSchema)
|
|
25
|
+
.describe("List of selectors to query the accessibility tree"),
|
|
26
|
+
});
|
|
27
|
+
exports.QuerySelectorFirstInputSchema = zod_1.z.object({
|
|
28
|
+
selector: zod_1.z.string()
|
|
29
|
+
.describe("CSS-like selector to query the accessibility tree"),
|
|
30
|
+
withAncestors: zod_1.z.boolean()
|
|
31
|
+
.describe("Whether to include ancestor nodes in the result")
|
|
32
|
+
.default(true),
|
|
33
|
+
});
|
|
34
|
+
exports.QuerySelectorsFirstInputSchema = zod_1.z.object({
|
|
35
|
+
selectors: zod_1.z.array(exports.QuerySelectorFirstInputSchema)
|
|
36
|
+
.describe("List of selectors; for each, the first matching node is returned"),
|
|
37
|
+
});
|
|
38
|
+
//# sourceMappingURL=schemas.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemas.js","sourceRoot":"","sources":["../../../src/fastbrowser_mcp/libs/schemas.ts"],"names":[],"mappings":";;;AAAA,cAAc;AACd,6BAAwB;AAExB,+EAA+E;AAC/E,+EAA+E;AAC/E,uEAAuE;AACvE,2EAA2E;AAC3E,uEAAuE;AACvE,+EAA+E;AAC/E,+EAA+E;AAElE,QAAA,wBAAwB,GAAG,OAAC,CAAC,MAAM,CAAC;IAChD,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE;SAClB,QAAQ,CAAC,mDAAmD,CAAC;IAC/D,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE;SACf,QAAQ,CAAC,iFAAiF,CAAC;SAC3F,OAAO,CAAC,CAAC,CAAC;IACZ,aAAa,EAAE,OAAC,CAAC,OAAO,EAAE;SACxB,QAAQ,CAAC,iDAAiD,CAAC;SAC3D,OAAO,CAAC,IAAI,CAAC;CACf,CAAC,CAAC;AAEU,QAAA,yBAAyB,GAAG,OAAC,CAAC,MAAM,CAAC;IACjD,SAAS,EAAE,OAAC,CAAC,KAAK,CAAC,gCAAwB,CAAC;SAC1C,QAAQ,CAAC,mDAAmD,CAAC;CAC/D,CAAC,CAAC;AAEU,QAAA,6BAA6B,GAAG,OAAC,CAAC,MAAM,CAAC;IACrD,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE;SAClB,QAAQ,CAAC,mDAAmD,CAAC;IAC/D,aAAa,EAAE,OAAC,CAAC,OAAO,EAAE;SACxB,QAAQ,CAAC,iDAAiD,CAAC;SAC3D,OAAO,CAAC,IAAI,CAAC;CACf,CAAC,CAAC;AAEU,QAAA,8BAA8B,GAAG,OAAC,CAAC,MAAM,CAAC;IACtD,SAAS,EAAE,OAAC,CAAC,KAAK,CAAC,qCAA6B,CAAC;SAC/C,QAAQ,CAAC,kEAAkE,CAAC;CAC9E,CAAC,CAAC"}
|
|
@@ -29,4 +29,4 @@
|
|
|
29
29
|
- possible by selector language ? yes
|
|
30
30
|
- get link with an url starting with "https://www.welcometothejungle.com/fr/companies/"
|
|
31
31
|
- on a https://www.welcometothejungle.com/fr/jobs?refinementList%5Boffices.country_code%5D%5B%5D=FR&query=machine%20learning&page=7
|
|
32
|
-
- `npx tsx ./src/
|
|
32
|
+
- `npx tsx ./src/fastbrowser_cli/fastbrowser_cli.ts query_selectors_all --selector 'RootWebArea > link[url^="https://www.welcometothejungle.com/fr/companies/"]'`
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
#
|
|
1
|
+
# FastBrowser MCP
|
|
2
2
|
|
|
3
3
|
## Overview
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
FastBrowser is a Model Context Protocol (MCP) server accessible via STDIO. It acts as a smarter proxy in front of the official `chrome-devtools-mcp`, exposing a leaner, more efficient toolset tuned for LLM agents.
|
|
6
6
|
|
|
7
|
-
Where the standard Chrome DevTools MCP exposes a broad, low-level API,
|
|
7
|
+
Where the standard Chrome DevTools MCP exposes a broad, low-level API, FastBrowser adds a structured accessibility-tree query layer (`querySelectorsAll`) that lets an agent extract exactly the page nodes it needs in a single call — rather than consuming the entire raw snapshot. This reduces token usage and round-trips, making FastBrowser noticeably more efficient for web automation tasks.
|
|
8
8
|
|
|
9
9
|
## HTTP Server Architecture
|
|
10
10
|
|
|
11
|
-
Connecting to `
|
|
11
|
+
Connecting to `fastbrowser-mcp` on every invocation is slow: the MCP process starts cold, handshakes with `chrome-devtools-mcp`, and tears down after each session. To eliminate that overhead, `fastbrowser-httpd` acts as a persistent MCP client that connects to `fastbrowser-mcp` once and keeps that connection alive. It then re-exposes the same capabilities over a REST API, so `fastbrowser-cli` can issue commands with minimal latency. This splits the stack into two roles — a long-lived server that owns the MCP connection, and a thin client that issues commands and reads results — making interactions dramatically faster and easier to script.
|
|
12
12
|
|
|
13
13
|
## Components
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
FastBrowser is made up of three tools that work together:
|
|
16
16
|
|
|
17
|
-
- **
|
|
18
|
-
- **
|
|
19
|
-
- **
|
|
17
|
+
- **fastbrowser-mcp** — The MCP server, accessible via STDIO. It connects directly to Chrome via `chrome-devtools-mcp` and exposes a curated, efficient toolset to any MCP-compatible LLM agent.
|
|
18
|
+
- **fastbrowser-httpd** — A persistent HTTP server that acts as an MCP client connecting to `fastbrowser-mcp`. By keeping that MCP connection alive, it eliminates the cold-start penalty and exposes the same capabilities over a REST API for other clients to consume.
|
|
19
|
+
- **fastbrowser-cli** — A lightweight command-line client that talks to `fastbrowser-httpd` over a REST API. It provides a fast, scriptable interface to browser automation without the overhead of spawning a new MCP process each time.
|
|
20
20
|
|
|
21
21
|
### Tools exposed
|
|
22
22
|
|
|
@@ -32,49 +32,49 @@ FastWeb is made up of three tools that work together:
|
|
|
32
32
|
| `querySelectors` | Query the accessibility tree and return the first matching node per selector |
|
|
33
33
|
| `pressKeys` | Send a sequence of keyboard events |
|
|
34
34
|
|
|
35
|
-
##
|
|
35
|
+
## FastBrowser CLI Command Lines
|
|
36
36
|
|
|
37
|
-
Each command maps 1-to-1 to a
|
|
37
|
+
Each command maps 1-to-1 to a FastBrowser MCP tool and sends the request to `fastbrowser-httpd` via REST.
|
|
38
38
|
|
|
39
39
|
```bash
|
|
40
40
|
# List all open browser pages
|
|
41
|
-
|
|
41
|
+
fastbrowser-cli list_pages
|
|
42
42
|
|
|
43
43
|
# Open a new browser page
|
|
44
|
-
|
|
44
|
+
fastbrowser-cli new_page
|
|
45
45
|
|
|
46
46
|
# Close a page by its ID
|
|
47
|
-
|
|
47
|
+
fastbrowser-cli close_page --page-id <pageId>
|
|
48
48
|
|
|
49
49
|
# Navigate the current page to a URL
|
|
50
|
-
|
|
50
|
+
fastbrowser-cli navigate_page --url https://example.com
|
|
51
51
|
|
|
52
52
|
# Click an element by accessibility selector (fast path: "#1_42" is a direct uid reference)
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
fastbrowser-cli click --selector "#1_42"
|
|
54
|
+
fastbrowser-cli click -s 'button[name="Submit"]'
|
|
55
55
|
|
|
56
56
|
# Fill a form field by accessibility selector
|
|
57
|
-
|
|
57
|
+
fastbrowser-cli fill_form -s 'textbox[name="Email"]' --value "hello@example.com"
|
|
58
58
|
|
|
59
59
|
# Query the accessibility tree — --selector can be repeated for multiple selectors.
|
|
60
60
|
# --limit and --with-ancestors apply to all selectors equally.
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
fastbrowser-cli query_selectors_all --selector "button" --selector "link" --limit 5 --with-ancestors
|
|
62
|
+
fastbrowser-cli query_selectors_all --selector 'heading[level="1"]' --no-with-ancestors
|
|
63
63
|
|
|
64
64
|
# For per-selector control over limit/withAncestors, pass a JSON array directly:
|
|
65
|
-
|
|
65
|
+
fastbrowser-cli query_selectors_all --selectors-json '[{"selector":"button","limit":3,"withAncestors":true},{"selector":"link","limit":0,"withAncestors":false}]'
|
|
66
66
|
|
|
67
67
|
# Query the accessibility tree for the first matching element per selector (mirrors DOM querySelector).
|
|
68
68
|
# --selector can be repeated for multiple selectors; --with-ancestors applies to all.
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
fastbrowser-cli query_selectors --selector "button"
|
|
70
|
+
fastbrowser-cli query_selectors --selector "button" --selector "link" --no-with-ancestors
|
|
71
71
|
|
|
72
72
|
# For per-selector control over withAncestors, pass a JSON array directly:
|
|
73
|
-
|
|
73
|
+
fastbrowser-cli query_selectors --selectors-json '[{"selector":"button","withAncestors":true},{"selector":"link","withAncestors":false}]'
|
|
74
74
|
|
|
75
75
|
# Press a sequence of keys
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
fastbrowser-cli press_keys --keys "Tab, Tab, Enter"
|
|
77
|
+
fastbrowser-cli press_keys --keys "Hello, Tab, Enter"
|
|
78
78
|
```
|
|
79
79
|
|
|
80
80
|
## Implementation Notes
|
|
@@ -82,5 +82,5 @@ fastweb-cli press_keys --keys "Hello, Tab, Enter"
|
|
|
82
82
|
- The HTTP server is built with **Express**.
|
|
83
83
|
- All REST request and response payloads are validated with **Zod**.
|
|
84
84
|
- Every data structure has both a TypeScript `type` alias and a corresponding Zod schema — the type is derived from the schema via `z.infer<>` to keep them in sync.
|
|
85
|
-
- `
|
|
86
|
-
- `
|
|
85
|
+
- `fastbrowser-httpd` lives in `contrib/fastbrowser-httpd/`.
|
|
86
|
+
- `fastbrowser-cli` lives in `contrib/fastbrowser-cli/`.
|
package/package.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fastbrowser_cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "",
|
|
5
|
-
"main": "dist/
|
|
5
|
+
"main": "dist/fastbrowser_cli/fastbrowser_cli.js",
|
|
6
6
|
"bin": {
|
|
7
|
-
"fastbrowser_cli": "./dist/
|
|
7
|
+
"fastbrowser_cli": "./dist/fastbrowser_cli/fastbrowser_cli.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
|
-
"start:
|
|
11
|
-
"start:http-server": "npx tsx ./src/
|
|
12
|
-
"start:cli": "npx tsx ./src/
|
|
13
|
-
"inspect:
|
|
10
|
+
"start:fastbrowser_mcp": "npx tsx ./src/fastbrowser_mcp/fastbrowser_mcp.ts",
|
|
11
|
+
"start:http-server": "npx tsx ./src/fastbrowser_httpd/fastbrowser_httpd.ts",
|
|
12
|
+
"start:cli": "npx tsx ./src/fastbrowser_cli/fastbrowser_cli.ts",
|
|
13
|
+
"inspect:fastbrowser_mcp": "npx @modelcontextprotocol/inspector npx tsx ./src/fastbrowser_mcp/fastbrowser_mcp.ts mcp_server",
|
|
14
14
|
"inspect:chrome-devtools": "npx @modelcontextprotocol/inspector npx chrome-devtools-mcp@latest --autoconnect",
|
|
15
15
|
"build": "tsc -p tsconfig.json",
|
|
16
16
|
"publish:all": "npm run build && npm version patch && npm publish --access public",
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
2
|
+
name: fastbrowser
|
|
3
3
|
description: >
|
|
4
|
-
Control a live browser from the command line: navigate, click, fill forms, and query the accessibility tree with CSS-like selectors. Lighter alternative to Chrome DevTools MCP or Puppeteer. Triggers on: navigate/click/fill actions, page snapshots, or mentions of
|
|
4
|
+
Control a live browser from the command line: navigate, click, fill forms, and query the accessibility tree with CSS-like selectors. Lighter alternative to Chrome DevTools MCP or Puppeteer. Triggers on: navigate/click/fill actions, page snapshots, or mentions of fastbrowser.
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
#
|
|
7
|
+
# fastbrowser Skill
|
|
8
8
|
|
|
9
|
-
`
|
|
9
|
+
`fastbrowser-cli` is a command-line client for the FastWeb HTTP server, which keeps a persistent
|
|
10
10
|
MCP connection to a Chrome browser alive so commands incur minimal latency. Each command
|
|
11
11
|
maps 1-to-1 to a FastWeb tool and returns the tool's response on stdout.
|
|
12
12
|
|
|
@@ -15,7 +15,7 @@ maps 1-to-1 to a FastWeb tool and returns the tool's response on stdout.
|
|
|
15
15
|
Run the CLI directly via `tsx`:
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
|
-
npx
|
|
18
|
+
npx fastbrowser_cli <command> [flags]
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
## Typical Workflow
|
|
@@ -35,16 +35,16 @@ uid=1_0 RootWebArea "Example Domain" url="https://example.com/"
|
|
|
35
35
|
|
|
36
36
|
```bash
|
|
37
37
|
# List all open browser pages
|
|
38
|
-
npx
|
|
38
|
+
npx fastbrowser_cli list_pages
|
|
39
39
|
|
|
40
40
|
# Open a new page at a URL
|
|
41
|
-
npx
|
|
41
|
+
npx fastbrowser_cli new_page --url https://example.com
|
|
42
42
|
|
|
43
43
|
# Close a page by its numeric id
|
|
44
|
-
npx
|
|
44
|
+
npx fastbrowser_cli close_page --page-id 1
|
|
45
45
|
|
|
46
46
|
# Navigate the current page to a URL
|
|
47
|
-
npx
|
|
47
|
+
npx fastbrowser_cli navigate_page --url https://example.com
|
|
48
48
|
```
|
|
49
49
|
|
|
50
50
|
|
|
@@ -150,44 +150,44 @@ Example queries on it:
|
|
|
150
150
|
|
|
151
151
|
```bash
|
|
152
152
|
# Query the accessibility tree returning the FIRST match per selector (--selector is repeatable)
|
|
153
|
-
npx
|
|
153
|
+
npx fastbrowser_cli query_selectors --selector "button" --selector "link"
|
|
154
154
|
|
|
155
155
|
# Exclude ancestor nodes from the result
|
|
156
|
-
npx
|
|
156
|
+
npx fastbrowser_cli query_selectors --selector 'heading[level="1"]' --no-with-ancestors
|
|
157
157
|
|
|
158
158
|
# Per-selector control over withAncestors via JSON
|
|
159
|
-
npx
|
|
159
|
+
npx fastbrowser_cli query_selectors \
|
|
160
160
|
--selectors-json '[{"selector":"button","withAncestors":true},{"selector":"link","withAncestors":false}]'
|
|
161
161
|
|
|
162
162
|
# Query the accessibility tree returning ALL matches per selector (--selector is repeatable)
|
|
163
|
-
npx
|
|
163
|
+
npx fastbrowser_cli query_selectors_all --selector "button" --selector "link" --limit 5
|
|
164
164
|
|
|
165
165
|
# Exclude ancestor nodes from the result
|
|
166
|
-
npx
|
|
166
|
+
npx fastbrowser_cli query_selectors_all --selector 'heading[level="1"]' --no-with-ancestors
|
|
167
167
|
|
|
168
168
|
# Per-selector control over limit / withAncestors via JSON
|
|
169
|
-
npx
|
|
169
|
+
npx fastbrowser_cli query_selectors_all \
|
|
170
170
|
--selectors-json '[{"selector":"button","limit":3,"withAncestors":true},{"selector":"link","limit":0,"withAncestors":false}]'
|
|
171
171
|
|
|
172
172
|
# Take an accessibility-tree full page snapshot of the current page - very expensive, prefer targeted queries when possible
|
|
173
|
-
npx
|
|
173
|
+
npx fastbrowser_cli take_snapshot
|
|
174
174
|
```
|
|
175
175
|
|
|
176
176
|
## Interaction
|
|
177
177
|
|
|
178
178
|
```bash
|
|
179
179
|
# Click by a direct uid reference (fast path - no accessibility-tree lookup)
|
|
180
|
-
npx
|
|
180
|
+
npx fastbrowser_cli click --selector "#1_42"
|
|
181
181
|
|
|
182
182
|
# Click by any CSS-like selector - resolved to a uid internally
|
|
183
|
-
npx
|
|
183
|
+
npx fastbrowser_cli click -s 'button[name="Submit"]'
|
|
184
184
|
|
|
185
185
|
# Fill a single form field - selector can be a uid (#1_7) or any CSS-like selector
|
|
186
|
-
npx
|
|
186
|
+
npx fastbrowser_cli fill_form -s 'textbox[name="Email"]' --value "hello@example.com"
|
|
187
187
|
|
|
188
188
|
# Press a comma-separated sequence of keys (literals and named keys both work)
|
|
189
|
-
npx
|
|
190
|
-
npx
|
|
189
|
+
npx fastbrowser_cli press_keys --keys "Tab, Tab, Enter"
|
|
190
|
+
npx fastbrowser_cli press_keys --keys "Hello, Tab, Enter"
|
|
191
191
|
```
|
|
192
192
|
|
|
193
193
|
## Command Reference
|
|
@@ -211,4 +211,4 @@ npx tsx ../src/fastweb_cli/fastweb_cli.ts press_keys --keys "Hello, Tab, Enter"
|
|
|
211
211
|
## Output & Errors
|
|
212
212
|
|
|
213
213
|
Tool output is written to **stdout** — one line per response content part. On failure the
|
|
214
|
-
CLI writes `
|
|
214
|
+
CLI writes `fastbrowser-cli error: <message>` to **stderr** and exits with code 1.
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
// node imports
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
|
|
3
7
|
// npm imports
|
|
4
8
|
import { Command } from 'commander';
|
|
5
9
|
|
|
6
10
|
// local imports
|
|
7
11
|
import { HttpClient } from './libs/http-client.js';
|
|
8
12
|
import { ServerManager } from './libs/server-manager.js';
|
|
9
|
-
import type { QuerySelectorInput, QuerySelectorFirstInput, QuerySelectorsAllRequest, QuerySelectorRequest } from '../
|
|
13
|
+
import type { QuerySelectorInput, QuerySelectorFirstInput, QuerySelectorsAllRequest, QuerySelectorRequest } from '../fastbrowser_httpd/libs/tool-schemas.js';
|
|
10
14
|
|
|
11
15
|
|
|
12
16
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -40,7 +44,7 @@ async function runTool(cmd: Command, routeName: string, body: unknown): Promise<
|
|
|
40
44
|
HttpClient.printResponse(response);
|
|
41
45
|
} catch (err) {
|
|
42
46
|
const message = err instanceof Error ? err.message : String(err);
|
|
43
|
-
console.error(`
|
|
47
|
+
console.error(`fastbrowser-cli error: ${message}`);
|
|
44
48
|
process.exit(1);
|
|
45
49
|
}
|
|
46
50
|
}
|
|
@@ -127,54 +131,84 @@ function buildQuerySelectorFirstBody(opts: {
|
|
|
127
131
|
///////////////////////////////////////////////////////////////////////////////
|
|
128
132
|
///////////////////////////////////////////////////////////////////////////////
|
|
129
133
|
|
|
134
|
+
async function runInstall(skillFolder: string): Promise<void> {
|
|
135
|
+
const sourceSkillMd = path.resolve(__dirname, '../../skills/fastbrowser/SKILL.md');
|
|
136
|
+
const targetDir = path.resolve(skillFolder, 'skills', 'fastbrowser');
|
|
137
|
+
const targetSkillMd = path.join(targetDir, 'SKILL.md');
|
|
138
|
+
try {
|
|
139
|
+
await fs.promises.mkdir(targetDir, { recursive: true });
|
|
140
|
+
await fs.promises.copyFile(sourceSkillMd, targetSkillMd);
|
|
141
|
+
console.log(`Installed fastbrowser SKILL.md at ${targetSkillMd}`);
|
|
142
|
+
} catch (err) {
|
|
143
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
144
|
+
console.error(`fastbrowser-cli error: ${message}`);
|
|
145
|
+
process.exit(1);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
150
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
151
|
+
//
|
|
152
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
153
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
154
|
+
|
|
130
155
|
async function main(): Promise<void> {
|
|
156
|
+
const installIdx = process.argv.indexOf('--install');
|
|
157
|
+
if (installIdx !== -1) {
|
|
158
|
+
const next = process.argv[installIdx + 1];
|
|
159
|
+
const skillFolder = (next !== undefined && next.startsWith('-') === false) ? next : '.';
|
|
160
|
+
await runInstall(skillFolder);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
131
164
|
const program = new Command();
|
|
132
165
|
program
|
|
133
|
-
.name('
|
|
134
|
-
.description('CLI client for
|
|
135
|
-
.option('--server <url>', '
|
|
166
|
+
.name('fastbrowser-cli')
|
|
167
|
+
.description('CLI client for fastbrowser')
|
|
168
|
+
.option('--server <url>', 'fastbrowser-httpd URL (default: env FASTBROWSER_SERVER or http://localhost:8787)')
|
|
136
169
|
.option('--autostart', 'Auto-start the server before a command if it is not running', true)
|
|
137
|
-
.option('--no-autostart', 'Do not auto-start the server before a command')
|
|
170
|
+
.option('--no-autostart', 'Do not auto-start the server before a command')
|
|
171
|
+
.option('--install [skill-folder]', 'Install SKILL.md into <skill-folder>/skills/fastbrowser (default: .)');
|
|
138
172
|
|
|
139
173
|
const serverCmd = program
|
|
140
174
|
.command('server')
|
|
141
|
-
.description('Manage the
|
|
175
|
+
.description('Manage the fastbrowser HTTP server');
|
|
142
176
|
|
|
143
177
|
serverCmd
|
|
144
178
|
.command('start')
|
|
145
|
-
.description('Start the
|
|
179
|
+
.description('Start the fastbrowser HTTP server as a detached daemon')
|
|
146
180
|
.action(async (_opts, cmd: Command) => {
|
|
147
181
|
const server = getServerFromCmd(cmd);
|
|
148
182
|
try {
|
|
149
183
|
await ServerManager.start(server);
|
|
150
184
|
} catch (err) {
|
|
151
185
|
const message = err instanceof Error ? err.message : String(err);
|
|
152
|
-
console.error(`
|
|
186
|
+
console.error(`fastbrowser-cli error: ${message}`);
|
|
153
187
|
process.exit(1);
|
|
154
188
|
}
|
|
155
189
|
});
|
|
156
190
|
|
|
157
191
|
serverCmd
|
|
158
192
|
.command('stop')
|
|
159
|
-
.description('Stop the
|
|
193
|
+
.description('Stop the fastbrowser HTTP server')
|
|
160
194
|
.action(async (_opts, cmd: Command) => {
|
|
161
195
|
const server = getServerFromCmd(cmd);
|
|
162
196
|
try {
|
|
163
197
|
await ServerManager.stop(server);
|
|
164
198
|
} catch (err) {
|
|
165
199
|
const message = err instanceof Error ? err.message : String(err);
|
|
166
|
-
console.error(`
|
|
200
|
+
console.error(`fastbrowser-cli error: ${message}`);
|
|
167
201
|
process.exit(1);
|
|
168
202
|
}
|
|
169
203
|
});
|
|
170
204
|
|
|
171
205
|
serverCmd
|
|
172
206
|
.command('status')
|
|
173
|
-
.description('Report whether the
|
|
207
|
+
.description('Report whether the fastbrowser HTTP server is running')
|
|
174
208
|
.action(async (_opts, cmd: Command) => {
|
|
175
209
|
const server = getServerFromCmd(cmd);
|
|
176
210
|
const state = await ServerManager.status(server);
|
|
177
|
-
console.log(`
|
|
211
|
+
console.log(`fastbrowser server at ${server}: ${state}`);
|
|
178
212
|
if (state === 'stopped') process.exit(1);
|
|
179
213
|
});
|
|
180
214
|
|
|
@@ -254,7 +288,7 @@ async function main(): Promise<void> {
|
|
|
254
288
|
try {
|
|
255
289
|
body = buildQuerySelectorsBody(opts);
|
|
256
290
|
} catch (err) {
|
|
257
|
-
console.error(`
|
|
291
|
+
console.error(`fastbrowser-cli error: ${(err as Error).message}`);
|
|
258
292
|
process.exit(1);
|
|
259
293
|
}
|
|
260
294
|
await runTool(cmd, 'query_selectors_all', body);
|
|
@@ -279,7 +313,7 @@ async function main(): Promise<void> {
|
|
|
279
313
|
try {
|
|
280
314
|
body = buildQuerySelectorFirstBody(opts);
|
|
281
315
|
} catch (err) {
|
|
282
|
-
console.error(`
|
|
316
|
+
console.error(`fastbrowser-cli error: ${(err as Error).message}`);
|
|
283
317
|
process.exit(1);
|
|
284
318
|
}
|
|
285
319
|
await runTool(cmd, 'query_selectors', body);
|