Skip to main content

rustyclaw_core/gateway/protocol/
server.rs

1//! Server-side helpers for the gateway protocol.
2//!
3//! This module provides helpers for the gateway server to send frames to clients.
4
5use super::frames::{
6    ClientFrame, SecretEntryDto, deserialize_frame, serialize_frame, ServerFrame, ServerFrameType,
7    ServerPayload,
8};
9use anyhow::Result;
10use futures_util::SinkExt;
11use tokio_tungstenite::tungstenite::Message;
12
13/// Send a ServerFrame as a binary WebSocket message.
14/// Works with any sink that accepts Binary messages.
15pub async fn send_frame<S>(writer: &mut S, frame: &ServerFrame) -> Result<()>
16where
17    S: SinkExt<Message> + Unpin,
18{
19    let bytes = serialize_frame(frame).map_err(|e| anyhow::anyhow!("serialize failed: {}", e))?;
20    writer
21        .send(Message::Binary(bytes.into()))
22        .await
23        .map_err(|_e| anyhow::anyhow!("send failed"))
24}
25
26/// Parse a ClientFrame from binary WebSocket message bytes.
27pub fn parse_client_frame(bytes: &[u8]) -> Result<ClientFrame> {
28    deserialize_frame(bytes).map_err(|e| anyhow::anyhow!("parse failed: {}", e))
29}
30
31/// Build and send a hello frame.
32pub async fn send_hello<S>(
33    writer: &mut S,
34    agent: &str,
35    settings_dir: &str,
36    vault_locked: bool,
37    provider: Option<&str>,
38    model: Option<&str>,
39) -> Result<()>
40where
41    S: SinkExt<Message> + Unpin,
42{
43    let frame = ServerFrame {
44        frame_type: ServerFrameType::Hello,
45        payload: ServerPayload::Hello {
46            agent: agent.into(),
47            settings_dir: settings_dir.into(),
48            vault_locked,
49            provider: provider.map(|s| s.into()),
50            model: model.map(|s| s.into()),
51        },
52    };
53    send_frame(writer, &frame).await
54}
55
56/// Build and send an auth challenge frame.
57pub async fn send_auth_challenge<S>(writer: &mut S, method: &str) -> Result<()>
58where
59    S: SinkExt<Message> + Unpin,
60{
61    let frame = ServerFrame {
62        frame_type: ServerFrameType::AuthChallenge,
63        payload: ServerPayload::AuthChallenge {
64            method: method.into(),
65        },
66    };
67    send_frame(writer, &frame).await
68}
69
70/// Build and send an auth result frame.
71pub async fn send_auth_result<S>(
72    writer: &mut S,
73    ok: bool,
74    message: Option<&str>,
75    retry: Option<bool>,
76) -> Result<()>
77where
78    S: SinkExt<Message> + Unpin,
79{
80    let frame = ServerFrame {
81        frame_type: ServerFrameType::AuthResult,
82        payload: ServerPayload::AuthResult {
83            ok,
84            message: message.map(|s| s.into()),
85            retry,
86        },
87    };
88    send_frame(writer, &frame).await
89}
90
91/// Build and send an error frame.
92pub async fn send_error<S>(writer: &mut S, message: &str) -> Result<()>
93where
94    S: SinkExt<Message> + Unpin,
95{
96    let frame = ServerFrame {
97        frame_type: ServerFrameType::Error,
98        payload: ServerPayload::Error {
99            ok: false,
100            message: message.into(),
101        },
102    };
103    send_frame(writer, &frame).await
104}
105
106/// Build and send an info frame.
107pub async fn send_info<S>(writer: &mut S, message: &str) -> Result<()>
108where
109    S: SinkExt<Message> + Unpin,
110{
111    let frame = ServerFrame {
112        frame_type: ServerFrameType::Info,
113        payload: ServerPayload::Info {
114            message: message.into(),
115        },
116    };
117    send_frame(writer, &frame).await
118}
119
120/// Build and send a status frame.
121pub async fn send_status<S>(
122    writer: &mut S,
123    status: super::frames::StatusType,
124    detail: &str,
125) -> Result<()>
126where
127    S: SinkExt<Message> + Unpin,
128{
129    let frame = ServerFrame {
130        frame_type: ServerFrameType::Status,
131        payload: ServerPayload::Status {
132            status,
133            detail: detail.into(),
134        },
135    };
136    send_frame(writer, &frame).await
137}
138
139/// Build and send a vault_unlocked frame.
140pub async fn send_vault_unlocked<S>(writer: &mut S, ok: bool, message: Option<&str>) -> Result<()>
141where
142    S: SinkExt<Message> + Unpin,
143{
144    let frame = ServerFrame {
145        frame_type: ServerFrameType::VaultUnlocked,
146        payload: ServerPayload::VaultUnlocked {
147            ok,
148            message: message.map(|s| s.into()),
149        },
150    };
151    send_frame(writer, &frame).await
152}
153
154/// Build and send a secrets_list_result frame.
155pub async fn send_secrets_list_result<S>(
156    writer: &mut S,
157    ok: bool,
158    entries: Vec<SecretEntryDto>,
159) -> Result<()>
160where
161    S: SinkExt<Message> + Unpin,
162{
163    let frame = ServerFrame {
164        frame_type: ServerFrameType::SecretsListResult,
165        payload: ServerPayload::SecretsListResult { ok, entries },
166    };
167    send_frame(writer, &frame).await
168}
169
170/// Build and send a secrets_store_result frame.
171pub async fn send_secrets_store_result<S>(
172    writer: &mut S,
173    ok: bool,
174    message: &str,
175) -> Result<()>
176where
177    S: SinkExt<Message> + Unpin,
178{
179    let frame = ServerFrame {
180        frame_type: ServerFrameType::SecretsStoreResult,
181        payload: ServerPayload::SecretsStoreResult {
182            ok,
183            message: message.into(),
184        },
185    };
186    send_frame(writer, &frame).await
187}
188
189/// Build and send a secrets_get_result frame.
190pub async fn send_secrets_get_result<S>(
191    writer: &mut S,
192    ok: bool,
193    key: &str,
194    value: Option<&str>,
195    message: Option<&str>,
196) -> Result<()>
197where
198    S: SinkExt<Message> + Unpin,
199{
200    let frame = ServerFrame {
201        frame_type: ServerFrameType::SecretsGetResult,
202        payload: ServerPayload::SecretsGetResult {
203            ok,
204            key: key.into(),
205            value: value.map(|s| s.into()),
206            message: message.map(|s| s.into()),
207        },
208    };
209    send_frame(writer, &frame).await
210}
211
212/// Build and send a secrets_delete_result frame.
213pub async fn send_secrets_delete_result<S>(
214    writer: &mut S,
215    ok: bool,
216    message: Option<&str>,
217) -> Result<()>
218where
219    S: SinkExt<Message> + Unpin,
220{
221    let frame = ServerFrame {
222        frame_type: ServerFrameType::SecretsDeleteResult,
223        payload: ServerPayload::SecretsDeleteResult {
224            ok,
225            message: message.map(|s| s.into()),
226        },
227    };
228    send_frame(writer, &frame).await
229}
230
231/// Build and send a secrets_peek_result frame.
232pub async fn send_secrets_peek_result<S>(
233    writer: &mut S,
234    ok: bool,
235    fields: Vec<(String, String)>,
236    message: Option<&str>,
237) -> Result<()>
238where
239    S: SinkExt<Message> + Unpin,
240{
241    let frame = ServerFrame {
242        frame_type: ServerFrameType::SecretsPeekResult,
243        payload: ServerPayload::SecretsPeekResult {
244            ok,
245            fields,
246            message: message.map(|s| s.into()),
247        },
248    };
249    send_frame(writer, &frame).await
250}
251
252/// Build and send a secrets_set_policy_result frame.
253pub async fn send_secrets_set_policy_result<S>(
254    writer: &mut S,
255    ok: bool,
256    message: Option<&str>,
257) -> Result<()>
258where
259    S: SinkExt<Message> + Unpin,
260{
261    let frame = ServerFrame {
262        frame_type: ServerFrameType::SecretsSetPolicyResult,
263        payload: ServerPayload::SecretsSetPolicyResult {
264            ok,
265            message: message.map(|s| s.into()),
266        },
267    };
268    send_frame(writer, &frame).await
269}
270
271/// Build and send a secrets_set_disabled_result frame.
272pub async fn send_secrets_set_disabled_result<S>(
273    writer: &mut S,
274    ok: bool,
275    message: Option<&str>,
276) -> Result<()>
277where
278    S: SinkExt<Message> + Unpin,
279{
280    let frame = ServerFrame {
281        frame_type: ServerFrameType::SecretsSetDisabledResult,
282        payload: ServerPayload::SecretsSetDisabledResult {
283            ok,
284            message: message.map(|s| s.into()),
285        },
286    };
287    send_frame(writer, &frame).await
288}
289
290/// Build and send a secrets_delete_credential_result frame.
291pub async fn send_secrets_delete_credential_result<S>(
292    writer: &mut S,
293    ok: bool,
294    message: Option<&str>,
295) -> Result<()>
296where
297    S: SinkExt<Message> + Unpin,
298{
299    let frame = ServerFrame {
300        frame_type: ServerFrameType::SecretsDeleteCredentialResult,
301        payload: ServerPayload::SecretsDeleteCredentialResult {
302            ok,
303            message: message.map(|s| s.into()),
304        },
305    };
306    send_frame(writer, &frame).await
307}
308
309/// Build and send a secrets_has_totp_result frame.
310pub async fn send_secrets_has_totp_result<S>(writer: &mut S, has_totp: bool) -> Result<()>
311where
312    S: SinkExt<Message> + Unpin,
313{
314    let frame = ServerFrame {
315        frame_type: ServerFrameType::SecretsHasTotpResult,
316        payload: ServerPayload::SecretsHasTotpResult { has_totp },
317    };
318    send_frame(writer, &frame).await
319}
320
321/// Build and send a secrets_setup_totp_result frame.
322pub async fn send_secrets_setup_totp_result<S>(
323    writer: &mut S,
324    ok: bool,
325    uri: Option<&str>,
326    message: Option<&str>,
327) -> Result<()>
328where
329    S: SinkExt<Message> + Unpin,
330{
331    let frame = ServerFrame {
332        frame_type: ServerFrameType::SecretsSetupTotpResult,
333        payload: ServerPayload::SecretsSetupTotpResult {
334            ok,
335            uri: uri.map(|s| s.into()),
336            message: message.map(|s| s.into()),
337        },
338    };
339    send_frame(writer, &frame).await
340}
341
342/// Build and send a secrets_verify_totp_result frame.
343pub async fn send_secrets_verify_totp_result<S>(
344    writer: &mut S,
345    ok: bool,
346    message: Option<&str>,
347) -> Result<()>
348where
349    S: SinkExt<Message> + Unpin,
350{
351    let frame = ServerFrame {
352        frame_type: ServerFrameType::SecretsVerifyTotpResult,
353        payload: ServerPayload::SecretsVerifyTotpResult {
354            ok,
355            message: message.map(|s| s.into()),
356        },
357    };
358    send_frame(writer, &frame).await
359}
360
361/// Build and send a secrets_remove_totp_result frame.
362pub async fn send_secrets_remove_totp_result<S>(
363    writer: &mut S,
364    ok: bool,
365    message: Option<&str>,
366) -> Result<()>
367where
368    S: SinkExt<Message> + Unpin,
369{
370    let frame = ServerFrame {
371        frame_type: ServerFrameType::SecretsRemoveTotpResult,
372        payload: ServerPayload::SecretsRemoveTotpResult {
373            ok,
374            message: message.map(|s| s.into()),
375        },
376    };
377    send_frame(writer, &frame).await
378}
379
380/// Build and send a reload_result frame.
381pub async fn send_reload_result<S>(
382    writer: &mut S,
383    ok: bool,
384    provider: &str,
385    model: &str,
386    message: Option<&str>,
387) -> Result<()>
388where
389    S: SinkExt<Message> + Unpin,
390{
391    let frame = ServerFrame {
392        frame_type: ServerFrameType::ReloadResult,
393        payload: ServerPayload::ReloadResult {
394            ok,
395            provider: provider.into(),
396            model: model.into(),
397            message: message.map(|s| s.into()),
398        },
399    };
400    send_frame(writer, &frame).await
401}
402
403/// Build and send a chunk frame.
404pub async fn send_chunk<S>(writer: &mut S, delta: &str) -> Result<()>
405where
406    S: SinkExt<Message> + Unpin,
407{
408    let frame = ServerFrame {
409        frame_type: ServerFrameType::Chunk,
410        payload: ServerPayload::Chunk {
411            delta: delta.into(),
412        },
413    };
414    send_frame(writer, &frame).await
415}
416
417/// Build and send a response done frame.
418pub async fn send_response_done<S>(writer: &mut S, ok: bool) -> Result<()>
419where
420    S: SinkExt<Message> + Unpin,
421{
422    let frame = ServerFrame {
423        frame_type: ServerFrameType::ResponseDone,
424        payload: ServerPayload::ResponseDone { ok },
425    };
426    send_frame(writer, &frame).await
427}
428
429/// Build and send a stream start frame.
430pub async fn send_stream_start<S>(writer: &mut S) -> Result<()>
431where
432    S: SinkExt<Message> + Unpin,
433{
434    let frame = ServerFrame {
435        frame_type: ServerFrameType::StreamStart,
436        payload: ServerPayload::StreamStart,
437    };
438    send_frame(writer, &frame).await
439}
440
441/// Build and send a tool call frame.
442pub async fn send_tool_call<S>(
443    writer: &mut S,
444    id: &str,
445    name: &str,
446    arguments: &str,
447) -> Result<()>
448where
449    S: SinkExt<Message> + Unpin,
450{
451    let frame = ServerFrame {
452        frame_type: ServerFrameType::ToolCall,
453        payload: ServerPayload::ToolCall {
454            id: id.into(),
455            name: name.into(),
456            arguments: arguments.into(),
457        },
458    };
459    send_frame(writer, &frame).await
460}
461
462/// Build and send a tool result frame.
463pub async fn send_tool_result<S>(
464    writer: &mut S,
465    id: &str,
466    name: &str,
467    result: &str,
468    is_error: bool,
469) -> Result<()>
470where
471    S: SinkExt<Message> + Unpin,
472{
473    let frame = ServerFrame {
474        frame_type: ServerFrameType::ToolResult,
475        payload: ServerPayload::ToolResult {
476            id: id.into(),
477            name: name.into(),
478            result: result.into(),
479            is_error,
480        },
481    };
482    send_frame(writer, &frame).await
483}
484
485/// Build and send a tool approval request frame.
486pub async fn send_tool_approval_request<S>(
487    writer: &mut S,
488    id: &str,
489    name: &str,
490    arguments: &str,
491) -> Result<()>
492where
493    S: SinkExt<Message> + Unpin,
494{
495    let frame = ServerFrame {
496        frame_type: ServerFrameType::ToolApprovalRequest,
497        payload: ServerPayload::ToolApprovalRequest {
498            id: id.into(),
499            name: name.into(),
500            arguments: arguments.into(),
501        },
502    };
503    send_frame(writer, &frame).await
504}
505
506/// Build and send a user-prompt request frame (for the `ask_user` tool).
507pub async fn send_user_prompt_request<S>(
508    writer: &mut S,
509    id: &str,
510    prompt: &crate::user_prompt_types::UserPrompt,
511) -> Result<()>
512where
513    S: SinkExt<Message> + Unpin,
514{
515    let frame = ServerFrame {
516        frame_type: ServerFrameType::UserPromptRequest,
517        payload: ServerPayload::UserPromptRequest {
518            id: id.into(),
519            prompt: prompt.clone(),
520        },
521    };
522    send_frame(writer, &frame).await
523}