Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions crates/openshell-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -773,8 +773,12 @@ enum ProviderCommands {
offset: u32,

/// Print only provider names, one per line.
#[arg(long)]
#[arg(long, conflicts_with = "output")]
names: bool,

/// Output format.
#[arg(short = 'o', long = "output", value_enum, default_value_t = OutputFormat::Table, conflicts_with = "names")]
output: OutputFormat,
},

/// List available provider profiles.
Expand Down Expand Up @@ -2906,8 +2910,10 @@ async fn main() -> Result<()> {
limit,
offset,
names,
output,
} => {
run::provider_list(endpoint, limit, offset, names, &tls).await?;
run::provider_list(endpoint, limit, offset, names, output.as_str(), &tls)
.await?;
}
ProviderCommands::ListProfiles { output } => {
run::provider_list_profiles(endpoint, output.as_str(), &tls).await?;
Expand Down
59 changes: 59 additions & 0 deletions crates/openshell-cli/src/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4718,11 +4718,65 @@ pub async fn provider_get(server: &str, name: &str, tls: &TlsOptions) -> Result<
Ok(())
}

fn provider_to_json(provider: &Provider) -> serde_json::Value {
let mut obj = serde_json::Map::new();

// Core fields
obj.insert("id".to_string(), serde_json::json!(provider.object_id()));
obj.insert(
"name".to_string(),
serde_json::json!(provider.object_name()),
);
obj.insert("type".to_string(), serde_json::json!(provider.r#type));

// Credential keys (NEVER values - security)
let credential_keys: Vec<String> = provider.credentials.keys().cloned().collect();
obj.insert(
"credential_keys".to_string(),
serde_json::json!(credential_keys),
);

// Config (non-secret configuration)
if !provider.config.is_empty() {
obj.insert("config".to_string(), serde_json::json!(provider.config));
}

// Metadata fields (only if metadata exists)
if let Some(meta) = &provider.metadata {
if !meta.labels.is_empty() {
obj.insert("labels".to_string(), serde_json::json!(meta.labels));
}
if meta.resource_version != 0 {
obj.insert(
"resource_version".to_string(),
serde_json::json!(meta.resource_version),
);
}
if meta.created_at_ms != 0 {
obj.insert(
"created_at_ms".to_string(),
serde_json::json!(meta.created_at_ms),
);
}
}

// Credential expiration times (only if present)
if !provider.credential_expires_at_ms.is_empty() {
obj.insert(
"credential_expires_at_ms".to_string(),
serde_json::json!(provider.credential_expires_at_ms),
);
}

serde_json::Value::Object(obj)
}

pub async fn provider_list(
server: &str,
limit: u32,
offset: u32,
names_only: bool,
output: &str,
tls: &TlsOptions,
) -> Result<()> {
let mut client = grpc_client(server, tls).await?;
Expand All @@ -4732,6 +4786,11 @@ pub async fn provider_list(
.into_diagnostic()?;
let providers = response.into_inner().providers;

// Handle structured output formats (json, yaml)
if crate::output::print_output_collection(output, &providers, provider_to_json)? {
return Ok(());
}

if providers.is_empty() {
if !names_only {
println!("No providers found.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -997,7 +997,7 @@ async fn provider_cli_run_functions_support_full_crud_flow() {
run::provider_get(&ts.endpoint, "my-claude", &ts.tls)
.await
.expect("provider get");
run::provider_list(&ts.endpoint, 100, 0, false, &ts.tls)
run::provider_list(&ts.endpoint, 100, 0, false, "table", &ts.tls)
.await
.expect("provider list");

Expand Down
Loading