Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
name: Release
run-name: "Release v${{ github.event.inputs.version }}"

on:
workflow_dispatch:
inputs:
version:
description: 'Release version'
required: true

jobs:
release:
Expand Down
12 changes: 9 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,22 @@

**Sandboxed bash made for agents**

![CI](https://github.com/capsulerun/bash/actions/workflows/ci.yml/badge.svg)
![CI](https://img.shields.io/github/actions/workflow/status/capsulerun/bash/ci.yml?branch=main) ![Release](https://img.shields.io/github/v/release/capsulerun/bash)

[Getting Started](#getting-started) • [How It Works](#how-it-works) • [Contributing](#contributing)

![Example Shell](assets/example.gif)
</div>

## Quick Start
## Getting Started

### TypeScript SDK

```bash
npm install @capsule-run/bash @capsule-run/bash-wasm
```

### TypeScript SDK
Run it:

```typescript
import { Bash } from "@capsule-run/bash";
Expand Down Expand Up @@ -60,6 +64,8 @@ See the [MCP README](packages/bash-mcp) for configuration details.

### Interactive shell

Clone the repository, then run from the project root:

```bash
pnpm -s bash-wasm-shell
```
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "capsulerun-bash-monorepo",
"version": "0.1.0",
"version": "0.1.1",
"private": true,
"description": "Sandboxed bash for agents",
"license": "Apache-2.0",
Expand Down
4 changes: 1 addition & 3 deletions packages/bash-mcp/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
# `Capsule` bash mcp

[![MCP Server Release](https://github.com/capsule-run/bash/actions/workflows/mcp-integration-release.yml/badge.svg)](https://github.com/capsule-run/bash/actions/workflows/mcp-integration-release.yml)
# `Capsule` Bash MCP

An MCP server that gives your AI agent the ability to run bash commands in a secure, persistent, WebAssembly-sandboxed environment.

Expand Down
2 changes: 1 addition & 1 deletion packages/bash-mcp/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@capsule-run/bash-mcp",
"version": "0.1.0",
"version": "0.1.1",
"description": "MCP server exposing sandboxed bash made for agents",
"type": "module",
"main": "./src/index.ts",
Expand Down
2 changes: 1 addition & 1 deletion packages/bash-mcp/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ function getSession(sessionId: string): Bash {

const server = new McpServer({
name: "@capsule-run/bash-mcp",
version: "0.1.0",
version: "0.1.1",
});

server.registerTool(
Expand Down
2 changes: 1 addition & 1 deletion packages/bash-types/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# @capsule-run/bash-types

Shared TypeScript types for the [`@capsule-run/bash`](https://github.com/capsule-run/bash) ecosystem.
Shared TypeScript types for the [`@capsule-run/bash`](https://github.com/capsulerun/bash) ecosystem.
2 changes: 1 addition & 1 deletion packages/bash-types/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@capsule-run/bash-types",
"version": "0.1.0",
"version": "0.1.1",
"description": "",
"type": "module",
"homepage": "https://github.com/capsulerun/bash",
Expand Down
2 changes: 1 addition & 1 deletion packages/bash-types/src/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export interface BaseRuntime {
/**
* Preload the runtime
*/
preload(state: State, name?: string): Promise<void>;
preload(state: State, name?: string): Promise<boolean>;

/**
* Execute a code
Expand Down
1 change: 0 additions & 1 deletion packages/bash-wasm/README.md

This file was deleted.

26 changes: 26 additions & 0 deletions packages/bash-wasm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# `Capsule` Bash WASM Runtime

**WebAssembly runtime for `@capsule-run/bash`**

## Install

```bash
npm install @capsule-run/bash @capsule-run/bash-wasm
```

## Usage

```typescript
import { Bash } from "@capsule-run/bash";
import { WasmRuntime } from "@capsule-run/bash-wasm";

const bash = new Bash({ runtime: new WasmRuntime() });
```

> `WasmRuntime` runs in Node.js only. Browser environments are not supported.

For full documentation, visit the [GitHub repository](https://github.com/capsulerun/bash).

## License

Apache License 2.0.
2 changes: 1 addition & 1 deletion packages/bash-wasm/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@capsule-run/bash-wasm",
"version": "0.1.0",
"version": "0.1.1",
"description": "Sandboxed bash for agents",
"type": "module",
"homepage": "https://github.com/capsulerun/bash",
Expand Down
6 changes: 5 additions & 1 deletion packages/bash-wasm/src/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,14 @@ export class WasmRuntime implements BaseRuntime {
}
}

async preload(state: State, name: string = "js") {
async preload(state: State, name: string = "js"): Promise<boolean> {
if(name === "js") {
await run({
file: this.jsSandbox,
args: ["PRELOAD", JSON.stringify(state)],
mounts: [`${this.hostWorkspace}::/`]
})
return true;
}

if(name === "python") {
Expand All @@ -45,7 +46,10 @@ export class WasmRuntime implements BaseRuntime {
args: ["PRELOAD", JSON.stringify(state)],
mounts: [`${this.hostWorkspace}::/`]
})
return true;
}

return false;
}

async executeCode(state: State, code: string, language: string = "js"): Promise<unknown> {
Expand Down
1 change: 0 additions & 1 deletion packages/bash/README.md

This file was deleted.

37 changes: 37 additions & 0 deletions packages/bash/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# `Capsule` Bash

**Sandboxed bash made for agents**

## Install

```bash
npm install @capsule-run/bash @capsule-run/bash-wasm
```

## Usage

```typescript
import { Bash } from "@capsule-run/bash";
import { WasmRuntime } from "@capsule-run/bash-wasm";

const bash = new Bash({ runtime: new WasmRuntime() });

const result = await bash.run("mkdir src && touch src/index.ts");

console.log(result);
/*
{
stdout: "Folder created ✔\nFile created ✔",
stderr: "",
diff: { created: ['src', 'src/index.ts'], modified: [], deleted: [] },
duration: 10,
exitCode: 0,
}
*/
```

For full documentation, visit the [GitHub repository](https://github.com/capsulerun/bash).

## License

Apache License 2.0.
2 changes: 1 addition & 1 deletion packages/bash/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@capsule-run/bash",
"version": "0.1.0",
"version": "0.1.1",
"description": "Sandboxed bash for agents",
"type": "module",
"homepage": "https://github.com/capsulerun/bash",
Expand Down
22 changes: 21 additions & 1 deletion packages/bash/src/core/bash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ export class Bash {
private parser: Parser;
private executor: Executor;
private customCommands: CustomCommand[];
public isPythonSandboxReady: boolean;
public isJsSandboxReady: boolean;


public readonly stateManager: StateManager;

Expand All @@ -24,13 +27,30 @@ export class Bash {
this.executor = new Executor(runtime, this.customCommands, this.stateManager.state);

this.filesystem.init();

this.isPythonSandboxReady = false;
this.isJsSandboxReady = false;
}

async preload(name: string = "js") {
await this.runtime.preload(this.stateManager.state, name);
const ready = await this.runtime.preload(this.stateManager.state, name);

if (name === "js") {
this.isJsSandboxReady = ready;
} else if (name === "python") {
this.isPythonSandboxReady = ready;
}
}

async run(command: string): Promise<CommandResult> {
if (!this.isJsSandboxReady) {
await this.preload("js");
}

if (command.includes("python") && !this.isPythonSandboxReady) {
await this.preload("python");
}

const ast = this.parser.parse(command);
return this.executor.execute(ast);
}
Expand Down
17 changes: 11 additions & 6 deletions packages/bash/src/core/executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,17 @@ export class Executor {
}

async execute(node: ASTNode, stdin = ''): Promise<CommandResult> {
switch (node.type) {
case 'command': return this.executeCommand(node, stdin);
case 'pipeline': return this.executePipeline(node);
case 'and': return this.executeAnd(node);
case 'or': return this.executeOr(node);
case 'sequence': return this.executeSequence(node);
try {
switch (node.type) {
case 'command': return this.executeCommand(node, stdin);
case 'pipeline': return this.executePipeline(node);
case 'and': return this.executeAnd(node);
case 'or': return this.executeOr(node);
case 'sequence': return this.executeSequence(node);
}
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
return { stdout: '', stderr: message, exitCode: 1, state: { cwd: this.state.cwd, env: this.state.env, exitCode: 1 }, diff: { created: [], modified: [], deleted: [] }, durationMs: 0 };
}
}

Expand Down
Loading