openclaw-navigator 4.3.1 → 4.3.3
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/cli.mjs +26 -11
- package/mcp.mjs +18 -54
- package/package.json +1 -1
package/cli.mjs
CHANGED
|
@@ -18,8 +18,8 @@ import { randomUUID } from "node:crypto";
|
|
|
18
18
|
import { existsSync } from "node:fs";
|
|
19
19
|
import { createServer } from "node:http";
|
|
20
20
|
import { networkInterfaces, hostname, userInfo } from "node:os";
|
|
21
|
-
import { fileURLToPath } from "node:url";
|
|
22
21
|
import { dirname, join } from "node:path";
|
|
22
|
+
import { fileURLToPath } from "node:url";
|
|
23
23
|
// readline reserved for future interactive mode
|
|
24
24
|
|
|
25
25
|
// ── Colors (ANSI) ──────────────────────────────────────────────────────────
|
|
@@ -687,18 +687,33 @@ ${BOLD}How it works:${RESET}
|
|
|
687
687
|
// This makes the 15 navigator tools discoverable by the OC agent.
|
|
688
688
|
// Uses --scope home so it persists in ~/.mcporter/mcporter.json.
|
|
689
689
|
try {
|
|
690
|
-
const reg = spawn(
|
|
691
|
-
"
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
690
|
+
const reg = spawn(
|
|
691
|
+
"mcporter",
|
|
692
|
+
[
|
|
693
|
+
"config",
|
|
694
|
+
"add",
|
|
695
|
+
"navigator",
|
|
696
|
+
"--command",
|
|
697
|
+
process.execPath,
|
|
698
|
+
"--arg",
|
|
699
|
+
mcpPath,
|
|
700
|
+
"--env",
|
|
701
|
+
`NAVIGATOR_BRIDGE_URL=http://localhost:${port}`,
|
|
702
|
+
"--description",
|
|
703
|
+
"Navigator browser control (15 tools: navigate, click, fill, read, etc.)",
|
|
704
|
+
"--scope",
|
|
705
|
+
"home",
|
|
706
|
+
],
|
|
707
|
+
{ stdio: ["ignore", "pipe", "pipe"] },
|
|
708
|
+
);
|
|
698
709
|
|
|
699
710
|
let regOut = "";
|
|
700
|
-
reg.stdout?.on("data", (d) => {
|
|
701
|
-
|
|
711
|
+
reg.stdout?.on("data", (d) => {
|
|
712
|
+
regOut += d.toString();
|
|
713
|
+
});
|
|
714
|
+
reg.stderr?.on("data", (d) => {
|
|
715
|
+
regOut += d.toString();
|
|
716
|
+
});
|
|
702
717
|
|
|
703
718
|
reg.on("close", (code) => {
|
|
704
719
|
if (code === 0) {
|
package/mcp.mjs
CHANGED
|
@@ -16,15 +16,11 @@
|
|
|
16
16
|
|
|
17
17
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
18
18
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
19
|
-
import {
|
|
20
|
-
ListToolsRequestSchema,
|
|
21
|
-
CallToolRequestSchema,
|
|
22
|
-
} from "@modelcontextprotocol/sdk/types.js";
|
|
19
|
+
import { ListToolsRequestSchema, CallToolRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
23
20
|
|
|
24
21
|
// ── Configuration ─────────────────────────────────────────────────────────
|
|
25
22
|
|
|
26
|
-
const BRIDGE_URL =
|
|
27
|
-
process.env.NAVIGATOR_BRIDGE_URL ?? "http://localhost:18790";
|
|
23
|
+
const BRIDGE_URL = process.env.NAVIGATOR_BRIDGE_URL ?? "http://localhost:18790";
|
|
28
24
|
const POLL_INTERVAL_MS = 500;
|
|
29
25
|
const DEFAULT_TIMEOUT_MS = 15_000;
|
|
30
26
|
const WAIT_READY_TIMEOUT_MS = 20_000;
|
|
@@ -65,11 +61,7 @@ async function bridgePost(path, body) {
|
|
|
65
61
|
* @param {number} [timeout] - max wait in ms
|
|
66
62
|
* @returns {Promise<object|null>} matched event or null on timeout
|
|
67
63
|
*/
|
|
68
|
-
async function waitForEvent(
|
|
69
|
-
predicate,
|
|
70
|
-
startTime,
|
|
71
|
-
timeout = DEFAULT_TIMEOUT_MS,
|
|
72
|
-
) {
|
|
64
|
+
async function waitForEvent(predicate, startTime, timeout = DEFAULT_TIMEOUT_MS) {
|
|
73
65
|
const deadline = Date.now() + timeout;
|
|
74
66
|
|
|
75
67
|
while (Date.now() < deadline) {
|
|
@@ -128,8 +120,7 @@ async function sendCommand(command, payload, poll) {
|
|
|
128
120
|
if (poll.eventType) {
|
|
129
121
|
predicate = (e) => e.type === poll.eventType;
|
|
130
122
|
} else if (poll.commandName) {
|
|
131
|
-
predicate = (e) =>
|
|
132
|
-
e.type === "command.result" && e.data?.command === poll.commandName;
|
|
123
|
+
predicate = (e) => e.type === "command.result" && e.data?.command === poll.commandName;
|
|
133
124
|
} else {
|
|
134
125
|
return { ok: true, commandId: postResult.commandId, command };
|
|
135
126
|
}
|
|
@@ -198,8 +189,7 @@ const TOOLS = [
|
|
|
198
189
|
},
|
|
199
190
|
{
|
|
200
191
|
name: "navigator_close_tab",
|
|
201
|
-
description:
|
|
202
|
-
"Close a browser tab by its index or ID. Returns immediately after queuing.",
|
|
192
|
+
description: "Close a browser tab by its index or ID. Returns immediately after queuing.",
|
|
203
193
|
inputSchema: {
|
|
204
194
|
type: "object",
|
|
205
195
|
properties: {
|
|
@@ -212,20 +202,17 @@ const TOOLS = [
|
|
|
212
202
|
// ── Poll by event type ──
|
|
213
203
|
{
|
|
214
204
|
name: "navigator_list_tabs",
|
|
215
|
-
description:
|
|
216
|
-
"List all open tabs in the Navigator browser with their URLs, titles, and IDs.",
|
|
205
|
+
description: "List all open tabs in the Navigator browser with their URLs, titles, and IDs.",
|
|
217
206
|
inputSchema: { type: "object", properties: {} },
|
|
218
207
|
},
|
|
219
208
|
{
|
|
220
209
|
name: "navigator_snapshot",
|
|
221
|
-
description:
|
|
222
|
-
"Get a snapshot of the browser state — active tab, URL, title, and full tab list.",
|
|
210
|
+
description: "Get a snapshot of the browser state — active tab, URL, title, and full tab list.",
|
|
223
211
|
inputSchema: { type: "object", properties: {} },
|
|
224
212
|
},
|
|
225
213
|
{
|
|
226
214
|
name: "navigator_get_text",
|
|
227
|
-
description:
|
|
228
|
-
"Get the text content of the active page (equivalent to document.body.innerText).",
|
|
215
|
+
description: "Get the text content of the active page (equivalent to document.body.innerText).",
|
|
229
216
|
inputSchema: { type: "object", properties: {} },
|
|
230
217
|
},
|
|
231
218
|
|
|
@@ -247,8 +234,7 @@ const TOOLS = [
|
|
|
247
234
|
},
|
|
248
235
|
{
|
|
249
236
|
name: "navigator_fill",
|
|
250
|
-
description:
|
|
251
|
-
"Fill an input field with text. Waits for confirmation from the browser.",
|
|
237
|
+
description: "Fill an input field with text. Waits for confirmation from the browser.",
|
|
252
238
|
inputSchema: {
|
|
253
239
|
type: "object",
|
|
254
240
|
properties: {
|
|
@@ -273,16 +259,14 @@ const TOOLS = [
|
|
|
273
259
|
properties: {
|
|
274
260
|
selector: {
|
|
275
261
|
type: "string",
|
|
276
|
-
description:
|
|
277
|
-
"CSS selector of the form or an element inside it (submits closest form)",
|
|
262
|
+
description: "CSS selector of the form or an element inside it (submits closest form)",
|
|
278
263
|
},
|
|
279
264
|
},
|
|
280
265
|
},
|
|
281
266
|
},
|
|
282
267
|
{
|
|
283
268
|
name: "navigator_scroll",
|
|
284
|
-
description:
|
|
285
|
-
"Scroll the page in a direction (up/down/top/bottom) or by exact pixel offsets.",
|
|
269
|
+
description: "Scroll the page in a direction (up/down/top/bottom) or by exact pixel offsets.",
|
|
286
270
|
inputSchema: {
|
|
287
271
|
type: "object",
|
|
288
272
|
properties: {
|
|
@@ -389,28 +373,18 @@ async function handleTool(name, args) {
|
|
|
389
373
|
|
|
390
374
|
// ── Poll by event type ──
|
|
391
375
|
case "navigator_list_tabs":
|
|
392
|
-
return jsonResult(
|
|
393
|
-
await sendCommand("tabs.list", {}, { eventType: "tabs.list" }),
|
|
394
|
-
);
|
|
376
|
+
return jsonResult(await sendCommand("tabs.list", {}, { eventType: "tabs.list" }));
|
|
395
377
|
|
|
396
378
|
case "navigator_snapshot":
|
|
397
|
-
return jsonResult(
|
|
398
|
-
await sendCommand("snapshot", {}, { eventType: "snapshot" }),
|
|
399
|
-
);
|
|
379
|
+
return jsonResult(await sendCommand("snapshot", {}, { eventType: "snapshot" }));
|
|
400
380
|
|
|
401
381
|
case "navigator_get_text":
|
|
402
|
-
return jsonResult(
|
|
403
|
-
await sendCommand("page.content", {}, { eventType: "page.content" }),
|
|
404
|
-
);
|
|
382
|
+
return jsonResult(await sendCommand("page.content", {}, { eventType: "page.content" }));
|
|
405
383
|
|
|
406
384
|
// ── Poll by command.result ──
|
|
407
385
|
case "navigator_click":
|
|
408
386
|
return jsonResult(
|
|
409
|
-
await sendCommand(
|
|
410
|
-
"click",
|
|
411
|
-
{ selector: args.selector },
|
|
412
|
-
{ commandName: "click" },
|
|
413
|
-
),
|
|
387
|
+
await sendCommand("click", { selector: args.selector }, { commandName: "click" }),
|
|
414
388
|
);
|
|
415
389
|
|
|
416
390
|
case "navigator_fill":
|
|
@@ -442,25 +416,15 @@ async function handleTool(name, args) {
|
|
|
442
416
|
|
|
443
417
|
case "navigator_execute_js":
|
|
444
418
|
return jsonResult(
|
|
445
|
-
await sendCommand(
|
|
446
|
-
"execute",
|
|
447
|
-
{ code: args.code },
|
|
448
|
-
{ commandName: "execute" },
|
|
449
|
-
),
|
|
419
|
+
await sendCommand("execute", { code: args.code }, { commandName: "execute" }),
|
|
450
420
|
);
|
|
451
421
|
|
|
452
422
|
case "navigator_get_html":
|
|
453
|
-
return jsonResult(
|
|
454
|
-
await sendCommand("page.html", {}, { commandName: "page.html" }),
|
|
455
|
-
);
|
|
423
|
+
return jsonResult(await sendCommand("page.html", {}, { commandName: "page.html" }));
|
|
456
424
|
|
|
457
425
|
case "navigator_query_element":
|
|
458
426
|
return jsonResult(
|
|
459
|
-
await sendCommand(
|
|
460
|
-
"dom.query",
|
|
461
|
-
{ selector: args.selector },
|
|
462
|
-
{ commandName: "dom.query" },
|
|
463
|
-
),
|
|
427
|
+
await sendCommand("dom.query", { selector: args.selector }, { commandName: "dom.query" }),
|
|
464
428
|
);
|
|
465
429
|
|
|
466
430
|
case "navigator_wait_ready":
|