mount acp in the goosed server to migrate using acp protocols iteratively (#9097)
This commit is contained in:
@@ -6,6 +6,12 @@ use axum::{
|
||||
};
|
||||
use subtle::ConstantTimeEq;
|
||||
|
||||
fn token_matches(candidate: Option<&str>, expected: &str) -> bool {
|
||||
candidate
|
||||
.map(|key| bool::from(key.as_bytes().ct_eq(expected.as_bytes())))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
pub async fn check_token(
|
||||
State(state): State<String>,
|
||||
request: Request,
|
||||
@@ -24,10 +30,32 @@ pub async fn check_token(
|
||||
.get("X-Secret-Key")
|
||||
.and_then(|value| value.to_str().ok());
|
||||
|
||||
match secret_key {
|
||||
Some(key) if bool::from(key.as_bytes().ct_eq(state.as_bytes())) => {
|
||||
Ok(next.run(request).await)
|
||||
}
|
||||
_ => Err(StatusCode::UNAUTHORIZED),
|
||||
if token_matches(secret_key, &state) {
|
||||
Ok(next.run(request).await)
|
||||
} else {
|
||||
Err(StatusCode::UNAUTHORIZED)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn check_acp_token(
|
||||
State(state): State<String>,
|
||||
request: Request,
|
||||
next: Next,
|
||||
) -> Result<Response, StatusCode> {
|
||||
let header_token = request
|
||||
.headers()
|
||||
.get("X-Secret-Key")
|
||||
.and_then(|value| value.to_str().ok());
|
||||
|
||||
let query_token = request.uri().query().and_then(|query| {
|
||||
url::form_urlencoded::parse(query.as_bytes())
|
||||
.find(|(key, _)| key == "token")
|
||||
.map(|(_, value)| value.into_owned())
|
||||
});
|
||||
|
||||
if token_matches(header_token, &state) || token_matches(query_token.as_deref(), &state) {
|
||||
Ok(next.run(request).await)
|
||||
} else {
|
||||
Err(StatusCode::UNAUTHORIZED)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,14 @@ use crate::state;
|
||||
use anyhow::Result;
|
||||
use axum::middleware;
|
||||
use axum_server::Handle;
|
||||
use goose_server::auth::check_token;
|
||||
use goose::acp::server_factory::{AcpServer, AcpServerFactoryConfig};
|
||||
use goose::acp::transport::create_acp_router;
|
||||
use goose::agents::GoosePlatform;
|
||||
use goose::config::paths::Paths;
|
||||
use goose_server::auth::{check_acp_token, check_token};
|
||||
#[cfg(any(feature = "rustls-tls", feature = "native-tls"))]
|
||||
use goose_server::tls::setup_tls;
|
||||
use std::sync::Arc;
|
||||
use tower_http::cors::{Any, CorsLayer};
|
||||
use tracing::info;
|
||||
|
||||
@@ -64,12 +69,28 @@ pub async fn run() -> Result<()> {
|
||||
.allow_methods(Any)
|
||||
.allow_headers(Any);
|
||||
|
||||
let app = crate::routes::configure(app_state.clone(), secret_key.clone())
|
||||
.layer(middleware::from_fn_with_state(
|
||||
secret_key.clone(),
|
||||
check_token,
|
||||
))
|
||||
.layer(cors);
|
||||
// TODO(acp-migration): When ui/desktop launches `goose serve` directly,
|
||||
// move any goosed-only ACP setup into the goose serve path before deleting
|
||||
// this bridge. In particular, verify everything ACP currently gets from
|
||||
// goosed startup/AppState initialization, including builtin extension
|
||||
// registration and the desktop platform identity.
|
||||
let acp_server = Arc::new(AcpServer::new(AcpServerFactoryConfig {
|
||||
builtins: vec!["developer".to_string()],
|
||||
data_dir: Paths::data_dir(),
|
||||
config_dir: Paths::config_dir(),
|
||||
goose_platform: GoosePlatform::GooseDesktop,
|
||||
additional_source_roots: Vec::new(),
|
||||
}));
|
||||
|
||||
let rest_router = crate::routes::configure(app_state.clone(), secret_key.clone()).layer(
|
||||
middleware::from_fn_with_state(secret_key.clone(), check_token),
|
||||
);
|
||||
let acp_router = create_acp_router(acp_server).layer(middleware::from_fn_with_state(
|
||||
secret_key.clone(),
|
||||
check_acp_token,
|
||||
));
|
||||
|
||||
let app = rest_router.merge(acp_router).layer(cors);
|
||||
|
||||
let addr = settings.socket_addr();
|
||||
|
||||
|
||||
@@ -33,7 +33,12 @@ impl GooseAcpAgent {
|
||||
pub(super) async fn on_get_extensions(
|
||||
&self,
|
||||
) -> Result<GetExtensionsResponse, agent_client_protocol::Error> {
|
||||
let extensions = crate::config::extensions::get_all_extensions();
|
||||
let extensions = crate::config::extensions::get_all_extensions()
|
||||
.into_iter()
|
||||
.filter(|ext| {
|
||||
!crate::agents::extension_manager::is_hidden_extension(&ext.config.name())
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let warnings = crate::config::extensions::get_warnings();
|
||||
let extensions_json = extensions
|
||||
.into_iter()
|
||||
|
||||
@@ -94,10 +94,8 @@ async fn health() -> &'static str {
|
||||
"ok"
|
||||
}
|
||||
|
||||
pub fn create_router(server: Arc<AcpServer>, secret_key: String) -> Router {
|
||||
let registry = Arc::new(connection::ConnectionRegistry::new(server));
|
||||
|
||||
let cors = CorsLayer::new()
|
||||
fn acp_cors_layer() -> CorsLayer {
|
||||
CorsLayer::new()
|
||||
.allow_origin(Any)
|
||||
.allow_methods([Method::GET, Method::POST, Method::DELETE, Method::OPTIONS])
|
||||
.allow_headers([
|
||||
@@ -113,14 +111,26 @@ pub fn create_router(server: Arc<AcpServer>, secret_key: String) -> Router {
|
||||
.expose_headers([
|
||||
HeaderName::from_static("acp-connection-id"),
|
||||
HeaderName::from_static("acp-session-id"),
|
||||
]);
|
||||
])
|
||||
}
|
||||
|
||||
fn create_acp_routes(server: Arc<AcpServer>) -> Router {
|
||||
let registry = Arc::new(connection::ConnectionRegistry::new(server));
|
||||
|
||||
Router::new()
|
||||
.route("/health", get(health))
|
||||
.route("/status", get(health))
|
||||
.route("/acp", post(http::handle_post).with_state(registry.clone()))
|
||||
.route("/acp", get(handle_get).with_state(registry.clone()))
|
||||
.route("/acp", delete(http::handle_delete).with_state(registry))
|
||||
.merge(super::mcp_app_proxy::routes(secret_key))
|
||||
.layer(cors)
|
||||
}
|
||||
|
||||
pub fn create_acp_router(server: Arc<AcpServer>) -> Router {
|
||||
create_acp_routes(server).layer(acp_cors_layer())
|
||||
}
|
||||
|
||||
pub fn create_router(server: Arc<AcpServer>, secret_key: String) -> Router {
|
||||
create_acp_routes(server)
|
||||
.route("/health", get(health))
|
||||
.route("/status", get(health))
|
||||
.merge(super::mcp_app_proxy::routes(secret_key))
|
||||
.layer(acp_cors_layer())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user