// running an rnx command inside the calling process instead of spawning the CLI. // // the parse and the dispatch here are the CLI's own: parseRnxArgs, the command // registry behind it, and the same per-command handlers main.ts reaches. what // changes is the transport and the boundary. the caller hands in the WsBridge, // so one vocabulary and one parser run against three of them: the local ws // daemon, a cloud box's commands route, and, inside a box shell in workerd, the // Box Durable Object's own command router with no network hop. // // the boundary is run-rnx.ts: output comes back as a value rather than reaching // a terminal, and a command that would have called process.exit throws instead, // which is what makes this safe to call in workerd, where process.exit cancels // the whole request. import { parseRnxArgs, TOP_LEVEL_RUNTIME_COMMANDS } from './parse-args' import { type RnxRunResult, runRnx } from './run-rnx' import type { ParsedBridgeCliArgs, WsBridge } from './ws-bridge' // the verbs that reach a simulator through the bridge and nothing else. every // other registered command needs the machine rnx is installed on: a device to // boot, a browser to open, a credential store, the bridge daemon, or the local // filesystem. `state` and `reset` sit outside runInspect but take the same // bridge, so they belong here too. const IN_PROCESS_COMMANDS: ReadonlySet = new Set([ ...TOP_LEVEL_RUNTIME_COMMANDS, 'state', 'reset', ]) const RUNNABLE = [...IN_PROCESS_COMMANDS].sort().join(', ') export interface RnxInProcessOptions { /** the command and its arguments, with no node/script prefix. */ argv: string[] /** * the transport the command runs against. it takes the parsed bridge args * because the local host builds its bridge out of `--sim`, `--port`, and * `--timeout`, while a box ignores all three and answers on its own socket. */ createBridge: (parsed: ParsedBridgeCliArgs) => WsBridge } export async function runRnxInProcess( options: RnxInProcessOptions, ): Promise { return runRnx(options.argv, async (argv) => { const parsed = parseRnxArgs(argv) for (const warning of parsed.warnings) console.error(warning) const command = parsed.command if (!command || !IN_PROCESS_COMMANDS.has(command)) { const named = command ? `rnx ${command}` : 'rnx' console.error( ` ${named} is not available here; this rnx drives a simulator and has no machine to run on. runs here: ${RUNNABLE}`, ) return 1 } const simTarget = parsed.globalFlags['sim'] ?? parsed.globalFlags['session'] const commandArgs = typeof simTarget === 'string' ? ['--sim', simTarget, ...parsed.commandArgs] : parsed.commandArgs if (commandArgs.includes('--then')) { console.error( ' rnx do --then is not available here; a chain runs against a sim connection rather than the bridge. run the actions one at a time.', ) return 1 } const port = parsed.globalFlags['port'] const opts = { ...(typeof port === 'number' ? { port } : {}), createBridge: options.createBridge, } if (command === 'state') { const { runState } = await import('./commands/state') return runState(commandArgs, opts) } if (command === 'reset') { const { runReset } = await import('./commands/reset') return runReset(commandArgs, opts) } const { runInspect } = await import('./commands/inspect') await runInspect([command, ...commandArgs], { ...opts, verbose: parsed.verbose }) return 0 }) } // the transport contract and the command's own name, re-exported so a host // that only wants to register this entry and supply a bridge (a Box Durable // Object, say) has one module to import. export { rnxPublicBrand } from '../src/public-brand' export type { BridgeClaimResult, BridgeSimInfo, ParsedBridgeCliArgs, WsBridge, } from './ws-bridge'