Kernel

The object kernel_wrapper exposes. A thin bridge to the kernel worker: it forwards the kernel's RPC methods to JavaScript and delivers the kernel's callbacks to handlers you register. Construct it, register your handlers, then call start(). The page must be cross-origin isolated, since the kernel runs threads.

Constructor

new Kernel(kernelUrl: string)

Creates the bridge. Nothing connects until start().

kernelUrl
Absolute URL of the kernel worker module script, e.g. /kernel.js.

Methods

start(): Promise<void>

Spawns the kernel as a module Web Worker and connects the RPC channel. Provisioning the filesystem is your job once it resolves.

returns
A promise that resolves when the bridge is ready to take calls.
const kernel = new Kernel('/kernel.js');
kernel.onStreamOut((stream, bytes) => term.write(bytes));
await kernel.start();
await kernel.untar('/', sysrootBytes);

run(argv: string[], env: string[], cwd: string, piped: boolean): Promise<number>

Spawns a session running argv[0] with the given argument vector.

argv
The full argument vector; argv[0] is the program to run.
env
Environment as "VAR=value" strings.
cwd
Absolute working directory for the session.
piped
false gives the session a fresh tty; true gives it pipes for stdio and no controlling tty (the remote-exec shape, e.g. a language server).
returns
A promise resolving to the session id (sid, equal to the leader's pid) at spawn time, not at exit. Watch onProcessEvent for the matching SessionEnded to learn the exit code.
const sid = await kernel.run(['/usr/bin/sh'], ['HOME=/root'], '/root', false);

hangup(sid: number): void

Ends a whole session as if its terminal closed, tearing down its process group.

sid
The session id returned by run.

resizeTty(ttyId: number, cols: number, rows: number): void

Tells the kernel a tty session's terminal changed size, delivering SIGWINCH to its foreground process group.

ttyId
The stream_id from the session's SessionStarted event.
cols, rows
The new terminal dimensions in cells.

writeFile(path: string, data: Uint8Array): void

Writes bytes to the kernel filesystem, creating parent directories as needed. A fire-and-forget notification: it returns immediately with no acknowledgement.

path
Absolute destination path.
data
The file contents.

readFile(path: string): Promise<Uint8Array>

Reads a file's current contents from the kernel filesystem.

path
Absolute path to read.
returns
A promise resolving to the file's bytes, rejecting if it cannot be read.

untar(path: string, data: Uint8Array): Promise<void>

Extracts a tar archive into the filesystem, rooted at path. Usually called once after start() to mount the sysroot at /.

path
Absolute directory to extract into.
data
The tar archive bytes.
returns
A promise that resolves when extraction completes, rejecting on a malformed archive.

Handlers

Register handlers before start() so no early event is missed. onProcessEvent adds a listener; each stream handler sets the single callback for its channel.

onProcessEvent(cb: (event: object) => void): void

Called for every process and session lifecycle event. Each event is a plain object keyed by its variant:

SessionStarted
{ sid, stream_id, piped, argv }  the session's stdio is ready; route io for stream_id (and resizeTty it, for a tty).
SessionEnded
{ sid, result }  the session finished; result is { Ok: code } (code is null on signal death) or { Err: message }.
ProcessStarted
{ sid, pid, parent_pid, pgid, argv }
ProcessExited
{ sid, pid, result }

run resolves at spawn; SessionEnded is the completion signal.

onStreamOut(cb: (stream: number, data: Uint8Array) => void | Promise<void>): void

Called with a session's stdout bytes. If cb returns a promise, the kernel's next write for that stream waits on it: this is the backpressure ack, so resolve it once you have consumed the bytes.

stream
The stream the bytes belong to (a session's stream_id).
data
The output bytes.
kernel.onStreamOut((stream, bytes) => { term.write(bytes); });

onStreamErr(cb: (stream: number, data: Uint8Array) => void | Promise<void>): void

Like onStreamOut, but for stderr (fd 2). Only piped sessions emit it; a tty merges stderr into stdout. Returning a promise applies the same backpressure.

onStreamIn(cb: (stream: number) => Promise<Uint8Array | null>): void

Asked for a session's stdin. The kernel long-polls: it calls cb, awaits the result, feeds those bytes, and calls again. Return a pending promise you resolve when input arrives.

stream
The stream requesting input.
returns
A promise resolving to the input bytes, or null for end-of-input (EOF).
kernel.onStreamIn((stream) => nextKeystrokes(stream)); // Promise<Uint8Array | null>

onStreamClosed(cb: (stream: number) => void): void

Called when a stream is finished, after its last output has been acknowledged; any pending onStreamIn for it is then cancelled. This is the stream closing, not the session ending: watch SessionEnded for that.

stream
The stream that closed.