Repository navigation
feat: add JSON/YAML output format to provider list command #1745
Description
Problem Statement
The openshell provider list command currently only supports table output and a --names flag for line-delimited names. Other list commands in the CLI (sandbox list, provider list-profiles) support structured output formats (JSON, YAML) via the -o/--output flag, enabling programmatic consumption and integration with other tools. Provider list should follow this established pattern for consistency and usability.
Proposed Design
Add an -o/--output flag to openshell provider list with support for table (default), json, and yaml formats, following the existing OutputFormat enum pattern used by other commands.
CLI Changes
Command definition (crates/openshell-cli/src/main.rs:764-778):
List {
/// Maximum number of providers to return.
#[arg(long, default_value_t = 100)]
limit: u32,
/// Offset into the provider list.
#[arg(long, default_value_t = 0)]
offset: u32,
/// Print only provider names, one per line.
#[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,
}Function signature (crates/openshell-cli/src/run.rs:4618):
pub async fn provider_list(
server: &str,
limit: u32,
offset: u32,
names_only: bool,
output: &str, // new parameter
tls: &TlsOptions,
) -> Result<()>Implementation pattern (similar to sandbox_list at run.rs:3162-3242):
match output {
"json" => {
let items: Vec<serde_json::Value> = providers.iter().map(provider_to_json).collect();
println!("{}", serde_json::to_string_pretty(&items).into_diagnostic()?);
return Ok(());
}
"yaml" => {
let items: Vec<serde_json::Value> = providers.iter().map(provider_to_json).collect();
print!("{}", serde_yml::to_string(&items).into_diagnostic()?);
return Ok(());
}
"table" => {} // existing table rendering
_ => return Err(miette!("unsupported output format: {output}")),
}Helper function (new):
fn provider_to_json(provider: &Provider) -> serde_json::Value {
use serde_json::json;
let mut obj = serde_json::Map::new();
// Always include core fields
obj.insert("id".to_string(), json!(provider.object_id()));
obj.insert("name".to_string(), json!(provider.object_name()));
obj.insert("type".to_string(), 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(), json!(credential_keys));
// Config (non-secret configuration)
if !provider.config.is_empty() {
obj.insert("config".to_string(), 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(), json!(meta.labels));
}
if meta.resource_version != 0 {
obj.insert("resource_version".to_string(), json!(meta.resource_version));
}
if meta.created_at_ms != 0 {
obj.insert("created_at_ms".to_string(), 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(), json!(provider.credential_expires_at_ms));
}
serde_json::Value::Object(obj)
}Security Considerations
- MUST NOT expose credential values in JSON/YAML output
- Only include credential key names (verified:
provider getcommand already follows this pattern at run.rs:4583) - Config values are safe to expose (non-secret configuration)
Testing
-
Unit tests (add to
main.rs, following sandbox_list pattern at 3716-3745):provider_list_accepts_output_json()provider_list_accepts_output_yaml()provider_list_output_conflicts_with_names()
-
Integration test (update
provider_commands_integration.rs:1000):// Test JSON output run::provider_list(&ts.endpoint, 100, 0, false, "json", &ts.tls) .await .expect("provider list json");
Alternatives Considered
-
Separate command (
provider list-json): Rejected. Breaks CLI consistency and requires users to remember different commands. -
Always include JSON in table output: Rejected. Noisy for interactive use; structured formats should be opt-in.
-
Use
--formatinstead of--output: Rejected. Inconsistent with existingsandbox listandprovider list-profilescommands that use-o/--output. -
Include optional fields with default values: Rejected. Cleaner JSON when absent fields are omitted entirely.
Agent Investigation
Codebase exploration:
- Located provider list implementation at
crates/openshell-cli/src/main.rs:764-778andcrates/openshell-cli/src/run.rs:4618-4677 - Confirmed
OutputFormatenum exists (Table, Yaml, Json) atmain.rs:653-657 - Verified pattern consistency with
sandbox list(main.rs:1299-1324, run.rs:3162-3275) - Verified pattern consistency with
provider list-profiles(main.rs:780-786, run.rs:4680-4730) - Confirmed security model:
provider getonly exposes credential keys, not values (run.rs:4583) - Identified reusable helpers: JSON/YAML serialization pattern
Files to modify:
crates/openshell-cli/src/main.rs- Addoutputfield toProviderCommands::List, add unit testscrates/openshell-cli/src/run.rs- Addoutputparameter toprovider_list(), implement format switching, addprovider_to_json()helpercrates/openshell-cli/tests/provider_commands_integration.rs- Update test call signature
Impact:
- Low risk: follows established patterns
- No breaking changes: default behavior (table output) unchanged
- Enables programmatic usage (jq, yq, CI scripts)
Activity
johntmyers commentedon Jun 4, 2026
Can we update this to audit all CLI commands that need this and provide a common solution that doesn't duplicate code and allows all commands present and future to pick up this behavior easily?
jeffmaury commentedon Jun 4, 2026
Created #1750
jeffmaury commentedon Jun 9, 2026
Implementation Plan
Issue type:
featComplexity: Low
Confidence: High — pattern is proven in production, generic helpers already exist
Summary
Add
-o/--outputflag support (json, yaml, table) toopenshell provider listby migrating it to use the existing generic output formatter introduced in #1750. This follows the established pattern already used bysandbox list,gateway list, andprovider list-profilescommands.Scope
crates/openshell-cli/src/main.rs: Addoutputfield toProviderCommands::List, addconflicts_withconstraint, update invocationcrates/openshell-cli/src/run.rs: Addoutputparameter toprovider_list(), addprovider_to_json()helper, insert early-return for structured formatscrates/openshell-cli/tests/provider_commands_integration.rs: Update test call signatureImplementation Steps
Add
provider_to_json()helper (run.rs:~4720)Update
provider_list()function (run.rs:4721-4780)output: &strparameter to signaturecrate::output::print_output_collection(output, &providers, provider_to_json)?early-return after fetching providersUpdate CLI definition (main.rs:764-778, ~880-885)
output: OutputFormatfield toProviderCommands::Liststructconflicts_with = "names"to output fieldconflicts_with = "output"to names fieldoutput.as_str()Update integration test (provider_commands_integration.rs:~1000)
provider_list()call to include"table"parameterManual testing and security verification
-o json,-o yaml,-o table,--names--namesand-oconflictTest Plan
Risks & Open Questions
Low risk: Follows established pattern used by sandbox_list, gateway_list, provider_list_profiles. Generic output helpers are already tested and in production use. No breaking changes (default behavior unchanged).
Security validation required: Before merging, manually inspect JSON/YAML output to confirm credential values are never exposed (only keys should appear). Compare with
provider getcommand output which already follows this pattern (run.rs:4686).Documentation Impact
None expected — this change affects CLI command output format only, not gateway configuration or published user-facing behavior documentation.
Revision 1 — initial plan
jeffmaury commentedon Jun 9, 2026
Implementation Complete
PR: #1830
What was built
Added
-o/--outputflag support (json, yaml, table) toopenshell provider listusing the existing generic output formatter from #1750. Follows the same pattern assandbox list,gateway list, andprovider list-profiles.Tests
Security
The
provider_to_json()helper only exposes credential keys, never values, preventing CWE-532.Docs updated
None needed — CLI output format change only, no gateway config or user-facing behavior documentation required.
The issue will auto-close when the PR is merged.
feat(cli): add JSON/YAML output format to provider list command
feat(cli): add JSON/YAML output format to provider list command (#1830)