diff --git a/crates/goose-server/src/openapi.rs b/crates/goose-server/src/openapi.rs index 384fad28e1dc..1caa9ced49ce 100644 --- a/crates/goose-server/src/openapi.rs +++ b/crates/goose-server/src/openapi.rs @@ -7,7 +7,7 @@ use goose::conversation::Conversation; use goose::model::ModelConfig; use goose::permission::permission_confirmation::PrincipalType; use goose::providers::base::{ConfigKey, ModelInfo, ProviderMetadata, ProviderType}; -use goose::session::{Session, SessionInsights, SessionType}; +use goose::session::{Session, SessionInsights, SessionType, SystemInfo}; use rmcp::model::{ Annotations, Content, EmbeddedResource, Icon, ImageContent, JsonObject, RawAudioContent, RawEmbeddedResource, RawImageContent, RawResource, RawTextContent, ResourceContents, Role, @@ -327,6 +327,7 @@ derive_utoipa!(Icon as IconSchema); #[openapi( paths( super::routes::status::status, + super::routes::status::system_info, super::routes::status::diagnostics, super::routes::mcp_ui_proxy::mcp_ui_proxy, super::routes::config_management::backup_config, @@ -483,6 +484,7 @@ derive_utoipa!(Icon as IconSchema); Session, SessionInsights, SessionType, + SystemInfo, Conversation, IconSchema, goose::session::extension_data::ExtensionData, diff --git a/crates/goose-server/src/routes/status.rs b/crates/goose-server/src/routes/status.rs index 110d4c7ad87c..b9844dcc6caa 100644 --- a/crates/goose-server/src/routes/status.rs +++ b/crates/goose-server/src/routes/status.rs @@ -1,8 +1,8 @@ use axum::body::Body; use axum::http::HeaderValue; use axum::response::IntoResponse; -use axum::{extract::Path, http::StatusCode, routing::get, Router}; -use goose::session::generate_diagnostics; +use axum::{extract::Path, http::StatusCode, routing::get, Json, Router}; +use goose::session::{generate_diagnostics, get_system_info, SystemInfo}; #[utoipa::path(get, path = "/status", responses( @@ -13,6 +13,15 @@ async fn status() -> String { "ok".to_string() } +#[utoipa::path(get, path = "/system_info", + responses( + (status = 200, description = "System information", body = SystemInfo), + ) +)] +async fn system_info() -> Json { + Json(get_system_info()) +} + #[utoipa::path(get, path = "/diagnostics/{session_id}", responses( (status = 200, description = "Diagnostics zip file", content_type = "application/zip", body = Vec), @@ -42,5 +51,6 @@ async fn diagnostics(Path(session_id): Path) -> impl IntoResponse { pub fn routes() -> Router { Router::new() .route("/status", get(status)) + .route("/system_info", get(system_info)) .route("/diagnostics/{session_id}", get(diagnostics)) } diff --git a/crates/goose/src/session/diagnostics.rs b/crates/goose/src/session/diagnostics.rs index 4c311eb2860e..47f1bdda898d 100644 --- a/crates/goose/src/session/diagnostics.rs +++ b/crates/goose/src/session/diagnostics.rs @@ -1,30 +1,81 @@ +use crate::config::base::Config; +use crate::config::extensions::get_enabled_extensions; use crate::config::paths::Paths; use crate::providers::utils::LOGS_TO_KEEP; use crate::session::SessionManager; +use serde::{Deserialize, Serialize}; use std::fs::{self}; use std::io::Cursor; use std::io::Write; +use utoipa::ToSchema; use zip::write::FileOptions; use zip::ZipWriter; +#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] +pub struct SystemInfo { + pub app_version: String, + pub os: String, + pub os_version: String, + pub architecture: String, + pub provider: Option, + pub model: Option, + pub enabled_extensions: Vec, +} + +impl SystemInfo { + pub fn collect() -> Self { + let config = Config::global(); + let provider = config.get_goose_provider().ok(); + let model = config.get_goose_model().ok(); + let enabled_extensions = get_enabled_extensions() + .into_iter() + .map(|ext| ext.name().to_string()) + .collect(); + + Self { + app_version: env!("CARGO_PKG_VERSION").to_string(), + os: std::env::consts::OS.to_string(), + os_version: sys_info::os_release().unwrap_or_else(|_| "unknown".to_string()), + architecture: std::env::consts::ARCH.to_string(), + provider, + model, + enabled_extensions, + } + } + + pub fn to_text(&self) -> String { + format!( + "App Version: {}\n\ + OS: {}\n\ + OS Version: {}\n\ + Architecture: {}\n\ + Provider: {}\n\ + Model: {}\n\ + Enabled Extensions: {}\n\ + Timestamp: {}\n", + self.app_version, + self.os, + self.os_version, + self.architecture, + self.provider.as_deref().unwrap_or("unknown"), + self.model.as_deref().unwrap_or("unknown"), + self.enabled_extensions.join(", "), + chrono::Utc::now().to_rfc3339() + ) + } +} + +pub fn get_system_info() -> SystemInfo { + SystemInfo::collect() +} + pub async fn generate_diagnostics(session_id: &str) -> anyhow::Result> { let logs_dir = Paths::in_state_dir("logs"); let config_dir = Paths::config_dir(); let config_path = config_dir.join("config.yaml"); let data_dir = Paths::data_dir(); - let system_info = format!( - "App Version: {}\n\ - OS: {}\n\ - OS Version: {}\n\ - Architecture: {}\n\ - Timestamp: {}\n", - env!("CARGO_PKG_VERSION"), - std::env::consts::OS, - sys_info::os_release().unwrap_or_else(|_| "unknown".to_string()), - std::env::consts::ARCH, - chrono::Utc::now().to_rfc3339() - ); + let system_info = SystemInfo::collect(); let mut buffer = Vec::new(); { @@ -55,7 +106,7 @@ pub async fn generate_diagnostics(session_id: &str) -> anyhow::Result> { } zip.start_file("system.txt", options)?; - zip.write_all(system_info.as_bytes())?; + zip.write_all(system_info.to_text().as_bytes())?; let schedule_json = data_dir.join("schedule.json"); if schedule_json.exists() { diff --git a/crates/goose/src/session/mod.rs b/crates/goose/src/session/mod.rs index b8b7c8d5a28c..28a1768c1701 100644 --- a/crates/goose/src/session/mod.rs +++ b/crates/goose/src/session/mod.rs @@ -4,6 +4,6 @@ pub mod extension_data; mod legacy; pub mod session_manager; -pub use diagnostics::generate_diagnostics; +pub use diagnostics::{generate_diagnostics, get_system_info, SystemInfo}; pub use extension_data::{EnabledExtensionsState, ExtensionData, ExtensionState, TodoState}; pub use session_manager::{Session, SessionInsights, SessionManager, SessionType}; diff --git a/ui/desktop/openapi.json b/ui/desktop/openapi.json index f7091b80cc63..8e0d67ce9feb 100644 --- a/ui/desktop/openapi.json +++ b/ui/desktop/openapi.json @@ -2531,6 +2531,26 @@ } } }, + "/system_info": { + "get": { + "tags": [ + "super::routes::status" + ], + "operationId": "system_info", + "responses": { + "200": { + "description": "System information", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SystemInfo" + } + } + } + } + } + } + }, "/telemetry/event": { "post": { "tags": [ @@ -5711,6 +5731,44 @@ "propertyName": "type" } }, + "SystemInfo": { + "type": "object", + "required": [ + "app_version", + "os", + "os_version", + "architecture", + "enabled_extensions" + ], + "properties": { + "app_version": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "enabled_extensions": { + "type": "array", + "items": { + "type": "string" + } + }, + "model": { + "type": "string", + "nullable": true + }, + "os": { + "type": "string" + }, + "os_version": { + "type": "string" + }, + "provider": { + "type": "string", + "nullable": true + } + } + }, "SystemNotificationContent": { "type": "object", "required": [ diff --git a/ui/desktop/src/api/sdk.gen.ts b/ui/desktop/src/api/sdk.gen.ts index b5325697e506..05c10fec0ea0 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, CheckProviderData, ConfirmToolActionData, ConfirmToolActionErrors, ConfirmToolActionResponses, CreateCustomProviderData, CreateCustomProviderErrors, CreateCustomProviderResponses, CreateRecipeData, CreateRecipeErrors, CreateRecipeResponses, CreateScheduleData, CreateScheduleErrors, CreateScheduleResponses, DecodeRecipeData, DecodeRecipeErrors, DecodeRecipeResponses, DeleteRecipeData, DeleteRecipeErrors, DeleteRecipeResponses, DeleteScheduleData, DeleteScheduleErrors, DeleteScheduleResponses, DeleteSessionData, DeleteSessionErrors, DeleteSessionResponses, DetectProviderData, DetectProviderErrors, DetectProviderResponses, DiagnosticsData, DiagnosticsErrors, DiagnosticsResponses, EditMessageData, EditMessageErrors, EditMessageResponses, EncodeRecipeData, EncodeRecipeErrors, EncodeRecipeResponses, ExportSessionData, ExportSessionErrors, ExportSessionResponses, GetCustomProviderData, GetCustomProviderErrors, GetCustomProviderResponses, GetExtensionsData, GetExtensionsErrors, GetExtensionsResponses, GetPricingData, GetPricingResponses, GetProviderModelsData, GetProviderModelsErrors, GetProviderModelsResponses, GetSessionData, GetSessionErrors, GetSessionExtensionsData, GetSessionExtensionsErrors, GetSessionExtensionsResponses, GetSessionInsightsData, GetSessionInsightsErrors, GetSessionInsightsResponses, GetSessionResponses, GetSlashCommandsData, GetSlashCommandsResponses, GetToolsData, GetToolsErrors, GetToolsResponses, GetTunnelStatusData, GetTunnelStatusResponses, ImportSessionData, ImportSessionErrors, ImportSessionResponses, InitConfigData, InitConfigErrors, InitConfigResponses, InspectRunningJobData, InspectRunningJobErrors, InspectRunningJobResponses, KillRunningJobData, KillRunningJobResponses, 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, RestartAgentData, RestartAgentErrors, RestartAgentResponses, ResumeAgentData, ResumeAgentErrors, ResumeAgentResponses, RunNowHandlerData, RunNowHandlerErrors, RunNowHandlerResponses, SaveRecipeData, SaveRecipeErrors, SaveRecipeResponses, ScanRecipeData, ScanRecipeResponses, ScheduleRecipeData, ScheduleRecipeErrors, ScheduleRecipeResponses, SendTelemetryEventData, SendTelemetryEventResponses, SessionsHandlerData, SessionsHandlerErrors, SessionsHandlerResponses, SetConfigProviderData, SetRecipeSlashCommandData, SetRecipeSlashCommandErrors, SetRecipeSlashCommandResponses, StartAgentData, StartAgentErrors, StartAgentResponses, StartOpenrouterSetupData, StartOpenrouterSetupResponses, StartTetrateSetupData, StartTetrateSetupResponses, StartTunnelData, StartTunnelErrors, StartTunnelResponses, StatusData, StatusResponses, StopTunnelData, StopTunnelErrors, StopTunnelResponses, UnpauseScheduleData, UnpauseScheduleErrors, UnpauseScheduleResponses, UpdateAgentProviderData, UpdateAgentProviderErrors, UpdateAgentProviderResponses, UpdateCustomProviderData, UpdateCustomProviderErrors, UpdateCustomProviderResponses, UpdateFromSessionData, UpdateFromSessionErrors, UpdateFromSessionResponses, UpdateScheduleData, UpdateScheduleErrors, UpdateScheduleResponses, UpdateSessionNameData, UpdateSessionNameErrors, UpdateSessionNameResponses, 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, CheckProviderData, ConfirmToolActionData, ConfirmToolActionErrors, ConfirmToolActionResponses, CreateCustomProviderData, CreateCustomProviderErrors, CreateCustomProviderResponses, CreateRecipeData, CreateRecipeErrors, CreateRecipeResponses, CreateScheduleData, CreateScheduleErrors, CreateScheduleResponses, DecodeRecipeData, DecodeRecipeErrors, DecodeRecipeResponses, DeleteRecipeData, DeleteRecipeErrors, DeleteRecipeResponses, DeleteScheduleData, DeleteScheduleErrors, DeleteScheduleResponses, DeleteSessionData, DeleteSessionErrors, DeleteSessionResponses, DetectProviderData, DetectProviderErrors, DetectProviderResponses, DiagnosticsData, DiagnosticsErrors, DiagnosticsResponses, EditMessageData, EditMessageErrors, EditMessageResponses, EncodeRecipeData, EncodeRecipeErrors, EncodeRecipeResponses, ExportSessionData, ExportSessionErrors, ExportSessionResponses, GetCustomProviderData, GetCustomProviderErrors, GetCustomProviderResponses, GetExtensionsData, GetExtensionsErrors, GetExtensionsResponses, GetPricingData, GetPricingResponses, GetProviderModelsData, GetProviderModelsErrors, GetProviderModelsResponses, GetSessionData, GetSessionErrors, GetSessionExtensionsData, GetSessionExtensionsErrors, GetSessionExtensionsResponses, GetSessionInsightsData, GetSessionInsightsErrors, GetSessionInsightsResponses, GetSessionResponses, GetSlashCommandsData, GetSlashCommandsResponses, GetToolsData, GetToolsErrors, GetToolsResponses, GetTunnelStatusData, GetTunnelStatusResponses, ImportSessionData, ImportSessionErrors, ImportSessionResponses, InitConfigData, InitConfigErrors, InitConfigResponses, InspectRunningJobData, InspectRunningJobErrors, InspectRunningJobResponses, KillRunningJobData, KillRunningJobResponses, 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, RestartAgentData, RestartAgentErrors, RestartAgentResponses, ResumeAgentData, ResumeAgentErrors, ResumeAgentResponses, RunNowHandlerData, RunNowHandlerErrors, RunNowHandlerResponses, SaveRecipeData, SaveRecipeErrors, SaveRecipeResponses, ScanRecipeData, ScanRecipeResponses, ScheduleRecipeData, ScheduleRecipeErrors, ScheduleRecipeResponses, SendTelemetryEventData, SendTelemetryEventResponses, SessionsHandlerData, SessionsHandlerErrors, SessionsHandlerResponses, SetConfigProviderData, SetRecipeSlashCommandData, SetRecipeSlashCommandErrors, SetRecipeSlashCommandResponses, StartAgentData, StartAgentErrors, StartAgentResponses, StartOpenrouterSetupData, StartOpenrouterSetupResponses, StartTetrateSetupData, StartTetrateSetupResponses, StartTunnelData, StartTunnelErrors, StartTunnelResponses, StatusData, StatusResponses, StopTunnelData, StopTunnelErrors, StopTunnelResponses, SystemInfoData, SystemInfoResponses, UnpauseScheduleData, UnpauseScheduleErrors, UnpauseScheduleResponses, UpdateAgentProviderData, UpdateAgentProviderErrors, UpdateAgentProviderResponses, UpdateCustomProviderData, UpdateCustomProviderErrors, UpdateCustomProviderResponses, UpdateFromSessionData, UpdateFromSessionErrors, UpdateFromSessionResponses, UpdateScheduleData, UpdateScheduleErrors, UpdateScheduleResponses, UpdateSessionNameData, UpdateSessionNameErrors, UpdateSessionNameResponses, UpdateSessionUserRecipeValuesData, UpdateSessionUserRecipeValuesErrors, UpdateSessionUserRecipeValuesResponses, UpdateWorkingDirData, UpdateWorkingDirErrors, UpdateWorkingDirResponses, UpsertConfigData, UpsertConfigErrors, UpsertConfigResponses, UpsertPermissionsData, UpsertPermissionsErrors, UpsertPermissionsResponses, ValidateConfigData, ValidateConfigErrors, ValidateConfigResponses } from './types.gen'; export type Options = Options2 & { /** @@ -435,6 +435,8 @@ export const updateSessionUserRecipeValues = (options?: Options) => (options?.client ?? client).get({ url: '/status', ...options }); +export const systemInfo = (options?: Options) => (options?.client ?? client).get({ url: '/system_info', ...options }); + export const sendTelemetryEvent = (options: Options) => (options.client ?? client).post({ url: '/telemetry/event', ...options, diff --git a/ui/desktop/src/api/types.gen.ts b/ui/desktop/src/api/types.gen.ts index e6f6d6745585..e100560eae7c 100644 --- a/ui/desktop/src/api/types.gen.ts +++ b/ui/desktop/src/api/types.gen.ts @@ -988,6 +988,16 @@ export type SuccessCheck = { type: 'Shell'; }; +export type SystemInfo = { + app_version: string; + architecture: string; + enabled_extensions: Array; + model?: string | null; + os: string; + os_version: string; + provider?: string | null; +}; + export type SystemNotificationContent = { msg: string; notificationType: SystemNotificationType; @@ -3137,6 +3147,22 @@ export type StatusResponses = { export type StatusResponse = StatusResponses[keyof StatusResponses]; +export type SystemInfoData = { + body?: never; + path?: never; + query?: never; + url: '/system_info'; +}; + +export type SystemInfoResponses = { + /** + * System information + */ + 200: SystemInfo; +}; + +export type SystemInfoResponse = SystemInfoResponses[keyof SystemInfoResponses]; + export type SendTelemetryEventData = { body: TelemetryEventRequest; path?: never; diff --git a/ui/desktop/src/components/ChatInput.tsx b/ui/desktop/src/components/ChatInput.tsx index ec3e63d7f0b9..20e3cd87db5b 100644 --- a/ui/desktop/src/components/ChatInput.tsx +++ b/ui/desktop/src/components/ChatInput.tsx @@ -26,7 +26,7 @@ import { DroppedFile, useFileDrop } from '../hooks/useFileDrop'; import { Recipe } from '../recipe'; import MessageQueue from './MessageQueue'; import { detectInterruption } from '../utils/interruptionDetector'; -import { DiagnosticsModal } from './ui/DownloadDiagnostics'; +import { DiagnosticsModal } from './ui/Diagnostics'; import { getSession, Message } from '../api'; import CreateRecipeFromSessionModal from './recipes/CreateRecipeFromSessionModal'; import CreateEditRecipeModal from './recipes/CreateEditRecipeModal'; diff --git a/ui/desktop/src/components/ui/Diagnostics.tsx b/ui/desktop/src/components/ui/Diagnostics.tsx new file mode 100644 index 000000000000..f937be404b54 --- /dev/null +++ b/ui/desktop/src/components/ui/Diagnostics.tsx @@ -0,0 +1,191 @@ +import React, { useState } from 'react'; +import { AlertTriangle, Download, Github } from 'lucide-react'; +import { Button } from './button'; +import { toastError } from '../../toasts'; +import { diagnostics, systemInfo } from '../../api'; + +interface DiagnosticsModalProps { + isOpen: boolean; + onClose: () => void; + sessionId: string; +} + +export const DiagnosticsModal: React.FC = ({ + isOpen, + onClose, + sessionId, +}) => { + const [isDownloading, setIsDownloading] = useState(false); + const [isFilingBug, setIsFilingBug] = useState(false); + + const handleDownload = async () => { + setIsDownloading(true); + + try { + const response = await diagnostics({ + path: { session_id: sessionId }, + throwOnError: true, + }); + + const blob = new Blob([response.data], { type: 'application/zip' }); + const url = window.URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = `diagnostics_${sessionId}.zip`; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + window.URL.revokeObjectURL(url); + + onClose(); + } catch { + toastError({ + title: 'Diagnostics Error', + msg: 'Failed to download diagnostics', + }); + } finally { + setIsDownloading(false); + } + }; + + const handleFileGitHubIssue = async () => { + setIsFilingBug(true); + + try { + const response = await systemInfo({ throwOnError: true }); + const info = response.data; + + const providerModel = + info.provider && info.model + ? `${info.provider} – ${info.model}` + : info.provider || info.model || '[e.g. Google – gemini-1.5-pro]'; + + const extensions = + info.enabled_extensions.length > 0 + ? info.enabled_extensions.join(', ') + : '[e.g. Computer Controller, Figma]'; + + const body = `**Describe the bug** + +💡 Before filing, please check common issues: +https://block.github.io/goose/docs/troubleshooting + +📦 To help us debug faster, attach your **diagnostics zip** if possible. +👉 How to capture it: https://block.github.io/goose/docs/troubleshooting/diagnostics-and-reporting/ + +A clear and concise description of what the bug is. + +--- + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +--- + +**Expected behavior** +A clear and concise description of what you expected to happen. + +--- + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +--- + +**Please provide the following information** +- **OS & Arch:** ${info.os} ${info.os_version} ${info.architecture} +- **Interface:** UI +- **Version:** ${info.app_version} +- **Extensions enabled:** ${extensions} +- **Provider & Model:** ${providerModel} + +--- + +**Additional context** +Add any other context about the problem here. +`; + + const params = new URLSearchParams({ + template: 'bug_report.md', + body: body, + labels: 'bug', + }); + + window.open(`https://github.com/block/goose/issues/new?${params.toString()}`, '_blank'); + onClose(); + } catch { + toastError({ + title: 'Error', + msg: 'Failed to get system information', + }); + } finally { + setIsFilingBug(false); + } + }; + + if (!isOpen) return null; + + return ( +
+
+
+ +
+

Report a Problem

+

+ You can download a diagnostics zip file to share with the team, or file a bug directly + on GitHub with your system details pre-filled. A diagnostics report contains the + following: +

+
    +
  • Basic system info
  • +
  • Your current session messages
  • +
  • Recent log files
  • +
  • Configuration settings
  • +
+

+ Warning: If your session contains sensitive information, do not share + the diagnostics file publicly. +

+

+ If you file a bug, consider attaching the diagnostics report to it. +

+
+
+
+ + + +
+
+
+ ); +}; diff --git a/ui/desktop/src/components/ui/DownloadDiagnostics.tsx b/ui/desktop/src/components/ui/DownloadDiagnostics.tsx deleted file mode 100644 index 376563182131..000000000000 --- a/ui/desktop/src/components/ui/DownloadDiagnostics.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import React, { useState } from 'react'; -import { AlertTriangle } from 'lucide-react'; -import { Button } from './button'; -import { toastError } from '../../toasts'; -import { diagnostics } from '../../api'; - -interface DiagnosticsModalProps { - isOpen: boolean; - onClose: () => void; - sessionId: string; -} - -export const DiagnosticsModal: React.FC = ({ - isOpen, - onClose, - sessionId, -}) => { - const [isDownloading, setIsDownloading] = useState(false); - - const handleDownload = async () => { - setIsDownloading(true); - - try { - const response = await diagnostics({ - path: { session_id: sessionId }, - throwOnError: true, - }); - - const blob = new Blob([response.data], { type: 'application/zip' }); - const url = window.URL.createObjectURL(blob); - const a = document.createElement('a'); - a.href = url; - a.download = `diagnostics_${sessionId}.zip`; - document.body.appendChild(a); - a.click(); - document.body.removeChild(a); - window.URL.revokeObjectURL(url); - - onClose(); - } catch { - toastError({ - title: 'Diagnostics Error', - msg: 'Failed to download diagnostics', - }); - } finally { - setIsDownloading(false); - } - }; - - if (!isOpen) return null; - - return ( -
-
-
- -
-

Download Diagnostics

-

- Hit the download button to get a zip file containing all the important information to - diagnose a problem in goose. You can share this file with the team or if you are a - developer look at it yourself. -

-
    -
  • Basic system info
  • -
  • Your current session messages
  • -
  • Recent log files
  • -
  • Configuration settings
  • -
-

- Warning: If your session contains sensitive information, do not share - this file publicly. -

-
-
-
- - -
-
-
- ); -};