mirror of
https://github.com/openai/codex.git
synced 2026-11-02 08:19:52 +00:00
Prepare external agent migration for source adapters (#32884)
## What changed - Add an optional `source` selector to `externalAgentConfig/detect`, while preserving the existing behavior for omitted, `null`, and unrecognized values. - Pass `claude-code` as the source for TUI detection and import requests. - Move source-specific migration behavior behind an adapter boundary and consolidate session migration into `codex-external-agent-migration`. ## Testing - Cover explicit, omitted, and unknown detection source values with app-server protocol tests. - Add unit coverage for the extracted migration and hook helpers. GitOrigin-RevId: 63bd3dcb485f3ad7bd996022db4e351506aeea77
This commit is contained in:
committed by
copyberry
parent
0877afbe85
commit
bc8222b8d9
@@ -1 +0,0 @@
|
||||
pub(crate) mod external_agent_config;
|
||||
@@ -0,0 +1,6 @@
|
||||
mod processor;
|
||||
mod service;
|
||||
mod session_importer;
|
||||
|
||||
pub(crate) use processor::ExternalAgentConfigRequestProcessor;
|
||||
pub(crate) use processor::ExternalAgentConfigRequestProcessorArgs;
|
||||
+21
-23
@@ -1,20 +1,10 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::config::external_agent_config::ExternalAgentConfigDetectOptions;
|
||||
use crate::config::external_agent_config::ExternalAgentConfigImportItemResult as CoreImportItemResult;
|
||||
use crate::config::external_agent_config::ExternalAgentConfigImportOutcome as CoreImportOutcome;
|
||||
use crate::config::external_agent_config::ExternalAgentConfigImportRawError as CoreImportRawError;
|
||||
use crate::config::external_agent_config::ExternalAgentConfigMigrationItem as CoreMigrationItem;
|
||||
use crate::config::external_agent_config::ExternalAgentConfigMigrationItemType as CoreMigrationItemType;
|
||||
use crate::config::external_agent_config::ExternalAgentConfigService;
|
||||
use crate::config::external_agent_config::NamedMigration as CoreNamedMigration;
|
||||
use crate::config::external_agent_config::PendingPluginImport;
|
||||
use crate::config::external_agent_config::PluginImportOutcome;
|
||||
use crate::config::external_agent_config::record_import_error;
|
||||
use crate::config_manager::ConfigManager;
|
||||
use crate::error_code::internal_error;
|
||||
use crate::outgoing_message::ConnectionRequestId;
|
||||
use crate::outgoing_message::OutgoingMessageSender;
|
||||
use crate::request_processors::ConfigRequestProcessor;
|
||||
use codex_analytics::AnalyticsEventsClient;
|
||||
use codex_analytics::ExternalAgentConfigImportCompletedInput;
|
||||
use codex_analytics::ExternalAgentConfigImportFailureInput;
|
||||
@@ -41,7 +31,7 @@ use codex_app_server_protocol::ServerNotification;
|
||||
use codex_app_server_protocol::SkillMigration;
|
||||
use codex_arg0::Arg0DispatchPaths;
|
||||
use codex_core::ThreadManager;
|
||||
use codex_external_agent_sessions::ExternalAgentSessionMigration as CoreSessionMigration;
|
||||
use codex_external_agent_migration::sessions::ExternalAgentSessionMigration as CoreSessionMigration;
|
||||
use codex_rollout::StateDbHandle;
|
||||
use codex_state::ExternalAgentConfigImportFailureRecord;
|
||||
use codex_state::ExternalAgentConfigImportSuccessRecord;
|
||||
@@ -49,8 +39,18 @@ use codex_thread_store::ThreadStore;
|
||||
use std::collections::HashSet;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use super::ConfigRequestProcessor;
|
||||
use super::external_agent_session_import::ExternalAgentSessionImporter;
|
||||
use super::service::ExternalAgentConfigDetectOptions;
|
||||
use super::service::ExternalAgentConfigImportItemResult as CoreImportItemResult;
|
||||
use super::service::ExternalAgentConfigImportOutcome as CoreImportOutcome;
|
||||
use super::service::ExternalAgentConfigImportRawError as CoreImportRawError;
|
||||
use super::service::ExternalAgentConfigMigrationItem as CoreMigrationItem;
|
||||
use super::service::ExternalAgentConfigMigrationItemType as CoreMigrationItemType;
|
||||
use super::service::ExternalAgentConfigService;
|
||||
use super::service::NamedMigration as CoreNamedMigration;
|
||||
use super::service::PendingPluginImport;
|
||||
use super::service::PluginImportOutcome;
|
||||
use super::service::record_import_error;
|
||||
use super::session_importer::ExternalAgentSessionImporter;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -487,15 +487,13 @@ impl ExternalAgentConfigRequestProcessor {
|
||||
description: migration_item.description,
|
||||
cwd: migration_item.cwd,
|
||||
details: migration_item.details.map(|details| {
|
||||
crate::config::external_agent_config::MigrationDetails {
|
||||
super::service::MigrationDetails {
|
||||
plugins: details
|
||||
.plugins
|
||||
.into_iter()
|
||||
.map(|plugin| {
|
||||
crate::config::external_agent_config::PluginsMigration {
|
||||
marketplace_name: plugin.marketplace_name,
|
||||
plugin_names: plugin.plugin_names,
|
||||
}
|
||||
.map(|plugin| super::service::PluginsMigration {
|
||||
marketplace_name: plugin.marketplace_name,
|
||||
plugin_names: plugin.plugin_names,
|
||||
})
|
||||
.collect(),
|
||||
skills: details
|
||||
@@ -832,7 +830,7 @@ fn protocol_import_type_result(item_result: &CoreImportItemResult) -> ProtocolIm
|
||||
}
|
||||
|
||||
fn protocol_import_success(
|
||||
success: &crate::config::external_agent_config::ExternalAgentConfigImportSuccess,
|
||||
success: &super::service::ExternalAgentConfigImportSuccess,
|
||||
) -> ProtocolImportSuccess {
|
||||
ProtocolImportSuccess {
|
||||
item_type: protocol_migration_item_type(success.item_type),
|
||||
@@ -898,5 +896,5 @@ fn migration_items_need_runtime_refresh(items: &[ExternalAgentConfigMigrationIte
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "external_agent_config_processor_tests.rs"]
|
||||
mod external_agent_config_processor_tests;
|
||||
#[path = "processor_tests.rs"]
|
||||
mod tests;
|
||||
+229
-617
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,242 @@
|
||||
use codex_external_agent_migration::RewriteProfile;
|
||||
use codex_external_agent_migration::sessions::ExternalAgentSessionMigration;
|
||||
use codex_external_agent_migration::sessions::detect_recent_cla_sessions;
|
||||
use serde_json::Value as JsonValue;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::HashSet;
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use toml::Value as TomlValue;
|
||||
|
||||
use super::MigrationDetails;
|
||||
use super::source_cla;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub(super) struct InstructionSourceGroup {
|
||||
pub(super) scope: PathBuf,
|
||||
pub(super) sources: Vec<PathBuf>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub(super) struct MarketplaceImportSource {
|
||||
pub(super) source: String,
|
||||
pub(super) ref_name: Option<String>,
|
||||
}
|
||||
|
||||
pub(super) struct DetectedSourcePlugins {
|
||||
pub(super) description: String,
|
||||
pub(super) details: MigrationDetails,
|
||||
}
|
||||
|
||||
pub(super) struct PluginDetectionContext<'a> {
|
||||
pub(super) external_agent_home: &'a Path,
|
||||
pub(super) source_settings: &'a Path,
|
||||
pub(super) source_root: &'a Path,
|
||||
pub(super) settings: Option<&'a JsonValue>,
|
||||
pub(super) configured_plugin_ids: &'a HashSet<String>,
|
||||
pub(super) configured_marketplace_plugins: &'a BTreeMap<String, HashSet<String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
||||
pub(super) enum ExternalAgentSource {
|
||||
#[default]
|
||||
Cla,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub(super) enum SourceFeature {
|
||||
Config,
|
||||
Plugins,
|
||||
Sessions,
|
||||
}
|
||||
|
||||
impl ExternalAgentSource {
|
||||
pub(super) fn config_dir(self) -> &'static str {
|
||||
match self {
|
||||
Self::Cla => source_cla::CONFIG_DIR,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn supports(self, feature: SourceFeature) -> bool {
|
||||
match (self, feature) {
|
||||
(
|
||||
Self::Cla,
|
||||
SourceFeature::Config | SourceFeature::Plugins | SourceFeature::Sessions,
|
||||
) => true,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn settings_file_name(self, project_scope: bool) -> &'static str {
|
||||
match (self, project_scope) {
|
||||
(Self::Cla, _) => "settings.json",
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn effective_settings(
|
||||
self,
|
||||
source_settings: &Path,
|
||||
) -> io::Result<Option<JsonValue>> {
|
||||
match self {
|
||||
Self::Cla => source_cla::effective_settings(source_settings),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn detect_plugins(
|
||||
self,
|
||||
context: PluginDetectionContext<'_>,
|
||||
) -> io::Result<Option<DetectedSourcePlugins>> {
|
||||
match self {
|
||||
Self::Cla => Ok(source_cla::detect_plugins(&context)),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn can_detect_plugins(self, settings: Option<&JsonValue>) -> bool {
|
||||
match self {
|
||||
Self::Cla => source_cla::can_detect_plugins(settings),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn detect_recent_sessions(
|
||||
self,
|
||||
external_agent_home: &Path,
|
||||
codex_home: &Path,
|
||||
) -> io::Result<Vec<ExternalAgentSessionMigration>> {
|
||||
match self {
|
||||
Self::Cla => detect_recent_cla_sessions(external_agent_home, codex_home),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn marketplace_import_sources(
|
||||
self,
|
||||
external_agent_home: &Path,
|
||||
source_root: &Path,
|
||||
source_settings: &Path,
|
||||
) -> io::Result<BTreeMap<String, MarketplaceImportSource>> {
|
||||
match self {
|
||||
Self::Cla => Ok(source_cla::effective_settings(source_settings)?
|
||||
.as_ref()
|
||||
.map(|settings| {
|
||||
source_cla::marketplace_import_sources(
|
||||
settings,
|
||||
external_agent_home,
|
||||
source_root,
|
||||
)
|
||||
})
|
||||
.unwrap_or_default()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn append_config(
|
||||
self,
|
||||
root: &mut toml::map::Map<String, TomlValue>,
|
||||
settings: &serde_json::Map<String, JsonValue>,
|
||||
) {
|
||||
match self {
|
||||
Self::Cla => source_cla::append_config(root, settings),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn build_mcp_config(
|
||||
self,
|
||||
source_root: &Path,
|
||||
external_agent_home: &Path,
|
||||
settings: Option<&JsonValue>,
|
||||
) -> io::Result<TomlValue> {
|
||||
match self {
|
||||
Self::Cla => source_cla::build_mcp_config(source_root, external_agent_home, settings),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn mcp_source_path(self, source_root: PathBuf) -> PathBuf {
|
||||
match self {
|
||||
Self::Cla => source_root,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn repo_instruction_source_groups(
|
||||
self,
|
||||
repo_root: &Path,
|
||||
) -> io::Result<Vec<InstructionSourceGroup>> {
|
||||
match self {
|
||||
Self::Cla => source_cla::repo_instruction_source_groups(repo_root),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn home_instruction_sources(
|
||||
self,
|
||||
external_agent_home: &Path,
|
||||
) -> io::Result<Vec<PathBuf>> {
|
||||
match self {
|
||||
Self::Cla => source_cla::home_instruction_sources(external_agent_home),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn read_instruction_source(self, path: &Path) -> io::Result<String> {
|
||||
match self {
|
||||
Self::Cla => source_cla::read_instruction_source(path),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn import_commands(
|
||||
self,
|
||||
source_commands: &Path,
|
||||
target_skills: &Path,
|
||||
) -> io::Result<Vec<String>> {
|
||||
match self {
|
||||
Self::Cla => source_cla::import_source_commands(source_commands, target_skills),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn count_missing_commands(
|
||||
self,
|
||||
source_commands: &Path,
|
||||
target_skills: &Path,
|
||||
) -> io::Result<usize> {
|
||||
match self {
|
||||
Self::Cla => source_cla::count_missing_source_commands(source_commands, target_skills),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn missing_command_names(
|
||||
self,
|
||||
source_commands: &Path,
|
||||
target_skills: &Path,
|
||||
) -> io::Result<Vec<String>> {
|
||||
match self {
|
||||
Self::Cla => source_cla::missing_source_command_names(source_commands, target_skills),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn import_subagents(
|
||||
self,
|
||||
source_agents: &Path,
|
||||
target_agents: &Path,
|
||||
) -> io::Result<Vec<String>> {
|
||||
match self {
|
||||
Self::Cla => source_cla::import_source_subagents(source_agents, target_agents),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn hook_event_names(
|
||||
self,
|
||||
source_dir: &Path,
|
||||
target_hooks: &Path,
|
||||
) -> io::Result<Vec<String>> {
|
||||
match self {
|
||||
Self::Cla => source_cla::source_hook_event_names(source_dir, target_hooks),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn import_hooks(self, source_dir: &Path, target_hooks: &Path) -> io::Result<bool> {
|
||||
match self {
|
||||
Self::Cla => source_cla::import_source_hooks(source_dir, target_hooks),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn rewrite_profile(self) -> RewriteProfile {
|
||||
match self {
|
||||
Self::Cla => source_cla::REWRITE_PROFILE,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,483 @@
|
||||
use codex_core_plugins::marketplace_add::is_local_marketplace_source;
|
||||
use codex_external_agent_migration::CommandDescriptionMode;
|
||||
use codex_external_agent_migration::CommandMigrationProfile;
|
||||
use codex_external_agent_migration::RewriteProfile;
|
||||
use codex_external_agent_migration::build_mcp_config_from_external;
|
||||
use codex_external_agent_migration::count_missing_commands_with_profile;
|
||||
use codex_external_agent_migration::hook_migration_event_names_cla;
|
||||
use codex_external_agent_migration::import_commands_with_profile;
|
||||
use codex_external_agent_migration::import_hooks_cla;
|
||||
use codex_external_agent_migration::import_subagents_with_rewrite_profile;
|
||||
use codex_external_agent_migration::missing_command_names_with_profile;
|
||||
use codex_plugin::PluginId;
|
||||
use serde_json::Value as JsonValue;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::HashSet;
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use toml::Value as TomlValue;
|
||||
|
||||
use super::MigrationDetails;
|
||||
use super::PluginsMigration;
|
||||
use super::is_non_empty_text_file;
|
||||
use super::source::DetectedSourcePlugins;
|
||||
use super::source::InstructionSourceGroup;
|
||||
use super::source::MarketplaceImportSource;
|
||||
use super::source::PluginDetectionContext;
|
||||
|
||||
pub(super) const CONFIG_DIR: &str = ".claude";
|
||||
pub(super) const CONFIG_MD: &str = "CLAUDE.md";
|
||||
pub(super) const KNOWN_MARKETPLACES_PATH: &str = "plugins/known_marketplaces.json";
|
||||
pub(super) const OFFICIAL_MARKETPLACE_NAME: &str = "claude-plugins-official";
|
||||
pub(super) const OFFICIAL_MARKETPLACE_SOURCE: &str = "anthropics/claude-plugins-official";
|
||||
pub(super) const REWRITE_PROFILE: RewriteProfile = RewriteProfile::new(
|
||||
CONFIG_MD,
|
||||
&[
|
||||
"claude code",
|
||||
"claude-code",
|
||||
"claude_code",
|
||||
"claudecode",
|
||||
"claude",
|
||||
],
|
||||
);
|
||||
const COMMAND_MIGRATION_PROFILE: CommandMigrationProfile =
|
||||
CommandMigrationProfile::new(REWRITE_PROFILE, CommandDescriptionMode::RequireFrontmatter);
|
||||
|
||||
pub(super) fn effective_settings(project_settings: &Path) -> io::Result<Option<JsonValue>> {
|
||||
let mut effective = super::read_external_settings(project_settings)?;
|
||||
let Some(settings_dir) = project_settings.parent() else {
|
||||
return Ok(effective);
|
||||
};
|
||||
let local_settings = settings_dir.join("settings.local.json");
|
||||
let local_settings = match super::read_external_settings(&local_settings) {
|
||||
Ok(Some(local_settings)) => local_settings,
|
||||
Ok(None) => return Ok(effective),
|
||||
Err(err) if err.kind() == io::ErrorKind::InvalidData => return Ok(effective),
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
if let Some(effective) = effective.as_mut() {
|
||||
merge_json_settings(effective, &local_settings);
|
||||
} else {
|
||||
effective = Some(local_settings);
|
||||
}
|
||||
Ok(effective)
|
||||
}
|
||||
|
||||
pub(super) fn append_config(
|
||||
root: &mut toml::map::Map<String, TomlValue>,
|
||||
settings: &serde_json::Map<String, JsonValue>,
|
||||
) {
|
||||
if settings
|
||||
.get("sandbox")
|
||||
.and_then(JsonValue::as_object)
|
||||
.and_then(|sandbox| sandbox.get("enabled"))
|
||||
.and_then(JsonValue::as_bool)
|
||||
== Some(true)
|
||||
{
|
||||
root.insert(
|
||||
"sandbox_mode".to_string(),
|
||||
TomlValue::String("workspace-write".to_string()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn detect_plugins(
|
||||
context: &PluginDetectionContext<'_>,
|
||||
) -> Option<DetectedSourcePlugins> {
|
||||
let settings = context.settings?;
|
||||
let import_sources =
|
||||
marketplace_import_sources(settings, context.external_agent_home, context.source_root);
|
||||
let details = extract_plugin_migration_details(
|
||||
settings,
|
||||
&import_sources,
|
||||
context.configured_plugin_ids,
|
||||
context.configured_marketplace_plugins,
|
||||
)?;
|
||||
Some(DetectedSourcePlugins {
|
||||
description: format!(
|
||||
"Migrate enabled plugins from {}",
|
||||
context.source_settings.display()
|
||||
),
|
||||
details,
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn can_detect_plugins(settings: Option<&JsonValue>) -> bool {
|
||||
settings.is_some()
|
||||
}
|
||||
|
||||
pub(super) fn marketplace_import_sources(
|
||||
settings: &JsonValue,
|
||||
external_agent_home: &Path,
|
||||
source_root: &Path,
|
||||
) -> BTreeMap<String, MarketplaceImportSource> {
|
||||
let known_marketplaces_path = external_agent_home.join(KNOWN_MARKETPLACES_PATH);
|
||||
let known_marketplaces = match super::read_external_settings(&known_marketplaces_path) {
|
||||
Ok(known_marketplaces) => known_marketplaces,
|
||||
Err(err) => {
|
||||
tracing::warn!(
|
||||
path = %known_marketplaces_path.display(),
|
||||
error = %err,
|
||||
"ignoring invalid external agent marketplace registry"
|
||||
);
|
||||
None
|
||||
}
|
||||
};
|
||||
let mut import_sources = known_marketplaces
|
||||
.as_ref()
|
||||
.map(|known_marketplaces| {
|
||||
collect_marketplace_import_sources(known_marketplaces, external_agent_home)
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
if let Some(extra_known_marketplaces) = settings
|
||||
.as_object()
|
||||
.and_then(|settings| settings.get("extraKnownMarketplaces"))
|
||||
{
|
||||
let mut scoped_marketplaces = extra_known_marketplaces.clone();
|
||||
if let Some(scoped_marketplaces) = scoped_marketplaces.as_object_mut() {
|
||||
for (name, scoped_marketplace) in scoped_marketplaces {
|
||||
import_sources.remove(name);
|
||||
let Some(known_marketplace) = known_marketplaces
|
||||
.as_ref()
|
||||
.and_then(JsonValue::as_object)
|
||||
.and_then(|known_marketplaces| known_marketplaces.get(name))
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
if scoped_marketplace.get("source") != known_marketplace.get("source") {
|
||||
continue;
|
||||
}
|
||||
let Some(install_location) = known_marketplace
|
||||
.get("installLocation")
|
||||
.and_then(JsonValue::as_str)
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
let install_location = Path::new(install_location);
|
||||
let install_location = if install_location.is_absolute() {
|
||||
install_location.to_path_buf()
|
||||
} else {
|
||||
external_agent_home.join(install_location)
|
||||
};
|
||||
let Some(scoped_marketplace) = scoped_marketplace.as_object_mut() else {
|
||||
continue;
|
||||
};
|
||||
scoped_marketplace.insert(
|
||||
"installLocation".to_string(),
|
||||
JsonValue::String(install_location.display().to_string()),
|
||||
);
|
||||
}
|
||||
}
|
||||
import_sources.extend(collect_marketplace_import_sources(
|
||||
&scoped_marketplaces,
|
||||
source_root,
|
||||
));
|
||||
}
|
||||
|
||||
if has_enabled_plugin_for_marketplace(settings, OFFICIAL_MARKETPLACE_NAME)
|
||||
&& !import_sources.contains_key(OFFICIAL_MARKETPLACE_NAME)
|
||||
{
|
||||
import_sources.insert(
|
||||
OFFICIAL_MARKETPLACE_NAME.to_string(),
|
||||
MarketplaceImportSource {
|
||||
source: OFFICIAL_MARKETPLACE_SOURCE.to_string(),
|
||||
ref_name: None,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
import_sources
|
||||
}
|
||||
|
||||
pub(super) fn build_mcp_config(
|
||||
source_root: &Path,
|
||||
external_agent_home: &Path,
|
||||
settings: Option<&JsonValue>,
|
||||
) -> io::Result<TomlValue> {
|
||||
build_mcp_config_from_external(source_root, Some(external_agent_home), settings)
|
||||
}
|
||||
|
||||
pub(super) fn repo_instruction_source_groups(
|
||||
repo_root: &Path,
|
||||
) -> io::Result<Vec<InstructionSourceGroup>> {
|
||||
for candidate in [
|
||||
repo_root.join(CONFIG_MD),
|
||||
repo_root.join(CONFIG_DIR).join(CONFIG_MD),
|
||||
] {
|
||||
if is_non_empty_text_file(&candidate)? {
|
||||
return Ok(vec![InstructionSourceGroup {
|
||||
scope: repo_root.to_path_buf(),
|
||||
sources: vec![candidate],
|
||||
}]);
|
||||
}
|
||||
}
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
pub(super) fn home_instruction_sources(external_agent_home: &Path) -> io::Result<Vec<PathBuf>> {
|
||||
let path = external_agent_home.join(CONFIG_MD);
|
||||
Ok(is_non_empty_text_file(&path)?
|
||||
.then_some(path)
|
||||
.into_iter()
|
||||
.collect())
|
||||
}
|
||||
|
||||
pub(super) fn read_instruction_source(path: &Path) -> io::Result<String> {
|
||||
fs::read_to_string(path)
|
||||
}
|
||||
|
||||
pub(super) fn import_source_commands(
|
||||
source_commands: &Path,
|
||||
target_skills: &Path,
|
||||
) -> io::Result<Vec<String>> {
|
||||
import_commands_with_profile(source_commands, target_skills, COMMAND_MIGRATION_PROFILE)
|
||||
}
|
||||
|
||||
pub(super) fn count_missing_source_commands(
|
||||
source_commands: &Path,
|
||||
target_skills: &Path,
|
||||
) -> io::Result<usize> {
|
||||
count_missing_commands_with_profile(source_commands, target_skills, COMMAND_MIGRATION_PROFILE)
|
||||
}
|
||||
|
||||
pub(super) fn missing_source_command_names(
|
||||
source_commands: &Path,
|
||||
target_skills: &Path,
|
||||
) -> io::Result<Vec<String>> {
|
||||
missing_command_names_with_profile(source_commands, target_skills, COMMAND_MIGRATION_PROFILE)
|
||||
}
|
||||
|
||||
pub(super) fn import_source_subagents(
|
||||
source_agents: &Path,
|
||||
target_agents: &Path,
|
||||
) -> io::Result<Vec<String>> {
|
||||
import_subagents_with_rewrite_profile(source_agents, target_agents, REWRITE_PROFILE)
|
||||
}
|
||||
|
||||
pub(super) fn source_hook_event_names(
|
||||
source_dir: &Path,
|
||||
target_hooks: &Path,
|
||||
) -> io::Result<Vec<String>> {
|
||||
hook_migration_event_names_cla(source_dir, target_hooks, REWRITE_PROFILE)
|
||||
}
|
||||
|
||||
pub(super) fn import_source_hooks(source_dir: &Path, target_hooks: &Path) -> io::Result<bool> {
|
||||
import_hooks_cla(source_dir, target_hooks, REWRITE_PROFILE)
|
||||
}
|
||||
|
||||
fn merge_json_settings(existing: &mut JsonValue, incoming: &JsonValue) {
|
||||
match (existing, incoming) {
|
||||
(JsonValue::Object(existing), JsonValue::Object(incoming)) => {
|
||||
for (key, incoming_value) in incoming {
|
||||
match existing.get_mut(key) {
|
||||
Some(existing_value) => merge_json_settings(existing_value, incoming_value),
|
||||
None => {
|
||||
existing.insert(key.clone(), incoming_value.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
(existing, incoming) => {
|
||||
*existing = incoming.clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_plugin_migration_details(
|
||||
settings: &JsonValue,
|
||||
import_sources: &BTreeMap<String, MarketplaceImportSource>,
|
||||
configured_plugin_ids: &HashSet<String>,
|
||||
configured_marketplace_plugins: &BTreeMap<String, HashSet<String>>,
|
||||
) -> Option<MigrationDetails> {
|
||||
let loadable_marketplaces = import_sources
|
||||
.iter()
|
||||
.filter_map(|(marketplace_name, source)| {
|
||||
is_local_marketplace_source(&source.source, source.ref_name.clone())
|
||||
.ok()
|
||||
.map(|_| marketplace_name.clone())
|
||||
})
|
||||
.collect::<HashSet<_>>();
|
||||
let mut plugins = BTreeMap::new();
|
||||
for plugin_id in collect_enabled_plugins(settings)
|
||||
.into_iter()
|
||||
.filter(|plugin_id| !configured_plugin_ids.contains(plugin_id))
|
||||
{
|
||||
let Ok(plugin_id) = PluginId::parse(&plugin_id) else {
|
||||
continue;
|
||||
};
|
||||
if let Some(installable_plugins) =
|
||||
configured_marketplace_plugins.get(&plugin_id.marketplace_name)
|
||||
{
|
||||
if !installable_plugins.contains(&plugin_id.plugin_name) {
|
||||
tracing::warn!(
|
||||
plugin_id = %plugin_id.as_key(),
|
||||
marketplace_name = %plugin_id.marketplace_name,
|
||||
"enabled external agent plugin was not found in configured marketplace"
|
||||
);
|
||||
continue;
|
||||
}
|
||||
} else if !loadable_marketplaces.contains(&plugin_id.marketplace_name) {
|
||||
tracing::warn!(
|
||||
plugin_id = %plugin_id.as_key(),
|
||||
marketplace_name = %plugin_id.marketplace_name,
|
||||
"marketplace source was not found for enabled external agent plugin"
|
||||
);
|
||||
continue;
|
||||
}
|
||||
let plugin_group = plugins
|
||||
.entry(plugin_id.marketplace_name.clone())
|
||||
.or_insert_with(|| PluginsMigration {
|
||||
marketplace_name: plugin_id.marketplace_name.clone(),
|
||||
plugin_names: Vec::new(),
|
||||
});
|
||||
plugin_group.plugin_names.push(plugin_id.plugin_name);
|
||||
}
|
||||
|
||||
let plugins = plugins
|
||||
.into_values()
|
||||
.filter_map(|mut plugin_group| {
|
||||
if plugin_group.plugin_names.is_empty() {
|
||||
return None;
|
||||
}
|
||||
plugin_group.plugin_names.sort();
|
||||
Some(plugin_group)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
if plugins.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(MigrationDetails {
|
||||
plugins,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
fn collect_enabled_plugins(settings: &JsonValue) -> Vec<String> {
|
||||
let Some(enabled_plugins) = settings
|
||||
.as_object()
|
||||
.and_then(|settings| settings.get("enabledPlugins"))
|
||||
.and_then(JsonValue::as_object)
|
||||
else {
|
||||
return Vec::new();
|
||||
};
|
||||
|
||||
enabled_plugins
|
||||
.iter()
|
||||
.filter_map(|(plugin_key, enabled)| {
|
||||
if !enabled.as_bool().unwrap_or(false) {
|
||||
return None;
|
||||
}
|
||||
PluginId::parse(plugin_key)
|
||||
.ok()
|
||||
.map(|plugin_id| plugin_id.as_key())
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn has_enabled_plugin_for_marketplace(settings: &JsonValue, marketplace_name: &str) -> bool {
|
||||
collect_enabled_plugins(settings)
|
||||
.into_iter()
|
||||
.any(|plugin_id| {
|
||||
PluginId::parse(&plugin_id)
|
||||
.map(|plugin_id| plugin_id.marketplace_name == marketplace_name)
|
||||
.unwrap_or(false)
|
||||
})
|
||||
}
|
||||
|
||||
fn collect_marketplace_import_sources(
|
||||
marketplaces: &JsonValue,
|
||||
source_root: &Path,
|
||||
) -> BTreeMap<String, MarketplaceImportSource> {
|
||||
marketplaces
|
||||
.as_object()
|
||||
.map(|extra_known_marketplaces| {
|
||||
extra_known_marketplaces
|
||||
.iter()
|
||||
.filter_map(|(name, value)| {
|
||||
let source_fields = if let Some(source) = value.get("source")
|
||||
&& source.is_object()
|
||||
{
|
||||
source.as_object()?
|
||||
} else {
|
||||
value.as_object()?
|
||||
};
|
||||
let source_kind = source_fields
|
||||
.get("source")
|
||||
.and_then(JsonValue::as_str)
|
||||
.map(str::trim);
|
||||
let declared_source = match source_kind {
|
||||
Some("github") => source_fields.get("repo"),
|
||||
Some("git") => source_fields.get("url"),
|
||||
Some("directory" | "local") => source_fields.get("path"),
|
||||
Some("file" | "url" | "npm" | "settings") => None,
|
||||
Some(_) => source_fields.get("source"),
|
||||
None => source_fields
|
||||
.get("repo")
|
||||
.or_else(|| source_fields.get("url"))
|
||||
.or_else(|| source_fields.get("path"))
|
||||
.or_else(|| value.get("source")),
|
||||
}
|
||||
.and_then(JsonValue::as_str)
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty());
|
||||
let materialized_source = value
|
||||
.get("installLocation")
|
||||
.and_then(JsonValue::as_str)
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())
|
||||
.and_then(|value| {
|
||||
let path = Path::new(value);
|
||||
let path = if path.is_absolute() {
|
||||
path.to_path_buf()
|
||||
} else {
|
||||
source_root.join(path)
|
||||
};
|
||||
path.is_dir().then(|| path.display().to_string())
|
||||
});
|
||||
let (source, ref_name) = if let Some(source) = declared_source {
|
||||
let source = if matches!(source_kind, Some("directory" | "local")) {
|
||||
let path = Path::new(source);
|
||||
if path.is_absolute() {
|
||||
path.to_path_buf()
|
||||
} else {
|
||||
source_root.join(path)
|
||||
}
|
||||
.display()
|
||||
.to_string()
|
||||
} else {
|
||||
resolve_external_marketplace_source(source, source_root)
|
||||
};
|
||||
let ref_name = source_fields
|
||||
.get("ref")
|
||||
.or_else(|| value.get("ref"))
|
||||
.and_then(JsonValue::as_str)
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())
|
||||
.map(ToOwned::to_owned);
|
||||
(source, ref_name)
|
||||
} else {
|
||||
(materialized_source?, None)
|
||||
};
|
||||
|
||||
Some((name.clone(), MarketplaceImportSource { source, ref_name }))
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn resolve_external_marketplace_source(source: &str, source_root: &Path) -> String {
|
||||
if !looks_like_relative_local_path(source) {
|
||||
return source.to_string();
|
||||
}
|
||||
|
||||
source_root.join(source).display().to_string()
|
||||
}
|
||||
|
||||
fn looks_like_relative_local_path(source: &str) -> bool {
|
||||
source.starts_with("./") || source.starts_with("../") || source == "." || source == ".."
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
use codex_external_agent_migration::RewriteProfile;
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub(super) fn display_source_paths(paths: &[PathBuf]) -> String {
|
||||
paths
|
||||
.iter()
|
||||
.map(|path| path.display().to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
}
|
||||
|
||||
pub(super) fn copy_dir_recursive(
|
||||
source: &Path,
|
||||
target: &Path,
|
||||
rewrite_profile: RewriteProfile,
|
||||
) -> io::Result<()> {
|
||||
fs::create_dir_all(target)?;
|
||||
|
||||
for entry in fs::read_dir(source)? {
|
||||
let entry = entry?;
|
||||
let source_path = entry.path();
|
||||
let target_path = target.join(entry.file_name());
|
||||
let file_type = entry.file_type()?;
|
||||
|
||||
if file_type.is_dir() {
|
||||
copy_dir_recursive(&source_path, &target_path, rewrite_profile)?;
|
||||
continue;
|
||||
}
|
||||
|
||||
if file_type.is_file() {
|
||||
if is_skill_md(&source_path) {
|
||||
rewrite_and_copy_text_file(&source_path, &target_path, rewrite_profile)?;
|
||||
} else {
|
||||
fs::copy(source_path, target_path)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn rewrite_external_agent_terms(
|
||||
content: &str,
|
||||
rewrite_profile: RewriteProfile,
|
||||
) -> String {
|
||||
let mut rewritten = replace_case_insensitive_with_boundaries(
|
||||
content,
|
||||
rewrite_profile.doc_file_name(),
|
||||
"AGENTS.md",
|
||||
);
|
||||
for from in rewrite_profile.term_variants() {
|
||||
rewritten = replace_case_insensitive_with_boundaries(&rewritten, from, "Codex");
|
||||
}
|
||||
for from in rewrite_profile.case_sensitive_term_variants() {
|
||||
rewritten = replace_with_boundaries(&rewritten, from, "Codex");
|
||||
}
|
||||
rewritten
|
||||
}
|
||||
|
||||
fn replace_with_boundaries(input: &str, needle: &str, replacement: &str) -> String {
|
||||
if needle.is_empty() {
|
||||
return input.to_string();
|
||||
}
|
||||
|
||||
let bytes = input.as_bytes();
|
||||
let mut output = String::with_capacity(input.len());
|
||||
let mut last_emitted = 0usize;
|
||||
let mut search_start = 0usize;
|
||||
|
||||
while let Some(relative_pos) = input[search_start..].find(needle) {
|
||||
let start = search_start + relative_pos;
|
||||
let end = start + needle.len();
|
||||
let boundary_before = start == 0 || !is_word_char(bytes[start - 1]);
|
||||
let boundary_after = end == bytes.len() || !is_word_char(bytes[end]);
|
||||
if boundary_before && boundary_after {
|
||||
output.push_str(&input[last_emitted..start]);
|
||||
output.push_str(replacement);
|
||||
last_emitted = end;
|
||||
}
|
||||
search_start = end;
|
||||
}
|
||||
output.push_str(&input[last_emitted..]);
|
||||
output
|
||||
}
|
||||
|
||||
fn is_skill_md(path: &Path) -> bool {
|
||||
path.file_name()
|
||||
.and_then(|name| name.to_str())
|
||||
.is_some_and(|name| name.eq_ignore_ascii_case("SKILL.md"))
|
||||
}
|
||||
|
||||
fn rewrite_and_copy_text_file(
|
||||
source: &Path,
|
||||
target: &Path,
|
||||
rewrite_profile: RewriteProfile,
|
||||
) -> io::Result<()> {
|
||||
let source_contents = fs::read_to_string(source)?;
|
||||
let rewritten = rewrite_external_agent_terms(&source_contents, rewrite_profile);
|
||||
fs::write(target, rewritten)
|
||||
}
|
||||
|
||||
fn replace_case_insensitive_with_boundaries(
|
||||
input: &str,
|
||||
needle: &str,
|
||||
replacement: &str,
|
||||
) -> String {
|
||||
let needle_lower = needle.to_ascii_lowercase();
|
||||
if needle_lower.is_empty() {
|
||||
return input.to_string();
|
||||
}
|
||||
|
||||
let haystack_lower = input.to_ascii_lowercase();
|
||||
let bytes = input.as_bytes();
|
||||
let mut output = String::with_capacity(input.len());
|
||||
let mut last_emitted = 0usize;
|
||||
let mut search_start = 0usize;
|
||||
|
||||
while let Some(relative_pos) = haystack_lower[search_start..].find(&needle_lower) {
|
||||
let start = search_start + relative_pos;
|
||||
let end = start + needle_lower.len();
|
||||
let boundary_before = start == 0 || !is_word_char(bytes[start - 1]);
|
||||
let boundary_after = end == bytes.len() || !is_word_char(bytes[end]);
|
||||
if boundary_before && boundary_after {
|
||||
output.push_str(&input[last_emitted..start]);
|
||||
output.push_str(replacement);
|
||||
last_emitted = end;
|
||||
}
|
||||
search_start = end;
|
||||
}
|
||||
output.push_str(&input[last_emitted..]);
|
||||
output
|
||||
}
|
||||
|
||||
fn is_word_char(byte: u8) -> bool {
|
||||
byte.is_ascii_alphanumeric() || byte == b'_'
|
||||
}
|
||||
+15
-10
@@ -1192,10 +1192,12 @@ async fn detect_home_skips_skills_when_all_skill_directories_exist() {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn import_repo_agents_md_rewrites_terms_and_skips_non_empty_targets() {
|
||||
async fn import_repo_agents_md_from_nested_cwd_rewrites_terms_and_skips_non_empty_targets() {
|
||||
let root = TempDir::new().expect("create tempdir");
|
||||
let repo_root = root.path().join("repo-a");
|
||||
let nested_cwd = repo_root.join("nested");
|
||||
let repo_with_existing_target = root.path().join("repo-b");
|
||||
fs::create_dir_all(&nested_cwd).expect("create nested cwd");
|
||||
fs::create_dir_all(repo_root.join(".git")).expect("create git");
|
||||
fs::create_dir_all(repo_with_existing_target.join(".git")).expect("create git");
|
||||
fs::write(
|
||||
@@ -1224,7 +1226,7 @@ async fn import_repo_agents_md_rewrites_terms_and_skips_non_empty_targets() {
|
||||
ExternalAgentConfigMigrationItem {
|
||||
item_type: ExternalAgentConfigMigrationItemType::AgentsMd,
|
||||
description: String::new(),
|
||||
cwd: Some(repo_root.clone()),
|
||||
cwd: Some(nested_cwd.clone()),
|
||||
details: None,
|
||||
},
|
||||
ExternalAgentConfigMigrationItem {
|
||||
@@ -1242,12 +1244,12 @@ async fn import_repo_agents_md_rewrites_terms_and_skips_non_empty_targets() {
|
||||
ExternalAgentConfigImportItemResult {
|
||||
item_type: ExternalAgentConfigMigrationItemType::AgentsMd,
|
||||
description: String::new(),
|
||||
cwd: Some(repo_root.clone()),
|
||||
cwd: Some(nested_cwd.clone()),
|
||||
success_count: 1,
|
||||
error_count: 0,
|
||||
successes: vec![import_success(
|
||||
ExternalAgentConfigMigrationItemType::AgentsMd,
|
||||
Some(repo_root.clone()),
|
||||
Some(nested_cwd),
|
||||
repo_root
|
||||
.join(EXTERNAL_AGENT_CONFIG_MD)
|
||||
.display()
|
||||
@@ -1830,7 +1832,7 @@ async fn detect_home_uses_materialized_known_marketplace_for_inline_npm_source()
|
||||
|
||||
#[test]
|
||||
fn marketplace_import_sources_prefers_scoped_source_over_registry_name_collision() {
|
||||
let (root, external_agent_home, codex_home) = fixture_paths();
|
||||
let (root, external_agent_home, _codex_home) = fixture_paths();
|
||||
let source_root = root.path().join("repo");
|
||||
let scoped_marketplace = source_root.join("repo-marketplace");
|
||||
let cached_marketplace = external_agent_home.join("plugins/marketplaces/debug");
|
||||
@@ -1861,8 +1863,8 @@ fn marketplace_import_sources_prefers_scoped_source_over_registry_name_collision
|
||||
}
|
||||
});
|
||||
|
||||
let import_sources = service_for_paths(external_agent_home, codex_home)
|
||||
.marketplace_import_sources(&settings, &source_root);
|
||||
let import_sources =
|
||||
source_cla::marketplace_import_sources(&settings, &external_agent_home, &source_root);
|
||||
|
||||
assert_eq!(
|
||||
import_sources.get("debug"),
|
||||
@@ -1875,7 +1877,7 @@ fn marketplace_import_sources_prefers_scoped_source_over_registry_name_collision
|
||||
|
||||
#[test]
|
||||
fn marketplace_import_sources_prefers_supported_declaration_over_materialization() {
|
||||
let (_root, external_agent_home, codex_home) = fixture_paths();
|
||||
let (_root, external_agent_home, _codex_home) = fixture_paths();
|
||||
let cached_marketplace = external_agent_home.join("plugins/marketplaces/acme-tools");
|
||||
fs::create_dir_all(&cached_marketplace).expect("create cached marketplace");
|
||||
fs::write(
|
||||
@@ -1894,8 +1896,11 @@ fn marketplace_import_sources_prefers_supported_declaration_over_materialization
|
||||
)
|
||||
.expect("write known marketplaces");
|
||||
|
||||
let import_sources = service_for_paths(external_agent_home.clone(), codex_home)
|
||||
.marketplace_import_sources(&serde_json::json!({}), &external_agent_home);
|
||||
let import_sources = source_cla::marketplace_import_sources(
|
||||
&serde_json::json!({}),
|
||||
&external_agent_home,
|
||||
&external_agent_home,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
import_sources.get("acme-tools"),
|
||||
+8
-8
@@ -5,12 +5,12 @@ use chrono::Utc;
|
||||
use codex_arg0::Arg0DispatchPaths;
|
||||
use codex_core::ThreadManager;
|
||||
use codex_core::config::ConfigOverrides;
|
||||
use codex_external_agent_sessions::CompletedExternalAgentSessionImport;
|
||||
use codex_external_agent_sessions::ExternalAgentSessionMigration;
|
||||
use codex_external_agent_sessions::ImportedExternalAgentSession;
|
||||
use codex_external_agent_sessions::PendingSessionImport;
|
||||
use codex_external_agent_sessions::prepare_validated_session_import;
|
||||
use codex_external_agent_sessions::record_completed_session_imports;
|
||||
use codex_external_agent_migration::sessions::CompletedExternalAgentSessionImport;
|
||||
use codex_external_agent_migration::sessions::ExternalAgentSessionMigration;
|
||||
use codex_external_agent_migration::sessions::ImportedExternalAgentSession;
|
||||
use codex_external_agent_migration::sessions::PendingSessionImport;
|
||||
use codex_external_agent_migration::sessions::prepare_validated_session_import;
|
||||
use codex_external_agent_migration::sessions::record_completed_session_imports;
|
||||
use codex_models_manager::manager::RefreshStrategy;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::models::BaseInstructions;
|
||||
@@ -27,8 +27,8 @@ use codex_thread_store::UpdateThreadMetadataParams;
|
||||
use futures::StreamExt;
|
||||
use tokio::sync::Semaphore;
|
||||
|
||||
use crate::config::external_agent_config::ExternalAgentConfigImportItemResult;
|
||||
use crate::config::external_agent_config::record_import_error;
|
||||
use super::service::ExternalAgentConfigImportItemResult;
|
||||
use super::service::record_import_error;
|
||||
use crate::config_manager::ConfigManager;
|
||||
|
||||
const SESSION_IMPORT_CONCURRENCY: usize = 5;
|
||||
@@ -87,7 +87,6 @@ mod attestation;
|
||||
mod auth_mode;
|
||||
mod bespoke_event_handling;
|
||||
mod command_exec;
|
||||
mod config;
|
||||
mod config_layer;
|
||||
mod config_manager;
|
||||
mod config_manager_service;
|
||||
@@ -98,6 +97,7 @@ mod dynamic_tools;
|
||||
mod effective_plugin_change;
|
||||
mod error_code;
|
||||
mod extensions;
|
||||
mod external_agent_migration;
|
||||
mod external_auth;
|
||||
mod filters;
|
||||
mod fs_watch;
|
||||
|
||||
@@ -13,6 +13,8 @@ use crate::extensions::ThreadExtensionDependencies;
|
||||
use crate::extensions::app_server_extension_event_sink;
|
||||
use crate::extensions::guardian_agent_spawner;
|
||||
use crate::extensions::thread_extensions;
|
||||
use crate::external_agent_migration::ExternalAgentConfigRequestProcessor;
|
||||
use crate::external_agent_migration::ExternalAgentConfigRequestProcessorArgs;
|
||||
use crate::fs_watch::FsWatchManager;
|
||||
use crate::outgoing_message::ConnectionId;
|
||||
use crate::outgoing_message::ConnectionRequestId;
|
||||
@@ -24,8 +26,6 @@ use crate::request_processors::CatalogRequestProcessor;
|
||||
use crate::request_processors::CommandExecRequestProcessor;
|
||||
use crate::request_processors::ConfigRequestProcessor;
|
||||
use crate::request_processors::EnvironmentRequestProcessor;
|
||||
use crate::request_processors::ExternalAgentConfigRequestProcessor;
|
||||
use crate::request_processors::ExternalAgentConfigRequestProcessorArgs;
|
||||
use crate::request_processors::FeedbackRequestProcessor;
|
||||
use crate::request_processors::FsRequestProcessor;
|
||||
use crate::request_processors::GitRequestProcessor;
|
||||
|
||||
@@ -501,8 +501,6 @@ mod catalog_processor;
|
||||
mod command_exec_processor;
|
||||
mod config_processor;
|
||||
mod environment_processor;
|
||||
mod external_agent_config_processor;
|
||||
mod external_agent_session_import;
|
||||
mod feedback_doctor_report;
|
||||
mod feedback_processor;
|
||||
mod fs_processor;
|
||||
@@ -525,8 +523,6 @@ pub(crate) use catalog_processor::CatalogRequestProcessor;
|
||||
pub(crate) use command_exec_processor::CommandExecRequestProcessor;
|
||||
pub(crate) use config_processor::ConfigRequestProcessor;
|
||||
pub(crate) use environment_processor::EnvironmentRequestProcessor;
|
||||
pub(crate) use external_agent_config_processor::ExternalAgentConfigRequestProcessor;
|
||||
pub(crate) use external_agent_config_processor::ExternalAgentConfigRequestProcessorArgs;
|
||||
pub(crate) use feedback_processor::FeedbackRequestProcessor;
|
||||
pub(crate) use fs_processor::FsRequestProcessor;
|
||||
pub(crate) use git_processor::GitRequestProcessor;
|
||||
|
||||
Reference in New Issue
Block a user