openclaw-navigator 5.7.2 → 5.7.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.
Files changed (2) hide show
  1. package/cli.mjs +48 -7
  2. package/package.json +1 -1
package/cli.mjs CHANGED
@@ -1703,6 +1703,23 @@ async function registerWithRelay(code, url, token, name) {
1703
1703
  });
1704
1704
  clearTimeout(timeout);
1705
1705
  const data = await res.json();
1706
+
1707
+ // Also register with token as the lookup key — so Navigator can
1708
+ // resolve by saved token even after the pairing code rotates.
1709
+ // Uses first 8 chars of token as a "token code" (avoids relay key limits).
1710
+ const tokenCode = `T${token.substring(0, 7)}`;
1711
+ try {
1712
+ const c2 = new AbortController();
1713
+ const t2 = setTimeout(() => c2.abort(), 5000);
1714
+ await fetch(`${RELAY_URL}/register`, {
1715
+ method: "POST",
1716
+ headers: { "Content-Type": "application/json" },
1717
+ body: JSON.stringify({ code: tokenCode, url, token, name }),
1718
+ signal: c2.signal,
1719
+ });
1720
+ clearTimeout(t2);
1721
+ } catch { /* non-critical */ }
1722
+
1706
1723
  return data.ok === true;
1707
1724
  } catch {
1708
1725
  return false;
@@ -1927,25 +1944,30 @@ module.exports = {
1927
1944
  await killPort(ocUIPort);
1928
1945
  startWebUI();
1929
1946
 
1930
- // ── Step 2: Persistent identity ─────────────────────────────────────
1931
- // Reuse the same pairing code + token across restarts so Navigator
1932
- // doesn't need to re-pair. The code resolves to the NEW tunnel URL
1933
- // via the relay, so everything reconnects automatically.
1947
+ // ── Step 2: Identity persistent token, rotating code ──────────────
1948
+ // Token is persistent: Navigator stores it after first pairing and uses
1949
+ // it for all future requests. This is the real auth credential.
1950
+ // Pairing code rotates every startup: it's a one-time handshake to
1951
+ // exchange the token. A new code every time means nobody can reuse
1952
+ // an old code to hijack the connection.
1934
1953
  const displayName = hostname().replace(/\.local$/, "");
1935
1954
  let token;
1936
1955
 
1937
1956
  const savedIdentity = freshIdentity ? null : loadBridgeIdentity();
1938
1957
  if (savedIdentity) {
1939
- pairingCode = savedIdentity.pairingCode;
1958
+ // Keep the token (Navigator already has it) but rotate the code
1940
1959
  token = savedIdentity.token;
1941
1960
  validTokens.add(token);
1942
- ok(`Restored pairing code: ${BOLD}${GREEN}${pairingCode}${RESET} (same as last session)`);
1961
+ pairingCode = generatePairingCode(); // fresh code every startup
1962
+ saveBridgeIdentity(pairingCode, token, displayName);
1963
+ ok(`Token restored (Navigator will auto-reconnect)`);
1964
+ ok(`New pairing code: ${BOLD}${GREEN}${pairingCode}${RESET} (rotated for security)`);
1943
1965
  } else {
1944
1966
  token = randomUUID().replace(/-/g, "");
1945
1967
  validTokens.add(token);
1946
1968
  pairingCode = generatePairingCode();
1947
1969
  saveBridgeIdentity(pairingCode, token, displayName);
1948
- ok(`New pairing code generated: ${BOLD}${GREEN}${pairingCode}${RESET}`);
1970
+ ok(`New pairing code: ${BOLD}${GREEN}${pairingCode}${RESET}`);
1949
1971
  }
1950
1972
 
1951
1973
  let gatewayURL = `http://localhost:${port}`;
@@ -2165,6 +2187,25 @@ module.exports = {
2165
2187
  console.log(`${BOLD}${GREEN}Bridge is running.${RESET} Waiting for Navigator to connect...`);
2166
2188
  console.log(`${DIM}Press Ctrl+C to stop.${RESET}\n`);
2167
2189
 
2190
+ // ── Relay heartbeat — re-register every 5 minutes to prevent expiry ──
2191
+ // Cloudflare KV entries may expire. This keeps our pairing code fresh
2192
+ // so Navigator can always resolve it, even after long idle periods.
2193
+ if (tunnelURL || gatewayURL) {
2194
+ const relayHeartbeat = async () => {
2195
+ const currentURL = tunnelURL || gatewayURL;
2196
+ const ok = await registerWithRelay(pairingCode, currentURL, token, displayName);
2197
+ if (ok) {
2198
+ info(` ${DIM}Relay heartbeat: code ${pairingCode} refreshed${RESET}`);
2199
+ } else {
2200
+ warn(`Relay heartbeat failed — Navigator may not be able to resolve code`);
2201
+ }
2202
+ };
2203
+ // First heartbeat immediately (in case initial registration failed)
2204
+ relayHeartbeat();
2205
+ // Then every 5 minutes
2206
+ setInterval(relayHeartbeat, 5 * 60 * 1000);
2207
+ }
2208
+
2168
2209
  // ── Step 6 (optional): Start MCP server + register with mcporter ────
2169
2210
  let mcpProcess = null;
2170
2211
  if (withMcp) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-navigator",
3
- "version": "5.7.2",
3
+ "version": "5.7.4",
4
4
  "description": "One-command bridge + tunnel for the Navigator browser — works on any machine, any OS",
5
5
  "keywords": [
6
6
  "browser",