From 12332b9dc2cc2823ef5cc218d3410896be95f170 Mon Sep 17 00:00:00 2001 From: Mavdol Date: Thu, 22 Jan 2026 23:52:40 +0100 Subject: [PATCH 1/8] Add javascript AST througt swc_ecma_ast --- .../utilities/introspection/javascript.rs | 182 ++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 crates/capsule-core/src/wasm/utilities/introspection/javascript.rs diff --git a/crates/capsule-core/src/wasm/utilities/introspection/javascript.rs b/crates/capsule-core/src/wasm/utilities/introspection/javascript.rs new file mode 100644 index 0000000..298db88 --- /dev/null +++ b/crates/capsule-core/src/wasm/utilities/introspection/javascript.rs @@ -0,0 +1,182 @@ +use std::collections::HashMap; +use swc_common::input::StringInput; +use swc_common::sync::Lrc; +use swc_common::{FileName, SourceMap}; +use swc_ecma_ast::{ + Callee, Decl, Expr, Lit, ModuleDecl, ModuleItem, ObjectLit, Prop, PropName, PropOrSpread, Stmt, Pat +}; +use swc_ecma_parser::{EsSyntax, Parser, Syntax, TsSyntax}; + +pub fn extract_js_task_configs( + source: &str, + is_typescript: bool, +) -> Option> { + let cm: Lrc = Default::default(); + let fm = cm.new_source_file( + Lrc::new(FileName::Custom("source".into())), + String::from(source), + ); + + let syntax = if is_typescript { + Syntax::Typescript(TsSyntax::default()) + } else { + Syntax::Es(EsSyntax::default()) + }; + + let mut parser = Parser::new(syntax, StringInput::from(&*fm), None); + let module = parser.parse_module().ok()?; + + let mut tasks: HashMap = HashMap::new(); + + for item in &module.body { + if let ModuleItem::Stmt(stmt) = item + && let Stmt::Decl(decl) = stmt + && let Decl::Var(var_decl) = decl { + for decl in var_decl.decls.iter() { + if let Some(init) = &decl.init + && let Some((var_name, config)) = extract_task_call(init, &decl.name) { + tasks.insert(var_name, serde_json::to_value(&config).unwrap()); + } + } + } + + if let ModuleItem::ModuleDecl(mod_decl) = item + && let ModuleDecl::ExportDecl(export) = mod_decl + && let Decl::Var(var_decl) = &export.decl { + for decl in var_decl.decls.iter() { + if let Some(init) = &decl.init + && let Some((var_name, config)) = extract_task_call(init, &decl.name) { + tasks.insert(var_name, serde_json::to_value(&config).unwrap()); + } + } + } + } + + if tasks.is_empty() { None } else { Some(tasks) } +} + +fn extract_task_call( + expr: &Expr, + binding_name: &Pat, +) -> Option<(String, HashMap)> { + let var_name = match binding_name { + Pat::Ident(ident) => ident.sym.as_str().to_string(), + _ => return None, + }; + + if let Expr::Call(call) = expr + && let Callee::Expr(callee) = &call.callee + && let Expr::Ident(ident) = callee.as_ref() + && ident.sym.as_ref() == "task" + && let Some(first_arg) = call.args.first() + && let Expr::Object(obj) = first_arg.expr.as_ref() { + let mut config = extract_object_literal(obj); + let task_name = config + .get("name") + .and_then(|v| v.as_str()) + .unwrap_or(&var_name) + .to_string(); + config.insert( + "name".to_string(), + serde_json::Value::String(task_name.clone()), + ); + return Some((task_name, config)); + } + + None +} + +fn extract_object_literal(obj: &ObjectLit) -> HashMap { + let mut config: HashMap = HashMap::new(); + + for prop in &obj.props { + if let PropOrSpread::Prop(prop) = prop + && let Prop::KeyValue(kv) = prop.as_ref() { + let key = match &kv.key { + PropName::Ident(ident) => ident.sym.as_str().to_string(), + PropName::Str(s) => s.value.as_str().unwrap_or_default().to_string(), + _ => continue, + }; + + if let Some(value) = extract_js_literal(&kv.value) { + config.insert(key, value); + } + } + } + + config +} + +fn extract_js_literal(expr: &Expr) -> Option { + match expr { + Expr::Lit(lit) => match lit { + Lit::Str(s) => Some(serde_json::Value::String( + s.value.as_str().unwrap_or_default().to_string(), + )), + Lit::Num(n) => serde_json::Number::from_f64(n.value).map(serde_json::Value::Number), + Lit::Bool(b) => Some(serde_json::Value::Bool(b.value)), + Lit::Null(_) => Some(serde_json::Value::Null), + _ => None, + }, + Expr::Array(arr) => { + let items: Option> = arr + .elems + .iter() + .filter_map(|e| e.as_ref()) + .map(|e| extract_js_literal(&e.expr)) + .collect(); + items.map(serde_json::Value::Array) + } + _ => None, + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_js_task_with_config() { + let source = r#" +const main = task({ name: "main", compute: "HIGH" }, () => { + return "hello"; +}); +"#; + let configs = extract_js_task_configs(source, false).unwrap(); + assert!(configs.contains_key("main")); + assert_eq!(configs["main"]["compute"], "HIGH"); + } + + #[test] + fn test_js_export_task() { + let source = r#" +export const main = task({ compute: "LOW" }, () => {}); +"#; + let configs = extract_js_task_configs(source, false).unwrap(); + assert!(configs.contains_key("main")); + assert_eq!(configs["main"]["compute"], "LOW"); + } + + #[test] + fn test_ts_task() { + let source = r#" +export const main = task({ name: "main", timeout: "30s" }, (): string => { + return "hello"; +}); +"#; + let configs = extract_js_task_configs(source, true).unwrap(); + assert!(configs.contains_key("main")); + assert_eq!(configs["main"]["timeout"], "30s"); + } + + #[test] + fn test_no_task() { + let source = r#" +function main() { + return "hello"; +} +"#; + let configs = extract_js_task_configs(source, false); + assert!(configs.is_none()); + } +} From c7f2c367f30d166c7bd092241d76fd05d76815f7 Mon Sep 17 00:00:00 2001 From: Mavdol Date: Thu, 22 Jan 2026 23:53:04 +0100 Subject: [PATCH 2/8] remove test deplication --- .../src/wasm/utilities/introspection/javascript.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/crates/capsule-core/src/wasm/utilities/introspection/javascript.rs b/crates/capsule-core/src/wasm/utilities/introspection/javascript.rs index 298db88..582b45f 100644 --- a/crates/capsule-core/src/wasm/utilities/introspection/javascript.rs +++ b/crates/capsule-core/src/wasm/utilities/introspection/javascript.rs @@ -147,16 +147,6 @@ const main = task({ name: "main", compute: "HIGH" }, () => { assert_eq!(configs["main"]["compute"], "HIGH"); } - #[test] - fn test_js_export_task() { - let source = r#" -export const main = task({ compute: "LOW" }, () => {}); -"#; - let configs = extract_js_task_configs(source, false).unwrap(); - assert!(configs.contains_key("main")); - assert_eq!(configs["main"]["compute"], "LOW"); - } - #[test] fn test_ts_task() { let source = r#" From 91756630f21ad761bf6312b52fb90c32e60d88ff Mon Sep 17 00:00:00 2001 From: Mavdol Date: Thu, 22 Jan 2026 23:54:17 +0100 Subject: [PATCH 3/8] add python AST system viarustpython_parser --- .../src/wasm/utilities/introspection/mod.rs | 2 + .../wasm/utilities/introspection/python.rs | 157 ++++++++++++++++++ crates/capsule-core/src/wasm/utilities/mod.rs | 1 + 3 files changed, 160 insertions(+) create mode 100644 crates/capsule-core/src/wasm/utilities/introspection/mod.rs create mode 100644 crates/capsule-core/src/wasm/utilities/introspection/python.rs diff --git a/crates/capsule-core/src/wasm/utilities/introspection/mod.rs b/crates/capsule-core/src/wasm/utilities/introspection/mod.rs new file mode 100644 index 0000000..b49f333 --- /dev/null +++ b/crates/capsule-core/src/wasm/utilities/introspection/mod.rs @@ -0,0 +1,2 @@ +pub mod javascript; +pub mod python; diff --git a/crates/capsule-core/src/wasm/utilities/introspection/python.rs b/crates/capsule-core/src/wasm/utilities/introspection/python.rs new file mode 100644 index 0000000..afa0457 --- /dev/null +++ b/crates/capsule-core/src/wasm/utilities/introspection/python.rs @@ -0,0 +1,157 @@ +use rustpython_parser::{Mode, parse, ast}; +use std::collections::HashMap; + +pub fn extract_python_task_configs(source: &str) -> Option> { + let ast = parse(source, Mode::Module, "").ok()?; + let mut tasks = HashMap::new(); + + let body = match ast { + ast::Mod::Module(m) => m.body, + _ => return None, + }; + + for stmt in body { + if let ast::Stmt::FunctionDef(func) = stmt { + for decorator in &func.decorator_list { + if let Some(config) = extract_task_decorator(decorator, func.name.as_ref()) { + let task_name = config + .get("name") + .and_then(|v| v.as_str()) + .unwrap_or(func.name.as_ref()) + .to_string(); + tasks.insert(task_name, serde_json::to_value(&config).ok()?); + } + } + } + } + + if tasks.is_empty() { None } else { Some(tasks) } +} + +fn extract_task_decorator( + decorator: &ast::Expr, + func_name: &str, +) -> Option> { + match decorator { + + ast::Expr::Name(name) if name.id.as_str() == "task" => { + let mut config: HashMap = HashMap::new(); + config.insert( + "name".to_string(), + serde_json::Value::String(func_name.to_string()), + ); + Some(config) + } + + ast::Expr::Call(call) => { + if let ast::Expr::Name(name) = call.func.as_ref() + && name.id.as_str() == "task" { + return extract_call_args(call, func_name); + } + None + } + _ => None, + } +} + +fn extract_call_args( + call: &ast::ExprCall, + func_name: &str, +) -> Option> { + let mut config: HashMap = HashMap::new(); + + for keyword in &call.keywords { + if let Some(arg_name) = &keyword.arg + && let Some(value) = extract_literal(&keyword.value) { + config.insert(arg_name.to_string(), value); + } + } + + if !config.contains_key("name") { + config.insert( + "name".to_string(), + serde_json::Value::String(func_name.to_string()), + ); + } + + Some(config) +} + +fn extract_literal(expr: &ast::Expr) -> Option { + match expr { + ast::Expr::Constant(c) => match &c.value { + ast::Constant::Str(s) => { + Some(serde_json::Value::String(s.to_string())) + } + ast::Constant::Int(i) => { + let val: i64 = i.to_string().parse().ok()?; + Some(serde_json::Value::Number(val.into())) + } + ast::Constant::Float(f) => { + serde_json::Number::from_f64(*f).map(serde_json::Value::Number) + } + ast::Constant::Bool(b) => Some(serde_json::Value::Bool(*b)), + ast::Constant::None => Some(serde_json::Value::Null), + _ => None, + }, + ast::Expr::List(list) => { + let items: Option> = + list.elts.iter().map(extract_literal).collect(); + items.map(serde_json::Value::Array) + } + _ => None, + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_task_with_name() { + let source = r#" +@task(name="main", compute="HIGH") +def my_func(): + pass +"#; + let configs = extract_python_task_configs(source).unwrap(); + assert!(configs.contains_key("main")); + let config = &configs["main"]; + assert_eq!(config["name"], "main"); + assert_eq!(config["compute"], "HIGH"); + } + + #[test] + fn test_bare_task_decorator() { + let source = r#" +@task +def main(): + pass +"#; + let configs = extract_python_task_configs(source).unwrap(); + assert!(configs.contains_key("main")); + } + + #[test] + fn test_task_with_allowed_files() { + let source = r#" +@task(name="main", allowed_files=["./data", "./config"]) +def main(): + pass +"#; + let configs = extract_python_task_configs(source).unwrap(); + let allowed = configs["main"]["allowed_files"].as_array().unwrap(); + assert_eq!(allowed.len(), 2); + assert_eq!(allowed[0], "./data"); + } + + #[test] + fn test_no_task_decorator() { + let source = r#" +def main(): + pass +"#; + let configs = extract_python_task_configs(source); + assert!(configs.is_none()); + } +} diff --git a/crates/capsule-core/src/wasm/utilities/mod.rs b/crates/capsule-core/src/wasm/utilities/mod.rs index 9020e10..ba8174b 100644 --- a/crates/capsule-core/src/wasm/utilities/mod.rs +++ b/crates/capsule-core/src/wasm/utilities/mod.rs @@ -1,3 +1,4 @@ +pub mod introspection; pub mod path_validator; pub mod task_config; pub mod task_reporter; From fb0295081fe4e57041a8c835a9a1292e6dac82c4 Mon Sep 17 00:00:00 2001 From: Mavdol Date: Thu, 22 Jan 2026 23:58:16 +0100 Subject: [PATCH 4/8] task registry to container --- crates/capsule-core/src/wasm/compiler/javascript.rs | 11 +++++++++++ crates/capsule-core/src/wasm/compiler/python.rs | 10 ++++++++++ 2 files changed, 21 insertions(+) diff --git a/crates/capsule-core/src/wasm/compiler/javascript.rs b/crates/capsule-core/src/wasm/compiler/javascript.rs index abcfb41..6a8f809 100644 --- a/crates/capsule-core/src/wasm/compiler/javascript.rs +++ b/crates/capsule-core/src/wasm/compiler/javascript.rs @@ -1,8 +1,10 @@ use std::fs; use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; +use std::collections::HashMap; use crate::wasm::utilities::wit_manager::WitManager; +use crate::wasm::utilities::introspection::javascript; #[derive(Debug)] pub enum JavascriptWasmCompilerError { @@ -353,4 +355,13 @@ export const taskRunner = exports; Ok(output_path) } + + pub fn introspect_task_registry( + &self, + ) -> Option> { + let source = fs::read_to_string(&self.source_path).ok()?; + let is_typescript = self.source_path.extension().is_some_and(|ext| ext == "ts"); + + javascript::extract_js_task_configs(&source, is_typescript) + } } diff --git a/crates/capsule-core/src/wasm/compiler/python.rs b/crates/capsule-core/src/wasm/compiler/python.rs index bd1b156..2786c98 100644 --- a/crates/capsule-core/src/wasm/compiler/python.rs +++ b/crates/capsule-core/src/wasm/compiler/python.rs @@ -3,8 +3,10 @@ use std::fs; use std::path::{Path, PathBuf}; use std::process::Command; use std::process::Stdio; +use std::collections::HashMap; use crate::wasm::utilities::wit_manager::WitManager; +use crate::wasm::utilities::introspection::python; pub enum PythonWasmCompilerError { CompileFailed(String), @@ -343,4 +345,12 @@ from capsule.app import TaskRunner, exports } } } + + pub fn introspect_task_registry( + &self, + ) -> Option> { + let source = fs::read_to_string(&self.source_path).ok()?; + + python::extract_python_task_configs(&source) + } } From b7d95b7537fc3937fa8070a218cc0b758df9f9ce Mon Sep 17 00:00:00 2001 From: Mavdol Date: Fri, 23 Jan 2026 00:13:06 +0100 Subject: [PATCH 5/8] refine runner to allow config in main --- crates/capsule-cli/Cargo.lock | 603 ++++++++++++++++++++++++- crates/capsule-cli/src/commands/run.rs | 37 +- crates/capsule-core/Cargo.lock | 594 +++++++++++++++++++++++- crates/capsule-core/Cargo.toml | 5 + 4 files changed, 1224 insertions(+), 15 deletions(-) diff --git a/crates/capsule-cli/Cargo.lock b/crates/capsule-cli/Cargo.lock index 6a6e98d..81b1c33 100644 --- a/crates/capsule-cli/Cargo.lock +++ b/crates/capsule-cli/Cargo.lock @@ -115,6 +115,17 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" +[[package]] +name = "ast_node" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb025ef00a6da925cf40870b9c8d008526b6004ece399cb0974209720f0b194" +dependencies = [ + "quote", + "swc_macros_common", + "syn", +] + [[package]] name = "async-trait" version = "0.1.89" @@ -150,6 +161,15 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "better_scoped_tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd228125315b132eed175bf47619ac79b945b26e56b848ba203ae4ea8603609" +dependencies = [ + "scoped-tls", +] + [[package]] name = "bitflags" version = "2.10.0" @@ -186,6 +206,16 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" +[[package]] +name = "bytes-str" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c60b5ce37e0b883c37eb89f79a1e26fbe9c1081945d024eee93e8d91a7e18b3" +dependencies = [ + "bytes", + "serde", +] + [[package]] name = "cap-fs-ext" version = "3.4.5" @@ -276,8 +306,12 @@ dependencies = [ "nanoid", "reqwest", "rusqlite", + "rustpython-parser", "serde", "serde_json", + "swc_common", + "swc_ecma_ast", + "swc_ecma_parser", "tokio", "toml 0.9.11+spec-1.1.0", "wasmtime", @@ -471,7 +505,7 @@ dependencies = [ "hashbrown 0.14.5", "log", "regalloc2", - "rustc-hash", + "rustc-hash 2.1.1", "serde", "smallvec", "target-lexicon", @@ -575,6 +609,12 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + [[package]] name = "crypto-common" version = "0.1.7" @@ -594,6 +634,27 @@ dependencies = [ "uuid", ] +[[package]] +name = "derive_more" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + [[package]] name = "digest" version = "0.10.7" @@ -755,6 +816,16 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "from_variant" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ff35a391aef949120a0340d690269b3d9f63460a6106e99bd07b961f345ea9" +dependencies = [ + "swc_macros_common", + "syn", +] + [[package]] name = "fs-set-times" version = "0.20.3" @@ -859,6 +930,15 @@ dependencies = [ "version_check", ] +[[package]] +name = "getopts" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe4fbac503b8d1f88e6676011885f34b7174f46e59956bba534ba83abded4df" +dependencies = [ + "unicode-width", +] + [[package]] name = "getrandom" version = "0.2.16" @@ -923,6 +1003,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash", + "allocator-api2", ] [[package]] @@ -956,6 +1037,20 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hstr" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faa57007c3c9dab34df2fa4c1fb52fe9c34ec5a27ed9d8edea53254b50cd7887" +dependencies = [ + "hashbrown 0.14.5", + "new_debug_unreachable", + "once_cell", + "rustc-hash 2.1.1", + "serde", + "triomphe", +] + [[package]] name = "http" version = "1.4.0" @@ -1260,12 +1355,33 @@ dependencies = [ "serde", ] +[[package]] +name = "is-macro" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57a3e447e24c22647738e4607f1df1e0ec6f72e16182c4cd199f647cdfb0e4" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.12.1" @@ -1321,6 +1437,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "lalrpop-util" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" + [[package]] name = "lazy_static" version = "1.5.0" @@ -1411,6 +1533,64 @@ dependencies = [ "libc", ] +[[package]] +name = "malachite" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fbdf9cb251732db30a7200ebb6ae5d22fe8e11397364416617d2c2cf0c51cb5" +dependencies = [ + "malachite-base", + "malachite-nz", + "malachite-q", +] + +[[package]] +name = "malachite-base" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ea0ed76adf7defc1a92240b5c36d5368cfe9251640dcce5bd2d0b7c1fd87aeb" +dependencies = [ + "hashbrown 0.14.5", + "itertools 0.11.0", + "libm", + "ryu", +] + +[[package]] +name = "malachite-bigint" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d149aaa2965d70381709d9df4c7ee1fc0de1c614a4efc2ee356f5e43d68749f8" +dependencies = [ + "derive_more", + "malachite", + "num-integer", + "num-traits", + "paste", +] + +[[package]] +name = "malachite-nz" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34a79feebb2bc9aa7762047c8e5495269a367da6b5a90a99882a0aeeac1841f7" +dependencies = [ + "itertools 0.11.0", + "libm", + "malachite-base", +] + +[[package]] +name = "malachite-q" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f235d5747b1256b47620f5640c2a17a88c7569eebdf27cd9cb130e1a619191" +dependencies = [ + "itertools 0.11.0", + "malachite-base", + "malachite-nz", +] + [[package]] name = "maybe-owned" version = "0.3.4" @@ -1452,6 +1632,32 @@ dependencies = [ "rand 0.8.5", ] +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", + "serde", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -1512,6 +1718,58 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_macros", + "phf_shared", +] + +[[package]] +name = "phf_codegen" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" +dependencies = [ + "phf_generator", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared", + "rand 0.8.5", +] + +[[package]] +name = "phf_macros" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher 1.0.1", +] + [[package]] name = "pin-project-lite" version = "0.2.16" @@ -1608,7 +1866,7 @@ dependencies = [ "pin-project-lite", "quinn-proto", "quinn-udp", - "rustc-hash", + "rustc-hash 2.1.1", "rustls 0.23.35", "socket2", "thiserror 2.0.17", @@ -1628,7 +1886,7 @@ dependencies = [ "lru-slab", "rand 0.9.2", "ring", - "rustc-hash", + "rustc-hash 2.1.1", "rustls 0.23.35", "rustls-pki-types", "slab", @@ -1767,7 +2025,7 @@ dependencies = [ "bumpalo", "hashbrown 0.15.5", "log", - "rustc-hash", + "rustc-hash 2.1.1", "smallvec", ] @@ -1843,6 +2101,12 @@ version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustc-hash" version = "2.1.1" @@ -1945,6 +2209,63 @@ dependencies = [ "untrusted", ] +[[package]] +name = "rustpython-ast" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cdaf8ee5c1473b993b398c174641d3aa9da847af36e8d5eb8291930b72f31a5" +dependencies = [ + "is-macro", + "malachite-bigint", + "rustpython-parser-core", + "static_assertions", +] + +[[package]] +name = "rustpython-parser" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "868f724daac0caf9bd36d38caf45819905193a901e8f1c983345a68e18fb2abb" +dependencies = [ + "anyhow", + "is-macro", + "itertools 0.11.0", + "lalrpop-util", + "log", + "malachite-bigint", + "num-traits", + "phf", + "phf_codegen", + "rustc-hash 1.1.0", + "rustpython-ast", + "rustpython-parser-core", + "tiny-keccak", + "unic-emoji-char", + "unic-ucd-ident", + "unicode_names2", +] + +[[package]] +name = "rustpython-parser-core" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4b6c12fa273825edc7bccd9a734f0ad5ba4b8a2f4da5ff7efe946f066d0f4ad" +dependencies = [ + "is-macro", + "memchr", + "rustpython-parser-vendored", +] + +[[package]] +name = "rustpython-parser-vendored" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04fcea49a4630a3a5d940f4d514dc4f575ed63c14c3e3ed07146634aed7f67a6" +dependencies = [ + "memchr", + "once_cell", +] + [[package]] name = "rustversion" version = "1.0.22" @@ -1957,6 +2278,12 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + [[package]] name = "semver" version = "1.0.27" @@ -1967,6 +2294,12 @@ dependencies = [ "serde_core", ] +[[package]] +name = "seq-macro" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc711410fbe7399f390ca1c3b60ad0f53f80e95c5eb935e52268a0e2cd49acc" + [[package]] name = "serde" version = "1.0.228" @@ -2066,6 +2399,18 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + [[package]] name = "slab" version = "0.4.11" @@ -2081,6 +2426,17 @@ dependencies = [ "serde", ] +[[package]] +name = "smartstring" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29" +dependencies = [ + "autocfg", + "static_assertions", + "version_check", +] + [[package]] name = "socket2" version = "0.6.1" @@ -2103,6 +2459,36 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" +[[package]] +name = "stacker" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1f8b29fb42aafcea4edeeb6b2f2d7ecd0d969c48b4cf0d2e64aafc471dd6e59" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "windows-sys 0.59.0", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "string_enum" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae36a4951ca7bd1cfd991c241584a9824a70f6aff1e7d4f693fb3f2465e4030e" +dependencies = [ + "quote", + "swc_macros_common", + "syn", +] + [[package]] name = "strsim" version = "0.11.1" @@ -2115,6 +2501,114 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +[[package]] +name = "swc_atoms" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4ccbe2ecad10ad7432100f878a107b1d972a8aee83ca53184d00c23a078bb8a" +dependencies = [ + "hstr", + "once_cell", + "serde", +] + +[[package]] +name = "swc_common" +version = "18.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1c06698254e9b47daaf9bbb062af489a350bd8d10dfaab0cabbd32d46cec69d" +dependencies = [ + "anyhow", + "ast_node", + "better_scoped_tls", + "bytes-str", + "either", + "from_variant", + "num-bigint", + "once_cell", + "rustc-hash 2.1.1", + "serde", + "siphasher 0.3.11", + "swc_atoms", + "swc_eq_ignore_macros", + "swc_visit", + "tracing", + "unicode-width", + "url", +] + +[[package]] +name = "swc_ecma_ast" +version = "20.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4682d975a3b4ea33f351e7938db4db41f757d9abe537af4780611ef006531698" +dependencies = [ + "bitflags", + "is-macro", + "num-bigint", + "once_cell", + "phf", + "rustc-hash 2.1.1", + "string_enum", + "swc_atoms", + "swc_common", + "swc_visit", + "unicode-id-start", +] + +[[package]] +name = "swc_ecma_parser" +version = "33.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cb866d9ca2df54daa4a460bdd105cd641d780f0578928cb1f7f4e54574bb2bb" +dependencies = [ + "bitflags", + "either", + "num-bigint", + "phf", + "rustc-hash 2.1.1", + "seq-macro", + "serde", + "smartstring", + "stacker", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "tracing", +] + +[[package]] +name = "swc_eq_ignore_macros" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c16ce73424a6316e95e09065ba6a207eba7765496fed113702278b7711d4b632" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "swc_macros_common" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1efbaa74943dc5ad2a2fb16cbd78b77d7e4d63188f3c5b4df2b4dcd2faaae" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "swc_visit" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62fb71484b486c185e34d2172f0eabe7f4722742aad700f426a494bb2de232a2" +dependencies = [ + "either", + "new_debug_unreachable", +] + [[package]] name = "syn" version = "2.0.111" @@ -2217,6 +2711,15 @@ dependencies = [ "syn", ] +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + [[package]] name = "tinystr" version = "0.8.2" @@ -2469,6 +2972,16 @@ dependencies = [ "syn", ] +[[package]] +name = "triomphe" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd69c5aa8f924c7519d6372789a74eac5b94fb0f8fcf0d4a97eb0bfc3e785f39" +dependencies = [ + "serde", + "stable_deref_trait", +] + [[package]] name = "try-lock" version = "0.2.5" @@ -2481,6 +2994,64 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-emoji-char" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b07221e68897210270a38bde4babb655869637af0f69407f96053a34f76494d" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-ident" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + +[[package]] +name = "unicode-id-start" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81b79ad29b5e19de4260020f8919b443b2ef0277d242ce532ec7b7a2cc8b6007" + [[package]] name = "unicode-ident" version = "1.0.22" @@ -2499,6 +3070,28 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "unicode_names2" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1673eca9782c84de5f81b82e4109dcfb3611c8ba0d52930ec4a9478f547b2dd" +dependencies = [ + "phf", + "unicode_names2_generator", +] + +[[package]] +name = "unicode_names2_generator" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91e5b84611016120197efd7dc93ef76774f4e084cd73c9fb3ea4a86c570c56e" +dependencies = [ + "getopts", + "log", + "phf_codegen", + "rand 0.8.5", +] + [[package]] name = "untrusted" version = "0.9.0" @@ -2810,7 +3403,7 @@ dependencies = [ "cranelift-frontend", "cranelift-native", "gimli", - "itertools", + "itertools 0.12.1", "log", "object 0.36.7", "smallvec", diff --git a/crates/capsule-cli/src/commands/run.rs b/crates/capsule-cli/src/commands/run.rs index 30514b3..199a3a8 100644 --- a/crates/capsule-cli/src/commands/run.rs +++ b/crates/capsule-cli/src/commands/run.rs @@ -1,8 +1,9 @@ +use std::collections::HashMap; use std::fmt; use std::path::{Path, PathBuf}; use std::time::Instant; -use capsule_core::config::manifest::{Manifest, ManifestError}; +use capsule_core::config::manifest::{CapsuleToml, Manifest, ManifestError}; use capsule_core::wasm::commands::create::CreateInstance; use capsule_core::wasm::commands::run::RunInstance; @@ -14,6 +15,7 @@ use capsule_core::wasm::execution_policy::{Compute, ExecutionPolicy}; use capsule_core::wasm::runtime::Runtime; use capsule_core::wasm::runtime::RuntimeConfig; use capsule_core::wasm::runtime::WasmRuntimeError; +use capsule_core::wasm::utilities::task_config::TaskConfig; use capsule_core::wasm::utilities::task_reporter::TaskReporter; pub enum RunError { @@ -60,9 +62,12 @@ impl From for RunError { } } +type TaskRegistry = HashMap; + struct CompileResult { wasm_path: PathBuf, cache_dir: PathBuf, + task_registry: Option, } fn compile_to_wasm(file_path: &Path) -> Result { @@ -75,17 +80,23 @@ fn compile_to_wasm(file_path: &Path) -> Result { "py" => { let compiler = PythonWasmCompiler::new(file_path)?; let wasm_path = compiler.compile_wasm()?; + let task_registry = compiler.introspect_task_registry(); + Ok(CompileResult { wasm_path, cache_dir: compiler.cache_dir, + task_registry, }) } "js" | "mjs" | "ts" => { let compiler = JavascriptWasmCompiler::new(file_path)?; let wasm_path = compiler.compile_wasm()?; + let task_registry = compiler.introspect_task_registry(); + Ok(CompileResult { wasm_path, cache_dir: compiler.cache_dir, + task_registry, }) } _ => Err(RunError::ExecutionFailed(format!( @@ -95,6 +106,17 @@ fn compile_to_wasm(file_path: &Path) -> Result { } } +fn extract_main_execution_policy( + task_registry: Option, + capsule_toml: &CapsuleToml, +) -> Option { + let registry = task_registry?; + let main_config = registry.get("main")?; + let task_config: TaskConfig = serde_json::from_value(main_config.clone()).ok()?; + + Some(task_config.to_execution_policy(capsule_toml)) +} + pub async fn execute( file_path: Option<&Path>, args: Vec, @@ -117,11 +139,16 @@ pub async fn execute( cache_dir: compile_result.cache_dir, verbose, }; - let runtime = Runtime::new(runtime_config, manifest.capsule_toml)?; - let execution_policy = ExecutionPolicy::default() - .compute(Some(Compute::Custom(u64::MAX))) - .allowed_files(vec![".".to_string()]); + let execution_policy = + extract_main_execution_policy(compile_result.task_registry, &manifest.capsule_toml) + .unwrap_or_else(|| { + ExecutionPolicy::default() + .compute(Some(Compute::Custom(u64::MAX))) + .allowed_files(vec![".".to_string()]) + }); + + let runtime = Runtime::new(runtime_config, manifest.capsule_toml)?; let project_root = file_path .canonicalize() diff --git a/crates/capsule-core/Cargo.lock b/crates/capsule-core/Cargo.lock index dca16f3..21b14d6 100644 --- a/crates/capsule-core/Cargo.lock +++ b/crates/capsule-core/Cargo.lock @@ -74,6 +74,17 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" +[[package]] +name = "ast_node" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb025ef00a6da925cf40870b9c8d008526b6004ece399cb0974209720f0b194" +dependencies = [ + "quote", + "swc_macros_common", + "syn", +] + [[package]] name = "async-trait" version = "0.1.89" @@ -118,6 +129,15 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "better_scoped_tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd228125315b132eed175bf47619ac79b945b26e56b848ba203ae4ea8603609" +dependencies = [ + "scoped-tls", +] + [[package]] name = "bitflags" version = "0.9.1" @@ -166,6 +186,16 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" +[[package]] +name = "bytes-str" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c60b5ce37e0b883c37eb89f79a1e26fbe9c1081945d024eee93e8d91a7e18b3" +dependencies = [ + "bytes", + "serde", +] + [[package]] name = "cap-fs-ext" version = "3.4.5" @@ -257,8 +287,12 @@ dependencies = [ "reqwest", "rusqlite", "rustfmt", + "rustpython-parser", "serde", "serde_json", + "swc_common", + "swc_ecma_ast", + "swc_ecma_parser", "tokio", "toml 0.9.11+spec-1.1.0", "wasmtime", @@ -405,7 +439,7 @@ dependencies = [ "hashbrown 0.14.5", "log 0.4.29", "regalloc2", - "rustc-hash", + "rustc-hash 2.1.1", "serde", "smallvec", "target-lexicon", @@ -509,6 +543,12 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + [[package]] name = "crypto-common" version = "0.1.7" @@ -528,6 +568,27 @@ dependencies = [ "uuid", ] +[[package]] +name = "derive_more" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid 0.2.6", +] + [[package]] name = "diff" version = "0.1.13" @@ -718,6 +779,16 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "from_variant" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ff35a391aef949120a0340d690269b3d9f63460a6106e99bd07b961f345ea9" +dependencies = [ + "swc_macros_common", + "syn", +] + [[package]] name = "fs-set-times" version = "0.20.3" @@ -901,6 +972,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash", + "allocator-api2", ] [[package]] @@ -934,6 +1006,20 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hstr" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faa57007c3c9dab34df2fa4c1fb52fe9c34ec5a27ed9d8edea53254b50cd7887" +dependencies = [ + "hashbrown 0.14.5", + "new_debug_unreachable", + "once_cell", + "rustc-hash 2.1.1", + "serde", + "triomphe", +] + [[package]] name = "http" version = "1.4.0" @@ -1238,6 +1324,27 @@ dependencies = [ "serde", ] +[[package]] +name = "is-macro" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57a3e447e24c22647738e4607f1df1e0ec6f72e16182c4cd199f647cdfb0e4" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.12.1" @@ -1303,6 +1410,12 @@ dependencies = [ "winapi-build", ] +[[package]] +name = "lalrpop-util" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" + [[package]] name = "lazy_static" version = "1.5.0" @@ -1402,6 +1515,64 @@ dependencies = [ "libc", ] +[[package]] +name = "malachite" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fbdf9cb251732db30a7200ebb6ae5d22fe8e11397364416617d2c2cf0c51cb5" +dependencies = [ + "malachite-base", + "malachite-nz", + "malachite-q", +] + +[[package]] +name = "malachite-base" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ea0ed76adf7defc1a92240b5c36d5368cfe9251640dcce5bd2d0b7c1fd87aeb" +dependencies = [ + "hashbrown 0.14.5", + "itertools 0.11.0", + "libm", + "ryu", +] + +[[package]] +name = "malachite-bigint" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d149aaa2965d70381709d9df4c7ee1fc0de1c614a4efc2ee356f5e43d68749f8" +dependencies = [ + "derive_more", + "malachite", + "num-integer", + "num-traits", + "paste", +] + +[[package]] +name = "malachite-nz" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34a79feebb2bc9aa7762047c8e5495269a367da6b5a90a99882a0aeeac1841f7" +dependencies = [ + "itertools 0.11.0", + "libm", + "malachite-base", +] + +[[package]] +name = "malachite-q" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f235d5747b1256b47620f5640c2a17a88c7569eebdf27cd9cb130e1a619191" +dependencies = [ + "itertools 0.11.0", + "malachite-base", + "malachite-nz", +] + [[package]] name = "maybe-owned" version = "0.3.4" @@ -1443,6 +1614,32 @@ dependencies = [ "rand 0.8.5", ] +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", + "serde", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -1497,6 +1694,58 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_macros", + "phf_shared", +] + +[[package]] +name = "phf_codegen" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" +dependencies = [ + "phf_generator", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared", + "rand 0.8.5", +] + +[[package]] +name = "phf_macros" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher 1.0.1", +] + [[package]] name = "pin-project-lite" version = "0.2.16" @@ -1593,7 +1842,7 @@ dependencies = [ "pin-project-lite", "quinn-proto", "quinn-udp", - "rustc-hash", + "rustc-hash 2.1.1", "rustls 0.23.35", "socket2", "thiserror 2.0.17", @@ -1613,7 +1862,7 @@ dependencies = [ "lru-slab", "rand 0.9.2", "ring", - "rustc-hash", + "rustc-hash 2.1.1", "rustls 0.23.35", "rustls-pki-types", "slab", @@ -1867,7 +2116,7 @@ dependencies = [ "bumpalo", "hashbrown 0.15.5", "log 0.4.29", - "rustc-hash", + "rustc-hash 2.1.1", "smallvec", ] @@ -1965,6 +2214,12 @@ version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustc-hash" version = "2.1.1" @@ -2101,6 +2356,63 @@ dependencies = [ "untrusted", ] +[[package]] +name = "rustpython-ast" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cdaf8ee5c1473b993b398c174641d3aa9da847af36e8d5eb8291930b72f31a5" +dependencies = [ + "is-macro", + "malachite-bigint", + "rustpython-parser-core", + "static_assertions", +] + +[[package]] +name = "rustpython-parser" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "868f724daac0caf9bd36d38caf45819905193a901e8f1c983345a68e18fb2abb" +dependencies = [ + "anyhow", + "is-macro", + "itertools 0.11.0", + "lalrpop-util", + "log 0.4.29", + "malachite-bigint", + "num-traits", + "phf", + "phf_codegen", + "rustc-hash 1.1.0", + "rustpython-ast", + "rustpython-parser-core", + "tiny-keccak", + "unic-emoji-char", + "unic-ucd-ident", + "unicode_names2", +] + +[[package]] +name = "rustpython-parser-core" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4b6c12fa273825edc7bccd9a734f0ad5ba4b8a2f4da5ff7efe946f066d0f4ad" +dependencies = [ + "is-macro", + "memchr", + "rustpython-parser-vendored", +] + +[[package]] +name = "rustpython-parser-vendored" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04fcea49a4630a3a5d940f4d514dc4f575ed63c14c3e3ed07146634aed7f67a6" +dependencies = [ + "memchr", + "once_cell", +] + [[package]] name = "rustversion" version = "1.0.22" @@ -2113,6 +2425,12 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + [[package]] name = "semver" version = "0.9.0" @@ -2138,6 +2456,12 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +[[package]] +name = "seq-macro" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc711410fbe7399f390ca1c3b60ad0f53f80e95c5eb935e52268a0e2cd49acc" + [[package]] name = "serde" version = "1.0.228" @@ -2237,6 +2561,18 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + [[package]] name = "slab" version = "0.4.11" @@ -2252,6 +2588,17 @@ dependencies = [ "serde", ] +[[package]] +name = "smartstring" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29" +dependencies = [ + "autocfg 1.5.0", + "static_assertions", + "version_check", +] + [[package]] name = "socket2" version = "0.6.1" @@ -2274,6 +2621,36 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" +[[package]] +name = "stacker" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1f8b29fb42aafcea4edeeb6b2f2d7ecd0d969c48b4cf0d2e64aafc471dd6e59" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "windows-sys 0.59.0", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "string_enum" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae36a4951ca7bd1cfd991c241584a9824a70f6aff1e7d4f693fb3f2465e4030e" +dependencies = [ + "quote", + "swc_macros_common", + "syn", +] + [[package]] name = "strings" version = "0.1.1" @@ -2289,6 +2666,114 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +[[package]] +name = "swc_atoms" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4ccbe2ecad10ad7432100f878a107b1d972a8aee83ca53184d00c23a078bb8a" +dependencies = [ + "hstr", + "once_cell", + "serde", +] + +[[package]] +name = "swc_common" +version = "18.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1c06698254e9b47daaf9bbb062af489a350bd8d10dfaab0cabbd32d46cec69d" +dependencies = [ + "anyhow", + "ast_node", + "better_scoped_tls", + "bytes-str", + "either", + "from_variant", + "num-bigint", + "once_cell", + "rustc-hash 2.1.1", + "serde", + "siphasher 0.3.11", + "swc_atoms", + "swc_eq_ignore_macros", + "swc_visit", + "tracing", + "unicode-width", + "url", +] + +[[package]] +name = "swc_ecma_ast" +version = "20.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4682d975a3b4ea33f351e7938db4db41f757d9abe537af4780611ef006531698" +dependencies = [ + "bitflags 2.10.0", + "is-macro", + "num-bigint", + "once_cell", + "phf", + "rustc-hash 2.1.1", + "string_enum", + "swc_atoms", + "swc_common", + "swc_visit", + "unicode-id-start", +] + +[[package]] +name = "swc_ecma_parser" +version = "33.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cb866d9ca2df54daa4a460bdd105cd641d780f0578928cb1f7f4e54574bb2bb" +dependencies = [ + "bitflags 2.10.0", + "either", + "num-bigint", + "phf", + "rustc-hash 2.1.1", + "seq-macro", + "serde", + "smartstring", + "stacker", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "tracing", +] + +[[package]] +name = "swc_eq_ignore_macros" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c16ce73424a6316e95e09065ba6a207eba7765496fed113702278b7711d4b632" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "swc_macros_common" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1efbaa74943dc5ad2a2fb16cbd78b77d7e4d63188f3c5b4df2b4dcd2faaae" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "swc_visit" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62fb71484b486c185e34d2172f0eabe7f4722742aad700f426a494bb2de232a2" +dependencies = [ + "either", + "new_debug_unreachable", +] + [[package]] name = "syn" version = "2.0.111" @@ -2451,6 +2936,15 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + [[package]] name = "tinystr" version = "0.8.2" @@ -2700,6 +3194,16 @@ dependencies = [ "syn", ] +[[package]] +name = "triomphe" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd69c5aa8f924c7519d6372789a74eac5b94fb0f8fcf0d4a97eb0bfc3e785f39" +dependencies = [ + "serde", + "stable_deref_trait", +] + [[package]] name = "try-lock" version = "0.2.5" @@ -2718,6 +3222,64 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abd2fc5d32b590614af8b0a20d837f32eca055edd0bbead59a9cfe80858be003" +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-emoji-char" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b07221e68897210270a38bde4babb655869637af0f69407f96053a34f76494d" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-ident" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + +[[package]] +name = "unicode-id-start" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81b79ad29b5e19de4260020f8919b443b2ef0277d242ce532ec7b7a2cc8b6007" + [[package]] name = "unicode-ident" version = "1.0.22" @@ -2748,6 +3310,28 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "unicode_names2" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1673eca9782c84de5f81b82e4109dcfb3611c8ba0d52930ec4a9478f547b2dd" +dependencies = [ + "phf", + "unicode_names2_generator", +] + +[[package]] +name = "unicode_names2_generator" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91e5b84611016120197efd7dc93ef76774f4e084cd73c9fb3ea4a86c570c56e" +dependencies = [ + "getopts", + "log 0.4.29", + "phf_codegen", + "rand 0.8.5", +] + [[package]] name = "untrusted" version = "0.9.0" @@ -3059,7 +3643,7 @@ dependencies = [ "cranelift-frontend", "cranelift-native", "gimli", - "itertools", + "itertools 0.12.1", "log 0.4.29", "object 0.36.7", "smallvec", diff --git a/crates/capsule-core/Cargo.toml b/crates/capsule-core/Cargo.toml index 24031e2..f2450a5 100644 --- a/crates/capsule-core/Cargo.toml +++ b/crates/capsule-core/Cargo.toml @@ -29,5 +29,10 @@ chrono = "0.4" colored = "2.1" toml = "0.9.11" +rustpython-parser = "0.4" +swc_ecma_parser = "33.0" +swc_common = "18.0" +swc_ecma_ast = "20.0" + [dev-dependencies] rustfmt = "0.10.0" From 3f807d3818b0a850bbf7aa9e748c856ea95b9d27 Mon Sep 17 00:00:00 2001 From: Mavdol Date: Fri, 23 Jan 2026 00:33:59 +0100 Subject: [PATCH 6/8] update doc --- README.md | 2 -- crates/capsule-cli/docs/README-pypi.md | 6 ++---- crates/capsule-cli/npm/README.md | 2 -- crates/capsule-sdk/javascript/README.md | 2 -- 4 files changed, 2 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 056409f..9e84b59 100644 --- a/README.md +++ b/README.md @@ -227,8 +227,6 @@ export const main = task({ ### File Access -The entry point task (main) has access to the entire project directory. Sub-tasks have no filesystem access by default and must declare `allowed_files` to access specific paths. - > [!NOTE] > Currently, `allowed_files` only supports directory paths, not individual files. diff --git a/crates/capsule-cli/docs/README-pypi.md b/crates/capsule-cli/docs/README-pypi.md index bbdb214..19f6266 100644 --- a/crates/capsule-cli/docs/README-pypi.md +++ b/crates/capsule-cli/docs/README-pypi.md @@ -106,19 +106,17 @@ def main() -> dict: ### File Access -The **entry point task** (main) has access to the entire project directory. Sub-tasks have **no filesystem access by default** and must declare `allowed_files` to access specific paths. - Python's standard file operations work normally. Use `open()`, `os`, `pathlib`, or any file manipulation library: ```python from capsule import task -@task(name="restricted_writer", allowed_files=["./output"]) # Sub-task with limited access +@task(name="restricted_writer", allowed_files=["./output"]) def restricted_writer() -> None: with open("./output/result.txt", "w") as f: f.write("result") -@task(name="main") # Has access to entire project +@task(name="main") def main() -> str: restricted_writer() ``` diff --git a/crates/capsule-cli/npm/README.md b/crates/capsule-cli/npm/README.md index b82cef7..22034e5 100644 --- a/crates/capsule-cli/npm/README.md +++ b/crates/capsule-cli/npm/README.md @@ -111,8 +111,6 @@ Task-level options always override these defaults. ### File Access -The **entry point task** (main) has access to the entire project directory. Sub-tasks have **no filesystem access by default** and must declare `allowedFiles` to access specific paths. - Node.js built-ins like `fs` are not available in the WebAssembly sandbox. Instead, use the `files` API provided by the SDK: ```typescript diff --git a/crates/capsule-sdk/javascript/README.md b/crates/capsule-sdk/javascript/README.md index e862013..98fad61 100644 --- a/crates/capsule-sdk/javascript/README.md +++ b/crates/capsule-sdk/javascript/README.md @@ -111,8 +111,6 @@ Task-level options always override these defaults. ### File Access -The entry point task (main) has access to the entire project directory. Sub-tasks have no filesystem access by default and must declare `allowedFiles` to access specific paths. - Node.js built-ins like `fs` are not available in the WebAssembly sandbox. Instead, use the `files` API provided by the SDK: ```typescript From fae1270419821c7fa823d7ed15ed11e32514f519 Mon Sep 17 00:00:00 2001 From: Mavdol Date: Fri, 23 Jan 2026 00:42:11 +0100 Subject: [PATCH 7/8] update doc --- README.md | 8 +++++--- crates/capsule-cli/docs/README-pypi.md | 2 +- crates/capsule-cli/npm/README.md | 2 ++ crates/capsule-sdk/javascript/README.md | 2 ++ 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 9e84b59..1f70d1e 100644 --- a/README.md +++ b/README.md @@ -227,8 +227,10 @@ export const main = task({ ### File Access +Tasks can read and write files within directories specified in `allowed_files`. Any attempt to access files outside these directories is not possible. + > [!NOTE] -> Currently, `allowed_files` only supports directory paths, not individual files. +> Currently, `allowed_files` supports directory paths, not individual files. #### Python @@ -237,12 +239,12 @@ Python's standard file operations work normally. Use `open()`, `os`, `pathlib`, ```python from capsule import task -@task(name="restricted_writer", allowed_files=["./output"]) # Sub-task with limited access +@task(name="restricted_writer", allowed_files=["./output"]) def restricted_writer() -> None: with open("./output/result.txt", "w") as f: f.write("result") -@task(name="main") # Has access to entire project +@task(name="main") def main() -> str: restricted_writer() ``` diff --git a/crates/capsule-cli/docs/README-pypi.md b/crates/capsule-cli/docs/README-pypi.md index 19f6266..28135f4 100644 --- a/crates/capsule-cli/docs/README-pypi.md +++ b/crates/capsule-cli/docs/README-pypi.md @@ -106,7 +106,7 @@ def main() -> dict: ### File Access -Python's standard file operations work normally. Use `open()`, `os`, `pathlib`, or any file manipulation library: +Tasks can read and write files within directories specified in `allowed_files`. Any attempt to access files outside these directories is not possible. ```python from capsule import task diff --git a/crates/capsule-cli/npm/README.md b/crates/capsule-cli/npm/README.md index 22034e5..d9b42a6 100644 --- a/crates/capsule-cli/npm/README.md +++ b/crates/capsule-cli/npm/README.md @@ -111,6 +111,8 @@ Task-level options always override these defaults. ### File Access +Tasks can read and write files within directories specified in `allowedFiles`. Any attempt to access files outside these directories is not possible. + Node.js built-ins like `fs` are not available in the WebAssembly sandbox. Instead, use the `files` API provided by the SDK: ```typescript diff --git a/crates/capsule-sdk/javascript/README.md b/crates/capsule-sdk/javascript/README.md index 98fad61..132c7f8 100644 --- a/crates/capsule-sdk/javascript/README.md +++ b/crates/capsule-sdk/javascript/README.md @@ -111,6 +111,8 @@ Task-level options always override these defaults. ### File Access +Tasks can read and write files within directories specified in `allowedFiles`. Any attempt to access files outside these directories is not possible. + Node.js built-ins like `fs` are not available in the WebAssembly sandbox. Instead, use the `files` API provided by the SDK: ```typescript From 7052fcff348ba906098519e8534f1adb463c1ee2 Mon Sep 17 00:00:00 2001 From: Mavdol Date: Fri, 23 Jan 2026 00:46:12 +0100 Subject: [PATCH 8/8] fix fmt issues --- .../src/wasm/compiler/javascript.rs | 8 +- .../capsule-core/src/wasm/compiler/python.rs | 8 +- .../utilities/introspection/javascript.rs | 89 ++++++++++--------- .../wasm/utilities/introspection/python.rs | 21 +++-- 4 files changed, 64 insertions(+), 62 deletions(-) diff --git a/crates/capsule-core/src/wasm/compiler/javascript.rs b/crates/capsule-core/src/wasm/compiler/javascript.rs index 6a8f809..f021120 100644 --- a/crates/capsule-core/src/wasm/compiler/javascript.rs +++ b/crates/capsule-core/src/wasm/compiler/javascript.rs @@ -1,10 +1,10 @@ +use std::collections::HashMap; use std::fs; use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; -use std::collections::HashMap; -use crate::wasm::utilities::wit_manager::WitManager; use crate::wasm::utilities::introspection::javascript; +use crate::wasm::utilities::wit_manager::WitManager; #[derive(Debug)] pub enum JavascriptWasmCompilerError { @@ -356,9 +356,7 @@ export const taskRunner = exports; Ok(output_path) } - pub fn introspect_task_registry( - &self, - ) -> Option> { + pub fn introspect_task_registry(&self) -> Option> { let source = fs::read_to_string(&self.source_path).ok()?; let is_typescript = self.source_path.extension().is_some_and(|ext| ext == "ts"); diff --git a/crates/capsule-core/src/wasm/compiler/python.rs b/crates/capsule-core/src/wasm/compiler/python.rs index 2786c98..b16bb50 100644 --- a/crates/capsule-core/src/wasm/compiler/python.rs +++ b/crates/capsule-core/src/wasm/compiler/python.rs @@ -1,12 +1,12 @@ +use std::collections::HashMap; use std::fmt; use std::fs; use std::path::{Path, PathBuf}; use std::process::Command; use std::process::Stdio; -use std::collections::HashMap; -use crate::wasm::utilities::wit_manager::WitManager; use crate::wasm::utilities::introspection::python; +use crate::wasm::utilities::wit_manager::WitManager; pub enum PythonWasmCompilerError { CompileFailed(String), @@ -346,9 +346,7 @@ from capsule.app import TaskRunner, exports } } - pub fn introspect_task_registry( - &self, - ) -> Option> { + pub fn introspect_task_registry(&self) -> Option> { let source = fs::read_to_string(&self.source_path).ok()?; python::extract_python_task_configs(&source) diff --git a/crates/capsule-core/src/wasm/utilities/introspection/javascript.rs b/crates/capsule-core/src/wasm/utilities/introspection/javascript.rs index 582b45f..afb2166 100644 --- a/crates/capsule-core/src/wasm/utilities/introspection/javascript.rs +++ b/crates/capsule-core/src/wasm/utilities/introspection/javascript.rs @@ -3,7 +3,8 @@ use swc_common::input::StringInput; use swc_common::sync::Lrc; use swc_common::{FileName, SourceMap}; use swc_ecma_ast::{ - Callee, Decl, Expr, Lit, ModuleDecl, ModuleItem, ObjectLit, Prop, PropName, PropOrSpread, Stmt, Pat + Callee, Decl, Expr, Lit, ModuleDecl, ModuleItem, ObjectLit, Pat, Prop, PropName, PropOrSpread, + Stmt, }; use swc_ecma_parser::{EsSyntax, Parser, Syntax, TsSyntax}; @@ -31,25 +32,29 @@ pub fn extract_js_task_configs( for item in &module.body { if let ModuleItem::Stmt(stmt) = item && let Stmt::Decl(decl) = stmt - && let Decl::Var(var_decl) = decl { - for decl in var_decl.decls.iter() { - if let Some(init) = &decl.init - && let Some((var_name, config)) = extract_task_call(init, &decl.name) { - tasks.insert(var_name, serde_json::to_value(&config).unwrap()); - } - } + && let Decl::Var(var_decl) = decl + { + for decl in var_decl.decls.iter() { + if let Some(init) = &decl.init + && let Some((var_name, config)) = extract_task_call(init, &decl.name) + { + tasks.insert(var_name, serde_json::to_value(&config).unwrap()); } + } + } if let ModuleItem::ModuleDecl(mod_decl) = item && let ModuleDecl::ExportDecl(export) = mod_decl - && let Decl::Var(var_decl) = &export.decl { - for decl in var_decl.decls.iter() { - if let Some(init) = &decl.init - && let Some((var_name, config)) = extract_task_call(init, &decl.name) { - tasks.insert(var_name, serde_json::to_value(&config).unwrap()); - } - } + && let Decl::Var(var_decl) = &export.decl + { + for decl in var_decl.decls.iter() { + if let Some(init) = &decl.init + && let Some((var_name, config)) = extract_task_call(init, &decl.name) + { + tasks.insert(var_name, serde_json::to_value(&config).unwrap()); } + } + } } if tasks.is_empty() { None } else { Some(tasks) } @@ -66,22 +71,23 @@ fn extract_task_call( if let Expr::Call(call) = expr && let Callee::Expr(callee) = &call.callee - && let Expr::Ident(ident) = callee.as_ref() - && ident.sym.as_ref() == "task" - && let Some(first_arg) = call.args.first() - && let Expr::Object(obj) = first_arg.expr.as_ref() { - let mut config = extract_object_literal(obj); - let task_name = config - .get("name") - .and_then(|v| v.as_str()) - .unwrap_or(&var_name) - .to_string(); - config.insert( - "name".to_string(), - serde_json::Value::String(task_name.clone()), - ); - return Some((task_name, config)); - } + && let Expr::Ident(ident) = callee.as_ref() + && ident.sym.as_ref() == "task" + && let Some(first_arg) = call.args.first() + && let Expr::Object(obj) = first_arg.expr.as_ref() + { + let mut config = extract_object_literal(obj); + let task_name = config + .get("name") + .and_then(|v| v.as_str()) + .unwrap_or(&var_name) + .to_string(); + config.insert( + "name".to_string(), + serde_json::Value::String(task_name.clone()), + ); + return Some((task_name, config)); + } None } @@ -91,17 +97,18 @@ fn extract_object_literal(obj: &ObjectLit) -> HashMap for prop in &obj.props { if let PropOrSpread::Prop(prop) = prop - && let Prop::KeyValue(kv) = prop.as_ref() { - let key = match &kv.key { - PropName::Ident(ident) => ident.sym.as_str().to_string(), - PropName::Str(s) => s.value.as_str().unwrap_or_default().to_string(), - _ => continue, - }; - - if let Some(value) = extract_js_literal(&kv.value) { - config.insert(key, value); - } + && let Prop::KeyValue(kv) = prop.as_ref() + { + let key = match &kv.key { + PropName::Ident(ident) => ident.sym.as_str().to_string(), + PropName::Str(s) => s.value.as_str().unwrap_or_default().to_string(), + _ => continue, + }; + + if let Some(value) = extract_js_literal(&kv.value) { + config.insert(key, value); } + } } config diff --git a/crates/capsule-core/src/wasm/utilities/introspection/python.rs b/crates/capsule-core/src/wasm/utilities/introspection/python.rs index afa0457..5d88325 100644 --- a/crates/capsule-core/src/wasm/utilities/introspection/python.rs +++ b/crates/capsule-core/src/wasm/utilities/introspection/python.rs @@ -1,4 +1,4 @@ -use rustpython_parser::{Mode, parse, ast}; +use rustpython_parser::{Mode, ast, parse}; use std::collections::HashMap; pub fn extract_python_task_configs(source: &str) -> Option> { @@ -33,7 +33,6 @@ fn extract_task_decorator( func_name: &str, ) -> Option> { match decorator { - ast::Expr::Name(name) if name.id.as_str() == "task" => { let mut config: HashMap = HashMap::new(); config.insert( @@ -45,9 +44,10 @@ fn extract_task_decorator( ast::Expr::Call(call) => { if let ast::Expr::Name(name) = call.func.as_ref() - && name.id.as_str() == "task" { - return extract_call_args(call, func_name); - } + && name.id.as_str() == "task" + { + return extract_call_args(call, func_name); + } None } _ => None, @@ -62,9 +62,10 @@ fn extract_call_args( for keyword in &call.keywords { if let Some(arg_name) = &keyword.arg - && let Some(value) = extract_literal(&keyword.value) { - config.insert(arg_name.to_string(), value); - } + && let Some(value) = extract_literal(&keyword.value) + { + config.insert(arg_name.to_string(), value); + } } if !config.contains_key("name") { @@ -80,9 +81,7 @@ fn extract_call_args( fn extract_literal(expr: &ast::Expr) -> Option { match expr { ast::Expr::Constant(c) => match &c.value { - ast::Constant::Str(s) => { - Some(serde_json::Value::String(s.to_string())) - } + ast::Constant::Str(s) => Some(serde_json::Value::String(s.to_string())), ast::Constant::Int(i) => { let val: i64 = i.to_string().parse().ok()?; Some(serde_json::Value::Number(val.into()))