use super::*;
#[derive(Parser)]
#[command(
name = "sac",
about = "agent",
after_help = "Commands:\n sac resume [SESSION_ID] Continue a saved session\n sac config [COMMAND] Manage sac configuration\n sac codex-auth [COMMAND] Manage ChatGPT Codex auth\n sac upgrade Reinstall the latest sac release"
)]
pub(super) struct RunCli {
#[arg(short = 'C', long)]
pub(super) directory: Option<PathBuf>,
#[command(flatten)]
pub(super) store: StoreArgs,
#[command(flatten)]
pub(super) ui: UiArgs,
#[command(flatten)]
pub(super) model: ModelArgs,
#[command(flatten)]
pub(super) sandbox: SandboxArgs,
}
#[derive(Parser)]
#[command(
name = "sac __worker",
about = "internal managed worker dispatch",
hide = true
)]
pub(super) struct ManagedWorkerCli {
#[command(flatten)]
pub(super) dispatch: WorkerDispatchArgs,
#[command(flatten)]
pub(super) store: StoreArgs,
#[command(flatten)]
pub(super) model: ModelArgs,
#[command(flatten)]
pub(super) sandbox: SandboxArgs,
}
#[derive(clap::Args)]
pub(super) struct StoreArgs {
#[arg(long)]
pub(super) store_path: Option<PathBuf>,
}
#[derive(clap::Args)]
pub(super) struct UiArgs {
#[arg(long, conflicts_with = "full")]
pub(super) compact: bool,
#[arg(long, conflicts_with = "compact")]
pub(super) full: bool,
}
#[derive(clap::Args, Default, Clone)]
pub(super) struct ModelArgs {
#[arg(long, value_enum)]
pub(super) backend: Option<BackendKind>,
#[arg(long = "effort", value_parser = parse_reasoning_effort_cli)]
pub(super) reasoning_effort: Option<ReasoningEffort>,
#[arg(long = "reasoning-summary", value_parser = parse_reasoning_summary_cli)]
pub(super) reasoning_summary: Option<ReasoningSummary>,
#[arg(long = "reasoning-context", value_parser = parse_reasoning_context_cli)]
pub(super) reasoning_context: Option<ReasoningContext>,
#[arg(long, hide = true)]
pub(super) api_base_url: Option<String>,
#[arg(long, hide = true)]
pub(super) api_model: Option<String>,
}
#[derive(clap::Args)]
pub(super) struct WorkerDispatchArgs {
#[arg(long)]
pub(super) session_id: String,
#[arg(long)]
pub(super) thread_name: String,
#[arg(long)]
pub(super) action: String,
#[arg(long = "source-thread")]
pub(super) source_threads: Vec<String>,
}
#[derive(clap::Args)]
pub(super) struct SandboxArgs {
#[arg(long)]
pub(super) sandbox: bool,
#[arg(long)]
pub(super) no_mount_cwd: bool,
#[arg(long = "mount")]
pub(super) mounts: Vec<String>,
#[arg(long = "mount-ro")]
pub(super) mounts_ro: Vec<String>,
#[arg(long)]
pub(super) sandbox_image: Option<String>,
#[arg(long = "sandbox-gpu")]
pub(super) sandbox_gpus: Vec<String>,
#[arg(long = "sandbox-shm-size")]
pub(super) sandbox_shm_size: Option<String>,
#[arg(long, hide = true)]
pub(super) sandbox_session_key: Option<String>,
#[arg(long, hide = true)]
pub(super) sandbox_workdir: Option<String>,
}
#[derive(Parser)]
#[command(name = "sac resume", about = "resume saved sac sessions")]
pub(super) struct ResumeCli {
pub(super) session_id: Option<String>,
#[arg(long)]
pub(super) last: bool,
#[arg(short = 'C', long)]
pub(super) directory: Option<PathBuf>,
#[command(flatten)]
pub(super) store: StoreArgs,
#[command(flatten)]
pub(super) ui: UiArgs,
#[command(flatten)]
pub(super) model: ModelArgs,
}
#[derive(Parser)]
#[command(name = "sac config", about = "manage sac configuration")]
pub(super) struct ConfigCli {
#[command(subcommand)]
pub(super) command: Option<ConfigCommand>,
}
#[derive(Subcommand)]
pub(super) enum ConfigCommand {
Init,
Path,
LogPath,
Logs,
TailLog,
Doctor,
Show,
Reload,
}
#[derive(Parser)]
#[command(name = "sac codex-auth", about = "manage ChatGPT Codex auth")]
pub(super) struct CodexAuthCli {
#[command(subcommand)]
pub(super) command: Option<CodexAuthCommand>,
}
#[derive(Subcommand)]
pub(super) enum CodexAuthCommand {
Login {
#[arg(long)]
headless: bool,
#[arg(long, conflicts_with_all = ["headless", "with_access_token"])]
with_api_key: bool,
#[arg(long, conflicts_with_all = ["headless", "with_api_key"])]
with_access_token: bool,
},
Status,
Logout,
}
#[derive(Parser)]
#[command(name = "sac upgrade", about = "reinstall the latest sac release")]
pub(super) struct UpgradeCli {
#[arg(long)]
pub(super) install_dir: Option<PathBuf>,
}
pub(super) enum ParsedCli {
Run(RunCli),
ManagedWorker(ManagedWorkerCli),
Resume(ResumeCli),
Config(ConfigCli),
CodexAuth(CodexAuthCli),
Upgrade(UpgradeCli),
}
pub(super) fn parse_cli() -> ParsedCli {
let args: Vec<OsString> = std::env::args_os().collect();
parse_cli_from(args)
}
pub(super) fn parse_cli_from(args: Vec<OsString>) -> ParsedCli {
if args
.get(1)
.is_some_and(|value| value == OsStr::new("resume"))
{
let mut resume_args = Vec::with_capacity(args.len().saturating_sub(1));
resume_args.push(OsString::from("sac resume"));
resume_args.extend(args.into_iter().skip(2));
ParsedCli::Resume(ResumeCli::parse_from(resume_args))
} else if args
.get(1)
.is_some_and(|value| value == OsStr::new("__worker"))
{
let mut worker_args = Vec::with_capacity(args.len().saturating_sub(1));
worker_args.push(OsString::from("sac __worker"));
worker_args.extend(args.into_iter().skip(2));
ParsedCli::ManagedWorker(ManagedWorkerCli::parse_from(worker_args))
} else if args
.get(1)
.is_some_and(|value| value == OsStr::new("codex-auth"))
{
let mut codex_auth_args = Vec::with_capacity(args.len().saturating_sub(1));
codex_auth_args.push(OsString::from("sac codex-auth"));
codex_auth_args.extend(args.into_iter().skip(2));
ParsedCli::CodexAuth(CodexAuthCli::parse_from(codex_auth_args))
} else if args
.get(1)
.is_some_and(|value| value == OsStr::new("config"))
{
let mut config_args = Vec::with_capacity(args.len().saturating_sub(1));
config_args.push(OsString::from("sac config"));
config_args.extend(args.into_iter().skip(2));
ParsedCli::Config(ConfigCli::parse_from(config_args))
} else if args
.get(1)
.is_some_and(|value| value == OsStr::new("upgrade"))
{
let mut upgrade_args = Vec::with_capacity(args.len().saturating_sub(1));
upgrade_args.push(OsString::from("sac upgrade"));
upgrade_args.extend(args.into_iter().skip(2));
ParsedCli::Upgrade(UpgradeCli::parse_from(upgrade_args))
} else {
ParsedCli::Run(RunCli::parse_from(args))
}
}
fn parse_reasoning_effort_cli(s: &str) -> Result<ReasoningEffort, String> {
s.parse()
}
fn parse_reasoning_summary_cli(s: &str) -> Result<ReasoningSummary, String> {
match s {
"auto" => Ok(ReasoningSummary::Auto),
"concise" => Ok(ReasoningSummary::Concise),
"detailed" => Ok(ReasoningSummary::Detailed),
other => Err(format!(
"invalid reasoning summary '{}'; expected: auto, concise, detailed",
other
)),
}
}
fn parse_reasoning_context_cli(s: &str) -> Result<ReasoningContext, String> {
match s {
"current_turn" => Ok(ReasoningContext::CurrentTurn),
"all_turns" => Ok(ReasoningContext::AllTurns),
other => Err(format!(
"invalid reasoning context '{}'; expected: current_turn, all_turns",
other
)),
}
}