mirror of
https://github.com/harivansh-afk/sandbox-agent.git
synced 2026-04-16 12:03:27 +00:00
refactor: rename engine/ to server/
This commit is contained in:
parent
016024c04b
commit
71ab40388c
37 changed files with 917 additions and 3 deletions
302
server/packages/error/src/lib.rs
Normal file
302
server/packages/error/src/lib.rs
Normal file
|
|
@ -0,0 +1,302 @@
|
|||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{Map, Value};
|
||||
use thiserror::Error;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema, ToSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ErrorType {
|
||||
InvalidRequest,
|
||||
UnsupportedAgent,
|
||||
AgentNotInstalled,
|
||||
InstallFailed,
|
||||
AgentProcessExited,
|
||||
TokenInvalid,
|
||||
PermissionDenied,
|
||||
SessionNotFound,
|
||||
SessionAlreadyExists,
|
||||
ModeNotSupported,
|
||||
StreamError,
|
||||
Timeout,
|
||||
}
|
||||
|
||||
impl ErrorType {
|
||||
pub fn as_urn(&self) -> &'static str {
|
||||
match self {
|
||||
Self::InvalidRequest => "urn:sandbox-agent:error:invalid_request",
|
||||
Self::UnsupportedAgent => "urn:sandbox-agent:error:unsupported_agent",
|
||||
Self::AgentNotInstalled => "urn:sandbox-agent:error:agent_not_installed",
|
||||
Self::InstallFailed => "urn:sandbox-agent:error:install_failed",
|
||||
Self::AgentProcessExited => "urn:sandbox-agent:error:agent_process_exited",
|
||||
Self::TokenInvalid => "urn:sandbox-agent:error:token_invalid",
|
||||
Self::PermissionDenied => "urn:sandbox-agent:error:permission_denied",
|
||||
Self::SessionNotFound => "urn:sandbox-agent:error:session_not_found",
|
||||
Self::SessionAlreadyExists => "urn:sandbox-agent:error:session_already_exists",
|
||||
Self::ModeNotSupported => "urn:sandbox-agent:error:mode_not_supported",
|
||||
Self::StreamError => "urn:sandbox-agent:error:stream_error",
|
||||
Self::Timeout => "urn:sandbox-agent:error:timeout",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn title(&self) -> &'static str {
|
||||
match self {
|
||||
Self::InvalidRequest => "Invalid Request",
|
||||
Self::UnsupportedAgent => "Unsupported Agent",
|
||||
Self::AgentNotInstalled => "Agent Not Installed",
|
||||
Self::InstallFailed => "Install Failed",
|
||||
Self::AgentProcessExited => "Agent Process Exited",
|
||||
Self::TokenInvalid => "Token Invalid",
|
||||
Self::PermissionDenied => "Permission Denied",
|
||||
Self::SessionNotFound => "Session Not Found",
|
||||
Self::SessionAlreadyExists => "Session Already Exists",
|
||||
Self::ModeNotSupported => "Mode Not Supported",
|
||||
Self::StreamError => "Stream Error",
|
||||
Self::Timeout => "Timeout",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn status_code(&self) -> u16 {
|
||||
match self {
|
||||
Self::InvalidRequest => 400,
|
||||
Self::UnsupportedAgent => 400,
|
||||
Self::AgentNotInstalled => 404,
|
||||
Self::InstallFailed => 500,
|
||||
Self::AgentProcessExited => 500,
|
||||
Self::TokenInvalid => 401,
|
||||
Self::PermissionDenied => 403,
|
||||
Self::SessionNotFound => 404,
|
||||
Self::SessionAlreadyExists => 409,
|
||||
Self::ModeNotSupported => 400,
|
||||
Self::StreamError => 502,
|
||||
Self::Timeout => 504,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, ToSchema)]
|
||||
pub struct ProblemDetails {
|
||||
#[serde(rename = "type")]
|
||||
pub type_: String,
|
||||
pub title: String,
|
||||
pub status: u16,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub detail: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub instance: Option<String>,
|
||||
#[serde(flatten, default, skip_serializing_if = "Map::is_empty")]
|
||||
pub extensions: Map<String, Value>,
|
||||
}
|
||||
|
||||
impl ProblemDetails {
|
||||
pub fn new(error_type: ErrorType, detail: Option<String>) -> Self {
|
||||
Self {
|
||||
type_: error_type.as_urn().to_string(),
|
||||
title: error_type.title().to_string(),
|
||||
status: error_type.status_code(),
|
||||
detail,
|
||||
instance: None,
|
||||
extensions: Map::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, ToSchema)]
|
||||
pub struct AgentError {
|
||||
#[serde(rename = "type")]
|
||||
pub type_: ErrorType,
|
||||
pub message: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub agent: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub session_id: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub details: Option<Value>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum SandboxError {
|
||||
#[error("invalid request: {message}")]
|
||||
InvalidRequest { message: String },
|
||||
#[error("unsupported agent: {agent}")]
|
||||
UnsupportedAgent { agent: String },
|
||||
#[error("agent not installed: {agent}")]
|
||||
AgentNotInstalled { agent: String },
|
||||
#[error("install failed: {agent}")]
|
||||
InstallFailed { agent: String, stderr: Option<String> },
|
||||
#[error("agent process exited: {agent}")]
|
||||
AgentProcessExited {
|
||||
agent: String,
|
||||
exit_code: Option<i32>,
|
||||
stderr: Option<String>,
|
||||
},
|
||||
#[error("token invalid")]
|
||||
TokenInvalid { message: Option<String> },
|
||||
#[error("permission denied")]
|
||||
PermissionDenied { message: Option<String> },
|
||||
#[error("session not found: {session_id}")]
|
||||
SessionNotFound { session_id: String },
|
||||
#[error("session already exists: {session_id}")]
|
||||
SessionAlreadyExists { session_id: String },
|
||||
#[error("mode not supported: {agent} {mode}")]
|
||||
ModeNotSupported { agent: String, mode: String },
|
||||
#[error("stream error: {message}")]
|
||||
StreamError { message: String },
|
||||
#[error("timeout")]
|
||||
Timeout { message: Option<String> },
|
||||
}
|
||||
|
||||
impl SandboxError {
|
||||
pub fn error_type(&self) -> ErrorType {
|
||||
match self {
|
||||
Self::InvalidRequest { .. } => ErrorType::InvalidRequest,
|
||||
Self::UnsupportedAgent { .. } => ErrorType::UnsupportedAgent,
|
||||
Self::AgentNotInstalled { .. } => ErrorType::AgentNotInstalled,
|
||||
Self::InstallFailed { .. } => ErrorType::InstallFailed,
|
||||
Self::AgentProcessExited { .. } => ErrorType::AgentProcessExited,
|
||||
Self::TokenInvalid { .. } => ErrorType::TokenInvalid,
|
||||
Self::PermissionDenied { .. } => ErrorType::PermissionDenied,
|
||||
Self::SessionNotFound { .. } => ErrorType::SessionNotFound,
|
||||
Self::SessionAlreadyExists { .. } => ErrorType::SessionAlreadyExists,
|
||||
Self::ModeNotSupported { .. } => ErrorType::ModeNotSupported,
|
||||
Self::StreamError { .. } => ErrorType::StreamError,
|
||||
Self::Timeout { .. } => ErrorType::Timeout,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_agent_error(&self) -> AgentError {
|
||||
let (agent, session_id, details) = match self {
|
||||
Self::InvalidRequest { .. } => (None, None, None),
|
||||
Self::UnsupportedAgent { agent } => {
|
||||
(Some(agent.clone()), None, None)
|
||||
}
|
||||
Self::AgentNotInstalled { agent } => (Some(agent.clone()), None, None),
|
||||
Self::InstallFailed { agent, stderr } => {
|
||||
let mut map = Map::new();
|
||||
if let Some(stderr) = stderr {
|
||||
map.insert("stderr".to_string(), Value::String(stderr.clone()));
|
||||
}
|
||||
(
|
||||
Some(agent.clone()),
|
||||
None,
|
||||
if map.is_empty() { None } else { Some(Value::Object(map)) },
|
||||
)
|
||||
}
|
||||
Self::AgentProcessExited {
|
||||
agent,
|
||||
exit_code,
|
||||
stderr,
|
||||
} => {
|
||||
let mut map = Map::new();
|
||||
if let Some(code) = exit_code {
|
||||
map.insert(
|
||||
"exitCode".to_string(),
|
||||
Value::Number(serde_json::Number::from(*code as i64)),
|
||||
);
|
||||
}
|
||||
if let Some(stderr) = stderr {
|
||||
map.insert("stderr".to_string(), Value::String(stderr.clone()));
|
||||
}
|
||||
(
|
||||
Some(agent.clone()),
|
||||
None,
|
||||
if map.is_empty() { None } else { Some(Value::Object(map)) },
|
||||
)
|
||||
}
|
||||
Self::TokenInvalid { message } => {
|
||||
let details = message.as_ref().map(|msg| {
|
||||
let mut map = Map::new();
|
||||
map.insert("message".to_string(), Value::String(msg.clone()));
|
||||
Value::Object(map)
|
||||
});
|
||||
(None, None, details)
|
||||
}
|
||||
Self::PermissionDenied { message } => {
|
||||
let details = message.as_ref().map(|msg| {
|
||||
let mut map = Map::new();
|
||||
map.insert("message".to_string(), Value::String(msg.clone()));
|
||||
Value::Object(map)
|
||||
});
|
||||
(None, None, details)
|
||||
}
|
||||
Self::SessionNotFound { session_id } => {
|
||||
(None, Some(session_id.clone()), None)
|
||||
}
|
||||
Self::SessionAlreadyExists { session_id } => {
|
||||
(None, Some(session_id.clone()), None)
|
||||
}
|
||||
Self::ModeNotSupported { agent, mode } => {
|
||||
let mut map = Map::new();
|
||||
map.insert("mode".to_string(), Value::String(mode.clone()));
|
||||
(
|
||||
Some(agent.clone()),
|
||||
None,
|
||||
Some(Value::Object(map)),
|
||||
)
|
||||
}
|
||||
Self::StreamError { message } => {
|
||||
let mut map = Map::new();
|
||||
map.insert("message".to_string(), Value::String(message.clone()));
|
||||
(None, None, Some(Value::Object(map)))
|
||||
}
|
||||
Self::Timeout { message } => {
|
||||
let details = message.as_ref().map(|msg| {
|
||||
let mut map = Map::new();
|
||||
map.insert("message".to_string(), Value::String(msg.clone()));
|
||||
Value::Object(map)
|
||||
});
|
||||
(None, None, details)
|
||||
}
|
||||
};
|
||||
|
||||
AgentError {
|
||||
type_: self.error_type(),
|
||||
message: self.to_string(),
|
||||
agent,
|
||||
session_id,
|
||||
details,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_problem_details(&self) -> ProblemDetails {
|
||||
let mut problem = ProblemDetails::new(self.error_type(), Some(self.to_string()));
|
||||
let agent_error = self.to_agent_error();
|
||||
|
||||
let mut extensions = Map::new();
|
||||
if let Some(agent) = agent_error.agent {
|
||||
extensions.insert("agent".to_string(), Value::String(agent));
|
||||
}
|
||||
if let Some(session_id) = agent_error.session_id {
|
||||
extensions.insert("sessionId".to_string(), Value::String(session_id));
|
||||
}
|
||||
if let Some(details) = agent_error.details {
|
||||
extensions.insert("details".to_string(), details);
|
||||
}
|
||||
problem.extensions = extensions;
|
||||
problem
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SandboxError> for ProblemDetails {
|
||||
fn from(value: SandboxError) -> Self {
|
||||
value.to_problem_details()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&SandboxError> for ProblemDetails {
|
||||
fn from(value: &SandboxError) -> Self {
|
||||
value.to_problem_details()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SandboxError> for AgentError {
|
||||
fn from(value: SandboxError) -> Self {
|
||||
value.to_agent_error()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&SandboxError> for AgentError {
|
||||
fn from(value: &SandboxError) -> Self {
|
||||
value.to_agent_error()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue