1use std::path::Path;
2use std::sync::Arc;
3
4use async_trait::async_trait;
5use harn_serve::{AcpRuntimeConfigurator, AcpServerConfig, AuthPolicy};
6use tokio::sync::mpsc;
7
8struct CliAcpRuntimeConfigurator;
9
10#[async_trait(?Send)]
11impl AcpRuntimeConfigurator for CliAcpRuntimeConfigurator {
12 async fn configure(
13 &self,
14 vm: &mut harn_vm::Vm,
15 source_path: Option<&Path>,
16 ) -> Result<(), String> {
17 #[cfg(feature = "hostlib")]
23 {
24 let _ = harn_hostlib::install_default(vm);
25 }
26
27 let Some(path) = source_path else {
28 return Ok(());
29 };
30
31 let extensions = crate::package::load_runtime_extensions(path);
32 crate::package::install_runtime_extensions(&extensions);
33 crate::package::install_manifest_triggers(vm, &extensions)
34 .await
35 .map_err(|error| format!("failed to install manifest triggers: {error}"))?;
36 crate::package::install_manifest_hooks(vm, &extensions)
37 .await
38 .map_err(|error| format!("failed to install manifest hooks: {error}"))?;
39 Ok(())
40 }
41}
42
43pub(crate) fn server_config(pipeline: Option<String>, auth_policy: AuthPolicy) -> AcpServerConfig {
44 let extensions = pipeline
45 .as_deref()
46 .map(Path::new)
47 .map(crate::package::load_runtime_extensions)
48 .unwrap_or_default();
49 AcpServerConfig::new(pipeline)
50 .with_auth_policy(auth_policy)
51 .with_runtime_configurator(Arc::new(CliAcpRuntimeConfigurator))
52 .with_llm_overrides(extensions.llm, extensions.capabilities)
53}
54
55fn ensure_acp_event_log(pipeline: Option<&str>) {
56 if harn_vm::event_log::active_event_log().is_none() {
57 let base_dir = pipeline
58 .map(Path::new)
59 .and_then(Path::parent)
60 .unwrap_or_else(|| Path::new("."));
61 if let Err(error) = harn_vm::event_log::install_default_for_base_dir(base_dir) {
62 eprintln!(
63 "[harn] ACP session replay disabled: failed to initialize EventLog for {}: {error}",
64 base_dir.display()
65 );
66 }
67 }
68}
69
70pub(crate) async fn run_acp_server(pipeline: Option<&str>, auth_policy: AuthPolicy) {
71 ensure_acp_event_log(pipeline);
72 harn_serve::run_acp_server(server_config(pipeline.map(str::to_string), auth_policy)).await;
73}
74
75pub(crate) async fn run_acp_channel_server(
76 pipeline: Option<String>,
77 request_rx: mpsc::UnboundedReceiver<serde_json::Value>,
78 response_tx: mpsc::UnboundedSender<String>,
79) {
80 harn_serve::run_acp_channel_server(
81 server_config(pipeline, AuthPolicy::allow_all()),
82 request_rx,
83 response_tx,
84 )
85 .await;
86}