codex-mobile-wire 0.2.0

Shared wire protocol types for codex-mobile rust components.
Documentation
use codex_mobile_contracts::{
    ApiError, DirectoryBookmarkRecord, DirectoryHistoryRecord, PendingServerRequestRecord,
    RuntimeStatusSnapshot, RuntimeSummary,
};
use serde::{Deserialize, Serialize};
use serde_json::Value;

#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct PersistedEvent {
    pub seq: i64,
    pub event_type: String,
    pub runtime_id: Option<String>,
    pub thread_id: Option<String>,
    pub payload: Value,
    pub created_at_ms: i64,
}

#[derive(Debug, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum ClientEnvelope {
    Hello {
        device_id: String,
        last_ack_seq: Option<i64>,
    },
    Request {
        request_id: String,
        action: String,
        #[serde(default)]
        payload: Value,
    },
    AckEvents {
        last_seq: i64,
    },
    Ping,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum ServerEnvelope {
    Hello {
        bridge_version: String,
        protocol_version: i32,
        runtime: RuntimeStatusSnapshot,
        runtimes: Vec<RuntimeSummary>,
        directory_bookmarks: Vec<DirectoryBookmarkRecord>,
        directory_history: Vec<DirectoryHistoryRecord>,
        pending_requests: Vec<PendingServerRequestRecord>,
    },
    Response {
        request_id: String,
        success: bool,
        #[serde(skip_serializing_if = "Option::is_none")]
        data: Option<Value>,
        #[serde(skip_serializing_if = "Option::is_none")]
        error: Option<ApiError>,
    },
    Event {
        seq: i64,
        event_type: String,
        #[serde(skip_serializing_if = "Option::is_none")]
        runtime_id: Option<String>,
        #[serde(skip_serializing_if = "Option::is_none")]
        thread_id: Option<String>,
        payload: Value,
    },
    Pong {
        server_time_ms: i64,
    },
}

pub fn ok_response(request_id: String, data: Value) -> ServerEnvelope {
    ServerEnvelope::Response {
        request_id,
        success: true,
        data: Some(data),
        error: None,
    }
}

pub fn event_envelope(event: PersistedEvent) -> ServerEnvelope {
    ServerEnvelope::Event {
        seq: event.seq,
        event_type: event.event_type,
        runtime_id: event.runtime_id,
        thread_id: event.thread_id,
        payload: event.payload,
    }
}