Ctrl K
ComunidadGuías…

auto-approve-devices.js

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
#!/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();