diff --git a/apps/ai-game-creator-shell/src-tauri/build.rs b/apps/ai-game-creator-shell/src-tauri/build.rs index bf73065ce..cd69f8180 100644 --- a/apps/ai-game-creator-shell/src-tauri/build.rs +++ b/apps/ai-game-creator-shell/src-tauri/build.rs @@ -1,5 +1,7 @@ #[path = "build_support/codex_bundle.rs"] mod codex_bundle; +#[path = "build_support/codex_package_metadata.rs"] +mod codex_package_metadata; #[path = "build_support/frontend_dist_guard.rs"] mod frontend_dist_guard; #[path = "build_support/godot_bundle.rs"] @@ -64,7 +66,7 @@ fn stage_codex_target(manifest_dir: &std::path::Path, target: &str) { .parent() .and_then(|apps_dir| apps_dir.parent()) .expect("AI 游戏创作应用必须位于仓库 apps 目录下"); - let package = layout.npm_package; + let package = format!("codex-{}", layout.platform); let source_candidates = [app_root, repo_root] .into_iter() .flat_map(|root| { @@ -99,7 +101,7 @@ fn stage_codex_target(manifest_dir: &std::path::Path, target: &str) { &fs::read(source.join("codex-package.json")).expect("读取 Codex 原生包元数据失败"), ) .expect("Codex 原生包元数据无效"); - codex_bundle::validate_package_metadata(&metadata, target, layout) + codex_package_metadata::validate_package_metadata(&metadata, target, layout) .unwrap_or_else(|error| panic!("{error}")); let target_dir = manifest_dir.join("resources/codex").join(layout.directory); let notice = target_dir.join("NOTICE.md"); diff --git a/apps/ai-game-creator-shell/src-tauri/build_support/codex_bundle.rs b/apps/ai-game-creator-shell/src-tauri/build_support/codex_bundle.rs index 09299bfb1..e2064d28d 100644 --- a/apps/ai-game-creator-shell/src-tauri/build_support/codex_bundle.rs +++ b/apps/ai-game-creator-shell/src-tauri/build_support/codex_bundle.rs @@ -7,7 +7,6 @@ pub const SCHEMA: &str = "genarrative-codex-sidecar.v2"; #[derive(Clone, Copy, Debug)] pub struct Layout { pub platform: &'static str, - pub npm_package: &'static str, pub directory: &'static str, pub executable: &'static str, pub files: &'static [&'static str], @@ -33,7 +32,6 @@ pub fn for_target(target: &str) -> Option { match target { "x86_64-pc-windows-msvc" => Some(Layout { platform: "win32-x64", - npm_package: "codex-win32-x64", directory: "win-x64", executable: "bin/codex.exe", files: WINDOWS_FILES, @@ -44,11 +42,6 @@ pub fn for_target(target: &str) -> Option { } else { "darwin-x64" }, - npm_package: if target.starts_with("aarch64") { - "codex-darwin-arm64" - } else { - "codex-darwin-x64" - }, directory: if target.starts_with("aarch64") { "mac-native/darwin-arm64" } else { @@ -61,24 +54,6 @@ pub fn for_target(target: &str) -> Option { } } -pub fn validate_package_metadata( - metadata: &serde_json::Value, - target: &str, - layout: Layout, -) -> Result<(), String> { - if metadata["layoutVersion"] == 1 - && metadata["version"] == VERSION - && metadata["target"] == target - && metadata["entrypoint"] == layout.executable - && metadata["resourcesDir"] == "codex-resources" - && metadata["pathDir"] == "codex-path" - { - Ok(()) - } else { - Err(format!("Codex 原生包版本、布局或架构不匹配目标 {target}")) - } -} - #[cfg(test)] mod tests { use super::*; @@ -87,13 +62,11 @@ mod tests { fn platform_layouts_are_explicit_and_preserve_upstream_components() { let mac = for_target("aarch64-apple-darwin").unwrap(); assert_eq!(mac.platform, "darwin-arm64"); - assert_eq!(mac.npm_package, "codex-darwin-arm64"); assert!(mac.files.contains(&"codex-resources/zsh/bin/zsh")); assert!(mac.files.contains(&"bin/codex-code-mode-host")); assert!(!mac.files.iter().any(|file| file.ends_with(".exe"))); let intel = for_target("x86_64-apple-darwin").unwrap(); assert_eq!(intel.platform, "darwin-x64"); - assert_eq!(intel.npm_package, "codex-darwin-x64"); assert_eq!(mac.directory, "mac-native/darwin-arm64"); assert_eq!(intel.directory, "mac-native/darwin-x64"); assert_ne!(mac.directory, intel.directory); @@ -107,34 +80,4 @@ mod tests { assert!(for_target("aarch64-pc-windows-msvc").is_none()); assert!(for_target("x86_64-unknown-linux-gnu").is_none()); } - - #[test] - fn metadata_rejects_version_architecture_and_layout_drift() { - let target = "aarch64-apple-darwin"; - let layout = for_target(target).unwrap(); - let valid = serde_json::json!({ - "layoutVersion": 1, - "version": VERSION, - "target": target, - "entrypoint": "bin/codex", - "resourcesDir": "codex-resources", - "pathDir": "codex-path", - }); - assert!(validate_package_metadata(&valid, target, layout).is_ok()); - for (key, value) in [ - ("layoutVersion", serde_json::json!(2)), - ("version", serde_json::json!("0.0.0")), - ("target", serde_json::json!("x86_64-apple-darwin")), - ("entrypoint", serde_json::json!("bin/codex.exe")), - ("resourcesDir", serde_json::json!("../private")), - ("pathDir", serde_json::json!(null)), - ] { - let mut invalid = valid.clone(); - invalid[key] = value; - assert!( - validate_package_metadata(&invalid, target, layout).is_err(), - "{key}" - ); - } - } } diff --git a/apps/ai-game-creator-shell/src-tauri/build_support/codex_package_metadata.rs b/apps/ai-game-creator-shell/src-tauri/build_support/codex_package_metadata.rs new file mode 100644 index 000000000..04b9b6a41 --- /dev/null +++ b/apps/ai-game-creator-shell/src-tauri/build_support/codex_package_metadata.rs @@ -0,0 +1,57 @@ +//! 随包阶段的原生包元数据校验,不进入运行时生产模块。 + +use super::codex_bundle::{Layout, VERSION}; + +pub fn validate_package_metadata( + metadata: &serde_json::Value, + target: &str, + layout: Layout, +) -> Result<(), String> { + if metadata["layoutVersion"] == 1 + && metadata["version"] == VERSION + && metadata["target"] == target + && metadata["entrypoint"] == layout.executable + && metadata["resourcesDir"] == "codex-resources" + && metadata["pathDir"] == "codex-path" + { + Ok(()) + } else { + Err(format!("Codex 原生包版本、布局或架构不匹配目标 {target}")) + } +} + +#[cfg(test)] +mod tests { + use super::super::codex_bundle::for_target; + use super::*; + + #[test] + fn metadata_rejects_version_architecture_and_layout_drift() { + let target = "aarch64-apple-darwin"; + let layout = for_target(target).unwrap(); + let valid = serde_json::json!({ + "layoutVersion": 1, + "version": VERSION, + "target": target, + "entrypoint": "bin/codex", + "resourcesDir": "codex-resources", + "pathDir": "codex-path", + }); + assert!(validate_package_metadata(&valid, target, layout).is_ok()); + for (key, value) in [ + ("layoutVersion", serde_json::json!(2)), + ("version", serde_json::json!("0.0.0")), + ("target", serde_json::json!("x86_64-apple-darwin")), + ("entrypoint", serde_json::json!("bin/codex.exe")), + ("resourcesDir", serde_json::json!("../private")), + ("pathDir", serde_json::json!(null)), + ] { + let mut invalid = valid.clone(); + invalid[key] = value; + assert!( + validate_package_metadata(&invalid, target, layout).is_err(), + "{key}" + ); + } + } +} diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/codex_cli.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/codex_cli.rs index 707e5e29a..918e1e6df 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/codex_cli.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/codex_cli.rs @@ -8,6 +8,11 @@ use tokio::io::{AsyncRead, AsyncReadExt, AsyncWriteExt}; #[path = "../../build_support/codex_bundle.rs"] pub(crate) mod codex_bundle; +// 复用构建端校验的既有单测,生产运行时只编译共享布局。 +#[cfg(test)] +#[path = "../../build_support/codex_package_metadata.rs"] +mod codex_package_metadata; + const GAME_CREATOR_CODEX_CLI_EXECUTABLE: &str = "codex"; const GAME_CREATOR_CODEX_CLI_PROMPT_MAX_BYTES: usize = 4 * 1024 * 1024; const GAME_CREATOR_CODEX_CLI_STDOUT_MAX_BYTES: usize = 4 * 1024 * 1024; diff --git a/apps/ai-game-creator-shell/src-tauri/src/command_exec.rs b/apps/ai-game-creator-shell/src-tauri/src/command_exec.rs index 02dd510ed..c3a0343e1 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/command_exec.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/command_exec.rs @@ -1968,60 +1968,14 @@ fn request_unix_project_command_process_group_termination( Err(format!("请求终止受控进程组失败:{error}")) } -async fn request_project_command_process_group_termination( - process_id: u32, -) -> Result<&'static str, String> { - #[cfg(unix)] - { - return request_unix_project_command_process_group_termination(process_id); - } - #[cfg(windows)] - { - let system_root = std::env::var_os("SystemRoot") - .ok_or_else(|| "请求终止受控进程组失败:缺少 SystemRoot".to_string())?; - let taskkill = fs::canonicalize(PathBuf::from(&system_root).join("System32/taskkill.exe")) - .map_err(|error| format!("请求终止受控进程组失败:定位 taskkill.exe 失败:{error}"))?; - if !taskkill.is_absolute() || !taskkill.is_file() { - return Err("请求终止受控进程组失败:taskkill.exe 不是绝对普通文件".to_string()); - } - let mut command = tokio::process::Command::new(taskkill); - command - .args(["/PID", &process_id.to_string(), "/T", "/F"]) - .env_clear() - .env("SystemRoot", &system_root) - .stdin(Stdio::null()) - .stdout(Stdio::null()) - .stderr(Stdio::null()); - crate::configure_windows_background_tokio_command(&mut command, false); - let status = command - .status() - .await - .map_err(|error| format!("请求终止受控进程组失败:启动 taskkill.exe 失败:{error}"))?; - if !status.success() { - return Err(format!( - "请求终止受控进程组失败:taskkill.exe 退出码 {}", - status - .code() - .map(|code| code.to_string()) - .unwrap_or_else(|| "none".to_string()) - )); - } - return Ok("已请求终止受控进程组"); - } - #[cfg(not(any(unix, windows)))] - { - let _ = process_id; - Err("请求终止受控进程组失败:当前平台不支持受控进程组终止".to_string()) - } -} - +#[cfg(target_os = "linux")] async fn terminate_project_command_process_group( child: &mut tokio::process::Child, ) -> Result { let process_id = child .id() .ok_or_else(|| "请求终止受控进程组失败:子进程缺少 pid".to_string())?; - let group_result = request_project_command_process_group_termination(process_id).await; + let group_result = request_unix_project_command_process_group_termination(process_id); let child_kill_error = child.start_kill().err(); let wait_result = child.wait().await; if let Err(error) = &group_result { diff --git a/apps/ai-game-creator-shell/src-tauri/src/command_sandbox.rs b/apps/ai-game-creator-shell/src-tauri/src/command_sandbox.rs index ae02e18b7..c944151cb 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/command_sandbox.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/command_sandbox.rs @@ -1,7 +1,10 @@ #[cfg(target_os = "linux")] use std::ffi::OsStr; +#[cfg(target_os = "linux")] use std::ffi::OsString; +#[cfg(target_os = "linux")] use std::fmt; +#[cfg(target_os = "linux")] use std::path::{Path, PathBuf}; #[cfg(target_os = "linux")] @@ -48,6 +51,7 @@ impl CommandSandboxMetadata { } } +#[cfg(target_os = "linux")] #[derive(Clone, Debug, Eq, PartialEq)] pub(crate) struct CommandSandboxLaunch { pub(crate) executable: PathBuf, @@ -66,12 +70,14 @@ pub(crate) struct StagedCommandSandboxLaunch { pub(crate) gate: LaunchGate, } +#[cfg(target_os = "linux")] #[derive(Clone, Debug, Eq, PartialEq)] pub(crate) struct CommandSandboxError { message: String, metadata: CommandSandboxMetadata, } +#[cfg(target_os = "linux")] impl CommandSandboxError { fn new(message: impl Into, metadata: CommandSandboxMetadata) -> Self { Self { @@ -79,19 +85,16 @@ impl CommandSandboxError { metadata, } } - - #[cfg(not(target_os = "linux"))] - pub(crate) fn metadata(&self) -> &CommandSandboxMetadata { - &self.metadata - } } +#[cfg(target_os = "linux")] impl fmt::Display for CommandSandboxError { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str(&self.message) } } +#[cfg(target_os = "linux")] impl std::error::Error for CommandSandboxError {} pub(crate) fn command_sandbox_platform_metadata() -> CommandSandboxMetadata { @@ -107,6 +110,7 @@ pub(crate) fn command_sandbox_platform_metadata() -> CommandSandboxMetadata { /// Builds a fail-closed launcher for a direct executable plus structured argv. /// It never falls back to launching the original command on the host. +#[cfg(target_os = "linux")] pub(crate) fn prepare_command_sandbox_launch( root: &Path, executable: &Path, @@ -114,19 +118,7 @@ pub(crate) fn prepare_command_sandbox_launch( cwd: &Path, environment: &[(OsString, OsString)], ) -> Result { - #[cfg(target_os = "linux")] - { - prepare_linux_command_sandbox_launch(root, executable, arguments, cwd, environment) - } - #[cfg(not(target_os = "linux"))] - { - let _ = (root, executable, arguments, cwd, environment); - let metadata = CommandSandboxMetadata::unsupported_legacy(); - Err(CommandSandboxError::new( - "当前平台没有可用的 OS-enforced workspace sandbox;拒绝宿主直通执行", - metadata, - )) - } + prepare_linux_command_sandbox_launch(root, executable, arguments, cwd, environment) } #[cfg(target_os = "linux")] @@ -1446,23 +1438,3 @@ print("SANDBOX_OK") #[cfg(target_os = "linux")] use linux::prepare_linux_command_sandbox_launch; - -#[cfg(all(test, not(target_os = "linux")))] -mod unsupported_tests { - use super::*; - - #[test] - fn unsupported_platform_returns_legacy_metadata_without_launcher() { - let error = prepare_command_sandbox_launch( - Path::new("."), - Path::new("tool"), - &[], - Path::new("."), - &[], - ) - .expect_err("unsupported platform must fail closed"); - assert_eq!(error.metadata().backend, "legacy-host-restricted"); - assert_eq!(error.metadata().mode, "fixed-command"); - assert_eq!(error.metadata().network, "proxy-only"); - } -} diff --git a/apps/ai-game-creator-shell/src-tauri/src/editor_adapter/mod.rs b/apps/ai-game-creator-shell/src-tauri/src/editor_adapter/mod.rs index c3c384ada..103de647a 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/editor_adapter/mod.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/editor_adapter/mod.rs @@ -5,4 +5,6 @@ //! plugin package they belong to under the `plugins/` workspace. This module //! only re-exports the shared contract so the host stays editor-agnostic. -pub(crate) use editor_adapter_api::{EditorAdapter, EditorConnectionInfo}; +pub(crate) use editor_adapter_api::EditorAdapter; +#[cfg(test)] +pub(crate) use editor_adapter_api::EditorConnectionInfo; diff --git a/apps/ai-game-creator-shell/src-tauri/src/editor_adapters.rs b/apps/ai-game-creator-shell/src-tauri/src/editor_adapters.rs index d98bdfb7b..2801d84eb 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/editor_adapters.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/editor_adapters.rs @@ -11,16 +11,45 @@ use std::path::PathBuf; use tauri::Manager; use crate::plugin_host::PluginHost; +#[cfg(all( + windows, + target_arch = "x86_64", + any(feature = "unity-editor-execute", feature = "godot-editor-execute") +))] use editor_adapter_api::{EditorAdapter, EditorConnectionInfo}; -use serde_json::{json, Value}; +#[cfg(any( + test, + all( + windows, + target_arch = "x86_64", + any(feature = "unity-editor-execute", feature = "godot-editor-execute") + ) +))] +use serde_json::json; +#[cfg(all( + windows, + target_arch = "x86_64", + any(feature = "unity-editor-execute", feature = "godot-editor-execute") +))] +use serde_json::Value; use std::path::Path; mod execution; pub(crate) use execution::*; /// GUI 只转发已有 Runner RPC;每个引擎的连接和回执均归同一个 owner。 +#[cfg(all( + windows, + target_arch = "x86_64", + any(feature = "unity-editor-execute", feature = "godot-editor-execute") +))] struct RunnerManagedEditorAdapter(ManagedEditor); +#[cfg(all( + windows, + target_arch = "x86_64", + any(feature = "unity-editor-execute", feature = "godot-editor-execute") +))] impl EditorAdapter for RunnerManagedEditorAdapter { fn id(&self) -> &'static str { self.0.adapter() diff --git a/apps/ai-game-creator-shell/src-tauri/src/plugin_host.rs b/apps/ai-game-creator-shell/src-tauri/src/plugin_host.rs index bdfc59f4c..61c2387e1 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/plugin_host.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/plugin_host.rs @@ -21,7 +21,7 @@ use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; use tauri::{Manager, State}; -use crate::editor_adapter::{EditorAdapter, EditorConnectionInfo}; +use crate::editor_adapter::EditorAdapter; type EditorRegistry = Arc>>>; type ProjectContext = Arc>>; @@ -1873,6 +1873,15 @@ impl PluginHost { Ok(()) } + #[cfg(any( + test, + all(windows, feature = "cocos-editor-execute"), + all( + windows, + target_arch = "x86_64", + any(feature = "unity-editor-execute", feature = "godot-editor-execute") + ) + ))] pub(crate) fn register_editor_adapter( &self, adapter: Box, @@ -1891,98 +1900,6 @@ impl PluginHost { editors.insert(adapter.id().to_string(), adapter); Ok(()) } - - pub(crate) fn detect_editor( - &self, - adapter: String, - project_path: String, - ) -> Result { - let state = self - .state - .lock() - .map_err(|_| "插件宿主锁已损坏".to_string())?; - let editors = state - .editors - .try_lock() - .map_err(|_| "编辑器注册表锁已损坏".to_string())?; - editors - .get(&adapter) - .ok_or_else(|| format!("未知编辑器适配器:{adapter}"))? - .detect(Path::new(project_path.trim())) - } - - pub(crate) fn connect_editor( - &self, - adapter: String, - pid: u32, - project_path: String, - version: String, - ) -> Result { - let state = self - .state - .lock() - .map_err(|_| "插件宿主锁已损坏".to_string())?; - let mut editors = state - .editors - .try_lock() - .map_err(|_| "编辑器注册表锁已损坏".to_string())?; - editors - .get_mut(&adapter) - .ok_or_else(|| format!("未知编辑器适配器:{adapter}"))? - .connect(pid, Path::new(project_path.trim()), version.trim()) - } - - pub(crate) fn disconnect_editor(&self, adapter: String) -> Result<(), String> { - let state = self - .state - .lock() - .map_err(|_| "插件宿主锁已损坏".to_string())?; - let mut editors = state - .editors - .try_lock() - .map_err(|_| "编辑器注册表锁已损坏".to_string())?; - if adapter == "godot-editor" { - if !editors.contains_key(&adapter) { - return Err(format!("未知编辑器适配器:{adapter}")); - } - let project = state - .active_project - .lock() - .map_err(|_| "项目上下文锁已损坏".to_string())? - .clone(); - drop(editors); - drop(state); - return crate::editor_adapters::disconnect_managed_editor_project( - crate::editor_adapters::ManagedEditor::Godot, - project.as_deref(), - ); - } - editors - .get_mut(&adapter) - .ok_or_else(|| format!("未知编辑器适配器:{adapter}"))? - .disconnect(); - Ok(()) - } - - pub(crate) fn translate_editor_rpc( - &self, - adapter: String, - method: String, - params: Value, - ) -> Result { - let state = self - .state - .lock() - .map_err(|_| "插件宿主锁已损坏".to_string())?; - let editors = state - .editors - .try_lock() - .map_err(|_| "编辑器注册表锁已损坏".to_string())?; - editors - .get(&adapter) - .ok_or_else(|| format!("未知编辑器适配器:{adapter}"))? - .translate_rpc(&method, params) - } } #[tauri::command] @@ -2078,6 +1995,7 @@ pub(crate) async fn set_agc_plugin_project_path( #[cfg(test)] mod tests { use super::*; + use crate::editor_adapter::EditorConnectionInfo; #[test] fn writer_receipt_loss_after_json_write_is_persistently_uncertain() { diff --git a/apps/ai-game-creator-shell/src-tauri/src/runner/server.rs b/apps/ai-game-creator-shell/src-tauri/src/runner/server.rs index 7df02de91..1605e5be0 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/runner/server.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/runner/server.rs @@ -22,6 +22,7 @@ pub(super) fn refresh_external_agent_runner_heartbeat( write_external_agent_runner_endpoint_atomic(&state.endpoint_path, &endpoint) } +#[cfg(any(target_os = "linux", test))] pub(super) fn bind_external_agent_runner_listener_with( mut fallback_ports: impl FnMut() -> Vec, mut bind: impl FnMut(u16) -> io::Result, diff --git a/docs/project-memory/todos/【待解决】主要构建入口编译警告清单-2026-09-23.md b/docs/project-memory/todos/【待解决】主要构建入口编译警告清单-2026-09-23.md index ea6fd62b7..5a2067fef 100644 --- a/docs/project-memory/todos/【待解决】主要构建入口编译警告清单-2026-09-23.md +++ b/docs/project-memory/todos/【待解决】主要构建入口编译警告清单-2026-09-23.md @@ -7,7 +7,7 @@ ## 1. 基线与范围 -首次诊断的基线提交为 `016356e509f11a1a638ce45ed51b9e40ef3e36a2`,诊断时工作树干净。环境为 Windows x64、Rust `1.98.1`、Node `v24.15.0`、npm `12.0.2`。第 2~6 节和附录保留首次诊断快照;后续处理状态及契约核查见第 7~19 节,不能将附录的全部条目视为仍未解决。 +首次诊断的基线提交为 `016356e509f11a1a638ce45ed51b9e40ef3e36a2`,诊断时工作树干净。环境为 Windows x64、Rust `1.98.1`、Node `v24.15.0`、npm `12.0.2`。第 2~6 节和附录保留首次诊断快照;后续处理状态及契约核查见第 7~20 节,不能将附录的全部条目视为仍未解决。 AGC Rust 使用默认 features、dev profile;237 是本轮编译器诊断数,不代表 237 个独立根因,也不是所有平台、features 和 test targets 的总数。另有 5 条不带常规源码 span 的 ts-rs 宏提示,不计入 237。 @@ -635,6 +635,23 @@ W076 只将 Pending(String) 收窄为 Pending;三处生产返回前继续记 最近一次实测仍为第 14 节的 50 条;第 15~18 节已处理 26 条,本批再处理 9 条,预期剩余 **15 条**(11 条平台 / feature / 构建脚本项、4 条暂留契约项),尚未重新编译确认。另有 5 条 ts-rs 提示,不包含在本计数中。 +## 20. 平台、feature 与构建脚本编译范围清理(2026-09-23) + +本批处理此前归为“其他配置使用”的 11 条诊断。复核后确认,该分类中也混有无调用的包装和平台占位实现,不能整组永久保留。 + +| 编号 | 实施结论 | +| --- | --- | +| W011/W012 | 共享 `Layout` 删除仅构建端使用的 `npm_package`,构建脚本根据原有 `platform` 生成相同包名。原包元数据校验移入 `build_support/codex_package_metadata.rs`,由 `build.rs` 正式调用;原校验用例随同一实现保留,运行时仅在测试编译中引入该模块。共享版本、平台布局与运行时完整性校验不变。 | +| W097/W098 | Linux 启动失败清理函数限定 Linux,直接调用现役 Unix 进程组终止 helper。删除无其它调用方的跨平台异步转发及其 Windows `taskkill` / 不支持平台分支;共享 Unix helper 仍供其它 Unix 路径使用,Windows 现役 Job Object 终止与回收保持不变。 | +| W101~W104 | 沙箱 Launch、Error、相关 impl、准备函数及导入限定 Linux。删除非 Linux 无生产调用的拒绝执行占位、专属错误 metadata getter 及其专属测试;跨平台审计使用的 `CommandSandboxMetadata` 和 `command_sandbox_platform_metadata` 保留。Linux bubblewrap 失败关闭逻辑和已有 Linux 测试不变。 | +| W117 | `RunnerManagedEditorAdapter` 的结构、实现与专用导入限定 Windows x64 且启用 Unity/Godot execute feature,与实际构造条件一致。 | +| W129 | 注册方法保留测试及真实 Cocos/Unity/Godot feature 调用;删除四个无人调用的宿主 detect/connect/disconnect/translate 包装。保留 EditorAdapter trait、实现、`host.rpc` 路由和禁用 / 切项目清理。仅测试所需的 `EditorConnectionInfo` 导入及 facade 重导出限定测试编译。 | +| W153 | 备用端口绑定算法限定 `cfg(any(target_os = "linux", test))`,保留 Linux 生产调用和三条跨平台算法测试;其它平台仍使用原有端口 0 绑定。 | + +验证:用临时独立 Cargo harness 直接引用当前工作树的两个构建辅助模块,离线运行原有平台布局与包元数据校验 **2 个测试,全部通过**;未编译 AGC 应用或执行其 build.rs。修改 Rust 文件的定向 rustfmt、编码检查、文档索引和 `git diff --check` 通过,另静态核对调用方与 cfg 条件。没有新增测试,也没有将退役业务实现仅为测试而保留;构建端原校验及其用例继续一起维护。 + +按用户要求未跑全量编译;AGC 默认应用构建、正式 editor features、Linux/macOS 构建及应用单测均未执行。最近一次应用 warning 实测仍为第 14 节的 50 条;第 15~20 节合计处理 46 条,预期剩余 **4 条暂留契约项:W004、W005、W006、W113**,不能将此预期当作重新编译结果。5 条 ts-rs 提示和前端 chunk 体积提示另行处理。 + ## 附录:237 条编译器诊断位置 以下是诊断的人工可读整理,不保存原始构建日志、本机路径或 target 缓存路径。位置相对 `apps/ai-game-creator-shell/src-tauri/`;行号只对应本节基线,修改后以符号搜索及重新编译为准。每条保留一个主要 span,编号用于本清单内跟踪,不表示独立业务缺陷。生成产物项须回到 `build_support/runtime_prompt_bundle.rs` 处理。 diff --git a/docs/technical/【技术方案】AGC通用插件宿主与编辑器适配-2026-09-09.md b/docs/technical/【技术方案】AGC通用插件宿主与编辑器适配-2026-09-09.md index 8f1cd4e16..b962ff322 100644 --- a/docs/technical/【技术方案】AGC通用插件宿主与编辑器适配-2026-09-09.md +++ b/docs/technical/【技术方案】AGC通用插件宿主与编辑器适配-2026-09-09.md @@ -122,6 +122,8 @@ host.rpc(method, params) `EditorAdapter` 契约位于通用 crate `server-rs/crates/editor-adapter-api`,只定义 `detect`、`connect`、`disconnect`、`translate_rpc` 和原生 `rpc`。宿主只保存适配器 registry,并把插件声明的适配器名称路由到对应实现;具体编辑器如何查找进程、校验 PID/项目/版本、建立连接和翻译编辑器消息,由插件包自带模块实现。 +宿主注册方法只在实际链接编辑器的 feature 或测试编译中存在:Cocos 对应 Windows 的 `cocos-editor-execute`,Runner 托管的 Unity/Godot 对应 Windows x64 的各自 execute feature。未启用这些 feature 的默认构建不编译专用适配器及其导入。插件操作统一通过 `host.rpc` 调用适配器的 `rpc`;没有调用方的宿主 detect/connect/disconnect/translate 包装不作为兼容接口保留,trait 方法、项目切换和禁用清理继续按现役合同执行。 + 宿主源码不包含编辑器专属进程名、注入逻辑或 Tauri 命令。第一个适配器 `cocos-editor` 由 `plugins/agc-cocos-editor` 提供:native 模块实现 `EditorAdapter`,由 `editor_adapters.rs` 在启动时按编译期链接注册。新增适配器不会改变 Plugin 生命周期、SDK 或权限协议。 当前 native 适配器仍由宿主在编译期链接(Cargo path 依赖);动态加载插件 native 模块不在本次范围,插件包格式与宿主协议不受此限制。