Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | import '@metamask/kernel-shims/endoify-node';
import { Logger } from '@metamask/logger';
import type { LogEntry } from '@metamask/logger';
import { makeKernel } from '@ocap/nodejs';
import { startDaemon } from '@ocap/nodejs/daemon';
import type { DaemonHandle } from '@ocap/nodejs/daemon';
import { mkdir, rm, writeFile } from 'node:fs/promises';
import { homedir } from 'node:os';
import { join } from 'node:path';
main().catch((error) => {
process.stderr.write(`Daemon fatal: ${String(error)}\n`);
process.exitCode = 1;
});
/**
* Main daemon entry point. Starts the daemon process and keeps it running.
*/
async function main(): Promise<void> {
const ocapDir = join(homedir(), '.ocap');
await mkdir(ocapDir, { recursive: true });
const logPath = join(ocapDir, 'daemon.log');
const logger = new Logger({
tags: ['daemon'],
transports: [makeFileTransport(logPath)],
});
const socketPath =
process.env.OCAP_SOCKET_PATH ?? join(ocapDir, 'daemon.sock');
const dbFilename = join(ocapDir, 'kernel.sqlite');
const { kernel, kernelDatabase } = await makeKernel({
resetStorage: false,
dbFilename,
logger,
});
const pidPath = join(ocapDir, 'daemon.pid');
let handle: DaemonHandle;
try {
await kernel.initIdentity();
await writeFile(pidPath, String(process.pid));
handle = await startDaemon({
socketPath,
kernel,
kernelDatabase,
onShutdown: async () => shutdown('RPC shutdown'),
});
} catch (error) {
try {
kernel.stop().catch(() => undefined);
kernelDatabase.close();
} catch {
// Best-effort cleanup.
}
rm(pidPath, { force: true }).catch(() => undefined);
throw error;
}
logger.info(`Daemon started. Socket: ${handle.socketPath}`);
let shutdownPromise: Promise<void> | undefined;
/**
* Shut down the daemon idempotently. Concurrent calls coalesce.
*
* @param reason - A label describing why shutdown was triggered.
* @returns A promise that resolves when shutdown completes.
*/
async function shutdown(reason: string): Promise<void> {
if (shutdownPromise === undefined) {
logger.info(`Shutting down (${reason})...`);
shutdownPromise = handle.close().finally(() => {
rm(pidPath, { force: true }).catch(() => undefined);
});
}
return shutdownPromise;
}
process.on('SIGTERM', () => {
shutdown('SIGTERM').catch(() => (process.exitCode = 1));
});
process.on('SIGINT', () => {
shutdown('SIGINT').catch(() => (process.exitCode = 1));
});
}
/**
* Create a file transport that writes logs to a file.
*
* @param logPath - The log file path.
* @returns A log transport function.
*/
function makeFileTransport(logPath: string) {
// eslint-disable-next-line @typescript-eslint/no-require-imports, n/global-require -- need sync fs for log transport
const fs = require('node:fs') as typeof import('node:fs');
return (entry: LogEntry): void => {
const line = `[${new Date().toISOString()}] [${entry.level}] ${entry.message ?? ''} ${(entry.data ?? []).map(String).join(' ')}\n`;
// eslint-disable-next-line n/no-sync -- synchronous write needed for log transport reliability
fs.appendFileSync(logPath, line);
};
}
|