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
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
run: pnpm install --frozen-lockfile

- name: Run tests
working-directory: sandboxes/js
working-directory: wasm-sandboxes/js
run: pnpm vitest run

python-sandbox-tests:
Expand Down Expand Up @@ -57,9 +57,9 @@ jobs:
python-version: '3.x'

- name: Install Python dependencies
working-directory: sandboxes/python
working-directory: wasm-sandboxes/python
run: pip install -r requirements.txt -q

- name: Run tests
working-directory: sandboxes/python
working-directory: wasm-sandboxes/python
run: pnpm vitest run
5 changes: 5 additions & 0 deletions packages/bash-types/src/bash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ export interface BashOptions {
*/
runtime: BaseRuntime;

/**
* The host workspace directory
*/
hostWorkspace?: string;

/**
* The initial working directory
*/
Expand Down
13 changes: 8 additions & 5 deletions packages/bash-types/src/runtime.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
import { State } from "./state";

export interface BaseRuntime {

/**
* Execute a command
* The host workspace directory
*/
executeCommand(code: string): Promise<RuntimeResult>;
hostWorkspace?: string;

/**
* Execute a code
*/
executeCode(language: string, code: string): Promise<RuntimeResult>;
executeCode(state: State, code: string, language: string): Promise<unknown>;

/**
* Execute a file
*/
executeFile(language: string, filePath: string): Promise<RuntimeResult>;
executeFile(state: State, filePath: string, language: string): Promise<unknown>;

/**
* Resolve a directory path
*/
resolveDirectoryPath(directoryPath: string): Promise<string>;
resolveDirectoryPath(state: State, directoryPath: string): Promise<string>;
}

export interface RuntimeResult {
Expand Down
1 change: 0 additions & 1 deletion packages/bash-types/src/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,4 @@ export interface State {
* The return code of the last executed command (ex: 0 for success, 1 for error).
*/
lastExitCode: number;

}
2 changes: 2 additions & 0 deletions packages/bash-wasm/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
"tsup": "^8.0.0"
},
"dependencies": {
"@capsule-run/cli": "^0.8.5",
"@capsule-run/sdk": "^0.8.5",
"@capsule-run/bash-types": "workspace:*"
}
}
69 changes: 59 additions & 10 deletions packages/bash-wasm/src/runtime.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,70 @@
import type { BaseRuntime, RuntimeResult } from "@capsule-run/bash-types";
import path from "path";
import fs from "fs";

import { run } from '@capsule-run/sdk/runner';

import type { BaseRuntime, State } from "@capsule-run/bash-types";


export class WasmRuntime implements BaseRuntime {
constructor() {}
private jsSandbox: string;
private pythonSandbox: string;

async executeCommand(code: string): Promise<RuntimeResult> {
throw new Error("Method not implemented.");
public hostWorkspace: string = "";

constructor() {
const jsWasmPath = path.resolve(__dirname, "../dist/sandboxes/js/sandbox.wasm");
const pyWasmPath = path.resolve(__dirname, "../dist/sandboxes/python/sandbox.wasm");

if (fs.existsSync(jsWasmPath) && fs.existsSync(pyWasmPath)) {
this.jsSandbox = jsWasmPath;
this.pythonSandbox = pyWasmPath;
} else {
this.jsSandbox = path.resolve(__dirname, "../../wasm-sandboxes/js/sandbox.ts");
// We need to install the requirements.txt first to run the python sandbox
this.pythonSandbox = path.resolve(__dirname, "../../wasm-sandboxes/python/sandbox.py");
}
}

async executeCode(language: string, code: string): Promise<RuntimeResult> {
throw new Error("Method not implemented.");
async executeCode(state: State, code: string, language: string = "js"): Promise<unknown> {
const result = await run({
file: language === "js" || language === "javascript" ? this.jsSandbox : this.pythonSandbox,
args: ["EXECUTE_CODE", JSON.stringify(state), code],
mounts: [`${this.hostWorkspace}::/`],
})

if (result.error) {
throw result.error.message;
}

return result.result;
}

async executeFile(language: string, filePath: string): Promise<RuntimeResult> {
throw new Error("Method not implemented.");
async executeFile(state: State, filePath: string, language: string = "js"): Promise<string> {
const result = await run({
file: language === "js" || language === "javascript" ? this.jsSandbox : this.pythonSandbox,
args: ["EXECUTE_FILE", JSON.stringify(state), filePath],
mounts: [`${this.hostWorkspace}::/`],
})

if (result.error) {
throw result.error.message;
}

return result.result as string;
}

async resolveDirectoryPath(directoryPath: string): Promise<string> {
throw new Error("Method not implemented.");
async resolveDirectoryPath(state: State, directoryPath: string): Promise<string> {
const result = await run({
file: this.jsSandbox,
args: ["RESOLVE_DIRECTORY_PATH", JSON.stringify(state), directoryPath],
mounts: [`${this.hostWorkspace}::/`],
})

if (result.error) {
throw result.error.message;
}

return result.result as string;
}
}
9 changes: 0 additions & 9 deletions packages/bash-wasm/vitest.config.ts

This file was deleted.

15 changes: 7 additions & 8 deletions packages/bash/src/core/bash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,22 @@ export class Bash {
private runtime: BaseRuntime;
private filesystem: Filesystem;

public readonly state: StateManager;
public readonly stateManager: StateManager;

constructor({ runtime, initialCwd = "workspace" }: BashOptions) {
constructor({ runtime, hostWorkspace = ".capsule/session/workspace", initialCwd = "workspace" }: BashOptions) {
this.runtime = runtime;
this.filesystem = new Filesystem(".capsule/session/workspace");
this.state = new StateManager(runtime, initialCwd);
this.runtime.hostWorkspace = hostWorkspace;
this.stateManager = new StateManager(runtime, initialCwd);
this.filesystem = new Filesystem(hostWorkspace);

this.filesystem.init();
}

run(command: string) {
this.runtime.executeCommand(command);
}
run(command: string) {}

reset() {
this.filesystem.reset();
this.state.reset();
this.stateManager.reset();
}

}
2 changes: 1 addition & 1 deletion packages/bash/src/core/filesystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export class Filesystem {
'etc/os-release': 'NAME="Capsule OS"\nVERSION="1.0"\nID=capsule\n',
'etc/passwd': 'root:x:0:0:root:/root:/bin/bash\n',
'proc/cpuinfo': 'processor\t: 0\nvendor_id\t: CapsuleVirtualCPU\n',
'workspace/README.md': '# Welcome to the Capsule Bash Environment\\n\\nYou are operating inside a secure and minimalist sandbox.\\n\\n## Environment Details:\\n- **OS:** Capsule Bash (Mocked Linux)\\n- **Capabilities:** Standard bash operations are supported, but advanced system calls may be restricted.\\n- **Working Directory:** `/workspace` is your designated safe zone.'
'workspace/README.md': '# Welcome to the Capsule Bash Environment\\n\\nYou are operating inside a secure and minimalist sandboxed bash.'
};

for (const [relativePath, content] of Object.entries(files)) {
Expand Down
11 changes: 8 additions & 3 deletions packages/bash/src/core/stateManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,14 @@ export class StateManager {
return this.state.cwd.startsWith('/') ? this.state.cwd : `/${this.state.cwd}`;
}

public async changeDirectory(targetPath: string): Promise<void> {
const resolvedPath = await this.runtime.resolveDirectoryPath(targetPath);
this.state.cwd = resolvedPath;
public async changeDirectory(targetPath: string): Promise<boolean> {
try {
const resolvedPath = await this.runtime.resolveDirectoryPath(this.state, targetPath);
this.state.cwd = resolvedPath;
return true;
} catch {
return false;
}
}

public setEnv(key: string, value: string): void {
Expand Down
29 changes: 9 additions & 20 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pnpm-workspace.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
packages:
- packages/*
- sandboxes/*
- wasm-sandboxes/*
File renamed without changes.
1 change: 0 additions & 1 deletion sandboxes/js/sandbox.ts → wasm-sandboxes/js/sandbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,6 @@ const executeCommand = task(
process.chdir(state.cwd);
const exports: { execute?: (args: string[]) => any } = {};

console.log(args)
const moduleWrapper = new Function('exports', scriptContent);

moduleWrapper(exports);
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading