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
4 changes: 2 additions & 2 deletions crates/capsule-cli/Cargo.lock

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

4 changes: 2 additions & 2 deletions crates/capsule-cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "capsule-run"
version = "0.8.0"
version = "0.8.5"
edition = "2024"
description = "Secure WASM runtime to isolate and manage AI agent tasks"
license = "Apache-2.0"
Expand All @@ -16,7 +16,7 @@ path = "src/main.rs"

[dependencies]
clap = { version = "4.5.53", features = ["derive"] }
capsule-core = { version= "0.8.0", path = "../capsule-core" }
capsule-core = { version= "0.8.5", path = "../capsule-core" }
tokio = { version = "1.48.0", features = ["rt", "rt-multi-thread", "macros"] }
serde_json = "1"
dotenvy = "0.15.7"
10 changes: 5 additions & 5 deletions crates/capsule-cli/npm/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@capsule-run/cli",
"version": "0.8.0",
"version": "0.8.5",
"description": "Secure WASM runtime to isolate and manage AI agent tasks",
"bin": {
"capsule": "./bin/capsule.js"
Expand Down Expand Up @@ -29,9 +29,9 @@
"node": ">=18"
},
"optionalDependencies": {
"@capsule-run/cli-darwin-arm64": "0.8.0",
"@capsule-run/cli-darwin-x64": "0.8.0",
"@capsule-run/cli-linux-x64": "0.8.0",
"@capsule-run/cli-win32-x64": "0.8.0"
"@capsule-run/cli-darwin-arm64": "0.8.5",
"@capsule-run/cli-darwin-x64": "0.8.5",
"@capsule-run/cli-linux-x64": "0.8.5",
"@capsule-run/cli-win32-x64": "0.8.5"
}
}
2 changes: 1 addition & 1 deletion crates/capsule-cli/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "maturin"

[project]
name = "capsule-run"
version = "0.8.0"
version = "0.8.5"
description = "Secure WASM runtime to isolate and manage AI agent tasks"
readme = "docs/README-pypi.md"
license = {text = "Apache-2.0"}
Expand Down
2 changes: 1 addition & 1 deletion crates/capsule-core/Cargo.lock

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

2 changes: 1 addition & 1 deletion crates/capsule-core/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "capsule-core"
version = "0.8.0"
version = "0.8.5"
edition = "2024"
description = "Core library for Capsule - WASM runtime for AI agent isolation"
license = "Apache-2.0"
Expand Down
67 changes: 33 additions & 34 deletions crates/capsule-core/src/wasm/compiler/javascript.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,22 +114,6 @@ impl JavascriptWasmCompiler {
}
}

fn find_package(package_name: &str, source_dir: &Path, sdk_path: &Path) -> PathBuf {
if let Some(project_node_modules) = Self::find_node_modules(source_dir) {
let project_path = project_node_modules.join(package_name);
if project_path.exists() {
return project_path;
}
}

let sdk_node_modules = sdk_path.join("node_modules").join(package_name);
if sdk_node_modules.exists() {
return sdk_node_modules;
}

source_dir.join("node_modules").join(package_name)
}

pub fn compile_wasm(&self, export: bool) -> Result<PathBuf, JavascriptWasmCompilerError> {
let source_dir = self.source_path.parent().ok_or_else(|| {
JavascriptWasmCompilerError::FsError("Cannot determine source directory".to_string())
Expand Down Expand Up @@ -200,11 +184,9 @@ export {{ incomingHandler }};
let sdk_path_normalized = Self::normalize_path_for_command(&sdk_path);
let output_wasm_normalized = Self::normalize_path_for_command(&self.output_wasm);

let unenv_path = Self::find_package("unenv", source_dir, &sdk_path_normalized);
let os_polyfill_path = sdk_path_normalized.join("dist/polyfills/os.js");
let process_polyfill_path = sdk_path_normalized.join("dist/polyfills/process.js");
let fs_polyfill_path = sdk_path_normalized.join("dist/polyfills/fs.js");

let mut esbuild_cmd = Self::npx_command();
esbuild_cmd
.arg("esbuild")
Expand Down Expand Up @@ -242,10 +224,10 @@ export {{ incomingHandler }};
.display()
));

esbuild_cmd.arg(format!("--alias:unenv={}", unenv_path.display()));

let unenv_node_dir = unenv_path.join("dist/runtime/node");
let mut stack = vec![(unenv_node_dir, String::new())];
let bundled_unenv_dir = sdk_path_normalized.join("dist/polyfills/unenv-runtime");
let bundled_node_dir = bundled_unenv_dir.join("node");
esbuild_cmd.arg(format!("--alias:unenv={}", bundled_unenv_dir.display()));
let mut stack = vec![(bundled_node_dir, String::new())];
while let Some((dir, prefix)) = stack.pop() {
let Ok(entries) = fs::read_dir(&dir) else {
continue;
Expand Down Expand Up @@ -378,19 +360,36 @@ export {{ incomingHandler }};
})?,
);

let output = Self::npx_command()
.arg("tsc")
.arg(&self.source_path)
let source_dir = self.source_path.parent().ok_or_else(|| {
JavascriptWasmCompilerError::FsError("Cannot determine source directory".to_string())
})?;

let cache_dir_normalized = Self::normalize_path_for_command(&self.cache_dir);

let mut cmd = Self::npx_command();
cmd.arg("tsc");

let tsconfig_path = source_dir.join("tsconfig.json");
if tsconfig_path.exists() {
cmd.arg("--project")
.arg(Self::normalize_path_for_command(&tsconfig_path));
} else {
cmd.arg(Self::normalize_path_for_command(&self.source_path))
.arg("--module")
.arg("esnext")
.arg("--target")
.arg("esnext")
.arg("--moduleResolution")
.arg("bundler")
.arg("--esModuleInterop")
.arg("--skipLibCheck");
}

let output = cmd
.arg("--outDir")
.arg(&self.cache_dir)
.arg("--module")
.arg("esnext")
.arg("--target")
.arg("esnext")
.arg("--moduleResolution")
.arg("bundler")
.arg("--esModuleInterop")
.arg("--skipLibCheck")
.arg(&cache_dir_normalized)
.arg("--rootDir")
.arg(Self::normalize_path_for_command(source_dir))
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.output()?;
Expand Down
4 changes: 2 additions & 2 deletions crates/capsule-sdk/javascript/package-lock.json

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

4 changes: 2 additions & 2 deletions crates/capsule-sdk/javascript/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@capsule-run/sdk",
"version": "0.8.0",
"version": "0.8.5",
"description": "Capsule JavaScript SDK - run AI agent tasks in secure WASM sandboxes",
"type": "module",
"main": "./dist/index.js",
Expand Down Expand Up @@ -30,7 +30,7 @@
"README.md"
],
"scripts": {
"build": "tsc",
"build": "tsc && node scripts/copy-unenv-polyfills.mjs",
"clean": "rm -rf dist"
},
"keywords": [
Expand Down
13 changes: 13 additions & 0 deletions crates/capsule-sdk/javascript/scripts/copy-unenv-polyfills.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { cpSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
import { createRequire } from 'module';

const __dirname = dirname(fileURLToPath(import.meta.url));
const require = createRequire(import.meta.url);

const unenvDir = join(dirname(require.resolve('unenv/package.json')), 'dist/runtime');
const outDir = join(__dirname, '../dist/polyfills/unenv-runtime');

cpSync(unenvDir, outDir, { recursive: true });
console.log(`Copied unenv runtime to ${outDir}`);
33 changes: 16 additions & 17 deletions crates/capsule-sdk/javascript/src/polyfills/fs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
* Provides both Node.js fs API
*/

import { getCwd } from './process.js';

declare const globalThis: {
'wasi:filesystem/types': any;
'wasi:filesystem/preopens': any;
Expand Down Expand Up @@ -76,11 +78,24 @@ function normalizePath(path: string): string {
return path;
}

/**
* Absolute paths pass through unchanged.
*/
function getEffectivePath(path: string): string {
if (path.startsWith('/')) return path;

const cwd = getCwd();
if (cwd === '.' || cwd === '') return path;
if (path === '.') return cwd;
if (path.startsWith('./')) return cwd.replace(/\/+$/, '') + '/' + path.slice(2);
return cwd.replace(/\/+$/, '') + '/' + path;
}

function resolvePath(path: string): { dir: Descriptor; relativePath: string } | null {
const preopens = getPreopenedDirs();
if (preopens.length === 0) return null;

const normalizedPath = normalizePath(path);
const normalizedPath = normalizePath(getEffectivePath(path));

let catchAll: { dir: Descriptor; relativePath: string } | null = null;

Expand Down Expand Up @@ -298,9 +313,6 @@ export function readdir(

// ---------------------------------------------------------------------------
// Sync implementations
// wasi:filesystem/types@0.2.0 descriptor methods (openAt, read, write, stat,
// readDirectory, etc.) are direct component-model imports — they are
// synchronous from JS's perspective. No asyncify, no event-loop blocking.
// ---------------------------------------------------------------------------

function enoent(path: string): Error {
Expand All @@ -312,7 +324,6 @@ function enoent(path: string): Error {

/**
* Read file contents synchronously.
* Backed by wasi:filesystem/types@0.2.0 — fully synchronous.
*/
export function readFileSync(path: string, options?: ReadFileOptions | Encoding): string | Uint8Array {
const resolved = resolvePath(path);
Expand All @@ -332,7 +343,6 @@ export function readFileSync(path: string, options?: ReadFileOptions | Encoding)

/**
* Write data to a file synchronously.
* Backed by wasi:filesystem/types@0.2.0 — fully synchronous.
*/
export function writeFileSync(path: string, data: string | Uint8Array, _options?: WriteFileOptions | Encoding): void {
const resolved = resolvePath(path);
Expand All @@ -354,7 +364,6 @@ export function writeFileSync(path: string, data: string | Uint8Array, _options?

/**
* Append data to a file synchronously, creating it if it doesn't exist.
* Backed by wasi:filesystem/types@0.2.0 — fully synchronous.
*/
export function appendFileSync(path: string, data: string | Uint8Array, _options?: WriteFileOptions | Encoding): void {
const resolved = resolvePath(path);
Expand All @@ -377,7 +386,6 @@ export function appendFileSync(path: string, data: string | Uint8Array, _options

/**
* Read directory contents synchronously.
* Backed by wasi:filesystem/types@0.2.0 — fully synchronous.
*/
export function readdirSync(path: string, _options?: any): string[] {
const resolved = resolvePath(path);
Expand Down Expand Up @@ -431,7 +439,6 @@ function makeStatResult(type: 'file' | 'directory' | 'notfound', size: bigint =

/**
* Get file stats synchronously.
* Backed by wasi:filesystem/types@0.2.0 — fully synchronous.
*/
export function statSync(path: string): StatResult {
const resolved = resolvePath(path);
Expand All @@ -456,7 +463,6 @@ export function lstatSync(path: string): StatResult {

/**
* Create a directory synchronously.
* Backed by wasi:filesystem/types@0.2.0 — fully synchronous.
*/
export function mkdirSync(path: string, options?: MkdirOptions): void {
if (options?.recursive) {
Expand All @@ -481,7 +487,6 @@ export function mkdirSync(path: string, options?: MkdirOptions): void {

/**
* Remove a directory synchronously.
* Backed by wasi:filesystem/types@0.2.0 — fully synchronous.
*/
export function rmdirSync(path: string, _options?: RmdirOptions): void {
const resolved = resolvePath(path);
Expand All @@ -495,7 +500,6 @@ export function rmdirSync(path: string, _options?: RmdirOptions): void {

/**
* Remove a file or directory synchronously.
* Backed by wasi:filesystem/types@0.2.0 — fully synchronous.
*/
export function rmSync(path: string, options?: RmOptions): void {
const resolved = resolvePath(path);
Expand Down Expand Up @@ -527,7 +531,6 @@ export function rmSync(path: string, options?: RmOptions): void {

/**
* Remove a file synchronously.
* Backed by wasi:filesystem/types@0.2.0 — fully synchronous.
*/
export function unlinkSync(path: string): void {
const resolved = resolvePath(path);
Expand All @@ -541,7 +544,6 @@ export function unlinkSync(path: string): void {

/**
* Copy a file synchronously.
* Backed by wasi:filesystem/types@0.2.0 — fully synchronous.
*/
export function copyFileSync(src: string, dest: string): void {
const data = readFileSync(src) as Uint8Array;
Expand All @@ -550,8 +552,6 @@ export function copyFileSync(src: string, dest: string): void {

/**
* Rename a file or directory synchronously.
* Falls back to copy+delete since wasi:filesystem/types@0.2.0
* does not expose a rename/link-at in the current WIT binding.
*/
export function renameSync(oldPath: string, newPath: string): void {
try {
Expand Down Expand Up @@ -582,7 +582,6 @@ export function accessSync(path: string, _mode?: number): void {

/**
* Check if a path exists synchronously.
* Backed by wasi:filesystem/types@0.2.0 — fully synchronous.
*/
export function existsSync(path: string): boolean {
const resolved = resolvePath(path);
Expand Down
Loading
Loading