@xbrowser/cli 1.22.0 → 1.23.1
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 +33 -3
- package/dist/{chunk-PIWNMC36.js → chunk-A3MORKYR.js} +1 -1
- package/dist/{chunk-OMU63E6J.js → chunk-FBQZ3PVD.js} +5 -8
- package/dist/chunk-LFX24QJZ.js +9076 -0
- package/dist/{chunk-TEXCXIBW.js → chunk-LPW6QS4M.js} +5 -8
- package/dist/chunk-TBSS7ES4.js +8732 -0
- package/dist/{chunk-NKW4A74J.js → chunk-W44UXZSC.js} +9 -105
- package/dist/cli.js +717 -9155
- package/dist/daemon-main.js +6 -6
- package/dist/{human-interaction-ZUTR5AC2.js → human-interaction-67K43B4O.js} +1 -1
- package/dist/{human-interaction-Y5IDH6LD.js → human-interaction-DK6QGYNC.js} +2 -1
- package/dist/{human-interaction-4YR2N6R6.js → human-interaction-MXST5ZQM.js} +1 -1
- package/dist/index.d.ts +15 -3
- package/dist/index.js +857 -9588
- package/dist/{plugin-singleton-FCNDN3FX.js → plugin-singleton-GJO7EZXB.js} +1 -1
- package/dist/{recovery-NXC35EQN.js → recovery-67DMH2OM.js} +3 -3
- package/dist/{recovery-L5GLDX4O.js → recovery-6RI2FE3E.js} +4 -3
- package/dist/{recovery-MDWAVXE4.js → recovery-LGY25LIM.js} +1 -1
- package/dist/server-DZUIBVVR.js +243 -0
- package/dist/server-HU4F5KN7.js +240 -0
- package/dist/{session-replayer-XM5L7LFD.js → session-replayer-N665T3G5.js} +22 -17
- package/package.json +11 -7
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
HumanInteractionManager
|
|
3
|
-
} from "./chunk-TEXCXIBW.js";
|
|
4
1
|
import {
|
|
5
2
|
buildViewerUrl
|
|
6
3
|
} from "./chunk-3OD76SUE.js";
|
|
4
|
+
import {
|
|
5
|
+
HumanInteractionManager
|
|
6
|
+
} from "./chunk-W44UXZSC.js";
|
|
7
7
|
import "./chunk-ZTHE5RBZ.js";
|
|
8
8
|
import "./chunk-Q2DFJQGS.js";
|
|
9
9
|
import "./chunk-KFQGP6VL.js";
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import {
|
|
2
|
+
HumanInteractionManager
|
|
3
|
+
} from "./chunk-LPW6QS4M.js";
|
|
1
4
|
import {
|
|
2
5
|
buildViewerUrl
|
|
3
6
|
} from "./chunk-3OD76SUE.js";
|
|
4
|
-
import
|
|
5
|
-
HumanInteractionManager
|
|
6
|
-
} from "./chunk-NKW4A74J.js";
|
|
7
|
+
import "./chunk-ZTHE5RBZ.js";
|
|
7
8
|
import "./chunk-Q2DFJQGS.js";
|
|
8
9
|
import "./chunk-KFQGP6VL.js";
|
|
9
10
|
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import {
|
|
2
|
+
executeChain,
|
|
3
|
+
executeCommand
|
|
4
|
+
} from "./chunk-LFX24QJZ.js";
|
|
5
|
+
import "./chunk-2Q3GQRUP.js";
|
|
6
|
+
import "./chunk-JKVUFP3G.js";
|
|
7
|
+
import "./chunk-3OD76SUE.js";
|
|
8
|
+
import "./chunk-ZTHE5RBZ.js";
|
|
9
|
+
import "./chunk-Q2DFJQGS.js";
|
|
10
|
+
import "./chunk-7OP2ISEY.js";
|
|
11
|
+
import "./chunk-GYB5BOSP.js";
|
|
12
|
+
import "./chunk-MYH6FIBZ.js";
|
|
13
|
+
import "./chunk-GDKLH7ZY.js";
|
|
14
|
+
import "./chunk-WYETQ5C7.js";
|
|
15
|
+
import "./chunk-75DNUI6M.js";
|
|
16
|
+
import "./chunk-KFQGP6VL.js";
|
|
17
|
+
|
|
18
|
+
// src/mcp/server.ts
|
|
19
|
+
var TOOLS = [
|
|
20
|
+
{
|
|
21
|
+
name: "browser_navigate",
|
|
22
|
+
description: "Navigate to a URL and optionally run follow-up commands as a chain. Use this to open pages. Session auto-creates; pass session to isolate state.",
|
|
23
|
+
inputSchema: {
|
|
24
|
+
type: "object",
|
|
25
|
+
properties: {
|
|
26
|
+
url: { type: "string", description: "URL to navigate to" },
|
|
27
|
+
session: { type: "string", description: "Session name (default: mcp)" },
|
|
28
|
+
chain: {
|
|
29
|
+
type: "string",
|
|
30
|
+
description: 'Optional follow-up command chain joined by && (e.g. `click "#btn" && text .result`). Full command list: xbrowser --help'
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
required: ["url"]
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: "browser_act",
|
|
38
|
+
description: "Run an interaction command on the current page: click / fill / type / press / select / check / hover / scroll / wait / eval. Selectors support CSS and text= prefix.",
|
|
39
|
+
inputSchema: {
|
|
40
|
+
type: "object",
|
|
41
|
+
properties: {
|
|
42
|
+
command: {
|
|
43
|
+
type: "string",
|
|
44
|
+
description: "Command word, e.g. click / fill / press / wait / eval"
|
|
45
|
+
},
|
|
46
|
+
args: {
|
|
47
|
+
type: "object",
|
|
48
|
+
description: 'Command parameters, e.g. { selector: "#input", value: "hello" }'
|
|
49
|
+
},
|
|
50
|
+
session: { type: "string", description: "Session name (default: mcp)" }
|
|
51
|
+
},
|
|
52
|
+
required: ["command"]
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: "browser_read",
|
|
57
|
+
description: "Read page content: text / html / title / url. Cheaper than snapshot for plain content.",
|
|
58
|
+
inputSchema: {
|
|
59
|
+
type: "object",
|
|
60
|
+
properties: {
|
|
61
|
+
kind: { type: "string", enum: ["text", "html", "title", "url"] },
|
|
62
|
+
selector: { type: "string", description: "Optional CSS selector to scope the read" },
|
|
63
|
+
session: { type: "string", description: "Session name (default: mcp)" }
|
|
64
|
+
},
|
|
65
|
+
required: ["kind"]
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
name: "browser_snapshot",
|
|
70
|
+
description: "Accessibility-tree snapshot of interactive elements \u2014 the token-efficient way for an agent to see the page. Returns refs usable with browser_act selectors.",
|
|
71
|
+
inputSchema: {
|
|
72
|
+
type: "object",
|
|
73
|
+
properties: {
|
|
74
|
+
interactiveOnly: { type: "boolean", description: "Only interactive elements (default true)" },
|
|
75
|
+
session: { type: "string", description: "Session name (default: mcp)" }
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: "browser_screenshot",
|
|
81
|
+
description: "Take a screenshot. Returns the file path (use screenshot --base64 chain for inline images).",
|
|
82
|
+
inputSchema: {
|
|
83
|
+
type: "object",
|
|
84
|
+
properties: {
|
|
85
|
+
output: { type: "string", description: "Output PNG path" },
|
|
86
|
+
fullPage: { type: "boolean" },
|
|
87
|
+
session: { type: "string", description: "Session name (default: mcp)" }
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
name: "browser_network",
|
|
93
|
+
description: "Inspect captured network requests: list / top / analyze.",
|
|
94
|
+
inputSchema: {
|
|
95
|
+
type: "object",
|
|
96
|
+
properties: {
|
|
97
|
+
action: { type: "string", enum: ["list", "top", "analyze"] },
|
|
98
|
+
session: { type: "string", description: "Session name (default: mcp)" }
|
|
99
|
+
},
|
|
100
|
+
required: ["action"]
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
name: "browser_replay",
|
|
105
|
+
description: "Replay a recorded flow with the self-healing engine \u2014 survives site redesigns via selector cascade (semantic candidates \u2192 fingerprints \u2192 coordinates \u2192 knowledge base). Deterministic, no LLM cost.",
|
|
106
|
+
inputSchema: {
|
|
107
|
+
type: "object",
|
|
108
|
+
properties: {
|
|
109
|
+
file: { type: "string", description: "Path to recorded YAML/JSON" },
|
|
110
|
+
slowMo: { type: "number", description: "ms delay between steps" },
|
|
111
|
+
session: { type: "string", description: "Session name (default: mcp)" }
|
|
112
|
+
},
|
|
113
|
+
required: ["file"]
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
];
|
|
117
|
+
var DEFAULT_SESSION = "mcp";
|
|
118
|
+
async function runTool(name, args) {
|
|
119
|
+
const session = args.session || DEFAULT_SESSION;
|
|
120
|
+
const sessionOpts = { sessionName: session };
|
|
121
|
+
const text = (v) => ({ content: [{ type: "text", text: JSON.stringify(v, null, 2) }] });
|
|
122
|
+
switch (name) {
|
|
123
|
+
case "browser_navigate": {
|
|
124
|
+
const url = args.url;
|
|
125
|
+
let chain = `goto ${url}`;
|
|
126
|
+
if (args.chain) chain += ` && ${args.chain}`;
|
|
127
|
+
const result = await executeChain(chain, sessionOpts);
|
|
128
|
+
return text({ ok: result.success, steps: result.steps?.map((s) => ({ command: s.command, success: s.success })) });
|
|
129
|
+
}
|
|
130
|
+
case "browser_act": {
|
|
131
|
+
const command = args.command;
|
|
132
|
+
const cmdArgs = args.args || {};
|
|
133
|
+
const result = await executeCommand(command, cmdArgs, session, {});
|
|
134
|
+
return text(result);
|
|
135
|
+
}
|
|
136
|
+
case "browser_read": {
|
|
137
|
+
const kind = args.kind;
|
|
138
|
+
const params = {};
|
|
139
|
+
if (args.selector) params.selector = args.selector;
|
|
140
|
+
const result = await executeCommand(kind, params, session, {});
|
|
141
|
+
return text(result);
|
|
142
|
+
}
|
|
143
|
+
case "browser_snapshot": {
|
|
144
|
+
const params = { interactiveOnly: args.interactiveOnly !== false };
|
|
145
|
+
const result = await executeCommand("snapshot", params, session, {});
|
|
146
|
+
return text(result);
|
|
147
|
+
}
|
|
148
|
+
case "browser_screenshot": {
|
|
149
|
+
const params = {
|
|
150
|
+
output: args.output || `output/mcp-${Date.now()}.png`,
|
|
151
|
+
...args.fullPage ? { fullPage: true } : {}
|
|
152
|
+
};
|
|
153
|
+
const result = await executeCommand("screenshot", params, session, {});
|
|
154
|
+
return text(result);
|
|
155
|
+
}
|
|
156
|
+
case "browser_network": {
|
|
157
|
+
const result = await executeCommand(`network:${args.action}`, {}, session, {});
|
|
158
|
+
return text(result);
|
|
159
|
+
}
|
|
160
|
+
case "browser_replay": {
|
|
161
|
+
const params = {
|
|
162
|
+
file: args.file,
|
|
163
|
+
...typeof args.slowMo === "number" ? { slowMo: args.slowMo } : {}
|
|
164
|
+
};
|
|
165
|
+
const result = await executeCommand("replay", params, session, {});
|
|
166
|
+
return text(result);
|
|
167
|
+
}
|
|
168
|
+
default:
|
|
169
|
+
throw new Error(`unknown tool: ${name}`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
function reply(id, result) {
|
|
173
|
+
process.stdout.write(JSON.stringify({ jsonrpc: "2.0", id, result }) + "\n");
|
|
174
|
+
}
|
|
175
|
+
function replyError(id, code, message) {
|
|
176
|
+
process.stdout.write(JSON.stringify({ jsonrpc: "2.0", id, error: { code, message } }) + "\n");
|
|
177
|
+
}
|
|
178
|
+
async function handleRequest(req) {
|
|
179
|
+
if (req.id === void 0) return;
|
|
180
|
+
switch (req.method) {
|
|
181
|
+
case "initialize":
|
|
182
|
+
reply(req.id, {
|
|
183
|
+
protocolVersion: "2024-11-05",
|
|
184
|
+
capabilities: { tools: {} },
|
|
185
|
+
serverInfo: { name: "xbrowser", version: process.env.npm_package_version || "1.23.0" }
|
|
186
|
+
});
|
|
187
|
+
break;
|
|
188
|
+
case "notifications/initialized":
|
|
189
|
+
break;
|
|
190
|
+
case "tools/list":
|
|
191
|
+
reply(req.id, { tools: TOOLS });
|
|
192
|
+
break;
|
|
193
|
+
case "tools/call": {
|
|
194
|
+
const toolName = req.params.name;
|
|
195
|
+
const toolArgs = req.params.arguments || {};
|
|
196
|
+
try {
|
|
197
|
+
const result = await runTool(toolName, toolArgs);
|
|
198
|
+
reply(req.id, { content: result.content });
|
|
199
|
+
} catch (err) {
|
|
200
|
+
reply(req.id, {
|
|
201
|
+
content: [{ type: "text", text: `Error: ${err.message}` }],
|
|
202
|
+
isError: true
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
default:
|
|
208
|
+
replyError(req.id, -32601, `method not found: ${req.method}`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
var inflight = 0;
|
|
212
|
+
function startMcpStdio() {
|
|
213
|
+
let buffer = "";
|
|
214
|
+
process.stdin.setEncoding("utf8");
|
|
215
|
+
process.stdin.on("data", (chunk) => {
|
|
216
|
+
buffer += chunk;
|
|
217
|
+
let idx;
|
|
218
|
+
while ((idx = buffer.indexOf("\n")) >= 0) {
|
|
219
|
+
const line = buffer.slice(0, idx).trim();
|
|
220
|
+
buffer = buffer.slice(idx + 1);
|
|
221
|
+
if (!line) continue;
|
|
222
|
+
try {
|
|
223
|
+
const req = JSON.parse(line);
|
|
224
|
+
inflight++;
|
|
225
|
+
handleRequest(req).catch((e) => {
|
|
226
|
+
if (req.id !== void 0) replyError(req.id, -32603, e.message);
|
|
227
|
+
}).finally(() => {
|
|
228
|
+
inflight--;
|
|
229
|
+
if (stdinClosed && inflight === 0) process.exit(0);
|
|
230
|
+
});
|
|
231
|
+
} catch {
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
let stdinClosed = false;
|
|
236
|
+
process.stdin.on("end", () => {
|
|
237
|
+
stdinClosed = true;
|
|
238
|
+
if (inflight === 0) process.exit(0);
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
export {
|
|
242
|
+
startMcpStdio
|
|
243
|
+
};
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import {
|
|
2
|
+
executeChain,
|
|
3
|
+
executeCommand
|
|
4
|
+
} from "./chunk-TBSS7ES4.js";
|
|
5
|
+
import "./chunk-JKVUFP3G.js";
|
|
6
|
+
import "./chunk-3OD76SUE.js";
|
|
7
|
+
import "./chunk-ZTHE5RBZ.js";
|
|
8
|
+
import "./chunk-Q2DFJQGS.js";
|
|
9
|
+
import "./chunk-IFKJO2UR.js";
|
|
10
|
+
import "./chunk-GYB5BOSP.js";
|
|
11
|
+
import "./chunk-MYH6FIBZ.js";
|
|
12
|
+
import "./chunk-GDKLH7ZY.js";
|
|
13
|
+
import "./chunk-KFQGP6VL.js";
|
|
14
|
+
|
|
15
|
+
// src/mcp/server.ts
|
|
16
|
+
var TOOLS = [
|
|
17
|
+
{
|
|
18
|
+
name: "browser_navigate",
|
|
19
|
+
description: "Navigate to a URL and optionally run follow-up commands as a chain. Use this to open pages. Session auto-creates; pass session to isolate state.",
|
|
20
|
+
inputSchema: {
|
|
21
|
+
type: "object",
|
|
22
|
+
properties: {
|
|
23
|
+
url: { type: "string", description: "URL to navigate to" },
|
|
24
|
+
session: { type: "string", description: "Session name (default: mcp)" },
|
|
25
|
+
chain: {
|
|
26
|
+
type: "string",
|
|
27
|
+
description: 'Optional follow-up command chain joined by && (e.g. `click "#btn" && text .result`). Full command list: xbrowser --help'
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
required: ["url"]
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: "browser_act",
|
|
35
|
+
description: "Run an interaction command on the current page: click / fill / type / press / select / check / hover / scroll / wait / eval. Selectors support CSS and text= prefix.",
|
|
36
|
+
inputSchema: {
|
|
37
|
+
type: "object",
|
|
38
|
+
properties: {
|
|
39
|
+
command: {
|
|
40
|
+
type: "string",
|
|
41
|
+
description: "Command word, e.g. click / fill / press / wait / eval"
|
|
42
|
+
},
|
|
43
|
+
args: {
|
|
44
|
+
type: "object",
|
|
45
|
+
description: 'Command parameters, e.g. { selector: "#input", value: "hello" }'
|
|
46
|
+
},
|
|
47
|
+
session: { type: "string", description: "Session name (default: mcp)" }
|
|
48
|
+
},
|
|
49
|
+
required: ["command"]
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: "browser_read",
|
|
54
|
+
description: "Read page content: text / html / title / url. Cheaper than snapshot for plain content.",
|
|
55
|
+
inputSchema: {
|
|
56
|
+
type: "object",
|
|
57
|
+
properties: {
|
|
58
|
+
kind: { type: "string", enum: ["text", "html", "title", "url"] },
|
|
59
|
+
selector: { type: "string", description: "Optional CSS selector to scope the read" },
|
|
60
|
+
session: { type: "string", description: "Session name (default: mcp)" }
|
|
61
|
+
},
|
|
62
|
+
required: ["kind"]
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: "browser_snapshot",
|
|
67
|
+
description: "Accessibility-tree snapshot of interactive elements \u2014 the token-efficient way for an agent to see the page. Returns refs usable with browser_act selectors.",
|
|
68
|
+
inputSchema: {
|
|
69
|
+
type: "object",
|
|
70
|
+
properties: {
|
|
71
|
+
interactiveOnly: { type: "boolean", description: "Only interactive elements (default true)" },
|
|
72
|
+
session: { type: "string", description: "Session name (default: mcp)" }
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: "browser_screenshot",
|
|
78
|
+
description: "Take a screenshot. Returns the file path (use screenshot --base64 chain for inline images).",
|
|
79
|
+
inputSchema: {
|
|
80
|
+
type: "object",
|
|
81
|
+
properties: {
|
|
82
|
+
output: { type: "string", description: "Output PNG path" },
|
|
83
|
+
fullPage: { type: "boolean" },
|
|
84
|
+
session: { type: "string", description: "Session name (default: mcp)" }
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: "browser_network",
|
|
90
|
+
description: "Inspect captured network requests: list / top / analyze.",
|
|
91
|
+
inputSchema: {
|
|
92
|
+
type: "object",
|
|
93
|
+
properties: {
|
|
94
|
+
action: { type: "string", enum: ["list", "top", "analyze"] },
|
|
95
|
+
session: { type: "string", description: "Session name (default: mcp)" }
|
|
96
|
+
},
|
|
97
|
+
required: ["action"]
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
name: "browser_replay",
|
|
102
|
+
description: "Replay a recorded flow with the self-healing engine \u2014 survives site redesigns via selector cascade (semantic candidates \u2192 fingerprints \u2192 coordinates \u2192 knowledge base). Deterministic, no LLM cost.",
|
|
103
|
+
inputSchema: {
|
|
104
|
+
type: "object",
|
|
105
|
+
properties: {
|
|
106
|
+
file: { type: "string", description: "Path to recorded YAML/JSON" },
|
|
107
|
+
slowMo: { type: "number", description: "ms delay between steps" },
|
|
108
|
+
session: { type: "string", description: "Session name (default: mcp)" }
|
|
109
|
+
},
|
|
110
|
+
required: ["file"]
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
];
|
|
114
|
+
var DEFAULT_SESSION = "mcp";
|
|
115
|
+
async function runTool(name, args) {
|
|
116
|
+
const session = args.session || DEFAULT_SESSION;
|
|
117
|
+
const sessionOpts = { sessionName: session };
|
|
118
|
+
const text = (v) => ({ content: [{ type: "text", text: JSON.stringify(v, null, 2) }] });
|
|
119
|
+
switch (name) {
|
|
120
|
+
case "browser_navigate": {
|
|
121
|
+
const url = args.url;
|
|
122
|
+
let chain = `goto ${url}`;
|
|
123
|
+
if (args.chain) chain += ` && ${args.chain}`;
|
|
124
|
+
const result = await executeChain(chain, sessionOpts);
|
|
125
|
+
return text({ ok: result.success, steps: result.steps?.map((s) => ({ command: s.command, success: s.success })) });
|
|
126
|
+
}
|
|
127
|
+
case "browser_act": {
|
|
128
|
+
const command = args.command;
|
|
129
|
+
const cmdArgs = args.args || {};
|
|
130
|
+
const result = await executeCommand(command, cmdArgs, session, {});
|
|
131
|
+
return text(result);
|
|
132
|
+
}
|
|
133
|
+
case "browser_read": {
|
|
134
|
+
const kind = args.kind;
|
|
135
|
+
const params = {};
|
|
136
|
+
if (args.selector) params.selector = args.selector;
|
|
137
|
+
const result = await executeCommand(kind, params, session, {});
|
|
138
|
+
return text(result);
|
|
139
|
+
}
|
|
140
|
+
case "browser_snapshot": {
|
|
141
|
+
const params = { interactiveOnly: args.interactiveOnly !== false };
|
|
142
|
+
const result = await executeCommand("snapshot", params, session, {});
|
|
143
|
+
return text(result);
|
|
144
|
+
}
|
|
145
|
+
case "browser_screenshot": {
|
|
146
|
+
const params = {
|
|
147
|
+
output: args.output || `output/mcp-${Date.now()}.png`,
|
|
148
|
+
...args.fullPage ? { fullPage: true } : {}
|
|
149
|
+
};
|
|
150
|
+
const result = await executeCommand("screenshot", params, session, {});
|
|
151
|
+
return text(result);
|
|
152
|
+
}
|
|
153
|
+
case "browser_network": {
|
|
154
|
+
const result = await executeCommand(`network:${args.action}`, {}, session, {});
|
|
155
|
+
return text(result);
|
|
156
|
+
}
|
|
157
|
+
case "browser_replay": {
|
|
158
|
+
const params = {
|
|
159
|
+
file: args.file,
|
|
160
|
+
...typeof args.slowMo === "number" ? { slowMo: args.slowMo } : {}
|
|
161
|
+
};
|
|
162
|
+
const result = await executeCommand("replay", params, session, {});
|
|
163
|
+
return text(result);
|
|
164
|
+
}
|
|
165
|
+
default:
|
|
166
|
+
throw new Error(`unknown tool: ${name}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
function reply(id, result) {
|
|
170
|
+
process.stdout.write(JSON.stringify({ jsonrpc: "2.0", id, result }) + "\n");
|
|
171
|
+
}
|
|
172
|
+
function replyError(id, code, message) {
|
|
173
|
+
process.stdout.write(JSON.stringify({ jsonrpc: "2.0", id, error: { code, message } }) + "\n");
|
|
174
|
+
}
|
|
175
|
+
async function handleRequest(req) {
|
|
176
|
+
if (req.id === void 0) return;
|
|
177
|
+
switch (req.method) {
|
|
178
|
+
case "initialize":
|
|
179
|
+
reply(req.id, {
|
|
180
|
+
protocolVersion: "2024-11-05",
|
|
181
|
+
capabilities: { tools: {} },
|
|
182
|
+
serverInfo: { name: "xbrowser", version: process.env.npm_package_version || "1.23.0" }
|
|
183
|
+
});
|
|
184
|
+
break;
|
|
185
|
+
case "notifications/initialized":
|
|
186
|
+
break;
|
|
187
|
+
case "tools/list":
|
|
188
|
+
reply(req.id, { tools: TOOLS });
|
|
189
|
+
break;
|
|
190
|
+
case "tools/call": {
|
|
191
|
+
const toolName = req.params.name;
|
|
192
|
+
const toolArgs = req.params.arguments || {};
|
|
193
|
+
try {
|
|
194
|
+
const result = await runTool(toolName, toolArgs);
|
|
195
|
+
reply(req.id, { content: result.content });
|
|
196
|
+
} catch (err) {
|
|
197
|
+
reply(req.id, {
|
|
198
|
+
content: [{ type: "text", text: `Error: ${err.message}` }],
|
|
199
|
+
isError: true
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
default:
|
|
205
|
+
replyError(req.id, -32601, `method not found: ${req.method}`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
var inflight = 0;
|
|
209
|
+
function startMcpStdio() {
|
|
210
|
+
let buffer = "";
|
|
211
|
+
process.stdin.setEncoding("utf8");
|
|
212
|
+
process.stdin.on("data", (chunk) => {
|
|
213
|
+
buffer += chunk;
|
|
214
|
+
let idx;
|
|
215
|
+
while ((idx = buffer.indexOf("\n")) >= 0) {
|
|
216
|
+
const line = buffer.slice(0, idx).trim();
|
|
217
|
+
buffer = buffer.slice(idx + 1);
|
|
218
|
+
if (!line) continue;
|
|
219
|
+
try {
|
|
220
|
+
const req = JSON.parse(line);
|
|
221
|
+
inflight++;
|
|
222
|
+
handleRequest(req).catch((e) => {
|
|
223
|
+
if (req.id !== void 0) replyError(req.id, -32603, e.message);
|
|
224
|
+
}).finally(() => {
|
|
225
|
+
inflight--;
|
|
226
|
+
if (stdinClosed && inflight === 0) process.exit(0);
|
|
227
|
+
});
|
|
228
|
+
} catch {
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
let stdinClosed = false;
|
|
233
|
+
process.stdin.on("end", () => {
|
|
234
|
+
stdinClosed = true;
|
|
235
|
+
if (inflight === 0) process.exit(0);
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
export {
|
|
239
|
+
startMcpStdio
|
|
240
|
+
};
|
|
@@ -222,20 +222,31 @@ var SessionReplayer = class {
|
|
|
222
222
|
break;
|
|
223
223
|
}
|
|
224
224
|
case "scroll": {
|
|
225
|
-
const [dir, distStr] = (action.value || "down:300").split(":");
|
|
226
|
-
const dist = Number(distStr) || 300;
|
|
227
|
-
const sign = dir === "up" ? -1 : dir === "left" ? 0 : 1;
|
|
228
225
|
const selector = await this.resolveAndWait(action).catch(() => void 0);
|
|
229
|
-
if (
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
226
|
+
if (action.value) {
|
|
227
|
+
const [dir, distStr] = action.value.split(":");
|
|
228
|
+
const dist = Number(distStr) || 300;
|
|
229
|
+
const dx = dir === "left" ? -dist : dir === "right" ? dist : 0;
|
|
230
|
+
const dy = dir === "up" ? -dist : dir === "down" ? dist : 0;
|
|
231
|
+
if (selector) {
|
|
232
|
+
await page.evaluate(`
|
|
233
|
+
(function() {
|
|
234
|
+
const el = ${queryJS(selector)};
|
|
235
|
+
if (el) { el.scrollLeft += ${dx}; el.scrollTop += ${dy}; }
|
|
236
|
+
})()
|
|
237
|
+
`).catch(() => {
|
|
238
|
+
});
|
|
239
|
+
} else {
|
|
240
|
+
await page.evaluate(`window.scrollBy(${dx}, ${dy})`).catch(() => {
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
} else if (typeof action.scrollX === "number" || typeof action.scrollY === "number") {
|
|
244
|
+
const sx = action.scrollX ?? 0;
|
|
245
|
+
const sy = action.scrollY ?? 0;
|
|
246
|
+
await page.evaluate(`window.scrollTo(${sx}, ${sy})`).catch(() => {
|
|
236
247
|
});
|
|
237
248
|
} else {
|
|
238
|
-
await page.evaluate(`window.scrollBy(0,
|
|
249
|
+
await page.evaluate(`window.scrollBy(0, 300)`).catch(() => {
|
|
239
250
|
});
|
|
240
251
|
}
|
|
241
252
|
break;
|
|
@@ -406,12 +417,6 @@ var SessionReplayer = class {
|
|
|
406
417
|
}, selector);
|
|
407
418
|
break;
|
|
408
419
|
}
|
|
409
|
-
case "scroll": {
|
|
410
|
-
await page.evaluate(() => {
|
|
411
|
-
window.scrollBy(action.scrollX ?? 0, action.scrollY ?? 0);
|
|
412
|
-
});
|
|
413
|
-
break;
|
|
414
|
-
}
|
|
415
420
|
default:
|
|
416
421
|
break;
|
|
417
422
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xbrowser/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.23.1",
|
|
4
4
|
"description": "Browser automation CLI for web scraping, headless browsing, SEO analysis, and AI agent workflows. A command-line alternative to Playwright, Puppeteer, and Selenium.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -72,30 +72,34 @@
|
|
|
72
72
|
"scripts": {
|
|
73
73
|
"build": "tsup",
|
|
74
74
|
"dev": "tsup --watch",
|
|
75
|
-
"lint": "eslint src/ bin/ --ext .ts",
|
|
75
|
+
"lint": "eslint src/ bin/ tests/ --ext .ts",
|
|
76
|
+
"lint:plugins": "eslint .xcli/plugins/ --ext .ts --max-warnings 0",
|
|
76
77
|
"lint:plugin-contract": "node lint-scripts/check-plugin-contract.mjs && node lint-scripts/check-plugin-status.mjs",
|
|
77
78
|
"lint:plugin-status": "node lint-scripts/check-plugin-status.mjs --update",
|
|
78
79
|
"stealth:check": "vitest run tests/cdp-driver/stealth-health.test.ts tests/cdp-driver/stealth-audit.test.ts tests/cdp-driver/stealth-probe.test.ts",
|
|
79
80
|
"lint:docs": "node lint-scripts/check-docs-consistency.mjs",
|
|
80
81
|
"typecheck": "tsc --noEmit",
|
|
82
|
+
"typecheck:tests": "tsc -p tsconfig.tests.json --noEmit",
|
|
83
|
+
"typecheck:plugins": "tsc -p tsconfig.plugins.json --noEmit",
|
|
81
84
|
"start": "node dist/cli.js",
|
|
82
85
|
"test": "vitest run",
|
|
83
86
|
"test:watch": "vitest",
|
|
84
87
|
"test:e2e": "vitest run --config vitest.e2e.config.ts",
|
|
85
|
-
"
|
|
88
|
+
"test:arena": "vitest run --config vitest.arena.config.ts",
|
|
89
|
+
"validate": "npm run typecheck && npm run typecheck:plugins && npm run lint && npm run lint:plugins && npm run lint:docs && npm run lint:plugin-contract && npm run build && npm test",
|
|
86
90
|
"prepare": "husky"
|
|
87
91
|
},
|
|
88
92
|
"dependencies": {
|
|
89
|
-
"@dyyz1993/xcli-core": "^0.19.
|
|
93
|
+
"@dyyz1993/xcli-core": "^0.19.1",
|
|
90
94
|
"@types/react-syntax-highlighter": "^15.5.13",
|
|
91
95
|
"@types/turndown": "^5.0.6",
|
|
92
96
|
"cheerio": "^1.2.0",
|
|
93
|
-
"imapflow": "^1.
|
|
97
|
+
"imapflow": "^1.7.8",
|
|
94
98
|
"lucide-react": "^1.14.0",
|
|
95
99
|
"react-markdown": "^10.1.0",
|
|
96
100
|
"react-syntax-highlighter": "^16.1.1",
|
|
97
101
|
"remark-gfm": "^4.0.1",
|
|
98
|
-
"sharp": "^0.
|
|
102
|
+
"sharp": "^0.35.4",
|
|
99
103
|
"turndown": "^7.2.4",
|
|
100
104
|
"turndown-plugin-gfm": "^1.0.2",
|
|
101
105
|
"ws": "^8.21.0",
|
|
@@ -124,6 +128,6 @@
|
|
|
124
128
|
]
|
|
125
129
|
},
|
|
126
130
|
"optionalDependencies": {
|
|
127
|
-
"undici": "^7.
|
|
131
|
+
"undici": "^7.29.1"
|
|
128
132
|
}
|
|
129
133
|
}
|