forked from MCERQUA/OpenVoiceUI
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathauto-approve-devices.js
More file actions
112 lines (101 loc) · 4 KB
/
Copy pathauto-approve-devices.js
File metadata and controls
112 lines (101 loc) · 4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#!/usr/bin/env node
// auto-approve-devices.js — Approve all pending OpenClaw devices.
// Runs on the host, execs into the openclaw container to move pending → paired.
// Called by start.js after containers are healthy.
//
// Polls for up to 2 minutes (24 retries × 5s) because OpenVoiceUI doesn't
// connect to OpenClaw until the user opens the browser — which happens AFTER
// Pinokio shows the "Open" button. We need to wait for that.
const { execSync } = require("child_process");
const COMPOSE = "docker compose -f docker-compose.yml -f docker-compose.local.yml";
const MAX_ATTEMPTS = 24; // 24 × 5s = 2 minutes
const DELAY_MS = 5000;
// MSYS_NO_PATHCONV=1 prevents Git Bash on Windows from converting /container/paths
const EXEC_ENV = Object.assign({}, process.env, { MSYS_NO_PATHCONV: "1" });
// Node one-liner that runs INSIDE the openclaw container
const script = `
const fs = require('fs');
try {
const pendingPath = '/root/.openclaw/devices/pending.json';
const pairedPath = '/root/.openclaw/devices/paired.json';
let pending = {};
let paired = {};
try { pending = JSON.parse(fs.readFileSync(pendingPath, 'utf8')); } catch(e) {}
try { paired = JSON.parse(fs.readFileSync(pairedPath, 'utf8')); } catch(e) {}
const pairedCount = Object.keys(paired).length;
let count = 0;
for (const entry of Object.values(pending)) {
if (entry.deviceId && entry.publicKey) {
paired[entry.deviceId] = {
publicKey: entry.publicKey,
name: entry.name || 'auto-approved',
role: entry.role || 'operator',
roles: entry.roles || ['operator'],
scopes: ['operator.admin', 'operator.read', 'operator.write'],
approvedScopes: ['operator.admin', 'operator.read', 'operator.write'],
paired: true,
pairedAt: new Date().toISOString(),
autoApproved: true,
};
count++;
}
}
if (count > 0) {
fs.writeFileSync(pairedPath, JSON.stringify(paired, null, 2));
fs.writeFileSync(pendingPath, '{}');
console.log('APPROVED:' + count);
} else if (pairedCount > 0) {
console.log('ALREADY_PAIRED:' + pairedCount);
} else {
console.log('APPROVED:0');
}
} catch(e) {
console.log('ERROR:' + e.message);
}
`.replace(/\n/g, " ").trim();
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function run() {
console.log(" Waiting for browser to connect (up to 2 minutes)...");
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
try {
const result = execSync(
`${COMPOSE} exec -T openclaw node -e "${script.replace(/"/g, '\\"')}"`,
{ encoding: "utf8", timeout: 15000, env: EXEC_ENV }
).trim();
// New device(s) approved
const approvedMatch = result.match(/APPROVED:(\d+)/);
if (approvedMatch && parseInt(approvedMatch[1]) > 0) {
console.log(` Auto-approved ${approvedMatch[1]} device(s) — ready to use!`);
return;
}
// Device already paired from a previous session
const pairedMatch = result.match(/ALREADY_PAIRED:(\d+)/);
if (pairedMatch && parseInt(pairedMatch[1]) > 0) {
console.log(` Device already paired (${pairedMatch[1]} device(s)) — ready to use!`);
return;
}
// Nothing yet — keep polling
if (attempt < MAX_ATTEMPTS) {
// Only log every 4th attempt to avoid spam
if (attempt % 4 === 0) {
const remaining = Math.round((MAX_ATTEMPTS - attempt) * DELAY_MS / 1000);
console.log(` Still waiting for browser connection... (${remaining}s remaining)`);
}
await sleep(DELAY_MS);
} else {
console.log(" Timeout: No device connected after 2 minutes.");
console.log(" If you see NOT_PAIRED errors, open the browser and restart the app.");
}
} catch (e) {
if (attempt < MAX_ATTEMPTS) {
// Container might still be starting — retry silently
await sleep(DELAY_MS);
} else {
console.log(" Device auto-approve failed:", e.message.split("\n")[0]);
}
}
}
}
run();