use std::collections::HashMap; use std::path::PathBuf; use base64::engine::general_purpose::STANDARD as BASE64_STANDARD; use codex_utils_pty::DEFAULT_OUTPUT_BYTES_CAP; use serde::Deserialize; use serde::Serialize; pub const INITIALIZE_METHOD: &str = "initialize"; pub const INITIALIZED_METHOD: &str = "initialized"; pub const EXEC_METHOD: &str = "command/exec"; pub const EXEC_WRITE_METHOD: &str = "command/exec/write"; pub const EXEC_TERMINATE_METHOD: &str = "command/exec/terminate"; pub const EXEC_OUTPUT_DELTA_METHOD: &str = "command/exec/outputDelta"; pub const EXEC_EXITED_METHOD: &str = "command/exec/exited"; pub const PROTOCOL_VERSION: &str = "exec-server.v0"; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(transparent)] pub struct ByteChunk(#[serde(with = "base64_bytes")] pub Vec); impl ByteChunk { pub fn into_inner(self) -> Vec { self.0 } } impl From> for ByteChunk { fn from(value: Vec) -> Self { Self(value) } } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct InitializeParams { pub client_name: String, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct InitializeResponse { pub protocol_version: String, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ExecParams { pub process_id: String, pub argv: Vec, pub cwd: PathBuf, pub env: HashMap, pub tty: bool, #[serde(default = "default_output_bytes_cap")] pub output_bytes_cap: usize, pub arg0: Option, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ExecResponse { pub process_id: String, pub running: bool, pub exit_code: Option, pub stdout: Option, pub stderr: Option, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct WriteParams { pub process_id: String, pub chunk: ByteChunk, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct WriteResponse { pub accepted: bool, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct TerminateParams { pub process_id: String, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct TerminateResponse { pub running: bool, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub enum ExecOutputStream { Stdout, Stderr, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ExecOutputDeltaNotification { pub process_id: String, pub stream: ExecOutputStream, pub chunk: ByteChunk, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ExecExitedNotification { pub process_id: String, pub exit_code: i32, } fn default_output_bytes_cap() -> usize { DEFAULT_OUTPUT_BYTES_CAP } mod base64_bytes { use super::BASE64_STANDARD; use base64::Engine as _; use serde::Deserialize; use serde::Deserializer; use serde::Serializer; pub fn serialize(bytes: &[u8], serializer: S) -> Result where S: Serializer, { serializer.serialize_str(&BASE64_STANDARD.encode(bytes)) } pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { let encoded = String::deserialize(deserializer)?; BASE64_STANDARD .decode(encoded) .map_err(serde::de::Error::custom) } }