Compare commits

...

3 Commits

Author SHA1 Message Date
canvrno-oai
fd4b37f969 Move error reporting to prevent missed reports 2026-03-13 15:04:53 -07:00
canvrno-oai
bb831401ca _default edge case 2026-03-13 13:29:38 -07:00
canvrno-oai
99738d1c52 Update config.toml with enablement setting when installing apps 2026-03-13 13:29:33 -07:00
2 changed files with 131 additions and 2 deletions

View File

@@ -63,6 +63,7 @@ use tracing::warn;
const DEFAULT_SKILLS_DIR_NAME: &str = "skills";
const DEFAULT_MCP_CONFIG_FILE: &str = ".mcp.json";
const DEFAULT_APP_CONFIG_FILE: &str = ".app.json";
const RESERVED_APP_DEFAULT_KEY: &str = "_default";
const OPENAI_CURATED_MARKETPLACE_NAME: &str = "openai-curated";
const REMOTE_PLUGIN_SYNC_TIMEOUT: Duration = Duration::from_secs(30);
static CURATED_REPO_SYNC_STARTED: AtomicBool = AtomicBool::new(false);
@@ -505,7 +506,8 @@ impl PluginsManager {
.await
.map_err(PluginInstallError::join)??;
ConfigService::new_with_defaults(self.codex_home.clone())
let config_service = ConfigService::new_with_defaults(self.codex_home.clone());
config_service
.write_value(ConfigValueWriteParams {
key_path: format!("plugins.{}", result.plugin_id.as_key()),
value: json!({
@@ -519,6 +521,32 @@ impl PluginsManager {
.map(|_| ())
.map_err(PluginInstallError::from)?;
for app in load_plugin_apps(result.installed_path.as_path()) {
if app.0 == RESERVED_APP_DEFAULT_KEY {
warn!(
plugin_id = ?result.plugin_id,
"skipping plugin app enablement for reserved app id"
);
continue;
}
let mut app_config = JsonMap::new();
app_config.insert("enabled".to_string(), JsonValue::Bool(true));
let mut apps_config = JsonMap::new();
apps_config.insert(app.0, JsonValue::Object(app_config));
config_service
.write_value(ConfigValueWriteParams {
key_path: "apps".to_string(),
value: JsonValue::Object(apps_config),
merge_strategy: MergeStrategy::Upsert,
file_path: None,
expected_version: None,
})
.await
.map(|_| ())
.map_err(PluginInstallError::from)?;
}
let analytics_events_client = match self.analytics_events_client.read() {
Ok(client) => client.clone(),
Err(err) => err.into_inner().clone(),

View File

@@ -835,12 +835,23 @@ fn load_plugins_rejects_invalid_plugin_keys() {
}
#[tokio::test]
async fn install_plugin_updates_config_with_relative_path_and_plugin_key() {
async fn install_plugin_updates_config_with_relative_path_and_enables_installed_apps() {
let tmp = tempfile::tempdir().unwrap();
let repo_root = tmp.path().join("repo");
fs::create_dir_all(repo_root.join(".git")).unwrap();
fs::create_dir_all(repo_root.join(".agents/plugins")).unwrap();
write_plugin(&repo_root, "sample-plugin", "sample-plugin");
fs::write(
repo_root.join("sample-plugin/.app.json"),
r#"{
"apps": {
"connector_example": {
"id": "connector_example"
}
}
}"#,
)
.unwrap();
fs::write(
repo_root.join(".agents/plugins/marketplace.json"),
r#"{
@@ -884,6 +895,96 @@ async fn install_plugin_updates_config_with_relative_path_and_plugin_key() {
let config = fs::read_to_string(tmp.path().join("config.toml")).unwrap();
assert!(config.contains(r#"[plugins."sample-plugin@debug"]"#));
assert!(config.contains("enabled = true"));
let config_toml: Value = toml::from_str(&config).unwrap();
assert_eq!(
config_toml
.get("apps")
.and_then(Value::as_table)
.and_then(|apps| apps.get("connector_example"))
.and_then(Value::as_table)
.and_then(|app| app.get("enabled"))
.and_then(Value::as_bool),
Some(true)
);
}
#[tokio::test]
async fn install_plugin_enables_existing_disabled_app_config() {
let tmp = tempfile::tempdir().unwrap();
let repo_root = tmp.path().join("repo");
fs::create_dir_all(repo_root.join(".git")).unwrap();
fs::create_dir_all(repo_root.join(".agents/plugins")).unwrap();
write_plugin(&repo_root, "sample-plugin", "sample-plugin");
fs::write(
repo_root.join("sample-plugin/.app.json"),
r#"{
"apps": {
"connector_example": {
"id": "connector_example"
}
}
}"#,
)
.unwrap();
fs::write(
tmp.path().join(CONFIG_TOML_FILE),
r#"[apps.connector_example]
enabled = false
display_name = "Example Connector"
"#,
)
.unwrap();
fs::write(
repo_root.join(".agents/plugins/marketplace.json"),
r#"{
"name": "debug",
"plugins": [
{
"name": "sample-plugin",
"source": {
"source": "local",
"path": "./sample-plugin"
},
"authPolicy": "ON_USE"
}
]
}"#,
)
.unwrap();
PluginsManager::new(tmp.path().to_path_buf())
.install_plugin(PluginInstallRequest {
plugin_name: "sample-plugin".to_string(),
marketplace_path: AbsolutePathBuf::try_from(
repo_root.join(".agents/plugins/marketplace.json"),
)
.unwrap(),
})
.await
.unwrap();
let config = fs::read_to_string(tmp.path().join(CONFIG_TOML_FILE)).unwrap();
let config_toml: Value = toml::from_str(&config).unwrap();
assert_eq!(
config_toml
.get("apps")
.and_then(Value::as_table)
.and_then(|apps| apps.get("connector_example"))
.and_then(Value::as_table)
.and_then(|app| app.get("enabled"))
.and_then(Value::as_bool),
Some(true)
);
assert_eq!(
config_toml
.get("apps")
.and_then(Value::as_table)
.and_then(|apps| apps.get("connector_example"))
.and_then(Value::as_table)
.and_then(|app| app.get("display_name"))
.and_then(Value::as_str),
Some("Example Connector")
);
}
#[tokio::test]