soma_codemode/
runner_io.rs1use tokio::io::AsyncWriteExt;
2
3use crate::protocol::{CodeModeRunnerInput, CodeModeRunnerOutput};
4use crate::runner::limits::MAX_STDIO_LINE_BYTES;
5use crate::ToolError;
6
7pub fn encode_runner_input(input: &CodeModeRunnerInput) -> Result<String, ToolError> {
8 encode_json_line(input, "input")
9}
10
11pub fn encode_runner_output(output: &CodeModeRunnerOutput) -> String {
12 serde_json::to_string(output).unwrap_or_else(|_| {
13 r#"{"type":"error","kind":"internal_error","message":"failed to encode output"}"#
14 .to_string()
15 })
16}
17
18pub fn decode_runner_input(line: &str) -> Result<CodeModeRunnerInput, ToolError> {
19 if line.len() > MAX_STDIO_LINE_BYTES {
20 return Err(ToolError::Sdk {
21 sdk_kind: "invalid_param".to_string(),
22 message: "runner input line exceeded limit".to_string(),
23 });
24 }
25 serde_json::from_str(line).map_err(|err| ToolError::Sdk {
26 sdk_kind: "invalid_param".to_string(),
27 message: format!("invalid runner input JSON: {err}"),
28 })
29}
30
31pub fn decode_runner_output(line: &str) -> Result<CodeModeRunnerOutput, ToolError> {
32 if line.len() > MAX_STDIO_LINE_BYTES {
33 return Err(ToolError::Sdk {
34 sdk_kind: "invalid_param".to_string(),
35 message: "runner output line exceeded limit".to_string(),
36 });
37 }
38 serde_json::from_str(line).map_err(|err| ToolError::Sdk {
39 sdk_kind: "invalid_param".to_string(),
40 message: format!("invalid runner output JSON: {err}"),
41 })
42}
43
44pub async fn write_runner_input<W: AsyncWriteExt + Unpin>(
45 writer: &mut W,
46 input: &CodeModeRunnerInput,
47) -> Result<(), ToolError> {
48 let mut line = encode_runner_input(input)?;
49 line.push('\n');
50 writer.write_all(line.as_bytes()).await.map_err(|err| {
51 ToolError::internal_message(format!("failed to write runner input: {err}"))
52 })?;
53 writer
54 .flush()
55 .await
56 .map_err(|err| ToolError::internal_message(format!("failed to flush runner input: {err}")))
57}
58
59pub async fn terminate_code_mode_runner(
60 child: &mut tokio::process::Child,
61 _child_pid: Option<u32>,
62) {
63 #[cfg(unix)]
64 if let Some(pid) = _child_pid {
65 use nix::sys::signal::Signal;
66 use nix::unistd::Pid;
67 let _ = nix::sys::signal::killpg(Pid::from_raw(pid as i32), Signal::SIGKILL);
68 }
69 let _ = child.kill().await;
70 let _ = child.wait().await;
71}
72
73fn encode_json_line<T: serde::Serialize>(value: &T, label: &str) -> Result<String, ToolError> {
74 let encoded = serde_json::to_string(value).map_err(|err| ToolError::Sdk {
75 sdk_kind: "internal_error".to_string(),
76 message: format!("failed to encode runner {label}: {err}"),
77 })?;
78 if encoded.len() > MAX_STDIO_LINE_BYTES {
79 return Err(ToolError::Sdk {
80 sdk_kind: "invalid_param".to_string(),
81 message: format!("runner {label} line exceeded limit"),
82 });
83 }
84 Ok(encoded)
85}