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, ServerFrame, ServerFrameType, ServerPayload, TaskInfoDto,
7    deserialize_frame, serialize_frame,
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>(writer: &mut S, ok: bool, message: &str) -> Result<()>
172where
173    S: SinkExt<Message> + Unpin,
174{
175    let frame = ServerFrame {
176        frame_type: ServerFrameType::SecretsStoreResult,
177        payload: ServerPayload::SecretsStoreResult {
178            ok,
179            message: message.into(),
180        },
181    };
182    send_frame(writer, &frame).await
183}
184
185/// Build and send a secrets_get_result frame.
186pub async fn send_secrets_get_result<S>(
187    writer: &mut S,
188    ok: bool,
189    key: &str,
190    value: Option<&str>,
191    message: Option<&str>,
192) -> Result<()>
193where
194    S: SinkExt<Message> + Unpin,
195{
196    let frame = ServerFrame {
197        frame_type: ServerFrameType::SecretsGetResult,
198        payload: ServerPayload::SecretsGetResult {
199            ok,
200            key: key.into(),
201            value: value.map(|s| s.into()),
202            message: message.map(|s| s.into()),
203        },
204    };
205    send_frame(writer, &frame).await
206}
207
208/// Build and send a secrets_delete_result frame.
209pub async fn send_secrets_delete_result<S>(
210    writer: &mut S,
211    ok: bool,
212    message: Option<&str>,
213) -> Result<()>
214where
215    S: SinkExt<Message> + Unpin,
216{
217    let frame = ServerFrame {
218        frame_type: ServerFrameType::SecretsDeleteResult,
219        payload: ServerPayload::SecretsDeleteResult {
220            ok,
221            message: message.map(|s| s.into()),
222        },
223    };
224    send_frame(writer, &frame).await
225}
226
227/// Build and send a secrets_peek_result frame.
228pub async fn send_secrets_peek_result<S>(
229    writer: &mut S,
230    ok: bool,
231    fields: Vec<(String, String)>,
232    message: Option<&str>,
233) -> Result<()>
234where
235    S: SinkExt<Message> + Unpin,
236{
237    let frame = ServerFrame {
238        frame_type: ServerFrameType::SecretsPeekResult,
239        payload: ServerPayload::SecretsPeekResult {
240            ok,
241            fields,
242            message: message.map(|s| s.into()),
243        },
244    };
245    send_frame(writer, &frame).await
246}
247
248/// Build and send a secrets_set_policy_result frame.
249pub async fn send_secrets_set_policy_result<S>(
250    writer: &mut S,
251    ok: bool,
252    message: Option<&str>,
253) -> Result<()>
254where
255    S: SinkExt<Message> + Unpin,
256{
257    let frame = ServerFrame {
258        frame_type: ServerFrameType::SecretsSetPolicyResult,
259        payload: ServerPayload::SecretsSetPolicyResult {
260            ok,
261            message: message.map(|s| s.into()),
262        },
263    };
264    send_frame(writer, &frame).await
265}
266
267/// Build and send a secrets_set_disabled_result frame.
268pub async fn send_secrets_set_disabled_result<S>(
269    writer: &mut S,
270    ok: bool,
271    message: Option<&str>,
272) -> Result<()>
273where
274    S: SinkExt<Message> + Unpin,
275{
276    let frame = ServerFrame {
277        frame_type: ServerFrameType::SecretsSetDisabledResult,
278        payload: ServerPayload::SecretsSetDisabledResult {
279            ok,
280            message: message.map(|s| s.into()),
281        },
282    };
283    send_frame(writer, &frame).await
284}
285
286/// Build and send a secrets_delete_credential_result frame.
287pub async fn send_secrets_delete_credential_result<S>(
288    writer: &mut S,
289    ok: bool,
290    message: Option<&str>,
291) -> Result<()>
292where
293    S: SinkExt<Message> + Unpin,
294{
295    let frame = ServerFrame {
296        frame_type: ServerFrameType::SecretsDeleteCredentialResult,
297        payload: ServerPayload::SecretsDeleteCredentialResult {
298            ok,
299            message: message.map(|s| s.into()),
300        },
301    };
302    send_frame(writer, &frame).await
303}
304
305/// Build and send a secrets_has_totp_result frame.
306pub async fn send_secrets_has_totp_result<S>(writer: &mut S, has_totp: bool) -> Result<()>
307where
308    S: SinkExt<Message> + Unpin,
309{
310    let frame = ServerFrame {
311        frame_type: ServerFrameType::SecretsHasTotpResult,
312        payload: ServerPayload::SecretsHasTotpResult { has_totp },
313    };
314    send_frame(writer, &frame).await
315}
316
317/// Build and send a secrets_setup_totp_result frame.
318pub async fn send_secrets_setup_totp_result<S>(
319    writer: &mut S,
320    ok: bool,
321    uri: Option<&str>,
322    message: Option<&str>,
323) -> Result<()>
324where
325    S: SinkExt<Message> + Unpin,
326{
327    let frame = ServerFrame {
328        frame_type: ServerFrameType::SecretsSetupTotpResult,
329        payload: ServerPayload::SecretsSetupTotpResult {
330            ok,
331            uri: uri.map(|s| s.into()),
332            message: message.map(|s| s.into()),
333        },
334    };
335    send_frame(writer, &frame).await
336}
337
338/// Build and send a secrets_verify_totp_result frame.
339pub async fn send_secrets_verify_totp_result<S>(
340    writer: &mut S,
341    ok: bool,
342    message: Option<&str>,
343) -> Result<()>
344where
345    S: SinkExt<Message> + Unpin,
346{
347    let frame = ServerFrame {
348        frame_type: ServerFrameType::SecretsVerifyTotpResult,
349        payload: ServerPayload::SecretsVerifyTotpResult {
350            ok,
351            message: message.map(|s| s.into()),
352        },
353    };
354    send_frame(writer, &frame).await
355}
356
357/// Build and send a secrets_remove_totp_result frame.
358pub async fn send_secrets_remove_totp_result<S>(
359    writer: &mut S,
360    ok: bool,
361    message: Option<&str>,
362) -> Result<()>
363where
364    S: SinkExt<Message> + Unpin,
365{
366    let frame = ServerFrame {
367        frame_type: ServerFrameType::SecretsRemoveTotpResult,
368        payload: ServerPayload::SecretsRemoveTotpResult {
369            ok,
370            message: message.map(|s| s.into()),
371        },
372    };
373    send_frame(writer, &frame).await
374}
375
376/// Build and send a reload_result frame.
377pub async fn send_reload_result<S>(
378    writer: &mut S,
379    ok: bool,
380    provider: &str,
381    model: &str,
382    message: Option<&str>,
383) -> Result<()>
384where
385    S: SinkExt<Message> + Unpin,
386{
387    let frame = ServerFrame {
388        frame_type: ServerFrameType::ReloadResult,
389        payload: ServerPayload::ReloadResult {
390            ok,
391            provider: provider.into(),
392            model: model.into(),
393            message: message.map(|s| s.into()),
394        },
395    };
396    send_frame(writer, &frame).await
397}
398
399/// Build and send a chunk frame.
400pub async fn send_chunk<S>(writer: &mut S, delta: &str) -> Result<()>
401where
402    S: SinkExt<Message> + Unpin,
403{
404    let frame = ServerFrame {
405        frame_type: ServerFrameType::Chunk,
406        payload: ServerPayload::Chunk {
407            delta: delta.into(),
408        },
409    };
410    send_frame(writer, &frame).await
411}
412
413/// Build and send a response done frame.
414pub async fn send_response_done<S>(writer: &mut S, ok: bool) -> Result<()>
415where
416    S: SinkExt<Message> + Unpin,
417{
418    let frame = ServerFrame {
419        frame_type: ServerFrameType::ResponseDone,
420        payload: ServerPayload::ResponseDone { ok },
421    };
422    send_frame(writer, &frame).await
423}
424
425/// Build and send a stream start frame.
426pub async fn send_stream_start<S>(writer: &mut S) -> Result<()>
427where
428    S: SinkExt<Message> + Unpin,
429{
430    let frame = ServerFrame {
431        frame_type: ServerFrameType::StreamStart,
432        payload: ServerPayload::StreamStart,
433    };
434    send_frame(writer, &frame).await
435}
436
437/// Build and send a tool call frame.
438pub async fn send_tool_call<S>(writer: &mut S, id: &str, name: &str, arguments: &str) -> Result<()>
439where
440    S: SinkExt<Message> + Unpin,
441{
442    let frame = ServerFrame {
443        frame_type: ServerFrameType::ToolCall,
444        payload: ServerPayload::ToolCall {
445            id: id.into(),
446            name: name.into(),
447            arguments: arguments.into(),
448        },
449    };
450    send_frame(writer, &frame).await
451}
452
453/// Build and send a tool result frame.
454pub async fn send_tool_result<S>(
455    writer: &mut S,
456    id: &str,
457    name: &str,
458    result: &str,
459    is_error: bool,
460) -> Result<()>
461where
462    S: SinkExt<Message> + Unpin,
463{
464    let frame = ServerFrame {
465        frame_type: ServerFrameType::ToolResult,
466        payload: ServerPayload::ToolResult {
467            id: id.into(),
468            name: name.into(),
469            result: result.into(),
470            is_error,
471        },
472    };
473    send_frame(writer, &frame).await
474}
475
476/// Build and send a tool approval request frame.
477pub async fn send_tool_approval_request<S>(
478    writer: &mut S,
479    id: &str,
480    name: &str,
481    arguments: &str,
482) -> Result<()>
483where
484    S: SinkExt<Message> + Unpin,
485{
486    let frame = ServerFrame {
487        frame_type: ServerFrameType::ToolApprovalRequest,
488        payload: ServerPayload::ToolApprovalRequest {
489            id: id.into(),
490            name: name.into(),
491            arguments: arguments.into(),
492        },
493    };
494    send_frame(writer, &frame).await
495}
496
497/// Build and send a user-prompt request frame (for the `ask_user` tool).
498pub async fn send_user_prompt_request<S>(
499    writer: &mut S,
500    id: &str,
501    prompt: &crate::user_prompt_types::UserPrompt,
502) -> Result<()>
503where
504    S: SinkExt<Message> + Unpin,
505{
506    let frame = ServerFrame {
507        frame_type: ServerFrameType::UserPromptRequest,
508        payload: ServerPayload::UserPromptRequest {
509            id: id.into(),
510            prompt: prompt.clone(),
511        },
512    };
513    send_frame(writer, &frame).await
514}
515
516/// Build and send a tasks update frame.
517pub async fn send_tasks_update<S>(writer: &mut S, tasks: Vec<TaskInfoDto>) -> Result<()>
518where
519    S: SinkExt<Message> + Unpin,
520{
521    let frame = ServerFrame {
522        frame_type: ServerFrameType::TasksUpdate,
523        payload: ServerPayload::TasksUpdate { tasks },
524    };
525    send_frame(writer, &frame).await
526}