Skip to content

Commit 51811fa

Browse files
committed
fix(template): sync gateway remote token + add devices commands (with validation)
Adds gateway.remote.token setup to prevent Control UI token mismatch and exposes devices list/approve in the debug console. Validates requestId and adds a small unit test.
1 parent 91f9331 commit 51811fa

3 files changed

Lines changed: 52 additions & 0 deletions

File tree

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"dev": "node src/server.js",
1010
"start": "node src/server.js",
1111
"lint": "node -c src/server.js",
12+
"test": "node --test",
1213
"smoke": "node scripts/smoke.js"
1314
},
1415
"dependencies": {

src/server.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,8 @@ app.get("/setup", requireSetupAuth, (_req, res) => {
309309
<option value="openclaw.logs.tail">openclaw logs --tail N</option>
310310
<option value="openclaw.config.get">openclaw config get &lt;path&gt;</option>
311311
<option value="openclaw.version">openclaw --version</option>
312+
<option value="openclaw.devices.list">openclaw devices list</option>
313+
<option value="openclaw.devices.approve">openclaw devices approve &lt;requestId&gt;</option>
312314
</select>
313315
<input id="consoleArg" placeholder="Optional arg (e.g. 200, gateway.port)" style="flex: 1" />
314316
<button id="consoleRun" style="background:#0f172a">Run</button>
@@ -551,8 +553,11 @@ app.post("/setup/api/run", requireSetupAuth, async (req, res) => {
551553
if (ok) {
552554
// Ensure gateway token is written into config so the browser UI can authenticate reliably.
553555
// (We also enforce loopback bind since the wrapper proxies externally.)
556+
// IMPORTANT: Set both gateway.auth.token (server-side) and gateway.remote.token (client-side)
557+
// to the same value so the Control UI can connect without "token mismatch" errors.
554558
await runCmd(OPENCLAW_NODE, clawArgs(["config", "set", "gateway.auth.mode", "token"]));
555559
await runCmd(OPENCLAW_NODE, clawArgs(["config", "set", "gateway.auth.token", OPENCLAW_GATEWAY_TOKEN]));
560+
await runCmd(OPENCLAW_NODE, clawArgs(["config", "set", "gateway.remote.token", OPENCLAW_GATEWAY_TOKEN]));
556561
await runCmd(OPENCLAW_NODE, clawArgs(["config", "set", "gateway.bind", "loopback"]));
557562
await runCmd(OPENCLAW_NODE, clawArgs(["config", "set", "gateway.port", String(INTERNAL_GATEWAY_PORT)]));
558563

@@ -688,6 +693,10 @@ const ALLOWED_CONSOLE_COMMANDS = new Set([
688693
"openclaw.doctor",
689694
"openclaw.logs.tail",
690695
"openclaw.config.get",
696+
697+
// Device management (for fixing "disconnected (1008): pairing required")
698+
"openclaw.devices.list",
699+
"openclaw.devices.approve",
691700
]);
692701

693702
app.post("/setup/api/console/run", requireSetupAuth, async (req, res) => {
@@ -744,6 +753,23 @@ app.post("/setup/api/console/run", requireSetupAuth, async (req, res) => {
744753
return res.status(r.code === 0 ? 200 : 500).json({ ok: r.code === 0, output: redactSecrets(r.output) });
745754
}
746755

756+
// Device management commands (for fixing "disconnected (1008): pairing required")
757+
if (cmd === "openclaw.devices.list") {
758+
const r = await runCmd(OPENCLAW_NODE, clawArgs(["devices", "list"]));
759+
return res.status(r.code === 0 ? 200 : 500).json({ ok: r.code === 0, output: redactSecrets(r.output) });
760+
}
761+
if (cmd === "openclaw.devices.approve") {
762+
const requestId = String(arg || "").trim();
763+
if (!requestId) {
764+
return res.status(400).json({ ok: false, error: "Missing device request ID" });
765+
}
766+
if (!/^[A-Za-z0-9_-]+$/.test(requestId)) {
767+
return res.status(400).json({ ok: false, error: "Invalid device request ID" });
768+
}
769+
const r = await runCmd(OPENCLAW_NODE, clawArgs(["devices", "approve", requestId]));
770+
return res.status(r.code === 0 ? 200 : 500).json({ ok: r.code === 0, output: redactSecrets(r.output) });
771+
}
772+
747773
return res.status(400).json({ ok: false, error: "Unhandled command" });
748774
} catch (err) {
749775
return res.status(500).json({ ok: false, error: String(err) });
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import test from "node:test";
2+
import assert from "node:assert/strict";
3+
4+
function validateRequestId(raw) {
5+
const requestId = String(raw || "").trim();
6+
if (!requestId) return { ok: false, error: "Missing device request ID" };
7+
if (!/^[A-Za-z0-9_-]+$/.test(requestId)) return { ok: false, error: "Invalid device request ID" };
8+
return { ok: true };
9+
}
10+
11+
test("devices approve requestId validation: missing", () => {
12+
assert.deepEqual(validateRequestId(""), { ok: false, error: "Missing device request ID" });
13+
assert.deepEqual(validateRequestId(" "), { ok: false, error: "Missing device request ID" });
14+
});
15+
16+
test("devices approve requestId validation: rejects weird chars", () => {
17+
assert.equal(validateRequestId("../../etc/passwd").ok, false);
18+
assert.equal(validateRequestId("abc def").ok, false);
19+
assert.equal(validateRequestId("abc$def").ok, false);
20+
});
21+
22+
test("devices approve requestId validation: allows typical ids", () => {
23+
assert.equal(validateRequestId("abc123").ok, true);
24+
assert.equal(validateRequestId("req_123-ABC").ok, true);
25+
});

0 commit comments

Comments
 (0)