Skip to main content

oxios_kernel/kernel_handle/
space_api.rs

1//! Space API — Space management and knowledge flow system calls.
2//!
3//! Provides REST API endpoints for:
4//! - Listing and querying Spaces
5//! - Space activation/switching
6//! - Space merge and archive operations
7//! - Knowledge flow monitoring
8
9use serde::{Deserialize, Serialize};
10use std::sync::Arc;
11
12#[allow(unused_imports)]
13use crate::event_bus::EventBus;
14use crate::space::{CrossRefEntry, Space, SpaceManager};
15use anyhow::Context;
16
17/// Serialized Space info for API responses.
18#[derive(Debug, Clone, Serialize, Deserialize)]
19#[allow(missing_docs)]
20pub struct SpaceInfo {
21    pub id: String,
22    pub name: String,
23    pub source: String,
24    pub active: bool,
25    pub paths: Vec<String>,
26    pub interaction_count: u64,
27    pub knowledge_visible: bool,
28    pub last_active: String,
29}
30
31impl From<&Space> for SpaceInfo {
32    fn from(space: &Space) -> Self {
33        Self {
34            id: space.id.to_string(),
35            name: space.name.clone(),
36            source: space.source.to_string(),
37            active: space.active,
38            paths: space
39                .paths
40                .iter()
41                .map(|p| p.to_string_lossy().to_string())
42                .collect(),
43            interaction_count: space.interaction_count,
44            knowledge_visible: space.knowledge_visible,
45            last_active: space.last_active_at.to_rfc3339(),
46        }
47    }
48}
49
50/// Serialized knowledge flow entry.
51#[derive(Debug, Clone, Serialize, Deserialize)]
52#[allow(missing_docs)]
53pub struct KnowledgeFlowInfo {
54    pub from: String,
55    pub to: String,
56    pub flow_type: String,
57    pub entry_count: usize,
58    pub timestamp: String,
59}
60
61impl From<&CrossRefEntry> for KnowledgeFlowInfo {
62    fn from(entry: &CrossRefEntry) -> Self {
63        Self {
64            from: entry.from.to_string(),
65            to: entry.to.to_string(),
66            flow_type: entry.flow.to_string(),
67            entry_count: entry.entry_ids.len(),
68            timestamp: entry.timestamp.to_rfc3339(),
69        }
70    }
71}
72
73/// Space system calls.
74#[allow(dead_code)]
75pub struct SpaceApi {
76    /// Space manager for Space lifecycle and routing.
77    pub(crate) space_manager: Arc<SpaceManager>,
78    /// Event bus (reserved for future event publishing).
79    #[allow(dead_code)]
80    pub(crate) event_bus: EventBus,
81}
82
83impl SpaceApi {
84    /// Create a new SpaceApi.
85    pub fn new(space_manager: Arc<SpaceManager>, event_bus: EventBus) -> Self {
86        Self {
87            space_manager,
88            event_bus,
89        }
90    }
91
92    /// List all Spaces.
93    pub fn list_spaces(&self) -> Vec<SpaceInfo> {
94        self.space_manager
95            .list()
96            .iter()
97            .map(SpaceInfo::from)
98            .collect()
99    }
100
101    /// Get current active Space.
102    pub fn current_space(&self) -> Option<SpaceInfo> {
103        self.space_manager
104            .current_space()
105            .as_ref()
106            .map(SpaceInfo::from)
107    }
108
109    /// Get Space details by ID.
110    pub async fn get_space(&self, id: &str) -> Option<SpaceInfo> {
111        let space_id = uuid::Uuid::parse_str(id).ok()?;
112        self.space_manager
113            .get_space(&space_id)
114            .await
115            .ok()
116            .flatten()
117            .as_ref()
118            .map(SpaceInfo::from)
119    }
120
121    /// Activate a Space by ID.
122    pub async fn activate(&self, id: &str) -> anyhow::Result<()> {
123        let space_id = uuid::Uuid::parse_str(id).context("Invalid Space ID")?;
124        self.space_manager
125            .activate(&space_id)
126            .await
127            .context("Failed to activate Space")
128    }
129
130    /// Archive a Space by ID.
131    pub async fn archive(&self, id: &str) -> anyhow::Result<()> {
132        let space_id = uuid::Uuid::parse_str(id).context("Invalid Space ID")?;
133
134        let space = self
135            .space_manager
136            .get_space(&space_id)
137            .await?
138            .context("Space not found")?;
139
140        // Remove from active and save
141        self.space_manager
142            .activate(&self.space_manager.default_space_id())
143            .await?;
144
145        tracing::info!(space_id = %space_id, name = %space.name, "Space archived");
146        Ok(())
147    }
148
149    /// Merge two Spaces.
150    pub async fn merge(&self, survivor_id: &str, absorbed_id: &str) -> anyhow::Result<()> {
151        let survivor = uuid::Uuid::parse_str(survivor_id).context("Invalid survivor Space ID")?;
152        let absorbed = uuid::Uuid::parse_str(absorbed_id).context("Invalid absorbed Space ID")?;
153
154        self.space_manager
155            .merge_spaces(survivor, absorbed)
156            .await
157            .context("Failed to merge Spaces")
158    }
159
160    /// Restore an archived Space.
161    pub async fn restore(&self, id: &str) -> anyhow::Result<()> {
162        let space_id = uuid::Uuid::parse_str(id).context("Invalid Space ID")?;
163
164        self.space_manager
165            .restore_from_archive(&space_id)
166            .await
167            .context("Failed to restore Space")
168    }
169
170    /// Get recent knowledge flow entries.
171    pub fn knowledge_flow(&self) -> Vec<KnowledgeFlowInfo> {
172        self.space_manager
173            .knowledge_bridge()
174            .map(|bridge| {
175                bridge
176                    .recent_references()
177                    .iter()
178                    .map(KnowledgeFlowInfo::from)
179                    .collect()
180            })
181            .unwrap_or_default()
182    }
183
184    /// Get knowledge flow for a specific Space.
185    pub fn knowledge_flow_for(&self, id: &str) -> Option<Vec<KnowledgeFlowInfo>> {
186        let space_id = uuid::Uuid::parse_str(id).ok()?;
187        Some(
188            self.space_manager
189                .knowledge_bridge()?
190                .references_for(space_id)
191                .iter()
192                .map(KnowledgeFlowInfo::from)
193                .collect(),
194        )
195    }
196}