| #!/usr/bin/env node |
| import { readdirSync, statSync, mkdirSync, readFileSync } from 'node:fs' |
| import { createServer } from 'node:net' |
| import { createHash } from 'node:crypto' |
| import { join, relative, dirname, basename, extname } from 'node:path' |
| import { fileURLToPath } from 'node:url' |
| import { createRequire } from 'node:module' |
|
|
| import { verifyOnce } from './icp-verifier.mjs' |
| import { run as runMetrics } from './metric-stream.mjs' |
| import { run as runBifrost } from './bifrost-translator.mjs' |
| import { run as runWatermark } from './watermark.mjs' |
| import { sealEvent } from './worm-chain.mjs' |
|
|
| const __dir = dirname(fileURLToPath(import.meta.url)) |
| const ROOT = join(__dir, '..') |
| const PAGES = join(ROOT, 'pages') |
| const OUT = join(ROOT, 'bifrost-out') |
| const PUBLIC = join(ROOT, 'public') |
| const GOVERNANCE = join(ROOT, 'governance', 'shadow-orchestrator.pl') |
| const DAY_MS = 24 * 60 * 60 * 1000 |
| const require = createRequire(import.meta.url) |
| const pl = require('tau-prolog') |
|
|
| let tick = 0 |
| let running = false |
| const relaySockets = new Set() |
|
|
| function parseArgs() { |
| const args = process.argv.slice(2) |
| const intervalIdx = args.indexOf('--interval') |
| return { |
| once: args.includes('--once'), |
| interval: intervalIdx >= 0 ? parseInt(args[intervalIdx + 1], 10) : parseInt(process.env.ORCHESTRATE_INTERVAL_MS || '30000', 10), |
| } |
| } |
|
|
| function walk(dir, match, files = []) { |
| const stack = [dir] |
| while (stack.length) { |
| const current = stack.pop() |
| let entries = [] |
| try { entries = readdirSync(current) } catch { continue } |
| for (const entry of entries) { |
| const full = join(current, entry) |
| let stat |
| try { stat = statSync(full) } catch { continue } |
| if (stat.isDirectory()) stack.push(full) |
| else if (match(full, stat)) files.push(full) |
| } |
| } |
| return files |
| } |
|
|
| function recentPageSources() { |
| const cutoff = Date.now() - DAY_MS |
| return walk(PAGES, (file, stat) => extname(file) === '.ts' && stat.mtimeMs >= cutoff) |
| } |
|
|
| function outFiles() { |
| return new Set(walk(OUT, file => true).map(file => relative(OUT, file))) |
| } |
|
|
| function prefixForSource(source) { |
| const rel = relative(PAGES, source) |
| const parts = rel.split(/[\\/]/).filter(Boolean) |
| const stem = basename(source).replace(/\.[^.]+$/, '') |
| const scope = parts.length > 1 ? parts.slice(0, -1).join('-') : 'page' |
| return stem === 'index' ? `${scope}-` : `${scope}-` |
| } |
|
|
| async function quiet(fn) { |
| const out = console.log |
| const err = console.error |
| console.log = (...args) => err(...args) |
| console.error = (...args) => err(...args) |
| try { return await fn() } |
| finally { |
| console.log = out |
| console.error = err |
| } |
| } |
|
|
| function prologAtom(value) { |
| return `'${String(value).replace(/\\/g, '\\\\').replace(/'/g, "\\'")}'` |
| } |
| |
| function queryGovernance(goal) { |
| const program = readFileSync(GOVERNANCE, 'utf8') |
| return new Promise((resolve, reject) => { |
| const session = pl.create(1000) |
| session.consult(program, { |
| success: () => { |
| session.query(goal, { |
| success: () => { |
| session.answer(answer => resolve(answer !== false)) |
| }, |
| error: reject, |
| }) |
| }, |
| error: reject, |
| }) |
| }) |
| } |
| |
| async function agentAllowed(name, phase) { |
| return queryGovernance(`agent_allowed(${prologAtom(name)}, ${prologAtom(phase)}).`) |
| } |
| |
| async function statusAllowed(name, status) { |
| return queryGovernance(`status_verdict(${prologAtom(name)}, ${prologAtom(status)}, 'ALLOW').`) |
| } |
| |
| async function dispatchGate(type) { |
| const allowed = await queryGovernance(`dispatch_verdict(${prologAtom(type)}, 'resurrect', 'ACCEPTED').`) |
| return { allowed, agent: 'resurrect', status: allowed ? 'ACCEPTED' : 'REJECTED' } |
| } |
| |
| async function sealed(name, status, payload = {}) { |
| const allowed = await statusAllowed(name, status) |
| const finalStatus = allowed ? status : 'REJECTED' |
| const governance = allowed ? 'tau-prolog:allow' : 'tau-prolog:reject' |
| const { seal } = sealEvent(name, 'agent-result', { status: finalStatus, governance, ...payload }) |
| return { name, status: finalStatus, seal, governance } |
| } |
| |
| async function requireAgent(name, phase) { |
| if (await agentAllowed(name, phase)) return |
| throw new Error(`tau_prolog_denied:${phase}:${name}`) |
| } |
| |
| function writeJsonLine(message) { |
| const line = JSON.stringify(message) + '\n' |
| process.stdout.write(line) |
| for (const socket of relaySockets) { |
| if (!socket.destroyed) socket.write(encodeWsFrame(JSON.stringify(message))) |
| } |
| } |
| |
| async function runTick() { |
| if (running) return |
| running = true |
| tick += 1 |
| const ts = new Date().toISOString() |
| const agents = [] |
| |
| try { |
| mkdirSync(PUBLIC, { recursive: true }) |
| mkdirSync(OUT, { recursive: true }) |
| |
| await requireAgent('icp-verifier', 'tick') |
| const icp = await quiet(() => verifyOnce({})) |
| if (icp.status === 'DRIFT_DETECTED') { |
| const incident = sealEvent('ORCHESTRATOR', 'icp-drift-incident', { tick, ts, icp }) |
| agents.push(await sealed('icp-verifier', 'DRIFT_DETECTED', { internalSeal: incident.seal })) |
| writeJsonLine({ tick, ts, agents }) |
| return |
| } |
| agents.push(await sealed('icp-verifier', icp.status || 'VERIFIED', { internalSeal: icp.seal })) |
| |
| await requireAgent('metric-stream', 'tick') |
| const metrics = await quiet(() => runMetrics({ |
| repoPath: ROOT, |
| outPath: join(PUBLIC, 'metrics.json'), |
| })) |
| agents.push(await sealed('metric-stream', 'UPDATED', { internalSeal: metrics.wormSeal })) |
| |
| await requireAgent('bifrost-translator', 'tick') |
| const before = outFiles() |
| const sources = recentPageSources() |
| const translations = [] |
| for (const sourcePath of sources) { |
| const result = await quiet(() => runBifrost({ |
| sourcePath, |
| targets: ['rust', 'lean4'], |
| outDir: OUT, |
| namePrefix: prefixForSource(sourcePath), |
| })) |
| translations.push(result) |
| } |
| agents.push(await sealed('bifrost-translator', sources.length ? 'TRANSLATED' : 'IDLE', { |
| files: sources.length, |
| internalSeals: translations.map(r => r.seal).filter(Boolean), |
| })) |
| |
| await requireAgent('watermark', 'tick') |
| const after = outFiles() |
| const newFiles = [...after].filter(file => !before.has(file)) |
| const watermark = await quiet(() => runWatermark({ target: OUT })) |
| agents.push(await sealed('watermark', watermark.marks.length ? 'WATERMARKED' : 'IDLE', { |
| files: newFiles, |
| filesMarked: watermark.marks.length, |
| internalSeal: watermark.seal, |
| })) |
| |
| writeJsonLine({ tick, ts, agents }) |
| } catch (error) { |
| const incident = sealEvent('ORCHESTRATOR', 'tick-error', { tick, ts, message: error.message }) |
| agents.push(await sealed('orchestrator', 'ERROR', { internalSeal: incident.seal, message: error.message })) |
| writeJsonLine({ tick, ts, agents }) |
| } finally { |
| running = false |
| } |
| } |
| |
| function encodeWsFrame(text) { |
| const body = Buffer.from(text) |
| if (body.length < 126) return Buffer.concat([Buffer.from([0x81, body.length]), body]) |
| if (body.length <= 0xffff) { |
| const header = Buffer.alloc(4) |
| header[0] = 0x81 |
| header[1] = 126 |
| header.writeUInt16BE(body.length, 2) |
| return Buffer.concat([header, body]) |
| } |
| const header = Buffer.alloc(10) |
| header[0] = 0x81 |
| header[1] = 127 |
| header.writeBigUInt64BE(BigInt(body.length), 2) |
| return Buffer.concat([header, body]) |
| } |
| |
| function decodeWsFrame(buffer) { |
| if (buffer.length < 2) return null |
| const opcode = buffer[0] & 0x0f |
| if (opcode === 0x8) return { close: true } |
| const masked = (buffer[1] & 0x80) !== 0 |
| let len = buffer[1] & 0x7f |
| let offset = 2 |
| if (len === 126) { |
| if (buffer.length < 4) return null |
| len = buffer.readUInt16BE(2) |
| offset = 4 |
| } else if (len === 127) { |
| if (buffer.length < 10) return null |
| len = Number(buffer.readBigUInt64BE(2)) |
| offset = 10 |
| } |
| const maskOffset = offset |
| if (masked) offset += 4 |
| if (buffer.length < offset + len) return null |
| const payload = Buffer.from(buffer.slice(offset, offset + len)) |
| if (masked) { |
| const mask = buffer.slice(maskOffset, maskOffset + 4) |
| for (let i = 0; i < payload.length; i += 1) payload[i] ^= mask[i % 4] |
| } |
| return { text: payload.toString('utf8') } |
| } |
| |
| function acceptHandshake(socket, request) { |
| const key = request.match(/Sec-WebSocket-Key:\s*(.+)\r?\n/i)?.[1]?.trim() |
| if (!key) { |
| socket.destroy() |
| return false |
| } |
| const accept = createHash('sha1') |
| .update(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11') |
| .digest('base64') |
| socket.write( |
| 'HTTP/1.1 101 Switching Protocols\r\n' + |
| 'Upgrade: websocket\r\n' + |
| 'Connection: Upgrade\r\n' + |
| `Sec-WebSocket-Accept: ${accept}\r\n\r\n` |
| ) |
| relaySockets.add(socket) |
| socket.once('close', () => relaySockets.delete(socket)) |
| socket.once('error', () => relaySockets.delete(socket)) |
| return true |
| } |
| |
| async function handleRelayMessage(text) { |
| let message |
| try { message = JSON.parse(text) } catch { return } |
| if (typeof message.type !== 'string' || !message.type.endsWith(':dispatch')) return |
| |
| tick += 1 |
| const ts = new Date().toISOString() |
| const gate = await dispatchGate(message.type) |
| if (!gate.allowed) { |
| const rejected = await sealed(gate.agent, gate.status, { |
| repo: message.repo, |
| dryRun: Boolean(message.dryRun), |
| reason: 'tau_prolog_dispatch_denied', |
| }) |
| writeJsonLine({ |
| tick, |
| ts, |
| type: 'ransom_worm:rejected', |
| repo: message.repo, |
| dryRun: Boolean(message.dryRun), |
| agents: [rejected], |
| }) |
| return |
| } |
| |
| const accepted = await sealed(gate.agent, gate.status, { |
| repo: message.repo, |
| dryRun: Boolean(message.dryRun), |
| }) |
| writeJsonLine({ |
| tick, |
| ts, |
| type: 'ransom_worm:accepted', |
| repo: message.repo, |
| dryRun: Boolean(message.dryRun), |
| agents: [accepted], |
| }) |
| } |
| |
| function startRelay(port) { |
| const server = createServer(socket => { |
| let handshaken = false |
| let pending = Buffer.alloc(0) |
| |
| socket.on('data', chunk => { |
| if (!handshaken) { |
| const request = chunk.toString('utf8') |
| if (!request.includes('\r\n\r\n')) return |
| handshaken = acceptHandshake(socket, request) |
| return |
| } |
| |
| pending = Buffer.concat([pending, chunk]) |
| const frame = decodeWsFrame(pending) |
| if (!frame) return |
| pending = Buffer.alloc(0) |
| if (frame.close) { |
| socket.end() |
| return |
| } |
| handleRelayMessage(frame.text).catch(error => { |
| const incident = sealEvent('ORCHESTRATOR', 'relay-error', { message: error.message }) |
| writeJsonLine({ |
| tick, |
| ts: new Date().toISOString(), |
| type: 'relay:error', |
| message: error.message, |
| seal: incident.seal, |
| }) |
| }) |
| }) |
| }) |
| |
| server.listen(port, '127.0.0.1', () => { |
| const address = server.address() |
| const boundPort = typeof address === 'object' && address ? address.port : port |
| writeJsonLine({ type: 'relay:ready', port: boundPort }) |
| }) |
| server.on('error', error => { |
| writeJsonLine({ type: 'relay:error', message: error.message }) |
| }) |
| } |
| |
| function start() { |
| const args = parseArgs() |
| const relayPort = Number.parseInt(process.env.ORCHESTRATE_WS_PORT || '', 10) |
| if (Number.isInteger(relayPort)) startRelay(relayPort) |
| runTick() |
| if (!args.once) setInterval(runTick, args.interval) |
| } |
| |
| start() |
| |