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