#!/usr/bin/env node import http from 'node:http'; import fs from 'node:fs'; import { pathToFileURL } from 'node:url'; import { PublicPersonalOS, DOMAINS } from './engine.mjs'; export function createLocalServer({ engine, executionHost }) { return http.createServer(async (request, response) => { const send = (status, value) => { response.writeHead(status, { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-store' }); response.end(JSON.stringify(value)); }; if (request.method === 'GET' && request.url === '/health') { return send(200, { service: 'hololake-public-personal-os', mode: 'LOCAL_DEVELOPMENT', domains: Object.keys(DOMAINS), executionAdapterConnected: Boolean(executionHost), liveRegistration: false }); } if (request.method !== 'POST' || !['/events', '/execute'].includes(request.url)) return send(404, { error: 'NOT_FOUND' }); let size = 0, chunks = []; try { for await (const chunk of request) { size += chunk.length; if (size > 65536) return send(413, { error: 'REQUEST_TOO_LARGE' }); chunks.push(chunk); } const envelope = JSON.parse(Buffer.concat(chunks).toString('utf8')); if (request.url === '/execute') { if (!executionHost) return send(409, { error: 'EXECUTION_HOST_NOT_CONNECTED' }); return send(200, await engine.executeAction(envelope, executionHost)); } if (envelope.action === 'START_ACTION') return send(409, { error: 'USE_EXECUTE_ENDPOINT' }); return send(200, engine.handle(envelope)); } catch (error) { const code = /^[A-Z_]+$/.test(error.message) ? error.message : 'INVALID_REQUEST_OR_STATE'; const status = /SIGN|SIGNER|AUTHORITY|OWNER|CONSENT|SEED_ONLY/.test(code) ? 403 : 400; return send(status, { error: code }); } }); } if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { const options = Object.fromEntries(process.argv.slice(2).reduce((pairs, v, i, a) => i % 2 ? pairs : [...pairs, [v, a[i + 1]]], [])); if (!options['--state'] || !options['--trust']) throw Error('USAGE: --state --trust [--port 3940]'); const trust = JSON.parse(fs.readFileSync(options['--trust'], 'utf8')); const engine = new PublicPersonalOS({ directory: options['--state'], trust }); const server = createLocalServer({ engine }); server.listen(Number(options['--port'] ?? 3940), '127.0.0.1', () => { console.log(JSON.stringify({ address: server.address(), mode: 'LOCAL_DEVELOPMENT', liveRegistration: false })); }); }