組込み
showcase の端末は @aletheia-works/formicarium の公開 export だけを使います:
/browser の createSession と、package の root の createCommandSession と
createInstaller です(installer は、nano のようにどの page にも入れるツールを、
何も表示せずに導入します)。自分の page でも同じことができます。
この guide は formicarium 0.9.0 の pack(候補の版)に合わせて書いています。npm から入れるには:
npm install @aletheia-works/formicarium@0.9.0
- 同じ origin。 Worker(
runtime/web/package-worker.bundle.js)とassets/paludarium/のコア(loader のindex.mjs、その隣のモジュール、paludarium.wasm、build-info.json)を page と同じ origin から配ります。 Worker・loader・wasm・ビルド情報の絶対 URL をassetsに渡し、ほかのモジュールは loader の隣から読み込みます。 - クロスオリジン分離。 COOP
same-originと COEPrequire-corpを送るか、 静的な hosting では coi-serviceworker などの service worker を使います。 - CSP。 同じ origin の script と Worker、WebAssembly の compile を許します。
たとえば
script-src 'self' 'wasm-unsafe-eval'; worker-src 'self'; connect-src 'self'です。blob:は要りません。コアは SHA-256 を照合した後に 資産の URL から読み込むので、信頼できる origin から配ります。 - guest。 同じ origin の guest の file と、その SHA256 と
build-info.jsonを書いた catalog(GuestCatalog)を使います。install <tool>はすべての bytes を確かめてから guest を登録します。
この code は、このサイトを作った pack の型で型検査しています。
// 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(); }}最初からツールを入れておく
Section titled “最初からツールを入れておく”ツールを導入済みの端末を開くには、command session を作る前に createInstaller
でツールを導入します。install <tool> と同じ確かめをしますが、何も書き出さない
ので、利用者には導入の行が見えません。導入したツールは、command session で名前を
打てば動き、どの guest の実行からも /usr/bin/<tool> として見えるので、ツールから
別のツールを起動できます(たとえば version control のツールが editor を開く)。
catalog の代わりに手元にある guest の bytes を登録するには、validateGuest で
確かめてから、その結果を 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 });}CommandSession.execute は command ごとに元の stdout と stderr の bytes を
返します。guest の実行では同じ bytes が onOutput でも少しずつ届くので、どちらか
一方だけを書きます。表示のときは bytes を文字に decode し、HTML として扱いません。
onProgress は全体の大きさが分からないとき totalBytes: null を返し、0 とは
区別します。取り消すには AbortSignal を渡し、page を離れるときは session を
dispose します。