Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 33 additions & 2 deletions crates/goose-sdk-types/src/custom_requests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -618,11 +618,42 @@ pub struct ExportSessionResponse {
pub data: String,
}

/// Import a session from a JSON string.
/// Import a session from a JSON string or share link.
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcRequest)]
#[request(method = "_goose/unstable/session/import", response = ImportSessionResponse)]
#[serde(rename_all = "camelCase")]
pub struct ImportSessionRequest {
pub data: String,
pub input: String,
pub source: SessionImportSource,
}

#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum SessionImportSource {
#[default]
Auto,
Json,
Nostr,
}

#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcRequest)]
#[request(
method = "_goose/unstable/session/share/nostr",
response = ShareSessionNostrResponse
)]
#[serde(rename_all = "camelCase")]
pub struct ShareSessionNostrRequest {
pub session_id: String,
pub relays: Vec<String>,
}

#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcResponse)]
#[serde(rename_all = "camelCase")]
pub struct ShareSessionNostrResponse {
pub deeplink: String,
pub nevent: String,
pub event_id: String,
pub relays: Vec<String>,
}

/// Import session response — metadata about the newly created session.
Expand Down
5 changes: 0 additions & 5 deletions crates/goose-server/src/openapi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -443,8 +443,6 @@ derive_utoipa!(IconTheme as IconThemeSchema);
super::routes::session_events::session_cancel,
super::routes::session::get_session,
super::routes::session::update_session_name,
super::routes::session::share_session_nostr,
super::routes::session::import_session_nostr,
super::routes::session::update_session_user_recipe_values,
super::routes::session::fork_session,
super::routes::session::get_session_extensions,
Expand Down Expand Up @@ -514,9 +512,6 @@ derive_utoipa!(IconTheme as IconThemeSchema);
super::routes::session_events::SessionReplyRequest,
super::routes::session_events::SessionReplyResponse,
super::routes::session_events::CancelRequest,
super::routes::session::ShareSessionNostrRequest,
super::routes::session::ShareSessionNostrResponse,
super::routes::session::ImportSessionNostrRequest,
super::routes::session::UpdateSessionNameRequest,
super::routes::session::UpdateSessionUserRecipeValuesRequest,
super::routes::session::UpdateSessionUserRecipeValuesResponse,
Expand Down
125 changes: 1 addition & 124 deletions crates/goose-server/src/routes/session.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::routes::errors::ErrorResponse;
use crate::routes::recipe_utils::{apply_recipe_to_agent, build_recipe_with_parameter_values};
use crate::state::AppState;
use axum::extract::{DefaultBodyLimit, State};
use axum::extract::State;
use axum::routing::post;
use axum::{
extract::Path,
Expand All @@ -11,10 +11,6 @@ use axum::{
};
use goose::agents::ExtensionConfig;
use goose::recipe::Recipe;
#[cfg(feature = "nostr")]
use goose::session::nostr_share;
#[cfg(feature = "nostr")]
use goose::session::session_manager::SessionType;
use goose::session::{EnabledExtensionsState, Session};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
Expand All @@ -40,30 +36,6 @@ pub struct UpdateSessionUserRecipeValuesResponse {
recipe: Recipe,
}

#[cfg_attr(not(feature = "nostr"), allow(dead_code))]
#[derive(Deserialize, ToSchema)]
#[serde(rename_all = "camelCase")]
pub struct ShareSessionNostrRequest {
#[serde(default)]
relays: Vec<String>,
}

#[derive(Serialize, ToSchema)]
#[serde(rename_all = "camelCase")]
pub struct ShareSessionNostrResponse {
deeplink: String,
nevent: String,
event_id: String,
relays: Vec<String>,
}

#[cfg_attr(not(feature = "nostr"), allow(dead_code))]
#[derive(Deserialize, ToSchema)]
#[serde(rename_all = "camelCase")]
pub struct ImportSessionNostrRequest {
deeplink: String,
}

#[derive(Deserialize, ToSchema)]
#[serde(rename_all = "camelCase")]
pub struct ForkRequest {
Expand Down Expand Up @@ -229,93 +201,6 @@ async fn update_session_user_recipe_values(
}
}

#[cfg_attr(not(feature = "nostr"), allow(unused_variables))]
#[utoipa::path(
post,
path = "/sessions/{session_id}/share/nostr",
request_body = ShareSessionNostrRequest,
params(
("session_id" = String, Path, description = "Unique identifier for the session")
),
responses(
(status = 200, description = "Session shared to Nostr successfully", body = ShareSessionNostrResponse),
(status = 401, description = "Unauthorized - Invalid or missing API key"),
(status = 404, description = "Session not found"),
(status = 500, description = "Internal server error")
),
security(
("api_key" = [])
),
tag = "Session Management"
)]
async fn share_session_nostr(
State(state): State<Arc<AppState>>,
Path(session_id): Path<String>,
Json(request): Json<ShareSessionNostrRequest>,
) -> Result<Json<ShareSessionNostrResponse>, StatusCode> {
#[cfg(feature = "nostr")]
{
let exported = state
.session_manager()
.export_session(&session_id)
.await
.map_err(|_| StatusCode::NOT_FOUND)?;

let relays = nostr_share::resolve_relays(request.relays, goose::config::Config::global());
let share = nostr_share::publish_session_json(&exported, relays)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;

Ok(Json(ShareSessionNostrResponse {
deeplink: share.deeplink,
nevent: share.nevent,
event_id: share.event_id,
relays: share.relays,
}))
}

#[cfg(not(feature = "nostr"))]
Err(StatusCode::NOT_FOUND)
}

#[cfg_attr(not(feature = "nostr"), allow(unused_variables))]
#[utoipa::path(
post,
path = "/sessions/import/nostr",
request_body = ImportSessionNostrRequest,
responses(
(status = 200, description = "Nostr shared session imported successfully", body = Session),
(status = 401, description = "Unauthorized - Invalid or missing API key"),
(status = 400, description = "Bad request - Invalid Nostr share link"),
(status = 500, description = "Internal server error")
),
security(
("api_key" = [])
),
tag = "Session Management"
)]
async fn import_session_nostr(
State(state): State<Arc<AppState>>,
Json(request): Json<ImportSessionNostrRequest>,
) -> Result<Json<Session>, StatusCode> {
#[cfg(feature = "nostr")]
{
let json = nostr_share::import_session_json_from_deeplink(&request.deeplink)
.await
.map_err(|_| StatusCode::BAD_REQUEST)?;
let session = state
.session_manager()
.import_session(&json, Some(SessionType::User))
.await
.map_err(|_| StatusCode::BAD_REQUEST)?;

Ok(Json(session))
}

#[cfg(not(feature = "nostr"))]
Err(StatusCode::NOT_FOUND)
}

#[utoipa::path(
post,
path = "/sessions/{session_id}/fork",
Expand Down Expand Up @@ -453,14 +338,6 @@ async fn get_session_extensions(
pub fn routes(state: Arc<AppState>) -> Router {
Router::new()
.route("/sessions/{session_id}", get(get_session))
.route(
"/sessions/{session_id}/share/nostr",
post(share_session_nostr).layer(DefaultBodyLimit::max(25 * 1024 * 1024)),
)
.route(
"/sessions/import/nostr",
post(import_session_nostr).layer(DefaultBodyLimit::max(25 * 1024 * 1024)),
)
.route("/sessions/{session_id}/name", put(update_session_name))
.route(
"/sessions/{session_id}/user_recipe_values",
Expand Down
5 changes: 5 additions & 0 deletions crates/goose/acp-meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,11 @@
"requestType": "ImportSessionRequest_unstable",
"responseType": "ImportSessionResponse_unstable"
},
{
"method": "_goose/unstable/session/share/nostr",
"requestType": "ShareSessionNostrRequest_unstable",
"responseType": "ShareSessionNostrResponse_unstable"
},
{
"method": "_goose/unstable/recipes/encode",
"requestType": "EncodeRecipeRequest_unstable",
Expand Down
83 changes: 80 additions & 3 deletions crates/goose/acp-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -2766,17 +2766,29 @@
"ImportSessionRequest_unstable": {
"type": "object",
"properties": {
"data": {
"input": {
"type": "string"
},
"source": {
"$ref": "#/$defs/SessionImportSource"
}
},
"required": [
"data"
"input",
"source"
],
"description": "Import a session from a JSON string.",
"description": "Import a session from a JSON string or share link.",
"x-side": "agent",
"x-method": "_goose/unstable/session/import"
},
"SessionImportSource": {
"type": "string",
"enum": [
"auto",
"json",
"nostr"
]
},
"ImportSessionResponse_unstable": {
"type": "object",
"properties": {
Expand Down Expand Up @@ -2808,6 +2820,54 @@
"x-side": "agent",
"x-method": "_goose/unstable/session/import"
},
"ShareSessionNostrRequest_unstable": {
"type": "object",
"properties": {
"sessionId": {
"type": "string"
},
"relays": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
"sessionId",
"relays"
],
"x-side": "agent",
"x-method": "_goose/unstable/session/share/nostr"
},
"ShareSessionNostrResponse_unstable": {
"type": "object",
"properties": {
"deeplink": {
"type": "string"
},
"nevent": {
"type": "string"
},
"eventId": {
"type": "string"
},
"relays": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
"deeplink",
"nevent",
"eventId",
"relays"
],
"x-side": "agent",
"x-method": "_goose/unstable/session/share/nostr"
},
"EncodeRecipeRequest_unstable": {
"type": "object",
"properties": {
Expand Down Expand Up @@ -5525,6 +5585,15 @@
"description": "Params for _goose/unstable/session/import",
"title": "ImportSessionRequest_unstable"
},
{
"allOf": [
{
"$ref": "#/$defs/ShareSessionNostrRequest_unstable"
}
],
"description": "Params for _goose/unstable/session/share/nostr",
"title": "ShareSessionNostrRequest_unstable"
},
{
"allOf": [
{
Expand Down Expand Up @@ -6199,6 +6268,14 @@
],
"title": "ImportSessionResponse_unstable"
},
{
"allOf": [
{
"$ref": "#/$defs/ShareSessionNostrResponse_unstable"
}
],
"title": "ShareSessionNostrResponse_unstable"
},
{
"allOf": [
{
Expand Down
2 changes: 1 addition & 1 deletion crates/goose/src/acp/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ use crate::providers::inventory::{
};
use crate::scheduler_trait::SchedulerTrait;
use crate::session::{
EnabledExtensionsState, ExtensionData, ExtensionState, Session, SessionManager,
EnabledExtensionsState, ExtensionData, ExtensionState, Session, SessionManager, SessionType,
};
use crate::source_roots::SourceRoot;
use crate::utils::sanitize_unicode_tags;
Expand Down
8 changes: 8 additions & 0 deletions crates/goose/src/acp/server/custom_dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,14 @@ impl GooseAcpAgent {
self.on_import_session(req).await
}

#[custom_method(ShareSessionNostrRequest)]
async fn dispatch_share_session_nostr(
&self,
req: ShareSessionNostrRequest,
) -> Result<ShareSessionNostrResponse, agent_client_protocol::Error> {
self.on_share_session_nostr(req).await
}

#[custom_method(EncodeRecipeRequest)]
async fn dispatch_encode_recipe(
&self,
Expand Down
Loading
Loading