diff --git a/RELEASE.md b/RELEASE.md index f0d9b91..42f7d96 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -24,6 +24,10 @@ All other workspace member crates inherit the version automatically. - `src/wasm_sandbox/guests/javascript/package.json` +### .NET (Directory.Build.props) + +- `src/sdk/dotnet/Directory.Build.props` + ## 2. Verify the Build ```sh diff --git a/src/sdk/dotnet/Justfile b/src/sdk/dotnet/Justfile index 0494939..2eb6f82 100644 --- a/src/sdk/dotnet/Justfile +++ b/src/sdk/dotnet/Justfile @@ -19,7 +19,7 @@ devnull := if os() == "windows" { "$null" } else { "/dev/null" } #### BUILD #### # Build everything (Rust FFI + .NET solution) -build target=default-target: (build-rust target) (build-dotnet target) +build target=default-target: (build-rust target) (build-dotnet target) (build-guests target) # Build only the Rust FFI crate build-rust target=default-target: @@ -29,6 +29,11 @@ build-rust target=default-target: build-dotnet target=default-target: dotnet build {{repo-root}}/src/sdk/dotnet/core/HyperlightSandbox.sln --configuration {{ if target == "release" { "Release" } else { "Debug" } }} +# Build guest modules that are embedded into guest NuGet packages +build-guests target=default-target: + cd {{repo-root}} && just -f src/wasm_sandbox/Justfile guest-build {{target}} + cd {{repo-root}} && just -f src/wasm_sandbox/Justfile js-guest-build + #### FORMAT #### # Apply all formatting (Rust + .NET) @@ -82,8 +87,8 @@ test-dotnet target=default-target: (build target) #### EXAMPLES #### -# Run the core examples (requires guest module: just wasm guest-build) -examples: +# Run the core examples +examples target=default-target: (build-guests target) dotnet run --project {{repo-root}}/src/sdk/dotnet/core/Examples/BasicExample/BasicExample.csproj dotnet run --project {{repo-root}}/src/sdk/dotnet/core/Examples/ToolRegistrationExample/ToolRegistrationExample.csproj dotnet run --project {{repo-root}}/src/sdk/dotnet/core/Examples/SnapshotExample/SnapshotExample.csproj @@ -99,11 +104,13 @@ copilot-sdk-example: #### DIST / PUBLISH #### # Build NuGet packages -dist target="release": (build target) +dist target="release": (build-guests target) (build target) {{mkdirp}} {{repo-root}}/dist/dotnetsdk dotnet pack {{repo-root}}/src/sdk/dotnet/core/PInvoke/HyperlightSandbox.PInvoke.csproj --configuration {{ if target == "release" { "Release" } else { "Debug" } }} --no-build --output {{repo-root}}/dist/dotnetsdk dotnet pack {{repo-root}}/src/sdk/dotnet/core/Api/HyperlightSandbox.Api.csproj --configuration {{ if target == "release" { "Release" } else { "Debug" } }} --no-build --output {{repo-root}}/dist/dotnetsdk dotnet pack {{repo-root}}/src/sdk/dotnet/core/Extensions.AI/HyperlightSandbox.Extensions.AI.csproj --configuration {{ if target == "release" { "Release" } else { "Debug" } }} --no-build --output {{repo-root}}/dist/dotnetsdk + dotnet pack {{repo-root}}/src/sdk/dotnet/core/Guest.Python/HyperlightSandbox.Guest.Python.csproj --configuration {{ if target == "release" { "Release" } else { "Debug" } }} --no-build --output {{repo-root}}/dist/dotnetsdk + dotnet pack {{repo-root}}/src/sdk/dotnet/core/Guest.JavaScript/HyperlightSandbox.Guest.JavaScript.csproj --configuration {{ if target == "release" { "Release" } else { "Debug" } }} --no-build --output {{repo-root}}/dist/dotnetsdk @echo "✅ NuGet packages created in dist/dotnetsdk/" # Publish NuGet packages to nuget.org (requires NUGET_API_KEY env var) @@ -157,4 +164,8 @@ clean: -{{rmrf}} {{repo-root}}/src/sdk/dotnet/core/Api/obj -{{rmrf}} {{repo-root}}/src/sdk/dotnet/core/Extensions.AI/bin -{{rmrf}} {{repo-root}}/src/sdk/dotnet/core/Extensions.AI/obj + -{{rmrf}} {{repo-root}}/src/sdk/dotnet/core/Guest.Python/bin + -{{rmrf}} {{repo-root}}/src/sdk/dotnet/core/Guest.Python/obj + -{{rmrf}} {{repo-root}}/src/sdk/dotnet/core/Guest.JavaScript/bin + -{{rmrf}} {{repo-root}}/src/sdk/dotnet/core/Guest.JavaScript/obj -{{rmrf}} {{repo-root}}/dist/dotnetsdk diff --git a/src/sdk/dotnet/README.md b/src/sdk/dotnet/README.md index 9866c31..375ac03 100644 --- a/src/sdk/dotnet/README.md +++ b/src/sdk/dotnet/README.md @@ -18,7 +18,6 @@ A .NET 8.0 SDK for running code in secure, sandboxed environments using [hyperli ```bash # Prerequisites -just wasm guest-build # Build the Python guest module just dotnet build # Build the .NET SDK + Rust FFI ``` @@ -26,10 +25,11 @@ just dotnet build # Build the .NET SDK + Rust FFI ```csharp using HyperlightSandbox.Api; +using HyperlightSandbox.Guest.Python; // Create a sandbox with the Python guest using var sandbox = new SandboxBuilder() - .WithModulePath("path/to/python-sandbox.aot") + .WithPythonModule() .Build(); // Execute code @@ -50,7 +50,7 @@ Register .NET functions that guest code can call: ```csharp using var sandbox = new SandboxBuilder() - .WithModulePath("python-sandbox.aot") + .WithPythonModule() .Build(); // Typed tool — auto-serializes args and result @@ -87,13 +87,24 @@ using var sandbox = new SandboxBuilder() var result = sandbox.Run("console.log('Hello from JS!');"); ``` +To run JavaScript through the Wasm guest module package instead, reference +`Hyperlight.HyperlightSandbox.Guest.JavaScript` and call: + +```csharp +using HyperlightSandbox.Guest.JavaScript; + +using var sandbox = new SandboxBuilder() + .WithJavaScriptModule() + .Build(); +``` + ### Snapshot/Restore Checkpoint sandbox state for fast resets between executions: ```csharp using var sandbox = new SandboxBuilder() - .WithModulePath("python-sandbox.aot") + .WithPythonModule() .Build(); // Cold start (~2.5s) @@ -114,7 +125,7 @@ sandbox.Run("print(x)"); // NameError: x is not defined ```csharp using var sandbox = new SandboxBuilder() - .WithModulePath("python-sandbox.aot") + .WithPythonModule() .WithInputDir("/path/to/input") // Read-only /input in guest .WithTempOutput() // Writable /output in guest .Build(); @@ -149,11 +160,12 @@ Use with GitHub Copilot SDK or Microsoft Agent Framework: ```csharp using HyperlightSandbox.Api; using HyperlightSandbox.Extensions.AI; +using HyperlightSandbox.Guest.Python; // Create a code execution tool with snapshot/restore for clean state using var codeTool = new CodeExecutionTool( new SandboxBuilder() - .WithModulePath("python-sandbox.aot") + .WithPythonModule() .WithTempOutput()); codeTool.RegisterTool("compute", @@ -229,6 +241,8 @@ just dotnet copilot-sdk-example # Run Copilot SDK example | `Hyperlight.HyperlightSandbox.PInvoke` | P/Invoke bindings + native library | | `Hyperlight.HyperlightSandbox.Api` | High-level API (Sandbox, tools, snapshots) | | `Hyperlight.HyperlightSandbox.Extensions.AI` | AI agent integration (CodeExecutionTool, AIFunction) | +| `Hyperlight.HyperlightSandbox.Guest.Python` | Bundled Python Wasm/AOT guest module + `WithPythonModule()` | +| `Hyperlight.HyperlightSandbox.Guest.JavaScript` | Bundled JavaScript Wasm/AOT guest module + `WithJavaScriptModule()` | ## API Reference @@ -237,6 +251,8 @@ just dotnet copilot-sdk-example # Run Copilot SDK example | Method | Description | |--------|-------------| | `WithModulePath(string)` | Path to `.wasm`/`.aot` guest (required for Wasm) | +| `WithPythonModule()` | Use the bundled Python guest from `Hyperlight.HyperlightSandbox.Guest.Python` | +| `WithJavaScriptModule()` | Use the bundled JavaScript guest from `Hyperlight.HyperlightSandbox.Guest.JavaScript` | | `WithBackend(SandboxBackend)` | `Wasm` (default) or `JavaScript` | | `WithHeapSize(string\|ulong)` | Guest heap size (e.g. `"50Mi"`, default: platform-dependent) | | `WithStackSize(string\|ulong)` | Guest stack size (e.g. `"35Mi"`, default: platform-dependent) | @@ -299,7 +315,7 @@ sandbox.Run("..."); - .NET 8.0 SDK or later - Rust 1.89+ (for building the FFI crate) - Linux (Windows support coming via hyperlight) -- `just wasm guest-build` for Wasm backend examples +- `just dotnet dist` builds the Python and JavaScript guest modules before packing their NuGet packages ## Contributing diff --git a/src/sdk/dotnet/core/Examples/BasicExample/BasicExample.csproj b/src/sdk/dotnet/core/Examples/BasicExample/BasicExample.csproj index d67765a..f92e2fc 100644 --- a/src/sdk/dotnet/core/Examples/BasicExample/BasicExample.csproj +++ b/src/sdk/dotnet/core/Examples/BasicExample/BasicExample.csproj @@ -7,6 +7,6 @@ - + diff --git a/src/sdk/dotnet/core/Examples/BasicExample/Program.cs b/src/sdk/dotnet/core/Examples/BasicExample/Program.cs index 9f485ec..fac3a46 100644 --- a/src/sdk/dotnet/core/Examples/BasicExample/Program.cs +++ b/src/sdk/dotnet/core/Examples/BasicExample/Program.cs @@ -3,19 +3,15 @@ // Mirrors: src/wasm_sandbox/examples/python_basics.rs // // Prerequisites: -// just wasm guest-build # builds python-sandbox.aot -// just dotnet build # builds the .NET SDK + FFI +// just dotnet build # builds the .NET SDK + bundled guest package using HyperlightSandbox.Api; -using HyperlightSandbox.Examples.Common; - -var guestPath = ExampleHelper.RequirePythonGuest(); +using HyperlightSandbox.Guest.Python; Console.WriteLine("=== Hyperlight Sandbox .NET — Basic Example ===\n"); -Console.WriteLine($"Guest module: {guestPath}\n"); using var sandbox = new SandboxBuilder() - .WithModulePath(guestPath) + .WithPythonModule() .Build(); // --- Test 1: Basic code execution --- diff --git a/src/sdk/dotnet/core/Examples/FilesystemExample/FilesystemExample.csproj b/src/sdk/dotnet/core/Examples/FilesystemExample/FilesystemExample.csproj index d67765a..f92e2fc 100644 --- a/src/sdk/dotnet/core/Examples/FilesystemExample/FilesystemExample.csproj +++ b/src/sdk/dotnet/core/Examples/FilesystemExample/FilesystemExample.csproj @@ -7,6 +7,6 @@ - + diff --git a/src/sdk/dotnet/core/Examples/FilesystemExample/Program.cs b/src/sdk/dotnet/core/Examples/FilesystemExample/Program.cs index 683e1f3..9ee56f6 100644 --- a/src/sdk/dotnet/core/Examples/FilesystemExample/Program.cs +++ b/src/sdk/dotnet/core/Examples/FilesystemExample/Program.cs @@ -3,16 +3,14 @@ // Mirrors: src/wasm_sandbox/examples/python_filesystem_demo.rs using HyperlightSandbox.Api; -using HyperlightSandbox.Examples.Common; - -var guestPath = ExampleHelper.RequirePythonGuest(); +using HyperlightSandbox.Guest.Python; Console.WriteLine("=== Hyperlight Sandbox .NET — Filesystem Example ===\n"); // --- Test 1: Temp output --- Console.WriteLine("═══ Test 1: Temp output directory ═══"); using (var sandbox = new SandboxBuilder() - .WithModulePath(guestPath) + .WithPythonModule() .WithTempOutput() .Build()) { @@ -38,7 +36,7 @@ with open("/output/hello.txt", "w") as f: try { using var sandbox = new SandboxBuilder() - .WithModulePath(guestPath) + .WithPythonModule() .WithInputDir(inputDir) .WithTempOutput() .Build(); diff --git a/src/sdk/dotnet/core/Examples/NetworkExample/NetworkExample.csproj b/src/sdk/dotnet/core/Examples/NetworkExample/NetworkExample.csproj index d67765a..f92e2fc 100644 --- a/src/sdk/dotnet/core/Examples/NetworkExample/NetworkExample.csproj +++ b/src/sdk/dotnet/core/Examples/NetworkExample/NetworkExample.csproj @@ -7,6 +7,6 @@ - + diff --git a/src/sdk/dotnet/core/Examples/NetworkExample/Program.cs b/src/sdk/dotnet/core/Examples/NetworkExample/Program.cs index 708ef54..ebfcd7b 100644 --- a/src/sdk/dotnet/core/Examples/NetworkExample/Program.cs +++ b/src/sdk/dotnet/core/Examples/NetworkExample/Program.cs @@ -3,16 +3,14 @@ // Mirrors: src/wasm_sandbox/examples/python_network_demo.rs using HyperlightSandbox.Api; -using HyperlightSandbox.Examples.Common; - -var guestPath = ExampleHelper.RequirePythonGuest(); +using HyperlightSandbox.Guest.Python; Console.WriteLine("=== Hyperlight Sandbox .NET — Network Example ===\n"); // --- Test 1: Allow a domain with all methods --- Console.WriteLine("═══ Test 1: HTTP GET with allowlisted domain ═══"); using (var sandbox = new SandboxBuilder() - .WithModulePath(guestPath) + .WithPythonModule() .Build()) { sandbox.AllowDomain("https://httpbin.org"); @@ -30,7 +28,7 @@ // --- Test 2: Method-filtered access --- Console.WriteLine("\n═══ Test 2: Method-filtered access (GET only) ═══"); using (var sandbox = new SandboxBuilder() - .WithModulePath(guestPath) + .WithPythonModule() .Build()) { // Only allow GET requests to httpbin.org. diff --git a/src/sdk/dotnet/core/Examples/SnapshotExample/Program.cs b/src/sdk/dotnet/core/Examples/SnapshotExample/Program.cs index fa3cfe2..f0fe79d 100644 --- a/src/sdk/dotnet/core/Examples/SnapshotExample/Program.cs +++ b/src/sdk/dotnet/core/Examples/SnapshotExample/Program.cs @@ -6,14 +6,12 @@ using System.Diagnostics; using HyperlightSandbox.Api; -using HyperlightSandbox.Examples.Common; - -var guestPath = ExampleHelper.RequirePythonGuest(); +using HyperlightSandbox.Guest.Python; Console.WriteLine("=== Hyperlight Sandbox .NET — Snapshot Example ===\n"); using var sandbox = new SandboxBuilder() - .WithModulePath(guestPath) + .WithPythonModule() .WithTempOutput() .Build(); diff --git a/src/sdk/dotnet/core/Examples/SnapshotExample/SnapshotExample.csproj b/src/sdk/dotnet/core/Examples/SnapshotExample/SnapshotExample.csproj index d67765a..f92e2fc 100644 --- a/src/sdk/dotnet/core/Examples/SnapshotExample/SnapshotExample.csproj +++ b/src/sdk/dotnet/core/Examples/SnapshotExample/SnapshotExample.csproj @@ -7,6 +7,6 @@ - + diff --git a/src/sdk/dotnet/core/Examples/ToolRegistrationExample/Program.cs b/src/sdk/dotnet/core/Examples/ToolRegistrationExample/Program.cs index 3c90283..9031dab 100644 --- a/src/sdk/dotnet/core/Examples/ToolRegistrationExample/Program.cs +++ b/src/sdk/dotnet/core/Examples/ToolRegistrationExample/Program.cs @@ -4,14 +4,12 @@ using System.Text.Json.Serialization; using HyperlightSandbox.Api; -using HyperlightSandbox.Examples.Common; - -var guestPath = ExampleHelper.RequirePythonGuest(); +using HyperlightSandbox.Guest.Python; Console.WriteLine("=== Hyperlight Sandbox .NET — Tool Registration Example ===\n"); using var sandbox = new SandboxBuilder() - .WithModulePath(guestPath) + .WithPythonModule() .Build(); // --- Register typed tools --- diff --git a/src/sdk/dotnet/core/Examples/ToolRegistrationExample/ToolRegistrationExample.csproj b/src/sdk/dotnet/core/Examples/ToolRegistrationExample/ToolRegistrationExample.csproj index d67765a..f92e2fc 100644 --- a/src/sdk/dotnet/core/Examples/ToolRegistrationExample/ToolRegistrationExample.csproj +++ b/src/sdk/dotnet/core/Examples/ToolRegistrationExample/ToolRegistrationExample.csproj @@ -7,6 +7,6 @@ - + diff --git a/src/sdk/dotnet/core/Guest.JavaScript/HyperlightSandbox.Guest.JavaScript.csproj b/src/sdk/dotnet/core/Guest.JavaScript/HyperlightSandbox.Guest.JavaScript.csproj new file mode 100644 index 0000000..a9f8ece --- /dev/null +++ b/src/sdk/dotnet/core/Guest.JavaScript/HyperlightSandbox.Guest.JavaScript.csproj @@ -0,0 +1,60 @@ + + + + net8.0 + enable + enable + All + latest + true + HyperlightSandbox.Guest.JavaScript + + + Hyperlight.HyperlightSandbox.Guest.JavaScript + JavaScript guest module package for hyperlight-sandbox. Bundles the prebuilt JavaScript AOT guest and provides SandboxBuilder.WithJavaScriptModule(). + hyperlight;sandbox;javascript;guest;module + Apache-2.0 + https://github.com/hyperlight-dev/hyperlight-sandbox + https://github.com/hyperlight-dev/hyperlight-sandbox + git + true + + + + $([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)../../../../..')) + $([System.IO.Path]::Combine('$(WorkspaceRoot)', 'src', 'wasm_sandbox', 'guests', 'javascript')) + + + + + + + + + + true + runtimes/linux-x64/native/ + PreserveNewest + runtimes/linux-x64/native/js-sandbox.aot + + + true + runtimes/win-x64/native/ + PreserveNewest + runtimes/win-x64/native/js-sandbox.aot + + + + + + + + diff --git a/src/sdk/dotnet/core/Guest.JavaScript/JavaScriptGuestModule.cs b/src/sdk/dotnet/core/Guest.JavaScript/JavaScriptGuestModule.cs new file mode 100644 index 0000000..0cdc4f7 --- /dev/null +++ b/src/sdk/dotnet/core/Guest.JavaScript/JavaScriptGuestModule.cs @@ -0,0 +1,75 @@ +using HyperlightSandbox.Api; + +namespace HyperlightSandbox.Guest.JavaScript; + +/// +/// Provides access to the bundled JavaScript guest module. +/// +public static class JavaScriptGuestModule +{ + /// The bundled JavaScript AOT module file name. + public const string ModuleFileName = "js-sandbox.aot"; + + /// + /// Returns the path to the JavaScript AOT guest module. + /// Follows the NuGet runtimes/{rid}/native/ probing convention + /// (same approach as SafeNativeMethods.DllImportResolver in PInvoke). + /// + public static string GetModulePath() => FindGuestFile(ModuleFileName); + + /// + /// Configures the builder to use the bundled JavaScript guest module with the Wasm backend. + /// + /// The sandbox builder to configure. + /// The same builder for chaining. + public static SandboxBuilder WithJavaScriptModule(this SandboxBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + return builder + .WithBackend(SandboxBackend.Wasm) + .WithModulePath(GetModulePath()); + } + + private static string FindGuestFile(string fileName) + { + string assemblyDir = Path.GetDirectoryName( + typeof(JavaScriptGuestModule).Assembly.Location) ?? AppContext.BaseDirectory; + + // The .aot binary is built on Windows but works on both linux-x64 and win-x64. + // Probe the OS-appropriate RID path first (matches SafeNativeMethods.DllImportResolver). + string rid = OperatingSystem.IsWindows() ? "win-x64" : "linux-x64"; + string nativePath = Path.Join(assemblyDir, "runtimes", rid, "native", fileName); + if (File.Exists(nativePath)) + { + return nativePath; + } + + // Flat output directory (some single-file / RID-specific publish layouts) + string flatPath = Path.Join(assemblyDir, fileName); + if (File.Exists(flatPath)) + { + return flatPath; + } + +#if DEBUG + // Walk up from assembly dir to find source guest build output (local dev without pack) + string? dir = assemblyDir; + while (dir != null) + { + string guestPath = Path.Join(dir, "src", "wasm_sandbox", "guests", "javascript", fileName); + if (File.Exists(guestPath)) + { + return guestPath; + } + + dir = Path.GetDirectoryName(dir); + } +#endif + + throw new FileNotFoundException( + $"JavaScript guest file '{fileName}' not found. " + + $"Searched '{nativePath}'. " + + "Ensure the package was built with 'just wasm js-guest-build'.", + nativePath); + } +} diff --git a/src/sdk/dotnet/core/Guest.Python/HyperlightSandbox.Guest.Python.csproj b/src/sdk/dotnet/core/Guest.Python/HyperlightSandbox.Guest.Python.csproj new file mode 100644 index 0000000..eaed036 --- /dev/null +++ b/src/sdk/dotnet/core/Guest.Python/HyperlightSandbox.Guest.Python.csproj @@ -0,0 +1,60 @@ + + + + net8.0 + enable + enable + All + latest + true + HyperlightSandbox.Guest.Python + + + Hyperlight.HyperlightSandbox.Guest.Python + Python guest module package for hyperlight-sandbox. Bundles the prebuilt Python AOT guest and provides SandboxBuilder.WithPythonModule(). + hyperlight;sandbox;python;guest;module + Apache-2.0 + https://github.com/hyperlight-dev/hyperlight-sandbox + https://github.com/hyperlight-dev/hyperlight-sandbox + git + true + + + + $([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)../../../../..')) + $([System.IO.Path]::Combine('$(WorkspaceRoot)', 'src', 'wasm_sandbox', 'guests', 'python')) + + + + + + + + + + true + runtimes/linux-x64/native/ + PreserveNewest + runtimes/linux-x64/native/python-sandbox.aot + + + true + runtimes/win-x64/native/ + PreserveNewest + runtimes/win-x64/native/python-sandbox.aot + + + + + + + + diff --git a/src/sdk/dotnet/core/Guest.Python/PythonGuestModule.cs b/src/sdk/dotnet/core/Guest.Python/PythonGuestModule.cs new file mode 100644 index 0000000..dae0a9c --- /dev/null +++ b/src/sdk/dotnet/core/Guest.Python/PythonGuestModule.cs @@ -0,0 +1,75 @@ +using HyperlightSandbox.Api; + +namespace HyperlightSandbox.Guest.Python; + +/// +/// Provides access to the bundled Python guest module. +/// +public static class PythonGuestModule +{ + /// The bundled Python AOT module file name. + public const string ModuleFileName = "python-sandbox.aot"; + + /// + /// Returns the path to the Python AOT guest module. + /// Follows the NuGet runtimes/{rid}/native/ probing convention + /// (same approach as SafeNativeMethods.DllImportResolver in PInvoke). + /// + public static string GetModulePath() => FindGuestFile(ModuleFileName); + + /// + /// Configures the builder to use the bundled Python guest module with the Wasm backend. + /// + /// The sandbox builder to configure. + /// The same builder for chaining. + public static SandboxBuilder WithPythonModule(this SandboxBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + return builder + .WithBackend(SandboxBackend.Wasm) + .WithModulePath(GetModulePath()); + } + + private static string FindGuestFile(string fileName) + { + string assemblyDir = Path.GetDirectoryName( + typeof(PythonGuestModule).Assembly.Location) ?? AppContext.BaseDirectory; + + // The .aot binary is built on Windows but works on both linux-x64 and win-x64. + // Probe the OS-appropriate RID path first (matches SafeNativeMethods.DllImportResolver). + string rid = OperatingSystem.IsWindows() ? "win-x64" : "linux-x64"; + string nativePath = Path.Join(assemblyDir, "runtimes", rid, "native", fileName); + if (File.Exists(nativePath)) + { + return nativePath; + } + + // Flat output directory (some single-file / RID-specific publish layouts) + string flatPath = Path.Join(assemblyDir, fileName); + if (File.Exists(flatPath)) + { + return flatPath; + } + +#if DEBUG + // Walk up from assembly dir to find source guest build output (local dev without pack) + string? dir = assemblyDir; + while (dir != null) + { + string guestPath = Path.Join(dir, "src", "wasm_sandbox", "guests", "python", fileName); + if (File.Exists(guestPath)) + { + return guestPath; + } + + dir = Path.GetDirectoryName(dir); + } +#endif + + throw new FileNotFoundException( + $"Python guest file '{fileName}' not found. " + + $"Searched '{nativePath}'. " + + "Ensure the package was built with 'just wasm guest-build'.", + nativePath); + } +} diff --git a/src/sdk/dotnet/core/HyperlightSandbox.sln b/src/sdk/dotnet/core/HyperlightSandbox.sln index ba19dc6..54c7111 100644 --- a/src/sdk/dotnet/core/HyperlightSandbox.sln +++ b/src/sdk/dotnet/core/HyperlightSandbox.sln @@ -13,6 +13,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HyperlightSandbox.Tests", " EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HyperlightSandbox.Extensions.AI", "Extensions.AI\HyperlightSandbox.Extensions.AI.csproj", "{04AFD6B6-B65F-4FBD-9EF7-68898D11907F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HyperlightSandbox.Guest.Python", "Guest.Python\HyperlightSandbox.Guest.Python.csproj", "{8B76CB08-67F9-44D4-B28D-927670F69C31}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HyperlightSandbox.Guest.JavaScript", "Guest.JavaScript\HyperlightSandbox.Guest.JavaScript.csproj", "{B35581C7-C4E4-4239-8E34-9CEDB3C40B92}" +EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{8D99959A-5255-4BD9-BBF9-A8447698D515}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BasicExample", "Examples\BasicExample\BasicExample.csproj", "{FE6854EE-0EE8-4A97-83EB-CCA003D45F16}" @@ -52,6 +56,14 @@ Global {04AFD6B6-B65F-4FBD-9EF7-68898D11907F}.Debug|Any CPU.Build.0 = Debug|Any CPU {04AFD6B6-B65F-4FBD-9EF7-68898D11907F}.Release|Any CPU.ActiveCfg = Release|Any CPU {04AFD6B6-B65F-4FBD-9EF7-68898D11907F}.Release|Any CPU.Build.0 = Release|Any CPU + {8B76CB08-67F9-44D4-B28D-927670F69C31}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8B76CB08-67F9-44D4-B28D-927670F69C31}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8B76CB08-67F9-44D4-B28D-927670F69C31}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8B76CB08-67F9-44D4-B28D-927670F69C31}.Release|Any CPU.Build.0 = Release|Any CPU + {B35581C7-C4E4-4239-8E34-9CEDB3C40B92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B35581C7-C4E4-4239-8E34-9CEDB3C40B92}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B35581C7-C4E4-4239-8E34-9CEDB3C40B92}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B35581C7-C4E4-4239-8E34-9CEDB3C40B92}.Release|Any CPU.Build.0 = Release|Any CPU {FE6854EE-0EE8-4A97-83EB-CCA003D45F16}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FE6854EE-0EE8-4A97-83EB-CCA003D45F16}.Debug|Any CPU.Build.0 = Debug|Any CPU {FE6854EE-0EE8-4A97-83EB-CCA003D45F16}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/src/sdk/dotnet/core/Tests/HyperlightSandbox.PackageTests/HyperlightSandbox.PackageTests.csproj b/src/sdk/dotnet/core/Tests/HyperlightSandbox.PackageTests/HyperlightSandbox.PackageTests.csproj index 255b257..ab15749 100644 --- a/src/sdk/dotnet/core/Tests/HyperlightSandbox.PackageTests/HyperlightSandbox.PackageTests.csproj +++ b/src/sdk/dotnet/core/Tests/HyperlightSandbox.PackageTests/HyperlightSandbox.PackageTests.csproj @@ -27,6 +27,8 @@ + + diff --git a/src/sdk/dotnet/core/Tests/HyperlightSandbox.PackageTests/PackageInstallationTests.cs b/src/sdk/dotnet/core/Tests/HyperlightSandbox.PackageTests/PackageInstallationTests.cs index cfddc0c..f3f8f64 100644 --- a/src/sdk/dotnet/core/Tests/HyperlightSandbox.PackageTests/PackageInstallationTests.cs +++ b/src/sdk/dotnet/core/Tests/HyperlightSandbox.PackageTests/PackageInstallationTests.cs @@ -1,4 +1,6 @@ using HyperlightSandbox.Api; +using HyperlightSandbox.Guest.JavaScript; +using HyperlightSandbox.Guest.Python; using Xunit; namespace HyperlightSandbox.PackageTests; @@ -81,4 +83,34 @@ public void PInvoke_NativeLibrary_LoadsAndCreatesHandle() Assert.NotNull(sandbox); } + + [Fact] + public void GuestPython_PackageMaterializesModuleAndConfiguresBuilder() + { + var modulePath = PythonGuestModule.GetModulePath(); + + Assert.True(File.Exists(modulePath)); + Assert.EndsWith(PythonGuestModule.ModuleFileName, modulePath, StringComparison.Ordinal); + + using var sandbox = new SandboxBuilder() + .WithPythonModule() + .Build(); + + Assert.NotNull(sandbox); + } + + [Fact] + public void GuestJavaScript_PackageMaterializesModuleAndConfiguresBuilder() + { + var modulePath = JavaScriptGuestModule.GetModulePath(); + + Assert.True(File.Exists(modulePath)); + Assert.EndsWith(JavaScriptGuestModule.ModuleFileName, modulePath, StringComparison.Ordinal); + + using var sandbox = new SandboxBuilder() + .WithJavaScriptModule() + .Build(); + + Assert.NotNull(sandbox); + } } diff --git a/src/wasm_sandbox/guests/javascript/package-lock.json b/src/wasm_sandbox/guests/javascript/package-lock.json index bc56d21..817f597 100644 --- a/src/wasm_sandbox/guests/javascript/package-lock.json +++ b/src/wasm_sandbox/guests/javascript/package-lock.json @@ -216,29 +216,6 @@ "wizer-win32-x64": "wizer" } }, - "node_modules/@emnapi/core": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", - "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.1", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", - "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@emnapi/wasi-threads": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",