using ClawdDotNet.Core.Engine; using ClawdDotNet.Core.Sandbox; namespace ClawdDotNet.Core.Runtimes; /// /// #712: A program as the runtime — for special cases, and the way to try the whole path /// without a model. The message comes on stdin, the briefing in the file named by /// CLAWD_BRIEFING_FILE; what the program prints is the result. A non-zero exit code /// is a failure, with the end of stderr as its reason. /// /// The program can reach the MCP server itself: $CLAWD_HELPER --mcp-bridge --socket /// $CLAWD_MCP_SOCKET with the token file in CLAWD_MCP_TOKEN_FILE. /// public sealed class CommandRuntime : IForeignRuntime { public string Kind => RuntimeKinds.Command; public SandboxRequest Prepare(ForeignRunContext context) { var runtime = context.Agent.Runtime; if (string.IsNullOrWhiteSpace(runtime.Command)) throw new ForeignRunException("No command configured for this agent ('runtime.command')."); File.WriteAllText(Path.Combine(context.PrivateDirectory, ForeignRunContext.BriefingFileName), context.Briefing); var command = runtime.Command.Trim(); var readOnly = new List(context.HelperPaths()); if (Path.IsPathRooted(command)) { var directory = Path.GetDirectoryName(command)!; if (!directory.StartsWith("/usr/", StringComparison.Ordinal) && directory != "/bin") readOnly.Add(directory); } else if (command.Contains('/')) { // Relative to the workspace — the run's copy of it. command = SandboxPaths.Work + "/" + command.TrimStart('.', '/'); } return new SandboxRequest { Executable = command, Arguments = runtime.Arguments, WorkspacePath = context.WorkCopy, ProtectedPaths = [], ReadOnlyPaths = readOnly, Mounts = context.Mounts(), Environment = context.McpEnvironment(), Egress = runtime.Egress, StandardInput = context.Message, Limits = ForeignRunner.Limits(runtime) }; } public ForeignRunOutcome Interpret(SandboxResult result, ForeignRunContext context, Action note) { if (result.TimedOut) return new ForeignRunOutcome { Status = AgentRunStatus.Cancelled, Text = "[The command timed out]" + ClaudeCodeRuntime.Tail(result.StandardError) }; if (result.OutputTruncated) return new ForeignRunOutcome { Status = AgentRunStatus.Failed, Text = "[The command produced more output than allowed]" }; if (result.ExitCode != 0) return new ForeignRunOutcome { Status = AgentRunStatus.Failed, Text = $"[The command ended with exit code {result.ExitCode}]" + ClaudeCodeRuntime.Tail(result.StandardError) }; return new ForeignRunOutcome { Status = AgentRunStatus.Completed, Text = result.StandardOutput.Trim(), Turns = 1 }; } }