Expose everything in the API too

This commit is contained in:
Will Pfleger
2025-10-31 14:12:21 -04:00
parent b0a0630b52
commit 42bf8043e2
7 changed files with 942 additions and 4 deletions
Generated
+1
View File
@@ -2882,6 +2882,7 @@ dependencies = [
"goose",
"goose-mcp",
"http 1.2.0",
"once_cell",
"reqwest 0.12.12",
"rmcp",
"schemars",
+1
View File
@@ -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"
+11
View File
@@ -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,
+395 -3
View File
@@ -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))
}
+296
View File
@@ -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": [
+41 -1
View File
@@ -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',
+197
View File
@@ -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;