1use crate::agent::{AgentWorkspace, PreparedAgentWorkspace};
6use crate::codec::host_service::{HostServiceChannel, connect_host_service, plain_channel};
7use crate::codec::runtime_provider::{
8 from_wire_append_runtime_logs_response, from_wire_hosted_app,
9 from_wire_list_runtime_sessions_response, from_wire_prepare_runtime_workspace_response,
10 from_wire_runtime_session, from_wire_runtime_support, to_wire_append_runtime_logs_request,
11 to_wire_get_runtime_session_request, to_wire_list_runtime_sessions_request,
12 to_wire_prepare_runtime_workspace_request, to_wire_remove_runtime_workspace_request,
13 to_wire_start_hosted_app_request, to_wire_start_runtime_session_request,
14 to_wire_stop_runtime_session_request,
15};
16use crate::generated::v1;
17use crate::rpc_support::GestaltError;
18
19pub type RuntimeEgressMode = i32;
21
22pub mod runtime_egress_mode {
24 pub const RUNTIME_EGRESS_MODE_UNSPECIFIED: i32 = 0;
26 pub const RUNTIME_EGRESS_MODE_NONE: i32 = 1;
28 pub const RUNTIME_EGRESS_MODE_CIDR: i32 = 2;
30 pub const RUNTIME_EGRESS_MODE_HOSTNAME: i32 = 3;
32}
33
34pub type RuntimeLogStream = i32;
36
37pub mod runtime_log_stream {
39 pub const RUNTIME_LOG_STREAM_UNSPECIFIED: i32 = 0;
41 pub const RUNTIME_LOG_STREAM_STDOUT: i32 = 1;
43 pub const RUNTIME_LOG_STREAM_STDERR: i32 = 2;
45 pub const RUNTIME_LOG_STREAM_RUNTIME: i32 = 3;
47}
48
49#[derive(Clone, Debug, Default, PartialEq)]
51pub struct AppendRuntimeLogsRequest {
52 pub session_id: String,
54 pub logs: Vec<RuntimeLogEntry>,
56}
57
58#[derive(Clone, Debug, Default, PartialEq)]
60pub struct AppendRuntimeLogsResponse {
61 pub last_seq: i64,
63}
64
65#[derive(Clone, Debug, Default, PartialEq)]
67pub struct GetRuntimeSessionRequest {
68 pub session_id: String,
70}
71
72#[derive(Clone, Debug, Default, PartialEq)]
74pub struct HostedApp {
75 pub id: String,
77 pub session_id: String,
79 pub app_name: String,
81 pub dial_target: String,
83}
84
85#[derive(Clone, Debug, Default, PartialEq)]
87pub struct ListRuntimeSessionsRequest {
88 pub page_size: i32,
90 pub page_token: String,
92}
93
94#[derive(Clone, Debug, Default, PartialEq)]
96pub struct ListRuntimeSessionsResponse {
97 pub sessions: Vec<RuntimeSession>,
99 pub next_page_token: String,
101}
102
103#[derive(Clone, Debug, Default, PartialEq)]
105pub struct PrepareRuntimeWorkspaceRequest {
106 pub session_id: String,
108 pub agent_session_id: String,
114 pub workspace: Option<AgentWorkspace>,
116}
117
118#[derive(Clone, Debug, Default, PartialEq)]
120pub struct PrepareRuntimeWorkspaceResponse {
121 pub workspace: Option<PreparedAgentWorkspace>,
123}
124
125#[derive(Clone, Debug, Default, PartialEq)]
127pub struct RemoveRuntimeWorkspaceRequest {
128 pub session_id: String,
130 pub agent_session_id: String,
135}
136
137#[derive(Clone, Debug, Default, PartialEq)]
139pub struct RuntimeImagePullAuth {
140 pub docker_config_json: String,
142}
143
144#[derive(Clone, Debug, Default, PartialEq)]
146pub struct RuntimeLogEntry {
147 pub stream: RuntimeLogStream,
149 pub message: String,
151 pub observed_at: Option<std::time::SystemTime>,
153 pub source_seq: i64,
155}
156
157#[derive(Clone, Debug, Default, PartialEq)]
159pub struct RuntimeSession {
160 pub id: String,
162 pub state: String,
164 pub metadata: std::collections::BTreeMap<String, String>,
166 pub lifecycle: Option<RuntimeSessionLifecycle>,
168 pub state_reason: String,
170 pub state_message: String,
172}
173
174#[derive(Clone, Debug, Default, PartialEq)]
176pub struct RuntimeSessionLifecycle {
177 pub started_at: Option<std::time::SystemTime>,
179 pub recommended_drain_at: Option<std::time::SystemTime>,
181 pub expires_at: Option<std::time::SystemTime>,
183}
184
185#[derive(Clone, Debug, Default, PartialEq)]
187pub struct RuntimeSupport {
188 pub can_host_apps: bool,
190 pub egress_mode: RuntimeEgressMode,
192 pub supports_prepare_workspace: bool,
194}
195
196#[derive(Clone, Debug, Default, PartialEq)]
203pub struct StartHostedAppRequest {
204 pub session_id: String,
206 pub app_name: String,
208 pub command: String,
210 pub args: Vec<String>,
212 pub env: std::collections::BTreeMap<String, String>,
214 pub allowed_hosts: Vec<String>,
216 pub default_action: String,
218 pub host_binary: String,
220 pub workdir: String,
222}
223
224#[derive(Clone, Debug, Default, PartialEq)]
226pub struct StartRuntimeSessionRequest {
227 pub app_name: String,
229 pub template: String,
231 pub image: String,
233 pub metadata: std::collections::BTreeMap<String, String>,
235 pub image_pull_auth: Option<RuntimeImagePullAuth>,
237}
238
239#[derive(Clone, Debug, Default, PartialEq)]
241pub struct StopRuntimeSessionRequest {
242 pub session_id: String,
244}
245
246pub struct Runtime {
248 inner: v1::runtime_client::RuntimeClient<tonic::transport::Channel>,
249 timeout: Option<std::time::Duration>,
250}
251
252impl Runtime {
253 pub fn new(channel: tonic::transport::Channel) -> Self {
255 Self {
256 inner: v1::runtime_client::RuntimeClient::new(channel),
257 timeout: None,
258 }
259 }
260
261 pub fn with_timeout(mut self, timeout: std::time::Duration) -> Self {
264 self.timeout = Some(timeout);
265 self
266 }
267
268 pub async fn get_support(&mut self) -> Result<RuntimeSupport, GestaltError> {
270 let mut tonic_request = tonic::Request::new(());
271 if let Some(timeout) = self.timeout {
272 tonic_request.set_timeout(timeout);
273 }
274 let response = self.inner.get_support(tonic_request).await?;
275 Ok(from_wire_runtime_support(response.into_inner()))
276 }
277
278 pub async fn start_session(
280 &mut self,
281 app_name: String,
282 template: String,
283 image: String,
284 image_pull_auth: Option<RuntimeImagePullAuth>,
285 ) -> Result<RuntimeSession, GestaltError> {
286 let request = StartRuntimeSessionRequest {
287 app_name,
288 template,
289 image,
290 image_pull_auth,
291 ..Default::default()
292 };
293 let mut tonic_request = tonic::Request::new(to_wire_start_runtime_session_request(request));
294 if let Some(timeout) = self.timeout {
295 tonic_request.set_timeout(timeout);
296 }
297 let response = self.inner.start_session(tonic_request).await?;
298 Ok(from_wire_runtime_session(response.into_inner()))
299 }
300
301 pub async fn start_session_raw(
303 &mut self,
304 request: StartRuntimeSessionRequest,
305 ) -> Result<RuntimeSession, GestaltError> {
306 let mut tonic_request = tonic::Request::new(to_wire_start_runtime_session_request(request));
307 if let Some(timeout) = self.timeout {
308 tonic_request.set_timeout(timeout);
309 }
310 let response = self.inner.start_session(tonic_request).await?;
311 Ok(from_wire_runtime_session(response.into_inner()))
312 }
313
314 pub async fn get_session(
316 &mut self,
317 session_id: String,
318 ) -> Result<RuntimeSession, GestaltError> {
319 let request = GetRuntimeSessionRequest { session_id };
320 let mut tonic_request = tonic::Request::new(to_wire_get_runtime_session_request(request));
321 if let Some(timeout) = self.timeout {
322 tonic_request.set_timeout(timeout);
323 }
324 let response = self.inner.get_session(tonic_request).await?;
325 Ok(from_wire_runtime_session(response.into_inner()))
326 }
327
328 pub async fn get_session_raw(
330 &mut self,
331 request: GetRuntimeSessionRequest,
332 ) -> Result<RuntimeSession, GestaltError> {
333 let mut tonic_request = tonic::Request::new(to_wire_get_runtime_session_request(request));
334 if let Some(timeout) = self.timeout {
335 tonic_request.set_timeout(timeout);
336 }
337 let response = self.inner.get_session(tonic_request).await?;
338 Ok(from_wire_runtime_session(response.into_inner()))
339 }
340
341 pub async fn list_sessions(
343 &mut self,
344 request: ListRuntimeSessionsRequest,
345 ) -> Result<ListRuntimeSessionsResponse, GestaltError> {
346 let mut tonic_request = tonic::Request::new(to_wire_list_runtime_sessions_request(request));
347 if let Some(timeout) = self.timeout {
348 tonic_request.set_timeout(timeout);
349 }
350 let response = self.inner.list_sessions(tonic_request).await?;
351 Ok(from_wire_list_runtime_sessions_response(
352 response.into_inner(),
353 ))
354 }
355
356 pub async fn stop_session(&mut self, session_id: String) -> Result<(), GestaltError> {
358 let request = StopRuntimeSessionRequest { session_id };
359 let mut tonic_request = tonic::Request::new(to_wire_stop_runtime_session_request(request));
360 if let Some(timeout) = self.timeout {
361 tonic_request.set_timeout(timeout);
362 }
363 self.inner.stop_session(tonic_request).await?;
364 Ok(())
365 }
366
367 pub async fn stop_session_raw(
369 &mut self,
370 request: StopRuntimeSessionRequest,
371 ) -> Result<(), GestaltError> {
372 let mut tonic_request = tonic::Request::new(to_wire_stop_runtime_session_request(request));
373 if let Some(timeout) = self.timeout {
374 tonic_request.set_timeout(timeout);
375 }
376 self.inner.stop_session(tonic_request).await?;
377 Ok(())
378 }
379
380 pub async fn prepare_workspace(
382 &mut self,
383 session_id: String,
384 agent_session_id: String,
385 workspace: Option<AgentWorkspace>,
386 ) -> Result<Option<PreparedAgentWorkspace>, GestaltError> {
387 let request = PrepareRuntimeWorkspaceRequest {
388 session_id,
389 agent_session_id,
390 workspace,
391 };
392 let mut tonic_request =
393 tonic::Request::new(to_wire_prepare_runtime_workspace_request(request));
394 if let Some(timeout) = self.timeout {
395 tonic_request.set_timeout(timeout);
396 }
397 let response = from_wire_prepare_runtime_workspace_response(
398 self.inner
399 .prepare_workspace(tonic_request)
400 .await?
401 .into_inner(),
402 );
403 Ok(response.workspace)
404 }
405
406 pub async fn prepare_workspace_raw(
408 &mut self,
409 request: PrepareRuntimeWorkspaceRequest,
410 ) -> Result<PrepareRuntimeWorkspaceResponse, GestaltError> {
411 let mut tonic_request =
412 tonic::Request::new(to_wire_prepare_runtime_workspace_request(request));
413 if let Some(timeout) = self.timeout {
414 tonic_request.set_timeout(timeout);
415 }
416 let response = self.inner.prepare_workspace(tonic_request).await?;
417 Ok(from_wire_prepare_runtime_workspace_response(
418 response.into_inner(),
419 ))
420 }
421
422 pub async fn remove_workspace(
424 &mut self,
425 session_id: String,
426 agent_session_id: String,
427 ) -> Result<(), GestaltError> {
428 let request = RemoveRuntimeWorkspaceRequest {
429 session_id,
430 agent_session_id,
431 };
432 let mut tonic_request =
433 tonic::Request::new(to_wire_remove_runtime_workspace_request(request));
434 if let Some(timeout) = self.timeout {
435 tonic_request.set_timeout(timeout);
436 }
437 self.inner.remove_workspace(tonic_request).await?;
438 Ok(())
439 }
440
441 pub async fn remove_workspace_raw(
443 &mut self,
444 request: RemoveRuntimeWorkspaceRequest,
445 ) -> Result<(), GestaltError> {
446 let mut tonic_request =
447 tonic::Request::new(to_wire_remove_runtime_workspace_request(request));
448 if let Some(timeout) = self.timeout {
449 tonic_request.set_timeout(timeout);
450 }
451 self.inner.remove_workspace(tonic_request).await?;
452 Ok(())
453 }
454
455 #[allow(clippy::too_many_arguments)]
457 pub async fn start_app(
458 &mut self,
459 session_id: String,
460 app_name: String,
461 command: String,
462 args: Vec<String>,
463 allowed_hosts: Vec<String>,
464 default_action: String,
465 host_binary: String,
466 workdir: String,
467 ) -> Result<HostedApp, GestaltError> {
468 let request = StartHostedAppRequest {
469 session_id,
470 app_name,
471 command,
472 args,
473 allowed_hosts,
474 default_action,
475 host_binary,
476 workdir,
477 ..Default::default()
478 };
479 let mut tonic_request = tonic::Request::new(to_wire_start_hosted_app_request(request));
480 if let Some(timeout) = self.timeout {
481 tonic_request.set_timeout(timeout);
482 }
483 let response = self.inner.start_app(tonic_request).await?;
484 Ok(from_wire_hosted_app(response.into_inner()))
485 }
486
487 pub async fn start_app_raw(
489 &mut self,
490 request: StartHostedAppRequest,
491 ) -> Result<HostedApp, GestaltError> {
492 let mut tonic_request = tonic::Request::new(to_wire_start_hosted_app_request(request));
493 if let Some(timeout) = self.timeout {
494 tonic_request.set_timeout(timeout);
495 }
496 let response = self.inner.start_app(tonic_request).await?;
497 Ok(from_wire_hosted_app(response.into_inner()))
498 }
499}
500
501pub struct RuntimeLogHost {
503 inner: v1::runtime_log_host_client::RuntimeLogHostClient<HostServiceChannel>,
504 timeout: Option<std::time::Duration>,
505}
506
507impl RuntimeLogHost {
508 pub fn new(channel: tonic::transport::Channel) -> Self {
510 Self {
511 inner: v1::runtime_log_host_client::RuntimeLogHostClient::new(plain_channel(channel)),
512 timeout: None,
513 }
514 }
515
516 pub fn with_timeout(mut self, timeout: std::time::Duration) -> Self {
519 self.timeout = Some(timeout);
520 self
521 }
522
523 pub async fn connect() -> Result<Self, GestaltError> {
525 Self::connect_named("").await
526 }
527
528 pub async fn connect_named(name: &str) -> Result<Self, GestaltError> {
530 Ok(Self {
531 inner: v1::runtime_log_host_client::RuntimeLogHostClient::new(
532 connect_host_service("runtime log host", name).await?,
533 ),
534 timeout: None,
535 })
536 }
537
538 pub async fn append_logs(
540 &mut self,
541 session_id: String,
542 logs: Vec<RuntimeLogEntry>,
543 ) -> Result<i64, GestaltError> {
544 let request = AppendRuntimeLogsRequest { session_id, logs };
545 let mut tonic_request = tonic::Request::new(to_wire_append_runtime_logs_request(request));
546 if let Some(timeout) = self.timeout {
547 tonic_request.set_timeout(timeout);
548 }
549 let response = from_wire_append_runtime_logs_response(
550 self.inner.append_logs(tonic_request).await?.into_inner(),
551 );
552 Ok(response.last_seq)
553 }
554
555 pub async fn append_logs_raw(
557 &mut self,
558 request: AppendRuntimeLogsRequest,
559 ) -> Result<AppendRuntimeLogsResponse, GestaltError> {
560 let mut tonic_request = tonic::Request::new(to_wire_append_runtime_logs_request(request));
561 if let Some(timeout) = self.timeout {
562 tonic_request.set_timeout(timeout);
563 }
564 let response = self.inner.append_logs(tonic_request).await?;
565 Ok(from_wire_append_runtime_logs_response(
566 response.into_inner(),
567 ))
568 }
569}