Mountain/IPC/WindServiceHandlers/Terminal/SerializeTerminalState.rs
1
2//! Serialise all active terminals to the `ISerializedTerminalState[]` shape
3//! that VS Code's `ILocalPtyService.serializeTerminalProcesses` contract
4//! requires.
5//!
6//! VS Code calls this immediately before a window reload to snapshot running
7//! PTY state. The result is written to storage and later passed back via
8//! `localPty:reviveTerminalProcesses` so the workbench can restore the panel
9//! without the user losing their shell sessions.
10//!
11//! ## Output shape per terminal
12//! ```json
13//! {
14//! "id": 1,
15//! "shellLaunchConfig": { "name": "zsh", "executable": "/bin/zsh", "args": [] },
16//! "processDetails": { "cwd": "/...", "pid": 1234, "title": "zsh" },
17//! "orphanQuestionReply": false,
18//! "replayEvent": { "events": [] },
19//! "timestamp": 1716134400000
20//! }
21//! ```
22//!
23//! Runtime handles (`PTYMaster`, `PTYInputTransmitter`, task `JoinHandle`s)
24//! are `#[serde(skip)]` in `TerminalStateDTO` and are intentionally absent
25//! from the wire payload - only the configuration fields needed to respawn
26//! the shell are serialised.
27
28use std::sync::Arc;
29
30use serde_json::{Value, json};
31
32use crate::RunTime::ApplicationRunTime::ApplicationRunTime;
33
34pub async fn Fn(RunTime:Arc<ApplicationRunTime>) -> Result<Value, String> {
35 let Terminals = RunTime
36 .Environment
37 .ApplicationState
38 .Feature
39 .Terminals
40 .ActiveTerminals
41 .lock()
42 .map_err(|Error| format!("SerializeTerminalState: lock poisoned: {}", Error))?;
43
44 let NowMs = std::time::SystemTime::now()
45 .duration_since(std::time::UNIX_EPOCH)
46 .map(|D| D.as_millis() as u64)
47 .unwrap_or(0);
48
49 let Serialized:Vec<Value> = Terminals
50 .iter()
51 .filter_map(|(TerminalId, ArcState)| {
52 let State = ArcState.lock().ok()?;
53
54 let Cwd = State.GetWorkingDirectory();
55
56 let Pid = State.OSProcessIdentifier.unwrap_or(0) as u64;
57
58 Some(json!({
59 "id": TerminalId,
60 "shellLaunchConfig": {
61 "name": State.Name,
62 "executable": State.ShellPath,
63 "args": State.ShellArguments,
64 "cwd": Cwd,
65 },
66 "processDetails": {
67 "cwd": Cwd,
68 "pid": Pid,
69 "title": State.Name,
70 },
71 // False means the orphan-question dialog was NOT shown;
72 // revived terminals start fresh without a stale prompt.
73 "orphanQuestionReply": false,
74 // Empty replay - the xterm buffer will be restored from the
75 // output replay buffer separately via `sky:replay-events`.
76 "replayEvent": { "events": [] },
77 "timestamp": NowMs,
78 }))
79 })
80 .collect();
81
82 Ok(Value::Array(Serialized))
83}