use serde::{Deserialize, Serialize};
pub mod agents;
pub mod answers;
#[cfg(feature = "client")]
pub mod client;
pub mod conventions;
pub mod credentials;
pub mod doctor;
pub mod events;
pub mod graph;
pub mod hosting;
pub mod jobs;
pub mod ledger;
pub mod manifests;
pub mod model;
pub mod preflight;
pub mod prompts;
pub mod search;
pub mod server;
pub mod team;
pub mod view;
mod account;
mod browse;
mod records;
mod repos;
mod settings;
pub use account::*;
pub use browse::*;
pub use records::*;
pub use repos::*;
pub use settings::*;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Done {
pub ok: bool,
pub message: String,
}
impl Done {
pub fn new(message: impl Into<String>) -> Done {
Done {
ok: true,
message: message.into(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ErrorBody {
pub error: ErrorDetail,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ErrorDetail {
pub code: String,
pub message: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub host: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub username: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Health {
pub status: String,
pub version: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Ready {
pub ready: bool,
pub index: bool,
pub stores: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IdRequest {
pub id: String,
}
#[cfg(test)]
mod tests {
use super::*;
fn same<T: Serialize + serde::de::DeserializeOwned>(json: &str) {
let v: T = serde_json::from_str(json).unwrap();
assert_eq!(serde_json::to_string(&v).unwrap(), json);
}
#[test]
fn answers_keep_their_wire_shape() {
same::<Me>(r#"{"mode":"local"}"#);
same::<Me>(
r#"{"mode":"server","signed_in":false,"email":null,"role":null,"sign_in":"email","sign_in_methods":["oidc","email","code"],"oidc_label":"Microsoft","public_url":"http://localhost:1","gateway_url":"https://mcp.example.com/mcp"}"#,
);
let older: Me = serde_json::from_str(
r#"{"mode":"server","signed_in":false,"email":null,"role":null,"sign_in":"code","public_url":"http://localhost:1"}"#,
)
.unwrap();
assert!(matches!(older, Me::Server { sign_in_methods, .. } if sign_in_methods.is_empty()));
same::<DeviceToken>(r#"{"status":"pending"}"#);
same::<DeviceToken>(
r#"{"status":"approved","token":"t","email":"a@example.com","expires":null}"#,
);
same::<Secrets>(
r#"{"secrets":[{"source":"deployment","name":"llm_api_key","label":"LLM API key"},{"name":"git:github.com","source":"stored","hint":"…abcd","updated":"2026-01-01T00:00:00+00:00","updated_by":"a@example.com","label":"GitHub token for github.com"}],"can_store":true}"#,
);
same::<IndexState>(r#"{"running":false,"events":[],"queued":[]}"#);
same::<IndexState>(
r#"{"id":"j","running":false,"cancelled":false,"ok":true,"status":"succeeded","events":[],"started":"s","finished":"f","queued":[{"id":"k","kind":"index","position":1,"started_by":null,"created":"c"}]}"#,
);
same::<Jobs>(
r#"{"jobs":[{"id":"k","kind":"index","started_by":null,"status":"queued","created":"c","started":null,"finished":null,"error":null,"position":1}]}"#,
);
same::<Invited>(r#"{"ok":true,"message":"Invitation emailed to a@example.com"}"#);
same::<ErrorBody>(r#"{"error":{"code":"not_found","message":"no such job"}}"#);
same::<Neighbourhood>(r#"{"nodes":[],"edges":[]}"#);
}
}