Files
PaperClipAI/docker/exe-dev-runner/lease.mjs
T
DottaandPaperclip 056703424a Add experimental durable exe.dev agent environments
Keep one VM per environment with independent lease cgroups, persistent workspaces, immutable runner images, and an explicit experimental gate. Add live compatibility, recovery, browser-preview, and stress campaigns.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-17 09:47:20 -05:00

90 lines
3.7 KiB
JavaScript

// Image-owned execution supervisor. One systemd cgroup per lease, independent
// sockets per command. Closing SSH is transport loss, never proof of termination.
import net from 'node:net';
import fs from 'node:fs';
import path from 'node:path';
import { spawn } from 'node:child_process';
const [mode, root] = process.argv.slice(2);
if (!root || !/^\/var\/lib\/paperclip-exe\/[a-f0-9]{32}\/[a-f0-9]{32}$/.test(root)) {
throw new Error('Invalid lease root');
}
const socketPath = path.join(root, 'control.sock');
const MAX_FRAME = 16 * 1024 * 1024;
const send = (socket, value) => !socket.destroyed && socket.write(JSON.stringify(value) + '\n');
function frames(stream, onFrame) {
let pending = '';
stream.setEncoding('utf8');
stream.on('data', (data) => {
pending += data;
if (pending.length > MAX_FRAME) return stream.destroy(new Error('Frame too large'));
for (;;) {
const newline = pending.indexOf('\n');
if (newline < 0) break;
const line = pending.slice(0, newline);
pending = pending.slice(newline + 1);
try { onFrame(JSON.parse(line)); }
catch { stream.destroy(new Error('Invalid frame')); }
}
});
}
if (mode === 'serve') {
process.umask(0o077);
fs.rmSync(socketPath, { force: true });
const server = net.createServer((socket) => {
let child;
let timer;
let done = false;
let timedOut = false;
const terminate = () => {
if (!child?.pid || done) return;
try { process.kill(-child.pid, 'SIGKILL'); } catch {}
};
socket.on('error', () => {});
// Commands survive a disconnected SSH client. The lease's cgroup remains
// authoritative; cancellation and expiry explicitly stop it from outside.
frames(socket, (message) => {
if (message.type === 'stdin' && child) {
child.stdin.write(Buffer.from(message.data, 'base64'));
return;
}
if (message.type === 'end' && child) { child.stdin.end(); return; }
if (message.type === 'cancel') { terminate(); return; }
if (message.type !== 'start' || child) throw new Error('Expected start');
if (typeof message.command !== 'string' || !Array.isArray(message.args)) throw new Error('Invalid command');
const cwd = message.cwd ?? path.join(root, 'workspace');
child = spawn(message.command, message.args, {
cwd,
env: { ...process.env, HOME: path.join(root, 'home'), ...message.env },
detached: true,
stdio: ['pipe', 'pipe', 'pipe'],
});
child.stdin.on('error', () => {});
child.stdout.on('data', (data) => send(socket, { type: 'stdout', data: data.toString('base64') }));
child.stderr.on('data', (data) => send(socket, { type: 'stderr', data: data.toString('base64') }));
const finish = (code, signal, error) => {
if (done) return;
done = true;
clearTimeout(timer);
send(socket, { type: 'exit', code, signal, timedOut, error });
socket.end();
};
child.on('error', (error) => finish(null, null, error.message));
// Use exit, not close: a deliberately detached daemon can retain an fd.
child.on('exit', (code, signal) => finish(code, signal));
if (message.timeoutMs > 0) timer = setTimeout(() => { timedOut = true; terminate(); }, message.timeoutMs);
});
});
server.listen(socketPath);
} else if (mode === 'call') {
const socket = net.connect(socketPath);
socket.on('error', (error) => { console.error(error.message); process.exitCode = 1; });
socket.on('connect', () => process.stdin.pipe(socket, { end: false }));
socket.pipe(process.stdout);
socket.on('end', () => { process.stdin.pause(); process.stdin.unref?.(); });
} else {
throw new Error('Invalid supervisor mode');
}