use claude_runner_core::{ ClaudeCommand, EffortLevel, IsolatedModel, REFRESH_DEFAULT_MODEL, RunnerError, run_isolated };
fn emit_credential_trace
(
label : &str,
creds_path : &str,
model : &IsolatedModel,
args : &[ String ],
timeout_secs : u64,
)
{
let temp_dir = std::env::temp_dir()
.join( format!( "claude_isolated_{}", std::process::id() ) );
let mut full_args = Vec::with_capacity( args.len() + 2 );
if let Some( id ) = model.model_id()
{
full_args.push( "--model".to_string() );
full_args.push( id.to_string() );
}
full_args.extend_from_slice( args );
let preview = ClaudeCommand::new()
.with_home( &temp_dir )
.with_args( full_args.iter().cloned() );
let env_out = preview.describe_env();
let cmd_out = preview.describe();
eprintln!( "# clr {label}" );
eprintln!( "# creds: {creds_path}" );
eprintln!( "# timeout: {timeout_secs}s" );
if !env_out.is_empty() { eprintln!( "{env_out}" ); }
eprintln!( "{cmd_out}" );
}
#[ allow( clippy::too_many_arguments ) ]
pub( super ) fn run_isolated_command
(
label : &str,
creds_path : &str,
timeout_secs : u64,
trace : bool,
model : IsolatedModel,
effort : EffortLevel,
message : Option< &str >,
passthrough_args : &[ String ],
skip_perms : bool,
no_chrome : bool,
) -> !
{
let mut args : Vec< String > = Vec::new();
if no_chrome { args.push( "--no-chrome".to_string() ); }
args.push( "--effort".to_string() );
args.push( effort.as_str().to_string() );
args.push( "--no-session-persistence".to_string() );
if skip_perms { args.push( "--dangerously-skip-permissions".to_string() ); }
if let Some( m ) = message
{
args.push( "--print".to_string() );
args.push( m.to_string() );
}
args.extend_from_slice( passthrough_args );
if trace { emit_credential_trace( label, creds_path, &model, &args, timeout_secs ); }
let creds_json = match std::fs::read_to_string( creds_path )
{
Ok( s ) => s,
Err( e ) =>
{
eprintln!( "Error: cannot read credentials file '{creds_path}': {e}" );
std::process::exit( 1 );
}
};
match run_isolated( &creds_json, args, timeout_secs, model )
{
Ok( result ) =>
{
if let Some( ref new_creds ) = result.credentials
{
if let Err( e ) = std::fs::write( creds_path, new_creds )
{
eprintln!( "Warning: could not write back refreshed credentials to '{creds_path}': {e}" );
}
}
if !result.stderr.is_empty() { eprint!( "{}", result.stderr ); }
if !result.stdout.is_empty() { print!( "{}", result.stdout ); }
let exit_code = if result.exit_code == -1 { 0 } else { result.exit_code };
std::process::exit( exit_code );
}
Err( RunnerError::Timeout { secs } | RunnerError::TimeoutWithOutput { secs, .. } ) =>
{
eprintln!( "Error: {label} subprocess timed out after {secs} seconds" );
std::process::exit( 2 );
}
Err( e ) =>
{
eprintln!( "Error: {e}" );
std::process::exit( 1 );
}
}
}
pub( super ) fn run_refresh_command
(
creds_path : &str,
timeout_secs : u64,
trace : bool,
) -> !
{
run_isolated_command(
"refresh",
creds_path,
timeout_secs,
trace,
IsolatedModel::Specific( REFRESH_DEFAULT_MODEL.to_string() ),
EffortLevel::Low,
Some( "." ),
&[],
false, true, );
}