/** * Universal Offload Worker (Remote) * * Handles worktree provisioning and parallel task execution based on 'playbooks'. */ import { spawn, spawnSync } from 'child_process'; import path from 'path'; import fs from 'fs'; const prNumber = process.argv[2]; const branchName = process.argv[3]; const policyPath = process.argv[4]; const action = process.argv[5] || 'review'; async function main() { if (!prNumber || !branchName || !policyPath) { console.error('Usage: tsx worker.ts [action]'); return 1; } const workDir = process.cwd(); // This is remoteWorkDir const targetDir = path.join(workDir, branchName); // 1. Provision PR Directory if (!fs.existsSync(targetDir)) { console.log(`🌿 Provisioning PR #${prNumber} into ${branchName}...`); const cloneCmd = `git clone --filter=blob:none https://github.com/google-gemini/gemini-cli.git ${targetDir}`; spawnSync(cloneCmd, { stdio: 'inherit', shell: true }); process.chdir(targetDir); spawnSync('gh', ['pr', 'checkout', prNumber], { stdio: 'inherit' }); } else { process.chdir(targetDir); } const logDir = path.join(targetDir, `.gemini/logs/offload-${prNumber}`); fs.mkdirSync(logDir, { recursive: true }); const geminiBin = path.join(workDir, 'node_modules/.bin/gemini'); // 2. Define Playbooks let tasks: any[] = []; if (action === 'review') { tasks = [ { id: 'build', name: 'Fast Build', cmd: `cd ${targetDir} && npm ci && npm run build` }, { id: 'ci', name: 'CI Checks', cmd: `gh pr checks ${prNumber}` }, { id: 'review', name: 'Gemini Analysis', cmd: `${geminiBin} --policy ${policyPath} --cwd ${targetDir} -p "/review-frontend ${prNumber}"` }, { id: 'verify', name: 'Behavioral Proof', cmd: `${geminiBin} --policy ${policyPath} --cwd ${targetDir} -p "Analyze the code in ${targetDir} and exercise it to prove it works."`, dep: 'build' } ]; } else if (action === 'fix') { tasks = [ { id: 'build', name: 'Fast Build', cmd: `cd ${targetDir} && npm ci && npm run build` }, { id: 'failures', name: 'Find Failures', cmd: `gh run view --log-failed` }, { id: 'fix', name: 'Iterative Fix', cmd: `${geminiBin} --policy ${policyPath} --cwd ${targetDir} -p "Address review comments and fix failing tests for PR ${prNumber}. Repeat until CI is green."`, dep: 'build' } ]; } else if (action === 'ready') { tasks = [ { id: 'clean', name: 'Clean Install', cmd: `npm run clean && npm ci` }, { id: 'preflight', name: 'Full Preflight', cmd: `npm run preflight`, dep: 'clean' }, { id: 'conflicts', name: 'Conflict Check', cmd: `git fetch origin main && git merge-base --is-ancestor origin/main HEAD || echo "CONFLICT"` } ]; } else if (action === 'open') { console.log(`šŸš€ Dropping into manual session for ${branchName}...`); process.exit(0); } const state: Record = {}; tasks.forEach(t => state[t.id] = { status: 'PENDING' }); return new Promise((resolve) => { function runTask(task: any) { if (task.dep && state[task.dep].status !== 'SUCCESS') { setTimeout(() => runTask(task), 1000); return; } state[task.id].status = 'RUNNING'; const proc = spawn(task.cmd, { shell: true, env: { ...process.env, FORCE_COLOR: '1' } }); const logStream = fs.createWriteStream(path.join(logDir, `${task.id}.log`)); proc.stdout.pipe(logStream); proc.stderr.pipe(logStream); proc.on('close', (code) => { const exitCode = code ?? 0; state[task.id].status = exitCode === 0 ? 'SUCCESS' : 'FAILED'; fs.writeFileSync(path.join(logDir, `${task.id}.exit`), exitCode.toString()); render(); }); } function render() { console.clear(); console.log(`==================================================`); console.log(`šŸš€ Offload | ${action.toUpperCase()} | PR #${prNumber}`); console.log(`šŸ“‚ Worktree: ${targetDir}`); console.log(`==================================================\n`); tasks.forEach(t => { const s = state[t.id]; const icon = s.status === 'SUCCESS' ? 'āœ…' : s.status === 'FAILED' ? 'āŒ' : s.status === 'RUNNING' ? 'ā³' : 'šŸ’¤'; console.log(` ${icon} ${t.name.padEnd(20)}: ${s.status}`); }); const allDone = tasks.every(t => ['SUCCESS', 'FAILED'].includes(state[t.id].status)); if (allDone) { console.log(`\n✨ Playbook complete. Launching interactive session...`); resolve(0); } } tasks.filter(t => !t.dep).forEach(runTask); tasks.filter(t => t.dep).forEach(runTask); const intervalId = setInterval(render, 1500); const checkAllDone = setInterval(() => { if (tasks.every(t => ['SUCCESS', 'FAILED'].includes(state[t.id].status))) { clearInterval(intervalId); clearInterval(checkAllDone); } }, 1000); }); } if (import.meta.url === `file://${process.argv[1]}`) { runWorker(process.argv.slice(2)).catch(console.error); }