diff options
| author | tanaka takashi <isazaka@gmail.com> | 2026-08-12 05:44:54 +0900 |
|---|---|---|
| committer | tanaka takashi <isazaka@gmail.com> | 2026-08-12 05:44:54 +0900 |
| commit | b56614c814b904c224315a11f9686f0d47abd3d3 (patch) | |
| tree | e6ae4feb852ea4492bf947eabba1e11ab157686a /src/Agent.App/Cli/CommandLine.cs | |
| parent | 98b6a2aeedd591058c1d81e401d24f3ded162926 (diff) | |
feat: revise sandbox usability around semantic policy and LPAC process trees
Rewrite the specification and design vocabulary around User Sandbox Configuration, Environment Resolution, Executable Approval, Effective Sandbox Policy, and Windows Enforcement. Move the Internal Tool catalog to the design without treating its count as a requirement, and update README, implementation planning, traceability, audit history, prompts, and the Python skill.
Introduce schema_version 1 profiles, workspaces, mounts, executable aliases, network modes, and resource presets. Add local override merging, resolution and approval stores with atomic user-only persistence, fail-closed fingerprints, localized diagnostics, and executable/sandbox management CLI commands.
Change Windows execution to LPAC entry approval with inherited descendant confinement. Separate read and execute ACLs, preserve protected OS DACLs, apply Job process/memory/timeout limits to the tree, enforce WFP network modes, prevent credential environment inheritance, disable out-of-tree MSBuild reuse, and harden transactional ACL/recovery cleanup.
Isolate planning prompts from execution instructions, reject split coding plans that cannot satisfy task-scoped evidence, preserve configured thinking, and prevent optional task_update failures from corrupting completion checks.
Add and update configuration, resolution, approval, LPAC, descendant, readonly/development, Registry/COM, WFP, recovery, .NET/Python/PowerShell process-tree, and Operational E2E coverage. Publish reproducible bundles while preserving deployment-local configuration, prompts, skills, logs, state, approvals, and resolutions.
Diffstat (limited to 'src/Agent.App/Cli/CommandLine.cs')
| -rw-r--r-- | src/Agent.App/Cli/CommandLine.cs | 102 |
1 files changed, 84 insertions, 18 deletions
diff --git a/src/Agent.App/Cli/CommandLine.cs b/src/Agent.App/Cli/CommandLine.cs index 3fdac9f..3b3fde1 100644 --- a/src/Agent.App/Cli/CommandLine.cs +++ b/src/Agent.App/Cli/CommandLine.cs @@ -1,38 +1,104 @@ namespace Agent.App.Cli; -public enum AppCommand { Serve, Check } +public enum AppCommand +{ + Serve, + Check, + ExecutableResolve, + ExecutableApprove, + ExecutableList, + ExecutableRevoke, + SandboxResolve, + SandboxConfirm, + SandboxShow +} -public sealed record CommandLine(AppCommand Command, string? ConfigPath, string? Language, string? Transport) +public sealed record CommandLine( + AppCommand Command, + string? ConfigPath, + string? Language, + string? Transport, + string? Alias = null, + string? ExecutablePath = null, + string? RuntimeRoot = null, + bool NoRuntime = false, + bool Yes = false, + string Format = "text") { public static CommandLine Parse(string[] args) { bool implicitServe = args.Length == 0 || args[0].StartsWith("--", StringComparison.Ordinal); - AppCommand command = implicitServe ? AppCommand.Serve : args[0] switch + int index = implicitServe ? 0 : 1; + AppCommand command; + string? alias = null; + if (implicitServe) command = AppCommand.Serve; + else if (args[0] == "serve") command = AppCommand.Serve; + else if (args[0] == "check") command = AppCommand.Check; + else if (args[0] is "executable" or "sandbox") { - "serve" => AppCommand.Serve, - "check" => AppCommand.Check, - _ => throw new CommandLineException("Expected 'serve' or 'check'.") - }; + if (index >= args.Length) throw new CommandLineException($"Expected a subcommand after '{args[0]}'."); + string subcommand = args[index++]; + command = (args[0], subcommand) switch + { + ("executable", "resolve") => AppCommand.ExecutableResolve, + ("executable", "approve") => AppCommand.ExecutableApprove, + ("executable", "list") => AppCommand.ExecutableList, + ("executable", "revoke") => AppCommand.ExecutableRevoke, + ("sandbox", "resolve") => AppCommand.SandboxResolve, + ("sandbox", "confirm") => AppCommand.SandboxConfirm, + ("sandbox", "show") => AppCommand.SandboxShow, + _ => throw new CommandLineException($"Unknown subcommand '{args[0]} {subcommand}'.") + }; + if (command is AppCommand.ExecutableResolve or AppCommand.ExecutableApprove or AppCommand.ExecutableRevoke) + { + if (index >= args.Length || args[index].StartsWith("--", StringComparison.Ordinal)) + throw new CommandLineException($"Executable Alias is required for '{args[0]} {subcommand}'."); + alias = args[index++]; + } + } + else throw new CommandLineException("Expected 'serve', 'check', 'executable', or 'sandbox'."); string? config = null; string? language = null; string? transport = null; - for (int i = implicitServe ? 0 : 1; i < args.Length; i++) + string? executablePath = null; + string? runtimeRoot = null; + bool noRuntime = false; + bool yes = false; + string format = "text"; + while (index < args.Length) { - string option = args[i]; - if (i + 1 >= args.Length) throw new CommandLineException($"Missing value for '{option}'."); - string value = args[++i]; + string option = args[index++]; switch (option) { - case "--config" when config is null: config = value; break; - case "--lang" when language is null: language = value; break; - case "--transport" when command == AppCommand.Serve && transport is null && value is "stdio" or "http": transport = value; break; - case "--transport" when command == AppCommand.Check: throw new CommandLineException("--transport is only valid for serve."); - case "--transport": throw new CommandLineException("Transport must be stdio or http."); - default: throw new CommandLineException($"Unknown or duplicate option '{option}'."); + case "--yes" when !yes: yes = true; break; + case "--no-runtime" when !noRuntime && runtimeRoot is null: noRuntime = true; break; + case "--config" when config is null: config = Value(option); break; + case "--lang" when language is null: language = Value(option); break; + case "--transport" when command == AppCommand.Serve && transport is null: + transport = Value(option); + if (transport is not ("stdio" or "http")) throw new CommandLineException("Transport must be stdio or http."); + break; + case "--path" when executablePath is null: executablePath = Value(option); break; + case "--runtime-root" when runtimeRoot is null && !noRuntime: runtimeRoot = Value(option); break; + case "--format" when command == AppCommand.SandboxShow: + format = Value(option); + if (format is not ("text" or "json")) throw new CommandLineException("Format must be text or json."); + break; + default: throw new CommandLineException($"Unknown, duplicate, or invalid option '{option}' for this command."); } } - return new CommandLine(command, config, language, transport); + bool management = command is not (AppCommand.Serve or AppCommand.Check); + if (management && config is null) throw new CommandLineException("--config <file> is required for executable and sandbox commands."); + if (yes && command is not (AppCommand.ExecutableApprove or AppCommand.ExecutableRevoke or AppCommand.SandboxConfirm)) + throw new CommandLineException("--yes is valid only for approve, revoke, and confirm."); + return new(command, config, language, transport, alias, executablePath, runtimeRoot, noRuntime, yes, format); + + string Value(string optionName) + { + if (index >= args.Length) throw new CommandLineException($"Missing value for '{optionName}'."); + return args[index++]; + } } } |
