diff --git a/crates/goose-server/src/openapi.rs b/crates/goose-server/src/openapi.rs index 431e9bf91424..2f021f94fc7f 100644 --- a/crates/goose-server/src/openapi.rs +++ b/crates/goose-server/src/openapi.rs @@ -469,6 +469,7 @@ derive_utoipa!(Icon as IconSchema); super::routes::recipe::recipe_to_yaml, super::routes::setup::start_openrouter_setup, super::routes::setup::start_tetrate_setup, + super::routes::setup::start_nanogpt_setup, super::routes::tunnel::start_tunnel, super::routes::tunnel::stop_tunnel, super::routes::tunnel::get_tunnel_status, @@ -510,6 +511,7 @@ derive_utoipa!(Icon as IconSchema); goose::providers::catalog::ProviderTemplate, goose::providers::catalog::ModelTemplate, goose::providers::catalog::ModelCapabilities, + super::routes::config_management::CreateCustomProviderResponse, super::routes::config_management::CheckProviderRequest, super::routes::config_management::SetProviderRequest, super::routes::config_management::ModelInfoQuery, diff --git a/crates/goose-server/src/routes/config_management.rs b/crates/goose-server/src/routes/config_management.rs index 60cdff93d9a0..9784c1628821 100644 --- a/crates/goose-server/src/routes/config_management.rs +++ b/crates/goose-server/src/routes/config_management.rs @@ -633,19 +633,24 @@ pub async fn validate_config() -> Result, ErrorResponse> { Ok(Json("Config file is valid".to_string())) } +#[derive(Serialize, ToSchema)] +pub struct CreateCustomProviderResponse { + pub provider_name: String, +} + #[utoipa::path( post, path = "/config/custom-providers", request_body = UpdateCustomProviderRequest, responses( - (status = 200, description = "Custom provider created successfully", body = String), + (status = 200, description = "Custom provider created successfully", body = CreateCustomProviderResponse), (status = 400, description = "Invalid request"), (status = 500, description = "Internal server error") ) )] pub async fn create_custom_provider( Json(request): Json, -) -> Result, ErrorResponse> { +) -> Result, ErrorResponse> { let config = goose::config::declarative_providers::create_custom_provider( goose::config::declarative_providers::CreateCustomProviderParams { engine: request.engine, @@ -663,7 +668,9 @@ pub async fn create_custom_provider( goose::providers::refresh_custom_providers().await?; - Ok(Json(format!("Custom provider added - ID: {}", config.id()))) + Ok(Json(CreateCustomProviderResponse { + provider_name: config.id().to_string(), + })) } #[utoipa::path( diff --git a/crates/goose-server/src/routes/setup.rs b/crates/goose-server/src/routes/setup.rs index fbf9ef01e79e..55a357db3dbd 100644 --- a/crates/goose-server/src/routes/setup.rs +++ b/crates/goose-server/src/routes/setup.rs @@ -1,6 +1,7 @@ use crate::routes::errors::ErrorResponse; use crate::state::AppState; use axum::{routing::post, Json, Router}; +use goose::config::signup_nanogpt::{complete_nanogpt_auth, configure_nanogpt}; use goose::config::signup_openrouter::OpenRouterAuth; use goose::config::signup_tetrate::{configure_tetrate, TetrateAuth}; use goose::config::{configure_openrouter, Config}; @@ -18,6 +19,7 @@ pub fn routes(state: Arc) -> Router { Router::new() .route("/handle_openrouter", post(start_openrouter_setup)) .route("/handle_tetrate", post(start_tetrate_setup)) + .route("/handle_nanogpt", post(start_nanogpt_setup)) .with_state(state) } @@ -50,7 +52,7 @@ async fn start_openrouter_setup() -> Result, ErrorResponse> } Err(e) => Ok(Json(SetupResponse { success: false, - message: format!("Setup failed: {}", e), + message: e.to_string(), })), } } @@ -84,7 +86,38 @@ async fn start_tetrate_setup() -> Result, ErrorResponse> { } Err(e) => Ok(Json(SetupResponse { success: false, - message: format!("Setup failed: {}", e), + message: e.to_string(), + })), + } +} + +#[utoipa::path( + post, + path = "/handle_nanogpt", + responses( + (status = 200, body=SetupResponse) + ), +)] +async fn start_nanogpt_setup() -> Result, ErrorResponse> { + match complete_nanogpt_auth().await { + Ok(api_key) => { + let config = Config::global(); + + if let Err(e) = configure_nanogpt(config, api_key) { + return Ok(Json(SetupResponse { + success: false, + message: format!("Failed to configure NanoGPT: {}", e), + })); + } + + Ok(Json(SetupResponse { + success: true, + message: "NanoGPT setup completed successfully".to_string(), + })) + } + Err(e) => Ok(Json(SetupResponse { + success: false, + message: e.to_string(), })), } } diff --git a/crates/goose/src/config/mod.rs b/crates/goose/src/config/mod.rs index e73349745d21..8fca0d134e73 100644 --- a/crates/goose/src/config/mod.rs +++ b/crates/goose/src/config/mod.rs @@ -7,6 +7,7 @@ mod migrations; pub mod paths; pub mod permission; pub mod search_path; +pub mod signup_nanogpt; pub mod signup_openrouter; pub mod signup_tetrate; @@ -21,6 +22,7 @@ pub use extensions::{ }; pub use goose_mode::GooseMode; pub use permission::PermissionManager; +pub use signup_nanogpt::configure_nanogpt; pub use signup_openrouter::configure_openrouter; pub use signup_tetrate::configure_tetrate; diff --git a/crates/goose/src/config/signup_nanogpt/mod.rs b/crates/goose/src/config/signup_nanogpt/mod.rs new file mode 100644 index 000000000000..0c7a3f513ced --- /dev/null +++ b/crates/goose/src/config/signup_nanogpt/mod.rs @@ -0,0 +1,128 @@ +use anyhow::{anyhow, Result}; +use reqwest::Client; +use serde::{Deserialize, Serialize}; +use std::time::Duration; +use tokio::time::{sleep, timeout}; + +use crate::config::Config; + +/// Default model for NanoGPT configuration +pub const NANOGPT_DEFAULT_MODEL: &str = "openai/gpt-4.1-nano"; + +const NANOGPT_START_URL: &str = "https://nano-gpt.com/api/cli-login/start"; +const NANOGPT_POLL_URL: &str = "https://nano-gpt.com/api/cli-login/poll"; +const AUTH_TIMEOUT: Duration = Duration::from_secs(180); // 3 minutes +const POLL_INTERVAL: Duration = Duration::from_secs(2); + +#[derive(Debug, Serialize)] +struct StartRequest { + client_name: String, +} + +#[derive(Debug, Deserialize)] +struct StartResponse { + device_code: String, + verification_uri_complete: String, +} + +#[derive(Debug, Serialize)] +struct PollRequest { + device_code: String, +} + +#[derive(Debug, Deserialize)] +struct PollResponse { + key: String, +} + +async fn poll_for_token(device_code: &str) -> Result { + let client = Client::new(); + + loop { + sleep(POLL_INTERVAL).await; + + let body = PollRequest { + device_code: device_code.to_string(), + }; + + let response = client.post(NANOGPT_POLL_URL).json(&body).send().await?; + // https://docs.nano-gpt.com/integrations/cli-login#response-codes + match response.status().as_u16() { + 200 => { + let poll_resp: PollResponse = response.json().await?; + return Ok(poll_resp.key); + } + 202 => { + continue; + } + 410 => { + return Err(anyhow!("Device code has expired - please try again")); + } + 409 => { + return Err(anyhow!("Device code has already been consumed")); + } + 404 => { + return Err(anyhow!("Invalid device code")); + } + 429 => { + return Err(anyhow!( + "Too many requests to NanoGPT. Please wait a moment and try again." + )); + } + other => { + let error_text = response.text().await.unwrap_or_default(); + return Err(anyhow!( + "Unexpected poll response: {} - {}", + other, + error_text + )); + } + } + } +} + +pub async fn complete_nanogpt_auth() -> Result { + let client = Client::new(); + let body = StartRequest { + client_name: "goose".to_string(), + }; + + let response = client.post(NANOGPT_START_URL).json(&body).send().await?; + + if !response.status().is_success() { + let status = response.status(); + let error_text = response.text().await.unwrap_or_default(); + return Err(anyhow!( + "Failed to start NanoGPT device flow: {} - {}", + status, + error_text + )); + } + + let start_resp: StartResponse = response.json().await?; + + println!("Opening browser for NanoGPT authentication..."); + + if let Err(e) = webbrowser::open(&start_resp.verification_uri_complete) { + eprintln!("Failed to open browser automatically: {}", e); + println!( + "Please open this URL manually: {}", + start_resp.verification_uri_complete + ); + } + + println!("Waiting for NanoGPT authorization..."); + + match timeout(AUTH_TIMEOUT, poll_for_token(&start_resp.device_code)).await { + Ok(Ok(api_key)) => Ok(api_key), + Ok(Err(e)) => Err(e), + Err(_) => Err(anyhow!("Authentication timed out - please try again")), + } +} + +pub fn configure_nanogpt(config: &Config, api_key: String) -> Result<()> { + config.set_secret("NANOGPT_API_KEY", &api_key)?; + config.set_goose_provider("nano-gpt")?; + config.set_goose_model(NANOGPT_DEFAULT_MODEL)?; + Ok(()) +} diff --git a/crates/goose/src/posthog.rs b/crates/goose/src/posthog.rs index 1c4693c0a8f3..b34945c16862 100644 --- a/crates/goose/src/posthog.rs +++ b/crates/goose/src/posthog.rs @@ -581,15 +581,14 @@ pub async fn emit_event( event_name: &str, mut properties: HashMap, ) -> Result<(), String> { - if !is_telemetry_enabled() { + // Only onboarding events are enabled for now. These bypass the telemetry + // check so we can track the funnel before the user makes their choice. + let is_onboarding_event = + event_name.starts_with("onboarding_") || event_name == "telemetry_preference_set"; + if !is_onboarding_event { return Ok(()); } - // Temporarily disabled - only session_started events are sent - let _ = (event_name, &mut properties); - return Ok(()); - - #[allow(unreachable_code)] let installation = load_or_create_installation(); insert(&mut properties, "os", std::env::consts::OS); diff --git a/crates/goose/src/providers/anthropic.rs b/crates/goose/src/providers/anthropic.rs index 2748011178f0..21338a732b18 100644 --- a/crates/goose/src/providers/anthropic.rs +++ b/crates/goose/src/providers/anthropic.rs @@ -168,6 +168,11 @@ impl ProviderDef for AnthropicProvider { ), ], ) + .with_setup_steps(vec![ + "Go to https://platform.claude.com/settings/keys", + "Click 'Create Key'", + "Copy the key and paste it above", + ]) } fn from_env( diff --git a/crates/goose/src/providers/base.rs b/crates/goose/src/providers/base.rs index 5b669394932f..47f644a26794 100644 --- a/crates/goose/src/providers/base.rs +++ b/crates/goose/src/providers/base.rs @@ -176,6 +176,9 @@ pub struct ProviderMetadata { pub model_doc_link: String, /// Required configuration keys pub config_keys: Vec, + /// step-by-step instructions for set up providers eg: api key + #[serde(default)] + pub setup_steps: Vec, } impl ProviderMetadata { @@ -208,6 +211,7 @@ impl ProviderMetadata { .collect(), model_doc_link: model_doc_link.to_string(), config_keys, + setup_steps: vec![], } } @@ -228,6 +232,7 @@ impl ProviderMetadata { known_models: models, model_doc_link: model_doc_link.to_string(), config_keys, + setup_steps: vec![], } } @@ -240,8 +245,14 @@ impl ProviderMetadata { known_models: vec![], model_doc_link: "".to_string(), config_keys: vec![], + setup_steps: vec![], } } + + pub fn with_setup_steps(mut self, steps: Vec<&str>) -> Self { + self.setup_steps = steps.into_iter().map(|s| s.to_string()).collect(); + self + } } /// Configuration key metadata for provider setup diff --git a/crates/goose/src/providers/google.rs b/crates/goose/src/providers/google.rs index fdd884ab4925..d521d9a376eb 100644 --- a/crates/goose/src/providers/google.rs +++ b/crates/goose/src/providers/google.rs @@ -121,6 +121,12 @@ impl ProviderDef for GoogleProvider { ConfigKey::new("GOOGLE_HOST", false, false, Some(GOOGLE_API_HOST), false), ], ) + .with_setup_steps(vec![ + "Go to https://aistudio.google.com and sign in with your Google account", + "Click 'Get API key' on the left sidebar", + "Create a new API key or select an existing one", + "Copy the key and paste it above", + ]) } fn from_env( diff --git a/crates/goose/src/providers/init.rs b/crates/goose/src/providers/init.rs index e9fd9b8879ae..ee07417ab619 100644 --- a/crates/goose/src/providers/init.rs +++ b/crates/goose/src/providers/init.rs @@ -20,6 +20,7 @@ use super::{ lead_worker::LeadWorkerProvider, litellm::LiteLLMProvider, local_inference::LocalInferenceProvider, + nanogpt::NanoGptProvider, ollama::OllamaProvider, openai::OpenAiProvider, openrouter::OpenRouterProvider, @@ -65,6 +66,7 @@ async fn init_registry() -> RwLock { registry.register::(false); registry.register::(true); registry.register::(false); + registry.register::(true); registry.register::(true); registry.register::(true); registry.register::(true); diff --git a/crates/goose/src/providers/mod.rs b/crates/goose/src/providers/mod.rs index c07bcb0d246a..50ae302be785 100644 --- a/crates/goose/src/providers/mod.rs +++ b/crates/goose/src/providers/mod.rs @@ -28,6 +28,7 @@ mod init; pub mod lead_worker; pub mod litellm; pub mod local_inference; +pub mod nanogpt; pub mod oauth; pub mod ollama; pub mod openai; diff --git a/crates/goose/src/providers/nanogpt.rs b/crates/goose/src/providers/nanogpt.rs new file mode 100644 index 000000000000..c5e12babdcf6 --- /dev/null +++ b/crates/goose/src/providers/nanogpt.rs @@ -0,0 +1,213 @@ +use super::api_client::{ApiClient, AuthMethod}; +use super::base::{ConfigKey, MessageStream, Provider, ProviderDef, ProviderMetadata}; +use super::errors::ProviderError; +use super::openai_compatible::{handle_status_openai_compat, stream_openai_compat}; +use super::retry::ProviderRetry; +use super::utils::{ImageFormat, RequestLog}; +use crate::conversation::message::Message; +use crate::model::ModelConfig; +use crate::providers::formats::openai::create_request; +use anyhow::Result; +use async_trait::async_trait; +use futures::future::BoxFuture; +use rmcp::model::Tool; + +const NANOGPT_PROVIDER_NAME: &str = "nano-gpt"; +pub const NANOGPT_API_HOST: &str = "https://nano-gpt.com/api/v1"; +pub const NANOGPT_SUBSCRIPTION_HOST: &str = "https://nano-gpt.com/api/subscription/v1"; +pub const NANOGPT_DEFAULT_MODEL: &str = "anthropic/claude-sonnet-4.6"; +pub const NANOGPT_DOC_URL: &str = "https://docs.nano-gpt.com/"; +const NANOGPT_API_KEY: &str = "NANOGPT_API_KEY"; + +#[derive(serde::Serialize)] +pub struct NanoGptProvider { + #[serde(skip)] + api_client: ApiClient, + model: ModelConfig, + #[serde(skip)] + name: String, +} + +impl NanoGptProvider { + async fn check_subscription(api_key: &str) -> bool { + let client = match ApiClient::new( + NANOGPT_SUBSCRIPTION_HOST.to_string(), + AuthMethod::BearerToken(api_key.to_string()), + ) { + Ok(c) => c, + Err(_) => return false, + }; + + match client.response_get(None, "usage").await { + Ok(resp) => resp + .json::() + .await + .ok() + .and_then(|json| json.get("active")?.as_bool()) + .unwrap_or(false), + Err(_) => false, + } + } + + pub async fn from_env(model: ModelConfig) -> Result { + let config = crate::config::Config::global(); + let api_key: String = config.get_secret(NANOGPT_API_KEY)?; + + let is_subscription = Self::check_subscription(&api_key).await; + let host = if is_subscription { + tracing::debug!("NanoGPT subscription active, using subscription endpoint"); + NANOGPT_SUBSCRIPTION_HOST.to_string() + } else { + tracing::debug!("NanoGPT using pay-as-you-go endpoint"); + NANOGPT_API_HOST.to_string() + }; + + let api_client = ApiClient::new(host, AuthMethod::BearerToken(api_key))?; + + Ok(Self { + api_client, + model, + name: NANOGPT_PROVIDER_NAME.to_string(), + }) + } +} + +impl ProviderDef for NanoGptProvider { + type Provider = Self; + + fn metadata() -> ProviderMetadata { + ProviderMetadata::new( + NANOGPT_PROVIDER_NAME, + "NanoGPT", + "Access multiple AI models through NanoGPT's unified API", + NANOGPT_DEFAULT_MODEL, + vec![NANOGPT_DEFAULT_MODEL], + NANOGPT_DOC_URL, + vec![ConfigKey::new(NANOGPT_API_KEY, true, true, None, true)], + ) + } + + fn from_env( + model: ModelConfig, + _extensions: Vec, + ) -> BoxFuture<'static, Result> { + Box::pin(Self::from_env(model)) + } +} + +#[async_trait] +impl Provider for NanoGptProvider { + fn get_name(&self) -> &str { + &self.name + } + + fn get_model_config(&self) -> ModelConfig { + self.model.clone() + } + + async fn fetch_supported_models(&self) -> Result, ProviderError> { + let response = self + .api_client + .request(None, "models?detailed=true") + .response_get() + .await + .map_err(|e| { + ProviderError::RequestFailed(format!( + "Failed to fetch models from NanoGPT API: {}", + e + )) + })?; + + let json: serde_json::Value = response.json().await.map_err(|e| { + ProviderError::RequestFailed(format!( + "Failed to parse NanoGPT models API response as JSON: {}", + e + )) + })?; + + if let Some(err_obj) = json.get("error") { + let msg = err_obj + .get("message") + .and_then(|v| v.as_str()) + .unwrap_or("unknown error"); + return Err(ProviderError::RequestFailed(format!( + "NanoGPT API returned an error: {}", + msg + ))); + } + + let data = json.get("data").and_then(|v| v.as_array()).ok_or_else(|| { + ProviderError::RequestFailed("Missing 'data' field in JSON response".into()) + })?; + + let mut models: Vec = data + .iter() + .filter_map(|model| { + let id = model.get("id").and_then(|v| v.as_str())?; + let supports_tool_calling = model + .get("capabilities") + .and_then(|c| c.get("tool_calling")) + .and_then(|v| v.as_bool()) + .unwrap_or(false); + if supports_tool_calling { + Some(id.to_string()) + } else { + None + } + }) + .collect(); + + models.sort(); + Ok(models) + } + + async fn stream( + &self, + model_config: &ModelConfig, + session_id: &str, + system: &str, + messages: &[Message], + tools: &[Tool], + ) -> Result { + let payload = create_request( + model_config, + system, + messages, + tools, + &ImageFormat::OpenAi, + true, + )?; + + let mut log = RequestLog::start(model_config, &payload)?; + + let response = self + .with_retry(|| async { + let resp = self + .api_client + .response_post(Some(session_id), "chat/completions", &payload) + .await?; + handle_status_openai_compat(resp).await + }) + .await + .inspect_err(|e| { + let _ = log.error(e); + })?; + + stream_openai_compat(response, log) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_metadata() { + let metadata = NanoGptProvider::metadata(); + assert_eq!(metadata.name, "nano-gpt"); + assert_eq!(metadata.default_model, "anthropic/claude-sonnet-4.6"); + assert_eq!(metadata.config_keys[0].name, NANOGPT_API_KEY); + assert!(metadata.config_keys[0].required); + assert!(metadata.config_keys[0].secret); + } +} diff --git a/crates/goose/src/providers/openai.rs b/crates/goose/src/providers/openai.rs index 24950796014b..ec0be3bbd1f7 100644 --- a/crates/goose/src/providers/openai.rs +++ b/crates/goose/src/providers/openai.rs @@ -338,6 +338,12 @@ impl ProviderDef for OpenAiProvider { ConfigKey::new("OPENAI_TIMEOUT", false, false, Some("600"), false), ], ) + .with_setup_steps(vec![ + "Go to https://platform.openai.com and sign up or log in", + "Navigate to API Keys in the left sidebar", + "Click 'Create new secret key'", + "Copy the key and paste it above", + ]) } fn from_env( diff --git a/crates/goose/src/providers/openrouter.rs b/crates/goose/src/providers/openrouter.rs index d8f26106cd79..6a3d02a99c9f 100644 --- a/crates/goose/src/providers/openrouter.rs +++ b/crates/goose/src/providers/openrouter.rs @@ -168,6 +168,11 @@ impl ProviderDef for OpenRouterProvider { ), ], ) + .with_setup_steps(vec![ + "Go to https://openrouter.ai/settings/keys", + "Click 'Create' or use an existing API key", + "Copy the key and paste it above", + ]) } fn from_env( diff --git a/crates/goose/src/providers/provider_registry.rs b/crates/goose/src/providers/provider_registry.rs index 6abe3c615cdb..42354e8b9e22 100644 --- a/crates/goose/src/providers/provider_registry.rs +++ b/crates/goose/src/providers/provider_registry.rs @@ -139,6 +139,7 @@ impl ProviderRegistry { known_models, model_doc_link: base_metadata.model_doc_link, config_keys, + setup_steps: vec![], }; self.entries.insert( diff --git a/crates/goose/tests/agent.rs b/crates/goose/tests/agent.rs index 830e3f1bfc67..515ab9b6e904 100644 --- a/crates/goose/tests/agent.rs +++ b/crates/goose/tests/agent.rs @@ -373,6 +373,7 @@ mod tests { known_models: vec![], model_doc_link: "".to_string(), config_keys: vec![], + setup_steps: vec![], } } diff --git a/crates/goose/tests/compaction.rs b/crates/goose/tests/compaction.rs index 2cf1aa6e6e39..4c81e745e1ae 100644 --- a/crates/goose/tests/compaction.rs +++ b/crates/goose/tests/compaction.rs @@ -191,6 +191,7 @@ impl ProviderDef for MockCompactionProvider { known_models: vec![], model_doc_link: "".to_string(), config_keys: vec![], + setup_steps: vec![], } } diff --git a/ui/desktop/openapi.json b/ui/desktop/openapi.json index 3498e27e1038..25e69576eeba 100644 --- a/ui/desktop/openapi.json +++ b/ui/desktop/openapi.json @@ -839,9 +839,9 @@ "200": { "description": "Custom provider created successfully", "content": { - "text/plain": { + "application/json": { "schema": { - "type": "string" + "$ref": "#/components/schemas/CreateCustomProviderResponse" } } } @@ -1888,6 +1888,26 @@ } } }, + "/handle_nanogpt": { + "post": { + "tags": [ + "super::routes::setup" + ], + "operationId": "start_nanogpt_setup", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SetupResponse" + } + } + } + } + } + } + }, "/handle_openrouter": { "post": { "tags": [ @@ -4279,6 +4299,17 @@ "$ref": "#/components/schemas/Message" } }, + "CreateCustomProviderResponse": { + "type": "object", + "required": [ + "provider_name" + ], + "properties": { + "provider_name": { + "type": "string" + } + } + }, "CreateRecipeRequest": { "type": "object", "required": [ @@ -6612,6 +6643,13 @@ "name": { "type": "string", "description": "The unique identifier for this provider" + }, + "setup_steps": { + "type": "array", + "items": { + "type": "string" + }, + "description": "step-by-step instructions for set up providers eg: api key" } } }, diff --git a/ui/desktop/src/App.tsx b/ui/desktop/src/App.tsx index c0e55e4f7e94..75d492a1b0af 100644 --- a/ui/desktop/src/App.tsx +++ b/ui/desktop/src/App.tsx @@ -16,6 +16,8 @@ import { ToastContainer } from 'react-toastify'; import AnnouncementModal from './components/AnnouncementModal'; import TelemetryOptOutModal from './components/TelemetryOptOutModal'; import ProviderGuard from './components/ProviderGuard'; +import OnboardingGuard from './components/onboarding/OnboardingGuard'; +import { USE_NEW_ONBOARDING } from './featureFlags'; import { createSession } from './sessions'; import { ChatType } from './types/chat'; @@ -656,11 +658,19 @@ export function AppInner() { - - - - + USE_NEW_ONBOARDING ? ( + + + + + + ) : ( + + + + + + ) } > } /> diff --git a/ui/desktop/src/api/index.ts b/ui/desktop/src/api/index.ts index 503ea9bbb82e..1204578660d4 100644 --- a/ui/desktop/src/api/index.ts +++ b/ui/desktop/src/api/index.ts @@ -1,4 +1,4 @@ // This file is auto-generated by @hey-api/openapi-ts -export { addExtension, agentAddExtension, agentRemoveExtension, backupConfig, callTool, cancelDownload, cancelLocalModelDownload, checkProvider, configureProviderOauth, confirmToolAction, createCustomProvider, createRecipe, createSchedule, decodeRecipe, deleteLocalModel, deleteModel, deleteRecipe, deleteSchedule, deleteSession, detectProvider, diagnostics, downloadHfModel, downloadModel, encodeRecipe, exportApp, exportSession, forkSession, getCanonicalModelInfo, getCustomProvider, getDictationConfig, getDownloadProgress, getExtensions, getLocalModelDownloadProgress, getModelSettings, getPrompt, getPrompts, getProviderCatalog, getProviderCatalogTemplate, getProviderModels, getRepoFiles, getSession, getSessionExtensions, getSessionInsights, getSlashCommands, getTools, getTunnelStatus, importApp, importSession, initConfig, inspectRunningJob, killRunningJob, listApps, listLocalModels, listModels, listRecipes, listSchedules, listSessions, mcpUiProxy, type Options, parseRecipe, pauseSchedule, providers, readAllConfig, readConfig, readResource, recipeToYaml, recoverConfig, removeConfig, removeCustomProvider, removeExtension, reply, resetPrompt, restartAgent, resumeAgent, runNowHandler, savePrompt, saveRecipe, scanRecipe, scheduleRecipe, searchHfModels, searchSessions, sendTelemetryEvent, sessionsHandler, setConfigProvider, setRecipeSlashCommand, startAgent, startOpenrouterSetup, startTetrateSetup, startTunnel, status, stopAgent, stopTunnel, systemInfo, transcribeDictation, unpauseSchedule, updateAgentProvider, updateCustomProvider, updateFromSession, updateModelSettings, updateSchedule, updateSession, updateSessionName, updateSessionUserRecipeValues, updateWorkingDir, upsertConfig, upsertPermissions, validateConfig } from './sdk.gen'; -export type { ActionRequired, ActionRequiredData, AddExtensionData, AddExtensionErrors, AddExtensionRequest, AddExtensionResponse, AddExtensionResponses, AgentAddExtensionData, AgentAddExtensionErrors, AgentAddExtensionResponse, AgentAddExtensionResponses, AgentRemoveExtensionData, AgentRemoveExtensionErrors, AgentRemoveExtensionResponse, AgentRemoveExtensionResponses, Annotations, Author, AuthorRequest, BackupConfigData, BackupConfigErrors, BackupConfigResponse, BackupConfigResponses, CallToolData, CallToolErrors, CallToolRequest, CallToolResponse, CallToolResponse2, CallToolResponses, CancelDownloadData, CancelDownloadErrors, CancelDownloadResponses, CancelLocalModelDownloadData, CancelLocalModelDownloadErrors, CancelLocalModelDownloadResponses, ChatRequest, CheckProviderData, CheckProviderRequest, ClientOptions, CommandType, ConfigKey, ConfigKeyQuery, ConfigResponse, ConfigureProviderOauthData, ConfigureProviderOauthErrors, ConfigureProviderOauthResponses, ConfirmToolActionData, ConfirmToolActionErrors, ConfirmToolActionRequest, ConfirmToolActionResponses, Content, ContentBlock, Conversation, CreateCustomProviderData, CreateCustomProviderErrors, CreateCustomProviderResponse, CreateCustomProviderResponses, CreateRecipeData, CreateRecipeErrors, CreateRecipeRequest, CreateRecipeResponse, CreateRecipeResponse2, CreateRecipeResponses, CreateScheduleData, CreateScheduleErrors, CreateScheduleRequest, CreateScheduleResponse, CreateScheduleResponses, CspMetadata, DeclarativeProviderConfig, DecodeRecipeData, DecodeRecipeErrors, DecodeRecipeRequest, DecodeRecipeResponse, DecodeRecipeResponse2, DecodeRecipeResponses, DeleteLocalModelData, DeleteLocalModelErrors, DeleteLocalModelResponses, DeleteModelData, DeleteModelErrors, DeleteModelResponses, DeleteRecipeData, DeleteRecipeErrors, DeleteRecipeRequest, DeleteRecipeResponse, DeleteRecipeResponses, DeleteScheduleData, DeleteScheduleErrors, DeleteScheduleResponse, DeleteScheduleResponses, DeleteSessionData, DeleteSessionErrors, DeleteSessionResponses, DetectProviderData, DetectProviderErrors, DetectProviderRequest, DetectProviderResponse, DetectProviderResponse2, DetectProviderResponses, DiagnosticsData, DiagnosticsErrors, DiagnosticsResponse, DiagnosticsResponses, DictationProvider, DictationProviderStatus, DownloadHfModelData, DownloadHfModelErrors, DownloadHfModelResponse, DownloadHfModelResponses, DownloadModelData, DownloadModelErrors, DownloadModelRequest, DownloadModelResponses, DownloadProgress, DownloadStatus, EmbeddedResource, EncodeRecipeData, EncodeRecipeErrors, EncodeRecipeRequest, EncodeRecipeResponse, EncodeRecipeResponse2, EncodeRecipeResponses, Envs, EnvVarConfig, ErrorResponse, ExportAppData, ExportAppError, ExportAppErrors, ExportAppResponse, ExportAppResponses, ExportSessionData, ExportSessionErrors, ExportSessionResponse, ExportSessionResponses, ExtensionConfig, ExtensionData, ExtensionEntry, ExtensionLoadResult, ExtensionQuery, ExtensionResponse, ForkRequest, ForkResponse, ForkSessionData, ForkSessionErrors, ForkSessionResponse, ForkSessionResponses, FrontendToolRequest, GetCanonicalModelInfoData, GetCanonicalModelInfoResponse, GetCanonicalModelInfoResponses, GetCustomProviderData, GetCustomProviderErrors, GetCustomProviderResponse, GetCustomProviderResponses, GetDictationConfigData, GetDictationConfigResponse, GetDictationConfigResponses, GetDownloadProgressData, GetDownloadProgressErrors, GetDownloadProgressResponse, GetDownloadProgressResponses, GetExtensionsData, GetExtensionsErrors, GetExtensionsResponse, GetExtensionsResponses, GetLocalModelDownloadProgressData, GetLocalModelDownloadProgressErrors, GetLocalModelDownloadProgressResponse, GetLocalModelDownloadProgressResponses, GetModelSettingsData, GetModelSettingsErrors, GetModelSettingsResponse, GetModelSettingsResponses, GetPromptData, GetPromptErrors, GetPromptResponse, GetPromptResponses, GetPromptsData, GetPromptsResponse, GetPromptsResponses, GetProviderCatalogData, GetProviderCatalogErrors, GetProviderCatalogResponse, GetProviderCatalogResponses, GetProviderCatalogTemplateData, GetProviderCatalogTemplateErrors, GetProviderCatalogTemplateResponse, GetProviderCatalogTemplateResponses, GetProviderModelsData, GetProviderModelsErrors, GetProviderModelsResponse, GetProviderModelsResponses, GetRepoFilesData, GetRepoFilesResponse, GetRepoFilesResponses, GetSessionData, GetSessionErrors, GetSessionExtensionsData, GetSessionExtensionsErrors, GetSessionExtensionsResponse, GetSessionExtensionsResponses, GetSessionInsightsData, GetSessionInsightsErrors, GetSessionInsightsResponse, GetSessionInsightsResponses, GetSessionResponse, GetSessionResponses, GetSlashCommandsData, GetSlashCommandsResponse, GetSlashCommandsResponses, GetToolsData, GetToolsErrors, GetToolsQuery, GetToolsResponse, GetToolsResponses, GetTunnelStatusData, GetTunnelStatusResponse, GetTunnelStatusResponses, GooseApp, GooseMode, HfGgufFile, HfModelInfo, HfQuantVariant, Icon, ImageContent, ImportAppData, ImportAppError, ImportAppErrors, ImportAppRequest, ImportAppResponse, ImportAppResponse2, ImportAppResponses, ImportSessionData, ImportSessionErrors, ImportSessionRequest, ImportSessionResponse, ImportSessionResponses, InitConfigData, InitConfigErrors, InitConfigResponse, InitConfigResponses, InspectJobResponse, InspectRunningJobData, InspectRunningJobErrors, InspectRunningJobResponse, InspectRunningJobResponses, JsonObject, KillJobResponse, KillRunningJobData, KillRunningJobResponses, ListAppsData, ListAppsError, ListAppsErrors, ListAppsRequest, ListAppsResponse, ListAppsResponse2, ListAppsResponses, ListLocalModelsData, ListLocalModelsResponse, ListLocalModelsResponses, ListModelsData, ListModelsResponse, ListModelsResponses, ListRecipeResponse, ListRecipesData, ListRecipesErrors, ListRecipesResponse, ListRecipesResponses, ListSchedulesData, ListSchedulesErrors, ListSchedulesResponse, ListSchedulesResponse2, ListSchedulesResponses, ListSessionsData, ListSessionsErrors, ListSessionsResponse, ListSessionsResponses, LoadedProvider, LocalModelResponse, McpAppResource, McpUiProxyData, McpUiProxyErrors, McpUiProxyResponses, Message, MessageContent, MessageEvent, MessageMetadata, ModelCapabilities, ModelConfig, ModelDownloadStatus, ModelInfo, ModelInfoData, ModelInfoQuery, ModelInfoResponse, ModelSettings, ModelTemplate, ParseRecipeData, ParseRecipeError, ParseRecipeErrors, ParseRecipeRequest, ParseRecipeResponse, ParseRecipeResponse2, ParseRecipeResponses, PauseScheduleData, PauseScheduleErrors, PauseScheduleResponse, PauseScheduleResponses, Permission, PermissionLevel, PermissionsMetadata, PrincipalType, PromptContentResponse, PromptsListResponse, ProviderCatalogEntry, ProviderDetails, ProviderEngine, ProviderMetadata, ProvidersData, ProvidersResponse, ProvidersResponse2, ProvidersResponses, ProviderTemplate, ProviderType, RawAudioContent, RawEmbeddedResource, RawImageContent, RawResource, RawTextContent, ReadAllConfigData, ReadAllConfigResponse, ReadAllConfigResponses, ReadConfigData, ReadConfigErrors, ReadConfigResponses, ReadResourceData, ReadResourceErrors, ReadResourceRequest, ReadResourceResponse, ReadResourceResponse2, ReadResourceResponses, ReasoningContent, Recipe, RecipeManifest, RecipeParameter, RecipeParameterInputType, RecipeParameterRequirement, RecipeToYamlData, RecipeToYamlError, RecipeToYamlErrors, RecipeToYamlRequest, RecipeToYamlResponse, RecipeToYamlResponse2, RecipeToYamlResponses, RecoverConfigData, RecoverConfigErrors, RecoverConfigResponse, RecoverConfigResponses, RedactedThinkingContent, RemoveConfigData, RemoveConfigErrors, RemoveConfigResponse, RemoveConfigResponses, RemoveCustomProviderData, RemoveCustomProviderErrors, RemoveCustomProviderResponse, RemoveCustomProviderResponses, RemoveExtensionData, RemoveExtensionErrors, RemoveExtensionRequest, RemoveExtensionResponse, RemoveExtensionResponses, ReplyData, ReplyErrors, ReplyResponse, ReplyResponses, RepoVariantsResponse, ResetPromptData, ResetPromptErrors, ResetPromptResponse, ResetPromptResponses, ResourceContents, ResourceMetadata, Response, RestartAgentData, RestartAgentErrors, RestartAgentRequest, RestartAgentResponse, RestartAgentResponse2, RestartAgentResponses, ResumeAgentData, ResumeAgentErrors, ResumeAgentRequest, ResumeAgentResponse, ResumeAgentResponse2, ResumeAgentResponses, RetryConfig, Role, RunNowHandlerData, RunNowHandlerErrors, RunNowHandlerResponse, RunNowHandlerResponses, RunNowResponse, SamplingConfig, SavePromptData, SavePromptErrors, SavePromptRequest, SavePromptResponse, SavePromptResponses, SaveRecipeData, SaveRecipeError, SaveRecipeErrors, SaveRecipeRequest, SaveRecipeResponse, SaveRecipeResponse2, SaveRecipeResponses, ScanRecipeData, ScanRecipeRequest, ScanRecipeResponse, ScanRecipeResponse2, ScanRecipeResponses, ScheduledJob, ScheduleRecipeData, ScheduleRecipeErrors, ScheduleRecipeRequest, ScheduleRecipeResponses, SearchHfModelsData, SearchHfModelsErrors, SearchHfModelsResponse, SearchHfModelsResponses, SearchSessionsData, SearchSessionsErrors, SearchSessionsResponse, SearchSessionsResponses, SendTelemetryEventData, SendTelemetryEventResponses, Session, SessionDisplayInfo, SessionExtensionsResponse, SessionInsights, SessionListResponse, SessionsHandlerData, SessionsHandlerErrors, SessionsHandlerResponse, SessionsHandlerResponses, SessionsQuery, SessionType, SetConfigProviderData, SetProviderRequest, SetRecipeSlashCommandData, SetRecipeSlashCommandErrors, SetRecipeSlashCommandResponses, SetSlashCommandRequest, Settings, SetupResponse, SlashCommand, SlashCommandsResponse, StartAgentData, StartAgentError, StartAgentErrors, StartAgentRequest, StartAgentResponse, StartAgentResponses, StartOpenrouterSetupData, StartOpenrouterSetupResponse, StartOpenrouterSetupResponses, StartTetrateSetupData, StartTetrateSetupResponse, StartTetrateSetupResponses, StartTunnelData, StartTunnelError, StartTunnelErrors, StartTunnelResponse, StartTunnelResponses, StatusData, StatusResponse, StatusResponses, StopAgentData, StopAgentErrors, StopAgentRequest, StopAgentResponse, StopAgentResponses, StopTunnelData, StopTunnelError, StopTunnelErrors, StopTunnelResponses, SubRecipe, SuccessCheck, SystemInfo, SystemInfoData, SystemInfoResponse, SystemInfoResponses, SystemNotificationContent, SystemNotificationType, TaskSupport, TelemetryEventRequest, Template, TextContent, ThinkingContent, TokenState, Tool, ToolAnnotations, ToolConfirmationRequest, ToolExecution, ToolInfo, ToolPermission, ToolRequest, ToolResponse, TranscribeDictationData, TranscribeDictationErrors, TranscribeDictationResponse, TranscribeDictationResponses, TranscribeRequest, TranscribeResponse, TunnelInfo, TunnelState, UiMetadata, UnpauseScheduleData, UnpauseScheduleErrors, UnpauseScheduleResponse, UnpauseScheduleResponses, UpdateAgentProviderData, UpdateAgentProviderErrors, UpdateAgentProviderResponses, UpdateCustomProviderData, UpdateCustomProviderErrors, UpdateCustomProviderRequest, UpdateCustomProviderResponse, UpdateCustomProviderResponses, UpdateFromSessionData, UpdateFromSessionErrors, UpdateFromSessionRequest, UpdateFromSessionResponses, UpdateModelSettingsData, UpdateModelSettingsErrors, UpdateModelSettingsResponse, UpdateModelSettingsResponses, UpdateProviderRequest, UpdateScheduleData, UpdateScheduleErrors, UpdateScheduleRequest, UpdateScheduleResponse, UpdateScheduleResponses, UpdateSessionData, UpdateSessionErrors, UpdateSessionNameData, UpdateSessionNameErrors, UpdateSessionNameRequest, UpdateSessionNameResponses, UpdateSessionRequest, UpdateSessionResponses, UpdateSessionUserRecipeValuesData, UpdateSessionUserRecipeValuesError, UpdateSessionUserRecipeValuesErrors, UpdateSessionUserRecipeValuesRequest, UpdateSessionUserRecipeValuesResponse, UpdateSessionUserRecipeValuesResponse2, UpdateSessionUserRecipeValuesResponses, UpdateWorkingDirData, UpdateWorkingDirErrors, UpdateWorkingDirRequest, UpdateWorkingDirResponses, UpsertConfigData, UpsertConfigErrors, UpsertConfigQuery, UpsertConfigResponse, UpsertConfigResponses, UpsertPermissionsData, UpsertPermissionsErrors, UpsertPermissionsQuery, UpsertPermissionsResponse, UpsertPermissionsResponses, ValidateConfigData, ValidateConfigErrors, ValidateConfigResponse, ValidateConfigResponses, WhisperModelResponse, WindowProps } from './types.gen'; +export { addExtension, agentAddExtension, agentRemoveExtension, backupConfig, callTool, cancelDownload, cancelLocalModelDownload, checkProvider, configureProviderOauth, confirmToolAction, createCustomProvider, createRecipe, createSchedule, decodeRecipe, deleteLocalModel, deleteModel, deleteRecipe, deleteSchedule, deleteSession, detectProvider, diagnostics, downloadHfModel, downloadModel, encodeRecipe, exportApp, exportSession, forkSession, getCanonicalModelInfo, getCustomProvider, getDictationConfig, getDownloadProgress, getExtensions, getLocalModelDownloadProgress, getModelSettings, getPrompt, getPrompts, getProviderCatalog, getProviderCatalogTemplate, getProviderModels, getRepoFiles, getSession, getSessionExtensions, getSessionInsights, getSlashCommands, getTools, getTunnelStatus, importApp, importSession, initConfig, inspectRunningJob, killRunningJob, listApps, listLocalModels, listModels, listRecipes, listSchedules, listSessions, mcpUiProxy, type Options, parseRecipe, pauseSchedule, providers, readAllConfig, readConfig, readResource, recipeToYaml, recoverConfig, removeConfig, removeCustomProvider, removeExtension, reply, resetPrompt, restartAgent, resumeAgent, runNowHandler, savePrompt, saveRecipe, scanRecipe, scheduleRecipe, searchHfModels, searchSessions, sendTelemetryEvent, sessionsHandler, setConfigProvider, setRecipeSlashCommand, startAgent, startNanogptSetup, startOpenrouterSetup, startTetrateSetup, startTunnel, status, stopAgent, stopTunnel, systemInfo, transcribeDictation, unpauseSchedule, updateAgentProvider, updateCustomProvider, updateFromSession, updateModelSettings, updateSchedule, updateSession, updateSessionName, updateSessionUserRecipeValues, updateWorkingDir, upsertConfig, upsertPermissions, validateConfig } from './sdk.gen'; +export type { ActionRequired, ActionRequiredData, AddExtensionData, AddExtensionErrors, AddExtensionRequest, AddExtensionResponse, AddExtensionResponses, AgentAddExtensionData, AgentAddExtensionErrors, AgentAddExtensionResponse, AgentAddExtensionResponses, AgentRemoveExtensionData, AgentRemoveExtensionErrors, AgentRemoveExtensionResponse, AgentRemoveExtensionResponses, Annotations, Author, AuthorRequest, BackupConfigData, BackupConfigErrors, BackupConfigResponse, BackupConfigResponses, CallToolData, CallToolErrors, CallToolRequest, CallToolResponse, CallToolResponse2, CallToolResponses, CancelDownloadData, CancelDownloadErrors, CancelDownloadResponses, CancelLocalModelDownloadData, CancelLocalModelDownloadErrors, CancelLocalModelDownloadResponses, ChatRequest, CheckProviderData, CheckProviderRequest, ClientOptions, CommandType, ConfigKey, ConfigKeyQuery, ConfigResponse, ConfigureProviderOauthData, ConfigureProviderOauthErrors, ConfigureProviderOauthResponses, ConfirmToolActionData, ConfirmToolActionErrors, ConfirmToolActionRequest, ConfirmToolActionResponses, Content, ContentBlock, Conversation, CreateCustomProviderData, CreateCustomProviderErrors, CreateCustomProviderResponse, CreateCustomProviderResponse2, CreateCustomProviderResponses, CreateRecipeData, CreateRecipeErrors, CreateRecipeRequest, CreateRecipeResponse, CreateRecipeResponse2, CreateRecipeResponses, CreateScheduleData, CreateScheduleErrors, CreateScheduleRequest, CreateScheduleResponse, CreateScheduleResponses, CspMetadata, DeclarativeProviderConfig, DecodeRecipeData, DecodeRecipeErrors, DecodeRecipeRequest, DecodeRecipeResponse, DecodeRecipeResponse2, DecodeRecipeResponses, DeleteLocalModelData, DeleteLocalModelErrors, DeleteLocalModelResponses, DeleteModelData, DeleteModelErrors, DeleteModelResponses, DeleteRecipeData, DeleteRecipeErrors, DeleteRecipeRequest, DeleteRecipeResponse, DeleteRecipeResponses, DeleteScheduleData, DeleteScheduleErrors, DeleteScheduleResponse, DeleteScheduleResponses, DeleteSessionData, DeleteSessionErrors, DeleteSessionResponses, DetectProviderData, DetectProviderErrors, DetectProviderRequest, DetectProviderResponse, DetectProviderResponse2, DetectProviderResponses, DiagnosticsData, DiagnosticsErrors, DiagnosticsResponse, DiagnosticsResponses, DictationProvider, DictationProviderStatus, DownloadHfModelData, DownloadHfModelErrors, DownloadHfModelResponse, DownloadHfModelResponses, DownloadModelData, DownloadModelErrors, DownloadModelRequest, DownloadModelResponses, DownloadProgress, DownloadStatus, EmbeddedResource, EncodeRecipeData, EncodeRecipeErrors, EncodeRecipeRequest, EncodeRecipeResponse, EncodeRecipeResponse2, EncodeRecipeResponses, Envs, EnvVarConfig, ErrorResponse, ExportAppData, ExportAppError, ExportAppErrors, ExportAppResponse, ExportAppResponses, ExportSessionData, ExportSessionErrors, ExportSessionResponse, ExportSessionResponses, ExtensionConfig, ExtensionData, ExtensionEntry, ExtensionLoadResult, ExtensionQuery, ExtensionResponse, ForkRequest, ForkResponse, ForkSessionData, ForkSessionErrors, ForkSessionResponse, ForkSessionResponses, FrontendToolRequest, GetCanonicalModelInfoData, GetCanonicalModelInfoResponse, GetCanonicalModelInfoResponses, GetCustomProviderData, GetCustomProviderErrors, GetCustomProviderResponse, GetCustomProviderResponses, GetDictationConfigData, GetDictationConfigResponse, GetDictationConfigResponses, GetDownloadProgressData, GetDownloadProgressErrors, GetDownloadProgressResponse, GetDownloadProgressResponses, GetExtensionsData, GetExtensionsErrors, GetExtensionsResponse, GetExtensionsResponses, GetLocalModelDownloadProgressData, GetLocalModelDownloadProgressErrors, GetLocalModelDownloadProgressResponse, GetLocalModelDownloadProgressResponses, GetModelSettingsData, GetModelSettingsErrors, GetModelSettingsResponse, GetModelSettingsResponses, GetPromptData, GetPromptErrors, GetPromptResponse, GetPromptResponses, GetPromptsData, GetPromptsResponse, GetPromptsResponses, GetProviderCatalogData, GetProviderCatalogErrors, GetProviderCatalogResponse, GetProviderCatalogResponses, GetProviderCatalogTemplateData, GetProviderCatalogTemplateErrors, GetProviderCatalogTemplateResponse, GetProviderCatalogTemplateResponses, GetProviderModelsData, GetProviderModelsErrors, GetProviderModelsResponse, GetProviderModelsResponses, GetRepoFilesData, GetRepoFilesResponse, GetRepoFilesResponses, GetSessionData, GetSessionErrors, GetSessionExtensionsData, GetSessionExtensionsErrors, GetSessionExtensionsResponse, GetSessionExtensionsResponses, GetSessionInsightsData, GetSessionInsightsErrors, GetSessionInsightsResponse, GetSessionInsightsResponses, GetSessionResponse, GetSessionResponses, GetSlashCommandsData, GetSlashCommandsResponse, GetSlashCommandsResponses, GetToolsData, GetToolsErrors, GetToolsQuery, GetToolsResponse, GetToolsResponses, GetTunnelStatusData, GetTunnelStatusResponse, GetTunnelStatusResponses, GooseApp, GooseMode, HfGgufFile, HfModelInfo, HfQuantVariant, Icon, ImageContent, ImportAppData, ImportAppError, ImportAppErrors, ImportAppRequest, ImportAppResponse, ImportAppResponse2, ImportAppResponses, ImportSessionData, ImportSessionErrors, ImportSessionRequest, ImportSessionResponse, ImportSessionResponses, InitConfigData, InitConfigErrors, InitConfigResponse, InitConfigResponses, InspectJobResponse, InspectRunningJobData, InspectRunningJobErrors, InspectRunningJobResponse, InspectRunningJobResponses, JsonObject, KillJobResponse, KillRunningJobData, KillRunningJobResponses, ListAppsData, ListAppsError, ListAppsErrors, ListAppsRequest, ListAppsResponse, ListAppsResponse2, ListAppsResponses, ListLocalModelsData, ListLocalModelsResponse, ListLocalModelsResponses, ListModelsData, ListModelsResponse, ListModelsResponses, ListRecipeResponse, ListRecipesData, ListRecipesErrors, ListRecipesResponse, ListRecipesResponses, ListSchedulesData, ListSchedulesErrors, ListSchedulesResponse, ListSchedulesResponse2, ListSchedulesResponses, ListSessionsData, ListSessionsErrors, ListSessionsResponse, ListSessionsResponses, LoadedProvider, LocalModelResponse, McpAppResource, McpUiProxyData, McpUiProxyErrors, McpUiProxyResponses, Message, MessageContent, MessageEvent, MessageMetadata, ModelCapabilities, ModelConfig, ModelDownloadStatus, ModelInfo, ModelInfoData, ModelInfoQuery, ModelInfoResponse, ModelSettings, ModelTemplate, ParseRecipeData, ParseRecipeError, ParseRecipeErrors, ParseRecipeRequest, ParseRecipeResponse, ParseRecipeResponse2, ParseRecipeResponses, PauseScheduleData, PauseScheduleErrors, PauseScheduleResponse, PauseScheduleResponses, Permission, PermissionLevel, PermissionsMetadata, PrincipalType, PromptContentResponse, PromptsListResponse, ProviderCatalogEntry, ProviderDetails, ProviderEngine, ProviderMetadata, ProvidersData, ProvidersResponse, ProvidersResponse2, ProvidersResponses, ProviderTemplate, ProviderType, RawAudioContent, RawEmbeddedResource, RawImageContent, RawResource, RawTextContent, ReadAllConfigData, ReadAllConfigResponse, ReadAllConfigResponses, ReadConfigData, ReadConfigErrors, ReadConfigResponses, ReadResourceData, ReadResourceErrors, ReadResourceRequest, ReadResourceResponse, ReadResourceResponse2, ReadResourceResponses, ReasoningContent, Recipe, RecipeManifest, RecipeParameter, RecipeParameterInputType, RecipeParameterRequirement, RecipeToYamlData, RecipeToYamlError, RecipeToYamlErrors, RecipeToYamlRequest, RecipeToYamlResponse, RecipeToYamlResponse2, RecipeToYamlResponses, RecoverConfigData, RecoverConfigErrors, RecoverConfigResponse, RecoverConfigResponses, RedactedThinkingContent, RemoveConfigData, RemoveConfigErrors, RemoveConfigResponse, RemoveConfigResponses, RemoveCustomProviderData, RemoveCustomProviderErrors, RemoveCustomProviderResponse, RemoveCustomProviderResponses, RemoveExtensionData, RemoveExtensionErrors, RemoveExtensionRequest, RemoveExtensionResponse, RemoveExtensionResponses, ReplyData, ReplyErrors, ReplyResponse, ReplyResponses, RepoVariantsResponse, ResetPromptData, ResetPromptErrors, ResetPromptResponse, ResetPromptResponses, ResourceContents, ResourceMetadata, Response, RestartAgentData, RestartAgentErrors, RestartAgentRequest, RestartAgentResponse, RestartAgentResponse2, RestartAgentResponses, ResumeAgentData, ResumeAgentErrors, ResumeAgentRequest, ResumeAgentResponse, ResumeAgentResponse2, ResumeAgentResponses, RetryConfig, Role, RunNowHandlerData, RunNowHandlerErrors, RunNowHandlerResponse, RunNowHandlerResponses, RunNowResponse, SamplingConfig, SavePromptData, SavePromptErrors, SavePromptRequest, SavePromptResponse, SavePromptResponses, SaveRecipeData, SaveRecipeError, SaveRecipeErrors, SaveRecipeRequest, SaveRecipeResponse, SaveRecipeResponse2, SaveRecipeResponses, ScanRecipeData, ScanRecipeRequest, ScanRecipeResponse, ScanRecipeResponse2, ScanRecipeResponses, ScheduledJob, ScheduleRecipeData, ScheduleRecipeErrors, ScheduleRecipeRequest, ScheduleRecipeResponses, SearchHfModelsData, SearchHfModelsErrors, SearchHfModelsResponse, SearchHfModelsResponses, SearchSessionsData, SearchSessionsErrors, SearchSessionsResponse, SearchSessionsResponses, SendTelemetryEventData, SendTelemetryEventResponses, Session, SessionDisplayInfo, SessionExtensionsResponse, SessionInsights, SessionListResponse, SessionsHandlerData, SessionsHandlerErrors, SessionsHandlerResponse, SessionsHandlerResponses, SessionsQuery, SessionType, SetConfigProviderData, SetProviderRequest, SetRecipeSlashCommandData, SetRecipeSlashCommandErrors, SetRecipeSlashCommandResponses, SetSlashCommandRequest, Settings, SetupResponse, SlashCommand, SlashCommandsResponse, StartAgentData, StartAgentError, StartAgentErrors, StartAgentRequest, StartAgentResponse, StartAgentResponses, StartNanogptSetupData, StartNanogptSetupResponse, StartNanogptSetupResponses, StartOpenrouterSetupData, StartOpenrouterSetupResponse, StartOpenrouterSetupResponses, StartTetrateSetupData, StartTetrateSetupResponse, StartTetrateSetupResponses, StartTunnelData, StartTunnelError, StartTunnelErrors, StartTunnelResponse, StartTunnelResponses, StatusData, StatusResponse, StatusResponses, StopAgentData, StopAgentErrors, StopAgentRequest, StopAgentResponse, StopAgentResponses, StopTunnelData, StopTunnelError, StopTunnelErrors, StopTunnelResponses, SubRecipe, SuccessCheck, SystemInfo, SystemInfoData, SystemInfoResponse, SystemInfoResponses, SystemNotificationContent, SystemNotificationType, TaskSupport, TelemetryEventRequest, Template, TextContent, ThinkingContent, TokenState, Tool, ToolAnnotations, ToolConfirmationRequest, ToolExecution, ToolInfo, ToolPermission, ToolRequest, ToolResponse, TranscribeDictationData, TranscribeDictationErrors, TranscribeDictationResponse, TranscribeDictationResponses, TranscribeRequest, TranscribeResponse, TunnelInfo, TunnelState, UiMetadata, UnpauseScheduleData, UnpauseScheduleErrors, UnpauseScheduleResponse, UnpauseScheduleResponses, UpdateAgentProviderData, UpdateAgentProviderErrors, UpdateAgentProviderResponses, UpdateCustomProviderData, UpdateCustomProviderErrors, UpdateCustomProviderRequest, UpdateCustomProviderResponse, UpdateCustomProviderResponses, UpdateFromSessionData, UpdateFromSessionErrors, UpdateFromSessionRequest, UpdateFromSessionResponses, UpdateModelSettingsData, UpdateModelSettingsErrors, UpdateModelSettingsResponse, UpdateModelSettingsResponses, UpdateProviderRequest, UpdateScheduleData, UpdateScheduleErrors, UpdateScheduleRequest, UpdateScheduleResponse, UpdateScheduleResponses, UpdateSessionData, UpdateSessionErrors, UpdateSessionNameData, UpdateSessionNameErrors, UpdateSessionNameRequest, UpdateSessionNameResponses, UpdateSessionRequest, UpdateSessionResponses, UpdateSessionUserRecipeValuesData, UpdateSessionUserRecipeValuesError, UpdateSessionUserRecipeValuesErrors, UpdateSessionUserRecipeValuesRequest, UpdateSessionUserRecipeValuesResponse, UpdateSessionUserRecipeValuesResponse2, UpdateSessionUserRecipeValuesResponses, UpdateWorkingDirData, UpdateWorkingDirErrors, UpdateWorkingDirRequest, UpdateWorkingDirResponses, UpsertConfigData, UpsertConfigErrors, UpsertConfigQuery, UpsertConfigResponse, UpsertConfigResponses, UpsertPermissionsData, UpsertPermissionsErrors, UpsertPermissionsQuery, UpsertPermissionsResponse, UpsertPermissionsResponses, ValidateConfigData, ValidateConfigErrors, ValidateConfigResponse, ValidateConfigResponses, WhisperModelResponse, WindowProps } from './types.gen'; diff --git a/ui/desktop/src/api/sdk.gen.ts b/ui/desktop/src/api/sdk.gen.ts index df71d662b40a..959cc195bc22 100644 --- a/ui/desktop/src/api/sdk.gen.ts +++ b/ui/desktop/src/api/sdk.gen.ts @@ -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, CallToolData, CallToolErrors, CallToolResponses, CancelDownloadData, CancelDownloadErrors, CancelDownloadResponses, CancelLocalModelDownloadData, CancelLocalModelDownloadErrors, CancelLocalModelDownloadResponses, CheckProviderData, ConfigureProviderOauthData, ConfigureProviderOauthErrors, ConfigureProviderOauthResponses, ConfirmToolActionData, ConfirmToolActionErrors, ConfirmToolActionResponses, CreateCustomProviderData, CreateCustomProviderErrors, CreateCustomProviderResponses, CreateRecipeData, CreateRecipeErrors, CreateRecipeResponses, CreateScheduleData, CreateScheduleErrors, CreateScheduleResponses, DecodeRecipeData, DecodeRecipeErrors, DecodeRecipeResponses, DeleteLocalModelData, DeleteLocalModelErrors, DeleteLocalModelResponses, DeleteModelData, DeleteModelErrors, DeleteModelResponses, DeleteRecipeData, DeleteRecipeErrors, DeleteRecipeResponses, DeleteScheduleData, DeleteScheduleErrors, DeleteScheduleResponses, DeleteSessionData, DeleteSessionErrors, DeleteSessionResponses, DetectProviderData, DetectProviderErrors, DetectProviderResponses, DiagnosticsData, DiagnosticsErrors, DiagnosticsResponses, DownloadHfModelData, DownloadHfModelErrors, DownloadHfModelResponses, DownloadModelData, DownloadModelErrors, DownloadModelResponses, EncodeRecipeData, EncodeRecipeErrors, EncodeRecipeResponses, ExportAppData, ExportAppErrors, ExportAppResponses, ExportSessionData, ExportSessionErrors, ExportSessionResponses, ForkSessionData, ForkSessionErrors, ForkSessionResponses, GetCanonicalModelInfoData, GetCanonicalModelInfoResponses, GetCustomProviderData, GetCustomProviderErrors, GetCustomProviderResponses, GetDictationConfigData, GetDictationConfigResponses, GetDownloadProgressData, GetDownloadProgressErrors, GetDownloadProgressResponses, GetExtensionsData, GetExtensionsErrors, GetExtensionsResponses, GetLocalModelDownloadProgressData, GetLocalModelDownloadProgressErrors, GetLocalModelDownloadProgressResponses, GetModelSettingsData, GetModelSettingsErrors, GetModelSettingsResponses, GetPromptData, GetPromptErrors, GetPromptResponses, GetPromptsData, GetPromptsResponses, GetProviderCatalogData, GetProviderCatalogErrors, GetProviderCatalogResponses, GetProviderCatalogTemplateData, GetProviderCatalogTemplateErrors, GetProviderCatalogTemplateResponses, GetProviderModelsData, GetProviderModelsErrors, GetProviderModelsResponses, GetRepoFilesData, GetRepoFilesResponses, GetSessionData, GetSessionErrors, GetSessionExtensionsData, GetSessionExtensionsErrors, GetSessionExtensionsResponses, GetSessionInsightsData, GetSessionInsightsErrors, GetSessionInsightsResponses, GetSessionResponses, GetSlashCommandsData, GetSlashCommandsResponses, GetToolsData, GetToolsErrors, GetToolsResponses, GetTunnelStatusData, GetTunnelStatusResponses, ImportAppData, ImportAppErrors, ImportAppResponses, ImportSessionData, ImportSessionErrors, ImportSessionResponses, InitConfigData, InitConfigErrors, InitConfigResponses, InspectRunningJobData, InspectRunningJobErrors, InspectRunningJobResponses, KillRunningJobData, KillRunningJobResponses, ListAppsData, ListAppsErrors, ListAppsResponses, ListLocalModelsData, ListLocalModelsResponses, ListModelsData, ListModelsResponses, ListRecipesData, ListRecipesErrors, ListRecipesResponses, ListSchedulesData, ListSchedulesErrors, ListSchedulesResponses, ListSessionsData, ListSessionsErrors, ListSessionsResponses, McpUiProxyData, McpUiProxyErrors, McpUiProxyResponses, ParseRecipeData, ParseRecipeErrors, ParseRecipeResponses, PauseScheduleData, PauseScheduleErrors, PauseScheduleResponses, ProvidersData, ProvidersResponses, ReadAllConfigData, ReadAllConfigResponses, ReadConfigData, ReadConfigErrors, ReadConfigResponses, ReadResourceData, ReadResourceErrors, ReadResourceResponses, RecipeToYamlData, RecipeToYamlErrors, RecipeToYamlResponses, RecoverConfigData, RecoverConfigErrors, RecoverConfigResponses, RemoveConfigData, RemoveConfigErrors, RemoveConfigResponses, RemoveCustomProviderData, RemoveCustomProviderErrors, RemoveCustomProviderResponses, RemoveExtensionData, RemoveExtensionErrors, RemoveExtensionResponses, ReplyData, ReplyErrors, ReplyResponses, ResetPromptData, ResetPromptErrors, ResetPromptResponses, RestartAgentData, RestartAgentErrors, RestartAgentResponses, ResumeAgentData, ResumeAgentErrors, ResumeAgentResponses, RunNowHandlerData, RunNowHandlerErrors, RunNowHandlerResponses, SavePromptData, SavePromptErrors, SavePromptResponses, SaveRecipeData, SaveRecipeErrors, SaveRecipeResponses, ScanRecipeData, ScanRecipeResponses, ScheduleRecipeData, ScheduleRecipeErrors, ScheduleRecipeResponses, SearchHfModelsData, SearchHfModelsErrors, SearchHfModelsResponses, SearchSessionsData, SearchSessionsErrors, SearchSessionsResponses, SendTelemetryEventData, SendTelemetryEventResponses, SessionsHandlerData, SessionsHandlerErrors, SessionsHandlerResponses, SetConfigProviderData, SetRecipeSlashCommandData, SetRecipeSlashCommandErrors, SetRecipeSlashCommandResponses, StartAgentData, StartAgentErrors, StartAgentResponses, StartOpenrouterSetupData, StartOpenrouterSetupResponses, StartTetrateSetupData, StartTetrateSetupResponses, StartTunnelData, StartTunnelErrors, StartTunnelResponses, StatusData, StatusResponses, StopAgentData, StopAgentErrors, StopAgentResponses, StopTunnelData, StopTunnelErrors, StopTunnelResponses, SystemInfoData, SystemInfoResponses, TranscribeDictationData, TranscribeDictationErrors, TranscribeDictationResponses, UnpauseScheduleData, UnpauseScheduleErrors, UnpauseScheduleResponses, UpdateAgentProviderData, UpdateAgentProviderErrors, UpdateAgentProviderResponses, UpdateCustomProviderData, UpdateCustomProviderErrors, UpdateCustomProviderResponses, UpdateFromSessionData, UpdateFromSessionErrors, UpdateFromSessionResponses, UpdateModelSettingsData, UpdateModelSettingsErrors, UpdateModelSettingsResponses, UpdateScheduleData, UpdateScheduleErrors, UpdateScheduleResponses, UpdateSessionData, UpdateSessionErrors, UpdateSessionNameData, UpdateSessionNameErrors, UpdateSessionNameResponses, UpdateSessionResponses, UpdateSessionUserRecipeValuesData, UpdateSessionUserRecipeValuesErrors, UpdateSessionUserRecipeValuesResponses, UpdateWorkingDirData, UpdateWorkingDirErrors, UpdateWorkingDirResponses, 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, CallToolData, CallToolErrors, CallToolResponses, CancelDownloadData, CancelDownloadErrors, CancelDownloadResponses, CancelLocalModelDownloadData, CancelLocalModelDownloadErrors, CancelLocalModelDownloadResponses, CheckProviderData, ConfigureProviderOauthData, ConfigureProviderOauthErrors, ConfigureProviderOauthResponses, ConfirmToolActionData, ConfirmToolActionErrors, ConfirmToolActionResponses, CreateCustomProviderData, CreateCustomProviderErrors, CreateCustomProviderResponses, CreateRecipeData, CreateRecipeErrors, CreateRecipeResponses, CreateScheduleData, CreateScheduleErrors, CreateScheduleResponses, DecodeRecipeData, DecodeRecipeErrors, DecodeRecipeResponses, DeleteLocalModelData, DeleteLocalModelErrors, DeleteLocalModelResponses, DeleteModelData, DeleteModelErrors, DeleteModelResponses, DeleteRecipeData, DeleteRecipeErrors, DeleteRecipeResponses, DeleteScheduleData, DeleteScheduleErrors, DeleteScheduleResponses, DeleteSessionData, DeleteSessionErrors, DeleteSessionResponses, DetectProviderData, DetectProviderErrors, DetectProviderResponses, DiagnosticsData, DiagnosticsErrors, DiagnosticsResponses, DownloadHfModelData, DownloadHfModelErrors, DownloadHfModelResponses, DownloadModelData, DownloadModelErrors, DownloadModelResponses, EncodeRecipeData, EncodeRecipeErrors, EncodeRecipeResponses, ExportAppData, ExportAppErrors, ExportAppResponses, ExportSessionData, ExportSessionErrors, ExportSessionResponses, ForkSessionData, ForkSessionErrors, ForkSessionResponses, GetCanonicalModelInfoData, GetCanonicalModelInfoResponses, GetCustomProviderData, GetCustomProviderErrors, GetCustomProviderResponses, GetDictationConfigData, GetDictationConfigResponses, GetDownloadProgressData, GetDownloadProgressErrors, GetDownloadProgressResponses, GetExtensionsData, GetExtensionsErrors, GetExtensionsResponses, GetLocalModelDownloadProgressData, GetLocalModelDownloadProgressErrors, GetLocalModelDownloadProgressResponses, GetModelSettingsData, GetModelSettingsErrors, GetModelSettingsResponses, GetPromptData, GetPromptErrors, GetPromptResponses, GetPromptsData, GetPromptsResponses, GetProviderCatalogData, GetProviderCatalogErrors, GetProviderCatalogResponses, GetProviderCatalogTemplateData, GetProviderCatalogTemplateErrors, GetProviderCatalogTemplateResponses, GetProviderModelsData, GetProviderModelsErrors, GetProviderModelsResponses, GetRepoFilesData, GetRepoFilesResponses, GetSessionData, GetSessionErrors, GetSessionExtensionsData, GetSessionExtensionsErrors, GetSessionExtensionsResponses, GetSessionInsightsData, GetSessionInsightsErrors, GetSessionInsightsResponses, GetSessionResponses, GetSlashCommandsData, GetSlashCommandsResponses, GetToolsData, GetToolsErrors, GetToolsResponses, GetTunnelStatusData, GetTunnelStatusResponses, ImportAppData, ImportAppErrors, ImportAppResponses, ImportSessionData, ImportSessionErrors, ImportSessionResponses, InitConfigData, InitConfigErrors, InitConfigResponses, InspectRunningJobData, InspectRunningJobErrors, InspectRunningJobResponses, KillRunningJobData, KillRunningJobResponses, ListAppsData, ListAppsErrors, ListAppsResponses, ListLocalModelsData, ListLocalModelsResponses, ListModelsData, ListModelsResponses, ListRecipesData, ListRecipesErrors, ListRecipesResponses, ListSchedulesData, ListSchedulesErrors, ListSchedulesResponses, ListSessionsData, ListSessionsErrors, ListSessionsResponses, McpUiProxyData, McpUiProxyErrors, McpUiProxyResponses, ParseRecipeData, ParseRecipeErrors, ParseRecipeResponses, PauseScheduleData, PauseScheduleErrors, PauseScheduleResponses, ProvidersData, ProvidersResponses, ReadAllConfigData, ReadAllConfigResponses, ReadConfigData, ReadConfigErrors, ReadConfigResponses, ReadResourceData, ReadResourceErrors, ReadResourceResponses, RecipeToYamlData, RecipeToYamlErrors, RecipeToYamlResponses, RecoverConfigData, RecoverConfigErrors, RecoverConfigResponses, RemoveConfigData, RemoveConfigErrors, RemoveConfigResponses, RemoveCustomProviderData, RemoveCustomProviderErrors, RemoveCustomProviderResponses, RemoveExtensionData, RemoveExtensionErrors, RemoveExtensionResponses, ReplyData, ReplyErrors, ReplyResponses, ResetPromptData, ResetPromptErrors, ResetPromptResponses, RestartAgentData, RestartAgentErrors, RestartAgentResponses, ResumeAgentData, ResumeAgentErrors, ResumeAgentResponses, RunNowHandlerData, RunNowHandlerErrors, RunNowHandlerResponses, SavePromptData, SavePromptErrors, SavePromptResponses, SaveRecipeData, SaveRecipeErrors, SaveRecipeResponses, ScanRecipeData, ScanRecipeResponses, ScheduleRecipeData, ScheduleRecipeErrors, ScheduleRecipeResponses, SearchHfModelsData, SearchHfModelsErrors, SearchHfModelsResponses, SearchSessionsData, SearchSessionsErrors, SearchSessionsResponses, SendTelemetryEventData, SendTelemetryEventResponses, SessionsHandlerData, SessionsHandlerErrors, SessionsHandlerResponses, SetConfigProviderData, SetRecipeSlashCommandData, SetRecipeSlashCommandErrors, SetRecipeSlashCommandResponses, StartAgentData, StartAgentErrors, StartAgentResponses, StartNanogptSetupData, StartNanogptSetupResponses, StartOpenrouterSetupData, StartOpenrouterSetupResponses, StartTetrateSetupData, StartTetrateSetupResponses, StartTunnelData, StartTunnelErrors, StartTunnelResponses, StatusData, StatusResponses, StopAgentData, StopAgentErrors, StopAgentResponses, StopTunnelData, StopTunnelErrors, StopTunnelResponses, SystemInfoData, SystemInfoResponses, TranscribeDictationData, TranscribeDictationErrors, TranscribeDictationResponses, UnpauseScheduleData, UnpauseScheduleErrors, UnpauseScheduleResponses, UpdateAgentProviderData, UpdateAgentProviderErrors, UpdateAgentProviderResponses, UpdateCustomProviderData, UpdateCustomProviderErrors, UpdateCustomProviderResponses, UpdateFromSessionData, UpdateFromSessionErrors, UpdateFromSessionResponses, UpdateModelSettingsData, UpdateModelSettingsErrors, UpdateModelSettingsResponses, UpdateScheduleData, UpdateScheduleErrors, UpdateScheduleResponses, UpdateSessionData, UpdateSessionErrors, UpdateSessionNameData, UpdateSessionNameErrors, UpdateSessionNameResponses, UpdateSessionResponses, UpdateSessionUserRecipeValuesData, UpdateSessionUserRecipeValuesErrors, UpdateSessionUserRecipeValuesResponses, UpdateWorkingDirData, UpdateWorkingDirErrors, UpdateWorkingDirResponses, UpsertConfigData, UpsertConfigErrors, UpsertConfigResponses, UpsertPermissionsData, UpsertPermissionsErrors, UpsertPermissionsResponses, ValidateConfigData, ValidateConfigErrors, ValidateConfigResponses } from './types.gen'; export type Options = Options2 & { /** @@ -317,6 +317,8 @@ export const transcribeDictation = (option } }); +export const startNanogptSetup = (options?: Options) => (options?.client ?? client).post({ url: '/handle_nanogpt', ...options }); + export const startOpenrouterSetup = (options?: Options) => (options?.client ?? client).post({ url: '/handle_openrouter', ...options }); export const startTetrateSetup = (options?: Options) => (options?.client ?? client).post({ url: '/handle_tetrate', ...options }); diff --git a/ui/desktop/src/api/types.gen.ts b/ui/desktop/src/api/types.gen.ts index b96c24ce0444..80c8702eb60d 100644 --- a/ui/desktop/src/api/types.gen.ts +++ b/ui/desktop/src/api/types.gen.ts @@ -154,6 +154,10 @@ export type ContentBlock = ({ export type Conversation = Array; +export type CreateCustomProviderResponse = { + provider_name: string; +}; + export type CreateRecipeRequest = { author?: AuthorRequest | null; session_id: string; @@ -927,6 +931,10 @@ export type ProviderMetadata = { * The unique identifier for this provider */ name: string; + /** + * step-by-step instructions for set up providers eg: api key + */ + setup_steps?: Array; }; export type ProviderTemplate = { @@ -2229,10 +2237,10 @@ export type CreateCustomProviderResponses = { /** * Custom provider created successfully */ - 200: string; + 200: CreateCustomProviderResponse; }; -export type CreateCustomProviderResponse = CreateCustomProviderResponses[keyof CreateCustomProviderResponses]; +export type CreateCustomProviderResponse2 = CreateCustomProviderResponses[keyof CreateCustomProviderResponses]; export type RemoveCustomProviderData = { body?: never; @@ -3071,6 +3079,19 @@ export type TranscribeDictationResponses = { export type TranscribeDictationResponse = TranscribeDictationResponses[keyof TranscribeDictationResponses]; +export type StartNanogptSetupData = { + body?: never; + path?: never; + query?: never; + url: '/handle_nanogpt'; +}; + +export type StartNanogptSetupResponses = { + 200: SetupResponse; +}; + +export type StartNanogptSetupResponse = StartNanogptSetupResponses[keyof StartNanogptSetupResponses]; + export type StartOpenrouterSetupData = { body?: never; path?: never; diff --git a/ui/desktop/src/components/LocalModelSetup.tsx b/ui/desktop/src/components/LocalModelSetup.tsx index 8efd72f6f6b5..bb96a22c8412 100644 --- a/ui/desktop/src/components/LocalModelSetup.tsx +++ b/ui/desktop/src/components/LocalModelSetup.tsx @@ -1,82 +1,17 @@ -import { useState, useEffect, useCallback, useRef } from 'react'; import { useConfig } from './ConfigContext'; -import { - listLocalModels, - downloadHfModel, - getLocalModelDownloadProgress, - cancelLocalModelDownload, - type DownloadProgress, - type LocalModelResponse, -} from '../api'; import { toastService } from '../toasts'; -import { trackOnboardingSetupFailed } from '../utils/analytics'; import { Goose } from './icons'; +import LocalModelPicker from './onboarding/LocalModelPicker'; interface LocalModelSetupProps { onSuccess: () => void; onCancel: () => void; } -const formatBytes = (bytes: number): string => { - if (bytes < 1024) return `${bytes}B`; - if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(0)}KB`; - if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(0)}MB`; - return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)}GB`; -}; - -const formatSize = (bytes: number): string => { - const mb = bytes / (1024 * 1024); - return mb >= 1024 ? `${(mb / 1024).toFixed(1)}GB` : `${mb.toFixed(0)}MB`; -}; - -type SetupPhase = 'loading' | 'select' | 'downloading' | 'error'; - export function LocalModelSetup({ onSuccess, onCancel }: LocalModelSetupProps) { const { upsert } = useConfig(); - const [phase, setPhase] = useState('loading'); - const [models, setModels] = useState([]); - const [selectedModelId, setSelectedModelId] = useState(null); - const [downloadProgress, setDownloadProgress] = useState(null); - const [errorMessage, setErrorMessage] = useState(null); - const [showAllModels, setShowAllModels] = useState(false); - const pollRef = useRef | null>(null); - - const cleanup = useCallback(() => { - if (pollRef.current) { - clearInterval(pollRef.current); - pollRef.current = null; - } - }, []); - useEffect(() => cleanup, [cleanup]); - - useEffect(() => { - const load = async () => { - try { - const response = await listLocalModels(); - if (response.data) { - setModels(response.data); - - const alreadyDownloaded = response.data.find((m) => m.status.state === 'Downloaded'); - if (alreadyDownloaded) { - setSelectedModelId(alreadyDownloaded.id); - } else { - const recommended = response.data.find((m: LocalModelResponse) => m.recommended); - if (recommended) setSelectedModelId(recommended.id); - } - } - } catch (error) { - console.error('Failed to load local models:', error); - setErrorMessage('Failed to load available models. Please try again.'); - setPhase('error'); - return; - } - setPhase('select'); - }; - load(); - }, []); - - const finishSetup = async (modelId: string) => { + const handleConfigured = async (_providerName: string, modelId: string) => { await upsert('GOOSE_PROVIDER', 'local', false); await upsert('GOOSE_MODEL', modelId, false); toastService.success({ @@ -86,323 +21,20 @@ export function LocalModelSetup({ onSuccess, onCancel }: LocalModelSetupProps) { onSuccess(); }; - const startDownload = async (modelId: string) => { - setPhase('downloading'); - setDownloadProgress(null); - setErrorMessage(null); - - const model = models.find((m) => m.id === modelId); - if (!model) { - setErrorMessage('Model not found'); - setPhase('error'); - return; - } - - try { - await downloadHfModel({ body: { spec: model.id } }); - } catch (error) { - console.error('Failed to start download:', error); - setErrorMessage('Failed to start download. Please try again.'); - trackOnboardingSetupFailed('local', 'download_start_failed'); - setPhase('error'); - return; - } - - pollRef.current = setInterval(async () => { - try { - const response = await getLocalModelDownloadProgress({ path: { model_id: modelId } }); - if (response.data) { - setDownloadProgress(response.data); - if (response.data.status === 'completed') { - cleanup(); - await finishSetup(modelId); - } else if (response.data.status === 'failed') { - cleanup(); - setErrorMessage(response.data.error || 'Download failed.'); - trackOnboardingSetupFailed('local', response.data.error || 'download_failed'); - setPhase('error'); - } else if (response.data.status === 'cancelled') { - cleanup(); - setPhase('select'); - } - } - } catch { - cleanup(); - setErrorMessage('Lost connection to download. Please try again.'); - trackOnboardingSetupFailed('local', 'progress_poll_failed'); - setPhase('error'); - } - }, 500); - }; - - const handleCancel = async () => { - if (phase === 'downloading' && selectedModelId) { - cleanup(); - try { - await cancelLocalModelDownload({ path: { model_id: selectedModelId } }); - } catch { - // best-effort - } - setDownloadProgress(null); - setPhase('select'); - } else { - onCancel(); - } - }; - - const handlePrimaryAction = async () => { - if (!selectedModelId) return; - const model = models.find((m) => m.id === selectedModelId); - if (!model) return; - if (model.status.state === 'Downloaded') { - await finishSetup(model.id); - } else { - await startDownload(model.id); - } - }; - - const recommended = models.find((m) => m.recommended); - const otherModels = models.filter((m) => m.id !== recommended?.id); - const selectedModel = models.find((m) => m.id === selectedModelId); - - if (phase === 'loading') { - return ( -
-
-

Checking available models...

-
- ); - } - return (
- {/* Header */}

Run Locally

- Download a model to run Goose entirely on your machine — no API keys, no accounts, + Download a model to run goose entirely on your machine — no API keys, no accounts, completely free and private.

- {/* Error state */} - {phase === 'error' && ( -
-
-

{errorMessage}

-
- - -
- )} - - {/* Model selection */} - {phase === 'select' && ( -
- {/* Recommended model card */} - {recommended && ( -
setSelectedModelId(recommended.id)} - className={`relative w-full p-4 sm:p-6 border rounded-xl cursor-pointer transition-all duration-200 group ${ - selectedModelId === recommended.id - ? 'border-blue-500 bg-blue-500/5' - : 'border-border-subtle hover:border-border-default' - }`} - > -
- - Best for your machine - -
-
- setSelectedModelId(recommended.id)} - className="cursor-pointer flex-shrink-0 mt-1" - /> -
-
- - {recommended.id} - - {recommended.status.state === 'Downloaded' && ( - - Ready - - )} -
-

- {formatSize(recommended.size_bytes)} -

-
-
-
- )} - - {/* Expandable other models */} - {otherModels.length > 0 && ( -
- - - {showAllModels && ( -
- {otherModels.map((model) => ( -
setSelectedModelId(model.id)} - className={`w-full p-4 border rounded-xl cursor-pointer transition-all duration-200 ${ - selectedModelId === model.id - ? 'border-blue-500 bg-blue-500/5' - : 'border-border-subtle hover:border-border-default' - }`} - > -
- setSelectedModelId(model.id)} - className="cursor-pointer flex-shrink-0 mt-0.5" - /> -
-
- - {model.id} - - - {formatSize(model.size_bytes)} - - {model.status.state === 'Downloaded' && ( - - Ready - - )} -
-
-
-
- ))} -
- )} -
- )} - - {/* Primary action */} - - - -
- )} - - {/* Downloading state */} - {phase === 'downloading' && selectedModel && ( -
-
-

- Downloading {selectedModel.id} -

- - {downloadProgress ? ( -
- {/* Progress bar */} -
-
-
- - {/* Stats row */} -
- - {formatBytes(downloadProgress.bytes_downloaded)} of{' '} - {formatBytes(downloadProgress.total_bytes)} - - {downloadProgress.progress_percent.toFixed(0)}% -
- -
- {downloadProgress.speed_bps ? ( - {formatBytes(downloadProgress.speed_bps)}/s - ) : ( - - )} - {downloadProgress.eta_seconds != null && downloadProgress.eta_seconds > 0 && ( - - ~ - {downloadProgress.eta_seconds < 60 - ? `${Math.round(downloadProgress.eta_seconds)}s` - : `${Math.round(downloadProgress.eta_seconds / 60)}m`}{' '} - remaining - - )} -
-
- ) : ( -
-
- Starting download... -
- )} -
- - -
- )} +
); } diff --git a/ui/desktop/src/components/ProviderGuard.tsx b/ui/desktop/src/components/ProviderGuard.tsx index f6751e25fae8..6234ba26a77f 100644 --- a/ui/desktop/src/components/ProviderGuard.tsx +++ b/ui/desktop/src/components/ProviderGuard.tsx @@ -79,7 +79,7 @@ export default function ProviderGuard({ didSelectProvider, children }: ProviderG } | null>(null); const handleTetrateSetup = async () => { - trackOnboardingProviderSelected('tetrate'); + trackOnboardingProviderSelected({ method: 'tetrate' }); try { const result = await startTetrateSetup(); if (result.success) { @@ -107,7 +107,7 @@ export default function ProviderGuard({ didSelectProvider, children }: ProviderG }; const handleChatGptCodexSetup = async () => { - trackOnboardingProviderSelected('chatgpt_codex'); + trackOnboardingProviderSelected({ method: 'chatgpt_codex' }); try { const result = await startChatGptCodexSetup(); if (result.success) { @@ -136,7 +136,7 @@ export default function ProviderGuard({ didSelectProvider, children }: ProviderG }; const handleApiKeySuccess = async (provider: string, _model: string, apiKey: string) => { - trackOnboardingProviderSelected('api_key'); + trackOnboardingProviderSelected({ method: 'api_key' }); const keyName = `${provider.toUpperCase()}_API_KEY`; await upsert(keyName, apiKey, true); await upsert('GOOSE_PROVIDER', provider, false); @@ -161,7 +161,7 @@ export default function ProviderGuard({ didSelectProvider, children }: ProviderG }; const handleOpenRouterSetup = async () => { - trackOnboardingProviderSelected('openrouter'); + trackOnboardingProviderSelected({ method: 'openrouter' }); try { const result = await startOpenRouterSetup(); if (result.success) { @@ -339,7 +339,7 @@ export default function ProviderGuard({ didSelectProvider, children }: ProviderG
{ - trackOnboardingProviderSelected('local'); + trackOnboardingProviderSelected({ method: 'local' }); setShowLocalModelSetup(true); }} className="w-full p-4 sm:p-6 bg-transparent border rounded-xl transition-all duration-200 cursor-pointer group" diff --git a/ui/desktop/src/components/onboarding/FreeOptionCards.tsx b/ui/desktop/src/components/onboarding/FreeOptionCards.tsx new file mode 100644 index 000000000000..a199b23a7f32 --- /dev/null +++ b/ui/desktop/src/components/onboarding/FreeOptionCards.tsx @@ -0,0 +1,145 @@ +import { useState } from 'react'; +import { startNanogptSetup } from '../../utils/nanogptSetup'; +import { startTetrateSetup } from '../../utils/tetrateSetup'; +import { Tetrate } from '../icons'; +import LocalModelPicker from './LocalModelPicker'; +import { HardDrive } from 'lucide-react'; + +const TETRATE = 'tetrate' as const; +const NANOGPT = 'nano-gpt' as const; +const LOCAL_PROVIDER = 'local' as const; +type FreeOption = typeof TETRATE | typeof NANOGPT | typeof LOCAL_PROVIDER; + +interface FreeOptionCardsProps { + onConfigured: (providerName: string, modelId?: string) => void; +} + +const ChevronRight = () => ( + + + +); + +const cardClass = (isSelected: boolean) => + `w-full p-4 bg-transparent border rounded-lg transition-all duration-200 cursor-pointer group ${ + isSelected ? 'border-blue-400' : 'hover:border-blue-400' + }`; + +export default function FreeOptionCards({ onConfigured }: FreeOptionCardsProps) { + const [error, setError] = useState<{ + message: string; + type: typeof TETRATE | typeof NANOGPT; + } | null>(null); + const [selectedProvider, setSelectedProvider] = useState(null); + + const handleSetup = async (type: typeof TETRATE | typeof NANOGPT) => { + setError(null); + setSelectedProvider(type); + try { + const result = type === TETRATE ? await startTetrateSetup() : await startNanogptSetup(); + if (result.success) { + onConfigured(type); + } else { + setError({ message: result.message, type }); + } + } catch { + setError({ message: 'An unexpected error occurred during setup.', type }); + } + }; + + const handleTetrateSetup = () => handleSetup(TETRATE); + const handleNanogptSetup = () => handleSetup(NANOGPT); + + const handleRunLocallyClick = () => { + setSelectedProvider(LOCAL_PROVIDER); + }; + + const handleRetry = () => { + if (!error) return; + if (error.type === TETRATE) { + handleTetrateSetup(); + } else { + handleNanogptSetup(); + } + }; + + if (selectedProvider === LOCAL_PROVIDER) { + return ( + setSelectedProvider(null)} /> + ); + } + + return ( +
+
+

Choose an option to get started.

+ +
+
+
+
+ + + Agent Router by Tetrate + +
+
+ +
+
+

+ Access multiple AI models with automatic setup. Sign up to receive $10 credit. +

+
+ +
+
+
+ + N + + NanoGPT +
+
+ +
+
+

+ Sign up to receive 60M free tokens for 7 days. +

+
+ +
+
+
+ + Use a Local Model + + Free & Private + +
+
+ +
+
+

+ Download a model and run entirely on your machine. No API keys, no accounts. +

+
+
+ + {error && ( +
+

{error.message}

+ +
+ )} +
+
+ ); +} diff --git a/ui/desktop/src/components/onboarding/LocalModelPicker.tsx b/ui/desktop/src/components/onboarding/LocalModelPicker.tsx new file mode 100644 index 000000000000..b349213241d8 --- /dev/null +++ b/ui/desktop/src/components/onboarding/LocalModelPicker.tsx @@ -0,0 +1,386 @@ +import { useState, useEffect, useCallback, useRef } from 'react'; +import { + listLocalModels, + downloadHfModel, + getLocalModelDownloadProgress, + cancelLocalModelDownload, + type DownloadProgress, + type LocalModelResponse, +} from '../../api'; +import { trackOnboardingSetupFailed } from '../../utils/analytics'; + +interface LocalModelPickerProps { + onConfigured: (providerName: string, modelId: string) => void; + onBack?: () => void; +} + +const formatBytes = (bytes: number): string => { + if (bytes < 1024) return `${bytes}B`; + if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(0)}KB`; + if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(0)}MB`; + return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)}GB`; +}; + +const formatSize = (bytes: number): string => { + const mb = bytes / (1024 * 1024); + return mb >= 1024 ? `${(mb / 1024).toFixed(1)}GB` : `${mb.toFixed(0)}MB`; +}; + +const LOCAL_PROVIDER = 'local'; + +type Phase = 'loading' | 'select' | 'downloading' | 'error'; + +export default function LocalModelPicker({ onConfigured, onBack }: LocalModelPickerProps) { + const [phase, setPhase] = useState('loading'); + const [models, setModels] = useState([]); + const [selectedModelId, setSelectedModelId] = useState(null); + const [downloadProgress, setDownloadProgress] = useState(null); + const [errorMessage, setErrorMessage] = useState(null); + const [showAllModels, setShowAllModels] = useState(false); + const pollRef = useRef | null>(null); + + const cleanup = useCallback(() => { + if (pollRef.current) { + clearInterval(pollRef.current); + pollRef.current = null; + } + }, []); + + useEffect(() => cleanup, [cleanup]); + + useEffect(() => { + const load = async () => { + try { + const response = await listLocalModels({ throwOnError: true }); + if (response.data) { + setModels(response.data); + + const alreadyDownloaded = response.data.find((m) => m.status.state === 'Downloaded'); + if (alreadyDownloaded) { + setSelectedModelId(alreadyDownloaded.id); + } else { + const recommended = response.data.find((m: LocalModelResponse) => m.recommended); + if (recommended) setSelectedModelId(recommended.id); + } + } + } catch (error) { + console.error('Failed to load local models:', error); + setErrorMessage('Failed to load available models. Please try again.'); + setPhase('error'); + return; + } + setPhase('select'); + }; + load(); + }, []); + + const finishSetup = (modelId: string) => { + onConfigured(LOCAL_PROVIDER, modelId); + }; + + const startDownload = async (modelId: string) => { + setPhase('downloading'); + setDownloadProgress(null); + setErrorMessage(null); + + const model = models.find((m) => m.id === modelId); + if (!model) { + setErrorMessage('Model not found'); + setPhase('error'); + return; + } + + try { + await downloadHfModel({ body: { spec: model.id }, throwOnError: true }); + } catch (error) { + console.error('Failed to start download:', error); + setErrorMessage('Failed to start download. Please try again.'); + trackOnboardingSetupFailed(LOCAL_PROVIDER, 'download_start_failed'); + setPhase('error'); + return; + } + + pollRef.current = setInterval(async () => { + try { + const response = await getLocalModelDownloadProgress({ + path: { model_id: modelId }, + throwOnError: true, + }); + if (response.data) { + setDownloadProgress(response.data); + if (response.data.status === 'completed') { + cleanup(); + finishSetup(modelId); + } else if (response.data.status === 'failed') { + cleanup(); + setErrorMessage(response.data.error || 'Download failed.'); + trackOnboardingSetupFailed(LOCAL_PROVIDER, response.data.error || 'download_failed'); + setPhase('error'); + } else if (response.data.status === 'cancelled') { + cleanup(); + setPhase('select'); + } + } + } catch { + cleanup(); + setErrorMessage('Lost connection to download. Please try again.'); + trackOnboardingSetupFailed(LOCAL_PROVIDER, 'progress_poll_failed'); + setPhase('error'); + } + }, 500); + }; + + const handleCancelDownload = async () => { + if (phase === 'downloading' && selectedModelId) { + cleanup(); + try { + await cancelLocalModelDownload({ path: { model_id: selectedModelId } }); + } catch { + // best-effort + } + setDownloadProgress(null); + setPhase('select'); + } + }; + + const handlePrimaryAction = async () => { + if (!selectedModelId) return; + const model = models.find((m) => m.id === selectedModelId); + if (!model) return; + if (model.status.state === 'Downloaded') { + finishSetup(model.id); + } else { + await startDownload(model.id); + } + }; + + const recommended = models.find((m) => m.recommended); + const otherModels = models.filter((m) => m.id !== recommended?.id); + const selectedModel = models.find((m) => m.id === selectedModelId); + + if (phase === 'loading') { + return ( +
+
+

Checking available models...

+
+ ); + } + + return ( +
+
+ {phase === 'error' && ( +
+
+

{errorMessage}

+
+ +
+ )} + + {phase === 'select' && ( +
+ {recommended && ( +
setSelectedModelId(recommended.id)} + className={`relative w-full p-4 border rounded-lg cursor-pointer transition-all duration-200 ${ + selectedModelId === recommended.id + ? 'border-blue-500 bg-blue-500/5' + : 'border-border-subtle hover:border-border-default' + }`} + > +
+ + Best for your machine + +
+
+ setSelectedModelId(recommended.id)} + className="cursor-pointer flex-shrink-0 mt-1" + /> +
+
+ + {recommended.id} + + {recommended.status.state === 'Downloaded' && ( + + Ready + + )} +
+

+ {formatSize(recommended.size_bytes)} +

+
+
+
+ )} + + {otherModels.length > 0 && ( +
+ + + {showAllModels && ( +
+ {otherModels.map((model) => ( +
setSelectedModelId(model.id)} + className={`w-full p-4 border rounded-lg cursor-pointer transition-all duration-200 ${ + selectedModelId === model.id + ? 'border-blue-500 bg-blue-500/5' + : 'border-border-subtle hover:border-border-default' + }`} + > +
+ setSelectedModelId(model.id)} + className="cursor-pointer flex-shrink-0 mt-0.5" + /> +
+
+ + {model.id} + + + {formatSize(model.size_bytes)} + + {model.status.state === 'Downloaded' && ( + + Ready + + )} +
+
+
+
+ ))} +
+ )} +
+ )} + + + + {onBack && ( + + )} +
+ )} + + {phase === 'downloading' && selectedModel && ( +
+
+

+ Downloading {selectedModel.id} +

+ + {downloadProgress ? ( +
+
+
+
+ +
+ + {formatBytes(downloadProgress.bytes_downloaded)} of{' '} + {formatBytes(downloadProgress.total_bytes)} + + {downloadProgress.progress_percent.toFixed(0)}% +
+ +
+ {downloadProgress.speed_bps ? ( + {formatBytes(downloadProgress.speed_bps)}/s + ) : ( + + )} + {downloadProgress.eta_seconds != null && downloadProgress.eta_seconds > 0 && ( + + ~ + {downloadProgress.eta_seconds < 60 + ? `${Math.round(downloadProgress.eta_seconds)}s` + : `${Math.round(downloadProgress.eta_seconds / 60)}m`}{' '} + remaining + + )} +
+
+ ) : ( +
+
+ Starting download... +
+ )} +
+ + +
+ )} +
+
+

+ Local models keep everything on your machine for full privacy. Performance and context + window size may vary compared to cloud providers depending on your hardware and model + size. +

+
+
+ ); +} diff --git a/ui/desktop/src/components/onboarding/OnboardingGuard.tsx b/ui/desktop/src/components/onboarding/OnboardingGuard.tsx new file mode 100644 index 000000000000..89a44b85cb90 --- /dev/null +++ b/ui/desktop/src/components/onboarding/OnboardingGuard.tsx @@ -0,0 +1,135 @@ +import { useEffect, useRef, useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { useConfig } from '../ConfigContext'; +import { useModelAndProvider } from '../ModelAndProviderContext'; +import { Goose } from '../icons'; +import ProviderSelector from './ProviderSelector'; +import OnboardingSuccess from './OnboardingSuccess'; +import { + trackOnboardingStarted, + trackOnboardingCompleted, + trackOnboardingProviderSelected, + trackTelemetryPreference, + setTelemetryEnabled as setAnalyticsTelemetryEnabled, +} from '../../utils/analytics'; + +const TELEMETRY_CONFIG_KEY = 'GOOSE_TELEMETRY_ENABLED'; + +interface OnboardingGuardProps { + children: React.ReactNode; +} + +export default function OnboardingGuard({ children }: OnboardingGuardProps) { + const navigate = useNavigate(); + const { read, upsert, getProviders } = useConfig(); + const { refreshCurrentModelAndProvider } = useModelAndProvider(); + + const [isCheckingProvider, setIsCheckingProvider] = useState(true); + const [hasProvider, setHasProvider] = useState(false); + const [hasSelection, setHasSelection] = useState(false); + const [configuredProvider, setConfiguredProvider] = useState(null); + const [configuredProviderDisplayName, setConfiguredProviderDisplayName] = useState( + null + ); + const [configuredModel, setConfiguredModel] = useState(null); + const hasTrackedOnboardingStart = useRef(false); + + useEffect(() => { + const checkProvider = async () => { + try { + const provider = ((await read('GOOSE_PROVIDER', false)) as string) || ''; + setHasProvider(provider.trim() !== ''); + } catch (error) { + console.error('Error checking provider:', error); + setHasProvider(false); + } finally { + setIsCheckingProvider(false); + } + }; + checkProvider(); + }, [read]); + + useEffect(() => { + if (!isCheckingProvider && !hasProvider && !hasTrackedOnboardingStart.current) { + trackOnboardingStarted(); + hasTrackedOnboardingStart.current = true; + } + }, [isCheckingProvider, hasProvider]); + + const handleConfigured = async (providerName: string, modelId?: string) => { + trackOnboardingProviderSelected({ provider: providerName }); + await upsert('GOOSE_PROVIDER', providerName, false); + const providers = await getProviders(true); + const matchedProvider = providers.find((p) => p.name === providerName); + if (modelId) { + await upsert('GOOSE_MODEL', modelId, false); + setConfiguredModel(modelId); + } else if (matchedProvider) { + await upsert('GOOSE_MODEL', matchedProvider.metadata.default_model, false); + setConfiguredModel(matchedProvider.metadata.default_model); + } + await refreshCurrentModelAndProvider(); + setConfiguredProvider(providerName); + setConfiguredProviderDisplayName(matchedProvider?.metadata.display_name || providerName); + }; + + const finishOnboarding = async (telemetryEnabled: boolean) => { + try { + await upsert(TELEMETRY_CONFIG_KEY, telemetryEnabled, false); + } catch (error) { + console.error('Failed to save telemetry preference:', error); + } + trackTelemetryPreference(telemetryEnabled, 'onboarding'); + if (configuredProvider) { + trackOnboardingCompleted(configuredProvider, configuredModel ?? undefined); + } + if (!telemetryEnabled) { + setAnalyticsTelemetryEnabled(false); + } + navigate('/', { replace: true }); + setHasProvider(true); + }; + + if (isCheckingProvider) { + return null; + } + + if (hasProvider) { + return <>{children}; + } + + if (configuredProviderDisplayName) { + return ( + + ); + } + + return ( +
+
+
+
+
+
+ +
+

Welcome to goose

+

+ Your local AI agent. Connect an AI model provider to get started. +

+
+ + setHasSelection(true)} + /> +
+
+
+
+ ); +} diff --git a/ui/desktop/src/components/onboarding/OnboardingSuccess.tsx b/ui/desktop/src/components/onboarding/OnboardingSuccess.tsx new file mode 100644 index 000000000000..6ba09891d78e --- /dev/null +++ b/ui/desktop/src/components/onboarding/OnboardingSuccess.tsx @@ -0,0 +1,78 @@ +import { useState } from 'react'; +import { Button } from '../ui/button'; +import PrivacyInfoModal from './PrivacyInfoModal'; + +const LOCAL_PROVIDER = 'local'; + +interface OnboardingSuccessProps { + providerName: string; + onFinish: (telemetryEnabled: boolean) => void; +} + +export default function OnboardingSuccess({ providerName, onFinish }: OnboardingSuccessProps) { + const [showPrivacyInfo, setShowPrivacyInfo] = useState(false); + const [telemetryOptIn, setTelemetryOptIn] = useState(true); + + return ( +
+
+
+
+
+
+ + + +
+

+ {providerName === LOCAL_PROVIDER + ? 'Local model ready' + : `Connected to ${providerName}`} +

+

You're all set to start using goose.

+
+ +
+

Privacy

+

+ Anonymous usage data helps improve goose. We never collect your conversations, code, + or personal data.{' '} + +

+ +
+ + +
+
+
+ + setShowPrivacyInfo(false)} /> +
+ ); +} diff --git a/ui/desktop/src/components/onboarding/PrivacyInfoModal.tsx b/ui/desktop/src/components/onboarding/PrivacyInfoModal.tsx new file mode 100644 index 000000000000..5dd3ee208a61 --- /dev/null +++ b/ui/desktop/src/components/onboarding/PrivacyInfoModal.tsx @@ -0,0 +1,38 @@ +import { Dialog, DialogContent, DialogHeader, DialogTitle } from '../ui/dialog'; + +interface PrivacyInfoModalProps { + isOpen: boolean; + onClose: () => void; +} + +export default function PrivacyInfoModal({ isOpen, onClose }: PrivacyInfoModalProps) { + return ( + !open && onClose()}> + + + Privacy details + + +
+

+ Anonymous usage data helps us understand how goose is used and identify areas for + improvement. +

+

What we collect:

+
    +
  • Operating system, version, and architecture
  • +
  • goose version and install method
  • +
  • Provider and model used
  • +
  • Extensions and tool usage counts (names only)
  • +
  • Session metrics (duration, interaction count, token usage)
  • +
  • Error types (e.g., "rate_limit", "auth" - no details)
  • +
+

+ We never collect your conversations, code, tool arguments, error messages, or any + personal data. You can change this setting anytime in Settings. +

+
+
+
+ ); +} diff --git a/ui/desktop/src/components/onboarding/ProviderConfigForm.tsx b/ui/desktop/src/components/onboarding/ProviderConfigForm.tsx new file mode 100644 index 000000000000..b01b382d3519 --- /dev/null +++ b/ui/desktop/src/components/onboarding/ProviderConfigForm.tsx @@ -0,0 +1,216 @@ +import { useState } from 'react'; +import { configureProviderOauth, ProviderDetails } from '../../api'; +import { useConfig } from '../ConfigContext'; +import DefaultProviderSetupForm, { + ConfigInput, +} from '../settings/providers/modal/subcomponents/forms/DefaultProviderSetupForm'; +import { providerConfigSubmitHandler } from '../settings/providers/modal/subcomponents/handlers/DefaultSubmitHandler'; +import ProviderLogo from '../settings/providers/modal/subcomponents/ProviderLogo'; +import { SecureStorageNotice } from '../settings/providers/modal/subcomponents/SecureStorageNotice'; +import { Button } from '../ui/button'; +import { LogIn, ChevronRight } from 'lucide-react'; + +function parseLinks(text: string) { + return text.split(/(https?:\/\/[^\s]+)/g).map((part, i) => + /^https?:\/\//.test(part) ? ( + { + e.preventDefault(); + window.electron.openExternal(part); + }} + className="underline hover:text-text-default cursor-pointer" + > + {part} + + ) : ( + part + ) + ); +} + +function OAuthForm({ + provider, + onConfigured, + onError, +}: { + provider: ProviderDetails; + onConfigured: (name: string) => void; + onError: (msg: string) => void; +}) { + const [isLoading, setIsLoading] = useState(false); + + const handleLogin = async () => { + setIsLoading(true); + try { + await configureProviderOauth({ + path: { name: provider.name }, + throwOnError: true, + }); + onConfigured(provider.name); + } catch (err) { + onError(`Sign-in failed: ${err instanceof Error ? err.message : String(err)}`); + } finally { + setIsLoading(false); + } + }; + + return ( +
+ +

+ A browser window will open for you to complete the login. +

+
+ ); +} + +function ApiKeyForm({ + provider, + onConfigured, + onError, +}: { + provider: ProviderDetails; + onConfigured: (name: string) => void; + onError: (msg: string) => void; +}) { + const { upsert } = useConfig(); + const [configValues, setConfigValues] = useState>({}); + const [validationErrors, setValidationErrors] = useState>({}); + const [isSubmitting, setIsSubmitting] = useState(false); + const [showSetupHelp, setShowSetupHelp] = useState(false); + const setupSteps = provider.metadata.setup_steps; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setValidationErrors({}); + + const parameters = provider.metadata.config_keys || []; + const errors: Record = {}; + parameters.forEach((param) => { + if ( + param.required && + !configValues[param.name]?.value && + !configValues[param.name]?.serverValue + ) { + errors[param.name] = `${param.name} is required`; + } + }); + + if (Object.keys(errors).length > 0) { + setValidationErrors(errors); + return; + } + + const toSubmit = Object.fromEntries( + Object.entries(configValues) + .filter(([, entry]) => !!entry.value) + .map(([k, entry]) => [k, entry.value || '']) + ); + + setIsSubmitting(true); + try { + await providerConfigSubmitHandler(upsert, provider, toSubmit); + onConfigured(provider.name); + } catch (err) { + const msg = + err instanceof Error + ? err.message + : typeof err === 'object' && err !== null && 'message' in err + ? String((err as Record).message) + : JSON.stringify(err); + onError(msg); + } finally { + setIsSubmitting(false); + } + }; + + return ( +
+ + {provider.metadata.config_keys.some((k) => k.required && k.secret) && } + {setupSteps && setupSteps.length > 0 && ( +
+ + {showSetupHelp && ( +
    + {setupSteps.map((step, i) => ( +
  1. {parseLinks(step)}
  2. + ))} +
+ )} +
+ )} +
+ +
+ + ); +} + +interface ProviderConfigFormProps { + provider: ProviderDetails; + onConfigured: (providerName: string) => void; +} + +export default function ProviderConfigForm({ provider, onConfigured }: ProviderConfigFormProps) { + const [error, setError] = useState(null); + + const isOAuthProvider = provider.metadata.config_keys.some((key) => key.oauth_flow); + + const renderForm = () => { + if (isOAuthProvider) { + return ; + } + return ; + }; + + return ( +
+
+
+ +
+

{provider.metadata.display_name}

+

{provider.metadata.description}

+
+
+ + {renderForm()} + + {error && ( +
+ {error} +
+ )} +
+
+ ); +} diff --git a/ui/desktop/src/components/onboarding/ProviderSelector.tsx b/ui/desktop/src/components/onboarding/ProviderSelector.tsx new file mode 100644 index 000000000000..06376d58bae2 --- /dev/null +++ b/ui/desktop/src/components/onboarding/ProviderSelector.tsx @@ -0,0 +1,196 @@ +import { useState, useEffect, useMemo } from 'react'; +import { + providers as fetchProviders, + createCustomProvider, + ProviderDetails, + UpdateCustomProviderRequest, +} from '../../api'; +import { Select } from '../ui/Select'; +import ProviderConfigForm from './ProviderConfigForm'; +import FreeOptionCards from './FreeOptionCards'; +import CustomProviderForm from '../settings/providers/modal/subcomponents/forms/CustomProviderForm'; +import { Dialog, DialogContent, DialogHeader, DialogTitle } from '../ui/dialog'; +import { Gift, Key, Plus } from 'lucide-react'; + +const FREE_OPTIONS = 'free-options' as const; +const OWN_PROVIDER = 'own-provider' as const; + +type SelectedPath = typeof FREE_OPTIONS | typeof OWN_PROVIDER | null; + +interface ProviderOption { + value: string; + label: string; + provider: ProviderDetails; +} + +interface ProviderSelectorProps { + onConfigured: (providerName: string, modelId?: string) => void; + onFirstSelection?: () => void; +} + +export default function ProviderSelector({ + onConfigured, + onFirstSelection, +}: ProviderSelectorProps) { + const [providerList, setProviderList] = useState([]); + const [selectedOption, setSelectedOption] = useState(null); + const [selectedPath, setSelectedPath] = useState(null); + const [showCustomModal, setShowCustomModal] = useState(false); + + useEffect(() => { + const load = async () => { + try { + const response = await fetchProviders({ throwOnError: true }); + if (response.data) { + const list = Array.isArray(response.data) + ? response.data + : (response.data as { providers: ProviderDetails[] }).providers || []; + setProviderList(list); + } + } catch (err) { + console.error('Failed to fetch providers:', err); + } + }; + load(); + }, []); + + const options: ProviderOption[] = useMemo(() => { + return [...providerList] + .sort((a, b) => { + const aPreferred = a.provider_type === 'Preferred' ? 0 : 1; + const bPreferred = b.provider_type === 'Preferred' ? 0 : 1; + if (aPreferred !== bPreferred) return aPreferred - bPreferred; + return a.metadata.display_name.localeCompare(b.metadata.display_name); + }) + .map((provider) => ({ + value: provider.name, + label: provider.metadata.display_name, + provider, + })); + }, [providerList]); + + const fuzzyFilterOption = (option: { label: string; value: string }, inputValue: string) => { + const normalize = (s: string) => s.toLowerCase().replace(/[\s_-]/g, ''); + return ( + normalize(option.label).includes(normalize(inputValue)) || + normalize(option.value).includes(normalize(inputValue)) + ); + }; + + const handleFreeCreditClick = () => { + setSelectedPath(FREE_OPTIONS); + setSelectedOption(null); + onFirstSelection?.(); + }; + + const handleOwnProviderClick = () => { + setSelectedPath(OWN_PROVIDER); + onFirstSelection?.(); + }; + + const handleProviderSelect = (option: ProviderOption | null) => { + setSelectedOption(option); + if (option) onFirstSelection?.(); + }; + + const handleCreateCustomProvider = async (data: UpdateCustomProviderRequest) => { + const result = await createCustomProvider({ body: data, throwOnError: true }); + setShowCustomModal(false); + if (result.data?.provider_name) { + onConfigured(result.data.provider_name); + } + }; + + const selectedProvider = selectedOption?.provider ?? null; + + return ( +
+
+
+ + + Use Free/Local Providers + +

+ Use a local model or a provider with free credits +

+
+ +
+ + + Connect to a Provider + +

Connect OpenAI, Anthropic, Google, etc

+
+
+ + {selectedPath === FREE_OPTIONS && ( +
+ +
+ )} + + {selectedPath === OWN_PROVIDER && ( +
+
+