feat: platform extension migrator + code mode rename (#6611)
This commit is contained in:
@@ -255,8 +255,9 @@ async fn add_builtins(agent: &Agent, builtins: Vec<String>) {
|
|||||||
let config = if PLATFORM_EXTENSIONS.contains_key(builtin.as_str()) {
|
let config = if PLATFORM_EXTENSIONS.contains_key(builtin.as_str()) {
|
||||||
ExtensionConfig::Platform {
|
ExtensionConfig::Platform {
|
||||||
name: builtin.clone(),
|
name: builtin.clone(),
|
||||||
bundled: None,
|
|
||||||
description: builtin.clone(),
|
description: builtin.clone(),
|
||||||
|
display_name: None,
|
||||||
|
bundled: None,
|
||||||
available_tools: Vec::new(),
|
available_tools: Vec::new(),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -314,8 +314,9 @@ impl CliSession {
|
|||||||
if PLATFORM_EXTENSIONS.contains_key(extension_name) {
|
if PLATFORM_EXTENSIONS.contains_key(extension_name) {
|
||||||
ExtensionConfig::Platform {
|
ExtensionConfig::Platform {
|
||||||
name: extension_name.to_string(),
|
name: extension_name.to_string(),
|
||||||
bundled: None,
|
|
||||||
description: extension_name.to_string(),
|
description: extension_name.to_string(),
|
||||||
|
display_name: None,
|
||||||
|
bundled: None,
|
||||||
available_tools: Vec::new(),
|
available_tools: Vec::new(),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -434,7 +434,7 @@ impl CodeExecutionClient {
|
|||||||
},
|
},
|
||||||
server_info: Implementation {
|
server_info: Implementation {
|
||||||
name: EXTENSION_NAME.to_string(),
|
name: EXTENSION_NAME.to_string(),
|
||||||
title: Some("Code Execution".to_string()),
|
title: Some("Code Mode".to_string()),
|
||||||
version: "1.0.0".to_string(),
|
version: "1.0.0".to_string(),
|
||||||
icons: None,
|
icons: None,
|
||||||
website_url: None,
|
website_url: None,
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ pub static PLATFORM_EXTENSIONS: Lazy<HashMap<&'static str, PlatformExtensionDef>
|
|||||||
todo_extension::EXTENSION_NAME,
|
todo_extension::EXTENSION_NAME,
|
||||||
PlatformExtensionDef {
|
PlatformExtensionDef {
|
||||||
name: todo_extension::EXTENSION_NAME,
|
name: todo_extension::EXTENSION_NAME,
|
||||||
|
display_name: "Todo",
|
||||||
description:
|
description:
|
||||||
"Enable a todo list for goose so it can keep track of what it is doing",
|
"Enable a todo list for goose so it can keep track of what it is doing",
|
||||||
default_enabled: true,
|
default_enabled: true,
|
||||||
@@ -59,6 +60,7 @@ pub static PLATFORM_EXTENSIONS: Lazy<HashMap<&'static str, PlatformExtensionDef>
|
|||||||
apps_extension::EXTENSION_NAME,
|
apps_extension::EXTENSION_NAME,
|
||||||
PlatformExtensionDef {
|
PlatformExtensionDef {
|
||||||
name: apps_extension::EXTENSION_NAME,
|
name: apps_extension::EXTENSION_NAME,
|
||||||
|
display_name: "Apps",
|
||||||
description:
|
description:
|
||||||
"Create and manage custom Goose apps through chat. Apps are HTML/CSS/JavaScript and run in sandboxed windows.",
|
"Create and manage custom Goose apps through chat. Apps are HTML/CSS/JavaScript and run in sandboxed windows.",
|
||||||
default_enabled: true,
|
default_enabled: true,
|
||||||
@@ -70,6 +72,7 @@ pub static PLATFORM_EXTENSIONS: Lazy<HashMap<&'static str, PlatformExtensionDef>
|
|||||||
chatrecall_extension::EXTENSION_NAME,
|
chatrecall_extension::EXTENSION_NAME,
|
||||||
PlatformExtensionDef {
|
PlatformExtensionDef {
|
||||||
name: chatrecall_extension::EXTENSION_NAME,
|
name: chatrecall_extension::EXTENSION_NAME,
|
||||||
|
display_name: "Chat Recall",
|
||||||
description:
|
description:
|
||||||
"Search past conversations and load session summaries for contextual memory",
|
"Search past conversations and load session summaries for contextual memory",
|
||||||
default_enabled: false,
|
default_enabled: false,
|
||||||
@@ -83,6 +86,7 @@ pub static PLATFORM_EXTENSIONS: Lazy<HashMap<&'static str, PlatformExtensionDef>
|
|||||||
"extensionmanager",
|
"extensionmanager",
|
||||||
PlatformExtensionDef {
|
PlatformExtensionDef {
|
||||||
name: extension_manager_extension::EXTENSION_NAME,
|
name: extension_manager_extension::EXTENSION_NAME,
|
||||||
|
display_name: "Extension Manager",
|
||||||
description:
|
description:
|
||||||
"Enable extension management tools for discovering, enabling, and disabling extensions",
|
"Enable extension management tools for discovering, enabling, and disabling extensions",
|
||||||
default_enabled: true,
|
default_enabled: true,
|
||||||
@@ -94,6 +98,7 @@ pub static PLATFORM_EXTENSIONS: Lazy<HashMap<&'static str, PlatformExtensionDef>
|
|||||||
skills_extension::EXTENSION_NAME,
|
skills_extension::EXTENSION_NAME,
|
||||||
PlatformExtensionDef {
|
PlatformExtensionDef {
|
||||||
name: skills_extension::EXTENSION_NAME,
|
name: skills_extension::EXTENSION_NAME,
|
||||||
|
display_name: "Skills",
|
||||||
description: "Load and use skills from relevant directories",
|
description: "Load and use skills from relevant directories",
|
||||||
default_enabled: true,
|
default_enabled: true,
|
||||||
client_factory: |ctx| Box::new(skills_extension::SkillsClient::new(ctx).unwrap()),
|
client_factory: |ctx| Box::new(skills_extension::SkillsClient::new(ctx).unwrap()),
|
||||||
@@ -104,7 +109,9 @@ pub static PLATFORM_EXTENSIONS: Lazy<HashMap<&'static str, PlatformExtensionDef>
|
|||||||
code_execution_extension::EXTENSION_NAME,
|
code_execution_extension::EXTENSION_NAME,
|
||||||
PlatformExtensionDef {
|
PlatformExtensionDef {
|
||||||
name: code_execution_extension::EXTENSION_NAME,
|
name: code_execution_extension::EXTENSION_NAME,
|
||||||
description: "Execute JavaScript code in a sandboxed environment",
|
display_name: "Code Mode",
|
||||||
|
description:
|
||||||
|
"Goose will make extension calls through code execution, saving tokens",
|
||||||
default_enabled: false,
|
default_enabled: false,
|
||||||
client_factory: |ctx| {
|
client_factory: |ctx| {
|
||||||
Box::new(code_execution_extension::CodeExecutionClient::new(ctx).unwrap())
|
Box::new(code_execution_extension::CodeExecutionClient::new(ctx).unwrap())
|
||||||
@@ -158,6 +165,7 @@ impl PlatformExtensionContext {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct PlatformExtensionDef {
|
pub struct PlatformExtensionDef {
|
||||||
pub name: &'static str,
|
pub name: &'static str,
|
||||||
|
pub display_name: &'static str,
|
||||||
pub description: &'static str,
|
pub description: &'static str,
|
||||||
pub default_enabled: bool,
|
pub default_enabled: bool,
|
||||||
pub client_factory: fn(PlatformExtensionContext) -> Box<dyn McpClientTrait>,
|
pub client_factory: fn(PlatformExtensionContext) -> Box<dyn McpClientTrait>,
|
||||||
@@ -335,6 +343,7 @@ pub enum ExtensionConfig {
|
|||||||
#[serde(deserialize_with = "deserialize_null_with_default")]
|
#[serde(deserialize_with = "deserialize_null_with_default")]
|
||||||
#[schema(required)]
|
#[schema(required)]
|
||||||
description: String,
|
description: String,
|
||||||
|
display_name: Option<String>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
bundled: Option<bool>,
|
bundled: Option<bool>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
|||||||
@@ -273,25 +273,34 @@ impl Config {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn load(&self) -> Result<Mapping, ConfigError> {
|
fn load(&self) -> Result<Mapping, ConfigError> {
|
||||||
if self.config_path.exists() {
|
let mut values = if self.config_path.exists() {
|
||||||
self.load_values_with_recovery()
|
self.load_values_with_recovery()?
|
||||||
} else {
|
} else {
|
||||||
// Config file doesn't exist, try to recover from backup first
|
// Config file doesn't exist, try to recover from backup first
|
||||||
tracing::info!("Config file doesn't exist, attempting recovery from backup");
|
tracing::info!("Config file doesn't exist, attempting recovery from backup");
|
||||||
|
|
||||||
if let Ok(backup_values) = self.try_restore_from_backup() {
|
if let Ok(backup_values) = self.try_restore_from_backup() {
|
||||||
tracing::info!("Successfully restored config from backup");
|
tracing::info!("Successfully restored config from backup");
|
||||||
return Ok(backup_values);
|
backup_values
|
||||||
|
} else {
|
||||||
|
// No backup available, create a default config
|
||||||
|
tracing::info!("No backup found, creating default configuration");
|
||||||
|
|
||||||
|
// Try to load from init-config.yaml if it exists, otherwise use empty config
|
||||||
|
let default_config = self.load_init_config_if_exists().unwrap_or_default();
|
||||||
|
|
||||||
|
self.create_and_save_default_config(default_config)?
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// No backup available, create a default config
|
// Run migrations on the loaded config
|
||||||
tracing::info!("No backup found, creating default configuration");
|
if crate::config::migrations::run_migrations(&mut values) {
|
||||||
|
if let Err(e) = self.save_values(values.clone()) {
|
||||||
// Try to load from init-config.yaml if it exists, otherwise use empty config
|
tracing::warn!("Failed to save migrated config: {}", e);
|
||||||
let default_config = self.load_init_config_if_exists().unwrap_or_default();
|
}
|
||||||
|
|
||||||
self.create_and_save_default_config(default_config)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(values)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn all_values(&self) -> Result<HashMap<String, Value>, ConfigError> {
|
pub fn all_values(&self) -> Result<HashMap<String, Value>, ConfigError> {
|
||||||
@@ -1203,13 +1212,7 @@ mod tests {
|
|||||||
// Print the final values for debugging
|
// Print the final values for debugging
|
||||||
println!("Final values: {:?}", final_values);
|
println!("Final values: {:?}", final_values);
|
||||||
|
|
||||||
assert_eq!(
|
// Check that our 3 keys are present (migrations may add additional keys like "extensions")
|
||||||
final_values.len(),
|
|
||||||
3,
|
|
||||||
"Expected 3 values, got {}",
|
|
||||||
final_values.len()
|
|
||||||
);
|
|
||||||
|
|
||||||
for i in 0..3 {
|
for i in 0..3 {
|
||||||
let key = format!("key{}", i);
|
let key = format!("key{}", i);
|
||||||
let value = format!("value{}", i);
|
let value = format!("value{}", i);
|
||||||
@@ -1285,19 +1288,22 @@ mod tests {
|
|||||||
// Try to load values - should create a fresh default config
|
// Try to load values - should create a fresh default config
|
||||||
let recovered_values = config.all_values()?;
|
let recovered_values = config.all_values()?;
|
||||||
|
|
||||||
// Should return empty config
|
// Note: migrations may add keys like "extensions", so we just verify
|
||||||
assert_eq!(recovered_values.len(), 0);
|
// that no user-defined keys exist (the config was reset)
|
||||||
|
assert!(
|
||||||
|
!recovered_values.contains_key("key1"),
|
||||||
|
"Should not have user keys after recovery"
|
||||||
|
);
|
||||||
|
|
||||||
// Verify that a clean config file was written to disk
|
// Verify that a clean config file was written to disk
|
||||||
let file_content = std::fs::read_to_string(config_file.path())?;
|
let file_content = std::fs::read_to_string(config_file.path())?;
|
||||||
|
|
||||||
// Should be valid YAML (empty object)
|
// Should be valid YAML
|
||||||
let parsed: serde_yaml::Value = serde_yaml::from_str(&file_content)?;
|
let parsed: serde_yaml::Value = serde_yaml::from_str(&file_content)?;
|
||||||
assert!(parsed.is_mapping());
|
assert!(parsed.is_mapping());
|
||||||
|
|
||||||
// Should be able to load it again without issues
|
// Should be able to load it again without issues
|
||||||
let reloaded_values = config.all_values()?;
|
let _reloaded_values = config.all_values()?;
|
||||||
assert_eq!(reloaded_values.len(), 0);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -1316,8 +1322,12 @@ mod tests {
|
|||||||
// Try to load values - should create a fresh default config file
|
// Try to load values - should create a fresh default config file
|
||||||
let values = config.all_values()?;
|
let values = config.all_values()?;
|
||||||
|
|
||||||
// Should return empty config
|
// Note: migrations may add keys like "extensions", so we just verify
|
||||||
assert_eq!(values.len(), 0);
|
// that no user-defined keys exist (the config was freshly created)
|
||||||
|
assert!(
|
||||||
|
!values.contains_key("key1"),
|
||||||
|
"Should not have user keys in fresh config"
|
||||||
|
);
|
||||||
|
|
||||||
// Verify that the config file was created
|
// Verify that the config file was created
|
||||||
assert!(config_path.exists());
|
assert!(config_path.exists());
|
||||||
@@ -1328,8 +1338,7 @@ mod tests {
|
|||||||
assert!(parsed.is_mapping());
|
assert!(parsed.is_mapping());
|
||||||
|
|
||||||
// Should be able to load it again without issues
|
// Should be able to load it again without issues
|
||||||
let reloaded_values = config.all_values()?;
|
let _reloaded_values = config.all_values()?;
|
||||||
assert_eq!(reloaded_values.len(), 0);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use super::base::Config;
|
use super::base::Config;
|
||||||
use crate::agents::extension::PLATFORM_EXTENSIONS;
|
|
||||||
use crate::agents::ExtensionConfig;
|
use crate::agents::ExtensionConfig;
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@@ -54,24 +53,6 @@ fn get_extensions_map() -> IndexMap<String, ExtensionEntry> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always inject platform extensions (code_execution, todo, skills, etc.)
|
|
||||||
// These are internal agent extensions that should always be available
|
|
||||||
for (name, def) in PLATFORM_EXTENSIONS.iter() {
|
|
||||||
if !extensions_map.contains_key(*name) {
|
|
||||||
extensions_map.insert(
|
|
||||||
name.to_string(),
|
|
||||||
ExtensionEntry {
|
|
||||||
config: ExtensionConfig::Platform {
|
|
||||||
name: def.name.to_string(),
|
|
||||||
description: def.description.to_string(),
|
|
||||||
bundled: Some(true),
|
|
||||||
available_tools: Vec::new(),
|
|
||||||
},
|
|
||||||
enabled: def.default_enabled,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
extensions_map
|
extensions_map
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,141 @@
|
|||||||
|
use crate::agents::extension::PLATFORM_EXTENSIONS;
|
||||||
|
use crate::agents::ExtensionConfig;
|
||||||
|
use crate::config::extensions::ExtensionEntry;
|
||||||
|
use serde_yaml::Mapping;
|
||||||
|
|
||||||
|
const EXTENSIONS_CONFIG_KEY: &str = "extensions";
|
||||||
|
|
||||||
|
pub fn run_migrations(config: &mut Mapping) -> bool {
|
||||||
|
let mut changed = false;
|
||||||
|
changed |= migrate_platform_extensions(config);
|
||||||
|
changed
|
||||||
|
}
|
||||||
|
|
||||||
|
fn migrate_platform_extensions(config: &mut Mapping) -> bool {
|
||||||
|
let extensions_key = serde_yaml::Value::String(EXTENSIONS_CONFIG_KEY.to_string());
|
||||||
|
|
||||||
|
let extensions_value = config
|
||||||
|
.get(&extensions_key)
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or(serde_yaml::Value::Mapping(Mapping::new()));
|
||||||
|
|
||||||
|
let mut extensions_map: Mapping = match extensions_value {
|
||||||
|
serde_yaml::Value::Mapping(m) => m,
|
||||||
|
_ => Mapping::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut needs_save = false;
|
||||||
|
|
||||||
|
for (name, def) in PLATFORM_EXTENSIONS.iter() {
|
||||||
|
let ext_key = serde_yaml::Value::String(name.to_string());
|
||||||
|
let existing = extensions_map.get(&ext_key);
|
||||||
|
|
||||||
|
let needs_migration = match existing {
|
||||||
|
None => true,
|
||||||
|
Some(value) => match serde_yaml::from_value::<ExtensionEntry>(value.clone()) {
|
||||||
|
Ok(entry) => {
|
||||||
|
if let ExtensionConfig::Platform {
|
||||||
|
description,
|
||||||
|
display_name,
|
||||||
|
..
|
||||||
|
} = &entry.config
|
||||||
|
{
|
||||||
|
description != def.description
|
||||||
|
|| display_name.as_deref() != Some(def.display_name)
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if needs_migration {
|
||||||
|
let enabled = existing
|
||||||
|
.and_then(|v| serde_yaml::from_value::<ExtensionEntry>(v.clone()).ok())
|
||||||
|
.map(|e| e.enabled)
|
||||||
|
.unwrap_or(def.default_enabled);
|
||||||
|
|
||||||
|
let new_entry = ExtensionEntry {
|
||||||
|
config: ExtensionConfig::Platform {
|
||||||
|
name: def.name.to_string(),
|
||||||
|
description: def.description.to_string(),
|
||||||
|
display_name: Some(def.display_name.to_string()),
|
||||||
|
bundled: Some(true),
|
||||||
|
available_tools: Vec::new(),
|
||||||
|
},
|
||||||
|
enabled,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Ok(value) = serde_yaml::to_value(&new_entry) {
|
||||||
|
extensions_map.insert(ext_key, value);
|
||||||
|
needs_save = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if needs_save {
|
||||||
|
config.insert(extensions_key, serde_yaml::Value::Mapping(extensions_map));
|
||||||
|
}
|
||||||
|
|
||||||
|
needs_save
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_migrate_platform_extensions_empty_config() {
|
||||||
|
let mut config = Mapping::new();
|
||||||
|
let changed = run_migrations(&mut config);
|
||||||
|
|
||||||
|
assert!(changed);
|
||||||
|
let extensions_key = serde_yaml::Value::String(EXTENSIONS_CONFIG_KEY.to_string());
|
||||||
|
assert!(config.contains_key(&extensions_key));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_migrate_platform_extensions_preserves_enabled_state() {
|
||||||
|
let mut config = Mapping::new();
|
||||||
|
let mut extensions = Mapping::new();
|
||||||
|
let todo_entry = ExtensionEntry {
|
||||||
|
config: ExtensionConfig::Platform {
|
||||||
|
name: "todo".to_string(),
|
||||||
|
description: "old description".to_string(),
|
||||||
|
display_name: Some("Old Name".to_string()),
|
||||||
|
bundled: Some(true),
|
||||||
|
available_tools: Vec::new(),
|
||||||
|
},
|
||||||
|
enabled: false,
|
||||||
|
};
|
||||||
|
extensions.insert(
|
||||||
|
serde_yaml::Value::String("todo".to_string()),
|
||||||
|
serde_yaml::to_value(&todo_entry).unwrap(),
|
||||||
|
);
|
||||||
|
config.insert(
|
||||||
|
serde_yaml::Value::String(EXTENSIONS_CONFIG_KEY.to_string()),
|
||||||
|
serde_yaml::Value::Mapping(extensions),
|
||||||
|
);
|
||||||
|
|
||||||
|
let changed = run_migrations(&mut config);
|
||||||
|
assert!(changed);
|
||||||
|
|
||||||
|
let extensions_key = serde_yaml::Value::String(EXTENSIONS_CONFIG_KEY.to_string());
|
||||||
|
let extensions = config.get(&extensions_key).unwrap().as_mapping().unwrap();
|
||||||
|
let todo_key = serde_yaml::Value::String("todo".to_string());
|
||||||
|
let todo_value = extensions.get(&todo_key).unwrap();
|
||||||
|
let todo_entry: ExtensionEntry = serde_yaml::from_value(todo_value.clone()).unwrap();
|
||||||
|
|
||||||
|
assert!(!todo_entry.enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_migrate_platform_extensions_idempotent() {
|
||||||
|
let mut config = Mapping::new();
|
||||||
|
run_migrations(&mut config);
|
||||||
|
|
||||||
|
let changed = run_migrations(&mut config);
|
||||||
|
assert!(!changed);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ pub mod declarative_providers;
|
|||||||
mod experiments;
|
mod experiments;
|
||||||
pub mod extensions;
|
pub mod extensions;
|
||||||
pub mod goose_mode;
|
pub mod goose_mode;
|
||||||
|
mod migrations;
|
||||||
pub mod paths;
|
pub mod paths;
|
||||||
pub mod permission;
|
pub mod permission;
|
||||||
pub mod search_path;
|
pub mod search_path;
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ enum RecipeExtensionConfigInternal {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
description: Option<String>,
|
description: Option<String>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
display_name: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
bundled: Option<bool>,
|
bundled: Option<bool>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
available_tools: Vec<String>,
|
available_tools: Vec<String>,
|
||||||
@@ -128,6 +130,7 @@ impl From<RecipeExtensionConfigInternal> for ExtensionConfig {
|
|||||||
available_tools
|
available_tools
|
||||||
},
|
},
|
||||||
Platform {
|
Platform {
|
||||||
|
display_name,
|
||||||
bundled,
|
bundled,
|
||||||
available_tools
|
available_tools
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -512,6 +512,7 @@ mod tests {
|
|||||||
description:
|
description:
|
||||||
"Enable a todo list for goose so it can keep track of what it is doing"
|
"Enable a todo list for goose so it can keep track of what it is doing"
|
||||||
.to_string(),
|
.to_string(),
|
||||||
|
display_name: Some("Todo".to_string()),
|
||||||
bundled: Some(true),
|
bundled: Some(true),
|
||||||
available_tools: vec![],
|
available_tools: vec![],
|
||||||
},
|
},
|
||||||
@@ -535,6 +536,7 @@ mod tests {
|
|||||||
let ext_config = ExtensionConfig::Platform {
|
let ext_config = ExtensionConfig::Platform {
|
||||||
name: "extensionmanager".to_string(),
|
name: "extensionmanager".to_string(),
|
||||||
description: "Extension Manager".to_string(),
|
description: "Extension Manager".to_string(),
|
||||||
|
display_name: Some("Extension Manager".to_string()),
|
||||||
bundled: Some(true),
|
bundled: Some(true),
|
||||||
available_tools: vec![],
|
available_tools: vec![],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3813,6 +3813,10 @@
|
|||||||
"description": {
|
"description": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"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"
|
||||||
|
|||||||
@@ -253,6 +253,7 @@ export type ExtensionConfig = {
|
|||||||
available_tools?: Array<string>;
|
available_tools?: Array<string>;
|
||||||
bundled?: boolean | null;
|
bundled?: boolean | null;
|
||||||
description: string;
|
description: string;
|
||||||
|
display_name?: string | null;
|
||||||
/**
|
/**
|
||||||
* The name used to identify this extension
|
* The name used to identify this extension
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ type SourceType = 'file' | 'deeplink';
|
|||||||
|
|
||||||
interface CleanExtension {
|
interface CleanExtension {
|
||||||
name: string;
|
name: string;
|
||||||
type: 'stdio' | 'sse' | 'builtin' | 'frontend' | 'streamable_http';
|
type: 'stdio' | 'sse' | 'builtin' | 'frontend' | 'streamable_http' | 'platform';
|
||||||
cmd?: string;
|
cmd?: string;
|
||||||
args?: string[];
|
args?: string[];
|
||||||
uri?: string;
|
uri?: string;
|
||||||
@@ -120,7 +120,7 @@ function recipeToYaml(recipe: Recipe): string {
|
|||||||
if (extAny.args) {
|
if (extAny.args) {
|
||||||
cleanExt.args = extAny.args as string[];
|
cleanExt.args = extAny.args as string[];
|
||||||
}
|
}
|
||||||
} else if (ext.type === 'builtin' && extAny.display_name) {
|
} else if ((ext.type === 'builtin' || ext.type === 'platform') && extAny.display_name) {
|
||||||
cleanExt.display_name = extAny.display_name as string;
|
cleanExt.display_name = extAny.display_name as string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -104,7 +104,9 @@ export function formatExtensionName(name: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getFriendlyTitle(extension: FixedExtensionEntry): string {
|
export function getFriendlyTitle(extension: FixedExtensionEntry): string {
|
||||||
const name = (extension.type === 'builtin' && extension.display_name) || extension.name;
|
const name =
|
||||||
|
((extension.type === 'builtin' || extension.type === 'platform') && extension.display_name) ||
|
||||||
|
extension.name;
|
||||||
return formatExtensionName(name);
|
return formatExtensionName(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user