This commit is contained in:
Generated
+161
-2
@@ -713,6 +713,12 @@ dependencies = [
|
||||
"tendril",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "downcast-rs"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
|
||||
|
||||
[[package]]
|
||||
name = "dpi"
|
||||
version = "0.1.2"
|
||||
@@ -775,7 +781,7 @@ dependencies = [
|
||||
"rustc_version",
|
||||
"toml 1.1.2+spec-1.1.0",
|
||||
"vswhom",
|
||||
"winreg",
|
||||
"winreg 0.55.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -822,10 +828,21 @@ version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f"
|
||||
dependencies = [
|
||||
"memoffset",
|
||||
"memoffset 0.9.1",
|
||||
"rustc_version",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "filedescriptor"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e40758ed24c9b2eeb76c35fb0aebc66c626084edd827e07e1552279814c6682d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"thiserror 1.0.69",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.9"
|
||||
@@ -1568,6 +1585,15 @@ dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ioctl-rs"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7970510895cee30b3e9128319f2cefd4bde883a39f38baa279567ba3a7eb97d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipnet"
|
||||
version = "2.12.0"
|
||||
@@ -1691,6 +1717,12 @@ dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libappindicator"
|
||||
version = "0.9.0"
|
||||
@@ -1787,6 +1819,15 @@ version = "2.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.9.1"
|
||||
@@ -1874,6 +1915,20 @@ version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.25.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"bitflags 1.3.2",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"memoffset 0.6.5",
|
||||
"pin-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-conv"
|
||||
version = "0.2.2"
|
||||
@@ -2248,6 +2303,12 @@ version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.33"
|
||||
@@ -2305,6 +2366,27 @@ dependencies = [
|
||||
"universal-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "portable-pty"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "806ee80c2a03dbe1a9fb9534f8d19e4c0546b790cde8fd1fea9d6390644cb0be"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags 1.3.2",
|
||||
"downcast-rs",
|
||||
"filedescriptor",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"log",
|
||||
"nix",
|
||||
"serial",
|
||||
"shared_library",
|
||||
"shell-words",
|
||||
"winapi",
|
||||
"winreg 0.10.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "potential_utf"
|
||||
version = "0.1.5"
|
||||
@@ -2453,6 +2535,7 @@ dependencies = [
|
||||
"aes-gcm",
|
||||
"argon2",
|
||||
"base64 0.22.1",
|
||||
"portable-pty",
|
||||
"rabby-core",
|
||||
"rand",
|
||||
"serde",
|
||||
@@ -2841,6 +2924,48 @@ dependencies = [
|
||||
"syn 2.0.118",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serial"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1237a96570fc377c13baa1b88c7589ab66edced652e43ffb17088f003db3e86"
|
||||
dependencies = [
|
||||
"serial-core",
|
||||
"serial-unix",
|
||||
"serial-windows",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serial-core"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f46209b345401737ae2125fe5b19a77acce90cd53e1658cda928e4fe9a64581"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serial-unix"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f03fbca4c9d866e24a459cbca71283f545a37f8e3e002ad8c70593871453cab7"
|
||||
dependencies = [
|
||||
"ioctl-rs",
|
||||
"libc",
|
||||
"serial-core",
|
||||
"termios",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serial-windows"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15c6d3b776267a75d31bbdfd5d36c0ca051251caafc285827052bc53bcdc8162"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"serial-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serialize-to-javascript"
|
||||
version = "0.1.2"
|
||||
@@ -2883,6 +3008,22 @@ dependencies = [
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shared_library"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a9e7e0f2bfae24d8a5b5a66c5b257a83c7412304311512a0c054cd5e619da11"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shell-words"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc6fe69c597f9c37bfeeeeeb33da3530379845f10be461a66d16d03eca2ded77"
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "2.0.1"
|
||||
@@ -3358,6 +3499,15 @@ dependencies = [
|
||||
"utf-8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termios"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d5d9cf598a6d7ce700a4e6a9199da127e6819a61e64b68609683cc9a01b5683a"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.69"
|
||||
@@ -4427,6 +4577,15 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.55.0"
|
||||
|
||||
@@ -16,3 +16,4 @@ base64 = "0.22"
|
||||
rand = "0.8"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
portable-pty = "0.8"
|
||||
|
||||
@@ -2,4 +2,5 @@ pub mod config_store;
|
||||
pub mod local_terminal;
|
||||
pub mod pty;
|
||||
pub mod ssh_runtime;
|
||||
pub mod terminal_session;
|
||||
pub mod wallet_vault;
|
||||
|
||||
@@ -0,0 +1,243 @@
|
||||
use portable_pty::{native_pty_system, Child, CommandBuilder, MasterPty, PtySize};
|
||||
use serde::Serialize;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
use std::io::{Read, Write};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct TerminalSessionId(pub u64);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
|
||||
pub struct TerminalSessionSize {
|
||||
pub cols: u16,
|
||||
pub rows: u16,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum TerminalSessionError {
|
||||
InvalidSize,
|
||||
MissingSession(u64),
|
||||
Io(String),
|
||||
Timeout(String),
|
||||
}
|
||||
|
||||
impl fmt::Display for TerminalSessionError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::InvalidSize => write!(f, "invalid terminal session size"),
|
||||
Self::MissingSession(id) => write!(f, "terminal session {id} does not exist"),
|
||||
Self::Io(message) => write!(f, "terminal session I/O error: {message}"),
|
||||
Self::Timeout(needle) => write!(f, "timed out waiting for terminal output: {needle}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for TerminalSessionError {}
|
||||
|
||||
pub struct TerminalSessionRegistry {
|
||||
next_id: u64,
|
||||
sessions: HashMap<TerminalSessionId, TerminalSession>,
|
||||
}
|
||||
|
||||
struct TerminalSession {
|
||||
master: Box<dyn MasterPty + Send>,
|
||||
child: Box<dyn Child + Send + Sync>,
|
||||
reader: Box<dyn Read + Send>,
|
||||
writer: Box<dyn Write + Send>,
|
||||
size: TerminalSessionSize,
|
||||
}
|
||||
|
||||
impl Default for TerminalSessionRegistry {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
next_id: 1,
|
||||
sessions: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TerminalSessionRegistry {
|
||||
pub fn spawn_local(
|
||||
&mut self,
|
||||
shell: impl AsRef<str>,
|
||||
cols: u16,
|
||||
rows: u16,
|
||||
) -> Result<TerminalSessionId, TerminalSessionError> {
|
||||
let size = session_size(cols, rows)?;
|
||||
let pty_system = native_pty_system();
|
||||
let pair = pty_system
|
||||
.openpty(PtySize {
|
||||
rows,
|
||||
cols,
|
||||
pixel_width: 0,
|
||||
pixel_height: 0,
|
||||
})
|
||||
.map_err(|err| TerminalSessionError::Io(err.to_string()))?;
|
||||
let command = CommandBuilder::new(shell.as_ref());
|
||||
let child = pair
|
||||
.slave
|
||||
.spawn_command(command)
|
||||
.map_err(|err| TerminalSessionError::Io(err.to_string()))?;
|
||||
let reader = pair
|
||||
.master
|
||||
.try_clone_reader()
|
||||
.map_err(|err| TerminalSessionError::Io(err.to_string()))?;
|
||||
let writer = pair
|
||||
.master
|
||||
.take_writer()
|
||||
.map_err(|err| TerminalSessionError::Io(err.to_string()))?;
|
||||
let id = TerminalSessionId(self.next_id);
|
||||
self.next_id += 1;
|
||||
self.sessions.insert(
|
||||
id,
|
||||
TerminalSession {
|
||||
master: pair.master,
|
||||
child,
|
||||
reader,
|
||||
writer,
|
||||
size,
|
||||
},
|
||||
);
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
pub fn write(
|
||||
&mut self,
|
||||
id: TerminalSessionId,
|
||||
bytes: &[u8],
|
||||
) -> Result<(), TerminalSessionError> {
|
||||
let session = self.session_mut(id)?;
|
||||
session
|
||||
.writer
|
||||
.write_all(bytes)
|
||||
.map_err(|err| TerminalSessionError::Io(err.to_string()))?;
|
||||
session
|
||||
.writer
|
||||
.flush()
|
||||
.map_err(|err| TerminalSessionError::Io(err.to_string()))
|
||||
}
|
||||
|
||||
pub fn read_until(
|
||||
&mut self,
|
||||
id: TerminalSessionId,
|
||||
needle: &str,
|
||||
timeout: Duration,
|
||||
) -> Result<String, TerminalSessionError> {
|
||||
let deadline = Instant::now() + timeout;
|
||||
let mut output = String::new();
|
||||
let mut buf = [0u8; 4096];
|
||||
while Instant::now() < deadline {
|
||||
let session = self.session_mut(id)?;
|
||||
match session.reader.read(&mut buf) {
|
||||
Ok(0) => std::thread::sleep(Duration::from_millis(10)),
|
||||
Ok(n) => {
|
||||
output.push_str(&String::from_utf8_lossy(&buf[..n]));
|
||||
if output.contains(needle) {
|
||||
return Ok(output);
|
||||
}
|
||||
}
|
||||
Err(err) if err.kind() == std::io::ErrorKind::Interrupted => continue,
|
||||
Err(err) => return Err(TerminalSessionError::Io(err.to_string())),
|
||||
}
|
||||
}
|
||||
Err(TerminalSessionError::Timeout(needle.to_string()))
|
||||
}
|
||||
|
||||
pub fn resize(
|
||||
&mut self,
|
||||
id: TerminalSessionId,
|
||||
cols: u16,
|
||||
rows: u16,
|
||||
) -> Result<(), TerminalSessionError> {
|
||||
let size = session_size(cols, rows)?;
|
||||
let session = self.session_mut(id)?;
|
||||
session
|
||||
.master
|
||||
.resize(PtySize {
|
||||
rows,
|
||||
cols,
|
||||
pixel_width: 0,
|
||||
pixel_height: 0,
|
||||
})
|
||||
.map_err(|err| TerminalSessionError::Io(err.to_string()))?;
|
||||
session.size = size;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn size(&self, id: TerminalSessionId) -> Result<TerminalSessionSize, TerminalSessionError> {
|
||||
self.sessions
|
||||
.get(&id)
|
||||
.map(|session| session.size)
|
||||
.ok_or(TerminalSessionError::MissingSession(id.0))
|
||||
}
|
||||
|
||||
pub fn kill(&mut self, id: TerminalSessionId) -> Result<(), TerminalSessionError> {
|
||||
let mut session = self
|
||||
.sessions
|
||||
.remove(&id)
|
||||
.ok_or(TerminalSessionError::MissingSession(id.0))?;
|
||||
session
|
||||
.child
|
||||
.kill()
|
||||
.map_err(|err| TerminalSessionError::Io(err.to_string()))
|
||||
}
|
||||
|
||||
fn session_mut(
|
||||
&mut self,
|
||||
id: TerminalSessionId,
|
||||
) -> Result<&mut TerminalSession, TerminalSessionError> {
|
||||
self.sessions
|
||||
.get_mut(&id)
|
||||
.ok_or(TerminalSessionError::MissingSession(id.0))
|
||||
}
|
||||
}
|
||||
|
||||
fn session_size(cols: u16, rows: u16) -> Result<TerminalSessionSize, TerminalSessionError> {
|
||||
if cols == 0 || rows == 0 {
|
||||
return Err(TerminalSessionError::InvalidSize);
|
||||
}
|
||||
Ok(TerminalSessionSize { cols, rows })
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::time::Duration;
|
||||
|
||||
#[test]
|
||||
fn session_registry_spawns_shell_and_reads_output() {
|
||||
let mut registry = TerminalSessionRegistry::default();
|
||||
let id = registry.spawn_local("/bin/sh", 80, 24).unwrap();
|
||||
|
||||
registry
|
||||
.write(id, b"printf rabby-pty-ready\nexit\n")
|
||||
.unwrap();
|
||||
let output = registry
|
||||
.read_until(id, "rabby-pty-ready", Duration::from_secs(3))
|
||||
.unwrap();
|
||||
|
||||
assert!(output.contains("rabby-pty-ready"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn session_registry_resizes_running_session() {
|
||||
let mut registry = TerminalSessionRegistry::default();
|
||||
let id = registry.spawn_local("/bin/sh", 80, 24).unwrap();
|
||||
|
||||
registry.resize(id, 120, 40).unwrap();
|
||||
let size = registry.size(id).unwrap();
|
||||
|
||||
assert_eq!(size.cols, 120);
|
||||
assert_eq!(size.rows, 40);
|
||||
registry.kill(id).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn session_registry_rejects_missing_session() {
|
||||
let mut registry = TerminalSessionRegistry::default();
|
||||
let err = registry.write(TerminalSessionId(999), b"x").unwrap_err();
|
||||
|
||||
assert_eq!(err, TerminalSessionError::MissingSession(999));
|
||||
}
|
||||
}
|
||||
+110
-1
@@ -5,9 +5,16 @@ use rabby_core::{
|
||||
use rabby_runtime::config_store::{ConfigStore, JsonFileConfigStore};
|
||||
use rabby_runtime::local_terminal::{LocalCommand, LocalCommandOutput, LocalTerminal};
|
||||
use rabby_runtime::ssh_runtime::{SshClient, SshCommand, SshCommandOutput};
|
||||
use rabby_runtime::terminal_session::{
|
||||
TerminalSessionId, TerminalSessionRegistry, TerminalSessionSize,
|
||||
};
|
||||
use rabby_runtime::wallet_vault::{EncryptedWalletVault, WalletVaultStatus};
|
||||
use serde::Serialize;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Mutex;
|
||||
use std::time::Duration;
|
||||
|
||||
struct TerminalState(Mutex<TerminalSessionRegistry>);
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct FeatureRow {
|
||||
@@ -92,6 +99,90 @@ fn run_ssh_command(
|
||||
.map_err(|err| format!("{err:?}"))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn terminal_session_start(
|
||||
state: tauri::State<TerminalState>,
|
||||
shell: Option<String>,
|
||||
cols: Option<u16>,
|
||||
rows: Option<u16>,
|
||||
) -> Result<u64, String> {
|
||||
let shell =
|
||||
shell.unwrap_or_else(|| std::env::var("SHELL").unwrap_or_else(|_| "/bin/sh".to_string()));
|
||||
let mut registry = state
|
||||
.0
|
||||
.lock()
|
||||
.map_err(|_| "terminal registry lock poisoned".to_string())?;
|
||||
registry
|
||||
.spawn_local(shell, cols.unwrap_or(80), rows.unwrap_or(24))
|
||||
.map(|id| id.0)
|
||||
.map_err(|err| err.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn terminal_session_write(
|
||||
state: tauri::State<TerminalState>,
|
||||
id: u64,
|
||||
input: String,
|
||||
) -> Result<(), String> {
|
||||
let mut registry = state
|
||||
.0
|
||||
.lock()
|
||||
.map_err(|_| "terminal registry lock poisoned".to_string())?;
|
||||
registry
|
||||
.write(TerminalSessionId(id), input.as_bytes())
|
||||
.map_err(|err| err.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn terminal_session_read_until(
|
||||
state: tauri::State<TerminalState>,
|
||||
id: u64,
|
||||
needle: String,
|
||||
timeout_ms: Option<u64>,
|
||||
) -> Result<String, String> {
|
||||
let mut registry = state
|
||||
.0
|
||||
.lock()
|
||||
.map_err(|_| "terminal registry lock poisoned".to_string())?;
|
||||
registry
|
||||
.read_until(
|
||||
TerminalSessionId(id),
|
||||
&needle,
|
||||
Duration::from_millis(timeout_ms.unwrap_or(1500)),
|
||||
)
|
||||
.map_err(|err| err.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn terminal_session_resize(
|
||||
state: tauri::State<TerminalState>,
|
||||
id: u64,
|
||||
cols: u16,
|
||||
rows: u16,
|
||||
) -> Result<TerminalSessionSize, String> {
|
||||
let mut registry = state
|
||||
.0
|
||||
.lock()
|
||||
.map_err(|_| "terminal registry lock poisoned".to_string())?;
|
||||
registry
|
||||
.resize(TerminalSessionId(id), cols, rows)
|
||||
.map_err(|err| err.to_string())?;
|
||||
registry
|
||||
.size(TerminalSessionId(id))
|
||||
.map_err(|err| err.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn terminal_session_kill(state: tauri::State<TerminalState>, id: u64) -> Result<(), String> {
|
||||
let mut registry = state
|
||||
.0
|
||||
.lock()
|
||||
.map_err(|_| "terminal registry lock poisoned".to_string())?;
|
||||
registry
|
||||
.kill(TerminalSessionId(id))
|
||||
.map_err(|err| err.to_string())
|
||||
}
|
||||
|
||||
fn resolve_vault_path(path: Option<String>) -> PathBuf {
|
||||
path.map(PathBuf::from)
|
||||
.unwrap_or_else(|| std::env::temp_dir().join("rabby-demo-wallet-vault.json"))
|
||||
@@ -124,6 +215,9 @@ fn resolve_config_path(path: Option<String>) -> PathBuf {
|
||||
|
||||
fn main() {
|
||||
tauri::Builder::default()
|
||||
.manage(TerminalState(
|
||||
Mutex::new(TerminalSessionRegistry::default()),
|
||||
))
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
workspace_summary,
|
||||
wallet_mvp_summary,
|
||||
@@ -133,7 +227,12 @@ fn main() {
|
||||
run_local_command,
|
||||
run_ssh_command,
|
||||
load_app_config,
|
||||
save_app_config
|
||||
save_app_config,
|
||||
terminal_session_start,
|
||||
terminal_session_write,
|
||||
terminal_session_read_until,
|
||||
terminal_session_resize,
|
||||
terminal_session_kill
|
||||
])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("failed to run Rabby Tauri application");
|
||||
@@ -181,6 +280,16 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn terminal_session_size_is_returnable_from_resize_command() {
|
||||
let size = TerminalSessionSize {
|
||||
cols: 100,
|
||||
rows: 30,
|
||||
};
|
||||
assert_eq!(size.cols, 100);
|
||||
assert_eq!(size.rows, 30);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn save_and_load_app_config_round_trips_custom_theme() {
|
||||
let unique = std::time::SystemTime::now()
|
||||
|
||||
Reference in New Issue
Block a user