Skip to main content

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}