1use serde::{Deserialize, Serialize};
2use serde_json::Value as JsonValue;
3
4#[derive(Debug, Clone, Serialize, Deserialize)]
5pub struct OsStatus {
6 pub runtime: RuntimeStatus,
7 pub postgres: PostgresStatus,
8 pub forge: ForgeStatus,
9}
10
11impl OsStatus {
12 pub fn offline() -> Self {
13 Self {
14 runtime: RuntimeStatus { version: String::new(), state: ServiceState::Offline },
15 postgres: PostgresStatus { state: ServiceState::Offline, port: None, data_dir: None },
16 forge: ForgeStatus { state: ServiceState::Offline, port: None },
17 }
18 }
19}
20
21#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct ForgeStatus {
23 pub state: ServiceState,
24 pub port: Option<u16>,
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct RuntimeStatus {
29 pub version: String,
30 pub state: ServiceState,
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct PostgresStatus {
35 pub state: ServiceState,
36 pub port: Option<u16>,
37 pub data_dir: Option<String>,
38}
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
41#[serde(rename_all = "lowercase")]
42pub enum ServiceState {
43 Online,
44 Offline,
45 Starting,
46 Stopping,
47 Error,
48}
49
50#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
51#[serde(rename_all = "lowercase")]
52pub enum AppType {
53 #[default]
54 App,
55 Integration,
56 Agent,
57}
58
59#[derive(Debug, Clone, Serialize, Deserialize)]
60#[serde(rename_all = "camelCase")]
61pub struct ActionDefinition {
62 pub id: String,
63 pub name: String,
64 #[serde(default)]
65 pub description: String,
66 #[serde(default)]
67 pub input_schema: Option<JsonValue>,
68 #[serde(default)]
69 pub output_schema: Option<JsonValue>,
70}
71
72#[derive(Debug, Clone, Serialize, Deserialize)]
73#[serde(rename_all = "camelCase")]
74pub struct AppManifest {
75 pub app_id: String,
76 pub name: String,
77 #[serde(default = "default_version")]
78 pub version: String,
79 #[serde(default)]
80 pub description: String,
81 #[serde(default, rename = "type")]
82 pub app_type: AppType,
83 #[serde(default)]
84 pub permissions: Option<PermissionsContract>,
85 #[serde(default)]
86 pub data_contract: Vec<EntityContract>,
87 #[serde(default, skip_serializing_if = "Vec::is_empty")]
88 pub actions: Vec<ActionDefinition>,
89 #[serde(default, skip_serializing_if = "Option::is_none")]
90 pub config_schema: Option<JsonValue>,
91 #[serde(default, skip_serializing_if = "Option::is_none")]
92 pub user_auth: Option<JsonValue>,
93 #[serde(default, skip_serializing_if = "Vec::is_empty")]
94 pub webhooks: Vec<WebhookDefinition>,
95 #[serde(default, skip_serializing_if = "Option::is_none")]
97 pub instructions: Option<String>,
98 #[serde(default, skip_serializing_if = "Option::is_none")]
100 pub trigger: Option<TriggerConfig>,
101 #[serde(default, skip_serializing_if = "Vec::is_empty")]
103 pub crons: Vec<CronDefinition>,
104 #[serde(default, skip_serializing_if = "Option::is_none")]
105 pub icon: Option<String>,
106 #[serde(default, skip_serializing_if = "Option::is_none")]
110 pub public: Option<PublicSurface>,
111}
112
113#[derive(Debug, Clone, Default, Serialize, Deserialize)]
120#[serde(rename_all = "camelCase")]
121pub struct PublicSurface {
122 #[serde(default, skip_serializing_if = "Vec::is_empty")]
126 pub rpcs: Vec<PublicRpc>,
127 #[serde(default, skip_serializing_if = "Vec::is_empty")]
130 pub collections: Vec<PublicCollection>,
131}
132
133#[derive(Debug, Clone, Serialize, Deserialize)]
134#[serde(rename_all = "camelCase")]
135pub struct PublicRpc {
136 pub name: String,
137 #[serde(default, skip_serializing_if = "Vec::is_empty")]
142 pub scope: Vec<String>,
143}
144
145#[derive(Debug, Clone, Serialize, Deserialize)]
146#[serde(rename_all = "camelCase")]
147pub struct PublicCollection {
148 pub entity: String,
149 pub actions: Vec<String>,
151}
152
153#[derive(Debug, Clone, Serialize, Deserialize)]
154#[serde(rename_all = "camelCase")]
155pub struct CronDefinition {
156 pub name: String,
157 pub schedule: String,
158 #[serde(default, skip_serializing_if = "Option::is_none")]
159 pub timezone: Option<String>,
160 #[serde(default, skip_serializing_if = "Option::is_none")]
161 pub method: Option<String>,
162 #[serde(default, skip_serializing_if = "Option::is_none")]
163 pub payload: Option<JsonValue>,
164 #[serde(default = "default_overlap_policy")]
165 pub overlap_policy: String,
166}
167
168#[derive(Debug, Clone, Serialize, Deserialize)]
169#[serde(untagged)]
170pub enum WebhookDefinition {
171 Simple(String),
172 #[serde(rename_all = "camelCase")]
173 Full { name: String, #[serde(default = "default_post")] method: String },
174}
175
176fn default_post() -> String { "POST".into() }
177
178impl WebhookDefinition {
179 pub fn name(&self) -> &str {
180 match self {
181 Self::Simple(s) => s.as_str(),
182 Self::Full { name, .. } => name.as_str(),
183 }
184 }
185 pub fn method(&self) -> &str {
186 match self {
187 Self::Simple(_) => "POST",
188 Self::Full { method, .. } => method.as_str(),
189 }
190 }
191}
192
193fn default_overlap_policy() -> String { "skip".into() }
194
195#[derive(Debug, Clone, Serialize, Deserialize)]
196#[serde(rename_all = "camelCase")]
197pub struct TriggerConfig {
198 pub app_id: String,
199 pub entity: String,
200 pub on: Vec<String>,
201}
202
203fn default_version() -> String {
204 "0.0.1".to_string()
205}
206
207#[derive(Debug, Clone, Serialize, Deserialize)]
208#[serde(rename_all = "camelCase")]
209pub struct EntityContract {
210 pub entity_name: String,
211 pub fields: Vec<FieldContract>,
212 #[serde(default, skip_serializing_if = "Option::is_none")]
213 pub identity_kind: Option<String>,
214 #[serde(default, skip_serializing_if = "Option::is_none")]
215 pub identity_key: Option<String>,
216}
217
218#[derive(Debug, Clone, Serialize, Deserialize)]
219#[serde(rename_all = "camelCase")]
220pub struct FieldContract {
221 pub name: String,
222 #[serde(rename = "type")]
223 pub field_type: String,
224 #[serde(default)]
225 pub required: bool,
226 #[serde(default)]
227 pub default_value: Option<JsonValue>,
228 #[serde(default)]
229 pub enum_values: Option<Vec<String>>,
230 #[serde(default)]
231 pub references: Option<FieldReference>,
232 #[serde(default)]
233 pub is_primary_key: Option<bool>,
234 #[serde(default, skip_serializing_if = "Option::is_none")]
235 pub on_delete: Option<OnDeletePolicy>,
236}
237
238#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
239#[serde(rename_all = "snake_case")]
240pub enum OnDeletePolicy {
241 Cascade,
242 Restrict,
243 SetNull,
244}
245
246#[derive(Debug, Clone, Serialize, Deserialize)]
247pub struct FieldReference {
248 pub entity: String,
249 pub field: String,
250}
251
252#[derive(Debug, Clone, Serialize, Deserialize)]
253#[serde(rename_all = "camelCase")]
254pub struct InstalledApp {
255 pub id: String,
256 pub name: String,
257 pub version: String,
258 pub status: String,
259 #[serde(rename = "type", default)]
260 pub app_type: AppType,
261 pub entities: Vec<String>,
262 #[serde(default)]
263 pub has_frontend: bool,
264 #[serde(default, skip_serializing_if = "Option::is_none")]
265 pub icon: Option<String>,
266}
267
268#[derive(Debug, Clone, Serialize, Deserialize)]
269#[serde(rename_all = "camelCase")]
270pub struct PermissionsContract {
271 #[serde(default)]
272 pub permissions: Vec<PermissionDeclaration>,
273}
274
275#[derive(Debug, Clone, Serialize, Deserialize)]
276pub struct PermissionDeclaration {
277 pub key: String,
278 #[serde(default)]
279 pub description: String,
280}
281
282#[derive(Debug, Clone, Serialize, Deserialize)]
283pub struct SchemaChange {
284 pub entity: String,
285 pub change_type: String,
286 pub column: String,
287 pub detail: Option<String>,
288}
289
290#[derive(Debug, Clone, Serialize, Deserialize)]
291pub struct SchemaVerification {
292 pub compliant: bool,
293 pub changes: Vec<SchemaChange>,
294}
295
296#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
297#[serde(rename_all = "lowercase")]
298pub enum ProviderType {
299 Anthropic,
300 OpenAI,
301 Bedrock,
302}
303
304#[derive(Debug, Clone, Serialize, Deserialize)]
305#[serde(rename_all = "camelCase")]
306pub struct AgentDefinition {
307 pub name: String,
308 #[serde(default)]
309 pub description: Option<String>,
310 #[serde(default)]
311 pub system_prompt: Option<String>,
312 #[serde(default)]
313 pub memory: Option<AgentMemory>,
314 #[serde(default)]
315 pub limits: Option<AgentLimits>,
316 #[serde(default)]
317 pub supervision: Option<SupervisionConfig>,
318}
319
320#[derive(Debug, Clone, Serialize, Deserialize)]
321pub struct AgentMemory {
322 pub enabled: bool,
323}
324
325#[derive(Debug, Clone, Serialize, Deserialize)]
326#[serde(rename_all = "camelCase")]
327pub struct AgentLimits {
328 #[serde(default)]
329 pub max_turns: Option<u32>,
330 #[serde(default)]
331 pub max_context_tokens: Option<u64>,
332 #[serde(default)]
333 pub keep_recent_messages: Option<u32>,
334}
335
336#[derive(Debug, Clone, Serialize, Deserialize)]
337#[serde(rename_all = "camelCase")]
338pub struct SupervisionConfig {
339 pub mode: SupervisionMode,
340 #[serde(default)]
341 pub policies: Vec<SupervisionPolicy>,
342}
343
344#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
345#[serde(rename_all = "lowercase")]
346pub enum SupervisionMode {
347 Autonomous,
348 Supervised,
349 Strict,
350}
351
352#[derive(Debug, Clone, Serialize, Deserialize)]
353#[serde(rename_all = "camelCase")]
354pub struct SupervisionPolicy {
355 pub action: String,
356 #[serde(default)]
357 pub entity: Option<String>,
358 #[serde(default)]
359 pub requires: Option<String>,
360 #[serde(default)]
361 pub rate_limit: Option<RateLimit>,
362}
363
364#[derive(Debug, Clone, Serialize, Deserialize)]
365pub struct RateLimit {
366 pub max: u32,
367 pub window: String,
368}
369
370#[derive(Debug, Clone, Serialize, Deserialize)]
371#[serde(rename_all = "camelCase")]
372pub struct McpServerConfig {
373 pub name: String,
374 pub transport: McpTransport,
375}
376
377#[derive(Debug, Clone, Serialize, Deserialize)]
378#[serde(tag = "type", rename_all = "lowercase")]
379pub enum McpTransport {
380 Stdio { command: String, #[serde(default)] args: Vec<String> },
381 Http { url: String, #[serde(default)] headers: std::collections::HashMap<String, String> },
382 #[deprecated = "use Http"]
383 Sse { url: String, #[serde(default)] headers: std::collections::HashMap<String, String> },
384 Cli { install: String },
385}
386
387#[derive(Debug, Clone, Serialize, Deserialize)]
388#[serde(rename_all = "camelCase")]
389pub struct ToolDescriptor {
390 pub name: String,
391 pub description: String,
392 pub input_schema: serde_json::Value,
393}
394
395#[derive(Debug, Clone, Serialize, Deserialize)]
396#[serde(rename_all = "lowercase")]
397pub enum Role {
398 User,
399 Assistant,
400}
401
402#[derive(Debug, Clone, Serialize, Deserialize)]
403#[serde(tag = "type", rename_all = "snake_case")]
404pub enum ContentBlock {
405 Text { text: String },
406 ToolUse { id: String, name: String, input: serde_json::Value },
407 ToolResult { tool_use_id: String, content: String, #[serde(default)] is_error: bool },
408}
409
410#[derive(Debug, Clone, Serialize, Deserialize)]
411pub struct ChatMessage {
412 pub role: Role,
413 pub content: Vec<ContentBlock>,
414}
415
416#[derive(Debug, Clone, Serialize, Deserialize)]
417pub struct ToolDef {
418 pub name: String,
419 pub description: String,
420 pub input_schema: serde_json::Value,
421}
422