mirror of
https://github.com/block/goose.git
synced 2026-07-17 12:56:20 +02:00
Expose everything in the API too
This commit is contained in:
Generated
+1
@@ -2882,6 +2882,7 @@ dependencies = [
|
||||
"goose",
|
||||
"goose-mcp",
|
||||
"http 1.2.0",
|
||||
"once_cell",
|
||||
"reqwest 0.12.12",
|
||||
"rmcp",
|
||||
"schemars",
|
||||
|
||||
@@ -39,6 +39,7 @@ reqwest = { version = "0.12.9", features = ["json", "rustls-tls", "blocking", "m
|
||||
tokio-util = "0.7.15"
|
||||
uuid = { version = "1.11", features = ["v4"] }
|
||||
serde_path_to_error = "0.1.20"
|
||||
once_cell = "1.19"
|
||||
|
||||
[[bin]]
|
||||
name = "goosed"
|
||||
|
||||
@@ -327,6 +327,10 @@ derive_utoipa!(Icon as IconSchema);
|
||||
super::routes::status::status,
|
||||
super::routes::status::diagnostics,
|
||||
super::routes::database::database_status,
|
||||
super::routes::database::create_backup,
|
||||
super::routes::database::list_backups,
|
||||
super::routes::database::delete_backups,
|
||||
super::routes::database::restore_backup,
|
||||
super::routes::config_management::backup_config,
|
||||
super::routes::config_management::recover_config,
|
||||
super::routes::config_management::validate_config,
|
||||
@@ -447,6 +451,13 @@ derive_utoipa!(Icon as IconSchema);
|
||||
SessionInsights,
|
||||
super::routes::database::DatabaseStatusResponse,
|
||||
super::routes::database::BackupInfo,
|
||||
super::routes::database::CreateBackupRequest,
|
||||
super::routes::database::CreateBackupResponse,
|
||||
super::routes::database::BackupsListResponse,
|
||||
super::routes::database::RestoreBackupRequest,
|
||||
super::routes::database::RestoreBackupResponse,
|
||||
super::routes::database::DeleteBackupsRequest,
|
||||
super::routes::database::DeleteBackupsResponse,
|
||||
Conversation,
|
||||
IconSchema,
|
||||
goose::session::extension_data::ExtensionData,
|
||||
|
||||
@@ -1,9 +1,23 @@
|
||||
use axum::{http::StatusCode, routing::get, Json, Router};
|
||||
use axum::{
|
||||
http::StatusCode,
|
||||
routing::{delete, get, post},
|
||||
Json, Router,
|
||||
};
|
||||
use chrono::{DateTime, Utc};
|
||||
use goose::config::paths::Paths;
|
||||
use goose::session::session_manager::SessionManager;
|
||||
use once_cell::sync::Lazy;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::Mutex;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use crate::routes::errors::ErrorResponse;
|
||||
|
||||
static BACKUP_MUTEX: Lazy<Arc<Mutex<()>>> = Lazy::new(|| Arc::new(Mutex::new(())));
|
||||
static RESTORE_MUTEX: Lazy<Arc<Mutex<()>>> = Lazy::new(|| Arc::new(Mutex::new(())));
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BackupInfo {
|
||||
@@ -13,6 +27,69 @@ pub struct BackupInfo {
|
||||
pub schema_version: Option<i32>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, ToSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreateBackupRequest {
|
||||
/// Optional custom name for the backup
|
||||
pub name: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, ToSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreateBackupResponse {
|
||||
/// Filename of the created backup
|
||||
pub filename: String,
|
||||
/// Size of the backup file in bytes
|
||||
pub size: u64,
|
||||
/// When the backup was created
|
||||
pub created_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, ToSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BackupsListResponse {
|
||||
/// List of available backups
|
||||
pub backups: Vec<BackupInfo>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, ToSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RestoreBackupRequest {
|
||||
/// Filename of the backup to restore
|
||||
pub filename: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, ToSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RestoreBackupResponse {
|
||||
/// Success message
|
||||
pub message: String,
|
||||
/// Backup that was restored
|
||||
pub restored_from: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, ToSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DeleteBackupsRequest {
|
||||
/// List of backup filenames to delete
|
||||
pub filenames: Vec<String>,
|
||||
/// Delete all backups if true
|
||||
pub delete_all: bool,
|
||||
/// Clean up orphaned WAL/SHM files
|
||||
pub cleanup_orphaned: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize, ToSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DeleteBackupsResponse {
|
||||
/// Successfully deleted files
|
||||
pub deleted: Vec<String>,
|
||||
/// Files that failed to delete
|
||||
pub failed: Vec<String>,
|
||||
/// Number of orphaned files cleaned
|
||||
pub orphaned_cleaned: usize,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DatabaseStatusResponse {
|
||||
@@ -75,6 +152,321 @@ pub async fn database_status() -> Result<Json<DatabaseStatusResponse>, StatusCod
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn routes() -> Router {
|
||||
Router::new().route("/database/status", get(database_status))
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/database/backup",
|
||||
request_body = CreateBackupRequest,
|
||||
responses(
|
||||
(status = 201, description = "Backup created successfully", body = CreateBackupResponse),
|
||||
(status = 401, description = "Unauthorized - Invalid or missing API key"),
|
||||
(status = 500, description = "Internal server error")
|
||||
),
|
||||
security(
|
||||
("api_key" = [])
|
||||
),
|
||||
tag = "Database Management"
|
||||
)]
|
||||
pub async fn create_backup(
|
||||
Json(request): Json<CreateBackupRequest>,
|
||||
) -> Result<(StatusCode, Json<CreateBackupResponse>), ErrorResponse> {
|
||||
let _lock = BACKUP_MUTEX.try_lock().map_err(|_| ErrorResponse {
|
||||
message: "A backup operation is already in progress".to_string(),
|
||||
status: StatusCode::CONFLICT,
|
||||
})?;
|
||||
|
||||
let backup_path = SessionManager::create_backup(request.name)
|
||||
.await
|
||||
.map_err(|e| ErrorResponse {
|
||||
message: format!("Failed to create backup: {}", e),
|
||||
status: StatusCode::INTERNAL_SERVER_ERROR,
|
||||
})?;
|
||||
|
||||
let filename = backup_path
|
||||
.file_name()
|
||||
.and_then(|n| n.to_str())
|
||||
.unwrap_or("unknown")
|
||||
.to_string();
|
||||
|
||||
let metadata = tokio::fs::metadata(&backup_path)
|
||||
.await
|
||||
.map_err(|e| ErrorResponse {
|
||||
message: format!("Failed to read backup file metadata: {}", e),
|
||||
status: StatusCode::INTERNAL_SERVER_ERROR,
|
||||
})?;
|
||||
|
||||
let size = metadata.len();
|
||||
let created_at = Utc::now();
|
||||
|
||||
Ok((
|
||||
StatusCode::CREATED,
|
||||
Json(CreateBackupResponse {
|
||||
filename,
|
||||
size,
|
||||
created_at,
|
||||
}),
|
||||
))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/database/backups",
|
||||
responses(
|
||||
(status = 200, description = "List of backups retrieved successfully", body = BackupsListResponse),
|
||||
(status = 401, description = "Unauthorized - Invalid or missing API key"),
|
||||
(status = 500, description = "Internal server error")
|
||||
),
|
||||
security(
|
||||
("api_key" = [])
|
||||
),
|
||||
tag = "Database Management"
|
||||
)]
|
||||
pub async fn list_backups() -> Result<Json<BackupsListResponse>, ErrorResponse> {
|
||||
let backups = SessionManager::list_backups()
|
||||
.await
|
||||
.map_err(|e| ErrorResponse {
|
||||
message: format!("Failed to list backups: {}", e),
|
||||
status: StatusCode::INTERNAL_SERVER_ERROR,
|
||||
})?;
|
||||
|
||||
let sanitized_backups = backups
|
||||
.into_iter()
|
||||
.map(|backup| {
|
||||
let filename = backup
|
||||
.path
|
||||
.file_name()
|
||||
.and_then(|n| n.to_str())
|
||||
.unwrap_or("unknown")
|
||||
.to_string();
|
||||
|
||||
BackupInfo {
|
||||
filename,
|
||||
created_at: backup.created_at,
|
||||
size: backup.size,
|
||||
schema_version: backup.schema_version,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(Json(BackupsListResponse {
|
||||
backups: sanitized_backups,
|
||||
}))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/database/restore",
|
||||
request_body = RestoreBackupRequest,
|
||||
responses(
|
||||
(status = 200, description = "Database restored successfully", body = RestoreBackupResponse),
|
||||
(status = 400, description = "Bad request - Invalid backup filename"),
|
||||
(status = 401, description = "Unauthorized - Invalid or missing API key"),
|
||||
(status = 404, description = "Backup not found"),
|
||||
(status = 500, description = "Internal server error")
|
||||
),
|
||||
security(
|
||||
("api_key" = [])
|
||||
),
|
||||
tag = "Database Management"
|
||||
)]
|
||||
pub async fn restore_backup(
|
||||
Json(request): Json<RestoreBackupRequest>,
|
||||
) -> Result<Json<RestoreBackupResponse>, ErrorResponse> {
|
||||
let _lock = RESTORE_MUTEX.try_lock().map_err(|_| ErrorResponse {
|
||||
message: "A restore operation is already in progress".to_string(),
|
||||
status: StatusCode::CONFLICT,
|
||||
})?;
|
||||
|
||||
let filename = request.filename.trim();
|
||||
if filename.is_empty() {
|
||||
return Err(ErrorResponse {
|
||||
message: "Backup filename cannot be empty".to_string(),
|
||||
status: StatusCode::BAD_REQUEST,
|
||||
});
|
||||
}
|
||||
|
||||
let backup_dir = Paths::backup_dir();
|
||||
let backup_path = backup_dir.join(filename);
|
||||
|
||||
let canonical_backup = backup_path.canonicalize().map_err(|_| ErrorResponse {
|
||||
message: format!("Backup '{}' not found", filename),
|
||||
status: StatusCode::NOT_FOUND,
|
||||
})?;
|
||||
let canonical_backup_dir = backup_dir.canonicalize().map_err(|e| ErrorResponse {
|
||||
message: format!("Failed to access backup directory: {}", e),
|
||||
status: StatusCode::INTERNAL_SERVER_ERROR,
|
||||
})?;
|
||||
|
||||
if !canonical_backup.starts_with(&canonical_backup_dir) {
|
||||
tracing::warn!("Path traversal attempt detected: {}", filename);
|
||||
return Err(ErrorResponse {
|
||||
message: "Invalid backup path".to_string(),
|
||||
status: StatusCode::FORBIDDEN,
|
||||
});
|
||||
}
|
||||
|
||||
if !canonical_backup.exists() {
|
||||
return Err(ErrorResponse {
|
||||
message: format!("Backup '{}' not found", filename),
|
||||
status: StatusCode::NOT_FOUND,
|
||||
});
|
||||
}
|
||||
|
||||
SessionManager::restore_backup(&canonical_backup)
|
||||
.await
|
||||
.map_err(|e| ErrorResponse {
|
||||
message: format!("Failed to restore backup: {}", e),
|
||||
status: StatusCode::INTERNAL_SERVER_ERROR,
|
||||
})?;
|
||||
|
||||
Ok(Json(RestoreBackupResponse {
|
||||
message: "Database restored successfully. Server restart required.".to_string(),
|
||||
restored_from: filename.to_string(),
|
||||
}))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
delete,
|
||||
path = "/database/backups/delete",
|
||||
request_body = DeleteBackupsRequest,
|
||||
responses(
|
||||
(status = 200, description = "Backups deleted successfully", body = DeleteBackupsResponse),
|
||||
(status = 400, description = "Bad request - Invalid parameters"),
|
||||
(status = 401, description = "Unauthorized - Invalid or missing API key"),
|
||||
(status = 500, description = "Internal server error")
|
||||
),
|
||||
security(
|
||||
("api_key" = [])
|
||||
),
|
||||
tag = "Database Management"
|
||||
)]
|
||||
pub async fn delete_backups(
|
||||
Json(request): Json<DeleteBackupsRequest>,
|
||||
) -> Result<Json<DeleteBackupsResponse>, ErrorResponse> {
|
||||
let _lock = BACKUP_MUTEX.try_lock().map_err(|_| ErrorResponse {
|
||||
message: "A backup operation is in progress, cannot delete".to_string(),
|
||||
status: StatusCode::CONFLICT,
|
||||
})?;
|
||||
|
||||
if request.delete_all && !request.filenames.is_empty() {
|
||||
return Err(ErrorResponse {
|
||||
message: "Cannot specify both deleteAll and specific filenames".to_string(),
|
||||
status: StatusCode::BAD_REQUEST,
|
||||
});
|
||||
}
|
||||
|
||||
if !request.delete_all && request.filenames.is_empty() {
|
||||
return Err(ErrorResponse {
|
||||
message: "Must specify either deleteAll or at least one filename".to_string(),
|
||||
status: StatusCode::BAD_REQUEST,
|
||||
});
|
||||
}
|
||||
|
||||
let backup_dir = Paths::backup_dir();
|
||||
|
||||
let files_to_delete: Vec<PathBuf> = if request.delete_all {
|
||||
let backups = SessionManager::list_backups()
|
||||
.await
|
||||
.map_err(|e| ErrorResponse {
|
||||
message: format!("Failed to list backups: {}", e),
|
||||
status: StatusCode::INTERNAL_SERVER_ERROR,
|
||||
})?;
|
||||
backups.into_iter().map(|b| b.path).collect()
|
||||
} else {
|
||||
let mut resolved_paths = Vec::new();
|
||||
for filename in &request.filenames {
|
||||
let path = backup_dir.join(filename);
|
||||
if path.exists() {
|
||||
let canonical = path.canonicalize().map_err(|e| ErrorResponse {
|
||||
message: format!("Failed to resolve backup path: {}", e),
|
||||
status: StatusCode::INTERNAL_SERVER_ERROR,
|
||||
})?;
|
||||
let canonical_backup_dir =
|
||||
backup_dir.canonicalize().map_err(|e| ErrorResponse {
|
||||
message: format!("Failed to access backup directory: {}", e),
|
||||
status: StatusCode::INTERNAL_SERVER_ERROR,
|
||||
})?;
|
||||
|
||||
if !canonical.starts_with(&canonical_backup_dir) {
|
||||
tracing::warn!("Path traversal attempt detected: {}", filename);
|
||||
return Err(ErrorResponse {
|
||||
message: "Invalid backup path".to_string(),
|
||||
status: StatusCode::FORBIDDEN,
|
||||
});
|
||||
}
|
||||
resolved_paths.push(canonical);
|
||||
}
|
||||
}
|
||||
resolved_paths
|
||||
};
|
||||
|
||||
let mut deleted = Vec::new();
|
||||
let mut failed = Vec::new();
|
||||
|
||||
for path in files_to_delete {
|
||||
let filename = path
|
||||
.file_name()
|
||||
.and_then(|n| n.to_str())
|
||||
.unwrap_or("unknown")
|
||||
.to_string();
|
||||
|
||||
match tokio::fs::remove_file(&path).await {
|
||||
Ok(_) => {
|
||||
deleted.push(filename.clone());
|
||||
|
||||
let wal_path = path.with_extension("db-wal");
|
||||
if wal_path.exists() {
|
||||
let _ = tokio::fs::remove_file(&wal_path).await;
|
||||
}
|
||||
|
||||
let shm_path = path.with_extension("db-shm");
|
||||
if shm_path.exists() {
|
||||
let _ = tokio::fs::remove_file(&shm_path).await;
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
failed.push(filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut orphaned_cleaned = 0;
|
||||
if request.cleanup_orphaned {
|
||||
orphaned_cleaned = cleanup_orphaned_files(&backup_dir).await;
|
||||
}
|
||||
|
||||
Ok(Json(DeleteBackupsResponse {
|
||||
deleted,
|
||||
failed,
|
||||
orphaned_cleaned,
|
||||
}))
|
||||
}
|
||||
|
||||
async fn cleanup_orphaned_files(backup_dir: &Path) -> usize {
|
||||
let mut cleaned = 0;
|
||||
|
||||
if let Ok(mut entries) = tokio::fs::read_dir(backup_dir).await {
|
||||
while let Ok(Some(entry)) = entries.next_entry().await {
|
||||
let path = entry.path();
|
||||
if let Some(extension) = path.extension() {
|
||||
let ext_str = extension.to_string_lossy();
|
||||
if ext_str == "db-wal" || ext_str == "db-shm" {
|
||||
let db_path = path.with_extension("db");
|
||||
if !db_path.exists() && tokio::fs::remove_file(&path).await.is_ok() {
|
||||
cleaned += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cleaned
|
||||
}
|
||||
|
||||
pub fn routes() -> Router {
|
||||
Router::new()
|
||||
.route("/database/status", get(database_status))
|
||||
.route("/database/backup", post(create_backup))
|
||||
.route("/database/backups", get(list_backups))
|
||||
.route("/database/backups/delete", delete(delete_backups))
|
||||
.route("/database/restore", post(restore_backup))
|
||||
}
|
||||
|
||||
@@ -930,6 +930,169 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/database/backup": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"Database Management"
|
||||
],
|
||||
"operationId": "create_backup",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/CreateBackupRequest"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Backup created successfully",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/CreateBackupResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized - Invalid or missing API key"
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal server error"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"api_key": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/database/backups": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Database Management"
|
||||
],
|
||||
"operationId": "list_backups",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "List of backups retrieved successfully",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/BackupsListResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized - Invalid or missing API key"
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal server error"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"api_key": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/database/backups/delete": {
|
||||
"delete": {
|
||||
"tags": [
|
||||
"Database Management"
|
||||
],
|
||||
"operationId": "delete_backups",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/DeleteBackupsRequest"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Backups deleted successfully",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/DeleteBackupsResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad request - Invalid parameters"
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized - Invalid or missing API key"
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal server error"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"api_key": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/database/restore": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"Database Management"
|
||||
],
|
||||
"operationId": "restore_backup",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/RestoreBackupRequest"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Database restored successfully",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/RestoreBackupResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad request - Invalid backup filename"
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized - Invalid or missing API key"
|
||||
},
|
||||
"404": {
|
||||
"description": "Backup not found"
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal server error"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"api_key": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/database/status": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@@ -2184,6 +2347,21 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"BackupsListResponse": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"backups"
|
||||
],
|
||||
"properties": {
|
||||
"backups": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/BackupInfo"
|
||||
},
|
||||
"description": "List of available backups"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ChatRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -2315,6 +2493,41 @@
|
||||
"$ref": "#/components/schemas/Message"
|
||||
}
|
||||
},
|
||||
"CreateBackupRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Optional custom name for the backup",
|
||||
"nullable": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"CreateBackupResponse": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"filename",
|
||||
"size",
|
||||
"createdAt"
|
||||
],
|
||||
"properties": {
|
||||
"createdAt": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "When the backup was created"
|
||||
},
|
||||
"filename": {
|
||||
"type": "string",
|
||||
"description": "Filename of the created backup"
|
||||
},
|
||||
"size": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"description": "Size of the backup file in bytes",
|
||||
"minimum": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"CreateRecipeRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -2506,6 +2719,60 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"DeleteBackupsRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"filenames",
|
||||
"deleteAll",
|
||||
"cleanupOrphaned"
|
||||
],
|
||||
"properties": {
|
||||
"cleanupOrphaned": {
|
||||
"type": "boolean",
|
||||
"description": "Clean up orphaned WAL/SHM files"
|
||||
},
|
||||
"deleteAll": {
|
||||
"type": "boolean",
|
||||
"description": "Delete all backups if true"
|
||||
},
|
||||
"filenames": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "List of backup filenames to delete"
|
||||
}
|
||||
}
|
||||
},
|
||||
"DeleteBackupsResponse": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"deleted",
|
||||
"failed",
|
||||
"orphanedCleaned"
|
||||
],
|
||||
"properties": {
|
||||
"deleted": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Successfully deleted files"
|
||||
},
|
||||
"failed": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Files that failed to delete"
|
||||
},
|
||||
"orphanedCleaned": {
|
||||
"type": "integer",
|
||||
"description": "Number of orphaned files cleaned",
|
||||
"minimum": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"DeleteRecipeRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -4096,6 +4363,35 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"RestoreBackupRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"filename"
|
||||
],
|
||||
"properties": {
|
||||
"filename": {
|
||||
"type": "string",
|
||||
"description": "Filename of the backup to restore"
|
||||
}
|
||||
}
|
||||
},
|
||||
"RestoreBackupResponse": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"message",
|
||||
"restoredFrom"
|
||||
],
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string",
|
||||
"description": "Success message"
|
||||
},
|
||||
"restoredFrom": {
|
||||
"type": "string",
|
||||
"description": "Backup that was restored"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ResumeAgentRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import type { Client, Options as Options2, TDataShape } from './client';
|
||||
import { client } from './client.gen';
|
||||
import type { AddExtensionData, AddExtensionErrors, AddExtensionResponses, AgentAddExtensionData, AgentAddExtensionErrors, AgentAddExtensionResponses, AgentRemoveExtensionData, AgentRemoveExtensionErrors, AgentRemoveExtensionResponses, BackupConfigData, BackupConfigErrors, BackupConfigResponses, ConfirmPermissionData, ConfirmPermissionErrors, ConfirmPermissionResponses, CreateCustomProviderData, CreateCustomProviderErrors, CreateCustomProviderResponses, CreateRecipeData, CreateRecipeErrors, CreateRecipeResponses, CreateScheduleData, CreateScheduleErrors, CreateScheduleResponses, DatabaseStatusData, DatabaseStatusErrors, DatabaseStatusResponses, DecodeRecipeData, DecodeRecipeErrors, DecodeRecipeResponses, DeleteRecipeData, DeleteRecipeErrors, DeleteRecipeResponses, DeleteScheduleData, DeleteScheduleErrors, DeleteScheduleResponses, DeleteSessionData, DeleteSessionErrors, DeleteSessionResponses, DiagnosticsData, DiagnosticsErrors, DiagnosticsResponses, EncodeRecipeData, EncodeRecipeErrors, EncodeRecipeResponses, ExportSessionData, ExportSessionErrors, ExportSessionResponses, GetCustomProviderData, GetCustomProviderErrors, GetCustomProviderResponses, GetExtensionsData, GetExtensionsErrors, GetExtensionsResponses, GetProviderModelsData, GetProviderModelsErrors, GetProviderModelsResponses, GetSessionData, GetSessionErrors, GetSessionInsightsData, GetSessionInsightsErrors, GetSessionInsightsResponses, GetSessionResponses, GetToolsData, GetToolsErrors, GetToolsResponses, ImportSessionData, ImportSessionErrors, ImportSessionResponses, InitConfigData, InitConfigErrors, InitConfigResponses, InspectRunningJobData, InspectRunningJobErrors, InspectRunningJobResponses, KillRunningJobData, KillRunningJobResponses, ListRecipesData, ListRecipesErrors, ListRecipesResponses, ListSchedulesData, ListSchedulesErrors, ListSchedulesResponses, ListSessionsData, ListSessionsErrors, ListSessionsResponses, ParseRecipeData, ParseRecipeErrors, ParseRecipeResponses, PauseScheduleData, PauseScheduleErrors, PauseScheduleResponses, ProvidersData, ProvidersResponses, ReadAllConfigData, ReadAllConfigResponses, ReadConfigData, ReadConfigErrors, ReadConfigResponses, RecoverConfigData, RecoverConfigErrors, RecoverConfigResponses, RemoveConfigData, RemoveConfigErrors, RemoveConfigResponses, RemoveCustomProviderData, RemoveCustomProviderErrors, RemoveCustomProviderResponses, RemoveExtensionData, RemoveExtensionErrors, RemoveExtensionResponses, ReplyData, ReplyErrors, ReplyResponses, ResumeAgentData, ResumeAgentErrors, ResumeAgentResponses, RunNowHandlerData, RunNowHandlerErrors, RunNowHandlerResponses, SaveRecipeData, SaveRecipeErrors, SaveRecipeResponses, ScanRecipeData, ScanRecipeResponses, SessionsHandlerData, SessionsHandlerErrors, SessionsHandlerResponses, StartAgentData, StartAgentErrors, StartAgentResponses, StartOpenrouterSetupData, StartOpenrouterSetupResponses, StartTetrateSetupData, StartTetrateSetupResponses, StatusData, StatusResponses, UnpauseScheduleData, UnpauseScheduleErrors, UnpauseScheduleResponses, UpdateAgentProviderData, UpdateAgentProviderErrors, UpdateAgentProviderResponses, UpdateCustomProviderData, UpdateCustomProviderErrors, UpdateCustomProviderResponses, UpdateFromSessionData, UpdateFromSessionErrors, UpdateFromSessionResponses, UpdateRouterToolSelectorData, UpdateRouterToolSelectorErrors, UpdateRouterToolSelectorResponses, UpdateScheduleData, UpdateScheduleErrors, UpdateScheduleResponses, UpdateSessionNameData, UpdateSessionNameErrors, UpdateSessionNameResponses, UpdateSessionUserRecipeValuesData, UpdateSessionUserRecipeValuesErrors, UpdateSessionUserRecipeValuesResponses, UpsertConfigData, UpsertConfigErrors, UpsertConfigResponses, UpsertPermissionsData, UpsertPermissionsErrors, UpsertPermissionsResponses, ValidateConfigData, ValidateConfigErrors, ValidateConfigResponses } from './types.gen';
|
||||
import type { AddExtensionData, AddExtensionErrors, AddExtensionResponses, AgentAddExtensionData, AgentAddExtensionErrors, AgentAddExtensionResponses, AgentRemoveExtensionData, AgentRemoveExtensionErrors, AgentRemoveExtensionResponses, BackupConfigData, BackupConfigErrors, BackupConfigResponses, ConfirmPermissionData, ConfirmPermissionErrors, ConfirmPermissionResponses, CreateBackupData, CreateBackupErrors, CreateBackupResponses, CreateCustomProviderData, CreateCustomProviderErrors, CreateCustomProviderResponses, CreateRecipeData, CreateRecipeErrors, CreateRecipeResponses, CreateScheduleData, CreateScheduleErrors, CreateScheduleResponses, DatabaseStatusData, DatabaseStatusErrors, DatabaseStatusResponses, DecodeRecipeData, DecodeRecipeErrors, DecodeRecipeResponses, DeleteBackupsData, DeleteBackupsErrors, DeleteBackupsResponses, DeleteRecipeData, DeleteRecipeErrors, DeleteRecipeResponses, DeleteScheduleData, DeleteScheduleErrors, DeleteScheduleResponses, DeleteSessionData, DeleteSessionErrors, DeleteSessionResponses, DiagnosticsData, DiagnosticsErrors, DiagnosticsResponses, EncodeRecipeData, EncodeRecipeErrors, EncodeRecipeResponses, ExportSessionData, ExportSessionErrors, ExportSessionResponses, GetCustomProviderData, GetCustomProviderErrors, GetCustomProviderResponses, GetExtensionsData, GetExtensionsErrors, GetExtensionsResponses, GetProviderModelsData, GetProviderModelsErrors, GetProviderModelsResponses, GetSessionData, GetSessionErrors, GetSessionInsightsData, GetSessionInsightsErrors, GetSessionInsightsResponses, GetSessionResponses, GetToolsData, GetToolsErrors, GetToolsResponses, ImportSessionData, ImportSessionErrors, ImportSessionResponses, InitConfigData, InitConfigErrors, InitConfigResponses, InspectRunningJobData, InspectRunningJobErrors, InspectRunningJobResponses, KillRunningJobData, KillRunningJobResponses, ListBackupsData, ListBackupsErrors, ListBackupsResponses, ListRecipesData, ListRecipesErrors, ListRecipesResponses, ListSchedulesData, ListSchedulesErrors, ListSchedulesResponses, ListSessionsData, ListSessionsErrors, ListSessionsResponses, ParseRecipeData, ParseRecipeErrors, ParseRecipeResponses, PauseScheduleData, PauseScheduleErrors, PauseScheduleResponses, ProvidersData, ProvidersResponses, ReadAllConfigData, ReadAllConfigResponses, ReadConfigData, ReadConfigErrors, ReadConfigResponses, RecoverConfigData, RecoverConfigErrors, RecoverConfigResponses, RemoveConfigData, RemoveConfigErrors, RemoveConfigResponses, RemoveCustomProviderData, RemoveCustomProviderErrors, RemoveCustomProviderResponses, RemoveExtensionData, RemoveExtensionErrors, RemoveExtensionResponses, ReplyData, ReplyErrors, ReplyResponses, RestoreBackupData, RestoreBackupErrors, RestoreBackupResponses, ResumeAgentData, ResumeAgentErrors, ResumeAgentResponses, RunNowHandlerData, RunNowHandlerErrors, RunNowHandlerResponses, SaveRecipeData, SaveRecipeErrors, SaveRecipeResponses, ScanRecipeData, ScanRecipeResponses, SessionsHandlerData, SessionsHandlerErrors, SessionsHandlerResponses, StartAgentData, StartAgentErrors, StartAgentResponses, StartOpenrouterSetupData, StartOpenrouterSetupResponses, StartTetrateSetupData, StartTetrateSetupResponses, StatusData, StatusResponses, UnpauseScheduleData, UnpauseScheduleErrors, UnpauseScheduleResponses, UpdateAgentProviderData, UpdateAgentProviderErrors, UpdateAgentProviderResponses, UpdateCustomProviderData, UpdateCustomProviderErrors, UpdateCustomProviderResponses, UpdateFromSessionData, UpdateFromSessionErrors, UpdateFromSessionResponses, UpdateRouterToolSelectorData, UpdateRouterToolSelectorErrors, UpdateRouterToolSelectorResponses, UpdateScheduleData, UpdateScheduleErrors, UpdateScheduleResponses, UpdateSessionNameData, UpdateSessionNameErrors, UpdateSessionNameResponses, UpdateSessionUserRecipeValuesData, UpdateSessionUserRecipeValuesErrors, UpdateSessionUserRecipeValuesResponses, UpsertConfigData, UpsertConfigErrors, UpsertConfigResponses, UpsertPermissionsData, UpsertPermissionsErrors, UpsertPermissionsResponses, ValidateConfigData, ValidateConfigErrors, ValidateConfigResponses } from './types.gen';
|
||||
|
||||
export type Options<TData extends TDataShape = TDataShape, ThrowOnError extends boolean = boolean> = Options2<TData, ThrowOnError> & {
|
||||
/**
|
||||
@@ -267,6 +267,46 @@ export const confirmPermission = <ThrowOnError extends boolean = false>(options:
|
||||
});
|
||||
};
|
||||
|
||||
export const createBackup = <ThrowOnError extends boolean = false>(options: Options<CreateBackupData, ThrowOnError>) => {
|
||||
return (options.client ?? client).post<CreateBackupResponses, CreateBackupErrors, ThrowOnError>({
|
||||
url: '/database/backup',
|
||||
...options,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...options.headers
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const listBackups = <ThrowOnError extends boolean = false>(options?: Options<ListBackupsData, ThrowOnError>) => {
|
||||
return (options?.client ?? client).get<ListBackupsResponses, ListBackupsErrors, ThrowOnError>({
|
||||
url: '/database/backups',
|
||||
...options
|
||||
});
|
||||
};
|
||||
|
||||
export const deleteBackups = <ThrowOnError extends boolean = false>(options: Options<DeleteBackupsData, ThrowOnError>) => {
|
||||
return (options.client ?? client).delete<DeleteBackupsResponses, DeleteBackupsErrors, ThrowOnError>({
|
||||
url: '/database/backups/delete',
|
||||
...options,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...options.headers
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const restoreBackup = <ThrowOnError extends boolean = false>(options: Options<RestoreBackupData, ThrowOnError>) => {
|
||||
return (options.client ?? client).post<RestoreBackupResponses, RestoreBackupErrors, ThrowOnError>({
|
||||
url: '/database/restore',
|
||||
...options,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...options.headers
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const databaseStatus = <ThrowOnError extends boolean = false>(options?: Options<DatabaseStatusData, ThrowOnError>) => {
|
||||
return (options?.client ?? client).get<DatabaseStatusResponses, DatabaseStatusErrors, ThrowOnError>({
|
||||
url: '/database/status',
|
||||
|
||||
@@ -32,6 +32,13 @@ export type BackupInfo = {
|
||||
size: number;
|
||||
};
|
||||
|
||||
export type BackupsListResponse = {
|
||||
/**
|
||||
* List of available backups
|
||||
*/
|
||||
backups: Array<BackupInfo>;
|
||||
};
|
||||
|
||||
export type ChatRequest = {
|
||||
messages: Array<Message>;
|
||||
recipe_name?: string | null;
|
||||
@@ -81,6 +88,28 @@ export type Content = RawTextContent | RawImageContent | RawEmbeddedResource | R
|
||||
|
||||
export type Conversation = Array<Message>;
|
||||
|
||||
export type CreateBackupRequest = {
|
||||
/**
|
||||
* Optional custom name for the backup
|
||||
*/
|
||||
name?: string | null;
|
||||
};
|
||||
|
||||
export type CreateBackupResponse = {
|
||||
/**
|
||||
* When the backup was created
|
||||
*/
|
||||
createdAt: string;
|
||||
/**
|
||||
* Filename of the created backup
|
||||
*/
|
||||
filename: string;
|
||||
/**
|
||||
* Size of the backup file in bytes
|
||||
*/
|
||||
size: number;
|
||||
};
|
||||
|
||||
export type CreateRecipeRequest = {
|
||||
author?: AuthorRequest | null;
|
||||
session_id: string;
|
||||
@@ -133,6 +162,36 @@ export type DecodeRecipeResponse = {
|
||||
recipe: Recipe;
|
||||
};
|
||||
|
||||
export type DeleteBackupsRequest = {
|
||||
/**
|
||||
* Clean up orphaned WAL/SHM files
|
||||
*/
|
||||
cleanupOrphaned: boolean;
|
||||
/**
|
||||
* Delete all backups if true
|
||||
*/
|
||||
deleteAll: boolean;
|
||||
/**
|
||||
* List of backup filenames to delete
|
||||
*/
|
||||
filenames: Array<string>;
|
||||
};
|
||||
|
||||
export type DeleteBackupsResponse = {
|
||||
/**
|
||||
* Successfully deleted files
|
||||
*/
|
||||
deleted: Array<string>;
|
||||
/**
|
||||
* Files that failed to delete
|
||||
*/
|
||||
failed: Array<string>;
|
||||
/**
|
||||
* Number of orphaned files cleaned
|
||||
*/
|
||||
orphanedCleaned: number;
|
||||
};
|
||||
|
||||
export type DeleteRecipeRequest = {
|
||||
id: string;
|
||||
};
|
||||
@@ -628,6 +687,24 @@ export type Response = {
|
||||
json_schema?: unknown;
|
||||
};
|
||||
|
||||
export type RestoreBackupRequest = {
|
||||
/**
|
||||
* Filename of the backup to restore
|
||||
*/
|
||||
filename: string;
|
||||
};
|
||||
|
||||
export type RestoreBackupResponse = {
|
||||
/**
|
||||
* Success message
|
||||
*/
|
||||
message: string;
|
||||
/**
|
||||
* Backup that was restored
|
||||
*/
|
||||
restoredFrom: string;
|
||||
};
|
||||
|
||||
export type ResumeAgentRequest = {
|
||||
load_model_and_extensions: boolean;
|
||||
session_id: string;
|
||||
@@ -1666,6 +1743,126 @@ export type ConfirmPermissionResponses = {
|
||||
200: unknown;
|
||||
};
|
||||
|
||||
export type CreateBackupData = {
|
||||
body: CreateBackupRequest;
|
||||
path?: never;
|
||||
query?: never;
|
||||
url: '/database/backup';
|
||||
};
|
||||
|
||||
export type CreateBackupErrors = {
|
||||
/**
|
||||
* Unauthorized - Invalid or missing API key
|
||||
*/
|
||||
401: unknown;
|
||||
/**
|
||||
* Internal server error
|
||||
*/
|
||||
500: unknown;
|
||||
};
|
||||
|
||||
export type CreateBackupResponses = {
|
||||
/**
|
||||
* Backup created successfully
|
||||
*/
|
||||
201: CreateBackupResponse;
|
||||
};
|
||||
|
||||
export type CreateBackupResponse2 = CreateBackupResponses[keyof CreateBackupResponses];
|
||||
|
||||
export type ListBackupsData = {
|
||||
body?: never;
|
||||
path?: never;
|
||||
query?: never;
|
||||
url: '/database/backups';
|
||||
};
|
||||
|
||||
export type ListBackupsErrors = {
|
||||
/**
|
||||
* Unauthorized - Invalid or missing API key
|
||||
*/
|
||||
401: unknown;
|
||||
/**
|
||||
* Internal server error
|
||||
*/
|
||||
500: unknown;
|
||||
};
|
||||
|
||||
export type ListBackupsResponses = {
|
||||
/**
|
||||
* List of backups retrieved successfully
|
||||
*/
|
||||
200: BackupsListResponse;
|
||||
};
|
||||
|
||||
export type ListBackupsResponse = ListBackupsResponses[keyof ListBackupsResponses];
|
||||
|
||||
export type DeleteBackupsData = {
|
||||
body: DeleteBackupsRequest;
|
||||
path?: never;
|
||||
query?: never;
|
||||
url: '/database/backups/delete';
|
||||
};
|
||||
|
||||
export type DeleteBackupsErrors = {
|
||||
/**
|
||||
* Bad request - Invalid parameters
|
||||
*/
|
||||
400: unknown;
|
||||
/**
|
||||
* Unauthorized - Invalid or missing API key
|
||||
*/
|
||||
401: unknown;
|
||||
/**
|
||||
* Internal server error
|
||||
*/
|
||||
500: unknown;
|
||||
};
|
||||
|
||||
export type DeleteBackupsResponses = {
|
||||
/**
|
||||
* Backups deleted successfully
|
||||
*/
|
||||
200: DeleteBackupsResponse;
|
||||
};
|
||||
|
||||
export type DeleteBackupsResponse2 = DeleteBackupsResponses[keyof DeleteBackupsResponses];
|
||||
|
||||
export type RestoreBackupData = {
|
||||
body: RestoreBackupRequest;
|
||||
path?: never;
|
||||
query?: never;
|
||||
url: '/database/restore';
|
||||
};
|
||||
|
||||
export type RestoreBackupErrors = {
|
||||
/**
|
||||
* Bad request - Invalid backup filename
|
||||
*/
|
||||
400: unknown;
|
||||
/**
|
||||
* Unauthorized - Invalid or missing API key
|
||||
*/
|
||||
401: unknown;
|
||||
/**
|
||||
* Backup not found
|
||||
*/
|
||||
404: unknown;
|
||||
/**
|
||||
* Internal server error
|
||||
*/
|
||||
500: unknown;
|
||||
};
|
||||
|
||||
export type RestoreBackupResponses = {
|
||||
/**
|
||||
* Database restored successfully
|
||||
*/
|
||||
200: RestoreBackupResponse;
|
||||
};
|
||||
|
||||
export type RestoreBackupResponse2 = RestoreBackupResponses[keyof RestoreBackupResponses];
|
||||
|
||||
export type DatabaseStatusData = {
|
||||
body?: never;
|
||||
path?: never;
|
||||
|
||||
Reference in New Issue
Block a user