openclaw-navigator 4.0.0 → 4.2.0
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 +125 -1
- package/package.json +1 -1
package/cli.mjs
CHANGED
|
@@ -344,6 +344,94 @@ function handleRequest(req, res) {
|
|
|
344
344
|
return;
|
|
345
345
|
}
|
|
346
346
|
|
|
347
|
+
// ── GET /navigator/capabilities ──
|
|
348
|
+
if (req.method === "GET" && path === "/navigator/capabilities") {
|
|
349
|
+
sendJSON(res, 200, {
|
|
350
|
+
ok: true,
|
|
351
|
+
version: "4.0.0",
|
|
352
|
+
commands: [
|
|
353
|
+
{
|
|
354
|
+
name: "navigate",
|
|
355
|
+
description: "Navigate to a URL in the OpenClaw group",
|
|
356
|
+
payload: { url: "required string", browserID: "optional string — target a specific tab" },
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
name: "tabs.list",
|
|
360
|
+
description: "List all tabs in the OpenClaw group",
|
|
361
|
+
payload: {},
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
name: "tabs.open",
|
|
365
|
+
description: "Open a new tab in the OpenClaw group",
|
|
366
|
+
payload: { url: "optional string — URL to load (blank tab if omitted)" },
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
name: "tabs.close",
|
|
370
|
+
description: "Close a tab in the OpenClaw group",
|
|
371
|
+
payload: { tabIndex: "optional int", tabId: "optional string — close by index or ID" },
|
|
372
|
+
},
|
|
373
|
+
{
|
|
374
|
+
name: "page.content",
|
|
375
|
+
description: "Get the text content of the active tab",
|
|
376
|
+
payload: {},
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
name: "snapshot",
|
|
380
|
+
description: "Get a snapshot of the OpenClaw group state",
|
|
381
|
+
payload: {},
|
|
382
|
+
},
|
|
383
|
+
{
|
|
384
|
+
name: "click",
|
|
385
|
+
description: "Click an element by CSS selector",
|
|
386
|
+
payload: { selector: "required string — CSS selector" },
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
name: "input.fill",
|
|
390
|
+
description: "Fill an input field with a value",
|
|
391
|
+
payload: {
|
|
392
|
+
selector: "required string — CSS selector",
|
|
393
|
+
value: "required string — text to fill",
|
|
394
|
+
},
|
|
395
|
+
},
|
|
396
|
+
{
|
|
397
|
+
name: "input.submit",
|
|
398
|
+
description: "Submit a form",
|
|
399
|
+
payload: { selector: "optional string — CSS selector of form or element inside form" },
|
|
400
|
+
},
|
|
401
|
+
{
|
|
402
|
+
name: "scroll",
|
|
403
|
+
description: "Scroll the page",
|
|
404
|
+
payload: {
|
|
405
|
+
direction: "optional string — 'up', 'down', 'top', 'bottom'",
|
|
406
|
+
x: "optional int — horizontal pixels",
|
|
407
|
+
y: "optional int — vertical pixels",
|
|
408
|
+
},
|
|
409
|
+
},
|
|
410
|
+
{
|
|
411
|
+
name: "execute",
|
|
412
|
+
description: "Execute arbitrary JavaScript and return the result",
|
|
413
|
+
payload: { code: "required string — JavaScript code to execute" },
|
|
414
|
+
},
|
|
415
|
+
{
|
|
416
|
+
name: "page.html",
|
|
417
|
+
description: "Get the full HTML of the page",
|
|
418
|
+
payload: {},
|
|
419
|
+
},
|
|
420
|
+
{
|
|
421
|
+
name: "dom.query",
|
|
422
|
+
description: "Query a DOM element for its tag, text, and attributes",
|
|
423
|
+
payload: { selector: "required string — CSS selector" },
|
|
424
|
+
},
|
|
425
|
+
{
|
|
426
|
+
name: "wait.ready",
|
|
427
|
+
description: "Wait until document.readyState is 'complete'",
|
|
428
|
+
payload: { timeout: "optional int — milliseconds (default: 10000)" },
|
|
429
|
+
},
|
|
430
|
+
],
|
|
431
|
+
});
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
|
|
347
435
|
// ── GET /navigator/events ──
|
|
348
436
|
if (req.method === "GET" && path === "/navigator/events") {
|
|
349
437
|
const limit = parseInt(url.searchParams.get("limit") ?? "50", 10);
|
|
@@ -361,6 +449,32 @@ function handleRequest(req, res) {
|
|
|
361
449
|
sendJSON(res, 404, { ok: false, error: "Not found" });
|
|
362
450
|
}
|
|
363
451
|
|
|
452
|
+
// ── Relay registration ────────────────────────────────────────────────────
|
|
453
|
+
|
|
454
|
+
const RELAY_URL = "https://navigator-relay.nav8-relay.workers.dev";
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* Register pairing code with the relay so Navigator can resolve it remotely.
|
|
458
|
+
* Returns true on success, false on failure (relay unavailable, etc.)
|
|
459
|
+
*/
|
|
460
|
+
async function registerWithRelay(code, url, token, name) {
|
|
461
|
+
try {
|
|
462
|
+
const controller = new AbortController();
|
|
463
|
+
const timeout = setTimeout(() => controller.abort(), 5000);
|
|
464
|
+
const res = await fetch(`${RELAY_URL}/register`, {
|
|
465
|
+
method: "POST",
|
|
466
|
+
headers: { "Content-Type": "application/json" },
|
|
467
|
+
body: JSON.stringify({ code, url, token, name }),
|
|
468
|
+
signal: controller.signal,
|
|
469
|
+
});
|
|
470
|
+
clearTimeout(timeout);
|
|
471
|
+
const data = await res.json();
|
|
472
|
+
return data.ok === true;
|
|
473
|
+
} catch {
|
|
474
|
+
return false;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
364
478
|
// ── Display helpers ────────────────────────────────────────────────────────
|
|
365
479
|
|
|
366
480
|
function showPairingCode(code) {
|
|
@@ -503,7 +617,17 @@ ${BOLD}How it works:${RESET}
|
|
|
503
617
|
pairingCode = generatePairingCode();
|
|
504
618
|
pairingData = { url: gatewayURL, token, name: displayName };
|
|
505
619
|
|
|
506
|
-
// ── Step 4:
|
|
620
|
+
// ── Step 4: Register with relay (for remote pairing) ────────────────
|
|
621
|
+
if (tunnelURL) {
|
|
622
|
+
const relayOk = await registerWithRelay(pairingCode, gatewayURL, token, displayName);
|
|
623
|
+
if (relayOk) {
|
|
624
|
+
ok("Code registered with relay — works from any device");
|
|
625
|
+
} else {
|
|
626
|
+
warn("Relay unavailable — code works on same machine only");
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
// ── Step 5: Show connection info ──────────────────────────────────────
|
|
507
631
|
showPairingCode(pairingCode);
|
|
508
632
|
|
|
509
633
|
// Deep link (always show as fallback)
|