Files
bare-operating-system/packages/bare-os-booter/lib/host/host-bridge-cli.js
T
2026-08-18 18:11:28 -04:00

250 lines
6.5 KiB
JavaScript

/**
* Host-bridge CLI delegates: hrpc, bundlebee staging hints, sidecar caps, pear-runtime matrix.
* Stock booter provides audit/probe paths; hosts replace ctx methods or set env flags (see each command).
*/
/** @param {Record<string, unknown>} ctx */
function env(ctx) {
return ctx.vfs?.env || {}
}
/**
* @param {Record<string, unknown>} ctx
* @param {string} name
*/
function hostCap(ctx, name) {
if (typeof ctx.bareOsHostCapability === 'function') {
try {
return !!ctx.bareOsHostCapability(name)
} catch {
return false
}
}
return false
}
/**
* @param {unknown} fn
* @returns {boolean}
*/
export function isStockHrpcRequest(fn) {
return (
typeof fn === 'function' &&
Object.prototype.hasOwnProperty.call(fn, '__bareOsStockHrpcRequest')
)
}
/**
* @param {Record<string, unknown>} ctx
* @param {string[]} argv
*/
export async function runHrpcCli(ctx, argv) {
const args = argv.slice(1).filter((a) => a !== '--')
if (
args.length === 0 ||
args[0] === '-h' ||
args[0] === '-help' ||
args[0] === '--help'
) {
ctx.console.log(
'usage: hrpc probe\n' +
' hrpc request <service> <method> [json]\n' +
'Bare OS: uses ctx.bareOsHrpcRequest (stock routes + optional host override via BARE_OS_HRPC_BRIDGE_WIRED) ' +
'or prints allowlist probe.\n'
)
return
}
if (args[0] === 'probe') {
const probe =
typeof ctx.bareOsHrpcAllowlistProbe === 'function'
? ctx.bareOsHrpcAllowlistProbe()
: { note: 'bareOsHrpcAllowlistProbe unavailable' }
ctx.console.log(JSON.stringify(probe, null, 2))
return
}
if (args[0] !== 'request') {
ctx.console.error('hrpc: unknown subcommand (try: hrpc probe | hrpc request ...)')
ctx.exitCode = 1
return
}
const service = args[1]
const method = args[2]
if (!service || !method) {
ctx.console.error('usage: hrpc request <service> <method> [json]')
ctx.exitCode = 1
return
}
let payload = {}
const jsonPart = args.slice(3).join(' ').trim()
if (jsonPart) {
try {
payload = JSON.parse(jsonPart)
} catch {
ctx.console.error('hrpc: invalid JSON payload')
ctx.exitCode = 1
return
}
}
const req = ctx.bareOsHrpcRequest
if (typeof req !== 'function') {
ctx.console.error('hrpc: ctx.bareOsHrpcRequest unavailable')
ctx.exitCode = 1
return
}
if (isStockHrpcRequest(req) && hostCap(ctx, 'hrpcBridge')) {
ctx.console.error(
'hrpc: host bridge capability is advertised, but stock handler is still active; host override was not installed.'
)
}
try {
const out = await req.call(ctx, service, method, payload)
if (out !== undefined) ctx.console.log(JSON.stringify(out, null, 2))
} catch (e) {
ctx.console.error(e?.message || String(e))
ctx.exitCode = 1
}
}
/**
* @param {Record<string, unknown>} ctx
* @param {string[]} argv
*/
export async function runBundlebeeCli(ctx, argv) {
const args = argv.slice(1).filter((a) => a !== '--')
if (
args.length === 0 ||
args[0] === '-h' ||
args[0] === '-help' ||
args[0] === '--help'
) {
ctx.console.log(
'usage: bundlebee hint [label]\n' +
' bundlebee status\n' +
'Emits process event bare-os:bundlebee-cli and prints BARE_OS_BUNDLEBEE_STAGE_JSON when set.\n'
)
return
}
if (args[0] === 'status') {
const raw = String(env(ctx).BARE_OS_BUNDLEBEE_STAGE_JSON || '').trim()
if (!raw) {
ctx.console.log(
JSON.stringify({
ok: false,
note: 'BARE_OS_BUNDLEBEE_STAGE_JSON unset; host may populate after pear stage.'
})
)
return
}
try {
ctx.console.log(JSON.stringify(JSON.parse(raw), null, 2))
} catch {
ctx.console.error('bundlebee: invalid BARE_OS_BUNDLEBEE_STAGE_JSON')
ctx.exitCode = 1
}
return
}
if (args[0] === 'hint') {
const label = args.slice(1).join(' ').trim() || 'default'
if (typeof globalThis.process?.emit === 'function') {
try {
globalThis.process.emit('bare-os:bundlebee-cli', {
label: label.slice(0, 256),
argv: argv.slice(0, 16),
ts: Date.now()
})
} catch (e) {
ctx.console.error('bundlebee: emit failed: ' + (e?.message || e))
ctx.exitCode = 1
return
}
}
ctx.console.log(
JSON.stringify({
ok: true,
emitted: 'bare-os:bundlebee-cli',
label
})
)
return
}
ctx.console.error('bundlebee: unknown subcommand')
ctx.exitCode = 1
}
/**
* @param {Record<string, unknown>} ctx
* @param {string[]} argv
*/
export async function runSidecarCli(ctx, argv) {
const args = argv.slice(1).filter((a) => a !== '--')
if (
args.length === 0 ||
args[0] === '-h' ||
args[0] === '-help' ||
args[0] === '--help'
) {
ctx.console.log(
'usage: sidecar cap <class>\n' +
'Calls ctx.bareOsSidecarResourceCap (emits bare-os:sidecar-resource-cap). ' +
'Set BARE_OS_SIDECAR_BRIDGE_WIRED=1 when a host listener consumes sidecar events.\n'
)
return
}
if (args[0] === 'cap' && args[1]) {
const fn = ctx.bareOsSidecarResourceCap
if (typeof fn !== 'function') {
ctx.console.error('sidecar: ctx.bareOsSidecarResourceCap missing')
ctx.exitCode = 1
return
}
try {
const ok = await fn.call(ctx, { capClass: String(args[1]).slice(0, 128) })
ctx.console.log(
JSON.stringify({
ok: !!ok,
capClass: args[1],
hostListenerExpected: hostCap(ctx, 'sidecarBridge')
})
)
} catch (e) {
ctx.console.error(e?.message || String(e))
ctx.exitCode = 1
}
return
}
ctx.console.error('sidecar: usage: sidecar cap <class>')
ctx.exitCode = 1
}
/**
* @param {Record<string, unknown>} ctx
* @param {string[]} argv
*/
export async function runPearRuntimeMatrixCli(ctx, argv) {
const args = argv.slice(1).filter((a) => a !== '--')
if (
args.includes('-h') ||
args.includes('--help') ||
args[0] === '-help'
) {
ctx.console.log(
'usage: pear-runtime-matrix\n' +
'Prints JSON from ctx.bareOsPearRuntimeMatrixProbe() (merges BARE_OS_PEAR_RUNTIME_MATRIX_JSON when set).\n'
)
return
}
const fn = ctx.bareOsPearRuntimeMatrixProbe
if (typeof fn !== 'function') {
ctx.console.error('pear-runtime-matrix: probe unavailable')
ctx.exitCode = 1
return
}
try {
const out = fn.call(ctx)
ctx.console.log(JSON.stringify(out, null, 2))
} catch (e) {
ctx.console.error(e?.message || String(e))
ctx.exitCode = 1
}
}