use unilang::data::{ ErrorCode, ErrorData, OutputData };
use unilang::interpreter::ExecutionContext;
use unilang::semantic::VerifiedCommand;
use unilang::types::Value;
use super::shared::{ is_dry, require_claude_paths, require_credential_store, io_err_to_error_data, resolve_account_name };
#[ inline ]
pub fn account_relogin_routine( cmd : VerifiedCommand, _ctx : ExecutionContext ) -> Result< OutputData, ErrorData >
{
let trace = crate::output::parse_int_flag( &cmd, "trace", 0 )? != 0;
let paths = require_claude_paths()?;
let credential_store = require_credential_store()?;
if trace { eprintln!( "[trace] account.relogin store: {}", credential_store.display() ) }
let raw_name = match cmd.arguments.get( "name" )
{
Some( Value::String( s ) ) if !s.is_empty() => s.clone(),
Some( Value::String( _ ) ) =>
return Err( ErrorData::new(
ErrorCode::ArgumentMissing,
"name:: value cannot be empty".to_string(),
) ),
_ =>
std::fs::read_to_string( credential_store.join( crate::account::active_marker_filename() ) )
.ok()
.map( | s | s.trim().to_string() )
.filter( | s | !s.is_empty() )
.ok_or_else( || ErrorData::new(
ErrorCode::InternalError,
"name:: omitted and no active account — set an active account via .account.use or pass name:: explicitly".to_string(),
) )?,
};
let name = resolve_account_name( &raw_name, &credential_store )?;
crate::account::check_switch_preconditions( &name, &credential_store )
.map_err( |e| io_err_to_error_data( &e, "account relogin" ) )?;
let force = crate::output::parse_int_flag( &cmd, "force", 0 )? != 0;
let owner = crate::account::read_owner( &credential_store, &name );
if !force && !crate::account::is_owned( &owner )
{
return Err( ErrorData::new(
ErrorCode::ArgumentTypeMismatch,
format!( "ownership violation: this account is owned by {owner}" ),
) );
}
let original_active = std::fs::read_to_string( credential_store.join( crate::account::active_marker_filename() ) )
.ok()
.map( | s | s.trim().to_string() )
.filter( | s | !s.is_empty() );
if is_dry( &cmd )
{
return Ok( OutputData::new(
format!( "[dry-run] would re-authenticate '{name}' via browser login\n" ),
"text",
) );
}
crate::account::switch_account( &name, &credential_store, &paths )
.map_err( |e| io_err_to_error_data( &e, "account relogin: switch" ) )?;
let creds_path = paths.credentials_file();
let before_creds = std::fs::read_to_string( &creds_path ).unwrap_or_default();
let spawn_result = claude_runner_core::ClaudeCommand::new()
.execute_interactive();
if let Err( e ) = spawn_result
{
if let Some( original ) = &original_active
{
if original != &name
{
let _ = crate::account::switch_account( original, &credential_store, &paths );
}
}
return Err( ErrorData::new(
ErrorCode::InternalError,
format!( "cannot spawn claude: {e}" ),
) );
}
let after_creds = std::fs::read_to_string( &creds_path ).unwrap_or_default();
let updated = after_creds != before_creds;
if updated
{
crate::account::save( &name, &credential_store, &paths, true, None, None, None, None )
.map_err( |e| io_err_to_error_data( &e, "account relogin: save" ) )?;
}
if let Some( original ) = &original_active
{
if original != &name
{
let _ = crate::account::switch_account( original, &credential_store, &paths );
}
}
if !updated
{
eprintln!( "relogin abandoned \u{2014} credentials unchanged for '{name}'" );
std::process::exit( 3 );
}
Ok( OutputData::new( format!( "re-authenticated '{name}' — credentials saved\n" ), "text" ) )
}