Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions package-lock.json

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

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
"private": true,
"description": "MXC sandbox dependency used by the OpenClaw tray build to copy wxc-exec.exe into the app output.",
"dependencies": {
"@microsoft/mxc-sdk": "^0.6.1"
"@microsoft/mxc-sdk": "^0.7.0"
}
}
34 changes: 22 additions & 12 deletions src/OpenClaw.Shared/Mxc/DirectAppContainerExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,24 +43,34 @@ public sealed class DirectAppContainerExecutor : ISandboxExecutor
/// </summary>
private const int Base64ConfigCharLimit = 25_000;

private readonly MxcAvailability _availability;
private readonly Func<MxcAvailability> _availabilityProvider;
private readonly IOpenClawLogger _logger;

public DirectAppContainerExecutor(MxcAvailability availability, IOpenClawLogger? logger = null)
/// <summary>
/// Resolves availability lazily via <paramref name="availabilityProvider"/> so the
/// executor always sees the current probe verdict. A fixed snapshot would freeze a
/// startup probe error for the executor's lifetime even after the host recovers
/// (the re-probe would update the caller's gate but not this executor).
/// </summary>
public DirectAppContainerExecutor(Func<MxcAvailability> availabilityProvider, IOpenClawLogger? logger = null)
{
_availability = availability;
_availabilityProvider = availabilityProvider ?? throw new ArgumentNullException(nameof(availabilityProvider));
_logger = logger ?? NullLogger.Instance;
}

public async Task<SandboxExecutionResult> ExecuteAsync(
SandboxExecutionRequest request,
CancellationToken ct = default)
{
if (!_availability.IsAppContainerAvailable)
// Resolve the live availability for THIS invocation so a transient startup
// probe error that has since recovered doesn't permanently fail us closed.
var availability = _availabilityProvider();

if (!availability.IsAppContainerAvailable)
throw new SandboxUnavailableException(
_availability.UnsupportedReasons.FirstOrDefault() ?? "AppContainer unavailable");
availability.UnsupportedReasons.FirstOrDefault() ?? "AppContainer unavailable");

if (!_availability.IsWxcExecResolvable || string.IsNullOrEmpty(_availability.WxcExecPath))
if (!availability.IsWxcExecResolvable || string.IsNullOrEmpty(availability.WxcExecPath))
throw new SandboxUnavailableException("wxc-exec.exe not found");

var capBytes = request.MaxOutputBytes is > 0 ? request.MaxOutputBytes.Value : DefaultMaxOutputBytes;
Expand All @@ -78,16 +88,16 @@ public async Task<SandboxExecutionResult> ExecuteAsync(
: config.Process.Cwd;

WarnIfUnsupportedVolume(config);
LogConfig(config, configJson, request);
LogConfig(config, configJson, request, availability.WxcExecPath);

MxcExecutor executor;
try
{
executor = new MxcExecutor(_availability.WxcExecPath, stdoutCapBytes: capInt, stderrCapBytes: capInt);
executor = new MxcExecutor(availability.WxcExecPath, stdoutCapBytes: capInt, stderrCapBytes: capInt);
}
catch (FileNotFoundException ex)
{
throw new SandboxUnavailableException($"wxc-exec.exe not found at {_availability.WxcExecPath}", ex);
throw new SandboxUnavailableException($"wxc-exec.exe not found at {availability.WxcExecPath}", ex);
}

// Local timeout + caller cancellation. Mirror the builder's
Expand Down Expand Up @@ -178,7 +188,7 @@ private static void TryDeleteDir(string? path)
try { if (Directory.Exists(path)) Directory.Delete(path, recursive: true); } catch { /* best-effort */ }
}

private void LogConfig(MxcConfig config, string configJson, SandboxExecutionRequest request)
private void LogConfig(MxcConfig config, string configJson, SandboxExecutionRequest request, string? wxcExecPath)
{
// Default: redacted summary. Field counts only; no paths, no command line,
// no env values. Useful for verifying Sandbox UI settings round-tripped
Expand All @@ -190,13 +200,13 @@ private void LogConfig(MxcConfig config, string configJson, SandboxExecutionRequ

var summary =
"[mxc] wxc-exec config (redacted) " +
$"wxcExec={_availability.WxcExecPath}; configBytes={Encoding.UTF8.GetByteCount(configJson)}; " +
$"wxcExec={wxcExecPath}; configBytes={Encoding.UTF8.GetByteCount(configJson)}; " +
$"containerId={config.ContainerId}; version={config.Version}; " +
$"commandLineLength={config.Process.CommandLine?.Length ?? 0}; " +
$"cwd={(string.IsNullOrEmpty(config.Process.Cwd) ? "<null>" : "<set>")}; " +
$"envKeys=[{string.Join(",", envKeys)}]; " +
$"timeoutMs={config.Process.TimeoutMs?.ToString() ?? "<null>"}; " +
$"capabilities=[{string.Join(",", config.AppContainer?.Capabilities ?? Array.Empty<string>())}]; " +
$"capabilities=[{string.Join(",", config.ProcessContainer?.Capabilities ?? Array.Empty<string>())}]; " +
$"readonlyCount={config.Filesystem?.ReadonlyPaths?.Length ?? 0}; " +
$"readwriteCount={config.Filesystem?.ReadwritePaths?.Length ?? 0}; " +
$"deniedCount={config.Filesystem?.DeniedPaths?.Length ?? 0}; " +
Expand Down
Loading
Loading