Skip to content

feat: add browser/WASM support for bashkit#646

Merged
chaliy merged 11 commits intomainfrom
claude/bashkit-browser-support-4LLyc
Mar 16, 2026
Merged

feat: add browser/WASM support for bashkit#646
chaliy merged 11 commits intomainfrom
claude/bashkit-browser-support-4LLyc

Conversation

@chaliy
Copy link
Contributor

@chaliy chaliy commented Mar 16, 2026

Summary

  • Enable bashkit to run in the browser via WebAssembly (wasm32-wasip1-threads target)
  • Add browser terminal example with Vite dev server and Catppuccin-themed terminal UI
  • Fix WASM performance: gate tokio::time::timeout + spawn_blocking with #[cfg(target_family = "wasm")] — reduces per-command latency from 4120ms to ~20ms
  • Upgrade @napi-rs/wasm-runtime to 1.x for compatibility with napi-rs CLI 3.5.x

What changed

  • crates/bashkit/src/lib.rs: Conditional compilation for WASM — parse inline without timeout/spawn_blocking on WASM targets
  • crates/bashkit/Cargo.toml: Split tokio features so rt-multi-thread and fs are native-only
  • crates/bashkit-js/package.json: Added browser field, exports conditions, build:wasm script
  • examples/browser/: Complete browser example with Vite config, COOP/COEP headers, terminal UI

Test plan

  • cargo test --all-features — all 2,909 tests pass
  • cargo clippy --all-targets --all-features -- -D warnings — clean
  • cargo fmt --check — clean
  • Browser smoke test via agent-browser — commands execute in ~20ms

chaliy and others added 11 commits March 16, 2026 03:59
Adds a browser-based terminal example using Bashkit compiled to
WebAssembly via NAPI-RS WASI threads. Includes Vite config with
required COOP/COEP headers for SharedArrayBuffer support.
Feature-gate tokio to use only WASM-compatible features (sync, macros,
io-util, rt, time) at the workspace level, with rt-multi-thread and fs
added per-crate for native targets only. This unblocks compilation for
wasm32-wasip1-threads.

- Add package.json `exports` with `browser` condition pointing to the
  existing bashkit.wasi-browser.js WASM loader
- Add @napi-rs/wasm-runtime as optional dependency for browser WASM
- Configure browser example Vite with esnext target (top-level await)
  and optimizeDeps exclusion for the WASM entry
- Add *.wasm to .gitignore (build artifacts)
- Update bashkit-bench, bashkit-eval, bashkit-python Cargo.toml to
  explicitly declare rt-multi-thread feature they need
The bashkit package is symlinked from node_modules into crates/bashkit-js/.
Vite follows symlinks and resolves @fs/ URLs for the WASM binary and worker
files, which are blocked by default strict fs policy. Adding server.fs.allow
for the project root fixes the 403 errors on .wasm and .mjs worker files.
Three fixes for the browser example failing with createRequire error:

1. Add top-level "browser" field to bashkit package.json pointing to
   bashkit.wasi-browser.js — the universal bundler convention
2. Add resolve.alias in Vite config that resolves the WASM browser
   entry via realpathSync, bypassing unreliable export condition
   matching when optimizeDeps.exclude is set
3. Use file: protocol for @everruns/bashkit dep so npm install
   creates a symlink to the local crate instead of fetching from npm
The WASM runtime 0.2.x was incompatible with WASM binaries built by
napi-rs CLI 3.5.x — missing _emnapi_async_worker linkage caused a
WebAssembly.Instance LinkError in the browser.

- Upgrade @napi-rs/wasm-runtime from ^0.2.9 to ^1.1.1 in both
  bashkit-js and the browser example
- Add build:wasm script to bashkit-js package.json
- Add npm start script to browser example that builds WASM then
  starts Vite dev server (single command setup)
- Verified end-to-end in headless Chromium: echo, cat, bash scripts
  all execute correctly in the browser
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
tokio::task::spawn_blocking and tokio::time::timeout don't work in WASM
(no blocking thread pool, timer driver unreliable). The parser timeout
was causing every command to take ~4120ms instead of ~20ms — the full
timeout duration was being waited even though parsing completed instantly.

On wasm32 targets, parse inline without timeout/spawn_blocking.
Native targets keep the existing behavior unchanged.

Before: 4120ms per command (echo, cat, anything)
After: 19-23ms per command (200x faster)
The field is unused on WASM targets since the timeout/spawn_blocking
path is skipped. Gate it to eliminate dead_code warning.
@chaliy chaliy merged commit c56e052 into main Mar 16, 2026
18 checks passed
@chaliy chaliy deleted the claude/bashkit-browser-support-4LLyc branch March 16, 2026 04:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant