/** * Host-side booter diagnostics (runs before guest VFS exists). * Avoids raw console.warn as the only sink; supports suppression and NDJSON file append. * * Env: * - BARE_OS_HOST_BOOTER_LOG — `stderr` (default), `0`/`false`/`off` (silent), `ndjson` (append file) * - BARE_OS_HOST_BOOTER_LOG_PATH — override path when mode is `ndjson` (default: ~/.bare-os/var/log/booter-host.ndjson) */ import fs from '#host-fs' import path from '#host-path' function hostDataRoot() { const override = globalThis.process?.env && globalThis.process.env.BARE_OS_HOST_DATA if (override) return path.resolve(String(override)) const home = (globalThis.process?.env && globalThis.process.env.HOME) || '/tmp' return path.join(String(home), '.bare-os') } function defaultNdjsonPath() { return path.join(hostDataRoot(), 'var', 'log', 'booter-host.ndjson') } /** * @param {string} code Stable machine-readable code (e.g. ctx_bare_load_failed). * @param {string} human Human-readable message (no secrets). * @param {string} [detail] Optional short detail (error.message, etc.). */ /** * Host-side informational line (boot trace off): stderr when mode is stderr; optional NDJSON append. * @param {string} code * @param {string} human * @param {string} [detail] */ export function bareOsHostBooterInfo(code, human, detail) { const env = globalThis.process?.env || {} const mode = String(env.BARE_OS_HOST_BOOTER_LOG || 'stderr') .trim() .toLowerCase() if ( mode === '0' || mode === 'false' || mode === 'off' || mode === 'no' || mode === 'silent' ) { return } const d = detail != null ? String(detail).slice(0, 4000) : '' const line = `[bare-os-booter][${code}] ${human}${d ? `: ${d}` : ''}` if (mode === 'ndjson') { const logPath = String(env.BARE_OS_HOST_BOOTER_LOG_PATH || '').trim() || defaultNdjsonPath() try { fs.mkdirSync(path.dirname(logPath), { recursive: true }) fs.appendFileSync( logPath, `${JSON.stringify({ type: 'booterHostInfo', code: String(code).slice(0, 128), message: String(human).slice(0, 2000), detail: d || undefined, ts: Date.now() })}\n`, 'utf8' ) } catch { /* fall through */ } } if (mode === 'ndjson' && env.BARE_OS_HOST_BOOTER_LOG_STDERR !== '1') { return } const err = globalThis.process?.stderr if (err && typeof err.write === 'function') { err.write(`${line}\n`) } else if (typeof console.info === 'function') { console.info(line) } } export function bareOsHostBooterWarn(code, human, detail) { const env = globalThis.process?.env || {} const mode = String(env.BARE_OS_HOST_BOOTER_LOG || 'stderr') .trim() .toLowerCase() if ( mode === '0' || mode === 'false' || mode === 'off' || mode === 'no' || mode === 'silent' ) { return } const d = detail != null ? String(detail).slice(0, 4000) : '' const line = `[bare-os-booter][${code}] ${human}${d ? `: ${d}` : ''}` if (mode === 'ndjson') { const logPath = String(env.BARE_OS_HOST_BOOTER_LOG_PATH || '').trim() || defaultNdjsonPath() try { fs.mkdirSync(path.dirname(logPath), { recursive: true }) fs.appendFileSync( logPath, `${JSON.stringify({ type: 'booterHostWarn', code: String(code).slice(0, 128), message: String(human).slice(0, 2000), detail: d || undefined, ts: Date.now() })}\n`, 'utf8' ) } catch { /* fall through to stderr */ } } if (mode === 'ndjson' && env.BARE_OS_HOST_BOOTER_LOG_STDERR !== '1') { return } if (typeof console !== 'undefined' && typeof console.warn === 'function') { console.warn(line) } }