send command telemetry one word at a time instead of one letter at a
Some checks failed
Some checks failed
time. remove escape sequences.
This commit is contained in:
parent
7bfbe0d86e
commit
d7efa60cea
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -2626,6 +2626,7 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"pop-telemetry",
|
"pop-telemetry",
|
||||||
"portable-pty",
|
"portable-pty",
|
||||||
|
"regex",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
|||||||
@ -31,6 +31,7 @@ env_logger = "0.11"
|
|||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
pop-telemetry = { git = "https://github.com/jasonwitty/pop-cli", branch = "main" }
|
pop-telemetry = { git = "https://github.com/jasonwitty/pop-cli", branch = "main" }
|
||||||
dirs = "5.0"
|
dirs = "5.0"
|
||||||
|
regex = "1.10"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "webterm"
|
name = "webterm"
|
||||||
|
|||||||
49
src/lib.rs
49
src/lib.rs
@ -44,7 +44,6 @@ use serde_json::json;
|
|||||||
|
|
||||||
const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(5);
|
const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(5);
|
||||||
const CLIENT_TIMEOUT: Duration = Duration::from_secs(10);
|
const CLIENT_TIMEOUT: Duration = Duration::from_secs(10);
|
||||||
const IDLE_TIMEOUT: Duration = Duration::from_secs(300); // 5 minutes
|
|
||||||
const IDLE_CHECK_INTERVAL: Duration = Duration::from_secs(30); // Check every 30 seconds
|
const IDLE_CHECK_INTERVAL: Duration = Duration::from_secs(30); // Check every 30 seconds
|
||||||
|
|
||||||
pub mod analytics;
|
pub mod analytics;
|
||||||
@ -229,6 +228,7 @@ pub struct Terminal {
|
|||||||
last_activity: Instant,
|
last_activity: Instant,
|
||||||
idle_timeout: Duration,
|
idle_timeout: Duration,
|
||||||
analytics: Option<Analytics>,
|
analytics: Option<Analytics>,
|
||||||
|
command_buffer: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Terminal {
|
impl Terminal {
|
||||||
@ -240,8 +240,9 @@ impl Terminal {
|
|||||||
ws,
|
ws,
|
||||||
command,
|
command,
|
||||||
last_activity: Instant::now(),
|
last_activity: Instant::now(),
|
||||||
idle_timeout: IDLE_TIMEOUT,
|
idle_timeout: Duration::from_secs(300),
|
||||||
analytics: None,
|
analytics: None,
|
||||||
|
command_buffer: String::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -253,8 +254,9 @@ impl Terminal {
|
|||||||
ws,
|
ws,
|
||||||
command,
|
command,
|
||||||
last_activity: Instant::now(),
|
last_activity: Instant::now(),
|
||||||
idle_timeout: IDLE_TIMEOUT,
|
idle_timeout: Duration::from_secs(300),
|
||||||
analytics: Some(analytics),
|
analytics: Some(analytics),
|
||||||
|
command_buffer: String::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -417,15 +419,52 @@ impl Handler<TerminadoMessage> for Terminal {
|
|||||||
// Reset idle timer on user input
|
// Reset idle timer on user input
|
||||||
self.last_activity = Instant::now();
|
self.last_activity = Instant::now();
|
||||||
|
|
||||||
// Track command in analytics
|
// Buffer input and track command only when Enter is pressed
|
||||||
if let Some(analytics) = &self.analytics {
|
if let Some(analytics) = &self.analytics {
|
||||||
let command = String::from_utf8_lossy(&io.0).to_string();
|
let input = String::from_utf8_lossy(&io.0).to_string();
|
||||||
|
|
||||||
|
// Check if input contains newline (Enter key)
|
||||||
|
if input.contains('\n') || input.contains('\r') {
|
||||||
|
// Strip ANSI escape sequences and control codes
|
||||||
|
// Pattern matches: ESC[ followed by any characters until a letter
|
||||||
|
let mut cleaned_command = self.command_buffer.clone();
|
||||||
|
|
||||||
|
// Remove ANSI escape sequences like [<35;2;1M
|
||||||
|
// This regex pattern matches ESC [ followed by any non-letter chars and ending with a letter
|
||||||
|
let escape_pattern = regex::Regex::new(r"\x1b\[[^\x1b]*?[a-zA-Z]").unwrap();
|
||||||
|
cleaned_command =
|
||||||
|
escape_pattern.replace_all(&cleaned_command, "").to_string();
|
||||||
|
|
||||||
|
// Remove CSI sequences without ESC prefix like [<35;2;1M
|
||||||
|
let csi_pattern = regex::Regex::new(r"\[<[0-9;]+[a-zA-Z]").unwrap();
|
||||||
|
cleaned_command = csi_pattern.replace_all(&cleaned_command, "").to_string();
|
||||||
|
|
||||||
|
// Remove any remaining control characters
|
||||||
|
cleaned_command = cleaned_command
|
||||||
|
.chars()
|
||||||
|
.filter(|c| !c.is_control() || c.is_ascii_whitespace())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let command = cleaned_command.trim().to_string();
|
||||||
|
|
||||||
|
// Track if command has actual content (alphanumeric chars)
|
||||||
|
if !command.is_empty() && command.chars().any(|c| c.is_ascii_alphanumeric())
|
||||||
|
{
|
||||||
|
log::info!("Tracking command: '{}'", command);
|
||||||
let analytics_clone = analytics.clone();
|
let analytics_clone = analytics.clone();
|
||||||
actix::spawn(async move {
|
actix::spawn(async move {
|
||||||
let _ = analytics_clone.track_command(&command, None).await;
|
let _ = analytics_clone.track_command(&command, None).await;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear buffer for next command
|
||||||
|
self.command_buffer.clear();
|
||||||
|
} else {
|
||||||
|
// Accumulate input in buffer
|
||||||
|
self.command_buffer.push_str(&input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let writer = match &mut self.pty_writer {
|
let writer = match &mut self.pty_writer {
|
||||||
Some(w) => w,
|
Some(w) => w,
|
||||||
None => {
|
None => {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user