commands to acp+ migration: extensions management (#8733)
This commit is contained in:
@@ -99,11 +99,43 @@ pub struct GetExtensionsRequest {}
|
|||||||
/// List configured extensions and any warnings.
|
/// List configured extensions and any warnings.
|
||||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcResponse)]
|
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcResponse)]
|
||||||
pub struct GetExtensionsResponse {
|
pub struct GetExtensionsResponse {
|
||||||
/// Array of ExtensionEntry objects with `enabled` flag and config details.
|
/// Array of ExtensionEntry objects with `enabled` flag, `configKey`, and flattened config details.
|
||||||
pub extensions: Vec<serde_json::Value>,
|
pub extensions: Vec<serde_json::Value>,
|
||||||
pub warnings: Vec<String>,
|
pub warnings: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Persist a new extension to the user's global goose config.
|
||||||
|
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcRequest)]
|
||||||
|
#[request(method = "_goose/config/extensions/add", response = EmptyResponse)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct AddConfigExtensionRequest {
|
||||||
|
pub name: String,
|
||||||
|
/// Extension configuration. Must be a JSON object matching one of the
|
||||||
|
/// `ExtensionConfig` variants (e.g. `stdio`, `streamable_http`, `builtin`).
|
||||||
|
/// `name` and `enabled` are injected server-side.
|
||||||
|
#[serde(default)]
|
||||||
|
pub extension_config: serde_json::Value,
|
||||||
|
#[serde(default)]
|
||||||
|
pub enabled: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove a persisted extension from the user's global goose config.
|
||||||
|
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcRequest)]
|
||||||
|
#[request(method = "_goose/config/extensions/remove", response = EmptyResponse)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct RemoveConfigExtensionRequest {
|
||||||
|
pub config_key: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Toggle the `enabled` flag for a persisted extension in the user's global goose config.
|
||||||
|
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcRequest)]
|
||||||
|
#[request(method = "_goose/config/extensions/toggle", response = EmptyResponse)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct ToggleConfigExtensionRequest {
|
||||||
|
pub config_key: String,
|
||||||
|
pub enabled: bool,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcRequest)]
|
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcRequest)]
|
||||||
#[request(method = "_goose/session/extensions", response = GetSessionExtensionsResponse)]
|
#[request(method = "_goose/session/extensions", response = GetSessionExtensionsResponse)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
|
|||||||
@@ -35,6 +35,21 @@
|
|||||||
"requestType": "GetExtensionsRequest",
|
"requestType": "GetExtensionsRequest",
|
||||||
"responseType": "GetExtensionsResponse"
|
"responseType": "GetExtensionsResponse"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"method": "_goose/config/extensions/add",
|
||||||
|
"requestType": "AddConfigExtensionRequest",
|
||||||
|
"responseType": "EmptyResponse"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "_goose/config/extensions/remove",
|
||||||
|
"requestType": "RemoveConfigExtensionRequest",
|
||||||
|
"responseType": "EmptyResponse"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "_goose/config/extensions/toggle",
|
||||||
|
"requestType": "ToggleConfigExtensionRequest",
|
||||||
|
"responseType": "EmptyResponse"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"method": "_goose/session/extensions",
|
"method": "_goose/session/extensions",
|
||||||
"requestType": "GetSessionExtensionsRequest",
|
"requestType": "GetSessionExtensionsRequest",
|
||||||
|
|||||||
@@ -151,7 +151,7 @@
|
|||||||
"extensions": {
|
"extensions": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {},
|
"items": {},
|
||||||
"description": "Array of ExtensionEntry objects with `enabled` flag and config details."
|
"description": "Array of ExtensionEntry objects with `enabled` flag, `configKey`, and flattened config details."
|
||||||
},
|
},
|
||||||
"warnings": {
|
"warnings": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
@@ -168,6 +168,60 @@
|
|||||||
"x-side": "agent",
|
"x-side": "agent",
|
||||||
"x-method": "_goose/config/extensions"
|
"x-method": "_goose/config/extensions"
|
||||||
},
|
},
|
||||||
|
"AddConfigExtensionRequest": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"extensionConfig": {
|
||||||
|
"description": "Extension configuration. Must be a JSON object matching one of the\n`ExtensionConfig` variants (e.g. `stdio`, `streamable_http`, `builtin`).\n`name` and `enabled` are injected server-side.",
|
||||||
|
"default": null
|
||||||
|
},
|
||||||
|
"enabled": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"name"
|
||||||
|
],
|
||||||
|
"description": "Persist a new extension to the user's global goose config.",
|
||||||
|
"x-side": "agent",
|
||||||
|
"x-method": "_goose/config/extensions/add"
|
||||||
|
},
|
||||||
|
"RemoveConfigExtensionRequest": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"configKey": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"configKey"
|
||||||
|
],
|
||||||
|
"description": "Remove a persisted extension from the user's global goose config.",
|
||||||
|
"x-side": "agent",
|
||||||
|
"x-method": "_goose/config/extensions/remove"
|
||||||
|
},
|
||||||
|
"ToggleConfigExtensionRequest": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"configKey": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"enabled": {
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"configKey",
|
||||||
|
"enabled"
|
||||||
|
],
|
||||||
|
"description": "Toggle the `enabled` flag for a persisted extension in the user's global goose config.",
|
||||||
|
"x-side": "agent",
|
||||||
|
"x-method": "_goose/config/extensions/toggle"
|
||||||
|
},
|
||||||
"GetSessionExtensionsRequest": {
|
"GetSessionExtensionsRequest": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -1408,6 +1462,33 @@
|
|||||||
"description": "Params for _goose/config/extensions",
|
"description": "Params for _goose/config/extensions",
|
||||||
"title": "GetExtensionsRequest"
|
"title": "GetExtensionsRequest"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/$defs/AddConfigExtensionRequest"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Params for _goose/config/extensions/add",
|
||||||
|
"title": "AddConfigExtensionRequest"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/$defs/RemoveConfigExtensionRequest"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Params for _goose/config/extensions/remove",
|
||||||
|
"title": "RemoveConfigExtensionRequest"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/$defs/ToggleConfigExtensionRequest"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Params for _goose/config/extensions/toggle",
|
||||||
|
"title": "ToggleConfigExtensionRequest"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"allOf": [
|
"allOf": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2870,7 +2870,17 @@ impl GooseAcpAgent {
|
|||||||
let warnings = crate::config::extensions::get_warnings();
|
let warnings = crate::config::extensions::get_warnings();
|
||||||
let extensions_json = extensions
|
let extensions_json = extensions
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|e| serde_json::to_value(&e))
|
.map(|e| {
|
||||||
|
let config_key = e.config.key();
|
||||||
|
let mut value = serde_json::to_value(&e)?;
|
||||||
|
if let Some(obj) = value.as_object_mut() {
|
||||||
|
obj.insert(
|
||||||
|
"config_key".to_string(),
|
||||||
|
serde_json::Value::String(config_key),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ok::<_, serde_json::Error>(value)
|
||||||
|
})
|
||||||
.collect::<Result<Vec<_>, _>>()
|
.collect::<Result<Vec<_>, _>>()
|
||||||
.map_err(|e| sacp::Error::internal_error().data(e.to_string()))?;
|
.map_err(|e| sacp::Error::internal_error().data(e.to_string()))?;
|
||||||
Ok(GetExtensionsResponse {
|
Ok(GetExtensionsResponse {
|
||||||
@@ -2879,6 +2889,63 @@ impl GooseAcpAgent {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[custom_method(AddConfigExtensionRequest)]
|
||||||
|
async fn on_add_config_extension(
|
||||||
|
&self,
|
||||||
|
req: AddConfigExtensionRequest,
|
||||||
|
) -> Result<EmptyResponse, sacp::Error> {
|
||||||
|
let mut obj = match req.extension_config {
|
||||||
|
serde_json::Value::Object(obj) => obj,
|
||||||
|
_ => {
|
||||||
|
return Err(
|
||||||
|
sacp::Error::invalid_params().data("extensionConfig must be a JSON object")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
obj.insert(
|
||||||
|
"name".to_string(),
|
||||||
|
serde_json::Value::String(req.name.clone()),
|
||||||
|
);
|
||||||
|
|
||||||
|
let config: crate::agents::ExtensionConfig =
|
||||||
|
serde_json::from_value(serde_json::Value::Object(obj))
|
||||||
|
.map_err(|e| sacp::Error::invalid_params().data(format!("bad config: {e}")))?;
|
||||||
|
|
||||||
|
crate::config::extensions::set_extension(crate::config::extensions::ExtensionEntry {
|
||||||
|
enabled: req.enabled,
|
||||||
|
config,
|
||||||
|
});
|
||||||
|
Ok(EmptyResponse {})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[custom_method(RemoveConfigExtensionRequest)]
|
||||||
|
async fn on_remove_config_extension(
|
||||||
|
&self,
|
||||||
|
req: RemoveConfigExtensionRequest,
|
||||||
|
) -> Result<EmptyResponse, sacp::Error> {
|
||||||
|
let keys = crate::config::extensions::get_all_extension_names();
|
||||||
|
if !keys.iter().any(|k| k == &req.config_key) {
|
||||||
|
return Err(sacp::Error::invalid_params()
|
||||||
|
.data(format!("Extension '{}' not found", req.config_key)));
|
||||||
|
}
|
||||||
|
crate::config::extensions::remove_extension(&req.config_key);
|
||||||
|
Ok(EmptyResponse {})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[custom_method(ToggleConfigExtensionRequest)]
|
||||||
|
async fn on_toggle_config_extension(
|
||||||
|
&self,
|
||||||
|
req: ToggleConfigExtensionRequest,
|
||||||
|
) -> Result<EmptyResponse, sacp::Error> {
|
||||||
|
let keys = crate::config::extensions::get_all_extension_names();
|
||||||
|
if !keys.iter().any(|k| k == &req.config_key) {
|
||||||
|
return Err(sacp::Error::invalid_params()
|
||||||
|
.data(format!("Extension '{}' not found", req.config_key)));
|
||||||
|
}
|
||||||
|
crate::config::extensions::set_extension_enabled(&req.config_key, req.enabled);
|
||||||
|
Ok(EmptyResponse {})
|
||||||
|
}
|
||||||
|
|
||||||
#[custom_method(GetSessionExtensionsRequest)]
|
#[custom_method(GetSessionExtensionsRequest)]
|
||||||
async fn on_get_session_extensions(
|
async fn on_get_session_extensions(
|
||||||
&self,
|
&self,
|
||||||
|
|||||||
@@ -1,168 +0,0 @@
|
|||||||
use serde_json::Value;
|
|
||||||
use tauri::State;
|
|
||||||
|
|
||||||
use crate::services::goose_config::GooseConfig;
|
|
||||||
|
|
||||||
fn yaml_to_json(yaml: serde_yaml::Value) -> Value {
|
|
||||||
match yaml {
|
|
||||||
serde_yaml::Value::Null => Value::Null,
|
|
||||||
serde_yaml::Value::Bool(b) => Value::Bool(b),
|
|
||||||
serde_yaml::Value::Number(n) => {
|
|
||||||
if let Some(i) = n.as_i64() {
|
|
||||||
Value::Number(i.into())
|
|
||||||
} else if let Some(u) = n.as_u64() {
|
|
||||||
Value::Number(u.into())
|
|
||||||
} else if let Some(f) = n.as_f64() {
|
|
||||||
serde_json::Number::from_f64(f)
|
|
||||||
.map(Value::Number)
|
|
||||||
.unwrap_or(Value::Null)
|
|
||||||
} else {
|
|
||||||
Value::Null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
serde_yaml::Value::String(s) => Value::String(s),
|
|
||||||
serde_yaml::Value::Sequence(seq) => {
|
|
||||||
Value::Array(seq.into_iter().map(yaml_to_json).collect())
|
|
||||||
}
|
|
||||||
serde_yaml::Value::Mapping(map) => {
|
|
||||||
let obj = map
|
|
||||||
.into_iter()
|
|
||||||
.filter_map(|(k, v)| {
|
|
||||||
let key = match k {
|
|
||||||
serde_yaml::Value::String(s) => s,
|
|
||||||
other => serde_yaml::to_string(&other).ok()?.trim().to_string(),
|
|
||||||
};
|
|
||||||
Some((key, yaml_to_json(v)))
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
Value::Object(obj)
|
|
||||||
}
|
|
||||||
serde_yaml::Value::Tagged(tagged) => yaml_to_json(tagged.value),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn json_to_yaml(json: Value) -> serde_yaml::Value {
|
|
||||||
match json {
|
|
||||||
Value::Null => serde_yaml::Value::Null,
|
|
||||||
Value::Bool(b) => serde_yaml::Value::Bool(b),
|
|
||||||
Value::Number(n) => {
|
|
||||||
if let Some(i) = n.as_i64() {
|
|
||||||
serde_yaml::Value::Number(i.into())
|
|
||||||
} else if let Some(u) = n.as_u64() {
|
|
||||||
serde_yaml::Value::Number(u.into())
|
|
||||||
} else if let Some(f) = n.as_f64() {
|
|
||||||
serde_yaml::Value::Number(f.into())
|
|
||||||
} else {
|
|
||||||
serde_yaml::Value::Null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Value::String(s) => serde_yaml::Value::String(s),
|
|
||||||
Value::Array(arr) => {
|
|
||||||
serde_yaml::Value::Sequence(arr.into_iter().map(json_to_yaml).collect())
|
|
||||||
}
|
|
||||||
Value::Object(obj) => {
|
|
||||||
let mut map = serde_yaml::Mapping::new();
|
|
||||||
for (k, v) in obj {
|
|
||||||
map.insert(serde_yaml::Value::String(k), json_to_yaml(v));
|
|
||||||
}
|
|
||||||
serde_yaml::Value::Mapping(map)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn name_to_key(name: &str) -> String {
|
|
||||||
let mut result = String::with_capacity(name.len());
|
|
||||||
for c in name.chars() {
|
|
||||||
match c {
|
|
||||||
c if c.is_ascii_alphanumeric() || c == '_' || c == '-' => result.push(c),
|
|
||||||
c if c.is_whitespace() => continue,
|
|
||||||
_ => result.push('_'),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result.to_lowercase()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub fn list_extensions(config: State<'_, GooseConfig>) -> Result<Vec<Value>, String> {
|
|
||||||
let raw = config.get_extensions_raw();
|
|
||||||
let mut entries = Vec::with_capacity(raw.len());
|
|
||||||
|
|
||||||
for (k, v) in raw {
|
|
||||||
let key = match k {
|
|
||||||
serde_yaml::Value::String(s) => s,
|
|
||||||
_ => continue,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut json = yaml_to_json(v);
|
|
||||||
|
|
||||||
if let Value::Object(ref mut obj) = json {
|
|
||||||
if !obj.contains_key("type") {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
obj.insert("config_key".to_string(), Value::String(key.clone()));
|
|
||||||
obj.entry("name".to_string())
|
|
||||||
.or_insert_with(|| Value::String(key));
|
|
||||||
obj.entry("enabled".to_string())
|
|
||||||
.or_insert(Value::Bool(false));
|
|
||||||
entries.push(json);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(entries)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub fn add_extension(
|
|
||||||
name: String,
|
|
||||||
extension_config: Value,
|
|
||||||
enabled: bool,
|
|
||||||
config: State<'_, GooseConfig>,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
let key = name_to_key(&name);
|
|
||||||
let mut raw = config.get_extensions_raw();
|
|
||||||
|
|
||||||
let mut entry = match extension_config {
|
|
||||||
Value::Object(obj) => obj,
|
|
||||||
_ => return Err("extension_config must be a JSON object".to_string()),
|
|
||||||
};
|
|
||||||
|
|
||||||
entry.insert("enabled".to_string(), Value::Bool(enabled));
|
|
||||||
entry.insert("name".to_string(), Value::String(name));
|
|
||||||
|
|
||||||
let yaml_value = json_to_yaml(Value::Object(entry));
|
|
||||||
raw.insert(serde_yaml::Value::String(key), yaml_value);
|
|
||||||
|
|
||||||
config.set_extensions_raw(raw)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub fn remove_extension(config_key: String, config: State<'_, GooseConfig>) -> Result<(), String> {
|
|
||||||
let mut raw = config.get_extensions_raw();
|
|
||||||
let yaml_key = serde_yaml::Value::String(config_key.clone());
|
|
||||||
if raw.remove(&yaml_key).is_none() {
|
|
||||||
return Err(format!("Extension '{}' not found", config_key));
|
|
||||||
}
|
|
||||||
config.set_extensions_raw(raw)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub fn toggle_extension(
|
|
||||||
config_key: String,
|
|
||||||
enabled: bool,
|
|
||||||
config: State<'_, GooseConfig>,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
let mut raw = config.get_extensions_raw();
|
|
||||||
|
|
||||||
let yaml_key = serde_yaml::Value::String(config_key.clone());
|
|
||||||
if let Some(entry) = raw.get_mut(&yaml_key) {
|
|
||||||
if let serde_yaml::Value::Mapping(ref mut map) = entry {
|
|
||||||
map.insert(
|
|
||||||
serde_yaml::Value::String("enabled".to_string()),
|
|
||||||
serde_yaml::Value::Bool(enabled),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
config.set_extensions_raw(raw)
|
|
||||||
} else {
|
|
||||||
Err(format!("Extension '{}' not found", config_key))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,7 +3,6 @@ pub mod agent_setup;
|
|||||||
pub mod agents;
|
pub mod agents;
|
||||||
pub mod credentials;
|
pub mod credentials;
|
||||||
pub mod doctor;
|
pub mod doctor;
|
||||||
pub mod extensions;
|
|
||||||
pub mod git;
|
pub mod git;
|
||||||
pub mod git_changes;
|
pub mod git_changes;
|
||||||
pub mod model_setup;
|
pub mod model_setup;
|
||||||
|
|||||||
@@ -56,10 +56,6 @@ pub fn run() {
|
|||||||
commands::projects::restore_project,
|
commands::projects::restore_project,
|
||||||
commands::doctor::run_doctor,
|
commands::doctor::run_doctor,
|
||||||
commands::doctor::run_doctor_fix,
|
commands::doctor::run_doctor_fix,
|
||||||
commands::extensions::list_extensions,
|
|
||||||
commands::extensions::add_extension,
|
|
||||||
commands::extensions::remove_extension,
|
|
||||||
commands::extensions::toggle_extension,
|
|
||||||
commands::git::get_git_state,
|
commands::git::get_git_state,
|
||||||
commands::git_changes::get_changed_files,
|
commands::git_changes::get_changed_files,
|
||||||
commands::git::git_switch_branch,
|
commands::git::git_switch_branch,
|
||||||
|
|||||||
@@ -321,26 +321,6 @@ impl GooseConfig {
|
|||||||
.collect())
|
.collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_extensions_raw(&self) -> serde_yaml::Mapping {
|
|
||||||
let config = self.read_config_map();
|
|
||||||
let key = serde_yaml::Value::String("extensions".to_string());
|
|
||||||
config
|
|
||||||
.get(&key)
|
|
||||||
.and_then(|v| v.as_mapping())
|
|
||||||
.cloned()
|
|
||||||
.unwrap_or_default()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_extensions_raw(&self, extensions: serde_yaml::Mapping) -> Result<(), String> {
|
|
||||||
let _guard = self.guard.lock().unwrap();
|
|
||||||
let mut config = self.read_config_map();
|
|
||||||
config.insert(
|
|
||||||
serde_yaml::Value::String("extensions".to_string()),
|
|
||||||
serde_yaml::Value::Mapping(extensions),
|
|
||||||
);
|
|
||||||
self.write_config_map(&config)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn delete_all_provider_fields(&self, provider_id: &str) -> Result<(), String> {
|
pub fn delete_all_provider_fields(&self, provider_id: &str) -> Result<(), String> {
|
||||||
let def = find_provider_def(provider_id)
|
let def = find_provider_def(provider_id)
|
||||||
.ok_or_else(|| format!("Unknown provider '{provider_id}'"))?;
|
.ok_or_else(|| format!("Unknown provider '{provider_id}'"))?;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { invoke } from "@tauri-apps/api/core";
|
import { getClient } from "@/shared/api/acpConnection";
|
||||||
import type { ExtensionConfig, ExtensionEntry } from "../types";
|
import type { ExtensionConfig, ExtensionEntry } from "../types";
|
||||||
|
|
||||||
export function nameToKey(name: string): string {
|
export function nameToKey(name: string): string {
|
||||||
@@ -9,7 +9,9 @@ export function nameToKey(name: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function listExtensions(): Promise<ExtensionEntry[]> {
|
export async function listExtensions(): Promise<ExtensionEntry[]> {
|
||||||
return invoke("list_extensions");
|
const client = await getClient();
|
||||||
|
const response = await client.goose.GooseConfigExtensions({});
|
||||||
|
return response.extensions as ExtensionEntry[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function addExtension(
|
export async function addExtension(
|
||||||
@@ -17,7 +19,8 @@ export async function addExtension(
|
|||||||
extensionConfig: ExtensionConfig,
|
extensionConfig: ExtensionConfig,
|
||||||
enabled: boolean,
|
enabled: boolean,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
return invoke("add_extension", {
|
const client = await getClient();
|
||||||
|
await client.goose.GooseConfigExtensionsAdd({
|
||||||
name,
|
name,
|
||||||
extensionConfig,
|
extensionConfig,
|
||||||
enabled,
|
enabled,
|
||||||
@@ -25,12 +28,14 @@ export async function addExtension(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function removeExtension(configKey: string): Promise<void> {
|
export async function removeExtension(configKey: string): Promise<void> {
|
||||||
return invoke("remove_extension", { configKey });
|
const client = await getClient();
|
||||||
|
await client.goose.GooseConfigExtensionsRemove({ configKey });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function toggleExtension(
|
export async function toggleExtension(
|
||||||
configKey: string,
|
configKey: string,
|
||||||
enabled: boolean,
|
enabled: boolean,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
return invoke("toggle_extension", { configKey, enabled });
|
const client = await getClient();
|
||||||
|
await client.goose.GooseConfigExtensionsToggle({ configKey, enabled });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ export interface ExtMethodProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
|
AddConfigExtensionRequest,
|
||||||
AddExtensionRequest,
|
AddExtensionRequest,
|
||||||
ArchiveSessionRequest,
|
ArchiveSessionRequest,
|
||||||
CheckSecretRequest,
|
CheckSecretRequest,
|
||||||
@@ -52,9 +53,11 @@ import type {
|
|||||||
ReadResourceResponse,
|
ReadResourceResponse,
|
||||||
RefreshProviderInventoryRequest,
|
RefreshProviderInventoryRequest,
|
||||||
RefreshProviderInventoryResponse,
|
RefreshProviderInventoryResponse,
|
||||||
|
RemoveConfigExtensionRequest,
|
||||||
RemoveConfigRequest,
|
RemoveConfigRequest,
|
||||||
RemoveExtensionRequest,
|
RemoveExtensionRequest,
|
||||||
RemoveSecretRequest,
|
RemoveSecretRequest,
|
||||||
|
ToggleConfigExtensionRequest,
|
||||||
UnarchiveSessionRequest,
|
UnarchiveSessionRequest,
|
||||||
UpdateSessionProjectRequest,
|
UpdateSessionProjectRequest,
|
||||||
UpdateSourceRequest,
|
UpdateSourceRequest,
|
||||||
@@ -123,6 +126,24 @@ export class GooseExtClient {
|
|||||||
return zGetExtensionsResponse.parse(raw) as GetExtensionsResponse;
|
return zGetExtensionsResponse.parse(raw) as GetExtensionsResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async GooseConfigExtensionsAdd(
|
||||||
|
params: AddConfigExtensionRequest,
|
||||||
|
): Promise<void> {
|
||||||
|
await this.conn.extMethod("_goose/config/extensions/add", params);
|
||||||
|
}
|
||||||
|
|
||||||
|
async GooseConfigExtensionsRemove(
|
||||||
|
params: RemoveConfigExtensionRequest,
|
||||||
|
): Promise<void> {
|
||||||
|
await this.conn.extMethod("_goose/config/extensions/remove", params);
|
||||||
|
}
|
||||||
|
|
||||||
|
async GooseConfigExtensionsToggle(
|
||||||
|
params: ToggleConfigExtensionRequest,
|
||||||
|
): Promise<void> {
|
||||||
|
await this.conn.extMethod("_goose/config/extensions/toggle", params);
|
||||||
|
}
|
||||||
|
|
||||||
async GooseSessionExtensions(
|
async GooseSessionExtensions(
|
||||||
params: GetSessionExtensionsRequest,
|
params: GetSessionExtensionsRequest,
|
||||||
): Promise<GetSessionExtensionsResponse> {
|
): Promise<GetSessionExtensionsResponse> {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// This file is auto-generated by @hey-api/openapi-ts
|
// This file is auto-generated by @hey-api/openapi-ts
|
||||||
|
|
||||||
export type { AddExtensionRequest, ArchiveSessionRequest, CheckSecretRequest, CheckSecretResponse, CreateSourceRequest, CreateSourceResponse, DeleteSessionRequest, DeleteSourceRequest, DictationConfigRequest, DictationConfigResponse, DictationDownloadProgress, DictationLocalModelStatus, DictationModelCancelRequest, DictationModelDeleteRequest, DictationModelDownloadProgressRequest, DictationModelDownloadProgressResponse, DictationModelDownloadRequest, DictationModelOption, DictationModelSelectRequest, DictationModelsListRequest, DictationModelsListResponse, DictationProviderStatusEntry, DictationTranscribeRequest, DictationTranscribeResponse, EmptyResponse, ExportSessionRequest, ExportSessionResponse, ExportSourceRequest, ExportSourceResponse, ExtRequest, ExtResponse, GetExtensionsRequest, GetExtensionsResponse, GetSessionExtensionsRequest, GetSessionExtensionsResponse, GetToolsRequest, GetToolsResponse, ImportSessionRequest, ImportSessionResponse, ImportSourcesRequest, ImportSourcesResponse, ListProvidersRequest, ListProvidersResponse, ListSourcesRequest, ListSourcesResponse, ProviderConfigKey, ProviderInventoryEntryDto, ProviderInventoryModelDto, ReadConfigRequest, ReadConfigResponse, ReadResourceRequest, ReadResourceResponse, RefreshProviderInventoryRequest, RefreshProviderInventoryResponse, RefreshProviderInventorySkipDto, RefreshProviderInventorySkipReasonDto, RemoveConfigRequest, RemoveExtensionRequest, RemoveSecretRequest, SourceEntry, SourceType, UnarchiveSessionRequest, UpdateSessionProjectRequest, UpdateSourceRequest, UpdateSourceResponse, UpdateWorkingDirRequest, UpsertConfigRequest, UpsertSecretRequest } from './types.gen.js';
|
export type { AddConfigExtensionRequest, AddExtensionRequest, ArchiveSessionRequest, CheckSecretRequest, CheckSecretResponse, CreateSourceRequest, CreateSourceResponse, DeleteSessionRequest, DeleteSourceRequest, DictationConfigRequest, DictationConfigResponse, DictationDownloadProgress, DictationLocalModelStatus, DictationModelCancelRequest, DictationModelDeleteRequest, DictationModelDownloadProgressRequest, DictationModelDownloadProgressResponse, DictationModelDownloadRequest, DictationModelOption, DictationModelSelectRequest, DictationModelsListRequest, DictationModelsListResponse, DictationProviderStatusEntry, DictationTranscribeRequest, DictationTranscribeResponse, EmptyResponse, ExportSessionRequest, ExportSessionResponse, ExportSourceRequest, ExportSourceResponse, ExtRequest, ExtResponse, GetExtensionsRequest, GetExtensionsResponse, GetSessionExtensionsRequest, GetSessionExtensionsResponse, GetToolsRequest, GetToolsResponse, ImportSessionRequest, ImportSessionResponse, ImportSourcesRequest, ImportSourcesResponse, ListProvidersRequest, ListProvidersResponse, ListSourcesRequest, ListSourcesResponse, ProviderConfigKey, ProviderInventoryEntryDto, ProviderInventoryModelDto, ReadConfigRequest, ReadConfigResponse, ReadResourceRequest, ReadResourceResponse, RefreshProviderInventoryRequest, RefreshProviderInventoryResponse, RefreshProviderInventorySkipDto, RefreshProviderInventorySkipReasonDto, RemoveConfigExtensionRequest, RemoveConfigRequest, RemoveExtensionRequest, RemoveSecretRequest, SourceEntry, SourceType, ToggleConfigExtensionRequest, UnarchiveSessionRequest, UpdateSessionProjectRequest, UpdateSourceRequest, UpdateSourceResponse, UpdateWorkingDirRequest, UpsertConfigRequest, UpsertSecretRequest } from './types.gen.js';
|
||||||
|
|
||||||
export const GOOSE_EXT_METHODS = [
|
export const GOOSE_EXT_METHODS = [
|
||||||
{
|
{
|
||||||
@@ -38,6 +38,21 @@ export const GOOSE_EXT_METHODS = [
|
|||||||
requestType: "GetExtensionsRequest",
|
requestType: "GetExtensionsRequest",
|
||||||
responseType: "GetExtensionsResponse",
|
responseType: "GetExtensionsResponse",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
method: "_goose/config/extensions/add",
|
||||||
|
requestType: "AddConfigExtensionRequest",
|
||||||
|
responseType: "EmptyResponse",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: "_goose/config/extensions/remove",
|
||||||
|
requestType: "RemoveConfigExtensionRequest",
|
||||||
|
responseType: "EmptyResponse",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: "_goose/config/extensions/toggle",
|
||||||
|
requestType: "ToggleConfigExtensionRequest",
|
||||||
|
responseType: "EmptyResponse",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
method: "_goose/session/extensions",
|
method: "_goose/session/extensions",
|
||||||
requestType: "GetSessionExtensionsRequest",
|
requestType: "GetSessionExtensionsRequest",
|
||||||
|
|||||||
@@ -90,12 +90,41 @@ export type GetExtensionsRequest = {
|
|||||||
*/
|
*/
|
||||||
export type GetExtensionsResponse = {
|
export type GetExtensionsResponse = {
|
||||||
/**
|
/**
|
||||||
* Array of ExtensionEntry objects with `enabled` flag and config details.
|
* Array of ExtensionEntry objects with `enabled` flag, `configKey`, and flattened config details.
|
||||||
*/
|
*/
|
||||||
extensions: Array<unknown>;
|
extensions: Array<unknown>;
|
||||||
warnings: Array<string>;
|
warnings: Array<string>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Persist a new extension to the user's global goose config.
|
||||||
|
*/
|
||||||
|
export type AddConfigExtensionRequest = {
|
||||||
|
name: string;
|
||||||
|
/**
|
||||||
|
* Extension configuration. Must be a JSON object matching one of the
|
||||||
|
* `ExtensionConfig` variants (e.g. `stdio`, `streamable_http`, `builtin`).
|
||||||
|
* `name` and `enabled` are injected server-side.
|
||||||
|
*/
|
||||||
|
extensionConfig?: unknown;
|
||||||
|
enabled?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a persisted extension from the user's global goose config.
|
||||||
|
*/
|
||||||
|
export type RemoveConfigExtensionRequest = {
|
||||||
|
configKey: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle the `enabled` flag for a persisted extension in the user's global goose config.
|
||||||
|
*/
|
||||||
|
export type ToggleConfigExtensionRequest = {
|
||||||
|
configKey: string;
|
||||||
|
enabled: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
export type GetSessionExtensionsRequest = {
|
export type GetSessionExtensionsRequest = {
|
||||||
sessionId: string;
|
sessionId: string;
|
||||||
};
|
};
|
||||||
@@ -626,7 +655,7 @@ export type DictationModelSelectRequest = {
|
|||||||
export type ExtRequest = {
|
export type ExtRequest = {
|
||||||
id: string;
|
id: string;
|
||||||
method: string;
|
method: string;
|
||||||
params?: AddExtensionRequest | RemoveExtensionRequest | GetToolsRequest | ReadResourceRequest | UpdateWorkingDirRequest | DeleteSessionRequest | GetExtensionsRequest | GetSessionExtensionsRequest | ListProvidersRequest | RefreshProviderInventoryRequest | ReadConfigRequest | UpsertConfigRequest | RemoveConfigRequest | CheckSecretRequest | UpsertSecretRequest | RemoveSecretRequest | ExportSessionRequest | ImportSessionRequest | UpdateSessionProjectRequest | ArchiveSessionRequest | UnarchiveSessionRequest | CreateSourceRequest | ListSourcesRequest | UpdateSourceRequest | DeleteSourceRequest | ExportSourceRequest | ImportSourcesRequest | DictationTranscribeRequest | DictationConfigRequest | DictationModelsListRequest | DictationModelDownloadRequest | DictationModelDownloadProgressRequest | DictationModelCancelRequest | DictationModelDeleteRequest | DictationModelSelectRequest | {
|
params?: AddExtensionRequest | RemoveExtensionRequest | GetToolsRequest | ReadResourceRequest | UpdateWorkingDirRequest | DeleteSessionRequest | GetExtensionsRequest | AddConfigExtensionRequest | RemoveConfigExtensionRequest | ToggleConfigExtensionRequest | GetSessionExtensionsRequest | ListProvidersRequest | RefreshProviderInventoryRequest | ReadConfigRequest | UpsertConfigRequest | RemoveConfigRequest | CheckSecretRequest | UpsertSecretRequest | RemoveSecretRequest | ExportSessionRequest | ImportSessionRequest | UpdateSessionProjectRequest | ArchiveSessionRequest | UnarchiveSessionRequest | CreateSourceRequest | ListSourcesRequest | UpdateSourceRequest | DeleteSourceRequest | ExportSourceRequest | ImportSourcesRequest | DictationTranscribeRequest | DictationConfigRequest | DictationModelsListRequest | DictationModelDownloadRequest | DictationModelDownloadProgressRequest | DictationModelCancelRequest | DictationModelDeleteRequest | DictationModelSelectRequest | {
|
||||||
[key: string]: unknown;
|
[key: string]: unknown;
|
||||||
} | null;
|
} | null;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -81,6 +81,30 @@ export const zGetExtensionsResponse = z.object({
|
|||||||
warnings: z.array(z.string())
|
warnings: z.array(z.string())
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Persist a new extension to the user's global goose config.
|
||||||
|
*/
|
||||||
|
export const zAddConfigExtensionRequest = z.object({
|
||||||
|
name: z.string(),
|
||||||
|
extensionConfig: z.unknown().optional().default(null),
|
||||||
|
enabled: z.boolean().optional().default(false)
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a persisted extension from the user's global goose config.
|
||||||
|
*/
|
||||||
|
export const zRemoveConfigExtensionRequest = z.object({
|
||||||
|
configKey: z.string()
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle the `enabled` flag for a persisted extension in the user's global goose config.
|
||||||
|
*/
|
||||||
|
export const zToggleConfigExtensionRequest = z.object({
|
||||||
|
configKey: z.string(),
|
||||||
|
enabled: z.boolean()
|
||||||
|
});
|
||||||
|
|
||||||
export const zGetSessionExtensionsRequest = z.object({
|
export const zGetSessionExtensionsRequest = z.object({
|
||||||
sessionId: z.string()
|
sessionId: z.string()
|
||||||
});
|
});
|
||||||
@@ -594,6 +618,9 @@ export const zExtRequest = z.object({
|
|||||||
zUpdateWorkingDirRequest,
|
zUpdateWorkingDirRequest,
|
||||||
zDeleteSessionRequest,
|
zDeleteSessionRequest,
|
||||||
zGetExtensionsRequest,
|
zGetExtensionsRequest,
|
||||||
|
zAddConfigExtensionRequest,
|
||||||
|
zRemoveConfigExtensionRequest,
|
||||||
|
zToggleConfigExtensionRequest,
|
||||||
zGetSessionExtensionsRequest,
|
zGetSessionExtensionsRequest,
|
||||||
zListProvidersRequest,
|
zListProvidersRequest,
|
||||||
zRefreshProviderInventoryRequest,
|
zRefreshProviderInventoryRequest,
|
||||||
|
|||||||
Reference in New Issue
Block a user