Embedding
The showcase terminals use only the public exports of
@aletheia-works/formicarium: createSession from /browser, and
createCommandSession and createInstaller from the package root (the
installer quietly installs the tools every page gets, such as nano). Your page
can do the same.
This guide matches the formicarium 0.9.0 pack (the candidate's version). Install it from npm:
npm install @aletheia-works/formicarium@0.9.0
Requirements
Section titled “Requirements”- Same origin. The Worker (
runtime/web/package-worker.bundle.js) and the core underassets/paludarium/(the loaderindex.mjs, the modules next to it,paludarium.wasmandbuild-info.json) must be served from your page’s origin. Pass the absolute URLs of the Worker, loader, wasm and build information asassets; the other modules are read next to the loader. - Cross-origin isolation. Serve COOP
same-originand COEPrequire-corp, or use a service worker such as coi-serviceworker on static hosting. - CSP. Allow same-origin scripts and Workers and WebAssembly compilation,
for example
script-src 'self' 'wasm-unsafe-eval'; worker-src 'self'; connect-src 'self'.blob:is not needed. The core is loaded from the asset URLs after its digests are checked, so serve them from an origin you trust. - Guests. Use a catalog (
GuestCatalog) whose entries name same-origin guest files with their SHA256 andbuild-info.json.install <tool>checks every byte before it registers a guest.
Example
Section titled “Example”This code is type-checked against the pack this site was built from.
// The embedding guide shows this file as it is; the build type-checks it// against the types of the candidate's pack.import { createCommandSession, decodeUtf8, type GuestCatalog,} from '@aletheia-works/formicarium';import { createSession } from '@aletheia-works/formicarium/browser';
/** * Installs one tool from a catalog and prints its version. `packageBase` is * the same-origin URL of the package files; the page must be cross-origin * isolated (COOP/COEP headers or a service worker such as coi-serviceworker). */export async function toolVersion( packageBase: URL, catalog: GuestCatalog, tool: string,): Promise<string> { const session = await createSession({ cwd: '/work/app', entries: [ { path: '/work', type: 'dir', mode: 0o755 }, { path: '/work/app', type: 'dir', mode: 0o755 }, ], assets: { loaderURL: new URL('assets/paludarium/index.mjs', packageBase), wasmURL: new URL('assets/paludarium/paludarium.wasm', packageBase), workerURL: new URL('runtime/web/package-worker.bundle.js', packageBase), buildInfoURL: new URL('assets/paludarium/build-info.json', packageBase), }, }); try { const shell = createCommandSession(session, { catalog }); await shell.execute(`install ${tool}`, { onProgress: (progress) => console.log(progress.phase, progress.processedBytes), }); const result = await shell.execute(`${tool} --version`); return decodeUtf8(result.stdout); } finally { await session.dispose(); }}Tools ready from the start
Section titled “Tools ready from the start”To open a terminal with tools already installed, install them with
createInstaller before you create the command session. It runs the same
checks as install <tool> but writes nothing, so the user never sees an install
line. An installed tool runs by name in the command session, and every guest
run finds it as /usr/bin/<tool>, so one tool can start another (for example a
version control tool that opens an editor). To register guest bytes you already
hold instead of a catalog entry, check them with validateGuest and pass the
result to registerValidatedGuest.
// The embedding guide shows this file as it is; the build type-checks it// against the types of the candidate's pack.import { type CommandSession, createCommandSession, createInstaller, type GuestCatalog, type Session,} from '@aletheia-works/formicarium';
/** * Installs `tools` before the user types anything, without writing to the * terminal, then returns the command session for the terminal. Each tool runs * by name there, and other guests find it as `/usr/bin/<tool>` (for example a * version control tool that opens an editor). */export async function terminalWithTools( session: Session, catalog: GuestCatalog, tools: readonly string[],): Promise<CommandSession> { const installer = createInstaller(session, catalog); for (const tool of tools) await installer.install(tool); return createCommandSession(session, { catalog });}Output and display
Section titled “Output and display”CommandSession.execute returns the original stdout and stderr bytes of
each command. For a guest run they also arrive in chunks through onOutput;
write one or the other, not both. Decode the bytes for display and never treat
them as HTML. onProgress reports totalBytes: null when the total is unknown,
which differs from 0. Pass an AbortSignal to cancel, and dispose the
session when the page goes away.