Add fresh default sessions and terminal status handling
This commit is contained in:
+80
-4
@@ -18,7 +18,7 @@ use std::fs;
|
||||
use std::io::{Read, Write};
|
||||
use std::net::{SocketAddr, ToSocketAddrs};
|
||||
use std::process::Command;
|
||||
use std::time::{Duration, Instant};
|
||||
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
|
||||
use tokio::net::UdpSocket;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
@@ -27,8 +27,10 @@ use tokio::sync::mpsc;
|
||||
struct Args {
|
||||
#[arg()]
|
||||
server: Option<String>,
|
||||
#[arg(long, default_value = "default")]
|
||||
session: String,
|
||||
#[arg(long)]
|
||||
session: Option<String>,
|
||||
#[arg(long)]
|
||||
new: bool,
|
||||
#[arg(long)]
|
||||
view_only: bool,
|
||||
#[arg(long)]
|
||||
@@ -73,7 +75,7 @@ async fn main() -> Result<()> {
|
||||
let args = Args::parse();
|
||||
let config = load_client_config(None).unwrap_or_default();
|
||||
let server = args.server.unwrap_or(config.server);
|
||||
let session = args.session;
|
||||
let session = select_session(args.session.as_deref(), args.new, &config.default_session);
|
||||
let mode = if args.view_only || config.view_only {
|
||||
"view-only"
|
||||
} else {
|
||||
@@ -201,6 +203,7 @@ async fn main() -> Result<()> {
|
||||
output_seq: ok.initial_seq,
|
||||
bytes: ok.snapshot,
|
||||
snapshot: true,
|
||||
closed: false,
|
||||
};
|
||||
if args.attach_only {
|
||||
render_frame(&first)?;
|
||||
@@ -235,6 +238,24 @@ fn local_bootstrap(
|
||||
)
|
||||
}
|
||||
|
||||
fn select_session(requested: Option<&str>, force_new: bool, configured_default: &str) -> String {
|
||||
if let Some(session) = requested {
|
||||
return session.to_string();
|
||||
}
|
||||
if force_new || configured_default == "new" || configured_default.is_empty() {
|
||||
return unique_session_name();
|
||||
}
|
||||
configured_default.to_string()
|
||||
}
|
||||
|
||||
fn unique_session_name() -> String {
|
||||
let millis = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap_or_default()
|
||||
.as_millis();
|
||||
format!("term-{millis}-{}", std::process::id())
|
||||
}
|
||||
|
||||
fn ssh_bootstrap(
|
||||
server: &str,
|
||||
ssh_port: Option<u16>,
|
||||
@@ -426,6 +447,7 @@ async fn try_ticket_attach(
|
||||
output_seq: ok.initial_seq,
|
||||
bytes: ok.snapshot.clone(),
|
||||
snapshot: true,
|
||||
closed: false,
|
||||
};
|
||||
let mut next = cached.clone();
|
||||
next.client_id = ok.client_id;
|
||||
@@ -481,8 +503,14 @@ async fn run_terminal(
|
||||
let _raw = RawMode::enter()?;
|
||||
let addr = resolve_addr(&cred.udp_host, cred.udp_port)?;
|
||||
let mut send_seq = 2u64;
|
||||
let mut last_packet_at = Instant::now();
|
||||
let mut status_visible = false;
|
||||
let mut status_tick = tokio::time::interval(Duration::from_secs(1));
|
||||
if let Some(frame) = first_frame {
|
||||
render_frame(&frame)?;
|
||||
if frame.closed {
|
||||
return Ok(());
|
||||
}
|
||||
cred.last_rendered_seq = frame.output_seq;
|
||||
send_ack(&socket, addr, &cred, &mut send_seq).await?;
|
||||
}
|
||||
@@ -528,6 +556,11 @@ async fn run_terminal(
|
||||
}
|
||||
recv = socket.recv_from(&mut recv_buf) => {
|
||||
let (n, _) = recv?;
|
||||
last_packet_at = Instant::now();
|
||||
if status_visible {
|
||||
clear_status_bar()?;
|
||||
status_visible = false;
|
||||
}
|
||||
let packet = protocol::decode(&recv_buf[..n])?;
|
||||
match packet.header.kind {
|
||||
PacketKind::Frame | PacketKind::ResumeOk => {
|
||||
@@ -536,11 +569,36 @@ async fn run_terminal(
|
||||
render_frame(&frame)?;
|
||||
cred.last_rendered_seq = frame.output_seq;
|
||||
send_ack(&socket, addr, &cred, &mut send_seq).await?;
|
||||
if frame.closed {
|
||||
break;
|
||||
}
|
||||
}
|
||||
PacketKind::Pong => {}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ = status_tick.tick() => {
|
||||
let stale = last_packet_at.elapsed();
|
||||
if stale >= Duration::from_secs(2) {
|
||||
draw_status_bar(&format!(
|
||||
"DOSH disconnected for {}s | session {} | trying UDP",
|
||||
stale.as_secs(),
|
||||
cred.session
|
||||
))?;
|
||||
status_visible = true;
|
||||
let packet = protocol::encode_encrypted(
|
||||
PacketKind::Ping,
|
||||
cred.client_id,
|
||||
send_seq,
|
||||
cred.last_rendered_seq,
|
||||
&cred.session_key,
|
||||
CLIENT_TO_SERVER,
|
||||
b"",
|
||||
)?;
|
||||
send_seq += 1;
|
||||
socket.send_to(&packet, addr).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@@ -588,6 +646,24 @@ fn render_frame(frame: &Frame) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn draw_status_bar(message: &str) -> Result<()> {
|
||||
let mut stdout = std::io::stdout();
|
||||
write!(
|
||||
stdout,
|
||||
"\x1b7\x1b[1;1H\x1b[44;97m\x1b[K {} \x1b[0m\x1b8",
|
||||
message
|
||||
)?;
|
||||
stdout.flush()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn clear_status_bar() -> Result<()> {
|
||||
let mut stdout = std::io::stdout();
|
||||
stdout.write_all(b"\x1b7\x1b[1;1H\x1b[K\x1b8")?;
|
||||
stdout.flush()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cache_path(root: &str, server: &str, session: &str, mode: &str) -> std::path::PathBuf {
|
||||
let safe = format!("{server}_{session}_{mode}")
|
||||
.chars()
|
||||
|
||||
Reference in New Issue
Block a user