use std::path::{Path, PathBuf};
use serde_json::json;
use uuid::Uuid;
use crate::configuration::{BundleConfiguration, TargetConfiguration};
use super::super::authorization::{
AuthorizationContext, grant_authorized_ui_sessions, load_authorization_context,
permission_max_pending,
};
use super::super::connection::BundleCatalog;
use super::super::delivery::{
QuiescenceOptions, deliver_one_target, enqueue_sync_delivery, prompt_batch_settings,
};
use super::super::routing::{
Addressing, Capability, OperationProfile, ResolvedRoute, requester_home_namespace,
resolve_raww_route,
};
use super::super::{
AsyncDeliveryTask, DeliveryPayloadMode, ListedSessionTransport, RelayError, RelayRequest,
RelayResponse, SCHEMA_VERSION, SendOutcome, bare_session_id, canonical_session_id, relay_error,
session_type_not_implemented,
};
use super::routed::{load_home_context, resolve_target_bundle, run_target_operation};
use super::sender::{SenderIdentity, resolve_sender_in_namespace};
struct RawwPrepared {
bundle: BundleConfiguration,
runtime_directory: PathBuf,
target_authorization: AuthorizationContext,
}
pub(in crate::relay) fn handle_raww_routed(
home_namespace: &str,
home_runtime_directory: Option<&Path>,
request: RelayRequest,
configuration_root: &Path,
bundle_catalog: &BundleCatalog,
) -> Result<RelayResponse, RelayError> {
let RelayRequest::Raww {
request_id,
requester_session,
target_session,
text,
no_enter,
} = request
else {
return Err(relay_error(
"internal_unexpected_request",
"non-raww request routed to the raww dispatcher",
None,
));
};
if target_session.trim().is_empty() {
return Err(relay_error(
"validation_invalid_params",
"target_session must be non-empty",
Some(json!({
"field": "target_session",
})),
));
}
if text.len() > 32 * 1024 {
return Err(relay_error(
"validation_invalid_params",
"raww text exceeds maximum size of 32 KiB",
Some(json!({
"field": "text",
"max_bytes": 32 * 1024,
"bytes": text.len(),
})),
));
}
let (home_bundle, authorization) = load_home_context(home_namespace, configuration_root)?;
let requester_session = bare_session_id(requester_session.as_str(), home_namespace);
let sender = resolve_sender_in_namespace(
home_bundle.as_ref(),
&authorization,
requester_session.as_str(),
"requester_session",
)?;
run_target_operation(
home_namespace,
&authorization,
OperationProfile {
capability: Capability::Raww,
addressing: Addressing::SingleTarget,
},
|| {
resolve_raww_route(
requester_home_namespace(sender.session_id.as_str(), home_namespace),
sender.session_id.as_str(),
target_session.as_str(),
)
},
|route| {
prepare_raww(
route,
home_namespace,
home_bundle.as_ref(),
home_runtime_directory,
configuration_root,
bundle_catalog,
)
},
|route, prepared| {
execute_raww(
route,
prepared,
&sender,
home_namespace,
text,
no_enter,
request_id,
)
},
)
}
fn prepare_raww(
route: &ResolvedRoute,
home_namespace: &str,
home_bundle: Option<&BundleConfiguration>,
home_runtime_directory: Option<&Path>,
configuration_root: &Path,
bundle_catalog: &BundleCatalog,
) -> Result<RawwPrepared, RelayError> {
let target_route = &route.targets[0];
let target_bundle_name = target_route.bundle_name.as_str();
let target_session_id = target_route
.session_id
.as_deref()
.expect("raww target carries a session id");
let (bundle, runtime_directory) = resolve_target_bundle(
home_namespace,
home_bundle,
home_runtime_directory,
target_bundle_name,
configuration_root,
bundle_catalog,
)?;
if !bundle
.members
.iter()
.any(|member| member.id == target_session_id)
{
return Err(relay_error(
"validation_unknown_target",
"target_session is not a canonical configured target identifier",
Some(json!({
"target_session": canonical_session_id(target_session_id, target_bundle_name),
})),
));
}
let target_authorization = load_authorization_context(configuration_root, Some(&bundle))?;
Ok(RawwPrepared {
bundle,
runtime_directory,
target_authorization,
})
}
fn execute_raww(
route: &ResolvedRoute,
prepared: RawwPrepared,
sender: &SenderIdentity,
home_namespace: &str,
text: String,
no_enter: bool,
request_id: Option<String>,
) -> Result<RelayResponse, RelayError> {
let RawwPrepared {
bundle: raww_bundle,
runtime_directory: raww_runtime_directory,
target_authorization,
} = prepared;
let target_session_id = route.targets[0]
.session_id
.as_deref()
.expect("raww target carries a session id");
let target_member = raww_bundle
.members
.iter()
.find(|member| member.id == target_session_id)
.expect("raww target existence validated in prepare_raww");
let transport = match &target_member.target {
TargetConfiguration::Tmux(_) => ListedSessionTransport::Tmux,
TargetConfiguration::Acp(_) => ListedSessionTransport::Acp,
TargetConfiguration::Ui | TargetConfiguration::Pubsub => {
return Err(session_type_not_implemented(
target_member.id.as_str(),
target_member.target.session_type(),
));
}
};
let message_id = Uuid::new_v4().to_string();
let sender_member = sender.to_bundle_member();
let permission_decider_sessions =
grant_authorized_ui_sessions(&target_authorization, &raww_bundle);
let queue_max_pending = permission_max_pending(&target_authorization);
let task = AsyncDeliveryTask {
bundle: raww_bundle.clone(),
sender_bundle_name: home_namespace.to_string(),
sender: sender_member,
authenticated_identity: None,
all_target_sessions: vec![target_member.id.clone()],
target_session: target_member.id.clone(),
target_is_ui: false,
message: text,
message_id: message_id.clone(),
quiescence: QuiescenceOptions::for_sync(None, None, None),
batch_settings: prompt_batch_settings(),
runtime_directory: raww_runtime_directory,
completion_sender: None,
payload_mode: DeliveryPayloadMode::RawInput,
append_enter: !no_enter,
permission_decider_sessions,
permission_max_pending: queue_max_pending,
};
let result = match &target_member.target {
TargetConfiguration::Acp(_) => enqueue_sync_delivery(task)?,
TargetConfiguration::Tmux(_) => deliver_one_target(&task)?,
TargetConfiguration::Ui | TargetConfiguration::Pubsub => {
return Err(session_type_not_implemented(
target_member.id.as_str(),
target_member.target.session_type(),
));
}
};
if result.outcome != SendOutcome::Delivered {
let reason = result
.reason
.unwrap_or_else(|| "raww dispatch failed".to_string());
let code = if matches!(
result.reason_code.as_deref(),
Some("runtime_acp_worker_unavailable")
) {
"runtime_target_unavailable"
} else {
"runtime_transport_write_failed"
};
return Err(relay_error(
code,
"raww dispatch failed",
Some(json!({
"target_session": result.target_session,
"transport": transport,
"reason": reason,
"reason_code": result.reason_code,
})),
));
}
let details = if transport == ListedSessionTransport::Acp {
Some(json!({
"delivery_phase": "accepted_in_progress",
}))
} else {
Some(json!({
"delivery_phase": "accepted_dispatched",
}))
};
Ok(RelayResponse::Raww {
schema_version: SCHEMA_VERSION.to_string(),
status: "accepted".to_string(),
target_session: canonical_session_id(
target_member.id.as_str(),
raww_bundle.bundle_name.as_str(),
),
transport,
request_id,
message_id: Some(message_id),
details,
})
}