Skip to main content

greentic_runner/
lib.rs

1#![forbid(unsafe_code)]
2//! Canonical entrypoint for embedding the Greentic runner.
3//!
4//! This crate provides two supported integration paths:
5//! - [`run_http_host`] mirrors the CLI and starts the HTTP server that exposes
6//!   ingress adapters, admin endpoints, and the pack watcher.
7//! - [`start_embedded_host`] constructs a [`RunnerHost`] without spinning up the
8//!   HTTP server so callers can drive `handle_activity` manually (desktop/dev
9//!   harnesses, tests, etc.).
10
11use anyhow::Result;
12
13pub use greentic_runner_host::{
14    self as host, Activity, ActivityKind, HostBuilder, HostServer, RunnerConfig, RunnerHost,
15    TenantHandle, config, http, pack, routing, runner, runtime, runtime_wasmtime, telemetry,
16    verify, watcher,
17};
18
19pub mod desktop {
20    pub use greentic_runner_desktop::*;
21}
22
23pub mod gen_bindings;
24pub mod info;
25
26/// Launch the canonical HTTP host. This is equivalent to running the
27/// `greentic-runner` binary with the provided [`RunnerConfig`].
28pub async fn run_http_host(cfg: RunnerConfig) -> Result<()> {
29    greentic_runner_host::run(cfg).await
30}
31
32/// Build and start a [`RunnerHost`] without wiring the HTTP ingress server.
33/// Callers are responsible for loading packs via [`RunnerHost::load_pack`] and
34/// invoking [`RunnerHost::handle_activity`] directly.
35pub async fn start_embedded_host(builder: HostBuilder) -> Result<RunnerHost> {
36    let host = builder.build()?;
37    host.start().await?;
38    Ok(host)
39}
40
41#[cfg(test)]
42mod tests {
43    use super::*;
44    use greentic_runner_host::config::{
45        FlowRetryConfig, HostConfig, OperatorPolicy, RateLimits, SecretsPolicy, StateStorePolicy,
46        WebhookPolicy,
47    };
48    use greentic_runner_host::trace::TraceConfig;
49    use greentic_runner_host::validate::ValidationConfig;
50    use std::collections::HashMap;
51    use std::path::PathBuf;
52
53    fn host_config() -> HostConfig {
54        HostConfig {
55            tenant: "embedded".into(),
56            bindings_path: PathBuf::from("<test>"),
57            flow_type_bindings: HashMap::new(),
58            rate_limits: RateLimits::default(),
59            retry: FlowRetryConfig::default(),
60            http_enabled: false,
61            secrets_policy: SecretsPolicy::allow_all(),
62            state_store_policy: StateStorePolicy::default(),
63            webhook_policy: WebhookPolicy::default(),
64            timers: Vec::new(),
65            oauth: None,
66            mocks: None,
67            pack_bindings: Vec::new(),
68            env_passthrough: Vec::new(),
69            trace: TraceConfig::from_env(),
70            validation: ValidationConfig::from_env(),
71            operator_policy: OperatorPolicy::allow_all(),
72            fast2flow: Default::default(),
73            #[cfg(feature = "agentic-worker")]
74            agents: HashMap::new(),
75            #[cfg(feature = "agentic-worker")]
76            graphs: HashMap::new(),
77        }
78    }
79
80    fn resolved_config() -> greentic_config::ResolvedConfig {
81        greentic_config::ResolvedConfig {
82            config: greentic_config_types::GreenticConfig {
83                schema_version: greentic_config_types::ConfigVersion::v1(),
84                environment: greentic_config_types::EnvironmentConfig {
85                    env_id: greentic_types::EnvId::new("local").expect("env"),
86                    deployment: None,
87                    connection: None,
88                    region: None,
89                },
90                paths: greentic_config_types::PathsConfig {
91                    greentic_root: PathBuf::from(".greentic"),
92                    state_dir: PathBuf::from(".greentic/state"),
93                    cache_dir: PathBuf::from(".greentic/cache"),
94                    logs_dir: PathBuf::from(".greentic/logs"),
95                },
96                packs: None,
97                services: None,
98                events: None,
99                runtime: greentic_config_types::RuntimeConfig::default(),
100                telemetry: greentic_config_types::TelemetryConfig::default(),
101                network: greentic_config_types::NetworkConfig::default(),
102                deployer: None,
103                secrets: greentic_config_types::SecretsBackendRefConfig::default(),
104                dev: None,
105            },
106            provenance: HashMap::new(),
107            warnings: Vec::new(),
108        }
109    }
110
111    #[tokio::test]
112    async fn start_embedded_host_rejects_empty_builder() {
113        assert!(start_embedded_host(HostBuilder::new()).await.is_err());
114    }
115
116    #[tokio::test]
117    async fn start_embedded_host_starts_with_minimal_config() {
118        let host = start_embedded_host(HostBuilder::new().with_config(host_config()))
119            .await
120            .expect("embedded host");
121        host.stop().await.expect("stop");
122    }
123
124    #[tokio::test]
125    async fn run_http_host_rejects_empty_runner_config() {
126        let cfg = RunnerConfig {
127            tenant_bindings: HashMap::new(),
128            pack: runner_core::env::PackConfig {
129                source: runner_core::env::PackSource::Fs,
130                index_location: runner_core::env::IndexLocation::File(PathBuf::from("index.json")),
131                cache_dir: PathBuf::from("cache"),
132                public_key: None,
133                network: None,
134            },
135            port: 0,
136            refresh_interval: std::time::Duration::from_secs(1),
137            routing: greentic_runner_host::RoutingConfig::default(),
138            admin: greentic_runner_host::AdminAuth::default(),
139            telemetry: None,
140            secrets_backend: greentic_runner_host::secrets::SecretsBackend::Env,
141            wasi_policy: greentic_runner_host::RunnerWasiPolicy::default(),
142            resolved_config: resolved_config(),
143            trace: greentic_runner_host::trace::TraceConfig::from_env(),
144            validation: greentic_runner_host::validate::ValidationConfig::from_env(),
145        };
146
147        assert!(run_http_host(cfg).await.is_err());
148    }
149}