gbash

WebAssembly

gbash compiles to WebAssembly and runs in the browser or Node.js. The @ewhauser/gbash-wasm package provides TypeScript bindings for both environments.

npm publishing is not yet enabled. Use a workspace reference or local path to depend on this package.

Browser

Import from the browser entry point:

import { Bash, defineCommand } from "@ewhauser/gbash-wasm/browser";

Creating an Instance

const bash = new Bash({
  cwd: "/home/agent",
  env: { MY_VAR: "hello" },
  files: {
    "/home/agent/greeting.txt": "hello world\n",
    "/home/agent/large.txt": async () => "lazy payload\n",
  },
});

BashOptions

OptionTypeDescription
cwdstringInitial working directory. Defaults to /home/agent.
envRecord<string, string>Environment variables.
filesInitialFilesFiles to pre-populate in the virtual filesystem. Supports strings, Uint8Array, lazy providers, and descriptor objects with content or lazy plus optional mode and mtime.
customCommandsCustomCommand[]Custom commands implemented in JavaScript.
wasmUrlstringOverride URL for gbash.wasm.
wasmExecUrlstringOverride URL for wasm_exec.js.

Executing Commands

const result = await bash.exec("echo $MY_VAR && cat /home/agent/greeting.txt");
console.log(result.stdout);
// hello
// hello world
console.log(result.exitCode); // 0

The return type is CommandResult:

type CommandResult = {
  stdout: string;
  stderr: string;
  exitCode: number;
};

Writing Files

bash.writeFile("/home/agent/data.json", '{"key": "value"}');
const result = await bash.exec("cat /home/agent/data.json");

Initial Files

files accepts:

  • string
  • Uint8Array
  • () => string | Uint8Array | Promise<string | Uint8Array>
  • { content: string | Uint8Array; mode?: number; mtime?: Date }
  • { lazy: () => string | Uint8Array | Promise<string | Uint8Array>; mode?: number; mtime?: Date }

Lazy providers are materialized on first content-sensitive access inside the sandbox.

Custom Commands

Define commands in JavaScript that are callable from bash scripts:

const upper = defineCommand("upper", (args) => {
  return {
    stdout: args.join(" ").toUpperCase() + "\n",
    stderr: "",
    exitCode: 0,
  };
});
 
const bash = new Bash({
  customCommands: [upper],
});
 
const result = await bash.exec("upper hello world");
// result.stdout => "HELLO WORLD\n"

The defineCommand helper takes a name and a handler function. The handler receives the argument list and returns a CommandResult (or a Promise<CommandResult> for async commands).

function defineCommand(
  name: string,
  run: (args: string[]) => CommandResult | Promise<CommandResult>,
): CustomCommand;

Cleanup

await bash.dispose();

Node.js

Import from the node entry point for server-side use:

import { Bash, defineCommand } from "@ewhauser/gbash-wasm/node";

The API is identical to the browser version. The Node.js entry point handles loading wasm_exec.js via import() and reads the .wasm binary from the filesystem or a URL.

const bash = new Bash({ cwd: "/home/agent" });
const result = await bash.exec("echo hello from node");
console.log(result.stdout); // "hello from node\n"

Asset Resolution

Both browser and node entry points co-locate gbash.wasm and wasm_exec.js alongside the package. You can override these paths via wasmUrl and wasmExecUrl in BashOptions if you need to serve the assets from a CDN or different location.