use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize)]
pub struct SyncPullResponse {
pub patterns: Vec<RemotePattern>,
pub version: i64,
}
#[derive(Debug, Deserialize)]
pub struct RemotePattern {
pub id: String,
pub name: String,
pub content: String,
pub version: i64,
#[serde(default)]
pub deleted: bool,
}
#[derive(Debug, Serialize)]
pub struct SyncPushRequest {
pub base_version: i64,
pub changes: Vec<PatternChange>,
#[serde(default)]
pub force_local: bool,
}
#[derive(Debug, Serialize)]
pub struct PatternChange {
pub action: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub pattern: Option<PatternPayload>,
}
#[derive(Debug, Serialize)]
pub struct PatternPayload {
pub name: String,
pub content: String,
}
#[derive(Debug, Deserialize)]
pub struct SyncPushResponse {
pub ok: bool,
#[serde(default)]
pub version: Option<i64>,
#[serde(default)]
pub conflict: Option<bool>,
}
#[derive(Debug, Deserialize)]
pub struct WorkflowListResponse {
pub data: Vec<serde_json::Value>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum FleetEntityType {
AgentProfile,
ModelBinding,
Skill,
}
impl FleetEntityType {
pub fn path_segment(self) -> &'static str {
match self {
Self::AgentProfile => "agent_profile",
Self::ModelBinding => "model_binding",
Self::Skill => "skill",
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FleetChange {
pub action: String,
pub logical_id: String,
pub content_hash: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub payload: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FleetEntity {
pub logical_id: String,
pub content_hash: String,
pub version: i64,
pub deleted: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub payload: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FleetPushRequest {
pub base_version: i64,
pub entity_type: FleetEntityType,
pub changes: Vec<FleetChange>,
#[serde(default)]
pub force_local: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FleetPushResponse {
pub ok: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub version: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub conflict: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FleetPullResponse {
pub entities: Vec<FleetEntity>,
pub version: i64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SkillFleetPayload {
pub manifest_yaml: String,
pub events_jsonl: String,
pub content_sha256: String,
}
#[cfg(test)]
mod fleet_tests {
use super::*;
#[test]
fn fleet_push_request_roundtrips() {
let req = FleetPushRequest {
base_version: 7,
entity_type: FleetEntityType::AgentProfile,
changes: vec![FleetChange {
action: "upsert".into(),
logical_id: "agent-abc".into(),
content_hash: "deadbeef".into(),
payload: Some("name: scout\n".into()),
}],
force_local: false,
};
let json = serde_json::to_string(&req).unwrap();
let back: FleetPushRequest = serde_json::from_str(&json).unwrap();
assert_eq!(back.base_version, 7);
assert_eq!(back.entity_type, FleetEntityType::AgentProfile);
assert_eq!(back.changes[0].logical_id, "agent-abc");
}
#[test]
fn fleet_entity_type_serializes_snake_case() {
assert_eq!(
serde_json::to_string(&FleetEntityType::ModelBinding).unwrap(),
"\"model_binding\""
);
}
#[test]
fn skill_entity_type_roundtrips() {
let s = serde_json::to_string(&FleetEntityType::Skill).unwrap();
assert_eq!(s, r#""skill""#);
let back: FleetEntityType = serde_json::from_str(&s).unwrap();
assert_eq!(back, FleetEntityType::Skill);
assert_eq!(FleetEntityType::Skill.path_segment(), "skill");
}
#[test]
fn skill_fleet_payload_roundtrips() {
let p = SkillFleetPayload {
manifest_yaml: "name: foo\n".into(),
events_jsonl: "{\"kind\":\"retrieval\"}\n".into(),
content_sha256: "abc123".into(),
};
let s = serde_json::to_string(&p).unwrap();
let back: SkillFleetPayload = serde_json::from_str(&s).unwrap();
assert_eq!(back.content_sha256, "abc123");
}
}