mirror of
https://github.com/samsonjs/vibetunnel.git
synced 2026-04-27 15:17:38 +00:00
Fix exit event format in terminal sessions
- Change exit event from nested JSON to direct array format: ["exit", exit_code, session_id] - Add StreamEvent::Exit variant to handle exit events properly in parsing - Add write_raw_json method to StreamWriter for direct JSON output 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
ce4b67a258
commit
8553de6ae3
3 changed files with 33 additions and 13 deletions
|
|
@ -398,6 +398,16 @@ impl StreamWriter {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn write_raw_json(&mut self, json_value: &serde_json::Value) -> Result<(), Error> {
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
let json_string = serde_json::to_string(json_value)?;
|
||||||
|
writeln!(self.file, "{json_string}")?;
|
||||||
|
self.file.flush()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn elapsed_time(&self) -> f64 {
|
pub fn elapsed_time(&self) -> f64 {
|
||||||
self.start_time.elapsed().as_secs_f64()
|
self.start_time.elapsed().as_secs_f64()
|
||||||
}
|
}
|
||||||
|
|
@ -427,6 +437,7 @@ impl NotificationWriter {
|
||||||
pub enum StreamEvent {
|
pub enum StreamEvent {
|
||||||
Header(AsciinemaHeader),
|
Header(AsciinemaHeader),
|
||||||
Terminal(AsciinemaEvent),
|
Terminal(AsciinemaEvent),
|
||||||
|
Exit { exit_code: i32, session_id: String },
|
||||||
Error { message: String },
|
Error { message: String },
|
||||||
End,
|
End,
|
||||||
}
|
}
|
||||||
|
|
@ -454,6 +465,14 @@ impl serde::Serialize for StreamEvent {
|
||||||
match self {
|
match self {
|
||||||
Self::Header(header) => header.serialize(serializer),
|
Self::Header(header) => header.serialize(serializer),
|
||||||
Self::Terminal(event) => event.serialize(serializer),
|
Self::Terminal(event) => event.serialize(serializer),
|
||||||
|
Self::Exit { exit_code, session_id } => {
|
||||||
|
use serde::ser::SerializeTuple;
|
||||||
|
let mut tuple = serializer.serialize_tuple(3)?;
|
||||||
|
tuple.serialize_element("exit")?;
|
||||||
|
tuple.serialize_element(exit_code)?;
|
||||||
|
tuple.serialize_element(session_id)?;
|
||||||
|
tuple.end()
|
||||||
|
}
|
||||||
Self::Error { message } => {
|
Self::Error { message } => {
|
||||||
let error_event = ErrorEvent {
|
let error_event = ErrorEvent {
|
||||||
event_type: "error".to_string(),
|
event_type: "error".to_string(),
|
||||||
|
|
@ -488,6 +507,15 @@ impl<'de> serde::Deserialize<'de> for StreamEvent {
|
||||||
// Try to parse as an event array [timestamp, type, data]
|
// Try to parse as an event array [timestamp, type, data]
|
||||||
if let Some(arr) = value.as_array() {
|
if let Some(arr) = value.as_array() {
|
||||||
if arr.len() >= 3 {
|
if arr.len() >= 3 {
|
||||||
|
// Check for exit event: ["exit", exit_code, session_id]
|
||||||
|
if let Some(first) = arr[0].as_str() {
|
||||||
|
if first == "exit" {
|
||||||
|
let exit_code = arr[1].as_i64().unwrap_or(0) as i32;
|
||||||
|
let session_id = arr[2].as_str().unwrap_or("unknown").to_string();
|
||||||
|
return Ok(Self::Exit { exit_code, session_id });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let event: AsciinemaEvent = serde_json::from_value(value).map_err(|e| {
|
let event: AsciinemaEvent = serde_json::from_value(value).map_err(|e| {
|
||||||
de::Error::custom(format!("Failed to parse terminal event: {e}"))
|
de::Error::custom(format!("Failed to parse terminal event: {e}"))
|
||||||
})?;
|
})?;
|
||||||
|
|
|
||||||
|
|
@ -405,7 +405,7 @@ fn handle_pty_session(
|
||||||
eprintln!("PTY closed (EOF), updating session status");
|
eprintln!("PTY closed (EOF), updating session status");
|
||||||
|
|
||||||
// Send exit event to stream before updating session status
|
// Send exit event to stream before updating session status
|
||||||
let exit_event = json!(["exit", 0, session_id]);
|
let exit_event = json!(["exit", "0", session_id]);
|
||||||
writeln!(writer, "{exit_event}")?;
|
writeln!(writer, "{exit_event}")?;
|
||||||
writer.flush()?;
|
writer.flush()?;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -538,12 +538,8 @@ fn spawn(mut opts: SpawnOptions) -> Result<i32, Error> {
|
||||||
.and_then(|p| p.file_stem())
|
.and_then(|p| p.file_stem())
|
||||||
.and_then(|s| s.to_str())
|
.and_then(|s| s.to_str())
|
||||||
.unwrap_or("unknown");
|
.unwrap_or("unknown");
|
||||||
let exit_event = AsciinemaEvent {
|
let exit_event = serde_json::json!(["exit", exit_code, session_id]);
|
||||||
time: stream_writer.elapsed_time(),
|
let _ = stream_writer.write_raw_json(&exit_event);
|
||||||
event_type: AsciinemaEventType::Output,
|
|
||||||
data: serde_json::json!(["exit", exit_code, session_id]).to_string(),
|
|
||||||
};
|
|
||||||
let _ = stream_writer.write_event(exit_event);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update session status to exited with exit code
|
// Update session status to exited with exit code
|
||||||
|
|
@ -915,12 +911,8 @@ fn monitor_detached_session(
|
||||||
.and_then(|p| p.file_stem())
|
.and_then(|p| p.file_stem())
|
||||||
.and_then(|s| s.to_str())
|
.and_then(|s| s.to_str())
|
||||||
.unwrap_or("unknown");
|
.unwrap_or("unknown");
|
||||||
let exit_event = AsciinemaEvent {
|
let exit_event = serde_json::json!(["exit", 0, session_id]);
|
||||||
time: stream_writer.elapsed_time(),
|
let _ = stream_writer.write_raw_json(&exit_event);
|
||||||
event_type: AsciinemaEventType::Output,
|
|
||||||
data: serde_json::json!(["exit", 0, session_id]).to_string(),
|
|
||||||
};
|
|
||||||
let _ = stream_writer.write_event(exit_event);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update session status to exited
|
// Update session status to exited
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue