Ctrl K
ComunidadGuías…

docker/context-profiles/run-sandbox.js

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

Archivo original ↗Preguntar a KiBot
#!/usr/bin/env node
'use strict';

// The installed tier router owns provisioning and cleanup. This acceptance
// driver transfers only an npm archive and a fixed verifier into the VM.
const assert = require('node:assert/strict');
const crypto = require('node:crypto');
const fs = require('node:fs');
const http = require('node:http');
const net = require('node:net');
const os = require('node:os');
const path = require('node:path');
const { spawn } = require('node:child_process');

const NODE_VERSION = '22.18.0';
const NODE_SHA = '2c12913cba67af77ded8a399df3fd91c2e7f8628c7079da40bb9ff33bf00dfc0';
const digest = bytes => crypto.createHash('sha256').update(bytes).digest('hex');
const quote = text => `'${String(text).replace(/'/g, `'"'"'`)}'`;

function command(executable, args, cwd, timeout = 900000) {
  return new Promise((resolve, reject) => {
    const child = spawn(executable, args, { cwd, env: process.env, stdio: ['ignore', 'pipe', 'pipe'], shell: false });
    let stdout = ''; let stderr = ''; let size = 0; let termination = null; let settled = false;
    const stop = reason => {
      if (!termination) termination = reason;
      child.kill('SIGKILL');
    };
    const timer = setTimeout(() => stop('timeout'), timeout);
    const collect = key => chunk => {
      size += chunk.length;
      if (size > 24 * 1024 * 1024) { stop('output-limit'); return; }
      if (key === 'stdout') stdout += chunk; else stderr += chunk;
    };
    child.stdout.on('data', collect('stdout')); child.stderr.on('data', collect('stderr'));
    child.once('error', error => {
      if (settled) return;
      settled = true; clearTimeout(timer); reject(error);
    });
    child.once('close', (code, signal) => {
      if (settled) return;
      settled = true; clearTimeout(timer); resolve({ code, signal, stdout, stderr, termination });
    });
  });
}

function fingerprintSandboxCli(executable) {
  const resolved = fs.realpathSync(executable);
  fs.accessSync(resolved, fs.constants.X_OK);
  const before = fs.statSync(resolved);
  assert.ok(before.isFile() && before.size > 0 && before.size <= 64 * 1024 * 1024,
    'Sandbox CLI must be a bounded executable file');
  const bytes = fs.readFileSync(resolved);
  const after = fs.statSync(resolved);
  assert.equal(after.dev, before.dev, 'Sandbox CLI changed during fingerprinting');
  assert.equal(after.ino, before.ino, 'Sandbox CLI changed during fingerprinting');
  assert.equal(after.size, before.size, 'Sandbox CLI changed during fingerprinting');
  assert.equal(after.mtimeMs, before.mtimeMs, 'Sandbox CLI changed during fingerprinting');
  const executableDigest = digest(bytes);
  const sourceRoot = path.basename(path.dirname(resolved)) === 'sandbox' ? path.dirname(resolved) : null;
  if (!sourceRoot) return { path: resolved, bytes: bytes.length, digest: executableDigest,
    implementation: { root: null, files: 1, bytes: bytes.length, digest: executableDigest } };
  const files = [];
  function visit(directory) {
    for (const entry of fs.readdirSync(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
      const file = path.join(directory, entry.name);
      assert.equal(entry.isSymbolicLink(), false, 'Sandbox CLI implementation must not contain symbolic links');
      if (entry.isDirectory()) visit(file);
      else {
        assert.equal(entry.isFile(), true, 'Sandbox CLI implementation must contain regular files only');
        files.push(file);
        assert.ok(files.length <= 512, 'Sandbox CLI implementation exceeds the file bound');
      }
    }
  }
  visit(sourceRoot);
  const hash = crypto.createHash('sha256'); let total = 0;
  for (const file of files) {
    const content = fs.readFileSync(file);
    total += content.length;
    assert.ok(total <= 32 * 1024 * 1024, 'Sandbox CLI implementation exceeds the byte bound');
    hash.update(path.relative(sourceRoot, file).split(path.sep).join('/')).update('\0').update(content);
  }
  return { path: resolved, bytes: bytes.length, digest: executableDigest,
    implementation: { root: sourceRoot, files: files.length, bytes: total, digest: hash.digest('hex') } };
}

function resolveSandboxCli(commandName = 'ecc-sandbox') {
  const candidates = path.isAbsolute(commandName) ? [commandName]
    : (process.env.PATH || '').split(path.delimiter).filter(directory => path.isAbsolute(directory))
      .map(directory => path.join(directory, commandName));
  const executable = candidates.find(candidate => {
    try { fs.accessSync(candidate, fs.constants.X_OK); return true; } catch { return false; }
  });
  assert.ok(executable, 'Sandbox CLI executable was not found');
  return fingerprintSandboxCli(executable);
}

function verifySandboxCli(binding) {
  const current = fingerprintSandboxCli(binding.path);
  assert.deepEqual(current, binding, 'Sandbox CLI changed after acceptance was staged');
  return current;
}

function validateReport(stdout, { tier, manifest }) {
  try {
    const report = JSON.parse(stdout);
    assert.ok(report && typeof report === 'object' && !Array.isArray(report));
    assert.equal(report.result, 'pass');
    assert.equal(report.backend, tier === 1 ? 'podman' : 'lume');
    assert.equal(report.tier, tier);
    assert.equal(report.execution_mode, 'real');
    const installDiff = report.install_diff;
    assert.ok(installDiff && typeof installDiff === 'object' && !Array.isArray(installDiff));
    for (const key of ['files_added', 'files_changed', 'files_deleted', 'path_changes',
      'services_registered', 'dotfiles_touched']) assert.ok(Array.isArray(installDiff[key]));
    if (tier === 1) assert.equal(installDiff.complete, true);
    else {
      assert.equal(installDiff.method, 'scan');
      assert.equal(installDiff.complete, false);
      assert.ok(report.notes?.includes('VM install diff is a bounded best-effort path scan, not a complete disk diff'));
    }
    assert.equal(report.assertions?.length, manifest.steps.assert.length);
    for (let index = 0; index < manifest.steps.assert.length; index++) {
      assert.deepEqual(report.assertions[index], { cmd: manifest.steps.assert[index], pass: true });
    }
    const assertion = manifest.steps.assert.at(-1);
    const step = report.steps?.findLast(item => item?.cmd === assertion);
    assert.equal(step?.exit, 0);
    assert.equal(typeof step.stdout_tail, 'string');
    const smoke = JSON.parse(step.stdout_tail.trim());
    assert.equal(smoke?.schemaVersion, 'ecc.context-sandbox-smoke.v1');
    assert.equal(smoke.passed, true);
    assert.equal(smoke.os, tier === 1 ? 'linux' : 'darwin');
    assert.equal(smoke.arch, 'arm64');
    assert.equal(smoke.authenticated, false);
    assert.equal(smoke.taskOutcomes, 'unobserved');
    assert.equal(smoke.matrix?.length, 10);
    const layouts = smoke.matrix.map(item => `${item.target}/${item.profile}`).sort();
    assert.deepEqual(layouts, ['claude/full', 'claude/lean', 'codex/full', 'codex/lean',
      'cursor/full', 'cursor/lean', 'opencode/full', 'opencode/lean', 'pi/full', 'pi/lean']);
    return { report, smoke };
  } catch {
    throw new Error('Sandbox acceptance report or final smoke payload is invalid');
  }
}

function manifestFor({ tier, archiveDigest, verifierDigest, url, runName }) {
  assert.ok([1, 2].includes(tier));
  for (const value of [archiveDigest, verifierDigest]) assert.match(value, /^[a-f0-9]{64}$/);
  assert.match(runName, /^[a-z0-9-]+$/);
  const guestRoot = tier === 1 ? `/home/ecc/${runName}` : `/tmp/${runName}`;
  const setup = [`mkdir -m 700 ${quote(guestRoot)}`];
  let runtime = '';
  if (tier === 2) {
    const parsed = new URL(url);
    assert.equal(parsed.protocol, 'http:');
    assert.equal(parsed.username, ''); assert.equal(parsed.password, '');
    assert.equal(net.isIP(parsed.hostname), 4, 'Artifact URL requires an IPv4 address');
    setup.push(`curl -fsS --max-time 120 https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-darwin-arm64.tar.gz -o ${quote(`${guestRoot}/node.tgz`)} && test "$(shasum -a 256 ${quote(`${guestRoot}/node.tgz`)} | cut -d ' ' -f 1)" = ${NODE_SHA} && tar -xzf ${quote(`${guestRoot}/node.tgz`)} -C ${quote(guestRoot)}`);
    runtime = `export PATH=${quote(`${guestRoot}/node-v${NODE_VERSION}-darwin-arm64/bin`)}:$PATH; `;
    for (const file of ['package.tgz', 'sandbox-smoke.js']) {
      setup.push(`curl -fsS --max-time 120 ${quote(`${url}/${file}`)} -o ${quote(`${guestRoot}/${file}`)}`);
    }
  } else {
    setup.push(`cp /workspace/source/package.tgz /workspace/source/sandbox-smoke.js ${quote(guestRoot)}/`);
  }
  const check = `const fs=require('fs'),c=require('crypto'); for(const [f,h] of ${JSON.stringify([['package.tgz', archiveDigest], ['sandbox-smoke.js', verifierDigest]])}) {if(c.createHash('sha256').update(fs.readFileSync(f)).digest('hex')!==h)throw Error('Input digest mismatch')}`;
  setup.push(`${runtime}cd ${quote(guestRoot)} && node -e ${quote(check)} && npm install --ignore-scripts --omit=dev --no-audit --no-fund --fetch-timeout=30000 --fetch-retries=1 --prefix consumer ./package.tgz && npm install --ignore-scripts --no-audit --no-fund --fetch-timeout=30000 --fetch-retries=1 --prefix tools @openai/codex@0.154.0 ${quote(`@openai/codex-${tier === 2 ? 'darwin' : 'linux'}-arm64@npm:@openai/codex@0.154.0-${tier === 2 ? 'darwin' : 'linux'}-arm64`)}`);
  const assertion = `${runtime}export PATH=${quote(`${guestRoot}/tools/node_modules/.bin`)}:$PATH; node ${quote(`${guestRoot}/sandbox-smoke.js`)} ${quote(`${guestRoot}/consumer/node_modules/ecc-universal`)} ${quote(guestRoot)}`;
  const manifest = { name: runName, needs: { os: [tier === 1 ? 'linux' : 'macos'], arch: ['arm64'],
    capabilities: ['clean-home', 'pkg-install', 'network:*'], trust: 'first-party', native: tier === 2 },
  resources: { cpu: 2, memory: tier === 1 ? '1GB' : '2GB', timeout: 900 },
  steps: { setup, assert: [assertion] }, report: 'install-diff' };
  for (const step of [...setup, assertion]) assert.ok(step.length <= 8192);
  return manifest;
}

async function serveInputs(files, host) {
  assert.equal(net.isIP(host), 4, 'Artifact host must be an explicit IPv4 address');
  const token = crypto.randomBytes(24).toString('hex');
  const requests = [];
  const server = http.createServer((request, response) => {
    const file = request.url?.startsWith(`/${token}/`) ? request.url.slice(token.length + 2) : '';
    if (request.method !== 'GET' || !Object.hasOwn(files, file) || requests.length >= 12) {
      response.writeHead(404).end(); return;
    }
    const bytes = files[file]; requests.push({ file, bytes: bytes.length, digest: digest(bytes) });
    response.writeHead(200, { 'Content-Length': bytes.length, 'Content-Type': 'application/octet-stream', 'Cache-Control': 'no-store' });
    response.end(bytes);
  });
  server.requestTimeout = 150000; server.headersTimeout = 10000;
  await new Promise((resolve, reject) => { server.once('error', reject); server.listen(0, host, resolve); });
  return { url: `http://${host}:${server.address().port}/${token}`, requests,
    close: () => new Promise(resolve => { server.close(resolve); server.closeAllConnections(); }) };
}

async function run(options) {
  assert.ok([1, 2].includes(options.tier), 'Choose --tier 1 or --tier 2');
  assert.equal(process.arch, 'arm64', 'This acceptance currently certifies arm64 only');
  const repoRoot = path.resolve(__dirname, '../..');
  if (options.sandboxCli) assert.ok(path.isAbsolute(options.sandboxCli), '--sandbox-cli must be an absolute trusted executable');
  const sandboxBinding = resolveSandboxCli(options.sandboxCli || 'ecc-sandbox');
  const sandboxCli = sandboxBinding.path;
  const stage = fs.mkdtempSync(path.join(os.tmpdir(), 'ecc-profile-sandbox-'));
  const resultRoot = path.resolve(options.output);
  fs.mkdirSync(resultRoot, { recursive: true, mode: 0o700 });
  const runName = `ecc-profile-tier${options.tier}-${crypto.randomUUID()}`;
  let server;
  const receipt = { schemaVersion: 'ecc.context-sandbox-acceptance.v1', runName, tier: options.tier,
    sourceRevision: (await command('git', ['rev-parse', 'HEAD'], repoRoot, 10000)).stdout.trim(),
    sourceDirty: (await command('git', ['status', '--porcelain'], repoRoot, 10000)).stdout.length > 0,
    sandboxCli, sandboxCliDigest: sandboxBinding.digest,
    sandboxImplementationDigest: sandboxBinding.implementation.digest, reportValidated: false,
    credentialsTransferred: false, artifactServerClosed: false, stageRemoved: false };
  try {
    const packed = await command('npm', ['pack', '--json', '--pack-destination', stage], repoRoot);
    assert.equal(packed.code, 0, packed.stderr);
    const pack = JSON.parse(packed.stdout)[0];
    const archive = fs.readFileSync(path.join(stage, pack.filename));
    assert.ok(archive.length < 64 * 1024 * 1024, 'Package exceeds transfer bound');
    const verifier = fs.readFileSync(path.join(__dirname, 'sandbox-smoke.js'));
    assert.ok(verifier.length < 65536);
    const files = { 'package.tgz': archive, 'sandbox-smoke.js': verifier };
    fs.writeFileSync(path.join(stage, 'package.tgz'), archive, { mode: 0o600 });
    fs.writeFileSync(path.join(stage, 'sandbox-smoke.js'), verifier, { mode: 0o600 });
    receipt.packageDigest = digest(archive); receipt.verifierDigest = digest(verifier);
    if (options.tier === 2) {
      const host = options.artifactHost || Object.values(os.networkInterfaces()).flat()
        .find(address => address.address === '192.168.64.1')?.address;
      assert.ok(host, 'Specify --artifact-host with a host IP reachable from the guest');
      server = await serveInputs(files, host);
    }
    const manifest = manifestFor({ tier: options.tier, archiveDigest: receipt.packageDigest,
      verifierDigest: receipt.verifierDigest, url: server?.url, runName });
    receipt.manifestDigest = digest(Buffer.from(JSON.stringify(manifest)));
    const manifestPath = path.join(stage, 'sandbox.json');
    fs.writeFileSync(manifestPath, JSON.stringify(manifest), { mode: 0o600 });
    fs.copyFileSync(manifestPath, path.join(resultRoot, `${runName}.manifest.json`));
    verifySandboxCli(sandboxBinding);
    const preview = await command(sandboxCli, ['run', manifestPath, '--local-only', '--dry-run'], stage, 30000);
    fs.writeFileSync(path.join(resultRoot, `${runName}.preview.json`), preview.stdout, { mode: 0o600 });
    assert.equal(preview.code, 0, preview.stdout || preview.stderr);
    const routes = JSON.parse(preview.stdout).routes;
    assert.equal(routes?.length, 1, 'Expected exactly one admitted sandbox route');
    assert.equal(routes[0].result, 'routable');
    assert.equal(routes[0].tier, options.tier, 'Router chose a different tier');
    assert.equal(routes[0].backend, options.tier === 1 ? 'podman' : 'lume', 'Router chose a different backend');
    process.stderr.write(`Starting ${runName}; package ${receipt.packageDigest}\n`);
    verifySandboxCli(sandboxBinding);
    const result = await command(sandboxCli, ['run', manifestPath, '--local-only'], stage, 960000);
    receipt.exitCode = result.code; receipt.signal = result.signal;
    fs.writeFileSync(path.join(resultRoot, `${runName}.report.json`), result.stdout, { mode: 0o600 });
    fs.writeFileSync(path.join(resultRoot, `${runName}.stderr.log`), result.stderr, { mode: 0o600 });
    receipt.reportPath = path.join(resultRoot, `${runName}.report.json`);
    assert.equal(result.code, 0, result.stdout || result.stderr);
    verifySandboxCli(sandboxBinding);
    const validated = validateReport(result.stdout, { tier: options.tier, manifest });
    receipt.reportValidated = true;
    receipt.smokeDigest = digest(Buffer.from(JSON.stringify(validated.smoke)));
    if (server) receipt.transfers = server.requests;
    return receipt;
  } finally {
    if (server) { await server.close(); receipt.artifactServerClosed = true; }
    else receipt.artifactServerClosed = true;
    fs.rmSync(stage, { recursive: true, force: true }); receipt.stageRemoved = !fs.existsSync(stage);
    fs.writeFileSync(path.join(resultRoot, `${runName}.driver.json`), JSON.stringify(receipt, null, 2), { mode: 0o600 });
  }
}

if (require.main === module) {
  const args = process.argv.slice(2); const options = {};
  for (let i = 0; i < args.length; i++) {
    if (args[i] === '--tier') options.tier = Number(args[++i]);
    else if (args[i] === '--output') options.output = args[++i];
    else if (args[i] === '--artifact-host') options.artifactHost = args[++i];
    else if (args[i] === '--sandbox-cli') options.sandboxCli = args[++i];
    else throw new Error(`Unknown option: ${args[i]}`);
  }
  if (!options.output) throw new Error('--output is required');
  run(options).then(receipt => { process.stdout.write(`${JSON.stringify(receipt, null, 2)}\n`); process.exitCode = receipt.exitCode === 0 ? 0 : 1; })
    .catch(error => { process.stderr.write(`${error.stack}\n`); process.exitCode = 1; });
}
module.exports = { command, manifestFor, resolveSandboxCli, serveInputs, validateReport,
  verifySandboxCli, run };