@@ -4716,14 +4716,16 @@ pub async fn provider_list_profiles(server: &str, output: &str, tls: &TlsOptions
47164716 }
47174717
47184718 println ! ( "{}" , "Available Provider Profiles:" . cyan( ) . bold( ) ) ;
4719+ let id_width = provider_profile_id_width ( & profiles) ;
4720+ let display_width = provider_profile_display_width ( & profiles) ;
47194721 let mut current_category = i32:: MIN ;
47204722 for profile in profiles {
47214723 if profile. category != current_category {
47224724 current_category = profile. category ;
47234725 println ! ( ) ;
47244726 println ! ( " {}" , display_provider_category( current_category) . bold( ) ) ;
47254727 }
4726- print_provider_type_row ( & profile) ;
4728+ print_provider_type_row ( & profile, id_width , display_width ) ;
47274729 }
47284730
47294731 Ok ( ( ) )
@@ -5194,21 +5196,65 @@ fn display_provider_category(category: i32) -> &'static str {
51945196 }
51955197}
51965198
5197- fn print_provider_type_row ( profile : & ProviderProfile ) {
5199+ const PROVIDER_PROFILE_ID_MAX_WIDTH : usize = 32 ;
5200+ const PROVIDER_PROFILE_DISPLAY_MAX_WIDTH : usize = 40 ;
5201+
5202+ fn provider_profile_id_width ( profiles : & [ ProviderProfile ] ) -> usize {
5203+ profiles
5204+ . iter ( )
5205+ . map ( |profile| {
5206+ profile
5207+ . id
5208+ . chars ( )
5209+ . count ( )
5210+ . min ( PROVIDER_PROFILE_ID_MAX_WIDTH )
5211+ } )
5212+ . max ( )
5213+ . unwrap_or ( 2 )
5214+ . max ( 2 )
5215+ }
5216+
5217+ fn provider_profile_display_width ( profiles : & [ ProviderProfile ] ) -> usize {
5218+ profiles
5219+ . iter ( )
5220+ . map ( |profile| {
5221+ profile
5222+ . display_name
5223+ . chars ( )
5224+ . count ( )
5225+ . min ( PROVIDER_PROFILE_DISPLAY_MAX_WIDTH )
5226+ } )
5227+ . max ( )
5228+ . unwrap_or ( 4 )
5229+ . max ( 4 )
5230+ }
5231+
5232+ fn print_provider_type_row ( profile : & ProviderProfile , id_width : usize , display_width : usize ) {
51985233 let inference = if profile. inference_capable {
51995234 " inference"
52005235 } else {
52015236 ""
52025237 } ;
5238+ let id = truncate_display ( & profile. id , PROVIDER_PROFILE_ID_MAX_WIDTH ) ;
5239+ let display_name = truncate_display ( & profile. display_name , PROVIDER_PROFILE_DISPLAY_MAX_WIDTH ) ;
52035240 println ! (
5204- " {:<12} {:<42} endpoints: {:<2}{}" ,
5205- profile. id,
5206- profile. display_name,
5241+ " {id:<id_width$} {display_name:<display_width$} endpoints: {:<2}{}" ,
52075242 profile. endpoints. len( ) ,
52085243 inference
52095244 ) ;
52105245}
52115246
5247+ fn truncate_display ( value : & str , max_width : usize ) -> String {
5248+ if value. chars ( ) . count ( ) <= max_width {
5249+ return value. to_string ( ) ;
5250+ }
5251+
5252+ let keep = max_width. saturating_sub ( 3 ) ;
5253+ let mut truncated = value. chars ( ) . take ( keep) . collect :: < String > ( ) ;
5254+ truncated. push_str ( "..." ) ;
5255+ truncated
5256+ }
5257+
52125258pub async fn provider_update (
52135259 server : & str ,
52145260 name : & str ,
@@ -6553,6 +6599,11 @@ where
65536599 W : Write + Send ,
65546600 E : Write + Send ,
65556601{
6602+ if version == 0 {
6603+ return sandbox_policy_get_effective_to_writer ( server, name, full, output, tls, writers)
6604+ . await ;
6605+ }
6606+
65566607 let ( stdout, stderr) = writers;
65576608 let mut client = grpc_client ( server, tls) . await ?;
65586609
@@ -6622,6 +6673,118 @@ where
66226673 Ok ( ( ) )
66236674}
66246675
6676+ async fn sandbox_policy_get_effective_to_writer < W , E > (
6677+ server : & str ,
6678+ name : & str ,
6679+ full : bool ,
6680+ output : & str ,
6681+ tls : & TlsOptions ,
6682+ writers : ( & mut W , & mut E ) ,
6683+ ) -> Result < ( ) >
6684+ where
6685+ W : Write + Send ,
6686+ E : Write + Send ,
6687+ {
6688+ let ( stdout, _stderr) = writers;
6689+ let mut client = grpc_client ( server, tls) . await ?;
6690+
6691+ let sandbox = client
6692+ . get_sandbox ( GetSandboxRequest {
6693+ name : name. to_string ( ) ,
6694+ } )
6695+ . await
6696+ . into_diagnostic ( ) ?
6697+ . into_inner ( )
6698+ . sandbox
6699+ . ok_or_else ( || miette ! ( "sandbox missing from response" ) ) ?;
6700+ let sandbox_id = sandbox. object_id ( ) ;
6701+ if sandbox_id. is_empty ( ) {
6702+ return Err ( miette ! ( "sandbox missing metadata" ) ) ;
6703+ }
6704+
6705+ let config = client
6706+ . get_sandbox_config ( GetSandboxConfigRequest {
6707+ sandbox_id : sandbox_id. to_string ( ) ,
6708+ } )
6709+ . await
6710+ . into_diagnostic ( ) ?
6711+ . into_inner ( ) ;
6712+ let policy = config
6713+ . policy
6714+ . as_ref ( )
6715+ . ok_or_else ( || miette ! ( "no active policy configured for sandbox '{name}'" ) ) ?;
6716+ let policy_source =
6717+ PolicySource :: try_from ( config. policy_source ) . unwrap_or ( PolicySource :: Sandbox ) ;
6718+ let policy_source_label = match policy_source {
6719+ PolicySource :: Global => "global" ,
6720+ PolicySource :: Sandbox => "sandbox" ,
6721+ PolicySource :: Unspecified => "unspecified" ,
6722+ } ;
6723+ let version = if policy_source == PolicySource :: Global && config. global_policy_version > 0 {
6724+ config. global_policy_version
6725+ } else {
6726+ config. version
6727+ } ;
6728+
6729+ match output {
6730+ "json" => {
6731+ let mut obj = serde_json:: Map :: new ( ) ;
6732+ obj. insert ( "scope" . to_string ( ) , serde_json:: json!( "sandbox" ) ) ;
6733+ obj. insert ( "sandbox" . to_string ( ) , serde_json:: json!( name) ) ;
6734+ obj. insert ( "version" . to_string ( ) , serde_json:: json!( version) ) ;
6735+ obj. insert ( "active_version" . to_string ( ) , serde_json:: json!( version) ) ;
6736+ obj. insert ( "hash" . to_string ( ) , serde_json:: json!( config. policy_hash) ) ;
6737+ obj. insert ( "status" . to_string ( ) , serde_json:: json!( "effective" ) ) ;
6738+ obj. insert (
6739+ "config_revision" . to_string ( ) ,
6740+ serde_json:: json!( config. config_revision) ,
6741+ ) ;
6742+ obj. insert (
6743+ "policy_source" . to_string ( ) ,
6744+ serde_json:: json!( policy_source_label) ,
6745+ ) ;
6746+ if config. global_policy_version > 0 {
6747+ obj. insert (
6748+ "global_policy_version" . to_string ( ) ,
6749+ serde_json:: json!( config. global_policy_version) ,
6750+ ) ;
6751+ }
6752+ if full {
6753+ obj. insert (
6754+ "policy" . to_string ( ) ,
6755+ openshell_policy:: sandbox_policy_to_json_value ( policy) ?,
6756+ ) ;
6757+ }
6758+ writeln ! (
6759+ stdout,
6760+ "{}" ,
6761+ serde_json:: to_string_pretty( & serde_json:: Value :: Object ( obj) ) . into_diagnostic( ) ?
6762+ )
6763+ . into_diagnostic ( ) ?;
6764+ }
6765+ "table" => {
6766+ writeln ! ( stdout, "Version: {version}" ) . into_diagnostic ( ) ?;
6767+ writeln ! ( stdout, "Hash: {}" , config. policy_hash) . into_diagnostic ( ) ?;
6768+ writeln ! ( stdout, "Status: Effective" ) . into_diagnostic ( ) ?;
6769+ writeln ! ( stdout, "Source: {policy_source_label}" ) . into_diagnostic ( ) ?;
6770+ writeln ! ( stdout, "Config rev: {}" , config. config_revision) . into_diagnostic ( ) ?;
6771+ if config. global_policy_version > 0 {
6772+ writeln ! ( stdout, "Global: {}" , config. global_policy_version)
6773+ . into_diagnostic ( ) ?;
6774+ }
6775+ if full {
6776+ writeln ! ( stdout, "---" ) . into_diagnostic ( ) ?;
6777+ let yaml_str = openshell_policy:: serialize_sandbox_policy ( policy)
6778+ . wrap_err ( "failed to serialize policy to YAML" ) ?;
6779+ write ! ( stdout, "{yaml_str}" ) . into_diagnostic ( ) ?;
6780+ }
6781+ }
6782+ _ => return Err ( miette ! ( "unsupported output format: {output}" ) ) ,
6783+ }
6784+
6785+ Ok ( ( ) )
6786+ }
6787+
66256788pub async fn sandbox_policy_get_global (
66266789 server : & str ,
66276790 version : u32 ,
0 commit comments