Fremde Laufzeiten: Claude Code und Befehl in der Sandbox, Egress-Proxy, Vorschlag statt Einsatz (#712)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
7d6c2c9495
commit
367ea387f6
@@ -0,0 +1,70 @@
|
||||
using ClawdDotNet.Core.Engine;
|
||||
using ClawdDotNet.Core.Sandbox;
|
||||
|
||||
namespace ClawdDotNet.Core.Runtimes;
|
||||
|
||||
/// <summary>
|
||||
/// #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
|
||||
/// <c>CLAWD_BRIEFING_FILE</c>; what the program prints is the result. A non-zero exit code
|
||||
/// is a failure, with the end of stderr as its reason.
|
||||
///
|
||||
/// <para>The program can reach the MCP server itself: <c>$CLAWD_HELPER --mcp-bridge --socket
|
||||
/// $CLAWD_MCP_SOCKET</c> with the token file in <c>CLAWD_MCP_TOKEN_FILE</c>.</para>
|
||||
/// </summary>
|
||||
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<string>();
|
||||
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<string> 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 };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user