diff --git a/apps/ai-game-creator-shell/scripts/start-dev-stack.mjs b/apps/ai-game-creator-shell/scripts/start-dev-stack.mjs index 8435bbfa9..9c7e9ee8d 100644 --- a/apps/ai-game-creator-shell/scripts/start-dev-stack.mjs +++ b/apps/ai-game-creator-shell/scripts/start-dev-stack.mjs @@ -436,6 +436,10 @@ function spawnChild(command, args, options, spawnImpl = spawn) { const child = spawnImpl(command, args, { ...options, shell: useShell, + // npm.cmd and the Windows shell otherwise create a visible console for + // every service in the dev stack. Their stdout/stderr is already inherited + // by the launcher, so no separate terminal window is useful. + windowsHide: process.platform === 'win32' ? true : options.windowsHide, // POSIX 下让每个长驻服务拥有独立进程组,退出时可以连同 npm、 // Node、Cargo 及其子进程一起清理,避免残留订阅任务持续刷日志。 detached: isPosix, diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/direct_runtime.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/direct_runtime.rs index 2d94613ed..0327cee03 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/direct_runtime.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/direct_runtime.rs @@ -10,7 +10,7 @@ const MAX_DIRECT_SYSTEM_PROMPT_CHARS: usize = 16 * 1024; const MIN_DIRECT_CLIENT_TURN_ID_CHARS: usize = 6; const MAX_DIRECT_CLIENT_TURN_ID_CHARS: usize = 160; const DIRECT_TAONIER_IDENTITY_GUIDANCE: &str = "对外身份合同:你是“陶泥儿”,是 Genarrative 的游戏创作助手。用户询问你是谁、你的名称或能力时,以陶泥儿的身份回答;不要把 Codex、ChatGPT、OpenAI、模型、通用 AI 助手或内部执行智能体当作自己的名称或对外身份。Codex app-server 仅是客户端内部执行技术;只有用户明确询问底层实现时才可如实说明,同时仍以陶泥儿自称。"; -const DIRECT_AGC_ENGINEERING_GUIDANCE: &str = "AGC 工程合同:当前 cwd 是用户选择的项目目录。新 Web 游戏使用 npm + Vite,Phaser 固定为 4.2.1,在 `game.js` 或模块中使用 `import Phaser from 'phaser'`;可以按需使用其它 npm 依赖,不得复制 Phaser bundle、使用 import map 或 CDN。先读取当前 cwd 下适用的 AGENTS.md、README 或项目说明。源码使用 cwd 相对路径,例如 `index.html`、`style.css`、`game.js`、`package.json`、`package-lock.json`、`assets/hero.png`;依赖安装与构建使用项目自己的 npm scripts,完成后必须从 `dist/index.html` 试玩。 原生文件工具、patch 和命令参数使用 cwd 相对路径,例如 `index.html`、`style.css`、`game.js`、`assets/hero.png`;如果 Codex 原生文件修改不可用,可以按需用客户端 `agc_write_file` 把文本写入项目相对路径。调用 `agc_write_file` 时,content 必须是目标文件的完整原始 UTF-8 正文;不得把 command.exec 的 Exit code、Wall time、Output 包装、终端日志或解释文字一起复制进 content,命令结果只能用于判断,不能当作文件正文。`../`、绝对路径、`.agent/`、`.git/`、密钥文件和 Runtime 控制面属于客户端边界,不能请求扩权或直接改写。DirectProject 提供 Codex 原生文件、搜索、命令、图片查看、Skill、经客户端注入的 `agc_tools` MCP,以及客户端扩展列表中用户已启用的第三方 MCP。用户明确指定第三方 MCP Server 或工具时,先在当前可用工具中查找并直接调用;找不到时如实说明,不得伪造。你可以按需选择这些能力:`agc_write_file` 写入代码、配置、资源依赖清单或说明文件;`agc_generate_image` 生成普通图片、角色图、视觉规范图(icon-spec)、UI 设计图或发布宣传图;`agc_edit_image` 修改已登记图片;`taonier_prepare_game_art` 准备完整游戏美术包及可用的 canonical 切片;`agc_list_registered_assets`、`agc_list_project_files`、`agc_list_account_assets`、`agc_import_account_assets` 用于发现和接入资源依赖;`agc_create_or_derive_resource` 用于视频、角色动画、音效或背景音乐;`agc_browser_playtest` 用于需要时的本地试玩观察;Skill references 按需使用相对路径直接读取。切图、资源依赖、规范图和试玩都只是可选工具提示,不要求调用、固定顺序或特定产物,AGC 不会据此替你拆任务、编排 DAG、做强验收或阻止继续执行;不要等待 Supervisor、harness 或宿主规划器。不要读取或输出凭据、Token、Cookie、auth.json、.env 或宿主私密路径;项目锁、付费提交、幂等键、下载校验和客户端投影由客户端处理。游戏文件真实变化后客户端可登记资源和版本,Codex 不直接保存或伪造项目版本。"; +const DIRECT_AGC_ENGINEERING_GUIDANCE: &str = "AGC 工程合同:当前 cwd 是用户选择的项目目录。先读取当前 cwd 下适用的 AGENTS.md、README 或项目说明并识别实际引擎与工程结构。用户明确指定 Cocos、Unity、Godot 或其它编辑器/引擎,而当前目录不具备对应工程结构时,必须先说明不匹配并提出澄清;在澄清前不得把请求改写成 Phaser/Web 实现,也不得写文件、安装依赖、构建或试玩。仅当用户确认继续当前工程或提供了匹配的项目目录后才执行。识别为 Cocos Creator 项目时,优先使用 `agc_cocos_execute` 或 Cocos 插件的 `cocos.editor.execute` 在已打开的 Creator 编辑器中操作;不要创建 Phaser 文件,不要把 Cocos 请求改写成 Web 工程。新 Web 游戏使用 npm + Vite,Phaser 固定为 4.2.1,在 `game.js` 或模块中使用 `import Phaser from 'phaser'`;可以按需使用其它 npm 依赖,不得复制 Phaser bundle、使用 import map 或 CDN。简单修改只完成用户明确要求的范围;安装依赖、构建和试玩是后续操作,除非用户明确要求或它们是完成该项不可替代的最小验证,否则不得擅自扩展任务。源码使用 cwd 相对路径,例如 `index.html`、`style.css`、`game.js`、`package.json`、`package-lock.json`、`assets/hero.png`;依赖安装与构建使用项目自己的 npm scripts。原生文件工具、patch 和命令参数使用 cwd 相对路径,例如 `index.html`、`style.css`、`game.js`、`assets/hero.png`;如果 Codex 原生文件修改不可用,可以按需用客户端 `agc_write_file` 把文本写入项目相对路径。调用 `agc_write_file` 时,content 必须是目标文件的完整原始 UTF-8 正文;不得把 command.exec 的 Exit code、Wall time、Output 包装、终端日志或解释文字一起复制进 content,命令结果只能用于判断,不能当作文件正文。`../`、绝对路径、`.agent/`、`.git/`、密钥文件和 Runtime 控制面属于客户端边界,不能请求扩权或直接改写。DirectProject 提供 Codex 原生文件、搜索、命令、图片查看、Skill、经客户端注入的 `agc_tools` MCP,以及客户端扩展列表中用户已启用的第三方 MCP。用户明确指定第三方 MCP Server 或工具时,先在当前可用工具中查找并直接调用;找不到时如实说明,不得伪造。你可以按需选择这些能力:`agc_write_file` 写入代码、配置、资源依赖清单或说明文件;`agc_generate_image` 生成普通图片、角色图、视觉规范图(icon-spec)、UI 设计图;`agc_edit_image` 修改已登记图片;`taonier_prepare_game_art` 准备完整游戏美术包及可用的 canonical 切片;`agc_list_registered_assets`、`agc_list_project_files`、`agc_list_account_assets`、`agc_import_account_assets` 用于发现和接入资源依赖;`agc_create_or_derive_resource` 用于视频、角色动画、音效或背景音乐;`agc_browser_playtest` 用于需要时的本地试玩观察;Skill references 按需使用相对路径直接读取。切图、资源依赖、规范图和试玩都只是可选工具提示,不要求调用、固定顺序或特定产物,AGC 不会据此替你拆任务、编排 DAG、做强验收或阻止继续执行;不要等待 Supervisor、harness 或宿主规划器。不要读取或输出凭据、Token、Cookie、auth.json、.env 或宿主私密路径;项目锁、付费提交、幂等键、下载校验和客户端投影由客户端处理。游戏文件真实变化后客户端可登记资源和版本,Codex 不直接保存或伪造项目版本。"; const DIRECT_CODEX_ART_SPEC_ASSET_PATH: &str = "assets/art-spec.png"; const DIRECT_CODEX_BACKGROUND_ASSET_PATH: &str = "assets/direct-game-background.png"; const DIRECT_CODEX_SPRITESHEET_ASSET_PATH: &str = "assets/art-spritesheet.png"; @@ -4714,6 +4714,9 @@ mod tests { assert!(prompt.contains("客户端扩展列表中用户已启用的第三方 MCP")); assert!(prompt.contains("用户明确指定第三方 MCP Server 或工具时")); assert!(prompt.contains("先读取当前 cwd 下适用的 AGENTS.md、README 或项目说明")); + assert!(prompt.contains("用户明确指定 Cocos、Unity、Godot 或其它编辑器/引擎")); + assert!(prompt.contains("在澄清前不得把请求改写成 Phaser/Web 实现")); + assert!(prompt.contains("简单修改只完成用户明确要求的范围")); assert!(prompt.contains("agc_write_file")); assert!(prompt.contains("content 必须是目标文件的完整原始 UTF-8 正文")); assert!(prompt.contains("不得把 command.exec 的 Exit code、Wall time、Output 包装")); diff --git a/apps/ai-game-creator-shell/src-tauri/src/commands.rs b/apps/ai-game-creator-shell/src-tauri/src/commands.rs index 351fcf45c..ebbbf0926 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/commands.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/commands.rs @@ -16,6 +16,10 @@ const AGENT_EDITOR_ASSET_LIBRARY_MAX_ITEMS: usize = 500; const AGENT_EDITOR_ASSET_ID_MAX_CHARS: usize = 512; const AUTOMATIC_PROJECT_NAME_MAX_PROMPT_CHARS: usize = 8_000; const AUTOMATIC_PROJECT_NAME_MAX_OUTPUT_TOKENS: u32 = 64; +// Project naming is an optional homepage enhancement. It must never hold the +// actual project creation flow behind the normal (potentially three-minute) +// generation timeout. +const AUTOMATIC_PROJECT_NAME_TIMEOUT_MS: u64 = 15_000; const AUTOMATIC_PROJECT_NAME_SYSTEM_PROMPT: &str = include_str!("../prompts/automatic-project-name.md"); @@ -55,11 +59,20 @@ async fn request_automatic_project_name(prompt: &str) -> Result, let user_prompt = build_automatic_project_name_prompt(prompt)?; let app_config = load_game_creator_app_config()?; if app_config.agent_mode == GAME_CREATOR_AGENT_MODE_CODEX_APP_SERVER { - let reply = crate::agent::direct_game_creator_home_codex_chat( - AUTOMATIC_PROJECT_NAME_SYSTEM_PROMPT.trim().to_string(), - user_prompt, + let reply = tokio::time::timeout( + std::time::Duration::from_millis(AUTOMATIC_PROJECT_NAME_TIMEOUT_MS), + crate::agent::direct_game_creator_home_codex_chat( + AUTOMATIC_PROJECT_NAME_SYSTEM_PROMPT.trim().to_string(), + user_prompt, + ), ) - .await?; + .await + .map_err(|_| { + format!( + "自动项目命名超时(超过 {} 秒)", + AUTOMATIC_PROJECT_NAME_TIMEOUT_MS / 1_000 + ) + })??; return Ok(normalize_suggested_project_name(reply.trim())); } let mut llm = app_config.llm.clone(); @@ -72,10 +85,18 @@ async fn request_automatic_project_name(prompt: &str) -> Result, .with_request_timeout_ms(llm.request_timeout_ms) .with_max_output_tokens(AUTOMATIC_PROJECT_NAME_MAX_OUTPUT_TOKENS) .with_web_search(false); - let response = client - .run(request) - .await - .map_err(|error| format!("自动项目命名失败:{error}"))?; + let response = tokio::time::timeout( + std::time::Duration::from_millis(AUTOMATIC_PROJECT_NAME_TIMEOUT_MS), + client.run(request), + ) + .await + .map_err(|_| { + format!( + "自动项目命名超时(超过 {} 秒)", + AUTOMATIC_PROJECT_NAME_TIMEOUT_MS / 1_000 + ) + })? + .map_err(|error| format!("自动项目命名失败:{error}"))?; Ok(normalize_suggested_project_name(response.text.trim())) } @@ -320,13 +341,19 @@ pub(crate) fn closest_existing_project_picker_directory(path: &Path) -> Option

Result { app.path() - .document_dir() - .map(|documents_root| documents_root.join(AUTOMATIC_PROJECTS_DIRECTORY_NAME)) - .map_err(|error| format!("无法读取系统文档目录:{error}")) + .app_data_dir() + .map(|app_data_root| { + // Automatic workspaces are AGC-managed data. Keeping them below + // the hardened per-user app-data root avoids applying the strict + // private-DACL gate to a user Documents directory whose inherited + // ACL AGC is not allowed to rewrite. + app_data_root.join(AUTOMATIC_PROJECTS_DIRECTORY_NAME) + }) + .map_err(|error| format!("无法读取 AGC 应用数据目录:{error}")) } pub(crate) fn create_automatic_local_game_project_at( @@ -463,6 +490,24 @@ pub(crate) fn import_local_godot_project( import_local_godot_project_at(root, project_id.trim(), name.trim()) } +#[tauri::command] +pub(crate) fn import_local_cocos_project( + project_path: String, + project_id: String, + name: String, +) -> Result { + let root = Path::new(project_path.trim()); + enforce_project_permission_policy(root, "project.create")?; + if discover_local_cocos_project_root(root)?.is_none() { + return Err( + "所选目录不是有效的 Cocos Creator 项目(需要 package.json.creator.version 和 assets/)" + .to_string(), + ); + } + let _lock = acquire_project_write_lock(root, "project.create")?; + import_local_cocos_project_at(root, project_id.trim(), name.trim()) +} + #[tauri::command] pub(crate) fn is_local_project_directory_non_empty(project_path: String) -> Result { let root = Path::new(project_path.trim()); @@ -515,6 +560,7 @@ pub(crate) fn inspect_local_project_directory( } let recent_run_trace = recent_game_creator_run_trace(root); let godot_project_root = discover_local_godot_project_root(root)?; + let cocos_project_root = discover_local_cocos_project_root(root)?; Ok(LocalProjectDirectoryStatus { project_path: root.to_string_lossy().into_owned(), exists: root.exists(), @@ -522,6 +568,8 @@ pub(crate) fn inspect_local_project_directory( is_game_creator_project: is_game_creator_project_directory(root), is_godot_project: godot_project_root.is_some(), godot_project_root, + is_cocos_project: cocos_project_root.is_some(), + cocos_project_root, project_name: game_creator_project_name(root), modified_at: project_directory_modified_at(root), manifest_error: game_creator_project_manifest_error(root), diff --git a/apps/ai-game-creator-shell/src-tauri/src/main.rs b/apps/ai-game-creator-shell/src-tauri/src/main.rs index 699a042af..f917c8b1c 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/main.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/main.rs @@ -402,6 +402,8 @@ struct LocalProjectDirectoryStatus { is_game_creator_project: bool, is_godot_project: bool, godot_project_root: Option, + is_cocos_project: bool, + cocos_project_root: Option, project_name: Option, modified_at: Option, manifest_error: Option, @@ -2584,6 +2586,7 @@ fn main() { create_automatic_local_game_project, init_local_game_project, import_local_godot_project, + import_local_cocos_project, is_local_project_directory_non_empty, inspect_local_project_directory, pick_local_project_directory, diff --git a/apps/ai-game-creator-shell/src-tauri/src/project/manifest.rs b/apps/ai-game-creator-shell/src-tauri/src/project/manifest.rs index c9e73140c..767e77292 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/project/manifest.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/project/manifest.rs @@ -169,6 +169,80 @@ fn validate_manifest_godot_project_root(value: Option<&str>) -> Result<(), Strin .map_err(|error| format!("manifest godotProjectRoot 无效:{error}")) } +fn validate_manifest_cocos_project_root(value: Option<&str>) -> Result<(), String> { + let Some(value) = value else { + return Ok(()); + }; + if value != "." { + return Err("manifest cocosProjectRoot 只能是 .".to_string()); + } + Ok(()) +} + +pub(crate) fn discover_local_cocos_project_root( + workspace_root: &Path, +) -> Result, String> { + if workspace_root.as_os_str().is_empty() || !workspace_root.is_absolute() { + return Err("Cocos 项目目录必须是绝对路径".to_string()); + } + if project_path_has_control_chars(workspace_root) { + return Err("Cocos 项目目录不能包含控制字符".to_string()); + } + let metadata = match fs::symlink_metadata(workspace_root) { + Ok(metadata) => metadata, + Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(None), + Err(error) => { + return Err(format!( + "读取 Cocos 项目目录失败:{}: {error}", + workspace_root.display() + )); + } + }; + if godot_metadata_is_link(&metadata) || !metadata.is_dir() { + return Ok(None); + } + let package_path = workspace_root.join("package.json"); + let package_metadata = match fs::symlink_metadata(&package_path) { + Ok(metadata) => metadata, + Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(None), + Err(error) => { + return Err(format!( + "读取 Cocos package.json 失败:{}: {error}", + package_path.display() + )); + } + }; + if godot_metadata_is_link(&package_metadata) || !package_metadata.is_file() { + return Ok(None); + } + let package_text = fs::read_to_string(&package_path).map_err(|error| { + format!( + "读取 Cocos package.json 失败:{}: {error}", + package_path.display() + ) + })?; + let package: serde_json::Value = serde_json::from_str(&package_text).map_err(|error| { + format!( + "解析 Cocos package.json 失败:{}: {error}", + package_path.display() + ) + })?; + let creator_version = package + .get("creator") + .and_then(|creator| creator.get("version")) + .and_then(serde_json::Value::as_str) + .filter(|version| !version.trim().is_empty()); + let assets = workspace_root.join("assets"); + let assets_metadata = fs::symlink_metadata(&assets).ok(); + if creator_version.is_none() + || !assets_metadata + .is_some_and(|metadata| !godot_metadata_is_link(&metadata) && metadata.is_dir()) + { + return Ok(None); + } + Ok(Some(".".to_string())) +} + pub(crate) fn discover_local_godot_project_root( workspace_root: &Path, ) -> Result, String> { @@ -626,6 +700,67 @@ pub(crate) fn import_local_godot_project_at( }) } +pub(crate) fn import_local_cocos_project_at( + root: &Path, + project_id: &str, + name: &str, +) -> Result { + if root.as_os_str().is_empty() || !root.is_absolute() { + return Err("Cocos 项目目录必须是绝对路径".to_string()); + } + if project_path_has_control_chars(root) { + return Err("Cocos 项目目录不能包含控制字符".to_string()); + } + prepare_game_creator_project_root_for_read(root, true, "Cocos 工作区目录")?; + discover_local_cocos_project_root(root)?.ok_or_else(|| { + "所选目录不是有效的 Cocos Creator 项目(需要 package.json.creator.version 和 assets/)" + .to_string() + })?; + if project_id.is_empty() { + return Err("项目 ID 不能为空".to_string()); + } + let name = normalize_game_creation_project_name(name)?; + let manifest_path = root.join(".agent/manifest.json"); + if manifest_storage_exists(&manifest_path)? { + let mut manifest = read_manifest(&manifest_path)?; + if manifest.cocos_project_root.as_deref() != Some(".") { + manifest.cocos_project_root = Some(".".to_string()); + write_manifest(&manifest_path, &manifest)?; + } + return Ok(InitLocalProjectResult { + project_path: root.to_string_lossy().into_owned(), + manifest_path: manifest_path.to_string_lossy().into_owned(), + manifest, + }); + } + let agent_db_path = root.join(".agent/agent.db"); + if !agent_db_path.exists() { + append_agent_db_record( + root, + serde_json::json!({ + "recordType": "project.import", + "projectId": project_id, + "name": name, + "projectKind": "cocos", + "cocosProjectRoot": ".", + }), + )?; + } + for relative in [".agent/logs", ".agent/runtime"] { + let path = root.join(relative); + ensure_game_creator_private_directory_tree(&path, "Cocos 项目 Agent 目录")?; + prepare_game_creator_private_path_for_read(&path, true, "Cocos 项目 Agent 目录")?; + } + let mut manifest = new_game_creation_app_manifest(project_id, name); + manifest.cocos_project_root = Some(".".to_string()); + write_manifest(&manifest_path, &manifest)?; + Ok(InitLocalProjectResult { + project_path: root.to_string_lossy().into_owned(), + manifest_path: manifest_path.to_string_lossy().into_owned(), + manifest, + }) +} + pub(crate) fn record_preview_state( root: &Path, status: GameCreationAppPreviewStatus, @@ -1374,6 +1509,8 @@ pub(crate) fn read_manifest(path: &Path) -> Result { if (supervisorChatOnly) return; - void setAgcPluginProjectPath(localProject?.projectPath ?? null).catch( - () => undefined, - ); + const nextProjectPath = localProject?.projectPath ?? null; + void setAgcPluginProjectPath(nextProjectPath) + .then(async () => { + if (workspaceProjectKind === 'cocos' && nextProjectPath) { + await startAgcPlugin('agc-cocos-editor'); + } + }) + .catch((error) => { + if (workspaceProjectKind !== 'cocos' || !nextProjectPath) { + return; + } + setWorkspaceStatus( + `Cocos Creator 插件未就绪:${ + error instanceof Error ? error.message : String(error) + }`, + ); + }); return () => { void setAgcPluginProjectPath(null).catch(() => undefined); }; - }, [localProject?.projectPath, supervisorChatOnly]); + }, [localProject?.projectPath, supervisorChatOnly, workspaceProjectKind]); const manifestRefreshMountedRef = useRef(true); const manifestRefreshStatesRef = useRef( @@ -1415,7 +1429,9 @@ export function App({ void openWorkspace( initialProjectPath, false, - initialProjectKind === 'godot' ? 'open' : 'create', + initialProjectKind === 'godot' || initialProjectKind === 'cocos' + ? 'open' + : 'create', initialProjectKind, ); // Initial project opening is guarded by initialProjectOpenedRef. diff --git a/apps/ai-game-creator-shell/src/app/types.ts b/apps/ai-game-creator-shell/src/app/types.ts index bd67ab570..940c8bbf1 100644 --- a/apps/ai-game-creator-shell/src/app/types.ts +++ b/apps/ai-game-creator-shell/src/app/types.ts @@ -128,7 +128,7 @@ export type AgcExtensionSummary = { clientExtension: ClientExtensionItem | null; }; -export type LocalProjectKind = 'web' | 'godot'; +export type LocalProjectKind = 'web' | 'godot' | 'cocos'; export type ProjectStartMode = 'planning' | 'direct-build'; @@ -182,6 +182,8 @@ export interface LocalProjectDirectoryStatus { isGameCreatorProject: boolean; isGodotProject: boolean; godotProjectRoot: string | null; + isCocosProject?: boolean; + cocosProjectRoot?: string | null; projectName: string | null; modifiedAt?: number | null; manifestError?: string | null; diff --git a/apps/ai-game-creator-shell/src/features/agent-runtime/model.ts b/apps/ai-game-creator-shell/src/features/agent-runtime/model.ts index 5067cc052..4c331835c 100644 --- a/apps/ai-game-creator-shell/src/features/agent-runtime/model.ts +++ b/apps/ai-game-creator-shell/src/features/agent-runtime/model.ts @@ -40,7 +40,7 @@ export function resolveProjectSupervisorRuntimeSubmission({ supervisorChatOnly, planningEntry = false, }: { - workspaceProjectKind: 'web' | 'godot'; + workspaceProjectKind: 'web' | 'godot' | 'cocos'; orchestrationMode: 'single-supervisor' | 'professional-dag'; supervisorChatOnly: boolean; planningEntry?: boolean; diff --git a/apps/ai-game-creator-shell/src/features/app-shell/ProjectCreation.tsx b/apps/ai-game-creator-shell/src/features/app-shell/ProjectCreation.tsx index 2aad6e5c3..db9b11200 100644 --- a/apps/ai-game-creator-shell/src/features/app-shell/ProjectCreation.tsx +++ b/apps/ai-game-creator-shell/src/features/app-shell/ProjectCreation.tsx @@ -24,13 +24,22 @@ import type { HomeProjectCreationController } from './useHomeProjectCreation'; import type { RecentProjectsController } from './useRecentProjects'; function projectKindLabel(project: RecentProjectRow) { + if (project.projectKind === 'cocos') { + return 'Cocos Creator'; + } + if (project.projectKind === 'unity') { + return 'Unity'; + } + if (project.projectKind === 'ue') { + return 'Unreal Engine'; + } if (project.projectKind === 'godot') { return project.godotProjectRoot && project.godotProjectRoot !== '.' ? `Godot · ${project.godotProjectRoot}` : 'Godot'; } if (project.projectKind === 'web') { - return 'GameAgent'; + return 'Phaser'; } return '待识别'; } @@ -314,7 +323,8 @@ export function ProjectsPage({ onSubmit={submitRename} > - {project.projectKind === 'godot' ? ( + {project.projectKind === 'godot' || + project.projectKind === 'cocos' ? (

+ {status} +

+ ) : null}
{ + delete window.__TAURI__; +}); + +test('打开 Cocos 目录走导入入口,不创建 Web 脚手架或发送初始消息', async () => { + const manifest = { + ...createGameCreationAppManifest('cocos-project', 'Cocos 项目'), + cocosProjectRoot: '.', + }; + const invoke = vi.fn(async (command: string) => { + if (command === 'inspect_local_project_directory') { + return { + exists: true, + isDirectory: true, + isGameCreatorProject: false, + isGodotProject: false, + godotProjectRoot: null, + isCocosProject: true, + cocosProjectRoot: '.', + projectName: 'Cocos 项目', + recentRunStatus: null, + recentRunStopReason: null, + }; + } + if (command === 'import_local_cocos_project') { + return { projectPath: 'C:/projects/cocos', manifest }; + } + if (command === 'get_local_game_project_revision') return { revision: 0 }; + throw new Error(`意外调用:${command}`); + }); + window.__TAURI__ = { core: { invoke } }; + const setLauncherView = vi.fn(); + const { result } = renderHook(() => + useHomeProjectCreation({ + setStatus: vi.fn(), + setLauncherView, + setAgentChatProjectPath: vi.fn(), + rememberRecentWorkspace: vi.fn(), + }), + ); + + await act(() => result.current.openProject('C:/projects/cocos', 'open')); + + expect(invoke).toHaveBeenCalledWith('import_local_cocos_project', { + projectPath: 'C:/projects/cocos', + projectId: expect.any(String), + name: 'cocos', + }); + expect(result.current.currentProjectContext).toMatchObject({ + projectKind: 'cocos', + projectPath: 'C:/projects/cocos', + initialPrompt: '', + manifest, + }); + expect(setLauncherView).toHaveBeenCalledWith('project-development'); + expect( + invoke.mock.calls.some( + ([command]) => command === 'init_local_game_project', + ), + ).toBe(false); +}); diff --git a/docs/project-memory/shared-memory/pitfalls.md b/docs/project-memory/shared-memory/pitfalls.md index 451a00738..cac752894 100644 --- a/docs/project-memory/shared-memory/pitfalls.md +++ b/docs/project-memory/shared-memory/pitfalls.md @@ -34,6 +34,21 @@ 主配置与 local overlay 的单文件原子写入不能保证整体成功;覆盖层写入失败会留下混合配置。保存前先序列化全部变更,多文件保存保留原内容,错误时逆序恢复并报告回滚失败;单文件保持原写入路径,成功后不回读、不触发外部诊断。此回滚仅处理可捕获错误,不承诺进程崩溃下的事务恢复。 +## 2026-09-11 首页自动工作区必须使用 AGC 管理目录 + +自动创建工作区使用 `app_data_dir()/projects`。不要把自动工作区根放到用户 +`Documents`:该目录通常带继承 ACL,AGC 的受管私有目录门禁会拒绝改写,导致 +首页命名回合成功后创建命令失败且不会留下项目目录。用户通过目录选择器创建的 +项目仍走 user-selected 权限范围。 + +## 2026-09-11 Cocos 项目必须走独立导入分支 + +Cocos Creator 根目录由 `package.json.creator.version` 与普通 `assets/` 目录识别; +打开时调用 `import_local_cocos_project` 建立最小 `.agent` 记录,不得复用 Phaser +新建脚手架。Cocos 入口还必须把项目上下文同步给内置插件,并使用 +`cocos-editor-execute` feature;否则目录选择成功后会因既有逻辑只认识 AGC/Godot +而无任何可见结果。 + ## 2026-09-09 `npm run agc` 的 Ctrl+C 不能只依赖 shell 包装层与端口健康检查 - **现象**:`npm run agc` 按 Ctrl+C 后终端回到提示符,但上个工作树的 `api-server.exe` / SpacetimeDB 仍在监听 `8082` / `8083` / `3101`;切到另一个 worktree 再启动 AGC 时,前端仍然连到上个工作树的后端,在改过数据库 / schema 的工作树上会串库。 diff --git a/docs/project-memory/todos/【目标编辑器适配】Unity与Unreal项目识别与导入-2026-09-11.md b/docs/project-memory/todos/【目标编辑器适配】Unity与Unreal项目识别与导入-2026-09-11.md new file mode 100644 index 000000000..3d49c3428 --- /dev/null +++ b/docs/project-memory/todos/【目标编辑器适配】Unity与Unreal项目识别与导入-2026-09-11.md @@ -0,0 +1,43 @@ +# Unity 与 Unreal 项目识别与导入 + +状态:开放,暂不实现 + +## 背景 + +AGC 的“打开项目”入口已经支持 AGC(`.agent/manifest.json`)、Cocos Creator +(`package.json.creator.version` + `assets/`)和 Godot(`project.godot`)。 + +首页与项目列表的显示层已经预留 `unity` / `ue` 两种项目类型: + +- `RecentProjectRow.projectKind`、`HomeProjectRow.projectKind` 已包含 + `unity`、`ue`; +- 类型标签、搜索关键词和图标分支已就绪; +- 打开入口目前只判定 AGC / Cocos / Godot,其余目录提示 + “未识别为支持的 AGC、Godot 或 Cocos Creator 项目”。 + +因此 `unity` / `ue` 今天不会出现在列表里,它们只是显示层占位,没有对应识别、 +导入命令或目标编辑器适配器。 + +## 结论 + +Unity、Unreal 的项目识别、导入和编辑器适配暂不实现。保留显示层占位,不写猜测性 +识别代码,也不为未接入的编辑器创建 `.agent` 记录。 + +## 未来实现时的最小增量 + +1. Rust:`discover_local_unity_project_root`(`Assets/` 与 + `ProjectSettings/ProjectVersion.txt`)、`discover_local_unreal_project_root` + (根目录或一层子目录的 `*.uproject`);把 `is_unity_project` / + `is_unreal_project` 加入 `LocalProjectDirectoryStatus`。 +2. Rust:新增 `import_local_unity_project` / `import_local_unreal_project` + 命令,manifest 增加对应项目根字段并做同 Cocos 的根校验。 +3. 前端:`openProject` 增加两条导入分支,`projectKind` 映射补齐 `unity` / `ue`。 +4. 图标:Unity、Unreal 各自使用独立图标,不再落到 `FolderKanban`。 +5. 适配器:只有真实接入目标编辑器连接与执行能力后,才注册对应 + `EditorAdapter`;不得先写死进程扫描或注入路径。 + +## 关闭条件 + +- Unity 与 Unreal 的目录识别、导入、项目类型显示在真实项目上通过验收; +- 各自的编辑器适配器要么完成真实验证,要么明确记录为未接入能力; +- 补齐对应技术文档,并删除本 TODO。 diff --git a/docs/technical/【技术方案】AGC Cocos Creator 编辑器桥接模块-2026-09-09.md b/docs/technical/【技术方案】AGC Cocos Creator 编辑器桥接模块-2026-09-09.md index 511db1b39..4e03f5d1f 100644 --- a/docs/technical/【技术方案】AGC Cocos Creator 编辑器桥接模块-2026-09-09.md +++ b/docs/technical/【技术方案】AGC Cocos Creator 编辑器桥接模块-2026-09-09.md @@ -48,6 +48,15 @@ plugins/agc-cocos-editor/ 该插件是**内置插件**:随客户端分发、不能卸载,只能通过 `set_agc_plugin_enabled` 控制是否可用。禁用后插件进程停止且不能启动,Runtime 工具 `cocos.editor.execute` 与 DirectProject 的 `agc_cocos_execute` 同时从工具目录、工具策略快照和 Agent 上下文里消失;重新启用后立即恢复。开关状态保存在 AppData `extensions/builtin-plugins.json`。 +## AGC 项目打开入口 + +AGC 识别根目录同时存在 `package.json.creator.version` 与普通 `assets/` 目录的 +Cocos Creator 项目,并将其标记为 `cocos` 项目类型。选择目录后通过 +`import_local_cocos_project` 建立最小 `.agent/` 运行记录,不创建 Phaser/Web +脚手架;工作区上下文同步到内置 `agc-cocos-editor` 插件,开发态默认启用 +`cocos-editor-execute` feature。若目录不是已支持的 AGC、Godot 或 Cocos 项目, +入口必须显示明确错误,不再静默返回。 + ## 第一阶段命令协议 DirectProject 的现役 `agc_tools` 目录通过 Windows `cocos-editor-execute` feature 注册 `agc_cocos_execute`,参数只有 `code`。客户端在 blocking worker 内调用插件 native 模块,保留项目锁和现有项目权限;当前 bridge 出现执行结果不确定后拒绝后续 execute。旧 Runtime 的对应工具名为 `cocos.editor.execute`,继续使用它已有的 pending action、权限和恢复语义;插件入口注册的同名命令走宿主 `host.rpc` → `EditorAdapter` 路径,两条路径共享同一 native 实现和不确定结果阻断语义。 diff --git a/package-lock.json b/package-lock.json index d81e19d56..3f8b4559e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -259,6 +259,7 @@ "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -4079,6 +4080,7 @@ "resolved": "https://registry.npmjs.org/@expo/metro-runtime/-/metro-runtime-56.0.15.tgz", "integrity": "sha512-WIWeVsL6kCSB57oYZdUA4MTkH7c67UFMIjdNoQzKXwxZYwBFE/xL2cGPDC3z8RWt0femzJTVxAVZUOW/hiqRzA==", "license": "MIT", + "peer": true, "dependencies": { "@expo/log-box": "^56.0.13", "anser": "^1.4.9", @@ -8114,7 +8116,6 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, - "peer": true, "engines": { "node": ">=10" }, @@ -8127,7 +8128,6 @@ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", "dev": true, - "peer": true, "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", @@ -8141,8 +8141,7 @@ "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true, - "peer": true + "dev": true }, "node_modules/@testing-library/react": { "version": "16.3.2", @@ -8226,8 +8225,7 @@ "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", - "dev": true, - "peer": true + "dev": true }, "node_modules/@types/babel__core": { "version": "7.20.5", @@ -8280,7 +8278,8 @@ "version": "4.3.20", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.20.tgz", "integrity": "sha512-/pC9HAB5I/xMlc5FP77qjCnI16ChlJfW0tGa0IUcFn38VJrTV6DeZ60NU5KZBtaOZqjdpwTWohz5HU1RrhiYxQ==", - "dev": true + "dev": true, + "peer": true }, "node_modules/@types/chai-subset": { "version": "1.3.6", @@ -8388,6 +8387,7 @@ "version": "22.19.15", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.15.tgz", "integrity": "sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg==", + "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -8406,6 +8406,7 @@ "version": "19.2.14", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -8414,6 +8415,7 @@ "version": "19.2.3", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "peer": true, "peerDependencies": { "@types/react": "^19.2.0" } @@ -8503,6 +8505,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "dev": true, + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/types": "6.21.0", @@ -8915,6 +8918,7 @@ "version": "8.16.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -9121,7 +9125,6 @@ "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "dequal": "^2.0.3" } @@ -9582,6 +9585,7 @@ "url": "https://github.com/sponsors/ai" } ], + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -10641,8 +10645,7 @@ "version": "0.5.16", "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", - "dev": true, - "peer": true + "dev": true }, "node_modules/domexception": { "version": "4.0.0", @@ -10851,6 +10854,7 @@ "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -11326,6 +11330,7 @@ "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -11616,6 +11621,7 @@ "resolved": "https://registry.npmjs.org/expo/-/expo-56.0.12.tgz", "integrity": "sha512-FxgdI/Yqva6iJOThZIHfvxlKPxs4EC4uScUnEswwSArR/Fj9k430O13R590LcOQTsdNsjIs+GBHwjfoAY6vmAQ==", "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.20.0", "@expo/cli": "^56.1.16", @@ -11800,6 +11806,7 @@ "resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-56.0.18.tgz", "integrity": "sha512-8AMtbDGl/WVPnWlmbpGmvcdnNCy9E4PFnwdVwj600vljkMDPSxcAcjw8GVXEPk3PpZ+ngTqsrkltWyj0UKYAxw==", "license": "MIT", + "peer": true, "dependencies": { "@expo/env": "~2.3.0" }, @@ -11855,6 +11862,7 @@ "resolved": "https://registry.npmjs.org/expo-font/-/expo-font-56.0.7.tgz", "integrity": "sha512-hpU/vRwPzsby9lPGkA4blDqLIIXYzoWnCZHr6PxvcWbY/uPObAiyhh6q+e0WYsB65SthK+PLH95jEnVag7fwEg==", "license": "MIT", + "peer": true, "dependencies": { "fontfaceobserver": "^2.1.0" }, @@ -12082,6 +12090,7 @@ "resolved": "https://registry.npmjs.org/expo-server/-/expo-server-56.0.5.tgz", "integrity": "sha512-SmM2p2g3Jrktpiazcst+OxhjSzOHXKAY4BPURHYHXvApzzoybMmrNF4IEZ8DKZ145BhSe4ydAmlEFCRTsdtgUQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=20.16.0" } @@ -13237,6 +13246,7 @@ "integrity": "sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } @@ -13910,7 +13920,6 @@ "resolved": "https://registry.npmjs.org/isomorphic.js/-/isomorphic.js-0.2.5.tgz", "integrity": "sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw==", "license": "MIT", - "peer": true, "funding": { "type": "GitHub Sponsors ❤", "url": "https://github.com/sponsors/dmonad" @@ -14175,6 +14184,7 @@ "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-22.1.0.tgz", "integrity": "sha512-/9AVW7xNbsBv6GfWho4TTNjEo9fe6Zhf9O7s0Fhhr3u+awPwAJMKwAMXnkk5vBxflqLW9hTHX/0cs+P3gW+cQw==", "dev": true, + "peer": true, "dependencies": { "abab": "^2.0.6", "cssstyle": "^3.0.0", @@ -14419,7 +14429,6 @@ "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.117.tgz", "integrity": "sha512-DeXj9X5xDCjgKLU/7RR+/HQEVzuuEUiwldwOGsHK/sfAfELGWEyTcf0x+uOvCvK3O2zPmZePXWL85vtia6GyZw==", "license": "MIT", - "peer": true, "dependencies": { "isomorphic.js": "^0.2.4" }, @@ -15215,7 +15224,6 @@ "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", "dev": true, - "peer": true, "bin": { "lz-string": "bin/bin.js" } @@ -17468,7 +17476,8 @@ "version": "2.16.0", "resolved": "https://registry.npmjs.org/overlayscrollbars/-/overlayscrollbars-2.16.0.tgz", "integrity": "sha512-N03oje/q7j93D0aLZtoCdsDSYLmhheSsv8H7oSLE7HhdV9P/bmCURtLV/KbPye7P/bpfyt/obSfDpGUYoJ0OWg==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/overlayscrollbars-react": { "version": "0.5.6", @@ -17898,6 +17907,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.12", "picocolors": "^1.1.1", @@ -18202,6 +18212,7 @@ "version": "19.2.4", "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -18320,6 +18331,7 @@ "version": "19.2.4", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", + "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -18373,6 +18385,7 @@ "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.86.0.tgz", "integrity": "sha512-17ALh/dd6AO4pgOVmOO5Axll5PbErEo3XFyLokyzW6usyi+OShIEPwUW26wLPlhVifgSOIfECCH0WN+0IqtJ1w==", "license": "MIT", + "peer": true, "dependencies": { "@react-native/assets-registry": "0.86.0", "@react-native/codegen": "0.86.0", @@ -18442,6 +18455,7 @@ "resolved": "https://registry.npmjs.org/react-native-webview/-/react-native-webview-13.16.1.tgz", "integrity": "sha512-If0eHhoEdOYDcHsX+xBFwHMbWBGK1BvGDQDQdVkwtSIXiq1uiqjkpWVP2uQ1as94J0CzvFE9PUNDuhiX0Z6ubw==", "license": "MIT", + "peer": true, "dependencies": { "escape-string-regexp": "^4.0.0", "invariant": "2.2.4" @@ -18624,6 +18638,7 @@ "version": "0.18.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -20337,6 +20352,7 @@ "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", "devOptional": true, + "peer": true, "dependencies": { "esbuild": "~0.27.0", "get-tsconfig": "^4.7.5" @@ -20412,6 +20428,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "devOptional": true, + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -20801,6 +20818,7 @@ "version": "6.4.1", "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", + "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", @@ -21788,6 +21806,7 @@ "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.34.6.tgz", "integrity": "sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==", "dev": true, + "peer": true, "dependencies": { "@types/chai": "^4.3.5", "@types/chai-subset": "^1.3.3", @@ -22566,6 +22585,7 @@ "version": "8.20.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz", "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==", + "peer": true, "engines": { "node": ">=10.0.0" }, @@ -22989,6 +23009,7 @@ "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "peer": true, "requires": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -25505,6 +25526,7 @@ "version": "56.0.15", "resolved": "https://registry.npmjs.org/@expo/metro-runtime/-/metro-runtime-56.0.15.tgz", "integrity": "sha512-WIWeVsL6kCSB57oYZdUA4MTkH7c67UFMIjdNoQzKXwxZYwBFE/xL2cGPDC3z8RWt0femzJTVxAVZUOW/hiqRzA==", + "peer": true, "requires": { "@expo/log-box": "^56.0.13", "anser": "^1.4.9", @@ -28125,15 +28147,13 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "peer": true + "dev": true }, "pretty-format": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", "dev": true, - "peer": true, "requires": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", @@ -28144,8 +28164,7 @@ "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true, - "peer": true + "dev": true } } }, @@ -28199,8 +28218,7 @@ "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", - "dev": true, - "peer": true + "dev": true }, "@types/babel__core": { "version": "7.20.5", @@ -28252,7 +28270,8 @@ "version": "4.3.20", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.20.tgz", "integrity": "sha512-/pC9HAB5I/xMlc5FP77qjCnI16ChlJfW0tGa0IUcFn38VJrTV6DeZ60NU5KZBtaOZqjdpwTWohz5HU1RrhiYxQ==", - "dev": true + "dev": true, + "peer": true }, "@types/chai-subset": { "version": "1.3.6", @@ -28348,6 +28367,7 @@ "version": "22.19.15", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.15.tgz", "integrity": "sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg==", + "peer": true, "requires": { "undici-types": "~6.21.0" } @@ -28365,6 +28385,7 @@ "version": "19.2.14", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "peer": true, "requires": { "csstype": "^3.2.2" } @@ -28373,6 +28394,7 @@ "version": "19.2.3", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "peer": true, "requires": {} }, "@types/semver": { @@ -28436,6 +28458,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "dev": true, + "peer": true, "requires": { "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/types": "6.21.0", @@ -28706,7 +28729,8 @@ "acorn": { "version": "8.16.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", - "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==" + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "peer": true }, "acorn-jsx": { "version": "5.3.2", @@ -28848,7 +28872,6 @@ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", "dev": true, - "peer": true, "requires": { "dequal": "^2.0.3" } @@ -29154,6 +29177,7 @@ "version": "4.28.1", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "peer": true, "requires": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -29855,8 +29879,7 @@ "version": "0.5.16", "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", - "dev": true, - "peer": true + "dev": true }, "domexception": { "version": "4.0.0", @@ -30023,6 +30046,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", "dev": true, + "peer": true, "requires": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -30353,6 +30377,7 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "dev": true, + "peer": true, "requires": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -30546,6 +30571,7 @@ "version": "56.0.12", "resolved": "https://registry.npmjs.org/expo/-/expo-56.0.12.tgz", "integrity": "sha512-FxgdI/Yqva6iJOThZIHfvxlKPxs4EC4uScUnEswwSArR/Fj9k430O13R590LcOQTsdNsjIs+GBHwjfoAY6vmAQ==", + "peer": true, "requires": { "@babel/runtime": "^7.20.0", "@expo/cli": "^56.1.16", @@ -30788,6 +30814,7 @@ "version": "56.0.18", "resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-56.0.18.tgz", "integrity": "sha512-8AMtbDGl/WVPnWlmbpGmvcdnNCy9E4PFnwdVwj600vljkMDPSxcAcjw8GVXEPk3PpZ+ngTqsrkltWyj0UKYAxw==", + "peer": true, "requires": { "@expo/env": "~2.3.0" }, @@ -30825,6 +30852,7 @@ "version": "56.0.7", "resolved": "https://registry.npmjs.org/expo-font/-/expo-font-56.0.7.tgz", "integrity": "sha512-hpU/vRwPzsby9lPGkA4blDqLIIXYzoWnCZHr6PxvcWbY/uPObAiyhh6q+e0WYsB65SthK+PLH95jEnVag7fwEg==", + "peer": true, "requires": { "fontfaceobserver": "^2.1.0" } @@ -30971,7 +30999,8 @@ "expo-server": { "version": "56.0.5", "resolved": "https://registry.npmjs.org/expo-server/-/expo-server-56.0.5.tgz", - "integrity": "sha512-SmM2p2g3Jrktpiazcst+OxhjSzOHXKAY4BPURHYHXvApzzoybMmrNF4IEZ8DKZ145BhSe4ydAmlEFCRTsdtgUQ==" + "integrity": "sha512-SmM2p2g3Jrktpiazcst+OxhjSzOHXKAY4BPURHYHXvApzzoybMmrNF4IEZ8DKZ145BhSe4ydAmlEFCRTsdtgUQ==", + "peer": true }, "expo-sharing": { "version": "56.0.18", @@ -31635,7 +31664,8 @@ "version": "16.8.1", "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.8.1.tgz", "integrity": "sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==", - "dev": true + "dev": true, + "peer": true }, "graphql-tag": { "version": "2.12.6", @@ -32064,8 +32094,7 @@ "isomorphic.js": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/isomorphic.js/-/isomorphic.js-0.2.5.tgz", - "integrity": "sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw==", - "peer": true + "integrity": "sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw==" }, "istanbul-lib-coverage": { "version": "3.2.2", @@ -32255,6 +32284,7 @@ "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-22.1.0.tgz", "integrity": "sha512-/9AVW7xNbsBv6GfWho4TTNjEo9fe6Zhf9O7s0Fhhr3u+awPwAJMKwAMXnkk5vBxflqLW9hTHX/0cs+P3gW+cQw==", "dev": true, + "peer": true, "requires": { "abab": "^2.0.6", "cssstyle": "^3.0.0", @@ -32437,7 +32467,6 @@ "version": "0.2.117", "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.117.tgz", "integrity": "sha512-DeXj9X5xDCjgKLU/7RR+/HQEVzuuEUiwldwOGsHK/sfAfELGWEyTcf0x+uOvCvK3O2zPmZePXWL85vtia6GyZw==", - "peer": true, "requires": { "isomorphic.js": "^0.2.4" } @@ -32877,8 +32906,7 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", - "dev": true, - "peer": true + "dev": true }, "magic-string": { "version": "0.30.21", @@ -34363,7 +34391,8 @@ "overlayscrollbars": { "version": "2.16.0", "resolved": "https://registry.npmjs.org/overlayscrollbars/-/overlayscrollbars-2.16.0.tgz", - "integrity": "sha512-N03oje/q7j93D0aLZtoCdsDSYLmhheSsv8H7oSLE7HhdV9P/bmCURtLV/KbPye7P/bpfyt/obSfDpGUYoJ0OWg==" + "integrity": "sha512-N03oje/q7j93D0aLZtoCdsDSYLmhheSsv8H7oSLE7HhdV9P/bmCURtLV/KbPye7P/bpfyt/obSfDpGUYoJ0OWg==", + "peer": true }, "overlayscrollbars-react": { "version": "0.5.6", @@ -34661,6 +34690,7 @@ "version": "8.5.15", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "peer": true, "requires": { "nanoid": "^3.3.12", "picocolors": "^1.1.1", @@ -34868,7 +34898,8 @@ "react": { "version": "19.2.4", "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", - "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==" + "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", + "peer": true }, "react-arborist": { "version": "3.16.0", @@ -34937,6 +34968,7 @@ "version": "19.2.4", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", + "peer": true, "requires": { "scheduler": "^0.27.0" } @@ -34974,6 +35006,7 @@ "version": "0.86.0", "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.86.0.tgz", "integrity": "sha512-17ALh/dd6AO4pgOVmOO5Axll5PbErEo3XFyLokyzW6usyi+OShIEPwUW26wLPlhVifgSOIfECCH0WN+0IqtJ1w==", + "peer": true, "requires": { "@react-native/assets-registry": "0.86.0", "@react-native/codegen": "0.86.0", @@ -35126,6 +35159,7 @@ "version": "13.16.1", "resolved": "https://registry.npmjs.org/react-native-webview/-/react-native-webview-13.16.1.tgz", "integrity": "sha512-If0eHhoEdOYDcHsX+xBFwHMbWBGK1BvGDQDQdVkwtSIXiq1uiqjkpWVP2uQ1as94J0CzvFE9PUNDuhiX0Z6ubw==", + "peer": true, "requires": { "escape-string-regexp": "^4.0.0", "invariant": "2.2.4" @@ -35134,7 +35168,8 @@ "react-refresh": { "version": "0.18.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", - "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==" + "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", + "peer": true }, "react-remove-scroll": { "version": "2.7.2", @@ -36295,6 +36330,7 @@ "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", "devOptional": true, + "peer": true, "requires": { "esbuild": "~0.27.0", "fsevents": "~2.3.3", @@ -36344,7 +36380,8 @@ "version": "5.8.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", - "devOptional": true + "devOptional": true, + "peer": true }, "ufo": { "version": "1.6.3", @@ -36580,6 +36617,7 @@ "version": "6.4.1", "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", + "peer": true, "requires": { "esbuild": "^0.25.0", "fdir": "^6.4.4", @@ -37007,6 +37045,7 @@ "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.34.6.tgz", "integrity": "sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==", "dev": true, + "peer": true, "requires": { "@types/chai": "^4.3.5", "@types/chai-subset": "^1.3.3", @@ -37422,6 +37461,7 @@ "version": "8.20.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz", "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==", + "peer": true, "requires": {} }, "xcode": { diff --git a/packages/shared/src/contracts/gameCreationApp.ts b/packages/shared/src/contracts/gameCreationApp.ts index bac24dc3d..0a8782d1a 100644 --- a/packages/shared/src/contracts/gameCreationApp.ts +++ b/packages/shared/src/contracts/gameCreationApp.ts @@ -592,6 +592,7 @@ export interface GameCreationAppManifest { commandRuns?: GameCreationAppCommandRunState[]; versions?: GameIterationVersion[]; godotProjectRoot?: string | null; + cocosProjectRoot?: string | null; } export interface GameCreationAgentToolCallTrace { diff --git a/scripts/dev.mjs b/scripts/dev.mjs index 237a31f31..964cba65b 100644 --- a/scripts/dev.mjs +++ b/scripts/dev.mjs @@ -1777,6 +1777,7 @@ class DevRunner { env, stdio: ['ignore', 'pipe', 'pipe'], shell: process.platform === 'win32', + windowsHide: process.platform === 'win32', }, ); @@ -2096,6 +2097,7 @@ class DevRunner { env: mergedEnv, stdio: ['ignore', 'pipe', 'pipe'], shell: process.platform === 'win32', + windowsHide: process.platform === 'win32', }, ); @@ -2171,6 +2173,7 @@ class DevRunner { env: mergedEnv, stdio: ['ignore', 'pipe', 'pipe'], shell: process.platform === 'win32', + windowsHide: process.platform === 'win32', }, ); @@ -2259,6 +2262,7 @@ class DevRunner { env, ...createDevServerSpawnOptions(), shell: process.platform === 'win32', + windowsHide: process.platform === 'win32', }, ); @@ -2303,6 +2307,7 @@ class DevRunner { env, ...createDevServerSpawnOptions(), shell: process.platform === 'win32', + windowsHide: process.platform === 'win32', }, ); @@ -3260,6 +3265,7 @@ function runForeground(command, args, { cwd, env, label }) { env, stdio: ['inherit', 'pipe', 'pipe'], shell: process.platform === 'win32', + windowsHide: process.platform === 'win32', }); child.stdout?.on('data', (chunk) => capture(chunk, process.stdout)); diff --git a/server-rs/crates/shared-contracts/src/game_creation_app.rs b/server-rs/crates/shared-contracts/src/game_creation_app.rs index a6000cf6f..5953f8c84 100644 --- a/server-rs/crates/shared-contracts/src/game_creation_app.rs +++ b/server-rs/crates/shared-contracts/src/game_creation_app.rs @@ -795,6 +795,8 @@ pub struct GameCreationAppManifest { pub versions: Vec, #[serde(default, skip_serializing_if = "Option::is_none")] pub godot_project_root: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub cocos_project_root: Option, } #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] @@ -928,6 +930,7 @@ pub fn new_game_creation_app_manifest( command_runs: Vec::new(), versions: Vec::new(), godot_project_root: None, + cocos_project_root: None, } }