integrat the app-code plugin to the signal-gateway-bin
This commit is contained in:
Generated
+2
-1
@@ -1748,7 +1748,6 @@ name = "signal-gateway-app-code"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"conf",
|
|
||||||
"flate2",
|
"flate2",
|
||||||
"regex",
|
"regex",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
@@ -1775,12 +1774,14 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"signal-gateway",
|
"signal-gateway",
|
||||||
|
"signal-gateway-app-code",
|
||||||
"syslog_rfc5424",
|
"syslog_rfc5424",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
"toml",
|
"toml",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@@ -10,10 +10,9 @@ workspace = true
|
|||||||
signal-gateway = { path = "../signal-gateway", default-features = false }
|
signal-gateway = { path = "../signal-gateway", default-features = false }
|
||||||
|
|
||||||
async-trait = { workspace = true }
|
async-trait = { workspace = true }
|
||||||
conf = { workspace = true }
|
|
||||||
flate2 = { workspace = true }
|
flate2 = { workspace = true }
|
||||||
regex = { workspace = true }
|
regex = { workspace = true }
|
||||||
reqwest = { workspace = true }
|
reqwest = { workspace = true, features = ["rustls-tls"] }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
tar = { workspace = true }
|
tar = { workspace = true }
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
//! tarballs from GitHub and caching them in memory.
|
//! tarballs from GitHub and caching them in memory.
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use conf::Conf;
|
|
||||||
use flate2::read::GzDecoder;
|
use flate2::read::GzDecoder;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
@@ -18,7 +17,8 @@ use tokio::sync::{Mutex, MutexGuard};
|
|||||||
use tracing::{error, info, warn};
|
use tracing::{error, info, warn};
|
||||||
|
|
||||||
/// A GitHub repository identifier (owner/repo).
|
/// A GitHub repository identifier (owner/repo).
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
|
#[serde(try_from = "String")]
|
||||||
pub struct GitHubRepo {
|
pub struct GitHubRepo {
|
||||||
/// The repository owner (user or organization).
|
/// The repository owner (user or organization).
|
||||||
pub owner: String,
|
pub owner: String,
|
||||||
@@ -44,18 +44,22 @@ impl FromStr for GitHubRepo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TryFrom<String> for GitHubRepo {
|
||||||
|
type Error = String;
|
||||||
|
|
||||||
|
fn try_from(s: String) -> Result<Self, Self::Error> {
|
||||||
|
s.parse()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Configuration for an application's source code access.
|
/// Configuration for an application's source code access.
|
||||||
#[derive(Clone, Conf, Debug)]
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
#[conf(serde)]
|
|
||||||
pub struct AppCodeConfig {
|
pub struct AppCodeConfig {
|
||||||
/// Name of the application (used to identify it in tool calls).
|
/// Name of the application (used to identify it in tool calls).
|
||||||
#[conf(long, env)]
|
|
||||||
pub name: String,
|
pub name: String,
|
||||||
/// GitHub repository in "owner/repo" format.
|
/// GitHub repository in "owner/repo" format.
|
||||||
#[conf(long, env, serde(use_value_parser))]
|
|
||||||
pub github: GitHubRepo,
|
pub github: GitHubRepo,
|
||||||
/// Path to file containing the GitHub personal access token.
|
/// Path to file containing the GitHub personal access token.
|
||||||
#[conf(long, env)]
|
|
||||||
pub token_file: PathBuf,
|
pub token_file: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ rustls-tls = ["signal-gateway/rustls-tls"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
signal-gateway = { path = "../signal-gateway" }
|
signal-gateway = { path = "../signal-gateway" }
|
||||||
|
signal-gateway-app-code = { path = "../signal-gateway-app-code" }
|
||||||
|
|
||||||
async-trait = { workspace = true }
|
async-trait = { workspace = true }
|
||||||
chrono = { workspace = true }
|
chrono = { workspace = true }
|
||||||
@@ -26,6 +27,7 @@ dotenvy = { workspace = true }
|
|||||||
hyper = { workspace = true }
|
hyper = { workspace = true }
|
||||||
hyper-util = { workspace = true }
|
hyper-util = { workspace = true }
|
||||||
reqwest = { workspace = true }
|
reqwest = { workspace = true }
|
||||||
|
url = { workspace = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
syslog_rfc5424 = { workspace = true }
|
syslog_rfc5424 = { workspace = true }
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
//! Extended configuration for application source code browsing.
|
||||||
|
|
||||||
|
use serde::Deserialize;
|
||||||
|
use signal_gateway_app_code::{AppCode, AppCodeConfig, ShaCallback};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tracing::warn;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
|
/// Extended configuration for AppCode with HTTP-based SHA fetching.
|
||||||
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
|
pub struct AppCodeConfigExt {
|
||||||
|
/// The base AppCode configuration.
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub config: AppCodeConfig,
|
||||||
|
/// URL to GET the current deployed version SHA.
|
||||||
|
pub version_sha_http_get: Url,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AppCodeConfigExt {
|
||||||
|
/// Convert to an AppCode instance with HTTP-based SHA callback.
|
||||||
|
pub fn into_app_code(self) -> Result<AppCode, std::io::Error> {
|
||||||
|
let url = self.version_sha_http_get.clone();
|
||||||
|
let client = reqwest::Client::new();
|
||||||
|
|
||||||
|
let sha_callback: ShaCallback = Arc::new(move || {
|
||||||
|
let url = url.clone();
|
||||||
|
let client = client.clone();
|
||||||
|
Box::pin(async move {
|
||||||
|
let response = client
|
||||||
|
.get(url.as_str())
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
.map_err(|e| -> Box<dyn std::error::Error + Send + Sync> {
|
||||||
|
Box::new(std::io::Error::other(format!(
|
||||||
|
"HTTP request to {url} failed: {e}"
|
||||||
|
)))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
if !response.status().is_success() {
|
||||||
|
return Err(Box::new(std::io::Error::other(format!(
|
||||||
|
"HTTP request to {url} returned {}",
|
||||||
|
response.status()
|
||||||
|
))) as Box<dyn std::error::Error + Send + Sync>);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut sha = response
|
||||||
|
.text()
|
||||||
|
.await
|
||||||
|
.map_err(|e| -> Box<dyn std::error::Error + Send + Sync> {
|
||||||
|
Box::new(std::io::Error::other(format!(
|
||||||
|
"Failed to read response from {url}: {e}"
|
||||||
|
)))
|
||||||
|
})?
|
||||||
|
.trim()
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
// Handle -dirty suffix
|
||||||
|
if let Some(clean_sha) = sha.strip_suffix("-dirty") {
|
||||||
|
warn!(
|
||||||
|
"Version SHA has -dirty suffix, using clean SHA: {}",
|
||||||
|
clean_sha
|
||||||
|
);
|
||||||
|
sha = clean_sha.to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(sha)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
AppCode::new(self.config, sha_callback)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ use conf::Conf;
|
|||||||
use hyper::service::service_fn;
|
use hyper::service::service_fn;
|
||||||
use hyper_util::{rt::TokioIo, server::conn::auto};
|
use hyper_util::{rt::TokioIo, server::conn::auto};
|
||||||
use signal_gateway::{CommandRouter, Gateway, GatewayConfig, Handling};
|
use signal_gateway::{CommandRouter, Gateway, GatewayConfig, Handling};
|
||||||
|
use signal_gateway_app_code::AppCodeTools;
|
||||||
use std::{env, fs, net::SocketAddr, path::PathBuf, sync::Arc, time::Duration};
|
use std::{env, fs, net::SocketAddr, path::PathBuf, sync::Arc, time::Duration};
|
||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
use tokio_util::sync::CancellationToken;
|
use tokio_util::sync::CancellationToken;
|
||||||
@@ -15,6 +16,9 @@ use tracing_subscriber::EnvFilter;
|
|||||||
mod admin_http;
|
mod admin_http;
|
||||||
use admin_http::AdminHttpConfig;
|
use admin_http::AdminHttpConfig;
|
||||||
|
|
||||||
|
mod app_code;
|
||||||
|
use app_code::AppCodeConfigExt;
|
||||||
|
|
||||||
mod syslog;
|
mod syslog;
|
||||||
use syslog::SyslogConfig;
|
use syslog::SyslogConfig;
|
||||||
|
|
||||||
@@ -43,6 +47,9 @@ pub struct Config {
|
|||||||
/// Optional HTTP endpoint for forwarding admin messages.
|
/// Optional HTTP endpoint for forwarding admin messages.
|
||||||
#[conf(flatten, prefix)]
|
#[conf(flatten, prefix)]
|
||||||
admin_http: Option<AdminHttpConfig>,
|
admin_http: Option<AdminHttpConfig>,
|
||||||
|
/// Application source code configurations for Claude tools.
|
||||||
|
#[conf(long, env, value_parser = serde_json::from_str)]
|
||||||
|
app_code: Vec<AppCodeConfigExt>,
|
||||||
#[conf(flatten, serde(flatten))]
|
#[conf(flatten, serde(flatten))]
|
||||||
gateway: GatewayConfig,
|
gateway: GatewayConfig,
|
||||||
}
|
}
|
||||||
@@ -123,11 +130,34 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let command_router = router_builder.build();
|
let command_router = router_builder.build();
|
||||||
let gateway = Gateway::builder(config.gateway)
|
|
||||||
|
// Build AppCode tools if configured
|
||||||
|
let app_code_tools = if !config.app_code.is_empty() {
|
||||||
|
let mut apps = Vec::new();
|
||||||
|
for app_config in config.app_code {
|
||||||
|
let name = app_config.config.name.clone();
|
||||||
|
match app_config.into_app_code() {
|
||||||
|
Ok(app) => apps.push(app),
|
||||||
|
Err(e) => {
|
||||||
|
error!("Failed to initialize app code '{name}': {e}");
|
||||||
|
return Err(e.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(Arc::new(AppCodeTools::new(apps)))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut gateway_builder = Gateway::builder(config.gateway)
|
||||||
.with_cancellation_token(token.clone())
|
.with_cancellation_token(token.clone())
|
||||||
.with_command_router(command_router)
|
.with_command_router(command_router);
|
||||||
.build()
|
|
||||||
.await;
|
if let Some(tools) = app_code_tools {
|
||||||
|
gateway_builder = gateway_builder.with_tools(tools);
|
||||||
|
}
|
||||||
|
|
||||||
|
let gateway = gateway_builder.build().await;
|
||||||
|
|
||||||
let listener = TcpListener::bind(config.http_listen_addr).await.unwrap();
|
let listener = TcpListener::bind(config.http_listen_addr).await.unwrap();
|
||||||
info!("Listening for http on {}", config.http_listen_addr);
|
info!("Listening for http on {}", config.http_listen_addr);
|
||||||
|
|||||||
Reference in New Issue
Block a user