11//! Container-mode status command implementation.
22
3- use crate :: commands:: container:: { exec_command, exec_command_with_status, systemd_available} ;
3+ use crate :: commands:: container:: { exec_command_with_status, systemd_available} ;
4+ use crate :: commands:: runtime_shared:: backend:: { ContainerBackend , default_container_port} ;
5+ use crate :: commands:: runtime_shared:: collect_status_view;
6+ use crate :: commands:: runtime_shared:: status_model:: {
7+ OpencodeHealthStatus , format_broker_health_label,
8+ } ;
49use crate :: output:: state_style;
510use anyhow:: Result ;
611use console:: style;
7- use opencode_cloud_core:: docker:: { HealthError , OPENCODE_WEB_PORT , check_health, get_cli_version} ;
8- use std:: fs;
9- use std:: path:: Path ;
12+ use opencode_cloud_core:: docker:: get_cli_version;
1013
1114const STATUS_LABEL_WIDTH : usize = 15 ;
1215
13- enum OpencodeHealthStatus {
14- Healthy ,
15- Starting ,
16- Unhealthy ,
17- Failed ,
18- }
19-
20- #[ derive( Clone , Copy ) ]
21- enum BrokerHealthStatus {
22- Healthy ,
23- Degraded ,
24- Unhealthy ,
25- }
26-
2716pub async fn cmd_status_container (
2817 _args : & crate :: commands:: StatusArgs ,
2918 quiet : bool ,
@@ -45,31 +34,17 @@ pub async fn cmd_status_container(
4534 }
4635 }
4736
48- let broker_running = if systemd {
49- systemd_service_active ( "opencode-broker.service" ) . await ?
50- } else {
51- process_running ( "pgrep" , & [ "-x" , "opencode-broker" ] ) . await ?
52- } ;
53-
54- let broker_socket = Path :: new ( "/run/opencode/auth.sock" ) . exists ( ) ;
55- let broker_health = map_broker_health_status ( broker_running, broker_socket) ;
56-
57- let opencode_version = read_opencode_version ( ) . await ;
58- let opencode_commit = read_opencode_commit ( ) ;
59- let opencode_cloud_version = read_opencode_cloud_version ( ) ;
60-
61- let health = if opencode_running {
62- get_opencode_health_status ( true ) . await
63- } else {
64- None
65- } ;
37+ let backend = ContainerBackend :: new ( systemd) ;
38+ let host_port = default_container_port ( ) ;
39+ let status_view =
40+ collect_status_view ( & backend, opencode_running, "127.0.0.1" , host_port) . await ?;
6641
6742 let state_label = if opencode_running {
68- match health {
43+ match status_view . opencode_health {
6944 Some ( OpencodeHealthStatus :: Healthy ) | None => "running" . to_string ( ) ,
7045 Some ( OpencodeHealthStatus :: Starting ) => "starting" . to_string ( ) ,
71- Some ( OpencodeHealthStatus :: Unhealthy ) => "unhealthy" . to_string ( ) ,
72- Some ( OpencodeHealthStatus :: Failed ) => "unknown" . to_string ( ) ,
46+ Some ( OpencodeHealthStatus :: Unhealthy ( _ ) ) => "unhealthy" . to_string ( ) ,
47+ Some ( OpencodeHealthStatus :: CheckFailed ) => "unknown" . to_string ( ) ,
7348 }
7449 } else {
7550 "stopped" . to_string ( )
@@ -80,28 +55,33 @@ pub async fn cmd_status_container(
8055 "{}" ,
8156 format_kv(
8257 "URL:" ,
83- style( format!( "http://127.0.0.1:{OPENCODE_WEB_PORT }" ) ) . cyan( )
58+ style( format!( "http://127.0.0.1:{host_port }" ) ) . cyan( )
8459 )
8560 ) ;
8661
87- let opencode_display = match ( opencode_version. as_deref ( ) , opencode_commit. as_deref ( ) ) {
88- ( Some ( version) , Some ( commit) ) => format ! ( "{version} ({commit})" ) ,
89- ( Some ( version) , None ) => version. to_string ( ) ,
90- ( None , Some ( commit) ) => format ! ( "unknown ({commit})" ) ,
91- ( None , None ) => "unknown" . to_string ( ) ,
92- } ;
62+ let opencode_display =
63+ format_opencode_display ( & status_view. opencode_version , & status_view. opencode_commit ) ;
9364 println ! ( "{}" , format_kv( "Opencode:" , opencode_display) ) ;
9465
9566 println ! (
9667 "{}" ,
97- format_kv( "Broker:" , format_broker_health_status( broker_health) )
68+ format_kv(
69+ "Broker:" ,
70+ format_broker_health_label( status_view. broker_health)
71+ )
9872 ) ;
9973
100- let runtime = if systemd { "systemd" } else { "tini" } ;
74+ let runtime = if status_view
75+ . capabilities
76+ . systemd_available
77+ . unwrap_or ( systemd)
78+ {
79+ "systemd"
80+ } else {
81+ "tini"
82+ } ;
10183 println ! ( "{}" , format_kv( "Runtime:" , runtime) ) ;
102-
103- let image_version = opencode_cloud_version. unwrap_or_else ( || "unknown" . to_string ( ) ) ;
104- println ! ( "{}" , format_kv( "Image:" , image_version) ) ;
84+ println ! ( "{}" , format_kv( "Image:" , & status_view. image_version) ) ;
10585
10686 let cli_version = get_cli_version ( ) ;
10787 println ! ( "{}" , format_kv( "CLI:" , format!( "v{cli_version}" ) ) ) ;
@@ -110,78 +90,24 @@ pub async fn cmd_status_container(
11090}
11191
11292async fn systemd_service_active ( service : & str ) -> Result < bool > {
113- let ( output , _status ) = exec_command_with_status ( "systemctl" , & [ "is-active" , service] ) . await ?;
114- Ok ( output . lines ( ) . next ( ) . map ( |line| line . trim ( ) ) == Some ( "active" ) )
93+ let ( _output , status ) = exec_command_with_status ( "systemctl" , & [ "is-active" , service] ) . await ?;
94+ Ok ( status == 0 )
11595}
11696
11797async fn process_running ( cmd : & str , args : & [ & str ] ) -> Result < bool > {
11898 let ( _output, status) = exec_command_with_status ( cmd, args) . await ?;
11999 Ok ( status == 0 )
120100}
121101
122- async fn read_opencode_version ( ) -> Option < String > {
123- let output = exec_command ( "/opt/opencode/bin/opencode" , & [ "--version" ] )
124- . await
125- . ok ( ) ?;
126- let version = output. lines ( ) . next ( ) ?. trim ( ) ;
127- if version. is_empty ( ) {
128- None
129- } else {
130- Some ( version. to_string ( ) )
131- }
132- }
133-
134- fn read_opencode_commit ( ) -> Option < String > {
135- let contents = fs:: read_to_string ( "/opt/opencode/COMMIT" ) . ok ( ) ?;
136- let commit = contents. lines ( ) . next ( ) ?. trim ( ) ;
137- if commit. is_empty ( ) {
138- None
139- } else {
140- Some ( commit. chars ( ) . take ( 7 ) . collect ( ) )
141- }
142- }
143-
144- fn read_opencode_cloud_version ( ) -> Option < String > {
145- let contents = fs:: read_to_string ( "/etc/opencode-cloud-version" ) . ok ( ) ?;
146- let version = contents. lines ( ) . next ( ) ?. trim ( ) ;
147- if version. is_empty ( ) {
148- None
149- } else {
150- Some ( version. to_string ( ) )
151- }
152- }
153-
154- async fn get_opencode_health_status ( include_probe : bool ) -> Option < OpencodeHealthStatus > {
155- if !include_probe {
156- return None ;
157- }
158-
159- match check_health ( "127.0.0.1" , OPENCODE_WEB_PORT ) . await {
160- Ok ( _) => Some ( OpencodeHealthStatus :: Healthy ) ,
161- Err ( HealthError :: ConnectionRefused ) | Err ( HealthError :: Timeout ) => {
162- Some ( OpencodeHealthStatus :: Starting )
163- }
164- Err ( HealthError :: Unhealthy ( _code) ) => Some ( OpencodeHealthStatus :: Unhealthy ) ,
165- Err ( _) => Some ( OpencodeHealthStatus :: Failed ) ,
102+ fn format_opencode_display ( version : & str , commit : & str ) -> String {
103+ match ( version, commit) {
104+ ( "unknown" , "unknown" ) => "unknown" . to_string ( ) ,
105+ ( "unknown" , commit) => format ! ( "unknown ({commit})" ) ,
106+ ( version, "unknown" ) => version. to_string ( ) ,
107+ ( version, commit) => format ! ( "{version} ({commit})" ) ,
166108 }
167109}
168110
169111fn format_kv ( label : & str , value : impl std:: fmt:: Display ) -> String {
170112 format ! ( "{label:<STATUS_LABEL_WIDTH$} {value}" )
171113}
172-
173- fn map_broker_health_status ( process_ok : bool , socket_ok : bool ) -> BrokerHealthStatus {
174- match ( process_ok, socket_ok) {
175- ( true , true ) => BrokerHealthStatus :: Healthy ,
176- ( true , false ) | ( false , true ) => BrokerHealthStatus :: Degraded ,
177- ( false , false ) => BrokerHealthStatus :: Unhealthy ,
178- }
179- }
180-
181- fn format_broker_health_status ( status : BrokerHealthStatus ) -> String {
182- match status {
183- BrokerHealthStatus :: Healthy => style ( "Healthy" ) . green ( ) . to_string ( ) ,
184- BrokerHealthStatus :: Degraded => style ( "Degraded" ) . yellow ( ) . to_string ( ) ,
185- BrokerHealthStatus :: Unhealthy => style ( "Unhealthy" ) . red ( ) . to_string ( ) ,
186- }
187- }
0 commit comments