@@ -26,21 +26,37 @@ pub enum AuthStatus {
2626}
2727
2828pub fn check_status ( profile_config : & config:: ProfileConfig ) -> AuthStatus {
29- let api_key_fallback = profile_config
30- . api_key
31- . as_deref ( )
32- . filter ( |k| !k. is_empty ( ) && * k != "PLACEHOLDER" ) ;
33-
34- // PKCE-origin sessions don't write an api_key, so absence of a key
35- // alone isn't "not configured" — only true if there's also no
36- // cached JWT session to validate.
37- if api_key_fallback. is_none ( ) && crate :: jwt:: load_session ( ) . is_none ( ) {
38- return AuthStatus :: NotConfigured ;
39- }
29+ // Same precedence as `ApiClient::new`:
30+ // 1. `sandbox run` child via env var
31+ // 2. on-disk sandbox session (sandbox set <id>)
32+ // 3. user-scoped CLI session / api_key fallback
33+ let api_url = profile_config. api_url . to_string ( ) ;
34+ let access_token = if let Some ( ( sandbox_jwt, _) ) =
35+ crate :: sandbox_session:: sandbox_token_in_use ( )
36+ {
37+ sandbox_jwt
38+ } else if crate :: sandbox_session:: load ( ) . is_some ( ) {
39+ match crate :: sandbox_session:: ensure_access_token ( & api_url) {
40+ Some ( t) => t,
41+ None => return AuthStatus :: Invalid ( 401 ) ,
42+ }
43+ } else {
44+ let api_key_fallback = profile_config
45+ . api_key
46+ . as_deref ( )
47+ . filter ( |k| !k. is_empty ( ) && * k != "PLACEHOLDER" ) ;
48+
49+ // PKCE-origin sessions don't write an api_key, so absence of a key
50+ // alone isn't "not configured" — only true if there's also no
51+ // cached JWT session to validate.
52+ if api_key_fallback. is_none ( ) && crate :: jwt:: load_session ( ) . is_none ( ) {
53+ return AuthStatus :: NotConfigured ;
54+ }
4055
41- let access_token = match crate :: jwt:: ensure_access_token ( profile_config, api_key_fallback) {
42- Ok ( t) => t,
43- Err ( _) => return AuthStatus :: Invalid ( 401 ) ,
56+ match crate :: jwt:: ensure_access_token ( profile_config, api_key_fallback) {
57+ Ok ( t) => t,
58+ Err ( _) => return AuthStatus :: Invalid ( 401 ) ,
59+ }
4460 } ;
4561
4662 let url = format ! ( "{}/workspaces" , profile_config. api_url) ;
@@ -64,26 +80,50 @@ pub fn status(profile: &str) {
6480 }
6581 } ;
6682
67- // The credential the CLI is *about to use*. Note: even when an
68- // override is set, the wire credential is still a JWT (minted on
69- // demand from the override) — but we report the user-visible source.
70- let method_label = match profile_config. api_key_source {
71- ApiKeySource :: Flag => "API Key flag" ,
72- ApiKeySource :: Env => "API Key env" ,
73- ApiKeySource :: Config => "CLI Session" ,
83+ // The credential the CLI is *about to use*. Precedence matches
84+ // `ApiClient::new`: env-var sandbox token (sandbox run child) >
85+ // on-disk sandbox session (sandbox set <id>) > user CLI session.
86+ let env_sandbox = crate :: sandbox_session:: sandbox_token_in_use ( ) ;
87+ let disk_sandbox = if env_sandbox. is_none ( ) {
88+ crate :: sandbox_session:: load ( )
89+ } else {
90+ None
7491 } ;
75-
76- // For Flag/Env we mask the api_key the user supplied. For the
77- // CLI session path we mask the refresh_token — it's stable across
78- // commands (unlike the 5-min access_token), so the tail stays
79- // recognizable between runs.
80- let credential_tail = match profile_config. api_key_source {
81- ApiKeySource :: Flag | ApiKeySource :: Env => profile_config
82- . api_key
83- . as_deref ( )
84- . map ( crate :: util:: mask_credential) ,
85- ApiKeySource :: Config => crate :: jwt:: load_session ( )
86- . map ( |s| crate :: util:: mask_credential ( & s. refresh_token ) ) ,
92+ let ( method_label, credential_tail) = if let Some ( ( token, sandbox_id) ) = & env_sandbox {
93+ let label = match sandbox_id {
94+ Some ( id) => format ! ( "Sandbox {id}" ) ,
95+ None => "Sandbox Session" . to_string ( ) ,
96+ } ;
97+ ( label, Some ( crate :: util:: mask_credential ( token) ) )
98+ } else if let Some ( s) = & disk_sandbox {
99+ // Use the refresh token for the displayed tail — it's stable
100+ // across refreshes (the access token rotates every 3 days), so
101+ // the tail stays recognizable between runs.
102+ let label = if s. sandbox_id . is_empty ( ) {
103+ "Sandbox Session" . to_string ( )
104+ } else {
105+ format ! ( "Sandbox {}" , s. sandbox_id)
106+ } ;
107+ ( label, Some ( crate :: util:: mask_credential ( & s. refresh_token ) ) )
108+ } else {
109+ let label = match profile_config. api_key_source {
110+ ApiKeySource :: Flag => "API Key flag" ,
111+ ApiKeySource :: Env => "API Key env" ,
112+ ApiKeySource :: Config => "CLI Session" ,
113+ } ;
114+ // For Flag/Env we mask the api_key the user supplied. For
115+ // the CLI session path we mask the refresh_token — it's
116+ // stable across commands (unlike the 5-min access_token),
117+ // so the tail stays recognizable between runs.
118+ let tail = match profile_config. api_key_source {
119+ ApiKeySource :: Flag | ApiKeySource :: Env => profile_config
120+ . api_key
121+ . as_deref ( )
122+ . map ( crate :: util:: mask_credential) ,
123+ ApiKeySource :: Config => crate :: jwt:: load_session ( )
124+ . map ( |s| crate :: util:: mask_credential ( & s. refresh_token ) ) ,
125+ } ;
126+ ( label. to_string ( ) , tail)
87127 } ;
88128 let method_suffix = match credential_tail {
89129 Some ( tail) => format ! ( " - {method_label} [{tail}]" ) ,
0 commit comments