@@ -1093,9 +1093,9 @@ enum SandboxCommands {
10931093 policy : Option < String > ,
10941094
10951095 /// Forward a local port to the sandbox before the initial command or shell starts.
1096- /// Keeps the sandbox alive.
1096+ /// Accepts [bind_address:]port (e.g. 8080, 0.0.0.0:8080). Keeps the sandbox alive.
10971097 #[ arg( long, conflicts_with = "no_keep" ) ]
1098- forward : Option < u16 > ,
1098+ forward : Option < String > ,
10991099
11001100 /// Allocate a pseudo-terminal for the remote command.
11011101 /// Defaults to auto-detection (on when stdin and stdout are terminals).
@@ -1359,8 +1359,8 @@ enum ForwardCommands {
13591359 /// Start forwarding a local port to a sandbox.
13601360 #[ command( help_template = LEAF_HELP_TEMPLATE , next_help_heading = "FLAGS" ) ]
13611361 Start {
1362- /// Port to forward (used as both local and remote port ).
1363- port : u16 ,
1362+ /// Port to forward: [bind_address:]port (e.g. 8080, 0.0.0.0:8080 ).
1363+ port : String ,
13641364
13651365 /// Sandbox name (defaults to last-used sandbox).
13661366 #[ arg( add = ArgValueCompleter :: new( completers:: complete_sandbox_names) ) ]
@@ -1377,7 +1377,7 @@ enum ForwardCommands {
13771377 /// Port that was forwarded.
13781378 port : u16 ,
13791379
1380- /// Sandbox name (defaults to last-used sandbox ).
1380+ /// Sandbox name (auto-detected from active forwards if omitted ).
13811381 #[ arg( add = ArgValueCompleter :: new( completers:: complete_sandbox_names) ) ]
13821382 name : Option < String > ,
13831383 } ,
@@ -1575,8 +1575,19 @@ async fn main() -> Result<()> {
15751575 command : Some ( fwd_cmd) ,
15761576 } ) => match fwd_cmd {
15771577 ForwardCommands :: Stop { port, name } => {
1578- let gateway_name = resolve_gateway_name ( & cli. gateway ) . unwrap_or_default ( ) ;
1579- let name = resolve_sandbox_name ( name, & gateway_name) ?;
1578+ let name = match name {
1579+ Some ( n) => n,
1580+ None => match run:: find_forward_by_port ( port) ? {
1581+ Some ( n) => {
1582+ eprintln ! ( "→ Found forward on sandbox '{n}'" ) ;
1583+ n
1584+ }
1585+ None => {
1586+ eprintln ! ( "{} No active forward found for port {port}" , "!" . yellow( ) , ) ;
1587+ return Ok ( ( ) ) ;
1588+ }
1589+ } ,
1590+ } ;
15801591 if run:: stop_forward ( & name, port) ? {
15811592 eprintln ! (
15821593 "{} Stopped forward of port {port} for sandbox {name}" ,
@@ -1600,12 +1611,20 @@ async fn main() -> Result<()> {
16001611 . max ( )
16011612 . unwrap_or ( 7 )
16021613 . max ( 7 ) ;
1614+ let bind_width = forwards
1615+ . iter ( )
1616+ . map ( |f| f. bind_addr . len ( ) )
1617+ . max ( )
1618+ . unwrap_or ( 4 )
1619+ . max ( 4 ) ;
16031620 println ! (
1604- "{:<width $} {:<8} {:<10} STATUS" ,
1621+ "{:<nw$} {:<bw $} {:<8} {:<10} STATUS" ,
16051622 "SANDBOX" ,
1623+ "BIND" ,
16061624 "PORT" ,
16071625 "PID" ,
1608- width = name_width,
1626+ nw = name_width,
1627+ bw = bind_width,
16091628 ) ;
16101629 for f in & forwards {
16111630 let status = if f. alive {
@@ -1614,12 +1633,14 @@ async fn main() -> Result<()> {
16141633 "dead" . red ( ) . to_string ( )
16151634 } ;
16161635 println ! (
1617- "{:<width $} {:<8} {:<10} {}" ,
1636+ "{:<nw$} {:<bw $} {:<8} {:<10} {}" ,
16181637 f. sandbox,
1638+ f. bind_addr,
16191639 f. port,
16201640 f. pid,
16211641 status,
1622- width = name_width,
1642+ nw = name_width,
1643+ bw = bind_width,
16231644 ) ;
16241645 }
16251646 }
@@ -1629,18 +1650,20 @@ async fn main() -> Result<()> {
16291650 name,
16301651 background,
16311652 } => {
1653+ let spec = openshell_core:: forward:: ForwardSpec :: parse ( & port) ?;
16321654 let ctx = resolve_gateway ( & cli. gateway , & cli. gateway_endpoint ) ?;
16331655 let mut tls = tls. with_gateway_name ( & ctx. name ) ;
16341656 apply_edge_auth ( & mut tls, & ctx. name ) ;
16351657 let name = resolve_sandbox_name ( name, & ctx. name ) ?;
1636- run:: sandbox_forward ( & ctx. endpoint , & name, port , background, & tls) . await ?;
1658+ run:: sandbox_forward ( & ctx. endpoint , & name, & spec , background, & tls) . await ?;
16371659 if background {
16381660 eprintln ! (
1639- "{} Forwarding port {port } to sandbox {name} in the background" ,
1661+ "{} Forwarding port {} to sandbox {name} in the background" ,
16401662 "✓" . green( ) . bold( ) ,
1663+ spec. port,
16411664 ) ;
1642- eprintln ! ( " Access at: http://127.0.0.1:{port}/" ) ;
1643- eprintln ! ( " Stop with: openshell forward stop {port } {name}" ) ;
1665+ eprintln ! ( " Access at: {}" , spec . access_url ( ) ) ;
1666+ eprintln ! ( " Stop with: openshell forward stop {} {name}" , spec . port ) ;
16441667 }
16451668 }
16461669 } ,
@@ -1864,6 +1887,9 @@ async fn main() -> Result<()> {
18641887 } ) ;
18651888
18661889 let editor = editor. map ( Into :: into) ;
1890+ let forward = forward
1891+ . map ( |s| openshell_core:: forward:: ForwardSpec :: parse ( & s) )
1892+ . transpose ( ) ?;
18671893 let keep = keep || !no_keep || editor. is_some ( ) || forward. is_some ( ) ;
18681894
18691895 // For `sandbox create`, a missing cluster is not fatal — the
0 commit comments