agent_first_http/cli/cmd/
profile.rs1use std::path::PathBuf;
4
5use clap::{Args as ClapArgs, Subcommand};
6
7use crate::cli::output;
8use crate::sdk::profile;
9use crate::shared::error::Error;
10use crate::shared::time::parse_duration;
11
12#[derive(ClapArgs, Debug)]
13pub struct Args {
14 #[command(subcommand)]
15 pub sub: ProfileSub,
16}
17
18#[derive(Subcommand, Debug)]
19pub enum ProfileSub {
20 List(ListArgs),
22 Info(InfoArgs),
24 LockStatus(InfoArgs),
26 Downloads(InfoArgs),
28 Delete(DeleteArgs),
30 Prune(PruneArgs),
32 Cookies(InfoArgs),
34}
35
36#[derive(ClapArgs, Debug)]
37pub struct ListArgs {
38 #[arg(long)]
40 pub profile_root: Option<PathBuf>,
41}
42
43#[derive(ClapArgs, Debug)]
44pub struct InfoArgs {
45 pub name: String,
47 #[arg(long)]
49 pub profile_root: Option<PathBuf>,
50}
51
52#[derive(ClapArgs, Debug)]
53pub struct DeleteArgs {
54 pub name: String,
56 #[arg(long)]
58 pub confirm: String,
59 #[arg(long)]
61 pub profile_root: Option<PathBuf>,
62}
63
64#[derive(ClapArgs, Debug)]
65pub struct PruneArgs {
66 #[arg(long)]
68 pub older_than: String,
69 #[arg(long, default_value_t = false)]
71 pub dry_run: bool,
72 #[arg(long)]
74 pub profile_root: Option<PathBuf>,
75}
76
77pub async fn run(args: Args) -> Result<(), Error> {
78 match args.sub {
79 ProfileSub::List(a) => {
80 let root = profile_root_for_output(a.profile_root.as_deref());
81 let entries = profile::list(a.profile_root.as_deref())?;
82 output::emit(
83 "profile_list",
84 &serde_json::json!({
85 "profile_root": root.display().to_string(),
86 "profiles": entries,
87 }),
88 )
89 }
90 ProfileSub::Info(a) => {
91 let entry = profile::info(&a.name, a.profile_root.as_deref())?;
92 output::emit("profile_info", &entry)
93 }
94 ProfileSub::LockStatus(a) => {
95 let status = profile::lock_status(&a.name, a.profile_root.as_deref())?;
96 output::emit("profile_lock_status", &status)
97 }
98 ProfileSub::Downloads(a) => {
99 let entry = profile::info(&a.name, a.profile_root.as_deref())?;
100 let download_dir = entry.path.join("downloads");
101 let download_dir = download_dir.canonicalize().unwrap_or(download_dir);
102 let downloads = profile::downloads(&a.name, a.profile_root.as_deref())?;
103 output::emit(
104 "profile_downloads",
105 &serde_json::json!({
106 "name": a.name,
107 "download_dir": download_dir.display().to_string(),
108 "downloads": downloads,
109 }),
110 )
111 }
112 ProfileSub::Delete(a) => {
113 profile::delete(&a.name, &a.confirm, a.profile_root.as_deref())?;
114 output::emit(
115 "profile_delete",
116 &serde_json::json!({"name": a.name, "deleted": true}),
117 )
118 }
119 ProfileSub::Prune(a) => {
120 let root = profile_root_for_output(a.profile_root.as_deref());
121 let older_than = parse_duration(&a.older_than)?;
122 let removed = profile::prune(older_than, a.dry_run, a.profile_root.as_deref())?;
123 output::emit(
124 "profile_prune",
125 &serde_json::json!({
126 "profile_root": root.display().to_string(),
127 "dry_run": a.dry_run,
128 "profiles": removed,
129 }),
130 )
131 }
132 ProfileSub::Cookies(a) => {
133 let entry = profile::info(&a.name, a.profile_root.as_deref())?;
134 let jar_path = entry.path.join("cookies.jar.json");
135 let jar = crate::sdk::profile::cookie_jar::CookieJar::load(&jar_path)?;
136 let cookies = jar.cookies_redacted();
137 output::emit(
138 "profile_cookies",
139 &serde_json::json!({
140 "name": a.name,
141 "jar_path": jar_path.display().to_string(),
142 "count": cookies.len(),
143 "cookies": cookies,
144 }),
145 )
146 }
147 }
148}
149
150fn profile_root_for_output(root: Option<&std::path::Path>) -> PathBuf {
151 root.map(PathBuf::from)
152 .unwrap_or_else(profile::paths::default_root)
153}