@@ -1836,6 +1836,8 @@ fn render_shell_supervisor_status(cwd: &str) -> AppResult<String> {
18361836 . unwrap_or ( default_socket) ;
18371837 let socket_kind = shell_supervisor_socket_kind ( & socket_path) ;
18381838 let protocol_health = shell_supervisor_protocol_health ( & socket_path, socket_kind == "socket" ) ;
1839+ let ( protocol_show, protocol_job_inventory) =
1840+ shell_supervisor_protocol_show ( & socket_path, socket_kind == "socket" , & protocol_health) ;
18391841 let supervisor_alive = manifest
18401842 . as_ref ( )
18411843 . and_then ( |manifest| manifest. supervisor_pid )
@@ -1847,7 +1849,7 @@ fn render_shell_supervisor_status(cwd: &str) -> AppResult<String> {
18471849 & protocol_health,
18481850 ) ;
18491851 Ok ( format ! (
1850- "kind: deepseek.exec_shell.supervisor_status.v1\n status: {status}\n platform: {}\n cwd: {}\n state_dir: {}\n manifest: {}\n manifest_exists: {}\n manifest_kind: {}\n socket: {}\n socket_kind: {socket_kind}\n protocol_health: {protocol_health}\n supervisor_pid: {}\n supervisor_alive: {}\n supervisor_epoch: {}\n protocol: {}\n methods: {}\n unsupported_methods: {}\n active_jobs: {}\n started_at: {}\n updated_at: {}\n note: this is the shell supervisor protocol/status skeleton; native PTY ownership, live attach, and TIOCSWINSZ resize are not implemented until a real supervisor process writes this state.\n " ,
1852+ "kind: deepseek.exec_shell.supervisor_status.v1\n status: {status}\n platform: {}\n cwd: {}\n state_dir: {}\n manifest: {}\n manifest_exists: {}\n manifest_kind: {}\n socket: {}\n socket_kind: {socket_kind}\n protocol_health: {protocol_health}\n protocol_show: {protocol_show} \ n supervisor_pid: {}\n supervisor_alive: {}\n supervisor_epoch: {}\n protocol: {}\n methods: {}\n unsupported_methods: {}\n active_jobs: {}\n started_at: {}\n updated_at: {} \n protocol_job_inventory: \n {}\n note: this is the shell supervisor protocol/status skeleton; native PTY ownership, live attach, and TIOCSWINSZ resize are not implemented until a real supervisor process writes this state.\n " ,
18511853 shell_supervisor_platform_label( ) ,
18521854 cwd,
18531855 state_dir. display( ) ,
@@ -1889,7 +1891,8 @@ fn render_shell_supervisor_status(cwd: &str) -> AppResult<String> {
18891891 manifest
18901892 . as_ref( )
18911893 . and_then( |manifest| manifest. updated_at. as_deref( ) )
1892- )
1894+ ) ,
1895+ protocol_job_inventory. unwrap_or_else( || "not_checked" . to_string( ) )
18931896 )
18941897 . trim_end ( )
18951898 . to_string ( ) )
@@ -1936,49 +1939,112 @@ fn shell_supervisor_protocol_health(socket_path: &Path, socket_ready: bool) -> S
19361939
19371940#[ cfg( unix) ]
19381941fn shell_supervisor_protocol_health_unix ( socket_path : & Path ) -> AppResult < String > {
1942+ let root = shell_supervisor_protocol_request_unix ( socket_path, "health" ) ?;
1943+ if root. get ( "method" ) . and_then ( json_as_string) == Some ( "health" )
1944+ && root. get ( "status" ) . and_then ( json_as_string) == Some ( "ok" )
1945+ {
1946+ Ok ( "ok" . to_string ( ) )
1947+ } else {
1948+ Ok ( format ! (
1949+ "unexpected_response: {}" ,
1950+ shell_compact_error_label( & json_value_to_string( & JsonValue :: Object ( root) ) )
1951+ ) )
1952+ }
1953+ }
1954+
1955+ fn shell_supervisor_protocol_show (
1956+ socket_path : & Path ,
1957+ socket_ready : bool ,
1958+ protocol_health : & str ,
1959+ ) -> ( String , Option < String > ) {
1960+ if !socket_ready {
1961+ return ( "not_checked" . to_string ( ) , None ) ;
1962+ }
1963+ if protocol_health != "ok" {
1964+ return ( "not_checked" . to_string ( ) , None ) ;
1965+ }
1966+ #[ cfg( unix) ]
1967+ {
1968+ match shell_supervisor_protocol_show_unix ( socket_path) {
1969+ Ok ( ( label, inventory) ) => ( label, inventory) ,
1970+ Err ( error) => (
1971+ format ! ( "error: {}" , shell_compact_error_label( & error. to_string( ) ) ) ,
1972+ None ,
1973+ ) ,
1974+ }
1975+ }
1976+ #[ cfg( not( unix) ) ]
1977+ {
1978+ let _ = socket_path;
1979+ ( "unsupported" . to_string ( ) , None )
1980+ }
1981+ }
1982+
1983+ #[ cfg( unix) ]
1984+ fn shell_supervisor_protocol_show_unix ( socket_path : & Path ) -> AppResult < ( String , Option < String > ) > {
1985+ let root = shell_supervisor_protocol_request_unix ( socket_path, "show" ) ?;
1986+ if root. get ( "method" ) . and_then ( json_as_string) == Some ( "show" )
1987+ && root. get ( "status" ) . and_then ( json_as_string) == Some ( "ok" )
1988+ {
1989+ let inventory = root
1990+ . get ( "job_inventory" )
1991+ . and_then ( json_as_string)
1992+ . map ( str:: to_string) ;
1993+ if let Some ( error) = root. get ( "job_inventory_error" ) . and_then ( json_as_string) {
1994+ return Ok ( (
1995+ format ! ( "inventory_error: {}" , shell_compact_error_label( error) ) ,
1996+ inventory,
1997+ ) ) ;
1998+ }
1999+ return Ok ( ( "ok" . to_string ( ) , inventory) ) ;
2000+ }
2001+ Ok ( (
2002+ format ! (
2003+ "unexpected_response: {}" ,
2004+ shell_compact_error_label( & json_value_to_string( & JsonValue :: Object ( root) ) )
2005+ ) ,
2006+ None ,
2007+ ) )
2008+ }
2009+
2010+ #[ cfg( unix) ]
2011+ fn shell_supervisor_protocol_request_unix (
2012+ socket_path : & Path ,
2013+ method : & str ,
2014+ ) -> AppResult < BTreeMap < String , JsonValue > > {
19392015 use std:: io:: { BufRead , BufReader , ErrorKind } ;
19402016 use std:: os:: unix:: net:: UnixStream ;
19412017
19422018 let mut stream = UnixStream :: connect ( socket_path)
1943- . map_err ( |error| app_error ( format ! ( "health connect failed: {error}" ) ) ) ?;
2019+ . map_err ( |error| app_error ( format ! ( "{method} connect failed: {error}" ) ) ) ?;
19442020 stream
19452021 . set_read_timeout ( Some ( Duration :: from_millis ( 250 ) ) )
1946- . map_err ( |error| app_error ( format ! ( "health read timeout setup failed: {error}" ) ) ) ?;
2022+ . map_err ( |error| app_error ( format ! ( "{method} read timeout setup failed: {error}" ) ) ) ?;
19472023 stream
19482024 . set_write_timeout ( Some ( Duration :: from_millis ( 250 ) ) )
1949- . map_err ( |error| app_error ( format ! ( "health write timeout setup failed: {error}" ) ) ) ?;
2025+ . map_err ( |error| app_error ( format ! ( "{method} write timeout setup failed: {error}" ) ) ) ?;
19502026 stream
1951- . write_all ( b"{ \" method\" :\" health \" }\n ")
1952- . map_err ( |error| app_error ( format ! ( "health request write failed: {error}" ) ) ) ?;
2027+ . write_all ( format ! ( "{{ \" method\" :\" {method} \" }} \n ") . as_bytes ( ) )
2028+ . map_err ( |error| app_error ( format ! ( "{method} request write failed: {error}" ) ) ) ?;
19532029
19542030 let mut response = String :: new ( ) ;
19552031 let mut reader = BufReader :: new ( stream) ;
19562032 match reader. read_line ( & mut response) {
1957- Ok ( 0 ) => return Err ( app_error ( "health response was empty") ) ,
2033+ Ok ( 0 ) => return Err ( app_error ( format ! ( "{method} response was empty") ) ) ,
19582034 Ok ( _) => { }
19592035 Err ( error) if matches ! ( error. kind( ) , ErrorKind :: TimedOut | ErrorKind :: WouldBlock ) => {
1960- return Err ( app_error ( "health response timed out") ) ;
2036+ return Err ( app_error ( format ! ( "{method} response timed out") ) ) ;
19612037 }
1962- Err ( error) => return Err ( app_error ( format ! ( "health response read failed: {error}" ) ) ) ,
2038+ Err ( error) => return Err ( app_error ( format ! ( "{method} response read failed: {error}" ) ) ) ,
19632039 }
19642040
1965- let root = parse_root_object ( response. trim ( ) ) . map_err ( |error| {
2041+ parse_root_object ( response. trim ( ) ) . map_err ( |error| {
19662042 app_error ( format ! (
1967- "health response was not valid JSON: {}; response={}" ,
2043+ "{method} response was not valid JSON: {}; response={}" ,
19682044 error,
19692045 shell_compact_error_label( response. trim( ) )
19702046 ) )
1971- } ) ?;
1972- if root. get ( "method" ) . and_then ( json_as_string) == Some ( "health" )
1973- && root. get ( "status" ) . and_then ( json_as_string) == Some ( "ok" )
1974- {
1975- Ok ( "ok" . to_string ( ) )
1976- } else {
1977- Ok ( format ! (
1978- "unexpected_response: {}" ,
1979- shell_compact_error_label( response. trim( ) )
1980- ) )
1981- }
2047+ } )
19822048}
19832049
19842050fn shell_compact_error_label ( value : & str ) -> String {
@@ -3407,7 +3473,7 @@ mod tests {
34073473
34083474 #[ cfg( unix) ]
34093475 #[ test]
3410- fn exec_shell_supervisor_status_probes_protocol_health ( ) {
3476+ fn exec_shell_supervisor_status_probes_protocol_health_and_show ( ) {
34113477 use std:: io:: { BufRead , BufReader , Write } ;
34123478 use std:: os:: unix:: net:: UnixListener ;
34133479
@@ -3433,19 +3499,26 @@ mod tests {
34333499 . unwrap ( ) ;
34343500
34353501 let handle = std:: thread:: spawn ( move || {
3436- let ( mut stream, _) = listener. accept ( ) . unwrap ( ) ;
3437- let mut request = String :: new ( ) ;
3438- {
3439- let mut reader = BufReader :: new ( & mut stream) ;
3440- reader. read_line ( & mut request) . unwrap ( ) ;
3502+ for ( expected_method, response) in [
3503+ (
3504+ "health" ,
3505+ br#"{"kind":"deepseek.exec_shell.supervisor.response.v1","method":"health","status":"ok"}"# . as_slice ( ) ,
3506+ ) ,
3507+ (
3508+ "show" ,
3509+ br#"{"kind":"deepseek.exec_shell.supervisor.response.v1","method":"show","status":"ok","job_inventory":"No background shell jobs."}"# . as_slice ( ) ,
3510+ ) ,
3511+ ] {
3512+ let ( mut stream, _) = listener. accept ( ) . unwrap ( ) ;
3513+ let mut request = String :: new ( ) ;
3514+ {
3515+ let mut reader = BufReader :: new ( & mut stream) ;
3516+ reader. read_line ( & mut request) . unwrap ( ) ;
3517+ }
3518+ assert ! ( request. contains( & format!( r#""method":"{expected_method}""# ) ) ) ;
3519+ stream. write_all ( response) . unwrap ( ) ;
3520+ stream. write_all ( b"\n " ) . unwrap ( ) ;
34413521 }
3442- assert ! ( request. contains( r#""method":"health""# ) ) ;
3443- stream
3444- . write_all (
3445- br#"{"kind":"deepseek.exec_shell.supervisor.response.v1","method":"health","status":"ok"}"# ,
3446- )
3447- . unwrap ( ) ;
3448- stream. write_all ( b"\n " ) . unwrap ( ) ;
34493522 } ) ;
34503523
34513524 let status = ExecShellSupervisorStatusTool
@@ -3463,6 +3536,21 @@ mod tests {
34633536 "{}" ,
34643537 status. summary
34653538 ) ;
3539+ assert ! (
3540+ status. summary. contains( "protocol_show: ok" ) ,
3541+ "{}" ,
3542+ status. summary
3543+ ) ;
3544+ assert ! (
3545+ status. summary. contains( "protocol_job_inventory:" ) ,
3546+ "{}" ,
3547+ status. summary
3548+ ) ;
3549+ assert ! (
3550+ status. summary. contains( "No background shell jobs." ) ,
3551+ "{}" ,
3552+ status. summary
3553+ ) ;
34663554
34673555 let _ = fs:: remove_dir_all ( root) ;
34683556 }
0 commit comments