mirror of
https://github.com/aljazceru/goose.git
synced 2026-01-04 06:54:25 +01:00
extensions: add a display name field (#1759)
This commit is contained in:
@@ -10,6 +10,27 @@ use serde_json::{json, Value};
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
|
fn get_display_name(extension_id: &str) -> String {
|
||||||
|
match extension_id {
|
||||||
|
"developer" => "Developer Tools".to_string(),
|
||||||
|
"computercontroller" => "Computer Controller".to_string(),
|
||||||
|
"googledrive" => "Google Drive".to_string(),
|
||||||
|
"memory" => "Memory".to_string(),
|
||||||
|
"tutorial" => "Tutorial".to_string(),
|
||||||
|
"jetbrains" => "JetBrains".to_string(),
|
||||||
|
// Add other extensions as needed
|
||||||
|
_ => {
|
||||||
|
extension_id
|
||||||
|
.chars()
|
||||||
|
.next()
|
||||||
|
.unwrap_or_default()
|
||||||
|
.to_uppercase()
|
||||||
|
.collect::<String>()
|
||||||
|
+ &extension_id[1..]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn handle_configure() -> Result<(), Box<dyn Error>> {
|
pub async fn handle_configure() -> Result<(), Box<dyn Error>> {
|
||||||
let config = Config::global();
|
let config = Config::global();
|
||||||
|
|
||||||
@@ -39,6 +60,7 @@ pub async fn handle_configure() -> Result<(), Box<dyn Error>> {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
config: ExtensionConfig::Builtin {
|
config: ExtensionConfig::Builtin {
|
||||||
name: "developer".to_string(),
|
name: "developer".to_string(),
|
||||||
|
display_name: Some(goose::config::DEFAULT_DISPLAY_NAME.to_string()),
|
||||||
timeout: Some(goose::config::DEFAULT_EXTENSION_TIMEOUT),
|
timeout: Some(goose::config::DEFAULT_EXTENSION_TIMEOUT),
|
||||||
},
|
},
|
||||||
})?;
|
})?;
|
||||||
@@ -464,10 +486,13 @@ pub fn configure_extensions_dialog() -> Result<(), Box<dyn Error>> {
|
|||||||
})
|
})
|
||||||
.interact()?;
|
.interact()?;
|
||||||
|
|
||||||
|
let display_name = get_display_name(&extension);
|
||||||
|
|
||||||
ExtensionManager::set(ExtensionEntry {
|
ExtensionManager::set(ExtensionEntry {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
config: ExtensionConfig::Builtin {
|
config: ExtensionConfig::Builtin {
|
||||||
name: extension.clone(),
|
name: extension.clone(),
|
||||||
|
display_name: Some(display_name),
|
||||||
timeout: Some(timeout),
|
timeout: Some(timeout),
|
||||||
},
|
},
|
||||||
})?;
|
})?;
|
||||||
|
|||||||
@@ -133,6 +133,7 @@ impl Session {
|
|||||||
for name in builtin_name.split(',') {
|
for name in builtin_name.split(',') {
|
||||||
let config = ExtensionConfig::Builtin {
|
let config = ExtensionConfig::Builtin {
|
||||||
name: name.trim().to_string(),
|
name: name.trim().to_string(),
|
||||||
|
display_name: None,
|
||||||
// TODO: should set a timeout
|
// TODO: should set a timeout
|
||||||
timeout: Some(goose::config::DEFAULT_EXTENSION_TIMEOUT),
|
timeout: Some(goose::config::DEFAULT_EXTENSION_TIMEOUT),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ enum ExtensionConfigRequest {
|
|||||||
Builtin {
|
Builtin {
|
||||||
/// The name of the built-in extension.
|
/// The name of the built-in extension.
|
||||||
name: String,
|
name: String,
|
||||||
|
display_name: Option<String>,
|
||||||
timeout: Option<u64>,
|
timeout: Option<u64>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -157,9 +158,15 @@ async fn add_extension(
|
|||||||
timeout,
|
timeout,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ExtensionConfigRequest::Builtin { name, timeout } => {
|
ExtensionConfigRequest::Builtin {
|
||||||
ExtensionConfig::Builtin { name, timeout }
|
name,
|
||||||
}
|
display_name,
|
||||||
|
timeout,
|
||||||
|
} => ExtensionConfig::Builtin {
|
||||||
|
name,
|
||||||
|
display_name,
|
||||||
|
timeout,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Acquire a lock on the agent and attempt to add the extension.
|
// Acquire a lock on the agent and attempt to add the extension.
|
||||||
|
|||||||
@@ -134,7 +134,12 @@ impl Capabilities {
|
|||||||
);
|
);
|
||||||
Box::new(McpClient::new(service))
|
Box::new(McpClient::new(service))
|
||||||
}
|
}
|
||||||
ExtensionConfig::Builtin { name, timeout } => {
|
#[allow(unused_variables)]
|
||||||
|
ExtensionConfig::Builtin {
|
||||||
|
name,
|
||||||
|
display_name,
|
||||||
|
timeout,
|
||||||
|
} => {
|
||||||
// For builtin extensions, we run the current executable with mcp and extension name
|
// For builtin extensions, we run the current executable with mcp and extension name
|
||||||
let cmd = std::env::current_exe()
|
let cmd = std::env::current_exe()
|
||||||
.expect("should find the current executable")
|
.expect("should find the current executable")
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ pub enum ExtensionConfig {
|
|||||||
Builtin {
|
Builtin {
|
||||||
/// The name used to identify this extension
|
/// The name used to identify this extension
|
||||||
name: String,
|
name: String,
|
||||||
|
display_name: Option<String>, // needed for the UI
|
||||||
timeout: Option<u64>,
|
timeout: Option<u64>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -86,6 +87,7 @@ impl Default for ExtensionConfig {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::Builtin {
|
Self::Builtin {
|
||||||
name: config::DEFAULT_EXTENSION.to_string(),
|
name: config::DEFAULT_EXTENSION.to_string(),
|
||||||
|
display_name: Some(config::DEFAULT_DISPLAY_NAME.to_string()),
|
||||||
timeout: Some(config::DEFAULT_EXTENSION_TIMEOUT),
|
timeout: Some(config::DEFAULT_EXTENSION_TIMEOUT),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use utoipa::ToSchema;
|
|||||||
pub const DEFAULT_EXTENSION: &str = "developer";
|
pub const DEFAULT_EXTENSION: &str = "developer";
|
||||||
pub const DEFAULT_EXTENSION_TIMEOUT: u64 = 300;
|
pub const DEFAULT_EXTENSION_TIMEOUT: u64 = 300;
|
||||||
pub const DEFAULT_EXTENSION_DESCRIPTION: &str = "";
|
pub const DEFAULT_EXTENSION_DESCRIPTION: &str = "";
|
||||||
|
pub const DEFAULT_DISPLAY_NAME: &str = "Developer";
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone, ToSchema)]
|
#[derive(Debug, Deserialize, Serialize, Clone, ToSchema)]
|
||||||
pub struct ExtensionEntry {
|
pub struct ExtensionEntry {
|
||||||
@@ -42,6 +43,7 @@ impl ExtensionManager {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
config: ExtensionConfig::Builtin {
|
config: ExtensionConfig::Builtin {
|
||||||
name: DEFAULT_EXTENSION.to_string(),
|
name: DEFAULT_EXTENSION.to_string(),
|
||||||
|
display_name: Some(DEFAULT_DISPLAY_NAME.to_string()),
|
||||||
timeout: Some(DEFAULT_EXTENSION_TIMEOUT),
|
timeout: Some(DEFAULT_EXTENSION_TIMEOUT),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ pub use base::{Config, ConfigError, APP_STRATEGY};
|
|||||||
pub use experiments::ExperimentManager;
|
pub use experiments::ExperimentManager;
|
||||||
pub use extensions::{ExtensionEntry, ExtensionManager};
|
pub use extensions::{ExtensionEntry, ExtensionManager};
|
||||||
|
|
||||||
|
pub use extensions::DEFAULT_DISPLAY_NAME;
|
||||||
pub use extensions::DEFAULT_EXTENSION;
|
pub use extensions::DEFAULT_EXTENSION;
|
||||||
pub use extensions::DEFAULT_EXTENSION_DESCRIPTION;
|
pub use extensions::DEFAULT_EXTENSION_DESCRIPTION;
|
||||||
pub use extensions::DEFAULT_EXTENSION_TIMEOUT;
|
pub use extensions::DEFAULT_EXTENSION_TIMEOUT;
|
||||||
|
|||||||
@@ -392,6 +392,10 @@
|
|||||||
"type"
|
"type"
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"display_name": {
|
||||||
|
"type": "string",
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The name used to identify this extension"
|
"description": "The name used to identify this extension"
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ export type ExtensionConfig = {
|
|||||||
timeout?: number | null;
|
timeout?: number | null;
|
||||||
type: 'stdio';
|
type: 'stdio';
|
||||||
} | {
|
} | {
|
||||||
|
display_name?: string | null;
|
||||||
/**
|
/**
|
||||||
* The name used to identify this extension
|
* The name used to identify this extension
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ export default function ExtensionsSection() {
|
|||||||
|
|
||||||
<div className="flex gap-4 pt-4 w-full">
|
<div className="flex gap-4 pt-4 w-full">
|
||||||
<Button
|
<Button
|
||||||
className="flex items-center gap-2 flex-1 justify-center text-textSubtle bg-black dark:bg-white hover:bg-subtle"
|
className="flex items-center gap-2 flex-1 justify-center text-white dark:text-textSubtle bg-black dark:bg-white hover:bg-subtle"
|
||||||
onClick={() => setIsAddModalOpen(true)}
|
onClick={() => setIsAddModalOpen(true)}
|
||||||
>
|
>
|
||||||
<Plus className="h-4 w-4" />
|
<Plus className="h-4 w-4" />
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import type { ExtensionConfig } from '../../../api/types.gen';
|
import type { ExtensionConfig } from '../../../api/types.gen';
|
||||||
import builtInExtensionsData from '../../../built-in-extensions.json';
|
import builtInExtensionsData from './built-in-extensions.json';
|
||||||
import { FixedExtensionEntry } from '@/src/components/ConfigContext';
|
import { FixedExtensionEntry } from '../../ConfigContext';
|
||||||
|
|
||||||
// Type definition for built-in extensions from JSON
|
// Type definition for built-in extensions from JSON
|
||||||
type BuiltinExtension = {
|
type BuiltinExtension = {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
display_name: string;
|
||||||
description: string;
|
description: string;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
type: 'builtin';
|
type: 'builtin';
|
||||||
@@ -50,13 +51,14 @@ export async function syncBuiltInExtensions(
|
|||||||
|
|
||||||
// Check each built-in extension
|
// Check each built-in extension
|
||||||
for (const builtinExt of builtinExtensions) {
|
for (const builtinExt of builtinExtensions) {
|
||||||
// Only add if the extension doesn't already exist
|
// Only add if the extension doesn't already exist -- use the id
|
||||||
if (!existingExtensionKeys.has(builtinExt.id)) {
|
if (!existingExtensionKeys.has(builtinExt.id)) {
|
||||||
console.log(`Adding built-in extension: ${builtinExt.id}`);
|
console.log(`Adding built-in extension: ${builtinExt.id}`);
|
||||||
|
|
||||||
// Convert to the ExtensionConfig format
|
// Convert to the ExtensionConfig format
|
||||||
const extConfig: ExtensionConfig = {
|
const extConfig: ExtensionConfig = {
|
||||||
name: builtinExt.name,
|
name: builtinExt.name,
|
||||||
|
display_name: builtinExt.display_name,
|
||||||
type: 'builtin',
|
type: 'builtin',
|
||||||
timeout: builtinExt.timeout ?? 300,
|
timeout: builtinExt.timeout ?? 300,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "developer",
|
||||||
|
"name": "developer",
|
||||||
|
"display_name": "Developer",
|
||||||
|
"description": "General development tools useful for software engineering.",
|
||||||
|
"enabled": true,
|
||||||
|
"type": "builtin",
|
||||||
|
"env_keys": [],
|
||||||
|
"timeout": 300
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "computercontroller",
|
||||||
|
"name": "computercontroller",
|
||||||
|
"display_name": "Computer Controller",
|
||||||
|
"description": "General computer control tools that don't require you to be a developer or engineer.",
|
||||||
|
"enabled": false,
|
||||||
|
"type": "builtin",
|
||||||
|
"env_keys": [],
|
||||||
|
"timeout": 300
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "memory",
|
||||||
|
"name": "memory",
|
||||||
|
"display_name": "Memory",
|
||||||
|
"description": "Teach goose your preferences as you go.",
|
||||||
|
"enabled": false,
|
||||||
|
"type": "builtin",
|
||||||
|
"env_keys": [],
|
||||||
|
"timeout": 300
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "jetbrains",
|
||||||
|
"display_name": "Jetbrains",
|
||||||
|
"name": "jetbrains",
|
||||||
|
"description": "Integration with any Jetbrains IDE",
|
||||||
|
"enabled": false,
|
||||||
|
"type": "builtin",
|
||||||
|
"env_keys": [],
|
||||||
|
"timeout": 300
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "tutorial",
|
||||||
|
"name": "tutorial",
|
||||||
|
"display_name": "Tutorial",
|
||||||
|
"description": "Access interactive tutorials and guides",
|
||||||
|
"enabled": false,
|
||||||
|
"type": "builtin",
|
||||||
|
"env_keys": []
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -24,7 +24,7 @@ export default function ExtensionItem({ extension, onToggle, onConfigure }: Exte
|
|||||||
return (
|
return (
|
||||||
<div className="rounded-lg border border-borderSubtle p-4 mb-2">
|
<div className="rounded-lg border border-borderSubtle p-4 mb-2">
|
||||||
<div className="flex items-center justify-between mb-2">
|
<div className="flex items-center justify-between mb-2">
|
||||||
<h3 className="font-medium text-textStandard">{getFriendlyTitle(extension.name)}</h3>
|
<h3 className="font-medium text-textStandard">{getFriendlyTitle(extension)}</h3>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{/* Only show config button for non-builtin extensions */}
|
{/* Only show config button for non-builtin extensions */}
|
||||||
{extension.type !== 'builtin' && (
|
{extension.type !== 'builtin' && (
|
||||||
|
|||||||
@@ -28,9 +28,21 @@ export default function ExtensionList({ extensions, onToggle, onConfigure }: Ext
|
|||||||
|
|
||||||
// Helper functions
|
// Helper functions
|
||||||
// Helper function to get a friendly title from extension name
|
// Helper function to get a friendly title from extension name
|
||||||
export function getFriendlyTitle(name: string): string {
|
export function getFriendlyTitle(extension: FixedExtensionEntry): string {
|
||||||
|
let name = '';
|
||||||
|
|
||||||
|
// if it's a builtin, check if there's a display_name (old configs didn't have this field)
|
||||||
|
if (extension.type === 'builtin' && 'display_name' in extension && extension.display_name) {
|
||||||
|
// If we have a display_name for a builtin, use it directly
|
||||||
|
return extension.display_name;
|
||||||
|
} else {
|
||||||
|
// For non-builtins or builtins without display_name
|
||||||
|
name = extension.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format the name to be more readable
|
||||||
return name
|
return name
|
||||||
.split('-')
|
.split(/[-_]/) // Split on hyphens and underscores
|
||||||
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
||||||
.join(' ');
|
.join(' ');
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user