cellstate/commands/
use_cmd.rs1use crate::session::{self, CliSession};
13use crate::ui;
14use clap::{Parser, Subcommand};
15use console::style;
16
17#[derive(Debug, Parser)]
22pub struct UseArgs {
23 #[arg(long)]
25 pub clear: bool,
26
27 #[command(subcommand)]
28 pub command: Option<UseCommand>,
29}
30
31#[derive(Debug, Subcommand)]
32pub enum UseCommand {
33 Tenant {
35 id: String,
37 },
38 Agent {
40 id: String,
42 },
43 Trajectory {
45 id: String,
47 },
48 Scope {
50 id: String,
52 },
53}
54
55pub fn run_with_json(args: &UseArgs, json: bool) -> anyhow::Result<()> {
60 let mut session = CliSession::load()?;
61
62 if args.clear {
63 session.clear_scope();
64 session.save()?;
65 if json {
66 println!(r#"{{"cleared": true}}"#);
67 } else {
68 ui::ok("Session scope cleared.");
69 }
70 return Ok(());
71 }
72
73 match &args.command {
74 None => {
75 if json {
76 let out = serde_json::json!({
77 "base_url": session.base_url.as_deref().unwrap_or(crate::http::DEFAULT_BASE_URL),
78 "api_key": session.api_key.as_deref().map(mask_api_key),
79 "tenant_id": session.tenant_id,
80 "agent_id": session.agent_id,
81 "trajectory_id": session.trajectory_id,
82 "scope_id": session.scope_id,
83 });
84 println!("{}", serde_json::to_string_pretty(&out).unwrap());
85 } else {
86 print_session(&session);
87 }
88 }
89 Some(cmd) => {
90 let (field, id) = match cmd {
91 UseCommand::Tenant { id } => {
92 session::validate_uuid(id)?;
93 session.tenant_id = Some(id.clone());
94 ("tenant", id.as_str())
95 }
96 UseCommand::Agent { id } => {
97 session::validate_uuid(id)?;
98 session.agent_id = Some(id.clone());
99 ("agent", id.as_str())
100 }
101 UseCommand::Trajectory { id } => {
102 session::validate_uuid(id)?;
103 session.trajectory_id = Some(id.clone());
104 ("trajectory", id.as_str())
105 }
106 UseCommand::Scope { id } => {
107 session::validate_uuid(id)?;
108 session.scope_id = Some(id.clone());
109 ("scope", id.as_str())
110 }
111 };
112 session.save()?;
113 if json {
114 println!(r#"{{"{}": "{}"}}"#, field, id);
115 } else {
116 ui::ok(&format!(
117 "{} set to {}",
118 field.to_uppercase().chars().next().unwrap().to_string() + &field[1..],
119 style(id).cyan()
120 ));
121 }
122 }
123 }
124
125 Ok(())
126}
127
128fn print_session(session: &CliSession) {
133 ui::print_banner();
134 ui::section("Session");
135
136 print_field(
137 "Base URL",
138 session.base_url.as_deref(),
139 Some(crate::http::DEFAULT_BASE_URL),
140 );
141 print_field(
142 "API Key",
143 session.api_key.as_deref().map(mask_api_key).as_deref(),
144 None,
145 );
146
147 ui::section("Scope");
148
149 print_field("Tenant", session.tenant_id.as_deref(), None);
150 print_field("Agent", session.agent_id.as_deref(), None);
151 print_field("Trajectory", session.trajectory_id.as_deref(), None);
152 print_field("Scope", session.scope_id.as_deref(), None);
153
154 println!();
155 println!("{}", style("Set scope: cellstate use tenant <uuid>").dim());
156 println!("{}", style("Clear all: cellstate use --clear").dim());
157 println!();
158}
159
160fn print_field(label: &str, value: Option<&str>, default: Option<&str>) {
161 match value {
162 Some(v) => ui::kv(label, v),
163 None => match default {
164 Some(d) => {
165 println!(
166 " {:<20} {} {}",
167 style(label).dim(),
168 style(d).cyan(),
169 style("(default)").dim()
170 );
171 }
172 None => ui::kv_missing(label),
173 },
174 }
175}
176
177fn mask_api_key(key: &str) -> String {
179 if key.len() <= 8 {
180 return "****".to_string();
181 }
182 let prefix = &key[..4];
183 let suffix = &key[key.len() - 4..];
184 format!("{prefix}...{suffix}")
185}