From f7474f16222ec709ecc76e223d8e0d00a134da03 Mon Sep 17 00:00:00 2001 From: Mavdol Date: Mon, 13 Apr 2026 01:37:14 +0200 Subject: [PATCH 1/6] normalize virtual CWD paths --- .../capsule-sdk/javascript/src/polyfills/fs.ts | 8 +++++--- .../javascript/src/polyfills/process.ts | 18 ++++++++++-------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/crates/capsule-sdk/javascript/src/polyfills/fs.ts b/crates/capsule-sdk/javascript/src/polyfills/fs.ts index 436174d..01f1549 100644 --- a/crates/capsule-sdk/javascript/src/polyfills/fs.ts +++ b/crates/capsule-sdk/javascript/src/polyfills/fs.ts @@ -107,9 +107,11 @@ function resolvePath(path: string): { dir: Descriptor; relativePath: string } | continue; } - if (normalizedPath.startsWith(normalizedGuest + '/')) { - const relativePath = normalizedPath.slice(normalizedGuest.length + 1); - return { dir: descriptor, relativePath }; + const guestPrefix = normalizedGuest.endsWith('/') ? normalizedGuest : normalizedGuest + '/'; + + if (normalizedPath.startsWith(guestPrefix)) { + const relativePath = normalizedPath.slice(guestPrefix.length); + return { dir: descriptor, relativePath: relativePath || '.' }; } if (normalizedPath === normalizedGuest) { diff --git a/crates/capsule-sdk/javascript/src/polyfills/process.ts b/crates/capsule-sdk/javascript/src/polyfills/process.ts index 01282c9..4ab102f 100644 --- a/crates/capsule-sdk/javascript/src/polyfills/process.ts +++ b/crates/capsule-sdk/javascript/src/polyfills/process.ts @@ -70,20 +70,23 @@ function getArgv(): string[] { } /** - * Initialized from wasi:cli/environment initialCwd(), falls back to '.'. + * Initialized from wasi:cli/environment initialCwd(), falls back to '/'. */ let _virtualCwd: string = (() => { try { const env = (globalThis as any)['wasi:cli/environment']; if (env && typeof env.initialCwd === 'function') { - return env.initialCwd() ?? '.'; + const initial = env.initialCwd(); + if (initial) { + return initial.startsWith('/') ? initial : '/' + initial; + } } } catch {} - return '.'; + return '/'; })(); /** - * Internal setter for virtual CWD — handles relative and absolute paths. + * Internal setter for virtual CWD — handles relative and absolute paths safely via absolute resolution. */ function setCwd(directory: string): void { if (!directory) return; @@ -92,11 +95,10 @@ function setCwd(directory: string): void { } else if (directory === '..') { const parts = _virtualCwd.split('/').filter(Boolean); parts.pop(); - _virtualCwd = parts.join('/') || '.'; + _virtualCwd = '/' + parts.join('/'); } else { - _virtualCwd = (_virtualCwd === '.' || _virtualCwd === '') - ? directory.replace(/\/+$/, '') - : _virtualCwd.replace(/\/+$/, '') + '/' + directory.replace(/\/+$/, ''); + const base = _virtualCwd.endsWith('/') ? _virtualCwd : _virtualCwd + '/'; + _virtualCwd = (base + directory).replace(/\/+$/, ''); } } From 86befa9ca2530003b696e10de9d0b302cedc48f4 Mon Sep 17 00:00:00 2001 From: Mavdol Date: Mon, 13 Apr 2026 01:54:12 +0200 Subject: [PATCH 2/6] add --args-file support to CLI and SDKs to handle large argument lists via temporary files --- crates/capsule-cli/Cargo.lock | 4 +- crates/capsule-cli/Cargo.toml | 4 +- crates/capsule-cli/npm/package.json | 10 ++-- crates/capsule-cli/pyproject.toml | 2 +- crates/capsule-cli/src/cli.rs | 6 +++ crates/capsule-cli/src/commands/shared.rs | 8 +++ crates/capsule-cli/src/main.rs | 11 ++++ crates/capsule-core/Cargo.lock | 2 +- crates/capsule-core/Cargo.toml | 2 +- .../capsule-sdk/javascript/package-lock.json | 4 +- crates/capsule-sdk/javascript/package.json | 2 +- crates/capsule-sdk/javascript/src/run.ts | 52 +++++++++++++++---- crates/capsule-sdk/python/pyproject.toml | 2 +- crates/capsule-sdk/python/src/capsule/run.py | 30 ++++++++++- 14 files changed, 111 insertions(+), 28 deletions(-) diff --git a/crates/capsule-cli/Cargo.lock b/crates/capsule-cli/Cargo.lock index ea7ae0c..eb16a0c 100644 --- a/crates/capsule-cli/Cargo.lock +++ b/crates/capsule-cli/Cargo.lock @@ -322,7 +322,7 @@ dependencies = [ [[package]] name = "capsule-core" -version = "0.8.5" +version = "0.8.6" dependencies = [ "anyhow", "blake3", @@ -351,7 +351,7 @@ dependencies = [ [[package]] name = "capsule-run" -version = "0.8.5" +version = "0.8.6" dependencies = [ "capsule-core", "clap", diff --git a/crates/capsule-cli/Cargo.toml b/crates/capsule-cli/Cargo.toml index b16a571..9f15888 100644 --- a/crates/capsule-cli/Cargo.toml +++ b/crates/capsule-cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "capsule-run" -version = "0.8.5" +version = "0.8.6" edition = "2024" description = "Secure WASM runtime to isolate and manage AI agent tasks" license = "Apache-2.0" @@ -16,7 +16,7 @@ path = "src/main.rs" [dependencies] clap = { version = "4.5.53", features = ["derive"] } -capsule-core = { version= "0.8.5", path = "../capsule-core" } +capsule-core = { version= "0.8.6", path = "../capsule-core" } tokio = { version = "1.48.0", features = ["rt", "rt-multi-thread", "macros"] } serde_json = "1" dotenvy = "0.15.7" diff --git a/crates/capsule-cli/npm/package.json b/crates/capsule-cli/npm/package.json index 1cf2508..16a4d58 100644 --- a/crates/capsule-cli/npm/package.json +++ b/crates/capsule-cli/npm/package.json @@ -1,6 +1,6 @@ { "name": "@capsule-run/cli", - "version": "0.8.5", + "version": "0.8.6", "description": "Secure WASM runtime to isolate and manage AI agent tasks", "bin": { "capsule": "./bin/capsule.js" @@ -29,9 +29,9 @@ "node": ">=18" }, "optionalDependencies": { - "@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" + "@capsule-run/cli-darwin-arm64": "0.8.6", + "@capsule-run/cli-darwin-x64": "0.8.6", + "@capsule-run/cli-linux-x64": "0.8.6", + "@capsule-run/cli-win32-x64": "0.8.6" } } diff --git a/crates/capsule-cli/pyproject.toml b/crates/capsule-cli/pyproject.toml index 9a77702..602eb1c 100644 --- a/crates/capsule-cli/pyproject.toml +++ b/crates/capsule-cli/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "maturin" [project] name = "capsule-run" -version = "0.8.5" +version = "0.8.6" description = "Secure WASM runtime to isolate and manage AI agent tasks" readme = "docs/README-pypi.md" license = {text = "Apache-2.0"} diff --git a/crates/capsule-cli/src/cli.rs b/crates/capsule-cli/src/cli.rs index 7594504..1bb5bff 100644 --- a/crates/capsule-cli/src/cli.rs +++ b/crates/capsule-cli/src/cli.rs @@ -23,6 +23,9 @@ pub enum Commands { #[arg(long, value_name = "HOST[::GUEST][:ro|:rw]")] mount: Vec, + #[arg(long, value_name = "FILE", conflicts_with = "args")] + args_file: Option, + #[arg(trailing_var_arg = true, allow_hyphen_values = true)] args: Vec, }, @@ -44,6 +47,9 @@ pub enum Commands { #[arg(long, value_name = "HOST[::GUEST][:ro|:rw]")] mount: Vec, + #[arg(long, value_name = "FILE", conflicts_with = "args")] + args_file: Option, + #[arg(trailing_var_arg = true, allow_hyphen_values = true)] args: Vec, }, diff --git a/crates/capsule-cli/src/commands/shared.rs b/crates/capsule-cli/src/commands/shared.rs index 55e7ce0..507e5e6 100644 --- a/crates/capsule-cli/src/commands/shared.rs +++ b/crates/capsule-cli/src/commands/shared.rs @@ -1,5 +1,13 @@ use std::path::Path; +pub fn load_args_file(path: &str) -> Result, String> { + let content = std::fs::read_to_string(path) + .map_err(|e| format!("Failed to read --args-file '{}': {}", path, e))?; + let args: Vec = serde_json::from_str(&content) + .map_err(|e| format!("Failed to parse --args-file '{}': {}", path, e))?; + Ok(args) +} + pub fn load_env_variables(project_root: &Path) -> Result<(), String> { let env_files = [".env", ".env.local", ".env.development", ".env.production"]; diff --git a/crates/capsule-cli/src/main.rs b/crates/capsule-cli/src/main.rs index a46571d..6b44546 100644 --- a/crates/capsule-cli/src/main.rs +++ b/crates/capsule-cli/src/main.rs @@ -7,6 +7,7 @@ use std::path::Path; use cli::{Cli, Commands}; use commands::{BuildError, ExecError, RunError, build, exec, run}; +use commands::shared::load_args_file; #[derive(Debug)] pub enum CliError { @@ -53,8 +54,13 @@ async fn main() -> Result<(), CliError> { json, verbose, mount, + args_file, args, } => { + let args = match args_file { + Some(ref path) => load_args_file(path).map_err(|e| CliError::RunError(e))?, + None => args, + }; let file_path = file.as_deref().map(Path::new); let result = run::execute(file_path, args, mount, json, verbose).await?; @@ -75,8 +81,13 @@ async fn main() -> Result<(), CliError> { json, verbose, mount, + args_file, args, } => { + let args = match args_file { + Some(ref path) => load_args_file(path).map_err(|e| CliError::ExecError(e))?, + None => args, + }; let result = exec::execute(Path::new(&file), args, mount, json, verbose).await?; if json { diff --git a/crates/capsule-core/Cargo.lock b/crates/capsule-core/Cargo.lock index de0a23a..8a7493c 100644 --- a/crates/capsule-core/Cargo.lock +++ b/crates/capsule-core/Cargo.lock @@ -302,7 +302,7 @@ dependencies = [ [[package]] name = "capsule-core" -version = "0.8.5" +version = "0.8.6" dependencies = [ "anyhow", "blake3", diff --git a/crates/capsule-core/Cargo.toml b/crates/capsule-core/Cargo.toml index 0a43c72..40dda71 100644 --- a/crates/capsule-core/Cargo.toml +++ b/crates/capsule-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "capsule-core" -version = "0.8.5" +version = "0.8.6" edition = "2024" description = "Core library for Capsule - WASM runtime for AI agent isolation" license = "Apache-2.0" diff --git a/crates/capsule-sdk/javascript/package-lock.json b/crates/capsule-sdk/javascript/package-lock.json index 05b6cab..2b7d31a 100644 --- a/crates/capsule-sdk/javascript/package-lock.json +++ b/crates/capsule-sdk/javascript/package-lock.json @@ -1,12 +1,12 @@ { "name": "@capsule-run/sdk", - "version": "0.8.5", + "version": "0.8.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@capsule-run/sdk", - "version": "0.8.5", + "version": "0.8.6", "license": "Apache-2.0", "dependencies": { "@bytecodealliance/jco": "^1.0.0", diff --git a/crates/capsule-sdk/javascript/package.json b/crates/capsule-sdk/javascript/package.json index 70b80d9..cc30057 100644 --- a/crates/capsule-sdk/javascript/package.json +++ b/crates/capsule-sdk/javascript/package.json @@ -1,6 +1,6 @@ { "name": "@capsule-run/sdk", - "version": "0.8.5", + "version": "0.8.6", "description": "Capsule JavaScript SDK - run AI agent tasks in secure WASM sandboxes", "type": "module", "main": "./dist/index.js", diff --git a/crates/capsule-sdk/javascript/src/run.ts b/crates/capsule-sdk/javascript/src/run.ts index 4347308..9bf53c6 100644 --- a/crates/capsule-sdk/javascript/src/run.ts +++ b/crates/capsule-sdk/javascript/src/run.ts @@ -6,8 +6,10 @@ */ import { execFile } from 'child_process'; -import { resolve, extname } from 'path'; -import { existsSync } from 'fs'; +import { resolve, extname, join } from 'path'; +import { existsSync, writeFileSync, unlinkSync } from 'fs'; +import { tmpdir } from 'os'; +import { randomUUID } from 'crypto'; import { HostRequest } from './task'; export interface RunnerOptions { @@ -33,6 +35,7 @@ export interface RunnerResult { } const WASM_EXTENSIONS = new Set(['.wasm']); +const ARGS_FILE_THRESHOLD = 8 * 1024; /** * Get the appropriate capsule command for the current platform @@ -44,6 +47,16 @@ function getCapsuleCommand(capsulePath: string): string { return capsulePath; } +/** + * Write args to a temp file and return its path. + * Caller is responsible for deleting it. + */ +function writeArgsFile(args: string[]): string { + const path = join(tmpdir(), `capsule-args-${randomUUID()}.json`); + writeFileSync(path, JSON.stringify(args), 'utf-8'); + return path; +} + /** * Run a Capsule task from a third-party application * @@ -66,20 +79,37 @@ export function run(options: RunnerOptions): Promise { } const subcommand = isWasm ? 'exec' : 'run'; + const mountFlags = mounts.flatMap(m => ['--mount', m]); - return new Promise((resolve, reject) => { - const mountFlags = mounts.flatMap(m => ['--mount', m]); - const cmdArgs = [subcommand, resolvedFile, '--json', ...mountFlags, ...args]; + const serializedArgs = JSON.stringify(args); + const useArgsFile = Buffer.byteLength(serializedArgs, 'utf-8') > ARGS_FILE_THRESHOLD; + + let argsFilePath: string | null = null; + let argsFlags: string[]; - let executable = command; - let executionArgs = cmdArgs; + if (useArgsFile) { + argsFilePath = writeArgsFile(args); + argsFlags = ['--args-file', argsFilePath]; + } else { + argsFlags = args; + } - if (process.platform === 'win32') { - executable = process.env.comspec || 'cmd.exe'; - executionArgs = ['/d', '/s', '/c', command, ...cmdArgs]; - } + const cmdArgs = [subcommand, resolvedFile, '--json', ...mountFlags, ...argsFlags]; + let executable = command; + let executionArgs = cmdArgs; + + if (process.platform === 'win32') { + executable = process.env.comspec || 'cmd.exe'; + executionArgs = ['/d', '/s', '/c', command, ...cmdArgs]; + } + + return new Promise((resolve, reject) => { execFile(executable, executionArgs, { cwd, encoding: 'utf-8' }, (error, stdout, stderr) => { + if (argsFilePath) { + try { unlinkSync(argsFilePath); } catch { } + } + if (error && !stdout) { if ((error as NodeJS.ErrnoException).code === 'ENOENT') { reject(new Error( diff --git a/crates/capsule-sdk/python/pyproject.toml b/crates/capsule-sdk/python/pyproject.toml index e4723c9..3187fae 100644 --- a/crates/capsule-sdk/python/pyproject.toml +++ b/crates/capsule-sdk/python/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "capsule" -version = "0.8.5" +version = "0.8.6" description = "Capsule Python SDK - run AI agent tasks in secure WASM sandboxes" readme = "README.md" requires-python = ">=3.10" diff --git a/crates/capsule-sdk/python/src/capsule/run.py b/crates/capsule-sdk/python/src/capsule/run.py index 58ded6c..ceaac53 100644 --- a/crates/capsule-sdk/python/src/capsule/run.py +++ b/crates/capsule-sdk/python/src/capsule/run.py @@ -8,10 +8,13 @@ import asyncio import json import os +import tempfile +import uuid from typing import Any, Optional, TypedDict _WASM_EXTENSIONS = {".wasm"} _SOURCE_EXTENSIONS = {".py"} +_ARGS_FILE_THRESHOLD = 8 * 1024 class RunnerOptions(TypedDict, total=False): @@ -92,7 +95,21 @@ async def run( subcommand = "exec" if ext in _WASM_EXTENSIONS else "run" mount_flags = [flag for m in mounts for flag in ("--mount", m)] - cmd = [capsule_path, subcommand, resolved_file, "--json", *mount_flags, *args] + + serialized_args = json.dumps(args).encode("utf-8") + args_file_path: Optional[str] = None + + if len(serialized_args) > _ARGS_FILE_THRESHOLD: + args_file_path = os.path.join( + tempfile.gettempdir(), f"capsule-args-{uuid.uuid4().hex}.json" + ) + with open(args_file_path, "w", encoding="utf-8") as f: + f.write(serialized_args.decode("utf-8")) + args_flags = ["--args-file", args_file_path] + else: + args_flags = args + + cmd = [capsule_path, subcommand, resolved_file, "--json", *mount_flags, *args_flags] try: process = await asyncio.create_subprocess_exec( @@ -104,6 +121,12 @@ async def run( stdout, stderr = await process.communicate() + if args_file_path: + try: + os.unlink(args_file_path) + except OSError: + pass + if process.returncode != 0 and not stdout: error_msg = stderr.decode("utf-8") if stderr else "Unknown error" raise RuntimeError(error_msg) @@ -117,6 +140,11 @@ async def run( ) except FileNotFoundError: + if args_file_path: + try: + os.unlink(args_file_path) + except OSError: + pass raise FileNotFoundError( "Capsule CLI not found. Use 'pip install capsule-run' to install it." ) From 913869992f6a72952f96e20b08a5128192c3b001 Mon Sep 17 00:00:00 2001 From: Mavdol Date: Mon, 13 Apr 2026 01:58:50 +0200 Subject: [PATCH 3/6] simplify error mapping using method references in CLI command execution --- crates/capsule-cli/src/main.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/capsule-cli/src/main.rs b/crates/capsule-cli/src/main.rs index 6b44546..5c25078 100644 --- a/crates/capsule-cli/src/main.rs +++ b/crates/capsule-cli/src/main.rs @@ -6,8 +6,8 @@ use std::fmt; use std::path::Path; use cli::{Cli, Commands}; -use commands::{BuildError, ExecError, RunError, build, exec, run}; use commands::shared::load_args_file; +use commands::{BuildError, ExecError, RunError, build, exec, run}; #[derive(Debug)] pub enum CliError { @@ -58,7 +58,7 @@ async fn main() -> Result<(), CliError> { args, } => { let args = match args_file { - Some(ref path) => load_args_file(path).map_err(|e| CliError::RunError(e))?, + Some(ref path) => load_args_file(path).map_err(CliError::RunError)?, None => args, }; let file_path = file.as_deref().map(Path::new); @@ -85,7 +85,7 @@ async fn main() -> Result<(), CliError> { args, } => { let args = match args_file { - Some(ref path) => load_args_file(path).map_err(|e| CliError::ExecError(e))?, + Some(ref path) => load_args_file(path).map_err(CliError::ExecError)?, None => args, }; let result = exec::execute(Path::new(&file), args, mount, json, verbose).await?; From d956db2648fa1c13731e5221803c02e25b8cf4de Mon Sep 17 00:00:00 2001 From: Mavdol Date: Tue, 14 Apr 2026 00:17:59 +0200 Subject: [PATCH 4/6] prioritize current working directory over file path for project root resolution --- crates/capsule-cli/src/commands/run.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/crates/capsule-cli/src/commands/run.rs b/crates/capsule-cli/src/commands/run.rs index 16ef600..1f5edab 100644 --- a/crates/capsule-cli/src/commands/run.rs +++ b/crates/capsule-cli/src/commands/run.rs @@ -83,11 +83,13 @@ pub async fn execute( reporter.start_progress("Initializing runtime"); - let project_root = file_path - .canonicalize() - .ok() - .and_then(|p| p.parent().map(|p| p.to_path_buf())) - .unwrap_or_else(|| std::env::current_dir().unwrap_or_default()); + let project_root = std::env::current_dir().unwrap_or_else(|_| { + file_path + .canonicalize() + .ok() + .and_then(|p| p.parent().map(|p| p.to_path_buf())) + .unwrap_or_default() + }); load_env_variables(&project_root).map_err(RunError::IoError)?; From 019aba626951d6a6b69ed61c1d00dcbe4a54e82a Mon Sep 17 00:00:00 2001 From: Mavdol Date: Tue, 14 Apr 2026 00:29:21 +0200 Subject: [PATCH 5/6] import standard library networking modules into the capsule package initialization --- crates/capsule-sdk/python/src/capsule/__init__.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/crates/capsule-sdk/python/src/capsule/__init__.py b/crates/capsule-sdk/python/src/capsule/__init__.py index 4cf89ad..c26a577 100644 --- a/crates/capsule-sdk/python/src/capsule/__init__.py +++ b/crates/capsule-sdk/python/src/capsule/__init__.py @@ -4,4 +4,13 @@ from .app import TaskRunner from .run import run, RunnerOptions, RunnerResult, ExecutionInfo, ErrorInfo +import socket +import ssl +import urllib.request +import urllib.parse +import urllib.error +import urllib.response +import http.client +import http.cookiejar + exports = app.exports From e19342d3ecd75fd74717f35dfab0089496f5fd31 Mon Sep 17 00:00:00 2001 From: Mavdol Date: Tue, 14 Apr 2026 01:44:01 +0200 Subject: [PATCH 6/6] bump version to 0.8.7 and normalize guest paths in path validator --- crates/capsule-cli/Cargo.lock | 4 +-- crates/capsule-cli/Cargo.toml | 4 +-- crates/capsule-cli/npm/package.json | 10 ++++---- crates/capsule-cli/pyproject.toml | 2 +- crates/capsule-core/Cargo.lock | 2 +- crates/capsule-core/Cargo.toml | 2 +- .../src/wasm/utilities/path_validator.rs | 25 +++++++++++++++++-- .../capsule-sdk/javascript/package-lock.json | 4 +-- crates/capsule-sdk/javascript/package.json | 2 +- crates/capsule-sdk/python/pyproject.toml | 2 +- .../python/src/capsule/__init__.py | 4 +-- 11 files changed, 41 insertions(+), 20 deletions(-) diff --git a/crates/capsule-cli/Cargo.lock b/crates/capsule-cli/Cargo.lock index eb16a0c..037fce3 100644 --- a/crates/capsule-cli/Cargo.lock +++ b/crates/capsule-cli/Cargo.lock @@ -322,7 +322,7 @@ dependencies = [ [[package]] name = "capsule-core" -version = "0.8.6" +version = "0.8.7" dependencies = [ "anyhow", "blake3", @@ -351,7 +351,7 @@ dependencies = [ [[package]] name = "capsule-run" -version = "0.8.6" +version = "0.8.7" dependencies = [ "capsule-core", "clap", diff --git a/crates/capsule-cli/Cargo.toml b/crates/capsule-cli/Cargo.toml index 9f15888..94e3355 100644 --- a/crates/capsule-cli/Cargo.toml +++ b/crates/capsule-cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "capsule-run" -version = "0.8.6" +version = "0.8.7" edition = "2024" description = "Secure WASM runtime to isolate and manage AI agent tasks" license = "Apache-2.0" @@ -16,7 +16,7 @@ path = "src/main.rs" [dependencies] clap = { version = "4.5.53", features = ["derive"] } -capsule-core = { version= "0.8.6", path = "../capsule-core" } +capsule-core = { version= "0.8.7", path = "../capsule-core" } tokio = { version = "1.48.0", features = ["rt", "rt-multi-thread", "macros"] } serde_json = "1" dotenvy = "0.15.7" diff --git a/crates/capsule-cli/npm/package.json b/crates/capsule-cli/npm/package.json index 16a4d58..76b7bcc 100644 --- a/crates/capsule-cli/npm/package.json +++ b/crates/capsule-cli/npm/package.json @@ -1,6 +1,6 @@ { "name": "@capsule-run/cli", - "version": "0.8.6", + "version": "0.8.7", "description": "Secure WASM runtime to isolate and manage AI agent tasks", "bin": { "capsule": "./bin/capsule.js" @@ -29,9 +29,9 @@ "node": ">=18" }, "optionalDependencies": { - "@capsule-run/cli-darwin-arm64": "0.8.6", - "@capsule-run/cli-darwin-x64": "0.8.6", - "@capsule-run/cli-linux-x64": "0.8.6", - "@capsule-run/cli-win32-x64": "0.8.6" + "@capsule-run/cli-darwin-arm64": "0.8.7", + "@capsule-run/cli-darwin-x64": "0.8.7", + "@capsule-run/cli-linux-x64": "0.8.7", + "@capsule-run/cli-win32-x64": "0.8.7" } } diff --git a/crates/capsule-cli/pyproject.toml b/crates/capsule-cli/pyproject.toml index 602eb1c..db226f1 100644 --- a/crates/capsule-cli/pyproject.toml +++ b/crates/capsule-cli/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "maturin" [project] name = "capsule-run" -version = "0.8.6" +version = "0.8.7" description = "Secure WASM runtime to isolate and manage AI agent tasks" readme = "docs/README-pypi.md" license = {text = "Apache-2.0"} diff --git a/crates/capsule-core/Cargo.lock b/crates/capsule-core/Cargo.lock index 8a7493c..8860f38 100644 --- a/crates/capsule-core/Cargo.lock +++ b/crates/capsule-core/Cargo.lock @@ -302,7 +302,7 @@ dependencies = [ [[package]] name = "capsule-core" -version = "0.8.6" +version = "0.8.7" dependencies = [ "anyhow", "blake3", diff --git a/crates/capsule-core/Cargo.toml b/crates/capsule-core/Cargo.toml index 40dda71..721849e 100644 --- a/crates/capsule-core/Cargo.toml +++ b/crates/capsule-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "capsule-core" -version = "0.8.6" +version = "0.8.7" edition = "2024" description = "Core library for Capsule - WASM runtime for AI agent isolation" license = "Apache-2.0" diff --git a/crates/capsule-core/src/wasm/utilities/path_validator.rs b/crates/capsule-core/src/wasm/utilities/path_validator.rs index 1f4901a..07f89fc 100644 --- a/crates/capsule-core/src/wasm/utilities/path_validator.rs +++ b/crates/capsule-core/src/wasm/utilities/path_validator.rs @@ -70,6 +70,16 @@ fn parse_path_spec(path_spec: &str) -> (String, Option, FileAccessMode) } } +fn to_absolute_guest_path(path: &str) -> String { + let stripped = path.trim_start_matches("./"); + + if stripped.starts_with('/') { + stripped.to_string() + } else { + format!("/{}", stripped) + } +} + pub fn validate_path( path_spec: &str, project_root: &Path, @@ -94,7 +104,8 @@ pub fn validate_path( return Err(PathValidationError::EscapesProjectDirectory(host_str)); } - let guest_path = guest_alias.unwrap_or_else(|| host_str.clone()); + let raw_guest = guest_alias.unwrap_or_else(|| host_str.clone()); + let guest_path = to_absolute_guest_path(&raw_guest); Ok(ParsedPath { path: resolved, @@ -132,7 +143,7 @@ mod tests { assert!(result.is_ok()); let parsed = result.unwrap(); - assert_eq!(parsed.guest_path, "./.capsule_test"); + assert_eq!(parsed.guest_path, "/.capsule_test"); } #[test] @@ -198,4 +209,14 @@ mod tests { assert_eq!(guest, Some("workspace".to_string())); assert_eq!(mode, FileAccessMode::ReadOnly); } + + #[test] + fn test_guest_path_normalization() { + assert_eq!(to_absolute_guest_path("./data"), "/data"); + assert_eq!(to_absolute_guest_path("data"), "/data"); + assert_eq!(to_absolute_guest_path("/data"), "/data"); + assert_eq!(to_absolute_guest_path("/"), "/"); + assert_eq!(to_absolute_guest_path("workspace"), "/workspace"); + assert_eq!(to_absolute_guest_path("./nested/dir"), "/nested/dir"); + } } diff --git a/crates/capsule-sdk/javascript/package-lock.json b/crates/capsule-sdk/javascript/package-lock.json index 2b7d31a..cac2fcc 100644 --- a/crates/capsule-sdk/javascript/package-lock.json +++ b/crates/capsule-sdk/javascript/package-lock.json @@ -1,12 +1,12 @@ { "name": "@capsule-run/sdk", - "version": "0.8.6", + "version": "0.8.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@capsule-run/sdk", - "version": "0.8.6", + "version": "0.8.7", "license": "Apache-2.0", "dependencies": { "@bytecodealliance/jco": "^1.0.0", diff --git a/crates/capsule-sdk/javascript/package.json b/crates/capsule-sdk/javascript/package.json index cc30057..9f19a61 100644 --- a/crates/capsule-sdk/javascript/package.json +++ b/crates/capsule-sdk/javascript/package.json @@ -1,6 +1,6 @@ { "name": "@capsule-run/sdk", - "version": "0.8.6", + "version": "0.8.7", "description": "Capsule JavaScript SDK - run AI agent tasks in secure WASM sandboxes", "type": "module", "main": "./dist/index.js", diff --git a/crates/capsule-sdk/python/pyproject.toml b/crates/capsule-sdk/python/pyproject.toml index 3187fae..7238d2b 100644 --- a/crates/capsule-sdk/python/pyproject.toml +++ b/crates/capsule-sdk/python/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "capsule" -version = "0.8.6" +version = "0.8.7" description = "Capsule Python SDK - run AI agent tasks in secure WASM sandboxes" readme = "README.md" requires-python = ">=3.10" diff --git a/crates/capsule-sdk/python/src/capsule/__init__.py b/crates/capsule-sdk/python/src/capsule/__init__.py index c26a577..d6867d5 100644 --- a/crates/capsule-sdk/python/src/capsule/__init__.py +++ b/crates/capsule-sdk/python/src/capsule/__init__.py @@ -4,8 +4,6 @@ from .app import TaskRunner from .run import run, RunnerOptions, RunnerResult, ExecutionInfo, ErrorInfo -import socket -import ssl import urllib.request import urllib.parse import urllib.error @@ -14,3 +12,5 @@ import http.cookiejar exports = app.exports + +