From 7532d459086cf18d47d92ea4ed96facfd8c22981 Mon Sep 17 00:00:00 2001 From: fengyuzhe Date: Wed, 31 Dec 2025 10:48:56 +0800 Subject: [PATCH 1/8] Add required field validation for the native platform. --- src/api/builder/schema.ts | 247 ++++++++++++++++++++++------------ src/mcp/hooks/builder.hook.ts | 107 +++++++++++++++ src/mcp/mcp.middleware.ts | 26 +--- 3 files changed, 272 insertions(+), 108 deletions(-) create mode 100644 src/mcp/hooks/builder.hook.ts diff --git a/src/api/builder/schema.ts b/src/api/builder/schema.ts index 6725f7ef..3364d14c 100644 --- a/src/api/builder/schema.ts +++ b/src/api/builder/schema.ts @@ -19,19 +19,19 @@ export const SchemaPolyfills = z.object({ export const SchemaBundleConfig = z.object({ root: z.string().describe('Root directory of the bundle'), // bundle 的根目录 priority: z.number().optional().describe('Priority'), // 优先级 - compressionType: z.enum(['none', 'merge_dep', 'merge_all_json', 'subpackage', 'zip']).default('none').optional().describe('Compression Type'), // 压缩类型 - isRemote: z.boolean().default(false).optional().describe('Is remote bundle'), // 是否是远程包 + compressionType: z.enum(['none', 'merge_dep', 'merge_all_json', 'subpackage', 'zip']).default('none').optional().describe('Compression type'), // 压缩类型 + isRemote: z.boolean().default(false).optional().describe('Whether it is a remote bundle'), // 是否是远程包 output: z.boolean().default(true).optional().describe('Whether to output this bundle'), // 是否输出此 bundle 包 - name: z.string().describe('Bundle Name'), // bundle 名称 + name: z.string().describe('Bundle name'), // bundle 名称 dest: z.string().optional().describe('Output directory of the bundle'), // bundle 的输出目录 - scriptDest: z.string().optional().describe('Output path for scripts'), // 脚本的输出地址 -}).describe('Bundle Configuration Options'); // Bundle 配置选项 + scriptDest: z.string().optional().describe('Output address of the script'), // 脚本的输出地址 +}).describe('Bundle configuration options'); // Bundle 配置选项 // Platform Enum - Accepts any string, built-in platform names are for reference only // 平台枚举 - 接受任意字符串,内置平台名称仅作为参考 -export const SchemaPlatform = z.string().describe('Platform Identifier (e.g., web-desktop, web-mobile, windows, mac, ios, etc.)'); // 平台标识符 (如: web-desktop, web-mobile, windows, mac, ios 等) -export const SchemaPlatformCanMake = z.string().describe('Platform Identifier supported for compilation (e.g., windows, mac, ios, android, etc.)'); // 支持编译的平台标识符 (如: windows, mac, ios, android 等) +export const SchemaPlatform = z.string().describe('Platform identifier (e.g., web-desktop, web-mobile, windows, mac, ios, etc.)'); // 平台标识符 (如: web-desktop, web-mobile, windows, mac, ios 等) +export const SchemaPlatformCanMake = z.string().describe('Platform identifier supported for compilation (e.g., windows, mac, ios, android, etc.)'); // 支持编译的平台标识符 (如: windows, mac, ios, android 等) -export const SchemaRoot = z.string().min(1).describe('Build Output Directory'); // 构建发布目录 +export const SchemaRoot = z.string().min(1).describe('Build release directory'); // 构建发布目录 export type IPlatformRoot = z.infer; export type TPlatform = z.infer; export type TPlatformCanMake = z.infer; @@ -42,25 +42,51 @@ export type TPlatformCanMake = z.infer; export const SchemaWebDesktopPackages = z.object({ useWebGPU: z.boolean().default(false).describe('Whether to use WebGPU rendering backend'), // 是否使用 WEBGPU 渲染后端 resolution: z.object({ - designHeight: z.number().describe('Design Height'), // 设计高度 - designWidth: z.number().describe('Design Width'), // 设计宽度 - }).describe('Game View Resolution'), // 游戏视图分辨率 -}).describe('Web Desktop Platform Configuration'); // Web Desktop 平台配置 + designHeight: z.number().describe('Design height'), // 设计高度 + designWidth: z.number().describe('Design width'), // 设计宽度 + }).describe('Game view resolution'), // 游戏视图分辨率 +}).describe('Web Desktop platform configuration'); // Web Desktop 平台配置 // Web Mobile Platform Configuration // Web Mobile 平台配置 export const SchemaWebMobilePackages = z.object({ useWebGPU: z.boolean().default(false).describe('Whether to use WebGPU rendering backend'), // 是否使用 WEBGPU 渲染后端 - orientation: z.enum(['portrait', 'landscape', 'auto']).default('auto').describe('Device Orientation'), // 设备方向 + orientation: z.enum(['portrait', 'landscape', 'auto']).default('auto').describe('Device orientation'), // 设备方向 embedWebDebugger: z.boolean().default(false).describe('Whether to embed Web debugger'), // 是否嵌入 Web 端调试工具 -}).describe('Web Mobile Platform Configuration'); // Web Mobile 平台配置 +}).describe('Web Mobile platform configuration'); // Web Mobile 平台配置 + +// iOS Packages Configuration // iOS Packages 配置 +export const SchemaIOSPackage = z.object({ + packageName: z.string() + .min(1, 'iOS package name cannot be empty') // iOS包名不能为空 + .describe('iOS application package name (required)'), // iOS应用包名(必填) + provisioningProfile: z.string().optional().describe('Provisioning profile'), // 描述文件 + certificate: z.string().optional().describe('Certificate name'), // 证书名称 + teamId: z.string().optional().describe('Developer Team ID'), // 开发者团队ID +}).describe('iOS platform specific configuration'); // iOS平台特定配置 + +// Mac Packages Configuration // Mac Packages 配置 +export const SchemaMacPackage = z.object({ + packageName: z.string() + .min(1, 'Mac package name cannot be empty') // Mac包名不能为空 + .describe('Mac application package name (required)') // Mac应用包名(必填) +}).describe('Mac platform specific configuration'); // Mac平台特定配置 + +// Android Packages Configuration // Android Packages 配置 +export const SchemaAndroidPackage = z.object({ + packageName: z.string() + .min(1, 'Android package name cannot be empty') // Android包名不能为空 + .describe('Android application package name (required)'), // Android应用包名(必填) + keystorePath: z.string().optional().describe('Keystore file path'), // 签名文件路径 + keystorePassword: z.string().optional().describe('Keystore password'), // 签名文件密码 +}).describe('Android platform specific configuration'); // Android平台特定配置 // ==================== Basic Build Configuration ==================== // 基础构建配置 -// Core Build Field Definitions (excluding platform and packages, defined in platform-specific configurations) // 核心构建字段定义(不包含 platform 和 packages,这些在平台特定配置中定义) +// Core Build Fields Definition (excluding platform and packages, defined in platform specific configuration) // 核心构建字段定义(不包含 platform 和 packages,这些在平台特定配置中定义) const BuildConfigCoreFields = z.object({ // Basic Information // 基础信息 - name: z.string().describe('Game Name, defaults to project name'), // 游戏名称,默认为项目名称 - outputName: z.string().describe('Build Output Name, defaults to platform name'), // 构建输出名称,默认为平台名称 + name: z.string().describe('Game name, defaults to project name'), // 游戏名称,默认为项目名称 + outputName: z.string().describe('Build output name, defaults to platform name'), // 构建输出名称,默认为平台名称 buildPath: z.string().describe('Generated game folder after build, use project:// protocol for project path'), // 构建后的游戏生成文件夹,项目下的地址请使用 project:// 协议 // Scene Configuration // 场景配置 @@ -73,19 +99,19 @@ const BuildConfigCoreFields = z.object({ // Polyfills and Script Configuration // Polyfills 和脚本配置 polyfills: SchemaPolyfills.describe('Implement JavaScript standard library not supported by the runtime environment'), // 实现运行环境并不支持的 JavaScript 标准库 - buildScriptTargets: z.string().describe('Target environment information required by the project, can pass a query string compatible with browserslist, e.g., > 0.4%'), // 项目需要支持的目标环境信息,可以传递一个和 browserslist 兼容的查询字符串,例如:> 0.4% + buildScriptTargets: z.string().describe('Target environment information supported by the project, can pass a query string compatible with browserslist, e.g., > 0.4%'), // 项目需要支持的目标环境信息,可以传递一个和 browserslist 兼容的查询字符串,例如:> 0.4% // Bundle Configuration // Bundle 配置 mainBundleCompressionType: z.enum(['none', 'merge_dep', 'merge_all_json', 'subpackage', 'zip']).describe('Specify the compression type of the main bundle'), // 指定主 bundle 的压缩类型 mainBundleIsRemote: z.boolean().describe('Whether the main Bundle is a remote package'), // main Bundle 是否是远程包 server: z.string().describe('Remote resource server address'), // 远程资源服务器地址 startSceneAssetBundle: z.boolean().describe('Specify the initial scene as a remote Bundle package'), // 指定初始场景为远程 Bundle 包 - bundleConfigs: z.array(SchemaBundleConfig).describe('Specify parameters for building Bundles, if not passed, pack according to the original configuration of all Bundles in the project'), // 构建 Bundle 的指定包含传参,未传递时按照项目内所有 Bundle 的原始配置打包 - moveRemoteBundleScript: z.boolean().describe('Remove scripts from remote Bundle packages, automatically checked for mini-game platforms'), // 移除远程包 Bundle 的脚本,小游戏平台将会自动勾选 + bundleConfigs: z.array(SchemaBundleConfig).describe('Specify parameters for building Bundle, if not passed, package according to the original configuration of all Bundles in the project'), // 构建 Bundle 的指定包含传参,未传递时按照项目内所有 Bundle 的原始配置打包 + moveRemoteBundleScript: z.boolean().describe('Remove scripts from remote Bundle packages, mini-game platforms will automatically check this'), // 移除远程包 Bundle 的脚本,小游戏平台将会自动勾选 // Code Processing // 代码处理 - nativeCodeBundleMode: z.enum(['wasm', 'asmjs', 'both']).describe('Specify the mode of Native Code to build'), // 指定构建的 Native Code 的模式 - sourceMaps: z.union([z.boolean(), z.literal('inline')]).describe('Whether to generate sourceMap. false: Disabled; true: Enabled (separate file); inline: Enabled (inline)'), // 是否生成 sourceMap。false: 关闭;true: 启用(独立文件);inline: 启用(内联) + nativeCodeBundleMode: z.enum(['wasm', 'asmjs', 'both']).describe('Specify the mode of the built Native Code'), // 指定构建的 Native Code 的模式 + sourceMaps: z.union([z.boolean(), z.literal('inline')]).describe('Whether to generate sourceMap. false: disabled; true: enabled (separate file); inline: enabled (inline)'), // 是否生成 sourceMap。false: 关闭;true: 启用(独立文件);inline: 启用(内联) experimentalEraseModules: z.boolean().describe('Whether to use experimental eraseModules'), // 是否使用实验性 eraseModules bundleCommonChunk: z.boolean().describe('Whether to embed common scripts in Bundle'), // 是否在 Bundle 中嵌入公共脚本 mangleProperties: z.boolean().describe('Whether to mangle properties'), // 是否混淆属性 @@ -107,16 +133,16 @@ const BuildConfigCoreFields = z.object({ textureCompress: z.boolean().optional().describe('Whether to use texture compression cache'), // 是否使用纹理压缩缓存 autoAtlas: z.boolean().optional().describe('Whether to use auto atlas cache'), // 是否使用自动合图缓存 serializeData: z.boolean().optional().describe('Whether to use serialized data cache'), // 是否使用序列化数据缓存 - }).optional().describe('Cache Configuration'), // 缓存配置 + }).optional().describe('Cache configuration'), // 缓存配置 }); -// Build Configuration Base Class: All fields optional (for API input, excluding platform and packages) // 构建配置基类:所有字段可选(用于 API 入参,不包含 platform 和 packages) -export const SchemaBuildBaseConfig = BuildConfigCoreFields.partial().describe('Basic Build Configuration (all fields optional)'); // 基础构建配置(所有字段可选) +// Base Build Configuration: All fields optional (for API input, excluding platform and packages) // 构建配置基类:所有字段可选(用于 API 入参,不包含 platform 和 packages) +export const SchemaBuildBaseConfig = BuildConfigCoreFields.partial().describe('Base build configuration (all fields optional)'); // 基础构建配置(所有字段可选) // Runtime/One-time Options (not included in configuration result) // 运行时/一次性选项(不进入配置结果) export const SchemaBuildRuntimeOptions = z.object({ configPath: z.string().optional().describe('Build configuration JSON file path'), // 构建配置 JSON 文件地址 - skipCheck: z.boolean().default(false).optional().describe('Skip build parameter check and auto-completion process. Only set to true when confirming other build parameters are complete, otherwise build may fail due to missing configuration'), // 跳过构建参数的检查和自动补全流程,请在确认其他构建参数都是完整的情况才能设置为 true ,否则可能因为缺少配置导致构建失败 + skipCheck: z.boolean().default(false).optional().describe('Skip the check and auto-completion process of build parameters, please set to true only when confirming that other build parameters are complete, otherwise the build may fail due to missing configuration'), // 跳过构建参数的检查和自动补全流程,请在确认其他构建参数都是完整的情况才能设置为 true ,否则可能因为缺少配置导致构建失败 taskId: z.string().optional().describe('Specify build task ID'), // 指定构建任务 ID taskName: z.string().optional().describe('Specify build task name'), // 指定构建任务名称 // logDest: z.string().optional().describe('Specify build log output path'), // 指定构建日志输出地址 @@ -124,41 +150,104 @@ export const SchemaBuildRuntimeOptions = z.object({ // ==================== Platform Specific Complete Build Options ==================== // 平台特定的完整构建选项 -// Web Desktop Complete Build Options (Input, all fields optional) // Web Desktop 完整构建选项(入参,所有字段可选) -export const SchemaWebDesktopBuildOption = SchemaBuildRuntimeOptions - .merge(SchemaBuildBaseConfig) +// Base Build Options (including runtime options) // 基础构建选项(包含运行时选项) +export const SchemaBuildBaseOption = SchemaBuildRuntimeOptions + .merge(SchemaBuildBaseConfig); + +// Web Desktop Complete Build Options // Web Desktop 完整构建选项 +export const SchemaWebDesktopBuildOption = SchemaBuildBaseOption .extend({ - platform: z.literal('web-desktop').describe('Build Platform').optional(), // 构建平台 + platform: z.literal('web-desktop').describe('Build platform'), // 构建平台 packages: z.object({ 'web-desktop': SchemaWebDesktopPackages.partial() - }).optional().describe('Web Desktop Platform Specific Configuration') // Web Desktop 平台特定配置 + }).optional().describe('Web Desktop platform specific configuration') // Web Desktop 平台特定配置 }) - .describe('Web Desktop Complete Build Options (all fields optional)'); // Web Desktop 完整构建选项(所有字段可选) + .describe('Web Desktop complete build options (all fields optional)'); // Web Desktop 完整构建选项(所有字段可选) -// Web Mobile Complete Build Options (Input, all fields optional) // Web Mobile 完整构建选项(入参,所有字段可选) -export const SchemaWebMobileBuildOption = SchemaBuildRuntimeOptions - .merge(SchemaBuildBaseConfig) +// Web Mobile Complete Build Options // Web Mobile 完整构建选项 +export const SchemaWebMobileBuildOption = SchemaBuildBaseOption .extend({ - platform: z.literal('web-mobile').describe('Build Platform').optional(), // 构建平台 + platform: z.literal('web-mobile').describe('Build platform'), // 构建平台 packages: z.object({ 'web-mobile': SchemaWebMobilePackages.partial() - }).optional().describe('Web Mobile Platform Specific Configuration') // Web Mobile 平台特定配置 + }).optional().describe('Web Mobile platform specific configuration') // Web Mobile 平台特定配置 }) - .describe('Web Mobile Complete Build Options (all fields optional)'); // Web Mobile 完整构建选项(所有字段可选) + .describe('Web Mobile complete build options (all fields optional)'); // Web Mobile 完整构建选项(所有字段可选) -// General Build Options (for API input) // 通用构建选项(用于 API 入参) -export const SchemaBuildOption = z.union([ - SchemaWebDesktopBuildOption, - SchemaWebMobileBuildOption, - SchemaBuildRuntimeOptions - .merge(SchemaBuildBaseConfig) +// Windows Build Options // Windows 构建选项 +export const SchemaWindowsBuildOption = SchemaBuildBaseOption + .extend({ + platform: z.literal('windows').describe('Build platform') // 构建平台 + }) + .describe('Windows platform build options'); // Windows平台构建选项 + +// iOS Build Options // iOS 构建选项 +export const SchemaIOSBuildOption = SchemaBuildBaseOption + .extend({ + platform: z.literal('ios').describe('Build platform'), // 构建平台 + packages: z.object({ + ios: z.intersection( + z.record(SchemaIOSPackage), // 动态的包配置 + z.any().optional() + ) + }).describe('iOS platform configuration') // iOS平台配置 + }) + .describe('iOS platform build options'); // iOS平台构建选项 + +// Android Build Options // Android 构建选项 +export const SchemaAndroidBuildOption = SchemaBuildBaseOption .extend({ - platform: SchemaPlatform.optional().describe('Build Platform'), // 构建平台 - packages: z.any().optional().describe('Platform Specific Configuration'), // 平台特定配置 + platform: z.literal('android').describe('Build platform'), // 构建平台 + packages: z.object({ + android: z.intersection( + z.record(SchemaAndroidPackage), // 动态的包配置 + z.any().optional() + ) + }).describe('Android platform configuration') // Android平台配置 }) -]).optional().describe('Build Options (for API input)'); // 构建选项(用于 API 入参) + .describe('Android platform build options'); // Android平台构建选项 + + + // Mac Build Options // Mac 构建选项 +export const SchemaMacBuildOption = SchemaBuildBaseOption +.extend({ + platform: z.literal('mac').describe('Build platform'), // 构建平台 + packages: z.object({ + mac: z.intersection( + z.record(SchemaMacPackage), // 动态的包配置 + z.any().optional() + ) + }).describe('Mac platform configuration') // Mac平台配置 +}) +.describe('Mac platform build options'); // Mac平台构建选项 + + +// Other Platform Build Options (Generic) // 其他平台构建选项(通用) +export const SchemaOtherPlatformBuildOption = SchemaBuildBaseOption + .extend({ + platform: SchemaPlatform.optional().describe('Build platform'), // 构建平台 + packages: z.any().optional().describe('Platform specific configuration'), // 平台特定配置 + }) + .describe('Other platform build options'); // 其他平台构建选项 + +export const SchemaKnownBuildOptions = [ + SchemaWebDesktopBuildOption, + SchemaWebMobileBuildOption, + SchemaWindowsBuildOption, + SchemaIOSBuildOption, + SchemaMacBuildOption, + SchemaAndroidBuildOption, +]; +// ==================== Create discriminatedUnion ==================== // +export const SchemaBuildOption = z.discriminatedUnion('platform', [ + ...SchemaKnownBuildOptions, + SchemaOtherPlatformBuildOption +] as any).default({}).describe('Build options (with platform preprocessing)'); // 构建选项(带平台预处理) + export type TBuildOption = z.infer; +// ==================== Result Type Definitions ==================== // 结果类型定义 + export const SchemaResultBase = z.object({ code: z.number().int().describe('Build exit code, 0 means success, others mean failure, 32 means parameter error, 34 means build failure, 37 means build busy, 50 means unknown error'), // 构建的退出码, 0 表示成功, 其他表示失败, 32 表示参数错误, 34 表示构建失败, 37 表示构建繁忙, 50 表示未知错误 dest: z.string().optional().describe('Generated game folder after build, currently output as project protocol address'), // 构建后的游戏生成文件夹,目前输出为 project 协议地址 @@ -167,15 +256,15 @@ export const SchemaResultBase = z.object({ export const SchemaBuildResult = SchemaResultBase.extend({ custom: z.object({ - nativePrjDir: z.string().optional().describe('Native project path after build'), // 构建后的原生项目地址 + nativePrjDir: z.string().optional().describe('Native project address after build'), // 构建后的原生项目地址 previewUrl: z.string().optional().describe('Default preview server address for web platform build'), // web 平台构建的默认预览服务器地址 }).optional().describe('Custom fields for different build platform results, in object format'), // 不同构建平台结果的自定义字段, object 形式 }).nullable().describe('Result after building the project'); // 构建项目后的结果 export const SchemaMakeResult = SchemaResultBase.extend({ custom: z.object({ - nativePrjDir: z.string().optional().describe('Native project path after build'), // 构建后的原生项目地址 - executableFile: z.string().optional().describe('Compiled executable file path'), // 编译后的可执行文件地址 + nativePrjDir: z.string().optional().describe('Native project address after build'), // 构建后的原生项目地址 + executableFile: z.string().optional().describe('Compiled executable file address'), // 编译后的可执行文件地址 }).optional().describe('Custom fields after compiling the project, in object format'), // 编译项目后的自定义字段, object 形式 }).nullable().describe('Result after compiling the project'); // 编译项目后的结果 @@ -184,16 +273,16 @@ export const SchemaPreviewSettingsResult = z.object({ CocosEngine: z.string().describe('Cocos Engine Version'), // Cocos Engine 版本 engine: z.object({ debug: z.boolean().describe('Whether it is debug mode'), // 是否是调试模式 - platform: z.string().describe('Build Platform'), // 构建平台 - customLayers: z.array(z.object({ name: z.string(), bit: z.number() })).describe('Custom Layers'), // 自定义层级 - sortingLayers: z.array(z.object({ id: z.number(), name: z.string(), value: z.number() })).describe('Sorting Layers'), // 排序层级 - macros: z.record(z.string(), z.any()).describe('Macro Definitions'), // 宏定义 - builtinAssets: z.array(z.string()).describe('Built-in Assets'), // 内置资源 + platform: z.string().describe('Build platform'), // 构建平台 + customLayers: z.array(z.object({ name: z.string(), bit: z.number() })).describe('Custom layers'), // 自定义层级 + sortingLayers: z.array(z.object({ id: z.number(), name: z.string(), value: z.number() })).describe('Sorting layers'), // 排序层级 + macros: z.record(z.string(), z.any()).describe('Macro definitions'), // 宏定义 + builtinAssets: z.array(z.string()).describe('Built-in assets'), // 内置资源 }), }), - script2library: z.record(z.string(), z.string()).describe('Mapping between scripts and libraries'), // 脚本与库的映射关系 + script2library: z.record(z.string(), z.string()).describe('Mapping relationship between scripts and libraries'), // 脚本与库的映射关系 bundleConfigs: z.array(z.object({ - name: z.string().describe('Bundle Name'), // bundle 名称 + name: z.string().describe('Bundle name'), // bundle 名称 uuids: z.array(z.string()).describe('List of resource UUIDs in the bundle'), // bundle 中的资源 UUID 列表 paths: z.record(z.string(), z.array(z.string())).describe('List of resource paths in the bundle'), // bundle 中的资源路径列表 scenes: z.record(z.string(), z.union([z.string(), z.number()])).describe('List of scenes in the bundle'), // bundle 中的场景列表 @@ -208,43 +297,23 @@ export const SchemaPreviewSettingsResult = z.object({ extensionMap: z.record(z.string(), z.array(z.union([z.string(), z.number()]))).describe('List of extended resources in the bundle'), // bundle 中的扩展资源列表 dependencyRelationships: z.record(z.string(), z.array(z.union([z.string(), z.number()]))).describe('List of dependency relationships in the bundle'), // bundle 中的依赖关系列表 hasPreloadScript: z.boolean().describe('Whether the bundle has scripts that need to be preloaded'), // bundle 是否有需要预加载的脚本 - })).describe('Bundle Configuration'), // bundle 配置 -}).describe('Get Preview Information Result').nullable(); // 获取预览信息结果 + })).describe('Bundle configuration'), // bundle 配置 +}).describe('Get preview information result').nullable(); // 获取预览信息结果 export type TPreviewSettingsResult = z.infer; // ==================== Build Configuration Query Result ==================== // 构建配置查询结果 -// Web Desktop Build Configuration Query Result (All fields required, including packages, excluding runtime options) // Web Desktop 构建配置查询结果(所有字段必填,包含 packages,不包含运行时选项) -const SchemaWebDesktopBuildConfigResult = BuildConfigCoreFields.partial() - .extend({ - platform: z.literal('web-desktop').describe('Build Platform'), // 构建平台 - packages: z.object({ - 'web-desktop': SchemaWebDesktopPackages - }).describe('Web Desktop Platform Specific Configuration') // Web Desktop 平台特定配置 - }) - .describe('Web Desktop Build Configuration Query Result'); // Web Desktop 构建配置查询结果 - -// Web Mobile Build Configuration Query Result (All fields required, including packages, excluding runtime options) // Web Mobile 构建配置查询结果(所有字段必填,包含 packages,不包含运行时选项) -const SchemaWebMobileBuildConfigResult = BuildConfigCoreFields.partial() - .extend({ - platform: z.literal('web-mobile').describe('Build Platform'), // 构建平台 - packages: z.object({ - 'web-mobile': SchemaWebMobilePackages - }).describe('Web Mobile Platform Specific Configuration') // Web Mobile 平台特定配置 - }) - .describe('Web Mobile Build Configuration Query Result'); // Web Mobile 构建配置查询结果 - -// Build Configuration Query Result: Union type, all fields required, including packages, excluding runtime options // 构建配置查询结果:union 类型,所有字段必填,包含 packages,不包含运行时选项 +// Build configuration query result: union type, all fields required, including packages, excluding runtime options export const SchemaBuildConfigResult = z.union([ - SchemaWebDesktopBuildConfigResult, - SchemaWebMobileBuildConfigResult, - BuildConfigCoreFields.partial() - .extend({ - platform: SchemaPlatform, - packages: z.any().optional().describe('Platform Specific Configuration'), // 平台特定配置 - }) -]).nullable().describe('Build Configuration Query Result (all fields required, including packages)'); // 构建配置查询结果(所有字段必填,包含 packages) + SchemaWebDesktopBuildOption.omit({ configPath: true, skipCheck: true, taskId: true, taskName: true }), + SchemaWebMobileBuildOption.omit({ configPath: true, skipCheck: true, taskId: true, taskName: true }), + SchemaWindowsBuildOption.omit({ configPath: true, skipCheck: true, taskId: true, taskName: true }), + SchemaIOSBuildOption.omit({ configPath: true, skipCheck: true, taskId: true, taskName: true }), + SchemaAndroidBuildOption.omit({ configPath: true, skipCheck: true, taskId: true, taskName: true }), + SchemaMacBuildOption.omit({ configPath: true, skipCheck: true, taskId: true, taskName: true }), + SchemaOtherPlatformBuildOption.omit({ configPath: true, skipCheck: true, taskId: true, taskName: true }), +]).nullable().describe('Build configuration query result (all fields required, including packages)'); // 构建配置查询结果(所有字段必填,包含 packages) export type TBuildConfigResult = z.infer; @@ -261,7 +330,7 @@ export type TWebDesktopPackages = z.infer; export type TWebMobilePackages = z.infer; // Run API Related Schema // Run API 相关 Schema -export const SchemaBuildDest = z.string().min(1).describe('Build Output Directory, supports absolute path and project:// protocol URL'); // 构建输出目录,支持绝对路径和 project:// 协议 URL +export const SchemaBuildDest = z.string().min(1).describe('Build output directory, supports absolute path and project:// protocol URL'); // 构建输出目录,支持绝对路径和 project:// 协议 URL export type TBuildDest = z.infer; export const SchemaRunResult = z.string().describe('Run URL'); // 运行 URL diff --git a/src/mcp/hooks/builder.hook.ts b/src/mcp/hooks/builder.hook.ts new file mode 100644 index 00000000..dfc2753f --- /dev/null +++ b/src/mcp/hooks/builder.hook.ts @@ -0,0 +1,107 @@ +import { z } from 'zod'; +import { join, resolve } from 'path'; +import { existsSync, readdirSync, readFileSync } from 'fs'; +import { SchemaBuildBaseOption, SchemaKnownBuildOptions, SchemaOtherPlatformBuildOption } from '../../api/builder/schema'; + +export class BuilderHook { + private dynamicPlatforms: string[] = []; + + constructor() { + this.scanPlatformPackages(); + } + + /** + * 扫描 packages/platforms 目录下的平台插件 + */ + private scanPlatformPackages() { + const platforms: string[] = []; + const platformsDir = resolve(__dirname, '../../../packages/platforms'); + + if (!existsSync(platformsDir)) { + this.dynamicPlatforms = platforms; + return; + } + + try { + const dirs = readdirSync(platformsDir); + for (const dir of dirs) { + const pkgJsonPath = join(platformsDir, dir, 'package.json'); + if (existsSync(pkgJsonPath)) { + try { + const pkgContent = JSON.parse(readFileSync(pkgJsonPath, 'utf-8')); + // 检查是否是平台插件 (contributes.builder.register === true) + if (pkgContent?.contributes?.builder?.register === true) { + // 优先使用 contributes.builder.platform,如果没有则使用 package.name + const platformName = pkgContent.contributes.builder.platform || pkgContent.name; + if (platformName) { + platforms.push(platformName); + } + } + } catch (e) { + console.warn(`Failed to parse package.json for ${dir}:`, e); + } + } + } + } catch (e) { + console.error('Failed to scan platform packages:', e); + } + + this.dynamicPlatforms = platforms; + } + + public onRegisterParam(toolName: string, param: any, inputSchemaFields: Record) { + if (toolName !== 'builder-build') return; + + const knownPlatforms = ['web-desktop', 'web-mobile', 'android', 'ios', 'windows', 'mac']; + // 合并去重 + const allPlatforms = Array.from(new Set([...knownPlatforms, ...this.dynamicPlatforms])); + const platformDesc = `Platform Identifier (e.g., ${allPlatforms.join(', ')})`; + + if (param.name === 'options') { + inputSchemaFields[param.name] = z.any(); + + // 动态构建 SchemaBuildOption + const dynamicSchemas = this.dynamicPlatforms.map(platform => { + return SchemaBuildBaseOption.extend({ + platform: z.literal(platform).describe('Build platform'), + packages: z.object({ + [platform]: z.any().optional().describe(`${platform} platform specific configuration`) + }).optional().describe(`${platform} platform specific configuration`) + }).describe(`${platform} complete build options`); + }); + + const newSchema = z.discriminatedUnion('platform', [ + ...SchemaKnownBuildOptions, + ...dynamicSchemas, + SchemaOtherPlatformBuildOption + ] as any).default({}).describe('Build options (with platform preprocessing)'); + + // 更新原始 meta 中的 schema,以便 list handler 使用 + param.schema = newSchema; + + } else if (param.name === 'platform') { + // 动态更新 platform 参数的描述,包含扫描到的平台 + const newPlatformSchema = param.schema.describe(platformDesc); + inputSchemaFields[param.name] = newPlatformSchema; + param.schema = newPlatformSchema; + } + } + + public onBeforeExecute(toolName: string, args: any) { + if (toolName !== 'builder-build') return; + + if (!args.options) { + args.options = {}; + } + if (typeof args.options === 'object' && !args.options.platform) { + // 注入 platform + args.options.platform = args.platform; + } + } + + public onValidationFailed(toolName: string, paramName: string, error: any) { + if (toolName === 'builder-build') { + throw new Error(`Parameter validation failed for ${paramName}: ${error instanceof Error ? error.message : String(error)}`); + } + } +} diff --git a/src/mcp/mcp.middleware.ts b/src/mcp/mcp.middleware.ts index 86cbfb62..83a0868f 100644 --- a/src/mcp/mcp.middleware.ts +++ b/src/mcp/mcp.middleware.ts @@ -8,6 +8,7 @@ import * as pkgJson from '../../package.json'; import { join } from 'path'; import { ResourceManager } from './resources'; import { HTTP_STATUS } from '../api/base/schema-base'; +import { BuilderHook } from './hooks/builder.hook'; import type { HttpStatusCode } from '../api/base/schema-base'; import stripAnsi from 'strip-ansi'; import { zodToJsonSchema } from 'zod-to-json-schema'; @@ -15,8 +16,10 @@ import { ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js'; export class McpMiddleware { private server: McpServer; private resourceManager: ResourceManager; + private builderHook: BuilderHook; constructor() { + this.builderHook = new BuilderHook(); // 创建 MCP server this.server = new McpServer({ name: 'cocos-cli-mcp-server', @@ -82,12 +85,8 @@ export class McpMiddleware { .sort((a, b) => a.index - b.index) .forEach(param => { if (param.name) { - // 特殊处理 builder-build 的 options 参数 - // 使用 z.any() 绕过 SDK 的初步验证,以便我们在 callback 中注入 platform - // 实际的严格验证会在 prepareMethodArguments 中使用 meta.paramSchemas 进行 - if (toolName === 'builder-build' && param.name === 'options') { - inputSchemaFields[param.name] = z.any(); - } else { + this.builderHook.onRegisterParam(toolName, param, inputSchemaFields); + if (!inputSchemaFields[param.name]) { inputSchemaFields[param.name] = param.schema; } } @@ -101,15 +100,7 @@ export class McpMiddleware { inputSchemaFields, async (args) => { // args 已经是验证过的参数对象 (对于 builder-build.options 是 any) - if (toolName === 'builder-build') { - if (!args.options) { - args.options = {}; - } - if (typeof args.options === 'object' && !args.options.platform) { - // 注入 platform - args.options.platform = args.platform; - } - } + this.builderHook.onBeforeExecute(toolName, args); try { // 这里的 prepareMethodArguments 主要是为了按顺序排列参数给 apply 使用 // 注意:args 是对象,prepareMethodArguments 需要处理对象 @@ -235,10 +226,7 @@ export class McpMiddleware { console.error(`Parameter validation failed for ${paramName}:`, error); - // 如果是 builder-build 接口,参数校验失败直接报错 - if (toolName === 'builder-build') { - throw new Error(`Parameter validation failed for ${paramName}: ${error instanceof Error ? error.message : String(error)}`); - } + this.builderHook.onValidationFailed(toolName, paramName, error); // 使用原始值 methodArgs[param.index] = value; From 2fd040c5edb8c25f5fc1d384a3362999101d832d Mon Sep 17 00:00:00 2001 From: fengyuzhe Date: Wed, 31 Dec 2025 11:54:45 +0800 Subject: [PATCH 2/8] refine --- src/api/builder/schema.ts | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/src/api/builder/schema.ts b/src/api/builder/schema.ts index 3364d14c..6aa485d8 100644 --- a/src/api/builder/schema.ts +++ b/src/api/builder/schema.ts @@ -186,10 +186,9 @@ export const SchemaIOSBuildOption = SchemaBuildBaseOption .extend({ platform: z.literal('ios').describe('Build platform'), // 构建平台 packages: z.object({ - ios: z.intersection( - z.record(SchemaIOSPackage), // 动态的包配置 - z.any().optional() - ) + ios: SchemaIOSPackage + .catchall(z.any()) // 允许其他任意字段 + .optional() }).describe('iOS platform configuration') // iOS平台配置 }) .describe('iOS platform build options'); // iOS平台构建选项 @@ -199,27 +198,25 @@ export const SchemaAndroidBuildOption = SchemaBuildBaseOption .extend({ platform: z.literal('android').describe('Build platform'), // 构建平台 packages: z.object({ - android: z.intersection( - z.record(SchemaAndroidPackage), // 动态的包配置 - z.any().optional() - ) + android: SchemaAndroidPackage + .catchall(z.any()) // 允许其他任意字段 + .optional() }).describe('Android platform configuration') // Android平台配置 }) .describe('Android platform build options'); // Android平台构建选项 - - // Mac Build Options // Mac 构建选项 + +// Mac Build Options // Mac 构建选项 export const SchemaMacBuildOption = SchemaBuildBaseOption -.extend({ - platform: z.literal('mac').describe('Build platform'), // 构建平台 - packages: z.object({ - mac: z.intersection( - z.record(SchemaMacPackage), // 动态的包配置 - z.any().optional() - ) - }).describe('Mac platform configuration') // Mac平台配置 -}) -.describe('Mac platform build options'); // Mac平台构建选项 + .extend({ + platform: z.literal('mac').describe('Build platform'), // 构建平台 + packages: z.object({ + mac: SchemaMacPackage + .catchall(z.any()) // 允许其他任意字段 + .optional() + }).describe('Mac platform configuration') // Mac平台配置 + }) + .describe('Mac platform build options'); // Mac平台构建选项 // Other Platform Build Options (Generic) // 其他平台构建选项(通用) From 8a68afe6531503fffdca6489ab7f217f857d4b18 Mon Sep 17 00:00:00 2001 From: fengyuzhe Date: Wed, 31 Dec 2025 15:32:17 +0800 Subject: [PATCH 3/8] refine --- src/api/builder/schema.ts | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/src/api/builder/schema.ts b/src/api/builder/schema.ts index 6aa485d8..0293ddc9 100644 --- a/src/api/builder/schema.ts +++ b/src/api/builder/schema.ts @@ -54,14 +54,18 @@ export const SchemaWebMobilePackages = z.object({ embedWebDebugger: z.boolean().default(false).describe('Whether to embed Web debugger'), // 是否嵌入 Web 端调试工具 }).describe('Web Mobile platform configuration'); // Web Mobile 平台配置 -// iOS Packages Configuration // iOS Packages 配置 -export const SchemaIOSPackage = z.object({ +// iOS Configuration // iOS 配置 +const SchemaIOSPackageBase = z.object({ packageName: z.string() .min(1, 'iOS package name cannot be empty') // iOS包名不能为空 .describe('iOS application package name (required)'), // iOS应用包名(必填) - provisioningProfile: z.string().optional().describe('Provisioning profile'), // 描述文件 - certificate: z.string().optional().describe('Certificate name'), // 证书名称 - teamId: z.string().optional().describe('Developer Team ID'), // 开发者团队ID + + osTarget: z.object({ + iphoneos: z.boolean().optional(), + simulator: z.boolean().optional(), + }).optional(), + targetVersion: z.string().optional(), + developerTeam: z.string().optional(), }).describe('iOS platform specific configuration'); // iOS平台特定配置 // Mac Packages Configuration // Mac Packages 配置 @@ -182,14 +186,27 @@ export const SchemaWindowsBuildOption = SchemaBuildBaseOption .describe('Windows platform build options'); // Windows平台构建选项 // iOS Build Options // iOS 构建选项 +const SchemaIOSPackageWithCatchall = SchemaIOSPackageBase.catchall(z.any()); +export const SchemaIOSPackage = SchemaIOSPackageWithCatchall + .refine((data) => { + // 当 osTarget.iphoneos 为 true 时,developerTeam 必须有值 + if (data.osTarget && data.osTarget.iphoneos === true) { + return data.developerTeam && data.developerTeam.trim().length > 0; + } + return true; + }, { + message: 'developerTeam is required when osTarget.iphoneos is true', + path: ['developerTeam'] + }) + .describe('iOS platform specific configuration'); + export const SchemaIOSBuildOption = SchemaBuildBaseOption .extend({ platform: z.literal('ios').describe('Build platform'), // 构建平台 packages: z.object({ ios: SchemaIOSPackage - .catchall(z.any()) // 允许其他任意字段 .optional() - }).describe('iOS platform configuration') // iOS平台配置 + }).describe('iOS platform configuration') }) .describe('iOS platform build options'); // iOS平台构建选项 From 42b5d42a53223640dd5adb430f0dd71f2fd3cd34 Mon Sep 17 00:00:00 2001 From: fengyuzhe Date: Tue, 6 Jan 2026 10:03:49 +0800 Subject: [PATCH 4/8] read configPath before build --- src/commands/build.ts | 15 +------------- src/mcp/hooks/builder.hook.ts | 39 ++++++++++++++++++++++++++++++++--- 2 files changed, 37 insertions(+), 17 deletions(-) diff --git a/src/commands/build.ts b/src/commands/build.ts index 8016aa57..68be7da5 100644 --- a/src/commands/build.ts +++ b/src/commands/build.ts @@ -20,20 +20,7 @@ export class BuildCommand extends BaseCommand { try { const resolvedPath = this.validateProjectPath(options.project); - if (options.buildConfig) { - if (!existsSync(options.buildConfig)) { - console.error(`config: ${options.buildConfig} is not exist!`); - process.exit(BuildExitCode.BUILD_FAILED); - } - console.debug(`Read config from path ${options.buildConfig}...`); - let data = readJSONSync(options.buildConfig); - // 功能点:options 传递的值,允许覆盖配置文件内的同属性值 - data = Object.assign(data, options); - // 避免修改原始 options - Object.assign(options, data); - // 移除旧的 key 方便和 configPath 未读取的情况做区分 - delete options.buildConfig; - } + //read options.configPath move to builder.hook.ts // 处理 Android 平台特定的命令行参数 if (options.platform === 'android') { diff --git a/src/mcp/hooks/builder.hook.ts b/src/mcp/hooks/builder.hook.ts index dfc2753f..8dadde58 100644 --- a/src/mcp/hooks/builder.hook.ts +++ b/src/mcp/hooks/builder.hook.ts @@ -93,9 +93,42 @@ export class BuilderHook { if (!args.options) { args.options = {}; } - if (typeof args.options === 'object' && !args.options.platform) { - // 注入 platform - args.options.platform = args.platform; + + // 处理 configPath + let options = args.options; + if (options.configPath) { + const configPath = options.configPath; + if (existsSync(configPath)) { + try { + const fileContent = JSON.parse(readFileSync(configPath, 'utf-8')); + // 合并配置,args.options 优先级高于配置文件 + options = args.options = { + ...fileContent, + ...options + }; + + // 删除 configPath 字段 + delete options.configPath; + } catch (e) { + console.warn(`Failed to load config file: ${configPath}`, e); + } + } + } + + if (typeof options === 'object') { + if (!options.platform) { + // 注入 platform + options.platform = args.platform; + } + + // sourceMaps exported by CocosEditor is a string, so need to convert it to boolean + if (options.sourceMaps && typeof options.sourceMaps !== 'boolean') { + if (options.sourceMaps === 'true') { + options.sourceMaps = true; + } else if (options.sourceMaps === 'false') { + options.sourceMaps = false; + } + } } } From f45b36b67d2fd2f782ec7e1fb4db569033956a7c Mon Sep 17 00:00:00 2001 From: fengyuzhe Date: Tue, 6 Jan 2026 10:17:13 +0800 Subject: [PATCH 5/8] refine --- src/api/builder/schema.ts | 84 ++++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 41 deletions(-) diff --git a/src/api/builder/schema.ts b/src/api/builder/schema.ts index d98d70b3..ddc2cd12 100644 --- a/src/api/builder/schema.ts +++ b/src/api/builder/schema.ts @@ -19,19 +19,19 @@ export const SchemaPolyfills = z.object({ export const SchemaBundleConfig = z.object({ root: z.string().describe('Root directory of the bundle'), // bundle 的根目录 priority: z.number().optional().describe('Priority'), // 优先级 - compressionType: z.enum(['none', 'merge_dep', 'merge_all_json', 'subpackage', 'zip']).default('none').optional().describe('Compression type'), // 压缩类型 - isRemote: z.boolean().default(false).optional().describe('Whether it is a remote bundle'), // 是否是远程包 + compressionType: z.enum(['none', 'merge_dep', 'merge_all_json', 'subpackage', 'zip']).default('none').optional().describe('Compression Type'), // 压缩类型 + isRemote: z.boolean().default(false).optional().describe('Is remote bundle'), // 是否是远程包 output: z.boolean().default(true).optional().describe('Whether to output this bundle'), // 是否输出此 bundle 包 - name: z.string().describe('Bundle name'), // bundle 名称 + name: z.string().describe('Bundle Name'), // bundle 名称 dest: z.string().optional().describe('Output directory of the bundle'), // bundle 的输出目录 - scriptDest: z.string().optional().describe('Output address of the script'), // 脚本的输出地址 -}).describe('Bundle configuration options'); // Bundle 配置选项 + scriptDest: z.string().optional().describe('Output path for scripts'), // 脚本的输出地址 +}).describe('Bundle Configuration Options'); // Bundle 配置选项 // Platform Enum - Accepts any string, built-in platform names are for reference only // 平台枚举 - 接受任意字符串,内置平台名称仅作为参考 -export const SchemaPlatform = z.string().describe('Platform identifier (e.g., web-desktop, web-mobile, windows, mac, ios, etc.)'); // 平台标识符 (如: web-desktop, web-mobile, windows, mac, ios 等) -export const SchemaPlatformCanMake = z.string().describe('Platform identifier supported for compilation (e.g., windows, mac, ios, android, etc.)'); // 支持编译的平台标识符 (如: windows, mac, ios, android 等) +export const SchemaPlatform = z.string().describe('Platform Identifier (e.g., web-desktop, web-mobile, windows, mac, ios, etc.)'); // 平台标识符 (如: web-desktop, web-mobile, windows, mac, ios 等) +export const SchemaPlatformCanMake = z.string().describe('Platform Identifier supported for compilation (e.g., windows, mac, ios, android, etc.)'); // 支持编译的平台标识符 (如: windows, mac, ios, android 等) -export const SchemaRoot = z.string().min(1).describe('Build release directory'); // 构建发布目录 +export const SchemaRoot = z.string().min(1).describe('Build Output Directory'); // 构建发布目录 export type IPlatformRoot = z.infer; export type TPlatform = z.infer; export type TPlatformCanMake = z.infer; @@ -42,17 +42,17 @@ export type TPlatformCanMake = z.infer; export const SchemaWebDesktopPackages = z.object({ useWebGPU: z.boolean().default(false).describe('Whether to use WebGPU rendering backend'), // 是否使用 WEBGPU 渲染后端 resolution: z.object({ - designHeight: z.number().describe('Design height'), // 设计高度 - designWidth: z.number().describe('Design width'), // 设计宽度 - }).describe('Game view resolution'), // 游戏视图分辨率 -}).describe('Web Desktop platform configuration'); // Web Desktop 平台配置 + designHeight: z.number().describe('Design Height'), // 设计高度 + designWidth: z.number().describe('Design Width'), // 设计宽度 + }).describe('Game View Resolution'), // 游戏视图分辨率 +}).describe('Web Desktop Platform Configuration'); // Web Desktop 平台配置 // Web Mobile Platform Configuration // Web Mobile 平台配置 export const SchemaWebMobilePackages = z.object({ useWebGPU: z.boolean().default(false).describe('Whether to use WebGPU rendering backend'), // 是否使用 WEBGPU 渲染后端 - orientation: z.enum(['portrait', 'landscape', 'auto']).default('auto').describe('Device orientation'), // 设备方向 + orientation: z.enum(['portrait', 'landscape', 'auto']).default('auto').describe('Device Orientation'), // 设备方向 embedWebDebugger: z.boolean().default(false).describe('Whether to embed Web debugger'), // 是否嵌入 Web 端调试工具 -}).describe('Web Mobile platform configuration'); // Web Mobile 平台配置 +}).describe('Web Mobile Platform Configuration'); // Web Mobile 平台配置 // iOS Configuration // iOS 配置 const SchemaIOSPackageBase = z.object({ @@ -86,11 +86,11 @@ export const SchemaAndroidPackage = z.object({ // ==================== Basic Build Configuration ==================== // 基础构建配置 -// Core Build Fields Definition (excluding platform and packages, defined in platform specific configuration) // 核心构建字段定义(不包含 platform 和 packages,这些在平台特定配置中定义) +// Core Build Field Definitions (excluding platform and packages, defined in platform-specific configurations) // 核心构建字段定义(不包含 platform 和 packages,这些在平台特定配置中定义) const BuildConfigCoreFields = z.object({ // Basic Information // 基础信息 - name: z.string().describe('Game name, defaults to project name'), // 游戏名称,默认为项目名称 - outputName: z.string().describe('Build output name, defaults to platform name'), // 构建输出名称,默认为平台名称 + name: z.string().describe('Game Name, defaults to project name'), // 游戏名称,默认为项目名称 + outputName: z.string().describe('Build Output Name, defaults to platform name'), // 构建输出名称,默认为平台名称 buildPath: z.string().describe('Generated game folder after build, use project:// protocol for project path'), // 构建后的游戏生成文件夹,项目下的地址请使用 project:// 协议 // Scene Configuration // 场景配置 @@ -103,19 +103,19 @@ const BuildConfigCoreFields = z.object({ // Polyfills and Script Configuration // Polyfills 和脚本配置 polyfills: SchemaPolyfills.describe('Implement JavaScript standard library not supported by the runtime environment'), // 实现运行环境并不支持的 JavaScript 标准库 - buildScriptTargets: z.string().describe('Target environment information supported by the project, can pass a query string compatible with browserslist, e.g., > 0.4%'), // 项目需要支持的目标环境信息,可以传递一个和 browserslist 兼容的查询字符串,例如:> 0.4% + buildScriptTargets: z.string().describe('Target environment information required by the project, can pass a query string compatible with browserslist, e.g., > 0.4%'), // 项目需要支持的目标环境信息,可以传递一个和 browserslist 兼容的查询字符串,例如:> 0.4% // Bundle Configuration // Bundle 配置 mainBundleCompressionType: z.enum(['none', 'merge_dep', 'merge_all_json', 'subpackage', 'zip']).describe('Specify the compression type of the main bundle'), // 指定主 bundle 的压缩类型 mainBundleIsRemote: z.boolean().describe('Whether the main Bundle is a remote package'), // main Bundle 是否是远程包 server: z.string().describe('Remote resource server address'), // 远程资源服务器地址 startSceneAssetBundle: z.boolean().describe('Specify the initial scene as a remote Bundle package'), // 指定初始场景为远程 Bundle 包 - bundleConfigs: z.array(SchemaBundleConfig).describe('Specify parameters for building Bundle, if not passed, package according to the original configuration of all Bundles in the project'), // 构建 Bundle 的指定包含传参,未传递时按照项目内所有 Bundle 的原始配置打包 - moveRemoteBundleScript: z.boolean().describe('Remove scripts from remote Bundle packages, mini-game platforms will automatically check this'), // 移除远程包 Bundle 的脚本,小游戏平台将会自动勾选 + bundleConfigs: z.array(SchemaBundleConfig).describe('Specify parameters for building Bundles, if not passed, pack according to the original configuration of all Bundles in the project'), // 构建 Bundle 的指定包含传参,未传递时按照项目内所有 Bundle 的原始配置打包 + moveRemoteBundleScript: z.boolean().describe('Remove scripts from remote Bundle packages, automatically checked for mini-game platforms'), // 移除远程包 Bundle 的脚本,小游戏平台将会自动勾选 // Code Processing // 代码处理 - nativeCodeBundleMode: z.enum(['wasm', 'asmjs', 'both']).describe('Specify the mode of the built Native Code'), // 指定构建的 Native Code 的模式 - sourceMaps: z.union([z.boolean(), z.literal('inline')]).describe('Whether to generate sourceMap. false: disabled; true: enabled (separate file); inline: enabled (inline)'), // 是否生成 sourceMap。false: 关闭;true: 启用(独立文件);inline: 启用(内联) + nativeCodeBundleMode: z.enum(['wasm', 'asmjs', 'both']).describe('Specify the mode of Native Code to build'), // 指定构建的 Native Code 的模式 + sourceMaps: z.union([z.boolean(), z.literal('inline')]).describe('Whether to generate sourceMap. false: Disabled; true: Enabled (separate file); inline: Enabled (inline)'), // 是否生成 sourceMap。false: 关闭;true: 启用(独立文件);inline: 启用(内联) experimentalEraseModules: z.boolean().describe('Whether to use experimental eraseModules'), // 是否使用实验性 eraseModules bundleCommonChunk: z.boolean().describe('Whether to embed common scripts in Bundle'), // 是否在 Bundle 中嵌入公共脚本 mangleProperties: z.boolean().describe('Whether to mangle properties'), // 是否混淆属性 @@ -137,16 +137,16 @@ const BuildConfigCoreFields = z.object({ textureCompress: z.boolean().optional().describe('Whether to use texture compression cache'), // 是否使用纹理压缩缓存 autoAtlas: z.boolean().optional().describe('Whether to use auto atlas cache'), // 是否使用自动合图缓存 serializeData: z.boolean().optional().describe('Whether to use serialized data cache'), // 是否使用序列化数据缓存 - }).optional().describe('Cache configuration'), // 缓存配置 + }).optional().describe('Cache Configuration'), // 缓存配置 }); -// Base Build Configuration: All fields optional (for API input, excluding platform and packages) // 构建配置基类:所有字段可选(用于 API 入参,不包含 platform 和 packages) -export const SchemaBuildBaseConfig = BuildConfigCoreFields.partial().describe('Base build configuration (all fields optional)'); // 基础构建配置(所有字段可选) +// Build Configuration Base Class: All fields optional (for API input, excluding platform and packages) // 构建配置基类:所有字段可选(用于 API 入参,不包含 platform 和 packages) +export const SchemaBuildBaseConfig = BuildConfigCoreFields.partial().describe('Basic Build Configuration (all fields optional)'); // 基础构建配置(所有字段可选) // Runtime/One-time Options (not included in configuration result) // 运行时/一次性选项(不进入配置结果) export const SchemaBuildRuntimeOptions = z.object({ configPath: z.string().optional().describe('Build configuration JSON file path'), // 构建配置 JSON 文件地址 - skipCheck: z.boolean().default(false).optional().describe('Skip the check and auto-completion process of build parameters, please set to true only when confirming that other build parameters are complete, otherwise the build may fail due to missing configuration'), // 跳过构建参数的检查和自动补全流程,请在确认其他构建参数都是完整的情况才能设置为 true ,否则可能因为缺少配置导致构建失败 + skipCheck: z.boolean().default(false).optional().describe('Skip build parameter check and auto-completion process. Only set to true when confirming other build parameters are complete, otherwise build may fail due to missing configuration'), // 跳过构建参数的检查和自动补全流程,请在确认其他构建参数都是完整的情况才能设置为 true ,否则可能因为缺少配置导致构建失败 taskId: z.string().optional().describe('Specify build task ID'), // 指定构建任务 ID taskName: z.string().optional().describe('Specify build task name'), // 指定构建任务名称 // logDest: z.string().optional().describe('Specify build log output path'), // 指定构建日志输出地址 @@ -158,25 +158,26 @@ export const SchemaBuildRuntimeOptions = z.object({ export const SchemaBuildBaseOption = SchemaBuildRuntimeOptions .merge(SchemaBuildBaseConfig); -// Web Desktop Complete Build Options // Web Desktop 完整构建选项 +// Web Desktop Complete Build Options (Input, all fields optional) // Web Desktop 完整构建选项(入参,所有字段可选) export const SchemaWebDesktopBuildOption = SchemaBuildBaseOption .extend({ platform: z.literal('web-desktop').describe('Build platform'), // 构建平台 packages: z.object({ 'web-desktop': SchemaWebDesktopPackages.partial() - }).optional().describe('Web Desktop platform specific configuration') // Web Desktop 平台特定配置 + }).optional().describe('Web Desktop Platform Specific Configuration') // Web Desktop 平台特定配置 }) - .describe('Web Desktop complete build options (all fields optional)'); // Web Desktop 完整构建选项(所有字段可选) + .describe('Web Desktop Complete Build Options (all fields optional)'); // Web Desktop 完整构建选项(所有字段可选) -// Web Mobile Complete Build Options // Web Mobile 完整构建选项 -export const SchemaWebMobileBuildOption = SchemaBuildBaseOption +// Web Mobile Complete Build Options (Input, all fields optional) // Web Mobile 完整构建选项(入参,所有字段可选) +export const SchemaWebMobileBuildOption = SchemaBuildRuntimeOptions + .merge(SchemaBuildBaseConfig) .extend({ platform: z.literal('web-mobile').describe('Build platform'), // 构建平台 packages: z.object({ 'web-mobile': SchemaWebMobilePackages.partial() - }).optional().describe('Web Mobile platform specific configuration') // Web Mobile 平台特定配置 + }).optional().describe('Web Mobile Platform Specific Configuration') // Web Mobile 平台特定配置 }) - .describe('Web Mobile complete build options (all fields optional)'); // Web Mobile 完整构建选项(所有字段可选) + .describe('Web Mobile Complete Build Options (all fields optional)'); // Web Mobile 完整构建选项(所有字段可选) // Windows Build Options // Windows 构建选项 export const SchemaWindowsBuildOption = SchemaBuildBaseOption @@ -270,15 +271,15 @@ export const SchemaResultBase = z.object({ export const SchemaBuildResult = SchemaResultBase.extend({ custom: z.object({ - nativePrjDir: z.string().optional().describe('Native project address after build'), // 构建后的原生项目地址 + nativePrjDir: z.string().optional().describe('Native project path after build'), // 构建后的原生项目地址 previewUrl: z.string().optional().describe('Default preview server address for web platform build'), // web 平台构建的默认预览服务器地址 }).optional().describe('Custom fields for different build platform results, in object format'), // 不同构建平台结果的自定义字段, object 形式 }).nullable().describe('Result after building the project'); // 构建项目后的结果 export const SchemaMakeResult = SchemaResultBase.extend({ custom: z.object({ - nativePrjDir: z.string().optional().describe('Native project address after build'), // 构建后的原生项目地址 - executableFile: z.string().optional().describe('Compiled executable file address'), // 编译后的可执行文件地址 + nativePrjDir: z.string().optional().describe('Native project path after build'), // 构建后的原生项目地址 + executableFile: z.string().optional().describe('Compiled executable file path'), // 编译后的可执行文件地址 }).optional().describe('Custom fields after compiling the project, in object format'), // 编译项目后的自定义字段, object 形式 }).nullable().describe('Result after compiling the project'); // 编译项目后的结果 @@ -294,9 +295,9 @@ export const SchemaPreviewSettingsResult = z.object({ builtinAssets: z.array(z.string()).describe('Built-in assets'), // 内置资源 }), }), - script2library: z.record(z.string(), z.string()).describe('Mapping relationship between scripts and libraries'), // 脚本与库的映射关系 + script2library: z.record(z.string(), z.string()).describe('Mapping between scripts and libraries'), // 脚本与库的映射关系 bundleConfigs: z.array(z.object({ - name: z.string().describe('Bundle name'), // bundle 名称 + name: z.string().describe('Bundle Name'), // bundle 名称 uuids: z.array(z.string()).describe('List of resource UUIDs in the bundle'), // bundle 中的资源 UUID 列表 paths: z.record(z.string(), z.array(z.string())).describe('List of resource paths in the bundle'), // bundle 中的资源路径列表 scenes: z.record(z.string(), z.union([z.string(), z.number()])).describe('List of scenes in the bundle'), // bundle 中的场景列表 @@ -311,14 +312,15 @@ export const SchemaPreviewSettingsResult = z.object({ extensionMap: z.record(z.string(), z.array(z.union([z.string(), z.number()]))).describe('List of extended resources in the bundle'), // bundle 中的扩展资源列表 dependencyRelationships: z.record(z.string(), z.array(z.union([z.string(), z.number()]))).describe('List of dependency relationships in the bundle'), // bundle 中的依赖关系列表 hasPreloadScript: z.boolean().describe('Whether the bundle has scripts that need to be preloaded'), // bundle 是否有需要预加载的脚本 - })).describe('Bundle configuration'), // bundle 配置 -}).describe('Get preview information result').nullable(); // 获取预览信息结果 + })).describe('Bundle Configuration'), // bundle 配置 +}).describe('Get Preview Information Result').nullable(); // 获取预览信息结果 export type TPreviewSettingsResult = z.infer; // ==================== Build Configuration Query Result ==================== // 构建配置查询结果 // Build configuration query result: union type, all fields required, including packages, excluding runtime options +// Build Configuration Query Result: Union type, all fields required, including packages, excluding runtime options // 构建配置查询结果:union 类型,所有字段必填,包含 packages,不包含运行时选项 export const SchemaBuildConfigResult = z.union([ SchemaWebDesktopBuildOption.omit({ configPath: true, skipCheck: true, taskId: true, taskName: true }), SchemaWebMobileBuildOption.omit({ configPath: true, skipCheck: true, taskId: true, taskName: true }), @@ -344,7 +346,7 @@ export type TWebDesktopPackages = z.infer; export type TWebMobilePackages = z.infer; // Run API Related Schema // Run API 相关 Schema -export const SchemaBuildDest = z.string().min(1).describe('Build output directory, supports absolute path and project:// protocol URL'); // 构建输出目录,支持绝对路径和 project:// 协议 URL +export const SchemaBuildDest = z.string().min(1).describe('Build Output Directory, supports absolute path and project:// protocol URL'); // 构建输出目录,支持绝对路径和 project:// 协议 URL export type TBuildDest = z.infer; export const SchemaRunResult = z.string().describe('Run URL'); // 运行 URL From 732e4524b5e1defb4034e0cc75f0e12337802224 Mon Sep 17 00:00:00 2001 From: fengyuzhe Date: Tue, 6 Jan 2026 10:28:42 +0800 Subject: [PATCH 6/8] refine --- src/api/builder/schema.ts | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/api/builder/schema.ts b/src/api/builder/schema.ts index ddc2cd12..b8097a9b 100644 --- a/src/api/builder/schema.ts +++ b/src/api/builder/schema.ts @@ -161,7 +161,7 @@ export const SchemaBuildBaseOption = SchemaBuildRuntimeOptions // Web Desktop Complete Build Options (Input, all fields optional) // Web Desktop 完整构建选项(入参,所有字段可选) export const SchemaWebDesktopBuildOption = SchemaBuildBaseOption .extend({ - platform: z.literal('web-desktop').describe('Build platform'), // 构建平台 + platform: z.literal('web-desktop').describe('Build Platform'), // 构建平台 packages: z.object({ 'web-desktop': SchemaWebDesktopPackages.partial() }).optional().describe('Web Desktop Platform Specific Configuration') // Web Desktop 平台特定配置 @@ -169,10 +169,9 @@ export const SchemaWebDesktopBuildOption = SchemaBuildBaseOption .describe('Web Desktop Complete Build Options (all fields optional)'); // Web Desktop 完整构建选项(所有字段可选) // Web Mobile Complete Build Options (Input, all fields optional) // Web Mobile 完整构建选项(入参,所有字段可选) -export const SchemaWebMobileBuildOption = SchemaBuildRuntimeOptions - .merge(SchemaBuildBaseConfig) +export const SchemaWebMobileBuildOption = SchemaBuildBaseOption .extend({ - platform: z.literal('web-mobile').describe('Build platform'), // 构建平台 + platform: z.literal('web-mobile').describe('Build Platform'), // 构建平台 packages: z.object({ 'web-mobile': SchemaWebMobilePackages.partial() }).optional().describe('Web Mobile Platform Specific Configuration') // Web Mobile 平台特定配置 @@ -182,7 +181,7 @@ export const SchemaWebMobileBuildOption = SchemaBuildRuntimeOptions // Windows Build Options // Windows 构建选项 export const SchemaWindowsBuildOption = SchemaBuildBaseOption .extend({ - platform: z.literal('windows').describe('Build platform') // 构建平台 + platform: z.literal('windows').describe('Build Platform') // 构建平台 }) .describe('Windows platform build options'); // Windows平台构建选项 @@ -203,7 +202,7 @@ export const SchemaIOSPackage = SchemaIOSPackageWithCatchall export const SchemaIOSBuildOption = SchemaBuildBaseOption .extend({ - platform: z.literal('ios').describe('Build platform'), // 构建平台 + platform: z.literal('ios').describe('Build Platform'), // 构建平台 packages: z.object({ ios: SchemaIOSPackage .optional() @@ -214,7 +213,7 @@ export const SchemaIOSBuildOption = SchemaBuildBaseOption // Android Build Options // Android 构建选项 export const SchemaAndroidBuildOption = SchemaBuildBaseOption .extend({ - platform: z.literal('android').describe('Build platform'), // 构建平台 + platform: z.literal('android').describe('Build Platform'), // 构建平台 packages: z.object({ android: SchemaAndroidPackage .catchall(z.any()) // 允许其他任意字段 @@ -227,7 +226,7 @@ export const SchemaAndroidBuildOption = SchemaBuildBaseOption // Mac Build Options // Mac 构建选项 export const SchemaMacBuildOption = SchemaBuildBaseOption .extend({ - platform: z.literal('mac').describe('Build platform'), // 构建平台 + platform: z.literal('mac').describe('Build Platform'), // 构建平台 packages: z.object({ mac: SchemaMacPackage .catchall(z.any()) // 允许其他任意字段 @@ -240,7 +239,7 @@ export const SchemaMacBuildOption = SchemaBuildBaseOption // Other Platform Build Options (Generic) // 其他平台构建选项(通用) export const SchemaOtherPlatformBuildOption = SchemaBuildBaseOption .extend({ - platform: SchemaPlatform.optional().describe('Build platform'), // 构建平台 + platform: SchemaPlatform.optional().describe('Build Platform'), // 构建平台 packages: z.any().optional().describe('Platform specific configuration'), // 平台特定配置 }) .describe('Other platform build options'); // 其他平台构建选项 @@ -288,11 +287,11 @@ export const SchemaPreviewSettingsResult = z.object({ CocosEngine: z.string().describe('Cocos Engine Version'), // Cocos Engine 版本 engine: z.object({ debug: z.boolean().describe('Whether it is debug mode'), // 是否是调试模式 - platform: z.string().describe('Build platform'), // 构建平台 - customLayers: z.array(z.object({ name: z.string(), bit: z.number() })).describe('Custom layers'), // 自定义层级 - sortingLayers: z.array(z.object({ id: z.number(), name: z.string(), value: z.number() })).describe('Sorting layers'), // 排序层级 - macros: z.record(z.string(), z.any()).describe('Macro definitions'), // 宏定义 - builtinAssets: z.array(z.string()).describe('Built-in assets'), // 内置资源 + platform: z.string().describe('Build Platform'), // 构建平台 + customLayers: z.array(z.object({ name: z.string(), bit: z.number() })).describe('Custom Layers'), // 自定义层级 + sortingLayers: z.array(z.object({ id: z.number(), name: z.string(), value: z.number() })).describe('Sorting Layers'), // 排序层级 + macros: z.record(z.string(), z.any()).describe('Macro Definitions'), // 宏定义 + builtinAssets: z.array(z.string()).describe('Built-in Assets'), // 内置资源 }), }), script2library: z.record(z.string(), z.string()).describe('Mapping between scripts and libraries'), // 脚本与库的映射关系 From 6f1354113e31e3a20caf96c97a39b7cb9118549f Mon Sep 17 00:00:00 2001 From: fengyuzhe Date: Tue, 6 Jan 2026 10:33:51 +0800 Subject: [PATCH 7/8] refine --- src/api/builder/schema.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/api/builder/schema.ts b/src/api/builder/schema.ts index b8097a9b..88a626f9 100644 --- a/src/api/builder/schema.ts +++ b/src/api/builder/schema.ts @@ -183,7 +183,7 @@ export const SchemaWindowsBuildOption = SchemaBuildBaseOption .extend({ platform: z.literal('windows').describe('Build Platform') // 构建平台 }) - .describe('Windows platform build options'); // Windows平台构建选项 + .describe('Windows Platform Build Options'); // Windows平台构建选项 // iOS Build Options // iOS 构建选项 const SchemaIOSPackageWithCatchall = SchemaIOSPackageBase.catchall(z.any()); @@ -198,7 +198,7 @@ export const SchemaIOSPackage = SchemaIOSPackageWithCatchall message: 'developerTeam is required when osTarget.iphoneos is true', path: ['developerTeam'] }) - .describe('iOS platform specific configuration'); + .describe('iOS Platform Specific Configuration'); export const SchemaIOSBuildOption = SchemaBuildBaseOption .extend({ @@ -206,9 +206,9 @@ export const SchemaIOSBuildOption = SchemaBuildBaseOption packages: z.object({ ios: SchemaIOSPackage .optional() - }).describe('iOS platform configuration') + }).describe('iOS Platform Configuration') // iOS平台配置 }) - .describe('iOS platform build options'); // iOS平台构建选项 + .describe('iOS Platform Build Options'); // iOS平台构建选项 // Android Build Options // Android 构建选项 export const SchemaAndroidBuildOption = SchemaBuildBaseOption @@ -218,9 +218,9 @@ export const SchemaAndroidBuildOption = SchemaBuildBaseOption android: SchemaAndroidPackage .catchall(z.any()) // 允许其他任意字段 .optional() - }).describe('Android platform configuration') // Android平台配置 + }).describe('Android Platform Configuration') // Android平台配置 }) - .describe('Android platform build options'); // Android平台构建选项 + .describe('Android Platform Build Options'); // Android平台构建选项 // Mac Build Options // Mac 构建选项 @@ -231,18 +231,18 @@ export const SchemaMacBuildOption = SchemaBuildBaseOption mac: SchemaMacPackage .catchall(z.any()) // 允许其他任意字段 .optional() - }).describe('Mac platform configuration') // Mac平台配置 + }).describe('Mac Platform Configuration') // Mac平台配置 }) - .describe('Mac platform build options'); // Mac平台构建选项 + .describe('Mac Platform Build Options'); // Mac平台构建选项 // Other Platform Build Options (Generic) // 其他平台构建选项(通用) export const SchemaOtherPlatformBuildOption = SchemaBuildBaseOption .extend({ platform: SchemaPlatform.optional().describe('Build Platform'), // 构建平台 - packages: z.any().optional().describe('Platform specific configuration'), // 平台特定配置 + packages: z.any().optional().describe('Platform Specific Configuration'), // 平台特定配置 }) - .describe('Other platform build options'); // 其他平台构建选项 + .describe('Other Platform Build Options (Generic)'); // 其他平台构建选项(通用) export const SchemaKnownBuildOptions = [ SchemaWebDesktopBuildOption, From 8565acda897342453101988f8ff3dce16c3c6cd6 Mon Sep 17 00:00:00 2001 From: fengyuzhe Date: Thu, 8 Jan 2026 18:35:23 +0800 Subject: [PATCH 8/8] build may be called by command --- src/commands/build.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/commands/build.ts b/src/commands/build.ts index 68be7da5..8016aa57 100644 --- a/src/commands/build.ts +++ b/src/commands/build.ts @@ -20,7 +20,20 @@ export class BuildCommand extends BaseCommand { try { const resolvedPath = this.validateProjectPath(options.project); - //read options.configPath move to builder.hook.ts + if (options.buildConfig) { + if (!existsSync(options.buildConfig)) { + console.error(`config: ${options.buildConfig} is not exist!`); + process.exit(BuildExitCode.BUILD_FAILED); + } + console.debug(`Read config from path ${options.buildConfig}...`); + let data = readJSONSync(options.buildConfig); + // 功能点:options 传递的值,允许覆盖配置文件内的同属性值 + data = Object.assign(data, options); + // 避免修改原始 options + Object.assign(options, data); + // 移除旧的 key 方便和 configPath 未读取的情况做区分 + delete options.buildConfig; + } // 处理 Android 平台特定的命令行参数 if (options.platform === 'android') {